diff options
Diffstat (limited to 'src/frontend/mame/luaengine.ipp')
-rw-r--r-- | src/frontend/mame/luaengine.ipp | 660 |
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 ↦ +}; + + +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...> ¬ifier, const char *desc) +{ + return + [this, ¬ifier, 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 |