// 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 #include #include #include #include #include template struct lua_engine::simple_list_wrapper { simple_list_wrapper(simple_list const &l) : list(l) { } simple_list const &list; }; template 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 : std::false_type { }; // these things should be treated as containers template struct is_container > : std::true_type { }; template struct is_container > : std::true_type { }; template struct is_container > : std::true_type { }; template struct usertype_container >; template struct usertype_container > : lua_engine::immutable_collection_helper, simple_list const, typename simple_list::auto_iterator> { private: static int next_pairs(lua_State *L) { typename usertype_container::indexed_iterator &i(stack::unqualified_get >(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 &self(usertype_container::get_self(L)); std::ptrdiff_t const index(stack::unqualified_get(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 &self(usertype_container::get_self(L)); T &target(stack::unqualified_get(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 &self(usertype_container::get_self(L)); return stack::push(L, self.list.count()); } static int empty(lua_State *L) { lua_engine::simple_list_wrapper &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 &self(usertype_container::get_self(L)); stack::push(L, next_pairs); stack::push >(L, self.list, self.list.begin()); stack::push(L, lua_nil); return 3; } }; template struct usertype_container > : lua_engine::immutable_collection_helper, T const, typename T::const_iterator> { private: template static int next_pairs(lua_State *L) { typename usertype_container::indexed_iterator &i(stack::unqualified_get >(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 static int start_pairs(lua_State *L) { lua_engine::tag_object_ptr_map &self(usertype_container::get_self(L)); stack::push(L, next_pairs); stack::push >(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 &self(usertype_container::get_self(L)); std::ptrdiff_t const index(stack::unqualified_get(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 &self(usertype_container::get_self(L)); char const *const tag(stack::unqualified_get(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 &self(usertype_container::get_self(L)); auto &obj(stack::unqualified_getsecond)>(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 &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 &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); } static int pairs(lua_State *L) { return start_pairs(L); } static int ipairs(lua_State *L) { return start_pairs(L); } }; } // namespace sol // automatically convert std::error_condition to string int sol_lua_push(sol::types, lua_State &L, std::error_condition &&value); // enums to automatically convert to strings int sol_lua_push(sol::types, lua_State *L, map_handler_type &&value); int sol_lua_push(sol::types, lua_State *L, endianness_t &&value); template struct lua_engine::immutable_container_helper { protected: static T &get_self(lua_State *L) { auto p(sol::stack::unqualified_check_get(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().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().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().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().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().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().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().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().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().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().c_str()); } }; template struct lua_engine::immutable_collection_helper : immutable_container_helper { 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 struct lua_engine::immutable_sequence_helper : immutable_collection_helper { protected: template static int next_pairs(lua_State *L) { auto &i(sol::stack::unqualified_get >(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) 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 static int start_pairs(lua_State *L) { T &self(immutable_sequence_helper::get_self(L)); sol::stack::push(L, next_pairs); sol::stack::push >(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(L, 2)); if ((0 >= index) || (self.items().size() < index)) { return sol::stack::push(L, sol::lua_nil); } else { if constexpr (std::is_reference_v) 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(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); } static int pairs(lua_State *L) { return start_pairs(L); } static int ipairs(lua_State *L) { return start_pairs(L); } }; struct lua_engine::addr_space { addr_space(address_space &s, device_memory_interface &d) : space(s), dev(d) { } template T mem_read(offs_t address); template void mem_write(offs_t address, T val); template T log_mem_read(offs_t address); template void log_mem_write(offs_t address, T val); template T direct_mem_read(offs_t address); template void direct_mem_write(offs_t address, T val); address_space &space; device_memory_interface &dev; }; template class lua_engine::enum_parser { public: constexpr enum_parser(std::initializer_list > 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, Size> m_map; }; //------------------------------------------------- // make_notifier_adder - make a function for // subscribing to a notifier //------------------------------------------------- template auto lua_engine::make_notifier_adder(util::notifier ¬ifier, const char *desc) { return [this, ¬ifier, desc] (sol::protected_function cb) { return notifier.subscribe( delegate( [this, desc, cbfunc = sol::protected_function(m_lua_state, cb)] (T... args) { auto status(invoke(cbfunc, std::forward(args)...)); if (!status.valid()) { auto err(status.template get()); 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 auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate &&), 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()); } else if (cb.is()) { (self.*setter)(delegate( [dflt, desc, cbfunc = sol::protected_function(m_lua_state, cb)] (A... args) -> R { auto status(invoke_direct(cbfunc, std::forward(args)...)); if (status.valid()) { if constexpr (std::is_same_v) { std::ignore = dflt; } else { auto result(status.template get >()); 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()); osd_printf_error("[LUA ERROR] error in %s callback: %s\n", desc, err.what()); if constexpr (!std::is_same_v) return dflt(); } })); } else { osd_printf_error("[LUA ERROR] must call %s with function or nil\n", name); } }; } #endif // MAME_FRONTEND_MAME_LUAENGINE_IPP