diff --git a/sourcemm/CSmmAPI.cpp b/sourcemm/CSmmAPI.cpp index 5cb9493..16b5894 100644 --- a/sourcemm/CSmmAPI.cpp +++ b/sourcemm/CSmmAPI.cpp @@ -225,22 +225,6 @@ void *CSmmAPI::MetaFactory(const char *iface, int *_ret, PluginId *id) #define ENGINEW32_OFFS 38 #define IA32_CALL 0xE8 -bool vcmp(const void *_addr1, const void *_addr2, size_t len) -{ - unsigned char *addr1 = (unsigned char *)_addr1; - unsigned char *addr2 = (unsigned char *)_addr2; - - for (size_t i=0; iGetCallback(); ptr = (unsigned char *)callback; #ifdef OS_LINUX - if (vcmp(ptr, ENGINE486_SIG, SIGLEN)) + if (UTIL_VerifySignature(ptr, ENGINE486_SIG, SIGLEN)) { offs = ENGINE486_OFFS; } - else if (vcmp(ptr, ENGINE686_SIG, SIGLEN)) + else if (UTIL_VerifySignature(ptr, ENGINE686_SIG, SIGLEN)) { offs = ENGINE686_OFFS; } - else if (vcmp(ptr, ENGINEAMD_SIG, SIGLEN)) + else if (UTIL_VerifySignature(ptr, ENGINEAMD_SIG, SIGLEN)) { offs = ENGINEAMD_OFFS; } #elif defined OS_WIN32 // Only one Windows engine binary so far... - if (vcmp(ptr, ENGINEW32_SIG, SIGLEN)) + if (UTIL_VerifySignature(ptr, ENGINEW32_SIG, SIGLEN)) { offs = ENGINEW32_OFFS; } @@ -541,45 +525,22 @@ int CSmmAPI::GetGameDLLVersion() /* :TODO: Make this prettier */ bool CSmmAPI::CacheUserMessages() { - SourceHook::MemFuncInfo info = {true, -1, 0, 0}; - SourceHook::GetFuncInfo(&IServerGameDLL::GetUserMessageInfo, info); - - /* Get address of original GetUserMessageInfo() */ - char *vfunc = reinterpret_cast(g_GameDllPatch->GetOrigFunc(info.vtbloffs, info.vtblindex)); - - /* If we can't get original function, that means there's no hook */ - if (vfunc == NULL) - { - /* Get virtual function address 'manually' then */ - char *adjustedptr = reinterpret_cast(g_GameDll.pGameDLL) + info.thisptroffs + info.vtbloffs; - char **vtable = *reinterpret_cast(adjustedptr); - - vfunc = vtable[info.vtblindex]; - } - - /* 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 = vfunc + *reinterpret_cast(vfunc + 1) + 5; - } - UserMsgDict *dict = NULL; + char *vfunc = UTIL_GetOrigFunction(&IServerGameDLL::GetUserMessageInfo, g_GameDll.pGameDLL, g_GameDllPatch); - if (vcmp(vfunc, MSGCLASS_SIG, MSGCLASS_SIGLEN)) + 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 (vcmp(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) { + } else if (UTIL_VerifySignature(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) { #ifdef OS_WIN32 /* If we get here, the code is possibly inlined like in Dystopia */ @@ -596,7 +557,7 @@ bool CSmmAPI::CacheUserMessages() dict = reinterpret_cast(*userMsgClass); #endif #ifdef OS_WIN32 - } else if (vcmp(vfunc, MSGCLASS3_SIG, MSGCLASS3_SIGLEN)) { + } else if (UTIL_VerifySignature(vfunc, MSGCLASS3_SIG, MSGCLASS3_SIGLEN)) { /* Get address of CUserMessages instance */ char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS3_OFFS); diff --git a/sourcemm/changelog.txt b/sourcemm/changelog.txt index 4961de2..93e8a76 100644 --- a/sourcemm/changelog.txt +++ b/sourcemm/changelog.txt @@ -1,12 +1,13 @@ -2008/??/?? 1.4.4: +2008/07/26 1.4.4: - Fixed a bug where loading plugins built for MM:S 1.3 or older (API ver <= 9) would cause a crash. - Fixed a bug where loading plugins using VDF files caused Metamod:Source - to crash on The Ship. + to crash on The Ship. (bug 1523) + - Fixed a class of crashes caused by improper cvar removal handling. (bug 1416) - Fixed a bug where VDF files were opened by MM:S even if the .vdf extension was not at the very end of the filename. For example, "plugin.vdf.disabled" - would have been opened in previous versions. - - Removed FCVAR_REPLICATED from MM:S convars. + would have been opened in previous versions. (bug 1534) + - Removed FCVAR_REPLICATED from MM:S convars. (bug 1479) 2008/01/23 1.4.3: - Metamod:Source can now be loaded via a .vdf instead of gameinfo.txt. @@ -23,7 +24,7 @@ - The client version of the "meta" command should now work with games using the latest Source beta (srcds0407). - Fixed amb233 (VSP listener didn't work with Steam dedicated version). - - Fixed amb277 (failed to get user message list in Kreedz Climbing). + - Fixed amb277 (failed to get user message list in Kreedz Climbing). 2007/04/05 1.4.0: - Added API functions for retrieving User Message info without potentially crashing. diff --git a/sourcemm/concommands.cpp b/sourcemm/concommands.cpp index e21bb31..72aed0b 100644 --- a/sourcemm/concommands.cpp +++ b/sourcemm/concommands.cpp @@ -9,6 +9,7 @@ */ #include +#include "convar_smm.h" #include "CSmmAPI.h" #include "concommands.h" #include "CPlugin.h" @@ -21,20 +22,25 @@ * @file concommands.cpp */ -CAlwaysRegisterableCommand g_EternalCommand; SMConVarAccessor g_SMConVarAccessor; +SMConVarAccessor::SMConVarAccessor() +{ + m_TopConCommandBase = NULL; +} + bool SMConVarAccessor::RegisterConCommandBase(ConCommandBase *pCommand) { - // Add the FCVAR_GAMEDLL flag - // => No crash on exit! - // UPDATE: Do _not_ add the FCVAR_GAMEDLL flag here, as it - // causes the command to be unusable on listenservers until you load a map - // We will set the FCVAR_GAMEDLL flag on all commands we have registered once we are being unloaded - //pCommand->AddFlags(FCVAR_GAMEDLL); + /* Add the FCVAR_GAMEDLL flag + * => No crash on exit! + * UPDATE: Do _not_ add the FCVAR_GAMEDLL flag here, as it + * causes the command to be unusable on listen servers until you load a map + * We will set the FCVAR_GAMEDLL flag on all commands we have registered once we are being unloaded + */ + // pCommand->AddFlags(FCVAR_GAMEDLL); m_RegisteredCommands.push_back(pCommand); - pCommand->SetNext( NULL ); + pCommand->SetNext(NULL); g_Engine.icvar->RegisterConCommandBase(pCommand); return true; @@ -42,8 +48,8 @@ bool SMConVarAccessor::RegisterConCommandBase(ConCommandBase *pCommand) bool SMConVarAccessor::Register(ConCommandBase *pCommand) { - //simple, don't mark as part of sourcemm! - pCommand->SetNext( NULL ); + /* Simple, don't mark as part of sourcemm! */ + pCommand->SetNext(NULL); g_Engine.icvar->RegisterConCommandBase(pCommand); return true; @@ -60,64 +66,47 @@ void SMConVarAccessor::MarkCommandsAsGameDLL() void SMConVarAccessor::Unregister(ConCommandBase *pCommand) { - ICvar *cv = g_Engine.icvar; - ConCommandBase *ptr = cv->GetCommands(); + ConCommandBase *pCur = NULL; + ConCommandBase *pPrev = NULL; - if (ptr == pCommand) + if (!pCommand || !pCommand->IsRegistered()) { - //first in list - g_EternalCommand.BringToFront(); - g_EternalCommand.SetNext(const_cast(pCommand->GetNext())); - } else { - //find us and unregister us - ConCommandBase *pPrev = NULL; - while (ptr) - { - if (ptr == pCommand) - break; - pPrev = ptr; - ptr = const_cast(ptr->GetNext()); - } - if (pPrev && ptr == pCommand) + return; + } + + pCur = g_Engine.icvar->GetCommands(); + pCommand->SetRegistered(false); + + if (!m_TopConCommandBase || !pCur) + { + return; + } + + if (pCur == pCommand) + { + *m_TopConCommandBase = const_cast(pCommand->GetNext()); + pCommand->SetNext(NULL); + return; + } + + pPrev = pCur; + pCur = const_cast(pCur->GetNext()); + + while (pCur) + { + if (pCur == pCommand) { pPrev->SetNext(const_cast(pCommand->GetNext())); + pCommand->SetNext(NULL); } - } -} -void SMConVarAccessor::UnregisterGameDLLCommands() -{ - ConCommandBase *begin = g_Engine.icvar->GetCommands(); - ConCommandBase *iter = begin; - ConCommandBase *prev = NULL; - while (iter) - { - // watch out for the ETERNAL COMMAND! - if (iter != &g_EternalCommand && iter->IsBitSet(FCVAR_GAMEDLL)) - { - // Remove it! - if (iter == begin) - { - g_EternalCommand.BringToFront(); - iter = const_cast(iter->GetNext()); - g_EternalCommand.SetNext(iter); - prev = &g_EternalCommand; - continue; - } - else - { - iter = const_cast(iter->GetNext()); - prev->SetNext(iter); - continue; - } - } - prev = iter; - iter = const_cast(iter->GetNext()); + pPrev = pCur; + pCur = const_cast(pCur->GetNext()); } } ConVar metamod_version("metamod_version", SOURCEMM_VERSION, FCVAR_SPONLY | FCVAR_NOTIFY, "Metamod:Source Version"); -#if defined WIN32 || defined _WIN32 +#ifdef OS_WIN32 ConVar mm_pluginsfile("mm_pluginsfile", "addons\\metamod\\metaplugins.ini", FCVAR_SPONLY, "Metamod:Source Plugins File"); ConVar mm_basedir("mm_basedir", "addons\\metamod", FCVAR_SPONLY, "Metamod:Source base folder"); #else @@ -646,53 +635,6 @@ CON_COMMAND(meta, "Metamod:Source Menu") CONMSG(" version - Version information\n"); } -CAlwaysRegisterableCommand::CAlwaysRegisterableCommand() -{ - Create("", NULL, FCVAR_UNREGISTERED|FCVAR_GAMEDLL); - m_pICvar = NULL; -} - -bool CAlwaysRegisterableCommand::IsRegistered( void ) const -{ - return false; -} - -void CAlwaysRegisterableCommand::BringToFront() -{ - if (!m_pICvar) - m_pICvar = g_Engine.icvar; - - // First, let's try to find us! - ConCommandBase *pPtr = m_pICvar->GetCommands(); - - if (pPtr == this) - { - // We are already at the beginning; Nothing to do - return; - } - - while (pPtr) - { - if (pPtr == this && pPtr->IsCommand() && stricmp(GetName(), pPtr->GetName()) == 0) - break; - ConCommandBase *pPrev = NULL; - while (pPtr) - { - if (pPtr == this) - break; - pPrev = pPtr; - pPtr = const_cast(pPtr->GetNext()); - } - if (pPrev && pPtr == this) - { - pPrev->SetNext(m_pNext); // Remove us from the list - } - // Now, register us - SetNext(NULL); - m_pICvar->RegisterConCommandBase(this); - } -} - void ClientCommand_handler(edict_t *client) { IVEngineServer *e = g_Engine.engine; @@ -800,3 +742,62 @@ const char *GetMetamodBaseDir() { return mm_basedir.GetString(); } + +/* Signature for ICvar::GetCommands() in vstdlib for Win32 and Linux. + * + * 20226EE0 A1 50 5C 5A 20 mov eax,dword ptr ds:[205A5C50h] <-- What we want + * 20226EE5 C3 ret + */ +#define CMDLIST_SIG "\xA1\x2A\x2A\x2A\x2A\xC3" +#define CMDLIST_SIGLEN 6 + +/* Linux symbol name of ConCommandBase list in vstdlib */ +#define CMDLIST_SYMBOL "_ZN14ConCommandBase18s_pConCommandBasesE" + +/* This function retrieves the address of the var that holds the top of the ConCommandBase list. + * Having this allows us to change the beginning of this list with ease. + * + * This craziness eliminates the need for the eternal command/cvar used previously which + * could have caused a crash as a result of registering commands/cvars more than once. + */ +bool SMConVarAccessor::InitConCommandBaseList() +{ + char *vfunc = UTIL_GetOrigFunction(&ICvar::GetCommands, g_Engine.icvar, g_CvarPatch); + + if (!vfunc) + { + return false; + } + +#ifdef OS_WIN32 + if (UTIL_VerifySignature(vfunc, CMDLIST_SIG, CMDLIST_SIGLEN)) + { + /* Skip past 0xA1 and get addr of ConCommandBase list var */ + m_TopConCommandBase = *reinterpret_cast(vfunc + 1); + return true; + } +#elif defined OS_LINUX + /* Try dlsym first */ + char path[PATH_SIZE]; + if (GetFileOfAddress((void *)g_Engine.icvar, path, sizeof(path))) + { + void *handle = dlopen(path, RTLD_NOW); + if (handle) + { + m_TopConCommandBase = reinterpret_cast(dlsym(handle, CMDLIST_SYMBOL)); + dlclose(handle); + return true; + } + } + + /* If dlsym failed, then verify signature of function */ + if (!m_TopConCommandBase && UTIL_VerifySignature(vfunc, CMDLIST_SIG, CMDLIST_SIGLEN)) + { + /* Skip past 0xA1 and get addr of ConCommandBase list var */ + m_TopConCommandBase = *reinterpret_cast(vfunc + 1); + return true; + } +#endif + + return false; +} diff --git a/sourcemm/concommands.h b/sourcemm/concommands.h index e3488e2..0ecf594 100644 --- a/sourcemm/concommands.h +++ b/sourcemm/concommands.h @@ -16,35 +16,24 @@ * @file concommands.h */ -#include -#include -#include "sourcemm.h" #include "convar_smm.h" +#include "sourcemm.h" #include "sh_list.h" class SMConVarAccessor : public IConCommandBaseAccessor { SourceHook::List m_RegisteredCommands; + ConCommandBase **m_TopConCommandBase; public: + SMConVarAccessor(); virtual bool RegisterConCommandBase(ConCommandBase *pCommand); bool Register(ConCommandBase *pCommand); void MarkCommandsAsGameDLL(); + bool InitConCommandBaseList(); void Unregister(ConCommandBase *pCommand); - void UnregisterGameDLLCommands(); void UnloadMetamodCommands(); }; -class CAlwaysRegisterableCommand : public ConCommandBase -{ - ICvar *m_pICvar; -public: - CAlwaysRegisterableCommand(); - bool IsRegistered( void ) const; - // If already registered, removes us - // Then it registers us again - void BringToFront(); -}; - void ClientCommand_handler(edict_t *client); const char *GetPluginsFile(); diff --git a/sourcemm/convar_smm.h b/sourcemm/convar_smm.h index 26abce9..dd4d9e3 100644 --- a/sourcemm/convar_smm.h +++ b/sourcemm/convar_smm.h @@ -198,6 +198,11 @@ protected: // ConVars in this executable use this 'global' to access values. static IConCommandBaseAccessor *s_pAccessor; +public: + inline void SetRegistered(bool registered) + { + m_bRegistered = registered; + } }; //----------------------------------------------------------------------------- @@ -242,7 +247,7 @@ private: FnCommandCompletionCallback m_fnCompletionCallback; bool m_bHasCompletionCallback; public: - FnCommandCallback GetCallback() { return m_fnCommandCallback; } + inline FnCommandCallback GetCallback() { return m_fnCommandCallback; } }; //----------------------------------------------------------------------------- diff --git a/sourcemm/sourcemm.cpp b/sourcemm/sourcemm.cpp index 33992c5..5fa436d 100644 --- a/sourcemm/sourcemm.cpp +++ b/sourcemm/sourcemm.cpp @@ -61,6 +61,7 @@ bool gParsedGameInfo = false; bool bGameInit = false; SourceHook::List gamedll_list; SourceHook::CallClass *g_GameDllPatch; +SourceHook::CallClass *g_CvarPatch; int g_GameDllVersion = 0; const char VSPIFACE_001[] = "ISERVERPLUGINCALLBACKS001"; const char VSPIFACE_002[] = "ISERVERPLUGINCALLBACKS002"; @@ -174,6 +175,7 @@ bool StartupMetamod(CreateInterfaceFn engineFactory, bool bWaitForGameInit) ConCommandBaseMgr::OneTimeInit(static_cast(&g_SMConVarAccessor)); g_GameDllPatch = SH_GET_CALLCLASS(g_GameDll.pGameDLL); + g_CvarPatch = SH_GET_CALLCLASS(g_Engine.icvar); if (g_GameDll.pGameClients) { @@ -195,8 +197,8 @@ bool StartupMetamod(CreateInterfaceFn engineFactory, bool bWaitForGameInit) if (!g_SmmAPI.CacheUserMessages()) { /* Don't know of a mod that has stripped out user messages completely, - * but perhaps should do something different here? - */ + * 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."); } @@ -207,6 +209,13 @@ bool StartupMetamod(CreateInterfaceFn engineFactory, bool bWaitForGameInit) LogMessage("[META] Failed to find filesystem interface, .vdf files will not be parsed."); } + if (!g_SMConVarAccessor.InitConCommandBaseList()) + { + /* This is very unlikely considering it's old engine */ + LogMessage("[META] Warning: Failed to find ConCommandBase list!"); + LogMessage("[META] Warning: ConVars and ConCommands cannot be unregistered properly! Please file a bug report."); + } + if (!bWaitForGameInit) { DoInitialPluginLoads(); @@ -603,13 +612,15 @@ void UnloadMetamod(bool shutting_down) { /* Add the FCVAR_GAMEDLL flag to our cvars so the engine removes them properly */ g_SMConVarAccessor.MarkCommandsAsGameDLL(); - g_SMConVarAccessor.UnregisterGameDLLCommands(); + g_Engine.icvar->UnlinkVariables(FCVAR_GAMEDLL); SH_CALL(g_GameDllPatch, &IServerGameDLL::DLLShutdown)(); } SH_RELEASE_CALLCLASS(g_GameDllPatch); + SH_RELEASE_CALLCLASS(g_CvarPatch); g_GameDllPatch = NULL; + g_CvarPatch = NULL; g_SourceHook.CompleteShutdown(); diff --git a/sourcemm/sourcemm.h b/sourcemm/sourcemm.h index eaab297..6b8ab2a 100644 --- a/sourcemm/sourcemm.h +++ b/sourcemm/sourcemm.h @@ -125,4 +125,7 @@ void UnloadMetamod(bool shutting_down); /** @brief Global CallClass for IServerGameDLL */ extern SourceHook::CallClass *g_GameDllPatch; +/** @brief Global CallClass for ICvar */ +extern SourceHook::CallClass *g_CvarPatch; + #endif //_INCLUDE_SOURCEMM_H diff --git a/sourcemm/svn_version.h b/sourcemm/svn_version.h index 0d4023c..972dec2 100644 --- a/sourcemm/svn_version.h +++ b/sourcemm/svn_version.h @@ -3,11 +3,11 @@ #ifndef _INCLUDE_SVN_VERSION_H_ #define _INCLUDE_SVN_VERSION_H_ -#define SVN_PRODUCT_VERSION "1.4.3" +#define SVN_PRODUCT_VERSION "1.4.4" -#define SVN_REVISION 656 -#define SVN_REVISION_STRING "656" -#define SVN_FILE_VERSION 1,4,3,656 -#define SVN_FILE_VERSION_STRING "1.4.3.656" +#define SVN_REVISION 705 +#define SVN_REVISION_STRING "705" +#define SVN_FILE_VERSION 1,4,4,705 +#define SVN_FILE_VERSION_STRING "1.4.4.705" #endif //_INCLUDE_SVN_VERSION_H_ diff --git a/sourcemm/util.cpp b/sourcemm/util.cpp index 6f1cdaa..035298b 100644 --- a/sourcemm/util.cpp +++ b/sourcemm/util.cpp @@ -406,3 +406,19 @@ size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list return len; } + +bool UTIL_VerifySignature(const void *addr, const char *sig, size_t len) +{ + unsigned char *addr1 = (unsigned char *) addr; + unsigned char *addr2 = (unsigned char *) sig; + + for (size_t i = 0; i < len; i++) + { + if (addr2[i] == '*') + continue; + if (addr1[i] != addr2[i]) + return false; + } + + return true; +} diff --git a/sourcemm/util.h b/sourcemm/util.h index 571179f..d8c8260 100644 --- a/sourcemm/util.h +++ b/sourcemm/util.h @@ -12,12 +12,15 @@ #define _INCLUDE_UTIL_H #include +#include /** * @brief Utility functions * @file util.h */ +#define IA32_JMP_IMM32 '\xE9' + /** * @brief Returns a pointer to the extension in a file name. */ @@ -73,4 +76,55 @@ bool UTIL_Relatize(char buffer[], const char *relTo, const char *relFrom); +/** + * @brief Compares memory address against a signature. + * + * @param addr Memory address to check. + * @param sig Signature used to check against memory address. Accept 0x2A as wildcard. + * @param len Length of signature. + * @return True if signature was verified, false otherwise. + */ +bool UTIL_VerifySignature(const void *addr, const char *sig, size_t len); + +/** + * @brief Returns the original function address of a given virtual function. + * + * @param mfp Member function pointer to virtual function. + * @param ptr Pointer to interface in which the virtual function belongs. + * @param cls A CallClass for the interface in which the virtual function belongs. + * @return Address of function originally pointed to by the virtual function. + */ +template +char *UTIL_GetOrigFunction(MFP vfunc, Iface *ptr, SourceHook::CallClass *cls) +{ + SourceHook::MemFuncInfo info = {true, -1, 0, 0}; + SourceHook::GetFuncInfo(vfunc, info); + + /* Get address of original GetUserMessageInfo() */ + char *func = reinterpret_cast(cls->GetOrigFunc(info.vtbloffs, info.vtblindex)); + + /* If we can't get original function, that means there's no hook */ + if (func == NULL) + { + /* Get virtual function address 'manually' then */ + char *adjustedptr = reinterpret_cast(ptr) + info.vtbloffs + info.vtbloffs; + char **vtable = *reinterpret_cast(adjustedptr); + + func = vtable[info.vtblindex]; + } + + /* Check for relative jumps */ + if (func[0] == IA32_JMP_IMM32) + { + /* Get address from displacement... + * + * Add 5 because it's relative to next instruction: + * Opcode <1 byte> + 32-bit displacement <4 bytes> + */ + func += *reinterpret_cast(func + 1) + 5; + } + + return func; +} + #endif //_INCLUDE_UTIL_H diff --git a/update_tool/Makefile b/update_tool/Makefile deleted file mode 100644 index 02273b2..0000000 --- a/update_tool/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -#(C)2004-2007 SourceMM Development Team -# Makefile written by David "BAILOPAN" Anderson - -HL2SDK = ../../hl2sdk -SMM_ROOT = .. -SRCDS = ~/srcds - -### EDIT BELOW FOR OTHER PROJECTS ### - -OPT_FLAGS = -O2 -funroll-loops -s -pipe -GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden -DEBUG_FLAGS = -g -ggdb3 -CPP = gcc-4.1 -BINARY = sourcemm_update_tool_i486.so - -HL2PUB = $(HL2SDK)/public -HL2LIB = $(HL2SDK)/linux_sdk - -OBJECTS = update_tool.cpp - -LINK = vstdlib_i486.so tier0_i486.so -static-libgcc - -INCLUDE = -I. -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \ - -I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SMM_ROOT) -I$(SMM_ROOT)/sourcehook - -ifeq "$(DEBUG)" "true" - BIN_DIR = Debug - CFLAGS = $(DEBUG_FLAGS) -else - BIN_DIR = Release - CFLAGS = $(OPT_FLAGS) -endif - -GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) - -CFLAGS += -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Wno-non-virtual-dtor -Werror -fPIC -fno-exceptions -fno-rtti -msse - -ifeq "$(GCC_VERSION)" "4" - CFLAGS += $(GCC4_FLAGS) -endif - -OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) - -$(BIN_DIR)/%.o: %.cpp - $(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $< - -all: - mkdir -p $(BIN_DIR) - ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so - ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so - nasm api_link.asm -f elf -o $(BIN_DIR)/api_link.o -DLINUX - $(MAKE) sourcemm - rm -rf $(BINARY) - ln -sf $(BIN_DIR)/$(BINARY) $(BINARY) - -sourcemm: $(OBJ_LINUX) - $(CPP) $(INCLUDE) $(CFLAGS) $(OBJ_LINUX) $(BIN_DIR)/api_link.o $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) - -debug: - $(MAKE) all DEBUG=true - -default: all - -clean: - rm -rf Release/*.o - rm -rf Release/$(BINARY) - rm -rf Debug/*.o - rm -rf Debug/$(BINARY) diff --git a/update_tool/README.txt b/update_tool/README.txt deleted file mode 100644 index 9c974a4..0000000 --- a/update_tool/README.txt +++ /dev/null @@ -1,90 +0,0 @@ -This is the README file for sourcemm_update_tool. - -This tool will automatically correct gameinfo.txt when your server gets updated, and Valve's updater overwrites Metamod:Source's changes. This tool is experimental, and is thus a separate download for now. - -1. INSTALLATION - - a. Extract the entire package to your mod folder. The structure should look like: - - /addons/metamod/bin/sourcemm_update_tool.dll - /addons/metamod/bin/sourcemm_update_tool_i486.so - /addons/metamod/README.txt - /addons/sourcemm_update_tool.vdf - /sourcemm_updater.conf - - b. Open /sourcemm_updater.conf with your favorite text editor. Change the - "cstrike" folder to match your mod folder. - -2. CONFIGURATION - - The sourcemm_updater.conf file has two configuration options. - - mmpath - Set this to the path Metamod:Source is located in. - This defaults to addons/metamod/bin - restart - Set this to how the server should be restarted when - gameinfo.txt is patched. There are three options: - - quit - Execute "quit" in the console. Currently does not work. - never - Do not restart. - error - Generate an error message. Because of a bug in SourceDS, - this will generate a crash dump on Windows, but the server - will successfully quit. - -3. USAGE - - You do not need to do anything to use the updater tool. When your server starts, - it silently checks to see if Metamod:Source is loaded. If not, it will make sure - gameinfo.txt is correctly set. Then, depending on how it's configured, it will - kill the server. Most game server provides have auto-restart functionality on their - servers; if not, you will need to manually restart the server. - - The update tool unloads itself immediately after the server starts, so it will not - use any resources, and will not display when you type 'plugin_print'. - -4. TROUBLESHOOTING - - This tool is currently experimental. There are two possible problems. For - any issue you encounter, you should post a report here: - - http://bugs.alliedmods.net/index.php?project=4 - - a. The updater tool does not patch gameinfo.txt - - Verify that the tool is loading. You can do this by opening up the - sourcemm_update_tool.vdf file and copying its file path. Then, enter - the following command in your server console: - - plugin_load - - Example: - - plugin_load cstrike\addons\metamod\bin\sourcemm_update_tool - - If you get the following reply: - - Failed to load plugin "cstrike\addons\sourcemm_update_tool" - Unable to load plugin "cstrike\addons\sourcemm_update_tool" - - Then the tool is working, and you should post a bug report. If instead, - you get: - - Unable to load plugin "cstrike\addons\sourcemm_update_tool" - Unable to load plugin "cstrike\addons\sourcemm_update_tool" - - Then the tool is not loading properly, and the path you are trying to use - is not correct. - - b. The server always dies, and you can't start it at all - - The updater tool is either crashing or not repairing Metamod:Source - correctly. First, try changing the 'restart' line in sourcemm_updater.conf - to the following line: - - restart = never - - If that does not fix the problem, remove the .vdf file so the updater tool - will not be loaded. - - In either case, you should post a bug report containing your mod name and - your gameinfo.txt as an attachment. - \ No newline at end of file diff --git a/update_tool/api_link.asm b/update_tool/api_link.asm deleted file mode 100644 index 6f94053..0000000 --- a/update_tool/api_link.asm +++ /dev/null @@ -1,138 +0,0 @@ -;;;; -;; (C)2005-2007 AlliedModders LLC -;; By the Metamod:Source Development Team -;; This software is licensed under the zlib/libpng free software license. -;; -;; This assembly file is a short thunk wrapper to let us load as a VSP and exit quietly, -;; without any overhead of the rest of the interface, and also to prevent linking against -;; tierX or vstdlib -;;;; - -;;Exports: -;; void GetBaseDir(char *buffer, maxlength); -;; void *GetThisPointer(); -;;Imports: -;; void LoadFunction(); - -section .text - -global GetThisPointer, GetGameDir, ServerCommand -global _GetThisPointer, _GetGameDir, _ServerCommand -global _GetICvar, GetICvar -extern _LoadFunction - -GetICvar: -_GetICvar: - mov eax, [icvar] - ret - -GetThisPointer: -_GetThisPointer: - mov eax, GLOBAL_POINTER - ret - -GetGameDir: -_GetGameDir: - push ebp - mov ebp, esp - - mov ecx, [engine] ;get this pointer - mov edx, [ecx] ;get the vtable - push dword [ebp+12] ;push maxlenth - push dword [ebp+8] ;push buffer - %ifdef LINUX - push ecx ;push this pointer - %endif - call dword [edx+216] ;call IVEngineServer::GetGameDir - %ifdef LINUX - add esp, 12 ;correct stack - %endif - - pop ebp - ret - -ServerCommand -_ServerCommand: - push ebp - mov ebp, esp - - mov ecx, [engine] ;get this pointer - mov edx, [ecx] ;get the vtable - push dword [ebp+8] ;push string - %ifdef LINUX - push ecx ;push this pointer - %endif - call dword [edx+144] ;call IVEngineServer::ServerCommand - %ifdef LINUX - add esp, 8 ;correct stack - %endif - - pop ebp - ret - -thisLoadFunction: - push ebp - mov ebp, esp - - push edi - - ;get factory - %ifdef LINUX - mov edi, [ebp+12] - %else - mov edi, [ebp+8] - %endif - - push dword 0 ;NULL - push dword VENGINESERVER ;iface name - call edi ;call factory - add esp, 8 ;correct stack - - test eax, eax ;do we have a valid pointer? - jz .end ;no, bail out - - mov [engine], eax ;store the engine pointer - - push dword 0 ;NULL - push dword VENGINECVAR ;iface name - call edi ;call factory - add esp, 8 ;correct stack - - test eax, eax ;do we have a valid pointer? - jz .end ;no, bail out - - mov [icvar], eax ;store the icvar pointer - - call _LoadFunction - -.end: - ;We never load, never ever ever! - xor eax, eax - - pop edi - - pop ebp - %ifdef LINUX - ret - %else - retn 8 - %endif - -thisUnloadFunction: - ret - -section .data - INTERFACE_NAME DB "ISERVERPLUGINCALLBACKS001", 0 - VENGINESERVER DB "VEngineServer021", 0 - VENGINECVAR DB "VEngineCvar003", 0 - - VIRTUAL_TABLE DD thisLoadFunction - DD thisUnloadFunction - ;We don't need any more of the vtable here - - GLOBAL_POINTER DD VIRTUAL_TABLE - - temp_ret DD 0 - temp_ptr DD temp_ret - engine DD 0 - icvar DD 0 diff --git a/update_tool/msvc8/update_tool.sln b/update_tool/msvc8/update_tool.sln deleted file mode 100644 index 5250f8a..0000000 --- a/update_tool/msvc8/update_tool.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "update_tool", "update_tool.vcproj", "{DDD1563F-7EE2-4E76-BE57-ED84A2664A51}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Debug|Win32.ActiveCfg = Debug|Win32 - {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Debug|Win32.Build.0 = Debug|Win32 - {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Release|Win32.ActiveCfg = Release|Win32 - {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/update_tool/msvc8/update_tool.vcproj b/update_tool/msvc8/update_tool.vcproj deleted file mode 100644 index 825e521..0000000 --- a/update_tool/msvc8/update_tool.vcproj +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/update_tool/sourcemm_update_tool.vdf b/update_tool/sourcemm_update_tool.vdf deleted file mode 100644 index 79a505c..0000000 --- a/update_tool/sourcemm_update_tool.vdf +++ /dev/null @@ -1,6 +0,0 @@ -//This is a sample VDF file. You need to edit "cstrike" to point to the mod you use. - -"Plugin" -{ - "file" "cstrike/addons/metamod/bin/sourcemm_update_tool" -} diff --git a/update_tool/sourcemm_updater.conf b/update_tool/sourcemm_updater.conf deleted file mode 100644 index bdf1eea..0000000 --- a/update_tool/sourcemm_updater.conf +++ /dev/null @@ -1,9 +0,0 @@ -;Use this to configure where Metamod resides, if you have -;changed its location -;mmpath = addons/metamod/bin - -;Use this to specify how the updater tool restarts SourceDS -; never - don't restart -; error - restart by generating a fatal error message -; quit - restart by issuing a "quit" server command -restart = error diff --git a/update_tool/update_tool.cpp b/update_tool/update_tool.cpp deleted file mode 100644 index ef6dbfe..0000000 --- a/update_tool/update_tool.cpp +++ /dev/null @@ -1,366 +0,0 @@ -#include -#include -#include -#include - -#if defined _MSC_VER -#define SEPCHAR "\\" -#define MMPATH "addons\\metamod\\bin" -#define WINDOWS_LEAN_AND_MEAN -#include -#elif defined __linux__ -#define SEPCHAR "/" -#define MMPATH "addons/metamod/bin" -#include -#endif - -#include - -extern "C" void GetGameDir(char *buffer, int maxlength); -extern "C" void *GetThisPointer(); -extern "C" void ServerCommand(const char *command); -extern "C" ICvar *GetICvar(); - -size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...); -bool s_isspace(char c); -bool RenameFile(const char *old, const char *newf); -bool RemoveFile(const char *file); - -/* This will be called by the thunk */ -#if defined _MSC_VER -extern "C" void LoadFunction() -#elif defined __linux__ -extern "C" void _LoadFunction() -#endif -{ - ICvar *pCvar = GetICvar(); - if (pCvar->FindVar("metamod_version") != NULL) - { - /* Already exists, bail out */ - return; - } - - char gamedir[260]; - char mmpath[260]; - - enum RestartMode - { - Restart_Never, - Restart_Error, - Restart_Quit, - }; - - RestartMode mode = Restart_Error; - - GetGameDir(gamedir, sizeof(gamedir)); - - /* Defaults */ - UTIL_Format(mmpath, sizeof(mmpath), "|gameinfo_path|%s", MMPATH); - - /* Read config */ - char config[260]; - UTIL_Format(config, sizeof(config), "%s" SEPCHAR "sourcemm_updater.conf", gamedir); - FILE *fpCfg = fopen(config, "rt"); - if (fpCfg) - { - char cfgLine[512]; - while (!feof(fpCfg) && fgets(cfgLine, sizeof(cfgLine), fpCfg) != NULL) - { - char key[255]; - - size_t keyLen = 0; - - /* Strip whitespace */ - char *input = cfgLine; - while (*input != '\0' && s_isspace(*input)) - { - input++; - } - - /* Strip ending whitespace */ - size_t len = strlen(input); - for (size_t i = len - 1; - i >= 0 && i < len; - i--) - { - if (s_isspace(input[i])) - { - input[i] = '\0'; - len--; - } else { - break; - } - } - - /* Eat stuff until we find a key */ - while (*input != '\0' && !s_isspace(*input)) - { - if (keyLen < sizeof(key)) - { - key[keyLen++] = *input; - } - input++; - } - key[keyLen] = '\0'; - - /* Eat spaces until we hit an = sign */ - while (*input != '\0' && *input != '=') - { - input++; - } - - if (*input == '=') - { - input++; - } - - /* Eat spaces again */ - while (*input != '\0' && s_isspace(*input)) - { - input++; - } - - /* Ignore comments */ - if (key[0] == ';') - { - continue; - } - - /* The rest is our key */ - if (strcmp(key, "mmpath") == 0) - { - UTIL_Format(mmpath, sizeof(mmpath), "%s", input); - } else if (strcmp(key, "restart") == 0) { - if (strcmp(input, "never") == 0) - { - mode = Restart_Never; - } else if (strcmp(input, "error") == 0) { - mode = Restart_Error; - } else if (strcmp(input, "quit") == 0) { - mode = Restart_Quit; - } - } - } - fclose(fpCfg); - } - - char old_path[260]; - char new_path[260]; - - UTIL_Format(old_path, sizeof(old_path), "%s" SEPCHAR "gameinfo.txt", gamedir); - UTIL_Format(new_path, sizeof(new_path), "%s" SEPCHAR "gameinfo.new.txt", gamedir); - - FILE *fp = fopen(old_path, "rt"); - - if (!fp) - { - return; - } - - FILE *op = fopen(new_path, "wt"); - if (!op) - { - fclose(fp); - return; - } - - enum ParseState - { - Parse_None, - Parse_Root, - Parse_GameInfo, - Parse_FileSystem, - Parse_SearchPaths, - }; - - ParseState ps = Parse_Root; - - char input[1024]; - char backup[1024]; - - bool bWroteOutput = false; - - while (!feof(fp) && fgets(input, sizeof(input), fp) != NULL) - { - UTIL_Format(backup, sizeof(backup), "%s", input); - - if (ps != Parse_None) - { - char *inbuf = input; - - /* Strip beginning whitespace */ - while (*inbuf != '\0' && s_isspace(*inbuf)) - { - inbuf++; - } - - /* Strip ending whitespace */ - size_t len = strlen(inbuf); - for (size_t i = len - 1; - i >= 0 && i < len; - i--) - { - if (s_isspace(inbuf[i])) - { - inbuf[i] = '\0'; - len--; - } else { - break; - } - } - - /* Strip quotation marks */ - if (inbuf[0] == '"' - && inbuf[len-1] == '"') - { - inbuf[len - 1] = '\0'; - inbuf = &inbuf[1]; - len -= 2; - } - - /* Do tests */ - if (ps == Parse_Root && strcmp(inbuf, "GameInfo") == 0) - { - ps = Parse_GameInfo; - } else if (ps == Parse_GameInfo && strcmp(inbuf, "FileSystem") == 0) { - ps = Parse_FileSystem; - } else if (ps == Parse_FileSystem && strcmp(inbuf, "SearchPaths") == 0) { - ps = Parse_SearchPaths; - } else if (ps == Parse_SearchPaths) { - const char *game = strstr(inbuf, "Game"); - if (game) - { - if (strstr(game, "GameBin") != NULL - && strstr(game, mmpath) != NULL) - { - fclose(op); - op = NULL; - break; /* Nothing more to do! */ - } else { - fputs("\t\t\tGameBin\t\t\t", op); - fputs(mmpath, op); - fputs("\n", op); - ps = Parse_None; - bWroteOutput = true; - } - } - } - } - - fputs(backup, op); - } - - if (!op) - { - /* Well, we can't really do anything else. Give up. */ - fclose(fp); - return; - } - - /* Close all streams */ - fclose(op); - fclose(fp); - - /* If we didn't change anything, abort here */ - if (!bWroteOutput) - { - RemoveFile(new_path); - return; - } - - /* Move the old file to a backup name */ - char backup_name[260]; - UTIL_Format(backup_name, sizeof(backup_name), "%s" SEPCHAR "gameinfo.backup.txt", gamedir); - - if (!RenameFile(old_path, backup_name)) - { - /* If we can't rename, just bail out. - * We don't want to overwrite the client's default - * without backing it up first! - */ - return; - } - if (!RenameFile(new_path, old_path)) - { - /* Since this failed, we really have no choice. - * Try and rename the old back. - */ - RenameFile(backup_name, old_path); - return; - } - RemoveFile(new_path); - - if (mode == Restart_Error) - { - Error("Server is restarting to load Metamod:Source"); - } else if (mode == Restart_Quit) { - ServerCommand("quit\n"); - } -} - -bool RemoveFile(const char *file) -{ -#if defined _MSC_VER - return (_unlink(file) == 0); -#else - return (unlink(file) == 0); -#endif -} - -bool s_isspace(char c) -{ - if ((unsigned)c & 0x80) - { - return false; - } else { - return isspace(c) ? true : false; - } -} - -size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - size_t len = vsnprintf(buffer, maxlength, fmt, ap); - va_end(ap); - - if (len >= maxlength) - { - len = maxlength - 1; - buffer[len] = '\0'; - } - - return len; -} - -bool RenameFile(const char *old, const char *newf) -{ -#if defined __linux__ - return (rename(old, newf) == 0); -#elif defined WIN32 - return (MoveFileA(old, newf) != 0); -#endif -} - -#if defined _MSC_VER -extern "C" __declspec(dllexport) void *CreateInterface(const char *iface, int *ret) -#elif defined __linux__ -extern "C" __attribute__((visibility("default"))) void *CreateInterface(const char *iface, int *ret) -#endif -{ - if (strcmp(iface, "ISERVERPLUGINCALLBACKS001") == 0) - { - if (ret) - { - *ret = 0; - } - return GetThisPointer(); - } - - if (ret) - { - *ret = 1; - } - return NULL; -}