From a5ebdd6e8211f7823678fc9b22830f829c4e51df Mon Sep 17 00:00:00 2001 From: Accelerator Date: Sun, 5 May 2024 14:08:27 +0300 Subject: [PATCH] v2.0.0 --- AMBuilder | 2 +- Makefile | 83 --------- Makefile_l4d | 83 --------- game_offsets.h | 20 +++ game_signature.h | 18 -- l4d1_offsets_linux.h | 9 + l4d1_offsets_win32.h | 9 + l4d1_signature_linux.h | 57 ------ l4d1_signature_win32.h | 40 ----- l4d2_offsets_linux.h | 9 + l4d2_offsets_win32.h | 9 + l4d2_signature_linux.h | 33 ---- l4d2_signature_win32.h | 32 ---- l4dtoolz_mm.cpp | 297 +++++++++++++++---------------- l4dtoolz_mm.h | 17 +- memutils.cpp | 395 +++++++++++++++++++++++++++++++++++++++++ memutils.h | 79 +++++++++ signature.cpp | 243 ------------------------- signature.h | 16 -- sm_symtable.h | 232 ++++++++++++++++++++++++ 20 files changed, 907 insertions(+), 776 deletions(-) delete mode 100644 Makefile delete mode 100644 Makefile_l4d create mode 100644 game_offsets.h delete mode 100644 game_signature.h create mode 100644 l4d1_offsets_linux.h create mode 100644 l4d1_offsets_win32.h delete mode 100644 l4d1_signature_linux.h delete mode 100644 l4d1_signature_win32.h create mode 100644 l4d2_offsets_linux.h create mode 100644 l4d2_offsets_win32.h delete mode 100644 l4d2_signature_linux.h delete mode 100644 l4d2_signature_win32.h create mode 100644 memutils.cpp create mode 100644 memutils.h delete mode 100644 signature.cpp delete mode 100644 signature.h create mode 100644 sm_symtable.h diff --git a/AMBuilder b/AMBuilder index cdf485c..6c29970 100644 --- a/AMBuilder +++ b/AMBuilder @@ -4,7 +4,7 @@ projectName = 'l4dtoolz' # smsdk_ext.cpp will be automatically added later sourceFiles = [ 'l4dtoolz_mm.cpp', - 'signature.cpp', + 'memutils.cpp', ] ############### diff --git a/Makefile b/Makefile deleted file mode 100644 index 3b7e0c4..0000000 --- a/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# Makefile - -HX_SDK = ../hl2sdk-l4d2 -HX_METAMOD = ../mmsource -# -# l4dtoolz_mm.so -# -HX_INCLUDE = -I. \ - -I$(HX_METAMOD)/core \ - -I$(HX_METAMOD)/core/sourcehook \ - -I$(HX_SDK)/public \ - -I$(HX_SDK)/public/tier0 \ - -I$(HX_SDK)/public/tier1 \ - -I$(HX_SDK)/public/engine \ - -I$(HX_SDK)/public/mathlib - -# -HX_FLAGS = -DSOURCE_ENGINE=9 - -# -HX_FLAGS += -DSE_EPISODEONE=1 \ - -DSE_DARKMESSIAH=2 \ - -DSE_ORANGEBOX=3 \ - -DSE_BLOODYGOODTIME=4 \ - -DSE_EYE=5 \ - -DSE_CSS=6 \ - -DSE_ORANGEBOXVALVE=7 \ - -DSE_LEFT4DEAD=8 \ - -DSE_LEFT4DEAD2=9 \ - -DSE_ALIENSWARM=10 \ - -DSE_PORTAL2=11 \ - -DSE_CSGO=12 - -# -HX_FLAGS += -DPOSIX \ - -Dstricmp=strcasecmp \ - -D_stricmp=strcasecmp \ - -D_strnicmp=strncasecmp \ - -Dstrnicmp=strncasecmp \ - -D_snprintf=snprintf \ - -D_vsnprintf=vsnprintf \ - -D_alloca=alloca \ - -Dstrcmpi=strcasecmp \ - -Wall \ - -Werror \ - -Wno-switch \ - -Wno-unused \ - -msse \ - -DHAVE_STDINT_H \ - -m32 \ - -DNDEBUG \ - -O3 \ - -funroll-loops \ - -pipe \ - -fno-strict-aliasing \ - -D_LINUX \ - -mfpmath=sse \ - -fvisibility=hidden \ - -Wno-non-virtual-dtor \ - -Wno-overloaded-virtual \ - -Wno-deprecated-register \ - -fno-exceptions \ - -fno-rtti \ - -fvisibility-inlines-hidden - -# -HX_LIB = Release/l4dtoolz_mm.o \ - Release/signature.o \ - $(HX_SDK)/lib/linux/tier1_i486.a \ - libvstdlib_srv.so \ - libtier0_srv.so - -# -all: - mkdir -p Release - ln -sf $(HX_SDK)/lib/linux/libvstdlib_srv.so libvstdlib_srv.so; - ln -sf $(HX_SDK)/lib/linux/libtier0_srv.so libtier0_srv.so; -# - clang $(HX_INCLUDE) $(HX_FLAGS) -o Release/l4dtoolz_mm.o -c l4dtoolz_mm.cpp - clang $(HX_INCLUDE) $(HX_FLAGS) -o Release/signature.o -c signature.cpp - clang $(HX_INCLUDE) $(HX_LIB) -m32 -shared -static-libgcc -ldl -lm -o Release/l4dtoolz_mm.so -# - rm -rf Release/*.o diff --git a/Makefile_l4d b/Makefile_l4d deleted file mode 100644 index 0828f11..0000000 --- a/Makefile_l4d +++ /dev/null @@ -1,83 +0,0 @@ -# Makefile - -HX_SDK = ../hl2sdk-l4d -HX_METAMOD = ../mmsource -# -# l4dtoolz_mm.so -# -HX_INCLUDE = -I. \ - -I$(HX_METAMOD)/core \ - -I$(HX_METAMOD)/core/sourcehook \ - -I$(HX_SDK)/public \ - -I$(HX_SDK)/public/tier0 \ - -I$(HX_SDK)/public/tier1 \ - -I$(HX_SDK)/public/engine \ - -I$(HX_SDK)/public/mathlib - -# -HX_FLAGS = -DSOURCE_ENGINE=8 - -# -HX_FLAGS += -DSE_EPISODEONE=1 \ - -DSE_DARKMESSIAH=2 \ - -DSE_ORANGEBOX=3 \ - -DSE_BLOODYGOODTIME=4 \ - -DSE_EYE=5 \ - -DSE_CSS=6 \ - -DSE_ORANGEBOXVALVE=7 \ - -DSE_LEFT4DEAD=8 \ - -DSE_LEFT4DEAD2=9 \ - -DSE_ALIENSWARM=10 \ - -DSE_PORTAL2=11 \ - -DSE_CSGO=12 - -# -HX_FLAGS += -DPOSIX \ - -Dstricmp=strcasecmp \ - -D_stricmp=strcasecmp \ - -D_strnicmp=strncasecmp \ - -Dstrnicmp=strncasecmp \ - -D_snprintf=snprintf \ - -D_vsnprintf=vsnprintf \ - -D_alloca=alloca \ - -Dstrcmpi=strcasecmp \ - -Wall \ - -Werror \ - -Wno-switch \ - -Wno-unused \ - -msse \ - -DHAVE_STDINT_H \ - -m32 \ - -DNDEBUG \ - -O3 \ - -funroll-loops \ - -pipe \ - -fno-strict-aliasing \ - -D_LINUX \ - -mfpmath=sse \ - -fvisibility=hidden \ - -Wno-non-virtual-dtor \ - -Wno-overloaded-virtual \ - -Wno-deprecated-register \ - -fno-exceptions \ - -fno-rtti \ - -fvisibility-inlines-hidden - -# -HX_LIB = Release/l4dtoolz_mm.o \ - Release/signature.o \ - $(HX_SDK)/lib/linux/tier1_i486.a \ - libvstdlib.so \ - libtier0.so - -# -all: - mkdir -p Release - ln -sf $(HX_SDK)/lib/linux/libvstdlib.so libvstdlib.so; - ln -sf $(HX_SDK)/lib/linux/libtier0.so libtier0.so; -# - clang $(HX_INCLUDE) $(HX_FLAGS) -o Release/l4dtoolz_mm.o -c l4dtoolz_mm.cpp - clang $(HX_INCLUDE) $(HX_FLAGS) -o Release/signature.o -c signature.cpp - clang $(HX_INCLUDE) $(HX_LIB) -m32 -shared -static-libgcc -ldl -lm -o Release/l4dtoolz_mm.so -# - rm -rf Release/*.o diff --git a/game_offsets.h b/game_offsets.h new file mode 100644 index 0000000..8ea0379 --- /dev/null +++ b/game_offsets.h @@ -0,0 +1,20 @@ +#ifndef _INCLUDE_GAME_OFFSETS_ +#define _INCLUDE_GAME_OFFSETS_ + +#ifdef WIN32 +int maxplayers_offs = 138; // m_nMaxClientsLimit (in CGameServer::SetMaxClients) +#if SOURCE_ENGINE == SE_LEFT4DEAD +#include "l4d1_offsets_win32.h" +#else +#include "l4d2_offsets_win32.h" +#endif +#else +int maxplayers_offs = 136; // m_nMaxClientsLimit (in CGameServer::SetMaxClients) +#if SOURCE_ENGINE == SE_LEFT4DEAD +#include "l4d1_offsets_linux.h" +#else +#include "l4d2_offsets_linux.h" +#endif +#endif + +#endif //_INCLUDE_GAME_OFFSETS_ diff --git a/game_signature.h b/game_signature.h deleted file mode 100644 index f9c6dec..0000000 --- a/game_signature.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _INCLUDE_GAME_SIGNATURE_ -#define _INCLUDE_GAME_SIGNATURE_ - -#ifdef WIN32 -#if SOURCE_ENGINE == SE_LEFT4DEAD -#include "l4d1_signature_win32.h" -#else -#include "l4d2_signature_win32.h" -#endif -#else -#if SOURCE_ENGINE == SE_LEFT4DEAD -#include "l4d1_signature_linux.h" -#else -#include "l4d2_signature_linux.h" -#endif -#endif - -#endif //_INCLUDE_GAME_SIGNATURE_ diff --git a/l4d1_offsets_linux.h b/l4d1_offsets_linux.h new file mode 100644 index 0000000..5714697 --- /dev/null +++ b/l4d1_offsets_linux.h @@ -0,0 +1,9 @@ +#ifndef _INCLUDE_L4D1_OFFSETS_LINUX_ +#define _INCLUDE_L4D1_OFFSETS_LINUX_ + +const char* engine_dll = "engine.so"; +int slots_offs = 94; // m_numGameSlots (in CGameServer::ExecGameTypeCfg) +int reservation_idx = 61; // CBaseServer::ReplyReservationRequest(netadr_s&, bf_read&) vtable +int maxhuman_idx = 132; // CTerrorGameRules::GetMaxHumanPlayers vtable + +#endif //_INCLUDE_L4D1_OFFSETS_LINUX_ diff --git a/l4d1_offsets_win32.h b/l4d1_offsets_win32.h new file mode 100644 index 0000000..b576673 --- /dev/null +++ b/l4d1_offsets_win32.h @@ -0,0 +1,9 @@ +#ifndef _INCLUDE_L4D1_OFFSETS_WIN32_ +#define _INCLUDE_L4D1_OFFSETS_WIN32_ + +int sv_offs = 6; // IServer pointer (in IVEngineServer::CreateFakeClient) +int slots_offs = 95; // m_numGameSlots (in CGameServer::ExecGameTypeCfg) +int reservation_idx = 60; // CBaseServer::ReplyReservationRequest(netadr_s&, bf_read&) vtable +int maxhuman_idx = 131; // CTerrorGameRules::GetMaxHumanPlayers vtable + +#endif //_INCLUDE_L4D1_OFFSETS_WIN32_ diff --git a/l4d1_signature_linux.h b/l4d1_signature_linux.h deleted file mode 100644 index 05f90ee..0000000 --- a/l4d1_signature_linux.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _INCLUDE_L4D1_SIGNATURE_LINUX_ -#define _INCLUDE_L4D1_SIGNATURE_LINUX_ - -const char* server_dll[] = {"server.so", 0}; -const char* engine_dll[] = {"engine.so", 0}; -const char* matchmaking_dll[] = {"matchmaking_ds.so", "matchmaking.so", 0}; - -//CBaseServer::GetMaxHumanPlayers(void)const -//function is in engine.so -const char* friends_lobby = "\x0F\x53\x83\xEC\x18\xE8\xC3\xC3\xC3\xC3\x81\xC3\xB9\xB8\x23\x00"; -unsigned char friends_lobby_new[] = {0x06, 0x00, 0xB8, 0x3C, 0x00, 0x00, 0x00, 0xC3}; -void* friends_lobby_org = NULL; - -//CBaseServer::ConnectClient(netadr_s &, int, int, int, char const*, char const*, char const*, int, CUtlVector> &, bool) -//function is in engine.so, loc jz/jnz, above #Valve_Reject_Hidden_Game -const char* lobby_sux = "\x0A\x8B\xAE\x78\x01\x00\x00\x85\xED\x0F\x85"; -const char* lobby_sux_new = "\x06\x00\xB8\x01\x00\x00\x00\x90"; -void* lobby_sux_org = NULL; - -//CBaseServer::ConnectClient(netadr_s &, int, int, int, char const*, char const*, char const*, int, CUtlVector> &, bool) -//function is in engine.so, above #Valve_Reject_Server_Full -const char* max_players = "\x0F\xFF\x50\xC3\x29\xC7\x01\xFD\x3B\xC3\xC3\xC3\x00\x00\x0F\x8E"; -unsigned char max_players_new[]= {0x06, 0x07, 0x83, 0xFF, 0x3C, 0x90, 0x90, 0x90}; -void* max_players_org = NULL; - -//CServerGameClients::GetMaxHumanPlayers(void) -//function is in server.so -const char* server_bplayers = "\x1F\x53\x83\xEC\xC3\xE8\xC3\xC3\xC3\xC3\x81\xC3\xC3\xC3\xC3\xC3\x8B\x83\xC3\xC3\xC3\xC3\x8B\x00\x85\xC0\x74\x10\x8B\x10\x89\x04"; -unsigned char server_bplayers_new[] = {0x06, 0x00, 0xB8, 0x3C, 0x00, 0x00, 0x00, 0xC3}; -void* server_bplayers_org = NULL; - -//CTerrorGameRules::ClientConnected(edict_t *, char const*, char const*, char *, int) -//function is in server.so -const char* human_limit = "\x12\x8B\x00\xC3\xC3\x24\x40\x89\x14\xC3\xFF\x90\xC3\x02\x00\x00\x39\xF0\x7E"; -const char* human_limit_new = "\x02\x11\x90\x90"; -void* human_limit_org = NULL; - -//CGameServer::SetMaxClients(int) -//function is in engine.so -const char* players = "\x13\x83\xBB\xC3\xC3\xC3\xC3\x01\x7F\xC3\x8B\x80\x0C\xC3\xC3\x00\x89\xC3\xC3\xE8"; -const char* players_new = "\x03\x1D\x89\xC2\xEB"; -void* players_org = NULL; - -//CBaseServer::SetReservationCookie(unsigned long long, char const*, ...) -//function in engine.so -const char* unreserved = "\x15\x55\x57\x56\x53\x81\xEC\x3C\x01\x00\x00\xE8\xC3\xC3\xC3\xC3\x81\xC3\x33\x8E\x23\x00"; -const char* unreserved_new = "\x01\x00\xC3"; -void* unreserved_org = NULL; - -//CMatchTitle::GetTotalNumPlayersSupported(void) -//function in matchmaking_ds.so -const char* lobby_match = "\x06\xB8\x08\x00\x00\x00\xC3"; -unsigned char lobby_match_new[] = {0x01, 0x02, 0xC3}; -void* lobby_match_org = NULL; - -#endif //_INCLUDE_L4D1_SIGNATURE_LINUX_ - diff --git a/l4d1_signature_win32.h b/l4d1_signature_win32.h deleted file mode 100644 index 681920c..0000000 --- a/l4d1_signature_win32.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _INCLUDE_L4D1_SIGNATURE_WIN32_ -#define _INCLUDE_L4D1_SIGNATURE_WIN32_ - -const char* server_dll[] = {"server.dll", 0}; -const char* engine_dll[] = {"engine.dll", 0}; -const char* matchmaking_dll[] = {"matchmaking_ds.dll", "matchmaking.dll", 0}; - -const char* friends_lobby = "\x12\x56\x8B\xF1\x8B\x0D\xC3\xC3\xC3\xC3\x85\xC9\x74\xC3\x8B\x01\x8B\x50\x48"; -unsigned char friends_lobby_new[] = {0x06, 0x00, 0xB8, 0x3C, 0x00, 0x00, 0x00, 0xC3}; -void* friends_lobby_org = NULL; - -const char* max_players = "\x28\x83\xBE\xC3\xC3\x00\x00\x00\x74\xC3\x8B\x54\xC3\xC3\x8B\x06\x8B\x7A\xC3\x8B\x50\x10\x8B\xCE\xFF\xD2\x2B\xF8\x8B\x06\x8B\x50\x08\x8B\xCE\xFF\xD2\x03\xC7\x3B\x86"; -unsigned char max_players_new[]= {0x06, 0x26, 0x83, 0xF8, 0x3C, 0x90, 0x90, 0x90}; -void* max_players_org = NULL; - -const char* lobby_sux_new = "\x02\x07\x90\x90"; -void* lobby_sux_org = NULL; - -const char* server_bplayers = "\x1D\x56\x8B\xF1\xE8\xC3\xC3\xC3\xC3\x8B\x4C\x24\x08\x89\x01\x8B\x0D\xC3\xC3\xC3\xC3\x85\xC9\x74\xC3\x8B\x11\x8B\x42\x48"; -unsigned char server_bplayers_new[] = {0x05, 0x1A, 0xB8, 0x3C, 0x00, 0x00, 0x00}; -void* server_bplayers_org = NULL; - -const char* human_limit = "\x15\x8B\x13\x8B\x82\xC3\xC3\x00\x00\x8B\xCB\xFF\xD0\x3B\xF8\x7C\xC3\x8B\xC3\xC3\xC3\x8B"; -const char* human_limit_new = "\x01\x0E\xEB"; -void* human_limit_org = NULL; - -const char* players = "\x10\x56\x8B\xF1\x8B\x86\xC3\x02\x00\x00\x8B\x4C\x24\x08\x3B\xC8\x7F"; -const char* players_new = "\x02\x0F\x90\x90"; -void* players_org = NULL; - -const char* unreserved = "\x1E\x81\xEC\xC3\xC3\x00\x00\x55\x8B\xAC\xC3\xC3\xC3\x00\x00\x56\x8B\xB4\xC3\xC3\xC3\x00\x00\x57\x8B\xBC\xC3\xC3\xC3\x00\x00"; -const char* unreserved_new = "\x01\x00\xC3"; -void* unreserved_org = NULL; - -const char* lobby_match = "\x06\xB8\x08\x00\x00\x00\xC3"; -unsigned char lobby_match_new[] = {0x01, 0x01, 0xC3}; -void* lobby_match_org = NULL; - -#endif //_INCLUDE_L4D1_SIGNATURE_WIN32_ - diff --git a/l4d2_offsets_linux.h b/l4d2_offsets_linux.h new file mode 100644 index 0000000..fc4b19b --- /dev/null +++ b/l4d2_offsets_linux.h @@ -0,0 +1,9 @@ +#ifndef _INCLUDE_L4D2_OFFSETS_LINUX_ +#define _INCLUDE_L4D2_OFFSETS_LINUX_ + +const char* engine_dll = "engine_srv.so"; +int slots_offs = 95; // m_numGameSlots (in CGameServer::ExecGameTypeCfg) +int reservation_idx = 62; // CBaseServer::ReplyReservationRequest(netadr_s&, bf_read&) vtable +int maxhuman_idx = 137; // CTerrorGameRules::GetMaxHumanPlayers vtable + +#endif //_INCLUDE_L4D2_OFFSETS_LINUX_ diff --git a/l4d2_offsets_win32.h b/l4d2_offsets_win32.h new file mode 100644 index 0000000..0ad164f --- /dev/null +++ b/l4d2_offsets_win32.h @@ -0,0 +1,9 @@ +#ifndef _INCLUDE_L4D2_OFFSETS_WIN32_ +#define _INCLUDE_L4D2_OFFSETS_WIN32_ + +int sv_offs = 8; // IServer pointer (in IVEngineServer::CreateFakeClient) +int slots_offs = 96; // m_numGameSlots (in CGameServer::ExecGameTypeCfg) +int reservation_idx = 61; // CBaseServer::ReplyReservationRequest(netadr_s&, bf_read&) vtable +int maxhuman_idx = 136; // CTerrorGameRules::GetMaxHumanPlayers vtable + +#endif //_INCLUDE_L4D2_OFFSETS_WIN32_ diff --git a/l4d2_signature_linux.h b/l4d2_signature_linux.h deleted file mode 100644 index 24f55c8..0000000 --- a/l4d2_signature_linux.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _INCLUDE_L4D2_SIGNATURE_LINUX_ -#define _INCLUDE_L4D2_SIGNATURE_LINUX_ - -const char* server_dll[] = {"server_srv.so", "server.so", 0}; -const char* engine_dll[] = {"engine_srv.so", "engine.so", 0}; -const char* matchmaking_dll[] = {"matchmaking_ds_srv.so", "matchmaking_srv.so", "matchmaking_ds.so", "matchmaking.so", 0}; - -const char* lobby_sux = "\x13\xFF\x50\xC3\x84\xC0\x0F\x84\xC3\xC3\xC3\xC3\x8B\x87\xC3\x01\x00\x00\x85\xC0"; -const char* lobby_sux_new = "\x06\x0B\xBE\x01\x00\x00\x00\x90"; -void *lobby_sux_org = NULL; - -const char* max_players = "\x15\x89\xC6\x8B\x07\x89\x3C\x24\xFF\x50\xC3\x29\xC6\x01\xF3\x3B\x9F\xC3\x01\x00\x00\x0F"; -unsigned char max_players_new[]= {0x06, 0x0E, 0x83, 0xFB, 0x3C, 0x90, 0x90, 0x90}; -void *max_players_org = NULL; - -const char* server_bplayers = "\x16\x55\x89\xE5\x83\xEC\x08\xE8\xC3\xC3\xC3\xC3\xC9\x3C\x01\x19\xC0\x83\xE0\xFC\x83\xC0\x08"; -unsigned char server_bplayers_new[] = {0x06, 0x00, 0xB8, 0x3C, 0x00, 0x00, 0x00, 0xC3}; -void *server_bplayers_org = NULL; - -const char* players = "\x23\x55\x89\xE5\x53\x83\xEC\xC3\x8B\x5D\x08\x8B\x55\x0C\x8B\x83\xC3\xC3\x00\x00\x39\xD0\x7C\x0B\x8B\x83\xC3\xC3\x00\x00\x39\xC2\x0F\x4D\xC2\x8B"; -const char* players_new = "\x03\x13\x89\xD0\xEB"; -void *players_org = NULL; - -const char* unreserved = "\x0F\x55\x89\xE5\x57\x56\x53\x81\xEC\x3C\x01\x00\x00\x8B\x5D\x08"; -const char* unreserved_new = "\x01\x00\xC3"; -void *unreserved_org = NULL; - -const char* lobby_match = "\x0A\x55\xB8\x08\x00\x00\x00\x89\xE5\x5D\xC3"; -unsigned char lobby_match_new[] = {0x01, 0x02, 0xC3}; -void *lobby_match_org = NULL; - -#endif //_INCLUDE_L4D2_SIGNATURE_LINUX_ - diff --git a/l4d2_signature_win32.h b/l4d2_signature_win32.h deleted file mode 100644 index f879e4c..0000000 --- a/l4d2_signature_win32.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _INCLUDE_L4D2_SIGNATURE_WIN32_ -#define _INCLUDE_L4D2_SIGNATURE_WIN32_ - -const char* server_dll[] = {"server.dll", 0}; -const char* engine_dll[] = {"engine.dll", 0}; -const char* matchmaking_dll[] = {"matchmaking_ds.dll", "matchmaking.dll", 0}; - -const char* max_players = "\x27\x83\xBE\xC3\xC3\x00\x00\x00\x74\xC3\x8B\xC3\xC3\x8B\x06\x8B\x7A\xC3\x8B\x50\x10\x8B\xCE\xFF\xD2\x2B\xF8\x8B\x06\x8B\x50\x08\x8B\xCE\xFF\xD2\x03\xC7\x3B\x86"; -unsigned char max_players_new[]= {0x06, 0x25, 0x83, 0xF8, 0x3C, 0x90, 0x90, 0x90}; -void* max_players_org = NULL; - -const char* lobby_sux_new = "\x02\x07\x90\x90"; -void* lobby_sux_org = NULL; - -const char* server_bplayers = "\x36\xF7\x05\xC3\xC3\xC3\xC3\x00\x10\x00\x00\x74\x07\xB8\xC3\xC3\xC3\xC3\xEB\x11\xA1\xC3\xC3\xC3\xC3\x8B\x40\x24\x85\xC0\x75\x05\xB8\xC3\xC3\xC3\xC3\x8B\x0D\xC3\xC3\xC3\xC3\x8B\x11\x50\x8B\x42\x10\xFF\xD0\x85\xC0\x74\x17"; -unsigned char server_bplayers_new[] = {0x06, 0x00, 0xB8, 0x3C, 0x00, 0x00, 0x00, 0xC3}; -void* server_bplayers_org = NULL; - -const char* players = "\x0F\x56\x8B\xF1\x8B\x86\xC3\x02\x00\x00\x8B\x4D\xC3\x3B\xC8\x7F"; -const char* players_new = "\x02\x0E\x90\x90"; -void* players_org = NULL; - -const char* unreserved = "\x21\x55\x8B\xEC\x81\xEC\xC3\xC3\x00\x00\xA1\xC3\xC3\xC3\xC3\x33\xC5\x89\xC3\xC3\x53\x8B\xC3\xC3\x56\x8B\xC3\xC3\x57\x8B\xC3\xC3\x3B\xBE"; -const char* unreserved_new = "\x01\x00\xC3"; -void* unreserved_org = NULL; - -const char* lobby_match = "\x06\xB8\x08\x00\x00\x00\xC3"; -unsigned char lobby_match_new[] = {0x01, 0x01, 0xC3}; -void* lobby_match_org = NULL; - -#endif //_INCLUDE_L4D2_SIGNATURE_WIN32_ - diff --git a/l4dtoolz_mm.cpp b/l4dtoolz_mm.cpp index b4d7f15..f53a9b0 100644 --- a/l4dtoolz_mm.cpp +++ b/l4dtoolz_mm.cpp @@ -1,166 +1,123 @@ #include "l4dtoolz_mm.h" -#include "signature.h" -#include "game_signature.h" +#include "game_offsets.h" +#include "memutils.h" #include "icommandline.h" +#include "server_class.h" +#include "sourcehook.h" +#include "matchmaking/imatchframework.h" l4dtoolz g_l4dtoolz; +IServerGameDLL* gamedll = NULL; +IServerGameClients* gameclients = NULL; IVEngineServer* engine = NULL; +IMatchFramework* g_pMatchFramework = NULL; ICvar* g_pCVar = NULL; +IServer* g_pGameIServer = NULL; +void* g_pGameRules = nullptr; +int m_numGameSlots = -1; -#if SOURCE_ENGINE == SE_LEFT4DEAD -void* l4dtoolz::max_players_friend_lobby = NULL; -void* l4dtoolz::chuman_limit = NULL; -#endif - -void* l4dtoolz::max_players_connect = NULL; -void* l4dtoolz::max_players_server_browser = NULL; -void* l4dtoolz::lobby_sux_ptr = NULL; -void* l4dtoolz::tmp_player = NULL; -void* l4dtoolz::unreserved_ptr = NULL; -void* l4dtoolz::lobby_match_ptr = NULL; +SH_DECL_HOOK1_void(IServerGameDLL, ApplyGameSettings, SH_NOATTRIB, 0, KeyValues*); +SH_DECL_HOOK0(IMatchTitle, GetTotalNumPlayersSupported, SH_NOATTRIB, 0, int); +SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, char const *, char const *, char const *, char const *, bool, bool); +SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, 0); +SH_DECL_MANUALHOOK0(CTerrorGameRules_GetMaxHumanPlayers, maxhuman_idx, 0, 0, int); +SH_DECL_MANUALHOOK2_void(CBaseServer_ReplyReservationRequest, reservation_idx, 0, 0, netadr_s&, CBitRead&); ConVar sv_maxplayers("sv_maxplayers", "-1", 0, "Max Human Players", true, -1, true, 32, l4dtoolz::OnChangeMaxplayers); -#if SOURCE_ENGINE == SE_LEFT4DEAD -ConVar sv_removehumanlimit("sv_removehumanlimit", "0", 0, "Remove Human limit reached kick", true, 0, true, 1, l4dtoolz::OnChangeRemovehumanlimit); -#endif ConVar sv_force_unreserved("sv_force_unreserved", "0", 0, "Disallow lobby reservation cookie", true, 0, true, 1, l4dtoolz::OnChangeUnreserved); void l4dtoolz::OnChangeMaxplayers ( IConVar *var, const char *pOldValue, float flOldValue ) { int new_value = ((ConVar*)var)->GetInt(); int old_value = atoi(pOldValue); -#if SOURCE_ENGINE == SE_LEFT4DEAD - if (max_players_friend_lobby == NULL || max_players_connect == NULL || max_players_server_browser == NULL || lobby_sux_ptr == NULL) { -#else - if (max_players_connect == NULL || max_players_server_browser == NULL || lobby_sux_ptr == NULL) { -#endif - Msg("sv_maxplayers init error\n"); + if (g_pGameIServer == NULL) { + Msg("g_pGameIServer pointer is not available\n"); return; } if(new_value != old_value) { if(new_value >= 0) { -#if SOURCE_ENGINE == SE_LEFT4DEAD - max_players_new[4] = friends_lobby_new[3] = server_bplayers_new[3] = (unsigned char)new_value; -#else - max_players_new[4] = server_bplayers_new[3] = (unsigned char)new_value; -#endif - if(lobby_match_ptr) { - lobby_match_new[2] = (unsigned char)new_value; - write_signature(lobby_match_ptr, lobby_match_new); - } else { - Msg("sv_maxplayers MS init error\n"); - } -#if SOURCE_ENGINE == SE_LEFT4DEAD - write_signature(max_players_friend_lobby, friends_lobby_new); -#endif - write_signature(max_players_connect, max_players_new); - write_signature(lobby_sux_ptr, lobby_sux_new); - write_signature(max_players_server_browser, server_bplayers_new); + m_numGameSlots = new_value; + *(int*)(((uint**)g_pGameIServer)+slots_offs) = m_numGameSlots; } else { -#if SOURCE_ENGINE == SE_LEFT4DEAD - write_signature(max_players_friend_lobby, friends_lobby_org); -#endif - write_signature(max_players_connect, max_players_org); - write_signature(lobby_sux_ptr, lobby_sux_org); - write_signature(max_players_server_browser, server_bplayers_org); - - if(lobby_match_ptr) - write_signature(lobby_match_ptr, lobby_match_org); + m_numGameSlots = -1; } } } -#if SOURCE_ENGINE == SE_LEFT4DEAD -void l4dtoolz::OnChangeRemovehumanlimit ( IConVar *var, const char *pOldValue, float flOldValue ) -{ - int new_value = ((ConVar*)var)->GetInt(); - int old_value = atoi(pOldValue); - if(chuman_limit == NULL) { - Msg( "sv_removehumanlimit init error\n"); - return; - } - if(new_value != old_value) { - if(new_value == 1) { - write_signature(chuman_limit, human_limit_new); - }else{ - write_signature(chuman_limit, human_limit_org); - } - } -} -#endif - void l4dtoolz::OnChangeUnreserved ( IConVar *var, const char *pOldValue, float flOldValue ) { int new_value = ((ConVar*)var)->GetInt(); int old_value = atoi(pOldValue); - if(unreserved_ptr == NULL ) { - Msg("unreserved_ptr init error\n"); + if (g_pGameIServer == NULL) { + Msg("g_pGameIServer pointer is not available\n"); return; } if(new_value != old_value) { if(new_value == 1) { - write_signature(unreserved_ptr, unreserved_new); engine->ServerCommand("sv_allow_lobby_connect_only 0\n"); - } else { - write_signature(unreserved_ptr, unreserved_org); } } } +void Hook_ApplyGameSettings(KeyValues *pKV) +{ + if (!pKV) { + return; + } + m_numGameSlots = sv_maxplayers.GetInt(); + if (m_numGameSlots == -1) { + return; + } + pKV->SetInt("members/numSlots", m_numGameSlots); +} + +void Hook_ReplyReservationRequest(netadr_s& adr, CBitRead& inmsg) +{ + if (sv_force_unreserved.GetInt()) { + RETURN_META(MRES_SUPERCEDE); + } + RETURN_META(MRES_IGNORED); +} + +int Hook_GetMaxHumanPlayers() +{ + if (m_numGameSlots > 0) { + RETURN_META_VALUE(MRES_SUPERCEDE, m_numGameSlots); + } + RETURN_META_VALUE(MRES_IGNORED, m_numGameSlots); +} + PLUGIN_EXPOSE(l4dtoolz, g_l4dtoolz); bool l4dtoolz::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) { PLUGIN_SAVEVARS(); + GET_V_IFACE_CURRENT(GetServerFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); + GET_V_IFACE_CURRENT(GetServerFactory, gameclients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS); GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); + GET_V_IFACE_CURRENT(GetEngineFactory, g_pMatchFramework, IMatchFramework, IMATCHFRAMEWORK_VERSION_STRING); GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); - ConVar_Register(0, this); - - struct base_addr_t base_addr; - base_addr.addr = NULL; - base_addr.len = 0; - - find_base_from_list(matchmaking_dll, &base_addr); - - if(!lobby_match_ptr) { - lobby_match_ptr = find_signature(lobby_match, &base_addr, 1); - get_original_signature(lobby_match_ptr, lobby_match_new, lobby_match_org); - } - - find_base_from_list(engine_dll, &base_addr); -#if SOURCE_ENGINE == SE_LEFT4DEAD - if(!max_players_friend_lobby) { - max_players_friend_lobby = find_signature(friends_lobby, &base_addr, 0); - get_original_signature(max_players_friend_lobby, friends_lobby_new, friends_lobby_org); - } -#endif - if(!max_players_connect) { - max_players_connect = find_signature(max_players, &base_addr, 0); - get_original_signature(max_players_connect, max_players_new, max_players_org); - } - if(!lobby_sux_ptr) { -#ifdef WIN32 - lobby_sux_ptr = max_players_connect; + void* handle = NULL; +#ifdef PLATFORM_WINDOWS + if (!(handle=SH_GET_ORIG_VFNPTR_ENTRY(engine, &IVEngineServer::CreateFakeClient))) { + Warning("Failed to get address 'IVEngineServer::CreateFakeClient'\n"); + } else { + g_pGameIServer = *reinterpret_cast(reinterpret_cast(handle)+sv_offs); #else - lobby_sux_ptr = find_signature(lobby_sux, &base_addr, 0); + if (!(handle=dlopen(engine_dll, RTLD_LAZY))) { + Warning("Could't open library '%s'\n", engine_dll); + } else { + g_pGameIServer = (IServer *)g_MemUtils.ResolveSymbol(handle, "sv"); + dlclose(handle); #endif - get_original_signature(lobby_sux_ptr, lobby_sux_new, lobby_sux_org); - } -#if SOURCE_ENGINE == SE_LEFT4DEAD -#ifdef WIN32 - if(!max_players_server_browser) { - max_players_server_browser = find_signature(server_bplayers, &base_addr, 0); - get_original_signature(max_players_server_browser, server_bplayers_new, server_bplayers_org); - } -#endif -#endif - if(!tmp_player) { - tmp_player = find_signature(players, &base_addr, 0); - if(tmp_player) { - get_original_signature(tmp_player, players_new, players_org); - write_signature(tmp_player, players_new); + int* m_nMaxClientsLimit = (int*)(((uint**)g_pGameIServer)+maxplayers_offs); + if (*m_nMaxClientsLimit != 0x12) { + Warning("Couldn't patch maxplayers\n"); + g_pGameIServer = NULL; + } else { + *m_nMaxClientsLimit = 0x20; const char *pszCmdLineMax; if(CommandLine()->CheckParm("-maxplayers", &pszCmdLineMax) || CommandLine()->CheckParm("+maxplayers", &pszCmdLineMax)) { char command[32]; @@ -170,62 +127,75 @@ bool l4dtoolz::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool engine->ServerCommand("maxplayers 31\n"); } engine->ServerExecute(); - write_signature(tmp_player, players_org); - free(players_org); - players_org = NULL; } } - if(!unreserved_ptr) { - unreserved_ptr = find_signature(unreserved, &base_addr, 0); - get_original_signature(unreserved_ptr, unreserved_new, unreserved_org); + + SH_ADD_HOOK(IServerGameDLL, ApplyGameSettings, gamedll, SH_STATIC(Hook_ApplyGameSettings), true); + SH_ADD_HOOK(IMatchTitle, GetTotalNumPlayersSupported, g_pMatchFramework->GetMatchTitle(), SH_STATIC(Hook_GetMaxHumanPlayers), false); + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &l4dtoolz::LevelInit, true); + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &l4dtoolz::LevelShutdown, false); + + if (g_pGameIServer) { + SH_ADD_MANUALHOOK(CBaseServer_ReplyReservationRequest, g_pGameIServer, SH_STATIC(Hook_ReplyReservationRequest), false); + } else { + Warning("g_pGameIServer pointer is not available\n"); } - find_base_from_list(server_dll, &base_addr); -#if SOURCE_ENGINE == SE_LEFT4DEAD - if(!chuman_limit) { - chuman_limit = find_signature(human_limit, &base_addr, 0); - get_original_signature(chuman_limit, human_limit_new, human_limit_org); - } -#ifndef WIN32 - if(!max_players_server_browser) { - max_players_server_browser = find_signature(server_bplayers, &base_addr, 0); - get_original_signature(max_players_server_browser, server_bplayers_new, server_bplayers_org); - } -#endif -#else - if(!max_players_server_browser) { - max_players_server_browser = find_signature(server_bplayers, &base_addr, 0); - get_original_signature(max_players_server_browser, server_bplayers_new, server_bplayers_org); - } -#endif + ConVar_Register(0, this); return true; } bool l4dtoolz::Unload(char *error, size_t maxlen) { -#if SOURCE_ENGINE == SE_LEFT4DEAD - write_signature(max_players_friend_lobby, friends_lobby_org); - write_signature(chuman_limit, human_limit_org); - free(friends_lobby_org); - free(human_limit_org); -#endif + SH_REMOVE_HOOK(IServerGameDLL, ApplyGameSettings, gamedll, SH_STATIC(Hook_ApplyGameSettings), true); + SH_REMOVE_HOOK(IMatchTitle, GetTotalNumPlayersSupported, g_pMatchFramework->GetMatchTitle(), SH_STATIC(Hook_GetMaxHumanPlayers), false); + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &l4dtoolz::LevelInit, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &l4dtoolz::LevelShutdown, false); - write_signature(max_players_connect, max_players_org); - write_signature(lobby_sux_ptr, lobby_sux_org); - write_signature(max_players_server_browser, server_bplayers_org); - write_signature(unreserved_ptr, unreserved_org); - write_signature(lobby_match_ptr, lobby_match_org); + if (g_pGameIServer) { + SH_REMOVE_MANUALHOOK(CBaseServer_ReplyReservationRequest, g_pGameIServer, SH_STATIC(Hook_ReplyReservationRequest), false); + } - free(max_players_org); - free(lobby_sux_org); - free(server_bplayers_org); - free(unreserved_org); - free(lobby_match_org); + LevelShutdown(); + ConVar_Unregister(); return true; } +bool l4dtoolz::LevelInit(const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) +{ + g_pGameRules = nullptr; + + ServerClass *pServerClass = UTIL_FindServerClass("CTerrorGameRulesProxy"); + if (pServerClass) { + int i, iCount; + iCount = pServerClass->m_pTable->GetNumProps(); + for (i = 0; i < iCount; i++) { + if (stricmp(pServerClass->m_pTable->GetProp(i)->GetName(), "terror_gamerules_data") == 0) { + g_pGameRules = (*pServerClass->m_pTable->GetProp(i)->GetDataTableProxyFn())(NULL, NULL, NULL, NULL, 0); + break; + } + } + } + + if (g_pGameRules) { + SH_ADD_MANUALHOOK(CTerrorGameRules_GetMaxHumanPlayers, g_pGameRules, SH_STATIC(Hook_GetMaxHumanPlayers), false); + } else { + Warning("g_pGameRules pointer is not available\n"); + } + + return true; +} + +void l4dtoolz::LevelShutdown() +{ + if (g_pGameRules) { + SH_REMOVE_MANUALHOOK(CTerrorGameRules_GetMaxHumanPlayers, g_pGameRules, SH_STATIC(Hook_GetMaxHumanPlayers), false); + g_pGameRules = nullptr; + } +} + size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) { va_list ap; @@ -243,6 +213,21 @@ size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) return len; } +ServerClass *UTIL_FindServerClass(const char *classname) +{ + ServerClass *sc = gamedll->GetAllServerClasses(); + while (sc) + { + if (strcmp(classname, sc->GetName()) == 0) + { + return sc; + } + sc = sc->m_pNext; + } + + return NULL; +} + const char *l4dtoolz::GetLicense() { return "GPLv3"; @@ -250,7 +235,7 @@ const char *l4dtoolz::GetLicense() const char *l4dtoolz::GetVersion() { - return "1.1.0.2"; + return "2.0.0"; } const char *l4dtoolz::GetDate() diff --git a/l4dtoolz_mm.h b/l4dtoolz_mm.h index f1ab688..b5e5b23 100644 --- a/l4dtoolz_mm.h +++ b/l4dtoolz_mm.h @@ -8,6 +8,8 @@ class l4dtoolz : public ISmmPlugin, public IConCommandBaseAccessor public: bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late); bool Unload(char *error, size_t maxlen); + bool LevelInit(const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); + void LevelShutdown(); public: const char *GetAuthor(); const char *GetName(); @@ -20,25 +22,12 @@ public: public: //IConCommandBaseAccessor bool RegisterConCommandBase(ConCommandBase *pVar); public: - static void OnChangeMaxplayers ( IConVar *var, const char *pOldValue, float flOldValue ); static void OnChangeUnreserved ( IConVar *var, const char *pOldValue, float flOldValue ); - -#if SOURCE_ENGINE == SE_LEFT4DEAD - static void OnChangeRemovehumanlimit ( IConVar *var, const char *pOldValue, float flOldValue ); - static void* max_players_friend_lobby; - static void* chuman_limit; -#endif - - static void* max_players_connect; - static void* max_players_server_browser; - static void* lobby_sux_ptr; - static void* tmp_player; - static void* unreserved_ptr; - static void* lobby_match_ptr; }; size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...); +ServerClass *UTIL_FindServerClass(const char *classname); extern l4dtoolz g_l4dtoolz; diff --git a/memutils.cpp b/memutils.cpp new file mode 100644 index 0000000..bf323bf --- /dev/null +++ b/memutils.cpp @@ -0,0 +1,395 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * MemoryUtils + * Copyright (C) 2004-2011 AlliedModders LLC., 2011 Prodigysim + * All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, the authors give you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + */ + +#include "memutils.h" +#include + +#if SH_SYS == SH_SYS_LINUX +#include +#include +#include +#include + +#define PAGE_SIZE 4096 +#define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) +#define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1)) +#define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC +#endif + +#if SH_SYS == SH_SYS_APPLE +#include +#include +#include +#include +#include + +/* Define things from 10.6 SDK for older SDKs */ +#ifndef MAC_OS_X_VERSION_10_6 +struct task_dyld_info +{ + mach_vm_address_t all_image_info_addr; + mach_vm_size_t all_image_info_size; +}; +typedef struct task_dyld_info task_dyld_info_data_t; +#define TASK_DYLD_INFO 17 +#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) +#endif // MAC_OS_X_VERSION_10_6 +#endif // SH_SYS_APPLE + +MemoryUtils g_MemUtils; + +MemoryUtils::MemoryUtils() +{ +#if SH_SYS == SH_SYS_APPLE + + Gestalt(gestaltSystemVersionMajor, &m_OSXMajor); + Gestalt(gestaltSystemVersionMinor, &m_OSXMinor); + + /* Get pointer to struct that describes all loaded mach-o images in process */ + if ((m_OSXMajor == 10 && m_OSXMinor >= 6) || m_OSXMajor > 10) + { + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); + m_ImageList = (struct dyld_all_image_infos *)dyld_info.all_image_info_addr; + } + else + { + struct nlist list[2]; + memset(list, 0, sizeof(list)); + list[0].n_un.n_name = (char *)"_dyld_all_image_infos"; + nlist("/usr/lib/dyld", list); + m_ImageList = (struct dyld_all_image_infos *)list[0].n_value; + } + +#endif +} + +MemoryUtils::~MemoryUtils() +{ +#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE + for (size_t i = 0; i < m_SymTables.size(); i++) + { + delete m_SymTables[i]; + } + m_SymTables.clear(); +#endif +} + +void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol) +{ +#if SH_SYS == SH_SYS_WIN32 + + return GetProcAddress((HMODULE)handle, symbol); + +#elif SH_SYS == SH_SYS_LINUX + + struct link_map *dlmap; + struct stat dlstat; + int dlfile; + uintptr_t map_base; + Elf32_Ehdr *file_hdr; + Elf32_Shdr *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr; + Elf32_Sym *symtab; + const char *shstrtab, *strtab; + uint16_t section_count; + uint32_t symbol_count; + LibSymbolTable *libtable; + SymbolTable *table; + Symbol *symbol_entry; + + dlmap = (struct link_map *)handle; + symtab_hdr = NULL; + strtab_hdr = NULL; + table = NULL; + + /* See if we already have a symbol table for this library */ + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlmap->l_addr) + { + table = &libtable->table; + break; + } + } + + /* If we don't have a symbol table for this library, then create one */ + if (table == NULL) + { + libtable = new LibSymbolTable(); + libtable->table.Initialize(); + libtable->lib_base = dlmap->l_addr; + libtable->last_pos = 0; + table = &libtable->table; + m_SymTables.push_back(libtable); + } + + /* See if the symbol is already cached in our table */ + symbol_entry = table->FindSymbol(symbol, strlen(symbol)); + if (symbol_entry != NULL) + { + return symbol_entry->address; + } + + /* If symbol isn't in our table, then we have open the actual library */ + dlfile = open(dlmap->l_name, O_RDONLY); + if (dlfile == -1 || fstat(dlfile, &dlstat) == -1) + { + close(dlfile); + return NULL; + } + + /* Map library file into memory */ + file_hdr = (Elf32_Ehdr *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0); + map_base = (uintptr_t)file_hdr; + if (file_hdr == MAP_FAILED) + { + close(dlfile); + return NULL; + } + close(dlfile); + + if (file_hdr->e_shoff == 0 || file_hdr->e_shstrndx == SHN_UNDEF) + { + munmap(file_hdr, dlstat.st_size); + return NULL; + } + + sections = (Elf32_Shdr *)(map_base + file_hdr->e_shoff); + section_count = file_hdr->e_shnum; + /* Get ELF section header string table */ + shstrtab_hdr = §ions[file_hdr->e_shstrndx]; + shstrtab = (const char *)(map_base + shstrtab_hdr->sh_offset); + + /* Iterate sections while looking for ELF symbol table and string table */ + for (uint16_t i = 0; i < section_count; i++) + { + Elf32_Shdr &hdr = sections[i]; + const char *section_name = shstrtab + hdr.sh_name; + + if (strcmp(section_name, ".symtab") == 0) + { + symtab_hdr = &hdr; + } + else if (strcmp(section_name, ".strtab") == 0) + { + strtab_hdr = &hdr; + } + } + + /* Uh oh, we don't have a symbol table or a string table */ + if (symtab_hdr == NULL || strtab_hdr == NULL) + { + munmap(file_hdr, dlstat.st_size); + return NULL; + } + + symtab = (Elf32_Sym *)(map_base + symtab_hdr->sh_offset); + strtab = (const char *)(map_base + strtab_hdr->sh_offset); + symbol_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize; + + /* Iterate symbol table starting from the position we were at last time */ + for (uint32_t i = libtable->last_pos; i < symbol_count; i++) + { + Elf32_Sym &sym = symtab[i]; + unsigned char sym_type = ELF32_ST_TYPE(sym.st_info); + const char *sym_name = strtab + sym.st_name; + Symbol *cur_sym; + + /* Skip symbols that are undefined or do not refer to functions or objects */ + if (sym.st_shndx == SHN_UNDEF || (sym_type != STT_FUNC && sym_type != STT_OBJECT)) + { + continue; + } + + /* Caching symbols as we go along */ + cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value)); + if (strcmp(symbol, sym_name) == 0) + { + symbol_entry = cur_sym; + libtable->last_pos = ++i; + break; + } + } + + munmap(file_hdr, dlstat.st_size); + return symbol_entry ? symbol_entry->address : NULL; + +#elif SH_SYS == SH_SYS_APPLE + + uintptr_t dlbase, linkedit_addr; + uint32_t image_count; + struct mach_header *file_hdr; + struct load_command *loadcmds; + struct segment_command *linkedit_hdr; + struct symtab_command *symtab_hdr; + struct nlist *symtab; + const char *strtab; + uint32_t loadcmd_count; + uint32_t symbol_count; + LibSymbolTable *libtable; + SymbolTable *table; + Symbol *symbol_entry; + + dlbase = 0; + image_count = m_ImageList->infoArrayCount; + linkedit_hdr = NULL; + symtab_hdr = NULL; + table = NULL; + + /* Loop through mach-o images in process. + * We can skip index 0 since that is just the executable. + */ + for (uint32_t i = 1; i < image_count; i++) + { + const struct dyld_image_info &info = m_ImageList->infoArray[i]; + + /* "Load" each one until we get a matching handle */ + void *h = dlopen(info.imageFilePath, RTLD_NOLOAD); + if (h == handle) + { + dlbase = (uintptr_t)info.imageLoadAddress; + dlclose(h); + break; + } + + dlclose(h); + } + + if (!dlbase) + { + /* Uh oh, we couldn't find a matching handle */ + return NULL; + } + + /* See if we already have a symbol table for this library */ + for (size_t i = 0; i < m_SymTables.size(); i++) + { + libtable = m_SymTables[i]; + if (libtable->lib_base == dlbase) + { + table = &libtable->table; + break; + } + } + + /* If we don't have a symbol table for this library, then create one */ + if (table == NULL) + { + libtable = new LibSymbolTable(); + libtable->table.Initialize(); + libtable->lib_base = dlbase; + libtable->last_pos = 0; + table = &libtable->table; + m_SymTables.push_back(libtable); + } + + /* See if the symbol is already cached in our table */ + symbol_entry = table->FindSymbol(symbol, strlen(symbol)); + if (symbol_entry != NULL) + { + return symbol_entry->address; + } + + /* If symbol isn't in our table, then we have to locate it in memory */ + + file_hdr = (struct mach_header *)dlbase; + loadcmds = (struct load_command *)(dlbase + sizeof(struct mach_header)); + loadcmd_count = file_hdr->ncmds; + + /* Loop through load commands until we find the ones for the symbol table */ + for (uint32_t i = 0; i < loadcmd_count; i++) + { + if (loadcmds->cmd == LC_SEGMENT && !linkedit_hdr) + { + struct segment_command *seg = (struct segment_command *)loadcmds; + if (strcmp(seg->segname, "__LINKEDIT") == 0) + { + linkedit_hdr = seg; + if (symtab_hdr) + { + break; + } + } + } + else if (loadcmds->cmd == LC_SYMTAB) + { + symtab_hdr = (struct symtab_command *)loadcmds; + if (linkedit_hdr) + { + break; + } + } + + /* Load commands are not of a fixed size which is why we add the size */ + loadcmds = (struct load_command *)((uintptr_t)loadcmds + loadcmds->cmdsize); + } + + if (!linkedit_hdr || !symtab_hdr || !symtab_hdr->symoff || !symtab_hdr->stroff) + { + /* Uh oh, no symbol table */ + return NULL; + } + + linkedit_addr = dlbase + linkedit_hdr->vmaddr; + symtab = (struct nlist *)(linkedit_addr + symtab_hdr->symoff - linkedit_hdr->fileoff); + strtab = (const char *)(linkedit_addr + symtab_hdr->stroff - linkedit_hdr->fileoff); + symbol_count = symtab_hdr->nsyms; + + /* Iterate symbol table starting from the position we were at last time */ + for (uint32_t i = libtable->last_pos; i < symbol_count; i++) + { + struct nlist &sym = symtab[i]; + /* Ignore the prepended underscore on all symbols, so +1 here */ + const char *sym_name = strtab + sym.n_un.n_strx + 1; + Symbol *cur_sym; + + /* Skip symbols that are undefined */ + if (sym.n_sect == NO_SECT) + { + continue; + } + + /* Caching symbols as we go along */ + cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlbase + sym.n_value)); + if (strcmp(symbol, sym_name) == 0) + { + symbol_entry = cur_sym; + libtable->last_pos = ++i; + break; + } + } + + return symbol_entry ? symbol_entry->address : NULL; + +#endif + return NULL; +} diff --git a/memutils.h b/memutils.h new file mode 100644 index 0000000..b0bec24 --- /dev/null +++ b/memutils.h @@ -0,0 +1,79 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * MemoryUtils + * Copyright (C) 2004-2011 AlliedModders LLC., 2011 ProdigySim + * All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, the authors give you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + */ + +#ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ +#define _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ + +#include +#include + +#include "sourcehook.h" +#include "sh_memory.h" + +#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE +#include +#include "sm_symtable.h" +using SourceHook::CVector; +#endif + +#if SH_SYS == SH_SYS_APPLE +#include +#endif + +#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE +struct LibSymbolTable +{ + SymbolTable table; + uintptr_t lib_base; + uint32_t last_pos; +}; +#endif + +class MemoryUtils +{ +public: + MemoryUtils(); + ~MemoryUtils(); + void *ResolveSymbol(void *handle, const char *symbol); + +#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE +private: + CVector m_SymTables; +#if SH_SYS == SH_SYS_APPLE + struct dyld_all_image_infos *m_ImageList; + SInt32 m_OSXMajor; + SInt32 m_OSXMinor; +#endif +#endif +}; + +extern MemoryUtils g_MemUtils; + +#endif // _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ diff --git a/signature.cpp b/signature.cpp deleted file mode 100644 index 611325c..0000000 --- a/signature.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include -#include -#include -#include "signature.h" - -#ifdef WIN32 -#include -#include -#else -#include -#include -#include -#endif - -#define SIGN_HEADER_LEN 2 -#define SIGN_LEN_BYTE 0 -#define SIGN_OFFSET_BYTE 1 - - -static int lock_region(const void *addr, unsigned int sign_len, unsigned int sign_off, int lock) -{ -#ifndef WIN32 - unsigned int all_adr; - unsigned int all_size; - unsigned int p_size; - unsigned int u_addr; - - p_size = sysconf(_SC_PAGESIZE); - u_addr = (unsigned int)addr; - - all_adr = (u_addr + sign_off) & ~(p_size-1); - all_size = u_addr - all_adr + sign_len; - - if(lock) { - mlock((void *)all_adr, all_size); - mprotect((void *)all_adr, all_size, PROT_READ|PROT_WRITE|PROT_EXEC); - } else - munlock((void *)all_adr, all_size); -#endif - return 0; -} - -void *find_signature(const char* mask, struct base_addr_t *base_addr, int pure) -{ - char *pBasePtr = (char *)base_addr->addr; - char *pEndPtr = pBasePtr+base_addr->len-(int)mask[SIGN_LEN_BYTE]; - int i; - char* tmp; - if(base_addr->addr == NULL) - return NULL; - -#ifndef WIN32 - unsigned int p_size = sysconf(_SC_PAGESIZE); - char* all_adr = (char*)((unsigned int)pBasePtr & ~(p_size-1)); - unsigned int size = pEndPtr - all_adr; - mlock(all_adr, size); -#endif - - while(pBasePtr < pEndPtr) - { - tmp = pBasePtr; - - for(i = 1; i <= mask[SIGN_LEN_BYTE]; ++i) { - if(!pure && mask[i] == '\xC3'){ - tmp++; - continue; - } - if(mask[i] != *tmp ) { - break; - } - tmp++; - } - if(i-1 == mask[0]) { -#ifndef WIN32 - munlock(all_adr, size); -#endif - return pBasePtr; - } - - pBasePtr++; - } - -#ifndef WIN32 - munlock(all_adr, size); -#endif - return NULL; -} - -#ifndef WIN32 -struct v_data{ - const char *fname; - void *baddr; - unsigned int blen; -}; - -static int callback(struct dl_phdr_info *info, size_t size, void *data) -{ - int i; - const char* filename; - - if (!info->dlpi_name || !info->dlpi_name[0]) - return 0; - - filename = ((struct v_data *)data)->fname; - - if(strstr(info->dlpi_name, filename)) { - if(strstr( info->dlpi_name, "metamod") == NULL) { - ((struct v_data *)data)->baddr = (void*)info->dlpi_addr; - ((struct v_data *)data)->blen = 0; - for(i = 0; i < info->dlpi_phnum; ++i) { - ((struct v_data *)data)->blen+=info->dlpi_phdr[i].p_filesz; - break; - } - return 1; - } - } - return 0; -} -#endif - -int find_base(const char* name, struct base_addr_t *base_addr) -{ -#ifdef WIN32 - MODULEENTRY32 modent; - HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); - if(hModuleSnap == INVALID_HANDLE_VALUE) { - return 0; - } - - modent.dwSize = sizeof(MODULEENTRY32); - - if(!Module32Next(hModuleSnap, &modent)){ - CloseHandle(hModuleSnap); - return 0; - } - do { - if(strstr( modent.szExePath, name)) { - if(strstr( modent.szExePath, "metamod")) - continue; - base_addr->addr = modent.modBaseAddr; - base_addr->len = modent.modBaseSize; - CloseHandle(hModuleSnap); - return 1; - } - } while(Module32Next(hModuleSnap, &modent)); - CloseHandle(hModuleSnap); -#else - struct v_data vdata; - vdata.fname = name; - if(dl_iterate_phdr(callback, &vdata)){ - base_addr->addr = vdata.baddr; - base_addr->len = vdata.blen; - return 1; - } -#endif - base_addr->addr = NULL; - base_addr->len = 0; - return 0; - -} - -int find_base_from_list(const char *name[], struct base_addr_t *base_addr) -{ - int ret = 0; - int i = 0; - base_addr->addr = NULL; - base_addr->len = 0; - - if (name == NULL) - return 0; - - while (name[i] != NULL && !(ret = find_base(name[i], base_addr))) - i++; - - return ret; -} - -int write_signature(const void* addr, const void* signature) -{ - if(!addr) - return 0; - - unsigned int u_addr_sign; - unsigned int sign_len; - unsigned int sign_off; - unsigned int u_addr; - - sign_len = ((unsigned char *)signature)[SIGN_LEN_BYTE]; - sign_off = ((unsigned char *)signature)[SIGN_OFFSET_BYTE]; - u_addr = (unsigned int)addr; - u_addr_sign = (unsigned int)signature; - -#ifdef WIN32 - HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); - WriteProcessMemory(h_process, (void *)(u_addr+sign_off), (void *)(u_addr_sign+SIGN_HEADER_LEN), sign_len, NULL); - CloseHandle(h_process); -#else - lock_region(addr, sign_len, sign_off, 1); - memcpy((void *)(u_addr+sign_off), (void *)(u_addr_sign+SIGN_HEADER_LEN), sign_len); - lock_region(addr, sign_len, sign_off, 0); -#endif - - return 1; -} - -int read_signature(const void *addr, void *signature) -{ - unsigned int u_addr_sign; - unsigned int sign_len; - unsigned int sign_off; - unsigned int u_addr; - - sign_len = ((unsigned char *)signature)[SIGN_LEN_BYTE]; - sign_off = ((unsigned char *)signature)[SIGN_OFFSET_BYTE]; - u_addr = (unsigned int)addr; - u_addr_sign = (unsigned int)signature; - -#ifdef WIN32 - HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); - ReadProcessMemory(h_process, (void *)(u_addr+sign_off), (void *)(u_addr_sign+SIGN_HEADER_LEN), sign_len, NULL); - CloseHandle(h_process); -#else - lock_region(addr, sign_len, sign_off, 1); - memcpy((void *)(u_addr_sign+SIGN_HEADER_LEN), (void *)(u_addr+sign_off), sign_len); - lock_region(addr, sign_len, sign_off, 0); -#endif - - return 0; -} - -int get_original_signature(const void *offset, const void *new_sig, void *&org_sig) -{ - unsigned int sign_len; - - if(!offset) - return 0; - - sign_len = ((unsigned char *)new_sig)[SIGN_LEN_BYTE]; - org_sig = malloc(sign_len + SIGN_HEADER_LEN); - memcpy(org_sig, new_sig, SIGN_HEADER_LEN); - return read_signature(offset, org_sig); -} - diff --git a/signature.h b/signature.h deleted file mode 100644 index 30323cd..0000000 --- a/signature.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _INCLUDE_SIGNATURE_ -#define _INCLUDE_SIGNATURE_ - -struct base_addr_t{ - void *addr; - unsigned int len; -}; - -void *find_signature(const char *mask, struct base_addr_t *base_addr, int pure); -int find_base(const char *name, struct base_addr_t *base_addr); -int find_base_from_list(const char *name[], struct base_addr_t *base_addr); -int write_signature(const void *addr, const void *signature); -int read_signature(const void *addr, void *signature); -int get_original_signature(const void *offset, const void *new_sig, void *&org_sig); - -#endif //_INCLUDE_SIGNATURE_ diff --git a/sm_symtable.h b/sm_symtable.h new file mode 100644 index 0000000..277a155 --- /dev/null +++ b/sm_symtable.h @@ -0,0 +1,232 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + */ + +#ifndef _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ +#define _INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_ + +#include +#include +#include +#include + +#define KESTRING_TABLE_START_SIZE 65536 + +struct Symbol +{ + size_t length; + uint32_t hash; + void *address; + Symbol *tbl_next; + + inline char *buffer() + { + return reinterpret_cast(this + 1); + } +}; + +class SymbolTable +{ +public: + ~SymbolTable() + { + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + free(sym); + sym = next; + } + } + free(buckets); + } + + bool Initialize() + { + buckets = (Symbol **)malloc(sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + if (buckets == NULL) + { + return false; + } + memset(buckets, 0, sizeof(Symbol *) * KESTRING_TABLE_START_SIZE); + + nbuckets = KESTRING_TABLE_START_SIZE; + nused = 0; + bucketmask = KESTRING_TABLE_START_SIZE - 1; + return true; + } + + static inline uint32_t HashString(const char *data, size_t len) + { + #undef get16bits + #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) + #define get16bits(d) (*((const uint16_t *) (d))) + #endif + #if !defined (get16bits) + #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) + #endif + uint32_t hash = len, tmp; + int rem; + + if (len <= 0 || data == NULL) + { + return 0; + } + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2 * sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= data[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += *data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; + + #undef get16bits + } + + Symbol **FindSymbolBucket(const char *str, size_t len, uint32_t hash) + { + uint32_t bucket = hash & bucketmask; + Symbol **pkvs = &buckets[bucket]; + + Symbol *kvs = *pkvs; + while (kvs != NULL) + { + if (len == kvs->length && memcmp(str, kvs->buffer(), len * sizeof(char)) == 0) + { + return pkvs; + } + pkvs = &kvs->tbl_next; + kvs = *pkvs; + } + + return pkvs; + } + + void ResizeSymbolTable() + { + uint32_t xnbuckets = nbuckets * 2; + Symbol **xbuckets = (Symbol **)malloc(sizeof(Symbol *) * xnbuckets); + if (xbuckets == NULL) + { + return; + } + memset(xbuckets, 0, sizeof(Symbol *) * xnbuckets); + uint32_t xbucketmask = xnbuckets - 1; + for (uint32_t i = 0; i < nbuckets; i++) + { + Symbol *sym = buckets[i]; + while (sym != NULL) + { + Symbol *next = sym->tbl_next; + uint32_t bucket = sym->hash & xbucketmask; + sym->tbl_next = xbuckets[bucket]; + xbuckets[bucket] = sym; + sym = next; + } + } + free(buckets); + buckets = xbuckets; + nbuckets = xnbuckets; + bucketmask = xbucketmask; + } + + Symbol *FindSymbol(const char *str, size_t len) + { + uint32_t hash = HashString(str, len); + Symbol **pkvs = FindSymbolBucket(str, len, hash); + return *pkvs; + } + + Symbol *InternSymbol(const char* str, size_t len, void *address) + { + uint32_t hash = HashString(str, len); + Symbol **pkvs = FindSymbolBucket(str, len, hash); + if (*pkvs != NULL) + { + return *pkvs; + } + + Symbol *kvs = (Symbol *)malloc(sizeof(Symbol) + sizeof(char) * (len + 1)); + kvs->length = len; + kvs->hash = hash; + kvs->address = address; + kvs->tbl_next = NULL; + memcpy(kvs + 1, str, sizeof(char) * (len + 1)); + *pkvs = kvs; + nused++; + + if (nused > nbuckets && nbuckets <= INT_MAX / 2) + { + ResizeSymbolTable(); + } + + return kvs; + } +private: + uint32_t nbuckets; + uint32_t nused; + uint32_t bucketmask; + Symbol **buckets; +}; + +#endif //_INCLUDE_SOURCEMOD_CORE_SYMBOLTABLE_H_