lsnes rr2-β24
[lsnes.git] / src / lua / inputmovie.cpp
bloba22e7c7ce6e783fdb34001c295b1d8e882dca5d0
1 #include "lua/internal.hpp"
2 #include "library/string.hpp"
3 #include "library/minmax.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/instance.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/messages.hpp"
8 #include "core/window.hpp"
9 #include <fstream>
12 namespace
14 class lua_inputmovie;
16 class lua_inputframe
18 friend class lua_inputmovie;
19 public:
20 lua_inputframe(lua::state& L, portctrl::frame _f);
21 static size_t overcommit(portctrl::frame _f) { return 0; }
22 int get_button(lua::state& L, lua::parameters& P)
24 unsigned port, controller, button;
26 P(P.skipped(), port, controller, button);
28 short value = getbutton(port, controller, button);
29 L.pushboolean(value ? 1 : 0);
30 return 1;
32 int get_axis(lua::state& L, lua::parameters& P)
34 unsigned port, controller, button;
36 P(P.skipped(), port, controller, button);
38 short value = getbutton(port, controller, button);
39 L.pushnumber(value);
40 return 1;
42 int set_axis(lua::state& L, lua::parameters& P)
44 unsigned port, controller, button;
45 short value;
47 P(P.skipped(), port, controller, button);
48 if(P.is_boolean()) value = P.arg<bool>() ? 1 : 0;
49 else if(P.is_number()) value = P.arg<short>();
50 else
51 P.expected("number or boolean");
53 setbutton(port, controller, button, value);
54 return 0;
56 int serialize(lua::state& L, lua::parameters& P)
58 char buf[MAX_SERIALIZED_SIZE];
59 f.serialize(buf);
60 L.pushstring(buf);
61 return 1;
63 int unserialize(lua::state& L, lua::parameters& P)
65 std::string buf;
67 P(P.skipped(), buf);
69 f.deserialize(buf.c_str());
70 return 0;
72 int get_stride(lua::state& L, lua::parameters& P)
74 L.pushnumber(f.size());
75 return 1;
77 portctrl::frame& get_frame()
79 return f;
81 std::string print()
83 char buf[MAX_SERIALIZED_SIZE];
84 f.serialize(buf);
85 return buf;
87 private:
88 short getbutton(unsigned port, unsigned controller, unsigned index)
90 return f.axis3(port, controller, index);
92 void setbutton(unsigned port, unsigned controller, unsigned index, short value)
94 return f.axis3(port, controller, index, value);
96 portctrl::frame f;
99 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0 = false);
100 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0)
102 movie& m = CORE().mlogic->get_movie();
103 if(port == 0 && controller == 0 && button == 0)
104 return m.get_pollcounters().max_polls() + (extra0 ? 1 : 0);
105 if(port == 0 && controller == 0 && m.get_pollcounters().get_framepflag())
106 return max(m.get_pollcounters().get_polls(port, controller, button), (uint32_t)1);
107 return m.get_pollcounters().get_polls(port, controller, button);
110 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
111 bool allow_past_end = false);
113 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
114 bool allow_past_end)
116 auto& core = CORE();
117 movie& m = core.mlogic->get_movie();
118 if(!m.readonly_mode())
119 throw std::runtime_error("Not in playback mode");
120 if(!allow_past_end && frame >= core.mlogic->get_mfile().input->size())
121 throw std::runtime_error("Index out of movie");
122 int32_t pc = get_pc_for(port, controller, button, true);
123 if(pc < 0)
124 throw std::runtime_error("Invalid control to edit");
125 uint64_t firstframe = m.get_current_frame_first_subframe();
126 uint64_t minframe = firstframe + pc;
127 uint64_t msize = core.mlogic->get_mfile().input->size();
128 if(minframe > msize || firstframe >= msize)
129 throw std::runtime_error("Can not edit finished movie");
130 if(frame < minframe)
131 throw std::runtime_error("Can not edit past");
134 int current_first_subframe(lua::state& L, lua::parameters& P)
136 movie& m = CORE().mlogic->get_movie();
137 L.pushnumber(m.get_current_frame_first_subframe());
138 return 1;
141 int pollcounter(lua::state& L, lua::parameters& P)
143 unsigned port, controller, button;
145 P(port, controller, button);
147 uint32_t ret = 0;
148 ret = get_pc_for(port, controller, button);
149 L.pushnumber(ret);
150 return 1;
153 portctrl::frame_vector& framevector(lua::state& L, lua::parameters& P);
155 int _copy_movie(lua::state& L, lua::parameters& P)
157 portctrl::frame_vector& v = framevector(L, P);
159 lua::_class<lua_inputmovie>::create(L, v);
160 return 1;
163 int _get_frame(lua::state& L, lua::parameters& P)
165 uint64_t n;
166 portctrl::frame_vector& v = framevector(L, P);
168 P(n);
170 if(n >= v.size())
171 throw std::runtime_error("Requested frame outside movie");
172 portctrl::frame _f = v[n];
173 lua::_class<lua_inputframe>::create(L, _f);
174 return 1;
177 int _set_frame(lua::state& L, lua::parameters& P)
179 auto& core = CORE();
180 uint64_t n;
181 lua_inputframe* f;
182 portctrl::frame_vector& v = framevector(L, P);
184 P(n, f);
186 if(n >= v.size())
187 throw std::runtime_error("Requested frame outside movie");
188 //Checks if requested frame is from movie.
189 if(&v == core.mlogic->get_mfile().input)
190 check_can_edit(0, 0, 0, n);
192 v[n] = f->get_frame();
194 if(&v == core.mlogic->get_mfile().input) {
195 //This can't add frames, so no need to adjust the movie.
196 core.supdater->update();
197 core.dispatch->status_update();
199 return 0;
202 int _get_size(lua::state& L, lua::parameters& P)
204 portctrl::frame_vector& v = framevector(L, P);
206 L.pushnumber(v.size());
207 return 1;
210 int _count_frames(lua::state& L, lua::parameters& P)
212 portctrl::frame_vector& v = framevector(L, P);
214 L.pushnumber(v.count_frames());
215 return 1;
218 int _find_frame(lua::state& L, lua::parameters& P)
220 uint64_t n;
221 portctrl::frame_vector& v = framevector(L, P);
223 P(n);
225 L.pushnumber(v.find_frame(n));
226 return 1;
229 int _subframe_to_frame(lua::state& L, lua::parameters& P)
231 uint64_t n;
232 portctrl::frame_vector& v = framevector(L, P);
234 P(n);
236 L.pushnumber(v.subframe_to_frame(n));
237 return 1;
240 int _blank_frame(lua::state& L, lua::parameters& P)
242 portctrl::frame_vector& v = framevector(L, P);
244 portctrl::frame _f = v.blank_frame(true);
245 lua::_class<lua_inputframe>::create(L, _f);
246 return 1;
249 int _append_frames(lua::state& L, lua::parameters& P)
251 auto& core = CORE();
252 uint64_t count;
253 portctrl::frame_vector& v = framevector(L, P);
255 P(count);
258 portctrl::frame_vector::notify_freeze freeze(v);
259 for(uint64_t i = 0; i < count; i++)
260 v.append(v.blank_frame(true));
262 if(&v == core.mlogic->get_mfile().input) {
263 core.supdater->update();
264 core.dispatch->status_update();
266 return 0;
269 int _append_frame(lua::state& L, lua::parameters& P)
271 auto& core = CORE();
272 lua_inputframe* f;
273 portctrl::frame_vector& v = framevector(L, P);
275 P(f);
277 v.append(v.blank_frame(true));
278 if(&v == core.mlogic->get_mfile().input) {
279 core.supdater->update();
280 core.dispatch->status_update();
281 check_can_edit(0, 0, 0, v.size() - 1);
283 v[v.size() - 1] = f->get_frame();
284 if(&v == core.mlogic->get_mfile().input) {
285 if(!v[v.size() - 1].sync()) {
286 core.supdater->update();
288 core.dispatch->status_update();
290 return 0;
293 int _truncate(lua::state& L, lua::parameters& P)
295 auto& core = CORE();
296 uint64_t n;
297 portctrl::frame_vector& v = framevector(L, P);
299 P(n);
301 if(n > v.size())
302 throw std::runtime_error("Requested truncate length longer than existing");
303 if(&v == core.mlogic->get_mfile().input)
304 check_can_edit(0, 0, 0, n);
305 v.resize(n);
306 if(&v == core.mlogic->get_mfile().input) {
307 core.supdater->update();
308 core.dispatch->status_update();
310 return 0;
313 int _edit(lua::state& L, lua::parameters& P)
315 auto& core = CORE();
316 uint64_t frame;
317 unsigned port, controller, button;
318 short value;
319 portctrl::frame_vector& v = framevector(L, P);
321 P(frame, port, controller, button);
322 if(P.is_boolean()) value = P.arg<bool>() ? 1 : 0;
323 else if(P.is_number()) P(value);
324 else
325 P.expected("number or boolean");
327 if(&v == core.mlogic->get_mfile().input)
328 check_can_edit(port, controller, button, frame);
329 v[frame].axis3(port, controller, button, value);
331 if(&v == core.mlogic->get_mfile().input) {
332 core.supdater->update();
333 core.dispatch->status_update();
335 return 0;
338 template<bool same>
339 int _copy_frames(lua::state& L, lua::parameters& P)
341 auto& core = CORE();
342 uint64_t dst, src, count;
343 bool backwards = same;
345 portctrl::frame_vector& dstv = framevector(L, P);
346 P(dst);
347 portctrl::frame_vector& srcv = same ? dstv : framevector(L, P);
348 P(src, count);
349 if(same) P(backwards);
351 if(src >= srcv.size() || src + count < src)
352 throw std::runtime_error("Source index out of movie");
353 if(dst > dstv.size() || dst + count < dst)
354 throw std::runtime_error("Destination index out of movie");
356 if(&dstv == core.mlogic->get_mfile().input)
357 check_can_edit(0, 0, 0, dst, true);
360 portctrl::frame_vector::notify_freeze freeze(dstv);
361 //Add enough blank frames to make the copy.
362 while(dst + count > dstv.size())
363 dstv.append(dstv.blank_frame(false));
365 for(uint64_t i = backwards ? (count - 1) : 0; i < count; i = backwards ? (i - 1) : (i + 1))
366 dstv[dst + i] = srcv[src + i];
368 if(&dstv == core.mlogic->get_mfile().input) {
369 core.supdater->update();
370 core.dispatch->status_update();
372 return 0;
375 int _serialize(lua::state& L, lua::parameters& P)
377 std::string filename;
378 bool binary;
380 portctrl::frame_vector& v = framevector(L, P);
382 P(filename, binary);
384 std::ofstream file(filename, binary ? std::ios_base::binary : std::ios_base::out);
385 if(!file)
386 throw std::runtime_error("Can't open file to write output to");
387 if(binary) {
388 uint64_t stride = v.get_stride();
389 uint64_t pageframes = v.get_frames_per_page();
390 uint64_t vsize = v.size();
391 size_t pagenum = 0;
392 while(vsize > 0) {
393 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
394 size_t bytes = count * stride;
395 unsigned char* content = v.get_page_buffer(pagenum++);
396 file.write(reinterpret_cast<char*>(content), bytes);
397 vsize -= count;
399 } else {
400 char buf[MAX_SERIALIZED_SIZE];
401 for(uint64_t i = 0; i < v.size(); i++) {
402 v[i].serialize(buf);
403 file << buf << std::endl;
406 return 0;
409 class lua_inputmovie
411 public:
412 lua_inputmovie(lua::state& L, const portctrl::frame_vector& _v);
413 lua_inputmovie(lua::state& L, portctrl::frame& _f);
414 static size_t overcommit(const portctrl::frame_vector& _v) { return 0; }
415 static size_t overcommit(portctrl::frame& _f) { return 0; }
416 int copy_movie(lua::state& L, lua::parameters& P)
418 return _copy_movie(L, P);
420 int get_frame(lua::state& L, lua::parameters& P)
422 return _get_frame(L, P);
424 int set_frame(lua::state& L, lua::parameters& P)
426 return _set_frame(L, P);
428 int get_size(lua::state& L, lua::parameters& P)
430 return _get_size(L, P);
432 int count_frames(lua::state& L, lua::parameters& P)
434 return _count_frames(L, P);
436 int find_frame(lua::state& L, lua::parameters& P)
438 return _find_frame(L, P);
440 int subframe_to_frame(lua::state& L, lua::parameters& P)
442 return _subframe_to_frame(L, P);
444 int blank_frame(lua::state& L, lua::parameters& P)
446 return _blank_frame(L, P);
448 int append_frames(lua::state& L, lua::parameters& P)
450 return _append_frames(L, P);
452 int append_frame(lua::state& L, lua::parameters& P)
454 return _append_frame(L, P);
456 int truncate(lua::state& L, lua::parameters& P)
458 return _truncate(L, P);
460 int edit(lua::state& L, lua::parameters& P)
462 return _edit(L, P);
464 int copy_frames(lua::state& L, lua::parameters& P)
466 return _copy_frames<true>(L, P);
468 int serialize(lua::state& L, lua::parameters& P)
470 return _serialize(L, P);
472 int debugdump(lua::state& L, lua::parameters& P)
474 char buf[MAX_SERIALIZED_SIZE];
475 for(uint64_t i = 0; i < v.size(); i++) {
476 v[i].serialize(buf);
477 messages << buf << std::endl;
479 return 0;
481 portctrl::frame_vector* get_frame_vector()
483 return &v;
485 std::string print()
487 size_t s = v.size();
488 return (stringfmt() << s << " " << ((s != 1) ? "frames" : "frame")).str();
490 private:
491 void common_init(lua::state& L);
492 portctrl::frame_vector v;
495 int copy_movie(lua::state& L, lua::parameters& P)
497 return _copy_movie(L, P);
500 int get_frame(lua::state& L, lua::parameters& P)
502 return _get_frame(L, P);
505 int set_frame(lua::state& L, lua::parameters& P)
507 return _set_frame(L, P);
510 int get_size(lua::state& L, lua::parameters& P)
512 return _get_size(L, P);
515 int count_frames(lua::state& L, lua::parameters& P)
517 return _count_frames(L, P);
520 int find_frame(lua::state& L, lua::parameters& P)
522 return _find_frame(L, P);
525 int subframe_to_frame(lua::state& L, lua::parameters& P)
527 return _subframe_to_frame(L, P);
530 int blank_frame(lua::state& L, lua::parameters& P)
532 return _blank_frame(L, P);
535 int append_frames(lua::state& L, lua::parameters& P)
537 return _append_frames(L, P);
540 int append_frame(lua::state& L, lua::parameters& P)
542 return _append_frame(L, P);
545 int truncate(lua::state& L, lua::parameters& P)
547 return _truncate(L, P);
550 int edit(lua::state& L, lua::parameters& P)
552 return _edit(L, P);
555 int copy_frames2(lua::state& L, lua::parameters& P)
557 return _copy_frames<false>(L, P);
560 int copy_frames(lua::state& L, lua::parameters& P)
562 return _copy_frames<true>(L, P);
565 int serialize(lua::state& L, lua::parameters& P)
567 return _serialize(L, P);
570 int unserialize(lua::state& L, lua::parameters& P)
572 lua_inputframe* f;
573 std::string filename;
574 bool binary;
576 P(f, filename, binary);
578 std::ifstream file(filename, binary ? std::ios_base::binary : std::ios_base::in);
579 if(!file)
580 throw std::runtime_error("Can't open file to read input from");
581 lua_inputmovie* m = lua::_class<lua_inputmovie>::create(L, f->get_frame());
582 portctrl::frame_vector& v = *m->get_frame_vector();
583 portctrl::frame_vector::notify_freeze freeze(v);
584 if(binary) {
585 uint64_t stride = v.get_stride();
586 uint64_t pageframes = v.get_frames_per_page();
587 uint64_t vsize = 0;
588 size_t pagenum = 0;
589 size_t pagesize = stride * pageframes;
590 while(file) {
591 v.resize(vsize + pageframes);
592 unsigned char* contents = v.get_page_buffer(pagenum++);
593 file.read(reinterpret_cast<char*>(contents), pagesize);
594 vsize += (file.gcount() / stride);
596 v.resize(vsize);
597 } else {
598 std::string line;
599 portctrl::frame tmpl = v.blank_frame(false);
600 while(file) {
601 std::getline(file, line);
602 istrip_CR(line);
603 if(line.length() == 0)
604 continue;
605 tmpl.deserialize(line.c_str());
606 v.append(tmpl);
609 return 1;
612 int current_branch(lua::state& L, lua::parameters& P)
614 L.pushlstring(CORE().mlogic->get_mfile().current_branch());
615 return 1;
618 int get_branches(lua::state& L, lua::parameters& P)
620 auto& core = CORE();
621 for(auto& i : core.mlogic->get_mfile().branches)
622 L.pushlstring(i.first);
623 return core.mlogic->get_mfile().branches.size();
626 portctrl::frame_vector& framevector(lua::state& L, lua::parameters& P)
628 auto& core = CORE();
629 if(P.is_nil()) {
630 P.skip();
631 return *core.mlogic->get_mfile().input;
632 } else if(P.is_string()) {
633 std::string x;
634 P(x);
635 if(!core.mlogic->get_mfile().branches.count(x))
636 throw std::runtime_error("No such branch");
637 return core.mlogic->get_mfile().branches[x];
638 } else if(P.is<lua_inputmovie>())
639 return *(P.arg<lua_inputmovie*>()->get_frame_vector());
640 else
641 return *core.mlogic->get_mfile().input;
644 lua::_class<lua_inputmovie> LUA_class_inputmovie(lua_class_movie, "INPUTMOVIE", {}, {
645 {"copy_movie", &lua_inputmovie::copy_movie},
646 {"get_frame", &lua_inputmovie::get_frame},
647 {"set_frame", &lua_inputmovie::set_frame},
648 {"get_size", &lua_inputmovie::get_size},
649 {"count_frames", &lua_inputmovie::count_frames},
650 {"find_frame", &lua_inputmovie::find_frame},
651 {"subframe_to_frame", &lua_inputmovie::subframe_to_frame},
652 {"blank_frame", &lua_inputmovie::blank_frame},
653 {"append_frames", &lua_inputmovie::append_frames},
654 {"append_frame", &lua_inputmovie::append_frame},
655 {"truncate", &lua_inputmovie::truncate},
656 {"edit", &lua_inputmovie::edit},
657 {"debugdump", &lua_inputmovie::debugdump},
658 {"copy_frames", &lua_inputmovie::copy_frames},
659 {"serialize", &lua_inputmovie::serialize},
660 }, &lua_inputmovie::print);
662 lua::_class<lua_inputframe> LUA_class_inputframe(lua_class_movie, "INPUTFRAME", {}, {
663 {"get_button", &lua_inputframe::get_button},
664 {"get_axis", &lua_inputframe::get_axis},
665 {"set_axis", &lua_inputframe::set_axis},
666 {"set_button", &lua_inputframe::set_axis},
667 {"serialize", &lua_inputframe::serialize},
668 {"unserialize", &lua_inputframe::unserialize},
669 {"get_stride", &lua_inputframe::get_stride},
670 }, &lua_inputframe::print);
672 lua::functions LUA_inputmovie_fns(lua_func_misc, "movie", {
673 {"current_first_subframe", current_first_subframe},
674 {"pollcounter", pollcounter},
675 {"copy_movie", copy_movie},
676 {"get_frame", get_frame},
677 {"set_frame", set_frame},
678 {"get_size", get_size},
679 {"count_frames", count_frames},
680 {"find_frame", find_frame},
681 {"subframe_to_frame", subframe_to_frame},
682 {"blank_frame", blank_frame},
683 {"append_frames", append_frames},
684 {"append_frame", append_frame},
685 {"truncate", truncate},
686 {"edit", edit},
687 {"copy_frames2", copy_frames2},
688 {"copy_frames", copy_frames},
689 {"serialize", serialize},
690 {"unserialize", unserialize},
691 {"current_branch", current_branch},
692 {"get_branches", get_branches},
695 lua_inputframe::lua_inputframe(lua::state& L, portctrl::frame _f)
697 f = _f;
700 void lua_inputmovie::common_init(lua::state& L)
704 lua_inputmovie::lua_inputmovie(lua::state& L, const portctrl::frame_vector& _v)
706 v = _v;
707 common_init(L);
710 lua_inputmovie::lua_inputmovie(lua::state& L, portctrl::frame& f)
712 v.clear(f.porttypes());
713 common_init(L);