From 1b582c8fbdc49b9610735c7cab14bad61b13eb2f Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Tue, 31 Mar 2020 18:22:39 +0300 Subject: [PATCH] Allow just-in-time override of input to be recorded from Lua This is useful e.g., for implementing .r16m playback. --- include/core/movie.hpp | 5 +++++ include/library/movie.hpp | 8 ++++++++ include/lua/lua.hpp | 3 +++ src/core/mainloop.cpp | 9 +++++++++ src/core/movie.cpp | 8 ++++++++ src/library/movie.cpp | 7 +++++++ src/lua/gui-core.cpp | 16 ++++++++++++++++ src/lua/input.cpp | 13 +++++++++++++ src/lua/inputmovie.cpp | 29 ++++++++++++++++++++++------- src/lua/lua.cpp | 11 +++++++++++ 10 files changed, 102 insertions(+), 7 deletions(-) diff --git a/include/core/movie.hpp b/include/core/movie.hpp index 95e6d42a..ac2343ae 100644 --- a/include/core/movie.hpp +++ b/include/core/movie.hpp @@ -89,7 +89,12 @@ public: * Release memory for mov, mf and rrd. */ void release_memory(); +/** + * Set frob with data routine. + */ + void set_frob_with_value(std::function func); private: + std::function frob_with_value; movie_logic(const movie_logic&); movie_logic& operator=(const movie_logic&); movie* mov; diff --git a/include/library/movie.hpp b/include/library/movie.hpp index 4cf393d4..f17fe47e 100644 --- a/include/library/movie.hpp +++ b/include/library/movie.hpp @@ -278,6 +278,12 @@ public: * Parameter data: New movie data. */ void set_movie_data(portctrl::frame_vector* data); +/** + * Set callback to frob with data. + * + * Parameter func: New callback. + */ + void set_frob_with_value(std::function func); private: class fchange_listener : public portctrl::frame_vector::fchange_listener { @@ -290,6 +296,8 @@ private: } _listener; movie(const movie& mov); movie& operator=(const movie& m); + //Frob with value. + std::function frob_with_value; //Sequence number. uint64_t seqno; //The poll flag handling. diff --git a/include/lua/lua.hpp b/include/lua/lua.hpp index fc37da2f..6b758f88 100644 --- a/include/lua/lua.hpp +++ b/include/lua/lua.hpp @@ -56,6 +56,7 @@ struct lua_state lua::state::callback_list* on_post_rewind; lua::state::callback_list* on_set_rewind; lua::state::callback_list* on_latch; + lua::state::callback_list* on_frob_with_value; void callback_do_paint(struct lua::render_context* ctx, bool non_synthethic) throw(); void callback_do_video(struct lua::render_context* ctx, bool& kill_frame, uint32_t& hscl, uint32_t& vscl) @@ -81,6 +82,7 @@ struct lua_state bool callback_do_button(uint32_t port, uint32_t controller, uint32_t index, const char* type); void callback_movie_lost(const char* what); void callback_do_latch(std::list& args); + void callback_frob_with_value(unsigned a, unsigned b, unsigned c, short& d); void run_startup_scripts(); void add_startup_script(const std::string& file); @@ -100,6 +102,7 @@ struct lua_state uint32_t* hscl; uint32_t* vscl; bool* veto_flag; + short* frob_output; std::set hooked_keys; uint64_t idle_hook_time; uint64_t timer_hook_time; diff --git a/src/core/mainloop.cpp b/src/core/mainloop.cpp index 2a51b5dd..28260e3a 100644 --- a/src/core/mainloop.cpp +++ b/src/core/mainloop.cpp @@ -837,10 +837,19 @@ void init_main_callbacks() ecore_callbacks = &lsnes_callbacks_obj; } +void frob_with_value(unsigned a, unsigned b, unsigned c, short& d) +{ + auto& core = CORE(); + core.lua2->callback_frob_with_value(a, b, c, d); +} + + void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) { lsnes_instance.emu_thread = threads::id(); auto& core = CORE(); + //Set up the frob with inputs routine. + core.mlogic->set_frob_with_value(frob_with_value); mywindowcallbacks mywcb(*core.dispatch, *core.runmode, *core.supdater); core.iqueue->system_thread_available = true; //Basic initialization. diff --git a/src/core/movie.cpp b/src/core/movie.cpp index 4cac9037..40d67d69 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -9,6 +9,7 @@ movie_logic::movie_logic() throw() { + frob_with_value = [](unsigned a, unsigned b, unsigned c, unsigned d) {}; mf = NULL; mov = NULL; rrd = NULL; @@ -18,6 +19,7 @@ void movie_logic::set_movie(movie& _mov, bool free_old) throw() { auto tmp = mov; mov = &_mov; + mov->set_frob_with_value(frob_with_value); if(free_old) delete tmp; } @@ -91,3 +93,9 @@ void movie_logic::release_memory() delete mf; mf = NULL; } + +void movie_logic::set_frob_with_value(std::function func) +{ + frob_with_value = func; + if(mov) mov->set_frob_with_value(func); +} diff --git a/src/library/movie.cpp b/src/library/movie.cpp index c8b4bd56..03543529 100644 --- a/src/library/movie.cpp +++ b/src/library/movie.cpp @@ -138,6 +138,11 @@ bool movie::get_DRDY(unsigned port, unsigned controller, unsigned ctrl) return pollcounters.get_DRDY(port, controller, ctrl); } +void movie::set_frob_with_value(std::function func) +{ + frob_with_value = func; +} + short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) { pollcounters.clear_DRDY(port, controller, ctrl); @@ -173,6 +178,7 @@ short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) return (*movie_data)[current_frame_first_subframe].axis3(port, controller, ctrl); } short new_value = current_controls.axis3(port, controller, ctrl); + frob_with_value(port, controller, ctrl, new_value); //Fortunately, we know this frame is the last one in movie_data. uint32_t pollcounter = pollcounters.get_polls(port, controller, ctrl); if(current_frame_first_subframe + pollcounter < movie_data->size()) { @@ -196,6 +202,7 @@ short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) movie::movie() : _listener(*this), tracker(memtracker::singleton(), movie_id, sizeof(*this)) { + frob_with_value = [](unsigned a, unsigned b, unsigned c, short& d){}; movie_data = NULL; seqno = 0; readonly = false; diff --git a/src/lua/gui-core.cpp b/src/lua/gui-core.cpp index 127bd089..2cd169e3 100644 --- a/src/lua/gui-core.cpp +++ b/src/lua/gui-core.cpp @@ -120,6 +120,21 @@ namespace return 0; } + int snes_bitplane_split(lua::state& L, lua::parameters& P) + { + unsigned short a[8]; + unsigned short b[4] = {0,0,0,0}; + P(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); + for(unsigned i = 0; i < 8; i++) { + for(unsigned j = 0; j < 4; j++) { + b[j] |= ((a[i] >> 2*j+0) & 1) << 7-i; + b[j] |= ((a[i] >> 2*j+1) & 1) << 15-i; + } + } + for(unsigned i = 0; i < 4; i++) L.pushnumber(b[i]); + return 4; + } + lua::functions LUA_guicore_fns(lua_func_misc, "gui", { {"left_gap", lua_gui_set_gap<&lua::render_context::left_gap, false>}, {"right_gap", lua_gui_set_gap<&lua::render_context::right_gap, false>}, @@ -137,5 +152,6 @@ namespace {"rainbow", rainbow}, {"kill_frame", kill_frame}, {"set_video_scale", set_video_scale}, + {"snes_bitplane_split", snes_bitplane_split}, }); } diff --git a/src/lua/input.cpp b/src/lua/input.cpp index 83b78dab..d7281b79 100644 --- a/src/lua/input.cpp +++ b/src/lua/input.cpp @@ -411,6 +411,18 @@ namespace return 0; } + int frob_with_value(lua::state& L, lua::parameters& P) + { + auto& core = CORE(); + short value; + + if(P.is_boolean()) value = P.arg() ? 1 : 0; + else if(P.is_number()) value = P.arg(); + else P.expected("number or boolean"); + if(core.lua2->frob_output) *core.lua2->frob_output = value; + return 0; + } + int controller_info(lua::state& L, lua::parameters& P) { auto& core = CORE(); @@ -520,5 +532,6 @@ namespace {"port_type", _port_type}, {"veto_button", veto_button}, {"controller_info", controller_info}, + {"frob_with_value", frob_with_value}, }); } diff --git a/src/lua/inputmovie.cpp b/src/lua/inputmovie.cpp index a22e7c7c..1430944d 100644 --- a/src/lua/inputmovie.cpp +++ b/src/lua/inputmovie.cpp @@ -53,6 +53,20 @@ namespace setbutton(port, controller, button, value); return 0; } + int fast_input(lua::state& L, lua::parameters& P) + { + unsigned short a[4],m; + P(P.skipped(), a[0], a[1], a[2], a[3], m); + for(unsigned i = 0; i < 4; i++) a[i] ^= m; + for(unsigned i = 0; i < 16; i++) { + auto w = a[i / 4]; + setbutton(1, 0, i, (w >> (6-i%4*2))&1); + setbutton(1, 1, i, (w >> (7-i%4*2))&1); + setbutton(2, 0, i, (w >> (14-i%4*2))&1); + setbutton(2, 1, i, (w >> (15-i%4*2))&1); + } + return 0; + } int serialize(lua::state& L, lua::parameters& P) { char buf[MAX_SERIALIZED_SIZE]; @@ -660,13 +674,14 @@ namespace }, &lua_inputmovie::print); lua::_class LUA_class_inputframe(lua_class_movie, "INPUTFRAME", {}, { - {"get_button", &lua_inputframe::get_button}, - {"get_axis", &lua_inputframe::get_axis}, - {"set_axis", &lua_inputframe::set_axis}, - {"set_button", &lua_inputframe::set_axis}, - {"serialize", &lua_inputframe::serialize}, - {"unserialize", &lua_inputframe::unserialize}, - {"get_stride", &lua_inputframe::get_stride}, + {"get_button", &lua_inputframe::get_button}, + {"get_axis", &lua_inputframe::get_axis}, + {"set_axis", &lua_inputframe::set_axis}, + {"set_button", &lua_inputframe::set_axis}, + {"serialize", &lua_inputframe::serialize}, + {"unserialize", &lua_inputframe::unserialize}, + {"get_stride", &lua_inputframe::get_stride}, + {"snes_fast_input", &lua_inputframe::fast_input}, }, &lua_inputframe::print); lua::functions LUA_inputmovie_fns(lua_func_misc, "movie", { diff --git a/src/lua/lua.cpp b/src/lua/lua.cpp index e9c1ab21..519beceb 100644 --- a/src/lua/lua.cpp +++ b/src/lua/lua.cpp @@ -196,6 +196,7 @@ lua_state::lua_state(lua::state& _L, command::group& _command, settingvar::group idle_hook_time = 0x7EFFFFFFFFFFFFFFULL; timer_hook_time = 0x7EFFFFFFFFFFFFFFULL; + frob_output = NULL; veto_flag = NULL; kill_frame = NULL; hscl = NULL; @@ -235,6 +236,7 @@ lua_state::lua_state(lua::state& _L, command::group& _command, settingvar::group on_post_rewind = new lua::state::callback_list(L, "post_rewind", "on_post_rewind"); on_set_rewind = new lua::state::callback_list(L, "set_rewind", "on_set_rewind"); on_latch = new lua::state::callback_list(L, "latch", "on_latch"); + on_frob_with_value = new lua::state::callback_list(L, "frob_with_value", "on_frob_with_value"); } lua_state::~lua_state() @@ -266,6 +268,7 @@ lua_state::~lua_state() delete on_post_rewind; delete on_set_rewind; delete on_latch; + delete on_frob_with_value; } void lua_state::callback_do_paint(struct lua::render_context* ctx, bool non_synthetic) throw() @@ -371,6 +374,14 @@ bool lua_state::callback_do_button(uint32_t port, uint32_t controller, uint32_t return flag; } +void lua_state::callback_frob_with_value(unsigned a, unsigned b, unsigned c, short& d) +{ + short value = d; + run_callback(*on_frob_with_value, lua::state::store_tag(frob_output, &value), lua::state::numeric_tag(a), + lua::state::numeric_tag(b), lua::state::numeric_tag(c), lua::state::numeric_tag(d)); + d = value; +} + namespace { lua::_class LUA_class_unsaferewind(lua_class_movie, "UNSAFEREWIND", {}, {