From 9eb35c8830d2cd62301387d83bd71c9c4042beb7 Mon Sep 17 00:00:00 2001 From: Pavol Marko Date: Fri, 23 Dec 2005 11:58:11 +0000 Subject: [PATCH] One bugfix, addition of META_RETURN_(VALUE_)NEWPARAMS (implemented using recalls), tests now support compiling against an older version of sourcehook.h (through the sourcehook_test.h file) while main.cpp which now also acts as a CSourceHookImpl factory compiles against the new version. --HG-- extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%40163 --- sourcehook/generate/sourcehook.h | 221 +++++++++++++++++++++++++---- sourcehook/generate/sourcehook.hxx | 123 ++++++++++++---- sourcehook/sh_stack.h | 10 ++ sourcehook/sourcehook.cpp | 100 ++++++++++++- sourcehook/sourcehook.h | 221 +++++++++++++++++++++++++---- sourcehook/sourcehook_impl.h | 24 +++- sourcehook/test/main.cpp | 36 +++++ sourcehook/test/sourcehook_test.h | 27 ++++ sourcehook/test/test.vcproj | 9 ++ sourcehook/test/test1.cpp | 27 +++- sourcehook/test/test2.cpp | 5 +- sourcehook/test/test3.cpp | 5 +- sourcehook/test/test4.cpp | 99 +++++++------ sourcehook/test/testbail.cpp | 117 +++++++++------ sourcehook/test/testbail.h | 81 ++--------- sourcehook/test/testbail2.cpp | 22 +-- sourcehook/test/testevents.h | 6 +- sourcehook/test/testmanual.cpp | 6 +- sourcehook/test/testrecall.cpp | 131 +++++++++++++++++ sourcehook/test/testreentr.cpp | 5 +- 20 files changed, 1001 insertions(+), 274 deletions(-) create mode 100644 sourcehook/test/sourcehook_test.h create mode 100644 sourcehook/test/testrecall.cpp diff --git a/sourcehook/generate/sourcehook.h b/sourcehook/generate/sourcehook.h index 681074a..5b4d9b2 100644 --- a/sourcehook/generate/sourcehook.h +++ b/sourcehook/generate/sourcehook.h @@ -372,7 +372,7 @@ namespace SourceHook virtual void SetStatusPtr(META_RES *mres) = 0; //!< Sets pointer to the status variable virtual void SetIfacePtrPtr(void **pp) = 0; //!< Sets pointer to the interface this pointer virtual void SetOrigRetPtr(const void *ptr) = 0; //!< Sets the original return pointer - virtual void SetOverrideRetPtr(const void *ptr) = 0; //!< Sets the override result pointer + virtual void SetOverrideRetPtr(void *ptr) = 0; //!< Sets the override result pointer virtual bool ShouldContinue() = 0; //!< Returns false if the hook loop should exit /** @@ -382,22 +382,89 @@ namespace SourceHook * @param pubFunc The hook manager's info function */ virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc) = 0; + + virtual void DoRecall() = 0; //!< Initiates a recall sequence + /* + HOW RECALLS WORK: + + The problem: + Users want the ability to change parameters of the called function + from inside their handler. + The solution: + 1) Mark as "recall" + 2) Recall the function + 3) => SH's hook func gets called: + 4) The first iterator points at the first hook the last hookfunc didn't execute yet + 5) does all iteration and returns normally + 6) The user's handler returns immediately + 7) The hook func returns immediately as well + + Also note that the recalled hookfunc starts with the status the recalling hookfunc + ended with. The last handler (doing the recall) is also able to specify its own + META_RES. + */ + + virtual void *GetOverrideRetPtr() = 0; //!< Returns the pointer set by SetOverrideRetPtr + + /** + * @brief Set up the hook loop. Equivalent to calling: + * SetStatusPtr, SetPrevResPtr, SetCurResPtr, SetIfacePtrPtr, SetOrigRetPtr, Get/SetOverrideRetPtr + * + * @param statusPtr pointer to status variable + * @param prevResPtr pointer to previous result variable + * @param curResPtr pointer to current result variable + * @param ifacePtrPtr pointer to interface this pointer variable + * @param origRetPr pointer to original return value variable. NULL for void funcs + * @param overrideRetPtr pointer to override return value variable. NULL for void funcs + * + * @return Override Return Pointer the hookfunc should use (may differ from overrideRetPtr + * when the hook func is being called as part of a recall + */ + virtual void *SetupHookLoop(META_RES *statusPtr, META_RES *prevResPtr, META_RES *curResPtr, + void **ifacePtrPtr, const void *origRetPtr, void *overrideRetPtr) = 0; }; } /************************************************************************/ /* High level interface */ /************************************************************************/ -#define SET_META_RESULT(result) SH_GLOB_SHPTR->SetRes(result) -#define RETURN_META(result) do { SET_META_RESULT(result); return; } while(0) -#define RETURN_META_VALUE(result, value) do { SET_META_RESULT(result); return (value); } while(0) - #define META_RESULT_STATUS SH_GLOB_SHPTR->GetStatus() #define META_RESULT_PREVIOUS SH_GLOB_SHPTR->GetPrevRes() #define META_RESULT_ORIG_RET(type) *reinterpret_cast(SH_GLOB_SHPTR->GetOrigRet()) #define META_RESULT_OVERRIDE_RET(type) *reinterpret_cast(SH_GLOB_SHPTR->GetOverrideRet()) #define META_IFACEPTR(type) reinterpret_cast(SH_GLOB_SHPTR->GetIfacePtr()) +#define SET_META_RESULT(result) SH_GLOB_SHPTR->SetRes(result) +#define RETURN_META(result) do { SET_META_RESULT(result); return; } while(0) +#define RETURN_META_VALUE(result, value) do { SET_META_RESULT(result); return (value); } while(0) + + +// NEVER-EVER call these from post hooks! +// also, only call it from the hook handlers directly! +// :TODO: enforce it +// :TODO: problems with SetOverrideResult and overloaded iface::func ? + +// SourceHook::SetOverrideRet is defined later. +#define RETURN_META_NEWPARAMS(result, iface, func, newparams) \ + do { \ + SET_META_RESULT(result); \ + SH_GLOB_SHPTR->DoRecall(); \ + META_IFACEPTR(iface)->func newparams; \ + RETURN_META(MRES_SUPERCEDE); \ + } while (0) + +#define RETURN_META_VALUE_NEWPARAMS(result, value, iface, func, newparams) \ + do { \ + SET_META_RESULT(result); \ + SH_GLOB_SHPTR->DoRecall(); \ + if ((result) >= MRES_OVERRIDE) \ + { \ + /* meh, set the override result here because we don't get a chance to return */ \ + /* before continuing the hook loop through the recall */ \ + SourceHook::SetOverrideResult(SH_GLOB_SHPTR, &iface::func, value); \ + } \ + RETURN_META_VALUE(MRES_SUPERCEDE, META_IFACEPTR(iface)->func newparams); \ + } while (0) /** * @brief Get/generate callclass for an interface pointer @@ -542,8 +609,8 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C using namespace ::SourceHook; \ MemFuncInfo mfi; \ GetFuncInfo(funcptr, mfi); \ - if (mfi.thisptroffs < 0) \ - return false; /* No virtual inheritance supported */ \ + if (mfi.thisptroffs < 0 || !mfi.isVirtual) \ + return false; /* No non-virtual functions / virtual inheritance supported */ \ \ return SH_GLOB_SHPTR->AddHook(SH_GLOB_PLUGPTR, iface, mfi.thisptroffs, \ SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, \ @@ -659,16 +726,12 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C META_RES status = MRES_IGNORED; \ META_RES prev_res; \ META_RES cur_res; \ - SH_GLOB_SHPTR->SetStatusPtr(&status); \ - SH_GLOB_SHPTR->SetPrevResPtr(&prev_res); \ - SH_GLOB_SHPTR->SetCurResPtr(&cur_res); \ rettype orig_ret; \ rettype override_ret; \ rettype plugin_ret; \ void* ifptr; \ - SH_GLOB_SHPTR->SetIfacePtrPtr(&ifptr); \ - SH_GLOB_SHPTR->SetOrigRetPtr(reinterpret_cast(&orig_ret)); \ - SH_GLOB_SHPTR->SetOverrideRetPtr(NULL); + rettype *pOverrideRet = reinterpret_cast(SH_GLOB_SHPTR->SetupHookLoop( \ + &status, &prev_res, &cur_res, &ifptr, &orig_ret, &override_ret)); #define SH_CALL_HOOKS(post, params) \ if (SH_GLOB_SHPTR->ShouldContinue()) \ @@ -683,10 +746,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C if (cur_res > status) \ status = cur_res; \ if (cur_res >= MRES_OVERRIDE) \ - { \ - override_ret = plugin_ret; \ - SH_GLOB_SHPTR->SetOverrideRetPtr(&override_ret); \ - } \ + *pOverrideRet = plugin_ret; \ if (!SH_GLOB_SHPTR->ShouldContinue()) \ { \ iter.SetToZero(); \ @@ -703,11 +763,11 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C orig_ret = (reinterpret_cast(this)->*mfp)params; \ } \ else \ - orig_ret = override_ret; + orig_ret = override_ret; \ #define SH_RETURN() \ SH_GLOB_SHPTR->HookLoopEnd(); \ - return status >= MRES_OVERRIDE ? override_ret : orig_ret; + return status >= MRES_OVERRIDE ? *pOverrideRet : orig_ret; #define SH_HANDLEFUNC(paramtypes, params, rettype) \ SH_SETUPCALLS(rettype, paramtypes, params) \ @@ -743,13 +803,8 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C META_RES status = MRES_IGNORED; \ META_RES prev_res; \ META_RES cur_res; \ - SH_GLOB_SHPTR->SetStatusPtr(&status); \ - SH_GLOB_SHPTR->SetPrevResPtr(&prev_res); \ - SH_GLOB_SHPTR->SetCurResPtr(&cur_res); \ void* ifptr; \ - SH_GLOB_SHPTR->SetIfacePtrPtr(&ifptr); \ - SH_GLOB_SHPTR->SetOverrideRetPtr(NULL); \ - SH_GLOB_SHPTR->SetOrigRetPtr(NULL); + SH_GLOB_SHPTR->SetupHookLoop(&status, &prev_res, &cur_res, &ifptr, NULL, NULL); \ #define SH_CALL_HOOKS_void(post, params) \ if (SH_GLOB_SHPTR->ShouldContinue()) \ @@ -791,6 +846,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C // Special vafmt handlers +// :TODO: what #define SH_HANDLEFUNC_vafmt(paramtypes, params_orig, params_plug, rettype) \ SH_SETUPCALLS(rettype, paramtypes, params_orig) \ SH_CALL_HOOKS(pre, params_plug) \ @@ -3497,6 +3553,121 @@ SH_CALL2(SourceHook::CallClass *ptr, MFP mfp, RetType(X::*mfp2)(Param1, Param #undef SH_MAKE_EXECUTABLECLASS_OB +////////////////////////////////////////////////////////////////////////// +// SetOverrideRet for recalls +// These take a ISourceHook pointer instead of using SH_GLOB_SHPTR directly +// The reason is that the user may want to redefine SH_GLOB_SHPTR - then the macros +// (META_RETURN_VALUE_NEWPARAMS) should obey the new pointer. + +namespace SourceHook +{ + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17, Param18), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17, Param18, Param19), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17, Param18, Param19, Param20), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } +} + #endif // The pope is dead. -> :( diff --git a/sourcehook/generate/sourcehook.hxx b/sourcehook/generate/sourcehook.hxx index 3eaa537..6dec17d 100755 --- a/sourcehook/generate/sourcehook.hxx +++ b/sourcehook/generate/sourcehook.hxx @@ -372,7 +372,7 @@ namespace SourceHook virtual void SetStatusPtr(META_RES *mres) = 0; //!< Sets pointer to the status variable virtual void SetIfacePtrPtr(void **pp) = 0; //!< Sets pointer to the interface this pointer virtual void SetOrigRetPtr(const void *ptr) = 0; //!< Sets the original return pointer - virtual void SetOverrideRetPtr(const void *ptr) = 0; //!< Sets the override result pointer + virtual void SetOverrideRetPtr(void *ptr) = 0; //!< Sets the override result pointer virtual bool ShouldContinue() = 0; //!< Returns false if the hook loop should exit /** @@ -382,22 +382,89 @@ namespace SourceHook * @param pubFunc The hook manager's info function */ virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc) = 0; + + virtual void DoRecall() = 0; //!< Initiates a recall sequence + /* + HOW RECALLS WORK: + + The problem: + Users want the ability to change parameters of the called function + from inside their handler. + The solution: + 1) Mark as "recall" + 2) Recall the function + 3) => SH's hook func gets called: + 4) The first iterator points at the first hook the last hookfunc didn't execute yet + 5) does all iteration and returns normally + 6) The user's handler returns immediately + 7) The hook func returns immediately as well + + Also note that the recalled hookfunc starts with the status the recalling hookfunc + ended with. The last handler (doing the recall) is also able to specify its own + META_RES. + */ + + virtual void *GetOverrideRetPtr() = 0; //!< Returns the pointer set by SetOverrideRetPtr + + /** + * @brief Set up the hook loop. Equivalent to calling: + * SetStatusPtr, SetPrevResPtr, SetCurResPtr, SetIfacePtrPtr, SetOrigRetPtr, Get/SetOverrideRetPtr + * + * @param statusPtr pointer to status variable + * @param prevResPtr pointer to previous result variable + * @param curResPtr pointer to current result variable + * @param ifacePtrPtr pointer to interface this pointer variable + * @param origRetPr pointer to original return value variable. NULL for void funcs + * @param overrideRetPtr pointer to override return value variable. NULL for void funcs + * + * @return Override Return Pointer the hookfunc should use (may differ from overrideRetPtr + * when the hook func is being called as part of a recall + */ + virtual void *SetupHookLoop(META_RES *statusPtr, META_RES *prevResPtr, META_RES *curResPtr, + void **ifacePtrPtr, const void *origRetPtr, void *overrideRetPtr) = 0; }; } /************************************************************************/ /* High level interface */ /************************************************************************/ -#define SET_META_RESULT(result) SH_GLOB_SHPTR->SetRes(result) -#define RETURN_META(result) do { SET_META_RESULT(result); return; } while(0) -#define RETURN_META_VALUE(result, value) do { SET_META_RESULT(result); return (value); } while(0) - #define META_RESULT_STATUS SH_GLOB_SHPTR->GetStatus() #define META_RESULT_PREVIOUS SH_GLOB_SHPTR->GetPrevRes() #define META_RESULT_ORIG_RET(type) *reinterpret_cast(SH_GLOB_SHPTR->GetOrigRet()) #define META_RESULT_OVERRIDE_RET(type) *reinterpret_cast(SH_GLOB_SHPTR->GetOverrideRet()) #define META_IFACEPTR(type) reinterpret_cast(SH_GLOB_SHPTR->GetIfacePtr()) +#define SET_META_RESULT(result) SH_GLOB_SHPTR->SetRes(result) +#define RETURN_META(result) do { SET_META_RESULT(result); return; } while(0) +#define RETURN_META_VALUE(result, value) do { SET_META_RESULT(result); return (value); } while(0) + + +// NEVER-EVER call these from post hooks! +// also, only call it from the hook handlers directly! +// :TODO: enforce it +// :TODO: problems with SetOverrideResult and overloaded iface::func ? + +// SourceHook::SetOverrideRet is defined later. +#define RETURN_META_NEWPARAMS(result, iface, func, newparams) \ + do { \ + SET_META_RESULT(result); \ + SH_GLOB_SHPTR->DoRecall(); \ + META_IFACEPTR(iface)->func newparams; \ + RETURN_META(MRES_SUPERCEDE); \ + } while (0) + +#define RETURN_META_VALUE_NEWPARAMS(result, value, iface, func, newparams) \ + do { \ + SET_META_RESULT(result); \ + SH_GLOB_SHPTR->DoRecall(); \ + if ((result) >= MRES_OVERRIDE) \ + { \ + /* meh, set the override result here because we don't get a chance to return */ \ + /* before continuing the hook loop through the recall */ \ + SourceHook::SetOverrideResult(SH_GLOB_SHPTR, &iface::func, value); \ + } \ + RETURN_META_VALUE(MRES_SUPERCEDE, META_IFACEPTR(iface)->func newparams); \ + } while (0) /** * @brief Get/generate callclass for an interface pointer @@ -542,8 +609,8 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C using namespace ::SourceHook; \ MemFuncInfo mfi; \ GetFuncInfo(funcptr, mfi); \ - if (mfi.thisptroffs < 0) \ - return false; /* No virtual inheritance supported */ \ + if (mfi.thisptroffs < 0 || !mfi.isVirtual) \ + return false; /* No non-virtual functions / virtual inheritance supported */ \ \ return SH_GLOB_SHPTR->AddHook(SH_GLOB_PLUGPTR, iface, mfi.thisptroffs, \ SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, \ @@ -659,16 +726,12 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C META_RES status = MRES_IGNORED; \ META_RES prev_res; \ META_RES cur_res; \ - SH_GLOB_SHPTR->SetStatusPtr(&status); \ - SH_GLOB_SHPTR->SetPrevResPtr(&prev_res); \ - SH_GLOB_SHPTR->SetCurResPtr(&cur_res); \ rettype orig_ret; \ rettype override_ret; \ rettype plugin_ret; \ void* ifptr; \ - SH_GLOB_SHPTR->SetIfacePtrPtr(&ifptr); \ - SH_GLOB_SHPTR->SetOrigRetPtr(reinterpret_cast(&orig_ret)); \ - SH_GLOB_SHPTR->SetOverrideRetPtr(NULL); + rettype *pOverrideRet = reinterpret_cast(SH_GLOB_SHPTR->SetupHookLoop( \ + &status, &prev_res, &cur_res, &ifptr, &orig_ret, &override_ret)); #define SH_CALL_HOOKS(post, params) \ if (SH_GLOB_SHPTR->ShouldContinue()) \ @@ -683,10 +746,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C if (cur_res > status) \ status = cur_res; \ if (cur_res >= MRES_OVERRIDE) \ - { \ - override_ret = plugin_ret; \ - SH_GLOB_SHPTR->SetOverrideRetPtr(&override_ret); \ - } \ + *pOverrideRet = plugin_ret; \ if (!SH_GLOB_SHPTR->ShouldContinue()) \ { \ iter.SetToZero(); \ @@ -703,11 +763,11 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C orig_ret = (reinterpret_cast(this)->*mfp)params; \ } \ else \ - orig_ret = override_ret; + orig_ret = override_ret; \ #define SH_RETURN() \ SH_GLOB_SHPTR->HookLoopEnd(); \ - return status >= MRES_OVERRIDE ? override_ret : orig_ret; + return status >= MRES_OVERRIDE ? *pOverrideRet : orig_ret; #define SH_HANDLEFUNC(paramtypes, params, rettype) \ SH_SETUPCALLS(rettype, paramtypes, params) \ @@ -743,13 +803,8 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C META_RES status = MRES_IGNORED; \ META_RES prev_res; \ META_RES cur_res; \ - SH_GLOB_SHPTR->SetStatusPtr(&status); \ - SH_GLOB_SHPTR->SetPrevResPtr(&prev_res); \ - SH_GLOB_SHPTR->SetCurResPtr(&cur_res); \ void* ifptr; \ - SH_GLOB_SHPTR->SetIfacePtrPtr(&ifptr); \ - SH_GLOB_SHPTR->SetOverrideRetPtr(NULL); \ - SH_GLOB_SHPTR->SetOrigRetPtr(NULL); + SH_GLOB_SHPTR->SetupHookLoop(&status, &prev_res, &cur_res, &ifptr, NULL, NULL); \ #define SH_CALL_HOOKS_void(post, params) \ if (SH_GLOB_SHPTR->ShouldContinue()) \ @@ -791,6 +846,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C // Special vafmt handlers +// :TODO: what #define SH_HANDLEFUNC_vafmt(paramtypes, params_orig, params_plug, rettype) \ SH_SETUPCALLS(rettype, paramtypes, params_orig) \ SH_CALL_HOOKS(pre, params_plug) \ @@ -1025,5 +1081,22 @@ SH_CALL2(SourceHook::CallClass *ptr, MFP mfp, RetType(X::*mfp2)(@Param%%|, @@ #undef SH_MAKE_EXECUTABLECLASS_OB +////////////////////////////////////////////////////////////////////////// +// SetOverrideRet for recalls +// These take a ISourceHook pointer instead of using SH_GLOB_SHPTR directly +// The reason is that the user may want to redefine SH_GLOB_SHPTR - then the macros +// (META_RETURN_VALUE_NEWPARAMS) should obey the new pointer. + +namespace SourceHook +{ +@VARARGS@ + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(@Param%%|, @), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } +@ENDARGS@ +} + #endif // The pope is dead. -> :( diff --git a/sourcehook/sh_stack.h b/sourcehook/sh_stack.h index c265edd..ff03166 100644 --- a/sourcehook/sh_stack.h +++ b/sourcehook/sh_stack.h @@ -174,6 +174,16 @@ namespace SourceHook return m_Elements[m_UsedSize - 1]; } + T &second() + { + return m_Elements[m_UsedSize - 2]; + } + + const T &second() const + { + return m_Elements[m_UsedSize - 2]; + } + iterator begin() { return iterator(this, 0); diff --git a/sourcehook/sourcehook.cpp b/sourcehook/sourcehook.cpp index 867d3e0..b343325 100644 --- a/sourcehook/sourcehook.cpp +++ b/sourcehook/sourcehook.cpp @@ -627,6 +627,7 @@ namespace SourceHook HookLoopInfo hli; hli.pCurIface = pIface; hli.shouldContinue = true; + hli.recall = false; m_HLIStack.push(hli); } @@ -657,6 +658,20 @@ namespace SourceHook const void *CSourceHookImpl::GetOverrideRet() { + // pOverrideRet is always set since recalls were introduced + // GetOverrideRetPtr was added; a function which always returns pOverrideRet + // so that RETURN_META_VALUE_NEWPARAMS can provide an override result + + // This means that we have to filter GetOverrideRet: + // If the status variable is < MRES_OVERRIDE, return NULL. + + return (*m_HLIStack.front().pStatus < MRES_OVERRIDE) ? + NULL : m_HLIStack.front().pOverrideRet; + } + + void *CSourceHookImpl::GetOverrideRetPtr() + { + // As described in the comment above: always return pOverrideRet return m_HLIStack.front().pOverrideRet; } @@ -673,11 +688,19 @@ namespace SourceHook void CSourceHookImpl::SetPrevResPtr(META_RES *mres) { m_HLIStack.front().pPrevRes = mres; + + // If we're recalling, drag the previous mres value to the new hookfunc + if (m_HLIStack.size() > 1 && m_HLIStack.second().recall) + *mres = *m_HLIStack.second().pPrevRes; } void CSourceHookImpl::SetStatusPtr(META_RES *mres) { m_HLIStack.front().pStatus = mres; + + // If we're recalling, drag the previous mres value to the new hookfunc + if (m_HLIStack.size() > 1 && m_HLIStack.second().recall) + *mres = *m_HLIStack.second().pStatus; } void CSourceHookImpl::SetIfacePtrPtr(void **pp) @@ -689,13 +712,61 @@ namespace SourceHook { m_HLIStack.front().pOrigRet = ptr; } - void CSourceHookImpl::SetOverrideRetPtr(const void *ptr) + void CSourceHookImpl::SetOverrideRetPtr(void *ptr) { m_HLIStack.front().pOverrideRet = ptr; } + + // New function which does all of the above + more :) + void *CSourceHookImpl::SetupHookLoop(META_RES *statusPtr, META_RES *prevResPtr, META_RES *curResPtr, + void **ifacePtrPtr, const void *origRetPtr, void *overrideRetPtr) + { + HookLoopInfo &hli = m_HLIStack.front(); + hli.pStatus = statusPtr; + hli.pPrevRes = prevResPtr; + hli.pCurRes = curResPtr; + hli.pIfacePtrPtr = ifacePtrPtr; + hli.pOrigRet = origRetPtr; + + // Handle some recall stuff + if (m_HLIStack.size() > 1 && m_HLIStack.second().recall) + { + HookLoopInfo &other = m_HLIStack.second(); + *statusPtr = *other.pStatus; + *prevResPtr = *other.pStatus; + hli.pOverrideRet = other.pOverrideRet; + } + else + hli.pOverrideRet = overrideRetPtr; + + // Tell the hook func which override ret ptr to use + return hli.pOverrideRet; + } + bool CSourceHookImpl::ShouldContinue() { - return m_HLIStack.front().shouldContinue; + // If recall is true, we shall not continue either. + // This is because, if it's true and ShouldContinue is called, it suggests that the + // actual recall is done and that we are back in the original handler which shall return + // immediately. + + return m_HLIStack.front().shouldContinue && !m_HLIStack.front().recall; + } + + void CSourceHookImpl::DoRecall() + { + if (!m_HLIStack.empty()) + { + m_HLIStack.front().recall = true; + CHookList *mlist = static_cast(m_HLIStack.front().pCurIface->GetPreHooks()); + mlist->m_Recall = true; + + // The hookfunc usually do this, but it won't have a chance to see it, + // so for recalls, we update status here if it's required + if (*m_HLIStack.front().pCurRes > *m_HLIStack.front().pStatus) + *m_HLIStack.front().pStatus = *m_HLIStack.front().pCurRes; + + } } //////////////////////////// @@ -897,12 +968,15 @@ namespace SourceHook // CHookList //////////////////////////// - CSourceHookImpl::CHookList::CHookList() : m_FreeIters(NULL), m_UsedIters(NULL) + CSourceHookImpl::CHookList::CHookList() : m_FreeIters(NULL), m_UsedIters(NULL), + m_Recall(false) { } - CSourceHookImpl::CHookList::CHookList(const CHookList &other) : m_List(other.m_List), m_FreeIters(NULL), m_UsedIters(NULL) + CSourceHookImpl::CHookList::CHookList(const CHookList &other) : m_List(other.m_List), + m_FreeIters(NULL), m_UsedIters(NULL), m_Recall(false) { } + CSourceHookImpl::CHookList::~CHookList() { while (m_FreeIters) @@ -932,12 +1006,22 @@ namespace SourceHook ret = new CIter(this); } + // Muuuh, if we're recalling, it shall be a copy of the last iterator, incremented by one + if (m_Recall && m_UsedIters) + { + ret->Set(m_UsedIters); // m_UsedIters is the last returned and not released iterator + ret->Next(); // Use next instead of directly incrementing its m_Iter: + // skips paused plugins + } + ret->m_pNext = m_UsedIters; ret->m_pPrev = NULL; if (m_UsedIters) m_UsedIters->m_pPrev = ret; m_UsedIters = ret; + m_Recall = false; + return ret; } void CSourceHookImpl::CHookList::ReleaseIter(IIter *pIter) @@ -958,6 +1042,9 @@ namespace SourceHook pIter2->m_pNext = m_FreeIters; m_FreeIters = pIter2; + + // Reset recall state. + m_Recall = false; } CSourceHookImpl::CHookList::CIter::CIter(CHookList *pList) : m_pList(pList), m_pNext(NULL) @@ -968,6 +1055,11 @@ namespace SourceHook { } + void CSourceHookImpl::CHookList::CIter::Set(CIter *pOther) + { + m_Iter = pOther->m_Iter; + } + void CSourceHookImpl::CHookList::CIter::GoToBegin() { m_Iter = m_pList->m_List.begin(); diff --git a/sourcehook/sourcehook.h b/sourcehook/sourcehook.h index 681074a..5b4d9b2 100644 --- a/sourcehook/sourcehook.h +++ b/sourcehook/sourcehook.h @@ -372,7 +372,7 @@ namespace SourceHook virtual void SetStatusPtr(META_RES *mres) = 0; //!< Sets pointer to the status variable virtual void SetIfacePtrPtr(void **pp) = 0; //!< Sets pointer to the interface this pointer virtual void SetOrigRetPtr(const void *ptr) = 0; //!< Sets the original return pointer - virtual void SetOverrideRetPtr(const void *ptr) = 0; //!< Sets the override result pointer + virtual void SetOverrideRetPtr(void *ptr) = 0; //!< Sets the override result pointer virtual bool ShouldContinue() = 0; //!< Returns false if the hook loop should exit /** @@ -382,22 +382,89 @@ namespace SourceHook * @param pubFunc The hook manager's info function */ virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc) = 0; + + virtual void DoRecall() = 0; //!< Initiates a recall sequence + /* + HOW RECALLS WORK: + + The problem: + Users want the ability to change parameters of the called function + from inside their handler. + The solution: + 1) Mark as "recall" + 2) Recall the function + 3) => SH's hook func gets called: + 4) The first iterator points at the first hook the last hookfunc didn't execute yet + 5) does all iteration and returns normally + 6) The user's handler returns immediately + 7) The hook func returns immediately as well + + Also note that the recalled hookfunc starts with the status the recalling hookfunc + ended with. The last handler (doing the recall) is also able to specify its own + META_RES. + */ + + virtual void *GetOverrideRetPtr() = 0; //!< Returns the pointer set by SetOverrideRetPtr + + /** + * @brief Set up the hook loop. Equivalent to calling: + * SetStatusPtr, SetPrevResPtr, SetCurResPtr, SetIfacePtrPtr, SetOrigRetPtr, Get/SetOverrideRetPtr + * + * @param statusPtr pointer to status variable + * @param prevResPtr pointer to previous result variable + * @param curResPtr pointer to current result variable + * @param ifacePtrPtr pointer to interface this pointer variable + * @param origRetPr pointer to original return value variable. NULL for void funcs + * @param overrideRetPtr pointer to override return value variable. NULL for void funcs + * + * @return Override Return Pointer the hookfunc should use (may differ from overrideRetPtr + * when the hook func is being called as part of a recall + */ + virtual void *SetupHookLoop(META_RES *statusPtr, META_RES *prevResPtr, META_RES *curResPtr, + void **ifacePtrPtr, const void *origRetPtr, void *overrideRetPtr) = 0; }; } /************************************************************************/ /* High level interface */ /************************************************************************/ -#define SET_META_RESULT(result) SH_GLOB_SHPTR->SetRes(result) -#define RETURN_META(result) do { SET_META_RESULT(result); return; } while(0) -#define RETURN_META_VALUE(result, value) do { SET_META_RESULT(result); return (value); } while(0) - #define META_RESULT_STATUS SH_GLOB_SHPTR->GetStatus() #define META_RESULT_PREVIOUS SH_GLOB_SHPTR->GetPrevRes() #define META_RESULT_ORIG_RET(type) *reinterpret_cast(SH_GLOB_SHPTR->GetOrigRet()) #define META_RESULT_OVERRIDE_RET(type) *reinterpret_cast(SH_GLOB_SHPTR->GetOverrideRet()) #define META_IFACEPTR(type) reinterpret_cast(SH_GLOB_SHPTR->GetIfacePtr()) +#define SET_META_RESULT(result) SH_GLOB_SHPTR->SetRes(result) +#define RETURN_META(result) do { SET_META_RESULT(result); return; } while(0) +#define RETURN_META_VALUE(result, value) do { SET_META_RESULT(result); return (value); } while(0) + + +// NEVER-EVER call these from post hooks! +// also, only call it from the hook handlers directly! +// :TODO: enforce it +// :TODO: problems with SetOverrideResult and overloaded iface::func ? + +// SourceHook::SetOverrideRet is defined later. +#define RETURN_META_NEWPARAMS(result, iface, func, newparams) \ + do { \ + SET_META_RESULT(result); \ + SH_GLOB_SHPTR->DoRecall(); \ + META_IFACEPTR(iface)->func newparams; \ + RETURN_META(MRES_SUPERCEDE); \ + } while (0) + +#define RETURN_META_VALUE_NEWPARAMS(result, value, iface, func, newparams) \ + do { \ + SET_META_RESULT(result); \ + SH_GLOB_SHPTR->DoRecall(); \ + if ((result) >= MRES_OVERRIDE) \ + { \ + /* meh, set the override result here because we don't get a chance to return */ \ + /* before continuing the hook loop through the recall */ \ + SourceHook::SetOverrideResult(SH_GLOB_SHPTR, &iface::func, value); \ + } \ + RETURN_META_VALUE(MRES_SUPERCEDE, META_IFACEPTR(iface)->func newparams); \ + } while (0) /** * @brief Get/generate callclass for an interface pointer @@ -542,8 +609,8 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C using namespace ::SourceHook; \ MemFuncInfo mfi; \ GetFuncInfo(funcptr, mfi); \ - if (mfi.thisptroffs < 0) \ - return false; /* No virtual inheritance supported */ \ + if (mfi.thisptroffs < 0 || !mfi.isVirtual) \ + return false; /* No non-virtual functions / virtual inheritance supported */ \ \ return SH_GLOB_SHPTR->AddHook(SH_GLOB_PLUGPTR, iface, mfi.thisptroffs, \ SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, \ @@ -659,16 +726,12 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C META_RES status = MRES_IGNORED; \ META_RES prev_res; \ META_RES cur_res; \ - SH_GLOB_SHPTR->SetStatusPtr(&status); \ - SH_GLOB_SHPTR->SetPrevResPtr(&prev_res); \ - SH_GLOB_SHPTR->SetCurResPtr(&cur_res); \ rettype orig_ret; \ rettype override_ret; \ rettype plugin_ret; \ void* ifptr; \ - SH_GLOB_SHPTR->SetIfacePtrPtr(&ifptr); \ - SH_GLOB_SHPTR->SetOrigRetPtr(reinterpret_cast(&orig_ret)); \ - SH_GLOB_SHPTR->SetOverrideRetPtr(NULL); + rettype *pOverrideRet = reinterpret_cast(SH_GLOB_SHPTR->SetupHookLoop( \ + &status, &prev_res, &cur_res, &ifptr, &orig_ret, &override_ret)); #define SH_CALL_HOOKS(post, params) \ if (SH_GLOB_SHPTR->ShouldContinue()) \ @@ -683,10 +746,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C if (cur_res > status) \ status = cur_res; \ if (cur_res >= MRES_OVERRIDE) \ - { \ - override_ret = plugin_ret; \ - SH_GLOB_SHPTR->SetOverrideRetPtr(&override_ret); \ - } \ + *pOverrideRet = plugin_ret; \ if (!SH_GLOB_SHPTR->ShouldContinue()) \ { \ iter.SetToZero(); \ @@ -703,11 +763,11 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C orig_ret = (reinterpret_cast(this)->*mfp)params; \ } \ else \ - orig_ret = override_ret; + orig_ret = override_ret; \ #define SH_RETURN() \ SH_GLOB_SHPTR->HookLoopEnd(); \ - return status >= MRES_OVERRIDE ? override_ret : orig_ret; + return status >= MRES_OVERRIDE ? *pOverrideRet : orig_ret; #define SH_HANDLEFUNC(paramtypes, params, rettype) \ SH_SETUPCALLS(rettype, paramtypes, params) \ @@ -743,13 +803,8 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C META_RES status = MRES_IGNORED; \ META_RES prev_res; \ META_RES cur_res; \ - SH_GLOB_SHPTR->SetStatusPtr(&status); \ - SH_GLOB_SHPTR->SetPrevResPtr(&prev_res); \ - SH_GLOB_SHPTR->SetCurResPtr(&cur_res); \ void* ifptr; \ - SH_GLOB_SHPTR->SetIfacePtrPtr(&ifptr); \ - SH_GLOB_SHPTR->SetOverrideRetPtr(NULL); \ - SH_GLOB_SHPTR->SetOrigRetPtr(NULL); + SH_GLOB_SHPTR->SetupHookLoop(&status, &prev_res, &cur_res, &ifptr, NULL, NULL); \ #define SH_CALL_HOOKS_void(post, params) \ if (SH_GLOB_SHPTR->ShouldContinue()) \ @@ -791,6 +846,7 @@ inline void SH_RELEASE_CALLCLASS_R(SourceHook::ISourceHook *shptr, SourceHook::C // Special vafmt handlers +// :TODO: what #define SH_HANDLEFUNC_vafmt(paramtypes, params_orig, params_plug, rettype) \ SH_SETUPCALLS(rettype, paramtypes, params_orig) \ SH_CALL_HOOKS(pre, params_plug) \ @@ -3497,6 +3553,121 @@ SH_CALL2(SourceHook::CallClass *ptr, MFP mfp, RetType(X::*mfp2)(Param1, Param #undef SH_MAKE_EXECUTABLECLASS_OB +////////////////////////////////////////////////////////////////////////// +// SetOverrideRet for recalls +// These take a ISourceHook pointer instead of using SH_GLOB_SHPTR directly +// The reason is that the user may want to redefine SH_GLOB_SHPTR - then the macros +// (META_RETURN_VALUE_NEWPARAMS) should obey the new pointer. + +namespace SourceHook +{ + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17, Param18), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17, Param18, Param19), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } + template + void SetOverrideResult(ISourceHook *shptr, RetType (Iface::*mfp)(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15, Param16, Param17, Param18, Param19, Param20), const RetType res) + { + *reinterpret_cast(shptr->GetOverrideRetPtr()) = res; + } +} + #endif // The pope is dead. -> :( diff --git a/sourcehook/sourcehook_impl.h b/sourcehook/sourcehook_impl.h index b9a44fa..a96f833 100644 --- a/sourcehook/sourcehook_impl.h +++ b/sourcehook/sourcehook_impl.h @@ -76,6 +76,7 @@ namespace SourceHook virtual ~CIter(); void GoToBegin(); + void Set(CIter *pOther); bool End(); void Next(); @@ -89,7 +90,16 @@ namespace SourceHook }; CIter *m_FreeIters; - CIter *m_UsedIters; + CIter *m_UsedIters; // The last returned and not-yet-released iter is always m_UsedIters + + // For recalls + bool m_Recall; + + void SetRecallState(); // Sets the list into a state where the next returned + // iterator (from GetIter) will be a copy of the last + // returned iterator, incremented by one. This is used in Recalls. + // The hook resets this state automatically on: + // GetIter, ReleaseIter CHookList(); CHookList(const CHookList &other); @@ -165,6 +175,8 @@ namespace SourceHook VfnPtrList m_VfnPtrs; + int m_HookFuncVersion; + public: virtual ~CHookManagerInfo(); @@ -221,10 +233,11 @@ namespace SourceHook META_RES *pCurRes; bool shouldContinue; + bool recall; //!< True if we're in a recall, eh. IIface *pCurIface; const void *pOrigRet; - const void *pOverrideRet; + void *pOverrideRet; void **pIfacePtrPtr; }; typedef CStack HookLoopInfoStack; @@ -357,7 +370,7 @@ namespace SourceHook void SetStatusPtr(META_RES *mres); //!< Sets pointer to the status variable void SetIfacePtrPtr(void **pp); //!< Sets pointer to the interface this pointer void SetOrigRetPtr(const void *ptr); //!< Sets the original return pointer - void SetOverrideRetPtr(const void *ptr); //!< Sets the override result pointer + void SetOverrideRetPtr(void *ptr); //!< Sets the override result pointer bool ShouldContinue(); //!< Returns false if the hook loop should exit /** @@ -367,6 +380,11 @@ namespace SourceHook * @param pubFunc The hook manager's info function */ virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc); + virtual void DoRecall(); //!< Initiates a recall sequence + virtual void *GetOverrideRetPtr(); //!< Returns the pointer set by SetOverrideRetPtr + + virtual void *SetupHookLoop(META_RES *statusPtr, META_RES *prevResPtr, META_RES *curResPtr, + void **ifacePtrPtr, const void *origRetPtr, void *overrideRetPtr); }; } diff --git a/sourcehook/test/main.cpp b/sourcehook/test/main.cpp index 03eb17a..349878f 100644 --- a/sourcehook/test/main.cpp +++ b/sourcehook/test/main.cpp @@ -2,6 +2,7 @@ // hello pm how are you // I'm fine, what about you? // not bad, just looking for mem leaks +// mem leaks in my code!? never! I have to preserve binary compatibility :( // This is a test file #include @@ -75,6 +76,7 @@ DO_TEST(PlugSys); DO_TEST(Bail); DO_TEST(Reentr); DO_TEST(Manual); +DO_TEST(Recall); int main(int argc, char *argv[]) { @@ -90,3 +92,37 @@ int main(int argc, char *argv[]) cin.read(&x, 1); } +SourceHook::ISourceHook *Test_Factory() +{ + return new SourceHook::CSourceHookImpl(); +} + +void Test_Delete(SourceHook::ISourceHook *shptr) +{ + delete static_cast(shptr); +} + +void Test_CompleteShutdown(SourceHook::ISourceHook *shptr) +{ + static_cast(shptr)->CompleteShutdown(); +} + +bool Test_IsPluginInUse(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug) +{ + return static_cast(shptr)->IsPluginInUse(plug); +} + +void Test_UnloadPlugin(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug) +{ + static_cast(shptr)->UnloadPlugin(plug); +} + +void Test_PausePlugin(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug) +{ + static_cast(shptr)->PausePlugin(plug); +} + +void Test_UnpausePlugin(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug) +{ + static_cast(shptr)->UnpausePlugin(plug); +} \ No newline at end of file diff --git a/sourcehook/test/sourcehook_test.h b/sourcehook/test/sourcehook_test.h new file mode 100644 index 0000000..9d914b4 --- /dev/null +++ b/sourcehook/test/sourcehook_test.h @@ -0,0 +1,27 @@ +// This file is used for backwards compatibility testing +// It allows us to test binary backwards compatibility by using an older include file HERE: +#include "sourcehook.h" // <-- here +// There. main.cpp which implements all of the following function is always usign sourcehook.h +// and the up-to-date sourcehook_impl.h/sourcehook.cpp. The tests use this file however. +// If the test needs an up-to-date version (like the recall test), it can simply +// #include "sourcehook.h" before including this, thus overriding our decision. + + +SourceHook::ISourceHook *Test_Factory(); +void Test_Delete(SourceHook::ISourceHook *shptr); + +struct CSHPtrAutoDestruction +{ + SourceHook::ISourceHook *m_SHPtr; + CSHPtrAutoDestruction(SourceHook::ISourceHook *shptr) : m_SHPtr(shptr) {} + ~CSHPtrAutoDestruction() { Test_Delete(m_SHPtr); } +}; + +#define GET_SHPTR(var) var = Test_Factory(); CSHPtrAutoDestruction ___autodestruction(var); + +// Access to CSourceHookImpl functions +void Test_CompleteShutdown(SourceHook::ISourceHook *shptr); +bool Test_IsPluginInUse(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug); +void Test_UnloadPlugin(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug); +void Test_PausePlugin(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug); +void Test_UnpausePlugin(SourceHook::ISourceHook *shptr, SourceHook::Plugin plug); diff --git a/sourcehook/test/test.vcproj b/sourcehook/test/test.vcproj index cd88316..f581a13 100644 --- a/sourcehook/test/test.vcproj +++ b/sourcehook/test/test.vcproj @@ -262,6 +262,9 @@ + + @@ -277,6 +280,9 @@ GeneratePreprocessedFile="0"/> + + @@ -305,6 +311,9 @@ + + diff --git a/sourcehook/test/test1.cpp b/sourcehook/test/test1.cpp index d7e50f9..3c08a68 100644 --- a/sourcehook/test/test1.cpp +++ b/sourcehook/test/test1.cpp @@ -1,5 +1,5 @@ #include -#include "sourcehook_impl.h" +#include "sourcehook_test.h" #include "testevents.h" #include "sh_memory.h" @@ -92,7 +92,7 @@ namespace virtual void F57(){} virtual void F58(){} virtual void F59(){} - virtual void F60(){} + virtual void F60(int &hello){} virtual void F61(){} virtual void F62(){} virtual void F63(){} @@ -350,6 +350,8 @@ namespace SH_DECL_HOOK0_void(Test, F9, SH_NOATTRIB, 0); SH_DECL_HOOK0_void(Test, F10, SH_NOATTRIB, 0); + SH_DECL_HOOK1_void(Test, F60, SH_NOATTRIB, 0, int&); + META_RES g_F1Pre_WhatToDo; META_RES g_F1Post_WhatToDo; @@ -384,8 +386,18 @@ namespace !META_RESULT_ORIG_RET(bool)); } + void F60_Pre(int &hello) + { + hello = 10; + } } +template T func(T a) +{ + return a; +} + + bool TestBasic(std::string &error) { // Simple test for ModuleInMemory @@ -399,8 +411,7 @@ bool TestBasic(std::string &error) new State_ModuleInMemory(false), NULL), "ModuleInMemory"); - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + GET_SHPTR(g_SHPtr); g_PLID = 1337; HandlersF1 f1_handlers; @@ -676,7 +687,13 @@ bool TestBasic(std::string &error) SH_REMOVE_HOOK_MEMFUNC(Test, F9, pTest, &f1_handlers, &HandlersF1::Pre, false); SH_REMOVE_HOOK_MEMFUNC(Test, F10, pTest, &f1_handlers, &HandlersF1::Pre, false); - g_SHImpl.CompleteShutdown(); + SH_ADD_HOOK_STATICFUNC(Test, F60, pTest, F60_Pre, false); + + int a = 0; + pTest->F60(a); + Test_CompleteShutdown(g_SHPtr); + + CHECK_COND(a == 10, "Part12.a"); return true; } diff --git a/sourcehook/test/test2.cpp b/sourcehook/test/test2.cpp index a365611..e9b814e 100644 --- a/sourcehook/test/test2.cpp +++ b/sourcehook/test/test2.cpp @@ -1,5 +1,5 @@ #include -#include "sourcehook_impl.h" +#include "sourcehook_test.h" #include "testevents.h" #include @@ -89,8 +89,7 @@ namespace bool TestVafmtAndOverload(std::string &error) { - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + GET_SHPTR(g_SHPtr); g_PLID = 1337; IGaben gabgab; diff --git a/sourcehook/test/test3.cpp b/sourcehook/test/test3.cpp index 01aaa2c..868e730 100644 --- a/sourcehook/test/test3.cpp +++ b/sourcehook/test/test3.cpp @@ -1,5 +1,5 @@ #include -#include "sourcehook_impl.h" +#include "sourcehook_test.h" #include "testevents.h" // TEST3 @@ -69,8 +69,7 @@ namespace bool TestThisPtrOffs(std::string &error) { - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + GET_SHPTR(g_SHPtr); g_PLID = 1337; Derived inst; diff --git a/sourcehook/test/test4.cpp b/sourcehook/test/test4.cpp index 004d5d0..b0e2f32 100644 --- a/sourcehook/test/test4.cpp +++ b/sourcehook/test/test4.cpp @@ -1,5 +1,5 @@ #include -#include "sourcehook_impl.h" +#include "sourcehook_test.h" #include "testevents.h" // TEST4 @@ -60,8 +60,7 @@ namespace bool TestPlugSys(std::string &error) { - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + GET_SHPTR(g_SHPtr); g_PLID = 1; Test inst; @@ -86,7 +85,7 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 1.1"); - g_SHImpl.CompleteShutdown(); + Test_CompleteShutdown(g_SHPtr); pInst->Func1(); pInst->Func2(); @@ -139,10 +138,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 2.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -152,7 +151,7 @@ bool TestPlugSys(std::string &error) NULL), "Part 2.2"); // Unload plugins one by one - g_SHImpl.UnloadPlugin(3); + Test_UnloadPlugin(g_SHPtr, 3); pInst->Func1(); pInst->Func2(); @@ -170,10 +169,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 2.3.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -182,7 +181,7 @@ bool TestPlugSys(std::string &error) new State_PluginInUse(4, false), NULL), "Part 2.3.2"); - g_SHImpl.UnloadPlugin(2); + Test_UnloadPlugin(g_SHPtr, 2); pInst->Func1(); pInst->Func2(); @@ -197,10 +196,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 2.4.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -209,7 +208,7 @@ bool TestPlugSys(std::string &error) new State_PluginInUse(4, false), NULL), "Part 2.4.2"); - g_SHImpl.UnloadPlugin(1); + Test_UnloadPlugin(g_SHPtr, 1); pInst->Func1(); pInst->Func2(); @@ -221,10 +220,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 2.5.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, false), @@ -275,10 +274,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 3.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -288,7 +287,7 @@ bool TestPlugSys(std::string &error) NULL), "Part 3.2"); // Unload plugins one by one - g_SHImpl.PausePlugin(3); + Test_PausePlugin(g_SHPtr, 3); pInst->Func1(); pInst->Func2(); @@ -306,10 +305,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 3.3.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -318,7 +317,7 @@ bool TestPlugSys(std::string &error) new State_PluginInUse(4, false), NULL), "Part 3.3.2"); - g_SHImpl.PausePlugin(2); + Test_PausePlugin(g_SHPtr, 2); pInst->Func1(); pInst->Func2(); @@ -333,10 +332,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 3.4.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -345,7 +344,7 @@ bool TestPlugSys(std::string &error) new State_PluginInUse(4, false), NULL), "Part 3.4.2"); - g_SHImpl.PausePlugin(1); + Test_PausePlugin(g_SHPtr, 1); pInst->Func1(); pInst->Func2(); @@ -357,10 +356,10 @@ bool TestPlugSys(std::string &error) new State_Func3_Called, NULL), "Part 3.5.1"); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -369,14 +368,14 @@ bool TestPlugSys(std::string &error) new State_PluginInUse(4, false), NULL), "Part 3.5.2"); - g_SHImpl.UnpausePlugin(1); - g_SHImpl.UnpausePlugin(2); - g_SHImpl.UnpausePlugin(3); + Test_UnpausePlugin(g_SHPtr, 1); + Test_UnpausePlugin(g_SHPtr, 2); + Test_UnpausePlugin(g_SHPtr, 3); - ADD_STATE(State_PluginInUse(1, g_SHImpl.IsPluginInUse(1))); - ADD_STATE(State_PluginInUse(2, g_SHImpl.IsPluginInUse(2))); - ADD_STATE(State_PluginInUse(3, g_SHImpl.IsPluginInUse(3))); - ADD_STATE(State_PluginInUse(4, g_SHImpl.IsPluginInUse(4))); + ADD_STATE(State_PluginInUse(1, Test_IsPluginInUse(g_SHPtr, 1))); + ADD_STATE(State_PluginInUse(2, Test_IsPluginInUse(g_SHPtr, 2))); + ADD_STATE(State_PluginInUse(3, Test_IsPluginInUse(g_SHPtr, 3))); + ADD_STATE(State_PluginInUse(4, Test_IsPluginInUse(g_SHPtr, 4))); CHECK_STATES((&g_States, new State_PluginInUse(1, true), @@ -405,7 +404,7 @@ bool TestPlugSys(std::string &error) NULL), "Part 3.7"); // 4) Shutdown :) - g_SHImpl.CompleteShutdown(); + Test_CompleteShutdown(g_SHPtr); pInst->Func1(); pInst->Func2(); diff --git a/sourcehook/test/testbail.cpp b/sourcehook/test/testbail.cpp index d42f38d..53b7dd5 100644 --- a/sourcehook/test/testbail.cpp +++ b/sourcehook/test/testbail.cpp @@ -1,74 +1,103 @@ // TESTBAIL -// Test for a bug Bail has found +// This test used to be a test for a bug BAIL found. +// That bug is now fixed so I've granted BAIL the pleasure of being a test for +// the correct priority ordering of hook managers based on their version. +/* + THE PROBLEM: + Old hook funcs don't work right when you combine override returns and recalls. + THE SOLUTION: + Always use a new hook func when possible. For this, hook funcs have to be able to say + " HELLO I'M NEW! " + + This file tests that functionality. + + How it works: + testbail.cpp compiles with old version of sourcehook.h. + It sets everything up, adds a hook on a function + Then testbail2.cpp which has the new version adds a hook on the same function and + does a recall and overrides the value in it. +*/ + +#include "sourcehook_test.h" #include "testbail.h" void *___testbail_gabgab; +SourceHook::ISourceHook *___testbail_shptr; namespace { - class zomg_lolz - { - public: - virtual void zomg() - { - } - }; - SH_DECL_HOOK0_void(zomg_lolz, zomg, SH_NOATTRIB, 0); - void Handler() - { - SH_REMOVE_HOOK_STATICFUNC(zomg_lolz, zomg, META_IFACEPTR(zomg_lolz), - Handler, false); - } - void Handler2() + int EatYams_Handler1(int a) { + ADD_STATE(State_EatYams_Handler1_Called(a)); + RETURN_META_VALUE(MRES_IGNORED, 0); } } +// These are here so they can access this CU's g_States +int ___testbail_EatYams_Handler2(int a) +{ + ADD_STATE(State_EatYams_Handler2_Called(a)); + RETURN_META_VALUE_NEWPARAMS(MRES_OVERRIDE, 6, IGaben, EatYams, (0xBEEF)); +} + +int ___testbail_EatYams_Handler3(int a) +{ + ADD_STATE(State_EatYams_Handler3_Called(a)); + RETURN_META_VALUE(MRES_IGNORED, 0); +} + bool TestBail(std::string &error) { - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + GET_SHPTR(g_SHPtr); g_PLID = 1; g_Gabgab = new IGaben; ___testbail_gabgab = (void*)g_Gabgab; + ___testbail_shptr = g_SHPtr; - g_Gabgab->EatYams(); + SH_ADD_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, EatYams_Handler1, false); - SH_ADD_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, EatYams0_Handler, false); + ADD_STATE(State_EatYams_Return(g_Gabgab->EatYams(0xDEAD))); - g_Gabgab->EatYams(); + CHECK_STATES((&g_States, + new State_EatYams_Handler1_Called(0xDEAD), + new State_EatYams_Called(0xDEAD), + new State_EatYams_Return(5), + NULL), "Part 1"); - ___TestBail2(); - - g_Gabgab->EatYams(); + if (!___TestBail2(error)) + return false; - SH_REMOVE_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, EatYams0_Handler, false); + CHECK_STATES((&g_States, + new State_EatYams_Handler1_Called(0xDEAD), + new State_EatYams_Handler2_Called(0xDEAD), + new State_EatYams_Handler3_Called(0xBEEF), + new State_EatYams_Called(0xBEEF), + NULL), "Part 2.1"); - g_Gabgab->EatYams(); + // WHAT IF NOW SOMEONE UNLOADS PLUGIN 2 !?!?!?!? + Test_UnloadPlugin(g_SHPtr, 2); + + ADD_STATE(State_EatYams_Return(g_Gabgab->EatYams(0xDEAD))); + + CHECK_STATES((&g_States, + new State_EatYams_Handler1_Called(0xDEAD), + new State_EatYams_Called(0xDEAD), + new State_EatYams_Return(5), + NULL), "Part 3"); + + SH_REMOVE_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, EatYams_Handler1, false); + + ADD_STATE(State_EatYams_Return(g_Gabgab->EatYams(0xDEAD))); + + CHECK_STATES((&g_States, + new State_EatYams_Called(0xDEAD), + new State_EatYams_Return(5), + NULL), "Part 4"); delete g_Gabgab; - // If it didn't crash, it's ok - - // NEW TEST: Remove hook from handler - zomg_lolz inst; - SH_ADD_HOOK_STATICFUNC(zomg_lolz, zomg, &inst, Handler, false); - SH_ADD_HOOK_STATICFUNC(zomg_lolz, zomg, &inst, Handler2, false); - - zomg_lolz *mwah = &inst; - mwah->zomg(); - mwah->zomg(); - - SH_ADD_HOOK_STATICFUNC(zomg_lolz, zomg, &inst, Handler, false); - SH_REMOVE_HOOK_STATICFUNC(zomg_lolz, zomg, &inst, Handler2, false); - - mwah->zomg(); - mwah->zomg(); - - // Shouldn't crash again... - return true; } diff --git a/sourcehook/test/testbail.h b/sourcehook/test/testbail.h index 62e6392..30a3b1a 100644 --- a/sourcehook/test/testbail.h +++ b/sourcehook/test/testbail.h @@ -2,13 +2,9 @@ // Shared data for testbail #include -#include "sourcehook_impl.h" #include "testevents.h" -#include -#include -#include -void ___TestBail2(); +bool ___TestBail2(std::string &error); namespace { @@ -16,79 +12,28 @@ namespace SourceHook::ISourceHook *g_SHPtr; SourceHook::Plugin g_PLID; - MAKE_STATE(State_EatYams_Called); - MAKE_STATE(State_EatYams_Handler_Called); - + MAKE_STATE_1(State_EatYams_Called, int); + MAKE_STATE_1(State_EatYams_Handler1_Called, int); + MAKE_STATE_1(State_EatYams_Handler2_Called, int); + MAKE_STATE_1(State_EatYams_Handler3_Called, int); + MAKE_STATE_1(State_EatYams_Return, int); class IGaben { public: - virtual void EatYams() + virtual int EatYams(int a) { - ADD_STATE(State_EatYams_Called); + ADD_STATE(State_EatYams_Called(a)); + return 5; } }; - SH_DECL_HOOK0_void(IGaben, EatYams, SH_NOATTRIB, 0); - - /* - SHINT_MAKE_GENERICSTUFF_BEGIN(IGaben, EatYams, 0, (static_cast - (&IGaben::EatYams))) - typedef fastdelegate::FastDelegate0<> FD; - virtual void Func() - { - // SH_HANDLEFUNC_void(IGaben, EatYams, (), ()); - - SH_SETUPCALLS_void((), ()) - SH_CALL_HOOKS_void(pre, ()) - if (status != MRES_SUPERCEDE) - { - void (EmptyClass::*mfp)(); - SH_SETUP_MFP(mfp); - (reinterpret_cast(ifinfo->GetPtr())->*mfp)(); - } - SH_CALL_HOOKS_void(post, ()) - SH_RETURN_void() - } - }; - SH_FHCls(IGaben,EatYams,0) SH_FHCls(IGaben,EatYams,0)::ms_Inst; - ::SourceHook::MemFuncInfo SH_FHCls(IGaben,EatYams,0)::ms_MFI; - ::SourceHook::IHookManagerInfo *SH_FHCls(IGaben,EatYams,0)::ms_HI; - const char *SH_FHCls(IGaben,EatYams,0)::ms_Proto = "SH_NOATTRIB"; - bool __SourceHook_FHAddIGabenEatYams(void *iface, bool post, - SH_FHCls(IGaben,EatYams,0)::FD handler) - { - using namespace ::SourceHook; - MemFuncInfo mfi; - GetFuncInfo((static_cast(&IGaben::EatYams)), mfi); - if (mfi.thisptroffs < 0) - return false; - - return SH_GLOB_SHPTR->AddHook(SH_GLOB_PLUGPTR, iface, mfi.thisptroffs, - SH_FHCls(IGaben,EatYams,0)::HookManPubFunc, - new CSHDelegate(handler), post); - } - bool __SourceHook_FHRemoveIGabenEatYams(void *iface, bool post, - SH_FHCls(IGaben,EatYams,0)::FD handler) - { - using namespace ::SourceHook; - MemFuncInfo mfi; - GetFuncInfo((static_cast(&IGaben::EatYams)), mfi); - if (mfi.thisptroffs < 0) - return false; - - CSHDelegate tmp(handler); - return SH_GLOB_SHPTR->RemoveHook(SH_GLOB_PLUGPTR, iface, mfi.thisptroffs, - SH_FHCls(IGaben,EatYams,0)::HookManPubFunc, &tmp, post); - } - */ - - void EatYams0_Handler() - { - ADD_STATE(State_EatYams_Handler_Called); - } + SH_DECL_HOOK1(IGaben, EatYams, SH_NOATTRIB, 0, int, int); IGaben *g_Gabgab; } extern void *___testbail_gabgab; +extern SourceHook::ISourceHook *___testbail_shptr; +extern int ___testbail_EatYams_Handler2(int a); +extern int ___testbail_EatYams_Handler3(int a); \ No newline at end of file diff --git a/sourcehook/test/testbail2.cpp b/sourcehook/test/testbail2.cpp index ce98700..543fbbb 100644 --- a/sourcehook/test/testbail2.cpp +++ b/sourcehook/test/testbail2.cpp @@ -1,23 +1,25 @@ // TESTBAIL // Different compilation unit +#include "sourcehook.h" +#include "sourcehook_test.h" #include "testbail.h" -void ___TestBail2() +// :TODO: Test new-old proto system compa + +bool ___TestBail2(std::string &error) { - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + g_SHPtr = ___testbail_shptr; g_PLID = 2; g_Gabgab = (IGaben*)___testbail_gabgab; - g_Gabgab->EatYams(); + SH_ADD_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, ___testbail_EatYams_Handler2, false); + SH_ADD_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, ___testbail_EatYams_Handler3, false); - SH_ADD_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, EatYams0_Handler, false); - - g_Gabgab->EatYams(); + int ret = g_Gabgab->EatYams(0xDEAD); - SH_REMOVE_HOOK_STATICFUNC(IGaben, EatYams, g_Gabgab, EatYams0_Handler, false); - - g_Gabgab->EatYams(); + CHECK_COND(ret == 6, "Part 2.1"); + + return true; } diff --git a/sourcehook/test/testevents.h b/sourcehook/test/testevents.h index 2504169..712860a 100644 --- a/sourcehook/test/testevents.h +++ b/sourcehook/test/testevents.h @@ -39,9 +39,6 @@ namespace typedef std::list StateList; - #define ADD_STATE(name) g_States.push_back(new name) - - void DumpStates(StateList *sl) { for (StateList::iterator iter = sl->begin(); iter != sl->end(); ++iter) @@ -101,6 +98,9 @@ namespace } } +#define ADD_STATE(name) g_States.push_back(new name) +#define ADD_STATE_PTR(statesptr, name) statesptr->push_back(new name) + #define CHECK_STATES(mwah, myerr) if (!StatesOk mwah) { error=myerr; return false; } else if (g_Verbose) { std::cout << "No error: " << myerr << std::endl; } #define MAKE_STATE(name) struct name : State { \ diff --git a/sourcehook/test/testmanual.cpp b/sourcehook/test/testmanual.cpp index c220d2c..2221e8e 100644 --- a/sourcehook/test/testmanual.cpp +++ b/sourcehook/test/testmanual.cpp @@ -1,5 +1,6 @@ #include -#include "sourcehook_impl.h" +#include "sourcehook.h" +#include "sourcehook_test.h" #include "testevents.h" // TESTMANUAL @@ -94,8 +95,7 @@ namespace bool TestManual(std::string &error) { - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + GET_SHPTR(g_SHPtr); g_PLID = 1337; TheWall inst; diff --git a/sourcehook/test/testrecall.cpp b/sourcehook/test/testrecall.cpp new file mode 100644 index 0000000..7009a1b --- /dev/null +++ b/sourcehook/test/testrecall.cpp @@ -0,0 +1,131 @@ +#include +#include "sourcehook.h" +#include "sourcehook_test.h" +#include "testevents.h" + +// TESTRECALL +// Test modifying parameters from hook handlers + +namespace +{ + StateList g_States; + SourceHook::ISourceHook *g_SHPtr; + SourceHook::Plugin g_PLID; + + MAKE_STATE_1(State_Func1, int); + MAKE_STATE_1(State_H1_Func1, int); + MAKE_STATE_1(State_H2_Func1, int); + MAKE_STATE_2(State_HP_Func1, int, void*); + + MAKE_STATE_1(State_Func2, int); + MAKE_STATE_1(State_H1_Func2, int); + MAKE_STATE_2(State_HP_Func2, int, int); + + struct Test + { + virtual void Func1(int a) + { + ADD_STATE(State_Func1(a)); + } + + virtual int Func2(int a) + { + ADD_STATE(State_Func2(a)); + return 1000; + } + }; + + void Handler1_Func1(int a) + { + ADD_STATE(State_H1_Func1(a)); + RETURN_META_NEWPARAMS(MRES_IGNORED, Test, Func1, (5)); + } + void Handler2_Func1(int a) + { + ADD_STATE(State_H2_Func1(a)); + RETURN_META_NEWPARAMS(MRES_IGNORED, Test, Func1, (a - 5)); + } + void HandlerPost_Func1(int a) + { + ADD_STATE(State_HP_Func1(a, META_IFACEPTR(void))); + } + + + int Handler1_Func2(int a) + { + ADD_STATE(State_H1_Func2(a)); + RETURN_META_VALUE_NEWPARAMS(MRES_OVERRIDE, 500, Test, Func2, (a - 10)); + } + + int HandlerPost_Func2(int a) + { + ADD_STATE(State_HP_Func2(a, META_RESULT_ORIG_RET(int))); + RETURN_META_VALUE(MRES_IGNORED, 0); + } + + SH_DECL_HOOK1_void(Test, Func1, SH_NOATTRIB, 0, int); + SH_DECL_HOOK1(Test, Func2, SH_NOATTRIB, 0, int, int); +} + +bool TestRecall(std::string &error) +{ + GET_SHPTR(g_SHPtr); + g_PLID = 1337; + + Test inst; + Test *ptr = &inst; + + SH_ADD_HOOK_STATICFUNC(Test, Func1, ptr, Handler1_Func1, false); + SH_ADD_HOOK_STATICFUNC(Test, Func1, ptr, Handler2_Func1, false); + SH_ADD_HOOK_STATICFUNC(Test, Func1, ptr, HandlerPost_Func1, true); + + ptr->Func1(77); + + CHECK_STATES((&g_States, + new State_H1_Func1(77), + new State_H2_Func1(5), + new State_Func1(0), + new State_HP_Func1(0, ptr), + NULL), "Part 1"); + + SH_REMOVE_HOOK_STATICFUNC(Test, Func1, ptr, Handler1_Func1, false); + SH_ADD_HOOK_STATICFUNC(Test, Func1, ptr, Handler2_Func1, false); + SH_ADD_HOOK_STATICFUNC(Test, Func1, ptr, Handler2_Func1, false); + SH_ADD_HOOK_STATICFUNC(Test, Func1, ptr, Handler2_Func1, false); + + ptr->Func1(77); + + CHECK_STATES((&g_States, + new State_H2_Func1(77), + new State_H2_Func1(72), + new State_H2_Func1(67), + new State_H2_Func1(62), + new State_Func1(57), + new State_HP_Func1(57, ptr), + NULL), "Part 2"); + + SH_REMOVE_HOOK_STATICFUNC(Test, Func1, ptr, Handler2_Func1, false); + SH_REMOVE_HOOK_STATICFUNC(Test, Func1, ptr, HandlerPost_Func1, true); + + ptr->Func1(77); + + CHECK_STATES((&g_States, + new State_Func1(77), + NULL), "Part 3"); + + // Func2 + + SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, Handler1_Func2, false); + SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, HandlerPost_Func2, true); + + int a = ptr->Func2(77); + CHECK_STATES((&g_States, + new State_H1_Func2(77), + new State_Func2(67), + new State_HP_Func2(67, 1000), // 1000 because it's the ORIG_RET + NULL), "Part 4"); + + CHECK_COND(a == 500, "Part 4.1"); + + return true; +} diff --git a/sourcehook/test/testreentr.cpp b/sourcehook/test/testreentr.cpp index 2acf1da..39f6446 100644 --- a/sourcehook/test/testreentr.cpp +++ b/sourcehook/test/testreentr.cpp @@ -2,7 +2,7 @@ // = calling hooks from hook handlers, etc #include -#include "sourcehook_impl.h" +#include "sourcehook_test.h" #include "testevents.h" namespace @@ -294,8 +294,7 @@ namespace bool TestReentr(std::string &error) { - SourceHook::CSourceHookImpl g_SHImpl; - g_SHPtr = &g_SHImpl; + GET_SHPTR(g_SHPtr); g_PLID = 1337; g_pC1 = &g_C1;