initial import of sdktools extension. we're missing some of the calling mechanisms and linux tests/support!

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40956
This commit is contained in:
David Anderson 2007-06-16 08:36:34 +00:00
parent c133002c14
commit bdf651634c
11 changed files with 1320 additions and 77 deletions

View File

@ -22,6 +22,7 @@
*/
#include "extension.h"
#include "vcallbuilder.h"
/**
* @file extension.cpp
@ -31,19 +32,42 @@
SDKTools g_SdkTools; /**< Global singleton for extension's main interface */
IServerGameEnts *gameents = NULL;
IBinTools *g_pBinTools = NULL;
IPlayerManager *g_pPlayers = NULL;
IGameConfig *g_pGameConf = NULL;
HandleType_t g_CallHandle = 0;
SMEXT_LINK(&g_SdkTools);
extern sp_nativeinfo_t g_CallNatives[];
bool SDKTools::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
SM_GET_IFACE(PLAYERMANAGER, g_pPlayers);
sharesys->AddDependency(myself, "bintools.ext", true, true);
sharesys->AddNatives(myself, g_CallNatives);
g_pShareSys->AddDependency(myself, "bintools.ext", true, true);
if (!gameconfs->LoadGameConfigFile("sdktools.games", &g_pGameConf, error, maxlength))
{
return false;
}
g_CallHandle = handlesys->CreateType("ValveCall", this, 0, NULL, NULL, myself->GetIdentity(), NULL);
return true;
}
void SDKTools::OnHandleDestroy(HandleType_t type, void *object)
{
if (type == g_CallHandle)
{
ValveCall *v = (ValveCall *)object;
delete v;
}
}
void SDKTools::SDK_OnUnload()
{
gameconfs->CloseGameConfigFile(g_pGameConf);
}
bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
{
GET_V_IFACE_CURRENT(serverFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);

View File

@ -37,80 +37,27 @@
* @brief Implementation of the SDK Tools extension.
* Note: Uncomment one of the pre-defined virtual functions in order to use it.
*/
class SDKTools : public SDKExtension
class SDKTools : public SDKExtension, public IHandleTypeDispatch
{
public:
/**
* @brief This is called after the initial loading sequence has been processed.
*
* @param error Error message buffer.
* @param maxlength Size of error message buffer.
* @param late Whether or not the module was loaded after map load.
* @return True to succeed loading, false to fail.
*/
void OnHandleDestroy(HandleType_t type, void *object);
public:
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
/**
* @brief This is called right before the extension is unloaded.
*/
//virtual void SDK_OnUnload();
/**
* @brief This is called once all known extensions have been loaded.
* Note: It is is a good idea to add natives here, if any are provided.
*/
virtual void SDK_OnUnload();
virtual void SDK_OnAllLoaded();
/**
* @brief Called when the pause state is changed.
*/
//virtual void SDK_OnPauseChange(bool paused);
/**
* @brief this is called when Core wants to know if your extension is working.
*
* @param error Error message buffer.
* @param maxlength Size of error message buffer.
* @return True if working, false otherwise.
*/
virtual bool QueryRunning(char *error, size_t maxlength);
public:
#if defined SMEXT_CONF_METAMOD
/**
* @brief Called when Metamod is attached, before the extension version is called.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @param late Whether or not Metamod considers this a late load.
* @return True to succeed, false to fail.
*/
virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late);
/**
* @brief Called when Metamod is detaching, after the extension version is called.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
//virtual bool SDK_OnMetamodUnload(char *error, size_t maxlen);
/**
* @brief Called when Metamod's pause state is changing.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param paused Pause state being set.
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlen);
#endif
};
extern IServerGameEnts *gameents;
extern IBinTools *g_pBinTools;
extern IPlayerManager *g_pPlayers;
extern IGameConfig *g_pGameConf;
extern HandleType_t g_CallHandle;
#endif //_INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_

View File

@ -62,6 +62,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="vstdlib.lib tier0.lib tier1.lib"
OutputFile="$(OutDir)\sdktools.ext.dll"
LinkIncremental="2"
GenerateDebugInformation="true"
@ -184,6 +185,18 @@
RelativePath="..\extension.cpp"
>
</File>
<File
RelativePath="..\vcallbuilder.cpp"
>
</File>
<File
RelativePath="..\vcaller.cpp"
>
</File>
<File
RelativePath="..\vdecoder.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
@ -194,6 +207,14 @@
RelativePath="..\extension.h"
>
</File>
<File
RelativePath="..\vcallbuilder.h"
>
</File>
<File
RelativePath="..\vdecoder.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"

View File

@ -26,7 +26,7 @@
/* Basic information exposed publicly */
#define SMEXT_CONF_NAME "SDK Tools"
#define SMEXT_CONF_DESCRIPTION "Sample extension to help developers" /* :TODO: Describe this, heh */
#define SMEXT_CONF_DESCRIPTION "Source SDK Tools"
#define SMEXT_CONF_VERSION "0.0.0.0"
#define SMEXT_CONF_AUTHOR "AlliedModders"
#define SMEXT_CONF_URL "http://www.sourcemod.net/"
@ -45,4 +45,12 @@
*/
#define SMEXT_CONF_METAMOD
/** Enable interfaces you want to use here by uncommenting lines */
//#define SMEXT_ENABLE_FORWARDSYS
#define SMEXT_ENABLE_HANDLESYS
#define SMEXT_ENABLE_PLAYERHELPERS
//#define SMEXT_ENABLE_DBMANAGER
#define SMEXT_ENABLE_GAMECONF
#define SMEXT_ENABLE_MEMUTILS
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_

View File

@ -25,11 +25,32 @@
* @brief Contains wrappers for making Extensions easier to write.
*/
IShareSys *g_pShareSys = NULL; /**< Share system */
IExtension *myself = NULL; /**< Ourself */
IHandleSys *g_pHandleSys = NULL; /**< Handle system */
IShareSys *g_pShareSys = NULL; /**< Share system */
IShareSys *sharesys = NULL; /**< Share system */
ISourceMod *g_pSM = NULL; /**< SourceMod helpers */
ISourceMod *smutils = NULL; /**< SourceMod helpers */
#if defined SMEXT_ENABLE_FORWARDSYS
IForwardManager *g_pForwards = NULL; /**< Forward system */
IForwardManager *forwards = NULL; /**< Forward system */
#endif
#if defined SMEXT_ENABLE_HANDLESYS
IHandleSys *g_pHandleSys = NULL; /**< Handle system */
IHandleSys *handlesys = NULL; /**< Handle system */
#endif
#if defined SMEXT_ENABLE_PLAYERHELPERS
IPlayerManager *playerhelpers = NULL; /**< Player helpers */
#endif //SMEXT_ENABLE_PLAYERHELPERS
#if defined SMEXT_ENABLE_DBMANAGER
IDBManager *dbi = NULL; /**< DB Manager */
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_GAMECONF
IGameConfigManager *gameconfs = NULL; /**< Game config manager */
#endif
#if defined SMEXT_ENABLE_MEMUTILS
IMemoryUtils *memutils = NULL;
#endif
/** Exports the main interface */
PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI()
@ -48,7 +69,7 @@ SDKExtension::SDKExtension()
bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late)
{
g_pShareSys = sys;
g_pShareSys = sharesys = sys;
myself = me;
#if defined SMEXT_CONF_METAMOD
@ -63,10 +84,28 @@ bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error,
return false;
}
#endif
SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys);
SM_GET_IFACE(SOURCEMOD, g_pSM);
smutils = g_pSM;
#if defined SMEXT_ENABLE_HANDLESYS
SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys);
handlesys = g_pHandleSys;
#endif
#if defined SMEXT_ENABLE_FORWARDSYS
SM_GET_IFACE(FORWARDMANAGER, g_pForwards);
forwards = g_pForwards;
#endif
#if defined SMEXT_ENABLE_PLAYERHELPERS
SM_GET_IFACE(PLAYERMANAGER, playerhelpers);
#endif
#if defined SMEXT_ENABLE_DBMANAGER
SM_GET_IFACE(DBI, dbi);
#endif
#if defined SMEXT_ENABLE_GAMECONF
SM_GET_IFACE(GAMECONFIG, gameconfs);
#endif
#if defined SMEXT_ENABLE_MEMUTILS
SM_GET_IFACE(MEMORYUTILS, memutils);
#endif
if (SDK_OnLoad(error, maxlength, late))
{

View File

@ -30,7 +30,21 @@
#include <sp_vm_api.h>
#include <sm_platform.h>
#include <ISourceMod.h>
#if defined SMEXT_ENABLE_FORWARDSYS
#include <IForwardSys.h>
#endif //SMEXT_ENABLE_FORWARDSYS
#if defined SMEXT_ENABLE_PLAYERHELPERS
#include <IPlayerHelpers.h>
#endif //SMEXT_ENABLE_PlAYERHELPERS
#if defined SMEXT_ENABLE_DBMANAGER
#include <IDBDriver.h>
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_GAMECONF
#include <IGameConfigs.h>
#endif
#if defined SMEXT_ENABLE_MEMUTILS
#include <IMemoryUtils.h>
#endif
#if defined SMEXT_CONF_METAMOD
#include <ISmmPlugin.h>
@ -108,7 +122,7 @@ public:
virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
#endif
public: // IExtensionInterface
public: //IExtensionInterface
virtual bool OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late);
virtual void OnExtensionUnload();
virtual void OnExtensionsAllLoaded();
@ -138,9 +152,9 @@ public: // IExtensionInterface
/** Returns date string */
virtual const char *GetExtensionDateString();
#if defined SMEXT_CONF_METAMOD
public: // ISmmPlugin
public: //ISmmPlugin
/** Called when the extension is attached to Metamod. */
virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late);
virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlength, bool late);
/** Returns the author to MM */
virtual const char *GetAuthor();
/** Returns the name to MM */
@ -158,11 +172,11 @@ public: // ISmmPlugin
/** Returns the logtag to MM */
virtual const char *GetLogTag();
/** Called on unload */
virtual bool Unload(char *error, size_t maxlen);
virtual bool Unload(char *error, size_t maxlength);
/** Called on pause */
virtual bool Pause(char *error, size_t maxlen);
virtual bool Pause(char *error, size_t maxlength);
/** Called on unpause */
virtual bool Unpause(char *error, size_t maxlen);
virtual bool Unpause(char *error, size_t maxlength);
private:
bool m_SourceMMLoaded;
bool m_WeAreUnloaded;
@ -171,12 +185,34 @@ private:
};
extern SDKExtension *g_pExtensionIface;
extern IExtension *myself;
extern IShareSys *g_pShareSys;
extern IExtension *myself;
extern IHandleSys *g_pHandleSys;
extern IShareSys *sharesys; /* Note: Newer name */
extern ISourceMod *g_pSM;
extern ISourceMod *smutils; /* Note: Newer name */
/* Optional interfaces are below */
#if defined SMEXT_ENABLE_FORWARDSYS
extern IForwardManager *g_pForwards;
extern IForwardManager *forwards; /* Note: Newer name */
#endif //SMEXT_ENABLE_FORWARDSYS
#if defined SMEXT_ENABLE_HANDLESYS
extern IHandleSys *g_pHandleSys;
extern IHandleSys *handlesys; /* Note: Newer name */
#endif //SMEXT_ENABLE_HANDLESYS
#if defined SMEXT_ENABLE_PLAYERHELPERS
extern IPlayerManager *playerhelpers;
#endif //SMEXT_ENABLE_PLAYERHELPERS
#if defined SMEXT_ENABLE_DBMANAGER
extern IDBManager *dbi;
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_GAMECONF
extern IGameConfigManager *gameconfs;
#endif
#if defined SMEXT_ENABLE_MEMUTILS
extern IMemoryUtils *memutils;
#endif
#if defined SMEXT_CONF_METAMOD
PLUGIN_GLOBALVARS();
@ -192,7 +228,7 @@ extern IServerGameDLL *gamedll;
{ \
if (error) \
{ \
snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \
snprintf(error, maxlength, "Could not find interface: %s (version: %d)", SMINTERFACE_##prefix##_NAME, SMINTERFACE_##prefix##_VERSION); \
return false; \
} \
}

View File

@ -0,0 +1,136 @@
#include "vcallbuilder.h"
#include "extension.h"
ValveCall::~ValveCall()
{
while (!stk.empty())
{
unsigned char *ptr = stk.front();
delete [] ptr;
stk.pop();
}
call->Destroy();
delete [] retbuf;
delete [] vparams;
}
unsigned char *ValveCall::stk_get()
{
unsigned char *ptr;
if (stk.empty())
{
ptr = new unsigned char[stackSize];
} else {
ptr = stk.front();
stk.pop();
}
return ptr;
}
void ValveCall::stk_put(unsigned char *ptr)
{
stk.push(ptr);
}
ValveCall *CreateValveVCall(unsigned int vtableIdx,
ValveCallType vcalltype,
const ValvePassInfo *retInfo,
const ValvePassInfo *params,
unsigned int numParams)
{
if (numParams > 32)
{
return NULL;
}
ValveCall *vc = new ValveCall;
size_t size = 0;
vc->stackSize = 0;
/* Get return information - encode only*/
PassInfo retBuf;
size_t retBufSize = 0;
if (retInfo)
{
if ((size = ValveParamToBinParam(retInfo->vtype, retInfo->type, retInfo->flags, &retBuf)) == 0)
{
delete vc;
return NULL;
}
retBufSize = size;
}
/* Get parameter info */
PassInfo paramBuf[32];
for (unsigned int i=0; i<numParams; i++)
{
if ((size = ValveParamToBinParam(params[i].vtype, params[i].type, params[i].flags, &paramBuf[i])) == 0)
{
delete vc;
return NULL;
}
vc->stackSize += size;
}
/* Now we can try creating the call */
if ((vc->call = g_pBinTools->CreateVCall(vtableIdx,
0,
0,
(retInfo ? &retBuf : NULL),
paramBuf,
numParams))
== NULL)
{
if (!vc->call)
{
delete vc;
return NULL;
}
}
/* Allocate extra space for thisptr AND ret buffer, even if we don't use it */
vc->vparams = new ValvePassInfo[numParams + 2];
/* We've got the call and everything is encoded.
* It's time to save the valve specific information and helper variables.
*/
if (retInfo)
{
/* Allocate and copy */
vc->retinfo = &(vc->vparams[numParams]);
*vc->retinfo = *retInfo;
vc->retinfo->offset = 0;
/* Allocate stack space */
vc->retbuf = new unsigned char[retBufSize];
} else {
vc->retinfo = NULL;
vc->retbuf = NULL;
}
/* Save the this info for the dynamic decoder */
vc->thisinfo = &(vc->vparams[numParams + 1]);
vc->thisinfo->type = PassType_Basic;
if (vcalltype == ValveCall_Entity)
{
vc->thisinfo->vtype = Valve_CBaseEntity;
vc->thisinfo->decflags |= VDECODE_FLAG_ALLOWWORLD;
} else if (vcalltype == ValveCall_Player) {
vc->thisinfo->vtype = Valve_CBasePlayer;
vc->thisinfo->decflags = 0;
}
vc->thisinfo->encflags = 0;
vc->thisinfo->flags = PASSFLAG_BYVAL;
vc->thisinfo->offset = 0;
vc->stackSize += sizeof(void *);
/* Now, save info about each parameter. */
for (unsigned int i=0; i<numParams; i++)
{
/* Copy */
vc->vparams[i] = params[i];
vc->vparams[i].offset = vc->call->GetParamInfo(i)->offset;
}
return vc;
}

View File

@ -0,0 +1,58 @@
#ifndef _INCLUDE_SOURCEMOD_VALVE_CALLER_H_
#define _INCLUDE_SOURCEMOD_VALVE_CALLER_H_
#include <sh_stack.h>
#include <extensions/IBinTools.h>
#include "vdecoder.h"
using namespace SourceMod;
using namespace SourceHook;
/**
* @brief Valve pre-defined calling types
*/
enum ValveCallType
{
ValveCall_Static, /**< Static call */
ValveCall_Entity, /**< Thiscall (CBaseEntity implicit first parameter) */
ValveCall_Player, /**< Thiscall (CBasePlayer implicit first parameter) */
};
/**
* @brief Valve parameter info
*/
struct ValvePassInfo
{
ValveType vtype; /**< IN: Valve type */
unsigned int decflags; /**< IN: VDECODE_FLAG_* */
unsigned int encflags; /**< IN: VENCODE_FLAG_* */
PassType type; /**< IN: Pass information */
unsigned int flags; /**< IN: Pass flags */
size_t offset; /**< OUT: stack offset */
};
/**
* @brief Info necessary to call a Valve function
*/
struct ValveCall
{
ICallWrapper *call; /**< From IBinTools */
ValvePassInfo *vparams; /**< Valve parameter info */
ValvePassInfo *retinfo; /**< Return buffer info */
ValvePassInfo *thisinfo; /**< Thiscall info */
size_t stackSize; /**< Stack size */
unsigned char *retbuf; /**< Return buffer */
CStack<unsigned char *> stk; /**< Parameter stack */
unsigned char *stk_get();
void stk_put(unsigned char *ptr);
~ValveCall();
};
ValveCall *CreateValveVCall(unsigned int vtableIdx,
ValveCallType vcalltype,
const ValvePassInfo *retInfo,
const ValvePassInfo *params,
unsigned int numParams);
#endif //_INCLUDE_SOURCEMOD_VALVE_CALLER_H_

View File

@ -0,0 +1,376 @@
#include "extension.h"
#include "vcallbuilder.h"
enum SDKLibrary
{
SDKLibrary_Server, /**< server.dll/server_i486.so */
SDKLibrary_Engine, /**< engine.dll/engine_*.so */
};
enum SDKPassMethod
{
SDKPass_Pointer, /**< Pass as a pointer */
SDKPass_Plain, /**< Pass as plain data */
SDKPass_ByValue, /**< Pass an object by value */
SDKPass_ByRef, /**< Pass an object by reference */
};
int s_vtbl_index = 0;
void *s_call_addr = NULL;
ValveCallType s_vcalltype = ValveCall_Static;
bool s_has_return = false;
ValvePassInfo s_return;
unsigned int s_numparams = 0;
ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
{
if (method == SDKPass_Pointer)
{
type = PassType_Basic;
if (vtype == Valve_POD
|| vtype == Valve_Float)
{
flags = PASSFLAG_BYREF;
} else {
flags = PASSFLAG_BYVAL;
}
} else if (method == SDKPass_Plain) {
type = PassType_Basic;
flags = PASSFLAG_BYVAL;
} else if (method == SDKPass_ByValue) {
if (vtype == Valve_Vector
|| vtype == Valve_QAngle)
{
type = PassType_Object;
} else {
type = PassType_Basic;
}
flags = PASSFLAG_BYVAL;
} else if (method == SDKPass_ByRef) {
if (vtype == Valve_Vector
|| vtype == Valve_QAngle)
{
type = PassType_Object;
} else {
type = PassType_Basic;
}
flags = PASSFLAG_BYREF;
}
}
static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
{
s_numparams = 0;
s_vtbl_index = 0;
s_call_addr = NULL;
s_has_return = false;
s_vcalltype = (ValveCallType)params[1];
return 1;
}
static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
{
s_vtbl_index = params[1];
return 1;
}
static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
{
void *addrInBase = NULL;
if (params[1] == SDKLibrary_Server)
{
addrInBase = g_SMAPI->serverFactory(false);
} else if (params[1] == SDKLibrary_Engine) {
addrInBase = g_SMAPI->engineFactory(false);
}
if (addrInBase == NULL)
{
return 0;
}
char *sig;
pContext->LocalToString(params[2], &sig);
#if defined PLATFORM_LINUX
if (sig[0] == '@')
{
Dl_info info;
if (dladdr(addrInBase, &info) == 0)
{
return 0;
}
void *handle = dlopen(info.dli_fname, RTLD_NOW);
if (!handle)
{
return 0;
}
s_call_addr = dlsym(handle, &sig[1]);
dlclose(handle);
return (s_call_addr != NULL) ? 1 : 0;
}
#endif
s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
return (s_call_addr != NULL) ? 1 : 0;
}
static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
{
IGameConfig *conf;
if (params[1] == BAD_HANDLE)
{
conf = g_pGameConf;
} else {
HandleError err;
if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
}
}
char *key;
pContext->LocalToString(params[3], &key);
if (params[2] == 0)
{
return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
} else if (params[2] == 1) {
bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
return (result && s_call_addr != NULL) ? 1 : 0;
}
return 0;
}
static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
{
s_has_return = true;
s_return.vtype = (ValveType)params[1];
DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
s_return.decflags = params[3];
s_return.encflags = params[4];
return 1;
}
static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
{
if (s_numparams >= SP_MAX_EXEC_PARAMS)
{
return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
}
ValvePassInfo *info = &s_params[s_numparams++];
info->vtype = (ValveType)params[1];
DecodePassMethod(info->vtype, (SDKPassMethod)params[2], info->type, info->flags);
info->decflags = params[3];
info->encflags = params[4];
return 1;
}
static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
{
ValveCall *vc = NULL;
if (s_vtbl_index)
{
vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
}
if (!vc)
{
return BAD_HANDLE;
}
Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
if (!hndl)
{
delete vc;
}
return hndl;
}
static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
{
ValveCall *vc;
HandleError err;
HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
}
unsigned char *ptr = vc->stk_get();
unsigned int numparams = (unsigned)params[0];
unsigned int startparam = 2;
/* Do we need to write a thispointer? */
if (vc->thisinfo &&
(vc->thisinfo->vtype == Valve_CBaseEntity
|| vc->thisinfo->vtype == Valve_CBasePlayer))
{
if (startparam > numparams)
{
vc->stk_put(ptr);
return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
}
if (DecodeValveParam(pContext,
params[startparam],
vc->thisinfo->vtype,
vc->thisinfo->decflags | VDECODE_FLAG_BYREF,
vc->thisinfo->type,
ptr) == Data_Fail)
{
vc->stk_put(ptr);
return 0;
}
startparam++;
}
/* See if we need to skip any more parameters */
unsigned int retparam = startparam;
if (vc->retinfo)
{
if (vc->retinfo->vtype == Valve_String)
{
startparam += 2;
} else if (vc->retinfo->vtype == Valve_Vector
|| vc->retinfo->vtype == Valve_QAngle)
{
startparam += 1;
}
}
unsigned int callparams = vc->call->GetParamCount();
bool will_copyback = false;
for (unsigned int i=0; i<callparams; i++)
{
unsigned int p = startparam + i;
if (p > numparams)
{
vc->stk_put(ptr);
return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
}
if (DecodeValveParam(pContext,
params[p],
vc->vparams[i].vtype,
vc->vparams[i].decflags | VDECODE_FLAG_BYREF,
vc->vparams[i].type,
ptr + vc->vparams[i].offset) == Data_Fail)
{
vc->stk_put(ptr);
return 0;
}
if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
{
will_copyback = true;
}
}
/* Make the actual call */
vc->call->Execute(ptr, vc->retbuf);
/* Do we need to copy anything back? */
if (will_copyback)
{
for (unsigned int i=0; i<callparams; i++)
{
if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
{
if (EncodeValveParam(pContext,
startparam + i,
vc->vparams[i].vtype,
vc->vparams[i].type,
ptr + vc->vparams[i].offset) == Data_Fail)
{
vc->stk_put(ptr);
return 0;
}
}
}
}
/* Save stack once and for all */
vc->stk_put(ptr);
/* Figure out how to decode the return information */
if (vc->retinfo)
{
if (vc->retinfo->vtype == Valve_String)
{
if (numparams < 3)
{
return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
}
cell_t *addr;
size_t written;
pContext->LocalToPhysAddr(params[retparam+1], &addr);
pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
return (cell_t)written;
} else if (vc->retinfo->vtype == Valve_Vector
|| vc->retinfo->vtype == Valve_QAngle)
{
if (numparams < 2)
{
return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
}
if (EncodeValveParam(pContext, params[retparam], vc->retinfo->vtype, vc->retinfo->type, vc->retbuf)
== Data_Fail)
{
return 0;
}
} else if (vc->retinfo->vtype == Valve_CBaseEntity
|| vc->retinfo->vtype == Valve_CBasePlayer)
{
CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
if (!pEntity)
{
return -1;
}
edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
if (!pEdict || pEdict->IsFree())
{
return -1;
}
return engine->IndexOfEdict(pEdict);
} else if (vc->retinfo->vtype == Valve_Edict) {
edict_t *pEdict = *(edict_t **)(vc->retbuf);
if (!pEdict || pEdict->IsFree())
{
return -1;
}
return engine->IndexOfEdict(pEdict);
} else {
cell_t *addr = (cell_t *)vc->retbuf;
if (vc->retinfo->flags & PASSFLAG_BYREF)
{
addr = *(cell_t **)addr;
}
return *addr;
}
}
return 0;
}
sp_nativeinfo_t g_CallNatives[] =
{
{"StartPrepSDKCall", StartPrepSDKCall},
{"PrepSDKCall_SetVirtual", PrepSDKCall_SetVirtual},
{"PrepSDKCall_SetSignature", PrepSDKCall_SetSignature},
{"PrepSDKCall_SetFromConf", PrepSDKCall_SetFromConf},
{"PrepSDKCall_SetReturnInfo", PrepSDKCall_SetReturnInfo},
{"PrepSDKCall_AddParameter", PrepSDKCall_AddParameter},
{"EndPrepSDKCall", EndPrepSDKCall},
{"SDKCall", SDKCall},
{NULL, NULL},
};

