Get rid of DECLARE_LUACLASS
[lsnes.git] / src / lua / inputmovie.cpp
blobc855021b66cb5aabefc6dc08c67d3442955d2d3b
1 #include "lua/internal.hpp"
2 #include "library/string.hpp"
3 #include "library/minmax.hpp"
4 #include "core/movie.hpp"
5 #include "core/moviedata.hpp"
6 #include "core/window.hpp"
7 #include <fstream>
9 void update_movie_state();
11 namespace
13 class lua_inputmovie;
15 class lua_inputframe
17 friend class lua_inputmovie;
18 public:
19 lua_inputframe(lua_state& L, controller_frame _f);
20 int get_button(lua_state& L, const std::string& fname)
22 unsigned port = L.get_numeric_argument<unsigned>(2, fname.c_str());
23 unsigned controller = L.get_numeric_argument<unsigned>(3, fname.c_str());
24 unsigned button = L.get_numeric_argument<unsigned>(4, fname.c_str());
25 short value = getbutton(port, controller, button);
26 L.pushboolean(value ? 1 : 0);
27 return 1;
29 int get_axis(lua_state& L, const std::string& fname)
31 unsigned port = L.get_numeric_argument<unsigned>(2, fname.c_str());
32 unsigned controller = L.get_numeric_argument<unsigned>(3, fname.c_str());
33 unsigned button = L.get_numeric_argument<unsigned>(4, fname.c_str());
34 short value = getbutton(port, controller, button);
35 L.pushnumber(value);
36 return 1;
38 int set_axis(lua_state& L, const std::string& fname)
40 unsigned port = L.get_numeric_argument<unsigned>(2, fname.c_str());
41 unsigned controller = L.get_numeric_argument<unsigned>(3, fname.c_str());
42 unsigned button = L.get_numeric_argument<unsigned>(4, fname.c_str());
43 short value;
44 if(L.type(5) == LUA_TBOOLEAN)
45 value = L.toboolean(5);
46 else if(L.type(5) == LUA_TNUMBER)
47 value = L.get_numeric_argument<short>(5, fname.c_str());
48 else
49 (stringfmt() << "Expected argument 5 of " << fname << " to be boolean or "
50 << "number").throwex();
51 setbutton(port, controller, button, value);
52 return 0;
54 int serialize(lua_state& L, const std::string& fname)
56 char buf[MAX_SERIALIZED_SIZE];
57 f.serialize(buf);
58 L.pushstring(buf);
59 return 1;
61 int unserialize(lua_state& L, const std::string& fname)
63 std::string buf = L.get_string(2, fname.c_str());
64 f.deserialize(buf.c_str());
65 return 0;
67 int get_stride(lua_state& L, const std::string& fname)
69 L.pushnumber(f.size());
70 return 1;
72 controller_frame& get_frame()
74 return f;
76 std::string print()
78 char buf[MAX_SERIALIZED_SIZE];
79 f.serialize(buf);
80 return buf;
82 private:
83 short getbutton(unsigned port, unsigned controller, unsigned index)
85 return f.axis3(port, controller, index);
87 void setbutton(unsigned port, unsigned controller, unsigned index, short value)
89 return f.axis3(port, controller, index, value);
91 controller_frame f;
94 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0 = false);
95 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0)
97 movie& m = movb.get_movie();
98 if(port == 0 && controller == 0 && button == 0)
99 return m.get_pollcounters().max_polls() + (extra0 ? 1 : 0);
100 if(port == 0 && controller == 0 && m.get_pollcounters().get_framepflag())
101 return max(m.get_pollcounters().get_polls(port, controller, button), (uint32_t)1);
102 return m.get_pollcounters().get_polls(port, controller, button);
105 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
106 bool allow_past_end = false);
108 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
109 bool allow_past_end)
111 movie& m = movb.get_movie();
112 if(!m.readonly_mode())
113 throw std::runtime_error("Not in read-only mode");
114 if(!allow_past_end && frame >= movb.get_movie().get_frame_vector().size())
115 throw std::runtime_error("Index out of movie");
116 int32_t pc = get_pc_for(port, controller, button, true);
117 if(pc < 0)
118 throw std::runtime_error("Invalid control to edit");
119 uint64_t firstframe = m.get_current_frame_first_subframe();
120 uint64_t minframe = firstframe + pc;
121 uint64_t msize = movb.get_movie().get_frame_vector().size();
122 if(minframe > msize || firstframe >= msize)
123 throw std::runtime_error("Can not edit finished movie");
124 if(frame < minframe)
125 throw std::runtime_error("Can not edit past");
128 function_ptr_luafun movie_cfs(lua_func_misc, "movie.current_first_subframe", [](lua_state& L,
129 const std::string& fname) -> int {
130 movie& m = movb.get_movie();
131 L.pushnumber(m.get_current_frame_first_subframe());
132 return 1;
135 function_ptr_luafun movie_pc(lua_func_misc, "movie.pollcounter", [](lua_state& L, const std::string& fname)
136 -> int {
137 unsigned port = L.get_numeric_argument<unsigned>(1, fname.c_str());
138 unsigned controller = L.get_numeric_argument<unsigned>(2, fname.c_str());
139 unsigned button = L.get_numeric_argument<unsigned>(3, fname.c_str());
140 uint32_t ret = 0;
141 ret = get_pc_for(port, controller, button);
142 L.pushnumber(ret);
143 return 1;
146 controller_frame_vector& framevector(lua_state& L, int& ptr, const std::string& fname);
148 int _copy_movie(lua_state& L, const std::string& fname)
150 int ptr = 1;
151 controller_frame_vector& v = framevector(L, ptr, fname);
153 lua_inputmovie* m = lua_class<lua_inputmovie>::create(L, v);
154 return 1;
157 int _get_frame(lua_state& L, const std::string& fname)
159 int ptr = 1;
160 controller_frame_vector& v = framevector(L, ptr, fname);
162 uint64_t n = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
163 if(n >= v.size())
164 throw std::runtime_error("Requested frame outside movie");
165 controller_frame _f = v[n];
166 lua_inputframe* f = lua_class<lua_inputframe>::create(L, _f);
167 return 1;
170 int _set_frame(lua_state& L, const std::string& fname)
172 int ptr = 1;
173 controller_frame_vector& v = framevector(L, ptr, fname);
175 uint64_t n = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
176 if(n >= v.size())
177 throw std::runtime_error("Requested frame outside movie");
178 //Checks if requested frame is from movie.
179 if(&v == &movb.get_movie().get_frame_vector())
180 check_can_edit(0, 0, 0, n);
182 lua_inputframe* f = lua_class<lua_inputframe>::get(L, ptr++, fname.c_str());
183 int64_t adjust = 0;
184 if(v[n].sync()) adjust--;
185 v[n] = f->get_frame();
186 if(v[n].sync()) adjust++;
188 if(&v == &movb.get_movie().get_frame_vector()) {
189 movb.get_movie().adjust_frame_count(adjust);
190 update_movie_state();
191 platform::notify_status();
193 return 0;
196 int _get_size(lua_state& L, const std::string& fname)
198 int ptr = 1;
199 controller_frame_vector& v = framevector(L, ptr, fname);
201 L.pushnumber(v.size());
202 return 1;
205 int _count_frames(lua_state& L, const std::string& fname)
207 int ptr = 1;
208 controller_frame_vector& v = framevector(L, ptr, fname);
210 uint64_t c = 0;
211 uint64_t s = v.size();
212 for(uint64_t i = 0; i < s; i++)
213 if(v[i].sync())
214 c++;
215 L.pushnumber(c);
216 return 1;
219 int _find_frame(lua_state& L, const std::string& fname)
221 int ptr = 1;
222 controller_frame_vector& v = framevector(L, ptr, fname);
224 uint64_t n = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
225 if(!n) {
226 L.pushnumber(-1);
227 return 1;
229 uint64_t c = 0;
230 uint64_t s = v.size();
231 for(uint64_t i = 0; i < s; i++)
232 if(v[i].sync() && ++c == n) {
233 L.pushnumber(i);
234 return 1;
236 L.pushnumber(-1);
237 return 1;
240 int _blank_frame(lua_state& L, const std::string& fname)
242 int ptr = 1;
243 controller_frame_vector& v = framevector(L, ptr, fname);
245 controller_frame _f = v.blank_frame(true);
246 lua_inputframe* f = lua_class<lua_inputframe>::create(L, _f);
247 return 1;
250 int _append_frames(lua_state& L, const std::string& fname)
252 int ptr = 1;
253 controller_frame_vector& v = framevector(L, ptr, fname);
255 uint64_t count = L.get_numeric_argument<uint64_t>(ptr++, "lua_inputmovie::append_frames");
256 for(uint64_t i = 0; i < count; i++)
257 v.append(v.blank_frame(true));
258 if(&v == &movb.get_movie().get_frame_vector()) {
259 movb.get_movie().adjust_frame_count(count);
260 update_movie_state();
261 platform::notify_status();
263 return 0;
266 int _append_frame(lua_state& L, const std::string& fname)
268 int ptr = 1;
269 controller_frame_vector& v = framevector(L, ptr, fname);
271 lua_inputframe* f = lua_class<lua_inputframe>::get(L, ptr++, fname.c_str());
273 v.append(v.blank_frame(true));
274 if(&v == &movb.get_movie().get_frame_vector()) {
275 movb.get_movie().adjust_frame_count(1);
276 update_movie_state();
277 platform::notify_status();
278 check_can_edit(0, 0, 0, v.size() - 1);
280 v[v.size() - 1] = f->get_frame();
281 if(&v == &movb.get_movie().get_frame_vector()) {
282 if(!v[v.size() - 1].sync()) {
283 movb.get_movie().adjust_frame_count(-1);
284 update_movie_state();
286 platform::notify_status();
288 return 0;
291 int _truncate(lua_state& L, const std::string& fname)
293 int ptr = 1;
294 controller_frame_vector& v = framevector(L, ptr, fname);
296 uint64_t n = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
297 if(n > v.size())
298 throw std::runtime_error("Requested truncate length longer than existing");
299 if(&v == &movb.get_movie().get_frame_vector())
300 check_can_edit(0, 0, 0, n);
301 v.resize(n);
302 if(&v == &movb.get_movie().get_frame_vector()) {
303 movb.get_movie().recount_frames();
304 update_movie_state();
305 platform::notify_status();
307 return 0;
310 int _edit(lua_state& L, const std::string& fname)
312 int ptr = 1;
313 controller_frame_vector& v = framevector(L, ptr, fname);
315 uint64_t frame = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
316 unsigned port = L.get_numeric_argument<unsigned>(ptr++, fname.c_str());
317 unsigned controller = L.get_numeric_argument<unsigned>(ptr++, fname.c_str());
318 unsigned button = L.get_numeric_argument<unsigned>(ptr++, fname.c_str());
319 short value;
320 int64_t schange = 0;
321 if(L.type(ptr) == LUA_TBOOLEAN)
322 value = L.toboolean(ptr);
323 else if(L.type(ptr) == LUA_TNUMBER)
324 value = L.get_numeric_argument<short>(ptr++, fname.c_str());
325 else
326 (stringfmt() << "Expected argument " << (ptr - 1) << " of " << fname << " to be boolean or "
327 << "number").throwex();
328 movie& m = movb.get_movie();
329 if(&v == &movb.get_movie().get_frame_vector())
330 check_can_edit(port, controller, button, frame);
331 if(port == 0 && controller == 0 && button == 0)
332 if(v[frame].sync() && !value) schange = -1;
333 v[frame].axis3(port, controller, button, value);
334 if(port == 0 && controller == 0 && button == 0)
335 if(!v[frame].sync() && value) schange = 1;
337 if(&v == &movb.get_movie().get_frame_vector()) {
338 if(schange) {
339 movb.get_movie().adjust_frame_count(schange);
340 update_movie_state();
342 platform::notify_status();
344 return 0;
347 template<bool same>
348 int _copy_frames(lua_state& L, const std::string& fname)
350 int ptr = 1;
351 controller_frame_vector& dstv = framevector(L, ptr, fname);
352 uint64_t dst = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
353 controller_frame_vector& srcv = same ? dstv : framevector(L, ptr, fname);
354 uint64_t src = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
355 uint64_t count = L.get_numeric_argument<uint64_t>(ptr++, fname.c_str());
356 int64_t schange = 0;
357 bool backwards = same ? L.get_bool(ptr++, fname.c_str()) : same;
359 if(src >= srcv.size() || src + count < src)
360 throw std::runtime_error("Source index out of movie");
361 if(dst > dstv.size() || dst + count < dst)
362 throw std::runtime_error("Destination index out of movie");
364 movie& m = movb.get_movie();
365 if(&dstv == &movb.get_movie().get_frame_vector())
366 check_can_edit(0, 0, 0, dst, true);
368 //Add enough blank frames to make the copy.
369 while(dst + count > dstv.size())
370 dstv.append(dstv.blank_frame(false));
372 for(uint64_t i = backwards ? (count - 1) : 0; i < count; i = backwards ? (i - 1) : (i + 1)) {
373 if(dstv[dst + i].sync()) schange--;
374 dstv[dst + i] = srcv[src + i];
375 if(dstv[dst + i].sync()) schange++;
377 if(&dstv == &movb.get_movie().get_frame_vector()) {
378 if(schange) {
379 movb.get_movie().adjust_frame_count(schange);
380 update_movie_state();
382 platform::notify_status();
384 return 0;
387 int _serialize(lua_state& L, const std::string& fname)
389 int ptr = 1;
390 controller_frame_vector& v = framevector(L, ptr, fname);
391 std::string filename = L.get_string(ptr++, fname.c_str());
392 bool binary = L.get_bool(ptr++, fname.c_str());
393 std::ofstream file(filename, binary ? std::ios_base::binary : std::ios_base::out);
394 if(!file)
395 throw std::runtime_error("Can't open file to write output to");
396 if(binary) {
397 uint64_t pages = v.get_page_count();
398 uint64_t stride = v.get_stride();
399 uint64_t pageframes = v.get_frames_per_page();
400 uint64_t vsize = v.size();
401 size_t pagenum = 0;
402 while(vsize > 0) {
403 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
404 size_t bytes = count * stride;
405 unsigned char* content = v.get_page_buffer(pagenum++);
406 file.write(reinterpret_cast<char*>(content), bytes);
407 vsize -= count;
409 } else {
410 char buf[MAX_SERIALIZED_SIZE];
411 for(uint64_t i = 0; i < v.size(); i++) {
412 v[i].serialize(buf);
413 file << buf << std::endl;
416 return 0;
419 class lua_inputmovie
421 public:
422 lua_inputmovie(lua_state& L, const controller_frame_vector& _v);
423 lua_inputmovie(lua_state& L, controller_frame& _f);
424 int copy_movie(lua_state& L, const std::string& fname)
426 return _copy_movie(L, fname.c_str());
428 int get_frame(lua_state& L, const std::string& fname)
430 return _get_frame(L, fname.c_str());
432 int set_frame(lua_state& L, const std::string& fname)
434 return _set_frame(L, fname.c_str());
436 int get_size(lua_state& L, const std::string& fname)
438 return _get_size(L, fname.c_str());
440 int count_frames(lua_state& L, const std::string& fname)
442 return _count_frames(L, fname.c_str());
444 int find_frame(lua_state& L, const std::string& fname)
446 return _find_frame(L, fname.c_str());
448 int blank_frame(lua_state& L, const std::string& fname)
450 return _blank_frame(L, fname.c_str());
452 int append_frames(lua_state& L, const std::string& fname)
454 return _append_frames(L, fname.c_str());
456 int append_frame(lua_state& L, const std::string& fname)
458 return _append_frame(L, fname.c_str());
460 int truncate(lua_state& L, const std::string& fname)
462 return _truncate(L, fname.c_str());
464 int edit(lua_state& L, const std::string& fname)
466 return _edit(L, fname.c_str());
468 int copy_frames(lua_state& L, const std::string& fname)
470 return _copy_frames<true>(L, fname.c_str());
472 int serialize(lua_state& L, const std::string& fname)
474 return _serialize(L, fname.c_str());
476 int debugdump(lua_state& L, const std::string& fname)
478 char buf[MAX_SERIALIZED_SIZE];
479 for(uint64_t i = 0; i < v.size(); i++) {
480 v[i].serialize(buf);
481 messages << buf << std::endl;
483 return 0;
485 controller_frame_vector* get_frame_vector()
487 return &v;
489 std::string print()
491 size_t s = v.size();
492 return (stringfmt() << s << " " << ((s != 1) ? "frames" : "frame")).str();
494 private:
495 void common_init(lua_state& L);
496 controller_frame_vector v;
499 function_ptr_luafun movie_getdata(lua_func_misc, "movie.copy_movie", [](lua_state& L,
500 const std::string& fname) -> int {
501 return _copy_movie(L, fname);
504 function_ptr_luafun movie_getframe(lua_func_misc, "movie.get_frame", [](lua_state& L,
505 const std::string& fname) -> int {
506 return _get_frame(L, fname);
509 function_ptr_luafun movie_setframe(lua_func_misc, "movie.set_frame", [](lua_state& L,
510 const std::string& fname) -> int {
511 return _set_frame(L, fname);
514 function_ptr_luafun movie_get_size(lua_func_misc, "movie.get_size", [](lua_state& L,
515 const std::string& fname) -> int {
516 return _get_size(L, fname);
519 function_ptr_luafun movie_count_frames(lua_func_misc, "movie.count_frames", [](lua_state& L,
520 const std::string& fname) -> int {
521 return _count_frames(L, fname);
524 function_ptr_luafun movie_find_frame(lua_func_misc, "movie.find_frame", [](lua_state& L,
525 const std::string& fname) -> int {
526 return _find_frame(L, fname);
529 function_ptr_luafun movie_blank_frame(lua_func_misc, "movie.blank_frame", [](lua_state& L,
530 const std::string& fname) -> int {
531 return _blank_frame(L, fname);
534 function_ptr_luafun movie_append_frames(lua_func_misc, "movie.append_frames", [](lua_state& L,
535 const std::string& fname) -> int {
536 return _append_frames(L, fname);
539 function_ptr_luafun movie_append_frame(lua_func_misc, "movie.append_frame", [](lua_state& L,
540 const std::string& fname) -> int {
541 return _append_frame(L, fname);
544 function_ptr_luafun movie_truncate(lua_func_misc, "movie.truncate", [](lua_state& L, const std::string& fname)
545 -> int {
546 return _truncate(L, fname);
549 function_ptr_luafun movie_edit(lua_func_misc, "movie.edit", [](lua_state& L, const std::string& fname)
550 -> int {
551 return _edit(L, fname);
554 function_ptr_luafun movie_copyframe2(lua_func_misc, "movie.copy_frames2", [](lua_state& L,
555 const std::string& fname) -> int {
556 return _copy_frames<false>(L, fname);
559 function_ptr_luafun movie_copyframe(lua_func_misc, "movie.copy_frames", [](lua_state& L,
560 const std::string& fname) -> int {
561 return _copy_frames<true>(L, fname);
564 function_ptr_luafun movie_serialize(lua_func_misc, "movie.serialize", [](lua_state& L,
565 const std::string& fname) -> int {
566 return _serialize(L, fname);
569 function_ptr_luafun movie_unserialize(lua_func_misc, "movie.unserialize", [](lua_state& L,
570 const std::string& fname) -> int {
571 lua_inputframe* f = lua_class<lua_inputframe>::get(L, 1, fname.c_str());
572 std::string filename = L.get_string(2, fname.c_str());
573 bool binary = L.get_bool(3, fname.c_str());
574 std::ifstream file(filename, binary ? std::ios_base::binary : std::ios_base::in);
575 if(!file)
576 throw std::runtime_error("Can't open file to read input from");
577 lua_inputmovie* m = lua_class<lua_inputmovie>::create(L, f->get_frame());
578 controller_frame_vector& v = *m->get_frame_vector();
579 if(binary) {
580 uint64_t stride = v.get_stride();
581 uint64_t pageframes = v.get_frames_per_page();
582 uint64_t vsize = 0;
583 size_t pagenum = 0;
584 size_t pagesize = stride * pageframes;
585 while(file) {
586 v.resize(vsize + pageframes);
587 unsigned char* contents = v.get_page_buffer(pagenum++);
588 file.read(reinterpret_cast<char*>(contents), pagesize);
589 vsize += (file.gcount() / stride);
591 v.resize(vsize);
592 } else {
593 std::string line;
594 controller_frame tmpl = v.blank_frame(false);
595 while(file) {
596 std::getline(file, line);
597 istrip_CR(line);
598 if(line.length() == 0)
599 continue;
600 tmpl.deserialize(line.c_str());
601 v.append(tmpl);
604 return 1;
608 controller_frame_vector& framevector(lua_state& L, int& ptr, const std::string& fname)
610 if(L.type(ptr) == LUA_TNIL) { //NONE can be handled as else case.
611 ptr++;
612 return movb.get_movie().get_frame_vector();
613 } else if(lua_class<lua_inputmovie>::is(L, ptr))
614 return *lua_class<lua_inputmovie>::get(L, ptr++, fname.c_str())->get_frame_vector();
615 else
616 return movb.get_movie().get_frame_vector();
619 lua_class<lua_inputmovie> class_inputmovie("INPUTMOVIE");
620 lua_class<lua_inputframe> class_inputframe("INPUTFRAME");
622 lua_inputframe::lua_inputframe(lua_state& L, controller_frame _f)
624 f = _f;
625 objclass<lua_inputframe>().bind_multi(L, {
626 {"get_button", &lua_inputframe::get_button},
627 {"get_axis", &lua_inputframe::get_axis},
628 {"set_axis", &lua_inputframe::set_axis},
629 {"set_button", &lua_inputframe::set_axis},
630 {"serialize", &lua_inputframe::serialize},
631 {"unserialize", &lua_inputframe::unserialize},
632 {"get_stride", &lua_inputframe::get_stride},
636 void lua_inputmovie::common_init(lua_state& L)
638 objclass<lua_inputmovie>().bind_multi(L, {
639 {"copy_movie", &lua_inputmovie::copy_movie},
640 {"get_frame", &lua_inputmovie::get_frame},
641 {"set_frame", &lua_inputmovie::set_frame},
642 {"get_size", &lua_inputmovie::get_size},
643 {"count_frames", &lua_inputmovie::count_frames},
644 {"find_frame", &lua_inputmovie::find_frame},
645 {"blank_frame", &lua_inputmovie::blank_frame},
646 {"append_frames", &lua_inputmovie::append_frames},
647 {"append_frame", &lua_inputmovie::append_frame},
648 {"truncate", &lua_inputmovie::truncate},
649 {"edit", &lua_inputmovie::edit},
650 {"debugdump", &lua_inputmovie::debugdump},
651 {"copy_frames", &lua_inputmovie::copy_frames},
652 {"serialize", &lua_inputmovie::serialize},
656 lua_inputmovie::lua_inputmovie(lua_state& L, const controller_frame_vector& _v)
658 v = _v;
659 common_init(L);
662 lua_inputmovie::lua_inputmovie(lua_state& L, controller_frame& f)
664 v.clear(f.porttypes());
665 common_init(L);