Add base support for Lua object overcommit
[lsnes.git] / src / lua / inputmovie.cpp
blobbedf52b62629e588be96b35c97adae125ebe7aa8
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 static size_t overcommit(controller_frame _f) { return 0; }
21 int get_button(lua::state& L, lua::parameters& P)
23 unsigned port, controller, button;
25 P(P.skipped(), port, controller, button);
27 short value = getbutton(port, controller, button);
28 L.pushboolean(value ? 1 : 0);
29 return 1;
31 int get_axis(lua::state& L, lua::parameters& P)
33 unsigned port, controller, button;
35 P(P.skipped(), port, controller, button);
37 short value = getbutton(port, controller, button);
38 L.pushnumber(value);
39 return 1;
41 int set_axis(lua::state& L, lua::parameters& P)
43 unsigned port, controller, button;
44 short value;
46 P(P.skipped(), port, controller, button);
47 if(P.is_boolean()) value = P.arg<bool>() ? 1 : 0;
48 else if(P.is_number()) value = P.arg<short>();
49 else
50 P.expected("number or boolean");
52 setbutton(port, controller, button, value);
53 return 0;
55 int serialize(lua::state& L, lua::parameters& P)
57 char buf[MAX_SERIALIZED_SIZE];
58 f.serialize(buf);
59 L.pushstring(buf);
60 return 1;
62 int unserialize(lua::state& L, lua::parameters& P)
64 std::string buf;
66 P(P.skipped(), buf);
68 f.deserialize(buf.c_str());
69 return 0;
71 int get_stride(lua::state& L, lua::parameters& P)
73 L.pushnumber(f.size());
74 return 1;
76 controller_frame& get_frame()
78 return f;
80 std::string print()
82 char buf[MAX_SERIALIZED_SIZE];
83 f.serialize(buf);
84 return buf;
86 private:
87 short getbutton(unsigned port, unsigned controller, unsigned index)
89 return f.axis3(port, controller, index);
91 void setbutton(unsigned port, unsigned controller, unsigned index, short value)
93 return f.axis3(port, controller, index, value);
95 controller_frame f;
98 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0 = false);
99 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0)
101 movie& m = movb.get_movie();
102 if(port == 0 && controller == 0 && button == 0)
103 return m.get_pollcounters().max_polls() + (extra0 ? 1 : 0);
104 if(port == 0 && controller == 0 && m.get_pollcounters().get_framepflag())
105 return max(m.get_pollcounters().get_polls(port, controller, button), (uint32_t)1);
106 return m.get_pollcounters().get_polls(port, controller, button);
109 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
110 bool allow_past_end = false);
112 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
113 bool allow_past_end)
115 movie& m = movb.get_movie();
116 if(!m.readonly_mode())
117 throw std::runtime_error("Not in read-only mode");
118 if(!allow_past_end && frame >= movb.get_mfile().input->size())
119 throw std::runtime_error("Index out of movie");
120 int32_t pc = get_pc_for(port, controller, button, true);
121 if(pc < 0)
122 throw std::runtime_error("Invalid control to edit");
123 uint64_t firstframe = m.get_current_frame_first_subframe();
124 uint64_t minframe = firstframe + pc;
125 uint64_t msize = movb.get_mfile().input->size();
126 if(minframe > msize || firstframe >= msize)
127 throw std::runtime_error("Can not edit finished movie");
128 if(frame < minframe)
129 throw std::runtime_error("Can not edit past");
132 int current_first_subframe(lua::state& L, lua::parameters& P)
134 movie& m = movb.get_movie();
135 L.pushnumber(m.get_current_frame_first_subframe());
136 return 1;
139 int pollcounter(lua::state& L, lua::parameters& P)
141 unsigned port, controller, button;
143 P(port, controller, button);
145 uint32_t ret = 0;
146 ret = get_pc_for(port, controller, button);
147 L.pushnumber(ret);
148 return 1;
151 controller_frame_vector& framevector(lua::state& L, lua::parameters& P);
153 int _copy_movie(lua::state& L, lua::parameters& P)
155 controller_frame_vector& v = framevector(L, P);
157 lua::_class<lua_inputmovie>::create(L, v);
158 return 1;
161 int _get_frame(lua::state& L, lua::parameters& P)
163 uint64_t n;
164 controller_frame_vector& v = framevector(L, P);
166 P(n);
168 if(n >= v.size())
169 throw std::runtime_error("Requested frame outside movie");
170 controller_frame _f = v[n];
171 lua::_class<lua_inputframe>::create(L, _f);
172 return 1;
175 int _set_frame(lua::state& L, lua::parameters& P)
177 uint64_t n;
178 lua_inputframe* f;
179 controller_frame_vector& v = framevector(L, P);
181 P(n, f);
183 if(n >= v.size())
184 throw std::runtime_error("Requested frame outside movie");
185 //Checks if requested frame is from movie.
186 if(&v == movb.get_mfile().input)
187 check_can_edit(0, 0, 0, n);
189 v[n] = f->get_frame();
191 if(&v == movb.get_mfile().input) {
192 //This can't add frames, so no need to adjust the movie.
193 update_movie_state();
194 platform::notify_status();
196 return 0;
199 int _get_size(lua::state& L, lua::parameters& P)
201 controller_frame_vector& v = framevector(L, P);
203 L.pushnumber(v.size());
204 return 1;
207 int _count_frames(lua::state& L, lua::parameters& P)
209 controller_frame_vector& v = framevector(L, P);
211 L.pushnumber(v.count_frames());
212 return 1;
215 int _find_frame(lua::state& L, lua::parameters& P)
217 uint64_t n;
218 controller_frame_vector& v = framevector(L, P);
220 P(n);
222 L.pushnumber(v.find_frame(n));
223 return 1;
226 int _blank_frame(lua::state& L, lua::parameters& P)
228 controller_frame_vector& v = framevector(L, P);
230 controller_frame _f = v.blank_frame(true);
231 lua::_class<lua_inputframe>::create(L, _f);
232 return 1;
235 int _append_frames(lua::state& L, lua::parameters& P)
237 uint64_t count;
238 controller_frame_vector& v = framevector(L, P);
240 P(count);
243 controller_frame_vector::notify_freeze freeze(v);
244 for(uint64_t i = 0; i < count; i++)
245 v.append(v.blank_frame(true));
247 if(&v == movb.get_mfile().input) {
248 update_movie_state();
249 platform::notify_status();
251 return 0;
254 int _append_frame(lua::state& L, lua::parameters& P)
256 lua_inputframe* f;
257 controller_frame_vector& v = framevector(L, P);
259 P(f);
261 v.append(v.blank_frame(true));
262 if(&v == movb.get_mfile().input) {
263 update_movie_state();
264 platform::notify_status();
265 check_can_edit(0, 0, 0, v.size() - 1);
267 v[v.size() - 1] = f->get_frame();
268 if(&v == movb.get_mfile().input) {
269 if(!v[v.size() - 1].sync()) {
270 update_movie_state();
272 platform::notify_status();
274 return 0;
277 int _truncate(lua::state& L, lua::parameters& P)
279 uint64_t n;
280 controller_frame_vector& v = framevector(L, P);
282 P(n);
284 if(n > v.size())
285 throw std::runtime_error("Requested truncate length longer than existing");
286 if(&v == movb.get_mfile().input)
287 check_can_edit(0, 0, 0, n);
288 v.resize(n);
289 if(&v == movb.get_mfile().input) {
290 update_movie_state();
291 platform::notify_status();
293 return 0;
296 int _edit(lua::state& L, lua::parameters& P)
298 uint64_t frame;
299 unsigned port, controller, button;
300 short value;
301 controller_frame_vector& v = framevector(L, P);
303 P(frame, port, controller, button);
304 if(P.is_boolean()) value = P.arg<bool>() ? 1 : 0;
305 else if(P.is_number()) P(value);
306 else
307 P.expected("number or boolean");
309 if(&v == movb.get_mfile().input)
310 check_can_edit(port, controller, button, frame);
311 v[frame].axis3(port, controller, button, value);
313 if(&v == movb.get_mfile().input) {
314 update_movie_state();
315 platform::notify_status();
317 return 0;
320 template<bool same>
321 int _copy_frames(lua::state& L, lua::parameters& P)
323 uint64_t dst, src, count;
324 bool backwards = same;
326 controller_frame_vector& dstv = framevector(L, P);
327 P(dst);
328 controller_frame_vector& srcv = same ? dstv : framevector(L, P);
329 P(src, count);
330 if(same) P(backwards);
332 if(src >= srcv.size() || src + count < src)
333 throw std::runtime_error("Source index out of movie");
334 if(dst > dstv.size() || dst + count < dst)
335 throw std::runtime_error("Destination index out of movie");
337 if(&dstv == movb.get_mfile().input)
338 check_can_edit(0, 0, 0, dst, true);
341 controller_frame_vector::notify_freeze freeze(dstv);
342 //Add enough blank frames to make the copy.
343 while(dst + count > dstv.size())
344 dstv.append(dstv.blank_frame(false));
346 for(uint64_t i = backwards ? (count - 1) : 0; i < count; i = backwards ? (i - 1) : (i + 1))
347 dstv[dst + i] = srcv[src + i];
349 if(&dstv == movb.get_mfile().input) {
350 update_movie_state();
351 platform::notify_status();
353 return 0;
356 int _serialize(lua::state& L, lua::parameters& P)
358 std::string filename;
359 bool binary;
361 controller_frame_vector& v = framevector(L, P);
363 P(filename, binary);
365 std::ofstream file(filename, binary ? std::ios_base::binary : std::ios_base::out);
366 if(!file)
367 throw std::runtime_error("Can't open file to write output to");
368 if(binary) {
369 uint64_t stride = v.get_stride();
370 uint64_t pageframes = v.get_frames_per_page();
371 uint64_t vsize = v.size();
372 size_t pagenum = 0;
373 while(vsize > 0) {
374 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
375 size_t bytes = count * stride;
376 unsigned char* content = v.get_page_buffer(pagenum++);
377 file.write(reinterpret_cast<char*>(content), bytes);
378 vsize -= count;
380 } else {
381 char buf[MAX_SERIALIZED_SIZE];
382 for(uint64_t i = 0; i < v.size(); i++) {
383 v[i].serialize(buf);
384 file << buf << std::endl;
387 return 0;
390 class lua_inputmovie
392 public:
393 lua_inputmovie(lua::state& L, const controller_frame_vector& _v);
394 lua_inputmovie(lua::state& L, controller_frame& _f);
395 static size_t overcommit(const controller_frame_vector& _v) { return 0; }
396 static size_t overcommit(controller_frame& _f) { return 0; }
397 int copy_movie(lua::state& L, lua::parameters& P)
399 return _copy_movie(L, P);
401 int get_frame(lua::state& L, lua::parameters& P)
403 return _get_frame(L, P);
405 int set_frame(lua::state& L, lua::parameters& P)
407 return _set_frame(L, P);
409 int get_size(lua::state& L, lua::parameters& P)
411 return _get_size(L, P);
413 int count_frames(lua::state& L, lua::parameters& P)
415 return _count_frames(L, P);
417 int find_frame(lua::state& L, lua::parameters& P)
419 return _find_frame(L, P);
421 int blank_frame(lua::state& L, lua::parameters& P)
423 return _blank_frame(L, P);
425 int append_frames(lua::state& L, lua::parameters& P)
427 return _append_frames(L, P);
429 int append_frame(lua::state& L, lua::parameters& P)
431 return _append_frame(L, P);
433 int truncate(lua::state& L, lua::parameters& P)
435 return _truncate(L, P);
437 int edit(lua::state& L, lua::parameters& P)
439 return _edit(L, P);
441 int copy_frames(lua::state& L, lua::parameters& P)
443 return _copy_frames<true>(L, P);
445 int serialize(lua::state& L, lua::parameters& P)
447 return _serialize(L, P);
449 int debugdump(lua::state& L, lua::parameters& P)
451 char buf[MAX_SERIALIZED_SIZE];
452 for(uint64_t i = 0; i < v.size(); i++) {
453 v[i].serialize(buf);
454 messages << buf << std::endl;
456 return 0;
458 controller_frame_vector* get_frame_vector()
460 return &v;
462 std::string print()
464 size_t s = v.size();
465 return (stringfmt() << s << " " << ((s != 1) ? "frames" : "frame")).str();
467 private:
468 void common_init(lua::state& L);
469 controller_frame_vector v;
472 int copy_movie(lua::state& L, lua::parameters& P)
474 return _copy_movie(L, P);
477 int get_frame(lua::state& L, lua::parameters& P)
479 return _get_frame(L, P);
482 int set_frame(lua::state& L, lua::parameters& P)
484 return _set_frame(L, P);
487 int get_size(lua::state& L, lua::parameters& P)
489 return _get_size(L, P);
492 int count_frames(lua::state& L, lua::parameters& P)
494 return _count_frames(L, P);
497 int find_frame(lua::state& L, lua::parameters& P)
499 return _find_frame(L, P);
502 int blank_frame(lua::state& L, lua::parameters& P)
504 return _blank_frame(L, P);
507 int append_frames(lua::state& L, lua::parameters& P)
509 return _append_frames(L, P);
512 int append_frame(lua::state& L, lua::parameters& P)
514 return _append_frame(L, P);
517 int truncate(lua::state& L, lua::parameters& P)
519 return _truncate(L, P);
522 int edit(lua::state& L, lua::parameters& P)
524 return _edit(L, P);
527 int copy_frames2(lua::state& L, lua::parameters& P)
529 return _copy_frames<false>(L, P);
532 int copy_frames(lua::state& L, lua::parameters& P)
534 return _copy_frames<true>(L, P);
537 int serialize(lua::state& L, lua::parameters& P)
539 return _serialize(L, P);
542 int unserialize(lua::state& L, lua::parameters& P)
544 lua_inputframe* f;
545 std::string filename;
546 bool binary;
548 P(f, filename, binary);
550 std::ifstream file(filename, binary ? std::ios_base::binary : std::ios_base::in);
551 if(!file)
552 throw std::runtime_error("Can't open file to read input from");
553 lua_inputmovie* m = lua::_class<lua_inputmovie>::create(L, f->get_frame());
554 controller_frame_vector& v = *m->get_frame_vector();
555 controller_frame_vector::notify_freeze freeze(v);
556 if(binary) {
557 uint64_t stride = v.get_stride();
558 uint64_t pageframes = v.get_frames_per_page();
559 uint64_t vsize = 0;
560 size_t pagenum = 0;
561 size_t pagesize = stride * pageframes;
562 while(file) {
563 v.resize(vsize + pageframes);
564 unsigned char* contents = v.get_page_buffer(pagenum++);
565 file.read(reinterpret_cast<char*>(contents), pagesize);
566 vsize += (file.gcount() / stride);
568 v.resize(vsize);
569 } else {
570 std::string line;
571 controller_frame tmpl = v.blank_frame(false);
572 while(file) {
573 std::getline(file, line);
574 istrip_CR(line);
575 if(line.length() == 0)
576 continue;
577 tmpl.deserialize(line.c_str());
578 v.append(tmpl);
581 return 1;
584 int current_branch(lua::state& L, lua::parameters& P)
586 L.pushlstring(movb.get_mfile().current_branch());
587 return 1;
590 int get_branches(lua::state& L, lua::parameters& P)
592 for(auto& i : movb.get_mfile().branches)
593 L.pushlstring(i.first);
594 return movb.get_mfile().branches.size();
597 controller_frame_vector& framevector(lua::state& L, lua::parameters& P)
599 if(P.is_nil()) {
600 P.skip();
601 return *movb.get_mfile().input;
602 } else if(P.is_string()) {
603 std::string x;
604 P(x);
605 if(!movb.get_mfile().branches.count(x))
606 throw std::runtime_error("No such branch");
607 return movb.get_mfile().branches[x];
608 } else if(P.is<lua_inputmovie>())
609 return *(P.arg<lua_inputmovie*>()->get_frame_vector());
610 else
611 return *movb.get_mfile().input;
614 lua::_class<lua_inputmovie> class_inputmovie(lua_class_movie, "INPUTMOVIE", {}, {
615 {"copy_movie", &lua_inputmovie::copy_movie},
616 {"get_frame", &lua_inputmovie::get_frame},
617 {"set_frame", &lua_inputmovie::set_frame},
618 {"get_size", &lua_inputmovie::get_size},
619 {"count_frames", &lua_inputmovie::count_frames},
620 {"find_frame", &lua_inputmovie::find_frame},
621 {"blank_frame", &lua_inputmovie::blank_frame},
622 {"append_frames", &lua_inputmovie::append_frames},
623 {"append_frame", &lua_inputmovie::append_frame},
624 {"truncate", &lua_inputmovie::truncate},
625 {"edit", &lua_inputmovie::edit},
626 {"debugdump", &lua_inputmovie::debugdump},
627 {"copy_frames", &lua_inputmovie::copy_frames},
628 {"serialize", &lua_inputmovie::serialize},
629 }, &lua_inputmovie::print);
631 lua::_class<lua_inputframe> class_inputframe(lua_class_movie, "INPUTFRAME", {}, {
632 {"get_button", &lua_inputframe::get_button},
633 {"get_axis", &lua_inputframe::get_axis},
634 {"set_axis", &lua_inputframe::set_axis},
635 {"set_button", &lua_inputframe::set_axis},
636 {"serialize", &lua_inputframe::serialize},
637 {"unserialize", &lua_inputframe::unserialize},
638 {"get_stride", &lua_inputframe::get_stride},
639 }, &lua_inputframe::print);
641 lua::functions inputmovie_fns(lua_func_misc, "movie", {
642 {"current_first_subframe", current_first_subframe},
643 {"pollcounter", pollcounter},
644 {"copy_movie", copy_movie},
645 {"get_frame", get_frame},
646 {"set_frame", set_frame},
647 {"get_size", get_size},
648 {"count_frames", count_frames},
649 {"find_frame", find_frame},
650 {"blank_frame", blank_frame},
651 {"append_frames", append_frames},
652 {"append_frame", append_frame},
653 {"truncate", truncate},
654 {"edit", edit},
655 {"copy_frames2", copy_frames2},
656 {"copy_frames", copy_frames},
657 {"serialize", serialize},
658 {"unserialize", unserialize},
659 {"current_branch", current_branch},
660 {"get_branches", get_branches},
663 lua_inputframe::lua_inputframe(lua::state& L, controller_frame _f)
665 f = _f;
668 void lua_inputmovie::common_init(lua::state& L)
672 lua_inputmovie::lua_inputmovie(lua::state& L, const controller_frame_vector& _v)
674 v = _v;
675 common_init(L);
678 lua_inputmovie::lua_inputmovie(lua::state& L, controller_frame& f)
680 v.clear(f.porttypes());
681 common_init(L);