// 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 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 ↦ }; namespace sol { 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); 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; }; // don't convert core_optons to a table directly template <> struct is_container : std::false_type { }; // buffer customisation sol::buffer *sol_lua_get(sol::types, lua_State *L, int index, sol::stack::record &tracking); int sol_lua_push(sol::types, lua_State *L, buffer *value); // 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, image_init_result &&value); int sol_lua_push(sol::types, lua_State *L, image_verify_result &&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); 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 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()(const char *text) const { 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; } T operator()(const std::string &text) const { return (*this)(text.c_str()); } private: std::array, SIZE> m_map; }; //------------------------------------------------- // 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( [this, dflt, desc, cbfunc = cb.as()] () -> R { if constexpr (std::is_same_v) { (void)dflt; (void)desc; invoke(cbfunc); } else { auto result(invoke(cbfunc).get >()); if (result) { return *result; } else { osd_printf_error("[LUA ERROR] invalid return from %s callback\n", desc); return dflt(); } } })); } else { osd_printf_error("[LUA ERROR] must call %s with function or nil\n", name); } }; } //------------------------------------------------- // invoke - invokes a function, wrapping profiler //------------------------------------------------- template inline sol::protected_function_result lua_engine::invoke(TFunc &&func, TArgs &&... args) { g_profiler.start(PROFILER_LUA); sol::protected_function_result result = func(std::forward(args)...); g_profiler.stop(); return result; } #endif // MAME_FRONTEND_MAME_LUAENGINE_IPP