Allow just-in-time override of input to be recorded from Lua

This is useful e.g., for implementing .r16m playback.
This commit is contained in:
Ilari Liusvaara 2020-03-31 18:22:39 +03:00
parent 1fe5434797
commit 1b582c8fbd
10 changed files with 102 additions and 7 deletions

View file

@ -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<void(unsigned,unsigned,unsigned,short&)> func);
private:
std::function<void(unsigned,unsigned,unsigned,short&)> frob_with_value;
movie_logic(const movie_logic&);
movie_logic& operator=(const movie_logic&);
movie* mov;

View file

@ -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<void(unsigned,unsigned,unsigned,short&)> 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<void(unsigned,unsigned,unsigned,short&)> frob_with_value;
//Sequence number.
uint64_t seqno;
//The poll flag handling.

View file

@ -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<std::string>& 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<std::string> hooked_keys;
uint64_t idle_hook_time;
uint64_t timer_hook_time;

View file

@ -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.

View file

@ -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<void(unsigned,unsigned,unsigned,short&)> func)
{
frob_with_value = func;
if(mov) mov->set_frob_with_value(func);
}

View file

@ -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<void(unsigned, unsigned, unsigned, short&)> 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;

View file

@ -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},
});
}

View file

@ -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<bool>() ? 1 : 0;
else if(P.is_number()) value = P.arg<short>();
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},
});
}

View file

@ -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_inputframe> 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", {

View file

@ -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_unsaferewind> LUA_class_unsaferewind(lua_class_movie, "UNSAFEREWIND", {}, {