Completly redone templates, added "vfnptrs"

--HG--
extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%4042
This commit is contained in:
Pavol Marko 2005-04-29 20:29:46 +00:00
parent 9bb40ae639
commit bb2cb311ad
5 changed files with 1146 additions and 730 deletions

View File

@ -20,7 +20,7 @@
#define SH_GLOB_PLUGPTR g_PLID
#endif
#define SH_ASSERT(x) if (!(x)) __asm { int 3 }
#define SH_ASSERT(x, info) if (!(x)) __asm { int 3 }
// System
#define SH_SYS_WIN32 1
@ -54,6 +54,8 @@
#include "sh_memfuncinfo.h"
#include "sh_memory.h"
#include <list>
#include <vector>
#include <map>
#include <algorithm>
// Good old metamod!
@ -73,6 +75,13 @@ namespace SourceHook
{
const int STRBUF_LEN=8192; // In bytes, for "vafmt" functions
/**
* @brief An empty class. No inheritance used. Used for original-function-call hacks
*/
class EmptyClass
{
};
/**
* @brief A plugin typedef
*
@ -136,66 +145,61 @@ namespace SourceHook
*/
struct HookManagerInfo
{
struct Iface
struct VfnPtr
{
struct Hook
struct Iface
{
ISHDelegate *handler; //!< Pointer to the handler
bool paused; //!< If true, the hook should not be executed
Plugin plug; //!< The owner plugin
struct Hook
{
ISHDelegate *handler; //!< Pointer to the handler
bool paused; //!< If true, the hook should not be executed
Plugin plug; //!< The owner plugin
};
void *ptr; //!< Pointer to the interface instance
std::list<Hook> hooks_pre; //!< A list of pre-hooks
std::list<Hook> hooks_post; //!< A list of post-hooks
bool operator ==(void *other) const
{
return ptr == other;
}
};
void *callclass; //!< Stores a call class for this interface
void *ptr; //!< Pointer to the interface instance
void *vfnptr; //!< Pointer to the function
void *orig_entry; //!< The original vtable entry
std::list<Hook> hooks_pre; //!< A list of pre-hooks
std::list<Hook> hooks_post; //!< A list of post-hooks
bool operator ==(void *other) const
typedef std::list<Iface> IfaceList;
typedef IfaceList::iterator IfaceListIter;
IfaceList ifaces;
bool operator ==(void *other)
{
return ptr == other;
return vfnptr == other;
}
};
Plugin plug; //!< The owner plugin
const char *proto; //!< The prototype of the function the hook manager is responsible for
int vtbl_idx; //!< The vtable index
int vtbl_offs; //!< The vtable offset
int thisptr_offs; //!< The this-pointer-adjuster
HookManagerPubFunc func; //!< The interface to the hook manager
int hookfunc_vtbl_idx; //!< the vtable index of the hookfunc
int hookfunc_vtbl_offs; //!< the vtable offset of the hookfunc
void *hookfunc_inst; //!< Instance of the class the hookfunc is in
void *hookfunc_vfnptr; //!< Pointer to the hookfunc impl
std::list<Iface> ifaces; //!< List of hooked interfaces
typedef std::list<VfnPtr> VfnPtrList;
typedef VfnPtrList::iterator VfnPtrListIter;
VfnPtrList vfnptrs; //!< List of hooked interfaces
};
/**
* @brief Structure describing a callclass
*/
struct CallClass
typedef std::vector<void*> OrigFuncs;
typedef std::map<int, OrigFuncs> OrigVTables;
template<class B> struct CallClass
{
struct VTable
{
void *ptr;
int actsize;
std::list<int> patches; //!< Already patched entries
bool operator ==(void *other) const
{
return ptr == other;
}
};
void *iface; //!< The iface pointer this callclass belongs to
size_t size; //!< The size of the callclass
void *ptr; //!< The actual "object"
typedef std::list<VTable> VTableList;
VTableList vtables; //!< Already known vtables
int refcounter; //!< How many times it was requested
bool operator ==(void *other) const
{
return ptr == other;
}
B *ptr;
OrigVTables vt;
};
typedef CallClass<void> GenericCallClass;
/**
* @brief The main SourceHook interface
*/
@ -214,7 +218,7 @@ namespace SourceHook
* @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post handler
*/
virtual bool AddHook(Plugin plug, void *iface, int ifacesize, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) = 0;
virtual bool AddHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) = 0;
/**
* @brief Removes a hook.
@ -227,7 +231,7 @@ namespace SourceHook
* @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post handler
*/
virtual bool RemoveHook(Plugin plug, void *iface, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) = 0;
virtual bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post) = 0;
/**
* @brief Checks whether a plugin has (a) hook manager(s) that is/are currently used by other plugins
@ -242,14 +246,14 @@ namespace SourceHook
* @param iface The interface pointer
* @param size Size of the class
*/
virtual void *GetCallClass(void *iface, size_t size) = 0;
virtual GenericCallClass *GetCallClass(void *iface) = 0;
/**
* @brief Release a callclass
*
* @param ptr Pointer to the callclass
*/
virtual void ReleaseCallClass(void *ptr) = 0;
virtual void ReleaseCallClass(GenericCallClass *ptr) = 0;
virtual void SetRes(META_RES res) = 0; //!< Sets the meta result
virtual META_RES GetPrevRes() = 0; //!< Gets the meta result of the previously called handler
@ -284,11 +288,16 @@ namespace SourceHook
/**
* @brief Get/generate callclass for an interface pointer
*
* @param ifacetype The type of the interface
* @param ifaceptr The interface pointer
*/
#define SH_GET_CALLCLASS(ifacetype, ifaceptr) reinterpret_cast<ifacetype*>(SH_GLOB_SHPTR->GetCallClass(ifaceptr, sizeof(ifacetype)))
#define SH_RELEASE_CALLCLASS(ptr) SH_GLOB_SHPTR->ReleaseCallClass(reinterpret_cast<void*>(ptr))
template<class ifacetype>
inline SourceHook::CallClass<ifacetype> *SH_GET_CALLCLASS(ifacetype *ptr)
{
return reinterpret_cast<SourceHook::CallClass<ifacetype>*>(
SH_GLOB_SHPTR->GetCallClass(reinterpret_cast<void*>(ptr)));
}
#define SH_RELEASE_CALLCLASS(ptr) SH_GLOB_SHPTR->ReleaseCallClass(reinterpret_cast<SourceHook::GenericCallClass*>(ptr))
#define SH_ADD_HOOK(ifacetype, ifacefunc, ifaceptr, handler, post) \
SourceHook::SH_FHAdd##ifacetype##ifacefunc((void*)ifaceptr, post, handler)
@ -307,6 +316,18 @@ namespace SourceHook
#define SH_NOATTRIB
#if SH_COMP == SH_COMP_MSVC
# define SH_SETUP_MFP(mfp) \
reinterpret_cast<void**>(&mfp)[0] = vfnptr.orig_entry;
#elif SH_COMP == SH_COMP_GCC
# define SH_SETUP_MFP(mfp) \
reinterpret_cast<void**>(&mfp)[0] = vfnptr.orig_entry; \
reinterpret_cast<void**>(&mfp)[1] = 0;
#else
# error Not supported yet.
#endif
//////////////////////////////////////////////////////////////////////////
#define SH_FHCls(ift, iff, ov) FHCls_##ift##iff##ov
@ -320,13 +341,10 @@ namespace SourceHook
GetFuncInfo(funcptr, mfi); \
param->vtbl_idx = mfi.vtblindex; \
param->vtbl_offs = mfi.vtbloffs; \
param->thisptr_offs = mfi.thisptroffs; \
if (param->thisptr_offs < 0) \
return 2; /*No virtual inheritance supported*/ \
\
GetFuncInfo(&SH_FHCls(ifacetype,ifacefunc,overload)::Func, mfi); \
param->hookfunc_vtbl_idx = mfi.vtblindex; \
param->hookfunc_vtbl_offs = mfi.vtbloffs; \
param->hookfunc_inst = (void*)&ms_Inst; \
param->hookfunc_vfnptr = \
reinterpret_cast<void**>(reinterpret_cast<char*>(&ms_Inst) + mfi.vtbloffs)[mfi.vtblindex]; \
return 0; \
} \
else if (action == HA_Register) \
@ -353,7 +371,7 @@ namespace SourceHook
static const char *ms_Proto; \
SHINT_MAKE_HOOKMANPUBFUNC(ifacetype, ifacefunc, overload, funcptr)
#define SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, proto) \
#define SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, proto, funcptr) \
}; \
const char *SH_FHCls(ifacetype,ifacefunc,overload)::ms_Proto = proto; \
SH_FHCls(ifacetype,ifacefunc,overload) SH_FHCls(ifacetype,ifacefunc,overload)::ms_Inst; \
@ -361,42 +379,67 @@ namespace SourceHook
bool SH_FHAdd##ifacetype##ifacefunc(void *iface, bool post, \
SH_FHCls(ifacetype,ifacefunc,overload)::FD handler) \
{ \
return SH_GLOB_SHPTR->AddHook(SH_GLOB_PLUGPTR, iface, sizeof(ifacetype), \
MemFuncInfo mfi; \
GetFuncInfo(funcptr, mfi); \
if (mfi.thisptroffs < 0) \
return false; /* No virtual inheritance supported */ \
\
return SH_GLOB_SHPTR->AddHook(SH_GLOB_PLUGPTR, iface, mfi.thisptroffs, \
SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, \
new CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD>(handler), post); \
} \
bool SH_FHRemove##ifacetype##ifacefunc(void *iface, bool post, \
SH_FHCls(ifacetype,ifacefunc,overload)::FD handler) \
{ \
MemFuncInfo mfi; \
GetFuncInfo(funcptr, mfi); \
if (mfi.thisptroffs < 0) \
return false; /* No virtual inheritance supported */ \
\
CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD> tmp(handler); \
return SH_GLOB_SHPTR->RemoveHook(SH_GLOB_PLUGPTR, iface, \
return SH_GLOB_SHPTR->RemoveHook(SH_GLOB_PLUGPTR, iface, mfi.thisptroffs, \
SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, &tmp, post); \
} \
}
#define SH_SETUPCALLS(rettype) \
/* 1) Find the iface ptr */ \
/* 1.1) Adjust to original this pointer */ \
void *origthis = this - ms_HI->thisptr_offs; \
std::list<HookManagerInfo::Iface>::iterator ifaceiter = std::find(ms_HI->ifaces.begin(), \
ms_HI->ifaces.end(), origthis); \
SH_ASSERT(ifaceiter != ms_HI->ifaces.end()); \
HookManagerInfo::Iface &ci = *ifaceiter; \
#define SH_SETUPCALLS(rettype, paramtypes, params) \
/* 1) Find the vfnptr */ \
void *ourvfnptr = reinterpret_cast<void*>( \
*reinterpret_cast<void***>(reinterpret_cast<char*>(this) + ms_HI->vtbl_offs) + ms_HI->vtbl_idx); \
\
HookManagerInfo::VfnPtrListIter vfptriter = std::find(ms_HI->vfnptrs.begin(), \
ms_HI->vfnptrs.end(), ourvfnptr); \
if (vfptriter == ms_HI->vfnptrs.end()) \
{ \
/* Bleh? Should be impossible! */ \
SH_ASSERT(0, "Called with vfnptr 0x%p which couldn't be found in the list"); \
} \
HookManagerInfo::VfnPtr &vfnptr = *vfptriter; \
/* 2) Find the iface */ \
HookManagerInfo::VfnPtr::IfaceListIter ifiter = std::find(vfnptr.ifaces.begin(), vfnptr.ifaces.end(), this); \
if (ifiter == vfnptr.ifaces.end()) \
{ \
/* The iface info was not found. Redirect the call to the original function. */ \
rettype (EmptyClass::*mfp)paramtypes; \
SH_SETUP_MFP(mfp); \
return (reinterpret_cast<EmptyClass*>(this)->*mfp)params; \
} \
HookManagerInfo::VfnPtr::Iface &ci = *ifiter; \
/* 2) Declare some vars and set it up */ \
std::list<HookManagerInfo::Iface::Hook> &prelist = ci.hooks_pre; \
std::list<HookManagerInfo::Iface::Hook> &postlist = ci.hooks_post; \
std::list<HookManagerInfo::VfnPtr::Iface::Hook> &prelist = ci.hooks_pre; \
std::list<HookManagerInfo::VfnPtr::Iface::Hook> &postlist = ci.hooks_post; \
rettype orig_ret, override_ret, plugin_ret; \
META_RES &cur_res = SH_GLOB_SHPTR->GetCurResRef(); \
META_RES &prev_res = SH_GLOB_SHPTR->GetPrevResRef(); \
META_RES &status = SH_GLOB_SHPTR->GetStatusRef(); \
status = MRES_IGNORED; \
SH_GLOB_SHPTR->SetIfacePtr(ci.ptr); \
SH_GLOB_SHPTR->SetIfacePtr(this); \
SH_GLOB_SHPTR->SetOrigRet(reinterpret_cast<void*>(&orig_ret)); \
SH_GLOB_SHPTR->SetOverrideRet(NULL);
#define SH_CALL_HOOKS(post, params) \
prev_res = MRES_IGNORED; \
for (std::list<HookManagerInfo::Iface::Hook>::iterator hiter = post##list.begin(); hiter != post##list.end(); ++hiter) \
for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hiter = post##list.begin(); hiter != post##list.end(); ++hiter) \
{ \
if (hiter->paused) continue; \
cur_res = MRES_IGNORED; \
@ -411,44 +454,64 @@ namespace SourceHook
} \
}
#define SH_CALL_ORIG(ifacetype, ifacefunc, params) \
#define SH_CALL_ORIG(ifacetype, ifacefunc, rettype, paramtypes, params) \
if (status != MRES_SUPERCEDE) \
orig_ret = reinterpret_cast<ifacetype*>(ci.callclass)->ifacefunc params; \
{ \
rettype (EmptyClass::*mfp)paramtypes; \
SH_SETUP_MFP(mfp); \
orig_ret = (reinterpret_cast<EmptyClass*>(ci.ptr)->*mfp)params; \
} \
else \
orig_ret = override_ret;
#define SH_RETURN() \
return status >= MRES_OVERRIDE ? override_ret : orig_ret;
#define SH_HANDLEFUNC(ifacetype, ifacefunc, params, rettype) \
SH_SETUPCALLS(rettype) \
#define SH_HANDLEFUNC(ifacetype, ifacefunc, paramtypes, params, rettype) \
SH_SETUPCALLS(rettype, paramtypes, params) \
SH_CALL_HOOKS(pre, params) \
SH_CALL_ORIG(ifacetype, ifacefunc, params) \
SH_CALL_ORIG(ifacetype, ifacefunc, rettype, paramtypes, params) \
SH_CALL_HOOKS(post, params) \
SH_RETURN()
//////////////////////////////////////////////////////////////////////////
#define SH_SETUPCALLS_void() \
/* 1) Find the iface ptr */ \
/* 1.1) Adjust to original this pointer */ \
void *origthis = this - ms_HI->thisptr_offs; \
std::list<HookManagerInfo::Iface>::iterator ifaceiter = std::find(ms_HI->ifaces.begin(), \
ms_HI->ifaces.end(), origthis); \
SH_ASSERT(ifaceiter != ms_HI->ifaces.end()); \
HookManagerInfo::Iface &ci = *ifaceiter; \
#define SH_SETUPCALLS_void(paramtypes, params) \
/* 1) Find the vfnptr */ \
void *ourvfnptr = reinterpret_cast<void*>( \
*reinterpret_cast<void***>(reinterpret_cast<char*>(this) + ms_HI->vtbl_offs) + ms_HI->vtbl_idx); \
\
HookManagerInfo::VfnPtrListIter vfptriter = std::find(ms_HI->vfnptrs.begin(), \
ms_HI->vfnptrs.end(), ourvfnptr); \
if (vfptriter == ms_HI->vfnptrs.end()) \
{ \
/* Bleh? Should be impossible! */ \
SH_ASSERT(0, "Called with vfnptr 0x%p which couldn't be found in the list"); \
} \
HookManagerInfo::VfnPtr &vfnptr = *vfptriter; \
/* 2) Find the iface */ \
HookManagerInfo::VfnPtr::IfaceListIter ifiter = std::find(vfnptr.ifaces.begin(), vfnptr.ifaces.end(), this); \
if (ifiter == vfnptr.ifaces.end()) \
{ \
/* The iface info was not found. Redirect the call to the original function. */ \
void (EmptyClass::*mfp)paramtypes; \
SH_SETUP_MFP(mfp); \
(reinterpret_cast<EmptyClass*>(this)->*mfp)params; \
return; \
} \
HookManagerInfo::VfnPtr::Iface &ci = *ifiter; \
/* 2) Declare some vars and set it up */ \
std::list<HookManagerInfo::Iface::Hook> &prelist = ci.hooks_pre; \
std::list<HookManagerInfo::Iface::Hook> &postlist = ci.hooks_post; \
std::list<HookManagerInfo::VfnPtr::Iface::Hook> &prelist = ci.hooks_pre; \
std::list<HookManagerInfo::VfnPtr::Iface::Hook> &postlist = ci.hooks_post; \
META_RES &cur_res = SH_GLOB_SHPTR->GetCurResRef(); \
META_RES &prev_res = SH_GLOB_SHPTR->GetPrevResRef(); \
META_RES &status = SH_GLOB_SHPTR->GetStatusRef(); \
status = MRES_IGNORED; \
SH_GLOB_SHPTR->SetIfacePtr(ci.ptr); \
SH_GLOB_SHPTR->SetIfacePtr(this); \
SH_GLOB_SHPTR->SetOverrideRet(NULL);
#define SH_CALL_HOOKS_void(post, params) \
prev_res = MRES_IGNORED; \
for (std::list<HookManagerInfo::Iface::Hook>::iterator hiter = post##list.begin(); hiter != post##list.end(); ++hiter) \
for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hiter = post##list.begin(); hiter != post##list.end(); ++hiter) \
{ \
if (hiter->paused) continue; \
cur_res = MRES_IGNORED; \
@ -458,54 +521,60 @@ namespace SourceHook
status = cur_res; \
}
#define SH_CALL_ORIG_void(ifacetype, ifacefunc, params) \
#define SH_CALL_ORIG_void(ifacetype, ifacefunc, paramtypes, params) \
if (status != MRES_SUPERCEDE) \
reinterpret_cast<ifacetype*>(ci.callclass)->ifacefunc params;
{ \
void (EmptyClass::*mfp)paramtypes; \
SH_SETUP_MFP(mfp); \
(reinterpret_cast<EmptyClass*>(ci.ptr)->*mfp)params; \
}
#define SH_RETURN_void()
#define SH_HANDLEFUNC_void(ifacetype, ifacefunc, params) \
SH_SETUPCALLS_void() \
#define SH_HANDLEFUNC_void(ifacetype, ifacefunc, paramtypes, params) \
SH_SETUPCALLS_void(paramtypes, params) \
SH_CALL_HOOKS_void(pre, params) \
SH_CALL_ORIG_void(ifacetype, ifacefunc, params) \
SH_CALL_ORIG_void(ifacetype, ifacefunc, paramtypes, params) \
SH_CALL_HOOKS_void(post, params) \
SH_RETURN_void()
// Special vafmt handlers
#define SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, params_orig, params_plug, rettype) \
SH_SETUPCALLS(rettype) \
#define SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, paramtypes, params_orig, params_plug, rettype) \
SH_SETUPCALLS(rettype, paramtypes, params_orig) \
SH_CALL_HOOKS(pre, params_plug) \
SH_CALL_ORIG(ifacetype, ifacefunc, params_orig) \
SH_CALL_ORIG(ifacetype, ifacefunc, rettype, paramtypes, params_orig) \
SH_CALL_HOOKS(post, params_plug) \
SH_RETURN()
#define SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, params_orig, params_plug) \
SH_SETUPCALLS_void() \
#define SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, paramtypes, params_orig, params_plug) \
SH_SETUPCALLS_void(paramtypes, params_orig) \
SH_CALL_HOOKS_void(pre, params_plug) \
SH_CALL_ORIG_void(ifacetype, ifacefunc, params_orig) \
SH_CALL_ORIG_void(ifacetype, ifacefunc, paramtypes, params_orig) \
SH_CALL_HOOKS_void(post, params_plug) \
SH_RETURN_void()
//////////////////////////////////////////////////////////////////////////
@VARARGS@
// *********
// ********* Support for @$@ arguments *********
#define SH_DECL_HOOK@$@(ifacetype, ifacefunc, attr, overload, rettype@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<rettype (ifacetype::*)(@param%%|, @)> \
(&ifacetype::ifacefunc))) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @@, @rettype> FD; \
virtual rettype Func(@param%% p%%|, @) attr \
{ SH_HANDLEFUNC(ifacetype, ifacefunc, (@p%%|, @), rettype); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype @"|" #param%%| @)
{ SH_HANDLEFUNC(ifacetype, ifacefunc, (@param%%|, @), (@p%%|, @), rettype); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype @"|" #param%%| @, \
(static_cast<rettype (ifacetype::*)(@param%%|, @)>(&ifacetype::ifacefunc)))
#define SH_DECL_HOOK@$@_void(ifacetype, ifacefunc, attr, overload@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<void (ifacetype::*)(@param%%|, @)> \
(&ifacetype::ifacefunc))) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @> FD; \
virtual void Func(@param%% p%%|, @) attr \
{ SH_HANDLEFUNC_void(ifacetype, ifacefunc, (@p%%|, @)); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr @"|" #param%%| @)
{ SH_HANDLEFUNC_void(ifacetype, ifacefunc, (@param%%|, @), (@p%%|, @)); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr @"|" #param%%| @, \
(static_cast<void (ifacetype::*)(@param%%|, @)>(&ifacetype::ifacefunc)))
#define SH_DECL_HOOK@$@_vafmt(ifacetype, ifacefunc, attr, overload, rettype@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<rettype (ifacetype::*)(@param%%|, @@, @const char *, ...)> \
@ -518,9 +587,10 @@ namespace SourceHook
va_start(ap, fmt); \
vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(ap); \
SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf), rettype); \
SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, (@param%%|, @@, @...), (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf), rettype); \
} \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype @"|" #param%%| @ "|const char*|...")
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype @"|" #param%%| @ "|const char*|...", \
(static_cast<rettype (ifacetype::*)(@param%%|, @@, @const char *, ...)>(&ifacetype::ifacefunc)))
#define SH_DECL_HOOK@$@_void_vafmt(ifacetype, ifacefunc, attr, overload, rettype@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<void (ifacetype::*)(@param%%|, @@, @const char *, ...)> \
@ -533,22 +603,70 @@ namespace SourceHook
va_start(ap, fmt); \
vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(ap); \
SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf)); \
SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, (@param%%|, @@, @...), (@p%%|, @@, @"%s", buf), (@p%%|, @@, @buf)); \
} \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr @"|" #param%%| @ "|const char*|...")
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr @"|" #param%%| @ "|const char*|...", \
(static_cast<void (ifacetype::*)(@param%%|, @@, @const char *, ...)>(&ifacetype::ifacefunc)))
@ENDARGS@
/*
#define SH_DECL_HOOK1(ifacetype, ifacefunc, attr, overload, rettype, param1) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload) \
typedef fastdelegate::FastDelegate1<param1, rettype> FD; \
virtual rettype Func(param1 p1) \
{ SH_HANDLEFUNC(ifacetype, ifacefunc, (p1), rettype); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype "|" #param1)
*/
//////////////////////////////////////////////////////////////////////////
// SH_CALL
#define SH_MAKE_EXECUTABLECLASS_OB(call, prms) \
{ \
MemFuncInfo mfi; \
MFI_Impl<sizeof(m_MFP)>::GetFuncInfo(m_MFP, mfi); \
OrigVTables::const_iterator iter = m_CC->vt.find(mfi.vtbloffs); \
if (iter == m_CC->vt.end() || mfi.vtblindex >= (int)iter->second.size() || iter->second[mfi.vtblindex] == NULL) \
return (m_CC->ptr->*m_MFP)call; \
\
/* It's hooked. Call the original function. */ \
union \
{ \
RetType(EmptyClass::*mfpnew)prms; \
void *addr; \
} u; \
u.addr = iter->second[mfi.vtblindex]; \
\
return (reinterpret_cast<EmptyClass*>(m_CC->ptr)->*u.mfpnew)call; \
}
namespace SourceHook
{
template<class CCType, class RetType, class MFPType> class ExecutableClass
{
CCType *m_CC;
MFPType m_MFP;
public:
ExecutableClass(CCType *cc, MFPType mfp) : m_CC(cc), m_MFP(mfp)
{
}
@VARARGS@
// Support for @$@ arguments
template<@class Param%%|, @> RetType operator()(@Param%% p%%|, @) const
SH_MAKE_EXECUTABLECLASS_OB((@p%%@), (@Param%%@))
@ENDARGS@
};
}
@VARARGS@
// Support for @$@ arguments
template <class X, class Y, class RetType@, @@class Param%%|, @>
SourceHook::ExecutableClass<SourceHook::CallClass<Y>, RetType, RetType (X::*)(@Param%%|, @)>
SH_CALL(SourceHook::CallClass<Y> *ptr, RetType(X::*mfp)(@Param%%|, @))
{
return SourceHook::ExecutableClass<SourceHook::CallClass<Y>, RetType, RetType (X::*)(@Param%%|, @)>(ptr, mfp);
}
@ENDARGS@
#undef SH_MAKE_EXECUTABLECLASS_BODY
#endif
// The pope is dead. -> :(

