#include #include "sourcehook.h" #include "sourcehook_test.h" #include "testevents.h" // TEST VP HOOKS // Test vfnptr-wide hooks // Also contains a test for removing hooks on deleted instances (by id) namespace { StateList g_States; SourceHook::ISourceHook *g_SHPtr; SourceHook::Plugin g_PLID; class IBase; MAKE_STATE_1(State_D1_Func1, IBase *); MAKE_STATE_1(State_D2_Func1, IBase *); MAKE_STATE_1(State_Func1_Pre, IBase *); MAKE_STATE_1(State_Func1_Post, IBase *); MAKE_STATE_1(State_D1_Func2, IBase *); MAKE_STATE_1(State_D2_Func2, IBase *); MAKE_STATE_1(State_Func2_Pre, IBase *); MAKE_STATE_1(State_Func2_Post, IBase *); MAKE_STATE_2(State_D1_Func3, IBase *, int); MAKE_STATE_2(State_D2_Func3, IBase *, int); MAKE_STATE_2(State_Func3_Pre, IBase *, int); MAKE_STATE_2(State_Func3_Post, IBase *, int); class IBase { public: virtual void Func1() = 0; virtual void Func2() = 0; virtual void Func3(int x) = 0; }; class CDerived1 : public IBase { public: virtual void Func1() { ADD_STATE(State_D1_Func1(this)); } virtual void Func2() { ADD_STATE(State_D1_Func2(this)); } virtual void Func3(int x) { ADD_STATE(State_D1_Func3(this, x)); } }; class CDerived2 : public IBase { public: virtual void Func1() { ADD_STATE(State_D2_Func1(this)); } virtual void Func2() { ADD_STATE(State_D2_Func2(this)); } virtual void Func3(int x) { ADD_STATE(State_D2_Func3(this, x)); } }; void Handler_Func1_Pre() { ADD_STATE(State_Func1_Pre(META_IFACEPTR(IBase))); } void Handler_Func1_Post() { ADD_STATE(State_Func1_Post(META_IFACEPTR(IBase))); } int g_F2_Pre_HookToRemove = 0; void Handler_Func2_Pre() { ADD_STATE(State_Func2_Pre(META_IFACEPTR(IBase))); SH_REMOVE_HOOK_ID(g_F2_Pre_HookToRemove); } void Handler_Func2_Post() { ADD_STATE(State_Func2_Post(META_IFACEPTR(IBase))); } void Handler_Func3_Pre(int x) { ADD_STATE(State_Func3_Pre(META_IFACEPTR(IBase), x)); RETURN_META_NEWPARAMS(MRES_IGNORED, &IBase::Func3, (x+1)); } void Handler_Func3_Post(int x) { ADD_STATE(State_Func3_Post(META_IFACEPTR(IBase), x)); } SH_DECL_HOOK0_void(IBase, Func1, SH_NOATTRIB, 0); SH_DECL_HOOK0_void(IBase, Func2, SH_NOATTRIB, 0); SH_DECL_HOOK1_void(IBase, Func3, SH_NOATTRIB, 0, int); SH_DECL_MANUALHOOK1_void(IBase_Func3_Manual, 2, 0, 0, int); } bool TestVPHooks(std::string &error) { GET_SHPTR(g_SHPtr); g_PLID = 1337; CDerived1 d1i1; CDerived1 d1i2; CDerived2 d2i1; IBase *p_d1i1 = &d1i1; IBase *p_d1i2 = &d1i2; IBase *p_d2i1 = &d2i1; int hook1 = SH_ADD_VPHOOK(IBase, Func1, p_d1i1, SH_STATIC(Handler_Func1_Pre), false); p_d1i1->Func1(); p_d1i2->Func1(); p_d2i1->Func1(); CHECK_STATES((&g_States, new State_Func1_Pre(p_d1i1), new State_D1_Func1(p_d1i1), new State_Func1_Pre(p_d1i2), new State_D1_Func1(p_d1i2), new State_D2_Func1(p_d2i1), NULL), "Part 1"); SH_CALL(p_d1i1, &IBase::Func1)(); SH_CALL(p_d1i2, &IBase::Func1)(); SH_CALL(p_d2i1, &IBase::Func1)(); CHECK_STATES((&g_States, new State_D1_Func1(p_d1i1), new State_D1_Func1(p_d1i2), new State_D2_Func1(p_d2i1), NULL), "Part 1.1"); SH_REMOVE_HOOK_ID(hook1); p_d1i1->Func1(); p_d1i2->Func1(); p_d2i1->Func1(); CHECK_STATES((&g_States, new State_D1_Func1(p_d1i1), new State_D1_Func1(p_d1i2), new State_D2_Func1(p_d2i1), NULL), "Part 2"); // Normal hook, then vp hook int hook2 = SH_ADD_HOOK(IBase, Func1, p_d1i1, SH_STATIC(Handler_Func1_Pre), false); hook1 = SH_ADD_VPHOOK(IBase, Func1, p_d1i1, SH_STATIC(Handler_Func1_Pre), false); p_d1i1->Func1(); p_d1i2->Func1(); p_d2i1->Func1(); CHECK_STATES((&g_States, new State_Func1_Pre(p_d1i1), new State_Func1_Pre(p_d1i1), new State_D1_Func1(p_d1i1), new State_Func1_Pre(p_d1i2), new State_D1_Func1(p_d1i2), new State_D2_Func1(p_d2i1), NULL), "Part 3"); SH_REMOVE_HOOK_ID(hook1); p_d1i1->Func1(); p_d1i2->Func1(); p_d2i1->Func1(); CHECK_STATES((&g_States, new State_Func1_Pre(p_d1i1), new State_D1_Func1(p_d1i1), new State_D1_Func1(p_d1i2), new State_D2_Func1(p_d2i1), NULL), "Part 4"); SH_REMOVE_HOOK_ID(hook2); p_d1i1->Func1(); p_d1i2->Func1(); p_d2i1->Func1(); CHECK_STATES((&g_States, new State_D1_Func1(p_d1i1), new State_D1_Func1(p_d1i2), new State_D2_Func1(p_d2i1), NULL), "Part 5"); // Test this: // Normal hook AND vp hook on Func2 // Func2's pre handler removes the VP hook. It has to work anyway :) hook1 = SH_ADD_VPHOOK(IBase, Func2, p_d1i1, SH_STATIC(Handler_Func2_Pre), false); hook2 = SH_ADD_HOOK(IBase, Func2, p_d1i1, SH_STATIC(Handler_Func2_Pre), false); g_F2_Pre_HookToRemove = hook1; p_d1i1->Func2(); p_d1i1->Func2(); CHECK_STATES((&g_States, new State_Func2_Pre(p_d1i1), new State_Func2_Pre(p_d1i1), new State_D1_Func2(p_d1i1), new State_Func2_Pre(p_d1i1), new State_D1_Func2(p_d1i1), NULL), "Part 6"); SH_REMOVE_HOOK_ID(hook1); // Hook function 3: // Using manualhook, VP hook1 = SH_ADD_MANUALVPHOOK(IBase_Func3_Manual, p_d1i1, SH_STATIC(Handler_Func3_Pre), false); // Normally, VP hook2 = SH_ADD_VPHOOK(IBase, Func3, p_d1i1, SH_STATIC(Handler_Func3_Pre), false); // Normally, no VP int hook3 = SH_ADD_HOOK(IBase, Func3, p_d1i1, SH_STATIC(Handler_Func3_Pre), false); p_d1i1->Func3(1); CHECK_STATES((&g_States, new State_Func3_Pre(p_d1i1, 1), // manual vp hook new State_Func3_Pre(p_d1i1, 2), // normal vp hook new State_Func3_Pre(p_d1i1, 3), // normal non-vp hook new State_D1_Func3(p_d1i1, 4), // function NULL), "Part 7.1"); p_d1i2->Func3(1); CHECK_STATES((&g_States, new State_Func3_Pre(p_d1i2, 1), // manual vp hook new State_Func3_Pre(p_d1i2, 2), // normal vp hook new State_D1_Func3(p_d1i2, 3), // function NULL), "Part 7.2"); SH_REMOVE_HOOK_ID(hook1); SH_REMOVE_HOOK_ID(hook2); SH_REMOVE_HOOK_ID(hook3); // Test direct VP hook p_d1i1->Func1(); p_d1i2->Func1(); // get vtable manually void *pVtable = *reinterpret_cast(p_d1i1); hook1 = SH_ADD_DVPHOOK(IBase, Func1, pVtable, SH_STATIC(Handler_Func1_Pre), false); p_d1i1->Func1(); p_d1i2->Func1(); CHECK_STATES((&g_States, new State_D1_Func1(p_d1i1), new State_D1_Func1(p_d1i2), new State_Func1_Pre(p_d1i1), new State_D1_Func1(p_d1i1), new State_Func1_Pre(p_d1i2), new State_D1_Func1(p_d1i2), NULL), "Part 8.1"); SH_REMOVE_HOOK_ID(hook1); // Now try manual dvp hooks p_d1i1->Func3(1); p_d1i2->Func3(1); hook1 = SH_ADD_MANUALDVPHOOK(IBase_Func3_Manual, pVtable, SH_STATIC(Handler_Func3_Pre), false); p_d1i1->Func3(1); p_d1i2->Func3(1); CHECK_STATES((&g_States, new State_D1_Func3(p_d1i1, 1), new State_D1_Func3(p_d1i2, 1), new State_Func3_Pre(p_d1i1, 1), // manual vp hook new State_D1_Func3(p_d1i1, 2), // function new State_Func3_Pre(p_d1i2, 1), // normal vp hook new State_D1_Func3(p_d1i2, 2), // function NULL), "Part 8.2"); // Test removing normal hooks even though the instance is deleted IBase *pOther = new CDerived1; SH_ADD_HOOK(IBase, Func1, pOther, SH_STATIC(Handler_Func1_Pre), false); delete pOther; // The following line may not crash! SH_REMOVE_HOOK(IBase, Func1, pOther, SH_STATIC(Handler_Func1_Pre), false); // Now test GetOrigVfnPtrEntry void *origfuncptr = (*reinterpret_cast(p_d1i1))[0]; CHECK_COND(SH_GET_ORIG_VFNPTR_ENTRY(p_d1i1, &IBase::Func1) == origfuncptr, "Part 9.1"); SH_ADD_HOOK(IBase, Func1, p_d1i1, SH_STATIC(Handler_Func1_Pre), false); CHECK_COND(SH_GET_ORIG_VFNPTR_ENTRY(p_d1i1, &IBase::Func1) == origfuncptr, "Part 9.2"); SH_REMOVE_HOOK(IBase, Func1, p_d1i1, SH_STATIC(Handler_Func1_Pre), false); CHECK_COND(SH_GET_ORIG_VFNPTR_ENTRY(p_d1i1, &IBase::Func1) == origfuncptr, "Part 9.3"); return true; }