Add gamedata parser

This commit is contained in:
Kenzzer 2025-09-28 23:37:44 +00:00
parent 600e07fafb
commit fff6f07ae3
No known key found for this signature in database
GPG Key ID: 64C3FD4332686DC1
14 changed files with 1145 additions and 233 deletions

View File

@ -6,7 +6,10 @@ sources = [
'src/globals.cpp',
'src/handle.cpp',
'src/register.cpp',
'src/natives/dhookparam.cpp'
'src/signatures.cpp',
'src/sp_inc.cpp',
'src/natives/dhookparam.cpp',
'src/natives/dhookreturn.cpp'
]
for cxx in builder.targets:

View File

@ -5,6 +5,7 @@
#include "globals.hpp"
#include "handle.hpp"
#include "hook.hpp"
#include "variable.hpp"
#if defined(DHOOKS_X86_64)
#include <khook/asm/x86_64.hpp>
@ -27,53 +28,6 @@ using AsmJit = KHook::Asm::x86_64_Jit;
using AsmJit = KHook::Asm::x86_Jit;
#endif
struct Variable {
public:
enum Alignment {
OneByte = 1,
TwoBytes = 2,
FourBytes = 4,
EightBytes = 8,
SixteenBytes = 16
};
sp::HookParamType dhook_type;
size_t dhook_size;
sp::DHookPassFlag dhook_pass_flags;
sp::DHookRegister dhook_custom_register;
// Provided by the ABI, used by the JIT
// If DHook is to ever support complex type, transform those fields
// into an array so that the SP natives know where to look when offsetting into an object
std::optional<AsmRegCode> reg_index;
std::optional<AsmFloatRegCode> float_reg_index;
std::optional<size_t> reg_offset;
Alignment alignment;
// Atm it's exactly the same as _dhook_size
// We differentiate it anyways, to avoid having to refactor the JIT
size_t type_size;
};
struct ReturnVariable {
public:
enum Alignment {
OneByte = 1,
TwoBytes = 2,
FourBytes = 4,
EightBytes = 8,
SixteenBytes = 16
};
sp::ReturnType dhook_type;
size_t dhook_size;
sp::DHookPassFlag dhook_pass_flags;
sp::DHookRegister dhook_custom_register;
std::optional<AsmRegCode> reg_index;
std::optional<AsmFloatRegCode> float_reg_index;
size_t reg_offset;
};
struct HookCallback;
class Capsule {
public:

View File

@ -9,6 +9,10 @@
#include <thread>
#include <vector>
namespace dhooks {
class SignatureGameConfig;
};
namespace dhooks::globals {
extern std::thread::id main_thread;
@ -17,6 +21,7 @@ extern SourceMod::IGameHelpers* gamehelpers;
extern SourceMod::IExtension* myself;
extern SourceMod::ISourceMod* sourcemod;
extern std::vector<sp_nativeinfo_t> natives;
extern dhooks::SignatureGameConfig* dhooks_config;
void init();

View File

@ -7,6 +7,8 @@
namespace dhooks::handle {
SourceMod::HandleType_t ParamReturn::HANDLE_TYPE = 0;
SourceMod::HandleType_t HookSetup::VIRTUAL_HANDLE_TYPE = 0;
SourceMod::HandleType_t HookSetup::ADDRESS_HANDLE_TYPE = 0;
ParamReturn::ParamReturn(const Capsule* capsule, GeneralRegister* generalregs, FloatRegister* floatregs, void* return_ptr) :
_handle(globals::handlesys->CreateHandle(handle::ParamReturn::HANDLE_TYPE, this, globals::myself->GetIdentity(), globals::myself->GetIdentity(), nullptr)),
@ -64,6 +66,8 @@ void init() {
// Do not allow cloning, the struct self-manage its handle
security.access[SourceMod::HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
ParamReturn::HANDLE_TYPE = globals::handlesys->CreateType("DHookParamReturn", nullptr, 0, nullptr, &security, globals::myself->GetIdentity(), nullptr);
HookSetup::VIRTUAL_HANDLE_TYPE = globals::handlesys->CreateType("DynamicHook", nullptr, 0, nullptr, &security, globals::myself->GetIdentity(), nullptr);
HookSetup::ADDRESS_HANDLE_TYPE = globals::handlesys->CreateType("DynamicDetour", nullptr, 0, nullptr, &security, globals::myself->GetIdentity(), nullptr);
}
}

View File

@ -1,10 +1,14 @@
#pragma once
#include "register.hpp"
#include "variable.hpp"
#include <sp_vm_types.h>
#include <IHandleSys.h>
#include <optional>
#include <vector>
namespace dhooks {
class Capsule;
}
@ -42,4 +46,23 @@ private:
void* _return;
};
class HookSetup {
public:
HookSetup(sp::ThisPointerType, sp::CallingConvention, void* address, const std::vector<Variable>& params, const ReturnVariable& ret);
HookSetup(sp::ThisPointerType, std::uint32_t offset, const std::vector<Variable>& params, const ReturnVariable& ret);
~HookSetup();
static SourceMod::HandleType_t VIRTUAL_HANDLE_TYPE;
static SourceMod::HandleType_t ADDRESS_HANDLE_TYPE;
private:
cell_t _handle;
std::optional<std::uint32_t> _virtual_offset;
std::optional<void*> _address;
sp::ThisPointerType _this_pointer;
sp::CallingConvention _dhook_call_conv;
std::vector<Variable> _dhook_params;
ReturnVariable _dhook_return;
};
};

View File

@ -5,6 +5,14 @@
namespace dhooks::natives::dhookparam {
static void FreeChangedVector(void* data) {
delete (sdk::Vector*)data;
}
static void FreeChangedCharPtr(void* data) {
delete[] (const char*)data;
}
inline handle::ParamReturn* Get(SourcePawn::IPluginContext* context, const cell_t param) {
SourceMod::HandleSecurity security;
security.pOwner = globals::myself->GetIdentity();
@ -33,175 +41,6 @@ inline cell_t GetParam(SourcePawn::IPluginContext* context, handle::ParamReturn*
return index;
}
cell_t DHookReturn_GetReturn(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
switch (ret.dhook_type) {
case sp::ReturnType_Int:
return *obj->GetReturn<int>();
case sp::ReturnType_Bool:
return (*obj->GetReturn<bool>()) ? 1 : 0;
case sp::ReturnType_CBaseEntity:
return globals::gamehelpers->EntityToBCompatRef(*obj->GetReturn<CBaseEntity*>());
case sp::ReturnType_Edict:
return globals::gamehelpers->IndexOfEdict(*obj->GetReturn<edict_t*>());
case sp::ReturnType_Float:
return sp_ftoc(*obj->GetReturn<float>());
default:
return context->ThrowNativeError("Invalid param type (%i) to get", ret.dhook_type);
}
return 0;
}
cell_t DHookReturn_SetReturn(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
switch (ret.dhook_type) {
case sp::ReturnType_Int:
*obj->GetReturn<int>() = params[2];
break;
case sp::ReturnType_Bool:
*obj->GetReturn<bool>() = params[2] != 0;
break;
case sp::ReturnType_CBaseEntity: {
CBaseEntity* entity = globals::gamehelpers->ReferenceToEntity(params[2]);
if (entity == nullptr) {
return context->ThrowNativeError("Invalid entity index passed for return value");
}
*obj->GetReturn<CBaseEntity*>() = entity;
break;
}
case sp::ReturnType_Edict: {
sdk::edict_t* edict = reinterpret_cast<sdk::edict_t*>(globals::gamehelpers->EdictOfIndex(params[2]));
if (edict == nullptr || edict->IsFree()) {
return context->ThrowNativeError("Invalid entity index passed for return value");
}
*obj->GetReturn<sdk::edict_t*>() = edict;
break;
}
case sp::ReturnType_Float:
*obj->GetReturn<float>() = sp_ctof(params[2]);
break;
default:
return context->ThrowNativeError("Invalid param type (%i) to get", ret.dhook_type);
}
return 0;
}
cell_t DHookReturn_GetReturnVector(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
cell_t *buffer;
context->LocalToPhysAddr(params[2], &buffer);
const auto& ret = obj->GetCapsule()->GetReturn();
sdk::Vector* vector = nullptr;
if (ret.dhook_type == sp::ReturnType_Vector) {
vector = obj->GetReturn<sdk::Vector>();
} else if(ret.dhook_type == sp::ReturnType_VectorPtr) {
auto pvector = obj->GetReturn<sdk::Vector*>();
if (*pvector == nullptr) {
return context->ThrowNativeError("Vector pointer is null");
}
vector = *pvector;
} else {
return context->ThrowNativeError("Return type is not a vector type");
}
buffer[0] = sp_ftoc(vector->x);
buffer[1] = sp_ftoc(vector->y);
buffer[2] = sp_ftoc(vector->z);
return 0;
}
static void FreeChangedVector(void* data) {
delete (sdk::Vector*)data;
}
cell_t DHookReturn_SetReturnVector(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
cell_t *buffer;
context->LocalToPhysAddr(params[2], &buffer);
const auto& ret = obj->GetCapsule()->GetReturn();
if (ret.dhook_type == sp::ReturnType_Vector) {
*(obj->GetReturn<sdk::Vector>()) = sdk::Vector(sp_ctof(buffer[0]), sp_ctof(buffer[1]), sp_ctof(buffer[2]));
}
else if (ret.dhook_type == sp::ReturnType_VectorPtr) {
// Lazily free the vector a frame later
auto vector = new sdk::Vector(sp_ctof(buffer[0]), sp_ctof(buffer[1]), sp_ctof(buffer[2]));
*(obj->GetReturn<sdk::Vector*>()) = vector;
globals::sourcemod->AddFrameAction(FreeChangedVector, vector);
} else {
return context->ThrowNativeError("Return type is not a vector type");
}
return 0;
}
cell_t DHookReturn_GetReturnString(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
switch (ret.dhook_type) {
case sp::ReturnType_String:
context->StringToLocal(params[2], params[3], obj->GetReturn<sdk::string_t>()->ToCStr());
return 1;
case sp::ReturnType_StringPtr:
context->StringToLocal(params[2], params[3], (obj->GetReturn<sdk::string_t*>() != nullptr) ? (*obj->GetReturn<sdk::string_t*>())->ToCStr() : "");
return 1;
case sp::ReturnType_CharPtr:
context->StringToLocal(params[2], params[3], (*(obj->GetReturn<const char*>()) == nullptr) ? "" : *(obj->GetReturn<const char*>()));
return 1;
default:
return context->ThrowNativeError("Invalid param type to get. Param is not a string.");
}
}
static void FreeChangedCharPtr(void* data) {
delete[] (const char*)data;
}
cell_t DHookReturn_SetReturnString(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
char *value;
context->LocalToString(params[2], &value);
switch (ret.dhook_type) {
case sp::ReturnType_CharPtr: {
auto new_str = new char[strlen(value) + 1];
strcpy(new_str, value);
*(obj->GetReturn<const char*>()) = new_str;
// Free it later (cheaply) after the function returned.
globals::sourcemod->AddFrameAction(FreeChangedCharPtr, new_str);
return 1;
}
default:
return context->ThrowNativeError("Invalid param type to get. Param is not a char pointer.");
}
}
cell_t DHookParam_GetParam(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
@ -703,19 +542,7 @@ void init(std::vector<sp_nativeinfo_t>& natives) {
{"DHookParam.GetObjectVarVector", DHookParam_GetParamObjectPtrVarVector},
{"DHookParam.SetObjectVarVector", DHookParam_SetParamObjectPtrVarVector},
{"DHookParam.GetObjectVarString", DHookParam_GetParamObjectPtrString},
{"DHookParam.IsNull", DHookParam_IsNullParam},
{"DHookGetReturn", DHookReturn_GetReturn},
{"DHookSetReturn", DHookReturn_SetReturn},
{"DHookGetReturnVector", DHookReturn_GetReturnVector},
{"DHookSetReturnVector", DHookReturn_SetReturnVector},
{"DHookGetReturnString", DHookReturn_GetReturnString},
{"DHookSetReturnString", DHookReturn_SetReturnString},
{"DHookReturn.Value.get", DHookReturn_GetReturn},
{"DHookReturn.Value.set", DHookReturn_SetReturn},
{"DHookReturn.GetVector", DHookReturn_GetReturnVector},
{"DHookReturn.SetVector", DHookReturn_SetReturnVector},
{"DHookReturn.GetString", DHookReturn_GetReturnString},
{"DHookReturn.SetString", DHookReturn_SetReturnString}
{"DHookParam.IsNull", DHookParam_IsNullParam}
};
natives.insert(natives.end(), std::begin(list), std::end(list));
}

View File

@ -0,0 +1,211 @@
#include "../globals.hpp"
#include "../sdk_types.hpp"
#include "../handle.hpp"
#include "../capsule.hpp"
namespace dhooks::natives::dhookreturn {
static void FreeChangedVector(void* data) {
delete (sdk::Vector*)data;
}
static void FreeChangedCharPtr(void* data) {
delete[] (const char*)data;
}
inline handle::ParamReturn* Get(SourcePawn::IPluginContext* context, const cell_t param) {
SourceMod::HandleSecurity security;
security.pOwner = globals::myself->GetIdentity();
security.pIdentity = globals::myself->GetIdentity();
SourceMod::Handle_t hndl = static_cast<SourceMod::Handle_t>(param);
handle::ParamReturn* obj = nullptr;
SourceMod::HandleError chnderr = globals::handlesys->ReadHandle(hndl, handle::ParamReturn::HANDLE_TYPE, &security, (void **)&obj);
if (chnderr != SourceMod::HandleError_None) {
context->ThrowNativeError("Invalid Handle %x (error %i: %s)", hndl, chnderr, globals::HandleErrorToString(chnderr));
return nullptr;
}
return obj;
}
cell_t DHookReturn_GetReturn(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
switch (ret.dhook_type) {
case sp::ReturnType_Int:
return *obj->GetReturn<int>();
case sp::ReturnType_Bool:
return (*obj->GetReturn<bool>()) ? 1 : 0;
case sp::ReturnType_CBaseEntity:
return globals::gamehelpers->EntityToBCompatRef(*obj->GetReturn<CBaseEntity*>());
case sp::ReturnType_Edict:
return globals::gamehelpers->IndexOfEdict(*obj->GetReturn<edict_t*>());
case sp::ReturnType_Float:
return sp_ftoc(*obj->GetReturn<float>());
default:
return context->ThrowNativeError("Invalid param type (%i) to get", ret.dhook_type);
}
return 0;
}
cell_t DHookReturn_SetReturn(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
switch (ret.dhook_type) {
case sp::ReturnType_Int:
*obj->GetReturn<int>() = params[2];
break;
case sp::ReturnType_Bool:
*obj->GetReturn<bool>() = params[2] != 0;
break;
case sp::ReturnType_CBaseEntity: {
CBaseEntity* entity = globals::gamehelpers->ReferenceToEntity(params[2]);
if (entity == nullptr) {
return context->ThrowNativeError("Invalid entity index passed for return value");
}
*obj->GetReturn<CBaseEntity*>() = entity;
break;
}
case sp::ReturnType_Edict: {
sdk::edict_t* edict = reinterpret_cast<sdk::edict_t*>(globals::gamehelpers->EdictOfIndex(params[2]));
if (edict == nullptr || edict->IsFree()) {
return context->ThrowNativeError("Invalid entity index passed for return value");
}
*obj->GetReturn<sdk::edict_t*>() = edict;
break;
}
case sp::ReturnType_Float:
*obj->GetReturn<float>() = sp_ctof(params[2]);
break;
default:
return context->ThrowNativeError("Invalid param type (%i) to get", ret.dhook_type);
}
return 0;
}
cell_t DHookReturn_GetReturnVector(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
cell_t *buffer;
context->LocalToPhysAddr(params[2], &buffer);
const auto& ret = obj->GetCapsule()->GetReturn();
sdk::Vector* vector = nullptr;
if (ret.dhook_type == sp::ReturnType_Vector) {
vector = obj->GetReturn<sdk::Vector>();
} else if(ret.dhook_type == sp::ReturnType_VectorPtr) {
auto pvector = obj->GetReturn<sdk::Vector*>();
if (*pvector == nullptr) {
return context->ThrowNativeError("Vector pointer is null");
}
vector = *pvector;
} else {
return context->ThrowNativeError("Return type is not a vector type");
}
buffer[0] = sp_ftoc(vector->x);
buffer[1] = sp_ftoc(vector->y);
buffer[2] = sp_ftoc(vector->z);
return 0;
}
cell_t DHookReturn_SetReturnVector(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
cell_t *buffer;
context->LocalToPhysAddr(params[2], &buffer);
const auto& ret = obj->GetCapsule()->GetReturn();
if (ret.dhook_type == sp::ReturnType_Vector) {
*(obj->GetReturn<sdk::Vector>()) = sdk::Vector(sp_ctof(buffer[0]), sp_ctof(buffer[1]), sp_ctof(buffer[2]));
}
else if (ret.dhook_type == sp::ReturnType_VectorPtr) {
// Lazily free the vector a frame later
auto vector = new sdk::Vector(sp_ctof(buffer[0]), sp_ctof(buffer[1]), sp_ctof(buffer[2]));
*(obj->GetReturn<sdk::Vector*>()) = vector;
globals::sourcemod->AddFrameAction(FreeChangedVector, vector);
} else {
return context->ThrowNativeError("Return type is not a vector type");
}
return 0;
}
cell_t DHookReturn_GetReturnString(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
switch (ret.dhook_type) {
case sp::ReturnType_String:
context->StringToLocal(params[2], params[3], obj->GetReturn<sdk::string_t>()->ToCStr());
return 1;
case sp::ReturnType_StringPtr:
context->StringToLocal(params[2], params[3], (obj->GetReturn<sdk::string_t*>() != nullptr) ? (*obj->GetReturn<sdk::string_t*>())->ToCStr() : "");
return 1;
case sp::ReturnType_CharPtr:
context->StringToLocal(params[2], params[3], (*(obj->GetReturn<const char*>()) == nullptr) ? "" : *(obj->GetReturn<const char*>()));
return 1;
default:
return context->ThrowNativeError("Invalid param type to get. Param is not a string.");
}
}
cell_t DHookReturn_SetReturnString(SourcePawn::IPluginContext* context, const cell_t* params) {
auto obj = Get(context, params[1]);
if (obj == nullptr) {
return 0;
}
const auto& ret = obj->GetCapsule()->GetReturn();
char *value;
context->LocalToString(params[2], &value);
switch (ret.dhook_type) {
case sp::ReturnType_CharPtr: {
auto new_str = new char[strlen(value) + 1];
strcpy(new_str, value);
*(obj->GetReturn<const char*>()) = new_str;
// Free it later (cheaply) after the function returned.
globals::sourcemod->AddFrameAction(FreeChangedCharPtr, new_str);
return 1;
}
default:
return context->ThrowNativeError("Invalid param type to get. Param is not a char pointer.");
}
}
void init(std::vector<sp_nativeinfo_t>& natives) {
sp_nativeinfo_t list[] = {
{"DHookGetReturn", DHookReturn_GetReturn},
{"DHookSetReturn", DHookReturn_SetReturn},
{"DHookGetReturnVector", DHookReturn_GetReturnVector},
{"DHookSetReturnVector", DHookReturn_SetReturnVector},
{"DHookGetReturnString", DHookReturn_GetReturnString},
{"DHookSetReturnString", DHookReturn_SetReturnString},
{"DHookReturn.Value.get", DHookReturn_GetReturn},
{"DHookReturn.Value.set", DHookReturn_SetReturn},
{"DHookReturn.GetVector", DHookReturn_GetReturnVector},
{"DHookReturn.SetVector", DHookReturn_SetReturnVector},
{"DHookReturn.GetString", DHookReturn_GetReturnString},
{"DHookReturn.SetString", DHookReturn_SetReturnString}
};
natives.insert(natives.end(), std::begin(list), std::end(list));
}
}

View File

@ -8,6 +8,7 @@ std::optional<AsmRegCode> Translate_DHookRegister(sp::DHookRegister reg) {
case sp::DHookRegister_AH:
case sp::DHookRegister_EAX:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RAX:
return KHook::Asm::rax;
#else
return KHook::Asm::eax;
@ -16,6 +17,7 @@ std::optional<AsmRegCode> Translate_DHookRegister(sp::DHookRegister reg) {
case sp::DHookRegister_CH:
case sp::DHookRegister_ECX:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RCX:
return KHook::Asm::rcx;
#else
return KHook::Asm::ecx;
@ -24,6 +26,7 @@ std::optional<AsmRegCode> Translate_DHookRegister(sp::DHookRegister reg) {
case sp::DHookRegister_DH:
case sp::DHookRegister_EDX:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RDX:
return KHook::Asm::rdx;
#else
return KHook::Asm::edx;
@ -32,30 +35,35 @@ std::optional<AsmRegCode> Translate_DHookRegister(sp::DHookRegister reg) {
case sp::DHookRegister_BH:
case sp::DHookRegister_EBX:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RBX:
return KHook::Asm::rbx;
#else
return KHook::Asm::ebx;
#endif
case sp::DHookRegister_ESP:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RSP:
return KHook::Asm::rsp;
#else
return KHook::Asm::esp;
#endif
case sp::DHookRegister_EBP:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RBP:
return KHook::Asm::rbp;
#else
return KHook::Asm::ebp;
#endif
case sp::DHookRegister_ESI:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RSI:
return KHook::Asm::rsi;
#else
return KHook::Asm::esi;
#endif
case sp::DHookRegister_EDI:
#ifdef DHOOKS_X86_64
case sp::DHookRegister_RDI:
return KHook::Asm::rdi;
#else
return KHook::Asm::edi;

View File

@ -6,7 +6,7 @@ namespace sdk {
class CBaseEntity;
static constexpr size_t FL_EDICT_FREE = (1<<1);
static constexpr std::size_t FL_EDICT_FREE = (1<<1);
struct edict_t {
public:
bool IsFree() { return (m_fStateFlags & FL_EDICT_FREE) != 0; }

View File

@ -0,0 +1,657 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "signatures.hpp"
#include "globals.hpp"
namespace dhooks {
SignatureGameConfig* dhooks_config = nullptr;
SignatureWrapper* g_CurrentSignature;
enum class ParseState
{
None,
Root,
Function,
Arguments,
Argument
};
ParseState g_ParseState;
unsigned int g_IgnoreLevel;
// The parent section type of a platform specific "windows" or "linux" section.
ParseState g_PlatformOnlyState;
std::string g_CurrentFunctionName;
ArgumentInfo g_CurrentArgumentInfo;
SignatureWrapper* SignatureGameConfig::GetFunctionSignature(const char* function)
{
auto sig = signatures_.find(function);
if (sig == signatures_.end())
return nullptr;
return (*sig).second;
}
/**
* Game config "Functions" section parsing.
*/
SourceMod::SMCResult SignatureGameConfig::ReadSMC_NewSection(const SourceMod::SMCStates *states, const char *name)
{
// We're ignoring the parent section. Ignore all child sections as well.
if (g_IgnoreLevel > 0)
{
g_IgnoreLevel++;
return SourceMod::SMCResult_Continue;
}
// Handle platform specific sections first.
#ifdef DHOOKS_X86_64
#if defined WIN32
if (!strcmp(name, "windows64"))
#elif defined _LINUX
if (!strcmp(name, "linux64"))
#elif defined _OSX
if (!strcmp(name, "mac64"))
#endif
#else
#if defined WIN32
if (!strcmp(name, "windows"))
#elif defined _LINUX
if (!strcmp(name, "linux"))
#elif defined _OSX
if (!strcmp(name, "mac"))
#endif
#endif
{
// We're already in a section for a different OS that we're ignoring. Can't have a section for our OS in here.
if (g_IgnoreLevel > 0)
{
globals::sourcemod->LogError(globals::myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
// We don't support nested (useless) sections of the same OS like "windows" { "windows" { "foo" "bar" } }
if (g_PlatformOnlyState != ParseState::None)
{
globals::sourcemod->LogError(globals::myself, "Duplicate platform specific section for \"%s\". Already parsing only for that OS: line: %i col: %i", name, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
// This is a specific block for us.
g_PlatformOnlyState = g_ParseState;
return SourceMod::SMCResult_Continue;
}
else if (!strcmp(name, "windows") || !strcmp(name, "linux") || !strcmp(name, "mac")
|| !strcmp(name, "windows64") || !strcmp(name, "linux64") || !strcmp(name, "mac64"))
{
if (g_PlatformOnlyState != ParseState::None)
{
globals::sourcemod->LogError(globals::myself, "Unreachable platform specific section in \"%s\" Function: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
// A specific block for a different platform.
g_IgnoreLevel++;
return SourceMod::SMCResult_Continue;
}
switch (g_ParseState) {
case ParseState::Root: {
auto sig = signatures_.find(name);
if (sig != signatures_.end())
g_CurrentSignature = (*sig).second;
else
g_CurrentSignature = new SignatureWrapper();
g_CurrentFunctionName = name;
g_ParseState = ParseState::Function;
break;
}
case ParseState::Function: {
if (!strcmp(name, "arguments"))
{
g_ParseState = ParseState::Arguments;
}
else
{
globals::sourcemod->LogError(globals::myself, "Unknown subsection \"%s\" (expected \"arguments\"): line: %i col: %i", name, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
break;
}
case ParseState::Arguments: {
g_ParseState = ParseState::Argument;
g_CurrentArgumentInfo.name = name;
// Reset the parameter info.
ParamInfo info;
info.type = sp::HookParamType_Unknown;
info.size = 0;
info.custom_register = sp::DHookRegister_Default;
info.flags = sp::DHookPass_ByVal;
g_CurrentArgumentInfo.info = info;
// See if we already have info about this argument.
for (auto &arg : g_CurrentSignature->args) {
if (!arg.name.compare(name)) {
// Continue changing that argument now.
g_CurrentArgumentInfo.info = arg.info;
break;
}
}
break;
}
default: {
globals::sourcemod->LogError(globals::myself, "Unknown subsection \"%s\": line: %i col: %i", name, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
return SourceMod::SMCResult_Continue;
}
SourceMod::SMCResult SignatureGameConfig::ReadSMC_KeyValue(const SourceMod::SMCStates* states, const char* key, const char* value)
{
// We don't care for anything in this section or subsections.
if (g_IgnoreLevel > 0)
return SourceMod::SMCResult_Continue;
switch (g_ParseState)
{
case ParseState::Function: {
if (!strcmp(key, "signature"))
{
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->offset.length() > 0)
{
globals::sourcemod->LogError(globals::myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
g_CurrentSignature->signature = value;
}
else if (!strcmp(key, "address"))
{
if (g_CurrentSignature->signature.length() > 0 || g_CurrentSignature->offset.length() > 0)
{
globals::sourcemod->LogError(globals::myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
g_CurrentSignature->address = value;
}
else if (!strcmp(key, "offset"))
{
if (g_CurrentSignature->address.length() > 0 || g_CurrentSignature->signature.length() > 0)
{
globals::sourcemod->LogError(globals::myself, "Cannot have \"signature\", \"address\" or \"offset\" keys at the same time in one function: line: %i col: %i", states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
g_CurrentSignature->offset = value;
}
else if (!strcmp(key, "callconv"))
{
sp::CallingConvention callConv;
if (!strcmp(value, "cdecl"))
callConv = sp::CallConv_CDECL;
else if (!strcmp(value, "thiscall"))
callConv = sp::CallConv_THISCALL;
else if (!strcmp(value, "stdcall"))
callConv = sp::CallConv_STDCALL;
else if (!strcmp(value, "fastcall"))
callConv = sp::CallConv_FASTCALL;
else
{
globals::sourcemod->LogError(globals::myself, "Invalid calling convention \"%s\": line: %i col: %i", value, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
g_CurrentSignature->callConv = callConv;
}
else if (!strcmp(key, "hooktype"))
{
sp::HookType hookType;
if (!strcmp(value, "entity"))
hookType = sp::HookType_Entity;
else if (!strcmp(value, "gamerules"))
hookType = sp::HookType_GameRules;
else if (!strcmp(value, "raw"))
hookType = sp::HookType_Raw;
else
{
globals::sourcemod->LogError(globals::myself, "Invalid hook type \"%s\": line: %i col: %i", value, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
g_CurrentSignature->hookType = hookType;
}
else if (!strcmp(key, "return"))
{
g_CurrentSignature->retType = GetReturnTypeFromString(value);
if (g_CurrentSignature->retType == sp::ReturnType_Unknown)
{
globals::sourcemod->LogError(globals::myself, "Invalid return type \"%s\": line: %i col: %i", value, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
else if (!strcmp(key, "this"))
{
if (!strcmp(value, "ignore"))
g_CurrentSignature->thisType = sp::ThisPointer_Ignore;
else if (!strcmp(value, "entity"))
g_CurrentSignature->thisType = sp::ThisPointer_CBaseEntity;
else if (!strcmp(value, "address"))
g_CurrentSignature->thisType = sp::ThisPointer_Address;
else
{
globals::sourcemod->LogError(globals::myself, "Invalid this type \"%s\": line: %i col: %i", value, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
else
{
globals::sourcemod->LogError(globals::myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
break;
}
case ParseState::Argument: {
if (!strcmp(key, "type"))
{
g_CurrentArgumentInfo.info.type = GetHookParamTypeFromString(value);
if (g_CurrentArgumentInfo.info.type == sp::HookParamType_Unknown)
{
globals::sourcemod->LogError(globals::myself, "Invalid argument type \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
else if (!strcmp(key, "size"))
{
g_CurrentArgumentInfo.info.size = static_cast<int>(strtol(value, NULL, 0));
if (g_CurrentArgumentInfo.info.size < 1)
{
globals::sourcemod->LogError(globals::myself, "Invalid argument size \"%s\" for argument \"%s\": line: %i col: %i", value, g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
else if (!strcmp(key, "flags"))
{
sp::DHookPassFlag flags = static_cast<sp::DHookPassFlag>(0);
if (strstr(value, "byval"))
flags = static_cast<sp::DHookPassFlag>(static_cast<int>(flags)|sp::DHookPass_ByVal);
if (strstr(value, "byref"))
flags = static_cast<sp::DHookPassFlag>(static_cast<int>(flags)|sp::DHookPass_ByRef);
/*
if (strstr(value, "odtor"))
flags |= PASSFLAG_ODTOR;
if (strstr(value, "octor"))
flags |= PASSFLAG_OCTOR;
if (strstr(value, "oassignop"))
flags |= PASSFLAG_OASSIGNOP;
#ifdef PASSFLAG_OCOPYCTOR
if (strstr(value, "ocopyctor"))
flags |= PASSFLAG_OCOPYCTOR;
#endif
#ifdef PASSFLAG_OUNALIGN
if (strstr(value, "ounalign"))
flags |= PASSFLAG_OUNALIGN;
#endif
*/
g_CurrentArgumentInfo.info.flags = flags;
}
else if (!strcmp(key, "register"))
{
g_CurrentArgumentInfo.info.custom_register = GetCustomRegisterFromString(value);
if (g_CurrentArgumentInfo.info.custom_register == sp::DHookRegister_Default)
{
globals::sourcemod->LogError(globals::myself, "Invalid register \"%s\": line: %i col: %i", value, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
else
{
globals::sourcemod->LogError(globals::myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
break;
}
default: {
globals::sourcemod->LogError(globals::myself, "Unknown key in Functions section \"%s\": line: %i col: %i", key, states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
return SourceMod::SMCResult_Continue;
}
SourceMod::SMCResult SignatureGameConfig::ReadSMC_LeavingSection(const SourceMod::SMCStates* states)
{
// We were ignoring this section.
if (g_IgnoreLevel > 0)
{
g_IgnoreLevel--;
return SourceMod::SMCResult_Continue;
}
// We were in a section only for our OS.
if (g_PlatformOnlyState == g_ParseState)
{
g_PlatformOnlyState = ParseState::None;
return SourceMod::SMCResult_Continue;
}
switch (g_ParseState)
{
case ParseState::Function:
g_ParseState = ParseState::Root;
if (!g_CurrentSignature->address.length() && !g_CurrentSignature->signature.length() && !g_CurrentSignature->offset.length())
{
globals::sourcemod->LogError(globals::myself, "Function \"%s\" doesn't have a \"signature\", \"offset\" nor \"address\" set: line: %i col: %i", g_CurrentFunctionName.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
if (!g_CurrentSignature->offset.length())
{
// DynamicDetours doesn't expose the passflags concept like SourceHook.
// See if we're trying to set some invalid flags on detour arguments.
for (auto &arg : g_CurrentSignature->args)
{
if ((arg.info.flags & ~sp::DHookPass_ByVal) > 0)
{
globals::sourcemod->LogError(globals::myself, "Function \"%s\" uses unsupported pass flags in argument \"%s\". Flags are only supported for virtual hooks: line: %i col: %i", g_CurrentFunctionName.c_str(), arg.name.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
}
// Save this function signature in our cache.
signatures_[g_CurrentFunctionName] = g_CurrentSignature;
g_CurrentFunctionName = "";
g_CurrentSignature = nullptr;
break;
case ParseState::Arguments:
g_ParseState = ParseState::Function;
break;
case ParseState::Argument:
g_ParseState = ParseState::Arguments;
if (g_CurrentArgumentInfo.info.type == sp::HookParamType_Unknown)
{
globals::sourcemod->LogError(globals::myself, "Missing argument type for argument \"%s\": line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
// The size wasn't set in the config. See if that's fine and we can guess it from the type.
if (!g_CurrentArgumentInfo.info.size)
{
if (g_CurrentArgumentInfo.info.type == sp::HookParamType_Object)
{
globals::sourcemod->LogError(globals::myself, "Object param \"%s\" being set with no size: line: %i col: %i", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
else
{
g_CurrentArgumentInfo.info.size = sp::GetParamTypeSize(g_CurrentArgumentInfo.info.type);
if (g_CurrentArgumentInfo.info.size == 0)
{
globals::sourcemod->LogError(globals::myself, "param \"%s\" size could not be auto-determined!", g_CurrentArgumentInfo.name.c_str(), states->line, states->col);
return SourceMod::SMCResult_HaltFail;
}
}
}
// See if we were changing an existing argument.
bool changed = false;
for (auto &arg : g_CurrentSignature->args)
{
if (!arg.name.compare(g_CurrentArgumentInfo.name))
{
arg.info = g_CurrentArgumentInfo.info;
changed = true;
break;
}
}
// This was a new argument. Add it to the end of the list.
if (!changed)
g_CurrentSignature->args.push_back(g_CurrentArgumentInfo);
g_CurrentArgumentInfo.name = "";
break;
}
return SourceMod::SMCResult_Continue;
}
void SignatureGameConfig::ReadSMC_ParseStart()
{
g_ParseState = ParseState::Root;
g_IgnoreLevel = 0;
g_PlatformOnlyState = ParseState::None;
g_CurrentSignature = nullptr;
g_CurrentFunctionName = "";
g_CurrentArgumentInfo.name = "";
}
sp::ReturnType SignatureGameConfig::GetReturnTypeFromString(const char* str)
{
if (!strcmp(str, "void"))
return sp::ReturnType_Void;
else if (!strcmp(str, "int"))
return sp::ReturnType_Int;
else if (!strcmp(str, "bool"))
return sp::ReturnType_Bool;
else if (!strcmp(str, "float"))
return sp::ReturnType_Float;
else if (!strcmp(str, "string"))
return sp::ReturnType_String;
else if (!strcmp(str, "stringptr"))
return sp::ReturnType_StringPtr;
else if (!strcmp(str, "charptr"))
return sp::ReturnType_CharPtr;
else if (!strcmp(str, "vector"))
return sp::ReturnType_Vector;
else if (!strcmp(str, "vectorptr"))
return sp::ReturnType_VectorPtr;
else if (!strcmp(str, "cbaseentity"))
return sp::ReturnType_CBaseEntity;
else if (!strcmp(str, "edict"))
return sp::ReturnType_Edict;
return sp::ReturnType_Unknown;
}
sp::HookParamType SignatureGameConfig::GetHookParamTypeFromString(const char* str)
{
if (!strcmp(str, "int"))
return sp::HookParamType_Int;
else if (!strcmp(str, "bool"))
return sp::HookParamType_Bool;
else if (!strcmp(str, "float"))
return sp::HookParamType_Float;
else if (!strcmp(str, "string"))
return sp::HookParamType_String;
else if (!strcmp(str, "stringptr"))
return sp::HookParamType_StringPtr;
else if (!strcmp(str, "charptr"))
return sp::HookParamType_CharPtr;
else if (!strcmp(str, "vectorptr"))
return sp::HookParamType_VectorPtr;
else if (!strcmp(str, "cbaseentity"))
return sp::HookParamType_CBaseEntity;
else if (!strcmp(str, "objectptr"))
return sp::HookParamType_ObjectPtr;
else if (!strcmp(str, "edict"))
return sp::HookParamType_Edict;
else if (!strcmp(str, "object"))
return sp::HookParamType_Object;
return sp::HookParamType_Unknown;
}
sp::DHookRegister SignatureGameConfig::GetCustomRegisterFromString(const char* str)
{
if (!strcmp(str, "al"))
return sp::DHookRegister_AL;
else if (!strcmp(str, "cl"))
return sp::DHookRegister_CL;
else if (!strcmp(str, "dl"))
return sp::DHookRegister_DL;
else if (!strcmp(str, "bl"))
return sp::DHookRegister_BL;
else if (!strcmp(str, "ah"))
return sp::DHookRegister_AH;
else if (!strcmp(str, "ch"))
return sp::DHookRegister_CH;
else if (!strcmp(str, "dh"))
return sp::DHookRegister_DH;
else if (!strcmp(str, "bh"))
return sp::DHookRegister_BH;
else if (!strcmp(str, "ax"))
return sp::DHookRegister_EAX;
else if (!strcmp(str, "cx"))
return sp::DHookRegister_ECX;
else if (!strcmp(str, "dx"))
return sp::DHookRegister_EDX;
else if (!strcmp(str, "bx"))
return sp::DHookRegister_EBX;
else if (!strcmp(str, "sp"))
return sp::DHookRegister_ESP;
else if (!strcmp(str, "bp"))
return sp::DHookRegister_EBP;
else if (!strcmp(str, "si"))
return sp::DHookRegister_ESI;
else if (!strcmp(str, "di"))
return sp::DHookRegister_EDI;
else if (!strcmp(str, "eax"))
return sp::DHookRegister_EAX;
else if (!strcmp(str, "ecx"))
return sp::DHookRegister_ECX;
else if (!strcmp(str, "edx"))
return sp::DHookRegister_EDX;
else if (!strcmp(str, "ebx"))
return sp::DHookRegister_EBX;
else if (!strcmp(str, "esp"))
return sp::DHookRegister_ESP;
else if (!strcmp(str, "ebp"))
return sp::DHookRegister_EBP;
else if (!strcmp(str, "esi"))
return sp::DHookRegister_ESI;
else if (!strcmp(str, "edi"))
return sp::DHookRegister_EDI;
else if (!strcmp(str, "rax"))
return sp::DHookRegister_RAX;
else if (!strcmp(str, "rcx"))
return sp::DHookRegister_RCX;
else if (!strcmp(str, "rdx"))
return sp::DHookRegister_RDX;
else if (!strcmp(str, "rbx"))
return sp::DHookRegister_RBX;
else if (!strcmp(str, "rsp"))
return sp::DHookRegister_RSP;
else if (!strcmp(str, "rbp"))
return sp::DHookRegister_RBP;
else if (!strcmp(str, "rsi"))
return sp::DHookRegister_RSI;
else if (!strcmp(str, "rdi"))
return sp::DHookRegister_RDI;
else if (!strcmp(str, "r8"))
return sp::DHookRegister_R8;
else if (!strcmp(str, "r9"))
return sp::DHookRegister_R9;
else if (!strcmp(str, "r10"))
return sp::DHookRegister_R10;
else if (!strcmp(str, "r11"))
return sp::DHookRegister_R11;
else if (!strcmp(str, "r12"))
return sp::DHookRegister_R12;
else if (!strcmp(str, "r13"))
return sp::DHookRegister_R13;
else if (!strcmp(str, "r14"))
return sp::DHookRegister_R14;
else if (!strcmp(str, "r15"))
return sp::DHookRegister_R15;
else if (!strcmp(str, "mm0"))
return sp::DHookRegister_XMM0;
else if (!strcmp(str, "mm1"))
return sp::DHookRegister_XMM1;
else if (!strcmp(str, "mm2"))
return sp::DHookRegister_XMM2;
else if (!strcmp(str, "mm3"))
return sp::DHookRegister_XMM3;
else if (!strcmp(str, "mm4"))
return sp::DHookRegister_XMM4;
else if (!strcmp(str, "mm5"))
return sp::DHookRegister_XMM5;
else if (!strcmp(str, "mm6"))
return sp::DHookRegister_XMM6;
else if (!strcmp(str, "mm7"))
return sp::DHookRegister_XMM7;
else if (!strcmp(str, "xmm0"))
return sp::DHookRegister_XMM0;
else if (!strcmp(str, "xmm1"))
return sp::DHookRegister_XMM1;
else if (!strcmp(str, "xmm2"))
return sp::DHookRegister_XMM2;
else if (!strcmp(str, "xmm3"))
return sp::DHookRegister_XMM3;
else if (!strcmp(str, "xmm4"))
return sp::DHookRegister_XMM4;
else if (!strcmp(str, "xmm5"))
return sp::DHookRegister_XMM5;
else if (!strcmp(str, "xmm6"))
return sp::DHookRegister_XMM6;
else if (!strcmp(str, "xmm7"))
return sp::DHookRegister_XMM7;
else if (!strcmp(str, "xmm8"))
return sp::DHookRegister_XMM8;
else if (!strcmp(str, "xmm9"))
return sp::DHookRegister_XMM9;
else if (!strcmp(str, "xmm10"))
return sp::DHookRegister_XMM10;
else if (!strcmp(str, "xmm11"))
return sp::DHookRegister_XMM11;
else if (!strcmp(str, "xmm12"))
return sp::DHookRegister_XMM12;
else if (!strcmp(str, "xmm13"))
return sp::DHookRegister_XMM13;
else if (!strcmp(str, "xmm14"))
return sp::DHookRegister_XMM14;
else if (!strcmp(str, "xmm15"))
return sp::DHookRegister_XMM15;
else if (!strcmp(str, "st0"))
return sp::DHookRegister_ST0;
return sp::DHookRegister_Default;
}
}

View File

@ -0,0 +1,96 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Dynamic Hooks Extension
* Copyright (C) 2012-2021 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SIGNATURES_H_
#define _INCLUDE_SIGNATURES_H_
#include "sp_inc.hpp"
#include "ITextParsers.h"
#include <string>
#include <vector>
#include <unordered_map>
namespace dhooks {
struct ParamInfo
{
sp::HookParamType type;
size_t size;
sp::DHookPassFlag flags;
sp::DHookRegister custom_register;
};
struct ArgumentInfo {
ArgumentInfo() : name()
{ }
ArgumentInfo(std::string name, ParamInfo info) : name(name), info(info)
{ }
std::string name;
ParamInfo info;
};
class SignatureWrapper {
public:
std::string signature;
std::string address;
std::string offset;
std::vector<ArgumentInfo> args;
sp::CallingConvention callConv;
sp::HookType hookType;
sp::ReturnType retType;
sp::ThisPointerType thisType;
};
class SignatureGameConfig : public SourceMod::ITextListener_SMC {
public:
SignatureWrapper* GetFunctionSignature(const char *function);
public:
//ITextListener_SMC
virtual SourceMod::SMCResult ReadSMC_NewSection(const SourceMod::SMCStates* states, const char* name) override;
virtual SourceMod::SMCResult ReadSMC_KeyValue(const SourceMod::SMCStates* states, const char* key, const char* value) override;
virtual SourceMod::SMCResult ReadSMC_LeavingSection(const SourceMod::SMCStates* states) override;
virtual void ReadSMC_ParseStart() override;
private:
sp::ReturnType GetReturnTypeFromString(const char* str);
sp::HookParamType GetHookParamTypeFromString(const char* str);
sp::DHookRegister GetCustomRegisterFromString(const char* str);
private:
std::unordered_map<std::string, SignatureWrapper*> signatures_;
};
}
#endif

View File

@ -0,0 +1,33 @@
#include "sp_inc.hpp"
#include "sdk_types.hpp"
namespace dhooks::sp {
std::size_t GetParamTypeSize(HookParamType type) {
switch (type) {
case HookParamType_Int:
return sizeof(int);
case HookParamType_Bool:
return sizeof(bool);
case HookParamType_Float:
return sizeof(float);
case HookParamType_String:
return sizeof(sdk::string_t);
case HookParamType_StringPtr:
return sizeof(sdk::string_t*);
case HookParamType_CharPtr:
return sizeof(const char*);
case HookParamType_VectorPtr:
return sizeof(sdk::Vector*);
case HookParamType_CBaseEntity:
return sizeof(sdk::CBaseEntity*);
case HookParamType_ObjectPtr:
return sizeof(void*);
case HookParamType_Edict:
return sizeof(sdk::edict_t*);
default:
return 0;
}
}
}

View File

@ -1,7 +1,16 @@
#pragma once
#include <cstdint>
namespace dhooks::sp {
enum HookType
{
HookType_Entity,
HookType_GameRules,
HookType_Raw
};
enum ThisPointerType
{
ThisPointer_Ignore,
@ -53,7 +62,7 @@ enum ReturnType
ReturnType_Edict
};
enum DHookPassFlag
enum DHookPassFlag : std::uint8_t
{
DHookPass_ByVal = (1<<0),
DHookPass_ByRef = (1<<1),
@ -99,7 +108,31 @@ enum DHookRegister
DHookRegister_XMM5,
DHookRegister_XMM6,
DHookRegister_XMM7,
DHookRegister_ST0
DHookRegister_ST0,
DHookRegister_RAX,
DHookRegister_RCX,
DHookRegister_RDX,
DHookRegister_RBX,
DHookRegister_RSP,
DHookRegister_RBP,
DHookRegister_RSI,
DHookRegister_RDI,
DHookRegister_R8,
DHookRegister_R9,
DHookRegister_R10,
DHookRegister_R11,
DHookRegister_R12,
DHookRegister_R13,
DHookRegister_R14,
DHookRegister_R15,
DHookRegister_XMM8,
DHookRegister_XMM9,
DHookRegister_XMM10,
DHookRegister_XMM11,
DHookRegister_XMM12,
DHookRegister_XMM13,
DHookRegister_XMM14,
DHookRegister_XMM15
};
enum ObjectValueType
@ -119,4 +152,6 @@ enum ObjectValueType
ObjectValueType_String
};
std::size_t GetParamTypeSize(HookParamType type);
}

View File

@ -0,0 +1,56 @@
#pragma once
#include "sp_inc.hpp"
#include "register.hpp"
#include <cstdint>
#include <optional>
namespace dhooks {
struct Variable {
public:
enum Alignment {
OneByte = 1,
TwoBytes = 2,
FourBytes = 4,
EightBytes = 8,
SixteenBytes = 16
};
sp::HookParamType dhook_type;
std::size_t dhook_size;
sp::DHookPassFlag dhook_pass_flags;
sp::DHookRegister dhook_custom_register;
// Provided by the ABI, used by the JIT
// If DHook is to ever support complex type, transform those fields
// into an array so that the SP natives know where to look when offsetting into an object
std::optional<AsmRegCode> reg_index;
std::optional<AsmFloatRegCode> float_reg_index;
std::optional<size_t> reg_offset;
Alignment alignment;
// Atm it's exactly the same as _dhook_size
// We differentiate it anyways, to avoid having to refactor the JIT
std::size_t type_size;
};
struct ReturnVariable {
public:
enum Alignment {
OneByte = 1,
TwoBytes = 2,
FourBytes = 4,
EightBytes = 8,
SixteenBytes = 16
};
sp::ReturnType dhook_type;
size_t dhook_size;
sp::DHookPassFlag dhook_pass_flags;
sp::DHookRegister dhook_custom_register;
std::optional<AsmRegCode> reg_index;
std::optional<AsmFloatRegCode> float_reg_index;
size_t reg_offset;
};
}