View File

@ -16,93 +16,38 @@
#include <algorithm>
#include "sourcehook_impl.h"
#if SH_SYS == SH_SYS_WIN32
# include <windows.h>
#elif SH_SYS == SH_SYS_LINUX
# include <sys/mman.h>
# include <limits.h>
# include <unistd.h>
# ifndef PAGESIZE
# define PAGESIZE 4096
# endif
#endif
#if SH_RUNTIME_CODEGEN == 1
# if SH_COMP == SH_COMP_GCC
# define SH_CCC_CODESIZE 15
// :TODO:
//void SH_CCC_MakeGate(void *origthisptr, unsigned char* &ptr, void *origvtable, int i)
//{
//}
# elif SH_COMP == SH_COMP_MSVC
# define SH_CCC_CODESIZE 19
void SH_CCC_MakeGate(void *origthisptr, void *ccthisptr, unsigned char* ptr, void *origfunc)
{
// --Subtract old this pointer to get the offset
// 00417EDF 81 E9 A1 7B 33 01 sub ecx,1337BA1h
*ptr++ = 0x81;
*ptr++ = 0xE9;
*reinterpret_cast<void**>(ptr) = ccthisptr;
ptr += sizeof(void*);
// --Add it to the new this pointer
// 00417EE5 81 C1 37 13 37 13 add ecx,13371337h
*ptr++ = 0x81;
*ptr++ = 0xC1;
*reinterpret_cast<void**>(ptr) = origthisptr;
ptr += sizeof(void*);
// --Prepare address
// 00417EEB B8 EF BE AD DE mov eax,0DEADBEEFh
*ptr++ = 0xB8;
*reinterpret_cast<void**>(ptr) = origfunc;
ptr += sizeof(void*);
// -- Do actual jump
// 00417EF0 FF E0 jmp eax
*ptr++ = 0xFF;
*ptr++ = 0xE0;
}
# endif
#endif
namespace SourceHook
{
CSourceHookImpl::CSourceHookImpl()
{
// Get page size
#if SH_SYS == SH_SYS_WIN32
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
m_PageSize = sysinfo.dwPageSize;
#elif SH_SYS == SH_SYS_LINUX
m_PageSize = PAGESIZE;
#else
# error Unsupported system
#endif
}
CSourceHookImpl::~CSourceHookImpl()
{
}
bool CSourceHookImpl::IsPluginInUse(Plugin plug)
{
// Iterate through all hook managers which are in this plugin
// Iterate through their hooks
// Iterate through their vfnptrs, ifaces, hooks
// If a hook from an other plugin is found, return true
// Return false otherwise
#define TMP_CHECK_LIST(name) \
for (iter3 = iter2->name.begin(); iter3 != iter2->name.end(); ++iter3) \
if (iter3->plug == plug) \
for (hook_iter = iface_iter->name.begin(); hook_iter != iface_iter->name.end(); ++hook_iter) \
if (hook_iter->plug == plug) \
return true;
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter)
for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end(); ++hmil_iter)
{
if (iter->plug == plug && !iter->ifaces.empty())
if (hmil_iter->plug != plug)
continue;
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
vfnptr_iter != hmil_iter->vfnptrs.end(); ++vfnptr_iter)
{
for (std::list<HookManagerInfo::Iface>::iterator iter2 = iter->ifaces.begin(); iter2 != iter->ifaces.end(); ++iter2)
for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
{
std::list<HookManagerInfo::Iface::Hook>::iterator iter3;
std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hook_iter;
TMP_CHECK_LIST(hooks_pre);
TMP_CHECK_LIST(hooks_post);
}
@ -116,24 +61,29 @@ namespace SourceHook
{
// 1) Manually remove all hooks by this plugin
std::list<RemoveHookInfo> hookstoremove;
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter)
#define TMP_CHECK_LIST(name, ispost) \
for (hook_iter = iface_iter->name.begin(); hook_iter != iface_iter->name.end(); ++hook_iter) \
if (hook_iter->plug == plug) \
hookstoremove.push_back(RemoveHookInfo(hook_iter->plug, iface_iter->ptr, \
hmil_iter->func, hook_iter->handler, ispost))
for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end(); ++hmil_iter)
{
for (std::list<HookManagerInfo::Iface>::iterator iter2 = iter->ifaces.begin(); iter2 != iter->ifaces.end();
++iter2)
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
vfnptr_iter != hmil_iter->vfnptrs.end(); ++vfnptr_iter)
{
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_pre.begin(); iter3 != iter2->hooks_pre.end(); ++iter3)
if (iter3->plug == plug)
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, false));
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_post.begin(); iter3 != iter2->hooks_post.end(); ++iter3)
if (iter3->plug == plug)
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, true));
for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
{
std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hook_iter;
TMP_CHECK_LIST(hooks_pre, false);
TMP_CHECK_LIST(hooks_post, true);
}
}
}
#undef TMP_CHECK_LIST
for (std::list<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
RemoveHook(rmiter->plug, rmiter->iface, rmiter->hpf, rmiter->handler, rmiter->post);
RemoveHook(*rmiter);
// 2) Other plugins may use hook managers in this plugin.
// Get a list of hook managers that are in this plugin and are used by other plugins
@ -141,16 +91,16 @@ namespace SourceHook
HookManInfoList tmphookmans;
bool erase = false;
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end();
erase ? iter=m_HookMans.erase(iter) : ++iter)
for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end();
erase ? hmil_iter=m_HookMans.erase(hmil_iter) : ++hmil_iter)
{
if (iter->plug == plug)
if (hmil_iter->plug == plug)
{
if (!iter->ifaces.empty())
if (!hmil_iter->vfnptrs.empty())
{
// All hooks by this plugin are already removed
// So if there is an iface, it has to be used by an other plugin
tmphookmans.push_back(*iter);
// So if there is a vfnptr, it has to be used by an other plugin
tmphookmans.push_back(*hmil_iter);
}
erase = true;
}
@ -159,29 +109,28 @@ namespace SourceHook
}
// For each hook manager:
for (HookManInfoList::iterator iter = tmphookmans.begin(); iter != tmphookmans.end(); ++iter)
for (HookManInfoList::iterator hmil_iter = tmphookmans.begin(); hmil_iter != tmphookmans.end(); ++hmil_iter)
{
// Find a suitable hook manager in an other plugin
HookManInfoList::iterator newHookMan = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
iter->proto, iter->vtbl_offs, iter->vtbl_idx, iter->thisptr_offs);
if (newHookMan == m_HookMans.end())
{
// This should _never_ happen.
// If there is a hook from an other plugin, the plugin must have provided a hook manager as well.
SH_ASSERT(0);
}
hmil_iter->proto, hmil_iter->vtbl_offs, hmil_iter->vtbl_idx);
// AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo/thisptroffs
SH_ASSERT(newHookMan->plug != plug);
// This should _never_ happen.
// If there is a hook from an other plugin, the plugin must have provided a hook manager as well.
SH_ASSERT(newHookMan != m_HookMans.end(),
"Could not find a suitable hook manager in an other plugin!");
// The first hooker should be always used - so the new hook manager has to be empty
SH_ASSERT(newHookMan->ifaces.empty());
// AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo
SH_ASSERT(newHookMan->plug != plug, "New hook manager from same plugin!");
// Move the ifaces from the old hook manager to the new one
newHookMan->ifaces = iter->ifaces;
// The first hook manager should be always used - so the new hook manager has to be empty
SH_ASSERT(newHookMan->vfnptrs.empty(), "New hook manager not empty!");
// Move the vfnptrs from the old hook manager to the new one
newHookMan->vfnptrs = hmil_iter->vfnptrs;
// Unregister the old one, register the new one
iter->func(HA_Unregister, NULL);
hmil_iter->func(HA_Unregister, NULL);
newHookMan->func(HA_Register, &(*newHookMan));
}
}
@ -189,43 +138,49 @@ namespace SourceHook
void CSourceHookImpl::CompleteShutdown()
{
std::list<RemoveHookInfo> hookstoremove;
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter)
#define TMP_CHECK_LIST(name, ispost) \
for (hook_iter = iface_iter->name.begin(); hook_iter != iface_iter->name.end(); ++hook_iter) \
hookstoremove.push_back(RemoveHookInfo(hook_iter->plug, iface_iter->ptr, \
hmil_iter->func, hook_iter->handler, ispost))
for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end(); ++hmil_iter)
{
for (std::list<HookManagerInfo::Iface>::iterator iter2 = iter->ifaces.begin(); iter2 != iter->ifaces.end();
++iter2)
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
vfnptr_iter != hmil_iter->vfnptrs.end(); ++vfnptr_iter)
{
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_pre.begin(); iter3 != iter2->hooks_pre.end(); ++iter3)
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, false));
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_post.begin(); iter3 != iter2->hooks_post.end(); ++iter3)
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, true));
for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
{
std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hook_iter;
TMP_CHECK_LIST(hooks_pre, false);
TMP_CHECK_LIST(hooks_post, true);
}
}
}
#undef TMP_CHECK_LIST
for (std::list<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
RemoveHook(rmiter->plug, rmiter->iface, rmiter->hpf, rmiter->handler, rmiter->post);
RemoveHook(*rmiter);
m_HookMans.clear();
}
bool CSourceHookImpl::AddHook(Plugin plug, void *iface, int ifacesize, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
bool CSourceHookImpl::AddHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
{
void *adjustediface = reinterpret_cast<void*>(reinterpret_cast<char*>(iface) + thisptr_offs);
// 1) Get info about the hook manager
HookManagerInfo tmp;
if (myHookMan(HA_GetInfo, &tmp) != 0)
return false;
// Add the proposed hook manager to the _end_ of the list if the plugin doesn't have a hook manager with this proto/vo/vi registered
HookManInfoList::iterator hookmaniter;
for (hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
HookManInfoList::iterator hkmi_iter;
for (hkmi_iter = m_HookMans.begin(); hkmi_iter != m_HookMans.end(); ++hkmi_iter)
{
if (hookmaniter->plug == plug && strcmp(hookmaniter->proto, tmp.proto) == 0 &&
hookmaniter->vtbl_offs == tmp.vtbl_offs && hookmaniter->vtbl_idx == tmp.vtbl_idx &&
hookmaniter->thisptr_offs == tmp.thisptr_offs)
if (hkmi_iter->plug == plug && strcmp(hkmi_iter->proto, tmp.proto) == 0 &&
hkmi_iter->vtbl_offs == tmp.vtbl_offs && hkmi_iter->vtbl_idx == tmp.vtbl_idx)
break;
}
if (hookmaniter == m_HookMans.end())
if (hkmi_iter == m_HookMans.end())
{
// No such hook manager from this plugin yet, add it!
tmp.func = myHookMan;
@ -234,47 +189,70 @@ namespace SourceHook
}
// Then, search for a suitable hook manager (from the beginning)
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(), tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx, tmp.thisptr_offs);
SH_ASSERT(hookman != m_HookMans.end());
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(), tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx);
SH_ASSERT(hookman != m_HookMans.end(), "No hookman found - but if there was none, we've just added one!");
// Tell it to store the pointer if it's not already active
if (hookman->ifaces.empty())
if (hookman->vfnptrs.empty())
hookman->func(HA_Register, &(*hookman));
std::list<HookManagerInfo::Iface>::iterator ifsiter = std::find(hookman->ifaces.begin(), hookman->ifaces.end(), iface);
void **cur_vtptr = *reinterpret_cast<void***>(
reinterpret_cast<char*>(adjustediface) + tmp.vtbl_offs);
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.vtbl_idx);
if (ifsiter == hookman->ifaces.end())
HookManagerInfo::VfnPtrListIter vfnptr_iter = std::find(
hookman->vfnptrs.begin(), hookman->vfnptrs.end(), cur_vfnptr);
if (vfnptr_iter == hookman->vfnptrs.end())
{
HookManagerInfo::Iface ifs;
ifs.ptr = iface;
ifs.callclass = GetCallClass(iface, ifacesize);
void *vtableptr = *reinterpret_cast<void**>(reinterpret_cast<char*>(iface) + hookman->vtbl_offs);
SetMemAccess(vtableptr, sizeof(void*) * hookman->vtbl_idx, SH_MEM_READ | SH_MEM_WRITE);
ifs.orig_entry = (*reinterpret_cast<void***>(reinterpret_cast<char*>(iface) + hookman->vtbl_offs))[hookman->vtbl_idx];
(*reinterpret_cast<void***>(reinterpret_cast<char*>(iface) + hookman->vtbl_offs))[hookman->vtbl_idx] =
(*reinterpret_cast<void***>(reinterpret_cast<char*>(hookman->hookfunc_inst) + hookman->hookfunc_vtbl_offs))[hookman->hookfunc_vtbl_idx];
hookman->ifaces.push_back(ifs);
ifsiter = hookman->ifaces.end();
--ifsiter;
// Add a new one
HookManagerInfo::VfnPtr vfp;
vfp.vfnptr = cur_vfnptr;
vfp.orig_entry = *reinterpret_cast<void**>(cur_vfnptr);
hookman->vfnptrs.push_back(vfp);
// Alter vtable entry
SetMemAccess(cur_vtptr, sizeof(void*) * tmp.vtbl_idx, SH_MEM_READ | SH_MEM_WRITE);
*reinterpret_cast<void**>(cur_vfnptr) = *reinterpret_cast<void**>(hookman->hookfunc_vfnptr);
// Make vfnptr_iter point to the new element
vfnptr_iter = hookman->vfnptrs.end();
--vfnptr_iter;
}
HookManagerInfo::Iface::Hook hookinfo;
HookManagerInfo::VfnPtr::IfaceListIter iface_iter = std::find(
vfnptr_iter->ifaces.begin(), vfnptr_iter->ifaces.end(), adjustediface);
if (iface_iter == vfnptr_iter->ifaces.end())
{
// Add a new one
HookManagerInfo::VfnPtr::Iface ifs;
ifs.ptr = adjustediface;
vfnptr_iter->ifaces.push_back(ifs);
// Make iface_iter point to the new element
iface_iter = vfnptr_iter->ifaces.end();
--iface_iter;
}
// Add the hook
HookManagerInfo::VfnPtr::Iface::Hook hookinfo;
hookinfo.handler = handler;
hookinfo.plug = plug;
hookinfo.paused = false;
if (post)
ifsiter->hooks_post.push_back(hookinfo);
iface_iter->hooks_post.push_back(hookinfo);
else
ifsiter->hooks_pre.push_back(hookinfo);
iface_iter->hooks_pre.push_back(hookinfo);
// Now that it is done, check whether we have to update any callclasses
for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
{
if (cciter->iface == iface)
if (cciter->cc.ptr == iface)
{
ApplyCallClassPatch(*cciter, tmp.vtbl_offs, tmp.vtbl_idx, ifsiter->orig_entry);
ApplyCallClassPatch(*cciter, tmp.vtbl_offs, tmp.vtbl_idx, vfnptr_iter->orig_entry);
// We can assume that there is no more callclass with the same iface
// We can assume that there is no more callclass for the same iface
break;
}
}
@ -282,7 +260,18 @@ namespace SourceHook
return true;
}
bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
bool CSourceHookImpl::RemoveHook(RemoveHookInfo info)
{
return RemoveHook(info.plug, info.iface, info.hookman, info.handler, info.post);
}
bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
{
return RemoveHook(plug, reinterpret_cast<void*>(reinterpret_cast<char*>(iface)+thisptr_offs),
myHookMan, handler, post);
}
bool CSourceHookImpl::RemoveHook(Plugin plug, void *adjustediface, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
{
HookManagerInfo tmp;
if (myHookMan(HA_GetInfo, &tmp) != 0)
@ -290,196 +279,110 @@ namespace SourceHook
// Find the hook manager and the hook
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx, tmp.thisptr_offs);
tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx);
if (hookman == m_HookMans.end())
return false;
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookman->ifaces.begin(); ifaceiter != hookman->ifaces.end(); ++ifaceiter)
void **cur_vtptr = *reinterpret_cast<void***>(
reinterpret_cast<char*>(adjustediface) + tmp.vtbl_offs);
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.vtbl_idx);
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->vfnptrs.begin();
vfnptr_iter != hookman->vfnptrs.end(); ++vfnptr_iter)
{
if (ifaceiter->ptr == iface)
if (vfnptr_iter->vfnptr == cur_vfnptr)
{
std::list<HookManagerInfo::Iface::Hook> &hooks = post ? ifaceiter->hooks_post : ifaceiter->hooks_pre;
bool erase;
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = hooks.begin();
hookiter != hooks.end(); erase ? hookiter = hooks.erase(hookiter) : ++hookiter)
for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
{
erase = hookiter->plug == plug && hookiter->handler->IsEqual(handler);
if (erase)
hookiter->handler->DeleteThis(); // Make the _plugin_ delete the handler object
std::list<HookManagerInfo::VfnPtr::Iface::Hook> &hooks =
post ? iface_iter->hooks_post : iface_iter->hooks_pre;
bool erase;
for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = hooks.begin();
hookiter != hooks.end(); erase ? hookiter = hooks.erase(hookiter) : ++hookiter)
{
erase = hookiter->plug == plug && hookiter->handler->IsEqual(handler);
if (erase)
hookiter->handler->DeleteThis(); // Make the _plugin_ delete the handler object
}
if (iface_iter->hooks_post.empty() && iface_iter->hooks_pre.empty())
{
vfnptr_iter->ifaces.erase(iface_iter);
if (vfnptr_iter->ifaces.empty())
{
// Deactivate the hook
*reinterpret_cast<void**>(vfnptr_iter->vfnptr) = vfnptr_iter->orig_entry;
hookman->vfnptrs.erase(vfnptr_iter);
if (hookman->vfnptrs.empty())
{
// Unregister the hook manager
hookman->func(HA_Unregister, NULL);
}
}
}
}
if (ifaceiter->hooks_post.empty() && ifaceiter->hooks_pre.empty())
{
// Deactivate the hook
(*reinterpret_cast<void***>(reinterpret_cast<char*>(ifaceiter->ptr) +
hookman->vtbl_offs))[hookman->vtbl_idx]= ifaceiter->orig_entry;
// Release the callclass
ReleaseCallClass(ifaceiter->callclass);
// Remove the iface info
hookman->ifaces.erase(ifaceiter);
if (hookman->ifaces.empty())
hookman->func(HA_Unregister, NULL);
}
// :TODO: Better return value? Or none?
return true;
}
}
return false;
}
void *CSourceHookImpl::GetCallClass(void *iface, size_t size)
GenericCallClass *CSourceHookImpl::GetCallClass(void *iface)
{
for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
{
if (cciter->iface == iface)
if (cciter->cc.ptr == iface)
{
++cciter->refcounter;
return cciter->ptr;
return &cciter->cc;
}
}
// The layout of callclasses is:
// Copy of the class; vtable entries pointing to gates
// Pointer to the corresponding CallClass object
// Copy of the class; vtable entries pointing to original functions
CallClass tmp;
char *ptr = new char[size * 2 + sizeof(void*)];
tmp.ptr = reinterpret_cast<void*>(ptr);
memcpy(reinterpret_cast<void*>(ptr), iface, size);
memcpy(reinterpret_cast<void*>(ptr + size + sizeof(void*)), iface, size);
tmp.iface = iface;
tmp.size = size;
CallClassInfo tmp;
tmp.refcounter = 1;
tmp.cc.ptr = iface;
// Go through _all_ hooks and apply any needed patches
for (HookManInfoList::iterator hookman = m_HookMans.begin(); hookman != m_HookMans.end(); ++hookman)
{
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookman->ifaces.begin(); ifaceiter != hookman->ifaces.end(); ++ifaceiter)
{
if (ifaceiter->ptr == iface)
{
if (!ApplyCallClassPatch(tmp, hookman->vtbl_offs, hookman->vtbl_idx, ifaceiter->orig_entry))
{
FreeCallClass(tmp);
return NULL;
}
}
}
}
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->vfnptrs.begin();
vfnptr_iter != hookman->vfnptrs.end(); ++vfnptr_iter)
for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
if (iface_iter->ptr == iface)
ApplyCallClassPatch(tmp, hookman->vtbl_offs, hookman->vtbl_idx,
vfnptr_iter->orig_entry);
m_CallClasses.push_back(tmp);
// The object has to be followed by the pointer to the vtable info object
*reinterpret_cast<void**>(ptr + size) = &m_CallClasses.back();
return tmp.ptr;
return &m_CallClasses.back().cc;
}
void CSourceHookImpl::ReleaseCallClass(void *ptr)
void CSourceHookImpl::ReleaseCallClass(GenericCallClass *ptr)
{
Impl_CallClassList::iterator iter = std::find(m_CallClasses.begin(), m_CallClasses.end(), ptr);
if (iter == m_CallClasses.end())
return;
--iter->refcounter;
if (iter->refcounter < 1)
{
FreeCallClass(*iter);
m_CallClasses.erase(iter);
}
}
void CSourceHookImpl::FreeCallClass(CallClass &cc)
void CSourceHookImpl::ApplyCallClassPatch(CallClassInfo &cc, int vtbl_offs, int vtbl_idx, void *orig_entry)
{
for (CallClass::VTableList::iterator vtbliter = cc.vtables.begin(); vtbliter != cc.vtables.end(); ++vtbliter)
{
#if SH_RUNTIME_CODEGEN == 1
// Delete generated callgates
for (std::list<int>::iterator cgiter = vtbliter->patches.begin(); cgiter != vtbliter->patches.end(); ++cgiter)
{
char *cgptr = reinterpret_cast<char**>(vtbliter->ptr)[*cgiter];
delete [] cgptr;
}
#endif
delete [] reinterpret_cast<char*>(vtbliter->ptr);
}
delete [] reinterpret_cast<char*>(cc.ptr);
OrigFuncs &tmpvec = cc.cc.vt[vtbl_offs];
if (tmpvec.size() <= (size_t)vtbl_idx)
tmpvec.resize(vtbl_idx+1);
tmpvec[vtbl_idx] = orig_entry;
}
bool CSourceHookImpl::ApplyCallClassPatch(CallClass &cc, int vtbl_offs, int vtbl_idx, void *orig_entry)
{
char *ptr = reinterpret_cast<char*>(cc.ptr);
void *vtable = *reinterpret_cast<void**>(ptr + vtbl_offs);
// Check whether we already know that vtable
CallClass::VTableList::iterator vtbliter = std::find(cc.vtables.begin(),
cc.vtables.end(), reinterpret_cast<void*>(vtable));
int actvtablesize=MAX_VTABLE_LEN;
if (vtbliter == cc.vtables.end())
{
CallClass::VTable newvtableinfo;
int pagesnum = (MAX_VTABLE_LEN % m_PageSize == 0) ? MAX_VTABLE_LEN / m_PageSize : MAX_VTABLE_LEN / m_PageSize + 1;
// Set all pages in the range to readwrite
// :TODO: Fix this!
char *pagebegin = reinterpret_cast<char*>(vtable) - (reinterpret_cast<int>(vtable) % m_PageSize);
for (int ipage = 0; ipage < pagesnum; ++ipage)
{
if (!SetMemAccess(reinterpret_cast<void*>(pagebegin + ipage * m_PageSize), 1,
SH_MEM_READ | SH_MEM_WRITE))
{
// We can't go here anymore
actvtablesize = static_cast<int>((pagebegin + ipage * m_PageSize) - reinterpret_cast<char*>(vtable));
break;
}
}
if (actvtablesize < 1)
{
// We can't access the vtable -> Quit
return false;
}
// Create a new vtable
newvtableinfo.ptr = reinterpret_cast<void*>(new char[actvtablesize]);
// Fill it with the information from the old vtable
memcpy(newvtableinfo.ptr, vtable, actvtablesize);
newvtableinfo.actsize = actvtablesize;
// Set the pointer in the object and add it to the list of already known vtables
*reinterpret_cast<void**>(ptr + vtbl_offs) = newvtableinfo.ptr;
cc.vtables.push_back(newvtableinfo);
vtbliter = cc.vtables.end();
--vtbliter;
// :TODO: When not codegen, patch the other vtable?
}
// Check whether we already have this patch
if (std::find(vtbliter->patches.begin(), vtbliter->patches.end(), vtbl_idx) == vtbliter->patches.end())
{
// No -> apply it
// Get a call gate
void *callgate = NULL;
#if SH_RUNTIME_CODEGEN == 1
unsigned char *cggen = new unsigned char[SH_CCC_CODESIZE];
//SH_CCC_MakeGate(cc.iface, cc.ptr, cggen, orig_entry);
callgate = (void*)cggen;
SetMemAccess(callgate, SH_CCC_CODESIZE, SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
#else
// :TODO:
#error Not supported yet!
#endif
vtbliter->patches.push_back(vtbl_idx);
reinterpret_cast<void**>(vtbliter->ptr)[vtbl_idx] = callgate;
}
return true;
}
CSourceHookImpl::HookManInfoList::iterator CSourceHookImpl::FindHookMan(HookManInfoList::iterator begin,
HookManInfoList::iterator end, const char *proto, int vtblofs, int vtblidx, int thisptrofs)
HookManInfoList::iterator end, const char *proto, int vtblofs, int vtblidx)
{
for (HookManInfoList::iterator hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
{
if (strcmp(hookmaniter->proto, proto) == 0 && hookmaniter->vtbl_offs == vtblofs && hookmaniter->vtbl_idx == vtblidx &&
hookmaniter->thisptr_offs == thisptrofs)
if (strcmp(hookmaniter->proto, proto) == 0 && hookmaniter->vtbl_offs == vtblofs &&
hookmaniter->vtbl_idx == vtblidx)
break;
}
return hookmaniter;
@ -489,38 +392,42 @@ namespace SourceHook
{
// Go through all hook managers, all interfaces, and set the status of all hooks of this plugin to paused
for (HookManInfoList::iterator hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookmaniter->ifaces.begin();
ifaceiter != hookmaniter->ifaces.end(); ++ifaceiter)
{
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin();
hookiter != ifaceiter->hooks_pre.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = true;
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hookmaniter->vfnptrs.begin();
vfnptr_iter != hookmaniter->vfnptrs.end(); ++vfnptr_iter)
for (HookManagerInfo::VfnPtr::IfaceListIter ifaceiter = vfnptr_iter->ifaces.begin();
ifaceiter != vfnptr_iter->ifaces.end(); ++ifaceiter)
{
for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin();
hookiter != ifaceiter->hooks_pre.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = true;
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
hookiter != ifaceiter->hooks_post.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = true;
}
for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
hookiter != ifaceiter->hooks_post.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = true;
}
}
void CSourceHookImpl::UnpausePlugin(Plugin plug)
{
// Go through all hook managers, all interfaces, and set the status of all hooks of this plugin to normal
// Go through all hook managers, all interfaces, and set the status of all hooks of this plugin to paused
for (HookManInfoList::iterator hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookmaniter->ifaces.begin();
ifaceiter != hookmaniter->ifaces.end(); ++ifaceiter)
{
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin();
hookiter != ifaceiter->hooks_pre.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = false;
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hookmaniter->vfnptrs.begin();
vfnptr_iter != hookmaniter->vfnptrs.end(); ++vfnptr_iter)
for (HookManagerInfo::VfnPtr::IfaceListIter ifaceiter = vfnptr_iter->ifaces.begin();
ifaceiter != vfnptr_iter->ifaces.end(); ++ifaceiter)
{
for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin();
hookiter != ifaceiter->hooks_pre.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = false;
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
hookiter != ifaceiter->hooks_post.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = false;
}
for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
hookiter != ifaceiter->hooks_post.end(); ++hookiter)
if (plug == hookiter->plug)
hookiter->paused = false;
}
}
void CSourceHookImpl::SetRes(META_RES res)

