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:
Downtown1 2010-01-11 22:46:44 -08:00
parent ead6ca73e5
commit 18865c44c8
9 changed files with 330 additions and 2 deletions

View File

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

View File

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

View File

@ -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},
};

View File

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

View File

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

View File

@ -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) */
};
/**

View File

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

View File

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

View File

@ -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;
};
/**