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:
parent
abd8737128
commit
4e21dbaf87
@ -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);
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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,16 +22,21 @@
|
|||||||
* @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);
|
||||||
|
|
||||||
@ -42,7 +48,7 @@ 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);
|
||||||
|
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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_
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
@ -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.
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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"
|
|
||||||
}
|
|
@ -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
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user