From e325ec18fc17248977982b05ca5fe182a732455b Mon Sep 17 00:00:00 2001 From: Antoine Willerval Date: Thu, 28 Nov 2024 11:57:36 +0100 Subject: [PATCH 1/2] Add more command suggestions (#54) --- source/proxy-dll/definitions/variables.cpp | 122 ++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/source/proxy-dll/definitions/variables.cpp b/source/proxy-dll/definitions/variables.cpp index 44ae31c..713a7fc 100644 --- a/source/proxy-dll/definitions/variables.cpp +++ b/source/proxy-dll/definitions/variables.cpp @@ -6529,7 +6529,127 @@ namespace variables "quit", "Shutdown the Game [Com_Quit_f]", 0x1DEE6107B26F8BB6 - } + }, + { + "gts", + "Set gametype setting, Usage: gts []", + 0x55155818fad5c0df + }, + { + "gametype_setting", + "Set gametype setting, Usage: gametype_setting []", + 0x46fb4ee1a7c51bcc + }, + { + "set", + "Set dvar value, Usage: set ", + 0x23b87195ce20e23 + }, + { + "setdvartotime", + "Set dvar value to time, Usage: setdvartotime ", + 0x1cc7ab7a4c8a4e7a + }, + { + "reset", + "Reset dvar value, Usage: reset ", + 0xf136ff2c086b760 + }, + { + "lobby_errorshutdown", + "Shutdown lobby with error", + 0x6a3a118a63f519a4 + }, + { + "lobby_reload", + "Reload lobby", + 0x67ac003bb1f9cfc9 + }, + { + "map", + "Load map, Usage: map ", + 0x80f5919176d2d91 + }, + { + "devmap", + "Load map in dev mode, Usage: devmap ", + 0x7e4ea29057c5f962 + }, + { + "gametype", + "Set the gametype, Usage: gametype ", + 0x6792c1f90c3a5c7f + }, + { + "exec", + "Exec file, Usage: exec ", + 0x67c75e608ea39b5c + }, + { + "disconnect", + "Disconnect from server", + 0x5926cb82738c446b + }, + { + "reconnect", + "Reconnect to localhost", + 0x31ab1e1fcc28efe6 + }, + { + "hostmigration_start", + "Start host migration", + 0x6bf6f7867a2b3202 + }, + { + "setperk", + "Set a perk, Usage: setperk ", + 0x262eebca5ebf646b + }, + { + "killserver", + "Shutdown the server", + 0x64fb81c94800815e + }, + { + "switchmaps", + "Switch now to the map", + 0x182ae2b46edd075a + }, + { + "msload", + "Start loading map switch, Usage: msload ", + 0xddcb9fb38629fcd + }, + { + "mspreload", + "Start loading map switch, Usage: mspreload ", + 0x291b1ea6866a7cfa + }, + { + "fast_restart", + "Fast restart", + 0x506e7e29c6d674bb + }, + { + "full_restart", + "Full restart", + 0x7cba6d063fbd73d0 + }, + { + "clientkick_for_reason", + "Kick client with a reason, Usage: clientkick_for_reason ", + 0x7086b005b6eb2871 + }, + { + "clientkick", + "Kick client, Usage: clientkick ", + 0x3e30e01add5a0b78 + }, + { + "clearpsdata", + "Clear PS data", + 0x2e8816c78dd363c7 + }, }; std::vector get_dvars_list() From dc46fdfad5308bb76f68088a06b105ae0d46a1c2 Mon Sep 17 00:00:00 2001 From: Antoine Willerval Date: Thu, 28 Nov 2024 11:58:49 +0100 Subject: [PATCH 2/2] reimplement acts gsc debug objects (#55) --- source/proxy-dll/component/debugging.cpp | 7 + source/proxy-dll/component/gsc_custom.cpp | 271 ++++++++++++++++++++- source/proxy-dll/component/gsc_custom.hpp | 6 + source/proxy-dll/component/gsc_funcs.cpp | 83 ++++++- source/proxy-dll/component/gsc_funcs.hpp | 1 + source/proxy-dll/component/mods.cpp | 109 ++++++++- source/proxy-dll/definitions/game.hpp | 2 + source/proxy-dll/definitions/scripting.hpp | 157 +++++++++++- 8 files changed, 624 insertions(+), 12 deletions(-) diff --git a/source/proxy-dll/component/debugging.cpp b/source/proxy-dll/component/debugging.cpp index 1fe2cee..6d7bc66 100644 --- a/source/proxy-dll/component/debugging.cpp +++ b/source/proxy-dll/component/debugging.cpp @@ -1,6 +1,7 @@ #include #include "definitions/game.hpp" #include "component/scheduler.hpp" +#include "component/gsc_custom.hpp" #include "loader/component_loader.hpp" #include @@ -73,6 +74,12 @@ namespace debugging void sys_error_stub(uint32_t code, const char* message) { + if (code == gsc_custom::linking_error) + { + gsc_custom::find_linking_issues(); + return; // converted to runtime error to avoid the crash + } + const char* error_message = runtime_errors::get_error_message(code); if (error_message) diff --git a/source/proxy-dll/component/gsc_custom.cpp b/source/proxy-dll/component/gsc_custom.cpp index 65708d3..81b598a 100644 --- a/source/proxy-dll/component/gsc_custom.cpp +++ b/source/proxy-dll/component/gsc_custom.cpp @@ -1,10 +1,13 @@ #include #include "gsc_custom.hpp" #include "gsc_funcs.hpp" +#include "hashes.hpp" #include "definitions/game.hpp" +#include "definitions/xassets.hpp" #include "loader/component_loader.hpp" #include #include +#include namespace gsc_custom { @@ -208,6 +211,20 @@ namespace gsc_custom inst_data.emplace_back(info); } + void vm_op_custom_devblock(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate) + { + byte* base = align_ptr(fs_0->pos); + int16_t delta = *(int16_t*)base; + + fs_0->pos = base + 2; + + if (!gsc_funcs::enable_dev_blocks) { + // default action, jump after the dev block + fs_0->pos = fs_0->pos + delta; + } + } + + void vm_op_custom_lazylink(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate) { byte* base = align_ptr(fs_0->pos); @@ -290,7 +307,212 @@ namespace gsc_custom } + void find_linking_issues() + { + logger::write(logger::LOG_TYPE_ERROR, "Linking error detected, searching cause..."); + + std::unordered_map> availables{}; + for (size_t _inst = 0; _inst < game::SCRIPTINSTANCE_MAX; _inst++) + { + size_t error{}; + game::scriptInstance_t inst = (game::scriptInstance_t)_inst; + for (size_t obj = 0; obj < game::gObjFileInfoCount[inst]; obj++) + { + game::objFileInfo_t& info = (*game::gObjFileInfo)[inst][obj]; + + if (!info.activeVersion) + { + continue; + } + + game::GSC_OBJ* prime_obj = info.activeVersion; + + availables.clear(); + + // load exports + game::GSC_EXPORT_ITEM* exports = (game::GSC_EXPORT_ITEM*)(prime_obj->magic + prime_obj->exports_offset); + + for (size_t i = 0; i < prime_obj->exports_count; i++) + { + availables[exports[i].name_space].insert(exports[i].name); + } + + // load using exports + int64_t* usings = (int64_t*)(prime_obj->magic + prime_obj->include_offset); + + for (size_t i = 0; i < prime_obj->include_count; i++) + { + game::BO4_AssetRef_t ref{ usings[i], 0 }; + xassets::scriptparsetree_header* spt = xassets::DB_FindXAssetHeader(xassets::ASSET_TYPE_SCRIPTPARSETREE, &ref, false, -1).scriptparsetree; + + if (!spt || !spt->buffer) + { + error++; + logger::write(logger::LOG_TYPE_ERROR, "[%s] Can't find #using %s in %s", inst ? "CSC" : "GSC", hashes::lookup_tmp("script", ref.hash), hashes::lookup_tmp("script", prime_obj->name)); + continue; + } + + game::GSC_EXPORT_ITEM* exports_using = (game::GSC_EXPORT_ITEM*)(spt->buffer->magic + spt->buffer->exports_offset); + + for (size_t j = 0; j < spt->buffer->exports_count; j++) + { + if (exports_using[j].flags & game::GSC_EXPORT_FLAGS::GEF_PRIVATE) + { + continue; // can't import private exports + } + availables[exports_using[j].name_space].insert(exports_using[j].name); + } + } + + game::GSC_IMPORT_ITEM* imports = (game::GSC_IMPORT_ITEM*)(prime_obj->magic + prime_obj->imports_offset); + + for (size_t i = 0; i < prime_obj->imports_count; i++) + { + game::GSC_IMPORT_ITEM* imp = imports; + + uint32_t* locations = reinterpret_cast(imp + 1); + imports = reinterpret_cast(locations + imp->num_address); + + if (imp->flags & game::GSC_IMPORT_FLAGS::GIF_DEV_CALL) + { + // ignore dev calls + continue; + } + + auto itn = availables.find(imp->name_space); + + if (itn != availables.end() && itn->second.contains(imp->name)) + { + continue; + } + + byte import_type = imp->flags & game::GSC_IMPORT_FLAGS::GIF_CALLTYPE_MASK; + + // search builtin calls + if ((imp->flags & game::GSC_IMPORT_FLAGS::GIF_GET_CALL) != 0 || imp->name_space == 0xC1243180 || imp->name_space == 0x222276A9) + { + + int type{}; + int ignored{}; + if (import_type == game::GSC_IMPORT_FLAGS::GIF_FUNC_METHOD || import_type == game::GSC_IMPORT_FLAGS::GIF_FUNCTION) + { + // &func or func() + if (inst) + { + if (game::CScr_GetFunction(imp->name, &type, &ignored, &ignored) && !type) + { + continue; + } + } + else + { + if (game::Scr_GetFunction(imp->name, &type, &ignored, &ignored) && !type) + { + continue; + } + } + } + + if (import_type == game::GSC_IMPORT_FLAGS::GIF_FUNC_METHOD || import_type == game::GSC_IMPORT_FLAGS::GIF_METHOD) + { + // &meth or meth() + if (inst) + { + if (game::CScr_GetMethod(imp->name, &type, &ignored, &ignored) && !type) + { + continue; + } + } + else + { + if (game::Scr_GetMethod(imp->name, &type, &ignored, &ignored) && !type) + { + continue; + } + } + } + } + + const char* func; + + if ((imp->flags & game::GSC_IMPORT_FLAGS::GIF_GET_CALL) != 0 || imp->name_space == 0xC1243180 || imp->name_space == 0x222276A9) + { + func = hashes::lookup_tmp("function", imp->name); + } + else + { + func = utilities::string::va("%s::%s", hashes::lookup_tmp("namespace", imp->name_space), hashes::lookup_tmp("function", imp->name)); + } + + const char* prefix; + + switch (import_type) + { + case game::GIF_FUNC_METHOD: + prefix = "&"; + break; + case game::GIF_FUNCTION: + case game::GIF_METHOD: + prefix = ""; + break; + case game::GIF_FUNCTION_THREAD: + case game::GIF_METHOD_THREAD: + prefix = "thread "; + break; + case game::GIF_FUNCTION_CHILDTHREAD: + case game::GIF_METHOD_CHILDTHREAD: + prefix = "childthread "; + break; + default: + prefix = ""; + break; + } + + logger::write(logger::LOG_TYPE_ERROR, "[%s] Unknown import %s%s in %s", + inst ? "CSC" : "GSC", prefix, func, hashes::lookup_tmp("script", prime_obj->name) + ); + for (size_t j = 0; j < imp->num_address; j++) + { + const char* scriptname{}; + int32_t sloc{}; + int32_t crc{}; + int32_t vm{}; + game::Scr_GetGscExportInfo(inst, prime_obj->magic + locations[j], &scriptname, &sloc, &crc, &vm); + if (scriptname) + { + logger::write(logger::LOG_TYPE_ERROR, "[%s] at %s", inst ? "CSC" : "GSC", scriptname); + } + else + { + logger::write(logger::LOG_TYPE_ERROR, "[%s] at %s@%lx", inst ? "CSC" : "GSC", hashes::lookup_tmp("script", prime_obj->name), locations[j]); + } + } + } + } + + if (error) + { + // convert the error to a terminal error to avoid a game crash + gsc_funcs::gsc_error("Find %lld GSC Linking error(s), see logs for more details", inst, true, error); + break; + } + } + // Can't find the error, we crash the server by default + gsc_funcs::gsc_error("GSC Linking error, see logs for more details", game::SCRIPTINSTANCE_SERVER, true); + } + + void patch_linking_sys_error() + { + auto scr_get_gsc_obj = 0x142748BB0_g; + + // skip the error and the autoexec + // 1C1 = syserr start + // 19F = end + utilities::hook::jump(scr_get_gsc_obj + 0x1C1, scr_get_gsc_obj + 0x19F); + } + utilities::hook::detour scr_get_gsc_obj_hook; + utilities::hook::detour gsc_obj_resolve_hook; void scr_get_gsc_obj_stub(game::scriptInstance_t inst, game::BO4_AssetRef_t* name, bool runScript) { if (game::gObjFileInfoCount[inst] == 0) @@ -305,16 +527,63 @@ namespace gsc_custom gsc_custom::link_detours(inst); } + int32_t gsc_obj_resolve_stub(game::scriptInstance_t inst, game::GSC_OBJ* prime_obj) + { + game::GSC_IMPORT_ITEM* import_item = prime_obj->get_imports(); + + for (size_t i = 0; i < prime_obj->imports_count; i++) + { + if ((import_item->flags & game::GIF_SHIELD_DEV_BLOCK_FUNC) != 0) + { + // enable or disable this dev import + if (gsc_funcs::enable_dev_blocks) + { + import_item->flags &= ~game::GIF_DEV_CALL; + } + else + { + import_item->flags |= game::GIF_DEV_CALL; + } + } + + // goto to the next element after the addresses + uint32_t* addresses = reinterpret_cast(import_item + 1); + import_item = reinterpret_cast(addresses + import_item->num_address); + } + + bool dv_func_back = gsc_funcs::enable_dev_func; + + if (gsc_funcs::enable_dev_blocks) + { + gsc_funcs::enable_dev_func = true; + } + + int32_t ret = gsc_obj_resolve_hook.invoke(inst, prime_obj); + + gsc_funcs::enable_dev_func = dv_func_back; + + return ret; + } + class component final : public component_interface { public: void post_unpack() override { // t8compiler custom opcode - game::gVmOpJumpTable[0x16] = vm_op_custom_lazylink; + game::gVmOpJumpTable[lazylink_opcode] = vm_op_custom_lazylink; + game::gVmOpJumpTable[shield_devblock_opcode] = vm_op_custom_devblock; // group gsc link scr_get_gsc_obj_hook.create(0x142748BB0_g, scr_get_gsc_obj_stub); + + if (gsc_funcs::enable_dev_blocks) + { + gsc_obj_resolve_hook.create(0x142746A30_g, gsc_obj_resolve_stub); + } + + + patch_linking_sys_error(); } }; } diff --git a/source/proxy-dll/component/gsc_custom.hpp b/source/proxy-dll/component/gsc_custom.hpp index af5e549..5b73477 100644 --- a/source/proxy-dll/component/gsc_custom.hpp +++ b/source/proxy-dll/component/gsc_custom.hpp @@ -3,6 +3,10 @@ namespace gsc_custom { + constexpr uint32_t linking_error = 1670707254; + constexpr uint16_t lazylink_opcode = 0x16; + constexpr uint16_t shield_devblock_opcode = 0x21; + enum gsic_field_type { GSIC_FIELD_DETOUR = 0 @@ -22,8 +26,10 @@ namespace gsc_custom struct gsic_info { bool sync{}; + bool dev_blocks{}; std::vector detours{}; }; void sync_gsic(game::scriptInstance_t inst, gsic_info& info); + void find_linking_issues(); } \ No newline at end of file diff --git a/source/proxy-dll/component/gsc_funcs.cpp b/source/proxy-dll/component/gsc_funcs.cpp index 9bb52a4..f958832 100644 --- a/source/proxy-dll/component/gsc_funcs.cpp +++ b/source/proxy-dll/component/gsc_funcs.cpp @@ -947,6 +947,59 @@ namespace gsc_funcs shield_from_json_push_struct(inst, doc); } + + void add_debug_command(game::scriptInstance_t inst) + { + int localclientnum; + const char* cmd; + + if (inst == game::SCRIPTINSTANCE_CLIENT) + { + // client, using param + localclientnum = (int)game::ScrVm_GetInt(inst, 0); + cmd = game::ScrVm_GetString(inst, 1); + } + else + { + // server, using primary + localclientnum = game::Com_LocalClients_GetPrimary(); + cmd = game::ScrVm_GetString(inst, 0); + } + + game::Cbuf_AddText(localclientnum, cmd); + } + + void hash_lookup(game::scriptInstance_t inst) + { + game::ScrVarType_t type = game::ScrVm_GetType(inst, 0); + + switch (type) + { + case game::TYPE_STRING: + game::ScrVm_AddConstString(inst, game::ScrVm_GetConstString(inst, 0)); + return; + case game::TYPE_HASH: + { + game::BO4_AssetRef_t hash; + const char* lookup = hashes::lookup(game::ScrVm_GetHash(&hash, inst, 0)->hash); + + if (lookup) + { + game::ScrVm_AddString(inst, lookup); + } + else + { + // can't find value, return base hash + game::ScrVm_AddHash(inst, &hash); + } + return; + } + default: + gsc_error("invalid hash param, a hash or a string should be used, received: %s", inst, false, game::var_typename[type]); + return; + } + } + game::BO4_BuiltinFunctionDef custom_functions_gsc[] = { @@ -1047,6 +1100,13 @@ namespace gsc_funcs .max_args = 2, .actionFunc = shield_to_json, .type = 0 + }, + {// ShieldHashLookup(hash hash) -> string + .canonId = canon_hash("ShieldHashLookup"), + .min_args = 1, + .max_args = 1, + .actionFunc = hash_lookup, + .type = 0 } }; game::BO4_BuiltinFunctionDef custom_functions_csc[] = @@ -1141,6 +1201,13 @@ namespace gsc_funcs .max_args = 2, .actionFunc = shield_to_json, .type = 0 + }, + {// ShieldHashLookup(hash hash) -> string + .canonId = canon_hash("ShieldHashLookup"), + .min_args = 1, + .max_args = 1, + .actionFunc = hash_lookup, + .type = 0 } }; @@ -1175,6 +1242,7 @@ namespace gsc_funcs bool enable_dev_func = false; + bool enable_dev_blocks = false; utilities::hook::detour scr_get_function_reverse_lookup; utilities::hook::detour cscr_get_function_reverse_lookup; @@ -1394,7 +1462,7 @@ namespace gsc_funcs game::GSC_OBJ* obj = info.activeVersion; - if (codepos >= obj->magic + obj->start_data && codepos < obj->magic + obj->start_data + obj->data_length) + if (codepos >= obj->magic + obj->cseg_offset && codepos < obj->magic + obj->cseg_offset + obj->cseg_size) { script_obj = obj; break; @@ -1461,10 +1529,19 @@ namespace gsc_funcs class component final : public component_interface { public: + void pre_start() override + { + // enable dev functions + enable_dev_func = utilities::json_config::ReadBoolean("gsc", "dev_funcs", false); + // enable custom compiled dev blocks + enable_dev_blocks = utilities::json_config::ReadBoolean("gsc", "dev_blocks", false); + } + void post_unpack() override { - // enable dev functions still available in the game - enable_dev_func = utilities::json_config::ReadBoolean("gsc", "dev_funcs", false); + // replace nulled function references + reinterpret_cast(0x144ED5D90_g)->actionFunc = add_debug_command; // csc + reinterpret_cast(0x1449BAD60_g)->actionFunc = add_debug_command; // gsc scr_get_function_reverse_lookup.create(0x1433AF8A0_g, scr_get_function_reverse_lookup_stub); cscr_get_function_reverse_lookup.create(0x141F132A0_g, cscr_get_function_reverse_lookup_stub); diff --git a/source/proxy-dll/component/gsc_funcs.hpp b/source/proxy-dll/component/gsc_funcs.hpp index 91d4b97..13286ef 100644 --- a/source/proxy-dll/component/gsc_funcs.hpp +++ b/source/proxy-dll/component/gsc_funcs.hpp @@ -7,6 +7,7 @@ namespace gsc_funcs constexpr auto serious_custom_func_name = "SeriousCustom"; extern bool enable_dev_func; + extern bool enable_dev_blocks; uint32_t canon_hash(const char* str); uint32_t canon_hash_pattern(const char* str); diff --git a/source/proxy-dll/component/mods.cpp b/source/proxy-dll/component/mods.cpp index 8a5a50e..ad5598a 100644 --- a/source/proxy-dll/component/mods.cpp +++ b/source/proxy-dll/component/mods.cpp @@ -6,8 +6,9 @@ #include "command.hpp" #include "loader/component_loader.hpp" -#include "definitions/xassets.hpp" #include "definitions/game.hpp" +#include "definitions/xassets.hpp" +#include "definitions/scripting.hpp" #include #include @@ -22,6 +23,13 @@ namespace mods { std::filesystem::path mod_dir = "project-bo4/mods"; namespace { + template + inline byte* align_ptr(byte* ptr) + { + return reinterpret_cast((reinterpret_cast(ptr) + sizeof(T) - 1) & ~(sizeof(T) - 1)); + } + + struct raw_file { xassets::raw_file_header header{}; @@ -144,6 +152,89 @@ namespace mods { return true; } + + bool load_acts_debug() + { + if ((data.length() < sizeof(game::GSC_OBJ) + sizeof(game::acts_debug::MAGIC)) + || *reinterpret_cast(&data[sizeof(game::GSC_OBJ)]) != game::acts_debug::MAGIC) + { + return true; // too small to be a debug file + } + game::acts_debug::GSC_ACTS_DEBUG& dbg = *reinterpret_cast(data.data() + sizeof(game::GSC_OBJ)); + + + logger::write(logger::LOG_TYPE_DEBUG, "loading acts debug file v%x.%llx", dbg.version, dbg.actsVersion); + + byte* ptr = (byte*)data.data(); + + if (dbg.has_feature(game::acts_debug::ADF_STRING)) + { + char* start = data.data(); + for (uint32_t* strs = dbg.get_strings(ptr); strs != dbg.get_strings_end(ptr); strs++) + { + const char* val = data.data() + *strs; + hashes::add_hash(fnv1a::generate_hash(val), val); + hashes::add_hash(gsc_funcs::canon_hash(val), val); + } + } + + if (dbg.has_feature(game::acts_debug::ADF_DETOUR)) + { + for (game::acts_debug::GSC_ACTS_DETOUR* detours = dbg.get_detours(ptr); detours != dbg.get_detours_end(ptr); detours++) + { + gsc_custom::gsic_detour& detour = gsic.detours.emplace_back(); + + detour.fixup_name = 0; + detour.replace_namespace = detours->name_space; + detour.replace_function = detours->name; + detour.fixup_offset = detours->location; + detour.fixup_size = detours->size; + detour.target_script = detours->script; + + logger::write(logger::LOG_TYPE_DEBUG, std::format( + "read detour : namespace_{:x}::function_{:x} / offset=0x{:x}+0x{:x}", + detour.replace_namespace, detour.target_script, detour.replace_function, + detour.fixup_offset, detour.fixup_size + )); + } + } + + if (gsc_funcs::enable_dev_blocks) + { + if (dbg.has_feature(game::acts_debug::ADF_DEVBLOCK_BEGIN)) + { + if (dbg.devblock_count) + { + gsic.dev_blocks = true; + + // we need to patch the dev function imports to load them + + game::GSC_OBJ* prime_obj = (game::GSC_OBJ*)data.data(); + + game::GSC_IMPORT_ITEM* import_item = prime_obj->get_imports(); + + for (size_t i = 0; i < prime_obj->imports_count; i++) + { + // mark this function for the custom linker + if ((import_item->flags & game::GIF_DEV_CALL) != 0) + { + import_item->flags |= game::GIF_SHIELD_DEV_BLOCK_FUNC; + } + + // goto to the next element after the addresses + uint32_t* addresses = reinterpret_cast(import_item + 1); + import_item = reinterpret_cast(addresses + import_item->num_address); + } + } + for (uint32_t* devblock = dbg.get_devblocks(ptr); devblock != dbg.get_devblocks_end(ptr); devblock++) + { + byte* base = align_ptr(ptr + *devblock); + *(uint16_t*)base = gsc_custom::shield_devblock_opcode; + } + } + } + return true; + } }; struct lua_file { @@ -427,17 +518,23 @@ namespace mods { return false; } - if (tmp.gsic.detours.size()) - { - logger::write(logger::LOG_TYPE_DEBUG, std::format("loaded {} detours", tmp.gsic.detours.size())); - } - if (tmp.data.length() < sizeof(game::GSC_OBJ) || *reinterpret_cast(tmp.data.data()) != gsc_magic) { logger::write(logger::LOG_TYPE_ERROR, std::format("bad scriptparsetree magic in {} for mod {}", spt_path.string(), mod_name)); return false; } + if (!tmp.load_acts_debug()) + { + logger::write(logger::LOG_TYPE_ERROR, std::format("error when reading ACTS DEBUG header of {} for mod {}", spt_path.string(), mod_name)); + return false; + } + + if (tmp.gsic.detours.size()) + { + logger::write(logger::LOG_TYPE_DEBUG, std::format("loaded {} detours", tmp.gsic.detours.size())); + } + // after this point we assume that the GSC file is well formatted game::GSC_OBJ* script_obj = reinterpret_cast(tmp.data.data()); diff --git a/source/proxy-dll/definitions/game.hpp b/source/proxy-dll/definitions/game.hpp index c1e58e4..e2c7723 100644 --- a/source/proxy-dll/definitions/game.hpp +++ b/source/proxy-dll/definitions/game.hpp @@ -491,6 +491,7 @@ namespace game // Cmd WEAK symbol Cbuf_AddText{ 0x143CDE880_g }; WEAK symbol Cbuf_ExecuteBuffer{ 0x143CDEBE0_g }; + WEAK symbol Com_LocalClients_GetPrimary{ 0x142893AF0_g }; WEAK symbol Cmd_AddCommandInternal{ 0x143CDEE80_g }; WEAK symbol Cbuf_AddServerText_f{ 0x143CDE870_g }; @@ -569,6 +570,7 @@ namespace game WEAK symbol Scr_GetFunction{ 0x1433AF840_g }; WEAK symbol CScr_GetMethod{ 0x141F13650_g }; WEAK symbol Scr_GetMethod{ 0x1433AFC20_g }; + WEAK symbol Scr_GetGscExportInfo{ 0x142748550_g }; WEAK symbol ScrVm_Error{ 0x142770330_g }; WEAK symbol scrVarPub{ 0x148307880_g }; diff --git a/source/proxy-dll/definitions/scripting.hpp b/source/proxy-dll/definitions/scripting.hpp index d2f722f..0267c8a 100644 --- a/source/proxy-dll/definitions/scripting.hpp +++ b/source/proxy-dll/definitions/scripting.hpp @@ -34,7 +34,7 @@ namespace game int32_t include_offset; uint16_t string_count; uint16_t exports_count; - int32_t start_data; + int32_t cseg_offset; int32_t string_offset; int16_t imports_count; uint16_t fixup_count; @@ -48,7 +48,7 @@ namespace game int32_t script_size; int32_t requires_implements_offset; int32_t ukn50; - int32_t data_length; + int32_t cseg_size; uint16_t include_count; byte ukn5a; byte requires_implements_count; @@ -283,4 +283,157 @@ namespace game int refCount; uint32_t groupId; }; + + enum GSC_EXPORT_FLAGS : byte + { + GEF_LINKED = 0x01, + GEF_AUTOEXEC = 0x02, + GEF_PRIVATE = 0x04, + GEF_CLASS_MEMBER = 0x08, + GEF_CLASS_DESTRUCTOR = 0x10, + GEF_VE = 0x20, + GEF_EVENT = 0x40, + GEF_CLASS_LINKED = 0x80, + GEF_CLASS_VTABLE = 0x86 + }; + + enum GSC_IMPORT_FLAGS : byte + { + GIF_FUNC_METHOD = 0x1, + GIF_FUNCTION = 0x2, + GIF_FUNCTION_THREAD = 0x3, + GIF_FUNCTION_CHILDTHREAD = 0x4, + GIF_METHOD = 0x5, + GIF_METHOD_THREAD = 0x6, + GIF_METHOD_CHILDTHREAD = 0x7, + GIF_CALLTYPE_MASK = 0xF, + GIF_DEV_CALL = 0x10, + GIF_GET_CALL = 0x20, + + GIF_SHIELD_DEV_BLOCK_FUNC = 0x80, + }; + + namespace acts_debug + { + constexpr uint64_t MAGIC = 0x0d0a42444124; + constexpr byte CURRENT_VERSION = 0x12; + + enum GSC_ACTS_DEBUG_FEATURES : byte + { + ADF_STRING = 0x10, + ADF_DETOUR = 0x11, + ADF_DEVBLOCK_BEGIN = 0x12, + ADF_LAZYLINK = 0x12, + ADF_CRC_LOC = 0x13, + ADF_DEVSTRING = 0x14, + ADF_LINES = 0x15, + ADF_FILES = 0x15, + ADF_FLAGS = 0x15, + }; + + enum GSC_ACTS_DEBUG_FLAGS : uint32_t + { + ADFG_OBFUSCATED = 1 << 0, + ADFG_DEBUG = 1 << 1, + ADFG_CLIENT = 1 << 2, + ADFG_PLATFORM_SHIFT = 3, + ADFG_PLATFORM_MASK = 0xF << ADFG_PLATFORM_SHIFT, + }; + + struct GSC_ACTS_DETOUR + { + uint64_t name_space; + uint64_t name; + uint64_t script; + uint32_t location; + uint32_t size; + }; + + struct GSC_ACTS_LAZYLINK + { + uint64_t name_space; + uint64_t name; + uint64_t script; + uint32_t num_address; + }; + + struct GSC_ACTS_DEVSTRING + { + uint32_t string; + uint32_t num_address; + }; + + struct GSC_ACTS_LINES + { + uint32_t start; + uint32_t end; + size_t lineNum; + }; + + struct GSC_ACTS_FILES + { + uint32_t filename; + size_t lineStart; + size_t lineEnd; + }; + + + struct GSC_ACTS_DEBUG + { + byte magic[sizeof(MAGIC)]; + byte version; + uint32_t flags; + uint64_t actsVersion; + uint32_t strings_offset{}; + uint32_t strings_count{}; + uint32_t detour_offset{}; + uint32_t detour_count{}; + uint32_t devblock_offset{}; + uint32_t devblock_count{}; + uint32_t lazylink_offset{}; + uint32_t lazylink_count{}; + uint32_t crc_offset{}; + uint32_t devstrings_offset{}; + uint32_t devstrings_count{}; + uint32_t lines_offset{}; + uint32_t lines_count{}; + uint32_t files_offset{}; + uint32_t files_count{}; + + constexpr bool has_feature(GSC_ACTS_DEBUG_FEATURES feature) const + { + return version >= feature; + } + + inline GSC_ACTS_DETOUR* get_detours(byte* start) + { + return reinterpret_cast(start + detour_offset); + } + + inline GSC_ACTS_DETOUR* get_detours_end(byte* start) + { + return get_detours(start) + detour_count; + } + + inline uint32_t* get_devblocks(byte* start) + { + return reinterpret_cast(start + devblock_offset); + } + + inline uint32_t* get_devblocks_end(byte* start) + { + return get_devblocks(start) + devblock_count; + } + + inline uint32_t* get_strings(byte* start) + { + return reinterpret_cast(start + strings_offset); + } + + inline uint32_t* get_strings_end(byte* start) + { + return get_strings(start) + strings_count; + } + }; + } } \ No newline at end of file