1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2024-11-29 11:24:19 +01:00
HLMetaModOfficial/core/provider/console.cpp
2016-10-11 13:29:37 -04:00

187 lines
4.9 KiB
C++

/**
* vim: set ts=4 :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2008 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id$
*/
#include "console.h"
#include "provider_ep2.h"
#include "metamod_util.h"
using namespace SourceHook;
SMConVarAccessor g_SMConVarAccessor;
#if SOURCE_ENGINE >= SE_ORANGEBOX
#else
#define RegisterConCommand RegisterConCommandBase
#endif
bool SMConVarAccessor::RegisterConCommandBase(ConCommandBase *pCommand)
{
m_RegisteredCommands.push_back(pCommand);
#if SOURCE_ENGINE < SE_ALIENSWARM
pCommand->SetNext(NULL);
#endif
icvar->RegisterConCommand(pCommand);
return true;
}
bool SMConVarAccessor::Register(ConCommandBase *pCommand)
{
#if SOURCE_ENGINE < SE_ALIENSWARM
pCommand->SetNext(NULL);
#endif
icvar->RegisterConCommand(pCommand);
return true;
}
void SMConVarAccessor::RemoveMetamodCommands()
{
List<ConCommandBase *>::iterator iter;
for (iter = m_RegisteredCommands.begin(); iter != m_RegisteredCommands.end(); iter++)
{
Unregister(*iter);
}
}
#if SOURCE_ENGINE < SE_ORANGEBOX
/* 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 = (char *)SH_GET_ORIG_VFNPTR_ENTRY(icvar, &ICvar::GetCommands);
if (*vfunc == '\xE9')
{
/* Get address from displacement...
*
* Add 5 because it's relative to next instruction:
* Opcode <1 byte> + 32-bit displacement <4 bytes>
*/
vfunc += *reinterpret_cast<int *>(vfunc + 1) + 5;
}
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 *)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;
}
#endif
void SMConVarAccessor::Unregister(ConCommandBase *pCommand)
{
#if SOURCE_ENGINE >= SE_ORANGEBOX
icvar->UnregisterConCommand(pCommand);
#else
ConCommandBase *pCur = NULL;
ConCommandBase *pPrev = NULL;
if (!pCommand)
{
return;
}
pCur = icvar->GetCommands();
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()));
pCommand->SetNext(NULL);
}
pPrev = pCur;
pCur = const_cast<ConCommandBase *>(pCur->GetNext());
}
#endif
}