// license:BSD-3-Clause // copyright-holders:Miodrag Milanovic,Luca Bruno /*************************************************************************** luaengine.c Controls execution of the core MAME system. ***************************************************************************/ #include #include #include #include "luabridge/Source/LuaBridge/LuaBridge.h" #include #include "emu.h" #include "mame.h" #include "cheat.h" #include "drivenum.h" #include "emuopts.h" #include "ui/ui.h" #include "luaengine.h" #include "uiinput.h" #include #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) != nullptr) /* 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_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 { 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 { 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 "" #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 >::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(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)); } int lua_engine::l_ioport_write(lua_State *L) { ioport_field *field = static_cast(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(_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(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 luabridge::LuaRef lua_engine::l_options_get_entries(const T *o) { T *options = const_cast(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(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(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(m); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef table = luabridge::LuaRef::newTable(L); for (auto &bank : mm->banks()) { table[bank.second->tag()] = bank.second.get(); } 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(m); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef table = luabridge::LuaRef::newTable(L); for (auto ®ion: mm->regions()) { table[region.second->name()] = region.second.get(); } return table; } //------------------------------------------------- // memory_shares - return memory_shares // -> manager:machine():memory().share[":maincpu"] //------------------------------------------------- luabridge::LuaRef lua_engine::l_memory_get_shares(const memory_manager *m) { memory_manager *mm = const_cast(m); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef table = luabridge::LuaRef::newTable(L); for (auto &share: mm->shares()) { table[share.first] = share.second.get(); } 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(c); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef entry_table = luabridge::LuaRef::newTable(L); int cheatnum = 0; for (auto &entry : cm->entries()) { entry_table[cheatnum++] = entry.get(); } 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::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(m); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef port_table = luabridge::LuaRef::newTable(L); for (auto &port : im->ports()) { port_table[port.second->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(i); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef f_table = luabridge::LuaRef::newTable(L); for (ioport_field &field : p->fields()) { if (field.type_class() != INPUT_CLASS_INTERNAL) 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(d); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef sp_table = luabridge::LuaRef::newTable(L); if(!dynamic_cast(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(d); lua_State *L = luaThis->m_lua_state; luabridge::LuaRef st_table = luabridge::LuaRef::newTable(L); if(!dynamic_cast(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(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 , // -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000) //------------------------------------------------- template int lua_engine::lua_addr_space::l_mem_read(lua_State *L) { address_space &sp = luabridge::Stack::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::is_signed) { lua_pushinteger(L, mem_content); } else { lua_pushunsigned(L, mem_content); } return 1; } //------------------------------------------------- // mem_write - templated memory writer for , // -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D) //------------------------------------------------- template int lua_engine::lua_addr_space::l_mem_write(lua_State *L) { address_space &sp = luabridge::Stack::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 , // -> manager:machine().devices[":maincpu"].spaces["program"]:read_direct_i8(0xC000) //------------------------------------------------- template int lua_engine::lua_addr_space::l_direct_mem_read(lua_State *L) { address_space &sp = luabridge::Stack::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::is_signed) { lua_pushinteger(L, mem_content); } else { lua_pushunsigned(L, mem_content); } return 1; } //------------------------------------------------- // mem_direct_write - templated memory writer for , // -> manager:machine().devices[":maincpu"].spaces["program"]:write_direct_u16(0xC000, 0xF00D) //------------------------------------------------- template int lua_engine::lua_addr_space::l_direct_mem_write(lua_State *L) { address_space &sp = luabridge::Stack::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 , // -> manager:machine():memory().regions[":maincpu"]:read_i8(0xC000) //------------------------------------------------- template int lua_engine::lua_memory_region::l_region_read(lua_State *L) { memory_region ®ion = luabridge::Stack::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::is_signed) { lua_pushinteger(L, mem_content); } else { lua_pushunsigned(L, mem_content); } return 1; } //------------------------------------------------- // region_write - templated region writer for , // -> manager:machine():memory().regions[":maincpu"]:write_u16(0xC000, 0xF00D) //------------------------------------------------- template int lua_engine::lua_memory_region::l_region_write(lua_State *L) { memory_region ®ion = luabridge::Stack::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; } //------------------------------------------------- // share_read - templated share readers for , // -> manager:machine():memory().shares[":maincpu"]:read_i8(0xC000) //------------------------------------------------- template int lua_engine::lua_memory_share::l_share_read(lua_State *L) { memory_share &share = luabridge::Stack::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 = share.bytewidth() - 1; UINT8* ptr = (UINT8*)share.ptr(); for(int i = 0; i < sizeof(T); i++) { int addr = share.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i; if(addr >= share.bytes()) continue; mem_content <<= 8; if(share.endianness() == ENDIANNESS_BIG) mem_content |= ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)]; else mem_content |= ptr[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)]; } if (std::numeric_limits::is_signed) { lua_pushinteger(L, mem_content); } else { lua_pushunsigned(L, mem_content); } return 1; } //------------------------------------------------- // share_write - templated share writer for , // -> manager:machine():memory().shares[":maincpu"]:write_u16(0xC000, 0xF00D) //------------------------------------------------- template int lua_engine::lua_memory_share::l_share_write(lua_State *L) { memory_share &share = luabridge::Stack::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 = share.bytewidth() - 1; UINT8* ptr = (UINT8*)share.ptr(); for(int i = 0; i < sizeof(T); i++) { int addr = share.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i; if(addr >= share.bytes()) continue; if(share.endianness() == ENDIANNESS_BIG) ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff; else ptr[(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::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::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::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::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::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::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::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::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::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(sc_width); y1 = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast(sc_height); x2 = MIN(MAX(0, (float) lua_tonumber(L, 4)), sc_width-1) / static_cast(sc_width); y2 = MIN(MAX(0, (float) lua_tonumber(L, 5)), sc_height-1) / static_cast(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::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(sc_width); y1 = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast(sc_height); x2 = MIN(MAX(0, (float) lua_tonumber(L, 4)), sc_width-1) / static_cast(sc_width); y2 = MIN(MAX(0, (float) lua_tonumber(L, 5)), sc_height-1) / static_cast(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::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(); auto justify = ui::text_layout::LEFT; float y, x = 0; if(lua_isnumber(L, 2)) { x = MIN(MAX(0, (float) lua_tonumber(L, 2)), sc_width-1) / static_cast(sc_width); y = MIN(MAX(0, (float) lua_tonumber(L, 3)), sc_height-1) / static_cast(sc_height); } else { std::string just_str = lua_tostring(L, 2); if(just_str == "right") justify = ui::text_layout::RIGHT; else if(just_str == "center") justify = ui::text_layout::CENTER; y = lua_tonumber(L, 3); } const char *msg = luaL_checkstring(L,4); rgb_t textcolor = UI_TEXT_COLOR; rgb_t bgcolor = UI_TEXT_BG_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, ui::text_layout::WORD, mame_ui_manager::NORMAL, textcolor, bgcolor, nullptr, nullptr); return 0; } int lua_engine::lua_emu_file::l_emu_file_read(lua_State *L) { lua_emu_file *file = luabridge::Stack::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; } int lua_engine::lua_ui_input::l_ui_input_find_mouse(lua_State *L) { ui_input_manager *ui_input = luabridge::Stack::get(L, 1); INT32 x, y; bool button; render_target *target = ui_input->find_mouse(&x, &y, &button); lua_pushnumber(L, x); lua_pushnumber(L, y); lua_pushboolean(L, button); luabridge::Stack::push(L, target); return 4; } int lua_engine::lua_render_target::l_render_view_bounds(lua_State *L) { render_target *target = luabridge::Stack::get(L, 1); const render_bounds &bounds = target->current_view()->bounds(); lua_pushnumber(L, bounds.x0); lua_pushnumber(L, bounds.x1); lua_pushnumber(L, bounds.y0); lua_pushnumber(L, bounds.y1); return 4; } 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(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(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 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 lock(g_mutex); done = msg.done; } while (done==0); // Do action on client side { std::lock_guard 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 nullptr; } //------------------------------------------------- // 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_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(); } void lua_engine::call_plugin(const char *data, const char *name) { std::string field("cb_"); field += name; 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; } lua_pushstring(m_lua_state, data); int error; if((error = lua_pcall(m_lua_state, 1, 0, 0)) != LUA_OK) { if(error == LUA_ERRRUN) printf("%s\n", lua_tostring(m_lua_state, -1)); lua_pop(m_lua_state, 1); return; } } int lua_engine::l_emu_register_callback(lua_State *L) { luaL_argcheck(L, lua_isfunction(L, 1), 1, "callback function expected"); luaL_argcheck(L, lua_isstring(L, 2), 2, "name (string) expected"); std::string name = luaL_checkstring(L, 2); std::string field = "cb_" + name; lua_pushvalue(L, 1); lua_setfield(L, LUA_REGISTRYINDEX, field.c_str()); return 1; } void lua_engine::menu_populate(std::string &menu, std::vector &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 (auto &port : machine().ioport().ports()) { for (ioport_field &field : port.second->fields()) { if (field.type_class() != INPUT_CLASS_INTERNAL) { 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::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::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 : ""; 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 ("register_callback", l_emu_register_callback ) .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 ("manager") .addFunction("machine", &machine_manager::machine) .addFunction("options", &machine_manager::options) .endClass() .beginClass ("mame_manager") .addFunction ("plugins", &mame_machine_manager::plugins) .addFunction ("cheat", &mame_machine_manager::cheat) .addFunction ("ui", &mame_machine_manager::ui) .endClass () .beginClass ("lua_machine") .addCFunction ("popmessage", &lua_machine::l_popmessage) .addCFunction ("logerror", &lua_machine::l_logerror) .endClass () .deriveClass ("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 ("render", &running_machine::render) .addFunction ("ioport", &running_machine::ioport) .addFunction ("parameters", &running_machine::parameters) .addFunction ("memory", &running_machine::memory) .addFunction ("options", &running_machine::options) .addFunction ("outputs", &running_machine::output) .addFunction ("input", &running_machine::ui_input) .addProperty ("paused", &running_machine::paused) .addProperty ("devices", &lua_engine::l_machine_get_devices) .addProperty ("screens", &lua_engine::l_machine_get_screens) .addProperty ("images", &lua_engine::l_machine_get_images) .endClass () .beginClass ("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") .addFunction ("name", &device_t::name) .addFunction ("shortname", &device_t::shortname) .addFunction ("tag", &device_t::tag) .addFunction ("owner", &device_t::owner) .addProperty ("spaces", &lua_engine::l_dev_get_memspaces) .addProperty ("state", &lua_engine::l_dev_get_states) .addProperty ("items", &lua_engine::l_dev_get_items) .endClass() .beginClass ("cheat") .addProperty ("enabled", &cheat_manager::enabled, &cheat_manager::set_enable) .addFunction ("reload", &cheat_manager::reload) .addFunction ("save_all", &cheat_manager::save_all) .addProperty ("entries", &lua_engine::l_cheat_get_entries) .endClass() .beginClass ("lua_cheat_entry") .addCFunction ("state", &lua_cheat_entry::l_get_state) .endClass() .deriveClass ("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") .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 ("ports", &lua_engine::l_ioport_get_ports) .endClass() .beginClass ("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 ("fields", &lua_engine::l_ioports_port_get_fields) .endClass() .beginClass ("ioport_field") .addFunction ("set_value", &ioport_field::set_value) .addProperty ("device", &ioport_field::device) .addProperty ("name", &ioport_field::name) .addProperty ("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_digital_joystick", &ioport_field::is_digital_joystick) .addProperty ("enabled", &ioport_field::enabled) .addProperty ("optional", &ioport_field::optional) .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 ("type", &ioport_field::type) .addProperty ("crosshair_scale", &ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale) .addProperty ("crosshair_offset", &ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset) .endClass() .beginClass ("core_options") .addFunction ("help", &core_options::output_help) .addFunction ("command", &core_options::command) .addProperty ("entries", &lua_engine::l_options_get_entries) .endClass() .beginClass ("lua_options_entry") .addCFunction ("value", &lua_options_entry::l_entry_value) .endClass() .deriveClass ("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") .endClass() .deriveClass ("ui_options") .endClass() .deriveClass ("plugin_options") .endClass() .beginClass ("parameters") .addFunction ("add", ¶meters_manager::add) .addFunction ("lookup", ¶meters_manager::lookup) .endClass() .beginClass ("lua_video_manager") .addCFunction ("begin_recording", &lua_video::l_begin_recording) .addCFunction ("end_recording", &lua_video::l_end_recording) .endClass() .deriveClass ("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 ("frameskip", &video_manager::frameskip, &video_manager::set_frameskip) .addProperty ("throttled", &video_manager::throttled, &video_manager::set_throttled) .addProperty ("throttle_rate", &video_manager::throttle_rate, &video_manager::set_throttle_rate) .endClass() .beginClass ("lua_addr_space") .addCFunction ("read_i8", &lua_addr_space::l_mem_read) .addCFunction ("read_u8", &lua_addr_space::l_mem_read) .addCFunction ("read_i16", &lua_addr_space::l_mem_read) .addCFunction ("read_u16", &lua_addr_space::l_mem_read) .addCFunction ("read_i32", &lua_addr_space::l_mem_read) .addCFunction ("read_u32", &lua_addr_space::l_mem_read) .addCFunction ("read_i64", &lua_addr_space::l_mem_read) .addCFunction ("read_u64", &lua_addr_space::l_mem_read) .addCFunction ("write_i8", &lua_addr_space::l_mem_write) .addCFunction ("write_u8", &lua_addr_space::l_mem_write) .addCFunction ("write_i16", &lua_addr_space::l_mem_write) .addCFunction ("write_u16", &lua_addr_space::l_mem_write) .addCFunction ("write_i32", &lua_addr_space::l_mem_write) .addCFunction ("write_u32", &lua_addr_space::l_mem_write) .addCFunction ("write_i64", &lua_addr_space::l_mem_write) .addCFunction ("write_u64", &lua_addr_space::l_mem_write) .addCFunction ("read_direct_i8", &lua_addr_space::l_direct_mem_read) .addCFunction ("read_direct_u8", &lua_addr_space::l_direct_mem_read) .addCFunction ("read_direct_i16", &lua_addr_space::l_direct_mem_read) .addCFunction ("read_direct_u16", &lua_addr_space::l_direct_mem_read) .addCFunction ("read_direct_i32", &lua_addr_space::l_direct_mem_read) .addCFunction ("read_direct_u32", &lua_addr_space::l_direct_mem_read) .addCFunction ("read_direct_i64", &lua_addr_space::l_direct_mem_read) .addCFunction ("read_direct_u64", &lua_addr_space::l_direct_mem_read) .addCFunction ("write_direct_i8", &lua_addr_space::l_direct_mem_write) .addCFunction ("write_direct_u8", &lua_addr_space::l_direct_mem_write) .addCFunction ("write_direct_i16", &lua_addr_space::l_direct_mem_write) .addCFunction ("write_direct_u16", &lua_addr_space::l_direct_mem_write) .addCFunction ("write_direct_i32", &lua_addr_space::l_direct_mem_write) .addCFunction ("write_direct_u32", &lua_addr_space::l_direct_mem_write) .addCFunction ("write_direct_i64", &lua_addr_space::l_direct_mem_write) .addCFunction ("write_direct_u64", &lua_addr_space::l_direct_mem_write) .endClass() .deriveClass ("addr_space") .addFunction("name", &address_space::name) .addProperty ("map", &lua_engine::l_addr_space_map) .endClass() .beginClass ("lua_input") .addCFunction ("find_mouse", &lua_ui_input::l_ui_input_find_mouse) .endClass() .deriveClass ("input") .addFunction ("pressed", &ui_input_manager::pressed) .endClass() .beginClass ("lua_target") .addCFunction ("view_bounds", &lua_render_target::l_render_view_bounds) .endClass() .deriveClass ("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 ("max_update_rate", &render_target::max_update_rate, &render_target::set_max_update_rate) .addProperty ("view", &render_target::view, &render_target::set_view) .addProperty ("orientation", &render_target::orientation, &render_target::set_orientation) .addProperty ("backdrops", &render_target::backdrops_enabled, &render_target::set_backdrops_enabled) .addProperty ("overlays", &render_target::overlays_enabled, &render_target::set_overlays_enabled) .addProperty ("bezels", &render_target::bezels_enabled, &render_target::set_bezels_enabled) .addProperty ("marquees", &render_target::marquees_enabled, &render_target::set_marquees_enabled) .addProperty ("screen_overlay", &render_target::screen_overlay_enabled, &render_target::set_screen_overlay_enabled) .addProperty ("zoom", &render_target::zoom_to_screen, &render_target::set_zoom_to_screen) .endClass() .beginClass ("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") .addFunction ("max_update_rate", &render_manager::max_update_rate) .addFunction ("ui_target", &render_manager::ui_target) .addFunction ("ui_container", &render_manager::ui_container) .addProperty ("targets", &lua_engine::l_render_get_targets) .endClass() .beginClass ("ui") .addFunction ("is_menu_active", &mame_ui_manager::is_menu_active) .addFunction ("options", &mame_ui_manager::options) .addProperty ("show_fps", &mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps) .addProperty ("show_profiler", &mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler) .addProperty ("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_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_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 ("dev_space") .addFunction ("name", &device_state_entry::symbol) .addProperty ("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") .addProperty ("banks", &lua_engine::l_memory_get_banks) .addProperty ("regions", &lua_engine::l_memory_get_regions) .addProperty ("shares", &lua_engine::l_memory_get_shares) .endClass() .beginClass ("lua_region") .addCFunction ("read_i8", &lua_memory_region::l_region_read) .addCFunction ("read_u8", &lua_memory_region::l_region_read) .addCFunction ("read_i16", &lua_memory_region::l_region_read) .addCFunction ("read_u16", &lua_memory_region::l_region_read) .addCFunction ("read_i32", &lua_memory_region::l_region_read) .addCFunction ("read_u32", &lua_memory_region::l_region_read) .addCFunction ("read_i64", &lua_memory_region::l_region_read) .addCFunction ("read_u64", &lua_memory_region::l_region_read) .addCFunction ("write_i8", &lua_memory_region::l_region_write) .addCFunction ("write_u8", &lua_memory_region::l_region_write) .addCFunction ("write_i16", &lua_memory_region::l_region_write) .addCFunction ("write_u16", &lua_memory_region::l_region_write) .addCFunction ("write_i32", &lua_memory_region::l_region_write) .addCFunction ("write_u32", &lua_memory_region::l_region_write) .addCFunction ("write_i64", &lua_memory_region::l_region_write) .addCFunction ("write_u64", &lua_memory_region::l_region_write) .endClass() .deriveClass ("region") .addProperty ("size", &memory_region::bytes) .endClass() .beginClass ("lua_share") .addCFunction ("read_i8", &lua_memory_share::l_share_read) .addCFunction ("read_u8", &lua_memory_share::l_share_read) .addCFunction ("read_i16", &lua_memory_share::l_share_read) .addCFunction ("read_u16", &lua_memory_share::l_share_read) .addCFunction ("read_i32", &lua_memory_share::l_share_read) .addCFunction ("read_u32", &lua_memory_share::l_share_read) .addCFunction ("read_i64", &lua_memory_share::l_share_read) .addCFunction ("read_u64", &lua_memory_share::l_share_read) .addCFunction ("write_i8", &lua_memory_share::l_share_write) .addCFunction ("write_u8", &lua_memory_share::l_share_write) .addCFunction ("write_i16", &lua_memory_share::l_share_write) .addCFunction ("write_u16", &lua_memory_share::l_share_write) .addCFunction ("write_i32", &lua_memory_share::l_share_write) .addCFunction ("write_u32", &lua_memory_share::l_share_write) .addCFunction ("write_i64", &lua_memory_share::l_share_write) .addCFunction ("write_u64", &lua_memory_share::l_share_write) .endClass() .deriveClass ("region") .addProperty ("size", &memory_share::bytes) .endClass() .beginClass ("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() .beginClass ("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 ("device", static_cast(&device_image_interface::device)) .addProperty ("is_readable", &device_image_interface::is_readable) .addProperty ("is_writeable", &device_image_interface::is_writeable) .addProperty ("is_creatable", &device_image_interface::is_creatable) .addProperty ("is_reset_on_load", &device_image_interface::is_reset_on_load) .endClass() .beginClass ("file") .addConstructor () .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 ("item") .addConstructor () .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, (machine_manager*)mame_machine_manager::instance()); lua_setglobal(m_lua_state, "manager"); luabridge::push(m_lua_state, mame_machine_manager::instance()); lua_setglobal(m_lua_state, "mame_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 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 { static inline void push (lua_State* L, unsigned long long value) { lua_pushunsigned(L, static_cast (value)); } static inline unsigned long long get (lua_State* L, int index) { return static_cast (luaL_checkunsigned (L, index)); } }; }