summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/luaengine.ipp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/luaengine.ipp')
-rw-r--r--src/frontend/mame/luaengine.ipp660
1 files changed, 660 insertions, 0 deletions
diff --git a/src/frontend/mame/luaengine.ipp b/src/frontend/mame/luaengine.ipp
new file mode 100644
index 00000000000..e08f267538e
--- /dev/null
+++ b/src/frontend/mame/luaengine.ipp
@@ -0,0 +1,660 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic
+/***************************************************************************
+
+ luaengine.ipp
+
+ Controls execution of the core MAME system.
+
+***************************************************************************/
+#ifndef MAME_FRONTEND_MAME_LUAENGINE_IPP
+#define MAME_FRONTEND_MAME_LUAENGINE_IPP
+
+#include "luaengine.h"
+
+#include "options.h"
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <cassert>
+#include <system_error>
+#include <type_traits>
+
+
+
+template <typename T>
+struct lua_engine::simple_list_wrapper
+{
+ simple_list_wrapper(simple_list<T> const &l) : list(l) { }
+
+ simple_list<T> const &list;
+};
+
+
+template <typename T>
+struct lua_engine::tag_object_ptr_map
+{
+ tag_object_ptr_map(T const &m) : map(m) { }
+
+ T const &map;
+};
+
+
+class lua_engine::buffer_helper
+{
+private:
+ class proxy
+ {
+ private:
+ buffer_helper &m_host;
+ char *m_space;
+ size_t const m_size;
+
+ public:
+ proxy(proxy const &) = delete;
+ proxy &operator=(proxy const &) = delete;
+
+ proxy(proxy &&that) : m_host(that.m_host), m_space(that.m_space), m_size(that.m_size)
+ {
+ that.m_space = nullptr;
+ }
+
+ proxy(buffer_helper &host, size_t size) : m_host(host), m_space(luaL_prepbuffsize(&host.m_buffer, size)), m_size(size)
+ {
+ m_host.m_prepared = true;
+ }
+
+ ~proxy()
+ {
+ if (m_space)
+ {
+ assert(m_host.m_prepared);
+ luaL_addsize(&m_host.m_buffer, 0U);
+ m_host.m_prepared = false;
+ }
+ }
+
+ char *get()
+ {
+ return m_space;
+ }
+
+ void add(size_t size)
+ {
+ assert(m_space);
+ assert(size <= m_size);
+ assert(m_host.m_prepared);
+ m_space = nullptr;
+ luaL_addsize(&m_host.m_buffer, size);
+ m_host.m_prepared = false;
+ }
+ };
+
+ luaL_Buffer m_buffer;
+ bool m_valid;
+ bool m_prepared;
+
+public:
+ buffer_helper(buffer_helper const &) = delete;
+ buffer_helper &operator=(buffer_helper const &) = delete;
+
+ buffer_helper(lua_State *L)
+ {
+ luaL_buffinit(L, &m_buffer);
+ m_valid = true;
+ m_prepared = false;
+ }
+
+ ~buffer_helper()
+ {
+ assert(!m_prepared);
+ if (m_valid)
+ luaL_pushresult(&m_buffer);
+ }
+
+ void push()
+ {
+ assert(m_valid);
+ assert(!m_prepared);
+ luaL_pushresult(&m_buffer);
+ m_valid = false;
+ }
+
+ proxy prepare(size_t size)
+ {
+ assert(m_valid);
+ assert(!m_prepared);
+ return proxy(*this, size);
+ }
+};
+
+
+class lua_engine::palette_wrapper
+{
+public:
+ palette_wrapper(uint32_t numcolors, uint32_t numgroups) : m_palette(palette_t::alloc(numcolors, numgroups))
+ {
+ }
+
+ palette_wrapper(palette_t &pal) : m_palette(&pal)
+ {
+ m_palette->ref();
+ }
+
+ palette_wrapper(palette_wrapper const &that) : m_palette(that.m_palette)
+ {
+ m_palette->ref();
+ }
+
+ ~palette_wrapper()
+ {
+ m_palette->deref();
+ }
+
+ palette_wrapper &operator=(palette_wrapper const &that)
+ {
+ that.m_palette->ref();
+ m_palette->deref();
+ m_palette = that.m_palette;
+ return *this;
+ }
+
+ palette_t const &palette() const
+ {
+ return *m_palette;
+ }
+
+ palette_t &palette()
+ {
+ return *m_palette;
+ }
+
+private:
+ palette_t *m_palette;
+};
+
+
+namespace sol {
+
+// don't convert core_options to a table directly
+template <> struct is_container<core_options> : std::false_type { };
+
+
+// these things should be treated as containers
+template <typename T> struct is_container<lua_engine::devenum<T> > : std::true_type { };
+template <typename T> struct is_container<lua_engine::simple_list_wrapper<T> > : std::true_type { };
+template <typename T> struct is_container<lua_engine::tag_object_ptr_map<T> > : std::true_type { };
+
+
+template <typename T> struct usertype_container<lua_engine::devenum<T> >;
+
+
+template <typename T>
+struct usertype_container<lua_engine::simple_list_wrapper<T> > : lua_engine::immutable_collection_helper<lua_engine::simple_list_wrapper<T>, simple_list<T> const, typename simple_list<T>::auto_iterator>
+{
+private:
+ 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;
+ result = stack::push(L, i.ix + 1);
+ result += stack::push_reference(L, *i.it);
+ ++i;
+ return result;
+ }
+
+public:
+ static int at(lua_State *L)
+ {
+ lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
+ std::ptrdiff_t const index(stack::unqualified_get<std::ptrdiff_t>(L, 2));
+ if ((0 >= index) || (self.list.count() < index))
+ return stack::push(L, lua_nil);
+ else
+ return stack::push_reference(L, *self.list.find(index - 1));
+ }
+
+ static int get(lua_State *L) { return at(L); }
+ static int index_get(lua_State *L) { return at(L); }
+
+ static int index_of(lua_State *L)
+ {
+ lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
+ T &target(stack::unqualified_get<T>(L, 2));
+ int const found(self.list.indexof(target));
+ if (0 > found)
+ return stack::push(L, lua_nil);
+ else
+ return stack::push(L, found + 1);
+ }
+
+ static int size(lua_State *L)
+ {
+ lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
+ return stack::push(L, self.list.count());
+ }
+
+ static int empty(lua_State *L)
+ {
+ lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
+ return stack::push(L, self.list.empty());
+ }
+
+ static int next(lua_State *L) { return stack::push(L, next_pairs); }
+ static int pairs(lua_State *L) { return ipairs(L); }
+
+ static int ipairs(lua_State *L)
+ {
+ lua_engine::simple_list_wrapper<T> &self(usertype_container::get_self(L));
+ stack::push(L, next_pairs);
+ stack::push<user<typename usertype_container::indexed_iterator> >(L, self.list, self.list.begin());
+ stack::push(L, lua_nil);
+ return 3;
+ }
+};
+
+
+template <typename T>
+struct usertype_container<lua_engine::tag_object_ptr_map<T> > : lua_engine::immutable_collection_helper<lua_engine::tag_object_ptr_map<T>, T const, typename T::const_iterator>
+{
+private:
+ 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->first);
+ result += stack::push_reference(L, *i.it->second);
+ ++i;
+ return result;
+ }
+
+ template <bool Indexed>
+ static int start_pairs(lua_State *L)
+ {
+ lua_engine::tag_object_ptr_map<T> &self(usertype_container::get_self(L));
+ stack::push(L, next_pairs<Indexed>);
+ stack::push<user<typename usertype_container::indexed_iterator> >(L, self.map, self.map.begin());
+ stack::push(L, lua_nil);
+ return 3;
+ }
+
+public:
+ static int at(lua_State *L)
+ {
+ lua_engine::tag_object_ptr_map<T> &self(usertype_container::get_self(L));
+ std::ptrdiff_t const index(stack::unqualified_get<std::ptrdiff_t>(L, 2));
+ if ((0 >= index) || (self.map.size() < index))
+ return stack::push(L, lua_nil);
+ auto const found(std::next(self.map.begin(), index - 1));
+ if (!found->second)
+ return stack::push(L, lua_nil);
+ else
+ return stack::push_reference(L, *found->second);
+ }
+
+ static int get(lua_State *L)
+ {
+ lua_engine::tag_object_ptr_map<T> &self(usertype_container::get_self(L));
+ char const *const tag(stack::unqualified_get<char const *>(L));
+ auto const found(self.map.find(tag));
+ if ((self.map.end() == found) || !found->second)
+ return stack::push(L, lua_nil);
+ else
+ return stack::push_reference(L, *found->second);
+ }
+
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
+
+ static int index_of(lua_State *L)
+ {
+ lua_engine::tag_object_ptr_map<T> &self(usertype_container::get_self(L));
+ auto &obj(stack::unqualified_get<decltype(*self.map.begin()->second)>(L, 2));
+ auto it(self.map.begin());
+ std::ptrdiff_t ix(0);
+ while ((self.map.end() != it) && (it->second.get() != &obj))
+ {
+ ++it;
+ ++ix;
+ }
+ if (self.map.end() == it)
+ return stack::push(L, lua_nil);
+ else
+ return stack::push(L, ix + 1);
+ }
+
+ static int size(lua_State *L)
+ {
+ lua_engine::tag_object_ptr_map<T> &self(usertype_container::get_self(L));
+ return stack::push(L, self.map.size());
+ }
+
+ static int empty(lua_State *L)
+ {
+ lua_engine::tag_object_ptr_map<T> &self(usertype_container::get_self(L));
+ return stack::push(L, self.map.empty());
+ }
+
+ 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); }
+};
+
+} // namespace sol
+
+
+// automatically convert std::error_condition to string
+int sol_lua_push(sol::types<std::error_condition>, lua_State &L, std::error_condition &&value);
+
+// enums to automatically convert to strings
+int sol_lua_push(sol::types<map_handler_type>, lua_State *L, map_handler_type &&value);
+int sol_lua_push(sol::types<endianness_t>, lua_State *L, endianness_t &&value);
+
+
+template <typename T>
+struct lua_engine::immutable_container_helper
+{
+protected:
+ static T &get_self(lua_State *L)
+ {
+ auto p(sol::stack::unqualified_check_get<T *>(L, 1));
+ if (!p)
+ luaL_error(L, "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", sol::detail::demangle<T>().c_str());
+ if (!*p)
+ luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type", sol::detail::demangle<T>().c_str());
+ return **p;
+ }
+
+public:
+ static int set(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'set(key, value)' on type '%s': container is not modifiable", sol::detail::demangle<T>().c_str());
+ }
+
+ static int index_set(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'container[key] = value' on type '%s': container is not modifiable", sol::detail::demangle<T>().c_str());
+ }
+
+ static int add(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'add' on type '%s': container is not modifiable", sol::detail::demangle<T>().c_str());
+ }
+
+ static int insert(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'insert' on type '%s': container is not modifiable", sol::detail::demangle<T>().c_str());
+ }
+
+ static int find(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'find' on type '%s': no supported comparison operator for the value type", sol::detail::demangle<T>().c_str());
+ }
+
+ static int index_of(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'index_of' on type '%s': no supported comparison operator for the value type", sol::detail::demangle<T>().c_str());
+ }
+
+ static int clear(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'clear' on type '%s': container is not modifiable", sol::detail::demangle<T>().c_str());
+ }
+
+ static int erase(lua_State *L)
+ {
+ return luaL_error(L, "sol: cannot call 'erase' on type '%s': container is not modifiable", sol::detail::demangle<T>().c_str());
+ }
+};
+
+
+template <typename T, typename C, typename I>
+struct lua_engine::immutable_collection_helper : immutable_container_helper<T>
+{
+protected:
+ using iterator = I;
+
+ struct indexed_iterator
+ {
+ indexed_iterator(C &s, iterator i) : src(s), it(i), ix(0U) { }
+
+ C &src;
+ iterator it;
+ std::size_t ix;
+
+ indexed_iterator &operator++()
+ {
+ ++it;
+ ++ix;
+ return *this;
+ }
+ };
+};
+
+
+template <typename T, typename C, typename I>
+struct lua_engine::immutable_sequence_helper : immutable_collection_helper<T, C, I>
+{
+protected:
+ template <bool Indexed>
+ static int next_pairs(lua_State *L)
+ {
+ auto &i(sol::stack::unqualified_get<sol::user<typename immutable_sequence_helper::indexed_iterator> >(L, 1));
+ if (i.src.end() == i.it)
+ return sol::stack::push(L, sol::lua_nil);
+ int result;
+ if constexpr (Indexed)
+ result = sol::stack::push(L, i.ix + 1);
+ else
+ result = T::push_key(L, i.it, i.ix);
+ if constexpr (std::is_reference_v<decltype(T::unwrap(i.it))>)
+ result += sol::stack::push_reference(L, std::ref(T::unwrap(i.it)));
+ else
+ result += sol::stack::push_reference(L, T::unwrap(i.it));
+ ++i;
+ return result;
+ }
+
+ template <bool Indexed>
+ static int start_pairs(lua_State *L)
+ {
+ T &self(immutable_sequence_helper::get_self(L));
+ sol::stack::push(L, next_pairs<Indexed>);
+ sol::stack::push<sol::user<typename immutable_sequence_helper::indexed_iterator> >(L, self.items(), self.items().begin());
+ sol::stack::push(L, sol::lua_nil);
+ return 3;
+ }
+
+public:
+ static int at(lua_State *L)
+ {
+ T &self(immutable_sequence_helper::get_self(L));
+ std::ptrdiff_t const index(sol::stack::unqualified_get<std::ptrdiff_t>(L, 2));
+ if ((0 >= index) || (self.items().size() < index))
+ {
+ return sol::stack::push(L, sol::lua_nil);
+ }
+ else
+ {
+ if constexpr (std::is_reference_v<decltype(T::unwrap(std::next(self.items().begin(), index - 1)))>)
+ return sol::stack::push_reference(L, std::ref(T::unwrap(std::next(self.items().begin(), index - 1))));
+ else
+ return sol::stack::push_reference(L, T::unwrap(std::next(self.items().begin(), index - 1)));
+ }
+ }
+
+ static int index_of(lua_State *L)
+ {
+ T &self(immutable_sequence_helper::get_self(L));
+ auto it(self.items().begin());
+ std::ptrdiff_t ix(0);
+ auto const &item(sol::stack::unqualified_get<decltype(T::unwrap(it))>(L, 2));
+ while ((self.items().end() != it) && (&item != &T::unwrap(it)))
+ {
+ ++it;
+ ++ix;
+ }
+ if (self.items().end() == it)
+ return sol::stack::push(L, sol::lua_nil);
+ else
+ return sol::stack::push(L, ix + 1);
+ }
+
+ static int size(lua_State *L)
+ {
+ T &self(immutable_sequence_helper::get_self(L));
+ return sol::stack::push(L, self.items().size());
+ }
+
+ static int empty(lua_State *L)
+ {
+ T &self(immutable_sequence_helper::get_self(L));
+ return sol::stack::push(L, self.items().empty());
+ }
+
+ static int next(lua_State *L) { return sol::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); }
+};
+
+
+
+struct lua_engine::addr_space
+{
+ addr_space(address_space &s, device_memory_interface &d) : space(s), dev(d) { }
+
+ template <typename T> T mem_read(offs_t address);
+ template <typename T> void mem_write(offs_t address, T val);
+ template <typename T> T log_mem_read(offs_t address);
+ template <typename T> void log_mem_write(offs_t address, T val);
+ template <typename T> T direct_mem_read(offs_t address);
+ template <typename T> void direct_mem_write(offs_t address, T val);
+
+ address_space &space;
+ device_memory_interface &dev;
+};
+
+
+template <typename T, size_t Size>
+class lua_engine::enum_parser
+{
+public:
+ constexpr enum_parser(std::initializer_list<std::pair<std::string_view, T> > values)
+ {
+ if (values.size() != Size)
+ throw false && "size template argument incorrectly specified";
+ std::copy(values.begin(), values.end(), m_map.begin());
+ }
+
+ T operator()(std::string_view text) const
+ {
+ auto iter = std::find_if(
+ m_map.begin() + 1,
+ m_map.end(),
+ [&text] (const auto &x) { return text == x.first; });
+ if (iter == m_map.end())
+ iter = m_map.begin();
+ return iter->second;
+ }
+
+private:
+ std::array<std::pair<std::string_view, T>, Size> m_map;
+};
+
+
+//-------------------------------------------------
+// make_notifier_adder - make a function for
+// subscribing to a notifier
+//-------------------------------------------------
+
+template <typename... T>
+auto lua_engine::make_notifier_adder(util::notifier<T...> &notifier, const char *desc)
+{
+ return
+ [this, &notifier, desc] (sol::protected_function cb)
+ {
+ return notifier.subscribe(
+ delegate<void (T...)>(
+ [this, desc, cbfunc = sol::protected_function(m_lua_state, cb)] (T... args)
+ {
+ auto status(invoke(cbfunc, std::forward<T>(args)...));
+ if (!status.valid())
+ {
+ auto err(status.template get<sol::error>());
+ osd_printf_error("[LUA ERROR] error in %s callback: %s\n", desc, err.what());
+ }
+ }));
+ };
+}
+
+
+//-------------------------------------------------
+// make_simple_callback_setter - make a callback
+// setter for simple cases
+//-------------------------------------------------
+
+template <typename T, typename D, typename R, typename... A>
+auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R (A...)> &&), D &&dflt, const char *name, const char *desc)
+{
+ return
+ [this, setter, dflt, name, desc] (T &self, sol::object cb)
+ {
+ if (cb == sol::lua_nil)
+ {
+ (self.*setter)(delegate<R (A...)>());
+ }
+ else if (cb.is<sol::protected_function>())
+ {
+ (self.*setter)(delegate<R (A...)>(
+ [dflt, desc, cbfunc = sol::protected_function(m_lua_state, cb)] (A... args) -> R
+ {
+ auto status(invoke_direct(cbfunc, std::forward<A>(args)...));
+ if (status.valid())
+ {
+ if constexpr (std::is_same_v<R, void>)
+ {
+ std::ignore = dflt;
+ }
+ else
+ {
+ auto result(status.template get<std::optional<R> >());
+ if (result)
+ {
+ return *result;
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] invalid return from %s callback\n", desc);
+ return dflt();
+ }
+ }
+ }
+ else
+ {
+ auto err(status.template get<sol::error>());
+ osd_printf_error("[LUA ERROR] error in %s callback: %s\n", desc, err.what());
+ if constexpr (!std::is_same_v<R, void>)
+ return dflt();
+ }
+ }));
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] must call %s with function or nil\n", name);
+ }
+ };
+}
+
+#endif // MAME_FRONTEND_MAME_LUAENGINE_IPP