From fff6f07ae32fb9bd68221a8c1692cf5f0c34ed3a Mon Sep 17 00:00:00 2001 From: Kenzzer <14257866+kenzzer@users.noreply.github.com> Date: Sun, 28 Sep 2025 23:37:44 +0000 Subject: [PATCH] Add gamedata parser --- extensions/dhooks/AMBuilder | 5 +- extensions/dhooks/src/capsule.hpp | 48 +- extensions/dhooks/src/globals.hpp | 5 + extensions/dhooks/src/handle.cpp | 4 + extensions/dhooks/src/handle.hpp | 23 + extensions/dhooks/src/natives/dhookparam.cpp | 191 +---- extensions/dhooks/src/natives/dhookreturn.cpp | 211 ++++++ extensions/dhooks/src/register.cpp | 8 + extensions/dhooks/src/sdk_types.hpp | 2 +- extensions/dhooks/src/signatures.cpp | 657 ++++++++++++++++++ extensions/dhooks/src/signatures.hpp | 96 +++ extensions/dhooks/src/sp_inc.cpp | 33 + extensions/dhooks/src/sp_inc.hpp | 39 +- extensions/dhooks/src/variable.hpp | 56 ++ 14 files changed, 1145 insertions(+), 233 deletions(-) create mode 100644 extensions/dhooks/src/natives/dhookreturn.cpp create mode 100644 extensions/dhooks/src/signatures.cpp create mode 100644 extensions/dhooks/src/signatures.hpp create mode 100644 extensions/dhooks/src/sp_inc.cpp create mode 100644 extensions/dhooks/src/variable.hpp diff --git a/extensions/dhooks/AMBuilder b/extensions/dhooks/AMBuilder index 79315f888..7cdabd329 100644 --- a/extensions/dhooks/AMBuilder +++ b/extensions/dhooks/AMBuilder @@ -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: diff --git a/extensions/dhooks/src/capsule.hpp b/extensions/dhooks/src/capsule.hpp index 6d3422677..0fcd18cb1 100644 --- a/extensions/dhooks/src/capsule.hpp +++ b/extensions/dhooks/src/capsule.hpp @@ -5,6 +5,7 @@ #include "globals.hpp" #include "handle.hpp" #include "hook.hpp" +#include "variable.hpp" #if defined(DHOOKS_X86_64) #include @@ -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 reg_index; - std::optional float_reg_index; - std::optional 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 reg_index; - std::optional float_reg_index; - size_t reg_offset; -}; - struct HookCallback; class Capsule { public: diff --git a/extensions/dhooks/src/globals.hpp b/extensions/dhooks/src/globals.hpp index fe56c2358..fb14dd056 100644 --- a/extensions/dhooks/src/globals.hpp +++ b/extensions/dhooks/src/globals.hpp @@ -9,6 +9,10 @@ #include #include +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 natives; +extern dhooks::SignatureGameConfig* dhooks_config; void init(); diff --git a/extensions/dhooks/src/handle.cpp b/extensions/dhooks/src/handle.cpp index 8a10227b9..f054ff304 100644 --- a/extensions/dhooks/src/handle.cpp +++ b/extensions/dhooks/src/handle.cpp @@ -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); } } \ No newline at end of file diff --git a/extensions/dhooks/src/handle.hpp b/extensions/dhooks/src/handle.hpp index c8c552a3f..7004bf508 100644 --- a/extensions/dhooks/src/handle.hpp +++ b/extensions/dhooks/src/handle.hpp @@ -1,10 +1,14 @@ #pragma once #include "register.hpp" +#include "variable.hpp" #include #include +#include +#include + namespace dhooks { class Capsule; } @@ -42,4 +46,23 @@ private: void* _return; }; +class HookSetup { +public: + HookSetup(sp::ThisPointerType, sp::CallingConvention, void* address, const std::vector& params, const ReturnVariable& ret); + HookSetup(sp::ThisPointerType, std::uint32_t offset, const std::vector& 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 _virtual_offset; + std::optional _address; + + sp::ThisPointerType _this_pointer; + sp::CallingConvention _dhook_call_conv; + std::vector _dhook_params; + ReturnVariable _dhook_return; +}; + }; \ No newline at end of file diff --git a/extensions/dhooks/src/natives/dhookparam.cpp b/extensions/dhooks/src/natives/dhookparam.cpp index c5dd9cda3..40c9c2e2f 100644 --- a/extensions/dhooks/src/natives/dhookparam.cpp +++ b/extensions/dhooks/src/natives/dhookparam.cpp @@ -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(); - case sp::ReturnType_Bool: - return (*obj->GetReturn()) ? 1 : 0; - case sp::ReturnType_CBaseEntity: - return globals::gamehelpers->EntityToBCompatRef(*obj->GetReturn()); - case sp::ReturnType_Edict: - return globals::gamehelpers->IndexOfEdict(*obj->GetReturn()); - case sp::ReturnType_Float: - return sp_ftoc(*obj->GetReturn()); - 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() = params[2]; - break; - case sp::ReturnType_Bool: - *obj->GetReturn() = 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() = entity; - break; - } - case sp::ReturnType_Edict: { - sdk::edict_t* edict = reinterpret_cast(globals::gamehelpers->EdictOfIndex(params[2])); - if (edict == nullptr || edict->IsFree()) { - return context->ThrowNativeError("Invalid entity index passed for return value"); - } - *obj->GetReturn() = edict; - break; - } - case sp::ReturnType_Float: - *obj->GetReturn() = 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(); - } else if(ret.dhook_type == sp::ReturnType_VectorPtr) { - auto pvector = obj->GetReturn(); - 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(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()) = 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()->ToCStr()); - return 1; - case sp::ReturnType_StringPtr: - context->StringToLocal(params[2], params[3], (obj->GetReturn() != nullptr) ? (*obj->GetReturn())->ToCStr() : ""); - return 1; - case sp::ReturnType_CharPtr: - context->StringToLocal(params[2], params[3], (*(obj->GetReturn()) == nullptr) ? "" : *(obj->GetReturn())); - 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()) = 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& 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)); } diff --git a/extensions/dhooks/src/natives/dhookreturn.cpp b/extensions/dhooks/src/natives/dhookreturn.cpp new file mode 100644 index 000000000..c86ffe830 --- /dev/null +++ b/extensions/dhooks/src/natives/dhookreturn.cpp @@ -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(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(); + case sp::ReturnType_Bool: + return (*obj->GetReturn()) ? 1 : 0; + case sp::ReturnType_CBaseEntity: + return globals::gamehelpers->EntityToBCompatRef(*obj->GetReturn()); + case sp::ReturnType_Edict: + return globals::gamehelpers->IndexOfEdict(*obj->GetReturn()); + case sp::ReturnType_Float: + return sp_ftoc(*obj->GetReturn()); + 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() = params[2]; + break; + case sp::ReturnType_Bool: + *obj->GetReturn() = 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() = entity; + break; + } + case sp::ReturnType_Edict: { + sdk::edict_t* edict = reinterpret_cast(globals::gamehelpers->EdictOfIndex(params[2])); + if (edict == nullptr || edict->IsFree()) { + return context->ThrowNativeError("Invalid entity index passed for return value"); + } + *obj->GetReturn() = edict; + break; + } + case sp::ReturnType_Float: + *obj->GetReturn() = 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(); + } else if(ret.dhook_type == sp::ReturnType_VectorPtr) { + auto pvector = obj->GetReturn(); + 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(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()) = 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()->ToCStr()); + return 1; + case sp::ReturnType_StringPtr: + context->StringToLocal(params[2], params[3], (obj->GetReturn() != nullptr) ? (*obj->GetReturn())->ToCStr() : ""); + return 1; + case sp::ReturnType_CharPtr: + context->StringToLocal(params[2], params[3], (*(obj->GetReturn()) == nullptr) ? "" : *(obj->GetReturn())); + 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()) = 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& 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)); +} + +} \ No newline at end of file diff --git a/extensions/dhooks/src/register.cpp b/extensions/dhooks/src/register.cpp index c031c25d0..051609fe8 100644 --- a/extensions/dhooks/src/register.cpp +++ b/extensions/dhooks/src/register.cpp @@ -8,6 +8,7 @@ std::optional 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 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 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 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; diff --git a/extensions/dhooks/src/sdk_types.hpp b/extensions/dhooks/src/sdk_types.hpp index 77b1936db..94a396615 100644 --- a/extensions/dhooks/src/sdk_types.hpp +++ b/extensions/dhooks/src/sdk_types.hpp @@ -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; } diff --git a/extensions/dhooks/src/signatures.cpp b/extensions/dhooks/src/signatures.cpp new file mode 100644 index 000000000..85a57eee4 --- /dev/null +++ b/extensions/dhooks/src/signatures.cpp @@ -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 . + * + * 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 . + * + * 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(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(0); + if (strstr(value, "byval")) + flags = static_cast(static_cast(flags)|sp::DHookPass_ByVal); + if (strstr(value, "byref")) + flags = static_cast(static_cast(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; +} + +} \ No newline at end of file diff --git a/extensions/dhooks/src/signatures.hpp b/extensions/dhooks/src/signatures.hpp new file mode 100644 index 000000000..0deb02523 --- /dev/null +++ b/extensions/dhooks/src/signatures.hpp @@ -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 . + * + * 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 . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SIGNATURES_H_ +#define _INCLUDE_SIGNATURES_H_ + +#include "sp_inc.hpp" + +#include "ITextParsers.h" + +#include +#include +#include + +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 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 signatures_; +}; + +} +#endif \ No newline at end of file diff --git a/extensions/dhooks/src/sp_inc.cpp b/extensions/dhooks/src/sp_inc.cpp new file mode 100644 index 000000000..fbb8d1b73 --- /dev/null +++ b/extensions/dhooks/src/sp_inc.cpp @@ -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; + } +} + +} \ No newline at end of file diff --git a/extensions/dhooks/src/sp_inc.hpp b/extensions/dhooks/src/sp_inc.hpp index df1949cca..d5ff6276d 100644 --- a/extensions/dhooks/src/sp_inc.hpp +++ b/extensions/dhooks/src/sp_inc.hpp @@ -1,7 +1,16 @@ #pragma once +#include + 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); + } \ No newline at end of file diff --git a/extensions/dhooks/src/variable.hpp b/extensions/dhooks/src/variable.hpp new file mode 100644 index 000000000..1e234bc29 --- /dev/null +++ b/extensions/dhooks/src/variable.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "sp_inc.hpp" +#include "register.hpp" + +#include +#include + +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 reg_index; + std::optional float_reg_index; + std::optional 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 reg_index; + std::optional float_reg_index; + size_t reg_offset; +}; +} \ No newline at end of file