diff options
author | 2016-04-23 11:25:03 +0200 | |
---|---|---|
committer | 2016-04-23 11:26:47 +0200 | |
commit | 1c726824f2b2cc7c6c03ff975e548b72f9b2c0a9 (patch) | |
tree | 020f20da5a8cd822065c1839bc7a53f826803a3d /src/frontend/mame/luaengine.cpp | |
parent | 01657a63c13c79c5f045ba4ecb4c2fd7f7c5a1f8 (diff) |
Split UI and frontend part from core [Miodrag Milanovic]
Diffstat (limited to 'src/frontend/mame/luaengine.cpp')
-rw-r--r-- | src/frontend/mame/luaengine.cpp | 2623 |
1 files changed, 2623 insertions, 0 deletions
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp new file mode 100644 index 00000000000..b8d8644218a --- /dev/null +++ b/src/frontend/mame/luaengine.cpp @@ -0,0 +1,2623 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic,Luca Bruno +/*************************************************************************** + + luaengine.c + + Controls execution of the core MAME system. + +***************************************************************************/ + +#include <limits> +#include <thread> +#include <lua.hpp> +#include "luabridge/Source/LuaBridge/LuaBridge.h" +#include <signal.h> +#include "emu.h" +#include "mame.h" +#include "cheat.h" +#include "drivenum.h" +#include "emuopts.h" +#include "ui/ui.h" +#include "luaengine.h" +#include <mutex> + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wshift-count-overflow" +#endif +//************************************************************************** +// LUA ENGINE +//************************************************************************** + +#if !defined(LUA_PROMPT) +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " +#endif + +#if !defined(LUA_MAXINPUT) +#define LUA_MAXINPUT 512 +#endif + +#define lua_readline(b,p) \ + (fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ + +static lua_State *globalL = nullptr; + +#define luai_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#define luai_writeline() (luai_writestring("\n", 1), fflush(stdout)) + +const char *const lua_engine::tname_ioport = "lua.ioport"; +lua_engine* lua_engine::luaThis = nullptr; + +extern "C" { + int luaopen_lsqlite3(lua_State *L); + int luaopen_zlib(lua_State *L); + int luaopen_lfs(lua_State *L); +} + +static void lstop(lua_State *L, lua_Debug *ar) +{ + (void)ar; /* unused arg. */ + lua_sethook(L, nullptr, 0, 0); + luaL_error(L, "interrupted!"); +} + + +static void laction(int i) +{ + signal(i, SIG_DFL); /* if another SIGINT happens before lstop, + terminate process (default action) */ + lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} + +int lua_engine::report(int status) { + if (status != LUA_OK && !lua_isnil(m_lua_state, -1)) + { + const char *msg = lua_tostring(m_lua_state, -1); + if (msg == nullptr) msg = "(error object is not a string)"; + lua_writestringerror("%s\n", msg); + lua_pop(m_lua_state, 1); + /* force a complete garbage collection in case of errors */ + lua_gc(m_lua_state, LUA_GCCOLLECT, 0); + } + return status; +} + + +static int traceback (lua_State *L) +{ + const char *msg = lua_tostring(L, 1); + if (msg) + luaL_traceback(L, L, msg, 1); + else if (!lua_isnoneornil(L, 1)) + { /* is there an error object? */ + if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ + lua_pushliteral(L, "(no error message)"); + } + return 1; +} + + +int lua_engine::docall(int narg, int nres) +{ + int status; + int base = lua_gettop(m_lua_state) - narg; /* function index */ + lua_pushcfunction(m_lua_state, traceback); /* push traceback function */ + lua_insert(m_lua_state, base); /* put it under chunk and args */ + globalL = m_lua_state; /* to be available to 'laction' */ + signal(SIGINT, laction); + status = lua_pcall(m_lua_state, narg, nres, base); + signal(SIGINT, SIG_DFL); + lua_remove(m_lua_state, base); /* remove traceback function */ + return status; +} + +namespace luabridge +{ +template <> +struct Stack <osd_file::error> +{ + static void push(lua_State *L, osd_file::error error) + { + std::string strerror; + switch(error) + { + case osd_file::error::NONE: + lua_pushboolean(L, false); + return; + case osd_file::error::FAILURE: + strerror = "failure"; + break; + case osd_file::error::OUT_OF_MEMORY: + strerror = "out_of_memory"; + break; + case osd_file::error::NOT_FOUND: + strerror = "not_found"; + break; + case osd_file::error::ACCESS_DENIED: + strerror = "access_denied"; + break; + case osd_file::error::ALREADY_OPEN: + strerror = "already_open"; + break; + case osd_file::error::TOO_MANY_FILES: + strerror = "too_many_files"; + break; + case osd_file::error::INVALID_DATA: + strerror = "invalid_data"; + break; + case osd_file::error::INVALID_ACCESS: + strerror = "invalid_access"; + break; + default: + strerror = "unknown_error"; + break; + } + lua_pushstring(L, strerror.c_str()); + } +}; +template <> +struct Stack <map_handler_type> +{ + static void push(lua_State *L, map_handler_type error) + { + std::string type; + switch(error) + { + case AMH_NONE: + type = "none"; + break; + case AMH_RAM: + type = "ram"; + break; + case AMH_ROM: + type = "rom"; + break; + case AMH_NOP: + type = "nop"; + break; + case AMH_UNMAP: + type = "unmap"; + break; + case AMH_DEVICE_DELEGATE: + type = "delegate"; + break; + case AMH_PORT: + type = "port"; + break; + case AMH_BANK: + type = "bank"; + break; + case AMH_DEVICE_SUBMAP: + type = "submap"; + break; + default: + type = "unknown"; + break; + } + lua_pushstring(L, type.c_str()); + } +}; +} +/* mark in error messages for incomplete statements */ +#define EOFMARK "<eof>" +#define marklen (sizeof(EOFMARK)/sizeof(char) - 1) + +int lua_engine::incomplete(int status) +{ + if (status == LUA_ERRSYNTAX) + { + size_t lmsg; + const char *msg = lua_tolstring(m_lua_state, -1, &lmsg); + if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) + { + lua_pop(m_lua_state, 1); + return 1; + } + } + return 0; /* else... */ +} + +lua_engine::hook::hook() +{ + L = nullptr; + cb = -1; +} + +void lua_engine::hook::set(lua_State *lua, int idx) +{ + if (L) + luaL_unref(L, LUA_REGISTRYINDEX, cb); + + if (lua_isnil(lua, idx)) { + L = nullptr; + cb = -1; + + } else { + L = lua; + lua_pushvalue(lua, idx); + cb = luaL_ref(lua, LUA_REGISTRYINDEX); + } +} + +lua_State *lua_engine::hook::precall() +{ + lua_State *T = lua_newthread(L); + lua_rawgeti(T, LUA_REGISTRYINDEX, cb); + return T; +} + +void lua_engine::hook::call(lua_engine *engine, lua_State *T, int nparam) +{ + engine->resume(T, nparam, L); +} + +void lua_engine::resume(lua_State *L, int nparam, lua_State *root) +{ + int s = lua_resume(L, nullptr, nparam); + switch(s) { + case LUA_OK: + if(!root) { + std::map<lua_State *, std::pair<lua_State *, int> >::iterator i = thread_registry.find(L); + if(i != thread_registry.end()) { + luaL_unref(i->second.first, LUA_REGISTRYINDEX, i->second.second); + thread_registry.erase(i); + } + } else + lua_pop(root, 1); + break; + + case LUA_YIELD: + if(root) { + int id = luaL_ref(root, LUA_REGISTRYINDEX); + thread_registry[L] = std::pair<lua_State *, int>(root, id); + } + break; + + default: + osd_printf_error("[LUA ERROR] %s\n", lua_tostring(L, -1)); + lua_pop(L, 1); + break; + } +} + +void lua_engine::resume(void *lua, INT32 param) +{ + resume(static_cast<lua_State *>(lua)); +} + +int lua_engine::l_ioport_write(lua_State *L) +{ + ioport_field *field = static_cast<ioport_field *>(getparam(L, 1, tname_ioport)); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "value expected"); + field->set_value(lua_tointeger(L, 2)); + return 0; +} + +//------------------------------------------------- +// emu_app_name - return application name +//------------------------------------------------- + +int lua_engine::l_emu_app_name(lua_State *L) +{ + lua_pushstring(L, emulator_info::get_appname_lower()); + return 1; +} + +//------------------------------------------------- +// emu_app_version - return application version +//------------------------------------------------- + +int lua_engine::l_emu_app_version(lua_State *L) +{ + lua_pushstring(L, bare_build_version); + return 1; +} + + +//------------------------------------------------- +// emu_gamename - returns game full name +//------------------------------------------------- + +int lua_engine::l_emu_gamename(lua_State *L) +{ + lua_pushstring(L, luaThis->machine().system().description); + return 1; +} + +//------------------------------------------------- +// emu_romname - returns rom base name +//------------------------------------------------- + +int lua_engine::l_emu_romname(lua_State *L) +{ + lua_pushstring(L, luaThis->machine().basename()); + return 1; +} + +//------------------------------------------------- +// emu_softname - returns softlist name +//------------------------------------------------- + +int lua_engine::l_emu_softname(lua_State *L) +{ + lua_pushstring(L, luaThis->machine().options().software_name()); + return 1; +} + +//------------------------------------------------- +// emu_pause/emu_unpause - pause/unpause game +//------------------------------------------------- + +int lua_engine::l_emu_pause(lua_State *L) +{ + luaThis->machine().pause(); + return 0; +} + +int lua_engine::l_emu_unpause(lua_State *L) +{ + luaThis->machine().resume(); + return 0; +} + +//------------------------------------------------- +// emu_keypost - post keys to natural keyboard +//------------------------------------------------- + +int lua_engine::l_emu_keypost(lua_State *L) +{ + const char *keys = luaL_checkstring(L,1); + luaThis->machine().ioport().natkeyboard().post_utf8(keys); + return 1; +} + +int lua_engine::l_emu_time(lua_State *L) +{ + lua_pushnumber(L, luaThis->machine().time().as_double()); + return 1; +} + +void lua_engine::emu_after_done(void *_h, INT32 param) +{ + hook *h = static_cast<hook *>(_h); + h->call(this, h->precall(), 0); + delete h; +} + +int lua_engine::emu_after(lua_State *L) +{ + luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected"); + struct hook *h = new hook; + h->set(L, 2); + machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::emu_after_done), this), 0, h); + return 0; +} + +int lua_engine::l_emu_after(lua_State *L) +{ + return luaThis->emu_after(L); +} + +int lua_engine::emu_wait(lua_State *L) +{ + luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected"); + machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), this), 0, L); + return lua_yieldk(L, 0, 0, nullptr); +} + +int lua_engine::l_emu_wait(lua_State *L) +{ + return luaThis->emu_wait(L); +} + +void lua_engine::output_notifier(const char *outname, INT32 value) +{ + if (hook_output_cb.active()) { + lua_State *L = hook_output_cb.precall(); + lua_pushstring(L, outname); + lua_pushnumber(L, value); + hook_output_cb.call(this, L, 2); + } +} + +void lua_engine::s_output_notifier(const char *outname, INT32 value, void *param) +{ + static_cast<lua_engine *>(param)->output_notifier(outname, value); +} + +void lua_engine::emu_hook_output(lua_State *L) +{ + luaL_argcheck(L, lua_isfunction(L, 1), 1, "callback function expected"); + hook_output_cb.set(L, 1); + + if (!output_notifier_set) { + machine().output().set_notifier(nullptr, s_output_notifier, this); + output_notifier_set = true; + } +} + +int lua_engine::l_emu_hook_output(lua_State *L) +{ + luaThis->emu_hook_output(L); + return 0; +} + +int lua_engine::l_emu_set_hook(lua_State *L) +{ + luaThis->emu_set_hook(L); + return 0; +} + +void lua_engine::emu_set_hook(lua_State *L) +{ + luaL_argcheck(L, lua_isfunction(L, 1) || lua_isnil(L, 1), 1, "callback function expected"); + luaL_argcheck(L, lua_isstring(L, 2), 2, "message (string) expected"); + const char *hookname = luaL_checkstring(L,2); + + if (strcmp(hookname, "output") == 0) { + hook_output_cb.set(L, 1); + if (!output_notifier_set) { + machine().output().set_notifier(nullptr, s_output_notifier, this); + output_notifier_set = true; + } + } else if (strcmp(hookname, "frame") == 0) { + hook_frame_cb.set(L, 1); + } else { + lua_writestringerror("%s", "Unknown hook name, aborting.\n"); + } +} + +//------------------------------------------------- +// options_entry - return table of option entries +// -> manager:options().entries +// -> manager:machine():options().entries +// -> manager:machine():ui():options().entries +//------------------------------------------------- + +template <typename T> +luabridge::LuaRef lua_engine::l_options_get_entries(const T *o) +{ + T *options = const_cast<T *>(o); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef entries_table = luabridge::LuaRef::newTable(L); + + int unadorned_index = 0; + for (typename T::entry &curentry : *options) + { + const char *name = curentry.name(); + bool is_unadorned = false; + // check if it's unadorned + if (name && strlen(name) && !strcmp(name, options->unadorned(unadorned_index))) + { + unadorned_index++; + is_unadorned = true; + } + if (!curentry.is_header() && !curentry.is_command() && !curentry.is_internal() && !is_unadorned) + entries_table[name] = &curentry; + } + + return entries_table; +} + +//------------------------------------------------- +// machine_get_screens - return table of available screens userdata +// -> manager:machine().screens[":screen"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_machine_get_screens(const running_machine *r) +{ + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef screens_table = luabridge::LuaRef::newTable(L); + + for (device_t *dev = r->first_screen(); dev != nullptr; dev = dev->next()) { + screen_device *sc = dynamic_cast<screen_device *>(dev); + if (sc && sc->configured() && sc->started() && sc->type()) { + screens_table[sc->tag()] = sc; + } + } + + return screens_table; +} + +//------------------------------------------------- +// machine_get_devices - return table of available devices userdata +// -> manager:machine().devices[":maincpu"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_machine_get_devices(const running_machine *r) +{ + running_machine *m = const_cast<running_machine *>(r); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef devs_table = luabridge::LuaRef::newTable(L); + + device_t *root = &(m->root_device()); + devs_table = devtree_dfs(root, devs_table); + + return devs_table; +} + +//------------------------------------------------- +// machine_get_images - return table of available image devices userdata +// -> manager:machine().images["flop1"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_machine_get_images(const running_machine *r) +{ + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef image_table = luabridge::LuaRef::newTable(L); + + for (device_image_interface &image : image_interface_iterator(r->root_device())) + { + image_table[image.brief_instance_name()] = ℑ + image_table[image.instance_name()] = ℑ + } + + return image_table; +} + +//------------------------------------------------- +// memory_banks - return memory_banks +// -> manager:machine():memory().banks["maincpu"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_memory_get_banks(const memory_manager *m) +{ + memory_manager *mm = const_cast<memory_manager *>(m); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef table = luabridge::LuaRef::newTable(L); + + for (memory_bank &bank : mm->banks()) { + table[bank.tag()] = &bank; + } + + return table; +} + +//------------------------------------------------- +// memory_regions - return memory_regions +// -> manager:machine():memory().region[":maincpu"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_memory_get_regions(const memory_manager *m) +{ + memory_manager *mm = const_cast<memory_manager *>(m); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef table = luabridge::LuaRef::newTable(L); + + for (memory_region ®ion: mm->regions()) { + table[region.name()] = ®ion; + } + + return table; +} + +//------------------------------------------------- +// machine_cheat_entries - return cheat entries +// -> manager:machine():cheat().entries[0] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_cheat_get_entries(const cheat_manager *c) +{ + cheat_manager *cm = const_cast<cheat_manager *>(c); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef entry_table = luabridge::LuaRef::newTable(L); + + int cheatnum = 0; + for (cheat_entry &entry : cm->entries()) { + entry_table[cheatnum++] = &entry; + } + + return entry_table; +} + +//------------------------------------------------- +// cheat_entry_state - return cheat entry state +// -> manager:machine():cheat().entries[0]:state() +//------------------------------------------------- + +int lua_engine::lua_cheat_entry::l_get_state(lua_State *L) +{ + cheat_entry *ce = luabridge::Stack<cheat_entry *>::get(L, 1); + + switch (ce->state()) + { + case SCRIPT_STATE_ON: lua_pushliteral(L, "on"); break; + case SCRIPT_STATE_RUN: lua_pushliteral(L, "run"); break; + case SCRIPT_STATE_CHANGE: lua_pushliteral(L, "change"); break; + case SCRIPT_STATE_COUNT: lua_pushliteral(L, "count"); break; + default: lua_pushliteral(L, "off"); break; + } + + return 1; +} + +//------------------------------------------------- +// machine_ioports - return table of ioports +// -> manager:machine():ioport().ports[':P1'] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_ioport_get_ports(const ioport_manager *m) +{ + ioport_manager *im = const_cast<ioport_manager *>(m); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef port_table = luabridge::LuaRef::newTable(L); + + for (ioport_port &port : im->ports()) { + port_table[port.tag()] = &port; + } + + return port_table; +} + +//------------------------------------------------- +// ioport_fields - return table of ioport fields +// -> manager:machine().ioport().ports[':P1'].fields[':'] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_ioports_port_get_fields(const ioport_port *i) +{ + ioport_port *p = const_cast<ioport_port *>(i); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef f_table = luabridge::LuaRef::newTable(L); + + for (ioport_field &field : p->fields()) { + f_table[field.name()] = &field; + } + + return f_table; +} + +//------------------------------------------------- +// render_get_targets - return table of render targets +// -> manager:machine():render().targets[0] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_render_get_targets(const render_manager *r) +{ + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef target_table = luabridge::LuaRef::newTable(L); + + int tc = 0; + for (render_target &curr_rt : r->targets()) + { + target_table[tc++] = &curr_rt; + } + + return target_table; +} + +// private helper for get_devices - DFS visit all devices in a running machine +luabridge::LuaRef lua_engine::devtree_dfs(device_t *root, luabridge::LuaRef devs_table) +{ + if (root) { + for (device_t &dev : root->subdevices()) { + if (dev.configured() && dev.started()) { + devs_table[dev.tag()] = &dev; + devtree_dfs(&dev, devs_table); + } + } + } + return devs_table; +} + +//------------------------------------------------- +// device_get_memspaces - return table of available address spaces userdata +// -> manager:machine().devices[":maincpu"].spaces["program"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_dev_get_memspaces(const device_t *d) +{ + device_t *dev = const_cast<device_t *>(d); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef sp_table = luabridge::LuaRef::newTable(L); + + if(!dynamic_cast<device_memory_interface *>(dev)) + return sp_table; + + for (address_spacenum sp = AS_0; sp < ADDRESS_SPACES; ++sp) { + if (dev->memory().has_space(sp)) { + sp_table[dev->memory().space(sp).name()] = &(dev->memory().space(sp)); + } + } + + return sp_table; +} + +//------------------------------------------------- +// device_get_state - return table of available state userdata +// -> manager:machine().devices[":maincpu"].state +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_dev_get_states(const device_t *d) +{ + device_t *dev = const_cast<device_t *>(d); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef st_table = luabridge::LuaRef::newTable(L); + + if(!dynamic_cast<device_state_interface *>(dev)) + return st_table; + + for (device_state_entry &s : dev->state().state_entries()) + { + // XXX: refrain from exporting non-visible entries? + st_table[s.symbol()] = &s; + } + + return st_table; +} + +//------------------------------------------------- +// device_get_item - return table of indexed items owned by this device +// -> manager:machine().devices[":maincpu"].items +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_dev_get_items(const device_t *d) +{ + device_t *dev = const_cast<device_t *>(d); + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef table = luabridge::LuaRef::newTable(L); + std::string tag = dev->tag(); + + // 10000 is enough? + for(int i = 0; i < 10000; i++) + { + std::string name; + const char *item; + unsigned int size, count; + void *base; + item = dev->machine().save().indexed_item(i, base, size, count); + if(!item) + break; + name = &(strchr(item, '/')[1]); + if(name.substr(0, name.find("/")) == tag) + { + name = name.substr(name.find("/") + 1, std::string::npos); + table[name] = i; + } + } + return table; +} + +lua_engine::lua_item::lua_item(int index) +{ + std::string name; + const char *item; + item = luaThis->machine().save().indexed_item(index, l_item_base, l_item_size, l_item_count); + if(!item) + { + l_item_base = nullptr; + l_item_size = 0; + l_item_count= 0; + } +} + +int lua_engine::lua_item::l_item_read(lua_State *L) +{ + luaL_argcheck(L, lua_isnumber(L, 2), 2, "offset (integer) expected"); + int offset = lua_tounsigned(L, 2); + int ret = 0; + if(!l_item_base || (offset > l_item_count)) + { + lua_pushnil(L); + return 1; + } + switch(l_item_size) + { + case 1: + default: + ret = ((UINT8 *)l_item_base)[offset]; + break; + case 2: + ret = ((UINT16 *)l_item_base)[offset]; + break; + case 4: + ret = ((UINT32 *)l_item_base)[offset]; + break; + case 8: + ret = ((UINT64 *)l_item_base)[offset]; + break; + } + lua_pushunsigned(L, ret); + return 1; +} + +int lua_engine::lua_item::l_item_write(lua_State *L) +{ + luaL_argcheck(L, lua_isnumber(L, 2), 2, "offset (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected"); + int offset = lua_tounsigned(L, 2); + UINT64 value = lua_tounsigned(L, 3); + if(!l_item_base || (offset > l_item_count)) + return 1; + switch(l_item_size) + { + case 1: + default: + ((UINT8 *)l_item_base)[offset] = (UINT8)value; + break; + case 2: + ((UINT16 *)l_item_base)[offset] = (UINT16)value; + break; + case 4: + ((UINT32 *)l_item_base)[offset] = (UINT32)value; + break; + case 8: + ((UINT64 *)l_item_base)[offset] = (UINT64)value; + break; + } + return 1; +} + +int lua_engine::lua_item::l_item_read_block(lua_State *L) +{ + luaL_argcheck(L, lua_isnumber(L, 2), 2, "offset (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "length (integer) expected"); + int offset = lua_tounsigned(L, 2); + int len = lua_tonumber(L, 3); + if(!l_item_base || ((offset + len) > (l_item_size * l_item_count))) + { + lua_pushnil(L); + return 1; + } + luaL_Buffer buff; + char *ptr = luaL_buffinitsize(L, &buff, len); + memcpy(ptr, l_item_base, len); + luaL_pushresultsize(&buff, len); + return 1; +} + +//------------------------------------------------- +// state_get_value - return value of a device state entry +// -> manager:machine().devices[":maincpu"].state["PC"].value +//------------------------------------------------- + +UINT64 lua_engine::l_state_get_value(const device_state_entry *d) +{ + device_state_interface *state = d->parent_state(); + if(state) { + luaThis->machine().save().dispatch_presave(); + return state->state_int(d->index()); + } else { + return 0; + } +} + +//------------------------------------------------- +// state_set_value - set value of a device state entry +// -> manager:machine().devices[":maincpu"].state["D0"].value = 0x0c00 +//------------------------------------------------- + +void lua_engine::l_state_set_value(device_state_entry *d, UINT64 val) +{ + device_state_interface *state = d->parent_state(); + if(state) { + state->set_state_int(d->index(), val); + luaThis->machine().save().dispatch_presave(); + } +} + +//------------------------------------------------- +// mem_read - templated memory readers for <sign>,<size> +// -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000) +//------------------------------------------------- + +template <typename T> +int lua_engine::lua_addr_space::l_mem_read(lua_State *L) +{ + address_space &sp = luabridge::Stack<address_space &>::get(L, 1); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); + offs_t address = lua_tounsigned(L, 2); + T mem_content = 0; + switch(sizeof(mem_content) * 8) { + case 8: + mem_content = sp.read_byte(address); + break; + case 16: + if (WORD_ALIGNED(address)) { + mem_content = sp.read_word(address); + } else { + mem_content = sp.read_word_unaligned(address); + } + break; + case 32: + if (DWORD_ALIGNED(address)) { + mem_content = sp.read_dword(address); + } else { + mem_content = sp.read_dword_unaligned(address); + } + break; + case 64: + if (QWORD_ALIGNED(address)) { + mem_content = sp.read_qword(address); + } else { + mem_content = sp.read_qword_unaligned(address); + } + break; + default: + break; + } + + if (std::numeric_limits<T>::is_signed) { + lua_pushinteger(L, mem_content); + } else { + lua_pushunsigned(L, mem_content); + } + + return 1; + +} + +//------------------------------------------------- +// mem_write - templated memory writer for <sign>,<size> +// -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template <typename T> +int lua_engine::lua_addr_space::l_mem_write(lua_State *L) +{ + address_space &sp = luabridge::Stack<address_space &>::get(L, 1); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected"); + offs_t address = lua_tounsigned(L, 2); + T val = lua_tounsigned(L, 3); + + switch(sizeof(val) * 8) { + case 8: + sp.write_byte(address, val); + break; + case 16: + if (WORD_ALIGNED(address)) { + sp.write_word(address, val); + } else { + sp.write_word_unaligned(address, val); + } + break; + case 32: + if (DWORD_ALIGNED(address)) { + sp.write_dword(address, val); + } else { + sp.write_dword_unaligned(address, val); + } + break; + case 64: + if (QWORD_ALIGNED(address)) { + sp.write_qword(address, val); + } else { + sp.write_qword_unaligned(address, val); + } + break; + default: + break; + } + + return 0; +} + +//------------------------------------------------- +// mem_direct_read - templated direct memory readers for <sign>,<size> +// -> manager:machine().devices[":maincpu"].spaces["program"]:read_direct_i8(0xC000) +//------------------------------------------------- + +template <typename T> +int lua_engine::lua_addr_space::l_direct_mem_read(lua_State *L) +{ + address_space &sp = luabridge::Stack<address_space &>::get(L, 1); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); + offs_t address = lua_tounsigned(L, 2); + T mem_content = 0; + offs_t lowmask = sp.data_width() / 8 - 1; + for(int i = 0; i < sizeof(T); i++) + { + int addr = sp.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i; + UINT8 *base = (UINT8 *)sp.get_read_ptr(sp.address_to_byte(addr & ~lowmask)); + if(!base) + continue; + mem_content <<= 8; + if(sp.endianness() == ENDIANNESS_BIG) + mem_content |= base[BYTE8_XOR_BE(addr) & lowmask]; + else + mem_content |= base[BYTE8_XOR_LE(addr) & lowmask]; + } + + if (std::numeric_limits<T>::is_signed) { + lua_pushinteger(L, mem_content); + } else { + lua_pushunsigned(L, mem_content); + } + + return 1; +} + +//------------------------------------------------- +// mem_direct_write - templated memory writer for <sign>,<size> +// -> manager:machine().devices[":maincpu"].spaces["program"]:write_direct_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template <typename T> +int lua_engine::lua_addr_space::l_direct_mem_write(lua_State *L) +{ + address_space &sp = luabridge::Stack<address_space &>::get(L, 1); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected"); + offs_t address = lua_tounsigned(L, 2); + T val = lua_tounsigned(L, 3); + offs_t lowmask = sp.data_width() / 8 - 1; + for(int i = 0; i < sizeof(T); i++) + { + int addr = sp.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i; + UINT8 *base = (UINT8 *)sp.get_read_ptr(sp.address_to_byte(addr & ~lowmask)); + if(!base) + continue; + if(sp.endianness() == ENDIANNESS_BIG) + base[BYTE8_XOR_BE(addr) & lowmask] = val & 0xff; + else + base[BYTE8_XOR_LE(addr) & lowmask] = val & 0xff; + val >>= 8; + } + + return 0; +} + +//------------------------------------------------- +// region_read - templated region readers for <sign>,<size> +// -> manager:machine():memory().region[":maincpu"]:read_i8(0xC000) +//------------------------------------------------- + +template <typename T> +int lua_engine::lua_memory_region::l_region_read(lua_State *L) +{ + memory_region ®ion = luabridge::Stack<memory_region &>::get(L, 1); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); + offs_t address = lua_tounsigned(L, 2); + T mem_content = 0; + offs_t lowmask = region.bytewidth() - 1; + for(int i = 0; i < sizeof(T); i++) + { + int addr = region.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i; + if(addr >= region.bytes()) + continue; + mem_content <<= 8; + if(region.endianness() == ENDIANNESS_BIG) + mem_content |= region.u8((BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)); + else + mem_content |= region.u8((BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)); + } + + if (std::numeric_limits<T>::is_signed) { + lua_pushinteger(L, mem_content); + } else { + lua_pushunsigned(L, mem_content); + } + + return 1; +} + +//------------------------------------------------- +// region_write - templated region writer for <sign>,<size> +// -> manager:machine():memory().region[":maincpu"]:write_u16(0xC000, 0xF00D) +//------------------------------------------------- + +template <typename T> +int lua_engine::lua_memory_region::l_region_write(lua_State *L) +{ + memory_region ®ion = luabridge::Stack<memory_region &>::get(L, 1); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected"); + offs_t address = lua_tounsigned(L, 2); + T val = lua_tounsigned(L, 3); + offs_t lowmask = region.bytewidth() - 1; + for(int i = 0; i < sizeof(T); i++) + { + int addr = region.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i; + if(addr >= region.bytes()) + continue; + if(region.endianness() == ENDIANNESS_BIG) + region.base()[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff; + else + region.base()[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff; + val >>= 8; + } + + return 0; +} + +luabridge::LuaRef lua_engine::l_addr_space_map(const address_space *space) +{ + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef map = luabridge::LuaRef::newTable(L); + + int i = 1; + for (address_map_entry &entry : space->map()->m_entrylist) + { + luabridge::LuaRef mapentry = luabridge::LuaRef::newTable(L); + mapentry["offset"] = space->address_to_byte(entry.m_addrstart) & space->bytemask(); + mapentry["endoff"] = space->address_to_byte(entry.m_addrend) & space->bytemask(); + mapentry["readtype"] = entry.m_read.m_type; + mapentry["writetype"] = entry.m_write.m_type; + map[i++] = mapentry; + } + return map; +} + +int lua_engine::lua_options_entry::l_entry_value(lua_State *L) +{ + core_options::entry *e = luabridge::Stack<core_options::entry *>::get(L, 1); + if(!e) { + return 0; + } + + luaL_argcheck(L, !lua_isfunction(L, 2), 2, "optional argument: unsupported value"); + + if (!lua_isnone(L, 2)) + { + std::string error; + // FIXME: not working with ui_options::entry + // TODO: optional arg for priority + luaThis->machine().options().set_value(e->name(), + lua_isboolean(L, 2) ? (lua_toboolean(L, 2) ? "1" : "0") : lua_tostring(L, 2), + OPTION_PRIORITY_CMDLINE, error); + + if (!error.empty()) + { + luaL_error(L, "%s", error.c_str()); + } + } + + switch (e->type()) + { + case OPTION_BOOLEAN: + lua_pushboolean(L, (atoi(e->value()) != 0)); + break; + case OPTION_INTEGER: + lua_pushnumber(L, atoi(e->value())); + break; + case OPTION_FLOAT: + lua_pushnumber(L, atof(e->value())); + break; + default: + lua_pushstring(L, e->value()); + break; + } + + return 1; +} + +//------------------------------------------------- +// begin_recording - start avi +// -> manager:machine():video():begin_recording() +//------------------------------------------------- + +int lua_engine::lua_video::l_begin_recording(lua_State *L) +{ + video_manager *vm = luabridge::Stack<video_manager *>::get(L, 1); + if (!vm) { + return 0; + } + + luaL_argcheck(L, lua_isstring(L, 2) || lua_isnone(L, 2), 2, "optional argument: filename, string expected"); + + const char *filename = lua_tostring(L, 2); + if (!lua_isnone(L, 2)) { + std::string vidname(filename); + strreplace(vidname, "/", PATH_SEPARATOR); + strreplace(vidname, "%g", luaThis->machine().basename()); + filename = vidname.c_str(); + } else { + filename = nullptr; + } + vm->begin_recording(filename, video_manager::MF_AVI); + + return 1; +} + +//------------------------------------------------- +// end_recording - start saving avi +// -> manager:machine():video():end_recording() +//------------------------------------------------- + +int lua_engine::lua_video::l_end_recording(lua_State *L) +{ + video_manager *vm = luabridge::Stack<video_manager *>::get(L, 1); + if (!vm) { + return 0; + } + + if (!vm->is_recording()) { + lua_writestringerror("%s", "No active recording to stop"); + return 0; + } + + vm->end_recording(video_manager::MF_AVI); + return 1; +} + +//------------------------------------------------- +// screen_height - return screen visible height +// -> manager:machine().screens[":screen"]:height() +//------------------------------------------------- + +int lua_engine::lua_screen::l_height(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc) { + return 0; + } + + lua_pushunsigned(L, sc->visible_area().height()); + return 1; +} + +//------------------------------------------------- +// screen_width - return screen visible width +// -> manager:machine().screens[":screen"]:width() +//------------------------------------------------- + +int lua_engine::lua_screen::l_width(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc) { + return 0; + } + + lua_pushunsigned(L, sc->visible_area().width()); + return 1; +} + + +//------------------------------------------------- +// screen_orientation - return screen orientation +// -> manager:machine().screens[":screen"]:orientation() +// -> rotation_angle (0, 90, 180, 270) +// -> flipx (true, false) +// -> flipy (true, false) +//------------------------------------------------- + +int lua_engine::lua_screen::l_orientation(lua_State *L) +{ + UINT32 flags = (luaThis->machine().system().flags & ORIENTATION_MASK); + + int rotation_angle = 0; + switch (flags) + { + case ORIENTATION_FLIP_X: + rotation_angle = 0; + break; + case ORIENTATION_SWAP_XY: + case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_X: + rotation_angle = 90; + break; + case ORIENTATION_FLIP_Y: + case ORIENTATION_FLIP_X|ORIENTATION_FLIP_Y: + rotation_angle = 180; + break; + case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_Y: + case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_X|ORIENTATION_FLIP_Y: + rotation_angle = 270; + break; + } + + lua_createtable(L, 2, 2); + lua_pushliteral(L, "rotation_angle"); + lua_pushinteger(L, rotation_angle); + + lua_settable(L, -3); + lua_pushliteral(L, "flipx"); + lua_pushboolean(L, (flags & ORIENTATION_FLIP_X)); + + lua_settable(L, -3); + lua_pushliteral(L, "flipy"); + lua_pushboolean(L, (flags & ORIENTATION_FLIP_Y)); + lua_settable(L, -3); + return 1; +} + +//------------------------------------------------- +// screen_refresh - return screen refresh rate +// -> manager:machine().screens[":screen"]:refresh() +//------------------------------------------------- + +int lua_engine::lua_screen::l_refresh(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc) { + return 0; + } + + lua_pushnumber(L, ATTOSECONDS_TO_HZ(sc->refresh_attoseconds())); + return 1; +} + +//------------------------------------------------- +// screen_snapshot - save png bitmap of screen to snapshots folder +// -> manager:machine().screens[":screen"]:snapshot("filename.png") +//------------------------------------------------- + +int lua_engine::lua_screen::l_snapshot(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc || !sc->machine().render().is_live(*sc)) + { + return 0; + } + + luaL_argcheck(L, lua_isstring(L, 2) || lua_isnone(L, 2), 2, "optional argument: filename, string expected"); + + emu_file file(sc->machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + osd_file::error filerr; + + if (!lua_isnone(L, 2)) { + const char *filename = lua_tostring(L, 2); + std::string snapstr(filename); + strreplace(snapstr, "/", PATH_SEPARATOR); + strreplace(snapstr, "%g", sc->machine().basename()); + filerr = file.open(snapstr.c_str()); + } + else + { + filerr = sc->machine().video().open_next(file, "png"); + } + + if (filerr != osd_file::error::NONE) + { + luaL_error(L, "osd_file::error=%d", filerr); + return 0; + } + + sc->machine().video().save_snapshot(sc, file); + lua_writestringerror("saved %s", file.fullpath()); + file.close(); + return 1; +} + +//------------------------------------------------- +// screen_type - return human readable screen type +// -> manager:machine().screens[":screen"]:type() +//------------------------------------------------- + +int lua_engine::lua_screen::l_type(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc) { + return 0; + } + + switch (sc->screen_type()) + { + case SCREEN_TYPE_RASTER: lua_pushliteral(L, "raster"); break; + case SCREEN_TYPE_VECTOR: lua_pushliteral(L, "vector"); break; + case SCREEN_TYPE_LCD: lua_pushliteral(L, "lcd"); break; + case SCREEN_TYPE_SVG: lua_pushliteral(L, "svg"); break; + default: lua_pushliteral(L, "unknown"); break; + } + + return 1; +} + +//------------------------------------------------- +// draw_box - draw a box on a screen container +// -> manager:machine().screens[":screen"]:draw_box(x1, y1, x2, y2, bgcolor, linecolor) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_box(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got 6 numerical parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 6), 6, "background color (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 7), 7, "outline color (integer) expected"); + + // retrieve all parameters + int sc_width = sc->visible_area().width(); + int sc_height = sc->visible_area().height(); + float x1, y1, x2, y2; + x1 = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast<float>(sc_width); + y1 = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast<float>(sc_height); + x2 = MIN(MAX(0, (float) lua_tonumber(L, 4)), sc_width-1) / static_cast<float>(sc_width); + y2 = MIN(MAX(0, (float) lua_tonumber(L, 5)), sc_height-1) / static_cast<float>(sc_height); + UINT32 bgcolor = lua_tounsigned(L, 6); + UINT32 fgcolor = lua_tounsigned(L, 7); + + // draw the box + render_container &rc = sc->container(); + mame_machine_manager::instance()->ui().draw_outlined_box(&rc, x1, y1, x2, y2, fgcolor, bgcolor); + + return 0; +} + +//------------------------------------------------- +// draw_line - draw a line on a screen container +// -> manager:machine().screens[":screen"]:draw_line(x1, y1, x2, y2, linecolor) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_line(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got 5 numerical parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 6), 6, "color (integer) expected"); + + // retrieve all parameters + int sc_width = sc->visible_area().width(); + int sc_height = sc->visible_area().height(); + float x1, y1, x2, y2; + x1 = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast<float>(sc_width); + y1 = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast<float>(sc_height); + x2 = MIN(MAX(0, (float) lua_tonumber(L, 4)), sc_width-1) / static_cast<float>(sc_width); + y2 = MIN(MAX(0, (float) lua_tonumber(L, 5)), sc_height-1) / static_cast<float>(sc_height); + UINT32 color = lua_tounsigned(L, 6); + + // draw the line + sc->container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + return 0; +} + +//------------------------------------------------- +// draw_text - draw text on a screen container +// if x is a position, then y is a pixel position, otherwise x and y are screen size relative +// -> manager:machine().screens[":screen"]:draw_text(x, y, message) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_text(lua_State *L) +{ + screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got proper parameters + luaL_argcheck(L, lua_isnumber(L, 2) || lua_isstring(L, 2), 2, "x (integer or string) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y (integer) expected"); + luaL_argcheck(L, lua_isstring(L, 4), 4, "message (string) expected"); + luaL_argcheck(L, lua_isinteger(L, 5) || lua_isnone(L, 5), 5, "optional argument: text color, integer expected (default: 0xffffffff)"); + + // retrieve all parameters + int sc_width = sc->visible_area().width(); + int sc_height = sc->visible_area().height(); + int justify = JUSTIFY_LEFT; + float y, x = 0; + if(lua_isnumber(L, 2)) + { + x = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast<float>(sc_width); + y = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast<float>(sc_height); + } + else + { + std::string just_str = lua_tostring(L, 2); + if(just_str == "right") + justify = JUSTIFY_RIGHT; + else if(just_str == "center") + justify = JUSTIFY_CENTER; + y = lua_tonumber(L, 3); + } + const char *msg = luaL_checkstring(L,4); + rgb_t textcolor = UI_TEXT_COLOR; + if (!lua_isnone(L, 5)) { + textcolor = rgb_t(lua_tounsigned(L, 5)); + } + + // draw the text + render_container &rc = sc->container(); + mame_machine_manager::instance()->ui().draw_text_full(&rc, msg, x, y, (1.0f - x), + justify, WRAP_WORD, DRAW_NORMAL, textcolor, + UI_TEXT_BG_COLOR, nullptr, nullptr); + return 0; +} + +int lua_engine::lua_emu_file::l_emu_file_read(lua_State *L) +{ + lua_emu_file *file = luabridge::Stack<lua_emu_file *>::get(L, 1); + luaL_argcheck(L, lua_isnumber(L, 2), 2, "length (integer) expected"); + int ret, len = lua_tonumber(L, 2); + luaL_Buffer buff; + char *ptr = luaL_buffinitsize(L, &buff, len); + ret = file->file.read(ptr, len); + luaL_pushresultsize(&buff, ret); + return 1; +} + +void *lua_engine::checkparam(lua_State *L, int idx, const char *tname) +{ + const char *name; + + if(!lua_getmetatable(L, idx)) + return nullptr; + + lua_rawget(L, LUA_REGISTRYINDEX); + name = lua_tostring(L, -1); + if(!name || strcmp(name, tname)) { + lua_pop(L, 1); + return nullptr; + } + lua_pop(L, 1); + + return *static_cast<void **>(lua_touserdata(L, idx)); +} + +void *lua_engine::getparam(lua_State *L, int idx, const char *tname) +{ + void *p = checkparam(L, idx, tname); + char msg[256]; + sprintf(msg, "%s expected", tname); + luaL_argcheck(L, p, idx, msg); + return p; +} + +void lua_engine::push(lua_State *L, void *p, const char *tname) +{ + void **pp = static_cast<void **>(lua_newuserdata(L, sizeof(void *))); + *pp = p; + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + +int lua_engine::l_emu_exit(lua_State *L) +{ + luaThis->machine().schedule_exit(); + return 1; +} + +int lua_engine::l_emu_start(lua_State *L) +{ + const char *system_name = luaL_checkstring(L,1); + + int index = driver_list::find(system_name); + if (index != -1) { + mame_machine_manager::instance()->schedule_new_driver(driver_list::driver(index)); + luaThis->machine().schedule_hard_reset(); + } + return 1; +} + +int lua_engine::luaopen_ioport(lua_State *L) +{ + static const struct luaL_Reg ioport_funcs [] = { + { "write", l_ioport_write }, + { nullptr, nullptr } /* sentinel */ + }; + + luaL_newmetatable(L, tname_ioport); + lua_pushvalue(L, -1); + lua_pushstring(L, tname_ioport); + lua_rawset(L, LUA_REGISTRYINDEX); + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); + lua_settable(L, -3); + luaL_setfuncs(L, ioport_funcs, 0); + return 1; +} + +struct msg { + std::string text; + int ready; + std::string response; + int status; + int done; +} msg; + +static std::mutex g_mutex; + +void lua_engine::serve_lua() +{ + osd_sleep(osd_ticks_per_second() / 1000 * 50); + printf("%s v%s\n%s\n%s\n\n", emulator_info::get_appname(),build_version,emulator_info::get_copyright_info(),LUA_COPYRIGHT); + fflush(stdout); + char buff[LUA_MAXINPUT]; + std::string oldbuff; + + const char *b = LUA_PROMPT; + + do { + // Wait for input + fputs(b, stdout); fflush(stdout); /* show prompt */ + fgets(buff, LUA_MAXINPUT, stdin); + + // Create message + { + std::lock_guard<std::mutex> lock(g_mutex); + if (msg.ready == 0) { + msg.text = oldbuff; + if (oldbuff.length() != 0) msg.text.append("\n"); + msg.text.append(buff); + msg.ready = 1; + msg.done = 0; + } + } + + // Wait for response + int done; + do { + osd_sleep(osd_ticks_per_second() / 1000); + std::lock_guard<std::mutex> lock(g_mutex); + done = msg.done; + } while (done==0); + + // Do action on client side + { + std::lock_guard<std::mutex> lock(g_mutex); + + if (msg.status == -1) { + b = LUA_PROMPT2; + oldbuff = msg.response; + } + else { + b = LUA_PROMPT; + oldbuff = ""; + } + msg.done = 0; + } + + } while (1); +} + +static void *serve_lua(void *param) +{ + lua_engine *engine = (lua_engine *)param; + engine->serve_lua(); + return NULL; +} + +//------------------------------------------------- +// lua_engine - constructor +//------------------------------------------------- + +lua_engine::lua_engine() +{ + m_machine = nullptr; + luaThis = this; + m_lua_state = luaL_newstate(); /* create state */ + output_notifier_set = false; + + luaL_checkversion(m_lua_state); + lua_gc(m_lua_state, LUA_GCSTOP, 0); /* stop collector during initialization */ + luaL_openlibs(m_lua_state); /* open libraries */ + + // Get package.preload so we can store builtins in it. + lua_getglobal(m_lua_state, "package"); + lua_getfield(m_lua_state, -1, "preload"); + lua_remove(m_lua_state, -2); // Remove package + + lua_pushcfunction(m_lua_state, luaopen_zlib); + lua_setfield(m_lua_state, -2, "zlib"); + + lua_pushcfunction(m_lua_state, luaopen_lsqlite3); + lua_setfield(m_lua_state, -2, "lsqlite3"); + + lua_pushcfunction(m_lua_state, luaopen_lfs); + lua_setfield(m_lua_state, -2, "lfs"); + + luaopen_ioport(m_lua_state); + + lua_gc(m_lua_state, LUA_GCRESTART, 0); + msg.ready = 0; + msg.status = 0; + msg.done = 0; +} + +//------------------------------------------------- +// ~lua_engine - destructor +//------------------------------------------------- + +lua_engine::~lua_engine() +{ + close(); +} + +int lua_engine::compile_with_env(const char *envname, const char *script, const char *env) +{ + std::string field = std::string("env_").append(envname); + int error; + lua_settop(m_lua_state, 0); + lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str()); + + if(!lua_istable(m_lua_state, -1)) + { + lua_newtable(m_lua_state); + lua_setfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str()); + lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str()); + } + + // optionally load a string to prepare the environment + if(env) + { + error = luaL_loadstring(m_lua_state, env); + if(error == LUA_OK) + lua_pushvalue(m_lua_state, -2); + if((error != LUA_OK) || ((error = lua_pcall(m_lua_state, 1, 0, 0)) != LUA_OK)) + { + if((error == LUA_ERRSYNTAX) || (error == LUA_ERRRUN)) + printf("%s\n", lua_tostring(m_lua_state, -1)); + lua_pop(m_lua_state, 1); + } + lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str()); + } + + if((error = luaL_loadstring(m_lua_state, script)) != LUA_OK) + { + if(error == LUA_ERRSYNTAX) + printf("%s\n", lua_tostring(m_lua_state, -1)); + lua_tostring(m_lua_state, -1); + lua_pop(m_lua_state, 2); + return -1; + } + lua_pushvalue(m_lua_state, -2); + lua_setupvalue(m_lua_state, -2, 1); + int ref = luaL_ref(m_lua_state, -2); + lua_pop(m_lua_state, 1); + return ref; +} + +template <typename Tout, typename Tin> +Tout lua_engine::run(const char *env, int ref, Tin in) +{ + Tout ret; + lua_settop(m_lua_state, 0); + luabridge::Stack<Tin>::push(m_lua_state, in); + run_internal(env, ref); + if(lua_isnil(m_lua_state, 1)) + ret = Tout(0); + else + ret = luabridge::Stack<Tout>::get(m_lua_state, 1); + lua_pop(m_lua_state, 1); + return ret; +} + +template <typename Tout> +Tout lua_engine::run(const char *env, int ref) +{ + Tout ret; + lua_settop(m_lua_state, 0); + lua_pushnil(m_lua_state); + run_internal(env, ref); + if(lua_isnil(m_lua_state, 1)) + ret = Tout(0); + else + ret = luabridge::Stack<Tout>::get(m_lua_state, 1); + lua_pop(m_lua_state, 1); + return ret; +} + +template <typename Tin> +void lua_engine::run(const char *env, int ref, Tin in) +{ + lua_settop(m_lua_state, 0); + luabridge::Stack<Tin>::push(m_lua_state, in); + run_internal(env, ref); + lua_pop(m_lua_state, 1); +} + +void lua_engine::run(const char *env, int ref) +{ + lua_settop(m_lua_state, 0); + lua_pushnil(m_lua_state); + run_internal(env, ref); + lua_pop(m_lua_state, 1); +} +// create specialization so luabridge doesn't have to be included everywhere +template int lua_engine::run<int>(const char *env, int ref); + +void lua_engine::run_internal(const char *env, int ref) +{ + std::string field = std::string("env_").append(env); + lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str()); + if(lua_istable(m_lua_state, -1)) + { + lua_rawgeti(m_lua_state, -1, ref); + if(lua_isfunction(m_lua_state, -1)) + { + lua_pushvalue(m_lua_state, -3); + int error; + if((error = lua_pcall(m_lua_state, 1, 1, 0)) != LUA_OK) + { + if(error == LUA_ERRRUN) + printf("%s\n", lua_tostring(m_lua_state, -1)); + lua_pop(m_lua_state, 1); + lua_pushnil(m_lua_state); + } + } + } + lua_replace(m_lua_state, 1); + lua_pop(m_lua_state, 1); +} + +void lua_engine::menu_populate(std::string &menu, std::vector<menu_item> &menu_list) +{ + std::string field = "menu_pop_" + menu; + lua_settop(m_lua_state, 0); + lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str()); + + if(!lua_isfunction(m_lua_state, -1)) + { + lua_pop(m_lua_state, 1); + return; + } + int error; + if((error = lua_pcall(m_lua_state, 0, 1, 0)) != LUA_OK) + { + if(error == LUA_ERRRUN) + printf("%s\n", lua_tostring(m_lua_state, -1)); + lua_pop(m_lua_state, 1); + return; + } + if(!lua_istable(m_lua_state, -1)) + { + lua_pop(m_lua_state, 1); + return; + } + + lua_pushnil(m_lua_state); + while(lua_next(m_lua_state, -2)) + { + if(lua_istable(m_lua_state, -1)) + { + menu_item item; + lua_rawgeti(m_lua_state, -1, 1); + item.text = lua_tostring(m_lua_state, -1); + lua_pop(m_lua_state, 1); + lua_rawgeti(m_lua_state, -1, 2); + item.subtext = lua_tostring(m_lua_state, -1); + lua_pop(m_lua_state, 1); + lua_rawgeti(m_lua_state, -1, 3); + item.flags = lua_tostring(m_lua_state, -1); + lua_pop(m_lua_state, 1); + menu_list.push_back(item); + } + lua_pop(m_lua_state, 1); + } + lua_pop(m_lua_state, 1); +} + +bool lua_engine::menu_callback(std::string &menu, int index, std::string event) +{ + std::string field = "menu_cb_" + menu; + bool ret = false; + lua_settop(m_lua_state, 0); + lua_getfield(m_lua_state, LUA_REGISTRYINDEX, field.c_str()); + + if(lua_isfunction(m_lua_state, -1)) + { + lua_pushinteger(m_lua_state, index); + lua_pushstring(m_lua_state, event.c_str()); + int error; + if((error = lua_pcall(m_lua_state, 2, 1, 0)) != LUA_OK) + { + if(error == 2) + printf("%s\n", lua_tostring(m_lua_state, -1)); + lua_pop(m_lua_state, 1); + return false; + } + ret = lua_toboolean(m_lua_state, -1); + lua_pop(m_lua_state, 1); + } + return ret; +} + +int lua_engine::l_emu_register_menu(lua_State *L) +{ + luaL_argcheck(L, lua_isfunction(L, 1), 1, "callback function expected"); + luaL_argcheck(L, lua_isfunction(L, 2), 2, "callback function expected"); + luaL_argcheck(L, lua_isstring(L, 3), 3, "name (string) expected"); + std::string name = luaL_checkstring(L, 3); + std::string cbfield = "menu_cb_" + name; + std::string popfield = "menu_pop_" + name; + luaThis->m_menu.push_back(std::string(name)); + lua_pushvalue(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, cbfield.c_str()); + lua_pushvalue(L, 2); + lua_setfield(L, LUA_REGISTRYINDEX, popfield.c_str()); + return 1; +} + +void lua_engine::execute_function(const char *id) +{ + lua_settop(m_lua_state, 0); + lua_getfield(m_lua_state, LUA_REGISTRYINDEX, id); + + if (lua_istable(m_lua_state, -1)) + { + lua_pushnil(m_lua_state); + while (lua_next(m_lua_state, -2) != 0) + { + if (lua_isfunction(m_lua_state, -1)) + { + int error; + if((error = lua_pcall(m_lua_state, 0, 0, 0)) != LUA_OK) + { + if(error == 2) + printf("%s\n", lua_tostring(m_lua_state, -1)); + lua_pop(m_lua_state, 1); + } + } + else + { + lua_pop(m_lua_state, 1); + } + } + } +} + +int lua_engine::register_function(lua_State *L, const char *id) +{ + if (!lua_isnil(L, 1)) + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, id); + if (lua_isnil(L, -1)) + { + lua_newtable(L); + } + luaL_checktype(L, -1, LUA_TTABLE); + int len = lua_rawlen(L, -1); + lua_pushnumber(L, len + 1); + lua_pushvalue(L, 1); + lua_rawset(L, -3); /* Stores the pair in the table */ + + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, id); + return 1; +} + +int lua_engine::l_emu_register_prestart(lua_State *L) +{ + return register_function(L, "LUA_ON_PRESTART"); +} + +int lua_engine::l_emu_register_start(lua_State *L) +{ + return register_function(L, "LUA_ON_START"); +} + +int lua_engine::l_emu_register_stop(lua_State *L) +{ + return register_function(L, "LUA_ON_STOP"); +} + +int lua_engine::l_emu_register_pause(lua_State *L) +{ + return register_function(L, "LUA_ON_PAUSE"); +} + +int lua_engine::l_emu_register_resume(lua_State *L) +{ + return register_function(L, "LUA_ON_RESUME"); +} + +int lua_engine::l_emu_register_frame(lua_State *L) +{ + return register_function(L, "LUA_ON_FRAME"); +} + +int lua_engine::l_emu_register_frame_done(lua_State *L) +{ + return register_function(L, "LUA_ON_FRAME_DONE"); +} + +void lua_engine::on_machine_prestart() +{ + execute_function("LUA_ON_PRESTART"); +} + +void lua_engine::on_machine_start() +{ + execute_function("LUA_ON_START"); +} + +void lua_engine::on_machine_stop() +{ + execute_function("LUA_ON_STOP"); +} + +void lua_engine::on_machine_pause() +{ + execute_function("LUA_ON_PAUSE"); +} + +void lua_engine::on_machine_resume() +{ + execute_function("LUA_ON_RESUME"); +} + +void lua_engine::on_machine_frame() +{ + execute_function("LUA_ON_FRAME"); +} + +void lua_engine::on_frame_done() +{ + execute_function("LUA_ON_FRAME_DONE"); +} + +void lua_engine::update_machine() +{ + lua_newtable(m_lua_state); + if (m_machine!=nullptr) + { + // Create the ioport array + for (ioport_port &port : machine().ioport().ports()) + { + for (ioport_field &field : port.fields()) + { + if (field.name()) + { + push(m_lua_state, &field, tname_ioport); + lua_setfield(m_lua_state, -2, field.name()); + } + } + } + } + lua_setglobal(m_lua_state, "ioport"); +} + +void lua_engine::attach_notifiers() +{ + machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(FUNC(lua_engine::on_machine_prestart), this), true); + machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(FUNC(lua_engine::on_machine_start), this)); + machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(lua_engine::on_machine_stop), this)); + machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(FUNC(lua_engine::on_machine_pause), this)); + machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(FUNC(lua_engine::on_machine_resume), this)); + machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(FUNC(lua_engine::on_machine_frame), this)); +} + +int lua_engine::lua_machine::l_popmessage(lua_State *L) +{ + running_machine *m = luabridge::Stack<running_machine *>::get(L, 1); + if(!lua_isstring(L, 2)) + m->popmessage(); + else + m->popmessage("%s", luaL_checkstring(L, 2)); + return 0; +} + +int lua_engine::lua_machine::l_logerror(lua_State *L) +{ + running_machine *m = luabridge::Stack<running_machine *>::get(L, 1); + luaL_argcheck(L, lua_isstring(L, 2), 2, "message (string) expected"); + m->logerror("[luaengine] %s\n", luaL_checkstring(L, 2)); + return 0; +} + +std::string lua_engine::get_print_buffer(lua_State *L) +{ + int nargs = lua_gettop(L); + + const std::string sep = " "; + + std::ostringstream ss; + bool first = true; + + for (int i = 1; i <= nargs; i++) { + const char* c = lua_tostring(L, i); + const std::string str = c ? c : "<nil>"; + if (first) first = false; + else ss << sep; + ss << str; + } + + return ss.str(); +} +int lua_engine::l_osd_printf_verbose(lua_State *L) +{ + osd_printf_verbose("%s\n",get_print_buffer(L).c_str()); + return 0; +} + +int lua_engine::l_osd_printf_error(lua_State *L) +{ + osd_printf_error("%s\n",get_print_buffer(L).c_str()); + return 0; +} + +int lua_engine::l_osd_printf_info(lua_State *L) +{ + osd_printf_info("%s\n",get_print_buffer(L).c_str()); + return 0; +} + +int lua_engine::l_osd_printf_debug(lua_State *L) +{ + osd_printf_debug("%s\n",get_print_buffer(L).c_str()); + return 0; +} + +//------------------------------------------------- +// initialize - initialize lua hookup to emu engine +//------------------------------------------------- + +void lua_engine::initialize() +{ + luabridge::getGlobalNamespace (m_lua_state) + .beginNamespace ("emu") + .addCFunction ("app_name", l_emu_app_name ) + .addCFunction ("app_version", l_emu_app_version ) + .addCFunction ("gamename", l_emu_gamename ) + .addCFunction ("romname", l_emu_romname ) + .addCFunction ("softname", l_emu_softname ) + .addCFunction ("keypost", l_emu_keypost ) + .addCFunction ("hook_output", l_emu_hook_output ) + .addCFunction ("sethook", l_emu_set_hook ) + .addCFunction ("time", l_emu_time ) + .addCFunction ("wait", l_emu_wait ) + .addCFunction ("after", l_emu_after ) + .addCFunction ("exit", l_emu_exit ) + .addCFunction ("start", l_emu_start ) + .addCFunction ("pause", l_emu_pause ) + .addCFunction ("unpause", l_emu_unpause ) + .addCFunction ("register_prestart", l_emu_register_prestart ) + .addCFunction ("register_start", l_emu_register_start ) + .addCFunction ("register_stop", l_emu_register_stop ) + .addCFunction ("register_pause", l_emu_register_pause ) + .addCFunction ("register_resume",l_emu_register_resume ) + .addCFunction ("register_frame", l_emu_register_frame ) + .addCFunction ("register_frame_done", l_emu_register_frame_done ) + .addCFunction ("register_menu", l_emu_register_menu ) + .addCFunction ("print_verbose", l_osd_printf_verbose ) + .addCFunction ("print_error", l_osd_printf_error ) + .addCFunction ("print_info", l_osd_printf_info ) + .addCFunction ("print_debug", l_osd_printf_debug ) + .beginClass <mame_machine_manager> ("manager") + .addFunction ("machine", &machine_manager::machine) + .addFunction ("options", &machine_manager::options) + .addFunction ("plugins", &mame_machine_manager::plugins) + .endClass () + .beginClass <lua_machine> ("lua_machine") + .addCFunction ("popmessage", &lua_machine::l_popmessage) + .addCFunction ("logerror", &lua_machine::l_logerror) + .endClass () + .deriveClass <running_machine, lua_machine> ("machine") + .addFunction ("exit", &running_machine::schedule_exit) + .addFunction ("hard_reset", &running_machine::schedule_hard_reset) + .addFunction ("soft_reset", &running_machine::schedule_soft_reset) + .addFunction ("save", &running_machine::schedule_save) + .addFunction ("load", &running_machine::schedule_load) + .addFunction ("system", &running_machine::system) + .addFunction ("video", &running_machine::video) + .addFunction ("ui", &running_machine::ui) + .addFunction ("render", &running_machine::render) + .addFunction ("ioport", &running_machine::ioport) + .addFunction ("parameters", &running_machine::parameters) + .addFunction ("cheat", &mame_machine_manager::cheat) + .addFunction ("memory", &running_machine::memory) + .addFunction ("options", &running_machine::options) + .addFunction ("outputs", &running_machine::output) + .addProperty <luabridge::LuaRef, void> ("devices", &lua_engine::l_machine_get_devices) + .addProperty <luabridge::LuaRef, void> ("screens", &lua_engine::l_machine_get_screens) + .addProperty <luabridge::LuaRef, void> ("images", &lua_engine::l_machine_get_images) + .endClass () + .beginClass <game_driver> ("game_driver") + .addData ("source_file", &game_driver::source_file) + .addData ("parent", &game_driver::parent) + .addData ("name", &game_driver::name) + .addData ("description", &game_driver::description) + .addData ("year", &game_driver::year) + .addData ("manufacturer", &game_driver::manufacturer) + .addData ("compatible_with", &game_driver::compatible_with) + .addData ("default_layout", &game_driver::default_layout) + .endClass () + .beginClass <device_t> ("device") + .addFunction ("name", &device_t::name) + .addFunction ("shortname", &device_t::shortname) + .addFunction ("tag", &device_t::tag) + .addFunction ("owner", &device_t::owner) + .addProperty <luabridge::LuaRef, void> ("spaces", &lua_engine::l_dev_get_memspaces) + .addProperty <luabridge::LuaRef, void> ("state", &lua_engine::l_dev_get_states) + .addProperty <luabridge::LuaRef, void> ("items", &lua_engine::l_dev_get_items) + .endClass() + .beginClass <cheat_manager> ("cheat") + .addProperty <bool, bool> ("enabled", &cheat_manager::enabled, &cheat_manager::set_enable) + .addFunction ("reload", &cheat_manager::reload) + .addFunction ("save_all", &cheat_manager::save_all) + .addProperty <luabridge::LuaRef, void> ("entries", &lua_engine::l_cheat_get_entries) + .endClass() + .beginClass <lua_cheat_entry> ("lua_cheat_entry") + .addCFunction ("state", &lua_cheat_entry::l_get_state) + .endClass() + .deriveClass <cheat_entry, lua_cheat_entry> ("cheat_entry") + .addFunction ("description", &cheat_entry::description) + .addFunction ("comment", &cheat_entry::comment) + .addFunction ("has_run_script", &cheat_entry::has_run_script) + .addFunction ("has_on_script", &cheat_entry::has_on_script) + .addFunction ("has_off_script", &cheat_entry::has_off_script) + .addFunction ("has_change_script", &cheat_entry::has_change_script) + .addFunction ("execute_off_script", &cheat_entry::execute_off_script) + .addFunction ("execute_on_script", &cheat_entry::execute_on_script) + .addFunction ("execute_run_script", &cheat_entry::execute_run_script) + .addFunction ("execute_change_script", &cheat_entry::execute_change_script) + .addFunction ("is_text_only", &cheat_entry::is_text_only) + .addFunction ("is_oneshot", &cheat_entry::is_oneshot) + .addFunction ("is_onoff", &cheat_entry::is_onoff) + .addFunction ("is_value_parameter", &cheat_entry::is_value_parameter) + .addFunction ("is_itemlist_parameter", &cheat_entry::is_itemlist_parameter) + .addFunction ("is_oneshot_parameter", &cheat_entry::is_oneshot_parameter) + .addFunction ("activate", &cheat_entry::activate) + .addFunction ("select_default_state", &cheat_entry::select_default_state) + .addFunction ("select_previous_state", &cheat_entry::select_previous_state) + .addFunction ("select_next_state", &cheat_entry::select_next_state) + .endClass() + .beginClass <ioport_manager> ("ioport") + .addFunction ("has_configs", &ioport_manager::has_configs) + .addFunction ("has_analog", &ioport_manager::has_analog) + .addFunction ("has_dips", &ioport_manager::has_dips) + .addFunction ("has_bioses", &ioport_manager::has_bioses) + .addFunction ("has_keyboard", &ioport_manager::has_keyboard) + .addFunction ("count_players", &ioport_manager::count_players) + .addProperty <luabridge::LuaRef, void> ("ports", &lua_engine::l_ioport_get_ports) + .endClass() + .beginClass <ioport_port> ("ioport_port") + .addFunction ("tag", &ioport_port::tag) + .addFunction ("active", &ioport_port::active) + .addFunction ("live", &ioport_port::live) + .addFunction ("read", &ioport_port::read) + .addFunction ("write", &ioport_port::write) + .addFunction ("field", &ioport_port::field) + .addProperty <luabridge::LuaRef, void> ("fields", &lua_engine::l_ioports_port_get_fields) + .endClass() + .beginClass <ioport_field> ("ioport_field") + .addFunction ("set_value", &ioport_field::set_value) + .addProperty ("device", &ioport_field::device) + .addProperty ("name", &ioport_field::name) + .addProperty <UINT8, UINT8> ("player", &ioport_field::player, &ioport_field::set_player) + .addProperty ("mask", &ioport_field::mask) + .addProperty ("defvalue", &ioport_field::defvalue) + .addProperty ("sensitivity", &ioport_field::sensitivity) + .addProperty ("way", &ioport_field::way) + .addProperty ("is_analog", &ioport_field::is_analog) + .addProperty ("is_digitial_joystick", &ioport_field::is_digital_joystick) + .addProperty ("enabled", &ioport_field::enabled) + .addProperty ("unused", &ioport_field::unused) + .addProperty ("cocktail", &ioport_field::cocktail) + .addProperty ("toggle", &ioport_field::toggle) + .addProperty ("rotated", &ioport_field::rotated) + .addProperty ("analog_reverse", &ioport_field::analog_reverse) + .addProperty ("analog_reset", &ioport_field::analog_reset) + .addProperty ("analog_wraps", &ioport_field::analog_wraps) + .addProperty ("analog_invert", &ioport_field::analog_invert) + .addProperty ("impulse", &ioport_field::impulse) + .addProperty <double, double> ("crosshair_scale", &ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale) + .addProperty <double, double> ("crosshair_offset", &ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset) + .endClass() + .beginClass <core_options> ("core_options") + .addFunction ("help", &core_options::output_help) + .addFunction ("command", &core_options::command) + .addProperty <luabridge::LuaRef, void> ("entries", &lua_engine::l_options_get_entries) + .endClass() + .beginClass <lua_options_entry> ("lua_options_entry") + .addCFunction ("value", &lua_options_entry::l_entry_value) + .endClass() + .deriveClass <core_options::entry, lua_options_entry> ("core_options_entry") + .addFunction ("description", &core_options::entry::description) + .addFunction ("default_value", &core_options::entry::default_value) + .addFunction ("minimum", &core_options::entry::minimum) + .addFunction ("maximum", &core_options::entry::maximum) + .addFunction ("has_range", &core_options::entry::has_range) + .endClass() + .deriveClass <emu_options, core_options> ("emu_options") + .endClass() + .deriveClass <ui_options, core_options> ("ui_options") + .endClass() + .deriveClass <plugin_options, core_options> ("plugin_options") + .endClass() + .beginClass <parameters_manager> ("parameters") + .addFunction ("add", ¶meters_manager::add) + .addFunction ("lookup", ¶meters_manager::lookup) + .endClass() + .beginClass <lua_video> ("lua_video_manager") + .addCFunction ("begin_recording", &lua_video::l_begin_recording) + .addCFunction ("end_recording", &lua_video::l_end_recording) + .endClass() + .deriveClass <video_manager, lua_video> ("video") + .addFunction ("snapshot", &video_manager::save_active_screen_snapshots) + .addFunction ("is_recording", &video_manager::is_recording) + .addFunction ("skip_this_frame", &video_manager::skip_this_frame) + .addFunction ("speed_factor", &video_manager::speed_factor) + .addFunction ("speed_percent", &video_manager::speed_percent) + .addProperty <int, int> ("frameskip", &video_manager::frameskip, &video_manager::set_frameskip) + .addProperty <bool, bool> ("throttled", &video_manager::throttled, &video_manager::set_throttled) + .addProperty <float, float> ("throttle_rate", &video_manager::throttle_rate, &video_manager::set_throttle_rate) + .endClass() + .beginClass <lua_addr_space> ("lua_addr_space") + .addCFunction ("read_i8", &lua_addr_space::l_mem_read<INT8>) + .addCFunction ("read_u8", &lua_addr_space::l_mem_read<UINT8>) + .addCFunction ("read_i16", &lua_addr_space::l_mem_read<INT16>) + .addCFunction ("read_u16", &lua_addr_space::l_mem_read<UINT16>) + .addCFunction ("read_i32", &lua_addr_space::l_mem_read<INT32>) + .addCFunction ("read_u32", &lua_addr_space::l_mem_read<UINT32>) + .addCFunction ("read_i64", &lua_addr_space::l_mem_read<INT64>) + .addCFunction ("read_u64", &lua_addr_space::l_mem_read<UINT64>) + .addCFunction ("write_i8", &lua_addr_space::l_mem_write<INT8>) + .addCFunction ("write_u8", &lua_addr_space::l_mem_write<UINT8>) + .addCFunction ("write_i16", &lua_addr_space::l_mem_write<INT16>) + .addCFunction ("write_u16", &lua_addr_space::l_mem_write<UINT16>) + .addCFunction ("write_i32", &lua_addr_space::l_mem_write<INT32>) + .addCFunction ("write_u32", &lua_addr_space::l_mem_write<UINT32>) + .addCFunction ("write_i64", &lua_addr_space::l_mem_write<INT64>) + .addCFunction ("write_u64", &lua_addr_space::l_mem_write<UINT64>) + .addCFunction ("read_direct_i8", &lua_addr_space::l_direct_mem_read<INT8>) + .addCFunction ("read_direct_u8", &lua_addr_space::l_direct_mem_read<UINT8>) + .addCFunction ("read_direct_i16", &lua_addr_space::l_direct_mem_read<INT16>) + .addCFunction ("read_direct_u16", &lua_addr_space::l_direct_mem_read<UINT16>) + .addCFunction ("read_direct_i32", &lua_addr_space::l_direct_mem_read<INT32>) + .addCFunction ("read_direct_u32", &lua_addr_space::l_direct_mem_read<UINT32>) + .addCFunction ("read_direct_i64", &lua_addr_space::l_direct_mem_read<INT64>) + .addCFunction ("read_direct_u64", &lua_addr_space::l_direct_mem_read<UINT64>) + .addCFunction ("write_direct_i8", &lua_addr_space::l_direct_mem_write<INT8>) + .addCFunction ("write_direct_u8", &lua_addr_space::l_direct_mem_write<UINT8>) + .addCFunction ("write_direct_i16", &lua_addr_space::l_direct_mem_write<INT16>) + .addCFunction ("write_direct_u16", &lua_addr_space::l_direct_mem_write<UINT16>) + .addCFunction ("write_direct_i32", &lua_addr_space::l_direct_mem_write<INT32>) + .addCFunction ("write_direct_u32", &lua_addr_space::l_direct_mem_write<UINT32>) + .addCFunction ("write_direct_i64", &lua_addr_space::l_direct_mem_write<INT64>) + .addCFunction ("write_direct_u64", &lua_addr_space::l_direct_mem_write<UINT64>) + .endClass() + .deriveClass <address_space, lua_addr_space> ("addr_space") + .addFunction("name", &address_space::name) + .addProperty <luabridge::LuaRef, void> ("map", &lua_engine::l_addr_space_map) + .endClass() + .beginClass <render_target> ("target") + .addFunction ("width", &render_target::width) + .addFunction ("height", &render_target::height) + .addFunction ("pixel_aspect", &render_target::pixel_aspect) + .addFunction ("hidden", &render_target::hidden) + .addFunction ("is_ui_target", &render_target::is_ui_target) + .addFunction ("index", &render_target::index) + .addProperty <float, float> ("max_update_rate", &render_target::max_update_rate, &render_target::set_max_update_rate) + .addProperty <int, int> ("view", &render_target::view, &render_target::set_view) + .addProperty <int, int> ("orientation", &render_target::orientation, &render_target::set_orientation) + .addProperty <bool, bool> ("backdrops", &render_target::backdrops_enabled, &render_target::set_backdrops_enabled) + .addProperty <bool, bool> ("overlays", &render_target::overlays_enabled, &render_target::set_overlays_enabled) + .addProperty <bool, bool> ("bezels", &render_target::bezels_enabled, &render_target::set_bezels_enabled) + .addProperty <bool, bool> ("marquees", &render_target::marquees_enabled, &render_target::set_marquees_enabled) + .addProperty <bool, bool> ("screen_overlay", &render_target::screen_overlay_enabled, &render_target::set_screen_overlay_enabled) + .addProperty <bool, bool> ("zoom", &render_target::zoom_to_screen, &render_target::set_zoom_to_screen) + .endClass() + .beginClass <render_container> ("render_container") + .addFunction ("orientation", &render_container::orientation) + .addFunction ("xscale", &render_container::xscale) + .addFunction ("yscale", &render_container::yscale) + .addFunction ("xoffset", &render_container::xoffset) + .addFunction ("yoffset", &render_container::yoffset) + .addFunction ("is_empty", &render_container::is_empty) + .endClass() + .beginClass <render_manager> ("render") + .addFunction ("max_update_rate", &render_manager::max_update_rate) + .addFunction ("ui_target", &render_manager::ui_target) + .addFunction ("ui_container", &render_manager::ui_container) + .addProperty <luabridge::LuaRef, void> ("targets", &lua_engine::l_render_get_targets) + .endClass() + .beginClass <mame_ui_manager> ("ui") + .addFunction ("is_menu_active", &mame_ui_manager::is_menu_active) + .addFunction ("options", &mame_ui_manager::options) + .addProperty <bool, bool> ("show_fps", &mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps) + .addProperty <bool, bool> ("show_profiler", &mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler) + .addProperty <bool, bool> ("single_step", &mame_ui_manager::single_step, &mame_ui_manager::set_single_step) + .addFunction ("get_line_height", &mame_ui_manager::get_line_height) + .endClass() + .beginClass <lua_screen> ("lua_screen_dev") + .addCFunction ("draw_box", &lua_screen::l_draw_box) + .addCFunction ("draw_line", &lua_screen::l_draw_line) + .addCFunction ("draw_text", &lua_screen::l_draw_text) + .addCFunction ("height", &lua_screen::l_height) + .addCFunction ("width", &lua_screen::l_width) + .addCFunction ("orientation", &lua_screen::l_orientation) + .addCFunction ("refresh", &lua_screen::l_refresh) + .addCFunction ("snapshot", &lua_screen::l_snapshot) + .addCFunction ("type", &lua_screen::l_type) + .endClass() + .deriveClass <screen_device, lua_screen> ("screen_dev") + .addFunction ("frame_number", &screen_device::frame_number) + .addFunction ("name", &screen_device::name) + .addFunction ("shortname", &screen_device::shortname) + .addFunction ("tag", &screen_device::tag) + .addFunction ("xscale", &screen_device::xscale) + .addFunction ("yscale", &screen_device::yscale) + .endClass() + .beginClass <device_state_entry> ("dev_space") + .addFunction ("name", &device_state_entry::symbol) + .addProperty <UINT64, UINT64> ("value", &lua_engine::l_state_get_value, &lua_engine::l_state_set_value) + .addFunction ("is_visible", &device_state_entry::visible) + .addFunction ("is_divider", &device_state_entry::divider) + .endClass() + .beginClass <memory_manager> ("memory") + .addProperty <luabridge::LuaRef, void> ("banks", &lua_engine::l_memory_get_banks) + .addProperty <luabridge::LuaRef, void> ("regions", &lua_engine::l_memory_get_regions) + .endClass() + .beginClass <lua_memory_region> ("lua_region") + .addCFunction ("read_i8", &lua_memory_region::l_region_read<INT8>) + .addCFunction ("read_u8", &lua_memory_region::l_region_read<UINT8>) + .addCFunction ("read_i16", &lua_memory_region::l_region_read<INT16>) + .addCFunction ("read_u16", &lua_memory_region::l_region_read<UINT16>) + .addCFunction ("read_i32", &lua_memory_region::l_region_read<INT32>) + .addCFunction ("read_u32", &lua_memory_region::l_region_read<UINT32>) + .addCFunction ("read_i64", &lua_memory_region::l_region_read<INT64>) + .addCFunction ("read_u64", &lua_memory_region::l_region_read<UINT64>) + .addCFunction ("write_i8", &lua_memory_region::l_region_write<INT8>) + .addCFunction ("write_u8", &lua_memory_region::l_region_write<UINT8>) + .addCFunction ("write_i16", &lua_memory_region::l_region_write<INT16>) + .addCFunction ("write_u16", &lua_memory_region::l_region_write<UINT16>) + .addCFunction ("write_i32", &lua_memory_region::l_region_write<INT32>) + .addCFunction ("write_u32", &lua_memory_region::l_region_write<UINT32>) + .addCFunction ("write_i64", &lua_memory_region::l_region_write<INT64>) + .addCFunction ("write_u64", &lua_memory_region::l_region_write<UINT64>) + .endClass() + .beginClass <output_manager> ("output") + .addFunction ("set_value", &output_manager::set_value) + .addFunction ("set_indexed_value", &output_manager::set_indexed_value) + .addFunction ("get_value", &output_manager::get_value) + .addFunction ("get_indexed_value", &output_manager::get_indexed_value) + .addFunction ("name_to_id", &output_manager::name_to_id) + .addFunction ("id_to_name", &output_manager::id_to_name) + .endClass() + .deriveClass <memory_region, lua_memory_region> ("region") + .addProperty <UINT32> ("size", &memory_region::bytes) + .endClass() + .beginClass <device_image_interface> ("image") + .addFunction ("exists", &device_image_interface::exists) + .addFunction ("filename", &device_image_interface::filename) + .addFunction ("longname", &device_image_interface::longname) + .addFunction ("manufacturer", &device_image_interface::manufacturer) + .addFunction ("year", &device_image_interface::year) + .addFunction ("software_list_name", &device_image_interface::software_list_name) + .addFunction ("image_type_name", &device_image_interface::image_type_name) + .addFunction ("load", &device_image_interface::load) + .addFunction ("unload", &device_image_interface::unload) + .addFunction ("crc", &device_image_interface::crc) + .addProperty <const device_t &> ("device", static_cast<const device_t &(device_image_interface::*)() const>(&device_image_interface::device)) + .addProperty <bool> ("is_readable", &device_image_interface::is_readable) + .addProperty <bool> ("is_writeable", &device_image_interface::is_writeable) + .addProperty <bool> ("is_creatable", &device_image_interface::is_creatable) + .addProperty <bool> ("is_reset_on_load", &device_image_interface::is_reset_on_load) + .endClass() + .beginClass <lua_emu_file> ("file") + .addConstructor <void (*)(const char *, UINT32)> () + .addCFunction ("read", &lua_emu_file::l_emu_file_read) + .addFunction ("open", &lua_emu_file::open) + .addFunction ("open_next", &lua_emu_file::open_next) + .addFunction ("seek", &lua_emu_file::seek) + .addFunction ("size", &lua_emu_file::size) + .addFunction ("filename", &lua_emu_file::filename) + .addFunction ("fullpath", &lua_emu_file::fullpath) + .endClass() + .beginClass <lua_item> ("item") + .addConstructor <void (*)(int)> () + .addData ("size", &lua_item::l_item_size, false) + .addData ("count", &lua_item::l_item_count, false) + .addCFunction ("read", &lua_item::l_item_read) + .addCFunction ("read_block", &lua_item::l_item_read_block) + .addCFunction ("write", &lua_item::l_item_write) + .endClass() + .endNamespace(); + + luabridge::push (m_lua_state, mame_machine_manager::instance()); + lua_setglobal(m_lua_state, "manager"); +} + +void lua_engine::start_console() +{ + std::thread th(::serve_lua, this); + th.detach(); +} + +//------------------------------------------------- +// frame_hook - called at each frame refresh, used to draw a HUD +//------------------------------------------------- +bool lua_engine::frame_hook() +{ + bool is_cb_hooked = false; + if (m_machine != nullptr) { + // invoke registered callback (if any) + is_cb_hooked = hook_frame_cb.active(); + if (is_cb_hooked) { + lua_State *L = hook_frame_cb.precall(); + hook_frame_cb.call(this, L, 0); + } + } + return is_cb_hooked; +} + +void lua_engine::periodic_check() +{ + std::lock_guard<std::mutex> lock(g_mutex); + if (msg.ready == 1) { + lua_settop(m_lua_state, 0); + int status = luaL_loadbuffer(m_lua_state, msg.text.c_str(), msg.text.length(), "=stdin"); + if (incomplete(status)==0) /* cannot try to add lines? */ + { + if (status == LUA_OK) status = docall(0, LUA_MULTRET); + report(status); + if (status == LUA_OK && lua_gettop(m_lua_state) > 0) /* any result to print? */ + { + luaL_checkstack(m_lua_state, LUA_MINSTACK, "too many results to print"); + lua_getglobal(m_lua_state, "print"); + lua_insert(m_lua_state, 1); + if (lua_pcall(m_lua_state, lua_gettop(m_lua_state) - 1, 0, 0) != LUA_OK) + lua_writestringerror("%s\n", lua_pushfstring(m_lua_state, + "error calling " LUA_QL("print") " (%s)", + lua_tostring(m_lua_state, -1))); + } + } + else + { + status = -1; + } + msg.status = status; + msg.response = msg.text; + msg.text = ""; + msg.ready = 0; + msg.done = 1; + } +} + +//------------------------------------------------- +// close - close and cleanup of lua engine +//------------------------------------------------- + +void lua_engine::close() +{ + lua_settop(m_lua_state, 0); /* clear stack */ + lua_close(m_lua_state); +} + +//------------------------------------------------- +// execute - load and execute script +//------------------------------------------------- + +void lua_engine::load_script(const char *filename) +{ + int s = luaL_loadfile(m_lua_state, filename); + report(s); + update_machine(); + start(); +} + +//------------------------------------------------- +// execute_string - execute script from string +//------------------------------------------------- + +void lua_engine::load_string(const char *value) +{ + int s = luaL_loadstring(m_lua_state, value); + report(s); + update_machine(); + start(); +} + +//------------------------------------------------- +// start - execute the loaded script +//------------------------------------------------- + +void lua_engine::start() +{ + resume(m_lua_state); +} + + +//************************************************************************** +// LuaBridge Stack specializations +//************************************************************************** + +namespace luabridge { + template <> + struct Stack <unsigned long long> { + static inline void push (lua_State* L, unsigned long long value) { + lua_pushunsigned(L, static_cast <lua_Unsigned> (value)); + } + + static inline unsigned long long get (lua_State* L, int index) { + return static_cast <unsigned long long> (luaL_checkunsigned (L, index)); + } + }; +} |