Refactor some generic reading/writing routines out of moviefile.cpp
[lsnes.git] / src / lua / inputmovie.cpp
blobce70ff829e7ab33ea5ec5d75a8f6df5c1d07a240
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, lua::parameters& P)
22 unsigned port, controller, button;
24 P(P.skipped(), port, controller, button);
26 short value = getbutton(port, controller, button);
27 L.pushboolean(value ? 1 : 0);
28 return 1;
30 int get_axis(lua::state& L, lua::parameters& P)
32 unsigned port, controller, button;
34 P(P.skipped(), port, controller, button);
36 short value = getbutton(port, controller, button);
37 L.pushnumber(value);
38 return 1;
40 int set_axis(lua::state& L, lua::parameters& P)
42 unsigned port, controller, button;
43 short value;
45 P(P.skipped(), port, controller, button);
46 if(P.is_boolean()) value = P.arg<bool>() ? 1 : 0;
47 else if(P.is_number()) value = P.arg<short>();
48 else
49 P.expected("number or boolean");
51 setbutton(port, controller, button, value);
52 return 0;
54 int serialize(lua::state& L, lua::parameters& P)
56 char buf[MAX_SERIALIZED_SIZE];
57 f.serialize(buf);
58 L.pushstring(buf);
59 return 1;
61 int unserialize(lua::state& L, lua::parameters& P)
63 std::string buf;
65 P(P.skipped(), buf);
67 f.deserialize(buf.c_str());
68 return 0;
70 int get_stride(lua::state& L, lua::parameters& P)
72 L.pushnumber(f.size());
73 return 1;
75 controller_frame& get_frame()
77 return f;
79 std::string print()
81 char buf[MAX_SERIALIZED_SIZE];
82 f.serialize(buf);
83 return buf;
85 private:
86 short getbutton(unsigned port, unsigned controller, unsigned index)
88 return f.axis3(port, controller, index);
90 void setbutton(unsigned port, unsigned controller, unsigned index, short value)
92 return f.axis3(port, controller, index, value);
94 controller_frame f;
97 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0 = false);
98 int32_t get_pc_for(unsigned port, unsigned controller, unsigned button, bool extra0)
100 movie& m = movb.get_movie();
101 if(port == 0 && controller == 0 && button == 0)
102 return m.get_pollcounters().max_polls() + (extra0 ? 1 : 0);
103 if(port == 0 && controller == 0 && m.get_pollcounters().get_framepflag())
104 return max(m.get_pollcounters().get_polls(port, controller, button), (uint32_t)1);
105 return m.get_pollcounters().get_polls(port, controller, button);
108 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
109 bool allow_past_end = false);
111 void check_can_edit(unsigned port, unsigned controller, unsigned button, uint64_t frame,
112 bool allow_past_end)
114 movie& m = movb.get_movie();
115 if(!m.readonly_mode())
116 throw std::runtime_error("Not in read-only mode");
117 if(!allow_past_end && frame >= movb.get_movie().get_frame_vector().size())
118 throw std::runtime_error("Index out of movie");
119 int32_t pc = get_pc_for(port, controller, button, true);
120 if(pc < 0)
121 throw std::runtime_error("Invalid control to edit");
122 uint64_t firstframe = m.get_current_frame_first_subframe();
123 uint64_t minframe = firstframe + pc;
124 uint64_t msize = movb.get_movie().get_frame_vector().size();
125 if(minframe > msize || firstframe >= msize)
126 throw std::runtime_error("Can not edit finished movie");
127 if(frame < minframe)
128 throw std::runtime_error("Can not edit past");
131 lua::fnptr2 movie_cfs(lua_func_misc, "movie.current_first_subframe", [](lua::state& L, lua::parameters& P)
132 -> int {
133 movie& m = movb.get_movie();
134 L.pushnumber(m.get_current_frame_first_subframe());
135 return 1;
138 lua::fnptr2 movie_pc(lua_func_misc, "movie.pollcounter", [](lua::state& L, lua::parameters& P) -> int {
139 unsigned port, controller, button;
141 P(port, controller, button);
143 uint32_t ret = 0;
144 ret = get_pc_for(port, controller, button);
145 L.pushnumber(ret);
146 return 1;
149 controller_frame_vector& framevector(lua::state& L, lua::parameters& P);
151 int _copy_movie(lua::state& L, lua::parameters& P)
153 controller_frame_vector& v = framevector(L, P);
155 lua_inputmovie* m = lua::_class<lua_inputmovie>::create(L, v);
156 return 1;
159 int _get_frame(lua::state& L, lua::parameters& P)
161 uint64_t n;
162 controller_frame_vector& v = framevector(L, P);
164 P(n);
166 if(n >= v.size())
167 throw std::runtime_error("Requested frame outside movie");
168 controller_frame _f = v[n];
169 lua_inputframe* f = lua::_class<lua_inputframe>::create(L, _f);
170 return 1;
173 int _set_frame(lua::state& L, lua::parameters& P)
175 uint64_t n;
176 lua_inputframe* f;
177 controller_frame_vector& v = framevector(L, P);
179 P(n, f);
181 if(n >= v.size())
182 throw std::runtime_error("Requested frame outside movie");
183 //Checks if requested frame is from movie.
184 if(&v == &movb.get_movie().get_frame_vector())
185 check_can_edit(0, 0, 0, n);
187 v[n] = f->get_frame();
189 if(&v == &movb.get_movie().get_frame_vector()) {
190 //This can't add frames, so no need to adjust the movie.
191 update_movie_state();
192 platform::notify_status();
194 return 0;
197 int _get_size(lua::state& L, lua::parameters& P)
199 int ptr = 1;
200 controller_frame_vector& v = framevector(L, P);
202 L.pushnumber(v.size());
203 return 1;
206 int _count_frames(lua::state& L, lua::parameters& P)
208 int ptr = 1;
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 if(!n) {
223 L.pushnumber(-1);
224 return 1;
226 uint64_t c = 0;
227 uint64_t s = v.size();
228 for(uint64_t i = 0; i < s; i++)
229 if(v[i].sync() && ++c == n) {
230 L.pushnumber(i);
231 return 1;
233 L.pushnumber(-1);
234 return 1;
237 int _blank_frame(lua::state& L, lua::parameters& P)
239 int ptr = 1;
240 controller_frame_vector& v = framevector(L, P);
242 controller_frame _f = v.blank_frame(true);
243 lua_inputframe* f = lua::_class<lua_inputframe>::create(L, _f);
244 return 1;
247 int _append_frames(lua::state& L, lua::parameters& P)
249 uint64_t count;
250 controller_frame_vector& v = framevector(L, P);
252 P(count);
255 controller_frame_vector::notify_freeze freeze(v);
256 for(uint64_t i = 0; i < count; i++)
257 v.append(v.blank_frame(true));
259 if(&v == &movb.get_movie().get_frame_vector()) {
260 update_movie_state();
261 platform::notify_status();
263 return 0;
266 int _append_frame(lua::state& L, lua::parameters& P)
268 lua_inputframe* f;
269 controller_frame_vector& v = framevector(L, P);
271 P(f);
273 v.append(v.blank_frame(true));
274 if(&v == &movb.get_movie().get_frame_vector()) {
275 update_movie_state();
276 platform::notify_status();
277 check_can_edit(0, 0, 0, v.size() - 1);
279 v[v.size() - 1] = f->get_frame();
280 if(&v == &movb.get_movie().get_frame_vector()) {
281 if(!v[v.size() - 1].sync()) {
282 update_movie_state();
284 platform::notify_status();
286 return 0;
289 int _truncate(lua::state& L, lua::parameters& P)
291 uint64_t n;
292 controller_frame_vector& v = framevector(L, P);
294 P(n);
296 if(n > v.size())
297 throw std::runtime_error("Requested truncate length longer than existing");
298 if(&v == &movb.get_movie().get_frame_vector())
299 check_can_edit(0, 0, 0, n);
300 v.resize(n);
301 if(&v == &movb.get_movie().get_frame_vector()) {
302 update_movie_state();
303 platform::notify_status();
305 return 0;
308 int _edit(lua::state& L, lua::parameters& P)
310 uint64_t frame;
311 unsigned port, controller, button;
312 short value;
313 controller_frame_vector& v = framevector(L, P);
315 P(frame, port, controller, button);
316 if(P.is_boolean()) value = P.arg<bool>() ? 1 : 0;
317 else if(P.is_number()) P(value);
318 else
319 P.expected("number or boolean");
321 movie& m = movb.get_movie();
322 if(&v == &movb.get_movie().get_frame_vector())
323 check_can_edit(port, controller, button, frame);
324 v[frame].axis3(port, controller, button, value);
326 if(&v == &movb.get_movie().get_frame_vector()) {
327 update_movie_state();
328 platform::notify_status();
330 return 0;
333 template<bool same>
334 int _copy_frames(lua::state& L, lua::parameters& P)
336 uint64_t dst, src, count;
337 bool backwards = same;
339 controller_frame_vector& dstv = framevector(L, P);
340 P(dst);
341 controller_frame_vector& srcv = same ? dstv : framevector(L, P);
342 P(src, count);
343 if(same) P(backwards);
345 if(src >= srcv.size() || src + count < src)
346 throw std::runtime_error("Source index out of movie");
347 if(dst > dstv.size() || dst + count < dst)
348 throw std::runtime_error("Destination index out of movie");
350 movie& m = movb.get_movie();
351 if(&dstv == &movb.get_movie().get_frame_vector())
352 check_can_edit(0, 0, 0, dst, true);
355 controller_frame_vector::notify_freeze freeze(dstv);
356 //Add enough blank frames to make the copy.
357 while(dst + count > dstv.size())
358 dstv.append(dstv.blank_frame(false));
360 for(uint64_t i = backwards ? (count - 1) : 0; i < count; i = backwards ? (i - 1) : (i + 1))
361 dstv[dst + i] = srcv[src + i];
363 if(&dstv == &movb.get_movie().get_frame_vector()) {
364 update_movie_state();
365 platform::notify_status();
367 return 0;
370 int _serialize(lua::state& L, lua::parameters& P)
372 std::string filename;
373 bool binary;
375 controller_frame_vector& v = framevector(L, P);
377 P(filename, binary);
379 std::ofstream file(filename, binary ? std::ios_base::binary : std::ios_base::out);
380 if(!file)
381 throw std::runtime_error("Can't open file to write output to");
382 if(binary) {
383 uint64_t pages = v.get_page_count();
384 uint64_t stride = v.get_stride();
385 uint64_t pageframes = v.get_frames_per_page();
386 uint64_t vsize = v.size();
387 size_t pagenum = 0;
388 while(vsize > 0) {
389 uint64_t count = (vsize > pageframes) ? pageframes : vsize;
390 size_t bytes = count * stride;
391 unsigned char* content = v.get_page_buffer(pagenum++);
392 file.write(reinterpret_cast<char*>(content), bytes);
393 vsize -= count;
395 } else {
396 char buf[MAX_SERIALIZED_SIZE];
397 for(uint64_t i = 0; i < v.size(); i++) {
398 v[i].serialize(buf);
399 file << buf << std::endl;
402 return 0;
405 class lua_inputmovie
407 public:
408 lua_inputmovie(lua::state& L, const controller_frame_vector& _v);
409 lua_inputmovie(lua::state& L, controller_frame& _f);
410 int copy_movie(lua::state& L, lua::parameters& P)
412 return _copy_movie(L, P);
414 int get_frame(lua::state& L, lua::parameters& P)
416 return _get_frame(L, P);
418 int set_frame(lua::state& L, lua::parameters& P)
420 return _set_frame(L, P);
422 int get_size(lua::state& L, lua::parameters& P)
424 return _get_size(L, P);
426 int count_frames(lua::state& L, lua::parameters& P)
428 return _count_frames(L, P);
430 int find_frame(lua::state& L, lua::parameters& P)
432 return _find_frame(L, P);
434 int blank_frame(lua::state& L, lua::parameters& P)
436 return _blank_frame(L, P);
438 int append_frames(lua::state& L, lua::parameters& P)
440 return _append_frames(L, P);
442 int append_frame(lua::state& L, lua::parameters& P)
444 return _append_frame(L, P);
446 int truncate(lua::state& L, lua::parameters& P)
448 return _truncate(L, P);
450 int edit(lua::state& L, lua::parameters& P)
452 return _edit(L, P);
454 int copy_frames(lua::state& L, lua::parameters& P)
456 return _copy_frames<true>(L, P);
458 int serialize(lua::state& L, lua::parameters& P)
460 return _serialize(L, P);
462 int debugdump(lua::state& L, lua::parameters& P)
464 char buf[MAX_SERIALIZED_SIZE];
465 for(uint64_t i = 0; i < v.size(); i++) {
466 v[i].serialize(buf);
467 messages << buf << std::endl;
469 return 0;
471 controller_frame_vector* get_frame_vector()
473 return &v;
475 std::string print()
477 size_t s = v.size();
478 return (stringfmt() << s << " " << ((s != 1) ? "frames" : "frame")).str();
480 private:
481 void common_init(lua::state& L);
482 controller_frame_vector v;
485 lua::fnptr2 movie_getdata(lua_func_misc, "movie.copy_movie", [](lua::state& L, lua::parameters& P) -> int {
486 return _copy_movie(L, P);
489 lua::fnptr2 movie_getframe(lua_func_misc, "movie.get_frame", [](lua::state& L, lua::parameters& P) -> int {
490 return _get_frame(L, P);
493 lua::fnptr2 movie_setframe(lua_func_misc, "movie.set_frame", [](lua::state& L, lua::parameters& P) -> int {
494 return _set_frame(L, P);
497 lua::fnptr2 movie_get_size(lua_func_misc, "movie.get_size", [](lua::state& L, lua::parameters& P) -> int {
498 return _get_size(L, P);
501 lua::fnptr2 movie_count_frames(lua_func_misc, "movie.count_frames", [](lua::state& L, lua::parameters& P)
502 -> int {
503 return _count_frames(L, P);
506 lua::fnptr2 movie_find_frame(lua_func_misc, "movie.find_frame", [](lua::state& L, lua::parameters& P) -> int {
507 return _find_frame(L, P);
510 lua::fnptr2 movie_blank_frame(lua_func_misc, "movie.blank_frame", [](lua::state& L, lua::parameters& P)
511 -> int {
512 return _blank_frame(L, P);
515 lua::fnptr2 movie_append_frames(lua_func_misc, "movie.append_frames", [](lua::state& L, lua::parameters& P)
516 -> int {
517 return _append_frames(L, P);
520 lua::fnptr2 movie_append_frame(lua_func_misc, "movie.append_frame", [](lua::state& L, lua::parameters& P)
521 -> int {
522 return _append_frame(L, P);
525 lua::fnptr2 movie_truncate(lua_func_misc, "movie.truncate", [](lua::state& L, lua::parameters& P) -> int {
526 return _truncate(L, P);
529 lua::fnptr2 movie_edit(lua_func_misc, "movie.edit", [](lua::state& L, lua::parameters& P) -> int {
530 return _edit(L, P);
533 lua::fnptr2 movie_copyframe2(lua_func_misc, "movie.copy_frames2", [](lua::state& L, lua::parameters& P)
534 -> int {
535 return _copy_frames<false>(L, P);
538 lua::fnptr2 movie_copyframe(lua_func_misc, "movie.copy_frames", [](lua::state& L, lua::parameters& P) -> int {
539 return _copy_frames<true>(L, P);
542 lua::fnptr2 movie_serialize(lua_func_misc, "movie.serialize", [](lua::state& L, lua::parameters& P) -> int {
543 return _serialize(L, P);
546 lua::fnptr2 movie_unserialize(lua_func_misc, "movie.unserialize", [](lua::state& L, lua::parameters& P)
547 -> int {
548 lua_inputframe* f;
549 std::string filename;
550 bool binary;
552 P(f, filename, binary);
554 std::ifstream file(filename, binary ? std::ios_base::binary : std::ios_base::in);
555 if(!file)
556 throw std::runtime_error("Can't open file to read input from");
557 lua_inputmovie* m = lua::_class<lua_inputmovie>::create(L, f->get_frame());
558 controller_frame_vector& v = *m->get_frame_vector();
559 controller_frame_vector::notify_freeze freeze(v);
560 if(binary) {
561 uint64_t stride = v.get_stride();
562 uint64_t pageframes = v.get_frames_per_page();
563 uint64_t vsize = 0;
564 size_t pagenum = 0;
565 size_t pagesize = stride * pageframes;
566 while(file) {
567 v.resize(vsize + pageframes);
568 unsigned char* contents = v.get_page_buffer(pagenum++);
569 file.read(reinterpret_cast<char*>(contents), pagesize);
570 vsize += (file.gcount() / stride);
572 v.resize(vsize);
573 } else {
574 std::string line;
575 controller_frame tmpl = v.blank_frame(false);
576 while(file) {
577 std::getline(file, line);
578 istrip_CR(line);
579 if(line.length() == 0)
580 continue;
581 tmpl.deserialize(line.c_str());
582 v.append(tmpl);
585 return 1;
589 controller_frame_vector& framevector(lua::state& L, lua::parameters& P)
591 if(P.is_nil()) {
592 P.skip();
593 return movb.get_movie().get_frame_vector();
594 } else if(P.is<lua_inputmovie>())
595 return *(P.arg<lua_inputmovie*>()->get_frame_vector());
596 else
597 return movb.get_movie().get_frame_vector();
600 lua::_class<lua_inputmovie> class_inputmovie(lua_class_movie, "INPUTMOVIE", {}, {
601 {"copy_movie", &lua_inputmovie::copy_movie},
602 {"get_frame", &lua_inputmovie::get_frame},
603 {"set_frame", &lua_inputmovie::set_frame},
604 {"get_size", &lua_inputmovie::get_size},
605 {"count_frames", &lua_inputmovie::count_frames},
606 {"find_frame", &lua_inputmovie::find_frame},
607 {"blank_frame", &lua_inputmovie::blank_frame},
608 {"append_frames", &lua_inputmovie::append_frames},
609 {"append_frame", &lua_inputmovie::append_frame},
610 {"truncate", &lua_inputmovie::truncate},
611 {"edit", &lua_inputmovie::edit},
612 {"debugdump", &lua_inputmovie::debugdump},
613 {"copy_frames", &lua_inputmovie::copy_frames},
614 {"serialize", &lua_inputmovie::serialize},
615 }, &lua_inputmovie::print);
617 lua::_class<lua_inputframe> class_inputframe(lua_class_movie, "INPUTFRAME", {}, {
618 {"get_button", &lua_inputframe::get_button},
619 {"get_axis", &lua_inputframe::get_axis},
620 {"set_axis", &lua_inputframe::set_axis},
621 {"set_button", &lua_inputframe::set_axis},
622 {"serialize", &lua_inputframe::serialize},
623 {"unserialize", &lua_inputframe::unserialize},
624 {"get_stride", &lua_inputframe::get_stride},
625 }, &lua_inputframe::print);
627 lua_inputframe::lua_inputframe(lua::state& L, controller_frame _f)
629 f = _f;
632 void lua_inputmovie::common_init(lua::state& L)
636 lua_inputmovie::lua_inputmovie(lua::state& L, const controller_frame_vector& _v)
638 v = _v;
639 common_init(L);
642 lua_inputmovie::lua_inputmovie(lua::state& L, controller_frame& f)
644 v.clear(f.porttypes());
645 common_init(L);