mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2024-12-12 00:08:50 +01:00
164 lines
3.5 KiB
C++
164 lines
3.5 KiB
C++
|
#include <string>
|
||
|
#include "sourcehook_test.h"
|
||
|
#include "sh_pagealloc.h"
|
||
|
#include "testevents.h"
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
StateList g_States;
|
||
|
SourceHook::ISourceHook *g_SHPtr;
|
||
|
SourceHook::Plugin g_PLID;
|
||
|
|
||
|
MAKE_STATE(State_Func_Called);
|
||
|
MAKE_STATE(State_Pre1_Called);
|
||
|
MAKE_STATE(State_Pre2_Called);
|
||
|
|
||
|
int g_CallNumber;
|
||
|
|
||
|
class Test
|
||
|
{
|
||
|
public:
|
||
|
virtual void Func()
|
||
|
{
|
||
|
ADD_STATE(State_Func_Called);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
Test *g_pInst;
|
||
|
|
||
|
void Handler_Func_Pre1()
|
||
|
{
|
||
|
ADD_STATE(State_Pre1_Called);
|
||
|
}
|
||
|
|
||
|
SH_DECL_HOOK0_void(Test, Func, SH_NOATTRIB, 0);
|
||
|
|
||
|
void Handler_Func_Pre2()
|
||
|
{
|
||
|
ADD_STATE(State_Pre2_Called);
|
||
|
|
||
|
++g_CallNumber;
|
||
|
if (g_CallNumber == 2)
|
||
|
{
|
||
|
// REMOVE ourselves
|
||
|
SH_REMOVE_HOOK(Test, Func, g_pInst, SH_STATIC(Handler_Func_Pre1), false);
|
||
|
SH_REMOVE_HOOK(Test, Func, g_pInst, SH_STATIC(Handler_Func_Pre2), false);
|
||
|
}
|
||
|
|
||
|
// Call again.
|
||
|
g_pInst->Func();
|
||
|
}
|
||
|
|
||
|
Test *MyInstanceFactory()
|
||
|
{
|
||
|
return new Test;
|
||
|
}
|
||
|
|
||
|
SourceHook::CPageAlloc g_ThunkAllocator(2);
|
||
|
void *g_OddThunkMemory = NULL;
|
||
|
void *g_OddThunk = NULL;
|
||
|
|
||
|
// Guarantees that Func()
|
||
|
// has a vtable entry showing to an ODD address
|
||
|
void PatchFuncWithOddThunk()
|
||
|
{
|
||
|
g_OddThunkMemory = g_ThunkAllocator.Alloc(10);
|
||
|
unsigned char* base = reinterpret_cast<unsigned char*>(g_OddThunkMemory);
|
||
|
|
||
|
if (((ptrdiff_t)base) & 1)
|
||
|
{
|
||
|
// ODD, ok.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// EVEN. make odd.
|
||
|
base += 1;
|
||
|
}
|
||
|
|
||
|
// Get vtable entry pointer
|
||
|
SourceHook::MemFuncInfo info = {true, -1, 0, 0};
|
||
|
SourceHook::GetFuncInfo(g_pInst, &Test::Func, info);
|
||
|
|
||
|
void *adjustediface = NULL;
|
||
|
void **cur_vtptr = NULL;
|
||
|
void **cur_vfnptr = NULL;
|
||
|
|
||
|
adjustediface = reinterpret_cast<void*>(reinterpret_cast<char*>(g_pInst) + info.thisptroffs);
|
||
|
|
||
|
cur_vtptr = *reinterpret_cast<void***>(
|
||
|
reinterpret_cast<char*>(adjustediface) + info.vtbloffs);
|
||
|
cur_vfnptr = reinterpret_cast<void**>(cur_vtptr + info.vtblindex);
|
||
|
|
||
|
|
||
|
// Original function
|
||
|
void *origEntry = *cur_vfnptr;
|
||
|
|
||
|
// Now generate the jump code
|
||
|
g_ThunkAllocator.SetRW(g_OddThunkMemory);
|
||
|
|
||
|
*(base + 0) = 0xE9; // offset jump, immediate operand
|
||
|
ptrdiff_t *offsetAddr = reinterpret_cast<ptrdiff_t*>(base + 1);
|
||
|
|
||
|
// destination = src + offset + 5
|
||
|
// <=> offset = destination - src - 5
|
||
|
*offsetAddr =
|
||
|
(reinterpret_cast<unsigned char*>(origEntry) - base) - 5;
|
||
|
|
||
|
g_ThunkAllocator.SetRE(g_OddThunkMemory);
|
||
|
|
||
|
g_OddThunk = reinterpret_cast<void*>(base);
|
||
|
|
||
|
// Now set the odd thunk as new vtable entry
|
||
|
SourceHook::SetMemAccess((void*)cur_vfnptr, sizeof(void*), SH_MEM_READ | SH_MEM_WRITE);
|
||
|
*cur_vfnptr = g_OddThunk;
|
||
|
}
|
||
|
|
||
|
void FreeOddThunk()
|
||
|
{
|
||
|
g_ThunkAllocator.Free(g_OddThunkMemory);
|
||
|
g_OddThunk = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool TestOddThunks(std::string &error)
|
||
|
{
|
||
|
GET_SHPTR(g_SHPtr);
|
||
|
g_PLID = 1337;
|
||
|
|
||
|
g_CallNumber = 0;
|
||
|
|
||
|
g_pInst = MyInstanceFactory();
|
||
|
|
||
|
PatchFuncWithOddThunk();
|
||
|
|
||
|
SH_ADD_HOOK(Test, Func, g_pInst, SH_STATIC(Handler_Func_Pre1), false);
|
||
|
SH_ADD_HOOK(Test, Func, g_pInst, SH_STATIC(Handler_Func_Pre2), false);
|
||
|
|
||
|
g_pInst->Func();
|
||
|
|
||
|
CHECK_STATES((&g_States,
|
||
|
// FIRST func() call
|
||
|
new State_Pre1_Called(),
|
||
|
new State_Pre2_Called(), // calls Func() again
|
||
|
|
||
|
// SECOND func() call
|
||
|
new State_Pre1_Called(),
|
||
|
new State_Pre2_Called(), // removes hooks and calls Func() again
|
||
|
|
||
|
// THIRD func() call
|
||
|
new State_Func_Called(),
|
||
|
|
||
|
// SECOND func() call
|
||
|
new State_Func_Called(),
|
||
|
|
||
|
// FIRST func() call
|
||
|
new State_Func_Called(),
|
||
|
NULL), "Part 1");
|
||
|
|
||
|
delete g_pInst;
|
||
|
FreeOddThunk();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|