Lua engine improvements (#9453)

Made auto-boot script errors and plugin bootstrap errors fatal.

Run auto-boot scripts in a sandbox.  Globals can be accessed, but not
set.  The sandbox is cleared on hard reset, but not on soft reset.

Added (hopefully) useful to string metafunctions to device_t and
address space that show short names and tags.

Fixed issues in plugins that surface when strict type checking is
enabled, as this means numbers and nil are not automatically converted
to strings.  Plugins should be tested with debug builds to check for
this.

Made save item read_block raise an error on invalid arguments rather
than returning an empty string, and made it use luaL_buffer directly
rather than using the helper wrapper.

Changed some more function bindings to use set_function to avoid issues
related to ThePhD/sol2#608, and got rid of some unnecessary lambda
captures.
This commit is contained in:
Vas Crabb 2022-03-23 20:27:30 +11:00 committed by GitHub
parent bb3f3e5abd
commit e6588480c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 417 additions and 342 deletions

View File

@ -157,8 +157,8 @@ local function populate_configure_menu(menu)
configure_selection_save = #menu configure_selection_save = #menu
end end
table.insert(menu, {_p('plugin-autofire', 'Hotkey'), key_name, hotkey_poller and 'lr' or ''}) table.insert(menu, {_p('plugin-autofire', 'Hotkey'), key_name, hotkey_poller and 'lr' or ''})
table.insert(menu, {_p('plugin-autofire', 'On frames'), current_button.on_frames, current_button.on_frames > 1 and 'lr' or 'r'}) table.insert(menu, {_p('plugin-autofire', 'On frames'), tostring(current_button.on_frames), current_button.on_frames > 1 and 'lr' or 'r'})
table.insert(menu, {_p('plugin-autofire', 'Off frames'), current_button.off_frames, current_button.off_frames > 1 and 'lr' or 'r'}) table.insert(menu, {_p('plugin-autofire', 'Off frames'), tostring(current_button.off_frames), current_button.off_frames > 1 and 'lr' or 'r'})
configure_menu_active = true configure_menu_active = true
end end

View File

@ -351,7 +351,7 @@ function cheatfind.startplugin()
for num, func in ipairs(menu) do for num, func in ipairs(menu) do
local item, f = func() local item, f = func()
if item then if item then
menu_list[#menu_list + 1] = item table.insert(menu_list, item)
menu_func[#menu_list] = f menu_func[#menu_list] = f
end end
end end
@ -715,9 +715,9 @@ function cheatfind.startplugin()
end end
local m local m
if optable[opsel] == "ltv" or optable[opsel] == "gtv" or optable[opsel] == "eqv" or optable[opsel] == "nev" then if optable[opsel] == "ltv" or optable[opsel] == "gtv" or optable[opsel] == "eqv" or optable[opsel] == "nev" then
m = { _("Value"), value, "" } m = { _("Value"), tostring(value), "" }
else else
m = { _("Difference"), value, "" } m = { _("Difference"), tostring(value), "" }
end end
local max = 100 -- max value? local max = 100 -- max value?
menu_lim(value, 0, max, m) menu_lim(value, 0, max, m)
@ -792,7 +792,7 @@ function cheatfind.startplugin()
end end
menu[#menu + 1] = function() return { "---", "", "off" }, nil end menu[#menu + 1] = function() return { "---", "", "off" }, nil end
menu[#menu + 1] = function() menu[#menu + 1] = function()
local m = { _("Match block"), matchsel, "" } local m = { _("Match block"), tostring(matchsel), "" }
menu_lim(matchsel, 0, #matches[#matches], m) menu_lim(matchsel, 0, #matches[#matches], m)
if matchsel == 0 then if matchsel == 0 then
m[2] = _("All") m[2] = _("All")
@ -1017,7 +1017,7 @@ function cheatfind.startplugin()
end end
if matches[#matches].count > 100 then if matches[#matches].count > 100 then
menu[#menu + 1] = function() menu[#menu + 1] = function()
local m = { _("Page"), matchpg, "on" } local m = { _("Page"), tostring(matchpg), "on" }
local max local max
if matchsel == 0 then if matchsel == 0 then
max = math.ceil(matches[#matches].count / 100) - 1 max = math.ceil(matches[#matches].count / 100) - 1

View File

@ -347,7 +347,7 @@ local function add_edit_items(items)
local input = manager.machine.input local input = manager.machine.input
local arrows local arrows
items[#items + 1] = { _p('plugin-inputmacro', 'Name'), edit_name_buffer and (edit_name_buffer .. '_') or edit_current_macro.name, '' } table.insert(items, { _p('plugin-inputmacro', 'Name'), edit_name_buffer and (edit_name_buffer .. '_') or edit_current_macro.name, '' })
edit_items[#items] = { action = 'name' } edit_items[#items] = { action = 'name' }
if not (edit_start_selection or edit_start_step or edit_menu_active) then if not (edit_start_selection or edit_start_step or edit_menu_active) then
edit_start_selection = #items edit_start_selection = #items
@ -356,11 +356,11 @@ local function add_edit_items(items)
local binding = edit_current_macro.binding local binding = edit_current_macro.binding
local activation = binding and input:seq_name(binding) or _p('plugin-inputmacro', '[not set]') local activation = binding and input:seq_name(binding) or _p('plugin-inputmacro', '[not set]')
items[#items + 1] = { _p('plugin-inputmacro', 'Activation sequence'), activation, edit_switch_poller and 'lr' or '' } table.insert(items, { _p('plugin-inputmacro', 'Activation sequence'), activation, edit_switch_poller and 'lr' or '' })
edit_items[#items] = { action = 'binding' } edit_items[#items] = { action = 'binding' }
local releaseaction = edit_current_macro.earlycancel and _p('plugin-inputmacro', 'Stop immediately') or _p('plugin-inputmacro', 'Complete macro') local releaseaction = edit_current_macro.earlycancel and _p('plugin-inputmacro', 'Stop immediately') or _p('plugin-inputmacro', 'Complete macro')
items[#items + 1] = { _p('plugin-inputmacro', 'On release'), releaseaction, edit_current_macro.earlycancel and 'r' or 'l' } table.insert(items, { _p('plugin-inputmacro', 'On release'), releaseaction, edit_current_macro.earlycancel and 'r' or 'l' })
edit_items[#items] = { action = 'releaseaction' } edit_items[#items] = { action = 'releaseaction' }
local holdaction local holdaction
@ -376,18 +376,18 @@ local function add_edit_items(items)
else else
holdaction = string.format(_p('plugin-inputmacro', 'Prolong step %d'), #edit_current_macro.steps) holdaction = string.format(_p('plugin-inputmacro', 'Prolong step %d'), #edit_current_macro.steps)
end end
items[#items + 1] = { _p('plugin-inputmacro', 'When held'), holdaction, arrows } table.insert(items, { _p('plugin-inputmacro', 'When held'), holdaction, arrows })
edit_items[#items] = { action = 'holdaction' } edit_items[#items] = { action = 'holdaction' }
for i, step in ipairs(edit_current_macro.steps) do for i, step in ipairs(edit_current_macro.steps) do
items[#items + 1] = { string.format(_p('plugin-inputmacro', 'Step %d'), i), '', 'heading' } table.insert(items, { string.format(_p('plugin-inputmacro', 'Step %d'), i), '', 'heading' })
items[#items + 1] = { _p('plugin-inputmacro', 'Delay (frames)'), step.delay, (step.delay > 0) and 'lr' or 'r' } table.insert(items, { _p('plugin-inputmacro', 'Delay (frames)'), tostring(step.delay), (step.delay > 0) and 'lr' or 'r' })
edit_items[#items] = { action = 'delay', step = i } edit_items[#items] = { action = 'delay', step = i }
if edit_start_step == i then if edit_start_step == i then
edit_start_selection = #items edit_start_selection = #items
end end
items[#items + 1] = { _p('plugin-inputmacro', 'Duration (frames)'), step.duration, (step.duration > 1) and 'lr' or 'r' } table.insert(items, { _p('plugin-inputmacro', 'Duration (frames)'), tostring(step.duration), (step.duration > 1) and 'lr' or 'r' })
edit_items[#items] = { action = 'duration', step = i } edit_items[#items] = { action = 'duration', step = i }
for j, input in ipairs(step.inputs) do for j, input in ipairs(step.inputs) do
@ -399,17 +399,17 @@ local function add_edit_items(items)
else else
inputname = _p('plugin-inputmacro', '[not set]') inputname = _p('plugin-inputmacro', '[not set]')
end end
items[#items + 1] = { string.format(_p('plugin-inputmacro', 'Input %d'), j), inputname, '' } table.insert(items, { string.format(_p('plugin-inputmacro', 'Input %d'), j), inputname, '' })
edit_items[#items] = { action = 'input', step = i, input = j } edit_items[#items] = { action = 'input', step = i, input = j }
end end
if step.inputs[#step.inputs].field then if step.inputs[#step.inputs].field then
items[#items + 1] = { _p('plugin-inputmacro', 'Add input'), '', '' } table.insert(items, { _p('plugin-inputmacro', 'Add input'), '', '' })
edit_items[#items] = { action = 'addinput', step = i } edit_items[#items] = { action = 'addinput', step = i }
end end
if #edit_current_macro.steps > 1 then if #edit_current_macro.steps > 1 then
items[#items + 1] = { _p('plugin-inputmacro', 'Delete step'), '', '' } table.insert(items, { _p('plugin-inputmacro', 'Delete step'), '', '' })
edit_items[#items] = { action = 'deletestep', step = i } edit_items[#items] = { action = 'deletestep', step = i }
end end
end end
@ -417,7 +417,7 @@ local function add_edit_items(items)
local laststep = edit_current_macro.steps[#edit_current_macro.steps] local laststep = edit_current_macro.steps[#edit_current_macro.steps]
if laststep.inputs[#laststep.inputs].field then if laststep.inputs[#laststep.inputs].field then
items[#items + 1] = { '---', '', '' } table.insert(items, { '---', '', '' })
arrows = 'lr' arrows = 'lr'
if edit_insert_position > #edit_current_macro.steps then if edit_insert_position > #edit_current_macro.steps then
@ -425,7 +425,7 @@ local function add_edit_items(items)
elseif edit_insert_position < 2 then elseif edit_insert_position < 2 then
arrows = 'r' arrows = 'r'
end end
items[#items + 1] = { _p('plugin-inputmacro', 'Add step at position'), edit_insert_position, arrows } table.insert(items, { _p('plugin-inputmacro', 'Add step at position'), tostring(edit_insert_position), arrows })
edit_items[#items] = { action = 'addstep', step = i } edit_items[#items] = { action = 'addstep', step = i }
end end
end end

View File

@ -38,7 +38,7 @@ function timer.startplugin()
{ '---', '', '' }, { '---', '', '' },
{ _p("plugin-timer", "Current time"), sectohms(time), "off" }, { _p("plugin-timer", "Current time"), sectohms(time), "off" },
{ _p("plugin-timer", "Total time"), sectohms(total), "off" }, { _p("plugin-timer", "Total time"), sectohms(total), "off" },
{ _p("plugin-timer", "Play Count"), play_count, "off" } }, { _p("plugin-timer", "Play Count"), tostring(play_count), "off" } },
highlight, highlight,
"idle" "idle"
end end

View File

@ -352,16 +352,18 @@ sol::object lua_engine::call_plugin(const std::string &name, sol::object in)
{ {
std::string field = "cb_" + name; std::string field = "cb_" + name;
sol::object obj = sol().registry()[field]; sol::object obj = sol().registry()[field];
if(obj.is<sol::protected_function>()) if (obj.is<sol::protected_function>())
{ {
auto res = invoke(obj.as<sol::protected_function>(), in); auto res = invoke(obj.as<sol::protected_function>(), in);
if(!res.valid()) if (!res.valid())
{ {
sol::error err = res; sol::error err = res;
osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what()); osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what());
} }
else else
{
return res.get<sol::object>(); return res.get<sol::object>();
}
} }
return sol::lua_nil; return sol::lua_nil;
} }
@ -380,7 +382,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
} }
else else
{ {
std::tuple<sol::table, std::optional<long>, std::string> table = res; std::tuple<sol::table, std::optional<long>, std::optional<std::string> > table = res;
for (auto &entry : std::get<0>(table)) for (auto &entry : std::get<0>(table))
{ {
if (entry.second.is<sol::table>()) if (entry.second.is<sol::table>())
@ -389,7 +391,10 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3)); menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3));
} }
} }
flags = std::get<2>(table); if (std::get<2>(table))
flags = *std::get<2>(table);
else
flags.clear();
return std::get<1>(table); return std::get<1>(table);
} }
} }
@ -400,7 +405,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event) std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
{ {
std::string field = "menu_cb_" + menu; std::string field = "menu_cb_" + menu;
std::pair<bool, std::optional<long> > ret(false, std::nullopt); std::pair<std::optional<bool>, std::optional<long> > ret(false, std::nullopt);
sol::object obj = sol().registry()[field]; sol::object obj = sol().registry()[field];
if (obj.is<sol::protected_function>()) if (obj.is<sol::protected_function>())
{ {
@ -415,7 +420,7 @@ std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::strin
ret = res; ret = res;
} }
} }
return ret; return std::make_pair(std::get<0>(ret) && *std::get<0>(ret), std::get<1>(ret));
} }
void lua_engine::set_machine(running_machine *machine) void lua_engine::set_machine(running_machine *machine)
@ -423,9 +428,10 @@ void lua_engine::set_machine(running_machine *machine)
m_machine = machine; m_machine = machine;
} }
int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback) template <typename T>
size_t lua_engine::enumerate_functions(const char *id, T &&callback)
{ {
int count = 0; size_t count = 0;
sol::object functable = sol().registry()[id]; sol::object functable = sol().registry()[id];
if (functable.is<sol::table>()) if (functable.is<sol::table>())
{ {
@ -439,23 +445,24 @@ int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol
break; break;
} }
} }
return true;
} }
return count; return count;
} }
bool lua_engine::execute_function(const char *id) bool lua_engine::execute_function(const char *id)
{ {
int count = enumerate_functions(id, [this](const sol::protected_function &func) size_t count = enumerate_functions(
{ id,
auto ret = invoke(func); [] (const sol::protected_function &func)
if(!ret.valid()) {
{ auto ret = invoke(func);
sol::error err = ret; if (!ret.valid())
osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what()); {
} sol::error err = ret;
return true; osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
}); }
return true;
});
return count > 0; return count > 0;
} }
@ -521,21 +528,23 @@ void lua_engine::on_periodic()
bool lua_engine::on_missing_mandatory_image(const std::string &instance_name) bool lua_engine::on_missing_mandatory_image(const std::string &instance_name)
{ {
bool handled = false; bool handled = false;
enumerate_functions("LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE", [this, &instance_name, &handled](const sol::protected_function &func) enumerate_functions(
{ "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE",
auto ret = invoke(func, instance_name); [&instance_name, &handled] (const sol::protected_function &func)
{
auto ret = invoke(func, instance_name);
if(!ret.valid()) if (!ret.valid())
{ {
sol::error err = ret; sol::error err = ret;
osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what()); osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
} }
else if (ret.get<bool>()) else if (ret.get<bool>())
{ {
handled = true; handled = true;
} }
return !handled; return !handled;
}); });
return handled; return handled;
} }
@ -829,7 +838,7 @@ void lua_engine::initialize()
auto space = buf.prepare(len); auto space = buf.prepare(len);
space.add(file.read(space.get(), len)); space.add(file.read(space.get(), len));
buf.push(); buf.push();
return sol::stack::pop<sol::object>(s); return sol::make_reference(s, sol::stack_reference(s, -1));
}); });
file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); }); file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); });
file_type.set("puts", &emu_file::puts); file_type.set("puts", &emu_file::puts);
@ -974,12 +983,14 @@ void lua_engine::initialize()
})); }));
item_type.set("size", sol::readonly(&save_item::size)); item_type.set("size", sol::readonly(&save_item::size));
item_type.set("count", sol::readonly(&save_item::count)); item_type.set("count", sol::readonly(&save_item::count));
item_type.set("read", [this](save_item &item, int offset) -> sol::object { item_type.set("read",
if(!item.base || (offset >= item.count)) [this] (save_item &item, int offset) -> sol::object
{
if (!item.base || (offset >= item.count))
return sol::lua_nil; return sol::lua_nil;
const void *const data = reinterpret_cast<const uint8_t *>(item.base) + (item.stride * (offset / item.valcount)); const void *const data = reinterpret_cast<const uint8_t *>(item.base) + (item.stride * (offset / item.valcount));
uint64_t ret = 0; uint64_t ret = 0;
switch(item.size) switch (item.size)
{ {
case 1: case 1:
default: default:
@ -997,33 +1008,38 @@ void lua_engine::initialize()
} }
return sol::make_object(sol(), ret); return sol::make_object(sol(), ret);
}); });
item_type.set("read_block", [](save_item &item, sol::this_state s, int offset, size_t len) { item_type.set("read_block",
buffer_helper buf(s); [] (save_item &item, sol::this_state s, uint32_t offset, size_t len) -> sol::object
auto space = buf.prepare(len); {
if(!item.base || ((offset + len) > (item.size * item.count))) if (!item.base)
{ {
space.add(0); luaL_error(s, "Invalid save item");
return sol::lua_nil;
} }
else
if ((offset + len) > (item.size * item.count))
{ {
const uint32_t blocksize = item.size * item.valcount; luaL_error(s, "Range extends beyond end of save item");
size_t remaining = len; return sol::lua_nil;
uint8_t *dest = reinterpret_cast<uint8_t *>(space.get());
while(remaining)
{
const uint32_t blockno = offset / blocksize;
const uint32_t available = blocksize - (offset % blocksize);
const uint32_t chunk = (available < remaining) ? available : remaining;
const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
std::memcpy(dest, source, chunk);
offset += chunk;
remaining -= chunk;
dest += chunk;
}
space.add(len);
} }
buf.push();
return sol::stack::pop<sol::object>(s); luaL_Buffer buff;
uint8_t *dest = reinterpret_cast<uint8_t *>(luaL_buffinitsize(s, &buff, len));
const uint32_t blocksize = item.size * item.valcount;
size_t remaining = len;
while (remaining)
{
const uint32_t blockno = offset / blocksize;
const uint32_t available = blocksize - (offset % blocksize);
const uint32_t chunk = (available < remaining) ? available : remaining;
const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
std::memcpy(dest, source, chunk);
offset += chunk;
remaining -= chunk;
dest += chunk;
}
luaL_pushresultsize(&buff, len);
return sol::make_reference(s, sol::stack_reference(s, -1));
}); });
item_type.set("write", [](save_item &item, int offset, uint64_t value) { item_type.set("write", [](save_item &item, int offset, uint64_t value) {
if(!item.base || (offset >= item.count)) if(!item.base || (offset >= item.count))
@ -1187,54 +1203,54 @@ void lua_engine::initialize()
auto machine_type = sol().registry().new_usertype<running_machine>("machine", sol::no_constructor); auto machine_type = sol().registry().new_usertype<running_machine>("machine", sol::no_constructor);
machine_type["exit"] = &running_machine::schedule_exit; machine_type.set_function("exit", &running_machine::schedule_exit);
machine_type["hard_reset"] = &running_machine::schedule_hard_reset; machine_type.set_function("hard_reset", &running_machine::schedule_hard_reset);
machine_type["soft_reset"] = &running_machine::schedule_soft_reset; machine_type.set_function("soft_reset", &running_machine::schedule_soft_reset);
machine_type["save"] = &running_machine::schedule_save; // TODO: some kind of completion notification? machine_type.set_function("save", &running_machine::schedule_save); // TODO: some kind of completion notification?
machine_type["load"] = &running_machine::schedule_load; // TODO: some kind of completion notification? machine_type.set_function("load", &running_machine::schedule_load); // TODO: some kind of completion notification?
machine_type["buffer_save"] = machine_type.set_function("buffer_save",
[] (running_machine &m, sol::this_state s) [] (running_machine &m, sol::this_state s)
{
// FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
// right now it's broken by anonymous timers, synchronize, etc.
lua_State *L = s;
luaL_Buffer buff;
int size = ram_state::get_size(m.save());
u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
save_error error = m.save().write_buffer(ptr, size);
if (error == STATERR_NONE)
{ {
luaL_pushresultsize(&buff, size); // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
return sol::make_reference(L, sol::stack_reference(L, -1)); // right now it's broken by anonymous timers, synchronize, etc.
} lua_State *L = s;
luaL_error(L, "State save error."); luaL_Buffer buff;
return sol::make_reference(L, nullptr); int size = ram_state::get_size(m.save());
}; u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
machine_type["buffer_load"] = save_error error = m.save().write_buffer(ptr, size);
[] (running_machine &m, sol::this_state s, std::string str) if (error == STATERR_NONE)
{ {
// FIXME: this needs to schedule loading from the buffer and return asynchronously somehow luaL_pushresultsize(&buff, size);
// right now it's broken by anonymous timers, synchronize, etc. return sol::make_reference(L, sol::stack_reference(L, -1));
save_error error = m.save().read_buffer((u8 *)str.data(), str.size()); }
if (error == STATERR_NONE) luaL_error(L, "State save error.");
return sol::make_reference(L, nullptr);
});
machine_type.set_function("buffer_load",
[] (running_machine &m, sol::this_state s, std::string str)
{ {
return true; // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
} // right now it's broken by anonymous timers, synchronize, etc.
else save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
if (error == STATERR_NONE)
{
return true;
}
else
{
luaL_error(s,"State load error.");
return false;
}
});
machine_type.set_function("popmessage",
[] (running_machine &m, std::optional<const char *> str)
{ {
luaL_error(s,"State load error."); if (str)
return false; m.popmessage("%s", *str);
} else
}; m.popmessage();
machine_type["popmessage"] = });
[] (running_machine &m, const char *str) machine_type.set_function("logerror", [] (running_machine &m, char const *str) { m.logerror("[luaengine] %s\n", str); });
{
if (str)
m.popmessage("%s", str);
else
m.popmessage();
};
machine_type["logerror"] = [] (running_machine &m, std::string const *str) { m.logerror("[luaengine] %s\n", str); };
machine_type["time"] = sol::property(&running_machine::time); machine_type["time"] = sol::property(&running_machine::time);
machine_type["system"] = sol::property(&running_machine::system); machine_type["system"] = sol::property(&running_machine::system);
machine_type["parameters"] = sol::property(&running_machine::parameters); machine_type["parameters"] = sol::property(&running_machine::parameters);
@ -1337,15 +1353,16 @@ void lua_engine::initialize()
auto device_type = sol().registry().new_usertype<device_t>("device", sol::no_constructor); auto device_type = sol().registry().new_usertype<device_t>("device", sol::no_constructor);
device_type["subtag"] = &device_t::subtag; device_type.set_function(sol::meta_function::to_string, [] (device_t &d) { return util::string_format("%s(%s)", d.shortname(), d.tag()); });
device_type["siblingtag"] = &device_t::siblingtag; device_type.set_function("subtag", &device_t::subtag);
device_type["memregion"] = &device_t::memregion; device_type.set_function("siblingtag", &device_t::siblingtag);
device_type["memshare"] = &device_t::memshare; device_type.set_function("memregion", &device_t::memregion);
device_type["membank"] = &device_t::membank; device_type.set_function("memshare", &device_t::memshare);
device_type["ioport"] = &device_t::ioport; device_type.set_function("membank", &device_t::membank);
device_type["subdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice); device_type.set_function("ioport", &device_t::ioport);
device_type["siblingdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice); device_type.set_function("subdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice));
device_type["parameter"] = &device_t::parameter; device_type.set_function("siblingdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice));
device_type.set_function("parameter", &device_t::parameter);
device_type["tag"] = sol::property(&device_t::tag); device_type["tag"] = sol::property(&device_t::tag);
device_type["basetag"] = sol::property(&device_t::basetag); device_type["basetag"] = sol::property(&device_t::basetag);
device_type["name"] = sol::property(&device_t::name); device_type["name"] = sol::property(&device_t::name);
@ -1914,35 +1931,29 @@ void lua_engine::resume(int nparam)
luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam); luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam);
} }
void lua_engine::run(sol::load_result res) //-------------------------------------------------
// load_script - load script from file path
//-------------------------------------------------
sol::load_result lua_engine::load_script(std::string const &filename)
{ {
if(res.valid()) return sol().load_file(filename);
{
auto ret = invoke(res.get<sol::protected_function>());
if(!ret.valid())
{
sol::error err = ret;
osd_printf_error("[LUA ERROR] in run: %s\n", err.what());
}
}
else
osd_printf_error("[LUA ERROR] %d loading Lua script\n", (int)res.status());
} }
//------------------------------------------------- //-------------------------------------------------
// execute - load and execute script // load_string - load script from string
//------------------------------------------------- //-------------------------------------------------
void lua_engine::load_script(const char *filename) sol::load_result lua_engine::load_string(std::string const &value)
{ {
run(sol().load_file(filename)); return sol().load(value);
} }
//------------------------------------------------- //-------------------------------------------------
// execute_string - execute script from string // make_environment - make a sandbox
//------------------------------------------------- //-------------------------------------------------
void lua_engine::load_string(const char *value) sol::environment lua_engine::make_environment()
{ {
run(sol().load(value)); return sol::environment(sol(), sol::create, sol().globals());
} }

View File

@ -34,7 +34,6 @@ class lua_engine
{ {
public: public:
// helper structures // helper structures
class buffer_helper;
template <typename T> struct devenum; template <typename T> struct devenum;
template <typename T> struct simple_list_wrapper; template <typename T> struct simple_list_wrapper;
template <typename T> struct tag_object_ptr_map; template <typename T> struct tag_object_ptr_map;
@ -48,8 +47,9 @@ public:
~lua_engine(); ~lua_engine();
void initialize(); void initialize();
void load_script(const char *filename); sol::load_result load_script(std::string const &filename);
void load_string(const char *value); sol::load_result load_string(std::string const &value);
sol::environment make_environment();
bool frame_hook(); bool frame_hook();
@ -122,9 +122,27 @@ public:
sol::state_view &sol() const { return *m_sol_state; } sol::state_view &sol() const { return *m_sol_state; }
private: template <typename Func, typename... Params>
template<typename T, size_t SIZE> class enum_parser; static std::decay_t<std::invoke_result_t<Func, Params...> > invoke(Func &&func, Params&&... args)
{
g_profiler.start(PROFILER_LUA);
try
{
auto result = func(std::forward<Params>(args)...);
g_profiler.stop();
return result;
}
catch (...)
{
g_profiler.stop();
throw;
}
}
private:
template <typename T, size_t Size> class enum_parser;
class buffer_helper;
struct addr_space; struct addr_space;
class tap_helper; class tap_helper;
class addr_space_change_notif; class addr_space_change_notif;
@ -170,17 +188,12 @@ private:
void resume(int nparam); void resume(int nparam);
void register_function(sol::function func, const char *id); void register_function(sol::function func, const char *id);
int enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback); template <typename T> size_t enumerate_functions(const char *id, T &&callback);
bool execute_function(const char *id); bool execute_function(const char *id);
sol::object call_plugin(const std::string &name, sol::object in); sol::object call_plugin(const std::string &name, sol::object in);
void close(); void close();
void run(sol::load_result res);
template <typename TFunc, typename... TArgs>
sol::protected_function_result invoke(TFunc &&func, TArgs&&... args);
void initialize_debug(sol::table &emu); void initialize_debug(sol::table &emu);
void initialize_input(sol::table &emu); void initialize_input(sol::table &emu);
void initialize_memory(sol::table &emu); void initialize_memory(sol::table &emu);

View File

@ -21,6 +21,24 @@
template <typename T>
struct lua_engine::simple_list_wrapper
{
simple_list_wrapper(simple_list<T> const &l) : list(l) { }
simple_list<T> const &list;
};
template <typename T>
struct lua_engine::tag_object_ptr_map
{
tag_object_ptr_map(T const &m) : map(m) { }
T const &map;
};
class lua_engine::buffer_helper class lua_engine::buffer_helper
{ {
private: private:
@ -110,24 +128,6 @@ public:
}; };
template <typename T>
struct lua_engine::simple_list_wrapper
{
simple_list_wrapper(simple_list<T> const &l) : list(l) { }
simple_list<T> const &list;
};
template <typename T>
struct lua_engine::tag_object_ptr_map
{
tag_object_ptr_map(T const &m) : map(m) { }
T const &map;
};
namespace sol { namespace sol {
// don't convert core_options to a table directly // don't convert core_options to a table directly
@ -491,13 +491,13 @@ struct lua_engine::addr_space
}; };
template <typename T, size_t SIZE> template <typename T, size_t Size>
class lua_engine::enum_parser class lua_engine::enum_parser
{ {
public: public:
constexpr enum_parser(std::initializer_list<std::pair<std::string_view, T> > values) constexpr enum_parser(std::initializer_list<std::pair<std::string_view, T> > values)
{ {
if (values.size() != SIZE) if (values.size() != Size)
throw false && "size template argument incorrectly specified"; throw false && "size template argument incorrectly specified";
std::copy(values.begin(), values.end(), m_map.begin()); std::copy(values.begin(), values.end(), m_map.begin());
} }
@ -514,7 +514,7 @@ public:
} }
private: private:
std::array<std::pair<std::string_view, T>, SIZE> m_map; std::array<std::pair<std::string_view, T>, Size> m_map;
}; };
@ -527,7 +527,7 @@ template <typename R, typename T, typename D>
auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&), D &&dflt, const char *name, const char *desc) auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&), D &&dflt, const char *name, const char *desc)
{ {
return return
[this, setter, dflt, name, desc] (T &self, sol::object cb) [setter, dflt, name, desc] (T &self, sol::object cb)
{ {
if (cb == sol::lua_nil) if (cb == sol::lua_nil)
{ {
@ -536,7 +536,7 @@ auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&
else if (cb.is<sol::protected_function>()) else if (cb.is<sol::protected_function>())
{ {
(self.*setter)(delegate<R ()>( (self.*setter)(delegate<R ()>(
[this, dflt, desc, cbfunc = cb.as<sol::protected_function>()] () -> R [dflt, desc, cbfunc = cb.as<sol::protected_function>()] () -> R
{ {
if constexpr (std::is_same_v<R, void>) if constexpr (std::is_same_v<R, void>)
{ {
@ -566,18 +566,4 @@ auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&
}; };
} }
//-------------------------------------------------
// invoke - invokes a function, wrapping profiler
//-------------------------------------------------
template <typename TFunc, typename... TArgs>
inline sol::protected_function_result lua_engine::invoke(TFunc &&func, TArgs &&... args)
{
g_profiler.start(PROFILER_LUA);
sol::protected_function_result result = func(std::forward<TArgs>(args)...);
g_profiler.stop();
return result;
}
#endif // MAME_FRONTEND_MAME_LUAENGINE_IPP #endif // MAME_FRONTEND_MAME_LUAENGINE_IPP

View File

@ -181,14 +181,14 @@ void lua_engine::initialize_debug(sol::table &emu)
{ "m", EXPSPACE_REGION } { "m", EXPSPACE_REGION }
}; };
auto const do_add_symbol = [this] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, std::optional<sol::protected_function> setter, std::optional<char const *> format) -> symbol_entry & auto const do_add_symbol = [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, std::optional<sol::protected_function> setter, std::optional<char const *> format) -> symbol_entry &
{ {
symbol_table::setter_func setfun; symbol_table::setter_func setfun;
if (setter) if (setter)
setfun = [this, cbfunc = std::move(*setter)] (u64 value) { invoke(cbfunc, value); }; setfun = [cbfunc = std::move(*setter)] (u64 value) { invoke(cbfunc, value); };
return st.table().add( return st.table().add(
name, name,
[this, cbfunc = std::move(getter)] () -> u64 [cbfunc = std::move(getter)] () -> u64
{ {
auto result = invoke(cbfunc).get<sol::optional<u64> >(); auto result = invoke(cbfunc).get<sol::optional<u64> >();
if (result) if (result)
@ -217,12 +217,12 @@ void lua_engine::initialize_debug(sol::table &emu)
[] (device_t &device) [] (device_t &device)
{ return std::make_shared<symbol_table_wrapper>(device.machine(), nullptr, &device); })); { return std::make_shared<symbol_table_wrapper>(device.machine(), nullptr, &device); }));
symbol_table_type.set_function("set_memory_modified_func", symbol_table_type.set_function("set_memory_modified_func",
[this] (symbol_table_wrapper &st, sol::object cb) [] (symbol_table_wrapper &st, sol::object cb)
{ {
if (cb == sol::lua_nil) if (cb == sol::lua_nil)
st.table().set_memory_modified_func(nullptr); st.table().set_memory_modified_func(nullptr);
else if (cb.is<sol::protected_function>()) else if (cb.is<sol::protected_function>())
st.table().set_memory_modified_func([this, cbfunc = cb.as<sol::protected_function>()] () { invoke(cbfunc); }); st.table().set_memory_modified_func([cbfunc = cb.as<sol::protected_function>()] () { invoke(cbfunc); });
else else
osd_printf_error("[LUA ERROR] must call set_memory_modified_func with function or nil\n"); osd_printf_error("[LUA ERROR] must call set_memory_modified_func with function or nil\n");
}); });
@ -246,19 +246,19 @@ void lua_engine::initialize_debug(sol::table &emu)
{ {
return do_add_symbol(st, name, getter, std::nullopt, nullptr); return do_add_symbol(st, name, getter, std::nullopt, nullptr);
}, },
[this] (symbol_table_wrapper &st, char const *name, int minparams, int maxparams, sol::protected_function execute) -> symbol_entry & [] (symbol_table_wrapper &st, sol::this_state s, char const *name, int minparams, int maxparams, sol::protected_function execute) -> symbol_entry &
{ {
return st.table().add( return st.table().add(
name, name,
minparams, minparams,
maxparams, maxparams,
[this, cbref = sol::reference(execute)] (int numparams, u64 const *paramlist) -> u64 [L = s.L, cbref = sol::reference(execute)] (int numparams, u64 const *paramlist) -> u64
{ {
sol::stack_reference traceback(m_lua_state, -sol::stack::push(m_lua_state, sol::default_traceback_error_handler)); sol::stack_reference traceback(L, -sol::stack::push(L, sol::default_traceback_error_handler));
cbref.push(); cbref.push();
sol::stack_aligned_stack_handler_function func(m_lua_state, -1, traceback); sol::stack_aligned_stack_handler_function func(L, -1, traceback);
for (int i = 0; numparams > i; ++i) for (int i = 0; numparams > i; ++i)
lua_pushinteger(m_lua_state, paramlist[i]); lua_pushinteger(L, paramlist[i]);
auto result = func(sol::stack_count(numparams)).get<sol::optional<u64> >(); auto result = func(sol::stack_count(numparams)).get<sol::optional<u64> >();
traceback.pop(); traceback.pop();
return result ? *result : 0; return result ? *result : 0;

View File

@ -195,7 +195,6 @@ public:
tap_helper(tap_helper &&) = delete; tap_helper(tap_helper &&) = delete;
tap_helper( tap_helper(
lua_engine &engine,
address_space &space, address_space &space,
read_or_write mode, read_or_write mode,
offs_t start, offs_t start,
@ -203,7 +202,6 @@ public:
std::string &&name, std::string &&name,
sol::protected_function &&callback) sol::protected_function &&callback)
: m_callback(std::move(callback)) : m_callback(std::move(callback))
, m_engine(engine)
, m_space(space) , m_space(space)
, m_handler() , m_handler()
, m_name(std::move(name)) , m_name(std::move(name))
@ -270,7 +268,7 @@ private:
m_name, m_name,
[this] (offs_t offset, T &data, T mem_mask) [this] (offs_t offset, T &data, T mem_mask)
{ {
auto result = m_engine.invoke(m_callback, offset, data, mem_mask).template get<sol::optional<T> >(); auto result = invoke(m_callback, offset, data, mem_mask).template get<sol::optional<T> >();
if (result) if (result)
data = *result; data = *result;
}, },
@ -283,7 +281,7 @@ private:
m_name, m_name,
[this] (offs_t offset, T &data, T mem_mask) [this] (offs_t offset, T &data, T mem_mask)
{ {
auto result = m_engine.invoke(m_callback, offset, data, mem_mask).template get<sol::optional<T> >(); auto result = invoke(m_callback, offset, data, mem_mask).template get<sol::optional<T> >();
if (result) if (result)
data = *result; data = *result;
}, },
@ -303,7 +301,6 @@ private:
}; };
sol::protected_function m_callback; sol::protected_function m_callback;
lua_engine &m_engine;
address_space &m_space; address_space &m_space;
memory_passthrough_handler m_handler; memory_passthrough_handler m_handler;
std::string m_name; std::string m_name;
@ -531,118 +528,125 @@ void lua_engine::initialize_memory(sol::table &emu)
{ {
auto addr_space_type = sol().registry().new_usertype<addr_space>("addr_space", sol::no_constructor); auto addr_space_type = sol().registry().new_usertype<addr_space>("addr_space", sol::no_constructor);
addr_space_type["read_i8"] = &addr_space::mem_read<s8>; addr_space_type.set_function(sol::meta_function::to_string,
addr_space_type["read_u8"] = &addr_space::mem_read<u8>; [] (addr_space const &sp)
addr_space_type["read_i16"] = &addr_space::mem_read<s16>; {
addr_space_type["read_u16"] = &addr_space::mem_read<u16>; device_t &d(sp.dev.device());
addr_space_type["read_i32"] = &addr_space::mem_read<s32>; return util::string_format("%s(%s):%s", d.shortname(), d.tag(), sp.space.name());
addr_space_type["read_u32"] = &addr_space::mem_read<u32>; });
addr_space_type["read_i64"] = &addr_space::mem_read<s64>; addr_space_type.set_function("read_i8", &addr_space::mem_read<s8>);
addr_space_type["read_u64"] = &addr_space::mem_read<u64>; addr_space_type.set_function("read_u8", &addr_space::mem_read<u8>);
addr_space_type["write_i8"] = &addr_space::mem_write<s8>; addr_space_type.set_function("read_i16", &addr_space::mem_read<s16>);
addr_space_type["write_u8"] = &addr_space::mem_write<u8>; addr_space_type.set_function("read_u16", &addr_space::mem_read<u16>);
addr_space_type["write_i16"] = &addr_space::mem_write<s16>; addr_space_type.set_function("read_i32", &addr_space::mem_read<s32>);
addr_space_type["write_u16"] = &addr_space::mem_write<u16>; addr_space_type.set_function("read_u32", &addr_space::mem_read<u32>);
addr_space_type["write_i32"] = &addr_space::mem_write<s32>; addr_space_type.set_function("read_i64", &addr_space::mem_read<s64>);
addr_space_type["write_u32"] = &addr_space::mem_write<u32>; addr_space_type.set_function("read_u64", &addr_space::mem_read<u64>);
addr_space_type["write_i64"] = &addr_space::mem_write<s64>; addr_space_type.set_function("write_i8", &addr_space::mem_write<s8>);
addr_space_type["write_u64"] = &addr_space::mem_write<u64>; addr_space_type.set_function("write_u8", &addr_space::mem_write<u8>);
addr_space_type["readv_i8"] = &addr_space::log_mem_read<s8>; addr_space_type.set_function("write_i16", &addr_space::mem_write<s16>);
addr_space_type["readv_u8"] = &addr_space::log_mem_read<u8>; addr_space_type.set_function("write_u16", &addr_space::mem_write<u16>);
addr_space_type["readv_i16"] = &addr_space::log_mem_read<s16>; addr_space_type.set_function("write_i32", &addr_space::mem_write<s32>);
addr_space_type["readv_u16"] = &addr_space::log_mem_read<u16>; addr_space_type.set_function("write_u32", &addr_space::mem_write<u32>);
addr_space_type["readv_i32"] = &addr_space::log_mem_read<s32>; addr_space_type.set_function("write_i64", &addr_space::mem_write<s64>);
addr_space_type["readv_u32"] = &addr_space::log_mem_read<u32>; addr_space_type.set_function("write_u64", &addr_space::mem_write<u64>);
addr_space_type["readv_i64"] = &addr_space::log_mem_read<s64>; addr_space_type.set_function("readv_i8", &addr_space::log_mem_read<s8>);
addr_space_type["readv_u64"] = &addr_space::log_mem_read<u64>; addr_space_type.set_function("readv_u8", &addr_space::log_mem_read<u8>);
addr_space_type["writev_i8"] = &addr_space::log_mem_write<s8>; addr_space_type.set_function("readv_i16", &addr_space::log_mem_read<s16>);
addr_space_type["writev_u8"] = &addr_space::log_mem_write<u8>; addr_space_type.set_function("readv_u16", &addr_space::log_mem_read<u16>);
addr_space_type["writev_i16"] = &addr_space::log_mem_write<s16>; addr_space_type.set_function("readv_i32", &addr_space::log_mem_read<s32>);
addr_space_type["writev_u16"] = &addr_space::log_mem_write<u16>; addr_space_type.set_function("readv_u32", &addr_space::log_mem_read<u32>);
addr_space_type["writev_i32"] = &addr_space::log_mem_write<s32>; addr_space_type.set_function("readv_i64", &addr_space::log_mem_read<s64>);
addr_space_type["writev_u32"] = &addr_space::log_mem_write<u32>; addr_space_type.set_function("readv_u64", &addr_space::log_mem_read<u64>);
addr_space_type["writev_i64"] = &addr_space::log_mem_write<s64>; addr_space_type.set_function("writev_i8", &addr_space::log_mem_write<s8>);
addr_space_type["writev_u64"] = &addr_space::log_mem_write<u64>; addr_space_type.set_function("writev_u8", &addr_space::log_mem_write<u8>);
addr_space_type["read_direct_i8"] = &addr_space::direct_mem_read<s8>; addr_space_type.set_function("writev_i16", &addr_space::log_mem_write<s16>);
addr_space_type["read_direct_u8"] = &addr_space::direct_mem_read<u8>; addr_space_type.set_function("writev_u16", &addr_space::log_mem_write<u16>);
addr_space_type["read_direct_i16"] = &addr_space::direct_mem_read<s16>; addr_space_type.set_function("writev_i32", &addr_space::log_mem_write<s32>);
addr_space_type["read_direct_u16"] = &addr_space::direct_mem_read<u16>; addr_space_type.set_function("writev_u32", &addr_space::log_mem_write<u32>);
addr_space_type["read_direct_i32"] = &addr_space::direct_mem_read<s32>; addr_space_type.set_function("writev_i64", &addr_space::log_mem_write<s64>);
addr_space_type["read_direct_u32"] = &addr_space::direct_mem_read<u32>; addr_space_type.set_function("writev_u64", &addr_space::log_mem_write<u64>);
addr_space_type["read_direct_i64"] = &addr_space::direct_mem_read<s64>; addr_space_type.set_function("read_direct_i8", &addr_space::direct_mem_read<s8>);
addr_space_type["read_direct_u64"] = &addr_space::direct_mem_read<u64>; addr_space_type.set_function("read_direct_u8", &addr_space::direct_mem_read<u8>);
addr_space_type["write_direct_i8"] = &addr_space::direct_mem_write<s8>; addr_space_type.set_function("read_direct_i16", &addr_space::direct_mem_read<s16>);
addr_space_type["write_direct_u8"] = &addr_space::direct_mem_write<u8>; addr_space_type.set_function("read_direct_u16", &addr_space::direct_mem_read<u16>);
addr_space_type["write_direct_i16"] = &addr_space::direct_mem_write<s16>; addr_space_type.set_function("read_direct_i32", &addr_space::direct_mem_read<s32>);
addr_space_type["write_direct_u16"] = &addr_space::direct_mem_write<u16>; addr_space_type.set_function("read_direct_u32", &addr_space::direct_mem_read<u32>);
addr_space_type["write_direct_i32"] = &addr_space::direct_mem_write<s32>; addr_space_type.set_function("read_direct_i64", &addr_space::direct_mem_read<s64>);
addr_space_type["write_direct_u32"] = &addr_space::direct_mem_write<u32>; addr_space_type.set_function("read_direct_u64", &addr_space::direct_mem_read<u64>);
addr_space_type["write_direct_i64"] = &addr_space::direct_mem_write<s64>; addr_space_type.set_function("write_direct_i8", &addr_space::direct_mem_write<s8>);
addr_space_type["write_direct_u64"] = &addr_space::direct_mem_write<u64>; addr_space_type.set_function("write_direct_u8", &addr_space::direct_mem_write<u8>);
addr_space_type["read_range"] = addr_space_type.set_function("write_direct_i16", &addr_space::direct_mem_write<s16>);
addr_space_type.set_function("write_direct_u16", &addr_space::direct_mem_write<u16>);
addr_space_type.set_function("write_direct_i32", &addr_space::direct_mem_write<s32>);
addr_space_type.set_function("write_direct_u32", &addr_space::direct_mem_write<u32>);
addr_space_type.set_function("write_direct_i64", &addr_space::direct_mem_write<s64>);
addr_space_type.set_function("write_direct_u64", &addr_space::direct_mem_write<u64>);
addr_space_type.set_function("read_range",
[] (addr_space &sp, sol::this_state s, u64 first, u64 last, int width, sol::object opt_step) -> sol::object [] (addr_space &sp, sol::this_state s, u64 first, u64 last, int width, sol::object opt_step) -> sol::object
{ {
lua_State *L = s;
luaL_Buffer buff;
offs_t space_size = sp.space.addrmask();
u64 step = 1; u64 step = 1;
if (opt_step.is<u64>()) if (opt_step.is<u64>())
{ {
step = opt_step.as<u64>(); step = opt_step.as<u64>();
if (step < 1 || step > last - first) if ((step < 1) || (step > last - first))
{ {
luaL_error(L, "Invalid step"); luaL_error(s, "Invalid step");
return sol::lua_nil; return sol::lua_nil;
} }
} }
if (first > space_size || last > space_size || last < first)
offs_t space_size = sp.space.addrmask();
if ((first > space_size) || (last > space_size) || (last < first))
{ {
luaL_error(L, "Invalid offset"); luaL_error(s, "Invalid offset");
return sol::lua_nil; return sol::lua_nil;
} }
luaL_Buffer buff;
int byte_count = width / 8 * (last - first + 1) / step; int byte_count = width / 8 * (last - first + 1) / step;
switch (width) switch (width)
{ {
case 8: case 8:
{ {
u8 *dest = (u8 *)luaL_buffinitsize(L, &buff, byte_count); u8 *dest = (u8 *)luaL_buffinitsize(s, &buff, byte_count);
for ( ; first <= last; first += step) for ( ; first <= last; first += step)
*dest++ = sp.mem_read<u8>(first); *dest++ = sp.mem_read<u8>(first);
break; break;
} }
case 16: case 16:
{ {
u16 *dest = (u16 *)luaL_buffinitsize(L, &buff, byte_count); u16 *dest = (u16 *)luaL_buffinitsize(s, &buff, byte_count);
for ( ; first <= last; first += step) for ( ; first <= last; first += step)
*dest++ = sp.mem_read<u16>(first); *dest++ = sp.mem_read<u16>(first);
break; break;
} }
case 32: case 32:
{ {
u32 *dest = (u32 *)luaL_buffinitsize(L, &buff, byte_count); u32 *dest = (u32 *)luaL_buffinitsize(s, &buff, byte_count);
for( ; first <= last; first += step) for( ; first <= last; first += step)
*dest++ = sp.mem_read<u32>(first); *dest++ = sp.mem_read<u32>(first);
break; break;
} }
case 64: case 64:
{ {
u64 *dest = (u64 *)luaL_buffinitsize(L, &buff, byte_count); u64 *dest = (u64 *)luaL_buffinitsize(s, &buff, byte_count);
for( ; first <= last; first += step) for( ; first <= last; first += step)
*dest++ = sp.mem_read<u64>(first); *dest++ = sp.mem_read<u64>(first);
break; break;
} }
default: default:
luaL_error(L, "Invalid width. Must be 8/16/32/64"); luaL_error(s, "Invalid width. Must be 8/16/32/64");
return sol::lua_nil; return sol::lua_nil;
} }
luaL_pushresultsize(&buff, byte_count); luaL_pushresultsize(&buff, byte_count);
return sol::make_reference(L, sol::stack_reference(L, -1)); return sol::make_reference(s, sol::stack_reference(s, -1));
}; });
addr_space_type["add_change_notifier"] = addr_space_type.set_function("add_change_notifier",
[this] (addr_space &sp, sol::protected_function &&cb) [] (addr_space &sp, sol::protected_function &&cb)
{ {
return sp.space.add_change_notifier( return sp.space.add_change_notifier(
[this, callback = std::move(cb)] (read_or_write mode) [callback = std::move(cb)] (read_or_write mode)
{ {
char const *modestr = ""; char const *modestr = "";
switch (mode) switch (mode)
@ -653,17 +657,17 @@ void lua_engine::initialize_memory(sol::table &emu)
} }
invoke(callback, modestr); invoke(callback, modestr);
}); });
}; });
addr_space_type["install_read_tap"] = addr_space_type.set_function("install_read_tap",
[this] (addr_space &sp, offs_t start, offs_t end, std::string &&name, sol::protected_function &&cb) [] (addr_space &sp, offs_t start, offs_t end, std::string &&name, sol::protected_function &&cb)
{ {
return std::make_unique<tap_helper>(*this, sp.space, read_or_write::READ, start, end, std::move(name), std::move(cb)); return std::make_unique<tap_helper>(sp.space, read_or_write::READ, start, end, std::move(name), std::move(cb));
}; });
addr_space_type["install_write_tap"] = addr_space_type.set_function("install_write_tap",
[this] (addr_space &sp, offs_t start, offs_t end, std::string &&name, sol::protected_function &&cb) [] (addr_space &sp, offs_t start, offs_t end, std::string &&name, sol::protected_function &&cb)
{ {
return std::make_unique<tap_helper>(*this, sp.space, read_or_write::WRITE, start, end, std::move(name), std::move(cb)); return std::make_unique<tap_helper>(sp.space, read_or_write::WRITE, start, end, std::move(name), std::move(cb));
}; });
addr_space_type["name"] = sol::property([] (addr_space &sp) { return sp.space.name(); }); addr_space_type["name"] = sol::property([] (addr_space &sp) { return sp.space.name(); });
addr_space_type["shift"] = sol::property([] (addr_space &sp) { return sp.space.addr_shift(); }); addr_space_type["shift"] = sol::property([] (addr_space &sp) { return sp.space.addr_shift(); });
addr_space_type["index"] = sol::property([] (addr_space &sp) { return sp.space.spacenum(); }); addr_space_type["index"] = sol::property([] (addr_space &sp) { return sp.space.spacenum(); });
@ -674,8 +678,8 @@ void lua_engine::initialize_memory(sol::table &emu)
auto tap_type = sol().registry().new_usertype<tap_helper>("mempassthrough", sol::no_constructor); auto tap_type = sol().registry().new_usertype<tap_helper>("mempassthrough", sol::no_constructor);
tap_type["reinstall"] = &tap_helper::reinstall; tap_type.set_function("reinstall", &tap_helper::reinstall);
tap_type["remove"] = &tap_helper::remove; tap_type.set_function("remove", &tap_helper::remove);
tap_type["addrstart"] = sol::property(&tap_helper::start); tap_type["addrstart"] = sol::property(&tap_helper::start);
tap_type["addrend"] = sol::property(&tap_helper::end); tap_type["addrend"] = sol::property(&tap_helper::end);
tap_type["name"] = sol::property(&tap_helper::name); tap_type["name"] = sol::property(&tap_helper::name);
@ -722,22 +726,22 @@ void lua_engine::initialize_memory(sol::table &emu)
auto region_type = sol().registry().new_usertype<memory_region>("region", sol::no_constructor); auto region_type = sol().registry().new_usertype<memory_region>("region", sol::no_constructor);
region_type["read_i8"] = &region_read<s8>; region_type.set_function("read_i8", &region_read<s8>);
region_type["read_u8"] = &region_read<u8>; region_type.set_function("read_u8", &region_read<u8>);
region_type["read_i16"] = &region_read<s16>; region_type.set_function("read_i16", &region_read<s16>);
region_type["read_u16"] = &region_read<u16>; region_type.set_function("read_u16", &region_read<u16>);
region_type["read_i32"] = &region_read<s32>; region_type.set_function("read_i32", &region_read<s32>);
region_type["read_u32"] = &region_read<u32>; region_type.set_function("read_u32", &region_read<u32>);
region_type["read_i64"] = &region_read<s64>; region_type.set_function("read_i64", &region_read<s64>);
region_type["read_u64"] = &region_read<u64>; region_type.set_function("read_u64", &region_read<u64>);
region_type["write_i8"] = &region_write<s8>; region_type.set_function("write_i8", &region_write<s8>);
region_type["write_u8"] = &region_write<u8>; region_type.set_function("write_u8", &region_write<u8>);
region_type["write_i16"] = &region_write<s16>; region_type.set_function("write_i16", &region_write<s16>);
region_type["write_u16"] = &region_write<u16>; region_type.set_function("write_u16", &region_write<u16>);
region_type["write_i32"] = &region_write<s32>; region_type.set_function("write_i32", &region_write<s32>);
region_type["write_u32"] = &region_write<u32>; region_type.set_function("write_u32", &region_write<u32>);
region_type["write_i64"] = &region_write<s64>; region_type.set_function("write_i64", &region_write<s64>);
region_type["write_u64"] = &region_write<u64>; region_type.set_function("write_u64", &region_write<u64>);
region_type["tag"] = sol::property(&memory_region::name); region_type["tag"] = sol::property(&memory_region::name);
region_type["size"] = sol::property(&memory_region::bytes); region_type["size"] = sol::property(&memory_region::bytes);
region_type["length"] = sol::property([] (memory_region &r) { return r.bytes() / r.bytewidth(); }); region_type["length"] = sol::property([] (memory_region &r) { return r.bytes() / r.bytewidth(); });
@ -747,22 +751,22 @@ void lua_engine::initialize_memory(sol::table &emu)
auto share_type = sol().registry().new_usertype<memory_share>("share", sol::no_constructor); auto share_type = sol().registry().new_usertype<memory_share>("share", sol::no_constructor);
share_type["read_i8"] = &share_read<s8>; share_type.set_function("read_i8", &share_read<s8>);
share_type["read_u8"] = &share_read<u8>; share_type.set_function("read_u8", &share_read<u8>);
share_type["read_i16"] = &share_read<s16>; share_type.set_function("read_i16", &share_read<s16>);
share_type["read_u16"] = &share_read<u16>; share_type.set_function("read_u16", &share_read<u16>);
share_type["read_i32"] = &share_read<s32>; share_type.set_function("read_i32", &share_read<s32>);
share_type["read_u32"] = &share_read<u32>; share_type.set_function("read_u32", &share_read<u32>);
share_type["read_i64"] = &share_read<s64>; share_type.set_function("read_i64", &share_read<s64>);
share_type["read_u64"] = &share_read<u64>; share_type.set_function("read_u64", &share_read<u64>);
share_type["write_i8"] = &share_write<s8>; share_type.set_function("write_i8", &share_write<s8>);
share_type["write_u8"] = &share_write<u8>; share_type.set_function("write_u8", &share_write<u8>);
share_type["write_i16"] = &share_write<s16>; share_type.set_function("write_i16", &share_write<s16>);
share_type["write_u16"] = &share_write<u16>; share_type.set_function("write_u16", &share_write<u16>);
share_type["write_i32"] = &share_write<s32>; share_type.set_function("write_i32", &share_write<s32>);
share_type["write_u32"] = &share_write<u32>; share_type.set_function("write_u32", &share_write<u32>);
share_type["write_i64"] = &share_write<s64>; share_type.set_function("write_i64", &share_write<s64>);
share_type["write_u64"] = &share_write<u64>; share_type.set_function("write_u64", &share_write<u64>);
share_type["tag"] = sol::property(&memory_share::name); share_type["tag"] = sol::property(&memory_share::name);
share_type["size"] = sol::property(&memory_share::bytes); share_type["size"] = sol::property(&memory_share::bytes);
share_type["length"] = sol::property([] (memory_share &s) { return s.bytes() / s.bytewidth(); }); share_type["length"] = sol::property([] (memory_share &s) { return s.bytes() / s.bytewidth(); });

View File

@ -203,8 +203,29 @@ void mame_machine_manager::start_luaengine()
if (!filerr) if (!filerr)
{ {
std::string exppath; std::string exppath;
osd_subst_env(exppath, std::string(file.fullpath())); osd_subst_env(exppath, file.fullpath());
m_lua->load_script(exppath.c_str()); auto &l(*lua());
auto load_result = l.load_script(exppath);
if (!load_result.valid())
{
sol::error err = load_result;
sol::load_status status = load_result.status();
fatalerror("Error plugin bootstrap script %s: %s error\n%s\n",
exppath,
sol::to_string(status),
err.what());
}
sol::protected_function func = load_result;
sol::protected_function_result call_result = l.invoke(func);
if (!call_result.valid())
{
sol::error err = call_result;
sol::call_status status = call_result.status();
fatalerror("Error running plugin bootstrap script %s: %s error\n%s\n",
options().autoboot_script(),
sol::to_string(status),
err.what());
}
} }
} }
} }
@ -295,21 +316,35 @@ int mame_machine_manager::execute()
TIMER_CALLBACK_MEMBER(mame_machine_manager::autoboot_callback) TIMER_CALLBACK_MEMBER(mame_machine_manager::autoboot_callback)
{ {
if (strlen(options().autoboot_script())!=0) { if (*options().autoboot_script())
mame_machine_manager::instance()->lua()->load_script(options().autoboot_script()); {
assert(m_autoboot_script);
sol::protected_function func = *m_autoboot_script;
sol::protected_function_result result = lua()->invoke(func);
if (!result.valid())
{
sol::error err = result;
sol::call_status status = result.status();
fatalerror("Error running autoboot script %s: %s error\n%s\n",
options().autoboot_script(),
sol::to_string(status),
err.what());
}
} }
else if (strlen(options().autoboot_command())!=0) { else if (*options().autoboot_command())
std::string cmd = std::string(options().autoboot_command()); {
std::string cmd(options().autoboot_command());
strreplace(cmd, "'", "\\'"); strreplace(cmd, "'", "\\'");
std::string val = std::string("emu.keypost('").append(cmd).append("')"); std::string val = std::string("emu.keypost('").append(cmd).append("')");
mame_machine_manager::instance()->lua()->load_string(val.c_str()); auto &l(*lua());
l.invoke(l.load_string(val));
} }
} }
void mame_machine_manager::reset() void mame_machine_manager::reset()
{ {
// setup autoboot if needed // setup autoboot if needed
m_autoboot_timer->adjust(attotime(options().autoboot_delay(),0),0); m_autoboot_timer->adjust(attotime(options().autoboot_delay(), 0), 0);
} }
ui_manager* mame_machine_manager::create_ui(running_machine& machine) ui_manager* mame_machine_manager::create_ui(running_machine& machine)
@ -337,7 +372,7 @@ void mame_machine_manager::before_load_settings(running_machine& machine)
m_lua->on_machine_before_load_settings(); m_lua->on_machine_before_load_settings();
} }
void mame_machine_manager::create_custom(running_machine& machine) void mame_machine_manager::create_custom(running_machine &machine)
{ {
// start the inifile manager // start the inifile manager
m_inifile = std::make_unique<inifile_manager>(m_ui->options()); m_inifile = std::make_unique<inifile_manager>(m_ui->options());
@ -347,6 +382,25 @@ void mame_machine_manager::create_custom(running_machine& machine)
// start favorite manager // start favorite manager
m_favorite = std::make_unique<favorite_manager>(m_ui->options()); m_favorite = std::make_unique<favorite_manager>(m_ui->options());
// attempt to load the autoboot script if configured
m_autoboot_script.reset();
if (*options().autoboot_script())
{
auto result = lua()->load_script(options().autoboot_script());
if (!result.valid())
{
sol::error err = result;
sol::load_status status = result.status();
fatalerror("Error loading autoboot script %s: %s error\n%s\n",
options().autoboot_script(),
sol::to_string(status),
err.what());
}
m_autoboot_script.reset(new sol::load_result(std::move(result)));
sol::protected_function func = *m_autoboot_script;
sol::set_environment(lua()->make_environment(), func);
}
} }
void mame_machine_manager::load_cheatfiles(running_machine& machine) void mame_machine_manager::load_cheatfiles(running_machine& machine)

View File

@ -12,6 +12,12 @@
#pragma once #pragma once
namespace sol {
struct load_result;
} // namespace sol
class plugin_options; class plugin_options;
class osd_interface; class osd_interface;
@ -71,19 +77,20 @@ private:
mame_machine_manager &operator=(mame_machine_manager const &) = delete; mame_machine_manager &operator=(mame_machine_manager const &) = delete;
mame_machine_manager &operator=(mame_machine_manager &&) = delete; mame_machine_manager &operator=(mame_machine_manager &&) = delete;
std::unique_ptr<plugin_options> m_plugins; // pointer to plugin options std::unique_ptr<plugin_options> m_plugins; // pointer to plugin options
std::unique_ptr<lua_engine> m_lua; std::unique_ptr<lua_engine> m_lua;
const game_driver * m_new_driver_pending; // pointer to the next pending driver const game_driver * m_new_driver_pending; // pointer to the next pending driver
bool m_firstrun; bool m_firstrun;
static mame_machine_manager *s_manager; emu_timer * m_autoboot_timer; // auto-boot timer
emu_timer *m_autoboot_timer; // autoboot timer std::unique_ptr<sol::load_result> m_autoboot_script; // auto-boot script
std::unique_ptr<mame_ui_manager> m_ui; // internal data from ui.cpp std::unique_ptr<mame_ui_manager> m_ui; // internal data from ui.cpp
std::unique_ptr<cheat_manager> m_cheat; // internal data from cheat.cpp std::unique_ptr<cheat_manager> m_cheat; // internal data from cheat.cpp
std::unique_ptr<inifile_manager> m_inifile; // internal data from inifile.c for INIs std::unique_ptr<inifile_manager> m_inifile; // internal data from inifile.c for INIs
std::unique_ptr<favorite_manager> m_favorite; // internal data from inifile.c for favorites std::unique_ptr<favorite_manager> m_favorite; // internal data from inifile.c for favorites
static mame_machine_manager *s_manager;
}; };
//************************************************************************** //**************************************************************************