From b8d286640cbb9f8086078c6819ab4633c541058d Mon Sep 17 00:00:00 2001 From: Maxime Leroy <19607336+maxime1907@users.noreply.github.com> Date: Mon, 16 Oct 2023 21:45:23 +0200 Subject: [PATCH] feat: gamedata support (#36) --- .gitignore | 3 +- AMBuilder | 3 +- CS2Fixes.vcxproj | 1 + CS2Fixes.vcxproj.filters | 3 + PackageScript | 2 + gamedata/cs2fixes.games.txt | 246 +++++++++++++++++ src/addresses.cpp | 19 +- src/addresses.h | 129 +-------- src/adminsystem.cpp | 2 +- src/cdetour.h | 36 +-- src/common.h | 3 - src/cs2_sdk/entity/cbaseentity.h | 7 +- .../interfaces/cgameresourceserviceserver.h | 9 +- src/cs2fixes.cpp | 64 ++++- src/detours.cpp | 71 +++-- src/detours.h | 3 +- src/events.cpp | 6 + src/gameconfig.cpp | 260 ++++++++++++++++++ src/gameconfig.h | 40 +++ src/mempatch.cpp | 21 +- src/mempatch.h | 15 +- src/patches.cpp | 30 +- src/patches.h | 6 + 23 files changed, 748 insertions(+), 231 deletions(-) create mode 100644 gamedata/cs2fixes.games.txt create mode 100644 src/gameconfig.cpp create mode 100644 src/gameconfig.h create mode 100644 src/patches.h diff --git a/.gitignore b/.gitignore index 439e7032..7c4dc0ae 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ bld/ [Bb]in/ [Oo]bj/ .vs/ -*.vcxproj.user \ No newline at end of file +*.vcxproj.user +.vscode diff --git a/AMBuilder b/AMBuilder index 952f0e6d..c1df8d06 100644 --- a/AMBuilder +++ b/AMBuilder @@ -60,7 +60,8 @@ for sdk_name in MMSPlugin.sdks: 'src/cs2_sdk/interfaces/cs2_interfaces.cpp', 'src/cs2_sdk/interfaces/centitysystem.cpp', 'src/ctimer.cpp', - 'src/playermanager.cpp' + 'src/playermanager.cpp', + 'src/gameconfig.cpp', ] if sdk_name in ['dota', 'cs2']: diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj index dda824f4..d04ccd57 100644 --- a/CS2Fixes.vcxproj +++ b/CS2Fixes.vcxproj @@ -177,6 +177,7 @@ + diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters index 7416f92e..7e2e08bb 100644 --- a/CS2Fixes.vcxproj.filters +++ b/CS2Fixes.vcxproj.filters @@ -89,6 +89,9 @@ Source Files + + Source Files + diff --git a/PackageScript b/PackageScript index 5d4f554b..6bfe5d90 100644 --- a/PackageScript +++ b/PackageScript @@ -51,4 +51,6 @@ with open(os.path.join(builder.buildPath, 'pdblog.txt'), 'wt') as fp: # Add CS2Fixes-specific files builder.AddFolder(os.path.join('addons', MMSPlugin.plugin_name, 'data')) configs_folder = builder.AddFolder(os.path.join('addons', MMSPlugin.plugin_name, 'configs')) +gamedata_folder = builder.AddFolder(os.path.join('addons', MMSPlugin.plugin_name, 'gamedata')) builder.AddCopy(os.path.join(builder.sourcePath, 'configs', 'admins.cfg'), configs_folder) +builder.AddCopy(os.path.join('gamedata', 'cs2fixes.games.txt'), gamedata_folder) diff --git a/gamedata/cs2fixes.games.txt b/gamedata/cs2fixes.games.txt new file mode 100644 index 00000000..b94227b6 --- /dev/null +++ b/gamedata/cs2fixes.games.txt @@ -0,0 +1,246 @@ +"Games" +{ + "csgo" + { + "Signatures" + { + // Logging + "Msg" + { + "library" "tier0" + "windows" "@Msg" + "linux" "@Msg" + } + "ConDMsg" + { + "library" "tier0" + "windows" "@ConDMsg" + "linux" "@ConDMsg" + } + "DevMsg" + { + "library" "tier0" + "windows" "@DevMsg" + "linux" "@DevMsg" + } + "Warning" + { + "library" "tier0" + "windows" "@Warning" + "linux" "@Warning" + } + "DevWarning" + { + "library" "tier0" + "windows" "@DevWarning" + "linux" "@DevWarning" + } + "LoggingSystem_Log" + { + "library" "tier0" + "windows" "@LoggingSystem_Log" + "linux" "@LoggingSystem_Log" + } + "LoggingSystem_LogDirect" + { + "library" "tier0" + "windows" "@LoggingSystem_LogDirect" + "linux" "@LoggingSystem_LogDirect" + } + "LoggingSystem_LogAssert" + { + "library" "tier0" + "windows" "@LoggingSystem_LogAssert" + "linux" "@LoggingSystem_LogAssert" + } + + // String: "\"Console<0>\" say \"%s\"\n" + "Host_Say" + { + "library" "server" + "windows" "\x44\x89\x4C\x24\x2A\x44\x88\x44\x24\x2A\x55\x53\x56\x57\x41\x54\x41\x55" + "linux" "\x55\x48\x89\xE5\x41\x57\x49\x89\xFF\x41\x56\x41\x55\x41\x54\x4D\x89\xC4" + } + // Called from Host_Say + "UTIL_SayTextFilter" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x2A\x55\x56\x57\x48\x8D\x6C\x24\x2A\x48\x81\xEC\x2A\x2A\x2A\x2A\x49\x8B\xD8" + "linux" "\x55\x48\x89\xE5\x41\x57\x49\x89\xD7\x31\xD2\x41\x56\x4C\x8D\x75\x98" + } + // Called from Host_Say + "UTIL_SayText2Filter" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x2A\x55\x56\x57\x48\x8D\x6C\x24\x2A\x48\x81\xEC\x2A\x2A\x2A\x2A\x41\x0F\xB6\xF8" + "linux" "\x55\x48\x89\xE5\x41\x57\x4C\x8D\xBD\x2A\x2A\x2A\x2A\x41\x56\x4D\x89\xC6\x41\x55\x4D\x89\xCD" + } + // String: "" + "UTIL_ClientPrintAll" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x81\xEC\x70\x01\x2A\x2A\x8B\xE9" + "linux" "\x55\x48\x89\xE5\x41\x57\x49\x89\xD7\x41\x56\x49\x89\xF6\x41\x55\x41\x89\xFD" + } + // String: "Console command too long" + "ClientPrint" + { + "library" "server" + "windows" "\x48\x85\xC9\x0F\x84\x2A\x2A\x2A\x2A\x48\x8B\xC4\x48\x89\x58\x18" + "linux" "\x55\x48\x89\xE5\x41\x57\x49\x89\xCF\x41\x56\x49\x89\xD6\x41\x55\x41\x89\xF5\x41\x54\x4C\x8D\xA5\xA0\xFE\xFF\xFF" + } + "NetworkStateChanged" + { + "library" "server" + "windows" "\x4C\x8B\xC9\x48\x8B\x09\x48\x85\xC9\x74\x2A\x48\x8B\x41\x10" + "linux" "\x4C\x8B\x07\x4D\x85\xC0\x74\x2A\x49\x8B\x40\x10" + } + "StateChanged" + { + "library" "server" + "windows" "\x48\x89\x54\x24\x10\x55\x53\x57\x41\x55" + "linux" "\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x89\xD3" + } + "VoiceShouldHear" + { + "library" "engine" + "windows" "\x48\x89\x5C\x24\x10\x44\x88\x4C\x24\x20\x44\x88\x44\x24\x18" + "linux" "\x55\x48\x89\xE5\x41\x57\x41\x89\xD7\x41\x56\x49\x89\xF6\x41\x55\x41\x54\x49\x89\xFC\x53\x48\x83\xEC\x28" + } + "IsHearingClient" + { + "library" "engine" + "windows" "\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\x3B\x91\xB8" + "linux" "\x55\x48\x89\xE5\x41\x55\x41\x54\x53\x48\x89\xFB\x48\x83\xEC\x08\x3B\xB7\xC8" + } + // String: "Playing sound on non-networked entity %s" + "CSoundEmitterSystem_EmitSound" + { + "library" "server" + "windows" "\x48\x8B\xC4\x4C\x89\x40\x18\x55\x57" + "linux" "\x48\xB8\x2A\x2A\x2A\x2A\xFF\xFF\xFF\xFF\x55\x48\x89\xE5\x41\x57\x41\x89\xF7" + } + // String: "hammerUniqueId" + "CBaseEntity_Spawn" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x30\x48\x8B\xF2" + "linux" "\x55\x48\x89\xE5\x41\x56\x41\x55\x41\x54\x49\x89\xF4\x53\x48\x89\xFB\x48\x83\xEC\x10\xE8\x2A\x2A\x2A\x2A" + } + // Is the 4th function in the CCSWeaponBase vtable + "CCSWeaponBase_Spawn" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x18\x48\x89\x74\x24\x20\x57\x48\x83\xEC\x30\x48\x8B\xDA\x48\x8B\xE9\xE8\x2A\x2A\x2A\x2A" + "linux" "\x55\x48\x89\xE5\x41\x57\x41\x56\x4C\x8D\x75\xC0\x41\x55\x49\x89\xFD\x41\x54\x49\x89\xF4" + } + // idk a good way to find this again, i just brute forced the vtable. offset is 133 on CTriggerPush + "TriggerPush_Touch" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x10\x48\x89\x7C\x24\x18\x55\x48\x8D\xAC\x24\x60\xE0\xFF\xFF" + "linux" "\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x49\x89\xF5\x41\x54\x49\x89\xFC\x53\x48\x81\xEC\x28\x20\x2A\x2A\xE8" + } + // this is called in CTriggerPush::Touch, using IDA pseudocode look in an `if ( ( v & 0x80 ) != 0 )` and then `if ( v > 0.0 ) SetGroundEntity()` + "SetGroundEntity" + { + "library" "server" + "windows" "\x48\x89\x5C\x24\x10\x48\x89\x6C\x24\x18\x48\x89\x7C\x24\x20\x41\x56\x48\x83\xEC\x20\x0F\xB6\x81\xC0\x02\x2A\x2A\x48" + "linux" "\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x49\x89\xF5\x41\x54\x49\x89\xFC\x53\x48\x83\xEC\x08\x0F\xB6\x87\xA8\x05\x2A\x2A\x83" + } + // Check vauff's pin in #scripting + "ServerMovementUnlock" + { + "library" "server" + "windows" "\x76\x2A\xF2\x0F\x10\x57\x2A\xF3\x0F\x10\x2A\x2A\x0F\x28\xCA\xF3\x0F\x59\xC0" + "linux" "\x0F\x87\x2A\x2A\x2A\x2A\x49\x8B\x7C\x24\x2A\xE8\x2A\x2A\x2A\x2A\x66\x0F\xEF\xED\x66\x0F\xD6\x85\x2A\x2A\x2A\x2A" + } + // Check vauff's pin in #scripting + "ClientMovementUnlock" + { + "library" "client" + "windows" "\x76\x2A\xF2\x0F\x10\x57\x2A\xF3\x0F\x10\x2A\x2A\x0F\x28\xCA\xF3\x0F\x59\xC0" + "linux" "\x0F\x87\x2A\x2A\x2A\x2A\x49\x8B\x7C\x24\x2A\xE8\x2A\x2A\x2A\x2A\x66\x0F\xEF\xED\x66\x0F\xD6\x85\x2A\x2A\x2A\x2A" + } + // Check tilgep's pin in #scripting + "VScriptEnable" + { + "library" "vscript" + "windows" "\xBE\x01\x2A\x2A\x2A\x2B\xD6\x74\x2A\x3B\xD6" + "linux" "\x83\xFE\x01\x0F\x84\x2A\x2A\x2A\x2A\x83" + } + // String: "Noise removal", there should be 3 customermachine checks + "HammerNoCustomerMachine" + { + "library" "hammer" + "windows" "\xFF\x15\x2A\x2A\x2A\x2A\x84\xC0\x0F\x85\x2A\x2A\x2A\x2A\xB9" + "linux" "" + } + // String: "player_jump", then find 42C80000h or in pseudocode "*(a2 + 68) = 1120403456;", changing from 100 to 145 + "CheckJumpButtonWater" + { + "library" "server" + "windows" "\xC8\x42\xEB\x2A\x48\x8B\x4B\x30" + "linux" "\xC8\x42\x66\x0F\xEF\xE4\x41\x0F\x2F\x65" + } + // String: "CCSPlayerPawnBase::SwitchTeam", just keep in mind this is actually CCSPlayerController::SwitchTeam + "CCSPlayerController_SwitchTeam" + { + "library" "server" + "windows" "\x40\x56\x57\x48\x81\xEC\x2A\x2A\x2A\x2A\x48\x8B\xF9\x8B\xF2\x8B\xCA" + "linux" "\x55\x48\x89\xE5\x41\x55\x49\x89\xFD\x89\xF7" + } + } + "Offsets" + { + "GameEntitySystem" + { + "windows" "88" + "linux" "80" + } + "Teleport" + { + "windows" "148" + "linux" "147" + } + "CollisionRulesChanged" + { + "windows" "173" + "linux" "172" + } + } + "Patches" + { + // Server + "ServerMovementUnlock" + { + "windows" "\xEB" + "linux" "\x90\x90\x90\x90\x90\x90" + } + "VScriptEnable" + { + "windows" "\xBE\x02" + "linux" "\x83\xFE\x02" + } + "FixWaterFloorJump" + { + "windows" "\x11\x43" + "linux" "\x11\x43" + } + + // Client + "ClientMovementUnlock" + { + "windows" "\xEB" + "linux" "\x90\x90\x90\x90\x90\x90" + } + + // Hammer + "HammerNoCustomerMachine" + { + "windows" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" + "linux" "" + } + } + } +} diff --git a/src/addresses.cpp b/src/addresses.cpp index e275691f..b03f5be8 100644 --- a/src/addresses.cpp +++ b/src/addresses.cpp @@ -24,9 +24,10 @@ #include "tier0/memdbgon.h" -#define RESOLVE_SIG(module, sig, variable) variable = (decltype(variable))module->FindSignature((uint8*)sig) +#define RESOLVE_SIG(gameConfig, name, variable) \ + variable = (decltype(variable))gameConfig->ResolveSignature(name) -void addresses::Initialize() +void addresses::Initialize(CGameConfig *g_GameConfig) { modules::engine = new CModule(ROOTBIN, "engine2"); modules::tier0 = new CModule(ROOTBIN, "tier0"); @@ -44,10 +45,10 @@ void addresses::Initialize() modules::hammer = new CModule(ROOTBIN, "tools/hammer"); #endif - RESOLVE_SIG(modules::server, sigs::NetworkStateChanged, addresses::NetworkStateChanged); - RESOLVE_SIG(modules::server, sigs::StateChanged, addresses::StateChanged); - RESOLVE_SIG(modules::server, sigs::UTIL_ClientPrintAll, addresses::UTIL_ClientPrintAll); - RESOLVE_SIG(modules::server, sigs::ClientPrint, addresses::ClientPrint); - RESOLVE_SIG(modules::server, sigs::SetGroundEntity, addresses::SetGroundEntity); - RESOLVE_SIG(modules::server, sigs::CCSPlayerController_SwitchTeam, addresses::CCSPlayerController_SwitchTeam); -} \ No newline at end of file + RESOLVE_SIG(g_GameConfig, "NetworkStateChanged", addresses::NetworkStateChanged); + RESOLVE_SIG(g_GameConfig, "StateChanged", addresses::StateChanged); + RESOLVE_SIG(g_GameConfig, "UTIL_ClientPrintAll", addresses::UTIL_ClientPrintAll); + RESOLVE_SIG(g_GameConfig, "ClientPrint", addresses::ClientPrint); + RESOLVE_SIG(g_GameConfig, "SetGroundEntity", addresses::SetGroundEntity); + RESOLVE_SIG(g_GameConfig, "CCSPlayerController_SwitchTeam", addresses::CCSPlayerController_SwitchTeam); +} diff --git a/src/addresses.h b/src/addresses.h index 7b52d6c9..348dc4f3 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -40,10 +40,11 @@ class CEntityInstance; class CBasePlayerController; class CCSPlayerController; class Z_CBaseEntity; +class CGameConfig; namespace addresses { - void Initialize(); + void Initialize(CGameConfig *g_GameConfig); inline void(FASTCALL *NetworkStateChanged)(int64 chainEntity, int64 offset, int64 a3); inline void(FASTCALL *StateChanged)(void *networkTransmitComponent, CEntityInstance *ent, int64 offset, int16 a4, int16 a5); @@ -52,129 +53,3 @@ namespace addresses inline void(FASTCALL *SetGroundEntity)(Z_CBaseEntity *ent, Z_CBaseEntity *ground); inline void(FASTCALL *CCSPlayerController_SwitchTeam)(CCSPlayerController *pController, uint32 team); } - -namespace offsets -{ -#ifdef _WIN32 - inline constexpr int GameEntitySystem = 0x58; - inline constexpr int Teleport = 148; - // PhysDisableEntityCollisions called on entities in two different scene - inline constexpr int CollisionRulesChanged = 173; -#else - inline constexpr int GameEntitySystem = 0x50; - inline constexpr int Teleport = 147; - // PhysDisableEntityCollisions called on entities in two different scene - inline constexpr int CollisionRulesChanged = 172; -#endif -} - -namespace sigs -{ -#ifdef _WIN32 -// Functions - // look for string "\"Console<0>\" say \"%s\"\n" - inline const byte *Host_Say = (byte *)"\x44\x89\x4C\x24\x2A\x44\x88\x44\x24\x2A\x55\x53\x56\x57\x41\x54\x41\x55"; - - // both of these are called from Host_Say - inline const byte *UTIL_SayTextFilter = (byte *)"\x48\x89\x5C\x24\x2A\x55\x56\x57\x48\x8D\x6C\x24\x2A\x48\x81\xEC\x2A\x2A\x2A\x2A\x49\x8B\xD8"; - inline const byte *UTIL_SayText2Filter = (byte *)"\x48\x89\x5C\x24\x2A\x55\x56\x57\x48\x8D\x6C\x24\x2A\x48\x81\xEC\x2A\x2A\x2A\x2A\x41\x0F\xB6\xF8"; - - // "" is given as a parameter to this - inline const byte *UTIL_ClientPrintAll = (byte *)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x81\xEC\x70\x01\x2A\x2A\x8B\xE9"; - - // "Console command too long" is given as a parameter to this - inline const byte *ClientPrint = (byte *)"\x48\x85\xC9\x0F\x84\x2A\x2A\x2A\x2A\x48\x8B\xC4\x48\x89\x58\x18"; - - inline const byte *NetworkStateChanged = (byte *)"\x4C\x8B\xC9\x48\x8B\x09\x48\x85\xC9\x74\x2A\x48\x8B\x41\x10"; - inline const byte *StateChanged = (byte *)"\x48\x89\x54\x24\x10\x55\x53\x57\x41\x55"; - - inline const byte *VoiceShouldHear = (byte *)"\x48\x89\x5C\x24\x10\x44\x88\x4C\x24\x20\x44\x88\x44\x24\x18"; - inline const byte *IsHearingClient = (byte *)"\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\x3B\x91\xB8"; - - // Find "Playing sound on non-networked entity %s" - inline const byte *CSoundEmitterSystem_EmitSound = (byte *)"\x48\x8B\xC4\x4C\x89\x40\x18\x55\x57"; - - // Has string "hammerUniqueId" - inline const byte *CBaseEntity_Spawn = (byte *)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x30\x48\x8B\xF2"; - - // Is the 4th function in the CCSWeaponBase vtable - inline const byte *CCSWeaponBase_Spawn = (byte *)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x18\x48\x89\x74\x24\x20\x57\x48\x83\xEC\x30\x48\x8B\xDA\x48\x8B\xE9\xE8\x2A\x2A\x2A\x2A"; - - // idk a good way to find this again, i just brute forced the vtable. offset is 133 on CTriggerPush - inline const byte *TriggerPush_Touch = (byte *)"\x48\x89\x5C\x24\x10\x48\x89\x7C\x24\x18\x55\x48\x8D\xAC\x24\x60\xE0\xFF\xFF"; - - // this is called in CTriggerPush::Touch, using IDA pseudocode look in an `if ( ( v & 0x80 ) != 0 )` and then `if ( v > 0.0 ) SetGroundEntity()` - inline const byte *SetGroundEntity = (byte *)"\x48\x89\x5C\x24\x10\x48\x89\x6C\x24\x18\x48\x89\x7C\x24\x20\x41\x56\x48\x83\xEC\x20\x0F\xB6\x81\xC0\x02\x2A\x2A\x48"; - - // literally just look for "CCSPlayerPawnBase::SwitchTeam", just keep in mind this is actually CCSPlayerController::SwitchTeam - inline const byte *CCSPlayerController_SwitchTeam = (byte *)"\x40\x56\x57\x48\x81\xEC\x2A\x2A\x2A\x2A\x48\x8B\xF9\x8B\xF2\x8B\xCA"; - -// Patches - // Check vauff's pin in #scripting - inline const byte *MovementUnlock = (byte *)"\x76\x2A\xF2\x0F\x10\x57\x2A\xF3\x0F\x10\x2A\x2A\x0F\x28\xCA\xF3\x0F\x59\xC0"; - inline const byte *Patch_MovementUnlock = (byte *)"\xEB"; - - // Check tilgep's pin in #scripting - inline const byte *VScriptEnable = (byte *)"\xBE\x01\x2A\x2A\x2A\x2B\xD6\x74\x2A\x3B\xD6"; - inline const byte *Patch_VScriptEnable = (byte *)"\xBE\x02"; - - // Find "Noise removal", there should be 3 customermachine checks - inline const byte *HammerNoCustomerMachine = (byte *)"\xFF\x15\x2A\x2A\x2A\x2A\x84\xC0\x0F\x85\x2A\x2A\x2A\x2A\xB9"; - inline const byte *Patch_HammerNoCustomerMachine = (byte *)"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"; - - // Find "player_jump", then find 42C80000h or in pseudocode "*(a2 + 68) = 1120403456;", changing from 100 to 145 - inline const byte* CheckJumpButtonWater = (byte*)"\xC8\x42\xEB\x2A\x48\x8B\x4B\x30"; - inline const byte* Patch_CheckJumpButton = (byte*)"\x11\x43"; -#else -// Functions - // look for string "\"Console<0>\" say \"%s\"\n" - inline const byte *Host_Say = (byte *)"\x55\x48\x89\xE5\x41\x57\x49\x89\xFF\x41\x56\x41\x55\x41\x54\x4D\x89\xC4"; - - // both of these are called from Host_Say - inline const byte *UTIL_SayTextFilter = (byte *)"\x55\x48\x89\xE5\x41\x57\x49\x89\xD7\x31\xD2\x41\x56\x4C\x8D\x75\x98"; - inline const byte *UTIL_SayText2Filter = (byte *)"\x55\x48\x89\xE5\x41\x57\x4C\x8D\xBD\x2A\x2A\x2A\x2A\x41\x56\x4D\x89\xC6\x41\x55\x4D\x89\xCD"; - - // "" is given as a parameter to this - inline const byte *UTIL_ClientPrintAll = (byte *)"\x55\x48\x89\xE5\x41\x57\x49\x89\xD7\x41\x56\x49\x89\xF6\x41\x55\x41\x89\xFD"; - - // "Console command too long" is given as a paramterer to this - inline const byte *ClientPrint = (byte *)"\x55\x48\x89\xE5\x41\x57\x49\x89\xCF\x41\x56\x49\x89\xD6\x41\x55\x41\x89\xF5\x41\x54\x4C\x8D\xA5\xA0\xFE\xFF\xFF"; - - inline const byte *NetworkStateChanged = (byte *)"\x4C\x8B\x07\x4D\x85\xC0\x74\x2A\x49\x8B\x40\x10"; - inline const byte *StateChanged = (byte *)"\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x89\xD3"; - - inline const byte *VoiceShouldHear = (byte *)"\x55\x48\x89\xE5\x41\x57\x41\x89\xD7\x41\x56\x49\x89\xF6\x41\x55\x41\x54\x49\x89\xFC\x53\x48\x83\xEC\x28"; - inline const byte *IsHearingClient = (byte *)"\x55\x48\x89\xE5\x41\x55\x41\x54\x53\x48\x89\xFB\x48\x83\xEC\x08\x3B\xB7\xC8"; - - // Find "Playing sound on non-networked entity %s" - inline const byte *CSoundEmitterSystem_EmitSound = (byte *)"\x48\xB8\x2A\x2A\x2A\x2A\xFF\xFF\xFF\xFF\x55\x48\x89\xE5\x41\x57\x41\x89\xF7"; - - // Has string "hammerUniqueId" - inline const byte *CBaseEntity_Spawn = (byte *)"\x55\x48\x89\xE5\x41\x56\x41\x55\x41\x54\x49\x89\xF4\x53\x48\x89\xFB\x48\x83\xEC\x10\xE8\x2A\x2A\x2A\x2A"; - - // Is the 4th function in the CCSWeaponBase vtable - inline const byte *CCSWeaponBase_Spawn = (byte *)"\x55\x48\x89\xE5\x41\x57\x41\x56\x4C\x8D\x75\xC0\x41\x55\x49\x89\xFD\x41\x54\x49\x89\xF4"; - - // idk a good way to find this again, i just brute forced the vtable. offset is 133 on CTriggerPush - inline const byte *TriggerPush_Touch = (byte *)"\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x49\x89\xF5\x41\x54\x49\x89\xFC\x53\x48\x81\xEC\x28\x20\x2A\x2A\xE8"; - - // this is called in CTriggerPush::Touch, using IDA pseudocode look in an `if ( ( v & 0x80 ) != 0 )` and then `if ( v > 0.0 ) SetGroundEntity()` - inline const byte *SetGroundEntity = (byte *)"\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x49\x89\xF5\x41\x54\x49\x89\xFC\x53\x48\x83\xEC\x08\x0F\xB6\x87\xA8\x05\x2A\x2A\x83"; - - // literally just look for "CCSPlayerPawnBase::SwitchTeam", just keep in mind this is actually CCSPlayerController::SwitchTeam - inline const byte *CCSPlayerController_SwitchTeam = (byte *)"\x55\x48\x89\xE5\x41\x55\x49\x89\xFD\x89\xF7"; - -// Patches - // Check vauff's pin in #scripting - inline const byte *MovementUnlock = (byte *)"\x0F\x87\x2A\x2A\x2A\x2A\x49\x8B\x7C\x24\x2A\xE8\x2A\x2A\x2A\x2A\x66\x0F\xEF\xED\x66\x0F\xD6\x85\x2A\x2A\x2A\x2A"; - inline const byte *Patch_MovementUnlock = (byte *)"\x90\x90\x90\x90\x90\x90"; - - // Check tilgep's pin in #scripting - inline const byte *VScriptEnable = (byte *)"\x83\xFE\x01\x0F\x84\x2A\x2A\x2A\x2A\x83"; - inline const byte *Patch_VScriptEnable = (byte *)"\x83\xFE\x02"; - - // Find "player_jump", then find 42C80000h or in pseudocode "*(a2 + 68) = 1120403456;", changing from 100 to 145 - inline const byte* CheckJumpButtonWater = (byte*)"\xC8\x42\x66\x0F\xEF\xE4\x41\x0F\x2F\x65"; - inline const byte* Patch_CheckJumpButton = (byte*)"\x11\x43"; -#endif -} \ No newline at end of file diff --git a/src/adminsystem.cpp b/src/adminsystem.cpp index 8731eb7b..bdc985fc 100644 --- a/src/adminsystem.cpp +++ b/src/adminsystem.cpp @@ -30,7 +30,7 @@ extern IVEngineServer2 *g_pEngineServer2; extern CEntitySystem *g_pEntitySystem; -CAdminSystem* g_pAdminSystem; +CAdminSystem* g_pAdminSystem = nullptr; CUtlMap g_CommandList(0, 0, DefLessFunc(uint32)); diff --git a/src/cdetour.h b/src/cdetour.h index bcaa78ca..fa203904 100644 --- a/src/cdetour.h +++ b/src/cdetour.h @@ -23,6 +23,8 @@ #include "module.h" #include "utlvector.h" #include "plat.h" +#include "gameconfig.h" +#include "addresses.h" class CDetourBase { @@ -35,11 +37,14 @@ template class CDetour : public CDetourBase { public: - CDetour(CModule** pModule, T* pfnDetour, const char* pszName, byte* pSignature = nullptr) : - m_pModule(pModule), m_pfnDetour(pfnDetour), m_pszName(pszName), m_pSignature(pSignature) + CDetour(T* pfnDetour, const char* pszName) : + m_pfnDetour(pfnDetour), m_pszName(pszName) { m_hook = nullptr; m_bInstalled = false; + m_pSignature = nullptr; + m_pSymbol = nullptr; + m_pModule = nullptr; } ~CDetour() @@ -47,7 +52,7 @@ class CDetour : public CDetourBase FreeDetour(); } - void CreateDetour(); + bool CreateDetour(CGameConfig *gameConfig); void EnableDetour(); void DisableDetour(); void FreeDetour() override; @@ -66,6 +71,7 @@ class CDetour : public CDetourBase T* m_pfnDetour; const char* m_pszName; byte* m_pSignature; + const char* m_pSymbol; T* m_pfnFunc; funchook_t* m_hook; bool m_bInstalled; @@ -74,24 +80,11 @@ class CDetour : public CDetourBase extern CUtlVector g_vecDetours; template -void CDetour::CreateDetour() +bool CDetour::CreateDetour(CGameConfig *gameConfig) { - if (!m_pModule || !(*m_pModule)) - { - Panic("Invalid Module\n", m_pszName); - return; - } - - if (!m_pSignature) - m_pfnFunc = (T*)dlsym((*m_pModule)->m_hModule, m_pszName); - else - m_pfnFunc = (T*)(*m_pModule)->FindSignature(m_pSignature); - + m_pfnFunc = (T*)gameConfig->ResolveSignature(m_pszName); if (!m_pfnFunc) - { - Panic("Could not find the address for %s to detour\n", m_pszName); - return; - } + return false; m_hook = funchook_create(); funchook_prepare(m_hook, (void**)&m_pfnFunc, (void*)m_pfnDetour); @@ -99,6 +92,7 @@ void CDetour::CreateDetour() g_vecDetours.AddToTail(this); Message("Successfully initialized detour for %s!\n", m_pszName); + return true; } template @@ -144,7 +138,7 @@ void CDetour::FreeDetour() Warning("funchook_destroy error for %s: %d %s\n", m_pszName, error, funchook_error_message(m_hook)); } -#define DECLARE_DETOUR(name, detour, modulepath) \ - CDetour name(modulepath, detour, #name, (byte*)sigs::name) +#define DECLARE_DETOUR(name, detour) \ + CDetour name(detour, #name) void FlushAllDetours(); diff --git a/src/common.h b/src/common.h index daae513f..fc585160 100644 --- a/src/common.h +++ b/src/common.h @@ -47,8 +47,5 @@ void UnlockConVars(); void UnlockConCommands(); void ToggleLogs(); -void InitPatches(); -void UndoPatches(); - void Message(const char *, ...); void Panic(const char *, ...); diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h index b94fcbf4..217fc8e9 100644 --- a/src/cs2_sdk/entity/cbaseentity.h +++ b/src/cs2_sdk/entity/cbaseentity.h @@ -24,9 +24,12 @@ #include "mathlib/vector.h" #include "baseentity.h" #include "ehandle.h" +#include "../../gameconfig.h" CGlobalVars* GetGameGlobals(); +extern CGameConfig *g_GameConfig; + class CNetworkTransmitComponent { public: @@ -135,9 +138,9 @@ class Z_CBaseEntity : public CBaseEntity Vector GetAbsOrigin() { return m_CBodyComponent->m_pSceneNode->m_vecAbsOrigin; } void SetAbsOrigin(Vector vecOrigin) { m_CBodyComponent->m_pSceneNode->m_vecAbsOrigin = vecOrigin; } - void Teleport(Vector *position, QAngle *angles, Vector *velocity) { CALL_VIRTUAL(void, offsets::Teleport, this, position, angles, velocity); } + void Teleport(Vector *position, QAngle *angles, Vector *velocity) { static int offset = g_GameConfig->GetOffset("Teleport"); CALL_VIRTUAL(void, offset, this, position, angles, velocity); } - void CollisionRulesChanged() { CALL_VIRTUAL(void, offsets::CollisionRulesChanged, this); } + void CollisionRulesChanged() { static int offset = g_GameConfig->GetOffset("CollisionRulesChanged"); CALL_VIRTUAL(void, offset, this); } CHandle GetHandle() { return m_pEntity->m_EHandle; } diff --git a/src/cs2_sdk/interfaces/cgameresourceserviceserver.h b/src/cs2_sdk/interfaces/cgameresourceserviceserver.h index 1138586f..c5cc30b3 100644 --- a/src/cs2_sdk/interfaces/cgameresourceserviceserver.h +++ b/src/cs2_sdk/interfaces/cgameresourceserviceserver.h @@ -20,15 +20,18 @@ #pragma once #include #include "interfaces/interfaces.h" -#include "../../addresses.h" +#include "../../gameconfig.h" class CGameEntitySystem; +extern CGameConfig *g_GameConfig; + class CGameResourceService { public: CGameEntitySystem *GetGameEntitySystem() { - return *reinterpret_cast((uintptr_t)(this) + offsets::GameEntitySystem); + static int offset = g_GameConfig->GetOffset("GameEntitySystem"); + return *reinterpret_cast((uintptr_t)(this) + offset); } -}; \ No newline at end of file +}; diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index 183ed41c..4227623a 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -30,6 +30,7 @@ #include "common.h" #include "commands.h" #include "detours.h" +#include "patches.h" #include "icvar.h" #include "interface.h" #include "tier0/dbg.h" @@ -42,6 +43,7 @@ #include #include "adminsystem.h" #include "eventlistener.h" +#include "gameconfig.h" #include "tier0/memdbgon.h" @@ -129,11 +131,12 @@ CON_COMMAND_F(toggle_logs, "Toggle printing most logs and warnings", FCVAR_SPONL } IGameEventSystem* g_gameEventSystem; -IGameEventManager2* g_gameEventManager; +IGameEventManager2* g_gameEventManager = nullptr; INetworkGameServer* g_networkGameServer; CGlobalVars* gpGlobals = nullptr; -CPlayerManager* g_playerManager; +CPlayerManager* g_playerManager = nullptr; IVEngineServer2* g_pEngineServer2; +CGameConfig *g_GameConfig = nullptr; PLUGIN_EXPOSE(CS2Fixes, g_CS2Fixes); bool CS2Fixes::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) @@ -166,8 +169,25 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool SH_ADD_HOOK_MEMFUNC(INetworkServerService, StartupServer, g_pNetworkServerService, this, &CS2Fixes::Hook_StartupServer, true); META_CONPRINTF( "All hooks started!\n" ); - - addresses::Initialize(); + + CBufferStringGrowable<256> gamedirpath; + g_pEngineServer2->GetGameDir(gamedirpath); + + std::string gamedirname = CGameConfig::GetDirectoryName(gamedirpath.Get()); + + const char *gamedataPath = "addons/cs2fixes/gamedata/cs2fixes.games.txt"; + Message("Loading %s for game: %s\n", gamedataPath, gamedirname.c_str()); + + g_GameConfig = new CGameConfig(gamedirname, gamedataPath); + char conf_error[255] = ""; + if (!g_GameConfig->Init(g_pFullFileSystem, conf_error, sizeof(conf_error))) + { + snprintf(error, maxlen, "Could not read %s: %s", g_GameConfig->GetPath().c_str(), conf_error); + Panic("%s\n", error); + return false; + } + + addresses::Initialize(g_GameConfig); interfaces::Initialize(); UnlockConVars(); @@ -177,8 +197,16 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool ConVar_Register(FCVAR_RELEASE | FCVAR_CLIENT_CAN_EXECUTE | FCVAR_GAMEDLL); - InitPatches(); - InitDetours(); + bool requiredInitLoaded = true; + + if (!InitPatches(g_GameConfig)) + requiredInitLoaded = false; + + if (!InitDetours(g_GameConfig)) + requiredInitLoaded = false; + + if (!requiredInitLoaded) + return false; g_playerManager = new CPlayerManager(); g_pAdminSystem = new CAdminSystem(); @@ -226,8 +254,14 @@ bool CS2Fixes::Unload(char *error, size_t maxlen) RemoveTimers(); UnregisterEventListeners(); - delete g_playerManager; - delete g_pAdminSystem; + if (g_playerManager != NULL) + delete g_playerManager; + + if (g_pAdminSystem != NULL) + delete g_pAdminSystem; + + if (g_GameConfig != NULL) + delete g_GameConfig; return true; } @@ -256,6 +290,8 @@ void CS2Fixes::Hook_StartupServer(const GameSessionConfiguration_t& config, ISou void CS2Fixes::Hook_PostEvent(CSplitScreenSlot nSlot, bool bLocalOnly, int nClientCount, const uint64* clients, INetworkSerializable* pEvent, const void* pData, unsigned long nSize, NetChannelBufType_t bufType) { + // Message( "Hook_PostEvent(%d, %d, %d, %lli)\n", nSlot, bLocalOnly, nClientCount, clients ); + NetMessageInfo_t* info = pEvent->GetNetMessageInfo(); //CMsgTEFireBullets @@ -288,6 +324,8 @@ void CS2Fixes::AllPluginsLoaded() /* This is where we'd do stuff that relies on the mod or other plugins * being initialized (for example, cvars added and events registered). */ + + Message( "AllPluginsLoaded\n" ); } void CS2Fixes::Hook_ClientActive( CPlayerSlot slot, bool bLoadGame, const char *pszName, uint64 xuid ) @@ -307,17 +345,18 @@ void CS2Fixes::Hook_ClientSettingsChanged( CPlayerSlot slot ) void CS2Fixes::Hook_OnClientConnected( CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID, const char *pszAddress, bool bFakePlayer ) { + META_CONPRINTF( "Hook_OnClientConnected(%d, \"%s\", %lli, \"%s\", \"%s\", %d)\n", slot, pszName, xuid, pszNetworkID, pszAddress, bFakePlayer ); + if(bFakePlayer) g_playerManager->OnBotConnected(slot); - - META_CONPRINTF( "Hook_OnClientConnected(%d, \"%s\", %lli, \"%s\", \"%s\", %d)\n", slot, pszName, xuid, pszNetworkID, pszAddress, bFakePlayer ); } bool CS2Fixes::Hook_ClientConnect( CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID, bool unk1, CBufferString *pRejectReason ) { - g_playerManager->OnClientConnected(slot); META_CONPRINTF( "Hook_ClientConnect(%d, \"%s\", %lli, \"%s\", %d, \"%s\")\n", slot, pszName, xuid, pszNetworkID, unk1, pRejectReason->ToGrowable()->Get() ); + g_playerManager->OnClientConnected(slot); + RETURN_META_VALUE(MRES_IGNORED, true); } @@ -328,8 +367,9 @@ void CS2Fixes::Hook_ClientPutInServer( CPlayerSlot slot, char const *pszName, in void CS2Fixes::Hook_ClientDisconnect( CPlayerSlot slot, int reason, const char *pszName, uint64 xuid, const char *pszNetworkID ) { - g_playerManager->OnClientDisconnect(slot); META_CONPRINTF( "Hook_ClientDisconnect(%d, %d, \"%s\", %lli, \"%s\")\n", slot, reason, pszName, xuid, pszNetworkID ); + + g_playerManager->OnClientDisconnect(slot); } void CS2Fixes::Hook_GameFrame( bool simulating, bool bFirstTick, bool bLastTick ) diff --git a/src/detours.cpp b/src/detours.cpp index f5fdc354..683b4e3d 100644 --- a/src/detours.cpp +++ b/src/detours.cpp @@ -33,6 +33,7 @@ #include "entity/ctriggerpush.h" #include "playermanager.h" #include "igameevents.h" +#include "gameconfig.h" #include "tier0/memdbgon.h" @@ -40,13 +41,13 @@ extern CGlobalVars *gpGlobals; extern CEntitySystem *g_pEntitySystem; extern IGameEventManager2 *g_gameEventManager; -DECLARE_DETOUR(Host_Say, Detour_Host_Say, &modules::server); -DECLARE_DETOUR(UTIL_SayTextFilter, Detour_UTIL_SayTextFilter, &modules::server); -DECLARE_DETOUR(UTIL_SayText2Filter, Detour_UTIL_SayText2Filter, &modules::server); -DECLARE_DETOUR(IsHearingClient, Detour_IsHearingClient, &modules::engine); -DECLARE_DETOUR(CSoundEmitterSystem_EmitSound, Detour_CSoundEmitterSystem_EmitSound, &modules::server); -DECLARE_DETOUR(CCSWeaponBase_Spawn, Detour_CCSWeaponBase_Spawn, &modules::server); -DECLARE_DETOUR(TriggerPush_Touch, Detour_TriggerPush_Touch, &modules::server); +DECLARE_DETOUR(Host_Say, Detour_Host_Say); +DECLARE_DETOUR(UTIL_SayTextFilter, Detour_UTIL_SayTextFilter); +DECLARE_DETOUR(UTIL_SayText2Filter, Detour_UTIL_SayText2Filter); +DECLARE_DETOUR(IsHearingClient, Detour_IsHearingClient); +DECLARE_DETOUR(CSoundEmitterSystem_EmitSound, Detour_CSoundEmitterSystem_EmitSound); +DECLARE_DETOUR(CCSWeaponBase_Spawn, Detour_CCSWeaponBase_Spawn); +DECLARE_DETOUR(TriggerPush_Touch, Detour_TriggerPush_Touch); void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, Z_CBaseEntity* pOther) { @@ -216,18 +217,18 @@ bool FASTCALL Detour_IsChannelEnabled(LoggingChannelID_t channelID, LoggingSever CDetour g_LoggingDetours[] = { - CDetour(&modules::tier0, Detour_Log, "Msg" ), - //CDetour( modules::tier0, Detour_Log, "?ConMsg@@YAXPEBDZZ" ), - //CDetour( modules::tier0, Detour_Log, "?ConColorMsg@@YAXAEBVColor@@PEBDZZ" ), - CDetour( &modules::tier0, Detour_Log, "ConDMsg" ), - CDetour( &modules::tier0, Detour_Log, "DevMsg" ), - CDetour( &modules::tier0, Detour_Log, "Warning" ), - CDetour( &modules::tier0, Detour_Log, "DevWarning" ), - //CDetour( modules::tier0, Detour_Log, "?DevWarning@@YAXPEBDZZ" ), - CDetour( &modules::tier0, Detour_Log, "LoggingSystem_Log" ), - CDetour( &modules::tier0, Detour_Log, "LoggingSystem_LogDirect" ), - CDetour( &modules::tier0, Detour_Log, "LoggingSystem_LogAssert" ), - //CDetour( modules::tier0, Detour_IsChannelEnabled, "LoggingSystem_IsChannelEnabled" ), + CDetour( Detour_Log, "Msg" ), + //CDetour( Detour_Log, "?ConMsg@@YAXPEBDZZ" ), + //CDetour( Detour_Log, "?ConColorMsg@@YAXAEBVColor@@PEBDZZ" ), + CDetour( Detour_Log, "ConDMsg" ), + CDetour( Detour_Log, "DevMsg" ), + CDetour( Detour_Log, "Warning" ), + CDetour( Detour_Log, "DevWarning" ), + //CDetour( Detour_Log, "?DevWarning@@YAXPEBDZZ" ), + CDetour( Detour_Log, "LoggingSystem_Log" ), + CDetour( Detour_Log, "LoggingSystem_LogDirect" ), + CDetour( Detour_Log, "LoggingSystem_LogAssert" ), + //CDetour( Detour_IsChannelEnabled, "LoggingSystem_IsChannelEnabled" ), }; void ToggleLogs() @@ -250,33 +251,47 @@ void ToggleLogs() CUtlVector g_vecDetours; -void InitDetours() +bool InitDetours(CGameConfig *gameConfig) { + bool success = true; + g_vecDetours.PurgeAndDeleteElements(); for (int i = 0; i < sizeof(g_LoggingDetours) / sizeof(*g_LoggingDetours); i++) - g_LoggingDetours[i].CreateDetour(); + { + if (!g_LoggingDetours[i].CreateDetour(gameConfig)) + success = false; + } - UTIL_SayTextFilter.CreateDetour(); + if (!UTIL_SayTextFilter.CreateDetour(gameConfig)) + success = false; UTIL_SayTextFilter.EnableDetour(); - UTIL_SayText2Filter.CreateDetour(); + if (!UTIL_SayText2Filter.CreateDetour(gameConfig)) + success = false; UTIL_SayText2Filter.EnableDetour(); - Host_Say.CreateDetour(); + if (!Host_Say.CreateDetour(gameConfig)) + success = false; Host_Say.EnableDetour(); - IsHearingClient.CreateDetour(); + if (!IsHearingClient.CreateDetour(gameConfig)) + success = false; IsHearingClient.EnableDetour(); - CSoundEmitterSystem_EmitSound.CreateDetour(); + if (!CSoundEmitterSystem_EmitSound.CreateDetour(gameConfig)) + success = false; CSoundEmitterSystem_EmitSound.EnableDetour(); - CCSWeaponBase_Spawn.CreateDetour(); + if (!CCSWeaponBase_Spawn.CreateDetour(gameConfig)) + success = false; CCSWeaponBase_Spawn.EnableDetour(); - TriggerPush_Touch.CreateDetour(); + if (!TriggerPush_Touch.CreateDetour(gameConfig)) + success = false; TriggerPush_Touch.EnableDetour(); + + return success; } void FlushAllDetours() diff --git a/src/detours.h b/src/detours.h index 7ffaf7a6..c400ac61 100644 --- a/src/detours.h +++ b/src/detours.h @@ -28,8 +28,9 @@ class CCSPlayerController; class CEntityIndex; class CCommand; class CTriggerPush; +class CGameConfig; -void InitDetours(); +bool InitDetours(CGameConfig *gameConfig); void FlushAllDetours(); void FASTCALL Detour_UTIL_SayTextFilter(IRecipientFilter &, const char *, CCSPlayerController *, uint64); diff --git a/src/events.cpp b/src/events.cpp index 2c1e8094..dbaf4130 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -34,6 +34,9 @@ CUtlVector g_vecEventListeners; void RegisterEventListeners() { + if (!g_gameEventManager) + return; + FOR_EACH_VEC(g_vecEventListeners, i) { g_gameEventManager->AddListener(g_vecEventListeners[i], g_vecEventListeners[i]->GetEventName(), true); @@ -42,6 +45,9 @@ void RegisterEventListeners() void UnregisterEventListeners() { + if (!g_gameEventManager) + return; + FOR_EACH_VEC(g_vecEventListeners, i) { g_gameEventManager->RemoveListener(g_vecEventListeners[i]); diff --git a/src/gameconfig.cpp b/src/gameconfig.cpp new file mode 100644 index 00000000..92cdbb9f --- /dev/null +++ b/src/gameconfig.cpp @@ -0,0 +1,260 @@ +#include "gameconfig.h" +#include "addresses.h" + +CGameConfig::CGameConfig(const std::string& gameDir, const std::string& path) +{ + this->m_szGameDir = gameDir; + this->m_szPath = path; + this->m_pKeyValues = new KeyValues("Games"); +} + +CGameConfig::~CGameConfig() +{ + delete m_pKeyValues; +} + +bool CGameConfig::Init(IFileSystem *filesystem, char *conf_error, int conf_error_size) +{ + if (!m_pKeyValues->LoadFromFile(filesystem, m_szPath.c_str(), nullptr)) + { + snprintf(conf_error, conf_error_size, "Failed to load gamedata file"); + return false; + } + + const KeyValues* game = m_pKeyValues->FindKey(m_szGameDir.c_str()); + if (game) + { +#if defined _LINUX + const char* platform = "linux"; +#else + const char* platform = "windows"; +#endif + + const KeyValues* offsets = game->FindKey("Offsets"); + if (offsets) + { + FOR_EACH_SUBKEY(offsets, it) + { + m_umOffsets[it->GetName()] = it->GetInt(platform, -1); + } + } + + const KeyValues* signatures = game->FindKey("Signatures"); + if (signatures) + { + FOR_EACH_SUBKEY(signatures, it) + { + m_umLibraries[it->GetName()] = std::string(it->GetString("library")); + m_umSignatures[it->GetName()] = std::string(it->GetString(platform)); + } + } + + const KeyValues* patches = game->FindKey("Patches"); + if (patches) + { + FOR_EACH_SUBKEY(patches, it) + { + m_umPatches[it->GetName()] = std::string(it->GetString(platform)); + } + } + } + else + { + snprintf(conf_error, conf_error_size, "Failed to find game: %s", m_szGameDir.c_str()); + return false; + } + return true; +} + +const std::string CGameConfig::GetPath() +{ + return m_szPath; +} + +const char *CGameConfig::GetSignature(const std::string& name) +{ + auto it = m_umSignatures.find(name); + if (it == m_umSignatures.end()) + { + return nullptr; + } + return it->second.c_str(); +} + +const char *CGameConfig::GetPatch(const std::string& name) +{ + auto it = m_umPatches.find(name); + if (it == m_umPatches.end()) + { + return nullptr; + } + return it->second.c_str(); +} + +int CGameConfig::GetOffset(const std::string& name) +{ + auto it = m_umOffsets.find(name); + if (it == m_umOffsets.end()) + { + return -1; + } + return it->second; +} + +const char *CGameConfig::GetLibrary(const std::string& name) +{ + auto it = m_umLibraries.find(name); + if (it == m_umLibraries.end()) + { + return nullptr; + } + return it->second.c_str(); +} + +CModule **CGameConfig::GetModule(const char *name) +{ + const char *library = this->GetLibrary(name); + if (library == NULL) + return NULL; + + if (strcmp(library, "engine") == 0) + return &modules::engine; + else if (strcmp(library, "server") == 0) + return &modules::server; + else if (strcmp(library, "client") == 0) + return &modules::client; + else if (strcmp(library, "vscript") == 0) + return &modules::vscript; + else if (strcmp(library, "tier0") == 0) + return &modules::tier0; +#ifdef _WIN32 + else if (strcmp(library, "hammer") == 0) + return &modules::hammer; +#endif + return NULL; +} + +bool CGameConfig::IsSymbol(const char *name) +{ + const char *sigOrSymbol = this->GetSignature(name); + if (sigOrSymbol == NULL || strlen(sigOrSymbol) <= 0) + { + Panic("Missing signature or symbol\n", name); + return false; + } + return sigOrSymbol[0] == '@'; +} + +const char* CGameConfig::GetSymbol(const char *name) +{ + const char *symbol = this->GetSignature(name); + + if (symbol == NULL || strlen(symbol) <= 1) + { + Panic("Missing symbol\n", name); + return NULL; + } + return symbol + 1; +} + +void *CGameConfig::ResolveSignature(const char *name) +{ + CModule **module = this->GetModule(name); + if (!module || !(*module)) + { + Panic("Invalid Module %s\n", name); + return NULL; + } + + void *address = nullptr; + if (this->IsSymbol(name)) + { + const char *symbol = this->GetSymbol(name); + if (symbol == NULL) + { + Panic("Invalid symbol for %s\n", name); + return NULL; + } + address = dlsym((*module)->m_hModule, symbol); + } + else + { + const char *signature = this->GetSignature(name); + if (signature == NULL) + { + Panic("Failed to find signature for %s\n", name); + return NULL; + } + + byte *pSignature = CGameConfig::HexToByte(signature); + if (pSignature == NULL) + return NULL; + address = (*module)->FindSignature(pSignature); + } + + if (address == NULL) + { + Panic("Failed to find address for %s\n", name); + return NULL; + } + return address; +} + +// Static functions +std::string CGameConfig::GetDirectoryName(const std::string &directoryPathInput) +{ + std::string directoryPath = std::string(directoryPathInput); + + size_t found = std::string(directoryPath).find_last_of("/\\"); + if (found != std::string::npos) + { + return std::string(directoryPath, found + 1); + } + return ""; +} + +int CGameConfig::HexStringToUint8Array(const char* hexString, uint8_t* byteArray, size_t maxBytes) +{ + if (hexString == NULL) { + printf("Invalid hex string.\n"); + return -1; + } + + size_t hexStringLength = strlen(hexString); + size_t byteCount = hexStringLength / 4; // Each "\\x" represents one byte. + + if (hexStringLength % 4 != 0 || byteCount == 0 || byteCount > maxBytes) { + printf("Invalid hex string format or byte count.\n"); + return -1; // Return an error code. + } + + for (size_t i = 0; i < hexStringLength; i += 4) { + if (sscanf(hexString + i, "\\x%2hhX", &byteArray[i / 4]) != 1) { + printf("Failed to parse hex string at position %zu.\n", i); + return -1; // Return an error code. + } + } + + byteArray[byteCount] = '\0'; // Add a null-terminating character. + + return byteCount; // Return the number of bytes successfully converted. +} + +byte *CGameConfig::HexToByte(const char *src) +{ + if (src == NULL || strlen(src) <= 0) + { + Panic("Invalid hex string\n"); + return NULL; + } + + size_t maxBytes = strlen(src) / 4; + uint8_t *dest = new uint8_t[maxBytes]; + int byteCount = CGameConfig::HexStringToUint8Array(src, dest, maxBytes); + if (byteCount <= 0) + { + Panic("Invalid hex format %s\n", src); + return NULL; + } + return (byte *)dest; +} diff --git a/src/gameconfig.h b/src/gameconfig.h new file mode 100644 index 00000000..80360635 --- /dev/null +++ b/src/gameconfig.h @@ -0,0 +1,40 @@ +#pragma once + +#include "KeyValues.h" + +#include +#include + +class CModule; + +class CGameConfig +{ +public: + CGameConfig(const std::string& gameDir, const std::string& path); + ~CGameConfig(); + + bool Init(IFileSystem *filesystem, char *conf_error, int conf_error_size); + const std::string GetPath(); + const char *GetLibrary(const std::string& name); + const char *GetSignature(const std::string& name); + const char* GetSymbol(const char *name); + const char *GetPatch(const std::string& name); + int GetOffset(const std::string& name); + void* GetAddress(const std::string& name, void *engine, void *server, char *error, int maxlen); + CModule **GetModule(const char *name); + bool IsSymbol(const char *name); + void *ResolveSignature(const char *name); + static std::string GetDirectoryName(const std::string &directoryPathInput); + static int HexStringToUint8Array(const char* hexString, uint8_t* byteArray, size_t maxBytes); + static byte *HexToByte(const char *src); + +private: + std::string m_szGameDir; + std::string m_szPath; + KeyValues* m_pKeyValues; + std::unordered_map m_umOffsets; + std::unordered_map m_umSignatures; + std::unordered_map m_umAddresses; + std::unordered_map m_umLibraries; + std::unordered_map m_umPatches; +}; diff --git a/src/mempatch.cpp b/src/mempatch.cpp index 83bfe181..a74c47ed 100644 --- a/src/mempatch.cpp +++ b/src/mempatch.cpp @@ -25,18 +25,22 @@ #include "tier0/memdbgon.h" -void CMemPatch::PerformPatch() +bool CMemPatch::PerformPatch(CGameConfig *gameConfig) { - if (!m_pModule) - return; - - m_pPatchAddress = (*m_pModule)->FindSignature(m_pSignature); + m_pPatchAddress = gameConfig->ResolveSignature(m_pSignatureName); + if (m_pPatchAddress == NULL) + return false; - if (!m_pPatchAddress) + const char *patch = gameConfig->GetPatch(m_pszName); + if (patch == NULL) { - Panic("Failed to find signature for %s\n", m_pszName); - return; + Panic("Failed to find patch for %s\n", m_pszName); + return false; } + m_pPatch = gameConfig->HexToByte(patch); + if (m_pPatch == NULL) + return false; + m_iPatchLength = V_strlen((char*)m_pPatch); m_pOriginalBytes = new byte[m_iPatchLength]; V_memcpy(m_pOriginalBytes, m_pPatchAddress, m_iPatchLength); @@ -44,6 +48,7 @@ void CMemPatch::PerformPatch() Plat_WriteMemory(m_pPatchAddress, (byte*)m_pPatch, m_iPatchLength); Message("Successfully patched %s at %p\n", m_pszName, m_pPatchAddress); + return true; } void CMemPatch::UndoPatch() diff --git a/src/mempatch.h b/src/mempatch.h index 2c594770..7524fd4d 100644 --- a/src/mempatch.h +++ b/src/mempatch.h @@ -21,19 +21,23 @@ #include "platform.h" #include "utils/module.h" +#include "gameconfig.h" class CMemPatch { public: - CMemPatch(CModule **pModule, const byte *pSignature, const byte *pPatch, const char *pszName, int iRepeat = 1) : - m_pModule(pModule), m_pSignature(pSignature), m_pPatch(pPatch), m_pszName(pszName), m_iRepeat(iRepeat) + CMemPatch(const char *pSignatureName, const char *pszName, int iRepeat = 1) : + m_pSignatureName(pSignatureName), m_pszName(pszName), m_iRepeat(iRepeat) { + m_pModule = nullptr; m_pPatchAddress = nullptr; - m_pOriginalBytes = nullptr; - m_iPatchLength = V_strlen((char*)m_pPatch); + m_pOriginalBytes = nullptr; + m_pSignature = nullptr; + m_pPatch = nullptr; + m_iPatchLength = 0; } - void PerformPatch(); + bool PerformPatch(CGameConfig *gameConfig); void UndoPatch(); void *GetPatchAddress() { return m_pPatchAddress; } @@ -43,6 +47,7 @@ class CMemPatch const byte *m_pSignature; const byte *m_pPatch; byte *m_pOriginalBytes; + const char *m_pSignatureName; const char *m_pszName; int m_iRepeat; int m_iPatchLength; diff --git a/src/patches.cpp b/src/patches.cpp index b2932cdf..d39eda91 100644 --- a/src/patches.cpp +++ b/src/patches.cpp @@ -26,39 +26,47 @@ #include "entity/ccsplayerpawn.h" #include "entity/cbasemodelentity.h" #include "addresses.h" +#include "patches.h" #include "tier0/memdbgon.h" CMemPatch g_CommonPatches[] = { - CMemPatch(&modules::server, sigs::MovementUnlock, sigs::Patch_MovementUnlock, "ServerMovementUnlock"), - CMemPatch(&modules::vscript, sigs::VScriptEnable, sigs::Patch_VScriptEnable, "VScriptEnable"), - CMemPatch(&modules::server, sigs::CheckJumpButtonWater, sigs::Patch_CheckJumpButton, "FixWaterFloorJump"), + CMemPatch("ServerMovementUnlock", "ServerMovementUnlock"), + CMemPatch("VScriptEnable", "VScriptEnable"), + CMemPatch("CheckJumpButtonWater", "FixWaterFloorJump"), }; CMemPatch g_ClientPatches[] = { - CMemPatch(&modules::client, sigs::MovementUnlock, sigs::Patch_MovementUnlock, "ClientMovementUnlock"), + CMemPatch("ClientMovementUnlock", "ClientMovementUnlock"), }; #ifdef _WIN32 CMemPatch g_ToolsPatches[] = { // Remove some -nocustomermachine checks without needing -nocustomermachine itself as it can break stuff, mainly to enable device selection in compiles - CMemPatch(&modules::hammer, sigs::HammerNoCustomerMachine, sigs::Patch_HammerNoCustomerMachine, "HammerNoCustomerMachine", 4), + CMemPatch("HammerNoCustomerMachine", "HammerNoCustomerMachine", 4), }; #endif -void InitPatches() +bool InitPatches(CGameConfig *g_GameConfig) { + bool success = true; for (int i = 0; i < sizeof(g_CommonPatches) / sizeof(*g_CommonPatches); i++) - g_CommonPatches[i].PerformPatch(); + { + if (!g_CommonPatches[i].PerformPatch(g_GameConfig)) + success = false; + } // Dedicated servers don't load client if (!CommandLine()->HasParm("-dedicated")) { for (int i = 0; i < sizeof(g_ClientPatches) / sizeof(*g_ClientPatches); i++) - g_ClientPatches[i].PerformPatch(); + { + if (!g_ClientPatches[i].PerformPatch(g_GameConfig)) + success = false; + } } #ifdef _WIN32 @@ -66,9 +74,13 @@ void InitPatches() if (CommandLine()->HasParm("-tools")) { for (int i = 0; i < sizeof(g_ToolsPatches) / sizeof(*g_ToolsPatches); i++) - g_ToolsPatches[i].PerformPatch(); + { + if (!g_ToolsPatches[i].PerformPatch(g_GameConfig)) + success = false; + } } #endif + return success; } void UndoPatches() diff --git a/src/patches.h b/src/patches.h new file mode 100644 index 00000000..255b64b4 --- /dev/null +++ b/src/patches.h @@ -0,0 +1,6 @@ +#pragma once + +#include "gameconfig.h" + +bool InitPatches(CGameConfig *gameConfig); +void UndoPatches();