diff --git a/include/core/movie.hpp b/include/core/movie.hpp index 11274668..6cdee2f9 100644 --- a/include/core/movie.hpp +++ b/include/core/movie.hpp @@ -268,6 +268,18 @@ public: * Adjust frame count. */ void adjust_frame_count(int64_t adjust) { frames_in_movie += adjust; } +/** + * Set reload mode flag (only effective in readonly mode). + */ + void set_reload_mode(bool newstate) throw() { reload_mode = newstate; } +/** + * Get reload mode flag. + */ + bool get_reload_mode() const throw() { return reload_mode; } +/** + * Read the current subframe. + */ + controller_frame current_subframe() throw(std::bad_alloc); private: //TRUE if readonly mode is active. bool readonly; @@ -296,6 +308,8 @@ private: uint32_t count_changes(uint64_t first_subframe) throw(); //Sequence number. uint64_t seqno; + //Reload mode flag. + bool reload_mode; }; /** diff --git a/src/core/mainloop.cpp b/src/core/mainloop.cpp index 0f169acd..2c64d5a6 100644 --- a/src/core/mainloop.cpp +++ b/src/core/mainloop.cpp @@ -173,7 +173,14 @@ controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_allo controls.reset(pending_reset_cycles); else if(!subframe) controls.reset(-1); - controller_frame tmp = controls.commit(movb.get_movie().get_current_frame()); + auto& mov = movb.get_movie(); + controller_frame tmp = controls.commit(mov.get_current_frame()); + if(mov.get_reload_mode() && mov.readonly_mode()) { + //In reload mode, read the present frame and XOR it. + controller_frame pframe; + pframe = mov.current_subframe(); + tmp = tmp ^ pframe; + } lua_callback_do_input(tmp, subframe); return tmp; } @@ -270,6 +277,8 @@ void update_movie_state() x << "C"; else if(!mo.readonly_mode()) x << "R"; + else if(mo.get_reload_mode()) + x << "L"; else if(mo.get_frame_count() >= mo.get_current_frame()) x << "P"; else @@ -636,6 +645,20 @@ namespace information_dispatch::do_status_update(); }); + function_ptr_command<> toggle_rlmode("toggle-rlmode", "Toggle reload mode", + "Syntax: toggle-rlmode\nToggles reload mode\n", + []() throw(std::bad_alloc, std::runtime_error) { + bool c = movb.get_movie().get_reload_mode(); + if(!c && !movb.get_movie().readonly_mode()) { + //If in read-write mode, set readonly mode. + movb.get_movie().readonly_mode(true); + information_dispatch::do_mode_change(true); + } + movb.get_movie().set_reload_mode(!c); + update_movie_state(); + information_dispatch::do_status_update(); + }); + function_ptr_command<> repaint("repaint", "Redraw the screen", "Syntax: repaint\nRedraws the screen\n", []() throw(std::bad_alloc, std::runtime_error) { @@ -700,6 +723,7 @@ namespace inverse_key iset_rwmode("set-rwmode", "Movie‣Switch to read/write"); inverse_key itoggle_romode("set-romode", "Movie‣Switch to read-only"); inverse_key itoggle_rwmode("toggle-rwmode", "Movie‣Toggle read-only"); + inverse_key itoggle_rlmode("toggle-rlmode", "Movie‣Toggle reload mode"); inverse_key irepaint("repaint", "System‣Repaint screen"); inverse_key itogglepause("toggle-pause-on-end", "Movie‣Toggle pause-on-end"); inverse_key irewind_movie("rewind-movie", "Movie‣Rewind movie"); diff --git a/src/core/movie.cpp b/src/core/movie.cpp index 14af3859..c0f95c6f 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -220,11 +220,32 @@ bool movie::get_DRDY(unsigned pid, unsigned ctrl) throw(std::logic_error) return pollcounters.get_DRDY(pid, ctrl); } +controller_frame movie::current_subframe() throw(std::bad_alloc) +{ + //Reload mode works like readwrite once movie end is reached. + if(readonly && (!reload_mode || current_frame_first_subframe < movie_data.size())) { + //In readonly mode... + //If at the end of the movie, return released / neutral... + //Before the beginning? Somebody screwed up (but return released / neutral anyway)... + if(current_frame_first_subframe >= movie_data.size() || current_frame == 0) + return movie_data.blank_frame(false); + //Otherwise find the last valid frame of input. + uint32_t changes = count_changes(current_frame_first_subframe); + uint32_t polls = pollcounters.max_polls(); + uint32_t index = (changes >= polls) ? polls : changes - 1; + return movie_data[current_frame_first_subframe + index].copy(index == 0); + } else { + //Readwrite mode. The frame is always empty. + return movie_data.blank_frame(false); + } +} + short movie::next_input(unsigned pid, unsigned ctrl) throw(std::bad_alloc, std::logic_error) { pollcounters.clear_DRDY(pid, ctrl); - if(readonly) { + //Reload mode works like readwrite once movie end is reached. + if(readonly && (!reload_mode || current_frame_first_subframe < movie_data.size())) { //In readonly mode... //If at the end of the movie, return released / neutral (but also record the poll)... if(current_frame_first_subframe >= movie_data.size()) { @@ -237,8 +258,11 @@ short movie::next_input(unsigned pid, unsigned ctrl) throw(std::bad_alloc, std:: //Otherwise find the last valid frame of input. uint32_t changes = count_changes(current_frame_first_subframe); uint32_t polls = pollcounters.increment_polls(pid, ctrl); - uint32_t index = (changes > polls) ? polls : changes - 1; + uint32_t index = (changes >= polls) ? polls : changes - 1; //debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush; + //Reload mode reloads the frames. + if(reload_mode && changes < polls) + movie_data[current_frame_first_subframe + index] = current_controls.copy(index == 0); return movie_data[current_frame_first_subframe + index].axis(pid, ctrl); } else { //Readwrite mode. @@ -283,6 +307,7 @@ movie::movie() throw(std::bad_alloc) { seqno = 0; readonly = false; + reload_mode = false; rerecords = "0"; _project_id = ""; current_frame = 0; @@ -548,6 +573,7 @@ movie& movie::operator=(const movie& m) { seqno++; readonly = m.readonly; + reload_mode = m.reload_mode; rerecords = m.rerecords; _project_id = m._project_id; movie_data = m.movie_data; diff --git a/src/core/moviedata.cpp b/src/core/moviedata.cpp index ad4e1528..0f024f6b 100644 --- a/src/core/moviedata.cpp +++ b/src/core/moviedata.cpp @@ -338,6 +338,7 @@ void do_load_beginning(bool reload) throw(std::bad_alloc, std::runtime_error) void do_load_state(struct moviefile& _movie, int lmode) { bool current_mode = movb.get_movie().readonly_mode(); + bool in_reload = movb.get_movie().get_reload_mode(); if(_movie.force_corrupt) throw std::runtime_error("Movie file invalid"); bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE; @@ -457,6 +458,9 @@ void do_load_state(struct moviefile& _movie, int lmode) if(lmode == LOAD_STATE_CURRENT && !current_mode) movb.get_movie().readonly_mode(false); information_dispatch::do_mode_change(movb.get_movie().readonly_mode()); + //Anything except load_state_preserve disenages reload mode. + if(lmode != LOAD_STATE_PRESERVE) + movb.get_movie().set_reload_mode(false); if(our_rom->rtype) messages << "ROM Type " << our_rom->rtype->get_hname() << " region " << our_rom->region->get_hname() << std::endl; diff --git a/src/lua/movie.cpp b/src/lua/movie.cpp index 7c5d8115..1d00fc5d 100644 --- a/src/lua/movie.cpp +++ b/src/lua/movie.cpp @@ -35,9 +35,39 @@ namespace function_ptr_luafun mrw("movie.readwrite", [](lua_State* LS, const std::string& fname) -> int { auto& m = get_movie(); m.readonly_mode(false); + m.set_reload_mode(false); return 0; }); + function_ptr_luafun mgm("movie.getmode", [](lua_State* LS, const std::string& fname) -> int { + auto& m = get_movie(); + if(m.readonly_mode()) + if(m.get_reload_mode()) + lua_pushstring(LS, "reload"); + else + lua_pushstring(LS, "readonly"); + else + lua_pushstring(LS, "readwrite"); + return 1; + }); + + function_ptr_luafun msm("movie.setmode", [](lua_State* LS, const std::string& fname) -> int { + auto& m = get_movie(); + std::string mod = get_string_argument(LS, 1, fname.c_str()); + if(mod == "reload") { + m.readonly_mode(true); + m.set_reload_mode(true); + } else if(mod == "readonly") { + m.readonly_mode(true); + m.set_reload_mode(false); + } else if(mod == "readwrite") { + m.readonly_mode(false); + m.set_reload_mode(false); + } + return 0; + }); + + function_ptr_luafun mfs("movie.frame_subframes", [](lua_State* LS, const std::string& fname) -> int { uint64_t frame = get_numeric_argument(LS, 1, "movie.frame_subframes"); auto& m = get_movie();