From deeae4f9b1177d4f2ac8df49d0f146885bb5f2a1 Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Thu, 26 May 2022 00:26:02 +0200 Subject: [PATCH] Debug missing assets + dump gsc scripts --- src/client/component/fastfiles.cpp | 63 ++++++++++++++++++++++++++- src/client/component/ui_scripting.cpp | 49 ++++++++++++++++++--- src/client/game/symbols.hpp | 2 +- 3 files changed, 107 insertions(+), 7 deletions(-) diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 80d294ba..1ff9a8ae 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -1,12 +1,16 @@ #include #include "loader/component_loader.hpp" -#include "fastfiles.hpp" +#include "game/dvars.hpp" + +#include "fastfiles.hpp" #include "command.hpp" #include "console.hpp" #include #include +#include +#include namespace fastfiles { @@ -15,6 +19,8 @@ namespace fastfiles namespace { utils::hook::detour db_try_load_x_file_internal_hook; + utils::hook::detour db_find_xasset_header_hook; + game::dvar_t* g_dump_scripts; void db_try_load_x_file_internal(const char* zone_name, const int flags) { @@ -25,6 +31,57 @@ namespace fastfiles }); db_try_load_x_file_internal_hook.invoke(zone_name, flags); } + + void dump_gsc_script(const std::string& name, game::XAssetHeader header) + { + if (!g_dump_scripts->current.enabled) + { + return; + } + + std::string buffer; + buffer.append(header.scriptfile->name, strlen(header.scriptfile->name) + 1); + buffer.append(reinterpret_cast(&header.scriptfile->compressedLen), 4); + buffer.append(reinterpret_cast(&header.scriptfile->len), 4); + buffer.append(reinterpret_cast(&header.scriptfile->bytecodeLen), 4); + buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen); + buffer.append(header.scriptfile->bytecode, header.scriptfile->bytecodeLen); + + const auto out_name = utils::string::va("gsc_dump/%s.gscbin", name.data()); + utils::io::write_file(out_name, buffer); + + console::info("Dumped %s\n", out_name); + } + + game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default) + { + const auto start = game::Sys_Milliseconds(); + const auto result = db_find_xasset_header_hook.invoke(type, name, allow_create_default); + const auto diff = game::Sys_Milliseconds() - start; + + if (type == game::XAssetType::ASSET_TYPE_SCRIPTFILE) + { + dump_gsc_script(name, result); + } + + if (diff > 100) + { + console::print( + result.data == nullptr + ? console::con_type_error + : console::con_type_warning, + "Waited %i msec for %sasset \"%s\", of type \"%s\"\n", + diff, + result.data == nullptr + ? "missing " + : "", + name, + game::g_assetNames[type] + ); + } + + return result; + } } std::string get_current_fastfile() @@ -52,6 +109,10 @@ namespace fastfiles { db_try_load_x_file_internal_hook.create( SELECT_VALUE(0x1F5700_b, 0x39A620_b), &db_try_load_x_file_internal); + + db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub); + + g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts"); } }; } diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 385ed0fc..a4bc3cf2 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -35,7 +35,6 @@ namespace ui_scripting utils::hook::detour hks_package_require_hook; utils::hook::detour hks_load_hook; - utils::hook::detour db_find_xasset_header_hook; const auto lui_common = utils::nt::load_resource(LUI_COMMON); @@ -274,9 +273,9 @@ namespace ui_scripting game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default) { - if (type != 0x3D || !is_loaded_script(globals.in_require_script)) + if (!is_loaded_script(globals.in_require_script)) { - return db_find_xasset_header_hook.invoke(type, name, allow_create_default); + return game::DB_FindXAssetHeader(type, name, allow_create_default); } const auto folder = globals.in_require_script.substr(0, globals.in_require_script.find_last_of("/\\")); @@ -291,7 +290,7 @@ namespace ui_scripting } else if (name_.starts_with("ui/LUI/")) { - return db_find_xasset_header_hook.invoke(type, name, allow_create_default); + return game::DB_FindXAssetHeader(type, name, allow_create_default); } return static_cast(nullptr); @@ -375,7 +374,47 @@ namespace ui_scripting return; } - db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub); + utils::hook::jump(SELECT_VALUE(0xE7419_b, 0x25E809_b), utils::hook::assemble([](utils::hook::assembler& a) + { + const auto loc = a.newLabel(); + + a.push(rax); + a.pushad64(); + a.call_aligned(db_find_xasset_header_stub); + a.mov(qword_ptr(rsp, 0x80), rax); + a.popad64(); + a.pop(rax); + + a.mov(rcx, r13); + a.test(rax, rax); + a.jnz(loc); + + a.jmp(SELECT_VALUE(0xE7426_b, 0x25E816_b)); + + a.bind(loc); + a.jmp(SELECT_VALUE(0xE748A_b, 0x25E87A_b)); + }), true); + + utils::hook::jump(SELECT_VALUE(0xE72CB_b, 0x25E6BB_b), utils::hook::assemble([](utils::hook::assembler& a) + { + const auto loc = a.newLabel(); + + a.push(rax); + a.pushad64(); + a.call_aligned(db_find_xasset_header_stub); + a.mov(qword_ptr(rsp, 0x80), rax); + a.popad64(); + a.pop(rax); + + a.test(rax, rax); + a.jnz(loc); + + a.jmp(SELECT_VALUE(0xE72D9_b, 0x25E6C9_b)); + + a.bind(loc); + a.jmp(SELECT_VALUE(0xE73EC_b, 0x25E7DC_b)); + }), true); + hks_load_hook.create(SELECT_VALUE(0xB46F0_b, 0x22C180_b), hks_load_stub); hks_package_require_hook.create(SELECT_VALUE(0x90070_b, 0x214040_b), hks_package_require_stub); diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index cf725731..1ba6d465 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -48,7 +48,7 @@ namespace game WEAK symbol Com_Quit_f{0x0, 0x1F9280}; WEAK symbol Com_Shutdown{0x0, 0x0}; - WEAK symbol Quit{0x105FF0, 0x17CF50}; + WEAK symbol Quit{0x3A5A20, 0x17CF50}; WEAK symbol CG_GameMessage{0x0, 0x316210}; WEAK symbol CG_GameMessageBold{0x0, 0x3122F0};