sm-ext-dhooks2/natives.cpp
Peace-Maker 88451023fb Fix changing of charptr and vectorptr returns and parameters
The new buffer/vector was freed before the old function was called with the new parameters
or the return value could be used by the called. This caused undefined behavior which seemed
to be fine before, where free didn't change the user-payload. free does change the user data now,
causing the changed values to be garbage.

Wait until the next frame before deleting the newly allocated buffers/vectors, so the original code had a chance
to use the live pointers. AddFrameAction might be a bad choice if our hook happens before the game ticks, but
we can tackle that problem when it happens ;)
2019-08-03 18:26:53 +02:00

1378 lines
41 KiB
C++

#include "natives.h"
#include "util.h"
#include "dynhooks_sourcepawn.h"
#include "signatures.h"
// Must match same enum in sdktools.inc
enum SDKFuncConfSource
{
SDKConf_Virtual,
SDKConf_Signature,
SDKConf_Address
};
bool GetHandleIfValidOrError(HandleType_t type, void **object, IPluginContext *pContext, cell_t param)
{
if(param == BAD_HANDLE)
{
return pContext->ThrowNativeError("Invalid Handle %i", BAD_HANDLE) != 0;
}
HandleError err;
HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
if((err = handlesys->ReadHandle(param, type, &sec, object)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", param, err) != 0;
}
return true;
}
IPluginFunction *GetCallback(IPluginContext *pContext, HookSetup * setup, const cell_t *params, cell_t callback_index)
{
IPluginFunction *ret = NULL;
if (params[0] >= callback_index)
{
ret = pContext->GetFunctionById(params[callback_index]);
}
if (!ret && setup->callback)
{
ret = setup->callback;
}
return ret;
}
//native Handle:DHookCreate(offset, HookType:hooktype, ReturnType:returntype, ThisPointerType:thistype, DHookCallback:callback = INVALID_FUNCTION); // Callback is now optional here.
cell_t Native_CreateHook(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup = new HookSetup((ReturnType)params[3], PASSFLAG_BYVAL, (HookType)params[2], (ThisPointerType)params[4], params[1], pContext->GetFunctionById(params[5]));
Handle_t hndl = handlesys->CreateHandle(g_HookSetupHandle, setup, pContext->GetIdentity(), myself->GetIdentity(), NULL);
if(!hndl)
{
delete setup;
return pContext->ThrowNativeError("Failed to create hook");
}
return hndl;
}
//native Handle:DHookCreateDetour(Address:funcaddr, CallingConvention:callConv, ReturnType:returntype, ThisPointerType:thistype);
cell_t Native_CreateDetour(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup = new HookSetup((ReturnType)params[3], PASSFLAG_BYVAL, (CallingConvention)params[2], (ThisPointerType)params[4], (void *)params[1]);
Handle_t hndl = handlesys->CreateHandle(g_HookSetupHandle, setup, pContext->GetIdentity(), myself->GetIdentity(), NULL);
if (!hndl)
{
delete setup;
return pContext->ThrowNativeError("Failed to create hook");
}
return hndl;
}
// native Handle:DHookCreateFromConf(Handle:gameconf, const String:function[]);
cell_t Native_DHookCreateFromConf(IPluginContext *pContext, const cell_t *params)
{
IGameConfig *conf;
HandleError err;
if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == nullptr)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
}
char *function;
pContext->LocalToString(params[2], &function);
SignatureWrapper *sig = g_pSignatures->GetFunctionSignature(function);
if (!sig)
{
return pContext->ThrowNativeError("Function signature \"%s\" was not found.", function);
}
HookSetup *setup = nullptr;
// This is a virtual hook.
if (sig->offset.length() > 0)
{
int offset;
if (!conf->GetOffset(sig->offset.chars(), &offset))
{
return BAD_HANDLE;
}
setup = new HookSetup(sig->retType, PASSFLAG_BYVAL, sig->hookType, sig->thisType, offset, nullptr);
}
// This is a detour.
else
{
void *addr = nullptr;;
if (sig->signature.length() > 0)
{
if (!conf->GetMemSig(sig->signature.chars(), &addr) || !addr)
{
return BAD_HANDLE;
}
}
else
{
if (!conf->GetAddress(sig->address.chars(), &addr) || !addr)
{
return BAD_HANDLE;
}
}
setup = new HookSetup(sig->retType, PASSFLAG_BYVAL, sig->callConv, sig->thisType, addr);
}
// Push all the arguments.
for (ArgumentInfo &arg : sig->args)
{
ParamInfo info = arg.info;
setup->params.push_back(info);
}
// Create the handle to hold this setup.
Handle_t hndl = handlesys->CreateHandle(g_HookSetupHandle, setup, pContext->GetIdentity(), myself->GetIdentity(), NULL);
if (!hndl)
{
delete setup;
return pContext->ThrowNativeError("Failed to create hook");
}
return hndl;
}
//native bool:DHookSetFromConf(Handle:setup, Handle:gameconf, SDKFuncConfSource:source, const String:name[]);
cell_t Native_SetFromConf(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup;
if (!GetHandleIfValidOrError(g_HookSetupHandle, (void **)&setup, pContext, params[1]))
{
return 0;
}
IGameConfig *conf;
HandleError err;
if ((conf = gameconfs->ReadHandle(params[2], pContext->GetIdentity(), &err)) == nullptr)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[2], err);
}
char *key;
pContext->LocalToString(params[4], &key);
int offset = -1;
void *addr = nullptr;;
switch (params[3])
{
case SDKConf_Virtual:
if (!conf->GetOffset(key, &offset))
{
return 0;
}
break;
case SDKConf_Signature:
if (!conf->GetMemSig(key, &addr) || !addr)
{
return 0;
}
break;
case SDKConf_Address:
if (!conf->GetAddress(key, &addr) || !addr)
{
return 0;
}
break;
default:
return pContext->ThrowNativeError("Unknown SDKFuncConfSource: %d", params[3]);
}
// Save the new info. This always invalidates the other option.
// Detour or vhook.
setup->funcAddr = addr;
setup->offset = offset;
return 1;
}
//native bool:DHookAddParam(Handle:setup, HookParamType:type, size=-1, DHookPassFlag:flag=DHookPass_ByVal, DHookRegister custom_register=DHookRegister_Default);
cell_t Native_AddParam(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup;
if(!GetHandleIfValidOrError(g_HookSetupHandle, (void **)&setup, pContext, params[1]))
{
return 0;
}
ParamInfo info;
info.type = (HookParamType)params[2];
if(params[0] >= 4)
{
info.flags = params[4];
}
else
{
info.flags = PASSFLAG_BYVAL;
}
if (params[0] >= 5)
{
PluginRegister custom_register = (PluginRegister)params[5];
info.custom_register = DynamicHooks_ConvertRegisterFrom(custom_register);
// Stay future proof.
if (info.custom_register == None && custom_register != DHookRegister_Default)
return pContext->ThrowNativeError("Unhandled DHookRegister %d", params[5]);
if (info.custom_register != None && info.type == HookParamType_Object)
return pContext->ThrowNativeError("Can't pass an object in a register.");
}
else
{
info.custom_register = None;
}
if(params[0] >= 3 && params[3] != -1)
{
info.size = params[3];
}
else if(info.type == HookParamType_Object)
{
return pContext->ThrowNativeError("Object param being set with no size");
}
else
{
info.size = GetParamTypeSize(info.type);
}
info.pass_type = GetParamTypePassType(info.type);
setup->params.push_back(info);
return 1;
}
// native bool:DHookEnableDetour(Handle:setup, bool:post, DHookCallback:callback);
cell_t Native_EnableDetour(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup;
if (!GetHandleIfValidOrError(g_HookSetupHandle, (void **)&setup, pContext, params[1]))
{
return 0;
}
if (setup->funcAddr == nullptr)
{
return pContext->ThrowNativeError("Hook not setup for a detour.");
}
IPluginFunction *callback = pContext->GetFunctionById(params[3]);
if (!callback)
{
return pContext->ThrowNativeError("Failed to retrieve function by id");
}
bool post = params[2] != 0;
HookType_t hookType = post ? HOOKTYPE_POST : HOOKTYPE_PRE;
// Check if we already detoured that function.
CHookManager *pDetourManager = GetHookManager();
CHook* pDetour = pDetourManager->FindHook(setup->funcAddr);
// If there is no detour on this function yet, create it.
if (!pDetour)
{
ICallingConvention *pCallConv = ConstructCallingConvention(setup);
pDetour = pDetourManager->HookFunction(setup->funcAddr, pCallConv);
if (!UpdateRegisterArgumentSizes(pDetour, setup))
return pContext->ThrowNativeError("A custom register for a parameter isn't supported.");
}
// Register our pre/post handler.
pDetour->AddCallback(hookType, (HookHandlerFn *)&HandleDetour);
// Add the plugin callback to the map.
return AddDetourPluginHook(hookType, pDetour, setup, callback);
}
// native bool:DHookDisableDetour(Handle:setup, bool:post, DHookCallback:callback);
cell_t Native_DisableDetour(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup;
if (!GetHandleIfValidOrError(g_HookSetupHandle, (void **)&setup, pContext, params[1]))
{
return 0;
}
if (setup->funcAddr == nullptr)
{
return pContext->ThrowNativeError("Hook not setup for a detour.");
}
IPluginFunction *callback = pContext->GetFunctionById(params[3]);
if (!callback)
{
return pContext->ThrowNativeError("Failed to retrieve function by id");
}
bool post = params[2] != 0;
HookType_t hookType = post ? HOOKTYPE_POST : HOOKTYPE_PRE;
// Check if we already detoured that function.
CHookManager *pDetourManager = GetHookManager();
CHook* pDetour = pDetourManager->FindHook(setup->funcAddr);
if (!pDetour || !pDetour->IsCallbackRegistered(hookType, (HookHandlerFn *)&HandleDetour))
{
return pContext->ThrowNativeError("Function not detoured.");
}
// Remove the callback from the hook.
return RemoveDetourPluginHook(hookType, pDetour, callback);
}
// native DHookEntity(Handle:setup, bool:post, entity, DHookRemovalCB:removalcb, DHookCallback:callback = INVALID_FUNCTION); // Both callbacks are optional
cell_t Native_HookEntity(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup;
if(!GetHandleIfValidOrError(g_HookSetupHandle, (void **)&setup, pContext, params[1]))
{
return 0;
}
if (setup->offset == -1)
{
return pContext->ThrowNativeError("Hook not setup for a virtual hook.");
}
if(setup->hookType != HookType_Entity)
{
return pContext->ThrowNativeError("Hook is not an entity hook");
}
bool post = params[2] != 0;
for(int i = g_pHooks.length() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->callback->hookType == HookType_Entity && manager->callback->entity == gamehelpers->ReferenceToBCompatRef(params[3]) && manager->callback->offset == setup->offset && manager->callback->post == post && manager->remove_callback == pContext->GetFunctionById(params[4]) && manager->callback->plugin_callback == setup->callback)
{
return manager->hookid;
}
}
CBaseEntity *pEnt = gamehelpers->ReferenceToEntity(params[3]);
if(!pEnt)
{
return pContext->ThrowNativeError("Invalid entity passed %i", params[2]);
}
IPluginFunction *cb = GetCallback(pContext, setup, params, 5);
if (!cb)
{
pContext->ThrowNativeError("Failed to hook entity %i, no callback provided", params[2]);
}
DHooksManager *manager = new DHooksManager(setup, pEnt, pContext->GetFunctionById(params[4]), cb, post);
if(!manager->hookid)
{
delete manager;
return 0;
}
g_pHooks.append(manager);
return manager->hookid;
}
// native DHookGamerules(Handle:setup, bool:post, DHookRemovalCB:removalcb, DHookCallback:callback = INVALID_FUNCTION); // Both callbacks are optional
cell_t Native_HookGamerules(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup;
if(!GetHandleIfValidOrError(g_HookSetupHandle, (void **)&setup, pContext, params[1]))
{
return 0;
}
if (setup->offset == -1)
{
return pContext->ThrowNativeError("Hook not setup for a virtual hook.");
}
if(setup->hookType != HookType_GameRules)
{
return pContext->ThrowNativeError("Hook is not a gamerules hook");
}
bool post = params[2] != 0;
for(int i = g_pHooks.length() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->callback->hookType == HookType_GameRules && manager->callback->offset == setup->offset && manager->callback->post == post && manager->remove_callback == pContext->GetFunctionById(params[3]) && manager->callback->plugin_callback == setup->callback)
{
return manager->hookid;
}
}
void *rules = g_pSDKTools->GetGameRules();
if(!rules)
{
return pContext->ThrowNativeError("Could not get gamerules pointer");
}
IPluginFunction *cb = GetCallback(pContext, setup, params, 4);
if (!cb)
{
pContext->ThrowNativeError("Failed to hook gamerules, no callback provided");
}
DHooksManager *manager = new DHooksManager(setup, rules, pContext->GetFunctionById(params[3]), cb, post);
if(!manager->hookid)
{
delete manager;
return 0;
}
g_pHooks.append(manager);
return manager->hookid;
}
// DHookRaw(Handle:setup, bool:post, Address:addr, DHookRemovalCB:removalcb, DHookCallback:callback = INVALID_FUNCTION); // Both callbacks are optional
cell_t Native_HookRaw(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup;
if(!GetHandleIfValidOrError(g_HookSetupHandle, (void **)&setup, pContext, params[1]))
{
return 0;
}
if (setup->offset == -1)
{
return pContext->ThrowNativeError("Hook not setup for a virtual hook.");
}
if(setup->hookType != HookType_Raw)
{
return pContext->ThrowNativeError("Hook is not a raw hook");
}
bool post = params[2] != 0;
void *iface = (void *)(params[3]);
for(int i = g_pHooks.length() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->callback->hookType == HookType_Raw && manager->addr == (intptr_t)iface && manager->callback->offset == setup->offset && manager->callback->post == post && manager->remove_callback == pContext->GetFunctionById(params[4]) && manager->callback->plugin_callback == setup->callback)
{
return manager->hookid;
}
}
if(!iface)
{
return pContext->ThrowNativeError("Invalid address passed");
}
IPluginFunction *cb = GetCallback(pContext, setup, params, 5);
if (!cb)
{
pContext->ThrowNativeError("Failed to hook address, no callback provided");
}
DHooksManager *manager = new DHooksManager(setup, iface, pContext->GetFunctionById(params[4]), cb, post);
if(!manager->hookid)
{
delete manager;
return 0;
}
g_pHooks.append(manager);
return manager->hookid;
}
// native bool:DHookRemoveHookID(hookid);
cell_t Native_RemoveHookID(IPluginContext *pContext, const cell_t *params)
{
for(int i = g_pHooks.length() -1; i >= 0; i--)
{
DHooksManager *manager = g_pHooks.at(i);
if(manager->hookid == params[1] && manager->callback->plugin_callback->GetParentRuntime()->GetDefaultContext() == pContext)
{
delete manager;
g_pHooks.remove(i);
return 1;
}
}
return 0;
}
// native any:DHookGetParam(Handle:hParams, num);
cell_t Native_GetParam(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] < 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
if(params[2] == 0)
{
return paramStruct->dg->params.size();
}
int index = params[2] - 1;
size_t offset = GetParamOffset(paramStruct, index);
void *addr = (void **)((intptr_t)paramStruct->orgParams + offset);
if(*(void **)addr == NULL && (paramStruct->dg->params.at(index).type == HookParamType_CBaseEntity || paramStruct->dg->params.at(index).type == HookParamType_Edict))
{
return pContext->ThrowNativeError("Trying to get value for null pointer.");
}
switch(paramStruct->dg->params.at(index).type)
{
case HookParamType_Int:
return *(int *)addr;
case HookParamType_Bool:
return *(cell_t *)addr != 0;
case HookParamType_CBaseEntity:
return gamehelpers->EntityToBCompatRef(*(CBaseEntity **)addr);
case HookParamType_Edict:
return gamehelpers->IndexOfEdict(*(edict_t **)addr);
case HookParamType_Float:
return sp_ftoc(*(float *)addr);
default:
return pContext->ThrowNativeError("Invalid param type (%i) to get", paramStruct->dg->params.at(index).type);
}
return 1;
}
// native DHookSetParam(Handle:hParams, param, any:value)
cell_t Native_SetParam(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
size_t offset = GetParamOffset(paramStruct, index);
void *addr = (void **)((intptr_t)paramStruct->newParams + offset);
switch(paramStruct->dg->params.at(index).type)
{
case HookParamType_Int:
*(int *)addr = params[3];
break;
case HookParamType_Bool:
*(bool *)addr = (params[3] ? true : false);
break;
case HookParamType_CBaseEntity:
{
CBaseEntity *pEnt = gamehelpers->ReferenceToEntity(params[2]);
if(!pEnt)
{
return pContext->ThrowNativeError("Invalid entity index passed for param value");
}
*(CBaseEntity **)addr = pEnt;
break;
}
case HookParamType_Edict:
{
edict_t *pEdict = gamehelpers->EdictOfIndex(params[2]);
if(!pEdict || pEdict->IsFree())
{
pContext->ThrowNativeError("Invalid entity index passed for param value");
}
*(edict_t **)addr = pEdict;
break;
}
case HookParamType_Float:
*(float *)addr = sp_ctof(params[3]);
break;
default:
return pContext->ThrowNativeError("Invalid param type (%i) to set", paramStruct->dg->params.at(index).type);
}
paramStruct->isChanged[index] = true;
return 1;
}
// native any:DHookGetReturn(Handle:hReturn);
cell_t Native_GetReturn(IPluginContext *pContext, const cell_t *params)
{
HookReturnStruct *returnStruct;
if(!GetHandleIfValidOrError(g_HookReturnHandle, (void **)&returnStruct, pContext, params[1]))
{
return 0;
}
switch(returnStruct->type)
{
case ReturnType_Int:
return *(int *)returnStruct->orgResult;
case ReturnType_Bool:
return *(bool *)returnStruct->orgResult? 1 : 0;
case ReturnType_CBaseEntity:
return gamehelpers->EntityToBCompatRef((CBaseEntity *)returnStruct->orgResult);
case ReturnType_Edict:
return gamehelpers->IndexOfEdict((edict_t *)returnStruct->orgResult);
case ReturnType_Float:
return sp_ftoc(*(float *)returnStruct->orgResult);
default:
return pContext->ThrowNativeError("Invalid param type (%i) to get", returnStruct->type);
}
return 1;
}
// native DHookSetReturn(Handle:hReturn, any:value)
cell_t Native_SetReturn(IPluginContext *pContext, const cell_t *params)
{
HookReturnStruct *returnStruct;
if(!GetHandleIfValidOrError(g_HookReturnHandle, (void **)&returnStruct, pContext, params[1]))
{
return 0;
}
switch(returnStruct->type)
{
case ReturnType_Int:
*(int *)returnStruct->newResult = params[2];
break;
case ReturnType_Bool:
*(bool *)returnStruct->newResult = params[2] != 0;
break;
case ReturnType_CBaseEntity:
{
CBaseEntity *pEnt = gamehelpers->ReferenceToEntity(params[2]);
if(!pEnt)
{
return pContext->ThrowNativeError("Invalid entity index passed for return value");
}
returnStruct->newResult = pEnt;
break;
}
case ReturnType_Edict:
{
edict_t *pEdict = gamehelpers->EdictOfIndex(params[2]);
if(!pEdict || pEdict->IsFree())
{
pContext->ThrowNativeError("Invalid entity index passed for return value");
}
returnStruct->newResult = pEdict;
break;
}
case ReturnType_Float:
*(float *)returnStruct->newResult = sp_ctof(params[2]);
break;
default:
return pContext->ThrowNativeError("Invalid param type (%i) to get",returnStruct->type);
}
returnStruct->isChanged = true;
return 1;
}
// native DHookGetParamVector(Handle:hParams, num, Float:vec[3])
cell_t Native_GetParamVector(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
size_t offset = GetParamOffset(paramStruct, index);
void *addr = (void **)((intptr_t)paramStruct->orgParams + offset);
if (*(void **)addr == NULL)
{
return pContext->ThrowNativeError("Trying to get value for null pointer.");
}
switch(paramStruct->dg->params.at(index).type)
{
case HookParamType_VectorPtr:
{
cell_t *buffer;
pContext->LocalToPhysAddr(params[3], &buffer);
SDKVector *vec = *(SDKVector **)addr;
buffer[0] = sp_ftoc(vec->x);
buffer[1] = sp_ftoc(vec->y);
buffer[2] = sp_ftoc(vec->z);
return 1;
}
}
return pContext->ThrowNativeError("Invalid param type to get. Param is not a vector.");
}
static void FreeChangedVector(void *pData)
{
delete (SDKVector *)pData;
}
// native DHookSetParamVector(Handle:hParams, num, Float:vec[3])
cell_t Native_SetParamVector(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
size_t offset = GetParamOffset(paramStruct, index);
void *addr = (void **)((intptr_t)paramStruct->newParams + offset);
switch(paramStruct->dg->params.at(index).type)
{
case HookParamType_VectorPtr:
{
cell_t *buffer;
pContext->LocalToPhysAddr(params[3], &buffer);
*(SDKVector **)addr = new SDKVector(sp_ctof(buffer[0]), sp_ctof(buffer[1]), sp_ctof(buffer[2]));
paramStruct->isChanged[index] = true;
// Free it later (cheaply) after the function returned.
smutils->AddFrameAction(FreeChangedVector, *(SDKVector **)addr);
return 1;
}
}
return pContext->ThrowNativeError("Invalid param type to set. Param is not a vector.");
}
// native DHookGetParamString(Handle:hParams, num, String:buffer[], size)
cell_t Native_GetParamString(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
size_t offset = GetParamOffset(paramStruct, index);
void *addr = (void **)((intptr_t)paramStruct->orgParams + offset);
if(*(void **)addr == NULL)
{
return pContext->ThrowNativeError("Trying to get value for null pointer.");
}
if(paramStruct->dg->params.at(index).type == HookParamType_CharPtr)
{
pContext->StringToLocal(params[3], params[4], *(const char **)addr);
}
return 1;
}
// native DHookGetReturnString(Handle:hReturn, String:buffer[], size)
cell_t Native_GetReturnString(IPluginContext *pContext, const cell_t *params)
{
HookReturnStruct *returnStruct;
if(!GetHandleIfValidOrError(g_HookReturnHandle, (void **)&returnStruct, pContext, params[1]))
{
return 0;
}
switch(returnStruct->type)
{
case ReturnType_String:
pContext->StringToLocal(params[2], params[3], (*(string_t *)returnStruct->orgResult == NULL_STRING) ? "" : STRING(*(string_t *)returnStruct->orgResult));
return 1;
case ReturnType_StringPtr:
pContext->StringToLocal(params[2], params[3], ((string_t *)returnStruct->orgResult == NULL) ? "" : ((string_t *)returnStruct->orgResult)->ToCStr());
return 1;
case ReturnType_CharPtr:
pContext->StringToLocal(params[2], params[3], ((char *)returnStruct->orgResult == NULL) ? "" : (const char *)returnStruct->orgResult);
return 1;
default:
return pContext->ThrowNativeError("Invalid param type to get. Param is not a string.");
}
}
static void FreeChangedCharPtr(void *pData)
{
delete[](char *)pData;
}
//native DHookSetReturnString(Handle:hReturn, String:value[])
cell_t Native_SetReturnString(IPluginContext *pContext, const cell_t *params)
{
HookReturnStruct *returnStruct;
if(!GetHandleIfValidOrError(g_HookReturnHandle, (void **)&returnStruct, pContext, params[1]))
{
return 0;
}
char *value;
pContext->LocalToString(params[2], &value);
switch(returnStruct->type)
{
case ReturnType_CharPtr:
{
returnStruct->newResult = new char[strlen(value) + 1];
strcpy((char *)returnStruct->newResult, value);
returnStruct->isChanged = true;
// Free it later (cheaply) after the function returned.
smutils->AddFrameAction(FreeChangedCharPtr, returnStruct->newResult);
return 1;
}
default:
return pContext->ThrowNativeError("Invalid param type to get. Param is not a char pointer.");
}
}
//native DHookSetParamString(Handle:hParams, num, String:value[])
cell_t Native_SetParamString(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
size_t offset = GetParamOffset(paramStruct, index);
void *addr = (void **)((intptr_t)paramStruct->newParams + offset);
char *value;
pContext->LocalToString(params[3], &value);
if(paramStruct->dg->params.at(index).type == HookParamType_CharPtr)
{
*(char **)addr = new char[strlen(value)+1];
strcpy(*(char **)addr, value);
paramStruct->isChanged[index] = true;
// Free it later (cheaply) after the function returned.
smutils->AddFrameAction(FreeChangedCharPtr, *(char **)addr);
}
return 1;
}
//native DHookAddEntityListener(ListenType:type, ListenCB:callback);
cell_t Native_AddEntityListener(IPluginContext *pContext, const cell_t *params)
{
if(g_pEntityListener)
{
return g_pEntityListener->AddPluginEntityListener((ListenType)params[1], pContext->GetFunctionById(params[2]));;
}
return pContext->ThrowNativeError("Failed to get g_pEntityListener");
}
//native bool:DHookRemoveEntityListener(ListenType:type, ListenCB:callback);
cell_t Native_RemoveEntityListener(IPluginContext *pContext, const cell_t *params)
{
if(g_pEntityListener)
{
return g_pEntityListener->RemovePluginEntityListener((ListenType)params[1], pContext->GetFunctionById(params[2]));;
}
return pContext->ThrowNativeError("Failed to get g_pEntityListener");
}
//native any:DHookGetParamObjectPtrVar(Handle:hParams, num, offset, ObjectValueType:type);
cell_t Native_GetParamObjectPtrVar(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
switch((ObjectValueType)params[4])
{
case ObjectValueType_Int:
{
return *(int *)((intptr_t)addr + params[3]);
}
case ObjectValueType_Bool:
return (*(bool *)((intptr_t)addr + params[3])) ? 1 : 0;
case ObjectValueType_Ehandle:
case ObjectValueType_EhandlePtr:
{
edict_t *pEdict = gamehelpers->GetHandleEntity(*(CBaseHandle *)((intptr_t)addr +params[3]));
if(!pEdict)
{
return -1;
}
return gamehelpers->IndexOfEdict(pEdict);
}
case ObjectValueType_Float:
{
return sp_ftoc(*(float *)((intptr_t)addr + params[3]));
}
case ObjectValueType_CBaseEntityPtr:
return gamehelpers->EntityToBCompatRef(*(CBaseEntity **)((intptr_t)addr + params[3]));
case ObjectValueType_IntPtr:
{
int *ptr = *(int **)((intptr_t)addr + params[3]);
return *ptr;
}
case ObjectValueType_BoolPtr:
{
bool *ptr = *(bool **)((intptr_t)addr + params[3]);
return *ptr ? 1 : 0;
}
case ObjectValueType_FloatPtr:
{
float *ptr = *(float **)((intptr_t)addr + params[3]);
return sp_ftoc(*ptr);
}
default:
return pContext->ThrowNativeError("Invalid Object value type");
}
}
//native DHookSetParamObjectPtrVar(Handle:hParams, num, offset, ObjectValueType:type, value)
cell_t Native_SetParamObjectPtrVar(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
switch((ObjectValueType)params[4])
{
case ObjectValueType_Int:
*(int *)((intptr_t)addr + params[3]) = params[5];
break;
case ObjectValueType_Bool:
*(bool *)((intptr_t)addr + params[3]) = params[5] != 0;
break;
case ObjectValueType_Ehandle:
case ObjectValueType_EhandlePtr:
{
edict_t *pEdict = gamehelpers->EdictOfIndex(params[5]);
if(!pEdict || pEdict->IsFree())
{
return pContext->ThrowNativeError("Invalid edict passed");
}
gamehelpers->SetHandleEntity(*(CBaseHandle *)((intptr_t)addr + params[3]), pEdict);
break;
}
case ObjectValueType_Float:
*(float *)((intptr_t)addr + params[3]) = sp_ctof(params[5]);
break;
case ObjectValueType_CBaseEntityPtr:
{
CBaseEntity *pEnt = gamehelpers->ReferenceToEntity(params[5]);
if(!pEnt)
{
return pContext->ThrowNativeError("Invalid entity passed");
}
*(CBaseEntity **)((intptr_t)addr + params[3]) = pEnt;
break;
}
case ObjectValueType_IntPtr:
{
int *ptr = *(int **)((intptr_t)addr + params[3]);
*ptr = params[5];
break;
}
case ObjectValueType_BoolPtr:
{
bool *ptr = *(bool **)((intptr_t)addr + params[3]);
*ptr = params[5] != 0;
break;
}
case ObjectValueType_FloatPtr:
{
float *ptr = *(float **)((intptr_t)addr + params[3]);
*ptr = sp_ctof(params[5]);
break;
}
default:
return pContext->ThrowNativeError("Invalid Object value type");
}
return 1;
}
//native DHookGetParamObjectPtrVarVector(Handle:hParams, num, offset, ObjectValueType:type, Float:buffer[3]);
cell_t Native_GetParamObjectPtrVarVector(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
cell_t *buffer;
pContext->LocalToPhysAddr(params[5], &buffer);
if((ObjectValueType)params[4] == ObjectValueType_VectorPtr || (ObjectValueType)params[4] == ObjectValueType_Vector)
{
SDKVector *vec;
if((ObjectValueType)params[4] == ObjectValueType_VectorPtr)
{
vec = *(SDKVector **)((intptr_t)addr + params[3]);
if(vec == NULL)
{
return pContext->ThrowNativeError("Trying to get value for null pointer.");
}
}
else
{
vec = (SDKVector *)((intptr_t)addr + params[3]);
}
buffer[0] = sp_ftoc(vec->x);
buffer[1] = sp_ftoc(vec->y);
buffer[2] = sp_ftoc(vec->z);
return 1;
}
return pContext->ThrowNativeError("Invalid Object value type (not a type of vector)");
}
//native DHookSetParamObjectPtrVarVector(Handle:hParams, num, offset, ObjectValueType:type, Float:value[3]);
cell_t Native_SetParamObjectPtrVarVector(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
cell_t *buffer;
pContext->LocalToPhysAddr(params[5], &buffer);
if((ObjectValueType)params[4] == ObjectValueType_VectorPtr || (ObjectValueType)params[4] == ObjectValueType_Vector)
{
SDKVector *vec;
if((ObjectValueType)params[4] == ObjectValueType_VectorPtr)
{
vec = *(SDKVector **)((intptr_t)addr + params[3]);
if(vec == NULL)
{
return pContext->ThrowNativeError("Trying to set value for null pointer.");
}
}
else
{
vec = (SDKVector *)((intptr_t)addr + params[3]);
}
vec->x = sp_ctof(buffer[0]);
vec->y = sp_ctof(buffer[1]);
vec->z = sp_ctof(buffer[2]);
return 1;
}
return pContext->ThrowNativeError("Invalid Object value type (not a type of vector)");
}
//native DHookGetParamObjectPtrString(Handle:hParams, num, offset, ObjectValueType:type, String:buffer[], size)
cell_t Native_GetParamObjectPtrString(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
switch((ObjectValueType)params[4])
{
case ObjectValueType_CharPtr:
{
char *ptr = *(char **)((intptr_t)addr + params[3]);
pContext->StringToLocal(params[5], params[6], ptr == NULL ? "" : (const char *)ptr);
break;
}
case ObjectValueType_String:
{
string_t string = *(string_t *)((intptr_t)addr + params[3]);
pContext->StringToLocal(params[5], params[6], string == NULL_STRING ? "" : STRING(string));
break;
}
default:
return pContext->ThrowNativeError("Invalid Object value type (not a type of string)");
}
return 1;
}
// DHookGetReturnVector(Handle:hReturn, Float:vec[3])
cell_t Native_GetReturnVector(IPluginContext *pContext, const cell_t *params)
{
HookReturnStruct *returnStruct;
if(!GetHandleIfValidOrError(g_HookReturnHandle, (void **)&returnStruct, pContext, params[1]))
{
return 0;
}
cell_t *buffer;
pContext->LocalToPhysAddr(params[2], &buffer);
if(returnStruct->type == ReturnType_Vector)
{
buffer[0] = sp_ftoc((*(SDKVector *)returnStruct->orgResult).x);
buffer[1] = sp_ftoc((*(SDKVector *)returnStruct->orgResult).y);
buffer[2] = sp_ftoc((*(SDKVector *)returnStruct->orgResult).z);
return 1;
}
else if(returnStruct->type == ReturnType_VectorPtr)
{
buffer[0] = sp_ftoc(((SDKVector *)returnStruct->orgResult)->x);
buffer[1] = sp_ftoc(((SDKVector *)returnStruct->orgResult)->y);
buffer[2] = sp_ftoc(((SDKVector *)returnStruct->orgResult)->z);
return 1;
}
return pContext->ThrowNativeError("Return type is not a vector type");
}
//DHookSetReturnVector(Handle:hReturn, Float:vec[3])
cell_t Native_SetReturnVector(IPluginContext *pContext, const cell_t *params)
{
HookReturnStruct *returnStruct;
if(!GetHandleIfValidOrError(g_HookReturnHandle, (void **)&returnStruct, pContext, params[1]))
{
return 0;
}
cell_t *buffer;
pContext->LocalToPhysAddr(params[2], &buffer);
if(returnStruct->type == ReturnType_Vector)
{
*(SDKVector *)returnStruct->newResult = SDKVector(sp_ctof(buffer[0]), sp_ctof(buffer[1]), sp_ctof(buffer[2]));
returnStruct->isChanged = true;
return 1;
}
else if(returnStruct->type == ReturnType_VectorPtr)
{
returnStruct->newResult = new SDKVector(sp_ctof(buffer[0]), sp_ctof(buffer[1]), sp_ctof(buffer[2]));
returnStruct->isChanged = true;
// Free it later (cheaply) after the function returned.
smutils->AddFrameAction(FreeChangedVector, returnStruct->newResult);
return 1;
}
return pContext->ThrowNativeError("Return type is not a vector type");
}
//native bool:DHookIsNullParam(Handle:hParams, num);
cell_t Native_IsNullParam(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetHandleIfValidOrError(g_HookParamsHandle, (void **)&paramStruct, pContext, params[1]))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
HookParamType type = paramStruct->dg->params.at(index).type;
//Check that the type is ptr
if(type == HookParamType_StringPtr || type == HookParamType_CharPtr || type == HookParamType_VectorPtr || type == HookParamType_CBaseEntity || type == HookParamType_ObjectPtr || type == HookParamType_Edict || type == HookParamType_Unknown)
return (paramStruct->orgParams[index] == NULL);
else
return pContext->ThrowNativeError("Param is not a pointer!");
}
sp_nativeinfo_t g_Natives[] =
{
{"DHookCreate", Native_CreateHook},
{"DHookCreateDetour", Native_CreateDetour},
{"DHookCreateFromConf", Native_DHookCreateFromConf},
{"DHookSetFromConf", Native_SetFromConf},
{"DHookAddParam", Native_AddParam},
{"DHookEnableDetour", Native_EnableDetour},
{"DHookDisableDetour", Native_DisableDetour},
{"DHookEntity", Native_HookEntity},
{"DHookGamerules", Native_HookGamerules},
{"DHookRaw", Native_HookRaw},
{"DHookRemoveHookID", Native_RemoveHookID},
{"DHookGetParam", Native_GetParam},
{"DHookGetReturn", Native_GetReturn},
{"DHookSetReturn", Native_SetReturn},
{"DHookSetParam", Native_SetParam},
{"DHookGetParamVector", Native_GetParamVector},
{"DHookGetReturnVector", Native_GetReturnVector},
{"DHookSetReturnVector", Native_SetReturnVector},
{"DHookSetParamVector", Native_SetParamVector},
{"DHookGetParamString", Native_GetParamString},
{"DHookGetReturnString", Native_GetReturnString},
{"DHookSetReturnString", Native_SetReturnString},
{"DHookSetParamString", Native_SetParamString},
{"DHookAddEntityListener", Native_AddEntityListener},
{"DHookRemoveEntityListener", Native_RemoveEntityListener},
{"DHookGetParamObjectPtrVar", Native_GetParamObjectPtrVar},
{"DHookSetParamObjectPtrVar", Native_SetParamObjectPtrVar},
{"DHookGetParamObjectPtrVarVector", Native_GetParamObjectPtrVarVector},
{"DHookSetParamObjectPtrVarVector", Native_SetParamObjectPtrVarVector},
{"DHookGetParamObjectPtrString", Native_GetParamObjectPtrString},
{"DHookIsNullParam", Native_IsNullParam},
{NULL, NULL}
};