Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / lua / inputmovie.cpp
blob084b24aa58028af72e3262240a63102b00d94ed8
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 _blank_frame(lua::state& L, lua::parameters& P)
231 portctrl::frame_vector& v = framevector(L, P);
233 portctrl::frame _f = v.blank_frame(true);
234 lua::_class<lua_inputframe>::create(L, _f);
235 return 1;
238 int _append_frames(lua::state& L, lua::parameters& P)
240 auto& core = CORE();
241 uint64_t count;
242 portctrl::frame_vector& v = framevector(L, P);
244 P(count);
247 portctrl::frame_vector::notify_freeze freeze(v);
248 for(uint64_t i = 0; i < count; i++)
249 v.append(v.blank_frame(true));
251 if(&v == core.mlogic->get_mfile().input) {
252 core.supdater->update();
253 core.dispatch->status_update();
255 return 0;
258 int _append_frame(lua::state& L, lua::parameters& P)
260 auto& core = CORE();
261 lua_inputframe* f;
262 portctrl::frame_vector& v = framevector(L, P);
264 P(f);
266 v.append(v.blank_frame(true));
267 if(&v == core.mlogic->get_mfile().input) {
268 core.supdater->update();
269 core.dispatch->status_update();
270 check_can_edit(0, 0, 0, v.size() - 1);
272 v[v.size() - 1] = f->get_frame();
273 if(&v == core.mlogic->get_mfile().input) {
274 if(!v[v.size() - 1].sync()) {
275 core.supdater->update();
277 core.dispatch->status_update();
279 return 0;
282 int _truncate(lua::state& L, lua::parameters& P)
284 auto& core = CORE();
285 uint64_t n;
286 portctrl::frame_vector& v = framevector(L, P);
288 P(n);
290 if(n > v.size())
291 throw std::runtime_error("Requested truncate length longer than existing");
292 if(&v == core.mlogic->get_mfile().input)
293 check_can_edit(0, 0, 0, n);
294 v.resize(n);
295 if(&v == core.mlogic->get_mfile().input) {
296 core.supdater->update();
297 core.dispatch->status_update();
299 return 0;
302 int _edit(lua::state& L, lua::parameters& P)
304 auto& core = CORE();
305 uint64_t frame;
306 unsigned port, controller, button;
307 short value;
308 portctrl::frame_vector& v = framevector(L, P);
310 P(frame, port, controller, button);
311 if(P.is_boolean()) value = P.arg<bool>() ? 1 : 0;
312 else if(P.is_number()) P(value);
313 else
314 P.expected("number or boolean");
316 if(&v == core.mlogic->get_mfile().input)
317 check_can_edit(port, controller, button, frame);
318 v[frame].axis3(port, controller, button, value);
320 if(&v == core.mlogic->get_mfile().input) {
321 core.supdater->update();
322 core.dispatch->status_update();
324 return 0;
327 template<bool same>
328 int _copy_frames(lua::state& L, lua::parameters& P)
330 auto& core = CORE();
331 uint64_t dst, src, count;
332 bool backwards = same;
334 portctrl::frame_vector& dstv = framevector(L, P);
335 P(dst);
336 portctrl::frame_vector& srcv = same ? dstv : framevector(L, P);
337 P(src, count);
338 if(same) P(backwards);
340 if(src >= srcv.size() || src + count < src)
341 throw std::runtime_error("Source index out of movie");
342 if(dst > dstv.size() || dst + count < dst)
343 throw std::runtime_error("Destination index out of movie");
345 if(&dstv == core.mlogic->get_mfile().input)
346 check_can_edit(0, 0, 0, dst, true);
349 portctrl::frame_vector::notify_freeze freeze(dstv);
350 //Add enough blank frames to make the copy.
351 while(dst + count > dstv.size())
352 dstv.append(dstv.blank_frame(false));
354 for(uint64_t i = backwards ? (count - 1) : 0; i < count; i = backwards ? (i - 1) : (i + 1))
355 dstv[dst + i] = srcv[src + i];
357 if(&dstv == core.mlogic->get_mfile().input) {
358 core.supdater->update();
359 core.dispatch->status_update();
361 return 0;
364 int _serialize(lua::state& L, lua::parameters& P)
366 std::string filename;
367 bool binary;
369 portctrl::frame_vector& v = framevector(L, P);
371 P(filename, binary);
373 std::ofstream file(filename, binary ? std::ios_base::binary : std::ios_base::out);
374 if(!file)
375 throw std::runtime_error("Can't open file to write output to");
376 if(binary) {
377 uint64_t stride = v.get_stride();
378 uint64_t pageframes = v.get_frames_per_page();
379 uint64_t vsize = v.size();
380 size_t pagenum = 0;
381 while(vsize > 0) {
382 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
383 size_t bytes = count * stride;
384 unsigned char* content = v.get_page_buffer(pagenum++);
385 file.write(reinterpret_cast<char*>(content), bytes);
386 vsize -= count;
388 } else {
389 char buf[MAX_SERIALIZED_SIZE];
390 for(uint64_t i = 0; i < v.size(); i++) {
391 v[i].serialize(buf);
392 file << buf << std::endl;
395 return 0;
398 class lua_inputmovie
400 public:
401 lua_inputmovie(lua::state& L, const portctrl::frame_vector& _v);
402 lua_inputmovie(lua::state& L, portctrl::frame& _f);
403 static size_t overcommit(const portctrl::frame_vector& _v) { return 0; }
404 static size_t overcommit(portctrl::frame& _f) { return 0; }
405 int copy_movie(lua::state& L, lua::parameters& P)
407 return _copy_movie(L, P);
409 int get_frame(lua::state& L, lua::parameters& P)
411 return _get_frame(L, P);
413 int set_frame(lua::state& L, lua::parameters& P)
415 return _set_frame(L, P);
417 int get_size(lua::state& L, lua::parameters& P)
419 return _get_size(L, P);
421 int count_frames(lua::state& L, lua::parameters& P)
423 return _count_frames(L, P);
425 int find_frame(lua::state& L, lua::parameters& P)
427 return _find_frame(L, P);
429 int blank_frame(lua::state& L, lua::parameters& P)
431 return _blank_frame(L, P);
433 int append_frames(lua::state& L, lua::parameters& P)
435 return _append_frames(L, P);
437 int append_frame(lua::state& L, lua::parameters& P)
439 return _append_frame(L, P);
441 int truncate(lua::state& L, lua::parameters& P)
443 return _truncate(L, P);
445 int edit(lua::state& L, lua::parameters& P)
447 return _edit(L, P);
449 int copy_frames(lua::state& L, lua::parameters& P)
451 return _copy_frames<true>(L, P);
453 int serialize(lua::state& L, lua::parameters& P)
455 return _serialize(L, P);
457 int debugdump(lua::state& L, lua::parameters& P)
459 char buf[MAX_SERIALIZED_SIZE];
460 for(uint64_t i = 0; i < v.size(); i++) {
461 v[i].serialize(buf);
462 messages << buf << std::endl;
464 return 0;
466 portctrl::frame_vector* get_frame_vector()
468 return &v;
470 std::string print()
472 size_t s = v.size();
473 return (stringfmt() << s << " " << ((s != 1) ? "frames" : "frame")).str();
475 private:
476 void common_init(lua::state& L);
477 portctrl::frame_vector v;
480 int copy_movie(lua::state& L, lua::parameters& P)
482 return _copy_movie(L, P);
485 int get_frame(lua::state& L, lua::parameters& P)
487 return _get_frame(L, P);
490 int set_frame(lua::state& L, lua::parameters& P)
492 return _set_frame(L, P);
495 int get_size(lua::state& L, lua::parameters& P)
497 return _get_size(L, P);
500 int count_frames(lua::state& L, lua::parameters& P)
502 return _count_frames(L, P);
505 int find_frame(lua::state& L, lua::parameters& P)
507 return _find_frame(L, P);
510 int blank_frame(lua::state& L, lua::parameters& P)
512 return _blank_frame(L, P);
515 int append_frames(lua::state& L, lua::parameters& P)
517 return _append_frames(L, P);
520 int append_frame(lua::state& L, lua::parameters& P)
522 return _append_frame(L, P);
525 int truncate(lua::state& L, lua::parameters& P)
527 return _truncate(L, P);
530 int edit(lua::state& L, lua::parameters& P)
532 return _edit(L, P);
535 int copy_frames2(lua::state& L, lua::parameters& P)
537 return _copy_frames<false>(L, P);
540 int copy_frames(lua::state& L, lua::parameters& P)
542 return _copy_frames<true>(L, P);
545 int serialize(lua::state& L, lua::parameters& P)
547 return _serialize(L, P);
550 int unserialize(lua::state& L, lua::parameters& P)
552 lua_inputframe* f;
553 std::string filename;
554 bool binary;
556 P(f, filename, binary);
558 std::ifstream file(filename, binary ? std::ios_base::binary : std::ios_base::in);
559 if(!file)
560 throw std::runtime_error("Can't open file to read input from");
561 lua_inputmovie* m = lua::_class<lua_inputmovie>::create(L, f->get_frame());
562 portctrl::frame_vector& v = *m->get_frame_vector();
563 portctrl::frame_vector::notify_freeze freeze(v);
564 if(binary) {
565 uint64_t stride = v.get_stride();
566 uint64_t pageframes = v.get_frames_per_page();
567 uint64_t vsize = 0;
568 size_t pagenum = 0;
569 size_t pagesize = stride * pageframes;
570 while(file) {
571 v.resize(vsize + pageframes);
572 unsigned char* contents = v.get_page_buffer(pagenum++);
573 file.read(reinterpret_cast<char*>(contents), pagesize);
574 vsize += (file.gcount() / stride);
576 v.resize(vsize);
577 } else {
578 std::string line;
579 portctrl::frame tmpl = v.blank_frame(false);
580 while(file) {
581 std::getline(file, line);
582 istrip_CR(line);
583 if(line.length() == 0)
584 continue;
585 tmpl.deserialize(line.c_str());
586 v.append(tmpl);
589 return 1;
592 int current_branch(lua::state& L, lua::parameters& P)
594 L.pushlstring(CORE().mlogic->get_mfile().current_branch());
595 return 1;
598 int get_branches(lua::state& L, lua::parameters& P)
600 auto& core = CORE();
601 for(auto& i : core.mlogic->get_mfile().branches)
602 L.pushlstring(i.first);
603 return core.mlogic->get_mfile().branches.size();
606 portctrl::frame_vector& framevector(lua::state& L, lua::parameters& P)
608 auto& core = CORE();
609 if(P.is_nil()) {
610 P.skip();
611 return *core.mlogic->get_mfile().input;
612 } else if(P.is_string()) {
613 std::string x;
614 P(x);
615 if(!core.mlogic->get_mfile().branches.count(x))
616 throw std::runtime_error("No such branch");
617 return core.mlogic->get_mfile().branches[x];
618 } else if(P.is<lua_inputmovie>())
619 return *(P.arg<lua_inputmovie*>()->get_frame_vector());
620 else
621 return *core.mlogic->get_mfile().input;
624 lua::_class<lua_inputmovie> LUA_class_inputmovie(lua_class_movie, "INPUTMOVIE", {}, {
625 {"copy_movie", &lua_inputmovie::copy_movie},
626 {"get_frame", &lua_inputmovie::get_frame},
627 {"set_frame", &lua_inputmovie::set_frame},
628 {"get_size", &lua_inputmovie::get_size},
629 {"count_frames", &lua_inputmovie::count_frames},
630 {"find_frame", &lua_inputmovie::find_frame},
631 {"blank_frame", &lua_inputmovie::blank_frame},
632 {"append_frames", &lua_inputmovie::append_frames},
633 {"append_frame", &lua_inputmovie::append_frame},
634 {"truncate", &lua_inputmovie::truncate},
635 {"edit", &lua_inputmovie::edit},
636 {"debugdump", &lua_inputmovie::debugdump},
637 {"copy_frames", &lua_inputmovie::copy_frames},
638 {"serialize", &lua_inputmovie::serialize},
639 }, &lua_inputmovie::print);
641 lua::_class<lua_inputframe> LUA_class_inputframe(lua_class_movie, "INPUTFRAME", {}, {
642 {"get_button", &lua_inputframe::get_button},
643 {"get_axis", &lua_inputframe::get_axis},
644 {"set_axis", &lua_inputframe::set_axis},
645 {"set_button", &lua_inputframe::set_axis},
646 {"serialize", &lua_inputframe::serialize},
647 {"unserialize", &lua_inputframe::unserialize},
648 {"get_stride", &lua_inputframe::get_stride},
649 }, &lua_inputframe::print);
651 lua::functions LUA_inputmovie_fns(lua_func_misc, "movie", {
652 {"current_first_subframe", current_first_subframe},
653 {"pollcounter", pollcounter},
654 {"copy_movie", copy_movie},
655 {"get_frame", get_frame},
656 {"set_frame", set_frame},
657 {"get_size", get_size},
658 {"count_frames", count_frames},
659 {"find_frame", find_frame},
660 {"blank_frame", blank_frame},
661 {"append_frames", append_frames},
662 {"append_frame", append_frame},
663 {"truncate", truncate},
664 {"edit", edit},
665 {"copy_frames2", copy_frames2},
666 {"copy_frames", copy_frames},
667 {"serialize", serialize},
668 {"unserialize", unserialize},
669 {"current_branch", current_branch},
670 {"get_branches", get_branches},
673 lua_inputframe::lua_inputframe(lua::state& L, portctrl::frame _f)
675 f = _f;
678 void lua_inputmovie::common_init(lua::state& L)
682 lua_inputmovie::lua_inputmovie(lua::state& L, const portctrl::frame_vector& _v)
684 v = _v;
685 common_init(L);
688 lua_inputmovie::lua_inputmovie(lua::state& L, portctrl::frame& f)
690 v.clear(f.porttypes());
691 common_init(L);