Added a new ValveCallType that allows for arbitrary |this| parameters, as well as associated features in gamedata and for reading/writing memory (bug 3520, r=dvander, sr=fyren).
This commit is contained in:
parent
ead6ca73e5
commit
18865c44c8
@ -63,6 +63,9 @@ static char g_GameName[256] = {'$', '\0'};
|
||||
#define PSTATE_GAMEDEFS_CRC 9
|
||||
#define PSTATE_GAMEDEFS_CRC_BINARY 10
|
||||
#define PSTATE_GAMEDEFS_CUSTOM 11
|
||||
#define PSTATE_GAMEDEFS_ADDRESSES 12
|
||||
#define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS 13
|
||||
#define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ 14
|
||||
|
||||
#if defined PLATFORM_WINDOWS
|
||||
#define PLATFORM_NAME "windows"
|
||||
@ -132,6 +135,7 @@ CGameConfig::CGameConfig(const char *file)
|
||||
m_pOffsets = sm_trie_create();
|
||||
m_pProps = sm_trie_create();
|
||||
m_pKeys = sm_trie_create();
|
||||
m_pAddresses = new KTrie<AddressConf>();
|
||||
m_pSigs = sm_trie_create();
|
||||
m_pStrings = new BaseStringTable(512);
|
||||
m_RefCount = 0;
|
||||
@ -145,6 +149,7 @@ CGameConfig::~CGameConfig()
|
||||
sm_trie_destroy(m_pOffsets);
|
||||
sm_trie_destroy(m_pProps);
|
||||
sm_trie_destroy(m_pKeys);
|
||||
delete m_pAddresses;
|
||||
sm_trie_destroy(m_pSigs);
|
||||
delete m_pStrings;
|
||||
}
|
||||
@ -212,6 +217,10 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
|
||||
m_ParseState = PSTATE_GAMEDEFS_CRC;
|
||||
bShouldBeReadingDefault = false;
|
||||
}
|
||||
else if (strcmp(name, "Addresses") == 0)
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_ADDRESSES;
|
||||
}
|
||||
else
|
||||
{
|
||||
ITextListener_SMC **pListen = g_GameConfigs.m_customHandlers.retrieve(name);
|
||||
@ -293,12 +302,41 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
|
||||
return m_CustomHandler->ReadSMC_NewSection(states, name);
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_ADDRESSES:
|
||||
{
|
||||
m_Address[0] = '\0';
|
||||
m_AddressSignature[0] = '\0';
|
||||
m_AddressReadCount = 0;
|
||||
|
||||
strncopy(m_Address, name, sizeof(m_Address));
|
||||
m_ParseState = PSTATE_GAMEDEFS_ADDRESSES_ADDRESS;
|
||||
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS:
|
||||
{
|
||||
if (strcmp(name, PLATFORM_NAME) == 0)
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(name, "linux") != 0 && strcmp(name, "windows") != 0)
|
||||
{
|
||||
g_Logger.LogError("[SM] Error while parsing Address section for \"%s\" (%s):", m_Address, m_CurFile);
|
||||
g_Logger.LogError("[SM] Unrecognized platform \"%s\"", name);
|
||||
}
|
||||
m_IgnoreLevel = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* No sub-sections allowed:
|
||||
case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
|
||||
case PSTATE_GAMEDEFS_KEYS:
|
||||
case PSTATE_GAMEDEFS_SUPPORTED:
|
||||
case PSTATE_GAMEDEFS_SIGNATURES_SIG:
|
||||
case PSTATE_GAMEDEFS_CRC_BINARY:
|
||||
case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ:
|
||||
*/
|
||||
default:
|
||||
{
|
||||
@ -376,6 +414,21 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
|
||||
bShouldBeReadingDefault = true;
|
||||
}
|
||||
}
|
||||
} else if (m_ParseState == PSTATE_GAMEDEFS_ADDRESSES_ADDRESS || m_ParseState == PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ) {
|
||||
if (strcmp(key, "read") == 0) {
|
||||
int limit = sizeof(m_AddressRead)/sizeof(m_AddressRead[0]);
|
||||
if (m_AddressReadCount < limit)
|
||||
{
|
||||
m_AddressRead[m_AddressReadCount] = atoi(value);
|
||||
m_AddressReadCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Logger.LogError("[SM] Error parsing Address \"%s\", does not support more than %d read offsets (gameconf \"%s\")", m_Address, limit, m_CurFile);
|
||||
}
|
||||
} else if (strcmp(key, "signature") == 0) {
|
||||
strncopy(m_AddressSignature, value, sizeof(m_AddressSignature));
|
||||
}
|
||||
} else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
|
||||
return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
|
||||
}
|
||||
@ -548,6 +601,36 @@ skip_find:
|
||||
sm_trie_replace(m_pSigs, m_offset, final_addr);
|
||||
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
|
||||
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_ADDRESSES:
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS:
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_ADDRESSES;
|
||||
|
||||
if (m_Address[0] == '\0')
|
||||
{
|
||||
g_Logger.LogError("[SM] Address sections must have names (gameconf \"%s\")", m_CurFile);
|
||||
break;
|
||||
}
|
||||
if (m_AddressSignature[0] == '\0')
|
||||
{
|
||||
g_Logger.LogError("[SM] Address section for \"%s\" did not specify a signature (gameconf \"%s\")", m_Address, m_CurFile);
|
||||
break;
|
||||
}
|
||||
|
||||
AddressConf addrConf(m_AddressSignature, sizeof(m_AddressSignature), m_AddressReadCount, m_AddressRead);
|
||||
m_pAddresses->replace(m_Address, addrConf);
|
||||
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ:
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_ADDRESSES_ADDRESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -689,6 +772,7 @@ bool CGameConfig::Reparse(char *error, size_t maxlength)
|
||||
sm_trie_clear(m_pOffsets);
|
||||
sm_trie_clear(m_pProps);
|
||||
sm_trie_clear(m_pKeys);
|
||||
m_pAddresses->clear();
|
||||
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
|
||||
@ -837,6 +921,53 @@ const char *CGameConfig::GetKeyValue(const char *key)
|
||||
return m_pStrings->GetString((int)obj);
|
||||
}
|
||||
|
||||
//memory addresses below 0x10000 are automatically considered invalid for dereferencing
|
||||
#define VALID_MINIMUM_MEMORY_ADDRESS 0x10000
|
||||
|
||||
bool CGameConfig::GetAddress(const char *key, void **retaddr)
|
||||
{
|
||||
AddressConf *addrConf;
|
||||
|
||||
addrConf = m_pAddresses->retrieve(key);
|
||||
if (!addrConf)
|
||||
{
|
||||
*retaddr = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
void *addr;
|
||||
if (!GetMemSig(addrConf->signatureName, &addr))
|
||||
{
|
||||
*retaddr = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < addrConf->readCount; ++i)
|
||||
{
|
||||
int offset = addrConf->read[i];
|
||||
|
||||
//NULLs in the middle of an indirection chain are bad, end NULL is ok
|
||||
if (addr == NULL || reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
|
||||
{
|
||||
*retaddr = NULL;
|
||||
return false;
|
||||
}
|
||||
addr = *(reinterpret_cast<void**>(reinterpret_cast<uint8_t*>(addr) + offset));
|
||||
}
|
||||
|
||||
*retaddr = addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
CGameConfig::AddressConf::AddressConf(char *sigName, unsigned sigLength, unsigned readCount, int *read)
|
||||
{
|
||||
unsigned readLimit = min(readCount, sizeof(this->read) / sizeof(this->read[0]));
|
||||
|
||||
strncopy(signatureName, sigName, sizeof(signatureName) / sizeof(signatureName[0]));
|
||||
this->readCount = readLimit;
|
||||
memcpy(&this->read[0], read, sizeof(this->read[0])*readLimit);
|
||||
}
|
||||
|
||||
SendProp *CGameConfig::GetSendProp(const char *key)
|
||||
{
|
||||
SendProp *pProp;
|
||||
|
@ -65,6 +65,7 @@ public: //IGameConfig
|
||||
bool GetOffset(const char *key, int *value);
|
||||
SendProp *GetSendProp(const char *key);
|
||||
bool GetMemSig(const char *key, void **addr);
|
||||
bool GetAddress(const char *key, void **addr);
|
||||
public:
|
||||
void IncRefCount();
|
||||
unsigned int DecRefCount();
|
||||
@ -93,6 +94,24 @@ private:
|
||||
/* Custom Sections */
|
||||
unsigned int m_CustomLevel;
|
||||
ITextListener_SMC *m_CustomHandler;
|
||||
|
||||
/* Support for reading Addresses */
|
||||
struct AddressConf
|
||||
{
|
||||
char signatureName[64];
|
||||
int readCount;
|
||||
int read[8];
|
||||
|
||||
AddressConf(char *sigName, unsigned sigLength, unsigned readCount, int *read);
|
||||
|
||||
AddressConf() {}
|
||||
};
|
||||
|
||||
char m_Address[64];
|
||||
char m_AddressSignature[64];
|
||||
int m_AddressReadCount;
|
||||
int m_AddressRead[8];
|
||||
KTrie<AddressConf> *m_pAddresses;
|
||||
};
|
||||
|
||||
class GameConfigManager :
|
||||
|
@ -673,6 +673,77 @@ static cell_t RequireFeature(IPluginContext *pContext, const cell_t *params)
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum NumberType
|
||||
{
|
||||
NumberType_Int8,
|
||||
NumberType_Int16,
|
||||
NumberType_Int32
|
||||
};
|
||||
|
||||
//memory addresses below 0x10000 are automatically considered invalid for dereferencing
|
||||
#define VALID_MINIMUM_MEMORY_ADDRESS 0x10000
|
||||
|
||||
static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
void *addr = reinterpret_cast<void*>(params[1]);
|
||||
|
||||
if (addr == NULL)
|
||||
{
|
||||
pContext->ThrowNativeError("Address cannot be null");
|
||||
}
|
||||
else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
|
||||
{
|
||||
pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
|
||||
}
|
||||
NumberType size = static_cast<NumberType>(params[2]);
|
||||
|
||||
switch(size)
|
||||
{
|
||||
case NumberType_Int8:
|
||||
return *reinterpret_cast<uint8_t*>(addr);
|
||||
case NumberType_Int16:
|
||||
return *reinterpret_cast<uint16_t*>(addr);
|
||||
case NumberType_Int32:
|
||||
return *reinterpret_cast<uint32_t*>(addr);
|
||||
default:
|
||||
pContext->ThrowNativeError("Invalid number types %d", size);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
void *addr = reinterpret_cast<void*>(params[1]);
|
||||
|
||||
if (addr == NULL)
|
||||
{
|
||||
pContext->ThrowNativeError("Address cannot be null");
|
||||
}
|
||||
else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
|
||||
{
|
||||
pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
|
||||
}
|
||||
cell_t data = params[2];
|
||||
|
||||
NumberType size = static_cast<NumberType>(params[3]);
|
||||
|
||||
switch(size)
|
||||
{
|
||||
case NumberType_Int8:
|
||||
*reinterpret_cast<uint8_t*>(addr) = data;
|
||||
case NumberType_Int16:
|
||||
*reinterpret_cast<uint16_t*>(addr) = data;
|
||||
case NumberType_Int32:
|
||||
*reinterpret_cast<uint32_t*>(addr) = data;
|
||||
default:
|
||||
pContext->ThrowNativeError("Invalid number types %d", size);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(coreNatives)
|
||||
{
|
||||
{"AutoExecConfig", AutoExecConfig},
|
||||
@ -699,6 +770,8 @@ REGISTER_NATIVES(coreNatives)
|
||||
{"VerifyCoreVersion", VerifyCoreVersion},
|
||||
{"GetFeatureStatus", GetFeatureStatus},
|
||||
{"RequireFeature", RequireFeature},
|
||||
{"LoadFromAddress", LoadFromAddress},
|
||||
{"StoreToAddress", StoreToAddress},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -128,6 +128,35 @@ static cell_t smn_GameConfGetKeyValue(IPluginContext *pCtx, const cell_t *params
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static cell_t smn_GameConfGetAddress(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
IGameConfig *gc;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_GameConfigsType, &sec, (void **)&gc))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid game config handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
char *key;
|
||||
void* val;
|
||||
pCtx->LocalToString(params[2], &key);
|
||||
|
||||
if (!gc->GetAddress(key, &val))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (cell_t)val;
|
||||
}
|
||||
|
||||
static GameConfigsNatives s_GameConfigsNatives;
|
||||
|
||||
REGISTER_NATIVES(gameconfignatives)
|
||||
@ -135,5 +164,6 @@ REGISTER_NATIVES(gameconfignatives)
|
||||
{"LoadGameConfigFile", smn_LoadGameConfigFile},
|
||||
{"GameConfGetOffset", smn_GameConfGetOffset},
|
||||
{"GameConfGetKeyValue", smn_GameConfGetKeyValue},
|
||||
{"GameConfGetAddress", smn_GameConfGetAddress},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
@ -250,7 +250,7 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
|
||||
|
||||
unsigned char *ptr = vc->stk_get();
|
||||
|
||||
unsigned int numparams = (unsigned)params[0];
|
||||
const unsigned int numparams = (unsigned)params[0];
|
||||
unsigned int startparam = 2;
|
||||
/* Do we need to write a thispointer? */
|
||||
|
||||
@ -308,6 +308,25 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
|
||||
*(void **)ptr = g_EntList;
|
||||
}
|
||||
break;
|
||||
case ValveCall_Raw:
|
||||
{
|
||||
//params[startparam] is an address to a pointer to THIS
|
||||
//params following this are params to the method we will invoke later
|
||||
if (startparam > numparams)
|
||||
{
|
||||
vc->stk_put(ptr);
|
||||
return pContext->ThrowNativeError("Expected a ThisPtr address, it wasn't found");
|
||||
}
|
||||
|
||||
//note: varargs pawn args are passed by-ref
|
||||
cell_t *cell;
|
||||
pContext->LocalToPhysAddr(params[startparam], &cell);
|
||||
void *thisptr = reinterpret_cast<void*>(*cell);
|
||||
|
||||
*(void **)ptr = thisptr;
|
||||
startparam++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,7 @@ enum ValveCallType
|
||||
ValveCall_Player, /**< Thiscall (CBasePlayer implicit first parameter) */
|
||||
ValveCall_GameRules, /**< Thiscall (CGameRules implicit first paramater) */
|
||||
ValveCall_EntityList, /**< Thiscall (CGlobalEntityList implicit first paramater) */
|
||||
ValveCall_Raw, /**< Thiscall (address explicit first parameter) */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -57,6 +57,7 @@ enum SDKCallType
|
||||
SDKCall_Player, /**< CBasePlayer call */
|
||||
SDKCall_GameRules, /**< CGameRules call */
|
||||
SDKCall_EntityList, /**< CGlobalEntityList call */
|
||||
SDKCall_Raw, /**< Raw Address call */
|
||||
};
|
||||
|
||||
enum SDKLibrary
|
||||
|
@ -386,6 +386,16 @@ native GameConfGetOffset(Handle:gc, const String:key[]);
|
||||
*/
|
||||
native bool:GameConfGetKeyValue(Handle:gc, const String:key[], String:buffer[], maxlen);
|
||||
|
||||
/*
|
||||
* Finds an address calculation in a GameConfig file,
|
||||
* performs LoadFromAddress on it as appropriate, then returns the final address.
|
||||
*
|
||||
* @param gameconf GameConfig Handle, or INVALID_HANDLE to use sdktools.games.txt.
|
||||
* @param name Name of the property to find.
|
||||
* @return An address calculated on success, or 0 on failure.
|
||||
*/
|
||||
native Address:GameConfGetAddress(Handle:gameconf, const String:name[]);
|
||||
|
||||
/**
|
||||
* Returns the operating system's "tick count," which is a number of
|
||||
* milliseconds since the operating system loaded. This can be used
|
||||
@ -630,6 +640,41 @@ native FeatureStatus:GetFeatureStatus(FeatureType:type, const String:name[]);
|
||||
native RequireFeature(FeatureType:type, const String:name[],
|
||||
const String:fmt[]="", any:...);
|
||||
|
||||
/**
|
||||
* Represents how many bytes we can read from an address with one load
|
||||
*/
|
||||
enum NumberType
|
||||
{
|
||||
NumberType_Int8,
|
||||
NumberType_Int16,
|
||||
NumberType_Int32
|
||||
};
|
||||
|
||||
enum Address
|
||||
{
|
||||
Address_Null = 0, //a typical invalid result when an address lookup fails
|
||||
Address_MinimumValid = 0x10000 //addresses below this value are considered invalid to use for Load/Store
|
||||
};
|
||||
|
||||
/**
|
||||
* Load up to 4 bytes from a memory address.
|
||||
*
|
||||
* @param addr Address to a memory location.
|
||||
* @param size How many bytes should be read.
|
||||
* @return The value that is stored at that address.
|
||||
*/
|
||||
native LoadFromAddress(Address:addr, NumberType:size);
|
||||
|
||||
/**
|
||||
* Store up to 4 bytes to a memory address.
|
||||
*
|
||||
* @param addr Address to a memory location.
|
||||
* @param data Value to store at the address.
|
||||
* @param size How many bytes should be written.
|
||||
* @noreturn
|
||||
*/
|
||||
native StoreToAddress(Address:addr, data, NumberType:size);
|
||||
|
||||
#include <helpers>
|
||||
#include <entity>
|
||||
#include <entity_prop_stocks>
|
||||
|
@ -42,7 +42,7 @@
|
||||
*/
|
||||
|
||||
#define SMINTERFACE_GAMECONFIG_NAME "IGameConfigManager"
|
||||
#define SMINTERFACE_GAMECONFIG_VERSION 5
|
||||
#define SMINTERFACE_GAMECONFIG_VERSION 6
|
||||
|
||||
class SendProp;
|
||||
|
||||
@ -89,6 +89,15 @@ namespace SourceMod
|
||||
* address is NULL.
|
||||
*/
|
||||
virtual bool GetMemSig(const char *key, void **addr) =0;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the value of an address from the "Address" section.
|
||||
*
|
||||
* @param key Key to retrieve from the Address section.
|
||||
* @param addr Pointer to store the memory address.
|
||||
* @return True on success, false on failure.
|
||||
*/
|
||||
virtual bool GetAddress(const char *key, void **addr) =0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user