diff --git a/src/client/component/lui.cpp b/src/client/component/lui.cpp index 1a9d4e15..50267901 100644 --- a/src/client/component/lui.cpp +++ b/src/client/component/lui.cpp @@ -36,6 +36,12 @@ namespace lui game::LUI_OpenMenu(0, params[1], 1, 0, 0); }); + + command::add("lui_restart", []() + { + utils::hook::invoke(game::base_address + 0x3203B0); + utils::hook::invoke(game::base_address + 0x32D370); + }); } }; } diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 9f7af16f..ff799919 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -21,19 +21,20 @@ namespace ui_scripting { + std::unordered_map functions; + std::unordered_map methods; + namespace { utils::hook::detour hksi_open_lib_hook; utils::hook::detour hksi_lual_error_hook; + utils::hook::detour hksi_lual_error_hook2; utils::hook::detour hksi_add_function_hook; utils::hook::detour hks_start_hook; utils::hook::detour hks_shutdown_hook; bool error_hook_enabled = false; - std::unordered_map functions; - std::unordered_map methods; - void* hksi_open_lib_stub(game::hks::lua_State* s, const char* libname, game::hks::luaL_Reg* l) { for (auto i = l; i->name; ++i) @@ -184,6 +185,7 @@ namespace ui_scripting hks_shutdown_hook.create(game::base_address + 0x3203B0, hks_shutdown_stub); hksi_open_lib_hook.create(game::base_address + 0x2E4530, hksi_open_lib_stub); hksi_lual_error_hook.create(game::base_address + 0x2E3E40, hksi_lual_error_stub); + hksi_lual_error_hook2.create(game::base_address + 0x2DCB40, hksi_lual_error_stub); hksi_add_function_hook.create(game::base_address + 0x2DB570, hksi_add_function_stub); } }; diff --git a/src/client/component/ui_scripting.hpp b/src/client/component/ui_scripting.hpp index 30e8a1bd..3189c7b0 100644 --- a/src/client/component/ui_scripting.hpp +++ b/src/client/component/ui_scripting.hpp @@ -4,6 +4,9 @@ namespace ui_scripting { + extern std::unordered_map functions; + extern std::unordered_map methods; + void enable_error_hook(); void disable_error_hook(); diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 688b41a6..59e3c2fd 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -942,12 +942,14 @@ namespace game char m_data[30]; }; + struct HashTable; + union HksValue { void* cClosure; void* closure; UserData* userData; - void* table; + HashTable* table; void* tstruct; InternString* str; void* thread; @@ -983,15 +985,31 @@ namespace game HksValue v; }; - struct lua_State + struct CallStack + { + void* m_records; + void* m_lastrecord; + void* m_current; + const unsigned int* m_current_lua_pc; + const unsigned int* m_hook_return_addr; + int m_hook_level; + }; + + struct ApiStack { - char __pad0[72]; HksObject* top; HksObject* base; HksObject* alloc_top; HksObject* bottom; }; + struct lua_State + { + char __pad0[24]; + CallStack m_callStack; + ApiStack m_apistack; + }; + using lua_function = int(__fastcall*)(lua_State*); struct luaL_Reg @@ -999,5 +1017,26 @@ namespace game const char* name; lua_function function; }; + + struct Node + { + HksObject m_key; + HksObject m_value; + }; + + struct Metatable + { + }; + + struct HashTable : ChunkHeader + { + Metatable* m_meta; + unsigned int m_version; + unsigned int m_mask; + Node* m_hashPart; + HksObject* m_arrayPart; + unsigned int m_arraySize; + Node* m_freeNode; + }; } } \ No newline at end of file diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 658e3d1a..1a5e5958 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -155,5 +155,10 @@ namespace game WEAK symbol hks_obj_tolstring{0x287410}; WEAK symbol hks_obj_getmetatable{0x2DA210}; WEAK symbol hks_obj_getfield{0x2D9E20}; + WEAK symbol hks_obj_settable{0x2DB040}; + WEAK symbol vm_call_internal{0x30AB60}; + WEAK symbol hksi_lua_pushvalue{0x2DE040}; + WEAK symbol Hashtable_Create{0x2C8290}; + WEAK symbol Hashtable_getNextHash{0x2D5150}; } } \ No newline at end of file diff --git a/src/client/game/ui_scripting/execution.cpp b/src/client/game/ui_scripting/execution.cpp index 47dd551e..353abbb7 100644 --- a/src/client/game/ui_scripting/execution.cpp +++ b/src/client/game/ui_scripting/execution.cpp @@ -1,5 +1,4 @@ #include -#include "value.hpp" #include "execution.hpp" #include "stack_isolation.hpp" #include "component/ui_scripting.hpp" @@ -8,122 +7,206 @@ namespace ui_scripting { - namespace + void push_value(const value& value) { - bool valid_state() - { - return *game::hks::lua_state != 0; - } - - void push_value(const value& value_) - { - if (!valid_state()) - { - throw std::runtime_error("Invalid lua state"); - } - - const auto state = *game::hks::lua_state; - - switch (value_.index()) - { - case 1: - { - const auto value = std::get(value_); - state->top->t = game::hks::HksObjectType::TBOOLEAN; - state->top->v.boolean = value; - state->top++; - break; - } - case 2: - { - const auto value = std::get(value_); - state->top->t = game::hks::HksObjectType::TNUMBER; - state->top->v.number = static_cast(value); - state->top++; - break; - } - case 3: - { - const auto value = std::get(value_); - state->top->t = game::hks::HksObjectType::TNUMBER; - state->top->v.number = value; - state->top++; - break; - } - case 4: - { - const auto str = std::get(value_); - game::hks::hksi_lua_pushlstring(state, str.data(), (unsigned int)str.size()); - break; - } - case 5: - { - const auto data = std::get(value_); - state->top->t = game::hks::HksObjectType::TLIGHTUSERDATA; - state->top->v.ptr = data.ptr; - state->top++; - break; - } - } - } - } - - bool is_integer(float number) - { - return static_cast(number) == number; + const auto state = *game::hks::lua_state; + const auto value_ = value.get_raw(); + *state->m_apistack.top = value_; + state->m_apistack.top++; } value get_return_value(int offset) { - if (!valid_state()) - { - throw std::runtime_error("Invalid lua state"); - } - const auto state = *game::hks::lua_state; - const auto top = &state->top[-1 - offset]; + const auto top = &state->m_apistack.top[-1 - offset]; + return *top; + } - switch (top->t) + void call_script_function(const function& function, const arguments& arguments) + { + const auto state = *game::hks::lua_state; + + stack_isolation _; + for (auto i = arguments.rbegin(); i != arguments.rend(); ++i) { - case game::hks::HksObjectType::TBOOLEAN: - { - return {top->v.boolean}; + push_value(*i); } - case game::hks::HksObjectType::TNUMBER: + push_value(function); + + enable_error_hook(); + const auto __ = gsl::finally([]() { - const auto number = top->v.number; - if (is_integer(number)) - { - return {static_cast(top->v.number)}; - } - else - { - return {top->v.number}; - } - } - case game::hks::HksObjectType::TSTRING: + disable_error_hook(); + }); + + // Not sure about this + + try { - const auto value = top->v.str->m_data; - return {std::string(value)}; + game::hks::vm_call_internal(state, static_cast(arguments.size()), 0, 0); } - case game::hks::HksObjectType::TLIGHTUSERDATA: + catch (const std::exception& e) { - return lightuserdata{top->v.ptr}; - } - default: - { - return {}; - } + throw std::runtime_error(std::string("Error executing script function: ") + e.what()); } } - std::vector call(const std::string& name, const arguments& arguments) + value get_field(const userdata& self, const value& key) { - if (!valid_state()) + const auto state = *game::hks::lua_state; + + stack_isolation _; + push_value(key); + + enable_error_hook(); + const auto __ = gsl::finally([]() { - throw std::runtime_error("Invalid lua state"); + disable_error_hook(); + }); + + game::hks::HksObject value{}; + game::hks::HksObject userdata{}; + userdata.t = game::hks::TUSERDATA; + userdata.v.ptr = self.ptr; + + try + { + game::hks::hks_obj_getfield(&value, state, &userdata, &state->m_apistack.top[-1]); + return value; + } + catch (const std::exception& e) + { + throw std::runtime_error(std::string("Error getting userdata field: ") + e.what()); + } + } + + value get_field(const table& self, const value& key) + { + const auto state = *game::hks::lua_state; + + stack_isolation _; + push_value(key); + + enable_error_hook(); + const auto __ = gsl::finally([]() + { + disable_error_hook(); + }); + + game::hks::HksObject value{}; + game::hks::HksObject userdata{}; + userdata.t = game::hks::TTABLE; + userdata.v.ptr = self.ptr; + + try + { + game::hks::hks_obj_getfield(&value, state, &userdata, &state->m_apistack.top[-1]); + return value; + } + catch (const std::exception& e) + { + throw std::runtime_error(std::string("Error getting table field: ") + e.what()); + } + } + + void set_field(const userdata& self, const value& key, const value& value) + { + const auto state = *game::hks::lua_state; + + stack_isolation _; + + enable_error_hook(); + const auto __ = gsl::finally([]() + { + disable_error_hook(); + }); + + game::hks::HksObject userdata{}; + userdata.t = game::hks::TUSERDATA; + userdata.v.ptr = self.ptr; + + try + { + game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw()); + } + catch (const std::exception& e) + { + throw std::runtime_error(std::string("Error setting userdata field: ") + e.what()); + } + } + + void set_field(const table& self, const value& key, const value& value) + { + const auto state = *game::hks::lua_state; + + stack_isolation _; + + enable_error_hook(); + const auto __ = gsl::finally([]() + { + disable_error_hook(); + }); + + game::hks::HksObject userdata{}; + userdata.t = game::hks::TTABLE; + userdata.v.ptr = self.ptr; + + try + { + game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw()); + } + catch (const std::exception& e) + { + throw std::runtime_error(std::string("Error setting table field: ") + e.what()); + } + } + + arguments call_method(const userdata& self, const std::string& name, const arguments& arguments) + { + const auto function = ui_scripting::find_method(name); + if (!function) + { + throw std::runtime_error("Function " + name + " not found"); } + stack_isolation _; + push_value(self); + for (auto i = arguments.rbegin(); i != arguments.rend(); ++i) + { + push_value(*i); + } + + enable_error_hook(); + const auto __ = gsl::finally([]() + { + disable_error_hook(); + }); + + try + { + const auto count = function(*game::hks::lua_state); + std::vector values; + + for (auto i = 0; i < count; i++) + { + values.push_back(get_return_value(i)); + } + + if (values.size() == 0) + { + values.push_back({}); + } + + return values; + } + catch (const std::exception& e) + { + throw std::runtime_error("Error executing method " + name + ": " + e.what()); + } + } + + arguments call(const std::string& name, const arguments& arguments) + { const auto function = ui_scripting::find_function(name); if (!function) { @@ -131,19 +214,17 @@ namespace ui_scripting } stack_isolation _; + for (auto i = arguments.rbegin(); i != arguments.rend(); ++i) + { + push_value(*i); + } + enable_error_hook(); const auto __ = gsl::finally([]() { disable_error_hook(); }); - enable_error_hook(); - - for (const auto& value_ : arguments) - { - push_value(value_); - } - try { const auto count = function(*game::hks::lua_state); diff --git a/src/client/game/ui_scripting/execution.hpp b/src/client/game/ui_scripting/execution.hpp index 75463723..17a39a70 100644 --- a/src/client/game/ui_scripting/execution.hpp +++ b/src/client/game/ui_scripting/execution.hpp @@ -1,7 +1,20 @@ #pragma once #include "game/game.hpp" +#include "types.hpp" +#include "value.hpp" namespace ui_scripting { - std::vector call(const std::string& name, const arguments& arguments); + void push_value(const value& value); + value get_return_value(int offset); + + void call_script_function(const function& function, const arguments& arguments); + + value get_field(const userdata& self, const value& key); + value get_field(const table& self, const value& key); + void set_field(const userdata& self, const value& key, const value& value); + void set_field(const table& self, const value& key, const value& value); + + arguments call_method(const userdata& self, const std::string& name, const arguments& arguments); + arguments call(const std::string& name, const arguments& arguments); } diff --git a/src/client/game/ui_scripting/lua/context.cpp b/src/client/game/ui_scripting/lua/context.cpp index 99f2c430..c56dbfe7 100644 --- a/src/client/game/ui_scripting/lua/context.cpp +++ b/src/client/game/ui_scripting/lua/context.cpp @@ -1,6 +1,7 @@ #include #include "context.hpp" #include "error.hpp" +#include "value_conversion.hpp" #include "../../scripting/execution.hpp" #include "../value.hpp" #include "../execution.hpp" @@ -26,7 +27,7 @@ namespace ui_scripting::lua { const auto animation_script = utils::nt::load_resource(LUA_ANIMATION_SCRIPT); - scripting::script_value convert(const sol::lua_value& value) + scripting::script_value script_convert(const sol::lua_value& value) { if (value.is()) { @@ -505,14 +506,7 @@ namespace ui_scripting::lua for (auto arg : va) { - if (arg.is()) - { - event.arguments.push_back(arg.as()); - } - else - { - event.arguments.push_back({}); - } + event.arguments.push_back(convert({s, arg})); } notify(event); @@ -529,9 +523,9 @@ namespace ui_scripting::lua } ); - element_type[sol::meta_function::new_index] = [](element& element, const std::string& attribute, const value& value) + element_type[sol::meta_function::new_index] = [](element& element, const sol::this_state s, const std::string& attribute, const sol::lua_value& value) { - element.attributes[attribute] = value; + element.attributes[attribute] = convert({s, value}); }; element_type[sol::meta_function::index] = [](element& element, const sol::this_state s, const std::string& attribute) @@ -541,7 +535,7 @@ namespace ui_scripting::lua return sol::lua_value{s, sol::lua_nil}; } - return sol::lua_value{s, element.attributes[attribute]}; + return sol::lua_value{s, convert(s, element.attributes[attribute])}; }; auto menu_type = state.new_usertype("menu"); @@ -579,14 +573,7 @@ namespace ui_scripting::lua for (auto arg : va) { - if (arg.is()) - { - event.arguments.push_back(arg.as()); - } - else - { - event.arguments.push_back({}); - } + event.arguments.push_back(convert({s, arg})); } notify(event); @@ -655,11 +642,13 @@ namespace ui_scripting::lua menu.close(); }; - menu_type["getelement"] = [](menu& menu, const sol::this_state s, const value& value, const std::string& attribute) + menu_type["getelement"] = [](menu& menu, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { + const auto value_ = convert({s, value}); + for (const auto& element : menu.children) { - if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value) + if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { return sol::lua_value{s, element}; } @@ -670,13 +659,14 @@ namespace ui_scripting::lua menu_type["getelements"] = sol::overload ( - [](menu& menu, const sol::this_state s, const value& value, const std::string& attribute) + [](menu& menu, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { + const auto value_ = convert({s, value}); auto result = sol::table::create(s.lua_state()); for (const auto& element : menu.children) { - if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value) + if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { result.add(element); } @@ -713,11 +703,13 @@ namespace ui_scripting::lua return sol::lua_value{s, &menus[name]}; }; - game_type["getelement"] = [](const game&, const sol::this_state s, const value& value, const std::string& attribute) + game_type["getelement"] = [](const game&, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { + const auto value_ = convert({s, value}); + for (const auto& element : elements) { - if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value) + if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { return sol::lua_value{s, element}; } @@ -728,13 +720,14 @@ namespace ui_scripting::lua game_type["getelements"] = sol::overload ( - [](const game&, const sol::this_state s, const value& value, const std::string& attribute) + [](const game&, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { + const auto value_ = convert({s, value}); auto result = sol::table::create(s.lua_state()); for (const auto& element : elements) { - if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value) + if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { result.add(element); } @@ -974,29 +967,128 @@ namespace ui_scripting::lua { return [name](const game&, const sol::this_state s, sol::variadic_args va) { - arguments arguments; + arguments arguments{}; for (auto arg : va) { - arguments.push_back(arg); + arguments.push_back(convert({s, arg})); } const auto values = call(name, arguments); - return sol::as_returns(values); + std::vector returns; + + for (const auto& value : values) + { + returns.push_back(convert(s, value)); + } + + return sol::as_returns(returns); }; }; game_type["call"] = [](const game&, const sol::this_state s, const std::string& name, sol::variadic_args va) { - arguments arguments; + arguments arguments{}; for (auto arg : va) { - arguments.push_back(arg); + arguments.push_back(convert({s, arg})); } const auto values = call(name, arguments); - return sol::as_returns(values); + std::vector returns; + + for (const auto& value : values) + { + returns.push_back(convert(s, value)); + } + + return sol::as_returns(returns); + }; + + auto userdata_type = state.new_usertype("userdata_"); + + for (const auto method : methods) + { + const auto name = method.first; + + userdata_type[name] = [name](const userdata& userdata, const sol::this_state s, sol::variadic_args va) + { + arguments arguments{}; + + for (auto arg : va) + { + arguments.push_back(convert({s, arg})); + } + + const auto values = call_method(userdata, name, arguments); + std::vector returns; + + for (const auto& value : values) + { + returns.push_back(convert(s, value)); + } + + return sol::as_returns(returns); + }; + } + + userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s, + const std::string& name) + { + return convert(s, userdata.get(name)); + }; + + userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s, + const std::string& name, const sol::lua_value& value) + { + userdata.set(name, convert({s, value})); + }; + + auto table_type = state.new_usertype("table_"); + + table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s, + const std::string& name) + { + return convert(s, table.get(name)); + }; + + table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s, + const std::string& name, const sol::lua_value& value) + { + table.set(name, convert({s, value})); + }; + + table_type[sol::meta_function::length] = [](const table& table) + { + return table.size(); + }; + + table_type["getkeys"] = [](const table& table, const sol::this_state s) + { + std::vector result; + const auto keys = table.get_keys(); + + for (const auto& key : keys) + { + result.push_back(convert(s, key)); + } + + return result; + }; + + auto function_type = state.new_usertype("function"); + + function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va) + { + arguments arguments{}; + + for (auto arg : va) + { + arguments.push_back(convert({s, arg})); + } + + function.call(arguments); }; struct player @@ -1018,7 +1110,7 @@ namespace ui_scripting::lua for (auto arg : args) { - arguments.push_back(convert({s, arg})); + arguments.push_back(script_convert({s, arg})); } const auto player_value = scripting::call("getentbynum", {0}); diff --git a/src/client/game/ui_scripting/lua/event_handler.cpp b/src/client/game/ui_scripting/lua/event_handler.cpp index e7603c0a..b215fef1 100644 --- a/src/client/game/ui_scripting/lua/event_handler.cpp +++ b/src/client/game/ui_scripting/lua/event_handler.cpp @@ -3,6 +3,7 @@ #include "error.hpp" #include "event_handler.hpp" +#include "value_conversion.hpp" namespace ui_scripting::lua { @@ -167,14 +168,7 @@ namespace ui_scripting::lua for (const auto& argument : event.arguments) { - if (argument.index() > 0) - { - arguments.emplace_back(argument); - } - else - { - arguments.emplace_back(sol::lua_value{this->state_, sol::lua_nil}); - } + arguments.emplace_back(convert(this->state_, argument)); } return arguments; diff --git a/src/client/game/ui_scripting/lua/value_conversion.cpp b/src/client/game/ui_scripting/lua/value_conversion.cpp new file mode 100644 index 00000000..51bbdbef --- /dev/null +++ b/src/client/game/ui_scripting/lua/value_conversion.cpp @@ -0,0 +1,121 @@ +#include +#include "value_conversion.hpp" +#include "../execution.hpp" + +namespace ui_scripting::lua +{ + namespace + { + table convert_table(const sol::table& t) + { + table res{}; + + t.for_each([res](const sol::object& key, const sol::object& value) + { + res.set(convert(key), convert(value)); + }); + + return res; + } + } + + value convert(const sol::lua_value& value) + { + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is
()) + { + return {value.as
()}; + } + + if (value.is()) + { + return {value.as()}; + } + + if (value.is()) + { + return {convert_table(value.as())}; + } + + return {}; + } + + sol::lua_value convert(lua_State* state, const value& value) + { + if (value.is()) + { + return {state, value.as()}; + } + + if (value.is()) + { + return {state, value.as()}; + } + + if (value.is()) + { + return {state, value.as()}; + } + + if (value.is()) + { + return {state, value.as()}; + } + + if (value.is()) + { + return {state, value.as()}; + } + + if (value.is
()) + { + return {state, value.as
()}; + } + + if (value.is()) + { + return {state, value.as()}; + } + + return {state, sol::lua_nil}; + } +} diff --git a/src/client/game/ui_scripting/lua/value_conversion.hpp b/src/client/game/ui_scripting/lua/value_conversion.hpp new file mode 100644 index 00000000..83328935 --- /dev/null +++ b/src/client/game/ui_scripting/lua/value_conversion.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "context.hpp" + +namespace ui_scripting::lua +{ + value convert(const sol::lua_value& value); + sol::lua_value convert(lua_State* state, const value& value); +} diff --git a/src/client/game/ui_scripting/stack_isolation.cpp b/src/client/game/ui_scripting/stack_isolation.cpp index dc51d08a..473d8098 100644 --- a/src/client/game/ui_scripting/stack_isolation.cpp +++ b/src/client/game/ui_scripting/stack_isolation.cpp @@ -7,24 +7,24 @@ namespace ui_scripting { const auto state = *game::hks::lua_state; - this->top_ = state->top; - this->base_ = state->base; - this->alloc_top_ = state->alloc_top; - this->bottom_ = state->bottom; + this->top_ = state->m_apistack.top; + this->base_ = state->m_apistack.base; + this->alloc_top_ = state->m_apistack.alloc_top; + this->bottom_ = state->m_apistack.bottom; - state->top = this->stack_; - state->base = state->top; - state->alloc_top = &this->stack_[ARRAYSIZE(this->stack_) - 1]; - state->bottom = state->top; + state->m_apistack.top = this->stack_; + state->m_apistack.base = state->m_apistack.top; + state->m_apistack.alloc_top = &this->stack_[ARRAYSIZE(this->stack_) - 1]; + state->m_apistack.bottom = state->m_apistack.top; } stack_isolation::~stack_isolation() { const auto state = *game::hks::lua_state; - state->top = this->top_; - state->base = this->base_; - state->alloc_top = this->alloc_top_; - state->bottom = this->bottom_; + state->m_apistack.top = this->top_; + state->m_apistack.base = this->base_; + state->m_apistack.alloc_top = this->alloc_top_; + state->m_apistack.bottom = this->bottom_; } } diff --git a/src/client/game/ui_scripting/types.cpp b/src/client/game/ui_scripting/types.cpp new file mode 100644 index 00000000..ac2051ad --- /dev/null +++ b/src/client/game/ui_scripting/types.cpp @@ -0,0 +1,98 @@ +#include +#include "types.hpp" +#include "execution.hpp" + +namespace ui_scripting +{ + /*************************************************************** + * Lightuserdata + **************************************************************/ + + lightuserdata::lightuserdata(void* ptr_) + : ptr(ptr_) + { + } + + /*************************************************************** + * Userdata + **************************************************************/ + + userdata::userdata(void* ptr_) + : ptr(ptr_) + { + } + + void userdata::set(const value& key, const value& value) const + { + set_field(*this, key, value); + } + + value userdata::get(const value& key) const + { + return get_field(*this, key); + } + + arguments userdata::call(const std::string& name, const arguments& arguments) const + { + return call_method(this->ptr, name, arguments); + } + + /*************************************************************** + * Table + **************************************************************/ + + table::table() + { + const auto state = *game::hks::lua_state; + this->ptr = game::hks::Hashtable_Create(state, 0, 0); + } + + table::table(game::hks::HashTable* ptr_) + : ptr(ptr_) + { + } + + arguments table::get_keys() const + { + arguments keys{}; + auto current = this->ptr->m_hashPart; + + while (current->m_key.t) + { + keys.push_back(current->m_key); + current++; + } + + return keys; + } + + unsigned int table::size() const + { + return static_cast(this->get_keys().size()); + } + + void table::set(const value& key, const value& value) const + { + set_field(*this, key, value); + } + + value table::get(const value& key) const + { + return get_field(*this, key); + } + + /*************************************************************** + * Function + **************************************************************/ + + function::function(void* ptr_) + : ptr(ptr_) + { + } + + arguments function::call(const arguments& arguments) const + { + call_script_function(*this, arguments); + return {}; + } +} diff --git a/src/client/game/ui_scripting/types.hpp b/src/client/game/ui_scripting/types.hpp new file mode 100644 index 00000000..a63ded36 --- /dev/null +++ b/src/client/game/ui_scripting/types.hpp @@ -0,0 +1,50 @@ +#pragma once +#include "game/game.hpp" +#include "value.hpp" + +namespace ui_scripting +{ + class lightuserdata + { + public: + lightuserdata(void*); + void* ptr; + }; + + class userdata + { + public: + userdata(void*); + + value get(const value& key) const; + void set(const value& key, const value& value) const; + arguments call(const std::string& name, const arguments& arguments) const; + + void* ptr; + }; + + class table + { + public: + table(); + table(game::hks::HashTable* ptr_); + + arguments get_keys() const; + unsigned int size() const; + + value get(const value& key) const; + void set(const value& key, const value& value) const; + + game::hks::HashTable* ptr; + }; + + class function + { + public: + function(void*); + + arguments call(const arguments& arguments) const; + + void* ptr; + }; +} diff --git a/src/client/game/ui_scripting/value.cpp b/src/client/game/ui_scripting/value.cpp new file mode 100644 index 00000000..f34ccba7 --- /dev/null +++ b/src/client/game/ui_scripting/value.cpp @@ -0,0 +1,280 @@ +#include +#include "execution.hpp" +#include "types.hpp" +#include "stack_isolation.hpp" +#include "value.hpp" + +namespace ui_scripting +{ + /*************************************************************** + * Constructors + **************************************************************/ + + value::value(const game::hks::HksObject& value) + : value_(value) + { + } + + value::value(const int value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TNUMBER; + obj.v.number = static_cast(value); + + this->value_ = obj; + } + + value::value(const unsigned int value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TNUMBER; + obj.v.number = static_cast(value); + + this->value_ = obj; + } + + value::value(const bool value) + : value(static_cast(value)) + { + } + + value::value(const float value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TNUMBER; + obj.v.number = static_cast(value); + + this->value_ = obj; + } + + value::value(const double value) + : value(static_cast(value)) + { + } + + value::value(const char* value) + { + game::hks::HksObject obj{}; + stack_isolation _; + + const auto state = *game::hks::lua_state; + game::hks::hksi_lua_pushlstring(state, value, (unsigned int)strlen(value)); + obj = state->m_apistack.top[-1]; + + this->value_ = obj; + } + + value::value(const std::string& value) + : value(value.data()) + { + } + + value::value(const lightuserdata& value) + { + this->value_.t = game::hks::TLIGHTUSERDATA; + this->value_.v.ptr = value.ptr; + } + + value::value(const userdata& value) + { + this->value_.t = game::hks::TUSERDATA; + this->value_.v.ptr = value.ptr; + } + + value::value(const table& value) + { + this->value_.t = game::hks::TTABLE; + this->value_.v.ptr = value.ptr; + } + + value::value(const function& value) + { + this->value_.t = game::hks::TIFUNCTION; + this->value_.v.ptr = value.ptr; + } + + /*************************************************************** + * Integer + **************************************************************/ + + template <> + bool value::is() const + { + return this->get_raw().t == game::hks::TNUMBER; + } + + template <> + bool value::is() const + { + return this->is(); + } + + template <> + bool value::is() const + { + return this->is(); + } + + template <> + int value::get() const + { + return static_cast(this->get_raw().v.number); + } + + template <> + unsigned int value::get() const + { + return static_cast(this->get_raw().v.number); + } + + template <> + bool value::get() const + { + return this->get_raw().v.native != 0; + } + + /*************************************************************** + * Float + **************************************************************/ + + template <> + bool value::is() const + { + return this->get_raw().t == game::hks::TNUMBER; + } + + template <> + bool value::is() const + { + return this->is(); + } + + template <> + float value::get() const + { + return this->get_raw().v.number; + } + + template <> + double value::get() const + { + return static_cast(this->get_raw().v.number); + } + + /*************************************************************** + * String + **************************************************************/ + + template <> + bool value::is() const + { + return this->get_raw().t == game::hks::TSTRING; + } + + template <> + bool value::is() const + { + return this->is(); + } + + template <> + const char* value::get() const + { + return this->get_raw().v.str->m_data; + } + + template <> + std::string value::get() const + { + return this->get(); + } + + bool value::operator==(const value& other) + { + if (this->get_raw().t != other.get_raw().t) + { + return false; + } + + if (this->get_raw().t == game::hks::TSTRING) + { + return this->get() == other.get(); + } + + return this->get_raw().v.native == other.get_raw().v.native; + } + + /*************************************************************** + * Lightuserdata + **************************************************************/ + + template <> + bool value::is() const + { + return this->get_raw().t == game::hks::TLIGHTUSERDATA; + } + + template <> + lightuserdata value::get() const + { + return this->get_raw().v.ptr; + } + + /*************************************************************** + * Userdata + **************************************************************/ + + template <> + bool value::is() const + { + return this->get_raw().t == game::hks::TUSERDATA; + } + + template <> + userdata value::get() const + { + return this->get_raw().v.ptr; + } + + /*************************************************************** + * Table + **************************************************************/ + + template <> + bool value::is
() const + { + return this->get_raw().t == game::hks::TTABLE; + } + + template <> + table value::get() const + { + return this->get_raw().v.table; + } + + /*************************************************************** + * Function + **************************************************************/ + + template <> + bool value::is() const + { + return this->get_raw().t == game::hks::TIFUNCTION + || this->get_raw().t == game::hks::TCFUNCTION; + } + + template <> + function value::get() const + { + return this->get_raw().v.ptr; + } + + /*************************************************************** + * + **************************************************************/ + + const game::hks::HksObject& value::get_raw() const + { + return this->value_; + } +} diff --git a/src/client/game/ui_scripting/value.hpp b/src/client/game/ui_scripting/value.hpp index ee684aaa..e9dce1bc 100644 --- a/src/client/game/ui_scripting/value.hpp +++ b/src/client/game/ui_scripting/value.hpp @@ -3,15 +3,56 @@ namespace ui_scripting { - struct lightuserdata + class lightuserdata; + class userdata; + class table; + class function; + + class value { - void* ptr; - bool operator==(const lightuserdata other) const noexcept + public: + value() = default; + value(const game::hks::HksObject& value); + + value(int value); + value(unsigned int value); + value(bool value); + + value(float value); + value(double value); + + value(const char* value); + value(const std::string& value); + + value(const lightuserdata& value); + value(const userdata& value); + value(const table& value); + value(const function& value); + + bool value::operator==(const value& other); + + template + bool is() const; + + template + T as() const { - return this->ptr == other.ptr; + if (!this->is()) + { + throw std::runtime_error("Invalid type"); + } + + return get(); } + + const game::hks::HksObject& get_raw() const; + + private: + template + T get() const; + + game::hks::HksObject value_{}; }; - using value = std::variant; using arguments = std::vector; }