From 699cf5401031c2050ceef6158911e369972c4e81 Mon Sep 17 00:00:00 2001 From: quaK Date: Wed, 30 Apr 2025 12:48:01 +0300 Subject: [PATCH 01/13] use different hook for some detours --- src/client/component/fov.cpp | 7 +++---- src/client/component/gameplay.cpp | 6 ++---- src/client/component/intro.cpp | 7 +++---- src/client/component/network.cpp | 5 ++--- src/client/component/party.cpp | 12 ++---------- src/client/component/patches.cpp | 8 +++----- src/client/component/profile_infos.cpp | 11 ----------- src/client/component/renderer.cpp | 7 +++---- src/client/component/scheduler.cpp | 21 +++++++++------------ 9 files changed, 27 insertions(+), 57 deletions(-) diff --git a/src/client/component/fov.cpp b/src/client/component/fov.cpp index ca28020a..29385c14 100644 --- a/src/client/component/fov.cpp +++ b/src/client/component/fov.cpp @@ -18,8 +18,6 @@ namespace fov game::dvar_t* cg_fovScale; game::dvar_t* cg_use_fov_comp; - utils::hook::detour cg_view_calc_fov_compensation_hook; - float cg_view_calc_fov_compensation_stub(game::cg_s* blob) { if (!cg_use_fov_comp->current.enabled) @@ -27,7 +25,7 @@ namespace fov return 0.0f; } - return cg_view_calc_fov_compensation_hook.invoke(blob); + return utils::hook::invoke(0x140889B60, blob); } } @@ -63,7 +61,8 @@ namespace fov // disable FOV compensation by default cg_use_fov_comp = game::Dvar_RegisterBool("cg_use_fov_comp", false, game::DVAR_FLAG_SAVED, "Use FOV offset compensation for the viewmodel"); - cg_view_calc_fov_compensation_hook.create(0x140889B60, cg_view_calc_fov_compensation_stub); + utils::hook::call(0x140186FC4, cg_view_calc_fov_compensation_stub); + utils::hook::call(0x1408D5940, cg_view_calc_fov_compensation_stub); // patch max fov values utils::hook::inject(0x14087E08B + 4, &max_fov); diff --git a/src/client/component/gameplay.cpp b/src/client/component/gameplay.cpp index 022554e9..d6f12116 100644 --- a/src/client/component/gameplay.cpp +++ b/src/client/component/gameplay.cpp @@ -16,13 +16,11 @@ namespace gameplay { namespace { - utils::hook::detour stuck_in_client_hook; - void stuck_in_client_stub(void* entity) { if (dvars::bg_playerEjection->current.enabled) { - stuck_in_client_hook.invoke(entity); + utils::hook::invoke(0x140AFD9B0, entity); } } @@ -144,7 +142,7 @@ namespace gameplay { // Implement ejection dvar dvars::bg_playerEjection = game::Dvar_RegisterBool("bg_playerEjection", true, game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off"); - stuck_in_client_hook.create(0x140AFD9B0, stuck_in_client_stub); + utils::hook::call(0x140AFA739, stuck_in_client_stub); // Implement bounces dvar dvars::bg_bounces = game::Dvar_RegisterBool("bg_bounces", false, game::DVAR_FLAG_REPLICATED, "Enables bounces"); diff --git a/src/client/component/intro.cpp b/src/client/component/intro.cpp index 38866bfe..afcb552f 100644 --- a/src/client/component/intro.cpp +++ b/src/client/component/intro.cpp @@ -9,8 +9,6 @@ namespace intro { namespace { - utils::hook::detour cinematic_start_playback_hook; - void cinematic_start_playback(const char* name, const int playbackFlags, const int startOffsetMsec, const bool fillerBink, const int pauseState) { @@ -26,7 +24,7 @@ namespace intro } } - cinematic_start_playback_hook.invoke(name, playbackFlags, startOffsetMsec, fillerBink, pauseState); + utils::hook::invoke(0x140DD6A10, name, playbackFlags, startOffsetMsec, fillerBink, pauseState); } } @@ -35,7 +33,8 @@ namespace intro public: void post_unpack() override { - cinematic_start_playback_hook.create(0x140DD6A10, cinematic_start_playback); + utils::hook::call(0x140DD69FF, cinematic_start_playback); + utils::hook::call(0x140DD69CF, cinematic_start_playback); } }; } diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 36974bf3..cea11644 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -58,7 +58,6 @@ namespace network namespace { - utils::hook::detour cl_dispatch_connectionless_packet_hook; bool cl_dispatch_connectionless_packet_stub(int client_num, game::netadr_s* from, game::msg_t* msg, int time) { if (handle_command(from, game::Cmd_Argv(0), msg)) @@ -66,7 +65,7 @@ namespace network return true; } - return cl_dispatch_connectionless_packet_hook.invoke(client_num, from, msg, time); + return utils::hook::invoke(0x1409B2250, client_num, from, msg, time); } int dw_send_to_stub(const int length, const char* data, game::netadr_s* to) @@ -310,7 +309,7 @@ namespace network utils::hook::jump(0x140D93D70, dw_recv_from_stub); // intercept command handling - cl_dispatch_connectionless_packet_hook.create(0x1409B2250, cl_dispatch_connectionless_packet_stub); + utils::hook::call(0x1403572B7, cl_dispatch_connectionless_packet_stub); // handle xuid without secure connection utils::hook::nop(0x140C53315, 2); diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index f032acc9..1ea0d9b6 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -483,13 +483,6 @@ namespace party utils::hook::invoke(0x140341430, 0, cmd); // CL_Main_AddReliableCommand } - utils::hook::detour cl_prepare_for_map_restart_hook; - void cl_prepare_for_map_restart_stub(const char* mapname, const char* gametype) - { - cl_prepare_for_map_restart_hook.invoke(mapname, gametype); - } - - utils::hook::detour cl_initialize_gamestate_hook; void cl_initialize_gamestate_stub(int local_client_num) { if (!game::Com_IsAnyLocalServerRunning()) @@ -497,7 +490,7 @@ namespace party send_user_info(); } - cl_initialize_gamestate_hook.invoke(local_client_num); + utils::hook::invoke(0x1409B2FA0, local_client_num); } } @@ -718,8 +711,7 @@ namespace party // enable custom kick reason in GScr_KickPlayer utils::hook::set(0x140B5377E, 0xEB); - cl_prepare_for_map_restart_hook.create(0x1409B3FE0, cl_prepare_for_map_restart_stub); - cl_initialize_gamestate_hook.create(0x1409B2FA0, cl_initialize_gamestate_stub); + utils::hook::call(0x1409B70A1, cl_initialize_gamestate_stub); command::add("senduserinfo", []() { diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 029d97a4..d0459330 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -17,10 +17,8 @@ namespace patches { utils::hook::detour com_register_common_dvars_hook; utils::hook::detour com_game_mode_supports_feature_hook; - utils::hook::detour cg_set_client_dvar_from_server_hook; utils::hook::detour live_get_map_index_hook; utils::hook::detour content_do_we_have_content_pack_hook; - utils::hook::detour init_network_dvars_hook; std::string get_login_username() { @@ -168,7 +166,7 @@ namespace patches if (result) { std::string index_str = std::to_string(index); - return cg_set_client_dvar_from_server_hook.invoke(client_num, cgame_glob, index_str.data(), value); + return utils::hook::invoke(0x140856D70, client_num, cgame_glob, index_str.data(), value); } } @@ -252,7 +250,7 @@ namespace patches content_do_we_have_content_pack_hook.create(0x140CE8550, content_do_we_have_content_pack_stub); // make setclientdvar behave like older games - cg_set_client_dvar_from_server_hook.create(0x140856D70, cg_set_client_dvar_from_server_stub); + utils::hook::call(0x14084A136, cg_set_client_dvar_from_server_stub); utils::hook::call(0x140B0A9BB, get_client_dvar_checksum); // setclientdvar utils::hook::call(0x140B0ACD7, get_client_dvar_checksum); // setclientdvars utils::hook::call(0x140B0A984, get_client_dvar); // setclientdvar @@ -266,7 +264,7 @@ namespace patches utils::hook::call(0x140B7CF11, cbuf_execute_buffer_internal_stub); // don't register every replicated dvar as a network dvar - init_network_dvars_hook.create(0x140B7A920, init_network_dvars_stub); + utils::hook::jump(0x140B7A920, init_network_dvars_stub); // some [data validation] anti tamper thing that kills performance dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ); diff --git a/src/client/component/profile_infos.cpp b/src/client/component/profile_infos.cpp index 2dd1cb7d..2071a7f3 100644 --- a/src/client/component/profile_infos.cpp +++ b/src/client/component/profile_infos.cpp @@ -207,22 +207,11 @@ namespace profile_infos }); } - namespace - { - utils::hook::detour session_unregister_remote_player_hook; - void session_unregister_remote_player_stub(game::SessionData* session, const int slot) - { - session_unregister_remote_player_hook.invoke(session, slot); - } - } - class component final : public component_interface { public: void post_unpack() override { - session_unregister_remote_player_hook.create(0x140C73970, session_unregister_remote_player_stub); - //dvars::override::register_int("playercard_cache_validity_life", 5000, 0, 3600000, 0x0); // 5sec network::on("profileInfo", [](const game::netadr_s& client_addr, const std::string_view& data) diff --git a/src/client/component/renderer.cpp b/src/client/component/renderer.cpp index 0d41660e..59785550 100644 --- a/src/client/component/renderer.cpp +++ b/src/client/component/renderer.cpp @@ -13,7 +13,6 @@ namespace renderer namespace { utils::hook::detour r_init_draw_method_hook; - utils::hook::detour r_update_front_end_dvar_options_hook; int get_fullbright_technique() { @@ -49,7 +48,7 @@ namespace renderer gfxdrawmethod(); } - return r_update_front_end_dvar_options_hook.invoke(); + return utils::hook::invoke(0x140E28B60); } } @@ -65,8 +64,8 @@ namespace renderer dvars::r_fullbright = game::Dvar_RegisterInt("r_fullbright", 0, 0, 2, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting"); - r_init_draw_method_hook.create(0x140DE9260, &r_init_draw_method_stub); - r_update_front_end_dvar_options_hook.create(0x140E28B60, &r_update_front_end_dvar_options_stub); + r_init_draw_method_hook.create(0x140DE9260, r_init_draw_method_stub); + utils::hook::call(0x140E264B3, r_update_front_end_dvar_options_stub); } }; } diff --git a/src/client/component/scheduler.cpp b/src/client/component/scheduler.cpp index c5f19bce..d235aeee 100644 --- a/src/client/component/scheduler.cpp +++ b/src/client/component/scheduler.cpp @@ -86,10 +86,6 @@ namespace scheduler volatile bool kill = false; std::thread thread; task_pipeline pipelines[pipeline::count]; - utils::hook::detour r_end_frame_hook; - utils::hook::detour g_run_frame_hook; - utils::hook::detour main_frame_hook; - utils::hook::detour hks_frame_hook; void execute(const pipeline type) { @@ -100,12 +96,12 @@ namespace scheduler void r_end_frame_stub() { execute(pipeline::renderer); - r_end_frame_hook.invoke(); + utils::hook::invoke(0x140E267B0); } void server_frame_stub() { - g_run_frame_hook.invoke(); + utils::hook::invoke(0x140B15E20); execute(pipeline::server); } @@ -116,7 +112,7 @@ namespace scheduler execute(pipeline::main); }); - return main_frame_hook.invoke(); + return utils::hook::invoke(0x140B8E2D0); } void hks_frame_stub() @@ -126,7 +122,7 @@ namespace scheduler { execute(pipeline::lui); } - hks_frame_hook.invoke(); + utils::hook::invoke(0x140613440); } } @@ -196,10 +192,11 @@ namespace scheduler void post_unpack() override { - r_end_frame_hook.create(0x140E267B0, scheduler::r_end_frame_stub); - g_run_frame_hook.create(0x140B15E20, scheduler::server_frame_stub); - main_frame_hook.create(0x140B8E2D0, scheduler::main_frame_stub); - hks_frame_hook.create(0x140613440, scheduler::hks_frame_stub); + utils::hook::call(0x14034728C, scheduler::r_end_frame_stub); + utils::hook::call(0x14048A7B9, scheduler::server_frame_stub); // sp call + utils::hook::call(0x140B21941, scheduler::server_frame_stub); // mp call + utils::hook::call(0x140B8E272, scheduler::main_frame_stub); + utils::hook::call(0x1406173C2, scheduler::hks_frame_stub); } void pre_destroy() override From a75c616e689df9c6eb948871b8da6857b9b673c8 Mon Sep 17 00:00:00 2001 From: quaK Date: Wed, 30 Apr 2025 12:49:15 +0300 Subject: [PATCH 02/13] dvars refactor --- src/client/component/dvars.cpp | 417 ++++++++++++++++++++----------- src/client/component/patches.cpp | 10 +- src/client/game/structs.hpp | 23 ++ 3 files changed, 307 insertions(+), 143 deletions(-) diff --git a/src/client/component/dvars.cpp b/src/client/component/dvars.cpp index dcddec74..0f2a5e2d 100644 --- a/src/client/component/dvars.cpp +++ b/src/client/component/dvars.cpp @@ -8,54 +8,195 @@ #include #include +#include // Include this header for intrinsic functions +#define __ROR4__(value, shift) _rotr(value, shift) // Define __ROR4__ using _rotr intrinsic +#define __ROL4__(value, shift) _rotl(value, shift) // Define __ROL4__ using _rotl intrinsic + namespace dvars { - struct dvar_base + namespace crypt { + void hash_boolean(game::DvarValue* value) // Unused + { + int hashed = value->boolean_.enabled; // this is always 0 in the actual game + int temp = 0; + unsigned int key = 0x89E1A665; + int xorKey = 0x87C6B47F; + + while (key != 0xE9CC2CC0) + { + switch (key) + { + case 0x89E1A665: + temp = static_cast(hashed); + xorKey = 0x008B237C; + break; + case 0x1C9C36AA: + temp = static_cast(hashed) ^ 0x7434; + xorKey = 0xAB74F2A4; + hashed = __ROR4__(hashed, 16); + break; + case 0x6006EB8D: + temp = static_cast(hashed) ^ 0x9133; + xorKey = 0xA6F5E979; + hashed = temp ^ __ROL4__(hashed, 16); + break; + case 0x896A8519: + temp ^= 0x52D0; + xorKey = 0x6B8E2B15; + hashed = __ROL4__(hashed, 16); + break; + case 0xC6F302F4: + xorKey = 0x2F3F2E34; + hashed = __ROR4__(hashed, 16); + break; + case 0xE2E4AE0C: + hashed ^= temp; + xorKey = 0x82E24581; + break; + case 0xF6B9AD6A: + temp ^= 0xCA71; + xorKey = 0x52DB7D55; + break; + } + key ^= xorKey; + } + + value->boolean_.hashedValue = hashed; + } + + void hash_integer(game::DvarValue* value) + { + int hashed = value->integer_.integer; + int temp = 0; + int xorKey = 0x87C6B47F; + unsigned int key = 0x89E1A665; + + while (key != 0xE9CC2CC0) + { + switch (key) + { + case 0x89E1A665: + temp = static_cast(hashed); + xorKey = 0x008B237C; + break; + case 0x1C9C36AA: + temp = static_cast(hashed) ^ 0x7434; + xorKey = 0xAB74F2A4; + hashed = __ROR4__(hashed, 16); + break; + case 0x6006EB8D: + temp = static_cast(hashed) ^ 0x9133; + xorKey = 0xA6F5E979; + hashed = temp ^ __ROL4__(hashed, 16); + break; + case 0x896A8519: + temp ^= 0x52D0; + xorKey = 0x6B8E2B15; + hashed = __ROL4__(hashed, 16); + break; + case 0xC6F302F4: + xorKey = 0x2F3F2E34; + hashed = __ROR4__(hashed, 16); + break; + case 0xE2E4AE0C: + hashed ^= temp; + xorKey = 0x82E24581; + break; + case 0xF6B9AD6A: + temp ^= 0xCA71; + xorKey = 0x52DB7D55; + break; + } + key ^= xorKey; + } + + value->integer_.hashedValue = hashed; + } + + void hash_enum(game::DvarValue* value) + { + hash_integer(value); + } + + void hash(game::DvarType type, game::DvarValue* value) + { + switch (type) + { + case game::DvarType::boolean: + hash_boolean(value); + break; + case game::DvarType::integer: + hash_integer(value); + break; + case game::DvarType::enumeration: + hash_enum(value); + break; + default: + break; + } + } + + int unhash_integer(game::dvar_t* dvar) + { + int original = dvar->current.integer_.integer; + if (original == 0) + return 0; + + int result = dvar->current.integer_.hashedValue; + int temp = 0; + int xorKey = 0x040ABA5A; + unsigned int key = 0x19B62CC6; + + while (key != 0x9E5E5A15) + { + switch (key) { + case 0x19B62CC6: + temp = static_cast(result); + xorKey = 0xC1BCD395; + break; + case 0x4AD465E7: + temp ^= 0xCA71; + xorKey = 0x05CA2DF8; + break; + case 0x8F9CFC5: + xorKey = 0x0A2F246E; + result = __ROL4__(temp ^ result, 16); + break; + case 0x6B2A7033: + temp ^= 0x52D0; + xorKey = 0xAF9030E3; + break; + case 0xC4BA40D0: + result = __ROR4__(result, 16); + xorKey = 0xCC438F15; + break; + case 0xD80AFF53: + result = temp ^ __ROR4__(result, 16) ^ 0x9133; + temp = static_cast(result); + xorKey = 0xB3208F60; + break; + case 0xEF93D298: + temp = static_cast(result) ^ 0x7434; + xorKey = 0x09C60C3F; + break; + } + key ^= xorKey; + } + + // if (result != original) ExitProcess(0); + return result; + } + } + + struct dvar_combined + { + std::string string{}; + game::DvarValue value{}; + game::DvarLimits limits{}; unsigned int flags{}; }; - struct dvar_bool : dvar_base - { - bool value{}; - }; - - struct dvar_float : dvar_base - { - float value{}; - float min{}; - float max{}; - }; - - struct dvar_vector2 : dvar_base - { - float x{}; - float y{}; - float min{}; - float max{}; - }; - - struct dvar_vector3 : dvar_base - { - float x{}; - float y{}; - float z{}; - float min{}; - float max{}; - }; - - struct dvar_int : dvar_base - { - int value{}; - int min{}; - int max{}; - }; - - struct dvar_string : dvar_base - { - std::string value{}; - }; - namespace { template @@ -94,75 +235,70 @@ namespace dvars namespace override { - static std::unordered_map register_bool_overrides; - static std::unordered_map register_float_overrides; - static std::unordered_map register_int_overrides; - static std::unordered_map register_string_overrides; - static std::unordered_map register_vector2_overrides; - static std::unordered_map register_vector3_overrides; + static std::unordered_map> register_overrides; void register_bool(const std::string& name, const bool value, const unsigned int flags) { - dvar_bool values; - values.value = value; + dvar_combined values; + values.value.enabled = value; values.flags = flags; - register_bool_overrides[name] = std::move(values); + register_overrides[game::DvarType::boolean][name] = std::move(values); } void register_float(const std::string& name, const float value, const float min, const float max, const unsigned int flags) { - dvar_float values; - values.value = value; - values.min = min; - values.max = max; + dvar_combined values; + values.value.value = value; + values.limits.value.min = min; + values.limits.value.max = max; values.flags = flags; - register_float_overrides[name] = std::move(values); + register_overrides[game::DvarType::value][name] = std::move(values); } void register_int(const std::string& name, const int value, const int min, const int max, const unsigned int flags) { - dvar_int values; - values.value = value; - values.min = min; - values.max = max; + dvar_combined values; + values.value.integer = value; + values.limits.integer.min = min; + values.limits.integer.max = max; values.flags = flags; - register_int_overrides[name] = std::move(values); + register_overrides[game::DvarType::integer][name] = std::move(values); } void register_string(const std::string& name, const std::string& value, const unsigned int flags) { - dvar_string values; - values.value = value; + dvar_combined values; + values.string = value; values.flags = flags; - register_string_overrides[name] = std::move(values); + register_overrides[game::DvarType::string][name] = std::move(values); } void register_vec2(const std::string& name, float x, float y, float min, float max, const unsigned int flags) { - dvar_vector2 values; - values.x = x; - values.y = y; - values.min = min; - values.max = max; + dvar_combined values; + values.value.vector[0] = x; + values.value.vector[1] = y; + values.limits.vector.min = min; + values.limits.vector.max = max; values.flags = flags; - register_vector2_overrides[name] = std::move(values); + register_overrides[game::DvarType::vec2][name] = std::move(values); } void register_vec3(const std::string& name, float x, float y, float z, float min, float max, const unsigned int flags) { - dvar_vector3 values; - values.x = x; - values.y = y; - values.z = z; - values.min = min; - values.max = max; + dvar_combined values; + values.value.vector[0] = x; + values.value.vector[1] = y; + values.value.vector[2] = z; + values.limits.vector.min = min; + values.limits.vector.max = max; values.flags = flags; - register_vector3_overrides[name] = std::move(values); + register_overrides[game::DvarType::vec3][name] = std::move(values); } } @@ -188,99 +324,107 @@ namespace dvars } } - utils::hook::detour dvar_register_bool_hook; - utils::hook::detour dvar_register_float_hook; - utils::hook::detour dvar_register_int_hook; - utils::hook::detour dvar_register_string_hook; - utils::hook::detour dvar_register_vector2_hook; - utils::hook::detour dvar_register_vector3_hook; - - game::dvar_t* dvar_register_bool(const char* name, bool value, unsigned int flags, const char* description) + void inline dvar_register_bool(const char* name, bool& value, unsigned int& flags, const char* description) { - auto* var = find_dvar(override::register_bool_overrides, name); + auto* var = find_dvar(override::register_overrides[game::DvarType::boolean], name); if (var) { - value = var->value; + value = var->value.enabled; flags = var->flags; } - - return dvar_register_bool_hook.invoke(name, value, flags, description); } - game::dvar_t* dvar_register_float(const char* name, float value, float min, float max, unsigned int flags, const char* description) + void inline dvar_register_float(const char* name, float& value, float& min, float& max, unsigned int flags, const char* description) { - auto* var = find_dvar(override::register_float_overrides, name); + auto* var = find_dvar(override::register_overrides[game::DvarType::value], name); if (var) { - value = var->value; - min = var->min; - max = var->max; + value = var->value.value; + min = var->limits.value.min; + max = var->limits.value.max; flags = var->flags; } - - return dvar_register_float_hook.invoke(name, value, min, max, flags, description); } - game::dvar_t* dvar_register_int(const char* name, int value, int min, int max, unsigned int flags, const char* description) + void inline dvar_register_int(const char* name, int& value, int& min, int& max, unsigned int& flags, const char* description) { - auto* var = find_dvar(override::register_int_overrides, name); + auto* var = find_dvar(override::register_overrides[game::DvarType::integer], name); if (var) { - value = var->value; - min = var->min; - max = var->max; + value = var->value.integer; + min = var->limits.integer.min; + max = var->limits.integer.max; flags = var->flags; } - - return dvar_register_int_hook.invoke(name, value, min, max, flags, description); } - game::dvar_t* dvar_register_string(const char* name, const char* value, unsigned int flags, const char* description) + void inline dvar_register_string(const char* name, const char* value, unsigned int& flags, const char* description) { - auto* var = find_dvar(override::register_string_overrides, name); + auto* var = find_dvar(override::register_overrides[game::DvarType::string], name); if (var) { - value = var->value.data(); + value = var->string.data(); flags = var->flags; } - - return dvar_register_string_hook.invoke(name, value, flags, description); } - game::dvar_t* dvar_register_vector2(const char* name, float x, float y, float min, float max, - unsigned int flags, const char* description) + void inline dvar_register_vector2(const char* name, float& x, float& y, float& min, float& max, + unsigned int& flags, const char* description) { - auto* var = find_dvar(override::register_vector2_overrides, name); + auto* var = find_dvar(override::register_overrides[game::DvarType::vec2], name); if (var) { - x = var->x; - y = var->y; - min = var->min; - max = var->max; + x = var->value.vector[0]; + y = var->value.vector[1]; + min = var->limits.vector.min; + max = var->limits.vector.max; flags = var->flags; } - - return dvar_register_vector2_hook.invoke(name, x, y, min, max, flags, description); } - game::dvar_t* dvar_register_vector3(const char* name, float x, float y, float z, float min, - float max, unsigned int flags, const char* description) + void inline dvar_register_vector3(const char* name, float& x, float& y, float& z, float& min, + float& max, unsigned int& flags, const char* description) { - auto* var = find_dvar(override::register_vector3_overrides, name); + auto* var = find_dvar(override::register_overrides[game::DvarType::vec3], name); if (var) { - x = var->x; - y = var->y; - z = var->z; - min = var->min; - max = var->max; + x = var->value.vector[0]; + y = var->value.vector[1]; + z = var->value.vector[2]; + min = var->limits.vector.min; + max = var->limits.vector.max; flags = var->flags; } - - return dvar_register_vector3_hook.invoke(name, x, y, z, min, max, flags, description); } - std::recursive_mutex _mutex; + void inline check_dvar_overrides(const char* name, game::DvarType& type, unsigned int& flags, game::DvarValue* value, game::DvarLimits* domain, const char* description) + { + switch (type) + { + case game::DvarType::boolean: + dvar_register_bool(name, value->enabled, flags, description); + break; + case game::DvarType::value: + dvar_register_float(name, value->value, domain->value.min, domain->value.max, flags, description); + break; + case game::DvarType::integer: + dvar_register_int(name, value->integer, domain->integer.min, domain->integer.max, flags, description); + break; + case game::DvarType::string: + dvar_register_string(name, value->string, flags, description); + break; + case game::DvarType::vec2: + dvar_register_vector2(name, value->vector[0], value->vector[1], domain->vector.min, domain->vector.max, flags, description); + break; + case game::DvarType::vec3: + dvar_register_vector3(name, value->vector[0], value->vector[1], value->vector[2], domain->vector.min, domain->vector.max, flags, description); + break; + default: + break; + } + + dvars::crypt::hash(type, value); + } utils::hook::detour dvar_register_new_hook; utils::hook::detour dvar_re_register_hook; @@ -290,7 +434,6 @@ namespace dvars game::dvar_t* dvar_register_new(const char* name, unsigned int checksum, game::DvarType type, unsigned int flags, game::DvarValue* value, game::DvarLimits* domain, char level, const char* description) { - std::lock_guard $(_mutex); auto* dvar = dvar_register_new_hook.invoke(name, checksum, type, flags, value, domain, level, description); if (dvar && name) @@ -305,11 +448,9 @@ namespace dvars return dvar; } - void dvar_re_register(game::dvar_t* dvar, const char* name, game::DvarType type, unsigned int flags, + inline void dvar_re_register(game::dvar_t* dvar, const char* name, game::DvarType type, unsigned int flags, game::DvarValue* resetValue, game::DvarLimits* domain, char level, const char* description) { - std::lock_guard $(_mutex); - if (dvar && name) { const auto disabled = find_dvar(disable::re_register_disables, name); @@ -330,8 +471,6 @@ namespace dvars game::dvar_t* dvar_de_register(game::dvar_t* dvar) { - std::lock_guard $(_mutex); - auto name = dvars::dvar_get_name(dvar); if (dvar && !name.empty()) { @@ -354,7 +493,8 @@ namespace dvars game::dvar_t* dvar_register_variant(const char* name, unsigned int checksum, game::DvarType type, unsigned int flags, game::DvarValue* value, game::DvarLimits* domain, const char* description) { - std::lock_guard $(_mutex); + check_dvar_overrides(name, type, flags, value, domain, description); + auto* dvar = dvar_register_variant_hook.invoke(name, checksum, type, flags, value, domain, description); if (dvar) @@ -377,13 +517,6 @@ namespace dvars public: void post_unpack() override { - dvar_register_bool_hook.create(0x140CEB380, &dvar_register_bool); - dvar_register_float_hook.create(0x140CEB890, &dvar_register_float); - dvar_register_int_hook.create(0x140CEB920, &dvar_register_int); - dvar_register_string_hook.create(0x140CEBD50, &dvar_register_string); - dvar_register_vector2_hook.create(0x140CEBF50, &dvar_register_vector2); - dvar_register_vector3_hook.create(0x140CEBFE0, &dvar_register_vector3); - dvar_register_new_hook.create(0x140CEBA60, dvar_register_new); dvar_re_register_hook.create(0x140CEC210, dvar_re_register); dvar_de_register_hook.create(0x140CE9F30, dvar_de_register); diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index d0459330..1f82ba41 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -267,7 +267,15 @@ namespace patches utils::hook::jump(0x140B7A920, init_network_dvars_stub); // some [data validation] anti tamper thing that kills performance - dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ); + dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_NONE); + dvars::override::register_int("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE); + utils::hook::set(0x1405C9FA0, 0xC3); // ValidateData + utils::hook::set(0x1405C9300, 0xC3); // ValidateMetaData + utils::hook::set(0x1405C9D70, 0xC3); // UpdateValidationDataInternal + utils::hook::set(0x1405C9590, 0xC3); // RegisterValidationData + utils::hook::set(0x1405C9960, 0xC3); // ShutdownValidationData + utils::hook::set(0x1405C8EC0, 0xC3); // FreeValidationData + utils::hook::set(0x1405C90C0, 0xC3); // killswitches dvars::override::register_bool("mission_team_contracts_enabled", true, game::DVAR_FLAG_READ); diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 4e331f9c..bce9a37b 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -122,6 +122,25 @@ namespace game rgb = 9, // Color without alpha }; + struct DvarValueBool + { + bool enabled; + char pad[3]; + int hashedValue; + }; + + struct DvarValueInt + { + int integer; + int hashedValue; + }; + + struct DvarValueEnum + { + int defaultIndex; + int hashedValue; + }; + union DvarValue { bool enabled; @@ -131,6 +150,10 @@ namespace game float vector[4]; const char* string; char color[4]; + + DvarValueBool boolean_; + DvarValueInt integer_; + DvarValueEnum enumeration_; }; struct $A37BA207B3DDD6345C554D4661813EDD From 157ed4db6ebb0cc9b1c4ebfe22cd52ba271fdffd Mon Sep 17 00:00:00 2001 From: quaK Date: Thu, 1 May 2025 15:00:20 +0300 Subject: [PATCH 03/13] fix stack overflow --- src/client/component/network.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index cea11644..3ef7b452 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -68,13 +68,6 @@ namespace network return utils::hook::invoke(0x1409B2250, client_num, from, msg, time); } - int dw_send_to_stub(const int length, const char* data, game::netadr_s* to) - { - sockaddr s = {}; - game::NetadrToSockadr(to, &s); - return sendto(*game::query_socket, data, length, 0, &s, sizeof(sockaddr)); - } - void sockadr_to_netadr(const sockaddr* s, game::netadr_s* a) { if (s->sa_family == 2) @@ -85,6 +78,18 @@ namespace network } } + int dw_send_to_stub(const int length, const char* data, game::netadr_s* to) + { + sockaddr s = {}; + game::NetadrToSockadr(to, &s); + const auto result = sendto(*game::query_socket, data, length, 0, &s, sizeof(sockaddr)); + if (result == SOCKET_ERROR) + { + console::warn("sendto failed: %s\n", std::system_category().message(GetLastError()).data()); + } + return result; + } + int dw_recv_from_stub(game::netadr_s* from, char* data, int maxsize) { sockaddr s = {}; @@ -249,19 +254,14 @@ namespace network void send_data(const game::netadr_s& address, const std::string& data) { auto size = static_cast(data.size()); + if (address.type == game::NA_LOOPBACK) { - if (size > 1280) - { - console::error("Packet was too long. Truncated!\n"); - size = 1280; - } - game::NET_SendLoopPacket(game::NS_CLIENT1, size, data.data(), &address); } - else + else if(address.type == game::NA_BROADCAST || address.type == game::NA_IP) { - game::Sys_SendPacket(size, data.data(), &address); + dw_send_to_stub(size, data.data(), const_cast(&address)); } } From 98775cfa7f71ec3ce7d5ece8c30915cc1236d841 Mon Sep 17 00:00:00 2001 From: quaK Date: Fri, 2 May 2025 20:38:18 +0300 Subject: [PATCH 04/13] make detour faster --- src/client/component/arxan/arxan.cpp | 15 +++++++++++---- src/client/component/fastfiles.cpp | 9 ++++----- src/client/main.cpp | 3 +++ src/common/utils/hook.cpp | 24 +++++++++++++++++------- src/common/utils/hook.hpp | 3 +++ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/client/component/arxan/arxan.cpp b/src/client/component/arxan/arxan.cpp index c185dd9a..58034cdc 100644 --- a/src/client/component/arxan/arxan.cpp +++ b/src/client/component/arxan/arxan.cpp @@ -9,14 +9,20 @@ #include #include -#include "integrity.hpp" -#include "breakpoints.hpp" -#include "illegal_instructions.hpp" - #define PRECOMPUTED_INTEGRITY_CHECKS #define PRECOMPUTED_BREAKPOINTS #define PRECOMPUTED_ILLEGAL_INSTRUCTIONS +#ifdef PRECOMPUTED_INTEGRITY_CHECKS +#include "integrity.hpp" +#endif +#ifdef PRECOMPUTED_BREAKPOINTS +#include "breakpoints.hpp" +#endif +#ifdef PRECOMPUTED_ILLEGAL_INSTRUCTIONS +#include "illegal_instructions.hpp" +#endif + #define ProcessDebugPort 7 #define ProcessDebugObjectHandle 30 #define ProcessDebugFlags 31 @@ -674,6 +680,7 @@ namespace arxan const auto nt_query_information_process = ntdll.get_proc("NtQueryInformationProcess"); nt_query_information_process_hook.create(nt_query_information_process, nt_query_information_process_stub); + nt_query_information_process_hook.enable(); nt_query_information_process_hook.move(); AddVectoredExceptionHandler(1, exception_filter); diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index b2441b99..88414480 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -113,6 +113,8 @@ namespace fastfiles HANDLE sys_create_file_stub(game::Sys_Folder folder, const char* base_filename) { + auto result = sys_createfile_hook.invoke(folder, base_filename); + const auto create_file_a = [](const std::string& filepath) { return CreateFileA(filepath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, @@ -138,12 +140,9 @@ namespace fastfiles return INVALID_HANDLE_VALUE; } - if (auto result = sys_createfile_hook.invoke(folder, base_filename)) + if (result != INVALID_HANDLE_VALUE) { - if (result != INVALID_HANDLE_VALUE) - { - return result; - } + return result; } std::string real_path{}; diff --git a/src/client/main.cpp b/src/client/main.cpp index 808973ff..849b8333 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -17,6 +17,8 @@ DECLSPEC_NORETURN void WINAPI exit_hook(const int code) DWORD_PTR WINAPI set_thread_affinity_mask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask) { component_loader::post_unpack(); + MH_ApplyQueued(); + return SetThreadAffinityMask(hThread, dwThreadAffinityMask); } @@ -154,6 +156,7 @@ int main() game::base_address = base_address; if (!component_loader::post_load()) return EXIT_FAILURE; + MH_ApplyQueued(); premature_shutdown = false; } diff --git a/src/common/utils/hook.cpp b/src/common/utils/hook.cpp index b58b34d3..614919e4 100644 --- a/src/common/utils/hook.cpp +++ b/src/common/utils/hook.cpp @@ -1,4 +1,4 @@ -#include "hook.hpp" +#include "hook.hpp" #include #include @@ -291,6 +291,16 @@ namespace utils::hook this->clear(); } + void detour::queue_enable() + { + MH_QueueEnableHook(this->place_); + } + + void detour::queue_disable() + { + MH_QueueDisableHook(this->place_); + } + void detour::enable() { MH_EnableHook(this->place_); @@ -317,7 +327,7 @@ namespace utils::hook throw std::runtime_error(string::va("Unable to create hook at location: %p", this->place_)); } - this->enable(); + this->queue_enable(); } void detour::create(const size_t place, void* target) @@ -540,15 +550,15 @@ namespace utils::hook asm_function(a); - void* result = nullptr; - auto err_result = runtime.add(&result, &code); + void* dst = nullptr; + auto result = runtime.add(&dst, &code); - if (err_result != asmjit::ErrorCode::kErrorOk) + if (result != asmjit::ErrorCode::kErrorOk) { - printf("ASMJIT ERROR: %s\n", asmjit::DebugUtils::errorAsString(err_result)); + throw std::runtime_error(string::va("ASMJIT ERROR: %s\n", asmjit::DebugUtils::errorAsString(result))); } - return result; + return dst; } void inject(size_t pointer, size_t data) diff --git a/src/common/utils/hook.hpp b/src/common/utils/hook.hpp index 6c8ba52b..707eb1c0 100644 --- a/src/common/utils/hook.hpp +++ b/src/common/utils/hook.hpp @@ -122,6 +122,9 @@ namespace utils::hook void enable(); void disable(); + void queue_enable(); + void queue_disable(); + void create(void* place, void* target); void create(size_t place, void* target); void clear(); From 27ae0e1e99e226884d70bb3cdeb499d4fadd2c01 Mon Sep 17 00:00:00 2001 From: quaK Date: Fri, 2 May 2025 21:08:17 +0300 Subject: [PATCH 05/13] Update gsc-tool --- deps/gsc-tool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/gsc-tool b/deps/gsc-tool index c63228d7..62c43d20 160000 --- a/deps/gsc-tool +++ b/deps/gsc-tool @@ -1 +1 @@ -Subproject commit c63228d7dcdeaa84c349fc10037d4ab0da542473 +Subproject commit 62c43d20462e2c0ebc65d3a551de9d0e2e9d0be4 From 5ba6c25c6bccf70902da072675f52ef87cfe136a Mon Sep 17 00:00:00 2001 From: quaK Date: Fri, 2 May 2025 21:09:02 +0300 Subject: [PATCH 06/13] use getguid instead of getxuid --- src/client/component/logprint.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/component/logprint.cpp b/src/client/component/logprint.cpp index 2ac86d66..a53db0dd 100644 --- a/src/client/component/logprint.cpp +++ b/src/client/component/logprint.cpp @@ -75,7 +75,7 @@ namespace logprint int lpselfnum = self_.call("getentitynumber").as(); std::string lpselfname = self_.get("name").as(); std::string lpselfteam = self_.get("team").as(); - std::string lpselfGuid = self_.call("getxuid").as(); + std::string lpselfGuid = self_.call("getguid").as(); int lpattacknum = -1; std::string lpattackname = ""; @@ -90,7 +90,7 @@ namespace logprint lpattacknum = attacker_.call("getentitynumber").as(); lpattackname = attacker_.get("name").as(); lpattackerteam = attacker_.get("team").as(); - lpattackGuid = attacker_.call("getxuid").as(); + lpattackGuid = attacker_.call("getguid").as(); } } @@ -136,7 +136,7 @@ namespace logprint int lpselfnum = self_.call("getentitynumber").as(); std::string lpselfname = self_.get("name").as(); std::string lpselfteam = self_.get("team").as(); - std::string lpselfGuid = self_.call("getxuid").as(); + std::string lpselfGuid = self_.call("getguid").as(); int lpattacknum = -1; std::string lpattackname = ""; @@ -151,7 +151,7 @@ namespace logprint lpattacknum = attacker_.call("getentitynumber").as(); lpattackname = attacker_.get("name").as(); lpattackerteam = attacker_.get("team").as(); - lpattackGuid = attacker_.call("getxuid").as(); + lpattackGuid = attacker_.call("getguid").as(); } } From f772604bb263197d7a679c38733a9a7667cab86f Mon Sep 17 00:00:00 2001 From: quaK Date: Sat, 3 May 2025 17:38:01 +0300 Subject: [PATCH 07/13] AI guid changes --- src/client/component/auth.cpp | 54 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index 5925640d..b94752f3 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -33,20 +33,13 @@ namespace auth std::string get_hw_profile_guid() { - auto hw_profile_path = (utils::properties::get_appdata_path() / "iw7-guid.dat").generic_string(); - if (utils::io::file_exists(hw_profile_path)) - { - utils::io::remove_file(hw_profile_path); - } - - HW_PROFILE_INFO info; + HW_PROFILE_INFO info{}; if (!GetCurrentHwProfileA(&info)) { return {}; } - auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) }; - return hw_profile_info; + return std::string{ info.szHwProfileGuid, std::strlen(info.szHwProfileGuid) }; } std::string get_protected_data() @@ -70,28 +63,29 @@ namespace auth std::string get_key_entropy() { - std::string entropy{}; - entropy.append(utils::smbios::get_uuid()); - entropy.append(get_hw_profile_guid()); - entropy.append(get_protected_data()); - entropy.append(get_hdd_serial()); + std::string raw_entropy; + raw_entropy.append(utils::smbios::get_uuid()); + raw_entropy.append(get_hw_profile_guid()); + raw_entropy.append(get_protected_data()); + raw_entropy.append(get_hdd_serial()); - if (entropy.empty()) + if (raw_entropy.empty()) { - entropy.resize(32); - utils::cryptography::random::get_data(entropy.data(), entropy.size()); + raw_entropy.resize(32); + utils::cryptography::random::get_data(raw_entropy.data(), raw_entropy.size()); } - return entropy; + return utils::cryptography::sha256::compute(raw_entropy); } bool load_key(utils::cryptography::ecc::key& key) { std::string data{}; + const auto key_path = (utils::properties::get_appdata_path() / "iw7-private.key").generic_string(); - auto key_path = (utils::properties::get_appdata_path() / "iw7-private.key").generic_string(); if (!utils::io::read_file(key_path, &data)) { + console::warn("Private key file not found at: %s\n", key_path.data()); return false; } @@ -113,10 +107,10 @@ namespace auth throw std::runtime_error("Failed to generate cryptographic key!"); } - auto key_path = (utils::properties::get_appdata_path() / "iw7-private.key").generic_string(); + const auto key_path = (utils::properties::get_appdata_path() / "iw7-private.key").generic_string(); if (!utils::io::write_file(key_path, key.serialize())) { - console::error("Failed to write cryptographic key!\n"); + console::error("Failed to write cryptographic key to: %s\n", key_path.data()); } console::info("Generated cryptographic key: %llX\n", key.get_hash()); @@ -137,12 +131,12 @@ namespace auth utils::cryptography::ecc::key get_key_internal() { - auto key = load_or_generate_key(); + const auto key = load_or_generate_key(); + const auto key_path = (utils::properties::get_appdata_path() / "iw7-public.key").generic_string(); - auto key_path = (utils::properties::get_appdata_path() / "iw7-public.key").generic_string(); if (!utils::io::write_file(key_path, key.get_public_key())) { - console::error("Failed to write public key!\n"); + console::error("Failed to write public key to: %s\n", key_path.data()); } return key; @@ -150,7 +144,14 @@ namespace auth utils::cryptography::ecc::key& get_key() { - static auto key = get_key_internal(); + static std::once_flag init_flag; + static utils::cryptography::ecc::key key{}; + + std::call_once(init_flag, []() + { + key = get_key_internal(); + }); + return key; } } @@ -159,7 +160,8 @@ namespace auth { if (game::environment::is_dedi()) { - return 0x110000100000000 | (::utils::cryptography::random::get_integer() & ~0x80000000); + static uint64_t session_guid = 0x110000100000000 | (::utils::cryptography::random::get_integer() & ~0x80000000); + return session_guid; } return get_key().get_hash(); From 4d7f90137124cc55b2a855ba46d16a6f2fbb8840 Mon Sep 17 00:00:00 2001 From: quaK Date: Sun, 4 May 2025 01:16:45 +0300 Subject: [PATCH 08/13] Fix session data --- src/client/component/network.cpp | 4 ++-- src/client/component/party.cpp | 31 +++++++++++++++++++------------ src/client/game/symbols.hpp | 5 +++++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 3ef7b452..b34230a5 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -312,8 +312,8 @@ namespace network utils::hook::call(0x1403572B7, cl_dispatch_connectionless_packet_stub); // handle xuid without secure connection - utils::hook::nop(0x140C53315, 2); - utils::hook::nop(0x140C55EC7, 6); + //utils::hook::nop(0x140C53315, 2); + //utils::hook::nop(0x140C55EC7, 6); utils::hook::jump(game::NET_CompareAdr, net_compare_address); utils::hook::jump(game::NET_CompareBaseAdr, net_compare_address); diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 1ea0d9b6..368579d2 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -460,8 +460,6 @@ namespace party memset(&*reinterpret_cast<__int64*>(0x144E3A490 + 8), 0, 0x78680ui64 - 8); } } - - utils::hook::invoke(0x140C55DF0); // SV_InitMP_SetXUIDConfigStrings } void reset_mem_stuff_stub(utils::hook::assembler& a) @@ -478,9 +476,7 @@ namespace party void send_user_info() { - auto userinfostring = utils::hook::invoke(0x1409B2850, 0); // CL_MainMP_GetUserInfoString - auto cmd = utils::string::va("userinfo \"%s\"", userinfostring); - utils::hook::invoke(0x140341430, 0, cmd); // CL_Main_AddReliableCommand + utils::hook::invoke(0x1409B3EB0, 0); // CL_MainMP_SendUserInfoCmd } void cl_initialize_gamestate_stub(int local_client_num) @@ -492,6 +488,23 @@ namespace party utils::hook::invoke(0x1409B2FA0, local_client_num); } + + utils::hook::detour sv_set_player_info_string_hook; + void sv_set_player_info_string_stub(int clientNum, const char* xuidString, const char* xnaddrString, const char* natTypeString, const char* npIdString, const char* partyIpString) + { + uint64_t xuid64{}; + game::StringToXUID(xuidString, &xuid64); + const auto lobby = game::SV_MainMP_GetServerLobby(); + if (game::Session_FindRegisteredUser(lobby, xuid64) == 0xFFFFFFFFui64) + { + const auto client = game::svs_clients[clientNum]; + const auto privateSlot = clientNum < game::SV_GameMP_GetAgentCount(); + game::Session_RegisterRemotePlayer(0, lobby, xuid64, privateSlot, clientNum, std::atoi(natTypeString), const_cast(xnaddrString), &client->remoteAddress); + } + + console::info("Setting client %i XUID to %s\n", clientNum, xuidString); + sv_set_player_info_string_hook.invoke(clientNum, xuidString, xnaddrString, natTypeString, npIdString, partyIpString); + } } void start_map(const std::string& mapname, bool dev) @@ -712,13 +725,7 @@ namespace party utils::hook::set(0x140B5377E, 0xEB); utils::hook::call(0x1409B70A1, cl_initialize_gamestate_stub); - - command::add("senduserinfo", []() - { - auto userinfostring = utils::hook::invoke(0x1409B2850, 0); // CL_MainMP_GetUserInfoString - auto cmd = utils::string::va("userinfo \"%s\"", userinfostring); - utils::hook::invoke(0x140341430, 0, cmd); // CL_Main_AddReliableCommand - }); + sv_set_player_info_string_hook.create(0x140C57360, sv_set_player_info_string_stub); command::add("map", [](const command::params& args) { diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 827d71a1..9e352c5e 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -207,6 +207,10 @@ namespace game #define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \ AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, CP, CC, game::R_Font_GetLegacyFontStyle(S), 0, 0, 0, 0) + WEAK symbol Session_FindRegisteredUser{ 0x140C72A00 }; + WEAK symbol Session_RegisterRemotePlayer{ 0x140C733E0 }; + WEAK symbol Session_GetXuid{ 0x140C72AB0 }; WEAK symbol Session_IsHost{ 0x140D9B470 }; @@ -273,6 +277,7 @@ namespace game WEAK symbol SV_CmdsSP_FastRestart_f{ 0x140C12AF0 }; WEAK symbol SV_ClientMP_GetClientPing{ 0x140C507D0 }; WEAK symbol SV_GameMP_GetGuid{ 0x140C12410 }; + WEAK symbol SV_GameMP_GetAgentCount{ 0x140C124A0 }; WEAK symbol SV_MainMP_KillLocalServer{ 0x140C58DF0 }; WEAK symbol SV_MainMP_GetServerLobby{ 0x140C58DA0 }; WEAK symbol SV_GameSendServerCommand{ 0x140C54780 }; From 02b494ba88eb366c49f960e8ef822a92f9ce8f78 Mon Sep 17 00:00:00 2001 From: quaK Date: Sun, 4 May 2025 01:17:08 +0300 Subject: [PATCH 09/13] apply dvar patch as soon as possible --- src/client/component/dvars.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/client/component/dvars.cpp b/src/client/component/dvars.cpp index 0f2a5e2d..c6282c1c 100644 --- a/src/client/component/dvars.cpp +++ b/src/client/component/dvars.cpp @@ -521,6 +521,9 @@ namespace dvars dvar_re_register_hook.create(0x140CEC210, dvar_re_register); dvar_de_register_hook.create(0x140CE9F30, dvar_de_register); dvar_register_variant_hook.create(0x140CEBDD0, dvar_register_variant); + + // We need to apply these straight away + MH_ApplyQueued(); } component_priority priority() override From 74d3770b55ae45c09ba19e0165982c4f96cc5e9a Mon Sep 17 00:00:00 2001 From: quaK Date: Sun, 4 May 2025 21:19:18 +0300 Subject: [PATCH 10/13] sabl and sabs mod support --- src/client/component/fastfiles.cpp | 2 +- src/client/component/party.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 88414480..7085b65f 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -121,7 +121,7 @@ namespace fastfiles FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr); }; - if (base_filename == "mod.ff"s) + if (base_filename == "mod.ff"s || base_filename == "mod.sabs"s || base_filename == "mod.sabl"s) { auto* fs_basepath = game::Dvar_FindVar("fs_basepath"); auto* fs_game = game::Dvar_FindVar("fs_game"); diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 368579d2..1fb461ba 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -81,6 +81,8 @@ namespace party std::vector mod_files = { {".ff", "mod_hash", false}, + {".sabl", "sabl_hash", false}, + {".sabs", "sabs_hash", false}, }; std::unordered_map hash_cache; @@ -572,7 +574,7 @@ namespace party command::execute(utils::string::va("seta ui_hardcore %d", hardcore->current.enabled), true); } - if (!utils::hook::invoke(0x1409CDCF0, game::Lobby_GetPartyData()) || game::Com_FrontEnd_IsInFrontEnd()) + if (!game::Lobby_GetPartyData()->party_systemActive || game::Com_FrontEnd_IsInFrontEnd()) { if (game::environment::is_dedi()) { From ec38e6c78147674a5239c8556a97cacd97a5ab6e Mon Sep 17 00:00:00 2001 From: quaK Date: Sun, 4 May 2025 23:03:14 +0300 Subject: [PATCH 11/13] fix build --- src/client/game/structs.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index bce9a37b..9fc6b20e 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1351,13 +1351,16 @@ namespace game SessionData* session; char __pad0[11436]; PartyPreloadMapStage preloadingMapStage; - char __pad1[101]; + char __pad1[92]; + int party_systemActive; + char __pad1_2[5]; bool m_gameStartSkipCountdown; char __pad2[110]; int lobbyFlags; bool gameStartRequested; }; static_assert(offsetof(PartyData, preloadingMapStage) == 11444); + static_assert(offsetof(PartyData, party_systemActive) == 11540); static_assert(offsetof(PartyData, m_gameStartSkipCountdown) == 11549); static_assert(offsetof(PartyData, lobbyFlags) == 11660); static_assert(offsetof(PartyData, gameStartRequested) == 11664); From f99682cbd1be4a62ff319b3667d5fc7dea846c1e Mon Sep 17 00:00:00 2001 From: quaK Date: Sun, 4 May 2025 23:03:42 +0300 Subject: [PATCH 12/13] apply gamemode on map start with this we can do map from main menu --- src/client/component/party.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 1fb461ba..777b22e2 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -509,6 +509,20 @@ namespace party } } + game::GameModeType get_game_mode_from_mapname(const std::string& mapname) + { + if (mapname.find("mp_") == 0) + { + return game::GAME_MODE_MP; + } + else if (mapname.find("cp_") == 0) + { + return game::GAME_MODE_CP; + } + + return game::GAME_MODE_SP; + } + void start_map(const std::string& mapname, bool dev) { if (game::Com_GameMode_GetActiveGameMode() == game::GAME_MODE_SP) @@ -522,6 +536,7 @@ namespace party { scheduler::once([=]() { + game::Com_GameMode_SetDesiredGameMode(get_game_mode_from_mapname(mapname)); start_map(mapname, dev); }, scheduler::pipeline::main, 1s); return; From f2afa1b787cfb4721e2116e6ed5a79c138865eb1 Mon Sep 17 00:00:00 2001 From: Skull <86374920+skkuull@users.noreply.github.com> Date: Mon, 5 May 2025 01:13:51 +0300 Subject: [PATCH 13/13] fix latin spanish crash --- src/client/component/patches.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 1f82ba41..0375bc7e 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -278,7 +278,6 @@ namespace patches utils::hook::set(0x1405C90C0, 0xC3); // killswitches - dvars::override::register_bool("mission_team_contracts_enabled", true, game::DVAR_FLAG_READ); dvars::override::register_bool("killswitch_store", false, game::DVAR_FLAG_READ); dvars::override::register_bool("killswitch_quartermaster", false, game::DVAR_FLAG_READ); dvars::override::register_bool("killswitch_cod_points", false, game::DVAR_FLAG_READ); @@ -289,6 +288,12 @@ namespace patches dvars::override::register_bool("killswitch_streak_variants", false, game::DVAR_FLAG_READ); dvars::override::register_bool("killswitch_blood_anvil", false, game::DVAR_FLAG_READ); + // don't use contracts in latin spanish + if (!game::SEH_GetLanguageName(17)) + { + dvars::override::register_bool("mission_team_contracts_enabled", true, game::DVAR_FLAG_READ); + } + // announcer packs if (!game::environment::is_dedi()) {