File diff suppressed because it is too large Load Diff

View File

@ -28,14 +28,15 @@ namespace SourceHook
*/
struct RemoveHookInfo
{
RemoveHookInfo(Plugin p, void *i, HookManagerPubFunc h, ISHDelegate *hd, bool ps)
: plug(p), iface(i), hpf(h), handler(hd), post(ps)
RemoveHookInfo(Plugin pplug, void *piface, HookManagerPubFunc phookman,
ISHDelegate *phandler, bool ppost)
: plug(pplug), iface(piface), hookman(phookman), handler(phandler), post(ppost)
{
}
Plugin plug;
void *iface;
HookManagerPubFunc hpf;
HookManagerPubFunc hookman;
ISHDelegate *handler;
bool post;
};
@ -45,27 +46,30 @@ namespace SourceHook
*/
typedef std::list<HookManagerInfo> HookManInfoList;
struct CallClassInfo
{
GenericCallClass cc;
int refcounter;
bool operator==(void *other)
{
return cc.ptr == other;
}
};
/**
* @brief A list of Impl_CallClass structures
* @brief A list of CallClass structures
*/
typedef std::list<CallClass> Impl_CallClassList;
typedef std::list<CallClassInfo> Impl_CallClassList;
Impl_CallClassList m_CallClasses; //!< A list of already generated callclasses
HookManInfoList m_HookMans; //!< A list of hook managers
int m_PageSize; //!< Stores the system's page size
/**
* @brief Finds a hook manager for a function based on a text-prototype, a vtable offset and a vtable index
*/
HookManInfoList::iterator FindHookMan(HookManInfoList::iterator begin, HookManInfoList::iterator end,
const char *proto, int vtblofs, int vtblidx, int thisptrofs);
const char *proto, int vtblofs, int vtblidx);
void FreeCallClass(CallClass &cc);
bool ApplyCallClassPatch(CallClass &cc, int vtbl_offs, int vtbl_idx, void *orig_entry);
static const int MAX_VTABLE_LEN = 4096; //!< Maximal vtable length in bytes
void ApplyCallClassPatch(CallClassInfo &cc, int vtbl_offs, int vtbl_idx, void *orig_entry);
META_RES m_Status, m_PrevRes, m_CurRes;
const void *m_OrigRet;
@ -97,7 +101,7 @@ namespace SourceHook
* @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post handler
*/
bool AddHook(Plugin plug, void *iface, int ifacesize, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post);
bool AddHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post);
/**
* @brief Removes a hook.
@ -106,12 +110,35 @@ namespace SourceHook
*
* @param plug The unique identifier of the plugin that calls this function
* @param iface The interface pointer
* @param thisptr_offs This pointer adjuster
* @param myHookMan A hook manager function that should be capable of handling the function
* @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post handler
*/
bool RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post);
/**
* @brief Removes a hook.
*
* @return True if the function succeeded, false otherwise
*
* @param plug The unique identifier of the plugin that calls this function
* @param iface Already adjusted interface pointer
* @param myHookMan A hook manager function that should be capable of handling the function
* @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post handler
*/
bool RemoveHook(Plugin plug, void *iface, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post);
/**
* @brief Removes a hook.
*
* @ return True if the function succeeded, false otherwise
*
* @param info A RemoveHookInfo structure, describing the hook
*/
bool RemoveHook(RemoveHookInfo info);
/**
* @brief Checks whether a plugin has (a) hook manager(s) that is/are currently used by other plugins
*
@ -138,14 +165,14 @@ namespace SourceHook
*
* @param iface The interface pointer
*/
void *GetCallClass(void *iface, size_t size);
GenericCallClass *GetCallClass(void *iface);
/**
* @brief Release a callclass
*
* @param ptr Pointer to the callclass
*/
virtual void ReleaseCallClass(void *ptr);
virtual void ReleaseCallClass(GenericCallClass *ptr);
virtual void SetRes(META_RES res); //!< Sets the meta result
virtual META_RES GetPrevRes(); //!< Gets the meta result of the previously called handler

