From 28cb3df6b907bf122954a00108d3212f7c5f7129 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Oct 2023 22:23:37 +0300 Subject: [PATCH 1/8] Add templated handler for manual hooks --- core/sourcehook/sourcehook.cpp | 36 + core/sourcehook/sourcehook.h | 653 +++++++++++++++++- core/sourcehook/sourcehook_hookmangen.cpp | 12 +- core/sourcehook/sourcehook_hookmangen.h | 4 +- core/sourcehook/sourcehook_impl.h | 16 + .../sourcehook_impl_chookmaninfo.cpp | 4 +- .../sourcehook/sourcehook_impl_chookmaninfo.h | 14 +- 7 files changed, 718 insertions(+), 21 deletions(-) diff --git a/core/sourcehook/sourcehook.cpp b/core/sourcehook/sourcehook.cpp index 8b66b2c3..a4362d3c 100644 --- a/core/sourcehook/sourcehook.cpp +++ b/core/sourcehook/sourcehook.cpp @@ -97,6 +97,18 @@ namespace SourceHook int CSourceHookImpl::AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) + { + return AddHook(plug, mode, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); + } + + int CSourceHookImpl::AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, + ISHDelegate *handler, bool post) + { + return AddHook(plug, mode, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); + } + + int CSourceHookImpl::AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, + ISHDelegate *handler, bool post) { if (mode != Hook_Normal && mode != Hook_VP && mode != Hook_DVP) return 0; @@ -172,6 +184,18 @@ namespace SourceHook bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) + { + return RemoveHook(plug, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); + } + + bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, + ISHDelegate *handler, bool post) + { + return RemoveHook(plug, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); + } + + bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, + ISHDelegate *handler, bool post) { // Get info about hook manager and compute adjustediface CHookManager tmpHookMan(plug, myHookMan); @@ -417,6 +441,18 @@ namespace SourceHook } void CSourceHookImpl::RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc) + { + // Find the hook manager + RemoveHookManager(plug, HookManagerPubFuncHandler(pubFunc)); + } + + void CSourceHookImpl::RemoveHookManager(Plugin plug, IHookManagerMemberFunc* pubFunc) + { + // Find the hook manager + RemoveHookManager(plug, HookManagerPubFuncHandler(pubFunc)); + } + + void CSourceHookImpl::RemoveHookManager(Plugin plug, const HookManagerPubFuncHandler &pubFunc) { // Find the hook manager CHookManList::iterator hookman_iter = m_HookManList.find(CHookManager::Descriptor(plug, pubFunc)); diff --git a/core/sourcehook/sourcehook.h b/core/sourcehook/sourcehook.h index f562bc23..6c60cea3 100644 --- a/core/sourcehook/sourcehook.h +++ b/core/sourcehook/sourcehook.h @@ -16,6 +16,8 @@ #ifndef __SOURCEHOOK_H__ #define __SOURCEHOOK_H__ +#include + // Interface revisions: // 1 - Initial revision // 2 - Changed to virtual functions for iterators and all queries @@ -40,6 +42,15 @@ #define SH_HOOKMANAUTOGEN_IFACE_VERSION 1 #define SH_HOOKMANAUTOGEN_IMPL_VERSION 1 +namespace SourceHook +{ +class ISourceHook; +typedef int Plugin; +} + +extern SourceHook::ISourceHook *g_SHPtr; +extern SourceHook::Plugin g_PLID; + // The value of SH_GLOB_SHPTR has to be a pointer to SourceHook::ISourceHook // It's used in various macros #ifndef SH_GLOB_SHPTR @@ -247,12 +258,146 @@ namespace SourceHook * @brief Pointer to hook manager interface function * * The hook manager should store hi for later use if store==true. It should then call hi->SetInfo(...) if hi - * is non-null. The hook manager can return 0 for success or a non-zero value if it doesn't want to be used. + * is non-null. The hook manager can return 0 for success or a non-zero value if it doesn't want to be used. * * @param hi A pointer to IHookManagerInfo */ typedef int (*HookManagerPubFunc)(bool store, IHookManagerInfo *hi); + /** + * @brief Pointer to hook manager interface function for member and static functions + * + * The hook manager should store hi for later use if store==true. It should then call hi->SetInfo(...) if hi + * is non-null. The hook manager can return 0 for success or a non-zero value if it doesn't want to be used. + * + * @param hi A pointer to IHookManagerInfo + */ + + class IHookManagerMemberFunc + { + public: + virtual ~IHookManagerMemberFunc() = default; + virtual int Call(bool store, IHookManagerInfo *hi) const = 0; + }; + + class HookManagerPubFuncHandler + { + public: + HookManagerPubFuncHandler() + : staticFunc_(nullptr) + , memberFunc_(nullptr) + { + } + + explicit HookManagerPubFuncHandler(HookManagerPubFunc func) + : staticFunc_(func) + , memberFunc_(nullptr) + { + } + + explicit HookManagerPubFuncHandler(IHookManagerMemberFunc* funcHandler) + : staticFunc_(nullptr) + , memberFunc_(funcHandler) + { + } + + explicit HookManagerPubFuncHandler(const HookManagerPubFuncHandler& other) + : staticFunc_(other.staticFunc_) + , memberFunc_(other.memberFunc_) + { + } + + explicit HookManagerPubFuncHandler(HookManagerPubFuncHandler&& other) + : staticFunc_(other.staticFunc_) + , memberFunc_(other.memberFunc_) + { + other.staticFunc_ = nullptr; + other.memberFunc_ = nullptr; + } + + HookManagerPubFuncHandler& operator=(const HookManagerPubFuncHandler& other) + { + staticFunc_ = other.staticFunc_; + memberFunc_ = other.memberFunc_; + return *this; + } + + HookManagerPubFuncHandler& operator=(HookManagerPubFuncHandler&& other) + { + if (this == &other) + return *this; + + staticFunc_ = other.staticFunc_; + memberFunc_ = other.memberFunc_; + other.staticFunc_ = nullptr; + other.memberFunc_ = nullptr; + return *this; + } + + HookManagerPubFuncHandler& operator=(HookManagerPubFunc func) + { + staticFunc_ = func; + memberFunc_ = nullptr; + return *this; + } + + HookManagerPubFuncHandler& operator=(IHookManagerMemberFunc* funcHandler) + { + staticFunc_ = nullptr; + memberFunc_ = funcHandler; + return *this; + } + + operator bool() const + { + return (staticFunc_ != nullptr || + memberFunc_ != nullptr); + } + + operator HookManagerPubFunc() const + { + return staticFunc_; + } + + operator IHookManagerMemberFunc*() const + { + return memberFunc_; + } + + int operator()(bool store, IHookManagerInfo *hi) const + { + if(staticFunc_ != nullptr) + return staticFunc_(store, hi); + + if(memberFunc_ != nullptr) + return memberFunc_->Call(store, hi); + + return 0; + } + + bool operator==(const HookManagerPubFuncHandler &rhs) const + { + return staticFunc_ == rhs.staticFunc_ && + memberFunc_ == rhs.memberFunc_; + } + + bool operator==(HookManagerPubFunc staticFunc) const + { + return staticFunc_ == staticFunc && + memberFunc_ == nullptr; + } + + bool operator==(const IHookManagerMemberFunc *memberFunc) const + { + return staticFunc_ == nullptr && + memberFunc_ == memberFunc; + } + + private: + HookManagerPubFunc staticFunc_; + IHookManagerMemberFunc* memberFunc_; + }; + class ISHDelegate { public: @@ -353,7 +498,7 @@ namespace SourceHook Hook_Normal, Hook_VP, Hook_DVP - }; + }; /** * @brief Add a (VP) hook. @@ -424,7 +569,7 @@ namespace SourceHook * @brief Remove a hook manager. Auto-removes all hooks attached to it from plugin plug. * * @param plug The owner of the hook manager - * @param pubFunc The hook manager's info function + * @param pubFunc The hook manager's info function */ virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc) = 0; @@ -499,6 +644,36 @@ namespace SourceHook const void *origRetPtr, void *overrideRetPtr) = 0; virtual void EndContext(IHookContext *pCtx) = 0; + + /** + * @brief Add a (VP) hook. + * + * @return non-zero hook id on success, 0 otherwise + * + * @param plug The unique identifier of the plugin that calls this function + * @param mode Can be either Hook_Normal or Hook_VP (vtable-wide hook) + * @param iface The interface pointer + * @param ifacesize The size of the class iface points to + * @param myHookMan A hook manager function that should be capable of handling the function + * @param handler A pointer to the hook handler something + * @param post Set to true if you want a post handler + */ + + virtual int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, + ISHDelegate *handler, bool post) = 0; + + // Source backwarts compat (only for normal hooks) + virtual bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, + ISHDelegate *handler, bool post) = 0; + + /** + * @brief Remove a hook manager. Auto-removes all hooks attached to it from plugin plug. + * + * @param plug The owner of the hook manager + * @param pubFunc The hook manager's info function + */ + + virtual void RemoveHookManager(Plugin plug, IHookManagerMemberFunc* pubFunc) = 0; }; @@ -4924,5 +5099,477 @@ namespace SourceHook } } + +namespace SourceHook +{ + +// Get type info for type T +template +inline void TypeInfo(PassInfo& passInfo) +{ + passInfo.size = sizeof(T); + passInfo.type = GetPassInfo::type; + passInfo.flags = GetPassInfo::flags; +} + +// Get type info for type T +template<> +inline void TypeInfo(PassInfo& passInfo) +{ + passInfo.size = 1; + passInfo.type = 0; + passInfo.flags = 0; +} + +template +class PassInfoInitializer +{ +public: + constexpr PassInfoInitializer() + { + InitializePassInfo(); + } + + const PassInfo *ParamsPassInfo() const + { + return params_; + } + + const size_t ParamsPassInfoSize() const + { + return sizeof...(ArgsType); + } + +private: + template + inline typename std::enable_if::type InitializePassInfo() + { + TypeInfo(params_[index]); + InitializePassInfo(); + } + + template + inline void InitializePassInfo() + { + TypeInfo(params_[index]); + } + + PassInfo params_[sizeof...(ArgsType)]; +}; + +template +struct ReturnTypeInfo +{ + static const size_t size() + { + return sizeof(T); + } + + static const int type() + { + return GetPassInfo::type; + } + + static const unsigned int flags() + { + return GetPassInfo::flags; + } +}; + +template <> +struct ReturnTypeInfo +{ + static const size_t size() + { + return 0; // why isn't it sizeof(void) like in TypeInfo? + } + + static const int type() + { + return 0; + } + + static const unsigned int flags() + { + return 0; + } +}; + +template +class CHookManagerMemberFuncHandler : public IHookManagerMemberFunc +{ +public: + typedef int (T::*HookManagerMemberFunc)(bool store, IHookManagerInfo *hi); + + explicit CHookManagerMemberFuncHandler(T* funcHandler, HookManagerMemberFunc func) + : funcHandler_(funcHandler) + , func_(func) + { + } + + virtual ~CHookManagerMemberFuncHandler() + { + } + +private: + virtual int Call(bool store, IHookManagerInfo *hi) const override + { + return (funcHandler_->*func_)(store, hi); + } + +private: + T* funcHandler_; + HookManagerMemberFunc func_; +}; + +template +class ManualHookHandler : public CHookManagerMemberFuncHandler> +{ +public: + typedef ManualHookHandler ThisType; + typedef fastdelegate::FastDelegate FD; + typedef ReturnType(EmptyClass::*ECMFP)(Params...); + typedef ExecutableClassN CallEC; + + template + struct HookedFuncType + { + typedef ReturnType (T::*HookFunc)(Params...); + }; + + ManualHookHandler() + : CHookManagerMemberFuncHandler(this, &ThisType::HookManPubFunc) + , thisPointerOffset_(0) + , vTableIndex_(0) + , vtableOffset_(0) + , msMFI_{false, 0, 0, 0} + , msHI_(nullptr) + , msProto_{sizeof...(Params), + {ReturnTypeInfo::size(), ReturnTypeInfo::type(), ReturnTypeInfo::flags()}, + paramInfosM_.ParamsPassInfo(), + 0, + __SH_EPI, + paramInfos2M_} + { + for(PassInfo::V2Info& paramInfo : paramInfos2M_) + { + paramInfo = __SH_EPI; + } + } + + virtual ~ManualHookHandler() + { + //TODO apply RAII, cleanup all related hooks here + } + + // TODO probably not needed for manual hooks + void Reconfigure() + { + msMFI_.isVirtual = true; + msMFI_.thisptroffs = thisPointerOffset_; + msMFI_.vtblindex = vTableIndex_; + msMFI_.vtbloffs = vtableOffset_; + } + + void Reconfigure(int vtblindex, int vtbloffs = 0, int thisptroffs = 0) + { + g_SHPtr->RemoveHookManager(g_PLID, this); + msMFI_.thisptroffs = thisptroffs; + msMFI_.vtblindex = vtblindex; + msMFI_.vtbloffs = vtbloffs; + } + + template + int Add(void *iface, T* callbackInstPtr, typename HookedFuncType::HookFunc callbackFuncPtr, bool post = true, ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) + { + typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); + typename ThisType::CMyDelegateImpl* tmp = new typename ThisType::CMyDelegateImpl(handler); // TODO use unique_ptr here + + return g_SHPtr->AddHook(g_PLID, mode, iface, thisPointerOffset_, this, tmp, post); + } + + template + bool Remove(void *iface, T* callbackInstPtr, typename HookedFuncType::HookFunc callbackFuncPtr, bool post = true) + { + typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); + typename ThisType::CMyDelegateImpl tmp(handler); + + return g_SHPtr->RemoveHook(g_PLID, iface, thisPointerOffset_, this, &tmp, post); + } + + // For void return type only + template::value, void>::type* = nullptr> + void Recall(META_RES result, Params... newparams) + { + g_SHPtr->SetRes(result); + g_SHPtr->DoRecall(); + SourceHook::EmptyClass *thisptr = reinterpret_cast(g_SHPtr->GetIfacePtr()); + (thisptr->*(GetRecallMFP(thisptr)))(newparams...); + g_SHPtr->SetRes(MRES_SUPERCEDE); + } + + // For any return type but void + template::value, void>::type* = nullptr> + U Recall(META_RES result, U value, Params... newparams) + { + g_SHPtr->SetRes(result); + g_SHPtr->DoRecall(); + if ((result) >= MRES_OVERRIDE) + { + /* see RETURN_META_VALUE_NEWPARAMS */ + SetOverrideResult(g_SHPtr, value); + } + EmptyClass *thisptr = reinterpret_cast(g_SHPtr->GetIfacePtr()); + g_SHPtr->SetRes(MRES_SUPERCEDE); + return (thisptr->*(GetRecallMFP(thisptr)))(newparams...); + } + +private: + struct IMyDelegate : ISHDelegate + { + virtual ReturnType Call(Params... params) = 0; + }; + + struct CMyDelegateImpl : public IMyDelegate + { + FD m_Deleg; + CMyDelegateImpl(FD deleg) + : m_Deleg(deleg) + { + } + + virtual ReturnType Call(Params... params) override + { + return m_Deleg(params...); + } + + virtual void DeleteThis() override + { + delete this; + } + + virtual bool IsEqual(ISHDelegate *pOtherDeleg) override + { + return m_Deleg == static_cast(pOtherDeleg)->m_Deleg; + } + }; + + // Implementation for any return type but void + template::value, void>::type* = nullptr> + U Func(Params... params) + { + /* 1) Set up calls */ + void *ourvfnptr = reinterpret_cast(*reinterpret_cast(reinterpret_cast(this) + msMFI_.vtbloffs) + msMFI_.vtblindex); + void *vfnptr_origentry; + + META_RES status = MRES_IGNORED; + META_RES prev_res; + META_RES cur_res; + + typedef typename ReferenceCarrier::type my_rettype; + my_rettype orig_ret; + my_rettype override_ret; + my_rettype plugin_ret; + IMyDelegate *iter = nullptr; + + IHookContext *pContext = g_SHPtr->SetupHookLoop(msHI_, + ourvfnptr, + reinterpret_cast(this), + &vfnptr_origentry, + &status, + &prev_res, + &cur_res, + &orig_ret, + &override_ret); + + prev_res = MRES_IGNORED; + + while ((iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + plugin_ret = iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + + if (cur_res >= MRES_OVERRIDE) + *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; + } + + if (status != MRES_SUPERCEDE && pContext->ShouldCallOrig()) + { + ReturnType (EmptyClass::*mfp)(Params...); + SH_SETUP_MFP(mfp); + + orig_ret = (reinterpret_cast(this)->*mfp)(params...); + } + else + orig_ret = override_ret; /* :TODO: ??? : use pContext->GetOverrideRetPtr() or not? */ + + prev_res = MRES_IGNORED; + while ((iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + plugin_ret = iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + + if (cur_res >= MRES_OVERRIDE) + *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; + } + + const void* repPtr = (status >= MRES_OVERRIDE) ? pContext->GetOverrideRetPtr() : + pContext->GetOrigRetPtr(); + + g_SHPtr->EndContext(pContext); + + return *reinterpret_cast(repPtr); + } + + // Implementation for void return type only + template::value, void>::type* = nullptr> + void Func(Params... params) + { + /* 1) Set up calls */ + void *ourvfnptr = reinterpret_cast(*reinterpret_cast(reinterpret_cast(this) + msMFI_.vtbloffs) + msMFI_.vtblindex); + void *vfnptr_origentry; + + META_RES status = MRES_IGNORED; + META_RES prev_res; + META_RES cur_res; + + IMyDelegate *iter = nullptr; + + IHookContext *pContext = g_SHPtr->SetupHookLoop(msHI_, + ourvfnptr, + reinterpret_cast(this), + &vfnptr_origentry, + &status, + &prev_res, + &cur_res, + nullptr, + nullptr); + + prev_res = MRES_IGNORED; + while ((iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + } + + if (status != MRES_SUPERCEDE && pContext->ShouldCallOrig()) + { + void (EmptyClass::*mfp)(Params...); + + reinterpret_cast(&mfp)[0] = vfnptr_origentry; + reinterpret_cast(&mfp)[1] = 0; + + (reinterpret_cast(this)->*mfp)(params...); + } + + prev_res = MRES_IGNORED; + while ( (iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + + iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + } + + g_SHPtr->EndContext(pContext); + } + + int HookManPubFunc(bool store, IHookManagerInfo *hi) + { + /* Verify interface version */ + if (g_SHPtr->GetIfaceVersion() != SH_IFACE_VERSION) + return 1; + + if (g_SHPtr->GetImplVersion() < SH_IMPL_VERSION) + return 1; + + if (store) + msHI_ = hi; + + if (hi) + { + MemFuncInfo mfi = {true, -1, 0, 0}; + GetFuncInfo(this, &ThisType::Func, mfi); + + hi->SetInfo(SH_HOOKMAN_VERSION, + msMFI_.vtbloffs, + msMFI_.vtblindex, + &msProto_, + reinterpret_cast(reinterpret_cast(this) + mfi.vtbloffs)[mfi.vtblindex]); + } + + return 0; + } + + typename ManualHookHandler::ECMFP GetRecallMFP(EmptyClass *thisptr) + { + union + { + typename ManualHookHandler::ECMFP mfp; + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + + u.s.addr = (*reinterpret_cast(reinterpret_cast(thisptr) + msMFI_.vtbloffs))[msMFI_.vtblindex]; + u.s.adjustor = 0; + + return u.mfp; + } + + typename ManualHookHandler::CallEC Call(void *ptr) + { + typename ManualHookHandler::ECMFP mfp; + void *vfnptr = reinterpret_cast(*reinterpret_cast((reinterpret_cast(ptr) + msMFI_.thisptroffs + msMFI_.vtbloffs) ) + msMFI_.vtblindex); + + /* patch mfp */ + *reinterpret_cast(&mfp) = *reinterpret_cast(vfnptr); + + if (sizeof(mfp) == 2*sizeof(void*)) /* gcc */ + *(reinterpret_cast(&mfp) + 1) = 0; + + return ManualHookHandler::CallEC(reinterpret_cast(ptr), mfp, vfnptr, g_SHPtr); + } + + // Implementation for any return type but void + template::value, void>::type* = nullptr> + void SetOverrideResult(ISourceHook *shptr, U value) + { + OverrideFunctor overrideFunc = SourceHook::SetOverrideResult(); + overrideFunc(shptr, value); + } + +private: + int thisPointerOffset_; // thisptroffs + int vTableIndex_; // vtblindex + int vtableOffset_; // vtbloffs + + MemFuncInfo msMFI_; // ms_MFI + IHookManagerInfo *msHI_; // ms_HI + + PassInfoInitializer paramInfosM_; // ParamInfosM + PassInfo::V2Info paramInfos2M_[sizeof...(Params) + 1]; // ParamInfos2M + ProtoInfo msProto_; // ms_Proto +}; + +} // SourceHook + #endif // The pope is dead. -> :( diff --git a/core/sourcehook/sourcehook_hookmangen.cpp b/core/sourcehook/sourcehook_hookmangen.cpp index 287a6432..205d8fb9 100644 --- a/core/sourcehook/sourcehook_hookmangen.cpp +++ b/core/sourcehook/sourcehook_hookmangen.cpp @@ -1959,12 +1959,12 @@ namespace SourceHook return m_GeneratedPubFunc; } - bool GenContext::Equal(const CProto &proto, int vtbl_offs, int vtbl_idx) + bool GenContext::Equal(const CProto &proto, int vtbl_offs, int vtbl_idx) const { return (m_OrigProto.ExactlyEqual(proto) && m_VtblOffs == vtbl_offs && m_VtblIdx == vtbl_idx); } - bool GenContext::Equal(HookManagerPubFunc other) + bool GenContext::Equal(HookManagerPubFunc other) const { return m_GeneratedPubFunc == other; } @@ -2013,11 +2013,9 @@ namespace SourceHook { return NULL; } - else - { - m_Contexts.push_back(sctx); - return sctx.m_GenContext->GetPubFunc(); - } + + m_Contexts.push_back(sctx); + return sctx.m_GenContext->GetPubFunc(); } void CHookManagerAutoGen::ReleaseHookMan(HookManagerPubFunc pubFunc) diff --git a/core/sourcehook/sourcehook_hookmangen.h b/core/sourcehook/sourcehook_hookmangen.h index 9e565f77..0193273c 100644 --- a/core/sourcehook/sourcehook_hookmangen.h +++ b/core/sourcehook/sourcehook_hookmangen.h @@ -265,8 +265,8 @@ namespace SourceHook GenContext(const ProtoInfo *proto, int vtbl_offs, int vtbl_idx, ISourceHook *pSHPtr); ~GenContext(); - bool Equal(const CProto &proto, int vtbl_offs, int vtbl_idx); - bool Equal(HookManagerPubFunc other); + bool Equal(const CProto &proto, int vtbl_offs, int vtbl_idx) const; + bool Equal(HookManagerPubFunc other) const; HookManagerPubFunc GetPubFunc(); }; diff --git a/core/sourcehook/sourcehook_impl.h b/core/sourcehook/sourcehook_impl.h index f9ef068f..f929cd8d 100644 --- a/core/sourcehook/sourcehook_impl.h +++ b/core/sourcehook/sourcehook_impl.h @@ -322,9 +322,21 @@ namespace SourceHook int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post); + int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, + ISHDelegate *handler, bool post); + + int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, + ISHDelegate *handler, bool post); + bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post); + bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, + ISHDelegate *handler, bool post); + + bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, + ISHDelegate *handler, bool post); + bool RemoveHookByID(int hookid); bool PauseHookByID(int hookid); @@ -355,6 +367,10 @@ namespace SourceHook void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc); + void RemoveHookManager(Plugin plug, IHookManagerMemberFunc* pubFunc); + + void RemoveHookManager(Plugin plug, const HookManagerPubFuncHandler &pubFunc); + void SetIgnoreHooks(void *vfnptr); void ResetIgnoreHooks(void *vfnptr); diff --git a/core/sourcehook/sourcehook_impl_chookmaninfo.cpp b/core/sourcehook/sourcehook_impl_chookmaninfo.cpp index 639b7a2a..21e20429 100644 --- a/core/sourcehook/sourcehook_impl_chookmaninfo.cpp +++ b/core/sourcehook/sourcehook_impl_chookmaninfo.cpp @@ -15,7 +15,7 @@ namespace SourceHook { namespace Impl { - CHookManager::CHookManager(Plugin ownerPlugin, HookManagerPubFunc pubFunc) + CHookManager::CHookManager(Plugin ownerPlugin, const HookManagerPubFuncHandler& pubFunc) : m_OwnerPlugin(ownerPlugin), m_PubFunc(pubFunc), m_Version(-1) { // Query pubfunc @@ -61,7 +61,7 @@ namespace SourceHook Unregister(); } - CHookManager *CHookManList::GetHookMan(Plugin plug, HookManagerPubFunc pubFunc) + CHookManager *CHookManList::GetHookMan(Plugin plug, const HookManagerPubFuncHandler &pubFunc) { CHookManager hm(plug, pubFunc); return GetHookMan(hm); diff --git a/core/sourcehook/sourcehook_impl_chookmaninfo.h b/core/sourcehook/sourcehook_impl_chookmaninfo.h index 6a805ea9..afb0bd3c 100644 --- a/core/sourcehook/sourcehook_impl_chookmaninfo.h +++ b/core/sourcehook/sourcehook_impl_chookmaninfo.h @@ -24,7 +24,7 @@ namespace SourceHook { // *** Data *** Plugin m_OwnerPlugin; - HookManagerPubFunc m_PubFunc; + HookManagerPubFuncHandler m_PubFunc; int m_VtblOffs; int m_VtblIdx; CProto m_Proto; @@ -38,15 +38,15 @@ namespace SourceHook struct Descriptor { Plugin m_OwnerPlugin; - HookManagerPubFunc m_PubFunc; - Descriptor(Plugin ownerPlugin, HookManagerPubFunc pubFunc) + HookManagerPubFuncHandler m_PubFunc; + Descriptor(Plugin ownerPlugin, const HookManagerPubFuncHandler &pubFunc) : m_OwnerPlugin(ownerPlugin), m_PubFunc(pubFunc) { } }; // *** Interface *** - CHookManager(Plugin ownerPlugin, HookManagerPubFunc pubFunc); + CHookManager(Plugin ownerPlugin, const HookManagerPubFuncHandler &pubFunc); inline bool operator==(const Descriptor &other) const; inline bool operator==(const CHookManager &other) const; @@ -58,7 +58,7 @@ namespace SourceHook inline const CProto &GetProto() const; inline int GetVersion() const; inline void *GetHookFunc() const; - inline HookManagerPubFunc GetPubFunc() const; + inline const HookManagerPubFuncHandler &GetPubFunc() const; void Register(); void Unregister(); @@ -79,7 +79,7 @@ namespace SourceHook class CHookManList : public List { public: - CHookManager *GetHookMan(Plugin plug, HookManagerPubFunc pubFunc); + CHookManager *GetHookMan(Plugin plug, const HookManagerPubFuncHandler &pubFunc); CHookManager *GetHookMan(CHookManager &hm); }; @@ -132,7 +132,7 @@ namespace SourceHook return *reinterpret_cast(m_HookfuncVfnptr); } - inline HookManagerPubFunc CHookManager::GetPubFunc() const + inline const HookManagerPubFuncHandler &CHookManager::GetPubFunc() const { return m_PubFunc; } From 1c92d104a174e4a6cda6e105919a99cdb7935a6d Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 4 Dec 2023 19:48:08 +0300 Subject: [PATCH 2/8] Add new proto info --- core/sourcehook/sourcehook.h | 192 ++++++++++++++---- .../sourcehook_impl_chookmaninfo.cpp | 10 + .../sourcehook/sourcehook_impl_chookmaninfo.h | 3 + core/sourcehook/sourcehook_impl_cproto.cpp | 40 +++- core/sourcehook/sourcehook_impl_cproto.h | 7 + 5 files changed, 216 insertions(+), 36 deletions(-) diff --git a/core/sourcehook/sourcehook.h b/core/sourcehook/sourcehook.h index 6c60cea3..5ed13961 100644 --- a/core/sourcehook/sourcehook.h +++ b/core/sourcehook/sourcehook.h @@ -184,6 +184,20 @@ namespace SourceHook */ struct PassInfo { + PassInfo() + : size(0) + , type(0) + , flags(0) + { + } + + PassInfo(size_t typeSize, int typeId, unsigned int typeFlags) + : size(typeSize) + , type(typeId) + , flags(typeFlags) + { + } + enum PassType { PassType_Unknown=0, /**< Unknown -- no extra info available */ @@ -217,6 +231,22 @@ namespace SourceHook struct V2Info { + V2Info() + : pNormalCtor(nullptr) + , pCopyCtor(nullptr) + , pDtor(nullptr) + , pAssignOperator(nullptr) + { + } + + V2Info(void *normalCtor, void *copyCtor, void *dtor, void *assignOperator) + : pNormalCtor(normalCtor) + , pCopyCtor(copyCtor) + , pDtor(dtor) + , pAssignOperator(assignOperator) + { + } + void *pNormalCtor; void *pCopyCtor; void *pDtor; @@ -252,6 +282,26 @@ namespace SourceHook const PassInfo::V2Info *paramsPassInfo2; }; + class IProtoInfo + { + public: + enum class ProtoInfoVersion : int + { + ProtoInfoVersionInvalid = -1, + ProtoInfoVersion1 = 0, + ProtoInfoVersion2 = 1 + }; + + virtual ~IProtoInfo() = default; + virtual size_t GetNumOfParams() const = 0; + virtual const PassInfo &GetRetPassInfo() const = 0; + virtual const PassInfo *GetParamsPassInfo() const = 0; + virtual int GetConvention() const = 0; + virtual IProtoInfo::ProtoInfoVersion GetVersion() const = 0; + virtual const PassInfo::V2Info &GetRetPassInfo2() const = 0; + virtual const PassInfo::V2Info *GetParamsPassInfo2() const = 0; + }; + struct IHookManagerInfo; /** @@ -409,6 +459,9 @@ namespace SourceHook { virtual void SetInfo(int hookman_version, int vtbloffs, int vtblidx, ProtoInfo *proto, void *hookfunc_vfnptr) = 0; + + virtual void SetInfo(int hookman_version, int vtbloffs, int vtblidx, + IProtoInfo *proto, void *hookfunc_vfnptr) = 0; }; // I'm adding support for functions which return references. @@ -5130,12 +5183,17 @@ class PassInfoInitializer InitializePassInfo(); } - const PassInfo *ParamsPassInfo() const + const PassInfo *GetParamsPassInfo() const { return params_; } - const size_t ParamsPassInfoSize() const + const PassInfo::V2Info *GetParamsPassInfoV2() const + { + return paramsV2_; + } + + const size_t GetParamsPassInfoSize() const { return sizeof...(ArgsType); } @@ -5155,6 +5213,32 @@ class PassInfoInitializer } PassInfo params_[sizeof...(ArgsType)]; + PassInfo::V2Info paramsV2_[sizeof...(ArgsType)]; +}; + +// For zero arguments +template<> +class PassInfoInitializer<> +{ +public: + constexpr PassInfoInitializer() + { + } + + const PassInfo *GetParamsPassInfo() const + { + return nullptr; + } + + const PassInfo::V2Info *GetParamsPassInfoV2() const + { + return nullptr; + } + + const size_t GetParamsPassInfoSize() const + { + return 0; + } }; template @@ -5181,7 +5265,7 @@ struct ReturnTypeInfo { static const size_t size() { - return 0; // why isn't it sizeof(void) like in TypeInfo? + return 0; } static const int type() @@ -5222,6 +5306,64 @@ class CHookManagerMemberFuncHandler : public IHookManagerMemberFunc HookManagerMemberFunc func_; }; +template +class CProtoInfo : public IProtoInfo +{ +public: + constexpr CProtoInfo() + : retPassInfo(ReturnTypeInfo::size(), + ReturnTypeInfo::type(), + ReturnTypeInfo::flags()) + { + } + + virtual ~CProtoInfo() override + { + } + + virtual size_t GetNumOfParams() const override + { + return paramsPassInfo.GetParamsPassInfoSize(); + } + + virtual const PassInfo &GetRetPassInfo() const override + { + return retPassInfo; + } + + virtual const PassInfo *GetParamsPassInfo() const override + { + return paramsPassInfo.GetParamsPassInfo(); + } + + virtual int GetConvention() const override + { + return 0; + } + + // version of the ProtoInfo structure. + virtual IProtoInfo::ProtoInfoVersion GetVersion() const override + { + return ProtoInfoVersion::ProtoInfoVersion2; + } + + virtual const PassInfo::V2Info &GetRetPassInfo2() const override + { + return retPassInfo2; + } + + virtual const PassInfo::V2Info *GetParamsPassInfo2() const override + { + return paramsPassInfo.GetParamsPassInfoV2(); + } + +private: + int numOfParams; //!< number of parameters + PassInfo retPassInfo; //!< PassInfo for the return value. size=0 -> no retval + PassInfo::V2Info retPassInfo2; //!< Version2 only + PassInfoInitializer paramsPassInfo; //!< PassInfos for the parameters +}; + template class ManualHookHandler : public CHookManagerMemberFuncHandler> { @@ -5237,40 +5379,26 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler(this, &ThisType::HookManPubFunc) - , thisPointerOffset_(0) - , vTableIndex_(0) - , vtableOffset_(0) , msMFI_{false, 0, 0, 0} , msHI_(nullptr) - , msProto_{sizeof...(Params), - {ReturnTypeInfo::size(), ReturnTypeInfo::type(), ReturnTypeInfo::flags()}, - paramInfosM_.ParamsPassInfo(), - 0, - __SH_EPI, - paramInfos2M_} { - for(PassInfo::V2Info& paramInfo : paramInfos2M_) - { - paramInfo = __SH_EPI; - } } + ManualHookHandler(const ManualHookHandler&) = delete; + + ManualHookHandler(ManualHookHandler&&) = delete; + + ManualHookHandler& operator=(const ManualHookHandler&) = delete; + + ManualHookHandler& operator=(ManualHookHandler&&) = delete; + virtual ~ManualHookHandler() { //TODO apply RAII, cleanup all related hooks here } - // TODO probably not needed for manual hooks - void Reconfigure() - { - msMFI_.isVirtual = true; - msMFI_.thisptroffs = thisPointerOffset_; - msMFI_.vtblindex = vTableIndex_; - msMFI_.vtbloffs = vtableOffset_; - } - void Reconfigure(int vtblindex, int vtbloffs = 0, int thisptroffs = 0) { g_SHPtr->RemoveHookManager(g_PLID, this); @@ -5285,7 +5413,7 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerAddHook(g_PLID, mode, iface, thisPointerOffset_, this, tmp, post); + return g_SHPtr->AddHook(g_PLID, mode, iface, 0, this, tmp, post); } template @@ -5294,7 +5422,7 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerRemoveHook(g_PLID, iface, thisPointerOffset_, this, &tmp, post); + return g_SHPtr->RemoveHook(g_PLID, iface, 0, this, &tmp, post); } // For void return type only @@ -5557,16 +5685,10 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler paramInfosM_; // ParamInfosM - PassInfo::V2Info paramInfos2M_[sizeof...(Params) + 1]; // ParamInfos2M - ProtoInfo msProto_; // ms_Proto + CProtoInfo msProto_; // ms_Proto }; } // SourceHook diff --git a/core/sourcehook/sourcehook_impl_chookmaninfo.cpp b/core/sourcehook/sourcehook_impl_chookmaninfo.cpp index 21e20429..bb497f72 100644 --- a/core/sourcehook/sourcehook_impl_chookmaninfo.cpp +++ b/core/sourcehook/sourcehook_impl_chookmaninfo.cpp @@ -37,6 +37,16 @@ namespace SourceHook m_HookfuncVfnptr = hookfunc_vfnptr; } + void CHookManager::SetInfo(int hookman_version, int vtbloffs, int vtblidx, + IProtoInfo *proto, void *hookfunc_vfnptr) + { + m_Version = hookman_version; + m_VtblOffs = vtbloffs; + m_VtblIdx = vtblidx; + m_Proto = proto; + m_HookfuncVfnptr = hookfunc_vfnptr; + } + void CHookManager::Register() { m_PubFunc(true, this); diff --git a/core/sourcehook/sourcehook_impl_chookmaninfo.h b/core/sourcehook/sourcehook_impl_chookmaninfo.h index afb0bd3c..5d757852 100644 --- a/core/sourcehook/sourcehook_impl_chookmaninfo.h +++ b/core/sourcehook/sourcehook_impl_chookmaninfo.h @@ -74,6 +74,9 @@ namespace SourceHook // *** IHookManagerInfo interface *** void SetInfo(int hookman_version, int vtbloffs, int vtblidx, ProtoInfo *proto, void *hookfunc_vfnptr); + + void SetInfo(int hookman_version, int vtbloffs, int vtblidx, + IProtoInfo *proto, void *hookfunc_vfnptr); }; class CHookManList : public List diff --git a/core/sourcehook/sourcehook_impl_cproto.cpp b/core/sourcehook/sourcehook_impl_cproto.cpp index ff8af288..a386c103 100644 --- a/core/sourcehook/sourcehook_impl_cproto.cpp +++ b/core/sourcehook/sourcehook_impl_cproto.cpp @@ -37,7 +37,6 @@ namespace SourceHook m_RetPassInfo.pDtor = NULL; m_RetPassInfo.pAssignOperator = NULL; - m_ParamsPassInfo.resize(pProto->numOfParams); for (int i = 1; i <= pProto->numOfParams; ++i) @@ -89,6 +88,45 @@ namespace SourceHook } } + void CProto::Fill(const IProtoInfo *pProto) + { + if (pProto == nullptr) + m_Version = -1; + + m_ParamsPassInfo.clear(); + + m_Version = static_cast(pProto->GetVersion()); + m_Convention = pProto->GetConvention(); + m_NumOfParams = pProto->GetNumOfParams(); + + const PassInfo& retPassInfo = pProto->GetRetPassInfo(); + m_RetPassInfo.size = retPassInfo.size; + m_RetPassInfo.type = retPassInfo.type; + m_RetPassInfo.flags = retPassInfo.flags; + + const PassInfo::V2Info& retPassInfo2 = pProto->GetRetPassInfo2(); + m_RetPassInfo.pNormalCtor = retPassInfo2.pNormalCtor; // should be nullptr for Version 1 + m_RetPassInfo.pCopyCtor = retPassInfo2.pCopyCtor; // should be nullptr for Version 1 + m_RetPassInfo.pDtor = retPassInfo2.pDtor; // should be nullptr for Version 1 + m_RetPassInfo.pAssignOperator = retPassInfo2.pAssignOperator; // should be nullptr for Version 1 + + m_ParamsPassInfo.resize(pProto->GetNumOfParams()); + + const PassInfo* paramsPassInfo = pProto->GetParamsPassInfo(); + const PassInfo::V2Info* paramsPassInfo2 = pProto->GetParamsPassInfo2(); + for (int i = 0; i != m_NumOfParams; ++i) + { + m_ParamsPassInfo[i].size = paramsPassInfo[i].size; + m_ParamsPassInfo[i].type = paramsPassInfo[i].type; + m_ParamsPassInfo[i].flags = paramsPassInfo[i].flags; + + m_ParamsPassInfo[i].pNormalCtor = paramsPassInfo2[i].pNormalCtor; // should be nullptr for Version 1 + m_ParamsPassInfo[i].pCopyCtor = paramsPassInfo2[i].pCopyCtor; // should be nullptr for Version 1 + m_ParamsPassInfo[i].pDtor = paramsPassInfo2[i].pDtor; // should be nullptr for Version 1 + m_ParamsPassInfo[i].pAssignOperator = paramsPassInfo2[i].pAssignOperator; // should be nullptr for Version 1 + } + } + // Basic compat test // Other than this, we assume that the plugins know what they're doing bool CProto::operator == (const CProto &other) const diff --git a/core/sourcehook/sourcehook_impl_cproto.h b/core/sourcehook/sourcehook_impl_cproto.h index cb436f11..c209dba6 100644 --- a/core/sourcehook/sourcehook_impl_cproto.h +++ b/core/sourcehook/sourcehook_impl_cproto.h @@ -53,6 +53,8 @@ namespace SourceHook void Fill(const ProtoInfo *pProto); + void Fill(const IProtoInfo *pProto); + // For old sourcehook.h: flags 0 -> assume ByVal static unsigned int GetRealFlags(const PassInfo &info) { @@ -69,6 +71,11 @@ namespace SourceHook Fill(pProto); } + CProto(const IProtoInfo *pProto) + { + Fill(pProto); + } + CProto(const CProto &other) : m_Version(other.m_Version), m_NumOfParams(other.m_NumOfParams), m_RetPassInfo(other.m_RetPassInfo), m_ParamsPassInfo(other.m_ParamsPassInfo), m_Convention(other.m_Convention) From 772e201e232da56410e4e2411b9f9aa3517345d1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 6 Dec 2023 20:09:10 +0300 Subject: [PATCH 3/8] Add methods to remove and pause manual hooks by index --- core/sourcehook/sourcehook.cpp | 6 ------ core/sourcehook/sourcehook.h | 22 ++++++++++++---------- core/sourcehook/sourcehook_impl.h | 3 --- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/core/sourcehook/sourcehook.cpp b/core/sourcehook/sourcehook.cpp index a4362d3c..fc96ee91 100644 --- a/core/sourcehook/sourcehook.cpp +++ b/core/sourcehook/sourcehook.cpp @@ -188,12 +188,6 @@ namespace SourceHook return RemoveHook(plug, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); } - bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, - ISHDelegate *handler, bool post) - { - return RemoveHook(plug, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); - } - bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, ISHDelegate *handler, bool post) { diff --git a/core/sourcehook/sourcehook.h b/core/sourcehook/sourcehook.h index 5ed13961..f813ee55 100644 --- a/core/sourcehook/sourcehook.h +++ b/core/sourcehook/sourcehook.h @@ -715,10 +715,6 @@ namespace SourceHook virtual int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, ISHDelegate *handler, bool post) = 0; - // Source backwarts compat (only for normal hooks) - virtual bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, - ISHDelegate *handler, bool post) = 0; - /** * @brief Remove a hook manager. Auto-removes all hooks attached to it from plugin plug. * @@ -5396,7 +5392,7 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerRemoveHookManager(g_PLID, this); } void Reconfigure(int vtblindex, int vtbloffs = 0, int thisptroffs = 0) @@ -5416,13 +5412,19 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerAddHook(g_PLID, mode, iface, 0, this, tmp, post); } - template - bool Remove(void *iface, T* callbackInstPtr, typename HookedFuncType::HookFunc callbackFuncPtr, bool post = true) + bool Remove(int hookid) { - typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); - typename ThisType::CMyDelegateImpl tmp(handler); + return g_SHPtr->RemoveHookByID(hookid); + } - return g_SHPtr->RemoveHook(g_PLID, iface, 0, this, &tmp, post); + bool Pause(int hookid) + { + return g_SHPtr->PauseHookByID(hookid); + } + + bool Unpause(int hookid) + { + return g_SHPtr->UnpauseHookByID(hookid); } // For void return type only diff --git a/core/sourcehook/sourcehook_impl.h b/core/sourcehook/sourcehook_impl.h index f929cd8d..fb3204fc 100644 --- a/core/sourcehook/sourcehook_impl.h +++ b/core/sourcehook/sourcehook_impl.h @@ -331,9 +331,6 @@ namespace SourceHook bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post); - bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, - ISHDelegate *handler, bool post); - bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, ISHDelegate *handler, bool post); From b0b1d5d707ac58ec37d078fa20e4240d867900bb Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 6 Dec 2023 20:44:50 +0300 Subject: [PATCH 4/8] Move manual hook handler to a separate header --- core/sourcehook/sourcehook.h | 559 ------------------------- core/sourcehook/sourcehook_handlers.h | 577 ++++++++++++++++++++++++++ 2 files changed, 577 insertions(+), 559 deletions(-) create mode 100644 core/sourcehook/sourcehook_handlers.h diff --git a/core/sourcehook/sourcehook.h b/core/sourcehook/sourcehook.h index f813ee55..a6511aeb 100644 --- a/core/sourcehook/sourcehook.h +++ b/core/sourcehook/sourcehook.h @@ -16,8 +16,6 @@ #ifndef __SOURCEHOOK_H__ #define __SOURCEHOOK_H__ -#include - // Interface revisions: // 1 - Initial revision // 2 - Changed to virtual functions for iterators and all queries @@ -42,15 +40,6 @@ #define SH_HOOKMANAUTOGEN_IFACE_VERSION 1 #define SH_HOOKMANAUTOGEN_IMPL_VERSION 1 -namespace SourceHook -{ -class ISourceHook; -typedef int Plugin; -} - -extern SourceHook::ISourceHook *g_SHPtr; -extern SourceHook::Plugin g_PLID; - // The value of SH_GLOB_SHPTR has to be a pointer to SourceHook::ISourceHook // It's used in various macros #ifndef SH_GLOB_SHPTR @@ -5147,553 +5136,5 @@ namespace SourceHook return reinterpret_cast(shptr->GetIfacePtr()); } } - - -namespace SourceHook -{ - -// Get type info for type T -template -inline void TypeInfo(PassInfo& passInfo) -{ - passInfo.size = sizeof(T); - passInfo.type = GetPassInfo::type; - passInfo.flags = GetPassInfo::flags; -} - -// Get type info for type T -template<> -inline void TypeInfo(PassInfo& passInfo) -{ - passInfo.size = 1; - passInfo.type = 0; - passInfo.flags = 0; -} - -template -class PassInfoInitializer -{ -public: - constexpr PassInfoInitializer() - { - InitializePassInfo(); - } - - const PassInfo *GetParamsPassInfo() const - { - return params_; - } - - const PassInfo::V2Info *GetParamsPassInfoV2() const - { - return paramsV2_; - } - - const size_t GetParamsPassInfoSize() const - { - return sizeof...(ArgsType); - } - -private: - template - inline typename std::enable_if::type InitializePassInfo() - { - TypeInfo(params_[index]); - InitializePassInfo(); - } - - template - inline void InitializePassInfo() - { - TypeInfo(params_[index]); - } - - PassInfo params_[sizeof...(ArgsType)]; - PassInfo::V2Info paramsV2_[sizeof...(ArgsType)]; -}; - -// For zero arguments -template<> -class PassInfoInitializer<> -{ -public: - constexpr PassInfoInitializer() - { - } - - const PassInfo *GetParamsPassInfo() const - { - return nullptr; - } - - const PassInfo::V2Info *GetParamsPassInfoV2() const - { - return nullptr; - } - - const size_t GetParamsPassInfoSize() const - { - return 0; - } -}; - -template -struct ReturnTypeInfo -{ - static const size_t size() - { - return sizeof(T); - } - - static const int type() - { - return GetPassInfo::type; - } - - static const unsigned int flags() - { - return GetPassInfo::flags; - } -}; - -template <> -struct ReturnTypeInfo -{ - static const size_t size() - { - return 0; - } - - static const int type() - { - return 0; - } - - static const unsigned int flags() - { - return 0; - } -}; - -template -class CHookManagerMemberFuncHandler : public IHookManagerMemberFunc -{ -public: - typedef int (T::*HookManagerMemberFunc)(bool store, IHookManagerInfo *hi); - - explicit CHookManagerMemberFuncHandler(T* funcHandler, HookManagerMemberFunc func) - : funcHandler_(funcHandler) - , func_(func) - { - } - - virtual ~CHookManagerMemberFuncHandler() - { - } - -private: - virtual int Call(bool store, IHookManagerInfo *hi) const override - { - return (funcHandler_->*func_)(store, hi); - } - -private: - T* funcHandler_; - HookManagerMemberFunc func_; -}; - -template -class CProtoInfo : public IProtoInfo -{ -public: - constexpr CProtoInfo() - : retPassInfo(ReturnTypeInfo::size(), - ReturnTypeInfo::type(), - ReturnTypeInfo::flags()) - { - } - - virtual ~CProtoInfo() override - { - } - - virtual size_t GetNumOfParams() const override - { - return paramsPassInfo.GetParamsPassInfoSize(); - } - - virtual const PassInfo &GetRetPassInfo() const override - { - return retPassInfo; - } - - virtual const PassInfo *GetParamsPassInfo() const override - { - return paramsPassInfo.GetParamsPassInfo(); - } - - virtual int GetConvention() const override - { - return 0; - } - - // version of the ProtoInfo structure. - virtual IProtoInfo::ProtoInfoVersion GetVersion() const override - { - return ProtoInfoVersion::ProtoInfoVersion2; - } - - virtual const PassInfo::V2Info &GetRetPassInfo2() const override - { - return retPassInfo2; - } - - virtual const PassInfo::V2Info *GetParamsPassInfo2() const override - { - return paramsPassInfo.GetParamsPassInfoV2(); - } - -private: - int numOfParams; //!< number of parameters - PassInfo retPassInfo; //!< PassInfo for the return value. size=0 -> no retval - PassInfo::V2Info retPassInfo2; //!< Version2 only - PassInfoInitializer paramsPassInfo; //!< PassInfos for the parameters -}; - -template -class ManualHookHandler : public CHookManagerMemberFuncHandler> -{ -public: - typedef ManualHookHandler ThisType; - typedef fastdelegate::FastDelegate FD; - typedef ReturnType(EmptyClass::*ECMFP)(Params...); - typedef ExecutableClassN CallEC; - - template - struct HookedFuncType - { - typedef ReturnType (T::*HookFunc)(Params...); - }; - - constexpr ManualHookHandler() - : CHookManagerMemberFuncHandler(this, &ThisType::HookManPubFunc) - , msMFI_{false, 0, 0, 0} - , msHI_(nullptr) - { - } - - ManualHookHandler(const ManualHookHandler&) = delete; - - ManualHookHandler(ManualHookHandler&&) = delete; - - ManualHookHandler& operator=(const ManualHookHandler&) = delete; - - ManualHookHandler& operator=(ManualHookHandler&&) = delete; - - virtual ~ManualHookHandler() - { - g_SHPtr->RemoveHookManager(g_PLID, this); - } - - void Reconfigure(int vtblindex, int vtbloffs = 0, int thisptroffs = 0) - { - g_SHPtr->RemoveHookManager(g_PLID, this); - msMFI_.thisptroffs = thisptroffs; - msMFI_.vtblindex = vtblindex; - msMFI_.vtbloffs = vtbloffs; - } - - template - int Add(void *iface, T* callbackInstPtr, typename HookedFuncType::HookFunc callbackFuncPtr, bool post = true, ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) - { - typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); - typename ThisType::CMyDelegateImpl* tmp = new typename ThisType::CMyDelegateImpl(handler); // TODO use unique_ptr here - - return g_SHPtr->AddHook(g_PLID, mode, iface, 0, this, tmp, post); - } - - bool Remove(int hookid) - { - return g_SHPtr->RemoveHookByID(hookid); - } - - bool Pause(int hookid) - { - return g_SHPtr->PauseHookByID(hookid); - } - - bool Unpause(int hookid) - { - return g_SHPtr->UnpauseHookByID(hookid); - } - - // For void return type only - template::value, void>::type* = nullptr> - void Recall(META_RES result, Params... newparams) - { - g_SHPtr->SetRes(result); - g_SHPtr->DoRecall(); - SourceHook::EmptyClass *thisptr = reinterpret_cast(g_SHPtr->GetIfacePtr()); - (thisptr->*(GetRecallMFP(thisptr)))(newparams...); - g_SHPtr->SetRes(MRES_SUPERCEDE); - } - - // For any return type but void - template::value, void>::type* = nullptr> - U Recall(META_RES result, U value, Params... newparams) - { - g_SHPtr->SetRes(result); - g_SHPtr->DoRecall(); - if ((result) >= MRES_OVERRIDE) - { - /* see RETURN_META_VALUE_NEWPARAMS */ - SetOverrideResult(g_SHPtr, value); - } - EmptyClass *thisptr = reinterpret_cast(g_SHPtr->GetIfacePtr()); - g_SHPtr->SetRes(MRES_SUPERCEDE); - return (thisptr->*(GetRecallMFP(thisptr)))(newparams...); - } - -private: - struct IMyDelegate : ISHDelegate - { - virtual ReturnType Call(Params... params) = 0; - }; - - struct CMyDelegateImpl : public IMyDelegate - { - FD m_Deleg; - CMyDelegateImpl(FD deleg) - : m_Deleg(deleg) - { - } - - virtual ReturnType Call(Params... params) override - { - return m_Deleg(params...); - } - - virtual void DeleteThis() override - { - delete this; - } - - virtual bool IsEqual(ISHDelegate *pOtherDeleg) override - { - return m_Deleg == static_cast(pOtherDeleg)->m_Deleg; - } - }; - - // Implementation for any return type but void - template::value, void>::type* = nullptr> - U Func(Params... params) - { - /* 1) Set up calls */ - void *ourvfnptr = reinterpret_cast(*reinterpret_cast(reinterpret_cast(this) + msMFI_.vtbloffs) + msMFI_.vtblindex); - void *vfnptr_origentry; - - META_RES status = MRES_IGNORED; - META_RES prev_res; - META_RES cur_res; - - typedef typename ReferenceCarrier::type my_rettype; - my_rettype orig_ret; - my_rettype override_ret; - my_rettype plugin_ret; - IMyDelegate *iter = nullptr; - - IHookContext *pContext = g_SHPtr->SetupHookLoop(msHI_, - ourvfnptr, - reinterpret_cast(this), - &vfnptr_origentry, - &status, - &prev_res, - &cur_res, - &orig_ret, - &override_ret); - - prev_res = MRES_IGNORED; - - while ((iter = static_cast(pContext->GetNext()))) - { - cur_res = MRES_IGNORED; - plugin_ret = iter->Call(params...); - prev_res = cur_res; - - if (cur_res > status) - status = cur_res; - - if (cur_res >= MRES_OVERRIDE) - *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; - } - - if (status != MRES_SUPERCEDE && pContext->ShouldCallOrig()) - { - ReturnType (EmptyClass::*mfp)(Params...); - SH_SETUP_MFP(mfp); - - orig_ret = (reinterpret_cast(this)->*mfp)(params...); - } - else - orig_ret = override_ret; /* :TODO: ??? : use pContext->GetOverrideRetPtr() or not? */ - - prev_res = MRES_IGNORED; - while ((iter = static_cast(pContext->GetNext()))) - { - cur_res = MRES_IGNORED; - plugin_ret = iter->Call(params...); - prev_res = cur_res; - - if (cur_res > status) - status = cur_res; - - if (cur_res >= MRES_OVERRIDE) - *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; - } - - const void* repPtr = (status >= MRES_OVERRIDE) ? pContext->GetOverrideRetPtr() : - pContext->GetOrigRetPtr(); - - g_SHPtr->EndContext(pContext); - - return *reinterpret_cast(repPtr); - } - - // Implementation for void return type only - template::value, void>::type* = nullptr> - void Func(Params... params) - { - /* 1) Set up calls */ - void *ourvfnptr = reinterpret_cast(*reinterpret_cast(reinterpret_cast(this) + msMFI_.vtbloffs) + msMFI_.vtblindex); - void *vfnptr_origentry; - - META_RES status = MRES_IGNORED; - META_RES prev_res; - META_RES cur_res; - - IMyDelegate *iter = nullptr; - - IHookContext *pContext = g_SHPtr->SetupHookLoop(msHI_, - ourvfnptr, - reinterpret_cast(this), - &vfnptr_origentry, - &status, - &prev_res, - &cur_res, - nullptr, - nullptr); - - prev_res = MRES_IGNORED; - while ((iter = static_cast(pContext->GetNext()))) - { - cur_res = MRES_IGNORED; - iter->Call(params...); - prev_res = cur_res; - - if (cur_res > status) - status = cur_res; - } - - if (status != MRES_SUPERCEDE && pContext->ShouldCallOrig()) - { - void (EmptyClass::*mfp)(Params...); - - reinterpret_cast(&mfp)[0] = vfnptr_origentry; - reinterpret_cast(&mfp)[1] = 0; - - (reinterpret_cast(this)->*mfp)(params...); - } - - prev_res = MRES_IGNORED; - while ( (iter = static_cast(pContext->GetNext()))) - { - cur_res = MRES_IGNORED; - - iter->Call(params...); - prev_res = cur_res; - - if (cur_res > status) - status = cur_res; - } - - g_SHPtr->EndContext(pContext); - } - - int HookManPubFunc(bool store, IHookManagerInfo *hi) - { - /* Verify interface version */ - if (g_SHPtr->GetIfaceVersion() != SH_IFACE_VERSION) - return 1; - - if (g_SHPtr->GetImplVersion() < SH_IMPL_VERSION) - return 1; - - if (store) - msHI_ = hi; - - if (hi) - { - MemFuncInfo mfi = {true, -1, 0, 0}; - GetFuncInfo(this, &ThisType::Func, mfi); - - hi->SetInfo(SH_HOOKMAN_VERSION, - msMFI_.vtbloffs, - msMFI_.vtblindex, - &msProto_, - reinterpret_cast(reinterpret_cast(this) + mfi.vtbloffs)[mfi.vtblindex]); - } - - return 0; - } - - typename ManualHookHandler::ECMFP GetRecallMFP(EmptyClass *thisptr) - { - union - { - typename ManualHookHandler::ECMFP mfp; - struct - { - void *addr; - intptr_t adjustor; - } s; - } u; - - u.s.addr = (*reinterpret_cast(reinterpret_cast(thisptr) + msMFI_.vtbloffs))[msMFI_.vtblindex]; - u.s.adjustor = 0; - - return u.mfp; - } - - typename ManualHookHandler::CallEC Call(void *ptr) - { - typename ManualHookHandler::ECMFP mfp; - void *vfnptr = reinterpret_cast(*reinterpret_cast((reinterpret_cast(ptr) + msMFI_.thisptroffs + msMFI_.vtbloffs) ) + msMFI_.vtblindex); - - /* patch mfp */ - *reinterpret_cast(&mfp) = *reinterpret_cast(vfnptr); - - if (sizeof(mfp) == 2*sizeof(void*)) /* gcc */ - *(reinterpret_cast(&mfp) + 1) = 0; - - return ManualHookHandler::CallEC(reinterpret_cast(ptr), mfp, vfnptr, g_SHPtr); - } - - // Implementation for any return type but void - template::value, void>::type* = nullptr> - void SetOverrideResult(ISourceHook *shptr, U value) - { - OverrideFunctor overrideFunc = SourceHook::SetOverrideResult(); - overrideFunc(shptr, value); - } - -private: - MemFuncInfo msMFI_; // ms_MFI - IHookManagerInfo *msHI_; // ms_HI - - CProtoInfo msProto_; // ms_Proto -}; - -} // SourceHook - #endif // The pope is dead. -> :( diff --git a/core/sourcehook/sourcehook_handlers.h b/core/sourcehook/sourcehook_handlers.h new file mode 100644 index 00000000..e6222ee2 --- /dev/null +++ b/core/sourcehook/sourcehook_handlers.h @@ -0,0 +1,577 @@ +/* ======== SourceHook ======== +* Copyright (C) 2004-2023 Metamod:Source Development Team +* No warranties of any kind +* +* License: zlib/libpng +* +* Author(s): mixern6 (SVRL_) +* ============================ +*/ + +/** +* @file sourcehook_handlers.h +* @brief Contains the public SourceHook API +*/ + +#ifndef __SOURCEHOOK_HANDLERS_H__ +#define __SOURCEHOOK_HANDLERS_H__ + +#include "sourcehook.h" +#include + +namespace SourceHook +{ +class ISourceHook; +typedef int Plugin; +} + +extern SourceHook::ISourceHook *g_SHPtr; +extern SourceHook::Plugin g_PLID; + +namespace SourceHook +{ + +// Get type info for type T +template +inline void TypeInfo(PassInfo& passInfo) +{ + passInfo.size = sizeof(T); + passInfo.type = GetPassInfo::type; + passInfo.flags = GetPassInfo::flags; +} + +// Get type info for type T +template<> +inline void TypeInfo(PassInfo& passInfo) +{ + passInfo.size = 1; + passInfo.type = 0; + passInfo.flags = 0; +} + +template +class PassInfoInitializer +{ +public: + constexpr PassInfoInitializer() + { + InitializePassInfo(); + } + + const PassInfo *GetParamsPassInfo() const + { + return params_; + } + + const PassInfo::V2Info *GetParamsPassInfoV2() const + { + return paramsV2_; + } + + const size_t GetParamsPassInfoSize() const + { + return sizeof...(ArgsType); + } + +private: + template + inline typename std::enable_if::type InitializePassInfo() + { + TypeInfo(params_[index]); + InitializePassInfo(); + } + + template + inline void InitializePassInfo() + { + TypeInfo(params_[index]); + } + + PassInfo params_[sizeof...(ArgsType)]; + PassInfo::V2Info paramsV2_[sizeof...(ArgsType)]; +}; + +// For zero arguments +template<> +class PassInfoInitializer<> +{ +public: + constexpr PassInfoInitializer() + { + } + + const PassInfo *GetParamsPassInfo() const + { + return nullptr; + } + + const PassInfo::V2Info *GetParamsPassInfoV2() const + { + return nullptr; + } + + const size_t GetParamsPassInfoSize() const + { + return 0; + } +}; + +template +struct ReturnTypeInfo +{ + static const size_t size() + { + return sizeof(T); + } + + static const int type() + { + return GetPassInfo::type; + } + + static const unsigned int flags() + { + return GetPassInfo::flags; + } +}; + +template <> +struct ReturnTypeInfo +{ + static const size_t size() + { + return 0; + } + + static const int type() + { + return 0; + } + + static const unsigned int flags() + { + return 0; + } +}; + +template +class CHookManagerMemberFuncHandler : public IHookManagerMemberFunc +{ +public: + typedef int (T::*HookManagerMemberFunc)(bool store, IHookManagerInfo *hi); + + explicit CHookManagerMemberFuncHandler(T* funcHandler, HookManagerMemberFunc func) + : funcHandler_(funcHandler) + , func_(func) + { + } + + virtual ~CHookManagerMemberFuncHandler() + { + } + +private: + virtual int Call(bool store, IHookManagerInfo *hi) const override + { + return (funcHandler_->*func_)(store, hi); + } + +private: + T* funcHandler_; + HookManagerMemberFunc func_; +}; + +template +class CProtoInfo : public IProtoInfo +{ +public: + constexpr CProtoInfo() + : retPassInfo(ReturnTypeInfo::size(), + ReturnTypeInfo::type(), + ReturnTypeInfo::flags()) + { + } + + virtual ~CProtoInfo() override + { + } + + virtual size_t GetNumOfParams() const override + { + return paramsPassInfo.GetParamsPassInfoSize(); + } + + virtual const PassInfo &GetRetPassInfo() const override + { + return retPassInfo; + } + + virtual const PassInfo *GetParamsPassInfo() const override + { + return paramsPassInfo.GetParamsPassInfo(); + } + + virtual int GetConvention() const override + { + return 0; + } + + // version of the ProtoInfo structure. + virtual IProtoInfo::ProtoInfoVersion GetVersion() const override + { + return ProtoInfoVersion::ProtoInfoVersion2; + } + + virtual const PassInfo::V2Info &GetRetPassInfo2() const override + { + return retPassInfo2; + } + + virtual const PassInfo::V2Info *GetParamsPassInfo2() const override + { + return paramsPassInfo.GetParamsPassInfoV2(); + } + +private: + int numOfParams; //!< number of parameters + PassInfo retPassInfo; //!< PassInfo for the return value. size=0 -> no retval + PassInfo::V2Info retPassInfo2; //!< Version2 only + PassInfoInitializer paramsPassInfo; //!< PassInfos for the parameters +}; + +template +class ManualHookHandler : public CHookManagerMemberFuncHandler> +{ +public: + typedef ManualHookHandler ThisType; + typedef fastdelegate::FastDelegate FD; + typedef ReturnType(EmptyClass::*ECMFP)(Params...); + typedef ExecutableClassN CallEC; + + template + struct HookedFuncType + { + typedef ReturnType (T::*HookFunc)(Params...); + }; + + constexpr ManualHookHandler() + : CHookManagerMemberFuncHandler(this, &ThisType::HookManPubFunc) + , msMFI_{false, 0, 0, 0} + , msHI_(nullptr) + { + } + + ManualHookHandler(const ManualHookHandler&) = delete; + + ManualHookHandler(ManualHookHandler&&) = delete; + + ManualHookHandler& operator=(const ManualHookHandler&) = delete; + + ManualHookHandler& operator=(ManualHookHandler&&) = delete; + + virtual ~ManualHookHandler() + { + g_SHPtr->RemoveHookManager(g_PLID, this); + } + + void Reconfigure(int vtblindex, int vtbloffs = 0, int thisptroffs = 0) + { + g_SHPtr->RemoveHookManager(g_PLID, this); + msMFI_.thisptroffs = thisptroffs; + msMFI_.vtblindex = vtblindex; + msMFI_.vtbloffs = vtbloffs; + } + + template + int Add(void *iface, T* callbackInstPtr, typename HookedFuncType::HookFunc callbackFuncPtr, bool post = true, ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) + { + typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); + typename ThisType::CMyDelegateImpl* tmp = new typename ThisType::CMyDelegateImpl(handler); // TODO use unique_ptr here + + return g_SHPtr->AddHook(g_PLID, mode, iface, 0, this, tmp, post); + } + + bool Remove(int hookid) + { + return g_SHPtr->RemoveHookByID(hookid); + } + + bool Pause(int hookid) + { + return g_SHPtr->PauseHookByID(hookid); + } + + bool Unpause(int hookid) + { + return g_SHPtr->UnpauseHookByID(hookid); + } + + // For void return type only + template::value, void>::type* = nullptr> + void Recall(META_RES result, Params... newparams) + { + g_SHPtr->SetRes(result); + g_SHPtr->DoRecall(); + SourceHook::EmptyClass *thisptr = reinterpret_cast(g_SHPtr->GetIfacePtr()); + (thisptr->*(GetRecallMFP(thisptr)))(newparams...); + g_SHPtr->SetRes(MRES_SUPERCEDE); + } + + // For any return type but void + template::value, void>::type* = nullptr> + U Recall(META_RES result, U value, Params... newparams) + { + g_SHPtr->SetRes(result); + g_SHPtr->DoRecall(); + if ((result) >= MRES_OVERRIDE) + { + /* see RETURN_META_VALUE_NEWPARAMS */ + SetOverrideResult(g_SHPtr, value); + } + EmptyClass *thisptr = reinterpret_cast(g_SHPtr->GetIfacePtr()); + g_SHPtr->SetRes(MRES_SUPERCEDE); + return (thisptr->*(GetRecallMFP(thisptr)))(newparams...); + } + +private: + struct IMyDelegate : ISHDelegate + { + virtual ReturnType Call(Params... params) = 0; + }; + + struct CMyDelegateImpl : public IMyDelegate + { + FD m_Deleg; + CMyDelegateImpl(FD deleg) + : m_Deleg(deleg) + { + } + + virtual ReturnType Call(Params... params) override + { + return m_Deleg(params...); + } + + virtual void DeleteThis() override + { + delete this; + } + + virtual bool IsEqual(ISHDelegate *pOtherDeleg) override + { + return m_Deleg == static_cast(pOtherDeleg)->m_Deleg; + } + }; + + // Implementation for any return type but void + template::value, void>::type* = nullptr> + U Func(Params... params) + { + /* 1) Set up calls */ + void *ourvfnptr = reinterpret_cast(*reinterpret_cast(reinterpret_cast(this) + msMFI_.vtbloffs) + msMFI_.vtblindex); + void *vfnptr_origentry; + + META_RES status = MRES_IGNORED; + META_RES prev_res; + META_RES cur_res; + + typedef typename ReferenceCarrier::type my_rettype; + my_rettype orig_ret; + my_rettype override_ret; + my_rettype plugin_ret; + IMyDelegate *iter = nullptr; + + IHookContext *pContext = g_SHPtr->SetupHookLoop(msHI_, + ourvfnptr, + reinterpret_cast(this), + &vfnptr_origentry, + &status, + &prev_res, + &cur_res, + &orig_ret, + &override_ret); + + prev_res = MRES_IGNORED; + + while ((iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + plugin_ret = iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + + if (cur_res >= MRES_OVERRIDE) + *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; + } + + if (status != MRES_SUPERCEDE && pContext->ShouldCallOrig()) + { + ReturnType (EmptyClass::*mfp)(Params...); + SH_SETUP_MFP(mfp); + + orig_ret = (reinterpret_cast(this)->*mfp)(params...); + } + else + orig_ret = override_ret; /* :TODO: ??? : use pContext->GetOverrideRetPtr() or not? */ + + prev_res = MRES_IGNORED; + while ((iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + plugin_ret = iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + + if (cur_res >= MRES_OVERRIDE) + *reinterpret_cast(pContext->GetOverrideRetPtr()) = plugin_ret; + } + + const void* repPtr = (status >= MRES_OVERRIDE) ? pContext->GetOverrideRetPtr() : + pContext->GetOrigRetPtr(); + + g_SHPtr->EndContext(pContext); + + return *reinterpret_cast(repPtr); + } + + // Implementation for void return type only + template::value, void>::type* = nullptr> + void Func(Params... params) + { + /* 1) Set up calls */ + void *ourvfnptr = reinterpret_cast(*reinterpret_cast(reinterpret_cast(this) + msMFI_.vtbloffs) + msMFI_.vtblindex); + void *vfnptr_origentry; + + META_RES status = MRES_IGNORED; + META_RES prev_res; + META_RES cur_res; + + IMyDelegate *iter = nullptr; + + IHookContext *pContext = g_SHPtr->SetupHookLoop(msHI_, + ourvfnptr, + reinterpret_cast(this), + &vfnptr_origentry, + &status, + &prev_res, + &cur_res, + nullptr, + nullptr); + + prev_res = MRES_IGNORED; + while ((iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + } + + if (status != MRES_SUPERCEDE && pContext->ShouldCallOrig()) + { + void (EmptyClass::*mfp)(Params...); + + reinterpret_cast(&mfp)[0] = vfnptr_origentry; + reinterpret_cast(&mfp)[1] = 0; + + (reinterpret_cast(this)->*mfp)(params...); + } + + prev_res = MRES_IGNORED; + while ( (iter = static_cast(pContext->GetNext()))) + { + cur_res = MRES_IGNORED; + + iter->Call(params...); + prev_res = cur_res; + + if (cur_res > status) + status = cur_res; + } + + g_SHPtr->EndContext(pContext); + } + + int HookManPubFunc(bool store, IHookManagerInfo *hi) + { + /* Verify interface version */ + if (g_SHPtr->GetIfaceVersion() != SH_IFACE_VERSION) + return 1; + + if (g_SHPtr->GetImplVersion() < SH_IMPL_VERSION) + return 1; + + if (store) + msHI_ = hi; + + if (hi) + { + MemFuncInfo mfi = {true, -1, 0, 0}; + GetFuncInfo(this, &ThisType::Func, mfi); + + hi->SetInfo(SH_HOOKMAN_VERSION, + msMFI_.vtbloffs, + msMFI_.vtblindex, + &msProto_, + reinterpret_cast(reinterpret_cast(this) + mfi.vtbloffs)[mfi.vtblindex]); + } + + return 0; + } + + typename ManualHookHandler::ECMFP GetRecallMFP(EmptyClass *thisptr) + { + union + { + typename ManualHookHandler::ECMFP mfp; + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + + u.s.addr = (*reinterpret_cast(reinterpret_cast(thisptr) + msMFI_.vtbloffs))[msMFI_.vtblindex]; + u.s.adjustor = 0; + + return u.mfp; + } + + typename ManualHookHandler::CallEC Call(void *ptr) + { + typename ManualHookHandler::ECMFP mfp; + void *vfnptr = reinterpret_cast(*reinterpret_cast((reinterpret_cast(ptr) + msMFI_.thisptroffs + msMFI_.vtbloffs) ) + msMFI_.vtblindex); + + /* patch mfp */ + *reinterpret_cast(&mfp) = *reinterpret_cast(vfnptr); + + if (sizeof(mfp) == 2*sizeof(void*)) /* gcc */ + *(reinterpret_cast(&mfp) + 1) = 0; + + return ManualHookHandler::CallEC(reinterpret_cast(ptr), mfp, vfnptr, g_SHPtr); + } + + // Implementation for any return type but void + template::value, void>::type* = nullptr> + void SetOverrideResult(ISourceHook *shptr, U value) + { + OverrideFunctor overrideFunc = SourceHook::SetOverrideResult(); + overrideFunc(shptr, value); + } + +private: + MemFuncInfo msMFI_; // ms_MFI + IHookManagerInfo *msHI_; // ms_HI + + CProtoInfo msProto_; // ms_Proto +}; + +} // SourceHook + +#endif // __SOURCEHOOK_HANDLERS_H__ From 0176ecba3d0d19d576521cde42a9a6cef5e45ec6 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 8 Dec 2023 00:29:59 +0300 Subject: [PATCH 5/8] Add RAII source hook delegate handler --- core/sourcehook/sourcehook.cpp | 28 ++------ core/sourcehook/sourcehook.h | 64 ++++++++++++++++++- core/sourcehook/sourcehook_handlers.h | 10 +-- core/sourcehook/sourcehook_impl.h | 10 +-- core/sourcehook/sourcehook_impl_chook.h | 12 ++-- .../sourcehook/sourcehook_impl_chookidman.cpp | 6 +- core/sourcehook/sourcehook_impl_chookidman.h | 6 +- core/sourcehook/sourcehook_impl_ciface.h | 10 --- 8 files changed, 86 insertions(+), 60 deletions(-) diff --git a/core/sourcehook/sourcehook.cpp b/core/sourcehook/sourcehook.cpp index fc96ee91..41c2b9e5 100644 --- a/core/sourcehook/sourcehook.cpp +++ b/core/sourcehook/sourcehook.cpp @@ -98,17 +98,13 @@ namespace SourceHook int CSourceHookImpl::AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) { - return AddHook(plug, mode, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); - } - - int CSourceHookImpl::AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, - ISHDelegate *handler, bool post) - { - return AddHook(plug, mode, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); + HookManagerPubFuncHandler pubFunc(myHookMan); + SHDelegateHandler shDelegate(handler); + return AddHook(plug, mode, iface, thisptr_offs, pubFunc, shDelegate, post); } int CSourceHookImpl::AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, - ISHDelegate *handler, bool post) + const SHDelegateHandler &handler, bool post) { if (mode != Hook_Normal && mode != Hook_VP && mode != Hook_DVP) return 0; @@ -184,15 +180,9 @@ namespace SourceHook bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) - { - return RemoveHook(plug, iface, thisptr_offs, HookManagerPubFuncHandler(myHookMan), handler, post); - } - - bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, - ISHDelegate *handler, bool post) { // Get info about hook manager and compute adjustediface - CHookManager tmpHookMan(plug, myHookMan); + CHookManager tmpHookMan(plug, HookManagerPubFuncHandler(myHookMan)); void *adjustediface = reinterpret_cast(reinterpret_cast(iface)+thisptr_offs); @@ -242,8 +232,6 @@ namespace SourceHook if (hook_iter == hooks.end()) return false; - hook_iter->GetHandler()->DeleteThis(); - // Iterator movage! List::iterator oldhookiter = hook_iter; hook_iter = hooks.erase(hook_iter); @@ -440,12 +428,6 @@ namespace SourceHook RemoveHookManager(plug, HookManagerPubFuncHandler(pubFunc)); } - void CSourceHookImpl::RemoveHookManager(Plugin plug, IHookManagerMemberFunc* pubFunc) - { - // Find the hook manager - RemoveHookManager(plug, HookManagerPubFuncHandler(pubFunc)); - } - void CSourceHookImpl::RemoveHookManager(Plugin plug, const HookManagerPubFuncHandler &pubFunc) { // Find the hook manager diff --git a/core/sourcehook/sourcehook.h b/core/sourcehook/sourcehook.h index a6511aeb..f015b9c4 100644 --- a/core/sourcehook/sourcehook.h +++ b/core/sourcehook/sourcehook.h @@ -16,6 +16,8 @@ #ifndef __SOURCEHOOK_H__ #define __SOURCEHOOK_H__ +#include + // Interface revisions: // 1 - Initial revision // 2 - Changed to virtual functions for iterators and all queries @@ -437,13 +439,69 @@ namespace SourceHook IHookManagerMemberFunc* memberFunc_; }; + class SHDelegateHandler; + class ISHDelegate { public: virtual bool IsEqual(ISHDelegate *pOtherDeleg) = 0; // pOtherDeleg is from the same plugin and hookman + private: + friend class SHDelegateHandler; virtual void DeleteThis() = 0; }; + // ISHDelegate doesn't provide a virtual destructor, use this wrapper to handle it + class SHDelegateHandler final + { + public: + template + static SHDelegateHandler Make(Params&&... params) + { + ISHDelegate* delegate = new U(std::forward(params)...); + return SHDelegateHandler(delegate); + } + + SHDelegateHandler() + : ptr_(nullptr, &SHDelegateHandler::DeleteDelegate) + { + } + + explicit SHDelegateHandler(ISHDelegate* delegate) + : ptr_(delegate, &SHDelegateHandler::DeleteDelegate) + { + } + + void reset() + { + ptr_.reset(); + } + + bool operator==(const SHDelegateHandler &other) const + { + return ptr_.get() == other.get(); + } + + ISHDelegate* operator->() const + { + return ptr_.get(); + } + + ISHDelegate* get() const + { + return ptr_.get(); + } + + private: + static void DeleteDelegate(ISHDelegate* delegate) + { + if(delegate) + delegate->DeleteThis(); + } + + private: + std::shared_ptr ptr_; + }; + struct IHookManagerInfo { virtual void SetInfo(int hookman_version, int vtbloffs, int vtblidx, @@ -701,8 +759,8 @@ namespace SourceHook * @param post Set to true if you want a post handler */ - virtual int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, - ISHDelegate *handler, bool post) = 0; + virtual int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, + const SHDelegateHandler &handler, bool post) = 0; /** * @brief Remove a hook manager. Auto-removes all hooks attached to it from plugin plug. @@ -711,7 +769,7 @@ namespace SourceHook * @param pubFunc The hook manager's info function */ - virtual void RemoveHookManager(Plugin plug, IHookManagerMemberFunc* pubFunc) = 0; + virtual void RemoveHookManager(Plugin plug, const HookManagerPubFuncHandler &pubFunc) = 0; }; diff --git a/core/sourcehook/sourcehook_handlers.h b/core/sourcehook/sourcehook_handlers.h index e6222ee2..8d11711b 100644 --- a/core/sourcehook/sourcehook_handlers.h +++ b/core/sourcehook/sourcehook_handlers.h @@ -271,12 +271,12 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerRemoveHookManager(g_PLID, this); + g_SHPtr->RemoveHookManager(g_PLID, HookManagerPubFuncHandler(this)); } void Reconfigure(int vtblindex, int vtbloffs = 0, int thisptroffs = 0) { - g_SHPtr->RemoveHookManager(g_PLID, this); + g_SHPtr->RemoveHookManager(g_PLID, HookManagerPubFuncHandler(this)); msMFI_.thisptroffs = thisptroffs; msMFI_.vtblindex = vtblindex; msMFI_.vtbloffs = vtbloffs; @@ -286,9 +286,9 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler::HookFunc callbackFuncPtr, bool post = true, ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) { typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); - typename ThisType::CMyDelegateImpl* tmp = new typename ThisType::CMyDelegateImpl(handler); // TODO use unique_ptr here - - return g_SHPtr->AddHook(g_PLID, mode, iface, 0, this, tmp, post); + HookManagerPubFuncHandler pubFunc(this); + SHDelegateHandler shDelegate = SHDelegateHandler::Make(handler); + return g_SHPtr->AddHook(g_PLID, mode, iface, 0, pubFunc, shDelegate, post); } bool Remove(int hookid) diff --git a/core/sourcehook/sourcehook_impl.h b/core/sourcehook/sourcehook_impl.h index fb3204fc..c73e3c58 100644 --- a/core/sourcehook/sourcehook_impl.h +++ b/core/sourcehook/sourcehook_impl.h @@ -322,18 +322,12 @@ namespace SourceHook int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post); - int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, IHookManagerMemberFunc* myHookMan, - ISHDelegate *handler, bool post); - int AddHook(Plugin plug, AddHookMode mode, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, - ISHDelegate *handler, bool post); + const SHDelegateHandler &handler, bool post); bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post); - bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, const HookManagerPubFuncHandler &myHookMan, - ISHDelegate *handler, bool post); - bool RemoveHookByID(int hookid); bool PauseHookByID(int hookid); @@ -364,8 +358,6 @@ namespace SourceHook void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc); - void RemoveHookManager(Plugin plug, IHookManagerMemberFunc* pubFunc); - void RemoveHookManager(Plugin plug, const HookManagerPubFuncHandler &pubFunc); void SetIgnoreHooks(void *vfnptr); diff --git a/core/sourcehook/sourcehook_impl_chook.h b/core/sourcehook/sourcehook_impl_chook.h index 2050ed79..dbd9e9ad 100644 --- a/core/sourcehook/sourcehook_impl_chook.h +++ b/core/sourcehook/sourcehook_impl_chook.h @@ -20,7 +20,7 @@ namespace SourceHook // *** Data *** Plugin m_OwnerPlugin; int m_ThisPointerOffset; - ISHDelegate *m_pHandler; + SHDelegateHandler m_pHandler; int m_HookID; bool m_Paused; public: @@ -30,9 +30,9 @@ namespace SourceHook { Plugin m_OwnerPlugin; int m_ThisPointerOffset; - ISHDelegate *m_pHandler; + SHDelegateHandler m_pHandler; - Descriptor(Plugin ownerPlugin, int thisPtrOffset, ISHDelegate *pHandler) + Descriptor(Plugin ownerPlugin, int thisPtrOffset, const SHDelegateHandler &pHandler) : m_OwnerPlugin(ownerPlugin), m_ThisPointerOffset(thisPtrOffset), m_pHandler(pHandler) { @@ -40,7 +40,7 @@ namespace SourceHook }; // *** Interface *** - inline CHook(Plugin ownerPlugin, int thisPtrOffset, ISHDelegate *pHandler, int hookid, bool paused=false); + inline CHook(Plugin ownerPlugin, int thisPtrOffset, const SHDelegateHandler &pHandler, int hookid, bool paused=false); inline bool operator==(const Descriptor &other) const; inline bool operator==(int hookid) const; inline Plugin GetOwnerPlugin() const; @@ -52,7 +52,7 @@ namespace SourceHook }; // *** Implementation *** - inline CHook::CHook(Plugin ownerPlugin, int thisPtrOffset, ISHDelegate *pHandler, int hookid, bool paused) + inline CHook::CHook(Plugin ownerPlugin, int thisPtrOffset, const SHDelegateHandler &pHandler, int hookid, bool paused) : m_OwnerPlugin(ownerPlugin), m_ThisPointerOffset(thisPtrOffset), m_pHandler(pHandler), m_HookID(hookid), m_Paused(paused) { @@ -82,7 +82,7 @@ namespace SourceHook inline ISHDelegate *CHook::GetHandler() const { - return m_pHandler; + return m_pHandler.get(); } inline void CHook::SetPaused(bool value) diff --git a/core/sourcehook/sourcehook_impl_chookidman.cpp b/core/sourcehook/sourcehook_impl_chookidman.cpp index 29a3d09a..5788fc1b 100644 --- a/core/sourcehook/sourcehook_impl_chookidman.cpp +++ b/core/sourcehook/sourcehook_impl_chookidman.cpp @@ -20,7 +20,7 @@ namespace SourceHook } int CHookIDManager::New(const CProto &proto, int vtbl_offs, int vtbl_idx, void *vfnptr, - void *adjustediface, Plugin plug, int thisptr_offs, ISHDelegate *handler, bool post) + void *adjustediface, Plugin plug, int thisptr_offs, const SHDelegateHandler &handler, bool post) { Entry tmp(proto, vtbl_offs, vtbl_idx, vfnptr, adjustediface, plug, thisptr_offs, handler, post); @@ -44,6 +44,7 @@ namespace SourceHook if (realid < 0 || realid >= static_cast(m_Entries.size()) || m_Entries[realid].isfree) return false; + m_Entries[realid].handler.reset(); m_Entries[realid].isfree = true; // :TODO: remove free ids from back sometimes ?? @@ -103,7 +104,10 @@ namespace SourceHook for (size_t i = 0; i < cursize; ++i) { if (!m_Entries[i].isfree && m_Entries[i].vfnptr == vfnptr) + { + m_Entries[i].handler.reset(); m_Entries[i].isfree = true; + } } } diff --git a/core/sourcehook/sourcehook_impl_chookidman.h b/core/sourcehook/sourcehook_impl_chookidman.h index 03f0bc59..5515773a 100644 --- a/core/sourcehook/sourcehook_impl_chookidman.h +++ b/core/sourcehook/sourcehook_impl_chookidman.h @@ -40,11 +40,11 @@ namespace SourceHook // hook Plugin plug; int thisptr_offs; - ISHDelegate *handler; + SHDelegateHandler handler; bool post; Entry(const CProto &pprt, int pvo, int pvi, void *pvp, void *pai, Plugin pplug, int pto, - ISHDelegate *ph, bool ppost) + const SHDelegateHandler &ph, bool ppost) : isfree(false), proto(pprt), vtbl_offs(pvo), vtbl_idx(pvi), vfnptr(pvp), adjustediface(pai), plug(pplug), thisptr_offs(pto), handler(ph), post(ppost) { @@ -60,7 +60,7 @@ namespace SourceHook public: CHookIDManager(); int New(const CProto &proto, int vtbl_offs, int vtbl_idx, void *vfnptr, void *adjustediface, - Plugin plug, int thisptr_offs, ISHDelegate *handler, bool post); + Plugin plug, int thisptr_offs, const SHDelegateHandler &handler, bool post); bool Remove(int hookid); const Entry * QueryHook(int hookid); diff --git a/core/sourcehook/sourcehook_impl_ciface.h b/core/sourcehook/sourcehook_impl_ciface.h index 978c16a0..9bb0f50b 100644 --- a/core/sourcehook/sourcehook_impl_ciface.h +++ b/core/sourcehook/sourcehook_impl_ciface.h @@ -48,16 +48,6 @@ namespace SourceHook inline CIface::~CIface() { - // Before getting deleted, delete all remaining hook handlers - for (List::iterator iter = m_PreHooks.begin(); iter != m_PreHooks.end(); ++iter) - { - iter->GetHandler()->DeleteThis(); - } - - for (List::iterator iter = m_PostHooks.begin(); iter != m_PostHooks.end(); ++iter) - { - iter->GetHandler()->DeleteThis(); - } } inline bool CIface::operator==(const Descriptor &other) From b51e5f99780ee5f66991bb2ac456e1203e5109a8 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 8 Dec 2023 00:37:38 +0300 Subject: [PATCH 6/8] Add entry vector optimization --- .../sourcehook/sourcehook_impl_chookidman.cpp | 45 +++++++++++++------ core/sourcehook/sourcehook_impl_chookidman.h | 18 ++++++-- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/core/sourcehook/sourcehook_impl_chookidman.cpp b/core/sourcehook/sourcehook_impl_chookidman.cpp index 5788fc1b..e16ecf42 100644 --- a/core/sourcehook/sourcehook_impl_chookidman.cpp +++ b/core/sourcehook/sourcehook_impl_chookidman.cpp @@ -19,6 +19,25 @@ namespace SourceHook { } + void CHookIDManager::OptimizeEntryVector() + { + const size_t curSize = m_Entries.size(); + size_t newSize = curSize; + for(size_t i = m_Entries.size(); i != 0; --i) + { + const size_t entryIndex = i - 1; + if (!m_Entries[entryIndex].IsFree()) + break; + + newSize = entryIndex; + } + + if(curSize != newSize) + { + m_Entries.resize(newSize); + } + } + int CHookIDManager::New(const CProto &proto, int vtbl_offs, int vtbl_idx, void *vfnptr, void *adjustediface, Plugin plug, int thisptr_offs, const SHDelegateHandler &handler, bool post) { @@ -27,7 +46,7 @@ namespace SourceHook size_t cursize = m_Entries.size(); for (size_t i = 0; i < cursize; ++i) { - if (m_Entries[i].isfree) + if (m_Entries[i].IsFree()) { m_Entries[i] = tmp; return static_cast(i) + 1; @@ -41,21 +60,20 @@ namespace SourceHook bool CHookIDManager::Remove(int hookid) { int realid = hookid - 1; - if (realid < 0 || realid >= static_cast(m_Entries.size()) || m_Entries[realid].isfree) + if (realid < 0 || realid >= static_cast(m_Entries.size()) || m_Entries[realid].IsFree()) return false; - m_Entries[realid].handler.reset(); - m_Entries[realid].isfree = true; + m_Entries[realid].Reset(); - // :TODO: remove free ids from back sometimes ?? + OptimizeEntryVector(); return true; } - const CHookIDManager::Entry * CHookIDManager::QueryHook(int hookid) + const CHookIDManager::Entry *CHookIDManager::QueryHook(int hookid) { int realid = hookid - 1; - if (realid < 0 || realid >= static_cast(m_Entries.size()) || m_Entries[realid].isfree) + if (realid < 0 || realid >= static_cast(m_Entries.size()) || m_Entries[realid].IsFree()) return NULL; return &m_Entries[realid]; @@ -68,7 +86,7 @@ namespace SourceHook size_t cursize = m_Entries.size(); for (size_t i = 0; i < cursize; ++i) { - if (!m_Entries[i].isfree && m_Entries[i].proto == proto && m_Entries[i].vtbl_offs == vtbl_offs && + if (!m_Entries[i].IsFree() && m_Entries[i].proto == proto && m_Entries[i].vtbl_offs == vtbl_offs && m_Entries[i].vtbl_idx == vtbl_idx && m_Entries[i].adjustediface == adjustediface && m_Entries[i].plug == plug && m_Entries[i].thisptr_offs == thisptr_offs && m_Entries[i].handler->IsEqual(handler) && m_Entries[i].post == post) { @@ -82,7 +100,7 @@ namespace SourceHook size_t cursize = m_Entries.size(); for (size_t i = 0; i < cursize; ++i) { - if (!m_Entries[i].isfree) + if (!m_Entries[i].IsFree()) output.push_back(static_cast(i) + 1); } } @@ -92,7 +110,7 @@ namespace SourceHook size_t cursize = m_Entries.size(); for (size_t i = 0; i < cursize; ++i) { - if (!m_Entries[i].isfree && m_Entries[i].plug == plug) + if (!m_Entries[i].IsFree() && m_Entries[i].plug == plug) output.push_back(static_cast(i) + 1); } } @@ -103,12 +121,13 @@ namespace SourceHook size_t cursize = m_Entries.size(); for (size_t i = 0; i < cursize; ++i) { - if (!m_Entries[i].isfree && m_Entries[i].vfnptr == vfnptr) + if (!m_Entries[i].IsFree() && m_Entries[i].vfnptr == vfnptr) { - m_Entries[i].handler.reset(); - m_Entries[i].isfree = true; + m_Entries[i].Reset(); } } + + OptimizeEntryVector(); } } diff --git a/core/sourcehook/sourcehook_impl_chookidman.h b/core/sourcehook/sourcehook_impl_chookidman.h index 5515773a..76a9e6e6 100644 --- a/core/sourcehook/sourcehook_impl_chookidman.h +++ b/core/sourcehook/sourcehook_impl_chookidman.h @@ -24,8 +24,6 @@ namespace SourceHook public: struct Entry { - bool isfree; - // hookman info CProto proto; int vtbl_offs; @@ -43,9 +41,19 @@ namespace SourceHook SHDelegateHandler handler; bool post; + bool IsFree() const + { + return handler.get() == nullptr; + } + + void Reset() + { + handler.reset(); + } + Entry(const CProto &pprt, int pvo, int pvi, void *pvp, void *pai, Plugin pplug, int pto, const SHDelegateHandler &ph, bool ppost) - : isfree(false), proto(pprt), vtbl_offs(pvo), vtbl_idx(pvi), vfnptr(pvp), + : proto(pprt), vtbl_offs(pvo), vtbl_idx(pvi), vfnptr(pvp), adjustediface(pai), plug(pplug), thisptr_offs(pto), handler(ph), post(ppost) { } @@ -57,6 +65,10 @@ namespace SourceHook // Internally, hookid 1 is stored as m_Entries[0] CVector m_Entries; + + // Remove free entries from back + void OptimizeEntryVector(); + public: CHookIDManager(); int New(const CProto &proto, int vtbl_offs, int vtbl_idx, void *vfnptr, void *adjustediface, From 2af7f6a2044785728f1ea4d29d9c1dd01c801807 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 8 Dec 2023 04:20:36 +0300 Subject: [PATCH 7/8] Add auxiliary functions to manual hook handler --- core/sourcehook/sourcehook_handlers.h | 35 ++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/core/sourcehook/sourcehook_handlers.h b/core/sourcehook/sourcehook_handlers.h index 8d11711b..73d9776f 100644 --- a/core/sourcehook/sourcehook_handlers.h +++ b/core/sourcehook/sourcehook_handlers.h @@ -283,7 +283,11 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler - int Add(void *iface, T* callbackInstPtr, typename HookedFuncType::HookFunc callbackFuncPtr, bool post = true, ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) + [[nodiscard]] int Add(void *iface, + T* callbackInstPtr, + typename HookedFuncType::HookFunc callbackFuncPtr, + bool post = true, + ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) { typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); HookManagerPubFuncHandler pubFunc(this); @@ -291,21 +295,22 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerAddHook(g_PLID, mode, iface, 0, pubFunc, shDelegate, post); } - bool Remove(int hookid) + inline bool Remove(int hookid) { return g_SHPtr->RemoveHookByID(hookid); } - bool Pause(int hookid) + inline bool Pause(int hookid) { return g_SHPtr->PauseHookByID(hookid); } - bool Unpause(int hookid) + inline bool Unpause(int hookid) { return g_SHPtr->UnpauseHookByID(hookid); } + // See RETURN_META_MNEWPARAMS // For void return type only template::value, void>::type* = nullptr> void Recall(META_RES result, Params... newparams) @@ -317,9 +322,10 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerSetRes(MRES_SUPERCEDE); } + // See RETURN_META_VALUE_MNEWPARAMS // For any return type but void template::value, void>::type* = nullptr> - U Recall(META_RES result, U value, Params... newparams) + [[nodiscard]] U Recall(META_RES result, U value, Params... newparams) { g_SHPtr->SetRes(result); g_SHPtr->DoRecall(); @@ -333,6 +339,23 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler*(GetRecallMFP(thisptr)))(newparams...); } + // See RETURN_META. + // NOTE: for RETURN_META_VALUE return the value after this call + inline void SetResult(META_RES res) + { + g_SHPtr->SetRes(res); + } + + [[nodiscard]] inline META_RES GetPreviousResult() const + { + return g_SHPtr->GetPrevRes(); + } + + [[nodiscard]] inline META_RES GetStatus() const + { + return g_SHPtr->GetStatus(); + } + private: struct IMyDelegate : ISHDelegate { @@ -513,7 +536,7 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler(this, &ThisType::Func, mfi); + GetFuncInfo(this, &ThisType::Func, mfi); hi->SetInfo(SH_HOOKMAN_VERSION, msMFI_.vtbloffs, From 191f20c4d0515146fb9c6401009fccbbc1b73a85 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 9 Dec 2023 22:41:35 +0300 Subject: [PATCH 8/8] Add support for static function callbacks --- core/sourcehook/sourcehook.h | 6 +- core/sourcehook/sourcehook_handlers.h | 162 ++++++++++++++++--- core/sourcehook/sourcehook_impl_chook.h | 2 +- core/sourcehook/sourcehook_impl_chookidman.h | 4 +- 4 files changed, 148 insertions(+), 26 deletions(-) diff --git a/core/sourcehook/sourcehook.h b/core/sourcehook/sourcehook.h index f015b9c4..d52213d6 100644 --- a/core/sourcehook/sourcehook.h +++ b/core/sourcehook/sourcehook.h @@ -471,14 +471,14 @@ namespace SourceHook { } - void reset() + void Reset() { ptr_.reset(); } bool operator==(const SHDelegateHandler &other) const { - return ptr_.get() == other.get(); + return ptr_.get() == other.Get(); } ISHDelegate* operator->() const @@ -486,7 +486,7 @@ namespace SourceHook return ptr_.get(); } - ISHDelegate* get() const + ISHDelegate* Get() const { return ptr_.get(); } diff --git a/core/sourcehook/sourcehook_handlers.h b/core/sourcehook/sourcehook_handlers.h index 73d9776f..2117505b 100644 --- a/core/sourcehook/sourcehook_handlers.h +++ b/core/sourcehook/sourcehook_handlers.h @@ -244,14 +244,30 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler ThisType; - typedef fastdelegate::FastDelegate FD; typedef ReturnType(EmptyClass::*ECMFP)(Params...); typedef ExecutableClassN CallEC; + template + struct HookedMemberFuncType + { + typedef ReturnType (U::*HookFunc)(Params...); + }; + + struct HookedStaticFuncType + { + typedef ReturnType (*HookFunc)(Params...); + }; + + template + struct HookedMemberFuncIFaceType + { + typedef ReturnType (U::*HookFunc)(T*, Params...); + }; + template - struct HookedFuncType + struct HookedStaticFuncIFaceType { - typedef ReturnType (T::*HookFunc)(Params...); + typedef ReturnType (*HookFunc)(T*, Params...); }; constexpr ManualHookHandler() @@ -271,27 +287,69 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerRemoveHookManager(g_PLID, HookManagerPubFuncHandler(this)); + HookManagerPubFuncHandler pubFunc(this); + g_SHPtr->RemoveHookManager(g_PLID, pubFunc); } void Reconfigure(int vtblindex, int vtbloffs = 0, int thisptroffs = 0) { - g_SHPtr->RemoveHookManager(g_PLID, HookManagerPubFuncHandler(this)); + HookManagerPubFuncHandler pubFunc(this); + g_SHPtr->RemoveHookManager(g_PLID, pubFunc); msMFI_.thisptroffs = thisptroffs; msMFI_.vtblindex = vtblindex; msMFI_.vtbloffs = vtbloffs; } + // Hook with a statuc function callback + // ReturnType Callback(Params...); + [[nodiscard]] int Add(void *iface, + typename HookedStaticFuncType::HookFunc callbackFuncPtr, + bool post = true, + ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) + { + HookManagerPubFuncHandler pubFunc(this); + SHDelegateHandler shDelegate = SHDelegateHandler::Make(callbackFuncPtr); + return g_SHPtr->AddHook(g_PLID, mode, iface, 0, pubFunc, shDelegate, post); + } + + // Hook with a statuc function callback with iface pointer first argument + // ReturnType Callback(T *iface, Params...); template + [[nodiscard]] int Add(T *iface, + typename HookedStaticFuncIFaceType::HookFunc callbackFuncPtr, + bool post = true, + ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) + { + HookManagerPubFuncHandler pubFunc(this); + SHDelegateHandler shDelegate = SHDelegateHandler::Make>(callbackFuncPtr); + return g_SHPtr->AddHook(g_PLID, mode, iface, 0, pubFunc, shDelegate, post); + } + + // Hook with a member function callback + // ReturnType U::Callback(Params...); + template::value, void>::type* = nullptr> [[nodiscard]] int Add(void *iface, - T* callbackInstPtr, - typename HookedFuncType::HookFunc callbackFuncPtr, + U* callbackInstPtr, + typename HookedMemberFuncType::HookFunc callbackFuncPtr, bool post = true, ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) { - typename ManualHookHandler::FD handler(callbackInstPtr, callbackFuncPtr); HookManagerPubFuncHandler pubFunc(this); - SHDelegateHandler shDelegate = SHDelegateHandler::Make(handler); + SHDelegateHandler shDelegate = SHDelegateHandler::Make>(callbackInstPtr, callbackFuncPtr); + return g_SHPtr->AddHook(g_PLID, mode, iface, 0, pubFunc, shDelegate, post); + } + + // Hook with a member function callback with iface pointer first argument + // ReturnType U::Callback(T *iface, Params...); + template::value, void>::type* = nullptr> + [[nodiscard]] int Add(T *iface, + U *callbackInstPtr, + typename HookedMemberFuncIFaceType::HookFunc callbackFuncPtr, + bool post = true, + ISourceHook::AddHookMode mode = ISourceHook::AddHookMode::Hook_Normal) + { + HookManagerPubFuncHandler pubFunc(this); + SHDelegateHandler shDelegate = SHDelegateHandler::Make>(callbackInstPtr, callbackFuncPtr); return g_SHPtr->AddHook(g_PLID, mode, iface, 0, pubFunc, shDelegate, post); } @@ -331,7 +389,6 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerDoRecall(); if ((result) >= MRES_OVERRIDE) { - /* see RETURN_META_VALUE_NEWPARAMS */ SetOverrideResult(g_SHPtr, value); } EmptyClass *thisptr = reinterpret_cast(g_SHPtr->GetIfacePtr()); @@ -360,29 +417,96 @@ class ManualHookHandler : public CHookManagerMemberFuncHandler + struct CStaticIFaceDelegateImpl : public IMyDelegate { - FD m_Deleg; - CMyDelegateImpl(FD deleg) - : m_Deleg(deleg) + typename HookedStaticFuncIFaceType::HookFunc funcPtr_; + CStaticIFaceDelegateImpl(typename HookedStaticFuncIFaceType::HookFunc callbackFuncPtr) + : funcPtr_(callbackFuncPtr) { } virtual ReturnType Call(Params... params) override { - return m_Deleg(params...); + return funcPtr_(reinterpret_cast(g_SHPtr->GetIfacePtr()), params...); } virtual void DeleteThis() override { delete this; } + }; + + template + struct CMemberDelegateImpl : public IMyDelegate + { + U* instPtr_; + typename HookedMemberFuncType::HookFunc funcPtr_; + CMemberDelegateImpl(U* callbackInstPtr, typename HookedMemberFuncType::HookFunc callbackFuncPtr) + : instPtr_(callbackInstPtr) + , funcPtr_(callbackFuncPtr) + { + } - virtual bool IsEqual(ISHDelegate *pOtherDeleg) override + virtual ReturnType Call(Params... params) override { - return m_Deleg == static_cast(pOtherDeleg)->m_Deleg; + return (instPtr_->*funcPtr_)(params...); + } + + virtual void DeleteThis() override + { + delete this; + } + }; + + template + struct CMemberIFaceDelegateImpl : public IMyDelegate + { + U* instPtr_; + typename HookedMemberFuncIFaceType::HookFunc funcPtr_; + CMemberIFaceDelegateImpl(U* callbackInstPtr, typename HookedMemberFuncIFaceType::HookFunc callbackFuncPtr) + : instPtr_(callbackInstPtr) + , funcPtr_(callbackFuncPtr) + { + } + + virtual ReturnType Call(Params... params) override + { + return (instPtr_->*funcPtr_)(reinterpret_cast(g_SHPtr->GetIfacePtr()), params...); + } + + virtual void DeleteThis() override + { + delete this; } }; @@ -499,9 +623,7 @@ class ManualHookHandler : public CHookManagerMemberFuncHandlerShouldCallOrig()) { void (EmptyClass::*mfp)(Params...); - - reinterpret_cast(&mfp)[0] = vfnptr_origentry; - reinterpret_cast(&mfp)[1] = 0; + SH_SETUP_MFP(mfp); (reinterpret_cast(this)->*mfp)(params...); } diff --git a/core/sourcehook/sourcehook_impl_chook.h b/core/sourcehook/sourcehook_impl_chook.h index dbd9e9ad..aef773ea 100644 --- a/core/sourcehook/sourcehook_impl_chook.h +++ b/core/sourcehook/sourcehook_impl_chook.h @@ -82,7 +82,7 @@ namespace SourceHook inline ISHDelegate *CHook::GetHandler() const { - return m_pHandler.get(); + return m_pHandler.Get(); } inline void CHook::SetPaused(bool value) diff --git a/core/sourcehook/sourcehook_impl_chookidman.h b/core/sourcehook/sourcehook_impl_chookidman.h index 76a9e6e6..f5a94eb6 100644 --- a/core/sourcehook/sourcehook_impl_chookidman.h +++ b/core/sourcehook/sourcehook_impl_chookidman.h @@ -43,12 +43,12 @@ namespace SourceHook bool IsFree() const { - return handler.get() == nullptr; + return handler.Get() == nullptr; } void Reset() { - handler.reset(); + handler.Reset(); } Entry(const CProto &pprt, int pvo, int pvi, void *pvp, void *pai, Plugin pplug, int pto,