diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index fd8f5da0..e9cb60b6 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -27,6 +27,7 @@ namespace ui_scripting { utils::hook::detour hksi_open_lib_hook; utils::hook::detour hksi_lual_error_hook; + utils::hook::detour hksi_add_function_hook; utils::hook::detour hks_start_hook; utils::hook::detour hks_shutdown_hook; @@ -83,6 +84,18 @@ namespace ui_scripting ui_scripting::lua::engine::stop(); hks_shutdown_hook.invoke(); } + + void hksi_add_function_stub(game::hks::lua_State* s, game::hks::lua_function f, int a3, const char* name, int a5) + { + if (name != "( lua_CFunction )LUI_CoD_LuaCall_UIExpression"s) + { + std::string name_ = name; + const auto sub = name_.substr(13, name_.size() - 14); + libs["Global"][sub] = f; + } + + hksi_add_function_hook.invoke(s, f, a3, name, a5); + } } void enable_error_hook() @@ -158,6 +171,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_add_function_hook.create(game::base_address + 0x2DB570, hksi_add_function_stub); } }; } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 2f93bd37..688b41a6 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -917,6 +917,23 @@ namespace game namespace hks { + struct GenericChunkHeader + { + unsigned __int64 m_flags; + }; + + struct ChunkHeader : GenericChunkHeader + { + ChunkHeader* m_next; + }; + + struct UserData : ChunkHeader + { + unsigned __int64 m_envAndSizeOffsetHighBits; + unsigned __int64 m_metaAndSizeOffsetLowBits; + char m_data[8]; + }; + struct InternString { unsigned __int64 m_flags; @@ -929,7 +946,7 @@ namespace game { void* cClosure; void* closure; - void* userData; + UserData* userData; void* table; void* tstruct; InternString* str; @@ -940,25 +957,29 @@ namespace game bool boolean; }; - enum HksType + enum HksObjectType { - HKS_LUA_TNONE = 0xFFFFFFFF, - HKS_LUA_TNIL = 0x0, - HKS_LUA_TBOOLEAN = 0x1, - HKS_LUA_TLIGHTUSERDATA = 0x2, - HKS_LUA_TNUMBER = 0x3, - HKS_LUA_TSTRING = 0x4, - HKS_LUA_TTABLE = 0x5, - HKS_LUA_TFUNCTION = 0x6, - HKS_LUA_TUSERDATA = 0x7, - HKS_LUA_TTHREAD = 0x8, - HKS_LUA_TUI64 = 0xB, - HKS_LUA_TSTRUCT = 0xC, + TANY = 0xFFFFFFFE, + TNONE = 0xFFFFFFFF, + TNIL = 0x0, + TBOOLEAN = 0x1, + TLIGHTUSERDATA = 0x2, + TNUMBER = 0x3, + TSTRING = 0x4, + TTABLE = 0x5, + TFUNCTION = 0x6, + TUSERDATA = 0x7, + TTHREAD = 0x8, + TIFUNCTION = 0x9, + TCFUNCTION = 0xA, + TUI64 = 0xB, + TSTRUCT = 0xC, + NUM_TYPE_OBJECTS = 0xE, }; struct HksObject { - HksType t; + HksObjectType t; HksValue v; }; diff --git a/src/client/game/ui_scripting/execution.cpp b/src/client/game/ui_scripting/execution.cpp index 3ef7c8cb..9265605c 100644 --- a/src/client/game/ui_scripting/execution.cpp +++ b/src/client/game/ui_scripting/execution.cpp @@ -29,7 +29,7 @@ namespace ui_scripting case 1: { const auto value = std::get(value_); - state->top->t = game::hks::HksType::HKS_LUA_TBOOLEAN; + state->top->t = game::hks::HksObjectType::TBOOLEAN; state->top->v.boolean = value; state->top++; break; @@ -37,7 +37,7 @@ namespace ui_scripting case 2: { const auto value = std::get(value_); - state->top->t = game::hks::HksType::HKS_LUA_TNUMBER; + state->top->t = game::hks::HksObjectType::TNUMBER; state->top->v.number = static_cast(value); state->top++; break; @@ -45,7 +45,7 @@ namespace ui_scripting case 3: { const auto value = std::get(value_); - state->top->t = game::hks::HksType::HKS_LUA_TNUMBER; + state->top->t = game::hks::HksObjectType::TNUMBER; state->top->v.number = value; state->top++; break; @@ -56,6 +56,15 @@ namespace ui_scripting 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; + } } } } @@ -65,7 +74,7 @@ namespace ui_scripting return static_cast(number) == number; } - value get_return_value() + value get_return_value(int offset) { if (!valid_state()) { @@ -73,16 +82,15 @@ namespace ui_scripting } const auto state = *game::hks::lua_state; - const auto top = &state->top[-1]; + const auto top = &state->top[-1 - offset]; switch (top->t) { - case game::hks::HksType::HKS_LUA_TBOOLEAN: + case game::hks::HksObjectType::TBOOLEAN: { return {top->v.boolean}; } - break; - case game::hks::HksType::HKS_LUA_TNUMBER: + case game::hks::HksObjectType::TNUMBER: { const auto number = top->v.number; if (is_integer(number)) @@ -94,13 +102,15 @@ namespace ui_scripting return {top->v.number}; } } - break; - case game::hks::HksType::HKS_LUA_TSTRING: + case game::hks::HksObjectType::TSTRING: { const auto value = top->v.str->m_data; return {std::string(value)}; } - break; + case game::hks::HksObjectType::TLIGHTUSERDATA: + { + return lightuserdata{top->v.ptr}; + } default: { return {}; @@ -108,13 +118,16 @@ namespace ui_scripting } } - value call(const std::string& name, const arguments& arguments) + std::vector call(const std::string& name, const arguments& arguments) { if (!valid_state()) { throw std::runtime_error("Invalid lua state"); } + const auto state = *game::hks::lua_state; + state->top = state->base; + const auto function = ui_scripting::find_function(name); if (!function) { @@ -135,8 +148,15 @@ namespace ui_scripting try { - function(*game::hks::lua_state); - return get_return_value(); + 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)); + } + + return values; } catch (const std::exception& e) { diff --git a/src/client/game/ui_scripting/execution.hpp b/src/client/game/ui_scripting/execution.hpp index e6ffc910..75463723 100644 --- a/src/client/game/ui_scripting/execution.hpp +++ b/src/client/game/ui_scripting/execution.hpp @@ -3,5 +3,5 @@ namespace ui_scripting { - value call(const std::string& name, const arguments& arguments); + std::vector 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 39ce10b9..20378d25 100644 --- a/src/client/game/ui_scripting/lua/context.cpp +++ b/src/client/game/ui_scripting/lua/context.cpp @@ -986,15 +986,8 @@ namespace ui_scripting::lua arguments.push_back(arg); } - const auto value = call(name, arguments); - if (value.index() == 0) - { - return sol::lua_value{s, sol::lua_nil}; - } - else - { - return sol::lua_value{s, value}; - } + const auto values = call(name, arguments); + return sol::as_returns(values); }; }; @@ -1007,14 +1000,14 @@ namespace ui_scripting::lua arguments.push_back(arg); } - const auto value = call(name, arguments); - if (value.index() == 0) + const auto values = call(name, arguments); + if (values.size() == 0) { return sol::lua_value{s, sol::lua_nil}; } else { - return sol::lua_value{s, value}; + return sol::lua_value{s, sol::as_returns(values)}; } }; diff --git a/src/client/game/ui_scripting/value.hpp b/src/client/game/ui_scripting/value.hpp index 12d88485..f9cc7d67 100644 --- a/src/client/game/ui_scripting/value.hpp +++ b/src/client/game/ui_scripting/value.hpp @@ -1,7 +1,18 @@ #pragma once +#include "game/game.hpp" namespace ui_scripting { - using value = std::variant; + struct lightuserdata + { + void* ptr; + + bool operator==(const lightuserdata other) const noexcept + { + return this->ptr == other.ptr; + } + }; + + using value = std::variant; using arguments = std::vector; }