View File

@ -402,93 +402,93 @@ bool Test_F16_int_post_handler(int x)
RETURN_META_VALUE(MRES_OVERRIDE, !META_RESULT_ORIG_RET(bool));
}
// I haven't checked the second F16 function yet
int main(int argc, char *argv[])
{
// Get an instance and call the functions using the pointer
// (otherwise the compiler optimizes away the vtable lookup)
Test zLOL;
Test zLOL2;
Test *zLOL_Ptr = &zLOL;
Test *zLOL2_Ptr = &zLOL2;
g_SHPtr = &g_SHImpl;
//////////////////////////////////////////////////////////////////////////
Test *cc = SH_GET_CALLCLASS(Test, zLOL_Ptr);
SourceHook::CallClass<Test> *cc = SH_GET_CALLCLASS(zLOL_Ptr);
printf("TEST1.1: Calling F299\n");
zLOL_Ptr->F299();
printf("TEST1.2: Calling F299 through cc\n");
cc->F299();
SH_CALL(cc, &Test::F299)();
printf("TEST1.3: Hooking it\n");
SH_ADD_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler, false);
zLOL2_Ptr->F299();
printf("TEST1.4: Calling F299\n");
zLOL_Ptr->F299();
printf("TEST1.5: Calling F299 through cc\n");
cc->F299();
SH_CALL(cc, &Test::F299)();
printf("TEST1.6: Adding one more pre and one post hook\n");
SH_ADD_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler2, false);
SH_ADD_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_post_handler, true);
printf("TEST1.7: Calling F299\n");
zLOL_Ptr->F299();
printf("TEST1.8: Calling F299 through cc\n");
cc->F299();
SH_CALL(cc, &Test::F299)();
printf("TEST1.9: Removing pre hooks\n");
SH_REMOVE_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler, false);
SH_REMOVE_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler2, false);
printf("TEST1.10: Calling F299\n");
zLOL_Ptr->F299();
printf("TEST1.11: Calling F299 through cc\n");
cc->F299();
SH_CALL(cc, &Test::F299)();
printf("TEST1.12: Removing post hook\n");
SH_REMOVE_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_post_handler, true);
printf("TEST1.13: Calling F299\n");
zLOL_Ptr->F299();
printf("TEST1.14: Calling F299 through cc\n");
cc->F299();
SH_CALL(cc, &Test::F299)();
//////////////////////////////////////////////////////////////////////////
printf("\n\n*************************************************\n\n");
printf("TEST2.1: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.2: Calling F16(155) through CC\n");
printf("Returned: %d\n", cc->F16(155) ? 1 : 0);
printf("Returned: %d\n", SH_CALL(cc, static_cast<bool(Test::*)(int)>(&Test::F16))(155) ? 1 : 0);
printf("TEST2.3: Hooking it\n");
SH_ADD_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_pre_handler, false);
printf("TEST2.4: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.5: Calling F16(155) through CC\n");
printf("Returned: %d\n", cc->F16(155) ? 1 : 0);
printf("Returned: %d\n", SH_CALL(cc, static_cast<bool(Test::*)(int)>(&Test::F16))(155) ? 1 : 0);
printf("TEST2.6: Adding post hook\n");
SH_ADD_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_post_handler, true);
printf("TEST2.7: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.8: Calling F16(155) through CC\n");
printf("Returned: %d\n", cc->F16(155) ? 1 : 0);
printf("TEST2.9: Removing pre hook\n");
printf("Returned: %d\n", SH_CALL(cc, static_cast<bool(Test::*)(int)>(&Test::F16))(155) ? 1 : 0);
printf("TEST2.XX.1: Pausing the plugin\n");
g_SHImpl.PausePlugin(g_PLID);
printf("TEST2.XX.2: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.XX.3: Calling F16(155) through CC\n");
printf("Returned: %d\n", cc->F16(155) ? 1 : 0);
printf("Returned: %d\n", SH_CALL(cc, static_cast<bool(Test::*)(int)>(&Test::F16))(155) ? 1 : 0);
printf("TEST2.XX.4: Unpausing the plugin\n");
g_SHImpl.UnpausePlugin(g_PLID);
printf("TEST2.XX.5: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.XX.6: Calling F16(155) through CC\n");
printf("Returned: %d\n", cc->F16(155) ? 1 : 0);
printf("Returned: %d\n", SH_CALL(cc, static_cast<bool(Test::*)(int)>(&Test::F16))(155) ? 1 : 0);
printf("TEST2.9: Removing pre hook\n");
SH_REMOVE_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_pre_handler, false);
printf("TEST2.10: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.11: Calling F16(155) through CC\n");
printf("Returned: %d\n", cc->F16(155) ? 1 : 0);
printf("Returned: %d\n", SH_CALL(cc, static_cast<bool(Test::*)(int)>(&Test::F16))(155) ? 1 : 0);
printf("TEST2.12: Removing post hook\n");
SH_REMOVE_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_post_handler, true);
printf("TEST2.10: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.11: Calling F16(155) through CC\n");
printf("Returned: %d\n", cc->F16(155) ? 1 : 0);
printf("Returned: %d\n", SH_CALL(cc, static_cast<bool(Test::*)(int)>(&Test::F16))(155) ? 1 : 0);
SH_RELEASE_CALLCLASS(cc);