1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2025-02-26 19:54:14 +01:00

-Fixed amb1416: Fixed a class of crashes caused by improper cvar removal handling. This encompasses a lot of crazy internal changes. Thank goodness I never have to do this for OB.

-Removed unused update tool
-Bumped version for tagging

--HG--
branch : sourcemm-1.4.4
extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/branches/sourcemm-1.4.4%40705
This commit is contained in:
Scott Ehlert 2008-07-26 05:45:07 +00:00
parent abd8737128
commit 4e21dbaf87
18 changed files with 227 additions and 1086 deletions

View File

@ -225,22 +225,6 @@ void *CSmmAPI::MetaFactory(const char *iface, int *_ret, PluginId *id)
#define ENGINEW32_OFFS 38 #define ENGINEW32_OFFS 38
#define IA32_CALL 0xE8 #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; i<len; i++)
{
if (addr2[i] == '*')
continue;
if (addr1[i] != addr2[i])
return false;
}
return true;
}
//Thanks to fysh for the idea of extracting info from "echo" and for //Thanks to fysh for the idea of extracting info from "echo" and for
// having the original offsets at hand! // having the original offsets at hand!
bool CSmmAPI::CacheCmds() bool CSmmAPI::CacheCmds()
@ -260,20 +244,20 @@ bool CSmmAPI::CacheCmds()
callback = ((ConCommand *)pBase)->GetCallback(); callback = ((ConCommand *)pBase)->GetCallback();
ptr = (unsigned char *)callback; ptr = (unsigned char *)callback;
#ifdef OS_LINUX #ifdef OS_LINUX
if (vcmp(ptr, ENGINE486_SIG, SIGLEN)) if (UTIL_VerifySignature(ptr, ENGINE486_SIG, SIGLEN))
{ {
offs = ENGINE486_OFFS; offs = ENGINE486_OFFS;
} }
else if (vcmp(ptr, ENGINE686_SIG, SIGLEN)) else if (UTIL_VerifySignature(ptr, ENGINE686_SIG, SIGLEN))
{ {
offs = ENGINE686_OFFS; offs = ENGINE686_OFFS;
} }
else if (vcmp(ptr, ENGINEAMD_SIG, SIGLEN)) else if (UTIL_VerifySignature(ptr, ENGINEAMD_SIG, SIGLEN))
{ {
offs = ENGINEAMD_OFFS; offs = ENGINEAMD_OFFS;
} }
#elif defined OS_WIN32 // Only one Windows engine binary so far... #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; offs = ENGINEW32_OFFS;
} }
@ -541,45 +525,22 @@ int CSmmAPI::GetGameDLLVersion()
/* :TODO: Make this prettier */ /* :TODO: Make this prettier */
bool CSmmAPI::CacheUserMessages() bool CSmmAPI::CacheUserMessages()
{ {
SourceHook::MemFuncInfo info = {true, -1, 0, 0};
SourceHook::GetFuncInfo(&IServerGameDLL::GetUserMessageInfo, info);
/* Get address of original GetUserMessageInfo() */
char *vfunc = reinterpret_cast<char *>(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<char *>(g_GameDll.pGameDLL) + info.thisptroffs + info.vtbloffs;
char **vtable = *reinterpret_cast<char ***>(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<int *>(vfunc + 1) + 5;
}
UserMsgDict *dict = NULL; 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 */ /* Get address of CUserMessages instance */
char **userMsgClass = *reinterpret_cast<char ***>(vfunc + MSGCLASS_OFFS); char **userMsgClass = *reinterpret_cast<char ***>(vfunc + MSGCLASS_OFFS);
/* Get address of CUserMessages::m_UserMessages */ /* Get address of CUserMessages::m_UserMessages */
dict = reinterpret_cast<UserMsgDict *>(*userMsgClass); dict = reinterpret_cast<UserMsgDict *>(*userMsgClass);
} else if (vcmp(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) { } else if (UTIL_VerifySignature(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) {
#ifdef OS_WIN32 #ifdef OS_WIN32
/* If we get here, the code is possibly inlined like in Dystopia */ /* If we get here, the code is possibly inlined like in Dystopia */
@ -596,7 +557,7 @@ bool CSmmAPI::CacheUserMessages()
dict = reinterpret_cast<UserMsgDict *>(*userMsgClass); dict = reinterpret_cast<UserMsgDict *>(*userMsgClass);
#endif #endif
#ifdef OS_WIN32 #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 */ /* Get address of CUserMessages instance */
char **userMsgClass = *reinterpret_cast<char ***>(vfunc + MSGCLASS3_OFFS); char **userMsgClass = *reinterpret_cast<char ***>(vfunc + MSGCLASS3_OFFS);

View File

@ -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) - Fixed a bug where loading plugins built for MM:S 1.3 or older (API ver <= 9)
would cause a crash. would cause a crash.
- Fixed a bug where loading plugins using VDF files caused Metamod:Source - 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 - 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" was not at the very end of the filename. For example, "plugin.vdf.disabled"
would have been opened in previous versions. would have been opened in previous versions. (bug 1534)
- Removed FCVAR_REPLICATED from MM:S convars. - Removed FCVAR_REPLICATED from MM:S convars. (bug 1479)
2008/01/23 1.4.3: 2008/01/23 1.4.3:
- Metamod:Source can now be loaded via a .vdf instead of gameinfo.txt. - Metamod:Source can now be loaded via a .vdf instead of gameinfo.txt.

View File

@ -9,6 +9,7 @@
*/ */
#include <ctype.h> #include <ctype.h>
#include "convar_smm.h"
#include "CSmmAPI.h" #include "CSmmAPI.h"
#include "concommands.h" #include "concommands.h"
#include "CPlugin.h" #include "CPlugin.h"
@ -21,20 +22,25 @@
* @file concommands.cpp * @file concommands.cpp
*/ */
CAlwaysRegisterableCommand g_EternalCommand;
SMConVarAccessor g_SMConVarAccessor; SMConVarAccessor g_SMConVarAccessor;
SMConVarAccessor::SMConVarAccessor()
{
m_TopConCommandBase = NULL;
}
bool SMConVarAccessor::RegisterConCommandBase(ConCommandBase *pCommand) bool SMConVarAccessor::RegisterConCommandBase(ConCommandBase *pCommand)
{ {
// Add the FCVAR_GAMEDLL flag /* Add the FCVAR_GAMEDLL flag
// => No crash on exit! * => No crash on exit!
// UPDATE: Do _not_ add the FCVAR_GAMEDLL flag here, as it * UPDATE: Do _not_ add the FCVAR_GAMEDLL flag here, as it
// causes the command to be unusable on listenservers until you load a map * 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 * We will set the FCVAR_GAMEDLL flag on all commands we have registered once we are being unloaded
//pCommand->AddFlags(FCVAR_GAMEDLL); */
// pCommand->AddFlags(FCVAR_GAMEDLL);
m_RegisteredCommands.push_back(pCommand); m_RegisteredCommands.push_back(pCommand);
pCommand->SetNext( NULL ); pCommand->SetNext(NULL);
g_Engine.icvar->RegisterConCommandBase(pCommand); g_Engine.icvar->RegisterConCommandBase(pCommand);
return true; return true;
@ -42,8 +48,8 @@ bool SMConVarAccessor::RegisterConCommandBase(ConCommandBase *pCommand)
bool SMConVarAccessor::Register(ConCommandBase *pCommand) bool SMConVarAccessor::Register(ConCommandBase *pCommand)
{ {
//simple, don't mark as part of sourcemm! /* Simple, don't mark as part of sourcemm! */
pCommand->SetNext( NULL ); pCommand->SetNext(NULL);
g_Engine.icvar->RegisterConCommandBase(pCommand); g_Engine.icvar->RegisterConCommandBase(pCommand);
return true; return true;
@ -60,64 +66,47 @@ void SMConVarAccessor::MarkCommandsAsGameDLL()
void SMConVarAccessor::Unregister(ConCommandBase *pCommand) void SMConVarAccessor::Unregister(ConCommandBase *pCommand)
{ {
ICvar *cv = g_Engine.icvar; ConCommandBase *pCur = NULL;
ConCommandBase *ptr = cv->GetCommands();
if (ptr == pCommand)
{
//first in list
g_EternalCommand.BringToFront();
g_EternalCommand.SetNext(const_cast<ConCommandBase *>(pCommand->GetNext()));
} else {
//find us and unregister us
ConCommandBase *pPrev = NULL; ConCommandBase *pPrev = NULL;
while (ptr)
if (!pCommand || !pCommand->IsRegistered())
{ {
if (ptr == pCommand) return;
break;
pPrev = ptr;
ptr = const_cast<ConCommandBase *>(ptr->GetNext());
} }
if (pPrev && ptr == pCommand)
pCur = g_Engine.icvar->GetCommands();
pCommand->SetRegistered(false);
if (!m_TopConCommandBase || !pCur)
{
return;
}
if (pCur == pCommand)
{
*m_TopConCommandBase = const_cast<ConCommandBase *>(pCommand->GetNext());
pCommand->SetNext(NULL);
return;
}
pPrev = pCur;
pCur = const_cast<ConCommandBase *>(pCur->GetNext());
while (pCur)
{
if (pCur == pCommand)
{ {
pPrev->SetNext(const_cast<ConCommandBase *>(pCommand->GetNext())); pPrev->SetNext(const_cast<ConCommandBase *>(pCommand->GetNext()));
pCommand->SetNext(NULL);
} }
}
}
void SMConVarAccessor::UnregisterGameDLLCommands() pPrev = pCur;
{ pCur = const_cast<ConCommandBase *>(pCur->GetNext());
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<ConCommandBase*>(iter->GetNext());
g_EternalCommand.SetNext(iter);
prev = &g_EternalCommand;
continue;
}
else
{
iter = const_cast<ConCommandBase*>(iter->GetNext());
prev->SetNext(iter);
continue;
}
}
prev = iter;
iter = const_cast<ConCommandBase*>(iter->GetNext());
} }
} }
ConVar metamod_version("metamod_version", SOURCEMM_VERSION, FCVAR_SPONLY | FCVAR_NOTIFY, "Metamod:Source Version"); 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_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"); ConVar mm_basedir("mm_basedir", "addons\\metamod", FCVAR_SPONLY, "Metamod:Source base folder");
#else #else
@ -646,53 +635,6 @@ CON_COMMAND(meta, "Metamod:Source Menu")
CONMSG(" version - Version information\n"); 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<ConCommandBase*>(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) void ClientCommand_handler(edict_t *client)
{ {
IVEngineServer *e = g_Engine.engine; IVEngineServer *e = g_Engine.engine;
@ -800,3 +742,62 @@ const char *GetMetamodBaseDir()
{ {
return mm_basedir.GetString(); 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<ConCommandBase ***>(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<ConCommandBase **>(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<ConCommandBase ***>(vfunc + 1);
return true;
}
#endif
return false;
}

View File

@ -16,35 +16,24 @@
* @file concommands.h * @file concommands.h
*/ */
#include <interface.h>
#include <eiface.h>
#include "sourcemm.h"
#include "convar_smm.h" #include "convar_smm.h"
#include "sourcemm.h"
#include "sh_list.h" #include "sh_list.h"
class SMConVarAccessor : public IConCommandBaseAccessor class SMConVarAccessor : public IConCommandBaseAccessor
{ {
SourceHook::List<ConCommandBase*> m_RegisteredCommands; SourceHook::List<ConCommandBase*> m_RegisteredCommands;
ConCommandBase **m_TopConCommandBase;
public: public:
SMConVarAccessor();
virtual bool RegisterConCommandBase(ConCommandBase *pCommand); virtual bool RegisterConCommandBase(ConCommandBase *pCommand);
bool Register(ConCommandBase *pCommand); bool Register(ConCommandBase *pCommand);
void MarkCommandsAsGameDLL(); void MarkCommandsAsGameDLL();
bool InitConCommandBaseList();
void Unregister(ConCommandBase *pCommand); void Unregister(ConCommandBase *pCommand);
void UnregisterGameDLLCommands();
void UnloadMetamodCommands(); 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); void ClientCommand_handler(edict_t *client);
const char *GetPluginsFile(); const char *GetPluginsFile();

View File

@ -198,6 +198,11 @@ protected:
// ConVars in this executable use this 'global' to access values. // ConVars in this executable use this 'global' to access values.
static IConCommandBaseAccessor *s_pAccessor; static IConCommandBaseAccessor *s_pAccessor;
public:
inline void SetRegistered(bool registered)
{
m_bRegistered = registered;
}
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -242,7 +247,7 @@ private:
FnCommandCompletionCallback m_fnCompletionCallback; FnCommandCompletionCallback m_fnCompletionCallback;
bool m_bHasCompletionCallback; bool m_bHasCompletionCallback;
public: public:
FnCommandCallback GetCallback() { return m_fnCommandCallback; } inline FnCommandCallback GetCallback() { return m_fnCommandCallback; }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -61,6 +61,7 @@ bool gParsedGameInfo = false;
bool bGameInit = false; bool bGameInit = false;
SourceHook::List<GameDllInfo *> gamedll_list; SourceHook::List<GameDllInfo *> gamedll_list;
SourceHook::CallClass<IServerGameDLL> *g_GameDllPatch; SourceHook::CallClass<IServerGameDLL> *g_GameDllPatch;
SourceHook::CallClass<ICvar> *g_CvarPatch;
int g_GameDllVersion = 0; int g_GameDllVersion = 0;
const char VSPIFACE_001[] = "ISERVERPLUGINCALLBACKS001"; const char VSPIFACE_001[] = "ISERVERPLUGINCALLBACKS001";
const char VSPIFACE_002[] = "ISERVERPLUGINCALLBACKS002"; const char VSPIFACE_002[] = "ISERVERPLUGINCALLBACKS002";
@ -174,6 +175,7 @@ bool StartupMetamod(CreateInterfaceFn engineFactory, bool bWaitForGameInit)
ConCommandBaseMgr::OneTimeInit(static_cast<IConCommandBaseAccessor *>(&g_SMConVarAccessor)); ConCommandBaseMgr::OneTimeInit(static_cast<IConCommandBaseAccessor *>(&g_SMConVarAccessor));
g_GameDllPatch = SH_GET_CALLCLASS(g_GameDll.pGameDLL); g_GameDllPatch = SH_GET_CALLCLASS(g_GameDll.pGameDLL);
g_CvarPatch = SH_GET_CALLCLASS(g_Engine.icvar);
if (g_GameDll.pGameClients) if (g_GameDll.pGameClients)
{ {
@ -207,6 +209,13 @@ bool StartupMetamod(CreateInterfaceFn engineFactory, bool bWaitForGameInit)
LogMessage("[META] Failed to find filesystem interface, .vdf files will not be parsed."); 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) if (!bWaitForGameInit)
{ {
DoInitialPluginLoads(); DoInitialPluginLoads();
@ -603,13 +612,15 @@ void UnloadMetamod(bool shutting_down)
{ {
/* Add the FCVAR_GAMEDLL flag to our cvars so the engine removes them properly */ /* Add the FCVAR_GAMEDLL flag to our cvars so the engine removes them properly */
g_SMConVarAccessor.MarkCommandsAsGameDLL(); g_SMConVarAccessor.MarkCommandsAsGameDLL();
g_SMConVarAccessor.UnregisterGameDLLCommands(); g_Engine.icvar->UnlinkVariables(FCVAR_GAMEDLL);
SH_CALL(g_GameDllPatch, &IServerGameDLL::DLLShutdown)(); SH_CALL(g_GameDllPatch, &IServerGameDLL::DLLShutdown)();
} }
SH_RELEASE_CALLCLASS(g_GameDllPatch); SH_RELEASE_CALLCLASS(g_GameDllPatch);
SH_RELEASE_CALLCLASS(g_CvarPatch);
g_GameDllPatch = NULL; g_GameDllPatch = NULL;
g_CvarPatch = NULL;
g_SourceHook.CompleteShutdown(); g_SourceHook.CompleteShutdown();

View File

@ -125,4 +125,7 @@ void UnloadMetamod(bool shutting_down);
/** @brief Global CallClass for IServerGameDLL */ /** @brief Global CallClass for IServerGameDLL */
extern SourceHook::CallClass<IServerGameDLL> *g_GameDllPatch; extern SourceHook::CallClass<IServerGameDLL> *g_GameDllPatch;
/** @brief Global CallClass for ICvar */
extern SourceHook::CallClass<ICvar> *g_CvarPatch;
#endif //_INCLUDE_SOURCEMM_H #endif //_INCLUDE_SOURCEMM_H

View File

@ -3,11 +3,11 @@
#ifndef _INCLUDE_SVN_VERSION_H_ #ifndef _INCLUDE_SVN_VERSION_H_
#define _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 705
#define SVN_REVISION_STRING "656" #define SVN_REVISION_STRING "705"
#define SVN_FILE_VERSION 1,4,3,656 #define SVN_FILE_VERSION 1,4,4,705
#define SVN_FILE_VERSION_STRING "1.4.3.656" #define SVN_FILE_VERSION_STRING "1.4.4.705"
#endif //_INCLUDE_SVN_VERSION_H_ #endif //_INCLUDE_SVN_VERSION_H_

View File

@ -406,3 +406,19 @@ size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list
return len; 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;
}

View File

@ -12,12 +12,15 @@
#define _INCLUDE_UTIL_H #define _INCLUDE_UTIL_H
#include <stdarg.h> #include <stdarg.h>
#include <sourcehook/sourcehook.h>
/** /**
* @brief Utility functions * @brief Utility functions
* @file util.h * @file util.h
*/ */
#define IA32_JMP_IMM32 '\xE9'
/** /**
* @brief Returns a pointer to the extension in a file name. * @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 *relTo,
const char *relFrom); 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 <class MFP, class Iface>
char *UTIL_GetOrigFunction(MFP vfunc, Iface *ptr, SourceHook::CallClass<Iface> *cls)
{
SourceHook::MemFuncInfo info = {true, -1, 0, 0};
SourceHook::GetFuncInfo(vfunc, info);
/* Get address of original GetUserMessageInfo() */
char *func = reinterpret_cast<char *>(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<char *>(ptr) + info.vtbloffs + info.vtbloffs;
char **vtable = *reinterpret_cast<char ***>(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<unsigned long *>(func + 1) + 5;
}
return func;
}
#endif //_INCLUDE_UTIL_H #endif //_INCLUDE_UTIL_H

View File

@ -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)

View File

@ -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:
<mod>/addons/metamod/bin/sourcemm_update_tool.dll
<mod>/addons/metamod/bin/sourcemm_update_tool_i486.so
<mod>/addons/metamod/README.txt
<mod>/addons/sourcemm_update_tool.vdf
<mod>/sourcemm_updater.conf
b. Open <mod>/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 <path>
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.

View File

@ -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

View File

@ -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

View File

@ -1,203 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="update_tool"
ProjectGUID="{DDD1563F-7EE2-4E76-BE57-ED84A2664A51}"
RootNamespace="update_tool"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
CommandLine="nasmw ..\api_link.asm -f win32 -oDebug\api_link.obj -DWIN32"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;UPDATE_TOOL_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_CRT_STD_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="Debug\api_link.obj tier0.lib"
OutputFile="$(OutDir)\sourcemm_update_tool.dll"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
CommandLine="nasmw ..\api_link.asm -f win32 -oRelease\api_link.obj -DWIN32"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;UPDATE_TOOL_EXPORTS;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="Release\api_link.obj tier0.lib"
OutputFile="$(OutDir)\sourcemm_update_tool.dll"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\update_tool.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -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"
}

View File

@ -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

View File

@ -1,366 +0,0 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#if defined _MSC_VER
#define SEPCHAR "\\"
#define MMPATH "addons\\metamod\\bin"
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#elif defined __linux__
#define SEPCHAR "/"
#define MMPATH "addons/metamod/bin"
#include <unistd.h>
#endif
#include <icvar.h>
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;
}