From 27d081700afb237ee08416faeb9a2521a97f0478 Mon Sep 17 00:00:00 2001 From: Jari van der Kaap Date: Mon, 2 Jan 2023 01:12:14 +0100 Subject: [PATCH 1/4] Add UI Scripting support --- src/client/component/ui_scripting.cpp | 370 ++++++++++ src/client/component/ui_scripting.hpp | 7 + src/client/game/game.cpp | 24 +- src/client/game/game.hpp | 4 +- src/client/game/structs.hpp | 651 +++++++++++++++++- src/client/game/symbols.hpp | 29 +- src/client/game/ui_scripting/execution.cpp | 171 +++++ src/client/game/ui_scripting/execution.hpp | 23 + src/client/game/ui_scripting/script_value.cpp | 400 +++++++++++ src/client/game/ui_scripting/script_value.hpp | 251 +++++++ src/client/game/ui_scripting/types.cpp | 351 ++++++++++ src/client/game/ui_scripting/types.hpp | 142 ++++ 12 files changed, 2403 insertions(+), 20 deletions(-) create mode 100644 src/client/component/ui_scripting.cpp create mode 100644 src/client/component/ui_scripting.hpp create mode 100644 src/client/game/ui_scripting/execution.cpp create mode 100644 src/client/game/ui_scripting/execution.hpp create mode 100644 src/client/game/ui_scripting/script_value.cpp create mode 100644 src/client/game/ui_scripting/script_value.hpp create mode 100644 src/client/game/ui_scripting/types.cpp create mode 100644 src/client/game/ui_scripting/types.hpp diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp new file mode 100644 index 00000000..17986c09 --- /dev/null +++ b/src/client/component/ui_scripting.cpp @@ -0,0 +1,370 @@ +#include +#include "loader/component_loader.hpp" +#include "game/game.hpp" + +#include "game/ui_scripting/execution.hpp" + +#include "ui_scripting.hpp" +#include "scheduler.hpp" + +#include +#include +#include + +namespace ui_scripting +{ + namespace + { + std::unordered_map> converted_functions; + + utils::hook::detour ui_cod_init_hook; + utils::hook::detour ui_shutdown_hook; + utils::hook::detour hks_package_require_hook; + utils::hook::detour lua_cod_getrawfile_hook; + + struct globals_t + { + std::string in_require_script; + std::unordered_map loaded_scripts; + std::unordered_map local_scripts; + bool load_raw_script{}; + std::string raw_script_name{}; + }; + + globals_t globals; + + bool is_loaded_script(const std::string& name) + { + return globals.loaded_scripts.contains(name); + } + + bool is_local_script(const std::string& name) + { + return globals.local_scripts.contains(name); + } + + std::string get_root_script(const std::string& name) + { + const auto itr = globals.loaded_scripts.find(name); + return (itr == globals.loaded_scripts.end()) ? std::string() : itr->second; + } + + table get_globals() + { + const auto state = *game::hks::lua_state; + return state->globals.v.table; + } + + void print_error(const std::string& error) + { + printf("************** LUI script execution error **************\n"); + printf("%s\n", error.data()); + printf("********************************************************\n"); + } + + void print_loading_script(const std::string& name) + { + printf("Loading LUI script '%s'\n", name.data()); + } + + std::string get_current_script(game::hks::lua_State* state) + { + game::hks::lua_Debug info{}; + game::hks::hksi_lua_getstack(state, 1, &info); + game::hks::hksi_lua_getinfo(state, "nSl", &info); + return info.short_src; + } + + int load_buffer(const std::string& name, const std::string& data) + { + const auto state = *game::hks::lua_state; + const auto sharing_mode = state->m_global->m_bytecodeSharingMode; + state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON; + + const auto _0 = utils::finally([&] + { + state->m_global->m_bytecodeSharingMode = sharing_mode; + }); + + game::hks::HksCompilerSettings compiler_settings{}; + return game::hks::hksi_hksL_loadbuffer(state, &compiler_settings, data.data(), data.size(), name.data()); + } + + void load_script(const std::string& name, const std::string& data) + { + globals.loaded_scripts[name] = name; + + const auto state = *game::hks::lua_state; + const auto lua = get_globals(); + state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON; + const auto load_results = lua["loadstring"](data, name); + state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_SECURE; + + if (load_results[0].is()) + { + const auto results = lua["pcall"](load_results); + if (!results[0].as()) + { + print_error(results[1].as()); + } + } + else if (load_results[1].is()) + { + print_error(load_results[1].as()); + } + } + + void load_local_script_files(const std::string& script_dir) + { + if (!utils::io::directory_exists(script_dir)) + { + return; + } + + const auto scripts = utils::io::list_files(script_dir); + + for (const auto& script : scripts) + { + if (std::filesystem::is_regular_file(script)) + { + const std::string file_path = script.substr(script.find("ui_scripts") + 11); + globals.local_scripts[file_path.c_str()] = script; + } + else if (std::filesystem::is_directory(script)) + { + load_local_script_files(script); + } + } + } + + void load_scripts(const std::string& script_dir) + { + if (!utils::io::directory_exists(script_dir)) + { + return; + } + + load_local_script_files(script_dir); + + const auto scripts = utils::io::list_files(script_dir); + + for (const auto& script : scripts) + { + std::string data; + + if (std::filesystem::is_directory(script) && utils::io::read_file(script + "/__init__.lua", &data)) + { + print_loading_script(script); + load_script(script + "/__init__.lua", data); + } + } + } + + void setup_functions() + { + const auto lua = get_globals(); + + using game = table; + auto game_type = game(); + lua["game"] = game_type; + } + + void enable_globals() + { + const auto lua = get_globals(); + const std::string code = + "local g = getmetatable(_G)\n" + "if not g then\n" + "g = {}\n" + "setmetatable(_G, g)\n" + "end\n" + "g.__newindex = nil\n"; + + const auto state = *game::hks::lua_state; + state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON; + lua["loadstring"](code)[0](); + state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_SECURE; + } + + void start() + { + globals = {}; + + const auto lua = get_globals(); + enable_globals(); + + setup_functions(); + + lua["print"] = function(reinterpret_cast(0x141D30290_g)); // hks::base_print + lua["table"]["unpack"] = lua["unpack"]; + lua["luiglobals"] = lua; + + load_scripts(game::get_host_library().get_folder().append("/data/ui_scripts/").string()); + load_scripts("boiii/ui_scripts/"); + load_scripts("data/ui_scripts/"); + } + + void try_start() + { + try + { + start(); + } + catch (const std::exception& ex) + { + printf("Failed to load LUI scripts: %s\n", ex.what()); + } + } + + void ui_cod_init_stub(void* a1, void* a2) + { + ui_cod_init_hook.invoke(a1, a2); + const auto _0 = utils::finally(&try_start); + } + + void ui_shutdown_stub() + { + converted_functions.clear(); + globals = {}; + return ui_shutdown_hook.invoke(); + } + + void* hks_package_require_stub(game::hks::lua_State* state) + { + const auto script = get_current_script(state); + const auto root = get_root_script(script); + globals.in_require_script = root; + return hks_package_require_hook.invoke(state); + } + + int hks_load_stub(game::hks::lua_State* state, void* compiler_options, void* reader, void* reader_data, void* debug_reader, void* debug_reader_data, const char* chunk_name) + { + if (globals.load_raw_script) + { + globals.load_raw_script = false; + globals.loaded_scripts[globals.raw_script_name] = globals.in_require_script; + return load_buffer(globals.raw_script_name, utils::io::read_file(globals.raw_script_name)); + } + + return utils::hook::invoke(0x141D3AFB0_g, state, compiler_options, reader, reader_data, debug_reader, debug_reader_data, chunk_name); + } + + game::XAssetHeader lua_cod_getrawfile_stub(char* filename) + { + game::XAssetHeader header{ .luaFile = nullptr }; + + if (!is_local_script(filename)) + { + return lua_cod_getrawfile_hook.invoke(filename); + } + + std::string target_script = globals.local_scripts[filename]; + + if (utils::io::file_exists(target_script)) + { + globals.load_raw_script = true; + globals.raw_script_name = target_script; + header.luaFile = reinterpret_cast(1); + + return header; + } + + return lua_cod_getrawfile_hook.invoke(filename); + } + + int luaopen_stub([[maybe_unused]] game::hks::lua_State* l) + { + return 0; + } + + int hks_base_stub([[maybe_unused]] game::hks::lua_State* l) + { + return 0; + } + }; + + int main_handler(game::hks::lua_State* state) + { + const auto value = state->m_apistack.base[-1]; + if (value.t != game::hks::TCFUNCTION) + { + return 0; + } + + const auto closure = value.v.cClosure; + if (!converted_functions.contains(closure)) + { + return 0; + } + + const auto& function = converted_functions[closure]; + + try + { + const auto args = get_return_values(); + const auto results = function(args); + + for (const auto& result : results) + { + push_value(result); + } + + return static_cast(results.size()); + } + catch (const std::exception& ex) + { + game::hks::hksi_luaL_error(state, ex.what()); + } + + return 0; + } + + template + game::hks::cclosure* convert_function(F f) + { + const auto state = *game::hks::lua_state; + const auto closure = game::hks::cclosure_Create(state, main_handler, 0, 0, 0); + converted_functions[closure] = wrap_function(f); + return closure; + } + + class component final : public client_component + { + public: + void post_unpack() override + { + // Do not allow the HKS vm to open LUA's libraries + utils::hook::jump(0x141D33510_g, luaopen_stub); // io + utils::hook::jump(0x141D33D20_g, luaopen_stub); // os + utils::hook::jump(0x141D34B40_g, luaopen_stub); // serialize + utils::hook::jump(0x141D34B10_g, luaopen_stub); // havokscript + utils::hook::jump(0x141D34190_g, luaopen_stub); // debug + + // Disable unsafe functions + utils::hook::jump(0x141D300B0_g, hks_base_stub); // base_loadfile + utils::hook::jump(0x141D31EE0_g, hks_base_stub); // base_load + + utils::hook::jump(0x141D2CF00_g, hks_base_stub); // string_dump + utils::hook::jump(0x141D2AF90_g, hks_base_stub); // os_execute + + utils::hook::call(0x141D4979A_g, hks_load_stub); + + hks_package_require_hook.create(0x141D28EF0_g, hks_package_require_stub); + ui_cod_init_hook.create(0x141F298B0_g, ui_cod_init_stub); + ui_shutdown_hook.create(0x14270E9C0_g, ui_shutdown_stub); + lua_cod_getrawfile_hook.create(0x141F0F880_g, lua_cod_getrawfile_stub); + + scheduler::once([]() { + game::dvar_t* dvar_callstack_ship = game::Dvar_FindVar("ui_error_callstack_ship"); + dvar_callstack_ship->flags = (game::dvarFlags_e)0; + game::dvar_t* dvar_report_delay= game::Dvar_FindVar("ui_error_report_delay"); + dvar_report_delay->flags = (game::dvarFlags_e)0; + + game::Dvar_SetFromStringByName("ui_error_callstack_ship", "1", true); + game::Dvar_SetFromStringByName("ui_error_report_delay", "0", true); + }, scheduler::pipeline::main); + } + }; +} + +REGISTER_COMPONENT(ui_scripting::component) diff --git a/src/client/component/ui_scripting.hpp b/src/client/component/ui_scripting.hpp new file mode 100644 index 00000000..4e6754a8 --- /dev/null +++ b/src/client/component/ui_scripting.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace ui_scripting +{ + template + game::hks::cclosure* convert_function(F f); +} diff --git a/src/client/game/game.cpp b/src/client/game/game.cpp index 052af5e0..20c6f0e1 100644 --- a/src/client/game/game.cpp +++ b/src/client/game/game.cpp @@ -1,27 +1,23 @@ #include #include "game.hpp" -#include namespace game -{ - namespace +{ + const utils::nt::library& get_host_library() { - const utils::nt::library& get_host_library() + static auto host_library = [] { - static auto host_library = [] + utils::nt::library host{}; + if (!host || host == utils::nt::library::get_by_address(get_base)) { - utils::nt::library host{}; - if (!host || host == utils::nt::library::get_by_address(get_base)) - { - throw std::runtime_error("Invalid host application"); - } + throw std::runtime_error("Invalid host application"); + } - return host; - }(); + return host; + }(); - return host_library; - } + return host_library; } size_t get_base() diff --git a/src/client/game/game.hpp b/src/client/game/game.hpp index 83cd194c..7476b788 100644 --- a/src/client/game/game.hpp +++ b/src/client/game/game.hpp @@ -1,9 +1,11 @@ #pragma once #include "structs.hpp" +#include namespace game -{ +{ + const utils::nt::library& get_host_library(); size_t get_base(); bool is_server(); diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index c787841d..6edf4c9c 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -412,6 +412,132 @@ namespace game ERROR_LUA = 0x200, ERROR_SOFTRESTART = 0x400, ERROR_SOFTRESTART_KEEPDW = 0x800, + }; + + enum XAssetType + { + ASSET_TYPE_PHYSPRESET = 0x0, + ASSET_TYPE_PHYSCONSTRAINTS = 0x1, + ASSET_TYPE_DESTRUCTIBLEDEF = 0x2, + ASSET_TYPE_XANIMPARTS = 0x3, + ASSET_TYPE_XMODEL = 0x4, + ASSET_TYPE_XMODELMESH = 0x5, + ASSET_TYPE_MATERIAL = 0x6, + ASSET_TYPE_COMPUTE_SHADER_SET = 0x7, + ASSET_TYPE_TECHNIQUE_SET = 0x8, + ASSET_TYPE_IMAGE = 0x9, + ASSET_TYPE_SOUND = 0xA, + ASSET_TYPE_SOUND_PATCH = 0xB, + ASSET_TYPE_CLIPMAP = 0xC, + ASSET_TYPE_COMWORLD = 0xD, + ASSET_TYPE_GAMEWORLD = 0xE, + ASSET_TYPE_MAP_ENTS = 0xF, + ASSET_TYPE_GFXWORLD = 0x10, + ASSET_TYPE_LIGHT_DEF = 0x11, + ASSET_TYPE_LENSFLARE_DEF = 0x12, + ASSET_TYPE_UI_MAP = 0x13, + ASSET_TYPE_FONT = 0x14, + ASSET_TYPE_FONTICON = 0x15, + ASSET_TYPE_LOCALIZE_ENTRY = 0x16, + ASSET_TYPE_WEAPON = 0x17, + ASSET_TYPE_WEAPONDEF = 0x18, + ASSET_TYPE_WEAPON_VARIANT = 0x19, + ASSET_TYPE_WEAPON_FULL = 0x1A, + ASSET_TYPE_CGMEDIA = 0x1B, + ASSET_TYPE_PLAYERSOUNDS = 0x1C, + ASSET_TYPE_PLAYERFX = 0x1D, + ASSET_TYPE_SHAREDWEAPONSOUNDS = 0x1E, + ASSET_TYPE_ATTACHMENT = 0x1F, + ASSET_TYPE_ATTACHMENT_UNIQUE = 0x20, + ASSET_TYPE_WEAPON_CAMO = 0x21, + ASSET_TYPE_CUSTOMIZATION_TABLE = 0x22, + ASSET_TYPE_CUSTOMIZATION_TABLE_FE_IMAGES = 0x23, + ASSET_TYPE_CUSTOMIZATION_TABLE_COLOR = 0x24, + ASSET_TYPE_SNDDRIVER_GLOBALS = 0x25, + ASSET_TYPE_FX = 0x26, + ASSET_TYPE_TAGFX = 0x27, + ASSET_TYPE_NEW_LENSFLARE_DEF = 0x28, + ASSET_TYPE_IMPACT_FX = 0x29, + ASSET_TYPE_IMPACT_SOUND = 0x2A, + ASSET_TYPE_PLAYER_CHARACTER = 0x2B, + ASSET_TYPE_AITYPE = 0x2C, + ASSET_TYPE_CHARACTER = 0x2D, + ASSET_TYPE_XMODELALIAS = 0x2E, + ASSET_TYPE_RAWFILE = 0x2F, + ASSET_TYPE_STRINGTABLE = 0x30, + ASSET_TYPE_STRUCTURED_TABLE = 0x31, + ASSET_TYPE_LEADERBOARD = 0x32, + ASSET_TYPE_DDL = 0x33, + ASSET_TYPE_GLASSES = 0x34, + ASSET_TYPE_TEXTURELIST = 0x35, + ASSET_TYPE_SCRIPTPARSETREE = 0x36, + ASSET_TYPE_KEYVALUEPAIRS = 0x37, + ASSET_TYPE_VEHICLEDEF = 0x38, + ASSET_TYPE_ADDON_MAP_ENTS = 0x39, + ASSET_TYPE_TRACER = 0x3A, + ASSET_TYPE_SLUG = 0x3B, + ASSET_TYPE_SURFACEFX_TABLE = 0x3C, + ASSET_TYPE_SURFACESOUNDDEF = 0x3D, + ASSET_TYPE_FOOTSTEP_TABLE = 0x3E, + ASSET_TYPE_ENTITYFXIMPACTS = 0x3F, + ASSET_TYPE_ENTITYSOUNDIMPACTS = 0x40, + ASSET_TYPE_ZBARRIER = 0x41, + ASSET_TYPE_VEHICLEFXDEF = 0x42, + ASSET_TYPE_VEHICLESOUNDDEF = 0x43, + ASSET_TYPE_TYPEINFO = 0x44, + ASSET_TYPE_SCRIPTBUNDLE = 0x45, + ASSET_TYPE_SCRIPTBUNDLELIST = 0x46, + ASSET_TYPE_RUMBLE = 0x47, + ASSET_TYPE_BULLETPENETRATION = 0x48, + ASSET_TYPE_LOCDMGTABLE = 0x49, + ASSET_TYPE_AIMTABLE = 0x4A, + ASSET_TYPE_ANIMSELECTORTABLESET = 0x4B, + ASSET_TYPE_ANIMMAPPINGTABLE = 0x4C, + ASSET_TYPE_ANIMSTATEMACHINE = 0x4D, + ASSET_TYPE_BEHAVIORTREE = 0x4E, + ASSET_TYPE_BEHAVIORSTATEMACHINE = 0x4F, + ASSET_TYPE_TTF = 0x50, + ASSET_TYPE_SANIM = 0x51, + ASSET_TYPE_LIGHT_DESCRIPTION = 0x52, + ASSET_TYPE_SHELLSHOCK = 0x53, + ASSET_TYPE_XCAM = 0x54, + ASSET_TYPE_BG_CACHE = 0x55, + ASSET_TYPE_TEXTURE_COMBO = 0x56, + ASSET_TYPE_FLAMETABLE = 0x57, + ASSET_TYPE_BITFIELD = 0x58, + ASSET_TYPE_ATTACHMENT_COSMETIC_VARIANT = 0x59, + ASSET_TYPE_MAPTABLE = 0x5A, + ASSET_TYPE_MAPTABLE_LOADING_IMAGES = 0x5B, + ASSET_TYPE_MEDAL = 0x5C, + ASSET_TYPE_MEDALTABLE = 0x5D, + ASSET_TYPE_OBJECTIVE = 0x5E, + ASSET_TYPE_OBJECTIVE_LIST = 0x5F, + ASSET_TYPE_UMBRA_TOME = 0x60, + ASSET_TYPE_NAVMESH = 0x61, + ASSET_TYPE_NAVVOLUME = 0x62, + ASSET_TYPE_BINARYHTML = 0x63, + ASSET_TYPE_LASER = 0x64, + ASSET_TYPE_BEAM = 0x65, + ASSET_TYPE_STREAMER_HINT = 0x66, + ASSET_TYPE_COUNT = 0x67, + ASSET_TYPE_STRING = 0x68, + ASSET_TYPE_ASSETLIST = 0x69, + ASSET_TYPE_REPORT = 0x6A, + ASSET_TYPE_DEPEND = 0x68, + ASSET_TYPE_FULL_COUNT = 0x6C, + }; + + struct LuaFile + { + const char* name; + int len; + const char* buffer; + }; + + union XAssetHeader + { + void* data; + LuaFile* luaFile; }; struct XZoneBuffer @@ -493,6 +619,23 @@ namespace game DVAR_TYPE_COLOR_LAB = 0xE, DVAR_TYPE_SESSIONMODE_BASE_DVAR = 0xF, DVAR_TYPE_COUNT = 0x10, + }; + + enum dvarFlags_e + { + DVAR_ARCHIVE = 1 << 0, + DVAR_USERINFO = 1 << 1, + DVAR_SYSTEMINFO = 1 << 2, + DVAR_CODINFO = 1 << 3, + DVAR_LATCH = 1 << 4, + DVAR_ROM = 1 << 5, + DVAR_SAVED = 1 << 6, + DVAR_INIT = 1 << 7, + DVAR_CHEAT = 1 << 8, + //DVAR_UNKNOWN = 1 << 9, + DVAR_EXTERNAL = 1 << 10, + //DVAR_UNKNOWN3x = 1 << 11-13, + DVAR_SESSIONMODE = 1 << 15 }; typedef float vec_t; @@ -868,7 +1011,513 @@ namespace game Agreement debugAgreement; JoinType joinType; JoinResult joinResult; - }; + }; + + + namespace hks + { + struct lua_State; + struct HashTable; + struct StringTable; + struct cclosure; + typedef int hksBool; + typedef char hksChar; + typedef unsigned __int8 hksByte; + typedef __int16 hksShort16; + typedef unsigned __int16 hksUshort16; + typedef float HksNumber; + typedef int hksInt32; + typedef unsigned int hksUint32; + typedef __int64 hksInt64; + typedef unsigned __int64 hksUint64; + + typedef int HksGcCost; + + + typedef size_t hksSize; + typedef void* (*lua_Alloc)(void*, void*, size_t, size_t); + typedef hksInt32(*lua_CFunction)(lua_State*); + + struct GenericChunkHeader + { + hksSize m_flags; + }; + + struct ChunkHeader : GenericChunkHeader + { + ChunkHeader* m_next; + }; + + struct ChunkList + { + ChunkHeader m_head; + }; + + struct UserData : ChunkHeader + { + unsigned __int64 m_envAndSizeOffsetHighBits; + unsigned __int64 m_metaAndSizeOffsetLowBits; + char m_data[8]; + }; + + struct InternString + { + unsigned __int64 m_flags; + unsigned __int64 m_lengthbits; + unsigned int m_hash; + char m_data[30]; + }; + + union HksValue + { + cclosure* cClosure; + void* closure; + UserData* userData; + HashTable* table; + void* tstruct; + InternString* str; + void* thread; + void* ptr; + float number; + unsigned int native; + bool boolean; + }; + + enum HksObjectType + { + TANY = 0xFFFFFFFE, + TNONE = 0xFFFFFFFF, + TNIL = 0x0, + TBOOLEAN = 0x1, + TLIGHTUSERDATA = 0x2, + TNUMBER = 0x3, + TSTRING = 0x4, + TTABLE = 0x5, + TFUNCTION = 0x6, // idk + TUSERDATA = 0x7, + TTHREAD = 0x8, + TIFUNCTION = 0x9, // Lua function + TCFUNCTION = 0xA, // C function + TUI64 = 0xB, + TSTRUCT = 0xC, + NUM_TYPE_OBJECTS = 0xE, + }; + + struct HksObject + { + HksObjectType t; + HksValue v; + }; + + const struct hksInstruction + { + unsigned int code; + }; + + struct ActivationRecord + { + HksObject* m_base; + const hksInstruction* m_returnAddress; + __int16 m_tailCallDepth; + __int16 m_numVarargs; + int m_numExpectedReturns; + }; + + struct CallStack + { + ActivationRecord* m_records; + ActivationRecord* m_lastrecord; + ActivationRecord* m_current; + const hksInstruction* m_current_lua_pc; + const hksInstruction* m_hook_return_addr; + int m_hook_level; + }; + + struct ApiStack + { + HksObject* top; + HksObject* base; + HksObject* alloc_top; + HksObject* bottom; + }; + + struct UpValue : ChunkHeader + { + HksObject m_storage; + HksObject* loc; + UpValue* m_next; + }; + + struct CallSite + { + _SETJMP_FLOAT128 m_jumpBuffer[16]; + CallSite* m_prev; + }; + + enum Status + { + NEW = 0x1, + RUNNING = 0x2, + YIELDED = 0x3, + DEAD_ERROR = 0x4, + }; + + enum HksError + { + HKS_NO_ERROR = 0, + HKS_ERRSYNTAX = -4, + HKS_ERRFILE = -5, + HKS_ERRRUN = -100, + HKS_ERRMEM = -200, + HKS_ERRERR = -300, + HKS_THROWING_ERROR = -500, + HKS_GC_YIELD = 1, + }; + + struct lua_Debug + { + int event; + const char* name; + const char* namewhat; + const char* what; + const char* source; + int currentline; + int nups; + int nparams; + int ishksfunc; + int linedefined; + int lastlinedefined; + char short_src[512]; + int callstack_level; + int is_tail_call; + }; + + using lua_function = int(__fastcall*)(lua_State*); + + struct luaL_Reg + { + const char* name; + lua_function function; + }; + + struct Node + { + HksObject m_key; + HksObject m_value; + }; + + struct StringPinner + { + struct Node + { + InternString* m_strings[32]; + Node* m_prev; + }; + + lua_State* const m_state; + StringPinner* const m_prev; + InternString** m_nextStringsPlace; + Node m_firstNode; + Node* m_currentNode; + }; + + struct StringTable + { + InternString** m_data; + unsigned int m_count; + unsigned int m_mask; + StringPinner* m_pinnedStrings; + }; + + 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; + }; + + struct cclosure : ChunkHeader + { + lua_function m_function; + HashTable* m_env; + __int16 m_numUpvalues; + __int16 m_flags; + InternString* m_name; + HksObject m_upvalues[1]; + }; + + enum HksCompilerSettings_BytecodeSharingFormat + { + BYTECODE_DEFAULT = 0x0, + BYTECODE_INPLACE = 0x1, + BYTECODE_REFERENCED = 0x2, + }; + + enum HksCompilerSettings_IntLiteralOptions + { + INT_LITERALS_NONE = 0x0, + INT_LITERALS_LUD = 0x1, + INT_LITERALS_32BIT = 0x1, + INT_LITERALS_UI64 = 0x2, + INT_LITERALS_64BIT = 0x2, + INT_LITERALS_ALL = 0x3, + }; + + struct HksCompilerSettings + { + int m_emitStructCode; + const char** m_stripNames; + int m_emitGlobalMemoization; + int _m_isHksGlobalMemoTestingMode; + HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat; + HksCompilerSettings_IntLiteralOptions m_enableIntLiterals; + int(*m_debugMap)(const char*, int); + }; + + enum HksBytecodeSharingMode : __int64 + { + HKS_BYTECODE_SHARING_OFF = 0, + HKS_BYTECODE_SHARING_ON = 1, + HKS_BYTECODE_SHARING_SECURE = 2 + }; + + struct HksGcWeights + { + int m_removeString; + int m_finalizeUserdataNoMM; + int m_finalizeUserdataGcMM; + int m_cleanCoroutine; + int m_removeWeak; + int m_markObject; + int m_traverseString; + int m_traverseUserdata; + int m_traverseCoroutine; + int m_traverseWeakTable; + int m_freeChunk; + int m_sweepTraverse; + }; + + struct GarbageCollector_Stack + { + void* m_storage; + unsigned int m_numEntries; + unsigned int m_numAllocated; + }; + + struct ProtoList + { + void** m_protoList; + unsigned __int16 m_protoSize; + unsigned __int16 m_protoAllocSize; + }; + + struct MemoryManager; + + struct GarbageCollector + { + struct ResumeStack + { + void* m_storage; + hksInt32 m_numEntries; + hksUint32 m_numAllocated; + }; + + struct GreyStack + { + HksObject* m_storage; + hksSize m_numEntries; + hksSize m_numAllocated; + }; + + struct RemarkStack + { + HashTable** m_storage; + hksSize m_numAllocated; + hksSize m_numEntries; + }; + + struct WeakStack_Entry + { + hksInt32 m_weakness; + HashTable* m_table; + }; + + struct WeakStack + { + WeakStack_Entry* m_storage; + hksInt32 m_numEntries; + hksUint32 m_numAllocated; + }; + + HksGcCost m_target; + HksGcCost m_stepsLeft; + HksGcCost m_stepLimit; + HksGcWeights m_costs; + HksGcCost m_unit; + void* m_jumpPoint; + lua_State* m_mainState; + lua_State* m_finalizerState; + MemoryManager* m_memory; + void* m_emergencyGCMemory; + hksInt32 m_phase; + ResumeStack m_resumeStack; + GreyStack m_greyStack; + RemarkStack m_remarkStack; + WeakStack m_weakStack; + hksBool m_finalizing; + HksObject m_safeTableValue; + lua_State* m_startOfStateStackList; + lua_State* m_endOfStateStackList; + lua_State* m_currentState; + HksObject m_safeValue; + void* m_compiler; + void* m_bytecodeReader; + void* m_bytecodeWriter; + hksInt32 m_pauseMultiplier; + HksGcCost m_stepMultiplier; + hksSize m_emergencyMemorySize; + bool m_stopped; + lua_CFunction m_gcPolicy; + hksSize m_pauseTriggerMemoryUsage; + hksInt32 m_stepTriggerCountdown; + hksUint32 m_stringTableIndex; + hksUint32 m_stringTableSize; + UserData* m_lastBlackUD; + UserData* m_activeUD; + }; + + enum MemoryManager_ChunkColor + { + RED = 0x0, + BLACK = 0x1, + }; + + enum Hks_DeleteCheckingMode + { + HKS_DELETE_CHECKING_OFF = 0x0, + HKS_DELETE_CHECKING_ACCURATE = 0x1, + HKS_DELETE_CHECKING_SAFE = 0x2, + }; + + struct MemoryManager + { + enum ChunkColor : __int32 + { + WHITE = 0x0, + BLACK = 0x1, + }; + + lua_Alloc m_allocator; + void* m_allocatorUd; + ChunkColor m_chunkColor; + hksSize m_used; + hksSize m_highwatermark; + ChunkList m_allocationList; + ChunkList m_sweepList; + ChunkHeader* m_lastKeptChunk; + lua_State* m_state; + }; + + struct StaticStringCache + { + HksObject m_objects[41]; + }; + + enum HksBytecodeEndianness + { + HKS_BYTECODE_DEFAULT_ENDIAN = 0x0, + HKS_BYTECODE_BIG_ENDIAN = 0x1, + HKS_BYTECODE_LITTLE_ENDIAN = 0x2, + }; + + struct RuntimeProfileData_Stats + { + unsigned __int64 hksTime; + unsigned __int64 callbackTime; + unsigned __int64 gcTime; + unsigned __int64 cFinalizerTime; + unsigned __int64 compilerTime; + unsigned int hkssTimeSamples; + unsigned int callbackTimeSamples; + unsigned int gcTimeSamples; + unsigned int compilerTimeSamples; + unsigned int num_newuserdata; + unsigned int num_tablerehash; + unsigned int num_pushstring; + unsigned int num_pushcfunction; + unsigned int num_newtables; + }; + + struct RuntimeProfileData + { + __int64 stackDepth; + __int64 callbackDepth; + unsigned __int64 lastTimer; + RuntimeProfileData_Stats frameStats; + unsigned __int64 gcStartTime; + unsigned __int64 finalizerStartTime; + unsigned __int64 compilerStartTime; + unsigned __int64 compilerStartGCTime; + unsigned __int64 compilerStartGCFinalizerTime; + unsigned __int64 compilerCallbackStartTime; + __int64 compilerDepth; + void* outFile; + lua_State* rootState; + }; + + struct HksGlobal + { + MemoryManager m_memory; + GarbageCollector m_collector; + StringTable m_stringTable; + __int64 padding3; + HksBytecodeSharingMode m_bytecodeSharingMode; + int padding; + HksObject m_registry; + ChunkList m_userDataList; + lua_State* m_root; + StaticStringCache m_staticStringCache; + void* m_debugger; + void* m_profiler; + RuntimeProfileData m_runProfilerData; + HksCompilerSettings m_compilerSettings; + int(*m_panicFunction)(lua_State*); + void* m_luaplusObjectList; + int m_heapAssertionFrequency; + int m_heapAssertionCount; + void (*m_logFunction)(lua_State*, const char*, ...); + void (*m_emergencyGCFailFunction)(lua_State*, size_t); + HksBytecodeEndianness m_bytecodeDumpEndianness; + int padding2; + }; + + struct lua_State + { + ChunkHeader baseclass; + HksGlobal* m_global; + CallStack m_callStack; + ApiStack m_apistack; + UpValue* pending; + HksObject globals; + HksObject m_cEnv; + CallSite* m_callsites; + int m_numberOfCCalls; + void* m_context; + InternString* m_name; + lua_State* m_nextState; + lua_State* m_nextStateStack; + Status m_status; + HksError m_error; + }; + } #ifdef __cplusplus } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 27971fbf..b8dbe231 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -34,7 +34,8 @@ namespace game // DB WEAK symbol DB_LoadXAssets{ 0x1414236A0 - }; + }; + WEAK symbol DB_FindXAssetHeader{ 0x141420ED0 }; // Live WEAK symbol Live_GetConnectivityInformation{0x141E0C410}; @@ -53,11 +54,11 @@ namespace game WEAK symbol Sys_GetTLS{0x142184210}; // Dvar - WEAK symbol Dvar_FindVar{0x1422BD730}; + WEAK symbol Dvar_FindVar{0x1422BD730}; WEAK symbol Dvar_GenerateHash{0x14133DBF0}; WEAK symbol Dvar_FindMalleableVar{0x1422BD6A0}; WEAK symbol Dvar_GetDebugName{0x1422BDCB0}; - WEAK symbol Dvar_GetString{0x1422BFFF0}; + WEAK symbol Dvar_GetString{0x1422BFFF0}; WEAK symbol Dvar_SetFromStringByName{ 0x1422C7F60 }; @@ -96,5 +97,25 @@ namespace game constexpr auto CMD_MAX_NESTING = 8; // Re-implementations - eModes Com_SessionMode_GetMode(); + eModes Com_SessionMode_GetMode(); + + namespace hks + { + WEAK symbol lua_state { 0x159C78D88 }; + WEAK symbol hksi_lua_pushlstring{ 0x140A18430 }; + + WEAK symbol hks_obj_settable{ 0x141D4B660 }; + WEAK symbol hks_obj_gettable{ 0x141D4ABF0 }; + WEAK symbol vm_call_internal{ 0x141D71070 }; + WEAK symbol Hashtable_Create{ 0x141D3B5F0 }; + WEAK symbol cclosure_Create{ 0x141D3B7E0 }; + WEAK symbol hksi_luaL_ref{ 0x141D4D1A0 }; + WEAK symbol hksi_luaL_unref{ 0x141D4D320 }; + + WEAK symbol hksi_hksL_loadbuffer{ 0x141D4BD80 }; + WEAK symbol hksi_lua_getinfo{ 0x141D4D960 }; + WEAK symbol hksi_lua_getstack{ 0x141D4DC20 }; + WEAK symbol hksi_luaL_error{ 0x141D4D050 }; + WEAK symbol s_compilerTypeName{ 0x140A18430 }; + } } diff --git a/src/client/game/ui_scripting/execution.cpp b/src/client/game/ui_scripting/execution.cpp new file mode 100644 index 00000000..826fad10 --- /dev/null +++ b/src/client/game/ui_scripting/execution.cpp @@ -0,0 +1,171 @@ +#include +#include "execution.hpp" + +//#include "component/console.hpp" + +namespace ui_scripting +{ + namespace + { + script_value get_field(void* ptr, game::hks::HksObjectType type, const script_value& key) + { + const auto state = *game::hks::lua_state; + const auto top = state->m_apistack.top; + + push_value(key); + + game::hks::HksObject value{}; + game::hks::HksObject obj{}; + obj.t = type; + obj.v.ptr = ptr; + + game::hks::hks_obj_gettable(&value, state, &obj, &state->m_apistack.top[-1]); + state->m_apistack.top = top; + return value; + } + + void set_field(void* ptr, game::hks::HksObjectType type, const script_value& key, const script_value& value) + { + const auto state = *game::hks::lua_state; + + game::hks::HksObject obj{}; + obj.t = type; + obj.v.ptr = ptr; + + game::hks::hks_obj_settable(state, &obj, &key.get_raw(), &value.get_raw()); + } + } + + void push_value(const script_value& value) + { + const auto state = *game::hks::lua_state; + *state->m_apistack.top = value.get_raw(); + state->m_apistack.top++; + } + + void push_value(const game::hks::HksObject& value) + { + const auto state = *game::hks::lua_state; + *state->m_apistack.top = value; + state->m_apistack.top++; + } + + script_value get_return_value(std::int64_t offset) + { + const auto state = *game::hks::lua_state; + return state->m_apistack.top[-1 - offset]; + } + + arguments get_return_values() + { + const auto state = *game::hks::lua_state; + const auto count = state->m_apistack.top - state->m_apistack.base; + arguments values; + + for (auto i = count - 1; i >= 0; i--) + { + values.push_back(get_return_value(i)); + } + + if (values.empty()) + { + values.push_back({}); + } + + return values; + } + + arguments get_return_values(game::hks::HksObject* base) + { + const auto state = *game::hks::lua_state; + const auto count = state->m_apistack.top - base; + arguments values; + + for (auto i = count - 1; i >= 0; i--) + { + values.push_back(get_return_value(i)); + } + + if (values.empty()) + { + values.push_back({}); + } + + return values; + } + + bool notify(const std::string& name, const event_arguments& arguments) + { + const auto state = *game::hks::lua_state; + if (state == nullptr) + { + return false; + } + + //const auto _0 = gsl::finally(game::LUI_LeaveCriticalSection); + //game::LUI_EnterCriticalSection(); + + try + { + const auto globals = table((*::game::hks::lua_state)->globals.v.table); + const auto engine = globals.get("Engine").as(); + const auto root = engine.get("GetLuiRoot")()[0].as(); + const auto process_event = root.get("processEvent"); + + table event{}; + event.set("name", name); + event.set("dispatchChildren", true); + + for (const auto& arg : arguments) + { + event.set(arg.first, arg.second); + } + + process_event(root, event); + return true; + } + catch (const std::exception& ex) + { + printf("Error processing event '%s' %s\n", name.data(), ex.what()); + } + + return false; + } + + arguments call_script_function(const function& function, const arguments& arguments) + { + const auto state = *game::hks::lua_state; + const auto top = state->m_apistack.top; + + push_value(function); + for (auto i = arguments.begin(); i != arguments.end(); ++i) + { + push_value(*i); + } + + game::hks::vm_call_internal(state, static_cast(arguments.size()), -1, nullptr); + const auto args = get_return_values(top); + state->m_apistack.top = top; + return args; + } + + script_value get_field(const userdata& self, const script_value& key) + { + return get_field(self.ptr, game::hks::TUSERDATA, key); + } + + script_value get_field(const table& self, const script_value& key) + { + return get_field(self.ptr, game::hks::TTABLE, key); + } + + void set_field(const userdata& self, const script_value& key, const script_value& value) + { + set_field(self.ptr, game::hks::TUSERDATA, key, value); + } + + void set_field(const table& self, const script_value& key, const script_value& value) + { + set_field(self.ptr, game::hks::TTABLE, key, value); + } +} diff --git a/src/client/game/ui_scripting/execution.hpp b/src/client/game/ui_scripting/execution.hpp new file mode 100644 index 00000000..d2e9cf0a --- /dev/null +++ b/src/client/game/ui_scripting/execution.hpp @@ -0,0 +1,23 @@ +#pragma once +#include "game/game.hpp" +#include "types.hpp" +#include "script_value.hpp" + +namespace ui_scripting +{ + void push_value(const script_value& value); + void push_value(const game::hks::HksObject& value); + + script_value get_return_value(std::int64_t offset); + arguments get_return_values(); + arguments get_return_values(game::hks::HksObject* base); + + bool notify(const std::string& name, const event_arguments& arguments); + + arguments call_script_function(const function& function, const arguments& arguments); + + script_value get_field(const userdata& self, const script_value& key); + script_value get_field(const table& self, const script_value& key); + void set_field(const userdata& self, const script_value& key, const script_value& value); + void set_field(const table& self, const script_value& key, const script_value& value); +} diff --git a/src/client/game/ui_scripting/script_value.cpp b/src/client/game/ui_scripting/script_value.cpp new file mode 100644 index 00000000..454c7dfc --- /dev/null +++ b/src/client/game/ui_scripting/script_value.cpp @@ -0,0 +1,400 @@ +#include +#include "execution.hpp" +#include "types.hpp" +#include "script_value.hpp" + +namespace ui_scripting +{ + hks_object::hks_object(const game::hks::HksObject& value) + { + this->assign(value); + } + + hks_object::hks_object(const hks_object& other) noexcept + { + this->operator=(other); + } + + hks_object::hks_object(hks_object&& other) noexcept + { + this->operator=(std::move(other)); + } + + hks_object& hks_object::operator=(const hks_object& other) noexcept + { + if (this != &other) + { + this->release(); + this->assign(other.value_); + } + + return *this; + } + + hks_object& hks_object::operator=(hks_object&& other) noexcept + { + if (this != &other) + { + this->release(); + this->value_ = other.value_; + other.value_.t = game::hks::TNONE; + } + + return *this; + } + + hks_object::~hks_object() + { + this->release(); + } + + const game::hks::HksObject& hks_object::get() const + { + return this->value_; + } + + void hks_object::assign(const game::hks::HksObject& value) + { + this->value_ = value; + + const auto state = *game::hks::lua_state; + const auto top = state->m_apistack.top; + + push_value(this->value_); + this->ref_ = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000); + state->m_apistack.top = top; + } + + void hks_object::release() + { + if (this->ref_) + { + game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref_); + this->value_.t = game::hks::TNONE; + } + } + + /*************************************************************** + * Constructors + **************************************************************/ + + script_value::script_value(const game::hks::HksObject& value) + : value_(value) + { + } + + script_value::script_value(const int value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TNUMBER; + obj.v.number = static_cast(value); + + this->value_ = obj; + } + + script_value::script_value(const unsigned int value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TNUMBER; + obj.v.number = static_cast(value); + + this->value_ = obj; + } + + script_value::script_value(const bool value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TBOOLEAN; + obj.v.boolean = value; + + this->value_ = obj; + } + + script_value::script_value(const float value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TNUMBER; + obj.v.number = static_cast(value); + + this->value_ = obj; + } + + script_value::script_value(const double value) + : script_value(static_cast(value)) + { + } + + script_value::script_value(const char* value, const std::size_t len) + { + game::hks::HksObject obj{}; + + const auto state = *game::hks::lua_state; + if (state == nullptr) + { + return; + } + + const auto top = state->m_apistack.top; + game::hks::hksi_lua_pushlstring(state, value, static_cast(len)); + obj = state->m_apistack.top[-1]; + state->m_apistack.top = top; + + this->value_ = obj; + } + + script_value::script_value(const char* value) + : script_value(value, std::strlen(value)) + { + } + + script_value::script_value(const std::string& value) + : script_value(value.data(), value.size()) + { + } + + script_value::script_value(const lightuserdata& value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TLIGHTUSERDATA; + obj.v.ptr = value.ptr; + + this->value_ = obj; + } + + script_value::script_value(const userdata& value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TUSERDATA; + obj.v.ptr = value.ptr; + + this->value_ = obj; + } + + script_value::script_value(const table& value) + { + game::hks::HksObject obj{}; + obj.t = game::hks::TTABLE; + obj.v.ptr = value.ptr; + + this->value_ = obj; + } + + script_value::script_value(const function& value) + { + game::hks::HksObject obj{}; + obj.t = value.type; + obj.v.ptr = value.ptr; + + this->value_ = obj; + } + + /*************************************************************** + * Integer + **************************************************************/ + + template <> + bool script_value::is() const + { + const auto number = this->get_raw().v.number; + return this->get_raw().t == game::hks::TNUMBER && static_cast(number) == number; + } + + template <> + bool script_value::is() const + { + return this->is(); + } + + template <> + int script_value::get() const + { + return static_cast(this->get_raw().v.number); + } + + template <> + unsigned int script_value::get() const + { + return static_cast(this->get_raw().v.number); + } + + /*************************************************************** + * Boolean + **************************************************************/ + + template <> + bool script_value::is() const + { + return this->get_raw().t == game::hks::TBOOLEAN; + } + + template <> + bool script_value::get() const + { + return this->get_raw().v.boolean; + } + + /*************************************************************** + * Float + **************************************************************/ + + template <> + bool script_value::is() const + { + return this->get_raw().t == game::hks::TNUMBER; + } + + template <> + bool script_value::is() const + { + return this->is(); + } + + template <> + float script_value::get() const + { + return this->get_raw().v.number; + } + + template <> + double script_value::get() const + { + return this->get_raw().v.number; + } + + /*************************************************************** + * String + **************************************************************/ + + template <> + bool script_value::is() const + { + return this->get_raw().t == game::hks::TSTRING; + } + + template <> + bool script_value::is() const + { + return this->is(); + } + + template <> + const char* script_value::get() const + { + return this->get_raw().v.str->m_data; + } + + template <> + std::string script_value::get() const + { + return this->get(); + } + + /*************************************************************** + * Lightuserdata + **************************************************************/ + + template <> + bool script_value::is() const + { + return this->get_raw().t == game::hks::TLIGHTUSERDATA; + } + + template <> + lightuserdata script_value::get() const + { + return this->get_raw().v.ptr; + } + + /*************************************************************** + * Userdata + **************************************************************/ + + template <> + bool script_value::is() const + { + return this->get_raw().t == game::hks::TUSERDATA; + } + + template <> + userdata script_value::get() const + { + return this->get_raw().v.ptr; + } + + /*************************************************************** + * Table + **************************************************************/ + + template <> + bool script_value::is
() const + { + return this->get_raw().t == game::hks::TTABLE; + } + + template <> + table script_value::get() const + { + return this->get_raw().v.table; + } + + /*************************************************************** + * Function + **************************************************************/ + + template <> + bool script_value::is() const + { + return this->get_raw().t == game::hks::TIFUNCTION + || this->get_raw().t == game::hks::TCFUNCTION; + } + + template <> + function script_value::get() const + { + return { this->get_raw().v.cClosure, this->get_raw().t }; + } + + /*************************************************************** + * + **************************************************************/ + + const game::hks::HksObject& script_value::get_raw() const + { + return this->value_.get(); + } + + bool script_value::operator==(const script_value& other) const + { + 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; + } + + arguments script_value::operator()() const + { + return this->as()(); + } + + arguments script_value::operator()(const arguments& arguments) const + { + return this->as()(arguments); + } + + function_argument::function_argument(const arguments& args, const script_value& value, const int index) + : values_(args), value_(value), index_(index) + { + } + + function_arguments::function_arguments(const arguments& values) + : values_(values) + { + } +} diff --git a/src/client/game/ui_scripting/script_value.hpp b/src/client/game/ui_scripting/script_value.hpp new file mode 100644 index 00000000..479e8a11 --- /dev/null +++ b/src/client/game/ui_scripting/script_value.hpp @@ -0,0 +1,251 @@ +#pragma once +#include "game/game.hpp" + +#include + +namespace ui_scripting +{ + class lightuserdata; + class userdata_value; + class userdata; + class table_value; + class table; + class function; + class script_value; + + template + std::string get_typename() + { + auto& info = typeid(T); + + if (info == typeid(std::string) || + info == typeid(const char*)) + { + return "string"; + } + + if (info == typeid(lightuserdata)) + { + return "lightuserdata"; + } + + if (info == typeid(userdata)) + { + return "userdata"; + } + + if (info == typeid(table)) + { + return "table"; + } + + if (info == typeid(function)) + { + return "function"; + } + + if (info == typeid(int) || + info == typeid(float) || + info == typeid(unsigned int)) + { + return "number"; + } + + if (info == typeid(bool)) + { + return "boolean"; + } + + return info.name(); + } + + class hks_object + { + public: + hks_object() = default; + hks_object(const game::hks::HksObject& value); + hks_object(const hks_object& other) noexcept; + hks_object(hks_object&& other) noexcept; + + hks_object& operator=(const hks_object& other) noexcept; + hks_object& operator=(hks_object&& other) noexcept; + + ~hks_object(); + + const game::hks::HksObject& get() const; + + private: + void assign(const game::hks::HksObject& value); + void release(); + + game::hks::HksObject value_{ game::hks::TNONE, {} }; + int ref_{}; + }; + + using arguments = std::vector; + using event_arguments = std::unordered_map; + + class script_value + { + public: + script_value() = default; + script_value(const game::hks::HksObject& value); + + script_value(int value); + script_value(unsigned int value); + script_value(bool value); + + script_value(float value); + script_value(double value); + + script_value(const char* value, std::size_t len); + script_value(const char* value); + script_value(const std::string& value); + + script_value(const lightuserdata& value); + script_value(const userdata& value); + script_value(const table& value); + script_value(const function& value); + + template class C, class T, typename TableType = table> + script_value(const C>& container) + { + TableType table_{}; + int index = 1; + + for (const auto& value : container) + { + table_.set(index++, value); + } + + game::hks::HksObject obj{}; + obj.t = game::hks::TTABLE; + obj.v.ptr = table_.ptr; + + this->value_ = obj; + } + + template + script_value(F f) + : script_value(function(f)) + { + } + + bool operator==(const script_value& other) const; + + arguments operator()() const; + arguments operator()(const arguments& arguments) const; + + template + arguments operator()(T... arguments) const + { + return this->as().call({ arguments... }); + } + + template + table_value operator[](const char(&key)[Size]) const + { + return { this->as
(), key }; + } + + template + table_value operator[](const T& key) const + { + return { this->as
(), key }; + } + + template + [[nodiscard]] bool is() const; + + template + T as() const + { + if (!this->is()) + { + const auto hks_typename = game::hks::s_compilerTypeName[this->get_raw().t + 2]; + const auto typename_ = get_typename(); + + throw std::runtime_error(utils::string::va("%s expected, got %s", typename_.data(), hks_typename)); + } + + return get(); + } + + template + operator T() const + { + return this->as(); + } + + [[nodiscard]] const game::hks::HksObject& get_raw() const; + + hks_object value_{}; + + private: + template + T get() const; + }; + + class variadic_args : public arguments + { + }; + + class function_argument + { + public: + function_argument(const arguments& args, const script_value& value, const int index); + + template + T as() const + { + try + { + return this->value_.as(); + } + catch (const std::exception& e) + { + throw std::runtime_error(utils::string::va("bad argument #%d (%s)", this->index_ + 1, e.what())); + } + } + + template <> + variadic_args as() const + { + variadic_args args{}; + for (auto i = this->index_; i < this->values_.size(); i++) + { + args.push_back(this->values_[i]); + } + return args; + } + + template + operator T() const + { + return this->as(); + } + + private: + arguments values_{}; + script_value value_{}; + int index_{}; + }; + + class function_arguments + { + public: + function_arguments(const arguments& values); + + function_argument operator[](const int index) const + { + if (static_cast(index) >= values_.size()) + { + return { values_, {}, index }; + } + + return { values_, values_[index], index }; + } + private: + arguments values_{}; + }; +} diff --git a/src/client/game/ui_scripting/types.cpp b/src/client/game/ui_scripting/types.cpp new file mode 100644 index 00000000..d51b7973 --- /dev/null +++ b/src/client/game/ui_scripting/types.cpp @@ -0,0 +1,351 @@ +#include +#include "types.hpp" +#include "execution.hpp" + +namespace ui_scripting +{ + /*************************************************************** + * Lightuserdata + **************************************************************/ + + lightuserdata::lightuserdata(void* ptr_) + : ptr(ptr_) + { + } + + /*************************************************************** + * Userdata + **************************************************************/ + + userdata::userdata(void* ptr_) + : ptr(ptr_) + { + this->add(); + } + + userdata::userdata(const userdata& other) + { + this->operator=(other); + } + + userdata::userdata(userdata&& other) noexcept + { + this->ptr = other.ptr; + this->ref = other.ref; + other.ref = 0; + } + + userdata::~userdata() + { + this->release(); + } + + userdata& userdata::operator=(const userdata& other) + { + if (&other != this) + { + this->release(); + this->ptr = other.ptr; + this->ref = other.ref; + this->add(); + } + + return *this; + } + + userdata& userdata::operator=(userdata&& other) noexcept + { + if (&other != this) + { + this->release(); + this->ptr = other.ptr; + this->ref = other.ref; + other.ref = 0; + } + + return *this; + } + + void userdata::add() + { + game::hks::HksObject value{}; + value.v.ptr = this->ptr; + value.t = game::hks::TUSERDATA; + + const auto state = *game::hks::lua_state; + const auto top = state->m_apistack.top; + + push_value(value); + + this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000); + state->m_apistack.top = top; + } + + void userdata::release() + { + if (this->ref) + { + game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref); + } + } + + void userdata::set(const script_value& key, const script_value& value) const + { + set_field(*this, key, value); + } + + script_value userdata::get(const script_value& key) const + { + return get_field(*this, key); + } + + userdata_value userdata::operator[](const script_value& key) const + { + return { *this, key }; + } + + userdata_value::userdata_value(const userdata& table, const script_value& key) + : userdata_(table), key_(key) + { + this->value_ = this->userdata_.get(key).get_raw(); + } + + void userdata_value::operator=(const script_value& value) + { + this->userdata_.set(this->key_, value); + this->value_ = value.get_raw(); + } + + bool userdata_value::operator==(const script_value& value) + { + return this->userdata_.get(this->key_) == value; + } + + /*************************************************************** + * Table + **************************************************************/ + + table::table() + { + const auto state = *game::hks::lua_state; + this->ptr = game::hks::Hashtable_Create(state, 0, 0); + this->add(); + } + + table::table(game::hks::HashTable* ptr_) + : ptr(ptr_) + { + this->add(); + } + + table::table(const table& other) + { + this->operator=(other); + } + + table::table(table&& other) noexcept + { + this->ptr = other.ptr; + this->ref = other.ref; + other.ref = 0; + } + + table::~table() + { + this->release(); + } + + table& table::operator=(const table& other) + { + if (&other != this) + { + this->release(); + this->ptr = other.ptr; + this->ref = other.ref; + this->add(); + } + + return *this; + } + + table& table::operator=(table&& other) noexcept + { + if (&other != this) + { + this->release(); + this->ptr = other.ptr; + this->ref = other.ref; + other.ref = 0; + } + + return *this; + } + + void table::add() + { + game::hks::HksObject value{}; + value.v.table = this->ptr; + value.t = game::hks::TTABLE; + + const auto state = *game::hks::lua_state; + const auto top = state->m_apistack.top; + + push_value(value); + + this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000); + state->m_apistack.top = top; + } + + void table::release() + { + if (this->ref) + { + game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref); + } + } + + void table::set(const script_value& key, const script_value& value) const + { + set_field(*this, key, value); + } + + table_value table::operator[](const script_value& key) const + { + return { *this, key }; + } + + script_value table::get(const script_value& key) const + { + return get_field(*this, key); + } + + table_value::table_value(const table& table, const script_value& key) + : table_(table), key_(key) + { + this->value_ = this->table_.get(key).get_raw(); + } + + void table_value::operator=(const script_value& value) + { + this->table_.set(this->key_, value); + this->value_ = value.get_raw(); + } + + void table_value::operator=(const table_value& value) + { + this->table_.set(this->key_, value); + this->value_ = value.get_raw(); + } + + bool table_value::operator==(const script_value& value) + { + return this->table_.get(this->key_) == value; + } + + bool table_value::operator==(const table_value& value) + { + return this->table_.get(this->key_) == value; + } + + /*************************************************************** + * Function + **************************************************************/ + + function::function(game::hks::lua_function func) + { + const auto state = *game::hks::lua_state; + this->ptr = game::hks::cclosure_Create(state, func, 0, 0, 0); + this->type = game::hks::HksObjectType::TCFUNCTION; + this->add(); + } + + function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_) + : ptr(ptr_), type(type_) + { + this->add(); + } + + function::function(const function& other) + { + this->operator=(other); + } + + function::function(function&& other) noexcept + { + this->ptr = other.ptr; + this->type = other.type; + this->ref = other.ref; + other.ref = 0; + } + + function::~function() + { + this->release(); + } + + function& function::operator=(const function& other) + { + if (&other != this) + { + this->release(); + this->ptr = other.ptr; + this->type = other.type; + this->ref = other.ref; + this->add(); + } + + return *this; + } + + function& function::operator=(function&& other) noexcept + { + if (&other != this) + { + this->release(); + this->ptr = other.ptr; + this->type = other.type; + this->ref = other.ref; + other.ref = 0; + } + + return *this; + } + + void function::add() + { + game::hks::HksObject value{}; + value.v.cClosure = this->ptr; + value.t = this->type; + + const auto state = *game::hks::lua_state; + const auto top = state->m_apistack.top; + + push_value(value); + + this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000); + state->m_apistack.top = top; + } + + void function::release() + { + if (this->ref) + { + game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref); + } + } + + arguments function::call(const arguments& arguments) const + { + return call_script_function(*this, arguments); + } + + arguments function::operator()(const arguments& arguments) const + { + return this->call(arguments); + } + + arguments function::operator()() const + { + return this->call({}); + } +} diff --git a/src/client/game/ui_scripting/types.hpp b/src/client/game/ui_scripting/types.hpp new file mode 100644 index 00000000..3bf5226f --- /dev/null +++ b/src/client/game/ui_scripting/types.hpp @@ -0,0 +1,142 @@ +#pragma once +#include "game/game.hpp" +#include "script_value.hpp" + +#include "component/ui_scripting.hpp" + +namespace ui_scripting +{ + class lightuserdata + { + public: + lightuserdata(void*); + void* ptr; + }; + + class userdata_value; + + class userdata + { + public: + userdata(void*); + + userdata(const userdata& other); + userdata(userdata&& other) noexcept; + + ~userdata(); + + userdata& operator=(const userdata& other); + userdata& operator=(userdata&& other) noexcept; + + script_value get(const script_value& key) const; + void set(const script_value& key, const script_value& value) const; + + userdata_value operator[](const script_value& key) const; + + void* ptr; + + private: + void add(); + void release(); + + int ref{}; + }; + + class userdata_value : public script_value + { + public: + userdata_value(const userdata& table, const script_value& key); + void operator=(const script_value& value); + bool operator==(const script_value& value); + private: + userdata userdata_; + script_value key_; + }; + + class table_value; + + class table + { + public: + table(); + table(game::hks::HashTable* ptr_); + + table(const table& other); + table(table&& other) noexcept; + + ~table(); + + table& operator=(const table& other); + table& operator=(table&& other) noexcept; + + [[nodiscard]] script_value get(const script_value& key) const; + void set(const script_value& key, const script_value& value) const; + + table_value operator[](const script_value& key) const; + + game::hks::HashTable* ptr; + + private: + void add(); + void release(); + + int ref{}; + }; + + class table_value : public script_value + { + public: + table_value(const table& table, const script_value& key); + void operator=(const script_value& value); + void operator=(const table_value& value); + bool operator==(const script_value& value); + bool operator==(const table_value& value); + private: + table table_; + script_value key_; + }; + + + class function + { + public: + function(game::hks::lua_function); + function(game::hks::cclosure*, game::hks::HksObjectType); + + template + function(F f) + { + this->ptr = ui_scripting::convert_function(f); + this->type = game::hks::TCFUNCTION; + } + + function(const function& other); + function(function&& other) noexcept; + + ~function(); + + function& operator=(const function& other); + function& operator=(function&& other) noexcept; + + arguments call(const arguments& arguments) const; + + arguments operator()(const arguments& arguments) const; + + template + arguments operator()(T... arguments) const + { + return this->call({ arguments... }); + } + + arguments operator()() const; + + game::hks::cclosure* ptr; + game::hks::HksObjectType type; + + private: + void add(); + void release(); + + int ref{}; + }; +} From 1d32386ada3934dd66bb421009b2801b87292f8e Mon Sep 17 00:00:00 2001 From: Jari van der Kaap Date: Mon, 2 Jan 2023 23:18:24 +0100 Subject: [PATCH 2/4] Changed back uiscripting require path for loaded scripts to relative path --- src/client/component/ui_scripting.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 17986c09..63310f4f 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -253,13 +253,23 @@ namespace ui_scripting { game::XAssetHeader header{ .luaFile = nullptr }; - if (!is_local_script(filename)) + if (!is_loaded_script(globals.in_require_script) && !is_local_script(filename)) { return lua_cod_getrawfile_hook.invoke(filename); } - std::string target_script = globals.local_scripts[filename]; - + const std::string name_ = filename; + std::string target_script; + if (is_loaded_script(globals.in_require_script)) + { + const auto folder = globals.in_require_script.substr(0, globals.in_require_script.find_last_of("/\\")); + target_script = folder + "/" + name_ + ".lua"; + } + else + { + target_script = globals.local_scripts[name_]; + } + if (utils::io::file_exists(target_script)) { globals.load_raw_script = true; From b45776d2ad5f8517d40209c32ab903d973ed0ff3 Mon Sep 17 00:00:00 2001 From: Jari van der Kaap Date: Tue, 3 Jan 2023 00:27:40 +0100 Subject: [PATCH 3/4] Changed UI Scripting init on frontend to different hook when everything is initialized --- src/client/component/ui_scripting.cpp | 15 +++++++++++++-- src/client/game/symbols.hpp | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 63310f4f..558e6ef9 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -18,6 +18,7 @@ namespace ui_scripting std::unordered_map> converted_functions; utils::hook::detour ui_cod_init_hook; + utils::hook::detour ui_cod_lobbyui_init_hook; utils::hook::detour ui_shutdown_hook; utils::hook::detour hks_package_require_hook; utils::hook::detour lua_cod_getrawfile_hook; @@ -216,9 +217,18 @@ namespace ui_scripting } } - void ui_cod_init_stub(void* a1, void* a2) + void ui_cod_init_stub(bool frontend) { - ui_cod_init_hook.invoke(a1, a2); + ui_cod_init_hook.invoke(frontend); + + if (game::Com_IsRunningUILevel()) + return; + const auto _0 = utils::finally(&try_start); + } + + void ui_cod_lobbyui_init_stub() + { + ui_cod_lobbyui_init_hook.invoke(); const auto _0 = utils::finally(&try_start); } @@ -361,6 +371,7 @@ namespace ui_scripting hks_package_require_hook.create(0x141D28EF0_g, hks_package_require_stub); ui_cod_init_hook.create(0x141F298B0_g, ui_cod_init_stub); + ui_cod_lobbyui_init_hook.create(0x141F2C620_g, ui_cod_lobbyui_init_stub); ui_shutdown_hook.create(0x14270E9C0_g, ui_shutdown_stub); lua_cod_getrawfile_hook.create(0x141F0F880_g, lua_cod_getrawfile_stub); diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index b8dbe231..44841ac9 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -16,7 +16,8 @@ namespace game // Com WEAK symbol Com_Printf{0x1421499C0}; WEAK symbol Com_Error_{0x1420F8BD0}; - WEAK symbol Com_SessionMode_IsMode{0x1420F7DD0}; + WEAK symbol Com_SessionMode_IsMode{0x1420F7DD0}; + WEAK symbol Com_IsRunningUILevel{0x142148DB0}; WEAK symbol Com_SwitchMode{ 0x14214AF30 }; From b8cf8b2a56134c881273d315ca607cadea868420 Mon Sep 17 00:00:00 2001 From: Jari van der Kaap Date: Thu, 2 Feb 2023 23:28:48 +0100 Subject: [PATCH 4/4] Added config file for saved dvars and added menu for unlocking loot --- data/ui_scripts/luafixes/__init__.lua | 4 + data/ui_scripts/stats/__init__.lua | 286 ++++++++++ src/client/component/dvars.cpp | 101 ++++ src/client/component/loot.cpp | 32 +- src/client/component/ui_scripting.cpp | 7 + src/client/game/structs.hpp | 765 +++++++++++++------------- src/client/game/symbols.hpp | 17 +- 7 files changed, 813 insertions(+), 399 deletions(-) create mode 100644 data/ui_scripts/luafixes/__init__.lua create mode 100644 data/ui_scripts/stats/__init__.lua create mode 100644 src/client/component/dvars.cpp diff --git a/data/ui_scripts/luafixes/__init__.lua b/data/ui_scripts/luafixes/__init__.lua new file mode 100644 index 00000000..e04639b6 --- /dev/null +++ b/data/ui_scripts/luafixes/__init__.lua @@ -0,0 +1,4 @@ + +-- Fix LUI_NULL_FUNCTION messages +function Engine.PIXBeginEvent() end +function Engine.PIXEndEvent() end \ No newline at end of file diff --git a/data/ui_scripts/stats/__init__.lua b/data/ui_scripts/stats/__init__.lua new file mode 100644 index 00000000..a3cb7737 --- /dev/null +++ b/data/ui_scripts/stats/__init__.lua @@ -0,0 +1,286 @@ +DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", function ( controller ) + local optionsTable = {} + + table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock all loot", "Whether loot should be locked based on the player's stats or always unlocked.", "MPStatsSettings_unlock_loot", "cg_unlockall_loot", { + { + option = "MENU_DISABLED", + value = 0, + default = true + }, + { + option = "MENU_ENABLED", + value = 1 + }, + }, nil, function(f1_arg0, f1_arg1, f1_arg2, dvarName, f1_arg4) + local oldValue = Engine.DvarInt( nil, dvarName ) + local newValue = f1_arg1.value + UpdateInfoModels( f1_arg1 ) + if oldValue == newValue then + return + end + Engine.SetDvar( dvarName, f1_arg1.value ) + Engine.SetDvar( "ui_enableAllHeroes", f1_arg1.value ) + end) ) + + return optionsTable +end) + +LUI.createMenu.MPStatsMenu = function ( controller ) + local self = CoD.Menu.NewForUIEditor( "MPStatsMenu" ) + if PreLoadFunc then + PreLoadFunc( self, controller ) + end + self.soundSet = "ChooseDecal" + self:setOwner( controller ) + self:setLeftRight( true, true, 0, 0 ) + self:setTopBottom( true, true, 0, 0 ) + self:playSound( "menu_open", controller ) + self.buttonModel = Engine.CreateModel( Engine.GetModelForController( controller ), "MPStatsMenu.buttonPrompts" ) + self.anyChildUsesUpdateState = true + + local GameSettingsBackground = CoD.GameSettings_Background.new( self, controller ) + GameSettingsBackground:setLeftRight( true, true, 0, 0 ) + GameSettingsBackground:setTopBottom( true, true, 0, 0 ) + GameSettingsBackground.MenuFrame.titleLabel:setText( Engine.Localize( "STATS SETTINGS" ) ) + GameSettingsBackground.MenuFrame.cac3dTitleIntermediary0.FE3dTitleContainer0.MenuTitle.TextBox1.Label0:setText( Engine.Localize( "STATS SETTINGS" ) ) + GameSettingsBackground.GameSettingsSelectedItemInfo.GameModeInfo:setAlpha( 0 ) + GameSettingsBackground.GameSettingsSelectedItemInfo.GameModeName:setAlpha( 0 ) + self:addElement( GameSettingsBackground ) + self.GameSettingsBackground = GameSettingsBackground + + local Options = CoD.Competitive_SettingsList.new( self, controller ) + Options:setLeftRight( true, false, 26, 741 ) + Options:setTopBottom( true, false, 135, 720 ) + Options.Title.DescTitle:setText( Engine.Localize( "Stats" ) ) + Options.ButtonList:setVerticalCount( 15 ) + Options.ButtonList:setDataSource( "MPStatsSettings" ) + self:addElement( Options ) + self.Options = Options + + self:AddButtonCallbackFunction( self, controller, Enum.LUIButton.LUI_KEY_XBB_PSCIRCLE, nil, function ( element, menu, controller, model ) + GoBack( self, controller ) + SetPerControllerTableProperty( controller, "disableGameSettingsOptions", nil ) + return true + end, function ( element, menu, controller ) + CoD.Menu.SetButtonLabel( menu, Enum.LUIButton.LUI_KEY_XBB_PSCIRCLE, "MENU_BACK" ) + return true + end, false ) + + GameSettingsBackground.MenuFrame:setModel( self.buttonModel, controller ) + Options.id = "Options" + + self:processEvent( { + name = "menu_loaded", + controller = controller + } ) + self:processEvent( { + name = "update_state", + menu = self + } ) + if not self:restoreState() then + self.Options:processEvent( { + name = "gain_focus", + controller = controller + } ) + end + + LUI.OverrideFunction_CallOriginalSecond( self, "close", function ( element ) + element.GameSettingsBackground:close() + element.Options:close() + Engine.UnsubscribeAndFreeModel( Engine.GetModel( Engine.GetModelForController( controller ), "MPStatsMenu.buttonPrompts" ) ) + end ) + + if PostLoadFunc then + PostLoadFunc( self, controller ) + end + + return self +end + +CoD.LobbyButtons.MP_STATS = { + stringRef = "STATS", + action = function ( self, element, controller, param, menu ) + SetPerControllerTableProperty( controller, "disableGameSettingsOptions", true ) + OpenPopup( menu, "MPStatsMenu", controller ) + end, + customId = "btnMPStats" +} + + +local IsGamescomDemo = function () + return Dvar.ui_execdemo_gamescom:get() +end + +local IsBetaDemo = function () + return Dvar.ui_execdemo_beta:get() +end + +local SetButtonState = function ( button, state ) + if state == nil then + return + elseif state == CoD.LobbyButtons.DISABLED then + button.disabled = true + elseif state == CoD.LobbyButtons.HIDDEN then + button.hidden = true + end +end + +local AddButton = function ( controller, options, button, isLargeButton ) + button.disabled = false + button.hidden = false + button.selected = false + button.warning = false + if button.defaultState ~= nil then + if button.defaultState == CoD.LobbyButtons.DISABLED then + button.disabled = true + elseif button.defaultState == CoD.LobbyButtons.HIDDEN then + button.hidden = true + end + end + if button.disabledFunc ~= nil then + button.disabled = button.disabledFunc( controller ) + end + if button.visibleFunc ~= nil then + button.hidden = not button.visibleFunc( controller ) + end + if IsBetaDemo() then + SetButtonState( button, button.demo_beta ) + elseif IsGamescomDemo() then + SetButtonState( button, button.demo_gamescom ) + end + if button.hidden then + return + end + local lobbyNav = LobbyData.GetLobbyNav() + if button.selectedFunc ~= nil then + button.selected = button.selectedFunc( button.selectedParam ) + elseif CoD.LobbyMenus.History[lobbyNav] ~= nil then + button.selected = CoD.LobbyMenus.History[lobbyNav] == button.customId + end + if button.newBreadcrumbFunc then + local f8_local1 = button.newBreadcrumbFunc + if type( f8_local1 ) == "string" then + f8_local1 = LUI.getTableFromPath( f8_local1 ) + end + if f8_local1 then + button.isBreadcrumbNew = f8_local1( controller ) + end + end + if button.warningFunc ~= nil then + button.warning = button.warningFunc( controller ) + end + if button.starterPack == CoD.LobbyButtons.STARTERPACK_UPGRADE then + button.starterPackUpgrade = true + if IsStarterPack() then + button.disabled = false + end + end + table.insert( options, { + optionDisplay = button.stringRef, + action = button.action, + param = button.param, + customId = button.customId, + isLargeButton = isLargeButton, + isLastButtonInGroup = false, + disabled = button.disabled, + selected = button.selected, + isBreadcrumbNew = button.isBreadcrumbNew, + warning = button.warning, + requiredChunk = button.selectedParam, + starterPackUpgrade = button.starterPackUpgrade, + unloadMod = button.unloadMod + } ) +end + +local AddLargeButton = function ( controller, options, button ) + AddButton( controller, options, button, true ) +end + +local AddSmallButton = function ( controller, options, button ) + AddButton( controller, options, button, false ) +end + +local AddSpacer = function ( options ) + if 0 < #options then + options[#options].isLastButtonInGroup = true + end +end + +CoD.LobbyMenus.MPButtonsOnline = function ( f26_arg0, f26_arg1, f26_arg2 ) + if f26_arg2 == 1 then + AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_FIND_MATCH ) + AddSpacer( f26_arg1 ) + end + AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_CAC_NO_WARNING ) + AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_SPECIALISTS_NO_WARNING ) + AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_SCORESTREAKS ) + if (Dvar.ui_execdemo_beta:get() or IsStarterPack()) and IsStoreAvailable() then + if CoD.isPC then + AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.STEAM_STORE ) + else + AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.STORE ) + end + end + if Engine.DvarBool( nil, "inventory_test_button_visible" ) then + AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_INVENTORY_TEST ) + end + AddSpacer( f26_arg1 ) + if not DisableBlackMarket() then + AddSmallButton( f26_arg0, f26_arg1, CoD.LobbyButtons.BLACK_MARKET ) + end + AddSpacer( f26_arg1 ) + AddSmallButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_STATS ) +end + +local targetButtons = { + [LobbyData.UITargets.UI_MAIN.id] = CoD.LobbyMenus.ModeSelect, + [LobbyData.UITargets.UI_MODESELECT.id] = CoD.LobbyMenus.ModeSelect, + [LobbyData.UITargets.UI_CPLOBBYLANGAME.id] = CoD.LobbyMenus.CPButtonsLAN, + [LobbyData.UITargets.UI_CPLOBBYLANCUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsLANCUSTOM, + [LobbyData.UITargets.UI_CPLOBBYONLINE.id] = CoD.LobbyMenus.CPButtonsOnline, + [LobbyData.UITargets.UI_CPLOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.CPButtonsPublicGame, + [LobbyData.UITargets.UI_CPLOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsCustomGame, + [LobbyData.UITargets.UI_CP2LOBBYLANGAME.id] = CoD.LobbyMenus.CPZMButtonsLAN, + [LobbyData.UITargets.UI_CP2LOBBYLANCUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsLANCUSTOM, + [LobbyData.UITargets.UI_CP2LOBBYONLINE.id] = CoD.LobbyMenus.CPZMButtonsOnline, + [LobbyData.UITargets.UI_CP2LOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.CPZMButtonsPublicGame, + [LobbyData.UITargets.UI_CP2LOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsCustomGame, + [LobbyData.UITargets.UI_DOALOBBYLANGAME.id] = CoD.LobbyMenus.DOAButtonsLAN, + [LobbyData.UITargets.UI_DOALOBBYONLINE.id] = CoD.LobbyMenus.DOAButtonsOnline, + [LobbyData.UITargets.UI_DOALOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.DOAButtonsPublicGame, + [LobbyData.UITargets.UI_MPLOBBYLANGAME.id] = CoD.LobbyMenus.MPButtonsLAN, + [LobbyData.UITargets.UI_MPLOBBYMAIN.id] = CoD.LobbyMenus.MPButtonsMain, + [LobbyData.UITargets.UI_MPLOBBYONLINE.id] = CoD.LobbyMenus.MPButtonsOnline, + [LobbyData.UITargets.UI_MPLOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.MPButtonsOnlinePublic, + [LobbyData.UITargets.UI_MPLOBBYONLINEMODGAME.id] = CoD.LobbyMenus.MPButtonsModGame, + [LobbyData.UITargets.UI_MPLOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.MPButtonsCustomGame, + [LobbyData.UITargets.UI_MPLOBBYONLINEARENA.id] = CoD.LobbyMenus.MPButtonsArena, + [LobbyData.UITargets.UI_MPLOBBYONLINEARENAGAME.id] = CoD.LobbyMenus.MPButtonsArenaGame, + [LobbyData.UITargets.UI_FRLOBBYONLINEGAME.id] = CoD.LobbyMenus.FRButtonsOnlineGame, + [LobbyData.UITargets.UI_FRLOBBYLANGAME.id] = CoD.LobbyMenus.FRButtonsLANGame, + [LobbyData.UITargets.UI_ZMLOBBYLANGAME.id] = CoD.LobbyMenus.ZMButtonsLAN, + [LobbyData.UITargets.UI_ZMLOBBYONLINE.id] = CoD.LobbyMenus.ZMButtonsOnline, + [LobbyData.UITargets.UI_ZMLOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.ZMButtonsPublicGame, + [LobbyData.UITargets.UI_ZMLOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.ZMButtonsCustomGame, + [LobbyData.UITargets.UI_MPLOBBYONLINETHEATER.id] = CoD.LobbyMenus.ButtonsTheaterGame, + [LobbyData.UITargets.UI_ZMLOBBYONLINETHEATER.id] = CoD.LobbyMenus.ButtonsTheaterGame +} +CoD.LobbyMenus.AddButtonsForTarget = function ( controller, id ) + local buttonFunc = targetButtons[id] + local model = nil + if Engine.IsLobbyActive( Enum.LobbyType.LOBBY_TYPE_GAME ) then + model = Engine.GetModel( DataSources.LobbyRoot.getModel( controller ), "gameClient.isHost" ) + else + model = Engine.GetModel( DataSources.LobbyRoot.getModel( controller ), "privateClient.isHost" ) + end + local isLeader = nil + if model ~= nil then + isLeader = Engine.GetModelValue( model ) + else + isLeader = 1 + end + local result = {} + buttonFunc( controller, result, isLeader ) + return result +end + diff --git a/src/client/component/dvars.cpp b/src/client/component/dvars.cpp new file mode 100644 index 00000000..4dcb0d00 --- /dev/null +++ b/src/client/component/dvars.cpp @@ -0,0 +1,101 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include +#include +#include + +#include "scheduler.hpp" + +namespace dvars +{ + namespace + { + bool initial_config_read = false; + utils::hook::detour dvar_register_new_hook; + utils::hook::detour dvar_set_variant_hook; + + utils::hook::detour set_config_dvar_hook; + utils::hook::detour for_each_name_match_hook; + utils::hook::detour get_debug_name_hook; + + + const std::string get_config_file_path() + { + return "players/user/config.cfg"; + } + + void write_archive_dvars() + { + std::string config_buffer; + + for (int i = 0; i < *game::g_dvarCount; ++i) + { + const auto* dvar = reinterpret_cast(&game::s_dvarPool[160 * i]); + + if (!dvar->debugName || (game::dvarFlags_e::DVAR_SAVED & dvar->flags) == 0) + continue; + + auto name = dvar->debugName; + auto value = game::Dvar_DisplayableValue(dvar); + + config_buffer.append(utils::string::va("set %s %s\n", name, value)); + } + + if (config_buffer.length() == 0) + return; + + utils::io::write_file(get_config_file_path(), config_buffer); + } + + void dvar_set_variant_stub(game::dvar_t* dvar, game::DvarValue* value, unsigned int source) + { + dvar_set_variant_hook.invoke(dvar, value, source); + + if (initial_config_read && (game::dvarFlags_e::DVAR_SAVED & dvar->flags) != 0 && dvar->debugName) + { + write_archive_dvars(); + } + } + + void read_archive_dvars() + { + const std::string path = get_config_file_path(); + + if (!utils::io::file_exists(path)) + return; + + std::string filedata; + utils::io::read_file(path, &filedata); + + game::Cbuf_ExecuteBuffer(0, game::ControllerIndex_t::CONTROLLER_INDEX_0, filedata.c_str()); + initial_config_read = true; + } + + game::dvar_t* dvar_register_new_stub(game::dvarStrHash_t hash, const char* dvar_name, game::dvarType_t type, unsigned int flags, + game::DvarValue value, game::DvarLimits domain, const char* description) + { + auto dvar = dvar_register_new_hook.invoke(hash, dvar_name, type, flags, value, domain, description); + + dvar->debugName = dvar_name; // TODO: gives access violation error + + return dvar; + } + } + + class component final : public client_component + { + public: + void post_unpack() override + { + scheduler::once(read_archive_dvars, scheduler::pipeline::main); + + //dvar_register_new_hook.create(0x1422C5330_g, dvar_register_new_stub); + dvar_set_variant_hook.create(0x1422C9A90_g, dvar_set_variant_stub); + } + }; +} + +REGISTER_COMPONENT(dvars::component) diff --git a/src/client/component/loot.cpp b/src/client/component/loot.cpp index ee1fcbb3..eaff7def 100644 --- a/src/client/component/loot.cpp +++ b/src/client/component/loot.cpp @@ -10,13 +10,20 @@ namespace loot { namespace { + game::dvar_t* dvar_cg_unlockall_loot; + utils::hook::detour loot_getitemquantity_hook; utils::hook::detour liveinventory_getitemquantity_hook; utils::hook::detour liveinventory_areextraslotspurchased_hook; - int loot_getitemquantity_stub(const game::ControllerIndex_t /*controller_index*/, const game::eModes mode, - const int /*item_id*/) + int loot_getitemquantity_stub(const game::ControllerIndex_t controller_index, const game::eModes mode, + const int item_id) { + if (!dvar_cg_unlockall_loot->current.enabled) + { + return loot_getitemquantity_hook.invoke(controller_index, mode, item_id); + } + if (mode == game::eModes::MODE_ZOMBIES) { return 999; @@ -28,8 +35,8 @@ namespace loot int liveinventory_getitemquantity_stub(const game::ControllerIndex_t controller_index, const int item_id) { // Item id's for extra CaC slots, CWL camo's and paid specialist outfits - if (item_id == 99003 || item_id >= 99018 && item_id <= 99021 || item_id == 99025 || item_id >= 90047 && - item_id <= 90064) + if (dvar_cg_unlockall_loot->current.enabled && (item_id == 99003 || item_id >= 99018 && item_id <= 99021 || item_id == 99025|| + item_id >= 90047 && item_id <= 90064)) { return 1; } @@ -37,15 +44,14 @@ namespace loot return liveinventory_getitemquantity_hook.invoke(controller_index, item_id); } - bool liveinventory_areextraslotspurchased_stub(const game::ControllerIndex_t /*controller_index*/) + bool liveinventory_areextraslotspurchased_stub(const game::ControllerIndex_t controller_index) { - return true; - } + if (dvar_cg_unlockall_loot->current.enabled) + { + return true; + } - void set_dvars_on_startup() - { - game::Dvar_SetFromStringByName("ui_enableAllHeroes", "1", true); - game::Dvar_SetFromStringByName("ui_allLootUnlocked", "1", true); + return liveinventory_areextraslotspurchased_hook.invoke(controller_index); } }; @@ -53,7 +59,9 @@ namespace loot { void post_unpack() override { - scheduler::once(set_dvars_on_startup, scheduler::pipeline::main); + dvar_cg_unlockall_loot = game::Dvar_RegisterBool(game::Dvar_GenerateHash("cg_unlockall_loot"), "cg_unlockall_loot", false, (game::dvarFlags_e)0x40, "Unlocks blackmarket loot"); + dvar_cg_unlockall_loot->debugName = "cg_unlockall_loot"; + loot_getitemquantity_hook.create(0x141E82C90_g, loot_getitemquantity_stub); liveinventory_getitemquantity_hook.create(0x141E090C0_g, liveinventory_getitemquantity_stub); liveinventory_areextraslotspurchased_hook.create(0x141E089E0_g, liveinventory_areextraslotspurchased_stub); diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 558e6ef9..1b3d392f 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -222,7 +222,14 @@ namespace ui_scripting ui_cod_init_hook.invoke(frontend); if (game::Com_IsRunningUILevel()) + { + // Fetch the names of the local files so file overrides are already handled + globals = {}; + load_local_script_files(game::get_host_library().get_folder().append("/data/ui_scripts/").string()); + load_local_script_files("boiii/ui_scripts/"); + load_local_script_files("data/ui_scripts/"); return; + } const auto _0 = utils::finally(&try_start); } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 6edf4c9c..aa11233f 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -412,8 +412,8 @@ namespace game ERROR_LUA = 0x200, ERROR_SOFTRESTART = 0x400, ERROR_SOFTRESTART_KEEPDW = 0x800, - }; - + }; + enum XAssetType { ASSET_TYPE_PHYSPRESET = 0x0, @@ -525,19 +525,19 @@ namespace game ASSET_TYPE_REPORT = 0x6A, ASSET_TYPE_DEPEND = 0x68, ASSET_TYPE_FULL_COUNT = 0x6C, - }; - - struct LuaFile - { - const char* name; - int len; - const char* buffer; - }; - - union XAssetHeader - { - void* data; - LuaFile* luaFile; + }; + + struct LuaFile + { + const char* name; + int len; + const char* buffer; + }; + + union XAssetHeader + { + void* data; + LuaFile* luaFile; }; struct XZoneBuffer @@ -619,8 +619,8 @@ namespace game DVAR_TYPE_COLOR_LAB = 0xE, DVAR_TYPE_SESSIONMODE_BASE_DVAR = 0xF, DVAR_TYPE_COUNT = 0x10, - }; - + }; + enum dvarFlags_e { DVAR_ARCHIVE = 1 << 0, @@ -661,7 +661,6 @@ namespace game vec4_t vector; const char* string; byte color[4]; - const dvar_t* indirect[3]; }; struct $7034703ED3857507327AE195CCA24A71 @@ -1011,15 +1010,15 @@ namespace game Agreement debugAgreement; JoinType joinType; JoinResult joinResult; - }; - - - namespace hks - { - struct lua_State; - struct HashTable; - struct StringTable; - struct cclosure; + }; + + + namespace hks + { + struct lua_State; + struct HashTable; + struct StringTable; + struct cclosure; typedef int hksBool; typedef char hksChar; typedef unsigned __int8 hksByte; @@ -1029,183 +1028,183 @@ namespace game typedef int hksInt32; typedef unsigned int hksUint32; typedef __int64 hksInt64; - typedef unsigned __int64 hksUint64; + typedef unsigned __int64 hksUint64; typedef int HksGcCost; - - + + typedef size_t hksSize; typedef void* (*lua_Alloc)(void*, void*, size_t, size_t); - typedef hksInt32(*lua_CFunction)(lua_State*); - - struct GenericChunkHeader - { - hksSize m_flags; - }; - - struct ChunkHeader : GenericChunkHeader - { - ChunkHeader* m_next; + typedef hksInt32(*lua_CFunction)(lua_State*); + + struct GenericChunkHeader + { + hksSize m_flags; + }; + + struct ChunkHeader : GenericChunkHeader + { + ChunkHeader* m_next; }; struct ChunkList { ChunkHeader m_head; - }; - - struct UserData : ChunkHeader - { - unsigned __int64 m_envAndSizeOffsetHighBits; - unsigned __int64 m_metaAndSizeOffsetLowBits; - char m_data[8]; - }; - - struct InternString - { - unsigned __int64 m_flags; - unsigned __int64 m_lengthbits; - unsigned int m_hash; - char m_data[30]; - }; - - union HksValue - { - cclosure* cClosure; - void* closure; - UserData* userData; - HashTable* table; - void* tstruct; - InternString* str; - void* thread; - void* ptr; - float number; - unsigned int native; - bool boolean; - }; - - enum HksObjectType - { - TANY = 0xFFFFFFFE, - TNONE = 0xFFFFFFFF, - TNIL = 0x0, - TBOOLEAN = 0x1, - TLIGHTUSERDATA = 0x2, - TNUMBER = 0x3, - TSTRING = 0x4, - TTABLE = 0x5, - TFUNCTION = 0x6, // idk - TUSERDATA = 0x7, - TTHREAD = 0x8, - TIFUNCTION = 0x9, // Lua function - TCFUNCTION = 0xA, // C function - TUI64 = 0xB, - TSTRUCT = 0xC, - NUM_TYPE_OBJECTS = 0xE, - }; - - struct HksObject - { - HksObjectType t; - HksValue v; - }; - - const struct hksInstruction - { - unsigned int code; - }; - - struct ActivationRecord - { - HksObject* m_base; - const hksInstruction* m_returnAddress; - __int16 m_tailCallDepth; - __int16 m_numVarargs; - int m_numExpectedReturns; - }; - - struct CallStack - { - ActivationRecord* m_records; - ActivationRecord* m_lastrecord; - ActivationRecord* m_current; - const hksInstruction* m_current_lua_pc; - const hksInstruction* m_hook_return_addr; - int m_hook_level; - }; - - struct ApiStack - { - HksObject* top; - HksObject* base; - HksObject* alloc_top; - HksObject* bottom; - }; - - struct UpValue : ChunkHeader - { - HksObject m_storage; - HksObject* loc; - UpValue* m_next; - }; - - struct CallSite - { - _SETJMP_FLOAT128 m_jumpBuffer[16]; - CallSite* m_prev; - }; - - enum Status - { - NEW = 0x1, - RUNNING = 0x2, - YIELDED = 0x3, - DEAD_ERROR = 0x4, - }; - - enum HksError - { - HKS_NO_ERROR = 0, - HKS_ERRSYNTAX = -4, - HKS_ERRFILE = -5, - HKS_ERRRUN = -100, - HKS_ERRMEM = -200, - HKS_ERRERR = -300, - HKS_THROWING_ERROR = -500, - HKS_GC_YIELD = 1, - }; - - struct lua_Debug - { - int event; - const char* name; - const char* namewhat; - const char* what; - const char* source; - int currentline; - int nups; - int nparams; - int ishksfunc; - int linedefined; - int lastlinedefined; - char short_src[512]; - int callstack_level; - int is_tail_call; - }; - - using lua_function = int(__fastcall*)(lua_State*); - - struct luaL_Reg - { - const char* name; - lua_function function; - }; - - struct Node - { - HksObject m_key; - HksObject m_value; - }; - + }; + + struct UserData : ChunkHeader + { + unsigned __int64 m_envAndSizeOffsetHighBits; + unsigned __int64 m_metaAndSizeOffsetLowBits; + char m_data[8]; + }; + + struct InternString + { + unsigned __int64 m_flags; + unsigned __int64 m_lengthbits; + unsigned int m_hash; + char m_data[30]; + }; + + union HksValue + { + cclosure* cClosure; + void* closure; + UserData* userData; + HashTable* table; + void* tstruct; + InternString* str; + void* thread; + void* ptr; + float number; + unsigned int native; + bool boolean; + }; + + enum HksObjectType + { + TANY = 0xFFFFFFFE, + TNONE = 0xFFFFFFFF, + TNIL = 0x0, + TBOOLEAN = 0x1, + TLIGHTUSERDATA = 0x2, + TNUMBER = 0x3, + TSTRING = 0x4, + TTABLE = 0x5, + TFUNCTION = 0x6, // idk + TUSERDATA = 0x7, + TTHREAD = 0x8, + TIFUNCTION = 0x9, // Lua function + TCFUNCTION = 0xA, // C function + TUI64 = 0xB, + TSTRUCT = 0xC, + NUM_TYPE_OBJECTS = 0xE, + }; + + struct HksObject + { + HksObjectType t; + HksValue v; + }; + + const struct hksInstruction + { + unsigned int code; + }; + + struct ActivationRecord + { + HksObject* m_base; + const hksInstruction* m_returnAddress; + __int16 m_tailCallDepth; + __int16 m_numVarargs; + int m_numExpectedReturns; + }; + + struct CallStack + { + ActivationRecord* m_records; + ActivationRecord* m_lastrecord; + ActivationRecord* m_current; + const hksInstruction* m_current_lua_pc; + const hksInstruction* m_hook_return_addr; + int m_hook_level; + }; + + struct ApiStack + { + HksObject* top; + HksObject* base; + HksObject* alloc_top; + HksObject* bottom; + }; + + struct UpValue : ChunkHeader + { + HksObject m_storage; + HksObject* loc; + UpValue* m_next; + }; + + struct CallSite + { + _SETJMP_FLOAT128 m_jumpBuffer[16]; + CallSite* m_prev; + }; + + enum Status + { + NEW = 0x1, + RUNNING = 0x2, + YIELDED = 0x3, + DEAD_ERROR = 0x4, + }; + + enum HksError + { + HKS_NO_ERROR = 0, + HKS_ERRSYNTAX = -4, + HKS_ERRFILE = -5, + HKS_ERRRUN = -100, + HKS_ERRMEM = -200, + HKS_ERRERR = -300, + HKS_THROWING_ERROR = -500, + HKS_GC_YIELD = 1, + }; + + struct lua_Debug + { + int event; + const char* name; + const char* namewhat; + const char* what; + const char* source; + int currentline; + int nups; + int nparams; + int ishksfunc; + int linedefined; + int lastlinedefined; + char short_src[512]; + int callstack_level; + int is_tail_call; + }; + + using lua_function = int(__fastcall*)(lua_State*); + + struct luaL_Reg + { + const char* name; + lua_function function; + }; + + struct Node + { + HksObject m_key; + HksObject m_value; + }; + struct StringPinner { struct Node @@ -1219,7 +1218,7 @@ namespace game InternString** m_nextStringsPlace; Node m_firstNode; Node* m_currentNode; - }; + }; struct StringTable { @@ -1227,102 +1226,102 @@ namespace game unsigned int m_count; unsigned int m_mask; StringPinner* m_pinnedStrings; - }; - - 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; - }; - - struct cclosure : ChunkHeader - { - lua_function m_function; - HashTable* m_env; - __int16 m_numUpvalues; - __int16 m_flags; - InternString* m_name; - HksObject m_upvalues[1]; - }; - - enum HksCompilerSettings_BytecodeSharingFormat - { - BYTECODE_DEFAULT = 0x0, - BYTECODE_INPLACE = 0x1, - BYTECODE_REFERENCED = 0x2, - }; - - enum HksCompilerSettings_IntLiteralOptions - { - INT_LITERALS_NONE = 0x0, - INT_LITERALS_LUD = 0x1, - INT_LITERALS_32BIT = 0x1, - INT_LITERALS_UI64 = 0x2, - INT_LITERALS_64BIT = 0x2, - INT_LITERALS_ALL = 0x3, - }; - - struct HksCompilerSettings - { - int m_emitStructCode; - const char** m_stripNames; - int m_emitGlobalMemoization; - int _m_isHksGlobalMemoTestingMode; - HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat; - HksCompilerSettings_IntLiteralOptions m_enableIntLiterals; - int(*m_debugMap)(const char*, int); - }; - - enum HksBytecodeSharingMode : __int64 - { + }; + + 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; + }; + + struct cclosure : ChunkHeader + { + lua_function m_function; + HashTable* m_env; + __int16 m_numUpvalues; + __int16 m_flags; + InternString* m_name; + HksObject m_upvalues[1]; + }; + + enum HksCompilerSettings_BytecodeSharingFormat + { + BYTECODE_DEFAULT = 0x0, + BYTECODE_INPLACE = 0x1, + BYTECODE_REFERENCED = 0x2, + }; + + enum HksCompilerSettings_IntLiteralOptions + { + INT_LITERALS_NONE = 0x0, + INT_LITERALS_LUD = 0x1, + INT_LITERALS_32BIT = 0x1, + INT_LITERALS_UI64 = 0x2, + INT_LITERALS_64BIT = 0x2, + INT_LITERALS_ALL = 0x3, + }; + + struct HksCompilerSettings + { + int m_emitStructCode; + const char** m_stripNames; + int m_emitGlobalMemoization; + int _m_isHksGlobalMemoTestingMode; + HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat; + HksCompilerSettings_IntLiteralOptions m_enableIntLiterals; + int(*m_debugMap)(const char*, int); + }; + + enum HksBytecodeSharingMode : __int64 + { HKS_BYTECODE_SHARING_OFF = 0, HKS_BYTECODE_SHARING_ON = 1, - HKS_BYTECODE_SHARING_SECURE = 2 - }; - - struct HksGcWeights - { - int m_removeString; - int m_finalizeUserdataNoMM; - int m_finalizeUserdataGcMM; - int m_cleanCoroutine; - int m_removeWeak; - int m_markObject; - int m_traverseString; - int m_traverseUserdata; - int m_traverseCoroutine; - int m_traverseWeakTable; - int m_freeChunk; - int m_sweepTraverse; - }; - - struct GarbageCollector_Stack - { - void* m_storage; - unsigned int m_numEntries; - unsigned int m_numAllocated; - }; - - struct ProtoList - { - void** m_protoList; - unsigned __int16 m_protoSize; - unsigned __int16 m_protoAllocSize; - }; - - struct MemoryManager; - - struct GarbageCollector - { + HKS_BYTECODE_SHARING_SECURE = 2 + }; + + struct HksGcWeights + { + int m_removeString; + int m_finalizeUserdataNoMM; + int m_finalizeUserdataGcMM; + int m_cleanCoroutine; + int m_removeWeak; + int m_markObject; + int m_traverseString; + int m_traverseUserdata; + int m_traverseCoroutine; + int m_traverseWeakTable; + int m_freeChunk; + int m_sweepTraverse; + }; + + struct GarbageCollector_Stack + { + void* m_storage; + unsigned int m_numEntries; + unsigned int m_numAllocated; + }; + + struct ProtoList + { + void** m_protoList; + unsigned __int16 m_protoSize; + unsigned __int16 m_protoAllocSize; + }; + + struct MemoryManager; + + struct GarbageCollector + { struct ResumeStack { void* m_storage; @@ -1355,8 +1354,8 @@ namespace game WeakStack_Entry* m_storage; hksInt32 m_numEntries; hksUint32 m_numAllocated; - }; - + }; + HksGcCost m_target; HksGcCost m_stepsLeft; HksGcCost m_stepLimit; @@ -1391,24 +1390,24 @@ namespace game hksUint32 m_stringTableIndex; hksUint32 m_stringTableSize; UserData* m_lastBlackUD; - UserData* m_activeUD; - }; - - enum MemoryManager_ChunkColor - { - RED = 0x0, - BLACK = 0x1, - }; - - enum Hks_DeleteCheckingMode - { - HKS_DELETE_CHECKING_OFF = 0x0, - HKS_DELETE_CHECKING_ACCURATE = 0x1, - HKS_DELETE_CHECKING_SAFE = 0x2, - }; - - struct MemoryManager - { + UserData* m_activeUD; + }; + + enum MemoryManager_ChunkColor + { + RED = 0x0, + BLACK = 0x1, + }; + + enum Hks_DeleteCheckingMode + { + HKS_DELETE_CHECKING_OFF = 0x0, + HKS_DELETE_CHECKING_ACCURATE = 0x1, + HKS_DELETE_CHECKING_SAFE = 0x2, + }; + + struct MemoryManager + { enum ChunkColor : __int32 { WHITE = 0x0, @@ -1423,58 +1422,58 @@ namespace game ChunkList m_allocationList; ChunkList m_sweepList; ChunkHeader* m_lastKeptChunk; - lua_State* m_state; - }; - - struct StaticStringCache - { - HksObject m_objects[41]; - }; - - enum HksBytecodeEndianness - { - HKS_BYTECODE_DEFAULT_ENDIAN = 0x0, - HKS_BYTECODE_BIG_ENDIAN = 0x1, - HKS_BYTECODE_LITTLE_ENDIAN = 0x2, - }; - - struct RuntimeProfileData_Stats - { - unsigned __int64 hksTime; - unsigned __int64 callbackTime; - unsigned __int64 gcTime; - unsigned __int64 cFinalizerTime; - unsigned __int64 compilerTime; - unsigned int hkssTimeSamples; - unsigned int callbackTimeSamples; - unsigned int gcTimeSamples; - unsigned int compilerTimeSamples; - unsigned int num_newuserdata; - unsigned int num_tablerehash; - unsigned int num_pushstring; - unsigned int num_pushcfunction; - unsigned int num_newtables; - }; - - struct RuntimeProfileData - { - __int64 stackDepth; - __int64 callbackDepth; - unsigned __int64 lastTimer; - RuntimeProfileData_Stats frameStats; - unsigned __int64 gcStartTime; - unsigned __int64 finalizerStartTime; - unsigned __int64 compilerStartTime; - unsigned __int64 compilerStartGCTime; - unsigned __int64 compilerStartGCFinalizerTime; - unsigned __int64 compilerCallbackStartTime; - __int64 compilerDepth; - void* outFile; - lua_State* rootState; - }; - - struct HksGlobal - { + lua_State* m_state; + }; + + struct StaticStringCache + { + HksObject m_objects[41]; + }; + + enum HksBytecodeEndianness + { + HKS_BYTECODE_DEFAULT_ENDIAN = 0x0, + HKS_BYTECODE_BIG_ENDIAN = 0x1, + HKS_BYTECODE_LITTLE_ENDIAN = 0x2, + }; + + struct RuntimeProfileData_Stats + { + unsigned __int64 hksTime; + unsigned __int64 callbackTime; + unsigned __int64 gcTime; + unsigned __int64 cFinalizerTime; + unsigned __int64 compilerTime; + unsigned int hkssTimeSamples; + unsigned int callbackTimeSamples; + unsigned int gcTimeSamples; + unsigned int compilerTimeSamples; + unsigned int num_newuserdata; + unsigned int num_tablerehash; + unsigned int num_pushstring; + unsigned int num_pushcfunction; + unsigned int num_newtables; + }; + + struct RuntimeProfileData + { + __int64 stackDepth; + __int64 callbackDepth; + unsigned __int64 lastTimer; + RuntimeProfileData_Stats frameStats; + unsigned __int64 gcStartTime; + unsigned __int64 finalizerStartTime; + unsigned __int64 compilerStartTime; + unsigned __int64 compilerStartGCTime; + unsigned __int64 compilerStartGCFinalizerTime; + unsigned __int64 compilerCallbackStartTime; + __int64 compilerDepth; + void* outFile; + lua_State* rootState; + }; + + struct HksGlobal + { MemoryManager m_memory; GarbageCollector m_collector; StringTable m_stringTable; @@ -1493,30 +1492,30 @@ namespace game void* m_luaplusObjectList; int m_heapAssertionFrequency; int m_heapAssertionCount; - void (*m_logFunction)(lua_State*, const char*, ...); + void (*m_logFunction)(lua_State*, const char*, ...); void (*m_emergencyGCFailFunction)(lua_State*, size_t); HksBytecodeEndianness m_bytecodeDumpEndianness; - int padding2; - }; - - struct lua_State + int padding2; + }; + + struct lua_State { - ChunkHeader baseclass; - HksGlobal* m_global; - CallStack m_callStack; - ApiStack m_apistack; - UpValue* pending; - HksObject globals; - HksObject m_cEnv; - CallSite* m_callsites; - int m_numberOfCCalls; - void* m_context; - InternString* m_name; - lua_State* m_nextState; - lua_State* m_nextStateStack; - Status m_status; - HksError m_error; - }; + ChunkHeader baseclass; + HksGlobal* m_global; + CallStack m_callStack; + ApiStack m_apistack; + UpValue* pending; + HksObject globals; + HksObject m_cEnv; + CallSite* m_callsites; + int m_numberOfCCalls; + void* m_context; + InternString* m_name; + lua_State* m_nextState; + lua_State* m_nextStateStack; + Status m_status; + HksError m_error; + }; } #ifdef __cplusplus diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 44841ac9..05329ec7 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -30,7 +30,8 @@ namespace game bool fromRemoteConsol)> Cmd_ExecuteSingleCommand{ 0x1420EDC20 }; - WEAK symbol Con_GetTextCopy{0x14133A7D0}; + WEAK symbol Con_GetTextCopy{0x14133A7D0}; + WEAK symbol Cbuf_ExecuteBuffer{0x14133BE10}; // DB WEAK symbol DB_LoadXAssets{ @@ -58,11 +59,19 @@ namespace game WEAK symbol Dvar_FindVar{0x1422BD730}; WEAK symbol Dvar_GenerateHash{0x14133DBF0}; WEAK symbol Dvar_FindMalleableVar{0x1422BD6A0}; - WEAK symbol Dvar_GetDebugName{0x1422BDCB0}; - WEAK symbol Dvar_GetString{0x1422BFFF0}; + WEAK symbol Dvar_GetDebugName{0x1422BDCB0}; + WEAK symbol Dvar_DisplayableValue{0x1422BCAE0}; + WEAK symbol Dvar_GetString{0x1422BFFF0}; + WEAK symbol Dvar_GetBool{ 0x1422BD930 }; + WEAK symbol Dvar_RegisterBool{ + 0x1422D1360 + }; + WEAK symbol Dvar_ForEach{ 0x1422BD760 }; WEAK symbol Dvar_SetFromStringByName{ 0x1422C7F60 - }; + }; + WEAK symbol s_dvarPool{ 0x157AC8220 }; + WEAK symbol g_dvarCount{ 0x157AC81CC }; // Rendering WEAK symbol