summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/luaengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/luaengine.cpp')
-rw-r--r--src/frontend/mame/luaengine.cpp3857
1 files changed, 1495 insertions, 2362 deletions
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index 9afe6827cfd..6aa2d3840ec 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -8,612 +8,482 @@
***************************************************************************/
-#include <lua.hpp>
#include "emu.h"
+#include "luaengine.ipp"
+
#include "mame.h"
+#include "pluginopts.h"
+#include "ui/pluginopt.h"
+#include "ui/ui.h"
+
+#include "imagedev/cassette.h"
+
#include "debugger.h"
-#include "debug/debugcon.h"
-#include "debug/debugcpu.h"
-#include "debug/points.h"
-#include "debug/textbuf.h"
#include "drivenum.h"
#include "emuopts.h"
-#include "ui/ui.h"
-#include "ui/pluginopt.h"
-#include "luaengine.h"
+#include "fileio.h"
+#include "inputdev.h"
#include "natkeyboard.h"
-#include "uiinput.h"
-#include "pluginopts.h"
+#include "screen.h"
#include "softlist.h"
-#include "inputdev.h"
+#include "uiinput.h"
+#include "corestr.h"
+
+#include <algorithm>
+#include <condition_variable>
#include <cstring>
+#include <mutex>
#include <thread>
-#ifdef __clang__
-#pragma clang diagnostic ignored "-Wshift-count-overflow"
-#endif
-#if defined(_MSC_VER)
-#pragma warning(disable:4503)
-#endif
-
//**************************************************************************
// LUA ENGINE
//**************************************************************************
-extern "C" {
- int luaopen_zlib(lua_State *L);
- int luaopen_lfs(lua_State *L);
- int luaopen_linenoise(lua_State *L);
- int luaopen_lsqlite3(lua_State *L);
-}
+int luaopen_zlib(lua_State *const L);
+extern "C" int luaopen_lfs(lua_State *L);
+int luaopen_linenoise(lua_State *L);
+int luaopen_lsqlite3(lua_State *L);
+
-namespace sol
+template <typename T>
+struct lua_engine::devenum
{
- class buffer
- {
- public:
- // sol does lua_settop(0), save userdata buffer in registry if necessary
- buffer(int size, lua_State *L)
- {
- ptr = luaL_buffinitsize(L, &buff, size);
- len = size;
- if(buff.b != buff.initb)
- {
- lua_pushvalue(L, -1);
- lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp");
- }
- }
- ~buffer()
- {
- lua_State *L = buff.L;
- if(lua_getfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp") != LUA_TNIL)
- {
- lua_pushnil(L);
- lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp");
- }
- else
- lua_pop(L, -1);
+ template <typename... U> devenum(device_t &d, U &&... args) : device(d), iter(d, std::forward<U>(args)...) { }
- luaL_pushresultsize(&buff, len);
- }
- void set_len(int size) { len = size; }
- int get_len() { return len; }
- char *get_ptr() { return ptr; }
- private:
- luaL_Buffer buff;
- int len;
- char *ptr;
- };
- template<>
- struct is_container<core_options> : std::false_type {}; // don't convert core_optons to a table directly
- namespace stack
+ device_t &device;
+ T iter;
+ int count = -1;
+};
+
+
+namespace {
+
+struct save_item
+{
+ void *base;
+ unsigned int size;
+ unsigned int count;
+ unsigned int valcount;
+ unsigned int blockcount;
+ unsigned int stride;
+};
+
+struct thread_context
+{
+private:
+ sol::state m_state;
+ std::string m_result;
+ std::mutex m_guard;
+ std::condition_variable m_sync;
+ bool m_busy = false;
+
+public:
+ bool m_yield = false;
+
+ thread_context()
{
- template <>
- struct pusher<osd_file::error>
- {
- static int push(lua_State *L, osd_file::error error)
- {
- const char *strerror;
- switch(error)
- {
- case osd_file::error::NONE:
- return stack::push(L, sol::nil);
- 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;
- }
- return stack::push(L, strerror);
- }
- };
- template <>
- struct checker<sol::buffer *>
- {
- template <typename Handler>
- static bool check (lua_State* L, int index, Handler&& handler, record& tracking)
- {
- return stack::check<int>(L, index, handler);
- }
- };
- template <>
- struct getter<sol::buffer *>
- {
- static sol::buffer *get(lua_State* L, int index, record& tracking)
- {
- return new sol::buffer(stack::get<int>(L, index), L);
- }
- };
- template <>
- struct pusher<sol::buffer *>
- {
- static int push(lua_State* L, sol::buffer *buff)
- {
- delete buff;
- return 1;
- }
- };
- template <>
- struct pusher<map_handler_type>
- {
- static int push(lua_State *L, map_handler_type type)
- {
- const char *typestr;
- switch(type)
+ m_state.open_libraries();
+ m_state["package"]["preload"]["zlib"] = &luaopen_zlib;
+ m_state["package"]["preload"]["lfs"] = &luaopen_lfs;
+ m_state["package"]["preload"]["linenoise"] = &luaopen_linenoise;
+ m_state.set_function("yield",
+ [this] ()
{
- case AMH_NONE:
- typestr = "none";
- break;
- case AMH_RAM:
- typestr = "ram";
- break;
- case AMH_ROM:
- typestr = "rom";
- break;
- case AMH_NOP:
- typestr = "nop";
- break;
- case AMH_UNMAP:
- typestr = "unmap";
- break;
- case AMH_DEVICE_DELEGATE:
- case AMH_DEVICE_DELEGATE_M:
- case AMH_DEVICE_DELEGATE_S:
- case AMH_DEVICE_DELEGATE_SM:
- case AMH_DEVICE_DELEGATE_MO:
- case AMH_DEVICE_DELEGATE_SMO:
- typestr = "delegate";
- break;
- case AMH_PORT:
- typestr = "port";
- break;
- case AMH_BANK:
- typestr = "bank";
- break;
- case AMH_DEVICE_SUBMAP:
- typestr = "submap";
- break;
- default:
- typestr = "unknown";
- break;
- }
- return stack::push(L, typestr);
- }
- };
+ std::unique_lock<std::mutex> yield_lock(m_guard);
+ m_result = m_state["status"];
+ m_yield = true;
+ m_sync.wait(yield_lock);
+ m_yield = false;
+ });
}
-}
-
-namespace
-{
- // ======================> enum_parser
- template<typename T, size_t SIZE>
- class enum_parser
+ bool start(sol::this_state s, char const *scr)
{
- public:
- constexpr enum_parser(std::initializer_list<std::pair<const char *, T>> values)
- {
- if (values.size() != SIZE)
- throw false && "size template argument incorrectly specified";
- std::copy(values.begin(), values.end(), m_map.begin());
- }
+ std::unique_lock<std::mutex> caller_lock(m_guard);
+ if (m_busy)
+ return false;
- T operator()(const char *text) const
+ sol::load_result res = m_state.load(scr);
+ if (!res.valid())
{
- auto iter = std::find_if(
- m_map.begin() + 1,
- m_map.end(),
- [text](const auto &x) { return !strcmp(text, x.first); });
- if (iter == m_map.end())
- iter = m_map.begin();
- return iter->second;
+ sol::error err = res;
+ luaL_error(s, err.what());
+ return false; // unreachable - luaL_error throws
}
- T operator()(const std::string &text) const
+ std::thread th(
+ [this, func = res.get<sol::protected_function>()] ()
+ {
+ auto ret = func();
+ std::unique_lock<std::mutex> result_lock(m_guard);
+ if (ret.valid())
+ {
+ auto result = ret.get<std::optional<char const *> >();
+ if (!result)
+ osd_printf_error("[LUA ERROR] in thread: return value must be string\n");
+ else if (!*result)
+ m_result.clear();
+ else
+ m_result = *result;
+ }
+ else
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in thread: %s\n", err.what());
+ }
+ m_busy = false;
+ });
+ m_busy = true;
+ m_yield = false;
+ th.detach(); // FIXME: this is unsafe as the thread function modifies members of the object
+ return true;
+ }
+
+ void resume(char const *val)
+ {
+ std::unique_lock<std::mutex> lock(m_guard);
+ if (m_yield)
{
- return (*this)(text.c_str());
+ if (val)
+ m_state["status"] = val;
+ else
+ m_state["status"] = sol::lua_nil;
+ m_sync.notify_all();
}
+ }
- private:
- std::array<std::pair<const char *, T>, SIZE> m_map;
- };
+ char const *result()
+ {
+ std::unique_lock<std::mutex> lock(m_guard);
+ if (m_busy && !m_yield)
+ return "";
+ else
+ return m_result.c_str();
+ }
+
+ bool busy() const
+ {
+ return m_busy;
+ }
};
-static const enum_parser<input_seq_type, 3> s_seq_type_parser =
+struct device_state_entries
{
- { "standard", SEQ_TYPE_STANDARD },
- { "increment", SEQ_TYPE_INCREMENT },
- { "decrement", SEQ_TYPE_DECREMENT },
-};
+ device_state_entries(device_state_interface const &s) : state(s) { }
+ device_state_interface::entrylist_type const &items() { return state.state_entries(); }
+ static device_state_entry const &unwrap(device_state_interface::entrylist_type::const_iterator const &it) { return **it; }
+ static int push_key(lua_State *L, device_state_interface::entrylist_type::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, (*it)->symbol()); }
-static const enum_parser<movie_recording::format, 2> s_movie_recording_format_parser =
-{
- { "avi", movie_recording::format::AVI },
- { "mng", movie_recording::format::MNG }
+ device_state_interface const &state;
};
-static const enum_parser<read_or_write, 4> s_read_or_write_parser =
+struct image_interface_formats
{
- { "r", read_or_write::READ },
- { "w", read_or_write::WRITE },
- { "rw", read_or_write::READWRITE },
- { "wr", read_or_write::READWRITE }
+ image_interface_formats(device_image_interface &i) : image(i) { }
+ device_image_interface::formatlist_type const &items() { return image.formatlist(); }
+
+ static image_device_format const &unwrap(device_image_interface::formatlist_type::const_iterator const &it) { return **it; }
+ static int push_key(lua_State *L, device_image_interface::formatlist_type::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, (*it)->name()); }
+
+ device_image_interface &image;
};
-static const enum_parser<ui::text_layout::text_justify, 3> s_text_justify_parser =
+struct plugin_options_plugins
{
- { "left", ui::text_layout::LEFT },
- { "right", ui::text_layout::RIGHT },
- { "center", ui::text_layout::CENTER },
-};
+ plugin_options_plugins(plugin_options &o) : options(o) { }
+ std::list<plugin_options::plugin> &items() { return options.plugins(); }
+ static plugin_options::plugin const &unwrap(std::list<plugin_options::plugin>::const_iterator const &it) { return *it; }
+ static int push_key(lua_State *L, std::list<plugin_options::plugin>::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, it->m_name); }
-//-------------------------------------------------
-// process_snapshot_filename - processes a snapshot
-// filename
-//-------------------------------------------------
+ plugin_options &options;
+};
-static std::string process_snapshot_filename(running_machine &machine, const char *s)
+
+template <typename T>
+void resume_tasks(lua_State *L, T &&tasks, bool status)
{
- std::string result(s);
- if (!osd_is_absolute_path(s))
+ for (int ref : tasks)
{
- strreplace(result, "/", PATH_SEPARATOR);
- strreplace(result, "%g", machine.basename());
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+ lua_State *const thread = lua_tothread(L, -1);
+ lua_pop(L, 1);
+ lua_pushboolean(thread, status ? 1 : 0);
+ int nresults = 0;
+ int const stat = lua_resume(thread, nullptr, 1, &nresults);
+ if ((stat != LUA_OK) && (stat != LUA_YIELD))
+ {
+ osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(thread, -1));
+ lua_pop(thread, 1);
+ }
+ else
+ {
+ lua_pop(thread, nresults);
+ }
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
}
- return result;
}
+} // anonymous namespace
-//-------------------------------------------------
-// mem_read - templated memory readers for <sign>,<size>
-// -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000)
-//-------------------------------------------------
-template <typename T>
-T lua_engine::addr_space::mem_read(offs_t address)
-{
- T mem_content = 0;
- switch(sizeof(mem_content) * 8) {
- case 8:
- mem_content = space.read_byte(address);
- break;
- case 16:
- if (WORD_ALIGNED(address)) {
- mem_content = space.read_word(address);
- } else {
- mem_content = space.read_word_unaligned(address);
- }
- break;
- case 32:
- if (DWORD_ALIGNED(address)) {
- mem_content = space.read_dword(address);
- } else {
- mem_content = space.read_dword_unaligned(address);
- }
- break;
- case 64:
- if (QWORD_ALIGNED(address)) {
- mem_content = space.read_qword(address);
- } else {
- mem_content = space.read_qword_unaligned(address);
- }
- break;
- default:
- break;
- }
+namespace sol {
- return mem_content;
-}
+template <> struct is_container<device_state_entries> : std::true_type { };
+template <> struct is_container<image_interface_formats> : std::true_type { };
+template <> struct is_container<plugin_options_plugins> : std::true_type { };
-//-------------------------------------------------
-// mem_write - templated memory writer for <sign>,<size>
-// -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D)
-//-------------------------------------------------
template <typename T>
-void lua_engine::addr_space::mem_write(offs_t address, T val)
+struct usertype_container<lua_engine::devenum<T> > : lua_engine::immutable_collection_helper<lua_engine::devenum<T>, T>
{
- switch(sizeof(val) * 8) {
- case 8:
- space.write_byte(address, val);
- break;
- case 16:
- if (WORD_ALIGNED(address)) {
- space.write_word(address, val);
- } else {
- space.write_word_unaligned(address, val);
- }
- break;
- case 32:
- if (DWORD_ALIGNED(address)) {
- space.write_dword(address, val);
- } else {
- space.write_dword_unaligned(address, val);
- }
- break;
- case 64:
- if (QWORD_ALIGNED(address)) {
- space.write_qword(address, val);
- } else {
- space.write_qword_unaligned(address, val);
- }
- break;
- default:
- break;
+private:
+ using enumerator = lua_engine::devenum<T>;
+
+ template <bool Indexed>
+ static int next_pairs(lua_State *L)
+ {
+ typename usertype_container::indexed_iterator &i(stack::unqualified_get<user<typename usertype_container::indexed_iterator> >(L, 1));
+ if (i.src.end() == i.it)
+ return stack::push(L, lua_nil);
+ int result;
+ if constexpr (Indexed)
+ result = stack::push(L, i.ix + 1);
+ else
+ result = stack::push(L, i.it->tag());
+ result += stack::push_reference(L, *i.it);
+ ++i;
+ return result;
}
-}
-//-------------------------------------------------
-// log_mem_read - templated logical memory readers for <sign>,<size>
-// -> manager:machine().devices[":maincpu"].spaces["program"]:read_log_i8(0xC000)
-//-------------------------------------------------
+ template <bool Indexed>
+ static int start_pairs(lua_State *L)
+ {
+ enumerator &self(usertype_container::get_self(L));
+ stack::push(L, next_pairs<Indexed>);
+ stack::push<user<typename usertype_container::indexed_iterator> >(L, self.iter, self.iter.begin());
+ stack::push(L, lua_nil);
+ return 3;
+ }
-template <typename T>
-T lua_engine::addr_space::log_mem_read(offs_t address)
-{
- T mem_content = 0;
- if(!dev.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
- return 0;
-
- switch(sizeof(mem_content) * 8) {
- case 8:
- mem_content = space.read_byte(address);
- break;
- case 16:
- if (WORD_ALIGNED(address)) {
- mem_content = space.read_word(address);
- } else {
- mem_content = space.read_word_unaligned(address);
- }
- break;
- case 32:
- if (DWORD_ALIGNED(address)) {
- mem_content = space.read_dword(address);
- } else {
- mem_content = space.read_dword_unaligned(address);
- }
- break;
- case 64:
- if (QWORD_ALIGNED(address)) {
- mem_content = space.read_qword(address);
- } else {
- mem_content = space.read_qword_unaligned(address);
- }
- break;
- default:
- break;
+public:
+ static int at(lua_State *L)
+ {
+ enumerator &self(usertype_container::get_self(L));
+ std::ptrdiff_t const index(stack::unqualified_get<std::ptrdiff_t>(L, 2));
+ auto const dev(self.iter.byindex(index - 1));
+ if (dev)
+ return stack::push_reference(L, *dev);
+ else
+ return stack::push(L, lua_nil);
}
- return mem_content;
-}
+ static int get(lua_State *L)
+ {
+ enumerator &self(usertype_container::get_self(L));
+ char const *const tag(stack::unqualified_get<char const *>(L));
+ device_t *const dev(self.device.subdevice(tag));
+ if (dev)
+ {
+ auto *const check(T(*dev, 0).first());
+ bool match;
+ if constexpr (std::is_base_of_v<device_t, decltype(*check)>)
+ match = check && (static_cast<device_t *>(check) == dev);
+ else if constexpr (std::is_base_of_v<device_interface, decltype(*check)>)
+ match = check && (&check->device() == dev);
+ else
+ match = check && (dynamic_cast<device_t *>(check) == dev);
+ if (match)
+ return stack::push_reference(L, *check);
+ }
+ return stack::push(L, lua_nil);
+ }
-//-------------------------------------------------
-// log_mem_write - templated logical memory writer for <sign>,<size>
-// -> manager:machine().devices[":maincpu"].spaces["program"]:write_log_u16(0xC000, 0xF00D)
-//-------------------------------------------------
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
-template <typename T>
-void lua_engine::addr_space::log_mem_write(offs_t address, T val)
-{
- if(!dev.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address))
- return;
-
- switch(sizeof(val) * 8) {
- case 8:
- space.write_byte(address, val);
- break;
- case 16:
- if (WORD_ALIGNED(address)) {
- space.write_word(address, val);
- } else {
- space.write_word_unaligned(address, val);
- }
- break;
- case 32:
- if (DWORD_ALIGNED(address)) {
- space.write_dword(address, val);
- } else {
- space.write_dword_unaligned(address, val);
- }
- break;
- case 64:
- if (QWORD_ALIGNED(address)) {
- space.write_qword(address, val);
- } else {
- space.write_qword_unaligned(address, val);
- }
- break;
- default:
- break;
+ static int index_of(lua_State *L)
+ {
+ enumerator &self(usertype_container::get_self(L));
+ auto &dev(stack::unqualified_get<decltype(*self.iter.first())>(L, 2));
+ std::ptrdiff_t found(self.iter.indexof(dev));
+ if (0 > found)
+ return stack::push(L, lua_nil);
+ else
+ return stack::push(L, found + 1);
}
-}
-//-------------------------------------------------
-// mem_direct_read - templated direct memory readers for <sign>,<size>
-// -> manager:machine().devices[":maincpu"].spaces["program"]:read_direct_i8(0xC000)
-//-------------------------------------------------
+ static int size(lua_State *L)
+ {
+ enumerator &self(usertype_container::get_self(L));
+ if (0 > self.count)
+ self.count = self.iter.count();
+ return stack::push(L, self.count);
+ }
-template <typename T>
-T lua_engine::addr_space::direct_mem_read(offs_t address)
-{
- T mem_content = 0;
- offs_t lowmask = space.data_width() / 8 - 1;
- for(int i = 0; i < sizeof(T); i++)
+ static int empty(lua_State *L)
{
- int addr = space.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
- uint8_t *base = (uint8_t *)space.get_read_ptr(addr & ~lowmask);
- if(!base)
- continue;
- mem_content <<= 8;
- if(space.endianness() == ENDIANNESS_BIG)
- mem_content |= base[BYTE8_XOR_BE(addr) & lowmask];
- else
- mem_content |= base[BYTE8_XOR_LE(addr) & lowmask];
+ enumerator &self(usertype_container::get_self(L));
+ if (0 > self.count)
+ self.count = self.iter.count();
+ return stack::push(L, !self.count);
}
- return mem_content;
-}
+ static int next(lua_State *L) { return stack::push(L, next_pairs<false>); }
+ static int pairs(lua_State *L) { return start_pairs<false>(L); }
+ static int ipairs(lua_State *L) { return start_pairs<true>(L); }
+};
-//-------------------------------------------------
-// mem_direct_write - templated memory writer for <sign>,<size>
-// -> manager:machine().devices[":maincpu"].spaces["program"]:write_direct_u16(0xC000, 0xF00D)
-//-------------------------------------------------
-template <typename T>
-void lua_engine::addr_space::direct_mem_write(offs_t address, T val)
+template <>
+struct usertype_container<device_state_entries> : lua_engine::immutable_sequence_helper<device_state_entries, device_state_interface::entrylist_type const, device_state_interface::entrylist_type::const_iterator>
{
- offs_t lowmask = space.data_width() / 8 - 1;
- for(int i = 0; i < sizeof(T); i++)
+private:
+ using entrylist_type = device_state_interface::entrylist_type;
+
+public:
+ static int get(lua_State *L)
{
- int addr = space.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
- uint8_t *base = (uint8_t *)space.get_read_ptr(addr & ~lowmask);
- if(!base)
- continue;
- if(space.endianness() == ENDIANNESS_BIG)
- base[BYTE8_XOR_BE(addr) & lowmask] = val & 0xff;
+ device_state_entries &self(get_self(L));
+ char const *const symbol(stack::unqualified_get<char const *>(L));
+ auto const found(std::find_if(
+ self.state.state_entries().begin(),
+ self.state.state_entries().end(),
+ [&symbol] (std::unique_ptr<device_state_entry> const &v) { return !std::strcmp(v->symbol(), symbol); }));
+ if (self.state.state_entries().end() != found)
+ return stack::push_reference(L, std::cref(**found));
else
- base[BYTE8_XOR_LE(addr) & lowmask] = val & 0xff;
- val >>= 8;
+ return stack::push(L, lua_nil);
}
-}
-//-------------------------------------------------
-// region_read - templated region readers for <sign>,<size>
-// -> manager:machine():memory().regions[":maincpu"]:read_i8(0xC000)
-//-------------------------------------------------
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
+};
-template <typename T>
-T lua_engine::region_read(memory_region &region, offs_t address)
+
+template <>
+struct usertype_container<image_interface_formats> : lua_engine::immutable_sequence_helper<image_interface_formats, device_image_interface::formatlist_type const, device_image_interface::formatlist_type::const_iterator>
{
- T mem_content = 0;
- offs_t lowmask = region.bytewidth() - 1;
- for(int i = 0; i < sizeof(T); i++)
+private:
+ using format_list = device_image_interface::formatlist_type;
+
+public:
+ static int get(lua_State *L)
{
- 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.as_u8((BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask));
+ image_interface_formats &self(get_self(L));
+ char const *const name(stack::unqualified_get<char const *>(L));
+ auto const found(std::find_if(
+ self.image.formatlist().begin(),
+ self.image.formatlist().end(),
+ [&name] (std::unique_ptr<image_device_format> const &v) { return v->name() == name; }));
+ if (self.image.formatlist().end() != found)
+ return stack::push_reference(L, std::cref(**found));
else
- mem_content |= region.as_u8((BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask));
+ return stack::push(L, lua_nil);
}
- return mem_content;
-}
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
+};
-//-------------------------------------------------
-// region_write - templated region writer for <sign>,<size>
-// -> manager:machine():memory().regions[":maincpu"]:write_u16(0xC000, 0xF00D)
-//-------------------------------------------------
-template <typename T>
-void lua_engine::region_write(memory_region &region, offs_t address, T val)
+template <>
+struct usertype_container<plugin_options_plugins> : lua_engine::immutable_sequence_helper<plugin_options_plugins, std::list<plugin_options::plugin> >
{
- offs_t lowmask = region.bytewidth() - 1;
- for(int i = 0; i < sizeof(T); i++)
+private:
+ using plugin_list = std::list<plugin_options::plugin>;
+
+public:
+ static int get(lua_State *L)
{
- 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;
+ plugin_options_plugins &self(get_self(L));
+ char const *const name(stack::unqualified_get<char const *>(L));
+ auto const found(std::find_if(
+ self.options.plugins().begin(),
+ self.options.plugins().end(),
+ [&name] (plugin_options::plugin const &p) { return p.m_name == name; }));
+ if (self.options.plugins().end() != found)
+ return stack::push_reference(L, std::cref(*found));
else
- region.base()[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
- val >>= 8;
+ return stack::push(L, lua_nil);
}
+
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
+};
+
+} // namespace sol
+
+
+int sol_lua_push(sol::types<std::error_condition>, lua_State *L, std::error_condition &&value)
+{
+ if (!value)
+ return sol::stack::push(L, sol::lua_nil);
+ else
+ return sol::stack::push(L, value.message());
}
-//-------------------------------------------------
-// share_read - templated share readers for <sign>,<size>
-// -> manager:machine():memory().shares[":maincpu"]:read_i8(0xC000)
-//-------------------------------------------------
-template <typename T>
-T lua_engine::share_read(memory_share &share, offs_t address)
+int sol_lua_push(sol::types<screen_type_enum>, lua_State *L, screen_type_enum &&value)
{
- T mem_content = 0;
- offs_t lowmask = share.bytewidth() - 1;
- uint8_t* ptr = (uint8_t*)share.ptr();
- for(int i = 0; i < sizeof(T); i++)
+ switch (value)
{
- 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)];
+ case SCREEN_TYPE_INVALID: return sol::stack::push(L, "invalid");
+ case SCREEN_TYPE_RASTER: return sol::stack::push(L, "raster");
+ case SCREEN_TYPE_VECTOR: return sol::stack::push(L, "vector");
+ case SCREEN_TYPE_LCD: return sol::stack::push(L, "svg");
+ case SCREEN_TYPE_SVG: return sol::stack::push(L, "none");
}
-
- return mem_content;
+ return sol::stack::push(L, "unknown");
}
+
//-------------------------------------------------
-// share_write - templated share writer for <sign>,<size>
-// -> manager:machine():memory().shares[":maincpu"]:write_u16(0xC000, 0xF00D)
+// process_snapshot_filename - processes a snapshot
+// filename
//-------------------------------------------------
-template <typename T>
-void lua_engine::share_write(memory_share &share, offs_t address, T val)
+static std::string process_snapshot_filename(running_machine &machine, const char *s)
{
- offs_t lowmask = share.bytewidth() - 1;
- uint8_t* ptr = (uint8_t*)share.ptr();
- for(int i = 0; i < sizeof(T); i++)
+ std::string result(s);
+ if (!osd_is_absolute_path(s))
{
- 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_LE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
- val >>= 8;
+ strreplace(result, "/", PATH_SEPARATOR);
+ strreplace(result, "%g", machine.basename());
}
+ return result;
}
+
//-------------------------------------------------
// lua_engine - constructor
//-------------------------------------------------
lua_engine::lua_engine()
+ : m_lua_state(nullptr)
+ , m_machine(nullptr)
+ , m_timer(nullptr)
{
- m_machine = nullptr;
- m_lua_state = luaL_newstate(); /* create state */
+ m_lua_state = luaL_newstate(); // create state
m_sol_state = std::make_unique<sol::state_view>(m_lua_state); // create sol view
+ m_notifiers.emplace();
luaL_checkversion(m_lua_state);
- lua_gc(m_lua_state, LUA_GCSTOP, 0); /* stop collector during initialization */
+ lua_gc(m_lua_state, LUA_GCSTOP, 0); // stop collector during initialization
sol().open_libraries();
// Get package.preload so we can store builtins in it.
@@ -638,76 +508,86 @@ sol::object lua_engine::call_plugin(const std::string &name, sol::object in)
{
std::string field = "cb_" + name;
sol::object obj = sol().registry()[field];
- if(obj.is<sol::protected_function>())
+ if (obj.is<sol::protected_function>())
{
auto res = invoke(obj.as<sol::protected_function>(), in);
- if(!res.valid())
+ if (!res.valid())
{
sol::error err = res;
osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what());
}
else
+ {
return res.get<sol::object>();
+ }
}
- return sol::make_object(sol(), sol::nil);
+ return sol::lua_nil;
}
-void lua_engine::menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string>> &menu_list)
+std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string> > &menu_list, std::string &flags)
{
std::string field = "menu_pop_" + menu;
sol::object obj = sol().registry()[field];
- if(obj.is<sol::protected_function>())
+ if (obj.is<sol::protected_function>())
{
auto res = invoke(obj.as<sol::protected_function>());
- if(!res.valid())
+ if (!res.valid())
{
sol::error err = res;
osd_printf_error("[LUA ERROR] in menu_populate: %s\n", err.what());
}
else
{
- sol::table table = res;
- for(auto &entry : table)
+ std::tuple<sol::table, std::optional<long>, std::optional<std::string> > table = res;
+ for (auto &entry : std::get<0>(table))
{
- if(entry.second.is<sol::table>())
+ if (entry.second.is<sol::table>())
{
sol::table enttable = entry.second.as<sol::table>();
menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3));
}
}
+ if (std::get<2>(table))
+ flags = *std::get<2>(table);
+ else
+ flags.clear();
+ return std::get<1>(table);
}
}
+ flags.clear();
+ return std::nullopt;
}
-bool lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
+std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
{
std::string field = "menu_cb_" + menu;
- bool ret = false;
+ std::pair<std::optional<bool>, std::optional<long> > ret(false, std::nullopt);
sol::object obj = sol().registry()[field];
- if(obj.is<sol::protected_function>())
+ if (obj.is<sol::protected_function>())
{
auto res = invoke(obj.as<sol::protected_function>(), index, event);
- if(!res.valid())
+ if (!res.valid())
{
sol::error err = res;
osd_printf_error("[LUA ERROR] in menu_callback: %s\n", err.what());
}
else
+ {
ret = res;
+ }
}
- return ret;
+ return std::make_pair(std::get<0>(ret) && *std::get<0>(ret), std::get<1>(ret));
}
void lua_engine::set_machine(running_machine *machine)
{
- if (!machine || (machine != m_machine))
- m_seq_poll.reset();
m_machine = machine;
}
-int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback)
+template <typename T>
+size_t lua_engine::enumerate_functions(const char *id, T &&callback)
{
- int count = 0;
+ size_t count = 0;
sol::object functable = sol().registry()[id];
if (functable.is<sol::table>())
{
@@ -721,23 +601,24 @@ int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol
break;
}
}
- return true;
}
return count;
}
bool lua_engine::execute_function(const char *id)
{
- int count = enumerate_functions(id, [this](const sol::protected_function &func)
- {
- auto ret = invoke(func);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
- }
- return true;
- });
+ size_t count = enumerate_functions(
+ id,
+ [this] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func);
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
+ }
+ return true;
+ });
return count > 0;
}
@@ -755,13 +636,25 @@ void lua_engine::on_machine_prestart()
execute_function("LUA_ON_PRESTART");
}
-void lua_engine::on_machine_start()
+void lua_engine::on_machine_reset()
{
+ m_notifiers->on_reset();
execute_function("LUA_ON_START");
}
void lua_engine::on_machine_stop()
{
+ // clear waiting tasks
+ m_timer = nullptr;
+ std::vector<int> expired;
+ expired.reserve(m_waiting_tasks.size());
+ for (auto const &waiting : m_waiting_tasks)
+ expired.emplace_back(waiting.second);
+ m_waiting_tasks.clear();
+ resume_tasks(m_lua_state, expired, false);
+ expired.clear();
+
+ m_notifiers->on_stop();
execute_function("LUA_ON_STOP");
}
@@ -772,22 +665,45 @@ void lua_engine::on_machine_before_load_settings()
void lua_engine::on_machine_pause()
{
+ m_notifiers->on_pause();
execute_function("LUA_ON_PAUSE");
}
void lua_engine::on_machine_resume()
{
+ m_notifiers->on_resume();
execute_function("LUA_ON_RESUME");
}
void lua_engine::on_machine_frame()
{
+ std::vector<int> tasks = std::move(m_frame_tasks);
+ m_frame_tasks.clear();
+ resume_tasks(m_lua_state, tasks, true); // TODO: doesn't need to return anything
+
+ m_notifiers->on_frame();
+
execute_function("LUA_ON_FRAME");
}
-void lua_engine::on_frame_done()
+void lua_engine::on_machine_presave()
+{
+ m_notifiers->on_presave();
+}
+
+void lua_engine::on_machine_postload()
{
- execute_function("LUA_ON_FRAME_DONE");
+ // clear waiting tasks
+ m_timer->reset();
+ std::vector<int> expired;
+ expired.reserve(m_waiting_tasks.size());
+ for (auto const &waiting : m_waiting_tasks)
+ expired.emplace_back(waiting.second);
+ m_waiting_tasks.clear();
+ resume_tasks(m_lua_state, expired, false);
+ expired.clear();
+
+ m_notifiers->on_postload();
}
void lua_engine::on_sound_update()
@@ -803,32 +719,38 @@ void lua_engine::on_periodic()
bool lua_engine::on_missing_mandatory_image(const std::string &instance_name)
{
bool handled = false;
- enumerate_functions("LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE", [this, &instance_name, &handled](const sol::protected_function &func)
- {
- auto ret = invoke(func, instance_name);
+ enumerate_functions(
+ "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE",
+ [this, &instance_name, &handled] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func, instance_name);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
- }
- else if (ret.get<bool>())
- {
- handled = true;
- }
- return !handled;
- });
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
+ }
+ else if (ret.get<bool>())
+ {
+ handled = true;
+ }
+ return !handled;
+ });
return handled;
}
void lua_engine::attach_notifiers()
{
machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_prestart, this), true);
- machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_start, this));
+ machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_reset, this));
machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&lua_engine::on_machine_stop, this));
machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&lua_engine::on_machine_pause, this));
machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&lua_engine::on_machine_resume, this));
machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&lua_engine::on_machine_frame, this));
+ machine().save().register_presave(save_prepost_delegate(FUNC(lua_engine::on_machine_presave), this));
+ machine().save().register_postload(save_prepost_delegate(FUNC(lua_engine::on_machine_postload), this));
+
+ m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(lua_engine::resume), this));
}
//-------------------------------------------------
@@ -838,6 +760,21 @@ void lua_engine::attach_notifiers()
void lua_engine::initialize()
{
+ static const enum_parser<movie_recording::format, 2> s_movie_recording_format_parser =
+ {
+ { "avi", movie_recording::format::AVI },
+ { "mng", movie_recording::format::MNG }
+ };
+
+
+ static const enum_parser<int, 3> s_seek_parser =
+ {
+ { "set", SEEK_SET },
+ { "cur", SEEK_CUR },
+ { "end", SEEK_END }
+ };
+
+
/* emu library
*
* emu.app_name() - return application name
@@ -854,9 +791,6 @@ void lua_engine::initialize()
* emu.unpause() - unpause emulation
* emu.step() - advance one frame
* emu.keypost(keys) - post keys to natural keyboard
- * emu.wait(len) - wait for len within coroutine
- * emu.lang_translate(str) - get translation for str if available
- * emu.subst_env(str) - substitute environment variables with values for str
*
* emu.register_prestart(callback) - register callback before reset
* emu.register_start(callback) - register callback after reset
@@ -873,21 +807,96 @@ void lua_engine::initialize()
* emu.register_before_load_settings(callback) - register callback to be run before settings are loaded
* emu.show_menu(menu_name) - show menu by name and pause the machine
*
- * emu.print_verbose(str) - output to stderr at verbose level
- * emu.print_error(str) - output to stderr at error level
- * emu.print_info(str) - output to stderr at info level
- * emu.print_debug(str) - output to stderr at debug level
+ * emu.device_enumerator(dev) - get device enumerator starting at arbitrary point in tree
+ * emu.screen_enumerator(dev) - get screen device enumerator starting at arbitrary point in tree
+ * emu.image_enumerator(dev) - get image interface enumerator starting at arbitrary point in tree
+ * emu.image_enumerator(dev) - get image interface enumerator starting at arbitrary point in tree
*/
sol::table emu = sol().create_named_table("emu");
+ emu["wait"] = sol::yielding(
+ [this] (sol::this_state s, sol::object duration, sol::variadic_args args)
+ {
+ attotime delay;
+ if (!duration)
+ {
+ luaL_error(s, "waiting duration expected");
+ }
+ else if (duration.is<attotime>())
+ {
+ delay = duration.as<attotime>();
+ }
+ else
+ {
+ auto seconds = duration.as<std::optional<double> >();
+ if (!seconds)
+ luaL_error(s, "waiting duration must be attotime or number");
+ delay = attotime::from_double(*seconds);
+ }
+ attotime const expiry = machine().time() + delay;
+
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ int const ref = luaL_ref(s, LUA_REGISTRYINDEX);
+
+ auto const pos = std::upper_bound(
+ m_waiting_tasks.begin(),
+ m_waiting_tasks.end(),
+ expiry,
+ [] (auto const &a, auto const &b) { return a < b.first; });
+ if (m_waiting_tasks.begin() == pos)
+ m_timer->reset(delay);
+ m_waiting_tasks.emplace(pos, expiry, ref);
+
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu["wait_next_update"] = sol::yielding(
+ [this] (sol::this_state s, sol::variadic_args args)
+ {
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ m_update_tasks.emplace_back(luaL_ref(s, LUA_REGISTRYINDEX));
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu["wait_next_frame"] = sol::yielding(
+ [this] (sol::this_state s, sol::variadic_args args)
+ {
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ m_frame_tasks.emplace_back(luaL_ref(s, LUA_REGISTRYINDEX));
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu.set_function("add_machine_reset_notifier", make_notifier_adder(m_notifiers->on_reset, "machine reset"));
+ emu.set_function("add_machine_stop_notifier", make_notifier_adder(m_notifiers->on_stop, "machine stop"));
+ emu.set_function("add_machine_pause_notifier", make_notifier_adder(m_notifiers->on_pause, "machine pause"));
+ emu.set_function("add_machine_resume_notifier", make_notifier_adder(m_notifiers->on_resume, "machine resume"));
+ emu.set_function("add_machine_frame_notifier", make_notifier_adder(m_notifiers->on_frame, "machine frame"));
+ emu.set_function("add_machine_pre_save_notifier", make_notifier_adder(m_notifiers->on_presave, "machine pre-save"));
+ emu.set_function("add_machine_post_load_notifier", make_notifier_adder(m_notifiers->on_postload, "machine post-load"));
+ emu.set_function("print_error", [] (const char *str) { osd_printf_error("%s\n", str); });
+ emu.set_function("print_warning", [] (const char *str) { osd_printf_warning("%s\n", str); });
+ emu.set_function("print_info", [] (const char *str) { osd_printf_info("%s\n", str); });
+ emu.set_function("print_verbose", [] (const char *str) { osd_printf_verbose("%s\n", str); });
+ emu.set_function("print_debug", [] (const char *str) { osd_printf_debug("%s\n", str); });
+ emu["lang_translate"] = sol::overload(
+ static_cast<char const *(*)(char const *)>(&lang_translate),
+ static_cast<char const *(*)(char const *, char const *)>(&lang_translate));
+ emu.set_function("subst_env", &osd_subst_env);
+
+ // TODO: stuff below here needs to be rationalised
emu["app_name"] = &emulator_info::get_appname_lower;
emu["app_version"] = &emulator_info::get_bare_build_version;
- emu["gamename"] = [this](){ return machine().system().type.fullname(); };
- emu["romname"] = [this](){ return machine().basename(); };
- emu["softname"] = [this]() { return machine().options().software_name(); };
- emu["keypost"] = [this](const char *keys){ machine().ioport().natkeyboard().post_utf8(keys); };
- emu["time"] = [this](){ return machine().time().as_double(); };
- emu["start"] = [this](const char *driver) {
+ emu["gamename"] = [this] () { return machine().system().type.fullname(); };
+ emu["romname"] = [this] () { return machine().basename(); };
+ emu["softname"] = [this] () { return machine().options().software_name(); };
+ emu["keypost"] = [this] (const char *keys) { machine().natkeyboard().post_utf8(keys); };
+ emu["time"] = [this] () { return machine().time().as_double(); };
+ emu["start"] =
+ [this](const char *driver)
+ {
int i = driver_list::find(driver);
if (i != -1)
{
@@ -896,69 +905,111 @@ void lua_engine::initialize()
}
return 1;
};
- emu["pause"] = [this](){ return machine().pause(); };
- emu["unpause"] = [this](){ return machine().resume(); };
- emu["step"] = [this]() {
+ emu["pause"] = [this] () { return machine().pause(); };
+ emu["unpause"] = [this] () { return machine().resume(); };
+ emu["step"] =
+ [this] ()
+ {
mame_machine_manager::instance()->ui().set_single_step(true);
machine().resume();
};
- emu["register_prestart"] = [this](sol::function func){ register_function(func, "LUA_ON_PRESTART"); };
- emu["register_start"] = [this](sol::function func){ register_function(func, "LUA_ON_START"); };
- emu["register_stop"] = [this](sol::function func){ register_function(func, "LUA_ON_STOP"); };
- emu["register_pause"] = [this](sol::function func){ register_function(func, "LUA_ON_PAUSE"); };
- emu["register_resume"] = [this](sol::function func){ register_function(func, "LUA_ON_RESUME"); };
- emu["register_frame"] = [this](sol::function func){ register_function(func, "LUA_ON_FRAME"); };
- emu["register_frame_done"] = [this](sol::function func){ register_function(func, "LUA_ON_FRAME_DONE"); };
- emu["register_sound_update"] = [this](sol::function func){ register_function(func, "LUA_ON_SOUND_UPDATE"); };
- emu["register_periodic"] = [this](sol::function func){ register_function(func, "LUA_ON_PERIODIC"); };
- emu["register_mandatory_file_manager_override"] = [this](sol::function func) { register_function(func, "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE"); };
+ emu["register_prestart"] = [this] (sol::function func) { register_function(func, "LUA_ON_PRESTART"); };
+ emu["register_start"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_start is deprecated - please use emu.add_machine_reset_notifier\n"); register_function(func, "LUA_ON_START"); };
+ emu["register_stop"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_stop is deprecated - please use emu.add_machine_stop_notifier\n"); register_function(func, "LUA_ON_STOP"); };
+ emu["register_pause"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_pause is deprecated - please use emu.add_machine_pause_notifier\n"); register_function(func, "LUA_ON_PAUSE"); };
+ emu["register_resume"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_resume is deprecated - please use emu.add_machine_resume_notifier\n"); register_function(func, "LUA_ON_RESUME"); };
+ emu["register_frame"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_frame is deprecated - please use emu.add_machine_frame_notifier\n"); register_function(func, "LUA_ON_FRAME"); };
+ emu["register_frame_done"] = [this] (sol::function func) { register_function(func, "LUA_ON_FRAME_DONE"); };
+ emu["register_sound_update"] = [this] (sol::function func) { register_function(func, "LUA_ON_SOUND_UPDATE"); };
+ emu["register_periodic"] = [this] (sol::function func) { register_function(func, "LUA_ON_PERIODIC"); };
+ emu["register_mandatory_file_manager_override"] = [this] (sol::function func) { register_function(func, "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE"); };
emu["register_before_load_settings"] = [this](sol::function func) { register_function(func, "LUA_ON_BEFORE_LOAD_SETTINGS"); };
- emu["register_menu"] = [this](sol::function cb, sol::function pop, const std::string &name) {
+ emu["register_menu"] =
+ [this] (sol::function cb, sol::function pop, const std::string &name)
+ {
std::string cbfield = "menu_cb_" + name;
std::string popfield = "menu_pop_" + name;
sol().registry()[cbfield] = cb;
sol().registry()[popfield] = pop;
m_menu.push_back(name);
};
- emu["show_menu"] = [this](const char *name) {
+ emu["show_menu"] =
+ [this](const char *name)
+ {
mame_ui_manager &mui = mame_machine_manager::instance()->ui();
render_container &container = machine().render().ui_container();
- ui::menu_plugin::show_menu(mui, container, (char *)name);
+ ui::menu_plugin::show_menu(mui, container, name);
};
- emu["register_callback"] = [this](sol::function cb, const std::string &name) {
+ emu["register_callback"] =
+ [this] (sol::function cb, const std::string &name)
+ {
std::string field = "cb_" + name;
sol().registry()[field] = cb;
};
- emu["print_verbose"] = [](const char *str) { osd_printf_verbose("%s\n", str); };
- emu["print_error"] = [](const char *str) { osd_printf_error("%s\n", str); };
- emu["print_info"] = [](const char *str) { osd_printf_info("%s\n", str); };
- emu["print_debug"] = [](const char *str) { osd_printf_debug("%s\n", str); };
- emu["driver_find"] = [this](const char *driver) -> sol::object {
- int i = driver_list::find(driver);
- if(i == -1)
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), driver_list::driver(i));
+ emu["osd_ticks"] = &osd_ticks;
+ emu["osd_ticks_per_second"] = &osd_ticks_per_second;
+ emu["driver_find"] =
+ [] (sol::this_state s, const char *driver) -> sol::object
+ {
+ const int i = driver_list::find(driver);
+ if (i < 0)
+ return sol::lua_nil;
+ return sol::make_object(s, driver_list::driver(i));
};
- emu["wait"] = lua_CFunction([](lua_State *L) {
- lua_engine *engine = mame_machine_manager::instance()->lua();
- luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
- int ret = lua_pushthread(L);
- if(ret == 1)
- return luaL_error(L, "cannot wait from outside coroutine");
- int ref = luaL_ref(L, LUA_REGISTRYINDEX);
- engine->machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), engine), ref, nullptr);
- return lua_yield(L, 0);
- });
- emu["lang_translate"] = &lang_translate;
emu["pid"] = &osd_getpid;
- emu["subst_env"] = [](const std::string &str) {
- std::string result;
- osd_subst_env(result, str);
- return result;
- };
-
-
-/* emu_file library
+ emu["device_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<device_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<device_enumerator>(dev, maxdepth); });
+ emu["palette_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<palette_interface_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<palette_interface_enumerator>(dev, maxdepth); });
+ emu["screen_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<screen_device_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<screen_device_enumerator>(dev, maxdepth); });
+ emu["cassette_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<cassette_device_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<cassette_device_enumerator>(dev, maxdepth); });
+ emu["image_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<image_interface_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<image_interface_enumerator>(dev, maxdepth); });
+ emu["slot_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<slot_interface_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<slot_interface_enumerator>(dev, maxdepth); });
+
+
+ auto notifier_subscription_type = sol().registry().new_usertype<util::notifier_subscription>("notifier_subscription", sol::no_constructor);
+ notifier_subscription_type["unsubscribe"] = &util::notifier_subscription::reset;
+ notifier_subscription_type["is_active"] = sol::property(&util::notifier_subscription::operator bool);
+
+ auto attotime_type = emu.new_usertype<attotime>(
+ "attotime",
+ sol::call_constructor, sol::constructors<attotime(), attotime(seconds_t, attoseconds_t), attotime(attotime const &)>());
+ attotime_type["from_double"] = &attotime::from_double;
+ attotime_type["from_ticks"] = static_cast<attotime (*)(u64, u32)>(&attotime::from_ticks);
+ attotime_type["from_seconds"] = &attotime::from_seconds;
+ attotime_type["from_msec"] = &attotime::from_msec;
+ attotime_type["from_usec"] = &attotime::from_usec;
+ attotime_type["from_nsec"] = &attotime::from_nsec;
+ attotime_type["as_double"] = &attotime::as_double;
+ attotime_type["as_hz"] = &attotime::as_hz;
+ attotime_type["as_khz"] = &attotime::as_khz;
+ attotime_type["as_mhz"] = &attotime::as_mhz;
+ attotime_type["as_ticks"] = static_cast<u64 (attotime::*)(u32) const>(&attotime::as_ticks);
+ attotime_type["is_zero"] = sol::property(&attotime::is_zero);
+ attotime_type["is_never"] = sol::property(&attotime::is_never);
+ attotime_type["attoseconds"] = sol::property(&attotime::attoseconds);
+ attotime_type["seconds"] = sol::property(&attotime::seconds);
+ attotime_type["msec"] = sol::property([] (attotime const &t) { return t.attoseconds() / ATTOSECONDS_PER_MILLISECOND; });
+ attotime_type["usec"] = sol::property([] (attotime const &t) { return t.attoseconds() / ATTOSECONDS_PER_MICROSECOND; });
+ attotime_type["nsec"] = sol::property([] (attotime const &t) { return t.attoseconds() / ATTOSECONDS_PER_NANOSECOND; });
+ attotime_type[sol::meta_function::to_string] = &attotime::to_string;
+ attotime_type[sol::meta_function::addition] = static_cast<attotime (*)(attotime const &, attotime const &)>(&operator+);
+ attotime_type[sol::meta_function::subtraction] = static_cast<attotime (*)(attotime const &, attotime const &)>(&operator-);
+ attotime_type[sol::meta_function::multiplication] = static_cast<attotime (*)(attotime const &, u32)>(&operator*);
+ attotime_type[sol::meta_function::division] = static_cast<attotime (*)(attotime const &, u32)>(&operator/);
+
+
+/* emu_file library
*
* emu.file([opt] searchpath, flags) - flags can be as in osdcore "OPEN_FLAG_*" or lua style
* with 'rwc' with addtional c for create *and truncate*
@@ -976,7 +1027,7 @@ void lua_engine::initialize()
* file:fullpath() -
*/
- auto file_type = emu.create_simple_usertype<emu_file>(sol::call_constructor, sol::initializers(
+ auto file_type = emu.new_usertype<emu_file>("file", sol::call_constructor, sol::initializers(
[](emu_file &file, u32 flags) { new (&file) emu_file(flags); },
[](emu_file &file, const char *path, u32 flags) { new (&file) emu_file(path, flags); },
[](emu_file &file, const char *mode) {
@@ -1017,56 +1068,47 @@ void lua_engine::initialize()
}
new (&file) emu_file(path, flags);
}));
- file_type.set("read", [](emu_file &file, sol::buffer *buff) { buff->set_len(file.read(buff->get_ptr(), buff->get_len())); return buff; });
+ file_type.set("read",
+ [] (emu_file &file, sol::this_state s, size_t len)
+ {
+ buffer_helper buf(s);
+ auto space = buf.prepare(len);
+ space.add(file.read(space.get(), len));
+ buf.push();
+ return sol::make_reference(s, sol::stack_reference(s, -1));
+ });
file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); });
- file_type.set("open", static_cast<osd_file::error (emu_file::*)(const std::string &)>(&emu_file::open));
+ file_type.set("puts", &emu_file::puts);
+ file_type.set("open", static_cast<std::error_condition (emu_file::*)(std::string_view)>(&emu_file::open));
file_type.set("open_next", &emu_file::open_next);
+ file_type.set("close", &emu_file::close);
file_type.set("seek", sol::overload(
[](emu_file &file) { return file.tell(); },
- [this](emu_file &file, s64 offset, int whence) -> sol::object {
+ [this] (emu_file &file, s64 offset, int whence) -> sol::object {
if(file.seek(offset, whence))
- return sol::make_object(sol(), sol::nil);
+ return sol::lua_nil;
else
return sol::make_object(sol(), file.tell());
},
[this](emu_file &file, const char* whence) -> sol::object {
- int wval = -1;
- const char *seekdirs[] = {"set", "cur", "end"};
- for(int i = 0; i < 3; i++)
- {
- if(!strncmp(whence, seekdirs[i], 3))
- {
- wval = i;
- break;
- }
- }
+ int wval = s_seek_parser(whence);
if(wval < 0 || wval >= 3)
- return sol::make_object(sol(), sol::nil);
+ return sol::lua_nil;
if(file.seek(0, wval))
- return sol::make_object(sol(), sol::nil);
+ return sol::lua_nil;
return sol::make_object(sol(), file.tell());
},
[this](emu_file &file, const char* whence, s64 offset) -> sol::object {
- int wval = -1;
- const char *seekdirs[] = {"set", "cur", "end"};
- for(int i = 0; i < 3; i++)
- {
- if(!strncmp(whence, seekdirs[i], 3))
- {
- wval = i;
- break;
- }
- }
+ int wval = s_seek_parser(whence);
if(wval < 0 || wval >= 3)
- return sol::make_object(sol(), sol::nil);
+ return sol::lua_nil;
if(file.seek(offset, wval))
- return sol::make_object(sol(), sol::nil);
+ return sol::lua_nil;
return sol::make_object(sol(), file.tell());
}));
file_type.set("size", &emu_file::size);
file_type.set("filename", &emu_file::filename);
file_type.set("fullpath", &emu_file::fullpath);
- emu.set_usertype("file", file_type);
/* thread library
@@ -1078,65 +1120,17 @@ void lua_engine::initialize()
* thread runs until yield() and/or terminates on return.
* thread:continue(val) - resume thread that has yielded and pass val to it
*
- * thread.result - get result of a terminated thread as string
- * thread.busy - check if thread is running
- * thread.yield - check if thread is yielded
+ * thread.result - get result of a terminated or yielding thread as string
+ * thread.busy - check if thread is running or yielding
+ * thread.yield - check if thread is yielding
*/
- auto thread_type = emu.create_simple_usertype<context>(sol::call_constructor, sol::constructors<sol::types<>>());
- thread_type.set("start", [](context &ctx, const char *scr) {
- std::string script(scr);
- if(ctx.busy)
- return false;
- std::thread th([&ctx, script]() {
- sol::state thstate;
- thstate.open_libraries();
- thstate["package"]["preload"]["zlib"] = &luaopen_zlib;
- thstate["package"]["preload"]["lfs"] = &luaopen_lfs;
- thstate["package"]["preload"]["linenoise"] = &luaopen_linenoise;
- sol::load_result res = thstate.load(script);
- if(res.valid())
- {
- sol::protected_function func = res.get<sol::protected_function>();
- thstate["yield"] = [&ctx, &thstate]() {
- std::mutex m;
- std::unique_lock<std::mutex> lock(m);
- ctx.result = thstate["status"];
- ctx.yield = true;
- ctx.sync.wait(lock);
- ctx.yield = false;
- thstate["status"] = ctx.result;
- };
- auto ret = func();
- if (ret.valid()) {
- const char *tmp = ret.get<const char *>();
- if (tmp != nullptr)
- ctx.result = tmp;
- else
- exit(0);
- }
- }
- ctx.busy = false;
- });
- ctx.busy = true;
- ctx.yield = false;
- th.detach();
- return true;
- });
- thread_type.set("continue", [](context &ctx, const char *val) {
- if(!ctx.yield)
- return;
- ctx.result = val;
- ctx.sync.notify_all();
- });
- thread_type.set("result", sol::property([](context &ctx) -> std::string {
- if(ctx.busy && !ctx.yield)
- return "";
- return ctx.result;
- }));
- thread_type.set("busy", sol::readonly(&context::busy));
- thread_type.set("yield", sol::readonly(&context::yield));
- emu.set_usertype("thread", thread_type);
+ auto thread_type = emu.new_usertype<thread_context>("thread", sol::call_constructor, sol::constructors<sol::types<>>());
+ thread_type.set_function("start", &thread_context::start);
+ thread_type.set_function("continue", &thread_context::resume);
+ thread_type["result"] = sol::property(&thread_context::result);
+ thread_type["busy"] = sol::property(&thread_context::busy);
+ thread_type["yield"] = sol::readonly(&thread_context::m_yield);
/* save_item library
@@ -1151,7 +1145,7 @@ void lua_engine::initialize()
* item:write(offset, value) - write entry value by index
*/
- auto item_type = emu.create_simple_usertype<save_item>(sol::call_constructor, sol::initializers([this](save_item &item, int index) {
+ auto item_type = emu.new_usertype<save_item>("item", sol::call_constructor, sol::initializers([this](save_item &item, int index) {
if(machine().save().indexed_item(index, item.base, item.size, item.valcount, item.blockcount, item.stride))
{
item.count = item.valcount * item.blockcount;
@@ -1168,12 +1162,14 @@ void lua_engine::initialize()
}));
item_type.set("size", sol::readonly(&save_item::size));
item_type.set("count", sol::readonly(&save_item::count));
- item_type.set("read", [this](save_item &item, int offset) -> sol::object {
- if(!item.base || (offset >= item.count))
- return sol::make_object(sol(), sol::nil);
+ item_type.set("read",
+ [this] (save_item &item, int offset) -> sol::object
+ {
+ if (!item.base || (offset >= item.count))
+ return sol::lua_nil;
const void *const data = reinterpret_cast<const uint8_t *>(item.base) + (item.stride * (offset / item.valcount));
uint64_t ret = 0;
- switch(item.size)
+ switch (item.size)
{
case 1:
default:
@@ -1191,29 +1187,38 @@ void lua_engine::initialize()
}
return sol::make_object(sol(), ret);
});
- item_type.set("read_block", [](save_item &item, int offset, sol::buffer *buff) {
- if(!item.base || ((offset + buff->get_len()) > (item.size * item.count)))
+ item_type.set("read_block",
+ [] (save_item &item, sol::this_state s, uint32_t offset, size_t len) -> sol::object
+ {
+ if (!item.base)
{
- buff->set_len(0);
+ luaL_error(s, "Invalid save item");
+ return sol::lua_nil;
}
- else
+
+ if ((offset + len) > (item.size * item.count))
{
- const uint32_t blocksize = item.size * item.valcount;
- uint32_t remaining = buff->get_len();
- uint8_t *dest = reinterpret_cast<uint8_t *>(buff->get_ptr());
- while(remaining)
- {
- const uint32_t blockno = offset / blocksize;
- const uint32_t available = blocksize - (offset % blocksize);
- const uint32_t chunk = (available < remaining) ? available : remaining;
- const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
- std::memcpy(dest, source, chunk);
- offset += chunk;
- remaining -= chunk;
- dest += chunk;
- }
+ luaL_error(s, "Range extends beyond end of save item");
+ return sol::lua_nil;
}
- return buff;
+
+ luaL_Buffer buff;
+ uint8_t *dest = reinterpret_cast<uint8_t *>(luaL_buffinitsize(s, &buff, len));
+ const uint32_t blocksize = item.size * item.valcount;
+ size_t remaining = len;
+ while (remaining)
+ {
+ const uint32_t blockno = offset / blocksize;
+ const uint32_t available = blocksize - (offset % blocksize);
+ const uint32_t chunk = (available < remaining) ? available : remaining;
+ const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
+ std::memcpy(dest, source, chunk);
+ offset += chunk;
+ remaining -= chunk;
+ dest += chunk;
+ }
+ luaL_pushresultsize(&buff, len);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
});
item_type.set("write", [](save_item &item, int offset, uint64_t value) {
if(!item.base || (offset >= item.count))
@@ -1236,10 +1241,9 @@ void lua_engine::initialize()
break;
}
});
- emu.set_usertype("item", item_type);
-/* core_options library
+/* core_options library
*
* manager:options()
* manager:machine():options()
@@ -1252,7 +1256,7 @@ void lua_engine::initialize()
* options.entries[] - get table of option entries (k=name, v=core_options::entry)
*/
- auto core_options_type = sol().registry().create_simple_usertype<core_options>("new", sol::no_constructor);
+ auto core_options_type = sol().registry().new_usertype<core_options>("core_options", "new", sol::no_constructor);
core_options_type.set("help", &core_options::output_help);
core_options_type.set("command", &core_options::command);
core_options_type.set("entries", sol::property([this](core_options &options) {
@@ -1275,10 +1279,48 @@ void lua_engine::initialize()
}
return table;
}));
- sol().registry().set_usertype("core_options", core_options_type);
-/* core_options::entry library
+/* emu_options library
+ *
+ * manager:options()
+ * manager:machine():options()
+ *
+ * options:slot_option(tag) - retrieves a specific slot option
+ */
+
+ auto emu_options_type = sol().registry().new_usertype<emu_options>("emu_options", sol::no_constructor, sol::base_classes, sol::bases<core_options>());
+ emu_options_type["slot_option"] = [] (emu_options &opts, std::string const &name) { return opts.find_slot_option(name); };
+
+
+/* slot_option library
+ *
+ * manager:options():slot_option("name")
+ * manager:machine():options():slot_option("name")
+ *
+ * slot_option:specify(card, bios) - specifies the value of the slot, potentially causing a recalculation
+ *
+ * slot_option.value - the actual value of the option, after being interpreted
+ * slot_option.specified_value - the value of the option, as specified from outside
+ * slot_option.bios - the bios, if any, associated with the slot
+ * slot_option.default_card_software - the software list item that is associated with this option, by default
+ */
+
+ auto slot_option_type = sol().registry().new_usertype<slot_option>("slot_option", sol::no_constructor);
+ slot_option_type["specify"] =
+ [] (slot_option &opt, std::string &&text, char const *bios)
+ {
+ opt.specify(std::move(text));
+ if (bios)
+ opt.set_bios(bios);
+ };
+ slot_option_type["value"] = sol::property(&slot_option::value);
+ slot_option_type["specified_value"] = sol::property(&slot_option::specified_value);
+ slot_option_type["bios"] = sol::property(&slot_option::bios);
+ slot_option_type["default_card_software"] = sol::property(&slot_option::default_card_software);
+
+
+/* core_options::entry library
*
* options.entries[entry_name]
*
@@ -1291,35 +1333,35 @@ void lua_engine::initialize()
* entry:has_range() - are min and max valid for entry
*/
- auto core_options_entry_type = sol().registry().create_simple_usertype<core_options::entry>("new", sol::no_constructor);
+ auto core_options_entry_type = sol().registry().new_usertype<core_options::entry>("core_options_entry", "new", sol::no_constructor);
core_options_entry_type.set("value", sol::overload(
[this](core_options::entry &e, bool val) {
- if(e.type() != OPTION_BOOLEAN)
+ if(e.type() != core_options::option_type::BOOLEAN)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(val ? "1" : "0", OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, float val) {
- if(e.type() != OPTION_FLOAT)
+ if(e.type() != core_options::option_type::FLOAT)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(string_format("%f", val), OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, int val) {
- if(e.type() != OPTION_INTEGER)
+ if(e.type() != core_options::option_type::INTEGER)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(string_format("%d", val), OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, const char *val) {
- if(e.type() != OPTION_STRING)
+ if(e.type() != core_options::option_type::STRING && e.type() != core_options::option_type::PATH && e.type() != core_options::option_type::MULTIPATH)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(val, OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e) -> sol::object {
if (e.type() == core_options::option_type::INVALID)
- return sol::make_object(sol(), sol::nil);
+ return sol::lua_nil;
switch(e.type())
{
case core_options::option_type::BOOLEAN:
@@ -1337,1456 +1379,727 @@ void lua_engine::initialize()
core_options_entry_type.set("minimum", &core_options::entry::minimum);
core_options_entry_type.set("maximum", &core_options::entry::maximum);
core_options_entry_type.set("has_range", &core_options::entry::has_range);
- sol().registry().set_usertype("core_options_entry", core_options_entry_type);
-
-/* running_machine library
- *
- * manager:machine()
- *
- * machine:exit() - close program
- * machine:hard_reset() - hard reset emulation
- * machine:soft_reset() - soft reset emulation
- * machine:save(filename) - save state to filename
- * machine:load(filename) - load state from filename
- * machine:buffer_save() - return save state buffer as binary string
- * machine:buffer_load(str) - load state from binary string buffer. returns true on success, otherwise nil
- * machine:popmessage(str) - print str as popup
- * machine:popmessage() - clear displayed popup message
- * machine:logerror(str) - print str to log
- * machine:system() - get game_driver for running driver
- * machine:video() - get video_manager
- * machine:sound() - get sound_manager
- * machine:render() - get render_manager
- * machine:ioport() - get ioport_manager
- * machine:parameters() - get parameter_manager
- * machine:memory() - get memory_manager
- * machine:options() - get machine core_options
- * machine:outputs() - get output_manager
- * machine:input() - get input_manager
- * machine:uiinput() - get ui_input_manager
- * machine:debugger() - get debugger_manager
- *
- * machine.paused - get paused state
- * machine.samplerate - get audio sample rate
- * machine.exit_pending
- * machine.hard_reset_pending
- *
- * machine.devices[] - get device table (k=tag, v=device_t)
- * machine.screens[] - get screens table (k=tag, v=screen_device)
- * machine.images[] - get available image devices table (k=type, v=device_image_interface)
- */
- auto machine_type = sol().registry().create_simple_usertype<running_machine>("new", sol::no_constructor);
- machine_type.set("exit", &running_machine::schedule_exit);
- machine_type.set("hard_reset", &running_machine::schedule_hard_reset);
- machine_type.set("soft_reset", &running_machine::schedule_soft_reset);
- machine_type.set("save", &running_machine::schedule_save);
- machine_type.set("load", &running_machine::schedule_load);
- machine_type.set("buffer_save", [](running_machine &m, sol::this_state s) {
- lua_State *L = s;
- luaL_Buffer buff;
- int size = ram_state::get_size(m.save());
- u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
- save_error error = m.save().write_buffer(ptr, size);
- if (error == STATERR_NONE)
+ auto machine_type = sol().registry().new_usertype<running_machine>("machine", sol::no_constructor);
+ machine_type.set_function("exit", &running_machine::schedule_exit);
+ machine_type.set_function("hard_reset", &running_machine::schedule_hard_reset);
+ machine_type.set_function("soft_reset", &running_machine::schedule_soft_reset);
+ machine_type.set_function("save", &running_machine::schedule_save); // TODO: some kind of completion notification?
+ machine_type.set_function("load", &running_machine::schedule_load); // TODO: some kind of completion notification?
+ machine_type.set_function("buffer_save",
+ [] (running_machine &m, sol::this_state s)
{
- luaL_pushresultsize(&buff, size);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- }
- luaL_error(L, "State save error.");
- return sol::make_reference(L, nullptr);
- });
- machine_type.set("buffer_load", [](running_machine &m, sol::this_state s, std::string str) {
- lua_State *L = s;
- save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
- if (error == STATERR_NONE)
- return true;
- else
- {
- luaL_error(L,"State load error.");
- return false;
- }
- });
- machine_type.set("system", &running_machine::system);
- machine_type.set("video", &running_machine::video);
- machine_type.set("sound", &running_machine::sound);
- machine_type.set("render", &running_machine::render);
- machine_type.set("ioport", &running_machine::ioport);
- machine_type.set("parameters", &running_machine::parameters);
- machine_type.set("memory", &running_machine::memory);
- machine_type.set("options", [](running_machine &m) { return static_cast<core_options *>(&m.options()); });
- machine_type.set("outputs", &running_machine::output);
- machine_type.set("input", &running_machine::input);
- machine_type.set("uiinput", &running_machine::ui_input);
- machine_type.set("debugger", [this](running_machine &m) -> sol::object {
- if(!(m.debug_flags & DEBUG_FLAG_ENABLED))
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), &m.debugger());
- });
- machine_type.set("paused", sol::property(&running_machine::paused));
- machine_type.set("samplerate", sol::property(&running_machine::sample_rate));
- machine_type.set("exit_pending", sol::property(&running_machine::exit_pending));
- machine_type.set("hard_reset_pending", sol::property(&running_machine::hard_reset_pending));
- machine_type.set("devices", sol::property([this](running_machine &m) {
- std::function<void(device_t &, sol::table)> tree;
- sol::table table = sol().create_table();
- tree = [&tree](device_t &root, sol::table table) {
- for(device_t &dev : root.subdevices())
+ // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ lua_State *L = s;
+ luaL_Buffer buff;
+ int size = ram_state::get_size(m.save());
+ u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
+ save_error error = m.save().write_buffer(ptr, size);
+ if (error == STATERR_NONE)
{
- if(dev.configured() && dev.started())
- {
- table[dev.tag()] = &dev;
- tree(dev, table);
- }
+ luaL_pushresultsize(&buff, size);
+ return sol::make_reference(L, sol::stack_reference(L, -1));
}
- };
- tree(m.root_device(), table);
- return table;
- }));
- machine_type.set("screens", sol::property([this](running_machine &r) {
- sol::table table = sol().create_table();
- for (screen_device &sc : screen_device_iterator(r.root_device()))
+ luaL_error(L, "State save error.");
+ return sol::make_reference(L, nullptr);
+ });
+ machine_type.set_function("buffer_load",
+ [] (running_machine &m, sol::this_state s, std::string str)
{
- if (sc.configured() && sc.started() && sc.type())
- table[sc.tag()] = &sc;
- }
- return table;
- }));
- machine_type.set("images", sol::property([this](running_machine &r) {
- sol::table image_table = sol().create_table();
- for(device_image_interface &image : image_interface_iterator(r.root_device()))
+ // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
+ if (error == STATERR_NONE)
+ {
+ return true;
+ }
+ else
+ {
+ luaL_error(s,"State load error.");
+ return false;
+ }
+ });
+ machine_type.set_function("popmessage",
+ [] (running_machine &m, std::optional<const char *> str)
{
- image_table[image.brief_instance_name()] = &image;
- image_table[image.instance_name()] = &image;
- }
- return image_table;
- }));
- machine_type.set("popmessage", sol::overload(
- [](running_machine &m, const char *str) { m.popmessage("%s", str); },
- [](running_machine &m) { m.popmessage(); }));
- machine_type.set("logerror", [](running_machine &m, const char *str) { m.logerror("[luaengine] %s\n", str); } );
- sol().registry().set_usertype("machine", machine_type);
-
-
-/* game_driver library
- *
- * emu.driver_find(driver_name)
- *
- * driver.source_file - relative path to the source file
- * driver.parent
- * driver.name
- * driver.description
- * driver.year
- * driver.manufacturer
- * driver.compatible_with
- * driver.default_layout
- * driver.orientation - screen rotation degree (rot0/90/180/270)
- * driver.type - machine type (arcade/console/computer/other)
- * driver.not_working - not considered working
- * driver.supports_save - supports save states
- * driver.no_cocktail - screen flip support is missing
- * driver.is_bios_root - this driver entry is a BIOS root
- * driver.requires_artwork - requires external artwork for key game elements
- * driver.clickable_artwork - artwork is clickable and requires mouse cursor
- * driver.unofficial - unofficial hardware modification
- * driver.no_sound_hw - system has no sound output
- * driver.mechanical - contains mechanical parts (pinball, redemption games, ...)
- * driver.is_incomplete - official system with blatantly incomplete hardware/software
- */
-
- auto game_driver_type = sol().registry().create_simple_usertype<game_driver>("new", sol::no_constructor);
- game_driver_type.set("source_file", sol::property([] (game_driver const &driver) { return &driver.type.source()[0]; }));
- game_driver_type.set("parent", sol::readonly(&game_driver::parent));
- game_driver_type.set("name", sol::property([] (game_driver const &driver) { return &driver.name[0]; }));
- game_driver_type.set("description", sol::property([] (game_driver const &driver) { return &driver.type.fullname()[0]; }));
- game_driver_type.set("year", sol::readonly(&game_driver::year));
- game_driver_type.set("manufacturer", sol::readonly(&game_driver::manufacturer));
- game_driver_type.set("compatible_with", sol::readonly(&game_driver::compatible_with));
- game_driver_type.set("default_layout", sol::readonly(&game_driver::default_layout));
- game_driver_type.set("orientation", sol::property([](game_driver const &driver) {
- std::string rot;
- switch (driver.flags & machine_flags::MASK_ORIENTATION)
+ if (str)
+ m.popmessage("%s", *str);
+ else
+ m.popmessage();
+ });
+ machine_type.set_function("logerror", [] (running_machine &m, char const *str) { m.logerror("[luaengine] %s\n", str); });
+ machine_type["time"] = sol::property(&running_machine::time);
+ machine_type["system"] = sol::property(&running_machine::system);
+ machine_type["parameters"] = sol::property(&running_machine::parameters);
+ machine_type["video"] = sol::property(&running_machine::video);
+ machine_type["sound"] = sol::property(&running_machine::sound);
+ machine_type["output"] = sol::property(&running_machine::output);
+ machine_type["memory"] = sol::property(&running_machine::memory);
+ machine_type["ioport"] = sol::property(&running_machine::ioport);
+ machine_type["input"] = sol::property(&running_machine::input);
+ machine_type["natkeyboard"] = sol::property(&running_machine::natkeyboard);
+ machine_type["uiinput"] = sol::property(&running_machine::ui_input);
+ machine_type["render"] = sol::property(&running_machine::render);
+ machine_type["debugger"] = sol::property(
+ [] (running_machine &m, sol::this_state s) -> sol::object
{
- case machine_flags::ROT0:
- rot = "rot0";
- break;
- case machine_flags::ROT90:
- rot = "rot90";
- break;
- case machine_flags::ROT180:
- rot = "rot180";
- break;
- case machine_flags::ROT270:
- rot = "rot270";
- break;
- default:
- rot = "undefined";
- break;
- }
- return rot;
- }));
- game_driver_type.set("type", sol::property([](game_driver const &driver) {
- std::string type;
- switch (driver.flags & machine_flags::MASK_TYPE)
+ if (m.debug_flags & DEBUG_FLAG_ENABLED)
+ return sol::make_object(s, &m.debugger());
+ else
+ return sol::lua_nil;
+ });
+ machine_type["options"] = sol::property(&running_machine::options);
+ machine_type["samplerate"] = sol::property(&running_machine::sample_rate);
+ machine_type["paused"] = sol::property(&running_machine::paused);
+ machine_type["exit_pending"] = sol::property(&running_machine::exit_pending);
+ machine_type["hard_reset_pending"] = sol::property(&running_machine::hard_reset_pending);
+ machine_type["devices"] = sol::property([] (running_machine &m) { return devenum<device_enumerator>(m.root_device()); });
+ machine_type["palettes"] = sol::property([] (running_machine &m) { return devenum<palette_interface_enumerator>(m.root_device()); });
+ machine_type["screens"] = sol::property([] (running_machine &m) { return devenum<screen_device_enumerator>(m.root_device()); });
+ machine_type["cassettes"] = sol::property([] (running_machine &m) { return devenum<cassette_device_enumerator>(m.root_device()); });
+ machine_type["images"] = sol::property([] (running_machine &m) { return devenum<image_interface_enumerator>(m.root_device()); });
+ machine_type["slots"] = sol::property([](running_machine &m) { return devenum<slot_interface_enumerator>(m.root_device()); });
+
+
+ auto game_driver_type = sol().registry().new_usertype<game_driver>("game_driver", sol::no_constructor);
+ game_driver_type["name"] = sol::property([] (game_driver const &driver) { return &driver.name[0]; });
+ game_driver_type["description"] = sol::property([] (game_driver const &driver) { return &driver.type.fullname()[0]; });
+ game_driver_type["year"] = sol::readonly(&game_driver::year);
+ game_driver_type["manufacturer"] = sol::readonly(&game_driver::manufacturer);
+ game_driver_type["parent"] = sol::readonly(&game_driver::parent);
+ game_driver_type["compatible_with"] = sol::property([] (game_driver const &driver) { return strcmp(driver.compatible_with, "0") ? driver.compatible_with : nullptr; });
+ game_driver_type["source_file"] = sol::property([] (game_driver const &driver) { return driver.type.source(); });
+ game_driver_type["orientation"] = sol::property(
+ [] (game_driver const &driver)
{
- case machine_flags::TYPE_ARCADE:
- type = "arcade";
- break;
- case machine_flags::TYPE_CONSOLE:
- type = "console";
- break;
- case machine_flags::TYPE_COMPUTER:
- type = "computer";
- break;
- default:
- type = "other";
- break;
- }
- return type;
- }));
- game_driver_type.set("not_working", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) > 0; }));
- game_driver_type.set("supports_save", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) > 0; }));
- game_driver_type.set("no_cocktail", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) > 0; }));
- game_driver_type.set("is_bios_root", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) > 0; }));
- game_driver_type.set("requires_artwork", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) > 0; }));
- game_driver_type.set("clickable_artwork", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) > 0; }));
- game_driver_type.set("unofficial", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) > 0; }));
- game_driver_type.set("no_sound_hw", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) > 0; }));
- game_driver_type.set("mechanical", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) > 0; }));
- game_driver_type.set("is_incomplete", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::IS_INCOMPLETE) > 0; } ));
- sol().registry().set_usertype("game_driver", game_driver_type);
-
-
-/* debugger_manager library (requires debugger to be active)
- *
- * manager:machine():debugger()
- *
- * debugger:command(command_string) - run command_string in debugger console
- *
- * debugger.consolelog[] - get consolelog text buffer (wrap_textbuf)
- * debugger.errorlog[] - get errorlog text buffer (wrap_textbuf)
- * debugger.visible_cpu - accessor for debugger active cpu for commands, affects debug views
- * debugger.execution_state - accessor for active cpu run state
- */
-
- struct wrap_textbuf { wrap_textbuf(const text_buffer &buf) : textbuf(buf) { } std::reference_wrapper<const text_buffer> textbuf; };
-
- auto debugger_type = sol().registry().create_simple_usertype<debugger_manager>("new", sol::no_constructor);
- debugger_type.set("command", [](debugger_manager &debug, const std::string &cmd) { debug.console().execute_command(cmd, false); });
- debugger_type.set("consolelog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); }));
- debugger_type.set("errorlog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); }));
- debugger_type.set("visible_cpu", sol::property(
- [](debugger_manager &debug) { debug.console().get_visible_cpu(); },
- [](debugger_manager &debug, device_t &dev) { debug.console().set_visible_cpu(&dev); }));
- debugger_type.set("execution_state", sol::property(
- [](debugger_manager &debug) {
- return debug.cpu().is_stopped() ? "stop" : "run";
- },
- [](debugger_manager &debug, const std::string &state) {
- if(state == "stop")
- debug.cpu().set_execution_stopped();
- else
- debug.cpu().set_execution_running();
- }));
- sol().registry().set_usertype("debugger", debugger_type);
-
-
-/* wrap_textbuf library (requires debugger to be active)
- *
- * manager:machine():debugger().consolelog
- * manager:machine():debugger().errorlog
- *
- * log[index] - get log entry
- * #log - entry count
- */
-
- sol().registry().new_usertype<wrap_textbuf>("text_buffer", "new", sol::no_constructor,
- "__metatable", [](){},
- "__newindex", [](){},
- "__index", [](wrap_textbuf &buf, int index) { return text_buffer_get_seqnum_line(buf.textbuf, index - 1); },
- "__len", [](wrap_textbuf &buf) { return text_buffer_num_lines(buf.textbuf) + text_buffer_line_index_to_seqnum(buf.textbuf, 0) - 1; });
-
-/* device_debug library (requires debugger to be active)
- *
- * manager:machine().devices[device_tag]:debug()
- *
- * debug:step([opt] steps) - run cpu steps, default 1
- * debug:go() - run cpu
- * debug:bpset(addr, [opt] cond, [opt] act) - set breakpoint on addr, cond and act are debugger
- * expressions. returns breakpoint index
- * debug:bpclr(idx) - clear break
- * debug:bplist()[] - table of breakpoints (k=index, v=debug_breakpoint)
- * debug:wpset(space, type, addr, len, [opt] cond, [opt] act) - set watchpoint, cond and act
- * are debugger expressions.
- * returns watchpoint index
- * debug:wpclr(idx) - clear watch
- * debug:wplist(space)[] - table of watchpoints (k=index, v=watchpoint)
- */
-
- auto device_debug_type = sol().registry().create_simple_usertype<device_debug>("new", sol::no_constructor);
- device_debug_type.set("step", [](device_debug &dev, sol::object num) {
- int steps = 1;
- if(num.is<int>())
- steps = num.as<int>();
- dev.single_step(steps);
- });
- device_debug_type.set("go", &device_debug::go);
- device_debug_type.set("bpset", [](device_debug &dev, offs_t addr, const char *cond, const char *act) { return dev.breakpoint_set(addr, cond, act); });
- device_debug_type.set("bpclr", &device_debug::breakpoint_clear);
- device_debug_type.set("bplist", [this](device_debug &dev) {
- sol::table table = sol().create_table();
- for(const auto &bpp : dev.breakpoint_list())
+ // FIXME: this works differently to the screen orientation function and the render target orientation property
+ // it should probably be made consistent with one of them
+ std::string rot;
+ switch (driver.flags & machine_flags::MASK_ORIENTATION)
+ {
+ case machine_flags::ROT0:
+ rot = "rot0";
+ break;
+ case machine_flags::ROT90:
+ rot = "rot90";
+ break;
+ case machine_flags::ROT180:
+ rot = "rot180";
+ break;
+ case machine_flags::ROT270:
+ rot = "rot270";
+ break;
+ default:
+ rot = "undefined";
+ break;
+ }
+ return rot;
+ });
+ game_driver_type["not_working"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) != 0; });
+ game_driver_type["supports_save"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) != 0; });
+ game_driver_type["no_cocktail"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) != 0; });
+ game_driver_type["is_bios_root"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) != 0; });
+ game_driver_type["requires_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) != 0; });
+ game_driver_type["unofficial"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) != 0; });
+ game_driver_type["no_sound_hw"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) != 0; });
+ game_driver_type["mechanical"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) != 0; });
+ game_driver_type["is_incomplete"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_INCOMPLETE) != 0; });
+
+
+ auto device_type = sol().registry().new_usertype<device_t>("device", sol::no_constructor);
+ device_type.set_function(sol::meta_function::to_string, [] (device_t &d) { return util::string_format("%s(%s)", d.shortname(), d.tag()); });
+ device_type.set_function("subtag", &device_t::subtag);
+ device_type.set_function("siblingtag", &device_t::siblingtag);
+ device_type.set_function("memregion", &device_t::memregion);
+ device_type.set_function("memshare", &device_t::memshare);
+ device_type.set_function("membank", &device_t::membank);
+ device_type.set_function("ioport", &device_t::ioport);
+ device_type.set_function("subdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice));
+ device_type.set_function("siblingdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice));
+ device_type.set_function("parameter", &device_t::parameter);
+ device_type["tag"] = sol::property(&device_t::tag);
+ device_type["basetag"] = sol::property(&device_t::basetag);
+ device_type["name"] = sol::property(&device_t::name);
+ device_type["shortname"] = sol::property(&device_t::shortname);
+ device_type["owner"] = sol::property(&device_t::owner);
+ device_type["configured"] = sol::property(&device_t::configured);
+ device_type["started"] = sol::property(&device_t::started);
+ device_type["debug"] = sol::property(
+ [] (device_t &dev, sol::this_state s) -> sol::object
{
- const debug_breakpoint &bpt = *bpp.second;
- sol::table bp = sol().create_table();
- bp["enabled"] = bpt.enabled();
- bp["address"] = bpt.address();
- bp["condition"] = bpt.condition();
- bp["action"] = bpt.action();
- table[bpt.index()] = bp;
- }
- return table;
- });
- device_debug_type.set("wpset", [](device_debug &dev, addr_space &sp, const std::string &type, offs_t addr, offs_t len, const char *cond, const char *act) {
- read_or_write wptype = s_read_or_write_parser(type);
- return dev.watchpoint_set(sp.space, wptype, addr, len, cond, act);
- });
- device_debug_type.set("wpclr", &device_debug::watchpoint_clear);
- device_debug_type.set("wplist", [this](device_debug &dev, addr_space &sp) {
- sol::table table = sol().create_table();
- for(auto &wpp : dev.watchpoint_vector(sp.space.spacenum()))
+ if (!(dev.machine().debug_flags & DEBUG_FLAG_ENABLED) || !dynamic_cast<cpu_device *>(&dev)) // debugger not enabled or not CPU
+ return sol::lua_nil;
+ return sol::make_object(s, dev.debug());
+ });
+ device_type["spaces"] = sol::property(
+ [this] (device_t &dev)
{
- sol::table wp = sol().create_table();
- wp["enabled"] = wpp->enabled();
- wp["address"] = wpp->address();
- wp["length"] = wpp->length();
- switch(wpp->type())
+ device_memory_interface *const memdev = dynamic_cast<device_memory_interface *>(&dev);
+ sol::table sp_table = sol().create_table();
+ if (!memdev)
+ return sp_table;
+ for (int sp = 0; sp < memdev->max_space_count(); ++sp)
{
- case read_or_write::READ:
- wp["type"] = "r";
- break;
- case read_or_write::WRITE:
- wp["type"] = "w";
- break;
- case read_or_write::READWRITE:
- wp["type"] = "rw";
- break;
- default: // huh?
- wp["type"] = "";
- break;
+ if (memdev->has_space(sp))
+ sp_table[memdev->space(sp).name()] = addr_space(memdev->space(sp), *memdev);
}
- wp["condition"] = wpp->condition();
- wp["action"] = wpp->action();
- table[wpp->index()] = wp;
- }
- return table;
- });
- sol().registry().set_usertype("device_debug", device_debug_type);
-
-
-/* device_t library
- *
- * manager:machine().devices[device_tag]
- *
- * device:name() - device long name
- * device:shortname() - device short name
- * device:tag() - device tree tag
- * device:owner() - device parent tag
- * device:debug() - debug interface, cpus only
- *
- * device.spaces[] - device address spaces table (k=name, v=addr_space)
- * device.state[] - device state entries table (k=name, v=device_state_entry)
- * device.items[] - device save state items table (k=name, v=index)
- * device.roms[] - device rom entry table (k=name, v=rom_entry)
- */
-
- auto device_type = sol().registry().create_simple_usertype<device_t>("new", sol::no_constructor);
- device_type.set("name", &device_t::name);
- device_type.set("shortname", &device_t::shortname);
- device_type.set("tag", &device_t::tag);
- device_type.set("owner", &device_t::owner);
- device_type.set("debug", [this](device_t &dev) -> sol::object {
- if(!(dev.machine().debug_flags & DEBUG_FLAG_ENABLED) || !dynamic_cast<cpu_device *>(&dev)) // debugger not enabled or not cpu
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), dev.debug());
- });
- device_type.set("spaces", sol::property([this](device_t &dev) {
- device_memory_interface *memdev = dynamic_cast<device_memory_interface *>(&dev);
- sol::table sp_table = sol().create_table();
- if(!memdev)
return sp_table;
- for(int sp = 0; sp < memdev->max_space_count(); ++sp)
+ });
+ device_type["state"] = sol::property(
+ [] (device_t &dev, sol::this_state s) -> sol::object
{
- if(memdev->has_space(sp))
- sp_table[memdev->space(sp).name()] = addr_space(memdev->space(sp), *memdev);
- }
- return sp_table;
- }));
- device_type.set("state", sol::property([this](device_t &dev) {
- sol::table st_table = sol().create_table();
- if(!dynamic_cast<device_state_interface *>(&dev))
- return st_table;
- // XXX: refrain from exporting non-visible entries?
- for(auto &s : dev.state().state_entries())
- st_table[s->symbol()] = s.get();
- return st_table;
- }));
- device_type.set("items", sol::property([this](device_t &dev) {
- sol::table table = sol().create_table();
- std::string tag = dev.tag();
- // 10000 is enough?
- for(int i = 0; i < 10000; i++)
+ device_state_interface const *state;
+ if (!dev.interface(state))
+ return sol::lua_nil;
+ return sol::make_object(s, device_state_entries(*state));
+ });
+ // FIXME: turn into a wrapper - it's stupid slow to walk on every property access
+ // also, this mixes up things like RAM areas with stuff saved by the device itself, so there's potential for key conflicts
+ device_type["items"] = sol::property(
+ [this] (device_t &dev)
{
- std::string name;
- const char *item;
- void *base;
- uint32_t size, valcount, blockcount, stride;
- item = dev.machine().save().indexed_item(i, base, size, valcount, blockcount, stride);
- if(!item)
- break;
- name = &(strchr(item, '/')[1]);
- if(name.substr(0, name.find('/')) == tag)
+ sol::table table = sol().create_table();
+ std::string const tag = dev.tag();
+ for (int i = 0; ; i++)
{
- name = name.substr(name.find('/') + 1, std::string::npos);
- table[name] = i;
- }
- }
- return table;
- }));
- device_type.set("roms", sol::property([this](device_t &dev) {
- sol::table table = sol().create_table();
- for(auto rom : dev.rom_region_vector())
- if(!rom.name().empty())
- table[rom.name()] = rom;
- return table;
- }));
- sol().registry().set_usertype("device", device_type);
+ char const *item;
+ void *base;
+ uint32_t size, valcount, blockcount, stride;
+ item = dev.machine().save().indexed_item(i, base, size, valcount, blockcount, stride);
+ if (!item)
+ break;
+ char const *name = &strchr(item, '/')[1];
+ if (!strncmp(tag.c_str(), name, tag.length()) && (name[tag.length()] == '/'))
+ table[name + tag.length() + 1] = i;
+ }
+ return table;
+ });
+ // FIXME: this is useless in its current form
+ device_type["roms"] = sol::property(
+ [this] (device_t &dev)
+ {
+ sol::table table = sol().create_table();
+ for (auto rom : dev.rom_region_vector())
+ if (!rom.name().empty())
+ table[rom.name()] = rom;
+ return table;
+ });
-/* addr_space library
- *
- * manager:machine().devices[device_tag].spaces[space]
- *
- * read/write by signedness u/i and bit-width 8/16/32/64:
- * space:read_*(addr)
- * space:write_*(addr, val)
- * space:read_log_*(addr)
- * space:write_log_*(addr, val)
- * space:read_direct_*(addr)
- * space:write_direct_*(addr, val)
- * space:read_range(first_addr, last_addr, width, [opt] step) - read range of addresses and
- * return as a binary string
- *
- * space.name - address space name
- * space.shift - address bus shift, bitshift required for a bytewise address
- * to map onto this space's address resolution (addressing granularity).
- * positive value means leftshift, negative means rightshift.
- * space.index
- * space.address_mask
- * space.data_width
- * space.endianness
- *
- * space.map[] - table of address map entries (k=index, v=address_map_entry)
- */
- auto addr_space_type = sol().registry().create_simple_usertype<addr_space>(sol::call_constructor, sol::constructors<sol::types<address_space &, device_memory_interface &>>());
- addr_space_type.set("read_i8", &addr_space::mem_read<int8_t>);
- addr_space_type.set("read_u8", &addr_space::mem_read<uint8_t>);
- addr_space_type.set("read_i16", &addr_space::mem_read<int16_t>);
- addr_space_type.set("read_u16", &addr_space::mem_read<uint16_t>);
- addr_space_type.set("read_i32", &addr_space::mem_read<int32_t>);
- addr_space_type.set("read_u32", &addr_space::mem_read<uint32_t>);
- addr_space_type.set("read_i64", &addr_space::mem_read<int64_t>);
- addr_space_type.set("read_u64", &addr_space::mem_read<uint64_t>);
- addr_space_type.set("write_i8", &addr_space::mem_write<int8_t>);
- addr_space_type.set("write_u8", &addr_space::mem_write<uint8_t>);
- addr_space_type.set("write_i16", &addr_space::mem_write<int16_t>);
- addr_space_type.set("write_u16", &addr_space::mem_write<uint16_t>);
- addr_space_type.set("write_i32", &addr_space::mem_write<int32_t>);
- addr_space_type.set("write_u32", &addr_space::mem_write<uint32_t>);
- addr_space_type.set("write_i64", &addr_space::mem_write<int64_t>);
- addr_space_type.set("write_u64", &addr_space::mem_write<uint64_t>);
- addr_space_type.set("read_log_i8", &addr_space::log_mem_read<int8_t>);
- addr_space_type.set("read_log_u8", &addr_space::log_mem_read<uint8_t>);
- addr_space_type.set("read_log_i16", &addr_space::log_mem_read<int16_t>);
- addr_space_type.set("read_log_u16", &addr_space::log_mem_read<uint16_t>);
- addr_space_type.set("read_log_i32", &addr_space::log_mem_read<int32_t>);
- addr_space_type.set("read_log_u32", &addr_space::log_mem_read<uint32_t>);
- addr_space_type.set("read_log_i64", &addr_space::log_mem_read<int64_t>);
- addr_space_type.set("read_log_u64", &addr_space::log_mem_read<uint64_t>);
- addr_space_type.set("write_log_i8", &addr_space::log_mem_write<int8_t>);
- addr_space_type.set("write_log_u8", &addr_space::log_mem_write<uint8_t>);
- addr_space_type.set("write_log_i16", &addr_space::log_mem_write<int16_t>);
- addr_space_type.set("write_log_u16", &addr_space::log_mem_write<uint16_t>);
- addr_space_type.set("write_log_i32", &addr_space::log_mem_write<int32_t>);
- addr_space_type.set("write_log_u32", &addr_space::log_mem_write<uint32_t>);
- addr_space_type.set("write_log_i64", &addr_space::log_mem_write<int64_t>);
- addr_space_type.set("write_log_u64", &addr_space::log_mem_write<uint64_t>);
- addr_space_type.set("read_direct_i8", &addr_space::direct_mem_read<int8_t>);
- addr_space_type.set("read_direct_u8", &addr_space::direct_mem_read<uint8_t>);
- addr_space_type.set("read_direct_i16", &addr_space::direct_mem_read<int16_t>);
- addr_space_type.set("read_direct_u16", &addr_space::direct_mem_read<uint16_t>);
- addr_space_type.set("read_direct_i32", &addr_space::direct_mem_read<int32_t>);
- addr_space_type.set("read_direct_u32", &addr_space::direct_mem_read<uint32_t>);
- addr_space_type.set("read_direct_i64", &addr_space::direct_mem_read<int64_t>);
- addr_space_type.set("read_direct_u64", &addr_space::direct_mem_read<uint64_t>);
- addr_space_type.set("write_direct_i8", &addr_space::direct_mem_write<int8_t>);
- addr_space_type.set("write_direct_u8", &addr_space::direct_mem_write<uint8_t>);
- addr_space_type.set("write_direct_i16", &addr_space::direct_mem_write<int16_t>);
- addr_space_type.set("write_direct_u16", &addr_space::direct_mem_write<uint16_t>);
- addr_space_type.set("write_direct_i32", &addr_space::direct_mem_write<int32_t>);
- addr_space_type.set("write_direct_u32", &addr_space::direct_mem_write<uint32_t>);
- addr_space_type.set("write_direct_i64", &addr_space::direct_mem_write<int64_t>);
- addr_space_type.set("write_direct_u64", &addr_space::direct_mem_write<uint64_t>);
- addr_space_type.set("read_range", [](addr_space &sp, sol::this_state s, u64 first, u64 last, int width, sol::object opt_step) {
- lua_State *L = s;
- luaL_Buffer buff;
- offs_t space_size = sp.space.addrmask();
- u64 step = 1;
- if (opt_step.is<u64>())
+ auto dipalette_type = sol().registry().new_usertype<device_palette_interface>("dipalette", sol::no_constructor);
+ dipalette_type.set_function("pen", &device_palette_interface::pen);
+ dipalette_type.set_function(
+ "pen_color",
+ [] (device_palette_interface const &pal, pen_t pen)
{
- step = opt_step.as<u64>();
- if (step < 1 || step > last - first)
- {
- luaL_error(L, "Invalid step");
- return sol::make_reference(L, nullptr);
- }
- }
- if (first > space_size || last > space_size || last < first)
+ return uint32_t(pal.pen_color(pen));
+ });
+ dipalette_type.set_function("pen_contrast", &device_palette_interface::pen_contrast);
+ dipalette_type.set_function("pen_indirect", &device_palette_interface::pen_indirect);
+ dipalette_type.set_function(
+ "indirect_color",
+ [] (device_palette_interface const &pal, int index)
{
- luaL_error(L, "Invalid offset");
- return sol::make_reference(L, nullptr);
- }
- int byte_count = width / 8 * (last - first + 1) / step;
- switch (width)
+ return uint32_t(pal.indirect_color(index));
+ });
+ dipalette_type["set_pen_color"] = sol::overload(
+ [] (device_palette_interface &pal, pen_t pen, uint32_t color)
{
- case 8:
+ pal.set_pen_color(pen, rgb_t(color));
+ },
+ static_cast<void (device_palette_interface::*)(pen_t, uint8_t, uint8_t, uint8_t)>(&device_palette_interface::set_pen_color));
+ dipalette_type.set_function("set_pen_red_level", &device_palette_interface::set_pen_red_level);
+ dipalette_type.set_function("set_pen_green_level", &device_palette_interface::set_pen_green_level);
+ dipalette_type.set_function("set_pen_blue_level", &device_palette_interface::set_pen_blue_level);
+ dipalette_type.set_function("set_pen_contrast", &device_palette_interface::set_pen_contrast);
+ dipalette_type.set_function("set_pen_indirect", &device_palette_interface::set_pen_indirect);
+ dipalette_type["set_indirect_color"] = sol::overload(
+ [] (device_palette_interface &pal, int index, uint32_t color)
{
- u8 *dest = (u8 *)luaL_buffinitsize(L, &buff, byte_count);
- for(; first <= last; first += step)
- *dest++ = sp.mem_read<u8>(first);
- break;
- }
- case 16:
+ pal.set_indirect_color(index, rgb_t(color));
+ },
+ [] (device_palette_interface &pal, int index, uint8_t r, uint8_t g, uint8_t b)
{
- u16 *dest = (u16 *)luaL_buffinitsize(L, &buff, byte_count);
- for(; first <= last; first += step)
- *dest++ = sp.mem_read<u16>(first);
- break;
- }
- case 32:
+ pal.set_indirect_color(index, rgb_t(r, g, b));
+ });
+ dipalette_type.set_function("set_shadow_factor", &device_palette_interface::set_shadow_factor);
+ dipalette_type.set_function("set_highlight_factor", &device_palette_interface::set_highlight_factor);
+ dipalette_type.set_function("set_shadow_mode", &device_palette_interface::set_shadow_mode);
+ dipalette_type["palette"] = sol::property(
+ [] (device_palette_interface &pal)
{
- u32 *dest = (u32 *)luaL_buffinitsize(L, &buff, byte_count);
- for(; first <= last; first += step)
- *dest++ = sp.mem_read<u32>(first);
- break;
- }
- case 64:
+ return pal.palette()
+ ? std::optional<palette_wrapper>(std::in_place, *pal.palette())
+ : std::optional<palette_wrapper>();
+ });
+ dipalette_type["entries"] = sol::property(&device_palette_interface::entries);
+ dipalette_type["indirect_entries"] = sol::property(&device_palette_interface::indirect_entries);
+ dipalette_type["black_pen"] = sol::property(&device_palette_interface::black_pen);
+ dipalette_type["white_pen"] = sol::property(&device_palette_interface::white_pen);
+ dipalette_type["shadows_enabled"] = sol::property(&device_palette_interface::shadows_enabled);
+ dipalette_type["highlights_enabled"] = sol::property(&device_palette_interface::hilights_enabled);
+ dipalette_type["device"] = sol::property(static_cast<device_t & (device_palette_interface::*)()>(&device_palette_interface::device));
+
+
+ auto screen_dev_type = sol().registry().new_usertype<screen_device>(
+ "screen_dev",
+ sol::no_constructor,
+ sol::base_classes, sol::bases<device_t>());
+ screen_dev_type.set_function(
+ "draw_box",
+ [] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
- u64 *dest = (u64 *)luaL_buffinitsize(L, &buff, byte_count);
- for(; first <= last; first += step)
- *dest++ = sp.mem_read<u64>(first);
- break;
- }
- default:
- luaL_error(L, "Invalid width. Must be 8/16/32/64");
- return sol::make_reference(L, nullptr);
- }
- luaL_pushresultsize(&buff, byte_count);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- });
- addr_space_type.set("name", sol::property([](addr_space &sp) { return sp.space.name(); }));
- addr_space_type.set("shift", sol::property([](addr_space &sp) { return sp.space.addr_shift(); }));
- addr_space_type.set("index", sol::property([](addr_space &sp) { return sp.space.spacenum(); }));
- addr_space_type.set("address_mask", sol::property([](addr_space &sp) { return sp.space.addrmask(); }));
- addr_space_type.set("data_width", sol::property([](addr_space &sp) { return sp.space.data_width(); }));
- addr_space_type.set("endianness", sol::property([](addr_space &sp) {
- std::string endianness;
- switch (sp.space.endianness())
+ float const sc_width(sdev.visible_area().width());
+ float const sc_height(sdev.visible_area().height());
+ x1 = std::clamp(x1, 0.0f, sc_width) / sc_width;
+ y1 = std::clamp(y1, 0.0f, sc_height) / sc_height;
+ x2 = std::clamp(x2, 0.0f, sc_width) / sc_width;
+ y2 = std::clamp(y2, 0.0f, sc_height) / sc_height;
+ mame_ui_manager &ui(mame_machine_manager::instance()->ui());
+ if (!fgcolor)
+ fgcolor = ui.colors().text_color();
+ if (!bgcolor)
+ bgcolor = ui.colors().background_color();
+ ui.draw_outlined_box(sdev.container(), x1, y1, x2, y2, *fgcolor, *bgcolor);
+ });
+ screen_dev_type.set_function(
+ "draw_line",
+ [] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional<uint32_t> color)
{
- case endianness_t::ENDIANNESS_BIG:
- endianness = "big";
- break;
- case endianness_t::ENDIANNESS_LITTLE:
- endianness = "little";
- break;
- }
- return endianness;
- }));
-
-
-/* address_map_entry library
- *
- * manager:machine().devices[device_tag].spaces[space].map[entry_index]
- *
- * mapentry.offset - address start
- * mapentry.endoff - address end
- * mapentry.readtype
- * mapentry.writetype
- */
- addr_space_type.set("map", sol::property([this](addr_space &sp) {
- address_space &space = sp.space;
- sol::table map = sol().create_table();
- for (address_map_entry &entry : space.map()->m_entrylist)
+ float const sc_width(sdev.visible_area().width());
+ float const sc_height(sdev.visible_area().height());
+ x1 = std::clamp(x1, 0.0f, sc_width) / sc_width;
+ y1 = std::clamp(y1, 0.0f, sc_height) / sc_height;
+ x2 = std::clamp(x2, 0.0f, sc_width) / sc_width;
+ y2 = std::clamp(y2, 0.0f, sc_height) / sc_height;
+ if (!color)
+ color = mame_machine_manager::instance()->ui().colors().text_color();
+ sdev.container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(*color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ });
+ screen_dev_type.set_function(
+ "draw_text",
+ [this] (screen_device &sdev, sol::object xobj, float y, char const *msg, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
- sol::table mapentry = sol().create_table();
- mapentry["offset"] = entry.m_addrstart & space.addrmask();
- mapentry["endoff"] = entry.m_addrend & space.addrmask();
- mapentry["readtype"] = entry.m_read.m_type;
- mapentry["writetype"] = entry.m_write.m_type;
- map.add(mapentry);
- }
- return map;
- }));
- sol().registry().set_usertype("addr_space", addr_space_type);
-
-
-/* ioport_manager library
- *
- * manager:machine():ioport()
- *
- * ioport:count_players() - get count of player controllers
- * ioport:type_group(type, player)
- * ioport:type_seq(type, player, seqtype) - get input sequence for ioport type/player
- *
- * ioport.ports[] - ioports table (k=tag, v=ioport_port)
- */
-
- auto ioport_manager_type = sol().registry().create_simple_usertype<ioport_manager>("new", sol::no_constructor);
- ioport_manager_type.set("count_players", &ioport_manager::count_players);
- ioport_manager_type.set("natkeyboard", &ioport_manager::natkeyboard);
- ioport_manager_type.set("type_group", [](ioport_manager &im, ioport_type type, int player) {
- return im.type_group(type, player);
- });
- ioport_manager_type.set("ports", sol::property([this](ioport_manager &im) {
- sol::table port_table = sol().create_table();
- for (auto &port : im.ports())
- port_table[port.second->tag()] = port.second.get();
- return port_table;
- }));
- ioport_manager_type.set("type_seq", [](ioport_manager &m, ioport_type type, int player, input_seq_type seqtype) {
- return sol::make_user(m.type_seq(type, player, seqtype));
- });
- sol().registry().set_usertype("ioport", ioport_manager_type);
-
-
-/* natural_keyboard library
- *
- * manager:machine():ioport():natkeyboard()
- *
- * natkeyboard:paste() - paste clipboard data
- * natkeyboard:post() - post data to natural keyboard
- * natkeyboard:post_coded() - post data to natural keyboard
- *
- * natkeyboard.empty - is the natural keyboard buffer empty?
- * natkeyboard.in_use - is the natural keyboard in use?
- */
-
- auto natkeyboard_type = sol().registry().create_simple_usertype<natural_keyboard>("new", sol::no_constructor);
- natkeyboard_type.set("empty", sol::property(&natural_keyboard::empty));
- natkeyboard_type.set("in_use", sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use));
- natkeyboard_type.set("paste", &natural_keyboard::paste);
- natkeyboard_type.set("post", [](natural_keyboard &nat, const std::string &text) { nat.post_utf8(text); });
- natkeyboard_type.set("post_coded", [](natural_keyboard &nat, const std::string &text) { nat.post_coded(text); });
- sol().registry().set_usertype("natkeyboard", natkeyboard_type);
-
-
-/* ioport_port library
- *
- * manager:machine():ioport().ports[port_tag]
- *
- * port:tag() - get port tag
- * port:active() - get port status
- * port:live() - get port ioport_port_live (TODO: not usable from lua as of now)
- * port:read() - get port value
- * port:write(val, mask) - set port to value & mask (output fields only, for other fields use field:set_value(val))
- * port:field(mask) - get ioport_field for port and mask
- *
- * port.fields[] - get ioport_field table (k=name, v=ioport_field)
- */
-
- auto ioport_port_type = sol().registry().create_simple_usertype<ioport_port>("new", sol::no_constructor);
- ioport_port_type.set("tag", &ioport_port::tag);
- ioport_port_type.set("active", &ioport_port::active);
- ioport_port_type.set("live", &ioport_port::live);
- ioport_port_type.set("read", &ioport_port::read);
- ioport_port_type.set("write", &ioport_port::write);
- ioport_port_type.set("field", &ioport_port::field);
- ioport_port_type.set("fields", sol::property([this](ioport_port &p){
- sol::table f_table = sol().create_table();
- // parse twice for custom and default names, default has priority
- for(ioport_field &field : p.fields())
- {
- if (field.type_class() != INPUT_CLASS_INTERNAL)
- f_table[field.name()] = &field;
- }
- for(ioport_field &field : p.fields())
+ float const sc_width(sdev.visible_area().width());
+ float const sc_height(sdev.visible_area().height());
+ auto justify = ui::text_layout::text_justify::LEFT;
+ float x = 0;
+ if (xobj.is<float>())
+ {
+ x = std::clamp(xobj.as<float>(), 0.0f, sc_width) / sc_width;
+ }
+ else if (xobj.is<char const *>())
+ {
+ char const *const justifystr(xobj.as<char const *>());
+ if (!strcmp(justifystr, "left"))
+ justify = ui::text_layout::text_justify::LEFT;
+ else if (!strcmp(justifystr, "right"))
+ justify = ui::text_layout::text_justify::RIGHT;
+ else if (!strcmp(justifystr, "center"))
+ justify = ui::text_layout::text_justify::CENTER;
+ }
+ else
+ {
+ luaL_error(m_lua_state, "Error in param 1 to draw_text");
+ return;
+ }
+ y = std::clamp(y, 0.0f, sc_height) / sc_height;
+ mame_ui_manager &ui(mame_machine_manager::instance()->ui());
+ if (!fgcolor)
+ fgcolor = ui.colors().text_color();
+ if (!bgcolor)
+ bgcolor = 0;
+ ui.draw_text_full(
+ sdev.container(),
+ msg,
+ x, y, (1.0f - x),
+ justify, ui::text_layout::word_wrapping::WORD,
+ mame_ui_manager::OPAQUE_, *fgcolor, *bgcolor);
+ });
+ screen_dev_type.set_function(
+ "orientation",
+ [] (screen_device &sdev)
{
- if (field.type_class() != INPUT_CLASS_INTERNAL)
+ uint32_t flags = sdev.orientation();
+ int rotation_angle = 0;
+ switch (flags)
{
- if(field.specific_name())
- f_table[field.specific_name()] = &field;
- else
- f_table[field.manager().type_name(field.type(), field.player())] = &field;
+ case ORIENTATION_SWAP_XY:
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X:
+ rotation_angle = 90;
+ flags ^= ORIENTATION_FLIP_X;
+ break;
+ case ORIENTATION_FLIP_Y:
+ case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ rotation_angle = 180;
+ flags ^= ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y;
+ break;
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y:
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ rotation_angle = 270;
+ flags ^= ORIENTATION_FLIP_Y;
+ break;
}
- }
- return f_table;
- }));
- sol().registry().set_usertype("ioport_port", ioport_port_type);
-
-
-/* ioport_field library
- *
- * manager:machine():ioport().ports[port_tag].fields[field_name]
- *
- * field:set_value(value)
- * field:set_input_seq(seq_type, seq)
- * field:input_seq(seq_type)
- * field:set_default_input_seq(seq_type, seq)
- * field:default_input_seq(seq_type)
- * field:keyboard_codes(which)
- *
- * field.device - get associated device_t
- * field.port - get associated ioport_port
- * field.live - get ioport_field_live
- * field.name
- * field.default_name
- * field.player
- * field.mask
- * field.defvalue
- * field.sensitivity
- * field.way - amount of available directions
- * field.type_class
- * field.is_analog
- * field.is_digital_joystick
- * field.enabled
- * field.optional
- * field.cocktail
- * field.toggle - whether field is a toggle
- * field.rotated
- * field.analog_reverse
- * field.analog_reset
- * field.analog_wraps
- * field.analog_invert
- * field.impulse
- * field.type
- * field.crosshair_scale
- * field.crosshair_offset
- * field.user_value
- *
- * field.settings[] - ioport_setting table (k=value, v=name)
- */
-
- auto ioport_field_type = sol().registry().create_simple_usertype<ioport_field>("new", sol::no_constructor);
- ioport_field_type.set("set_value", &ioport_field::set_value);
- ioport_field_type.set("set_input_seq", [](ioport_field &f, const std::string &seq_type_string, sol::user<input_seq> seq) {
- input_seq_type seq_type = s_seq_type_parser(seq_type_string);
- ioport_field::user_settings settings;
- f.get_user_settings(settings);
- settings.seq[seq_type] = seq;
- f.set_user_settings(settings);
- });
- ioport_field_type.set("input_seq", [](ioport_field &f, const std::string &seq_type_string) {
- input_seq_type seq_type = s_seq_type_parser(seq_type_string);
- return sol::make_user(f.seq(seq_type));
- });
- ioport_field_type.set("set_default_input_seq", [](ioport_field &f, const std::string &seq_type_string, sol::user<input_seq> seq) {
- input_seq_type seq_type = s_seq_type_parser(seq_type_string);
- f.set_defseq(seq_type, seq);
- });
- ioport_field_type.set("default_input_seq", [](ioport_field &f, const std::string &seq_type_string) {
- input_seq_type seq_type = s_seq_type_parser(seq_type_string);
- return sol::make_user(f.defseq(seq_type));
- });
- ioport_field_type.set("keyboard_codes", [this](ioport_field &f, int which) {
- sol::table result = sol().create_table();
- int index = 1;
- for (char32_t code : f.keyboard_codes(which))
- result[index++] = code;
- return result;
- });
- ioport_field_type.set("device", sol::property(&ioport_field::device));
- ioport_field_type.set("port", sol::property(&ioport_field::port));
- ioport_field_type.set("name", sol::property(&ioport_field::name));
- ioport_field_type.set("default_name", sol::property([](ioport_field &f) {
- return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player());
- }));
- ioport_field_type.set("player", sol::property(&ioport_field::player, &ioport_field::set_player));
- ioport_field_type.set("mask", sol::property(&ioport_field::mask));
- ioport_field_type.set("defvalue", sol::property(&ioport_field::defvalue));
- ioport_field_type.set("sensitivity", sol::property(&ioport_field::sensitivity));
- ioport_field_type.set("way", sol::property(&ioport_field::way));
- ioport_field_type.set("type_class", sol::property([](ioport_field &f) {
- switch (f.type_class())
+ return std::tuple<int, bool, bool>(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y);
+ });
+ screen_dev_type["time_until_pos"] = sol::overload(
+ [] (screen_device &sdev, int vpos) { return sdev.time_until_pos(vpos).as_double(); },
+ [] (screen_device &sdev, int vpos, int hpos) { return sdev.time_until_pos(vpos, hpos).as_double(); });
+ screen_dev_type.set_function("time_until_vblank_start", &screen_device::time_until_vblank_start);
+ screen_dev_type.set_function("time_until_vblank_end", &screen_device::time_until_vblank_end);
+ screen_dev_type.set_function(
+ "snapshot",
+ [this] (screen_device &sdev, char const *filename) -> sol::object
{
- case INPUT_CLASS_KEYBOARD: return "keyboard";
- case INPUT_CLASS_CONTROLLER: return "controller";
- case INPUT_CLASS_CONFIG: return "config";
- case INPUT_CLASS_DIPSWITCH: return "dipswitch";
- case INPUT_CLASS_MISC: return "misc";
- default: break;
- }
- throw false;
- }));
- ioport_field_type.set("is_analog", sol::property(&ioport_field::is_analog));
- ioport_field_type.set("is_digital_joystick", sol::property(&ioport_field::is_digital_joystick));
- ioport_field_type.set("enabled", sol::property(&ioport_field::enabled));
- ioport_field_type.set("optional", sol::property(&ioport_field::optional));
- ioport_field_type.set("cocktail", sol::property(&ioport_field::cocktail));
- ioport_field_type.set("toggle", sol::property(&ioport_field::toggle));
- ioport_field_type.set("rotated", sol::property(&ioport_field::rotated));
- ioport_field_type.set("analog_reverse", sol::property(&ioport_field::analog_reverse));
- ioport_field_type.set("analog_reset", sol::property(&ioport_field::analog_reset));
- ioport_field_type.set("analog_wraps", sol::property(&ioport_field::analog_wraps));
- ioport_field_type.set("analog_invert", sol::property(&ioport_field::analog_invert));
- ioport_field_type.set("impulse", sol::property(&ioport_field::impulse));
- ioport_field_type.set("type", sol::property(&ioport_field::type));
- ioport_field_type.set("live", sol::property(&ioport_field::live));
- ioport_field_type.set("crosshair_scale", sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale));
- ioport_field_type.set("crosshair_offset", sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset));
- ioport_field_type.set("user_value", sol::property(
- [](ioport_field &f) {
- ioport_field::user_settings settings;
- f.get_user_settings(settings);
- return settings.value;
- },
- [](ioport_field &f, ioport_value val) {
- ioport_field::user_settings settings;
- f.get_user_settings(settings);
- settings.value = val;
- f.set_user_settings(settings);
- }));
- ioport_field_type.set("settings", sol::property([this](ioport_field &f) {
- sol::table result = sol().create_table();
- for (ioport_setting &setting : f.settings())
- if (setting.enabled())
- result[setting.value()] = setting.name();
- return result;
- }));
- sol().registry().set_usertype("ioport_field", ioport_field_type);
-
-
-/* ioport_field_live library
- *
- * manager:machine():ioport().ports[port_tag].fields[field_name].live
- *
- * live.name
- */
-
- sol().registry().new_usertype<ioport_field_live>("ioport_field_live", "new", sol::no_constructor,
- "name", &ioport_field_live::name);
-
-
-/* parameters_manager library
- *
- * manager:machine():parameters()
- *
- * parameters:add(tag, val) - add tag = val parameter
- * parameters:lookup(tag) - get val for tag
- */
-
- sol().registry().new_usertype<parameters_manager>("parameters", "new", sol::no_constructor,
- "add", &parameters_manager::add,
- "lookup", &parameters_manager::lookup);
-
-
-/* video_manager library
- *
- * manager:machine():video()
- *
- * video:begin_recording([opt] filename, [opt] format) - start AVI recording to filename if given or default
- * video:end_recording() - stop AVI recording
- * video:is_recording() - get recording status
- * video:snapshot() - save shot of all screens
- * video:skip_this_frame() - is current frame going to be skipped
- * video:speed_factor() - get speed factor
- * video:speed_percent() - get percent from realtime
- * video:frame_update() - render a frame
- * video:size() - get width and height of snapshot bitmap in pixels
- * video:pixels() - get binary bitmap of all screens as string
- *
- * video.frameskip - current frameskip
- * video.throttled - throttle state
- * video.throttle_rate - throttle rate
- */
+ // FIXME: this shouldn't be a member of the screen device
+ // the screen is only used as a hint when configured for native snapshots and may be ignored
+ std::string snapstr;
+ bool is_absolute_path = false;
+ if (filename)
+ {
+ // a filename was specified; if it isn't absolute post-process it
+ snapstr = process_snapshot_filename(machine(), filename);
+ is_absolute_path = osd_is_absolute_path(snapstr);
+ }
- auto video_type = sol().registry().create_simple_usertype<video_manager>("new", sol::no_constructor);
- video_type.set("begin_recording", sol::overload(
- [this](video_manager &vm, const char *filename, const char *format_string) {
- std::string fn = process_snapshot_filename(machine(), filename);
- movie_recording::format format = s_movie_recording_format_parser(format_string);
- vm.begin_recording(fn.c_str(), format);
- },
- [this](video_manager &vm, const char *filename) {
- std::string fn = process_snapshot_filename(machine(), filename);
- vm.begin_recording(fn.c_str(), movie_recording::format::AVI);
- },
- [](video_manager &vm) {
- vm.begin_recording(nullptr, movie_recording::format::AVI);
- }));
- video_type.set("end_recording", [this](video_manager &vm) {
- if(!vm.is_recording())
+ // open the file
+ emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ std::error_condition filerr;
+ if (!snapstr.empty())
+ filerr = file.open(snapstr);
+ else
+ filerr = machine().video().open_next(file, "png");
+ if (filerr)
+ return sol::make_object(sol(), filerr);
+
+ // and save the snapshot
+ machine().video().save_snapshot(&sdev, file);
+ return sol::lua_nil;
+ });
+ screen_dev_type.set_function("pixel", &screen_device::pixel);
+ screen_dev_type.set_function(
+ "pixels",
+ [] (screen_device &sdev, sol::this_state s)
{
- machine().logerror("[luaengine] No active recording to stop\n");
- return;
- }
- vm.end_recording();
- });
- video_type.set("snapshot", &video_manager::save_active_screen_snapshots);
- video_type.set("is_recording", &video_manager::is_recording);
- video_type.set("skip_this_frame", &video_manager::skip_this_frame);
- video_type.set("speed_factor", &video_manager::speed_factor);
- video_type.set("speed_percent", &video_manager::speed_percent);
- video_type.set("effective_frameskip", &video_manager::effective_frameskip);
- video_type.set("frame_update", &video_manager::frame_update);
- video_type.set("size", [](video_manager &vm) {
- s32 width, height;
- vm.compute_snapshot_size(width, height);
- return std::tuple<s32, s32>(width, height);
- });
- video_type.set("pixels", [](video_manager &vm, sol::this_state s) {
- lua_State *L = s;
- luaL_Buffer buff;
- s32 width, height;
- vm.compute_snapshot_size(width, height);
- int size = width * height * 4;
- u32 *ptr = (u32 *)luaL_buffinitsize(L, &buff, size);
- vm.pixels(ptr);
- luaL_pushresultsize(&buff, size);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- });
- video_type.set("frameskip", sol::property(&video_manager::frameskip, &video_manager::set_frameskip));
- video_type.set("throttled", sol::property(&video_manager::throttled, &video_manager::set_throttled));
- video_type.set("throttle_rate", sol::property(&video_manager::throttle_rate, &video_manager::set_throttle_rate));
- sol().registry().set_usertype("video", video_type);
-
-
-/* sound_manager library
- *
- * manager:machine():sound()
- *
- * sound:start_recording() - begin audio recording
- * sound:stop_recording() - end audio recording
- * sound:ui_mute(turn_off) - turns on/off UI sound
- * sound:system_mute() - turns on/off system sound
- * sound:samples() - get current audio buffer contents in binary form as string (updates 50 times per second)
- *
- * sound.attenuation - sound attenuation
- */
-
- auto sound_type = sol().registry().create_simple_usertype<sound_manager>("new", sol::no_constructor);
- sound_type.set("start_recording", &sound_manager::start_recording);
- sound_type.set("stop_recording", &sound_manager::stop_recording);
- sound_type.set("ui_mute", &sound_manager::ui_mute);
- sound_type.set("debugger_mute", &sound_manager::debugger_mute);
- sound_type.set("system_mute", &sound_manager::system_mute);
- sound_type.set("samples", [](sound_manager &sm, sol::this_state s) {
- lua_State *L = s;
- luaL_Buffer buff;
- s32 count = sm.sample_count() * 2 * 2; // 2 channels, 2 bytes per sample
- s16 *ptr = (s16 *)luaL_buffinitsize(L, &buff, count);
- sm.samples(ptr);
- luaL_pushresultsize(&buff, count);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- });
- sound_type.set("attenuation", sol::property(&sound_manager::attenuation, &sound_manager::set_attenuation));
- sol().registry().set_usertype("sound", sound_type);
-
-
-/* input_manager library
- *
- * manager:machine():input()
- *
- * input:code_from_token(token) - get input_code for KEYCODE_* string token
- * input:code_pressed(code) - get pressed state for input_code
- * input:code_to_token(code) - get KEYCODE_* string token for code
- * input:code_name(code) - get code friendly name
- * input:seq_from_tokens(tokens) - get input_seq for multiple space separated KEYCODE_* string tokens
- * input:seq_pressed(seq) - get pressed state for input_seq
- * input:seq_to_tokens(seq) - get KEYCODE_* string tokens for seq
- * input:seq_name(seq) - get seq friendly name
- * input:seq_clean(seq) - clean the seq and remove invalid elements
- * input:seq_poll_start(class, [opt] start_seq) - start polling for input_item_class passed as string
- * (switch/abs[olute]/rel[ative]/max[imum])
- * input:seq_poll() - poll once, returns true if input was fetched
- * input:seq_poll_final() - get final input_seq
- * input.device_classes - returns device classes
- */
-
- auto input_type = sol().registry().create_simple_usertype<input_manager>("new", sol::no_constructor);
- input_type.set("code_from_token", [](input_manager &input, const char *token) { return sol::make_user(input.code_from_token(token)); });
- input_type.set("code_pressed", [](input_manager &input, sol::user<input_code> code) { return input.code_pressed(code); });
- input_type.set("code_to_token", [](input_manager &input, sol::user<input_code> code) { return input.code_to_token(code); });
- input_type.set("code_name", [](input_manager &input, sol::user<input_code> code) { return input.code_name(code); });
- input_type.set("seq_from_tokens", [](input_manager &input, const char *tokens) { input_seq seq; input.seq_from_tokens(seq, tokens); return sol::make_user(seq); });
- input_type.set("seq_pressed", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_pressed(seq); });
- input_type.set("seq_to_tokens", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_to_tokens(seq); });
- input_type.set("seq_name", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_name(seq); });
- input_type.set("seq_clean", [](input_manager &input, sol::user<input_seq> seq) { input_seq cleaned_seq = input.seq_clean(seq); return sol::make_user(cleaned_seq); });
- input_type.set("seq_poll_start", [this](input_manager &input, const char *cls_string, sol::object seq) {
- if (!m_seq_poll)
- m_seq_poll.reset(new input_sequence_poller(input));
-
- input_item_class cls;
- if (!strcmp(cls_string, "switch"))
- cls = ITEM_CLASS_SWITCH;
- else if (!strcmp(cls_string, "absolute") || !strcmp(cls_string, "abs"))
- cls = ITEM_CLASS_ABSOLUTE;
- else if (!strcmp(cls_string, "relative") || !strcmp(cls_string, "rel"))
- cls = ITEM_CLASS_RELATIVE;
- else if (!strcmp(cls_string, "maximum") || !strcmp(cls_string, "max"))
- cls = ITEM_CLASS_MAXIMUM;
- else
- cls = ITEM_CLASS_INVALID;
-
- if (seq.is<sol::user<input_seq>>())
- m_seq_poll->start(cls, seq.as<sol::user<input_seq>>());
- else
- m_seq_poll->start(cls);
- });
- input_type.set("seq_poll", [this](input_manager &input) -> sol::object {
- if (!m_seq_poll)
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), m_seq_poll->poll());
- });
- input_type.set("seq_poll_final", [this](input_manager &input) -> sol::object {
- if (!m_seq_poll)
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), sol::make_user(m_seq_poll->valid() ? m_seq_poll->sequence() : input_seq()));
- });
- input_type.set("seq_poll_modified", [this](input_manager &input) -> sol::object {
- if (!m_seq_poll)
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), m_seq_poll->modified());
- });
- input_type.set("seq_poll_valid", [this](input_manager &input) -> sol::object {
- if (!m_seq_poll)
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), m_seq_poll->valid());
- });
- input_type.set("seq_poll_sequence", [this](input_manager &input) -> sol::object {
- if (!m_seq_poll)
- return sol::make_object(sol(), sol::nil);
- return sol::make_object(sol(), sol::make_user(m_seq_poll->sequence()));
- });
- input_type.set("device_classes", sol::property([this](input_manager &input) {
- sol::table result = sol().create_table();
- for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++)
+ const rectangle &visarea = sdev.visible_area();
+ luaL_Buffer buff;
+ int size = visarea.height() * visarea.width() * 4;
+ u32 *const ptr = reinterpret_cast<u32 *>(luaL_buffinitsize(s, &buff, size));
+ sdev.pixels(ptr);
+ luaL_pushresultsize(&buff, size);
+ return std::make_tuple(sol::make_reference(s, sol::stack_reference(s, -1)), visarea.width(), visarea.height());
+ });
+ screen_dev_type["screen_type"] = sol::property(&screen_device::screen_type);
+ screen_dev_type["width"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().width(); });
+ screen_dev_type["height"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().height(); });
+ screen_dev_type["refresh"] = sol::property([] (screen_device &sdev) { return ATTOSECONDS_TO_HZ(sdev.refresh_attoseconds()); });
+ screen_dev_type["refresh_attoseconds"] = sol::property([] (screen_device &sdev) { return sdev.refresh_attoseconds(); });
+ screen_dev_type["xoffset"] = sol::property(&screen_device::xoffset);
+ screen_dev_type["yoffset"] = sol::property(&screen_device::yoffset);
+ screen_dev_type["xscale"] = sol::property(&screen_device::xscale);
+ screen_dev_type["yscale"] = sol::property(&screen_device::yscale);
+ screen_dev_type["pixel_period"] = sol::property([] (screen_device &sdev) { return sdev.pixel_period().as_double(); });
+ screen_dev_type["scan_period"] = sol::property([] (screen_device &sdev) { return sdev.scan_period().as_double(); });
+ screen_dev_type["frame_period"] = sol::property([] (screen_device &sdev) { return sdev.frame_period().as_double(); });
+ screen_dev_type["frame_number"] = &screen_device::frame_number;
+ screen_dev_type["container"] = sol::property(&screen_device::container);
+ screen_dev_type["palette"] = sol::property([] (screen_device const &sdev) { return sdev.has_palette() ? &sdev.palette() : nullptr; });
+
+
+ auto cass_type = sol().registry().new_usertype<cassette_image_device>(
+ "cassette",
+ sol::no_constructor,
+ sol::base_classes, sol::bases<device_t, device_image_interface>());
+ cass_type["stop"] = [] (cassette_image_device &c) { c.change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); };
+ cass_type["play"] = [] (cassette_image_device &c) { c.change_state(CASSETTE_PLAY, CASSETTE_MASK_UISTATE); };
+ cass_type["record"] = [] (cassette_image_device &c) { c.change_state(CASSETTE_RECORD, CASSETTE_MASK_UISTATE); };
+ cass_type["forward"] = &cassette_image_device::go_forward;
+ cass_type["reverse"] = &cassette_image_device::go_reverse;
+ cass_type["seek"] = [] (cassette_image_device &c, double time, const char* origin) { if (c.exists()) c.seek(time, s_seek_parser(origin)); };
+ cass_type["is_stopped"] = sol::property(&cassette_image_device::is_stopped);
+ cass_type["is_playing"] = sol::property(&cassette_image_device::is_playing);
+ cass_type["is_recording"] = sol::property(&cassette_image_device::is_recording);
+ cass_type["motor_state"] = sol::property(&cassette_image_device::motor_on, &cassette_image_device::set_motor);
+ cass_type["speaker_state"] = sol::property(&cassette_image_device::speaker_on, &cassette_image_device::set_speaker);
+ cass_type["position"] = sol::property(&cassette_image_device::get_position);
+ cass_type["length"] = sol::property([] (cassette_image_device &c) { return c.exists() ? c.get_length() : 0.0; });
+
+
+ auto image_type = sol().registry().new_usertype<device_image_interface>("image", sol::no_constructor);
+ image_type.set_function("load",
+ [] (device_image_interface &di, sol::this_state s, std::string_view path) -> sol::object
{
- input_class &devclass = input.device_class(devclass_id);
- result[devclass.name()] = &devclass;
- }
- return result;
- }));
- sol().registry().set_usertype("input", input_type);
-
-
-/* input_class library
- *
- * manager:machine():input().device_classes[devclass]
- *
- * devclass.name
- * devclass.enabled
- * devclass.multi
- * devclass.devices[]
- */
-
- auto input_class_type = sol().registry().create_simple_usertype<input_class>("new", sol::no_constructor);
- input_class_type.set("name", sol::property(&input_class::name));
- input_class_type.set("enabled", sol::property(&input_class::enabled, &input_class::enable));
- input_class_type.set("multi", sol::property(&input_class::multi, &input_class::set_multi));
- input_class_type.set("devices", sol::property([this](input_class &devclass) {
- sol::table result = sol().create_table();
- int index = 1;
- for (int devindex = 0; devindex <= devclass.maxindex(); devindex++)
+ auto [err, message] = di.load(path);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("load_software",
+ [] (device_image_interface &di, sol::this_state s, std::string_view identifier) -> sol::object
{
- input_device *dev = devclass.device(devindex);
- if (dev)
- result[index++] = dev;
- }
- return result;
- }));
- sol().registry().set_usertype("input_class", input_class_type);
-
-
-/* input_device library
- *
- * manager:machine():input().device_classes[devclass].devices[index]
- * device.name
- * device.id
- * device.devindex
- * device.items[]
- */
-
- auto input_device_type = sol().registry().create_simple_usertype<input_device>("new", sol::no_constructor);
- input_device_type.set("name", sol::property(&input_device::name));
- input_device_type.set("id", sol::property(&input_device::id));
- input_device_type.set("devindex", sol::property(&input_device::devindex));
- input_device_type.set("items", sol::property([this](input_device &dev) {
- sol::table result = sol().create_table();
- for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++)
+ auto [err, message] = di.load_software(identifier);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("unload", &device_image_interface::unload);
+ image_type.set_function("create",
+ [] (device_image_interface &di, sol::this_state s, std::string_view path) -> sol::object
{
- input_device_item *item = dev.item(id);
- if (item)
- result[id] = dev.item(id);
- }
- return result;
- }));
- sol().registry().set_usertype("input_device", input_device_type);
-
-
-/* input_device_item library
- *
- * manager:machine():input().device_classes[devclass].devices[index].items[item_id]
- * item.name
- * item.token
- * item:code()
- */
-
- auto input_device_item_type = sol().registry().create_simple_usertype<input_device_item>("new", sol::no_constructor);
- input_device_item_type.set("name", sol::property(&input_device_item::name));
- input_device_item_type.set("token", sol::property(&input_device_item::token));
- input_device_item_type.set("code", [](input_device_item &item) {
- input_code code(item.device().devclass(), item.device().devindex(), item.itemclass(), ITEM_MODIFIER_NONE, item.itemid());
- return sol::make_user(code);
- });
- sol().registry().set_usertype("input_device_item", input_device_item_type);
-
-
-/* ui_input_manager library
- *
- * manager:machine():uiinput()
- *
- * uiinput:find_mouse() - return x, y, button state, ui render target
- * uiinput:pressed(key) - get pressed state for ui key
- * uiinput.presses_enabled - enable/disable ui key presses
- */
-
- auto uiinput_type = sol().registry().create_simple_usertype<ui_input_manager>("new", sol::no_constructor);
- uiinput_type.set("find_mouse", [](ui_input_manager &ui) {
- int32_t x, y;
- bool button;
- render_target *rt = ui.find_mouse(&x, &y, &button);
- return std::tuple<int32_t, int32_t, bool, render_target *>(x, y, button, rt);
- });
- uiinput_type.set("pressed", &ui_input_manager::pressed);
- uiinput_type.set("presses_enabled", sol::property(&ui_input_manager::presses_enabled, &ui_input_manager::set_presses_enabled));
- sol().registry().set_usertype("uiinput", uiinput_type);
-
-
-/* render_target library
- *
- * manager:machine():render().targets[target_index]
- * manager:machine():render():ui_target()
- *
- * target:view_bounds() - get x0, x1, y0, y1 bounds for target
- * target:width() - get target width
- * target:height() - get target height
- * target:pixel_aspect() - get target aspect
- * target:hidden() - is target hidden
- * target:is_ui_target() - is ui render target
- * target:index() - target index
- * target:view_name([opt] index) - current target layout view name
- *
- * target.max_update_rate -
- * target.view - current target layout view
- * target.orientation - current target orientation
- * target.screen_overlay - enable overlays
- * target.zoom - enable zoom
- */
-
- auto target_type = sol().registry().create_simple_usertype<render_target>("new", sol::no_constructor);
- target_type.set("view_bounds", [](render_target &rt) {
- const render_bounds b = rt.current_view().bounds();
- return std::tuple<float, float, float, float>(b.x0, b.x1, b.y0, b.y1);
- });
- target_type.set("width", &render_target::width);
- target_type.set("height", &render_target::height);
- target_type.set("pixel_aspect", &render_target::pixel_aspect);
- target_type.set("hidden", &render_target::hidden);
- target_type.set("is_ui_target", &render_target::is_ui_target);
- target_type.set("index", &render_target::index);
- target_type.set("view_name", &render_target::view_name);
- target_type.set("max_update_rate", sol::property(&render_target::max_update_rate, &render_target::set_max_update_rate));
- target_type.set("view", sol::property(&render_target::view, &render_target::set_view));
- target_type.set("orientation", sol::property(&render_target::orientation, &render_target::set_orientation));
- target_type.set("screen_overlay", sol::property(&render_target::screen_overlay_enabled, &render_target::set_screen_overlay_enabled));
- target_type.set("zoom", sol::property(&render_target::zoom_to_screen, &render_target::set_zoom_to_screen));
- sol().registry().set_usertype("target", target_type);
-
-
-/* render_container library
- *
- * manager:machine():render():ui_container()
- *
- * container:orientation()
- * container:xscale()
- * container:yscale()
- * container:xoffset()
- * container:yoffset()
- * container:is_empty()
- */
-
- auto render_container_type = sol().registry().create_simple_usertype<render_container>("new", sol::no_constructor);
- render_container_type.set("orientation", &render_container::orientation);
- render_container_type.set("xscale", &render_container::xscale);
- render_container_type.set("yscale", &render_container::yscale);
- render_container_type.set("xoffset", &render_container::xoffset);
- render_container_type.set("yoffset", &render_container::yoffset);
- render_container_type.set("is_empty", &render_container::is_empty);
- sol().registry().set_usertype("render_container", render_container_type);
-
-
-/* render_manager library
- *
- * manager:machine():render()
- *
- * render:max_update_rate() -
- * render:ui_target() - render_target for ui drawing
- * render:ui_container() - render_container for ui drawing
- *
- * render.targets[] - render_target table
- */
-
- auto render_type = sol().registry().create_simple_usertype<render_manager>("new", sol::no_constructor);
- render_type.set("max_update_rate", &render_manager::max_update_rate);
- render_type.set("ui_target", &render_manager::ui_target);
- render_type.set("ui_container", &render_manager::ui_container);
- render_type.set("targets", sol::property([this](render_manager &r) {
- sol::table target_table = sol().create_table();
- int tc = 0;
- for(render_target &curr_rt : r.targets())
- target_table[tc++] = &curr_rt;
- return target_table;
- }));
- sol().registry().set_usertype("render", render_type);
-
-
-/* screen_device library
- *
- * manager:machine().screens[screen_tag]
- *
- * screen:draw_box(x1, y1, x2, y2, fillcol, linecol) - draw box from (x1, y1)-(x2, y2) colored linecol
- * filled with fillcol, color is 32bit argb
- * screen:draw_line(x1, y1, x2, y2, linecol) - draw line from (x1, y1)-(x2, y2) colored linecol
- * screen:draw_text(x || justify, y, message, [opt] fgcolor, [opt] bgcolor) - draw message at (x, y) or at line y
- * with left/right/center justification
- * screen:height() - screen height
- * screen:width() - screen width
- * screen:orientation() - screen angle, flipx, flipy
- * screen:refresh() - screen refresh rate in Hz
- * screen:refresh_attoseconds() - screen refresh rate in attoseconds
- * screen:snapshot([opt] filename) - save snap shot
- * screen:type() - screen drawing type
- * screen:frame_number() - screen frame count
- * screen:name() - screen device full name
- * screen:shortname() - screen device short name
- * screen:tag() - screen device tag
- * screen:xscale() - screen x scale factor
- * screen:yscale() - screen y scale factor
- * screen:pixel(x, y) - get pixel at (x, y) as packed RGB in a u32
- * screen:pixels() - get whole screen binary bitmap as string
- * screen:time_until_pos(vpos, hpos) - get the time until this screen pos is reached
- */
-
- auto screen_dev_type = sol().registry().create_simple_usertype<screen_device>("new", sol::no_constructor);
- screen_dev_type.set("draw_box", [](screen_device &sdev, float x1, float y1, float x2, float y2, uint32_t bgcolor, uint32_t fgcolor) {
- int sc_width = sdev.visible_area().width();
- int sc_height = sdev.visible_area().height();
- x1 = std::min(std::max(0.0f, x1), float(sc_width-1)) / float(sc_width);
- y1 = std::min(std::max(0.0f, y1), float(sc_height-1)) / float(sc_height);
- x2 = std::min(std::max(0.0f, x2), float(sc_width-1)) / float(sc_width);
- y2 = std::min(std::max(0.0f, y2), float(sc_height-1)) / float(sc_height);
- mame_machine_manager::instance()->ui().draw_outlined_box(sdev.container(), x1, y1, x2, y2, fgcolor, bgcolor);
- });
- screen_dev_type.set("draw_line", [](screen_device &sdev, float x1, float y1, float x2, float y2, uint32_t color) {
- int sc_width = sdev.visible_area().width();
- int sc_height = sdev.visible_area().height();
- x1 = std::min(std::max(0.0f, x1), float(sc_width-1)) / float(sc_width);
- y1 = std::min(std::max(0.0f, y1), float(sc_height-1)) / float(sc_height);
- x2 = std::min(std::max(0.0f, x2), float(sc_width-1)) / float(sc_width);
- y2 = std::min(std::max(0.0f, y2), float(sc_height-1)) / float(sc_height);
- sdev.container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- });
- screen_dev_type.set("draw_text", [this](screen_device &sdev, sol::object xobj, float y, const char *msg, sol::object color, sol::object bcolor) {
- int sc_width = sdev.visible_area().width();
- int sc_height = sdev.visible_area().height();
- auto justify = ui::text_layout::LEFT;
- float x = 0;
- if(xobj.is<float>())
+ auto [err, message] = di.create(path);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("display", &device_image_interface::call_display);
+ image_type.set_function("add_media_change_notifier",
+ [this] (device_image_interface &di, sol::protected_function cb)
{
- x = std::min(std::max(0.0f, xobj.as<float>()), float(sc_width-1)) / float(sc_width);
- y = std::min(std::max(0.0f, y), float(sc_height-1)) / float(sc_height);
- }
- else if(xobj.is<const char *>())
+ return di.add_media_change_notifier(
+ [this, cbfunc = sol::protected_function(m_lua_state, cb)] (device_image_interface::media_change_event ev)
+ {
+ char const *evstr(nullptr);
+ switch (ev)
+ {
+ case device_image_interface::media_change_event::LOADED:
+ evstr = "loaded";
+ break;
+ case device_image_interface::media_change_event::UNLOADED:
+ evstr = "unloaded";
+ break;
+ }
+ auto status(invoke(cbfunc, evstr));
+ if (!status.valid())
+ {
+ auto err(status.template get<sol::error>());
+ osd_printf_error("[LUA ERROR] error in media change callback: %s\n", err.what());
+ }
+ });
+ });
+ image_type["is_readable"] = sol::property(&device_image_interface::is_readable);
+ image_type["is_writeable"] = sol::property(&device_image_interface::is_writeable);
+ image_type["is_creatable"] = sol::property(&device_image_interface::is_creatable);
+ image_type["must_be_loaded"] = sol::property(&device_image_interface::must_be_loaded);
+ image_type["is_reset_on_load"] = sol::property(&device_image_interface::is_reset_on_load);
+ image_type["image_type_name"] = sol::property(&device_image_interface::image_type_name);
+ image_type["instance_name"] = sol::property(&device_image_interface::instance_name);
+ image_type["brief_instance_name"] = sol::property(&device_image_interface::brief_instance_name);
+ image_type["formatlist"] = sol::property([] (device_image_interface &image) { return image_interface_formats(image); });
+ image_type["exists"] = sol::property(&device_image_interface::exists);
+ image_type["readonly"] = sol::property(&device_image_interface::is_readonly);
+ image_type["filename"] = sol::property(&device_image_interface::filename);
+ image_type["crc"] = sol::property(&device_image_interface::crc);
+ image_type["loaded_through_softlist"] = sol::property(&device_image_interface::loaded_through_softlist);
+ image_type["software_list_name"] = sol::property(&device_image_interface::software_list_name);
+ image_type["software_longname"] = sol::property(
+ [] (device_image_interface &di)
{
- justify = s_text_justify_parser(xobj.as<const char *>());
- }
- else
+ software_info const *const si(di.software_entry());
+ return si ? si->longname().c_str() : nullptr;
+ });
+ image_type["software_publisher"] = sol::property(
+ [] (device_image_interface &di)
{
- luaL_error(m_lua_state, "Error in param 1 to draw_text");
- return;
- }
- rgb_t textcolor = mame_machine_manager::instance()->ui().colors().text_color();
- rgb_t bgcolor = 0;
- if(color.is<uint32_t>())
- textcolor = rgb_t(color.as<uint32_t>());
- if(bcolor.is<uint32_t>())
- bgcolor = rgb_t(bcolor.as<uint32_t>());
- mame_machine_manager::instance()->ui().draw_text_full(sdev.container(), msg, x, y, (1.0f - x),
- justify, ui::text_layout::WORD, mame_ui_manager::OPAQUE_, textcolor, bgcolor);
- });
- screen_dev_type.set("height", [](screen_device &sdev) { return sdev.visible_area().height(); });
- screen_dev_type.set("width", [](screen_device &sdev) { return sdev.visible_area().width(); });
- screen_dev_type.set("orientation", [](screen_device &sdev) {
- uint32_t flags = sdev.orientation();
- int rotation_angle = 0;
- switch (flags)
+ software_info const *const si(di.software_entry());
+ return si ? si->publisher().c_str() : nullptr;
+ });
+ image_type["software_year"] = sol::property(
+ [] (device_image_interface &di)
{
- 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;
- }
- return std::tuple<int, bool, bool>(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y);
- });
- screen_dev_type.set("refresh", [](screen_device &sdev) { return ATTOSECONDS_TO_HZ(sdev.refresh_attoseconds()); });
- screen_dev_type.set("refresh_attoseconds", [](screen_device &sdev) { return sdev.refresh_attoseconds(); });
- screen_dev_type.set("snapshot", [this](screen_device &sdev, sol::object filename) -> sol::object {
- std::string snapstr;
- bool is_absolute_path = false;
- if (filename.is<const char *>())
+ software_info const *const si(di.software_entry());
+ return si ? si->year().c_str() : nullptr;
+ });
+ image_type["software_parent"] = sol::property(
+ [] (device_image_interface &di)
{
- // a filename was specified; if it isn't absolute postprocess it
- snapstr = process_snapshot_filename(machine(), filename.as<const char *>());
- is_absolute_path = osd_is_absolute_path(snapstr);
- }
+ software_info const *const si(di.software_entry());
+ return si ? si->parentname().c_str() : nullptr;
+ });
+ image_type["device"] = sol::property(static_cast<device_t & (device_image_interface::*)()>(&device_image_interface::device));
- // open the file
- emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- osd_file::error filerr;
- if (!snapstr.empty())
- filerr = file.open(snapstr);
- else
- filerr = machine().video().open_next(file, "png");
- if (filerr != osd_file::error::NONE)
- return sol::make_object(sol(), filerr);
- // and save the snapshot
- machine().video().save_snapshot(&sdev, file);
- return sol::make_object(sol(), sol::nil);
- });
- screen_dev_type.set("type", [](screen_device &sdev) {
- switch (sdev.screen_type())
+ auto state_entry_type = sol().registry().new_usertype<device_state_entry>("state_entry", sol::no_constructor);
+ state_entry_type["value"] = sol::property(
+ [] (device_state_entry const &entry, sol::this_state s) -> sol::object
{
- case SCREEN_TYPE_RASTER: return "raster"; break;
- case SCREEN_TYPE_VECTOR: return "vector"; break;
- case SCREEN_TYPE_LCD: return "lcd"; break;
- case SCREEN_TYPE_SVG: return "svg"; break;
- default: break;
- }
- return "unknown";
- });
- screen_dev_type.set("frame_number", &screen_device::frame_number);
- screen_dev_type.set("name", &screen_device::name);
- screen_dev_type.set("shortname", &screen_device::shortname);
- screen_dev_type.set("tag", &screen_device::tag);
- screen_dev_type.set("xscale", &screen_device::xscale);
- screen_dev_type.set("yscale", &screen_device::yscale);
- screen_dev_type.set("pixel", [](screen_device &sdev, float x, float y) { return sdev.pixel((s32)x, (s32)y); });
- screen_dev_type.set("pixels", [](screen_device &sdev, sol::this_state s) {
- lua_State *L = s;
- const rectangle &visarea = sdev.visible_area();
+ if (entry.is_float())
+ return sol::make_object(s, entry.dvalue());
+ else
+ return sol::make_object(s, entry.value());
+ },
+ [] (device_state_entry const &entry, sol::this_state s, sol::object value)
+ {
+ if (!entry.writeable())
+ luaL_error(s, "cannot set value of read-only device state entry");
+ else if (entry.is_float())
+ entry.set_dvalue(value.as<double>());
+ else
+ entry.set_value(value.as<u64>());
+ });
+ state_entry_type["symbol"] = sol::property(&device_state_entry::symbol);
+ state_entry_type["visible"] = sol::property(&device_state_entry::visible);
+ state_entry_type["writeable"] = sol::property(&device_state_entry::writeable);
+ state_entry_type["is_float"] = sol::property(&device_state_entry::is_float);
+ state_entry_type["datamask"] = sol::property(
+ [] (device_state_entry const &entry)
+ {
+ return entry.is_float() ? std::optional<u64>() : std::optional<u64>(entry.datamask());
+ });
+ state_entry_type["datasize"] = sol::property(&device_state_entry::datasize);
+ state_entry_type["max_length"] = sol::property(&device_state_entry::max_length);
+ state_entry_type[sol::meta_function::to_string] = &device_state_entry::to_string;
+
+
+ auto format_type = sol().registry().new_usertype<image_device_format>("image_format", sol::no_constructor);
+ format_type["name"] = sol::property(&image_device_format::name);
+ format_type["description"] = sol::property(&image_device_format::description);
+ format_type["extensions"] = sol::property(
+ [this] (image_device_format const &format)
+ {
+ int index = 1;
+ sol::table option_table = sol().create_table();
+ for (std::string const &ext : format.extensions())
+ option_table[index++] = ext;
+ return option_table;
+ });
+ format_type["option_spec"] = sol::property(&image_device_format::optspec);
+
+
+ auto slot_type = sol().registry().new_usertype<device_slot_interface>("slot", sol::no_constructor);
+ slot_type["fixed"] = sol::property(&device_slot_interface::fixed);
+ slot_type["has_selectable_options"] = sol::property(&device_slot_interface::has_selectable_options);
+ slot_type["default_option"] = sol::property(&device_slot_interface::default_option);
+ slot_type["options"] = sol::property([] (device_slot_interface const &slot) { return standard_tag_object_ptr_map<device_slot_interface::slot_option>(slot.option_list()); });
+ slot_type["device"] = sol::property(static_cast<device_t & (device_slot_interface::*)()>(&device_slot_interface::device));
+
+
+ auto dislot_option_type = sol().registry().new_usertype<device_slot_interface::slot_option>("dislot_option", sol::no_constructor);
+ dislot_option_type["name"] = sol::property(&device_slot_interface::slot_option::name);
+ dislot_option_type["device_fullname"] = sol::property([] (device_slot_interface::slot_option &opt) { return opt.devtype().fullname(); });
+ dislot_option_type["device_shortname"] = sol::property([] (device_slot_interface::slot_option &opt) { return opt.devtype().shortname(); });
+ dislot_option_type["selectable"] = sol::property(&device_slot_interface::slot_option::selectable);
+ dislot_option_type["default_bios"] = sol::property(static_cast<char const * (device_slot_interface::slot_option::*)() const>(&device_slot_interface::slot_option::default_bios));
+ dislot_option_type["clock"] = sol::property(static_cast<u32 (device_slot_interface::slot_option::*)() const>(&device_slot_interface::slot_option::clock));
+
+
+ auto parameters_type = sol().registry().new_usertype<parameters_manager>("parameters", sol::no_constructor);
+ parameters_type["add"] = &parameters_manager::add;
+ parameters_type["lookup"] = &parameters_manager::lookup;
+
+
+ auto video_type = sol().registry().new_usertype<video_manager>("video", sol::no_constructor);
+ video_type["frame_update"] = [] (video_manager &vm) { vm.frame_update(true); };
+ video_type["snapshot"] = &video_manager::save_active_screen_snapshots;
+ video_type["begin_recording"] =
+ [this] (video_manager &vm, const char *filename, const char *format_string)
+ {
+ // FIXME: the filename substitution shouldn't be done here
+ std::string fn;
+ movie_recording::format format = movie_recording::format::AVI;
+ if (filename)
+ fn = process_snapshot_filename(machine(), filename);
+ if (format_string)
+ format = s_movie_recording_format_parser(format_string);
+ vm.begin_recording(filename ? fn.c_str() : nullptr, format);
+ };
+ video_type["end_recording"] = &video_manager::end_recording;
+ video_type["snapshot_size"] =
+ [] (video_manager &vm)
+ {
+ s32 width, height;
+ vm.compute_snapshot_size(width, height);
+ return std::make_tuple(width, height);
+ };
+ video_type["snapshot_pixels"] =
+ [] (video_manager &vm, sol::this_state s)
+ {
+ // TODO: would be better if this could return a tuple of (buffer, width, height)
+ s32 width, height;
+ vm.compute_snapshot_size(width, height);
+ int size = width * height * 4;
luaL_Buffer buff;
- int size = visarea.height() * visarea.width() * 4;
- u32 *ptr = (u32 *)luaL_buffinitsize(L, &buff, size);
- sdev.pixels(ptr);
+ u32 *ptr = (u32 *)luaL_buffinitsize(s, &buff, size);
+ vm.pixels(ptr);
luaL_pushresultsize(&buff, size);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- });
- screen_dev_type.set("time_until_pos", [](screen_device &sdev, int vpos, int hpos) { return sdev.time_until_pos(vpos, hpos).as_double(); });
- sol().registry().set_usertype("screen_dev", screen_dev_type);
-
-
-/* mame_ui_manager library
- *
- * manager:ui()
- *
- * ui:is_menu_active() - ui menu state
- * ui:options() - ui core_options
- * ui:get_line_height() - current ui font height
- * ui:get_string_width(str, scale) - get str width with ui font at scale factor of current font size
- * ui:get_char_width(char) - get width of utf8 glyph char with ui font
- * ui:set_aggressive_input_focus(bool)
- *
- * ui.single_step
- * ui.show_fps - fps display enabled
- * ui.show_profiler - profiler display enabled
- */
-
- auto ui_type = sol().registry().create_simple_usertype<mame_ui_manager>("new", sol::no_constructor);
- ui_type.set("is_menu_active", &mame_ui_manager::is_menu_active);
- ui_type.set("options", [](mame_ui_manager &m) { return static_cast<core_options *>(&m.options()); });
- ui_type.set("show_fps", sol::property(&mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps));
- ui_type.set("show_profiler", sol::property(&mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler));
- ui_type.set("single_step", sol::property(&mame_ui_manager::single_step, &mame_ui_manager::set_single_step));
- ui_type.set("get_line_height", &mame_ui_manager::get_line_height);
- ui_type.set("get_string_width", &mame_ui_manager::get_string_width);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
+ };
+ video_type["speed_factor"] = sol::property(&video_manager::speed_factor);
+ video_type["throttled"] = sol::property(&video_manager::throttled, &video_manager::set_throttled);
+ video_type["throttle_rate"] = sol::property(&video_manager::throttle_rate, &video_manager::set_throttle_rate);
+ video_type["frameskip"] = sol::property(&video_manager::frameskip, &video_manager::set_frameskip);
+ video_type["speed_percent"] = sol::property(&video_manager::speed_percent);
+ video_type["effective_frameskip"] = sol::property(&video_manager::effective_frameskip);
+ video_type["skip_this_frame"] = sol::property(&video_manager::skip_this_frame);
+ video_type["snap_native"] = sol::property(&video_manager::snap_native);
+ video_type["is_recording"] = sol::property(&video_manager::is_recording);
+ video_type["snapshot_target"] = sol::property(&video_manager::snapshot_target);
+
+
+ auto sound_type = sol().registry().new_usertype<sound_manager>("sound", sol::no_constructor);
+ sound_type["start_recording"] =
+ [] (sound_manager &sm, char const *filename)
+ {
+ return filename ? sm.start_recording(filename) : sm.start_recording();
+ };
+ sound_type["stop_recording"] = &sound_manager::stop_recording;
+ sound_type["get_samples"] =
+ [] (sound_manager &sm, sol::this_state s)
+ {
+ luaL_Buffer buff;
+ s32 const count = sm.sample_count() * 2 * 2; // 2 channels, 2 bytes per sample
+ s16 *const ptr = (s16 *)luaL_buffinitsize(s, &buff, count);
+ sm.samples(ptr);
+ luaL_pushresultsize(&buff, count);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
+ };
+ sound_type["muted"] = sol::property(&sound_manager::muted);
+ sound_type["ui_mute"] = sol::property(
+ static_cast<bool (sound_manager::*)() const>(&sound_manager::ui_mute),
+ static_cast<void (sound_manager::*)(bool)>(&sound_manager::ui_mute));
+ sound_type["debugger_mute"] = sol::property(
+ static_cast<bool (sound_manager::*)() const>(&sound_manager::debugger_mute),
+ static_cast<void (sound_manager::*)(bool)>(&sound_manager::debugger_mute));
+ sound_type["system_mute"] = sol::property(
+ static_cast<bool (sound_manager::*)() const>(&sound_manager::system_mute),
+ static_cast<void (sound_manager::*)(bool)>(&sound_manager::system_mute));
+ sound_type["attenuation"] = sol::property(
+ &sound_manager::attenuation,
+ &sound_manager::set_attenuation);
+ sound_type["recording"] = sol::property(&sound_manager::is_recording);
+
+
+ auto ui_type = sol().registry().new_usertype<mame_ui_manager>("ui", sol::no_constructor);
// sol converts char32_t to a string
- ui_type.set("get_char_width", [](mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); });
- ui_type.set("set_aggressive_input_focus", [](mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); });
- sol().registry().set_usertype("ui", ui_type);
-
-
-/* device_state_entry library
- *
- * manager:machine().devices[device_tag].state[state_name]
- *
- * state:name() - get device state name
- * state:is_visible() - is state visible in debugger
- * state:is_divider() - is state a divider
- *
- * state.value - get device state value
- */
-
- auto dev_space_type = sol().registry().create_simple_usertype<device_state_entry>("new", sol::no_constructor);
- dev_space_type.set("name", &device_state_entry::symbol);
- dev_space_type.set("value", sol::property(
- [this](device_state_entry &entry) -> uint64_t {
- device_state_interface *state = entry.parent_state();
- if(state)
- {
- machine().save().dispatch_presave();
- return state->state_int(entry.index());
- }
- return 0;
- },
- [this](device_state_entry &entry, uint64_t val) {
- device_state_interface *state = entry.parent_state();
- if(state)
- {
- state->set_state_int(entry.index(), val);
- machine().save().dispatch_presave();
- }
- }));
- dev_space_type.set("is_visible", &device_state_entry::visible);
- dev_space_type.set("is_divider", &device_state_entry::divider);
- sol().registry().set_usertype("dev_space", dev_space_type);
-
-
-/* rom_entry library
+ ui_type.set_function("get_char_width", [] (mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); });
+ ui_type.set_function("get_string_width", static_cast<float (mame_ui_manager::*)(std::string_view)>(&mame_ui_manager::get_string_width));
+ ui_type.set_function("set_aggressive_input_focus", [] (mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); });
+ ui_type["get_general_input_setting"] = sol::overload(
+ // TODO: overload with sequence type string - parser isn't available here
+ [] (mame_ui_manager &ui, ioport_type type, int player) { return ui.get_general_input_setting(type, player, SEQ_TYPE_STANDARD); },
+ [] (mame_ui_manager &ui, ioport_type type) { return ui.get_general_input_setting(type, 0, SEQ_TYPE_STANDARD); });
+ ui_type["options"] = sol::property([] (mame_ui_manager &m) { return static_cast<core_options *>(&m.options()); });
+ ui_type["line_height"] = sol::property([] (mame_ui_manager &m) { return m.get_line_height(); });
+ ui_type["menu_active"] = sol::property(&mame_ui_manager::is_menu_active);
+ ui_type["ui_active"] = sol::property(&mame_ui_manager::ui_active, &mame_ui_manager::set_ui_active);
+ ui_type["single_step"] = sol::property(&mame_ui_manager::single_step, &mame_ui_manager::set_single_step);
+ ui_type["show_fps"] = sol::property(&mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps);
+ ui_type["show_profiler"] = sol::property(&mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler);
+ ui_type["image_display_enabled"] = sol::property(&mame_ui_manager::image_display_enabled, &mame_ui_manager::set_image_display_enabled);
+
+
+/* rom_entry library
*
* manager:machine().devices[device_tag].roms[rom]
*
@@ -2797,225 +2110,53 @@ void lua_engine::initialize()
* rom:flags() - see romentry.h
*/
- auto rom_entry_type = sol().registry().create_simple_usertype<rom_entry>("new", sol::no_constructor);
+ auto rom_entry_type = sol().registry().new_usertype<rom_entry>("rom_entry", "new", sol::no_constructor);
rom_entry_type.set("name", &rom_entry::name);
rom_entry_type.set("hashdata", &rom_entry::hashdata);
rom_entry_type.set("offset", &rom_entry::get_offset);
rom_entry_type.set("length", &rom_entry::get_length);
rom_entry_type.set("flags", &rom_entry::get_flags);
- sol().registry().set_usertype("rom_entry", rom_entry_type);
-/* memory_manager library
- *
- * manager:machine():memory()
- *
- * memory.banks[] - table of memory banks (k=tag, v=memory_bank)
- * memory.regions[] - table of memory regions (k=tag, v=memory_region)
- * memory.shares[] - table of memory shares (k=tag, v=memory_share)
- */
-
- auto memory_type = sol().registry().create_simple_usertype<memory_manager>("new", sol::no_constructor);
- memory_type.set("banks", sol::property([this](memory_manager &mm) {
- sol::table table = sol().create_table();
- for (auto &bank : mm.banks())
- table[bank.second->tag()] = bank.second.get();
- return table;
- }));
- memory_type.set("regions", sol::property([this](memory_manager &mm) {
- sol::table table = sol().create_table();
- for (auto &region : mm.regions())
- table[region.second->name()] = region.second.get();
- return table;
- }));
- memory_type.set("shares", sol::property([this](memory_manager &mm) {
- sol::table table = sol().create_table();
- for (auto &share : mm.shares())
- table[share.first] = share.second.get();
- return table;
- }));
- sol().registry().set_usertype("memory", memory_type);
-
-
-/* memory_region library
- *
- * manager:machine():memory().regions[region_tag]
- *
- * read/write by signedness u/i and bit-width 8/16/32/64:
- * region:read_*(addr)
- * region:write_*(addr, val)
- *
- * region.size
- */
-
- auto region_type = sol().registry().create_simple_usertype<memory_region>("new", sol::no_constructor);
- region_type.set("read_i8", &region_read<int8_t>);
- region_type.set("read_u8", &region_read<uint8_t>);
- region_type.set("read_i16", &region_read<int16_t>);
- region_type.set("read_u16", &region_read<uint16_t>);
- region_type.set("read_i32", &region_read<int32_t>);
- region_type.set("read_u32", &region_read<uint32_t>);
- region_type.set("read_i64", &region_read<int64_t>);
- region_type.set("read_u64", &region_read<uint64_t>);
- region_type.set("write_i8", &region_write<int8_t>);
- region_type.set("write_u8", &region_write<uint8_t>);
- region_type.set("write_i16", &region_write<int16_t>);
- region_type.set("write_u16", &region_write<uint16_t>);
- region_type.set("write_i32", &region_write<int32_t>);
- region_type.set("write_u32", &region_write<uint32_t>);
- region_type.set("write_i64", &region_write<int64_t>);
- region_type.set("write_u64", &region_write<uint64_t>);
- region_type.set("size", sol::property(&memory_region::bytes));
- sol().registry().set_usertype("region", region_type);
-
-
-/* memory_share library
- *
- * manager:machine():memory().shares[share_tag]
- *
- * read/write by signedness u/i and bit-width 8/16/32/64:
- * share:read_*(addr)
- * share:write_*(addr, val)
- *
- * region.size
-*/
+ auto output_type = sol().registry().new_usertype<output_manager>("output", sol::no_constructor);
+ output_type["set_value"] = &output_manager::set_value;
+ output_type["set_indexed_value"] =
+ [] (output_manager &o, char const *basename, int index, int value)
+ {
+ o.set_value(util::string_format("%s%d", basename, index), value);
+ };
+ output_type["get_value"] = &output_manager::get_value;
+ output_type["get_indexed_value"] =
+ [] (output_manager &o, char const *basename, int index)
+ {
+ return o.get_value(util::string_format("%s%d", basename, index));
+ };
+ output_type["name_to_id"] = &output_manager::name_to_id;
+ output_type["id_to_name"] = &output_manager::id_to_name;
- auto share_type = sol().registry().create_simple_usertype<memory_share>("new", sol::no_constructor);
- share_type.set("read_i8", &share_read<int8_t>);
- share_type.set("read_u8", &share_read<uint8_t>);
- share_type.set("read_i16", &share_read<int16_t>);
- share_type.set("read_u16", &share_read<uint16_t>);
- share_type.set("read_i32", &share_read<int32_t>);
- share_type.set("read_u32", &share_read<uint32_t>);
- share_type.set("read_i64", &share_read<int64_t>);
- share_type.set("read_u64", &share_read<uint64_t>);
- share_type.set("write_i8", &share_write<int8_t>);
- share_type.set("write_u8", &share_write<uint8_t>);
- share_type.set("write_i16", &share_write<int16_t>);
- share_type.set("write_u16", &share_write<uint16_t>);
- share_type.set("write_i32", &share_write<int32_t>);
- share_type.set("write_u32", &share_write<uint32_t>);
- share_type.set("write_i64", &share_write<int64_t>);
- share_type.set("write_u64", &share_write<uint64_t>);
- share_type.set("size", sol::property(&memory_share::bytes));
- sol().registry().set_usertype("share", share_type);
-
-
-/* output_manager library
- *
- * manager:machine():outputs()
- *
- * outputs:set_value(name, val) - set output name to val
- * outputs:set_indexed_value(index, val) - set output index to val
- * outputs:get_value(name) - get output name value
- * outputs:get_indexed_value(index) - get output index value
- * outputs:name_to_id(name) - get index for name
- * outputs:id_to_name(index) - get name for index
- */
- auto output_type = sol().registry().create_simple_usertype<output_manager>("new", sol::no_constructor);
- output_type.set("set_value", &output_manager::set_value);
- output_type.set("set_indexed_value", [](output_manager &o, char const *basename, int index, int value) {
- o.set_value(util::string_format("%s%d", basename, index).c_str(), value);
- });
- output_type.set("get_value", &output_manager::get_value);
- output_type.set("get_indexed_value", [](output_manager &o, char const *basename, int index) {
- return o.get_value(util::string_format("%s%d", basename, index).c_str());
- });
- output_type.set("name_to_id", &output_manager::name_to_id);
- output_type.set("id_to_name", &output_manager::id_to_name);
- sol().registry().set_usertype("output", output_type);
+ auto mame_manager_type = sol().registry().new_usertype<mame_machine_manager>("manager", sol::no_constructor);
+ mame_manager_type["machine"] = sol::property(&mame_machine_manager::machine);
+ mame_manager_type["ui"] = sol::property(&mame_machine_manager::ui);
+ mame_manager_type["options"] = sol::property(&mame_machine_manager::options);
+ mame_manager_type["plugins"] = sol::property([] (mame_machine_manager &m) { return plugin_options_plugins(m.plugins()); });
+ sol()["manager"] = std::ref(*mame_machine_manager::instance());
+ sol()["mame_manager"] = std::ref(*mame_machine_manager::instance());
-/* device_image_interface library
- *
- * manager:machine().images[image_type]
- *
- * image:exists()
- * image:filename() - full path to the image file
- * image:longname()
- * image:manufacturer()
- * image:year()
- * image:software_list_name()
- * image:image_type_name() - floppy/cart/cdrom/tape/hdd etc
- * image:load(filename)
- * image:load_software(softlist_name)
- * image:unload()
- * image:create()
- * image:crc()
- * image:display()
- *
- * image.device - get associated device_t
- * image.instance_name
- * image.brief_instance_name
- * image.software_parent
- * image.is_readable
- * image.is_writeable
- * image.is_creatable
- * image.is_reset_on_load
- * image.must_be_loaded
- */
+ auto plugin_type = sol().registry().new_usertype<plugin_options::plugin>("plugin", sol::no_constructor);
+ plugin_type["name"] = sol::readonly(&plugin_options::plugin::m_name);
+ plugin_type["description"] = sol::readonly(&plugin_options::plugin::m_description);
+ plugin_type["type"] = sol::readonly(&plugin_options::plugin::m_type);
+ plugin_type["directory"] = sol::readonly(&plugin_options::plugin::m_directory);
+ plugin_type["start"] = sol::readonly(&plugin_options::plugin::m_start);
- auto image_type = sol().registry().create_simple_usertype<device_image_interface>("new", sol::no_constructor);
- image_type.set("exists", &device_image_interface::exists);
- image_type.set("filename", &device_image_interface::filename);
- image_type.set("longname", &device_image_interface::longname);
- image_type.set("manufacturer", &device_image_interface::manufacturer);
- image_type.set("year", &device_image_interface::year);
- image_type.set("software_list_name", &device_image_interface::software_list_name);
- image_type.set("software_parent", sol::property([](device_image_interface &di) {
- const software_info *si = di.software_entry();
- return si ? si->parentname() : "";
- }));
- image_type.set("image_type_name", &device_image_interface::image_type_name);
- image_type.set("load", &device_image_interface::load);
- image_type.set("load_software", static_cast<image_init_result (device_image_interface::*)(const std::string &)>(&device_image_interface::load_software));
- image_type.set("unload", &device_image_interface::unload);
- image_type.set("create", [](device_image_interface &di, const std::string &filename) { return di.create(filename); });
- image_type.set("crc", &device_image_interface::crc);
- image_type.set("display", [](device_image_interface &di) { return di.call_display(); });
- image_type.set("device", sol::property(static_cast<const device_t &(device_image_interface::*)() const>(&device_image_interface::device)));
- image_type.set("instance_name", sol::property(&device_image_interface::instance_name));
- image_type.set("brief_instance_name", sol::property(&device_image_interface::brief_instance_name));
- image_type.set("is_readable", sol::property(&device_image_interface::is_readable));
- image_type.set("is_writeable", sol::property(&device_image_interface::is_writeable));
- image_type.set("is_creatable", sol::property(&device_image_interface::is_creatable));
- image_type.set("is_reset_on_load", sol::property(&device_image_interface::is_reset_on_load));
- image_type.set("must_be_loaded", sol::property(&device_image_interface::must_be_loaded));
- sol().registry().set_usertype("image", image_type);
-
-
-/* mame_machine_manager library
- *
- * manager
- * mame_manager - alias of manager
- *
- * manager:machine() - running machine
- * manager:options() - core options
- * manager:plugins() - plugin options
- * manager:ui() - mame ui manager
- */
- sol().registry().new_usertype<mame_machine_manager>("manager", "new", sol::no_constructor,
- "machine", &machine_manager::machine,
- "options", [](mame_machine_manager &m) { return static_cast<core_options *>(&m.options()); },
- "plugins", [this](mame_machine_manager &m) {
- sol::table table = sol().create_table();
- for (auto &curentry : m.plugins().plugins())
- {
- sol::table plugin_table = sol().create_table();
- plugin_table["name"] = curentry.m_name;
- plugin_table["description"] = curentry.m_description;
- plugin_table["type"] = curentry.m_type;
- plugin_table["directory"] = curentry.m_directory;
- plugin_table["start"] = curentry.m_start;
- table[curentry.m_name] = plugin_table;
- }
- return table;
- },
- "ui", &mame_machine_manager::ui);
- sol()["manager"] = std::ref(*mame_machine_manager::instance());
- sol()["mame_manager"] = std::ref(*mame_machine_manager::instance());
+ // set up other user types
+ initialize_debug(emu);
+ initialize_input(emu);
+ initialize_memory(emu);
+ initialize_render(emu);
}
//-------------------------------------------------
@@ -3023,6 +2164,10 @@ void lua_engine::initialize()
//-------------------------------------------------
bool lua_engine::frame_hook()
{
+ std::vector<int> tasks = std::move(m_update_tasks);
+ m_update_tasks.clear();
+ resume_tasks(m_lua_state, tasks, true); // TODO: doesn't need to return anything
+
return execute_function("LUA_ON_FRAME_DONE");
}
@@ -3032,6 +2177,10 @@ bool lua_engine::frame_hook()
void lua_engine::close()
{
+ m_notifiers.reset();
+ m_menu.clear();
+ m_update_tasks.clear();
+ m_frame_tasks.clear();
m_sol_state.reset();
if (m_lua_state)
{
@@ -3041,62 +2190,46 @@ void lua_engine::close()
}
}
-void lua_engine::resume(void *ptr, int nparam)
+void lua_engine::resume(s32 param)
{
- lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, nparam);
- lua_State *L = lua_tothread(m_lua_state, -1);
- lua_pop(m_lua_state, 1);
- int stat = lua_resume(L, nullptr, 0);
- if((stat != LUA_OK) && (stat != LUA_YIELD))
- {
- osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(L, -1));
- lua_pop(L, 1);
- }
- luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam);
-}
-
-void lua_engine::run(sol::load_result res)
-{
- if(res.valid())
- {
- auto ret = invoke(res.get<sol::protected_function>());
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in run: %s\n", err.what());
- }
- }
- else
- osd_printf_error("[LUA ERROR] %d loading Lua script\n", (int)res.status());
+ attotime const now = machine().time();
+ auto const pos = std::find_if(
+ m_waiting_tasks.begin(),
+ m_waiting_tasks.end(),
+ [&now] (auto const &x) { return now < x.first; });
+ std::vector<int> expired;
+ expired.reserve(std::distance(m_waiting_tasks.begin(), pos));
+ for (auto it = m_waiting_tasks.begin(); pos != it; ++it)
+ expired.emplace_back(it->second);
+ m_waiting_tasks.erase(m_waiting_tasks.begin(), pos);
+ if (!m_waiting_tasks.empty())
+ m_timer->reset(m_waiting_tasks.begin()->first - now);
+ resume_tasks(m_lua_state, expired, true);
}
//-------------------------------------------------
-// execute - load and execute script
+// load_script - load script from file path
//-------------------------------------------------
-void lua_engine::load_script(const char *filename)
+sol::load_result lua_engine::load_script(std::string const &filename)
{
- run(sol().load_file(filename));
+ return sol().load_file(filename);
}
//-------------------------------------------------
-// execute_string - execute script from string
+// load_string - load script from string
//-------------------------------------------------
-void lua_engine::load_string(const char *value)
+sol::load_result lua_engine::load_string(std::string const &value)
{
- run(sol().load(value));
+ return sol().load(value);
}
//-------------------------------------------------
-// invoke - invokes a function, wrapping profiler
+// make_environment - make a sandbox
//-------------------------------------------------
-template<typename TFunc, typename... TArgs>
-sol::protected_function_result lua_engine::invoke(TFunc &&func, TArgs&&... args)
+sol::environment lua_engine::make_environment()
{
- g_profiler.start(PROFILER_LUA);
- sol::protected_function_result result = func(std::forward<TArgs>(args)...);
- g_profiler.stop();
- return result;
+ return sol::environment(sol(), sol::create, sol().globals());
}