diff --git a/core-legacy/CSmmAPI.cpp b/core-legacy/CSmmAPI.cpp index 5e64b8a..97c1cb9 100644 --- a/core-legacy/CSmmAPI.cpp +++ b/core-legacy/CSmmAPI.cpp @@ -14,6 +14,8 @@ #include "concommands.h" #include "CPlugin.h" #include "util.h" +#include "sh_memory.h" +#include /** * @brief Implementation of main API interface @@ -21,22 +23,32 @@ */ using namespace SourceMM; +using namespace SourceHook; + +struct UsrMsgInfo +{ + UsrMsgInfo() + { + } + UsrMsgInfo(int s, const char *t) : size(s), name(t) + { + } + int size; + String name; +}; CSmmAPI g_SmmAPI; +static CVector usermsgs_list; +static jmp_buf usermsg_end; + CSmmAPI::CSmmAPI() { m_ConPrintf = NULL; m_CmdCache = false; - m_MsgCount = -1; m_VSP = false; } -CSmmAPI::~CSmmAPI() -{ - m_UserMessages.RemoveAll(); -} - void CSmmAPI::LogMsg(ISmmPlugin *pl, const char *msg, ...) { va_list ap; @@ -491,149 +503,96 @@ int CSmmAPI::GetGameDLLVersion() return g_GameDllVersion; } -////////////////////////////////////////////////////////////////////// -// EVEN MORE HACKS HERE! YOU HAVE BEEN WARNED! // -// Signatures necessary in finding the pointer to the CUtlDict that // -// stores user message information. // -// IServerGameDLL::GetUserMessageInfo() normally crashes with bad // -// message indices. This is our answer to it. Yuck! <:-( // -////////////////////////////////////////////////////////////////////// -#ifdef OS_WIN32 - /* General Windows sig */ - #define MSGCLASS_SIGLEN 7 - #define MSGCLASS_SIG "\x8B\x0D\x2A\x2A\x2A\x2A\x56" - #define MSGCLASS_OFFS 2 - - /* Dystopia Wimdows hack */ - #define MSGCLASS2_SIGLEN 16 - #define MSGCLASS2_SIG "\x56\x8B\x74\x24\x2A\x85\xF6\x7C\x2A\x3B\x35\x2A\x2A\x2A\x2A\x7D" - #define MSGCLASS2_OFFS 11 - - /* Windows frame pointer sig */ - #define MSGCLASS3_SIGLEN 18 - #define MSGCLASS3_SIG "\x55\x8B\xEC\x51\x89\x2A\x2A\x8B\x2A\x2A\x50\x8B\x0D\x2A\x2A\x2A\x2A\xE8" - #define MSGCLASS3_OFFS 13 -#elif defined OS_LINUX - /* No frame pointer sig */ - #define MSGCLASS_SIGLEN 14 - #define MSGCLASS_SIG "\x53\x83\xEC\x2A\x8B\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x89" - #define MSGCLASS_OFFS 9 - - /* Frame pointer sig */ - #define MSGCLASS2_SIGLEN 16 - #define MSGCLASS2_SIG "\x55\x89\xE5\x53\x83\xEC\x2A\x8B\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x89" - #define MSGCLASS2_OFFS 11 -#endif - -/* This is the ugliest function in all of SourceMM */ -/* :TODO: Make this prettier */ -bool CSmmAPI::CacheUserMessages() +/* This only gets called if IServerGameDLL::GetUserMessageInfo() triggers it */ +void Detour_Error(const tchar *pMsg, ...) { - UserMsgDict *dict = NULL; - char *vfunc = UTIL_GetOrigFunction(&IServerGameDLL::GetUserMessageInfo, g_GameDll.pGameDLL); - - if (!vfunc) - { - return false; - } - - if (UTIL_VerifySignature(vfunc, MSGCLASS_SIG, MSGCLASS_SIGLEN)) - { - /* Get address of CUserMessages instance */ - char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS_OFFS); - - /* Get address of CUserMessages::m_UserMessages */ - dict = reinterpret_cast(*userMsgClass); - } else if (UTIL_VerifySignature(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) { - #ifdef OS_WIN32 - /* If we get here, the code is possibly inlined like in Dystopia */ - - /* Get the address of the CUtlRBTree */ - char *rbtree = *reinterpret_cast(vfunc + MSGCLASS2_OFFS); - - /* CUtlDict should be 8 bytes before the CUtlRBTree (hacktacular!) */ - dict = reinterpret_cast(rbtree - 8); - #elif defined OS_LINUX - /* Get address of CUserMessages instance */ - char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS2_OFFS); - - /* Get address of CUserMessages::m_UserMessages */ - dict = reinterpret_cast(*userMsgClass); - #endif - #ifdef OS_WIN32 - } else if (UTIL_VerifySignature(vfunc, MSGCLASS3_SIG, MSGCLASS3_SIGLEN)) { - /* Get address of CUserMessages instance */ - char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS3_OFFS); - - /* Get address of CUserMessages::m_UserMessages */ - dict = reinterpret_cast(*userMsgClass); - #endif - } - - if (dict) - { - m_MsgCount = dict->Count(); - - /* Ensure that count is within bounds of an unsigned byte, because that's what engine supports */ - if (m_MsgCount < 0 || m_MsgCount > 255) - { - m_MsgCount = -1; - return false; - } - - UserMessage *msg; - - /* Cache messages in our CUtlDict */ - for (int i = 0; i < m_MsgCount; i++) - { - msg = dict->Element(i); - m_UserMessages.Insert(msg->name, msg); - } - - return true; - } - - return false; + /* Jump back to setjmp() in CacheUserMessages() */ + longjmp(usermsg_end, 1); } -bool CSmmAPI::MsgCacheSuccessful() +/* IServerGameDLL::GetUserMessageInfo() crashes on games based on the old engine and + * early Orange Box. This is because Error() from tier0 gets called when a bad index is + * passed. This is all due to a bug in CUtlRBTree::IsValidIndex(). + * + * So we detour Error() to fix this. Our detour then jumps back into CacheUserMessages() + * to a point before GetUserMessageInfo() is called. The detour is then removed and we + * exit. + */ +void CSmmAPI::CacheUserMessages() { - return m_MsgCount > -1; + int q, size; + char buffer[256]; + unsigned char *target, *detour; + unsigned char orig_bytes[5]; + + target = (unsigned char *)&Error; + detour = (unsigned char *)&Detour_Error; + + /* Save bytes from target function */ + memcpy(orig_bytes, target, sizeof(orig_bytes)); + + /* Patch in relative jump to our Error() detour */ + SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + target[0] = IA32_JMP_IMM32; + *(int32_t *)&target[1] = (int32_t)(detour - (target + 5)); + + /* This is where longjmp() will end up */ + if (setjmp(usermsg_end)) + { + /* Restore bytes and memory protection */ + memcpy(target, orig_bytes, sizeof(orig_bytes)); + SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ|SH_MEM_EXEC); + return; + } + + q = 0; + + /* If GetUserMessageInfo() calls Error(), we should end up in our detour */ + while (g_GameDll.pGameDLL->GetUserMessageInfo(q, buffer, sizeof(buffer), size)) + { + usermsgs_list.push_back(UsrMsgInfo(size, buffer)); + q++; + } + + /* Jump back to setjmp() */ + longjmp(usermsg_end, 1); } int CSmmAPI::GetUserMessageCount() { - return m_MsgCount; + return (int)usermsgs_list.size(); } int CSmmAPI::FindUserMessage(const char *name, int *size) { - int index = m_UserMessages.Find(name); - - if (size && index > -1) + for (size_t i = 0; i < usermsgs_list.size(); i++) { - UserMessage *msg = m_UserMessages.Element(index); - *size = msg->size; + if (usermsgs_list[i].name.compare(name) == 0) + { + if (size) + { + *size = usermsgs_list[i].size; + } + return (int)i; + } } - return index; + return -1; } const char *CSmmAPI::GetUserMessage(int index, int *size) { - if (m_MsgCount <= 0 || index < 0 || index >= m_MsgCount) + if (index < 0 || index >= (int)usermsgs_list.size()) { return NULL; } - UserMessage *msg = m_UserMessages.Element(index); - if (size) { - *size = msg->size; + *size = usermsgs_list[index].size; } - return msg->name; + return usermsgs_list[index].name.c_str(); } IServerPluginCallbacks *CSmmAPI::GetVSPInfo(int *pVersion) diff --git a/core-legacy/CSmmAPI.h b/core-legacy/CSmmAPI.h index 95cb6df..76489f4 100644 --- a/core-legacy/CSmmAPI.h +++ b/core-legacy/CSmmAPI.h @@ -1,5 +1,5 @@ /* ======== SourceMM ======== - * Copyright (C) 2004-2008 Metamod:Source Development Team + * Copyright (C) 2004-2009 Metamod:Source Development Team * No warranties of any kind * * License: zlib/libpng @@ -17,15 +17,6 @@ */ #include "ISmmAPI.h" -#include - -struct UserMessage -{ - int size; - const char *name; -}; - -typedef CUtlDict UserMsgDict; typedef void (*CONPRINTF_FUNC)(const char *, ...); @@ -35,7 +26,6 @@ namespace SourceMM { public: CSmmAPI(); - ~CSmmAPI(); public: void LogMsg(ISmmPlugin *pl, const char *msg, ...); public: @@ -80,15 +70,12 @@ namespace SourceMM { return m_VSP; } - bool CacheUserMessages(); - bool MsgCacheSuccessful(); + void CacheUserMessages(); private: META_RES m_Res; CONPRINTF_FUNC m_ConPrintf; bool m_CmdCache; bool m_VSP; - int m_MsgCount; - UserMsgDict m_UserMessages; }; }; diff --git a/core-legacy/concommands.cpp b/core-legacy/concommands.cpp index 4f18550..282c789 100644 --- a/core-legacy/concommands.cpp +++ b/core-legacy/concommands.cpp @@ -176,29 +176,24 @@ CON_COMMAND(meta, "Metamod:Source Menu") } // Display user messages - if (g_SmmAPI.MsgCacheSuccessful()) + const char *msgname; + int msgsize; + int msgcount = g_SmmAPI.GetUserMessageCount(); + + if (msgcount > 0) { - const char *msgname; - int msgsize; - int msgcount = g_SmmAPI.GetUserMessageCount(); + CONMSG(" User Messages: %-32.31s %-5s %-5s\n", "Name", "Index", "Size"); - if (msgcount > 0) + for (int i = 0; i < msgcount; i++) { - CONMSG(" User Messages: %-32.31s %-5s %-5s\n", "Name", "Index", "Size"); + msgname = g_SmmAPI.GetUserMessage(i, &msgsize); - for (int i = 0; i < msgcount; i++) - { - msgname = g_SmmAPI.GetUserMessage(i, &msgsize); - - CONMSG(" %-32.31s %-5d %-5d\n", msgname, i, msgsize); - } - - CONMSG(" %d user message%s in total\n", msgcount, (msgcount > 1) ? "s" : ""); - } else { - CONMSG(" User Messages: None\n"); + CONMSG(" %-32.31s %-5d %-5d\n", msgname, i, msgsize); } + + CONMSG(" %d user message%s in total\n", msgcount, (msgcount > 1) ? "s" : ""); } else { - CONMSG(" User Messages: Failed to get list of user messages\n"); + CONMSG(" User Messages: None\n"); } return; diff --git a/core-legacy/oslink.h b/core-legacy/oslink.h index a21ea4d..56126a9 100644 --- a/core-legacy/oslink.h +++ b/core-legacy/oslink.h @@ -1,5 +1,5 @@ /* ======== SourceMM ======== - * Copyright (C) 2004-2008 Metamod:Source Development Team + * Copyright (C) 2004-2009 Metamod:Source Development Team * No warranties of any kind * * License: zlib/libpng @@ -75,6 +75,8 @@ bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength); #if defined __WIN32__ || defined _WIN32 || defined WIN32 typedef __int64 int64_t; typedef unsigned __int64 uint64_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; #elif defined __GNUC__ #include #if !__GLIBC_HAVE_LONG_LONG diff --git a/core-legacy/sourcemm.cpp b/core-legacy/sourcemm.cpp index 2c28b0f..2bc07c6 100644 --- a/core-legacy/sourcemm.cpp +++ b/core-legacy/sourcemm.cpp @@ -165,14 +165,7 @@ bool StartupMetamod(CreateInterfaceFn engineFactory, bool bWaitForGameInit) LogMessage("[META] Warning: Console messages will not be redirected to rcon console."); } - if (!g_SmmAPI.CacheUserMessages()) - { - /* Don't know of a mod that has stripped out user messages completely, - * but perhaps should do something different here? - */ - LogMessage("[META] Warning: Failed to get list of user messages."); - LogMessage("[META] Warning: The 'meta game' command will not display user messages."); - } + g_SmmAPI.CacheUserMessages(); baseFs = (IFileSystem *)((engineFactory)(FILESYSTEM_INTERFACE_VERSION, NULL)); if (baseFs == NULL) diff --git a/core/metamod_console.cpp b/core/metamod_console.cpp index 1a98f4d..ab35b0b 100644 --- a/core/metamod_console.cpp +++ b/core/metamod_console.cpp @@ -126,33 +126,26 @@ bool Command_Meta(IMetamodSourceCommandInfo *info) #endif // Display user messages + const char *msgname; + int msgsize; int messages = g_Metamod.GetUserMessageCount(); - if (messages != -1) + + if (messages > 0) { - const char *msgname; - int msgsize; + CONMSG(" User Messages: %-32.31s %-5s %-5s\n", "Name", "Index", "Size"); - if (messages > 0) + for (int i = 0; i < messages; i++) { - CONMSG(" User Messages: %-32.31s %-5s %-5s\n", "Name", "Index", "Size"); + msgname = g_Metamod.GetUserMessage(i, &msgsize); - for (int i = 0; i < messages; i++) - { - msgname = g_Metamod.GetUserMessage(i, &msgsize); - - CONMSG(" %-32.31s %-5d %-5d\n", msgname, i, msgsize); - } - - CONMSG(" %d user message%s in total\n", messages, (messages > 1) ? "s" : ""); - } - else - { - CONMSG(" User Messages: None\n"); + CONMSG(" %-32.31s %-5d %-5d\n", msgname, i, msgsize); } + + CONMSG(" %d user message%s in total\n", messages, (messages > 1) ? "s" : ""); } else { - CONMSG(" User Messages: Failed to get list of user messages\n"); + CONMSG(" User Messages: None\n"); } return true; diff --git a/core/metamod_oslink.h b/core/metamod_oslink.h index 3899faa..eb1b11a 100644 --- a/core/metamod_oslink.h +++ b/core/metamod_oslink.h @@ -1,8 +1,8 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ====================================================== * Metamod:Source - * Copyright (C) 2004-2008 AlliedModders LLC and authors. + * Copyright (C) 2004-2009 AlliedModders LLC and authors. * All rights reserved. * ====================================================== * @@ -21,8 +21,6 @@ * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id$ */ #ifndef _INCLUDE_OSLINK_H @@ -95,6 +93,8 @@ bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength); #if defined __WIN32__ || defined _WIN32 || defined WIN32 typedef __int64 int64_t; typedef unsigned __int64 uint64_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; #elif defined __GNUC__ #include #if !__GLIBC_HAVE_LONG_LONG diff --git a/core/provider/provider_ep2.cpp b/core/provider/provider_ep2.cpp index 50611d2..5a1e282 100644 --- a/core/provider/provider_ep2.cpp +++ b/core/provider/provider_ep2.cpp @@ -24,12 +24,12 @@ */ #include +#include #include "../metamod_oslink.h" #include #include #include #include -#include #include #include #include "../metamod_util.h" @@ -60,7 +60,8 @@ DLL_IMPORT ICommandLine *CommandLine(); #endif /* Functions */ -bool CacheUserMessages(); +void CacheUserMessages(); +void Detour_Error(const tchar *pMsg, ...); #if SOURCE_ENGINE >= SE_ORANGEBOX void ClientCommand(edict_t *pEdict, const CCommand &args); void LocalCommand_Meta(const CCommand &args); @@ -71,10 +72,10 @@ void LocalCommand_Meta(); void _ServerCommand(); /* Variables */ -static bool usermsgs_extracted = false; -static CVector usermsgs_list; static BaseProvider g_Ep1Provider; static List conbases_unreg; +static CVector usermsgs_list; +static jmp_buf usermsg_end; ICvar *icvar = NULL; IFileSystem *baseFs = NULL; @@ -90,22 +91,6 @@ SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *); #endif -bool AssumeUserMessages() -{ - int q, size; - char buffer[256]; - - q = 0; - - while (server->GetUserMessageInfo(q, buffer, sizeof(buffer), size)) - { - usermsgs_list.push_back(UsrMsgInfo(size, buffer)); - q++; - } - - return true; -} - void BaseProvider::ConsolePrint(const char *str) { #if SOURCE_ENGINE >= SE_ORANGEBOX @@ -155,10 +140,7 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, g_SMConVarAccessor.RegisterConCommandBase(&meta_local_cmd); - if ((usermsgs_extracted = CacheUserMessages()) == false) - { - usermsgs_extracted = AssumeUserMessages(); - } + CacheUserMessages(); #if SOURCE_ENGINE == SE_DARKMESSIAH if (!g_SMConVarAccessor.InitConCommandBaseList()) @@ -334,11 +316,6 @@ void BaseProvider::UnregisterConCommandBase(ConCommandBase *pCommand) int BaseProvider::GetUserMessageCount() { - if (!usermsgs_extracted) - { - return -1; - } - return (int)usermsgs_list.size(); } @@ -361,7 +338,7 @@ int BaseProvider::FindUserMessage(const char *name, int *size) const char *BaseProvider::GetUserMessage(int index, int *size) { - if (!usermsgs_extracted || index < 0 || index >= (int)usermsgs_list.size()) + if (index < 0 || index >= (int)usermsgs_list.size()) { return NULL; } @@ -534,153 +511,59 @@ void ClientCommand(edict_t *pEdict) RETURN_META(MRES_IGNORED); } -////////////////////////////////////////////////////////////////////// -// EVEN MORE HACKS HERE! YOU HAVE BEEN WARNED! // -// Signatures necessary in finding the pointer to the CUtlDict that // -// stores user message information. // -// IServerGameDLL::GetUserMessageInfo() normally crashes with bad // -// message indices. This is our answer to it. Yuck! <:-( // -////////////////////////////////////////////////////////////////////// -#ifdef OS_WIN32 - /* General Windows sig */ - #define MSGCLASS_SIGLEN 7 - #define MSGCLASS_SIG "\x8B\x0D\x2A\x2A\x2A\x2A\x56" - #define MSGCLASS_OFFS 2 - - /* Dystopia Windows hack */ - #define MSGCLASS2_SIGLEN 16 - #define MSGCLASS2_SIG "\x56\x8B\x74\x24\x2A\x85\xF6\x7C\x2A\x3B\x35\x2A\x2A\x2A\x2A\x7D" - #define MSGCLASS2_OFFS 11 - - /* Windows frame pointer sig */ - #define MSGCLASS3_SIGLEN 18 - #define MSGCLASS3_SIG "\x55\x8B\xEC\x51\x89\x2A\x2A\x8B\x2A\x2A\x50\x8B\x0D\x2A\x2A\x2A\x2A\xE8" - #define MSGCLASS3_OFFS 13 -#elif defined OS_LINUX - /* No frame pointer sig */ - #define MSGCLASS_SIGLEN 14 - #define MSGCLASS_SIG "\x53\x83\xEC\x2A\x8B\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x89" - #define MSGCLASS_OFFS 9 - - /* Frame pointer sig */ - #define MSGCLASS2_SIGLEN 16 - #define MSGCLASS2_SIG "\x55\x89\xE5\x53\x83\xEC\x2A\x8B\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x89" - #define MSGCLASS2_OFFS 11 -#endif - -struct UserMessage +/* This only gets called if IServerGameDLL::GetUserMessageInfo() triggers it */ +void Detour_Error(const tchar *pMsg, ...) { - int size; - const char *name; -}; - -typedef CUtlDict UserMsgDict; - -/* This is the ugliest function in all of MM:S */ -bool CacheUserMessages() -{ - UserMsgDict *dict = NULL; - - /* Get address of original GetUserMessageInfo() */ - char *vfunc = (char *)SH_GET_ORIG_VFNPTR_ENTRY(server, &IServerGameDLL::GetUserMessageInfo); - - /* Oh dear, we have a relative jump on our hands - * PVK II on Windows made me do this, but I suppose it doesn't hurt to check this on Linux too... - */ - if (*vfunc == '\xE9') - { - /* Get address from displacement... - * - * Add 5 because it's relative to next instruction: - * Opcode <1 byte> + 32-bit displacement <4 bytes> - */ - vfunc += *reinterpret_cast(vfunc + 1) + 5; - } - - if (UTIL_VerifySignature(vfunc, MSGCLASS_SIG, MSGCLASS_SIGLEN)) - { - /* Get address of CUserMessages instance */ - char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS_OFFS); - - /* Get address of CUserMessages::m_UserMessages */ - dict = reinterpret_cast(*userMsgClass); - } - else if (UTIL_VerifySignature(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) - { - #ifdef OS_WIN32 - /* If we get here, the code is possibly inlined like in Dystopia */ - - /* Get the address of the CUtlRBTree */ - char *rbtree = *reinterpret_cast(vfunc + MSGCLASS2_OFFS); - - /* CUtlDict should be 8 bytes before the CUtlRBTree (hacktacular!) */ - dict = reinterpret_cast(rbtree - 8); - #elif defined OS_LINUX - /* Get address of CUserMessages instance */ - char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS2_OFFS); - - /* Get address of CUserMessages::m_UserMessages */ - dict = reinterpret_cast(*userMsgClass); - #endif - #ifdef OS_WIN32 - } - else if (UTIL_VerifySignature(vfunc, MSGCLASS3_SIG, MSGCLASS3_SIGLEN)) - { - /* Get address of CUserMessages instance */ - char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS3_OFFS); - - /* Get address of CUserMessages::m_UserMessages */ - dict = reinterpret_cast(*userMsgClass); - #endif - } - - #if !defined OS_WIN32 - if (dict == NULL) - { - char path[255]; - if (GetFileOfAddress(vfunc, path, sizeof(path))) - { - void *handle = dlopen(path, RTLD_NOW); - if (handle != NULL) - { - void *addr = dlsym(handle, "usermessages"); - if (addr == NULL) - { - dlclose(handle); - return false; - } - dict = (UserMsgDict *)*(void **)addr; - dlclose(handle); - } - } - } - #endif - - if (dict != NULL) - { - int msg_count = dict->Count(); - - /* Ensure that count is within bounds of an unsigned byte, because that's what engine supports */ - if (msg_count < 0 || msg_count > 255) - { - return false; - } - - UserMessage *msg; - UsrMsgInfo u_msg; - - /* Cache messages in our CUtlDict */ - for (int i = 0; i < msg_count; i++) - { - msg = dict->Element(i); - u_msg.name = msg->name; - u_msg.size = msg->size; - usermsgs_list.push_back(u_msg); - } - - return true; - } - - return false; + /* Jump back to setjmp() in CacheUserMessages() */ + longjmp(usermsg_end, 1); } +#define IA32_JMP_IMM32 0xE9 + +/* IServerGameDLL::GetUserMessageInfo() crashes on games based on the old engine and + * early Orange Box. This is because Error() from tier0 gets called when a bad index is + * passed. This is all due to a bug in CUtlRBTree::IsValidIndex(). + * + * So we detour Error() to fix this. Our detour then jumps back into CacheUserMessages() + * to a point before GetUserMessageInfo() is called. The detour is then removed and we + * exit. + */ +void CacheUserMessages() +{ + int q, size; + char buffer[256]; + unsigned char *target, *detour; + unsigned char orig_bytes[5]; + + target = (unsigned char *)&Error; + detour = (unsigned char *)&Detour_Error; + + /* Save bytes from target function */ + memcpy(orig_bytes, target, sizeof(orig_bytes)); + + /* Patch in relative jump to our Error() detour */ + SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + target[0] = IA32_JMP_IMM32; + *(int32_t *)&target[1] = (int32_t)(detour - (target + 5)); + + /* This is where longjmp() will end up */ + if (setjmp(usermsg_end)) + { + /* Restore bytes and memory protection */ + memcpy(target, orig_bytes, sizeof(orig_bytes)); + SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ|SH_MEM_EXEC); + return; + } + + q = 0; + + /* If GetUserMessageInfo() calls Error(), we should end up in our detour */ + while (server->GetUserMessageInfo(q, buffer, sizeof(buffer), size)) + { + usermsgs_list.push_back(UsrMsgInfo(size, buffer)); + q++; + } + + /* Jump back to setjmp() */ + longjmp(usermsg_end, 1); +}