View File

@ -0,0 +1,505 @@
#include "smsdk_ext.h"
#include "extension.h"
#include "vdecoder.h"
using namespace SourceMod;
using namespace SourcePawn;
/**
* For object pointers, the data looks like this instead:
* 4 bytes: POINTER TO NEXT FOUR BYTES
* + bytes: Object internal data
*
* We use the virtual stack as extra fake stack space and create a temp object.
* If these objects had destructors, we'd need to fake destroy toom of course.
* Of course, BinTools only reads the first four bytes and passes the pointer.
*/
size_t ValveParamToBinParam(ValveType type,
PassType pass,
unsigned int flags,
PassInfo *info)
{
switch (type)
{
case Valve_Vector:
{
size_t mySize = sizeof(Vector *);
if (pass == PassType_Basic)
{
if (info->flags & PASSFLAG_BYREF)
{
return 0;
}
info->type = PassType_Basic;
info->flags = flags;
info->size = sizeof(Vector *);
mySize += sizeof(Vector);
} else if (pass == PassType_Object) {
info->type = PassType_Object;
info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
info->size = sizeof(Vector);
} else {
return 0;
}
return mySize;
}
case Valve_QAngle:
{
size_t mySize = sizeof(QAngle *);
if (pass == PassType_Basic)
{
if (info->flags & PASSFLAG_BYREF)
{
return 0;
}
info->type = PassType_Basic;
info->flags = flags;
info->size = sizeof(QAngle *);
mySize += sizeof(QAngle);
} else if (pass == PassType_Object) {
info->type = PassType_Object;
info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
info->size = sizeof(QAngle);
} else {
return 0;
}
return mySize;
}
case Valve_CBaseEntity:
case Valve_CBasePlayer:
case Valve_POD:
case Valve_Edict:
case Valve_String:
{
if (pass != PassType_Basic
|| (info->flags & PASSFLAG_BYREF))
{
return 0;
}
info->type = PassType_Basic;
info->flags = flags;
info->size = sizeof(void *);
return sizeof(void *);
}
case Valve_Float:
{
info->type = PassType_Float;
info->flags = flags;
info->size = sizeof(float);
return sizeof(float);
}
}
return 0;
}
DataStatus EncodeValveParam(IPluginContext *pContext,
cell_t param,
ValveType type,
PassType pass,
const void *buffer)
{
switch (type)
{
case Valve_Vector:
{
Vector *v = NULL;
if (pass == PassType_Basic)
{
v = *(Vector **)((unsigned char *)buffer + sizeof(Vector *));
} else if (pass == PassType_Object) {
v = (Vector *)buffer;
}
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
addr[0] = v->x;
addr[1] = v->y;
addr[2] = v->z;
return Data_Okay;
}
case Valve_QAngle:
{
QAngle *q = NULL;
if (pass == PassType_Basic)
{
q = *(QAngle **)((unsigned char *)buffer + sizeof(QAngle *));
} else if (pass == PassType_Object) {
q = (QAngle *)buffer;
}
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
addr[0] = q->x;
addr[1] = q->y;
addr[2] = q->z;
return Data_Okay;
}
case Valve_CBaseEntity:
case Valve_CBasePlayer:
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
CBaseEntity *pEntity = *(CBaseEntity **)buffer;
if (pEntity)
{
edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
*addr = engine->IndexOfEdict(pEdict);
} else {
*addr = -1;
}
return Data_Okay;
}
case Valve_Edict:
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
edict_t *pEdict = *(edict_t **)buffer;
if (pEdict)
{
*addr = engine->IndexOfEdict(pEdict);
} else {
*addr = -1;
}
return Data_Okay;
}
case Valve_POD:
case Valve_Float:
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
*addr = *(cell_t *)buffer;
return Data_Okay;
}
}
return Data_Fail;
}
DataStatus DecodeValveParam(IPluginContext *pContext,
cell_t param,
ValveType vtype,
unsigned int vflags,
PassType type,
void *buffer)
{
switch (vtype)
{
case Valve_Vector:
{
cell_t *addr;
int err;
err = pContext->LocalToPhysAddr(param, &addr);
unsigned char *mem = (unsigned char *)buffer;
if (type == PassType_Basic)
{
/* Store the object in the next N bytes, and store
* a pointer to that object right beforehand.
*/
Vector **realPtr = (Vector **)buffer;
if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
{
if (vflags & VDECODE_FLAG_ALLOWNULL)
{
*realPtr = NULL;
return Data_Okay;
} else {
pContext->ThrowNativeError("NULL not allowed");
return Data_Fail;
}
} else {
mem += sizeof(Vector *);
*realPtr = (Vector *)mem;
}
}
if (err != SP_ERROR_NONE)
{
pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
return Data_Fail;
}
/* Use placement new to initialize the object cleanly
* This has no destructor so we don't need to do
* DestroyValveParam() or something :]
*/
Vector *v = new (mem) Vector(
sp_ctof(addr[0]),
sp_ctof(addr[1]),
sp_ctof(addr[2]));
return Data_Okay;
}
case Valve_QAngle:
{
cell_t *addr;
int err;
err = pContext->LocalToPhysAddr(param, &addr);
unsigned char *mem = (unsigned char *)buffer;
if (type == PassType_Basic)
{
/* Store the object in the next N bytes, and store
* a pointer to that object right beforehand.
*/
QAngle **realPtr = (QAngle **)buffer;
if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
{
if (!(vflags & VDECODE_FLAG_ALLOWNULL))
{
pContext->ThrowNativeError("NULL not allowed");
return Data_Fail;
} else {
*realPtr = NULL;
return Data_Okay;
}
} else {
mem += sizeof(QAngle *);
*realPtr = (QAngle *)mem;
}
}
if (err != SP_ERROR_NONE)
{
pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
return Data_Fail;
}
/* Use placement new to initialize the object cleanly
* This has no destructor so we don't need to do
* DestroyValveParam() or something :]
*/
QAngle *v = new (mem) QAngle(
sp_ctof(addr[0]),
sp_ctof(addr[1]),
sp_ctof(addr[2]));
return Data_Okay;
}
case Valve_CBasePlayer:
{
edict_t *pEdict;
if (vflags & VDECODE_FLAG_BYREF)
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
param = *addr;
}
if (param >= 1 && param <= playerhelpers->GetMaxClients())
{
IGamePlayer *player = playerhelpers->GetGamePlayer(param);
if ((vflags & VDECODE_FLAG_ALLOWNOTINGAME)
&& !player->IsConnected())
{
pContext->ThrowNativeError("Client %d is not connected", param);
return Data_Fail;
} else if (!player->IsInGame()) {
pContext->ThrowNativeError("Client %d is not in game", param);
return Data_Fail;
}
pEdict = player->GetEdict();
} else if (param == -1) {
if (vflags & VDECODE_FLAG_ALLOWNULL)
{
pEdict = NULL;
} else {
pContext->ThrowNativeError("NULL not allowed");
return Data_Fail;
}
} else if (param == 0) {
if (vflags & VDECODE_FLAG_ALLOWWORLD)
{
pEdict = engine->PEntityOfEntIndex(0);
} else {
pContext->ThrowNativeError("World not allowed");
return Data_Fail;
}
} else {
pContext->ThrowNativeError("Entity index %d is not a valid client", param);
return Data_Fail;
}
CBaseEntity *pEntity;
if (pEdict)
{
IServerUnknown *pUnknown = pEdict->GetUnknown();
if (!pUnknown)
{
pContext->ThrowNativeError("Entity %d is a not an IServerUnknown");
return Data_Fail;
}
pEntity = pUnknown->GetBaseEntity();
if (!pEntity)
{
pContext->ThrowNativeError("Entity %d is not a CBaseEntity");
return Data_Fail;
}
} else {
pEdict = NULL;
}
CBaseEntity **ebuf = (CBaseEntity **)buffer;
*ebuf = pEntity;
return Data_Okay;
}
case Valve_CBaseEntity:
{
edict_t *pEdict;
if (vflags & VDECODE_FLAG_BYREF)
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
param = *addr;
}
if (param >= 1 && param <= playerhelpers->GetMaxClients())
{
IGamePlayer *player = playerhelpers->GetGamePlayer(param);
if ((vflags & VDECODE_FLAG_ALLOWNOTINGAME)
&& !player->IsConnected())
{
pContext->ThrowNativeError("Client %d is not connected", param);
return Data_Fail;
} else if (!player->IsInGame()) {
pContext->ThrowNativeError("Client %d is not in game", param);
return Data_Fail;
}
pEdict = player->GetEdict();
} else if (param == -1) {
if (vflags & VDECODE_FLAG_ALLOWNULL)
{
pEdict = NULL;
} else {
pContext->ThrowNativeError("NULL not allowed");
return Data_Fail;
}
} else if (param == 0) {
if (vflags & VDECODE_FLAG_ALLOWWORLD)
{
pEdict = engine->PEntityOfEntIndex(0);
} else {
pContext->ThrowNativeError("World not allowed");
return Data_Fail;
}
} else {
pEdict = engine->PEntityOfEntIndex(param);
if (!pEdict || pEdict->IsFree())
{
pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
return Data_Fail;
}
}
CBaseEntity *pEntity;
if (pEdict)
{
IServerUnknown *pUnknown = pEdict->GetUnknown();
if (!pUnknown)
{
pContext->ThrowNativeError("Entity %d is a not an IServerUnknown");
return Data_Fail;
}
pEntity = pUnknown->GetBaseEntity();
if (!pEntity)
{
pContext->ThrowNativeError("Entity %d is not a CBaseEntity");
return Data_Fail;
}
} else {
pEdict = NULL;
}
CBaseEntity **ebuf = (CBaseEntity **)buffer;
*ebuf = pEntity;
return Data_Okay;
}
case Valve_Edict:
{
edict_t *pEdict;
if (vflags & VDECODE_FLAG_BYREF)
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
param = *addr;
}
if (param >= 1 && param <= playerhelpers->GetMaxClients())
{
IGamePlayer *player = playerhelpers->GetGamePlayer(param);
if ((vflags & VDECODE_FLAG_ALLOWNOTINGAME)
&& !player->IsConnected())
{
pContext->ThrowNativeError("Client %d is not connected", param);
return Data_Fail;
} else if (!player->IsInGame()) {
pContext->ThrowNativeError("Client %d is not in game", param);
return Data_Fail;
}
pEdict = player->GetEdict();
} else if (param == -1) {
if (vflags & VDECODE_FLAG_ALLOWNULL)
{
pEdict = NULL;
} else {
pContext->ThrowNativeError("NULL not allowed");
return Data_Fail;
}
} else if (param == 0) {
if (vflags & VDECODE_FLAG_ALLOWWORLD)
{
pEdict = engine->PEntityOfEntIndex(0);
} else {
pContext->ThrowNativeError("World not allowed");
return Data_Fail;
}
} else {
pEdict = engine->PEntityOfEntIndex(param);
if (!pEdict || pEdict->IsFree())
{
pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
return Data_Fail;
}
}
edict_t **ebuf = (edict_t **)buffer;
*ebuf = pEdict;
return Data_Okay;
}
case Valve_POD:
case Valve_Float:
{
if (vflags & VDECODE_FLAG_BYREF)
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
param = *addr;
}
*(cell_t *)buffer = param;
return Data_Okay;
}
case Valve_String:
{
char *addr;
pContext->LocalToString(param, &addr);
*(char **)buffer = addr;
return Data_Okay;
}
}
return Data_Fail;
}

View File

@ -0,0 +1,93 @@
#ifndef _INCLUDE_SOURCEMOD_VDECODER_H_
#define _INCLUDE_SOURCEMOD_VDECODER_H_
#include <sm_platform.h>
#include <sp_vm_api.h>
#include <extensions/IBinTools.h>
using namespace SourceMod;
using namespace SourcePawn;
/**
* @brief Encapsulates types from the SDK
*/
enum ValveType
{
Valve_CBaseEntity, /**< CBaseEntity */
Valve_CBasePlayer, /**< CBasePlayer (disallow normal ents) */
Valve_Vector, /**< Vector */
Valve_QAngle, /**< QAngle */
Valve_POD, /**< Plain old data */
Valve_Float, /**< Float */
Valve_Edict, /**< Edict */
Valve_String, /**< String */
};
enum DataStatus
{
Data_Fail = 0,
Data_Okay = 1,
};
#define VDECODE_FLAG_ALLOWNULL (1<<0) /**< Allow NULL for pointers */
#define VDECODE_FLAG_ALLOWNOTINGAME (1<<1) /**< Allow players not in game */
#define VDECODE_FLAG_ALLOWWORLD (1<<2) /**< Allow World entity */
#define VDECODE_FLAG_BYREF (1<<3) /**< Floats/ints by reference */
#define VENCODE_FLAG_COPYBACK (1<<0) /**< Copy back data */
/**
* @brief Converts a valve parameter to a bintools parameter.
*
* @param type Valve type.
* @param pass Either basic or object.
* @param flags Either BYVAL or BYREF.
* @param info Buffer to store param info in.
* @return Number of bytes this will use in the virtual stack,
* or 0 if conversion was impossible.
*/
size_t ValveParamToBinParam(ValveType type,
PassType pass,
unsigned int flags,
PassInfo *info);
/**
* @brief Decodes data from a plugin to native data.
*
* Note: If you're going to return false, make sure to
* throw an error.
*
* @param pContext Plugin context.
* @param param Parameter value from params array.
* @param type Valve type.
* @param pass Pass info from bin tools.
* @param buffer Buffer space in the virutal stack.
* @return True on success, false otherwise.
*/
DataStatus DecodeValveParam(IPluginContext *pContext,
cell_t param,
ValveType type,
unsigned int vflags,
PassType pass,
void *buffer);
/**
* @brief Encodes native data back into a plugin.
*
* Note: If you're going to return false, make sure to
* throw an error.
*
* @param pContext Plugin context.
* @param param Parameter value from params array.
* @param type Valve type.
* @param pass Pass info from bin tools.
* @param buffer Buffer space in the virutal stack.
* @return True on success, false otherwise.
*/
DataStatus EncodeValveParam(IPluginContext *pContext,
cell_t param,
ValveType type,
PassType pass,
const void *buffer);
#endif //_INCLUDE_SOURCEMOD_VDECODER_H_