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 #define SH_GLOB_PLUGPTR g_PLID
#endif #endif
#define SH_ASSERT(x) if (!(x)) __asm { int 3 } #define SH_ASSERT(x, info) if (!(x)) __asm { int 3 }
// System // System
#define SH_SYS_WIN32 1 #define SH_SYS_WIN32 1
@ -54,6 +54,8 @@
#include "sh_memfuncinfo.h" #include "sh_memfuncinfo.h"
#include "sh_memory.h" #include "sh_memory.h"
#include <list> #include <list>
#include <vector>
#include <map>
#include <algorithm> #include <algorithm>
// Good old metamod! // Good old metamod!
@ -73,6 +75,13 @@ namespace SourceHook
{ {
const int STRBUF_LEN=8192; // In bytes, for "vafmt" functions 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 * @brief A plugin typedef
* *
@ -136,66 +145,61 @@ namespace SourceHook
*/ */
struct HookManagerInfo struct HookManagerInfo
{ {
struct Iface struct VfnPtr
{ {
struct Hook struct Iface
{ {
ISHDelegate *handler; //!< Pointer to the handler struct Hook
bool paused; //!< If true, the hook should not be executed {
Plugin plug; //!< The owner plugin 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 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 typedef std::list<Iface> IfaceList;
bool operator ==(void *other) const typedef IfaceList::iterator IfaceListIter;
IfaceList ifaces;
bool operator ==(void *other)
{ {
return ptr == other; return vfnptr == other;
} }
}; };
Plugin plug; //!< The owner plugin Plugin plug; //!< The owner plugin
const char *proto; //!< The prototype of the function the hook manager is responsible for const char *proto; //!< The prototype of the function the hook manager is responsible for
int vtbl_idx; //!< The vtable index int vtbl_idx; //!< The vtable index
int vtbl_offs; //!< The vtable offset int vtbl_offs; //!< The vtable offset
int thisptr_offs; //!< The this-pointer-adjuster
HookManagerPubFunc func; //!< The interface to the hook manager HookManagerPubFunc func; //!< The interface to the hook manager
int hookfunc_vtbl_idx; //!< the vtable index of the hookfunc void *hookfunc_vfnptr; //!< Pointer to the hookfunc impl
int hookfunc_vtbl_offs; //!< the vtable offset of the hookfunc
void *hookfunc_inst; //!< Instance of the class the hookfunc is in
std::list<Iface> ifaces; //!< List of hooked interfaces typedef std::list<VfnPtr> VfnPtrList;
typedef VfnPtrList::iterator VfnPtrListIter;
VfnPtrList vfnptrs; //!< List of hooked interfaces
}; };
/** typedef std::vector<void*> OrigFuncs;
* @brief Structure describing a callclass typedef std::map<int, OrigFuncs> OrigVTables;
*/
struct CallClass template<class B> struct CallClass
{ {
struct VTable B *ptr;
{ OrigVTables vt;
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;
}
}; };
typedef CallClass<void> GenericCallClass;
/** /**
* @brief The main SourceHook interface * @brief The main SourceHook interface
*/ */
@ -214,7 +218,7 @@ namespace SourceHook
* @param handler A pointer to a FastDelegate containing the hook handler * @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post 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. * @brief Removes a hook.
@ -227,7 +231,7 @@ namespace SourceHook
* @param handler A pointer to a FastDelegate containing the hook handler * @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post 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 * @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 iface The interface pointer
* @param size Size of the class * @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 * @brief Release a callclass
* *
* @param ptr Pointer to the 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 void SetRes(META_RES res) = 0; //!< Sets the meta result
virtual META_RES GetPrevRes() = 0; //!< Gets the meta result of the previously called handler 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 * @brief Get/generate callclass for an interface pointer
* *
* @param ifacetype The type of the interface
* @param ifaceptr The interface pointer * @param ifaceptr The interface pointer
*/ */
#define SH_GET_CALLCLASS(ifacetype, ifaceptr) reinterpret_cast<ifacetype*>(SH_GLOB_SHPTR->GetCallClass(ifaceptr, sizeof(ifacetype))) template<class ifacetype>
#define SH_RELEASE_CALLCLASS(ptr) SH_GLOB_SHPTR->ReleaseCallClass(reinterpret_cast<void*>(ptr)) 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) \ #define SH_ADD_HOOK(ifacetype, ifacefunc, ifaceptr, handler, post) \
SourceHook::SH_FHAdd##ifacetype##ifacefunc((void*)ifaceptr, post, handler) SourceHook::SH_FHAdd##ifacetype##ifacefunc((void*)ifaceptr, post, handler)
@ -307,6 +316,18 @@ namespace SourceHook
#define SH_NOATTRIB #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 #define SH_FHCls(ift, iff, ov) FHCls_##ift##iff##ov
@ -320,13 +341,10 @@ namespace SourceHook
GetFuncInfo(funcptr, mfi); \ GetFuncInfo(funcptr, mfi); \
param->vtbl_idx = mfi.vtblindex; \ param->vtbl_idx = mfi.vtblindex; \
param->vtbl_offs = mfi.vtbloffs; \ 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); \ GetFuncInfo(&SH_FHCls(ifacetype,ifacefunc,overload)::Func, mfi); \
param->hookfunc_vtbl_idx = mfi.vtblindex; \ param->hookfunc_vfnptr = \
param->hookfunc_vtbl_offs = mfi.vtbloffs; \ reinterpret_cast<void**>(reinterpret_cast<char*>(&ms_Inst) + mfi.vtbloffs)[mfi.vtblindex]; \
param->hookfunc_inst = (void*)&ms_Inst; \
return 0; \ return 0; \
} \ } \
else if (action == HA_Register) \ else if (action == HA_Register) \
@ -353,7 +371,7 @@ namespace SourceHook
static const char *ms_Proto; \ static const char *ms_Proto; \
SHINT_MAKE_HOOKMANPUBFUNC(ifacetype, ifacefunc, overload, funcptr) 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; \ const char *SH_FHCls(ifacetype,ifacefunc,overload)::ms_Proto = proto; \
SH_FHCls(ifacetype,ifacefunc,overload) SH_FHCls(ifacetype,ifacefunc,overload)::ms_Inst; \ 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, \ bool SH_FHAdd##ifacetype##ifacefunc(void *iface, bool post, \
SH_FHCls(ifacetype,ifacefunc,overload)::FD handler) \ 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, \ SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, \
new CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD>(handler), post); \ new CSHDelegate<SH_FHCls(ifacetype,ifacefunc,overload)::FD>(handler), post); \
} \ } \
bool SH_FHRemove##ifacetype##ifacefunc(void *iface, bool post, \ bool SH_FHRemove##ifacetype##ifacefunc(void *iface, bool post, \
SH_FHCls(ifacetype,ifacefunc,overload)::FD handler) \ 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); \ 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); \ SH_FHCls(ifacetype,ifacefunc,overload)::HookManPubFunc, &tmp, post); \
} \ } \
} }
#define SH_SETUPCALLS(rettype) \ #define SH_SETUPCALLS(rettype, paramtypes, params) \
/* 1) Find the iface ptr */ \ /* 1) Find the vfnptr */ \
/* 1.1) Adjust to original this pointer */ \ void *ourvfnptr = reinterpret_cast<void*>( \
void *origthis = this - ms_HI->thisptr_offs; \ *reinterpret_cast<void***>(reinterpret_cast<char*>(this) + ms_HI->vtbl_offs) + ms_HI->vtbl_idx); \
std::list<HookManagerInfo::Iface>::iterator ifaceiter = std::find(ms_HI->ifaces.begin(), \ \
ms_HI->ifaces.end(), origthis); \ HookManagerInfo::VfnPtrListIter vfptriter = std::find(ms_HI->vfnptrs.begin(), \
SH_ASSERT(ifaceiter != ms_HI->ifaces.end()); \ ms_HI->vfnptrs.end(), ourvfnptr); \
HookManagerInfo::Iface &ci = *ifaceiter; \ 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 */ \ /* 2) Declare some vars and set it up */ \
std::list<HookManagerInfo::Iface::Hook> &prelist = ci.hooks_pre; \ std::list<HookManagerInfo::VfnPtr::Iface::Hook> &prelist = ci.hooks_pre; \
std::list<HookManagerInfo::Iface::Hook> &postlist = ci.hooks_post; \ std::list<HookManagerInfo::VfnPtr::Iface::Hook> &postlist = ci.hooks_post; \
rettype orig_ret, override_ret, plugin_ret; \ rettype orig_ret, override_ret, plugin_ret; \
META_RES &cur_res = SH_GLOB_SHPTR->GetCurResRef(); \ META_RES &cur_res = SH_GLOB_SHPTR->GetCurResRef(); \
META_RES &prev_res = SH_GLOB_SHPTR->GetPrevResRef(); \ META_RES &prev_res = SH_GLOB_SHPTR->GetPrevResRef(); \
META_RES &status = SH_GLOB_SHPTR->GetStatusRef(); \ META_RES &status = SH_GLOB_SHPTR->GetStatusRef(); \
status = MRES_IGNORED; \ 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->SetOrigRet(reinterpret_cast<void*>(&orig_ret)); \
SH_GLOB_SHPTR->SetOverrideRet(NULL); SH_GLOB_SHPTR->SetOverrideRet(NULL);
#define SH_CALL_HOOKS(post, params) \ #define SH_CALL_HOOKS(post, params) \
prev_res = MRES_IGNORED; \ 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; \ if (hiter->paused) continue; \
cur_res = MRES_IGNORED; \ 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) \ 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 \ else \
orig_ret = override_ret; orig_ret = override_ret;
#define SH_RETURN() \ #define SH_RETURN() \
return status >= MRES_OVERRIDE ? override_ret : orig_ret; return status >= MRES_OVERRIDE ? override_ret : orig_ret;
#define SH_HANDLEFUNC(ifacetype, ifacefunc, params, rettype) \ #define SH_HANDLEFUNC(ifacetype, ifacefunc, paramtypes, params, rettype) \
SH_SETUPCALLS(rettype) \ SH_SETUPCALLS(rettype, paramtypes, params) \
SH_CALL_HOOKS(pre, 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_CALL_HOOKS(post, params) \
SH_RETURN() SH_RETURN()
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
#define SH_SETUPCALLS_void() \ #define SH_SETUPCALLS_void(paramtypes, params) \
/* 1) Find the iface ptr */ \ /* 1) Find the vfnptr */ \
/* 1.1) Adjust to original this pointer */ \ void *ourvfnptr = reinterpret_cast<void*>( \
void *origthis = this - ms_HI->thisptr_offs; \ *reinterpret_cast<void***>(reinterpret_cast<char*>(this) + ms_HI->vtbl_offs) + ms_HI->vtbl_idx); \
std::list<HookManagerInfo::Iface>::iterator ifaceiter = std::find(ms_HI->ifaces.begin(), \ \
ms_HI->ifaces.end(), origthis); \ HookManagerInfo::VfnPtrListIter vfptriter = std::find(ms_HI->vfnptrs.begin(), \
SH_ASSERT(ifaceiter != ms_HI->ifaces.end()); \ ms_HI->vfnptrs.end(), ourvfnptr); \
HookManagerInfo::Iface &ci = *ifaceiter; \ 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 */ \ /* 2) Declare some vars and set it up */ \
std::list<HookManagerInfo::Iface::Hook> &prelist = ci.hooks_pre; \ std::list<HookManagerInfo::VfnPtr::Iface::Hook> &prelist = ci.hooks_pre; \
std::list<HookManagerInfo::Iface::Hook> &postlist = ci.hooks_post; \ std::list<HookManagerInfo::VfnPtr::Iface::Hook> &postlist = ci.hooks_post; \
META_RES &cur_res = SH_GLOB_SHPTR->GetCurResRef(); \ META_RES &cur_res = SH_GLOB_SHPTR->GetCurResRef(); \
META_RES &prev_res = SH_GLOB_SHPTR->GetPrevResRef(); \ META_RES &prev_res = SH_GLOB_SHPTR->GetPrevResRef(); \
META_RES &status = SH_GLOB_SHPTR->GetStatusRef(); \ META_RES &status = SH_GLOB_SHPTR->GetStatusRef(); \
status = MRES_IGNORED; \ status = MRES_IGNORED; \
SH_GLOB_SHPTR->SetIfacePtr(ci.ptr); \ SH_GLOB_SHPTR->SetIfacePtr(this); \
SH_GLOB_SHPTR->SetOverrideRet(NULL); SH_GLOB_SHPTR->SetOverrideRet(NULL);
#define SH_CALL_HOOKS_void(post, params) \ #define SH_CALL_HOOKS_void(post, params) \
prev_res = MRES_IGNORED; \ 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; \ if (hiter->paused) continue; \
cur_res = MRES_IGNORED; \ cur_res = MRES_IGNORED; \
@ -458,54 +521,60 @@ namespace SourceHook
status = cur_res; \ status = cur_res; \
} }
#define SH_CALL_ORIG_void(ifacetype, ifacefunc, params) \ #define SH_CALL_ORIG_void(ifacetype, ifacefunc, paramtypes, params) \
if (status != MRES_SUPERCEDE) \ 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_RETURN_void()
#define SH_HANDLEFUNC_void(ifacetype, ifacefunc, params) \ #define SH_HANDLEFUNC_void(ifacetype, ifacefunc, paramtypes, params) \
SH_SETUPCALLS_void() \ SH_SETUPCALLS_void(paramtypes, params) \
SH_CALL_HOOKS_void(pre, 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_CALL_HOOKS_void(post, params) \
SH_RETURN_void() SH_RETURN_void()
// Special vafmt handlers // Special vafmt handlers
#define SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, params_orig, params_plug, rettype) \ #define SH_HANDLEFUNC_vafmt(ifacetype, ifacefunc, paramtypes, params_orig, params_plug, rettype) \
SH_SETUPCALLS(rettype) \ SH_SETUPCALLS(rettype, paramtypes, params_orig) \
SH_CALL_HOOKS(pre, params_plug) \ 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_CALL_HOOKS(post, params_plug) \
SH_RETURN() SH_RETURN()
#define SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, params_orig, params_plug) \ #define SH_HANDLEFUNC_void_vafmt(ifacetype, ifacefunc, paramtypes, params_orig, params_plug) \
SH_SETUPCALLS_void() \ SH_SETUPCALLS_void(paramtypes, params_orig) \
SH_CALL_HOOKS_void(pre, params_plug) \ 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_CALL_HOOKS_void(post, params_plug) \
SH_RETURN_void() SH_RETURN_void()
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@VARARGS@ @VARARGS@
// ********* // ********* Support for @$@ arguments *********
#define SH_DECL_HOOK@$@(ifacetype, ifacefunc, attr, overload, rettype@, param%%@) \ #define SH_DECL_HOOK@$@(ifacetype, ifacefunc, attr, overload, rettype@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<rettype (ifacetype::*)(@param%%|, @)> \ SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<rettype (ifacetype::*)(@param%%|, @)> \
(&ifacetype::ifacefunc))) \ (&ifacetype::ifacefunc))) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @@, @rettype> FD; \ typedef fastdelegate::FastDelegate@$@<@param%%|, @@, @rettype> FD; \
virtual rettype Func(@param%% p%%|, @) attr \ virtual rettype Func(@param%% p%%|, @) attr \
{ SH_HANDLEFUNC(ifacetype, ifacefunc, (@p%%|, @), rettype); } \ { SH_HANDLEFUNC(ifacetype, ifacefunc, (@param%%|, @), (@p%%|, @), rettype); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr "|" #rettype @"|" #param%%| @) 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%%@) \ #define SH_DECL_HOOK@$@_void(ifacetype, ifacefunc, attr, overload@, param%%@) \
SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<void (ifacetype::*)(@param%%|, @)> \ SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<void (ifacetype::*)(@param%%|, @)> \
(&ifacetype::ifacefunc))) \ (&ifacetype::ifacefunc))) \
typedef fastdelegate::FastDelegate@$@<@param%%|, @> FD; \ typedef fastdelegate::FastDelegate@$@<@param%%|, @> FD; \
virtual void Func(@param%% p%%|, @) attr \ virtual void Func(@param%% p%%|, @) attr \
{ SH_HANDLEFUNC_void(ifacetype, ifacefunc, (@p%%|, @)); } \ { SH_HANDLEFUNC_void(ifacetype, ifacefunc, (@param%%|, @), (@p%%|, @)); } \
SHINT_MAKE_GENERICSTUFF_END(ifacetype, ifacefunc, overload, #attr @"|" #param%%| @) 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%%@) \ #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 *, ...)> \ SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<rettype (ifacetype::*)(@param%%|, @@, @const char *, ...)> \
@ -518,9 +587,10 @@ namespace SourceHook
va_start(ap, fmt); \ va_start(ap, fmt); \
vsnprintf(buf, sizeof(buf), fmt, ap); \ vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(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%%@) \ #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 *, ...)> \ SHINT_MAKE_GENERICSTUFF_BEGIN(ifacetype, ifacefunc, overload, (static_cast<void (ifacetype::*)(@param%%|, @@, @const char *, ...)> \
@ -533,22 +603,70 @@ namespace SourceHook
va_start(ap, fmt); \ va_start(ap, fmt); \
vsnprintf(buf, sizeof(buf), fmt, ap); \ vsnprintf(buf, sizeof(buf), fmt, ap); \
va_end(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@ @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 #endif
// The pope is dead. -> :( // The pope is dead. -> :(

View File

@ -16,93 +16,38 @@
#include <algorithm> #include <algorithm>
#include "sourcehook_impl.h" #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 namespace SourceHook
{ {
CSourceHookImpl::CSourceHookImpl() 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() CSourceHookImpl::~CSourceHookImpl()
{ {
} }
bool CSourceHookImpl::IsPluginInUse(Plugin plug) bool CSourceHookImpl::IsPluginInUse(Plugin plug)
{ {
// Iterate through all hook managers which are in this plugin // 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 // If a hook from an other plugin is found, return true
// Return false otherwise // Return false otherwise
#define TMP_CHECK_LIST(name) \ #define TMP_CHECK_LIST(name) \
for (iter3 = iter2->name.begin(); iter3 != iter2->name.end(); ++iter3) \ for (hook_iter = iface_iter->name.begin(); hook_iter != iface_iter->name.end(); ++hook_iter) \
if (iter3->plug == plug) \ if (hook_iter->plug == plug) \
return true; 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_pre);
TMP_CHECK_LIST(hooks_post); TMP_CHECK_LIST(hooks_post);
} }
@ -116,24 +61,29 @@ namespace SourceHook
{ {
// 1) Manually remove all hooks by this plugin // 1) Manually remove all hooks by this plugin
std::list<RemoveHookInfo> hookstoremove; std::list<RemoveHookInfo> hookstoremove;
#define TMP_CHECK_LIST(name, ispost) \
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter) 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(); for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
++iter2) 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) for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
if (iter3->plug == plug) iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, false)); {
std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hook_iter;
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_post.begin(); iter3 != iter2->hooks_post.end(); ++iter3) TMP_CHECK_LIST(hooks_pre, false);
if (iter3->plug == plug) TMP_CHECK_LIST(hooks_post, true);
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, true)); }
} }
} }
#undef TMP_CHECK_LIST
for (std::list<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter) 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. // 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 // 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; HookManInfoList tmphookmans;
bool erase = false; bool erase = false;
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end();
erase ? iter=m_HookMans.erase(iter) : ++iter) 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 // All hooks by this plugin are already removed
// So if there is an iface, it has to be used by an other plugin // So if there is a vfnptr, it has to be used by an other plugin
tmphookmans.push_back(*iter); tmphookmans.push_back(*hmil_iter);
} }
erase = true; erase = true;
} }
@ -159,29 +109,28 @@ namespace SourceHook
} }
// For each hook manager: // 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 // Find a suitable hook manager in an other plugin
HookManInfoList::iterator newHookMan = FindHookMan(m_HookMans.begin(), m_HookMans.end(), HookManInfoList::iterator newHookMan = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
iter->proto, iter->vtbl_offs, iter->vtbl_idx, iter->thisptr_offs); hmil_iter->proto, hmil_iter->vtbl_offs, hmil_iter->vtbl_idx);
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);
}
// AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo/thisptroffs // This should _never_ happen.
SH_ASSERT(newHookMan->plug != plug); // 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 // AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo
SH_ASSERT(newHookMan->ifaces.empty()); SH_ASSERT(newHookMan->plug != plug, "New hook manager from same plugin!");
// Move the ifaces from the old hook manager to the new one // The first hook manager should be always used - so the new hook manager has to be empty
newHookMan->ifaces = iter->ifaces; 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 // Unregister the old one, register the new one
iter->func(HA_Unregister, NULL); hmil_iter->func(HA_Unregister, NULL);
newHookMan->func(HA_Register, &(*newHookMan)); newHookMan->func(HA_Register, &(*newHookMan));
} }
} }
@ -189,43 +138,49 @@ namespace SourceHook
void CSourceHookImpl::CompleteShutdown() void CSourceHookImpl::CompleteShutdown()
{ {
std::list<RemoveHookInfo> hookstoremove; std::list<RemoveHookInfo> hookstoremove;
#define TMP_CHECK_LIST(name, ispost) \
for (HookManInfoList::iterator iter = m_HookMans.begin(); iter != m_HookMans.end(); ++iter) 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(); for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
++iter2) 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) for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, false)); iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
{
for (std::list<HookManagerInfo::Iface::Hook>::iterator iter3 = iter2->hooks_post.begin(); iter3 != iter2->hooks_post.end(); ++iter3) std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hook_iter;
hookstoremove.push_back(RemoveHookInfo(iter3->plug, iter2->ptr, iter->func, iter3->handler, true)); 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) 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(); 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 // 1) Get info about the hook manager
HookManagerInfo tmp; HookManagerInfo tmp;
if (myHookMan(HA_GetInfo, &tmp) != 0) if (myHookMan(HA_GetInfo, &tmp) != 0)
return false; 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 // 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; HookManInfoList::iterator hkmi_iter;
for (hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter) for (hkmi_iter = m_HookMans.begin(); hkmi_iter != m_HookMans.end(); ++hkmi_iter)
{ {
if (hookmaniter->plug == plug && strcmp(hookmaniter->proto, tmp.proto) == 0 && if (hkmi_iter->plug == plug && strcmp(hkmi_iter->proto, tmp.proto) == 0 &&
hookmaniter->vtbl_offs == tmp.vtbl_offs && hookmaniter->vtbl_idx == tmp.vtbl_idx && hkmi_iter->vtbl_offs == tmp.vtbl_offs && hkmi_iter->vtbl_idx == tmp.vtbl_idx)
hookmaniter->thisptr_offs == tmp.thisptr_offs)
break; break;
} }
if (hookmaniter == m_HookMans.end()) if (hkmi_iter == m_HookMans.end())
{ {
// No such hook manager from this plugin yet, add it! // No such hook manager from this plugin yet, add it!
tmp.func = myHookMan; tmp.func = myHookMan;
@ -234,47 +189,70 @@ namespace SourceHook
} }
// Then, search for a suitable hook manager (from the beginning) // 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); HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(), tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx);
SH_ASSERT(hookman != m_HookMans.end()); 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 // 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)); 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; // Add a new one
ifs.ptr = iface; HookManagerInfo::VfnPtr vfp;
ifs.callclass = GetCallClass(iface, ifacesize); vfp.vfnptr = cur_vfnptr;
void *vtableptr = *reinterpret_cast<void**>(reinterpret_cast<char*>(iface) + hookman->vtbl_offs); vfp.orig_entry = *reinterpret_cast<void**>(cur_vfnptr);
SetMemAccess(vtableptr, sizeof(void*) * hookman->vtbl_idx, SH_MEM_READ | SH_MEM_WRITE); hookman->vfnptrs.push_back(vfp);
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] = // Alter vtable entry
(*reinterpret_cast<void***>(reinterpret_cast<char*>(hookman->hookfunc_inst) + hookman->hookfunc_vtbl_offs))[hookman->hookfunc_vtbl_idx]; SetMemAccess(cur_vtptr, sizeof(void*) * tmp.vtbl_idx, SH_MEM_READ | SH_MEM_WRITE);
hookman->ifaces.push_back(ifs); *reinterpret_cast<void**>(cur_vfnptr) = *reinterpret_cast<void**>(hookman->hookfunc_vfnptr);
ifsiter = hookman->ifaces.end();
--ifsiter; // 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.handler = handler;
hookinfo.plug = plug; hookinfo.plug = plug;
hookinfo.paused = false; hookinfo.paused = false;
if (post) if (post)
ifsiter->hooks_post.push_back(hookinfo); iface_iter->hooks_post.push_back(hookinfo);
else 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 // 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) 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; break;
} }
} }
@ -282,7 +260,18 @@ namespace SourceHook
return true; 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; HookManagerInfo tmp;
if (myHookMan(HA_GetInfo, &tmp) != 0) if (myHookMan(HA_GetInfo, &tmp) != 0)
@ -290,196 +279,110 @@ namespace SourceHook
// Find the hook manager and the hook // Find the hook manager and the hook
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(), 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()) if (hookman == m_HookMans.end())
return false; 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; for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
bool erase; iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = hooks.begin();
hookiter != hooks.end(); erase ? hookiter = hooks.erase(hookiter) : ++hookiter)
{ {
erase = hookiter->plug == plug && hookiter->handler->IsEqual(handler); std::list<HookManagerInfo::VfnPtr::Iface::Hook> &hooks =
if (erase) post ? iface_iter->hooks_post : iface_iter->hooks_pre;
hookiter->handler->DeleteThis(); // Make the _plugin_ delete the handler object
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 true;
} }
} }
return false; 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) for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
{ {
if (cciter->iface == iface) if (cciter->cc.ptr == iface)
{ {
++cciter->refcounter; ++cciter->refcounter;
return cciter->ptr; return &cciter->cc;
} }
} }
// The layout of callclasses is: CallClassInfo tmp;
// 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;
tmp.refcounter = 1; 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 (HookManInfoList::iterator hookman = m_HookMans.begin(); hookman != m_HookMans.end(); ++hookman)
{ for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->vfnptrs.begin();
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookman->ifaces.begin(); ifaceiter != hookman->ifaces.end(); ++ifaceiter) vfnptr_iter != hookman->vfnptrs.end(); ++vfnptr_iter)
{ for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
if (ifaceiter->ptr == iface) iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
{ if (iface_iter->ptr == iface)
if (!ApplyCallClassPatch(tmp, hookman->vtbl_offs, hookman->vtbl_idx, ifaceiter->orig_entry)) ApplyCallClassPatch(tmp, hookman->vtbl_offs, hookman->vtbl_idx,
{ vfnptr_iter->orig_entry);
FreeCallClass(tmp);
return NULL;
}
}
}
}
m_CallClasses.push_back(tmp); m_CallClasses.push_back(tmp);
// The object has to be followed by the pointer to the vtable info object return &m_CallClasses.back().cc;
*reinterpret_cast<void**>(ptr + size) = &m_CallClasses.back();
return tmp.ptr;
} }
void CSourceHookImpl::ReleaseCallClass(void *ptr) void CSourceHookImpl::ReleaseCallClass(GenericCallClass *ptr)
{ {
Impl_CallClassList::iterator iter = std::find(m_CallClasses.begin(), m_CallClasses.end(), ptr); Impl_CallClassList::iterator iter = std::find(m_CallClasses.begin(), m_CallClasses.end(), ptr);
if (iter == m_CallClasses.end()) if (iter == m_CallClasses.end())
return; return;
--iter->refcounter; --iter->refcounter;
if (iter->refcounter < 1) if (iter->refcounter < 1)
{
FreeCallClass(*iter);
m_CallClasses.erase(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) OrigFuncs &tmpvec = cc.cc.vt[vtbl_offs];
{ if (tmpvec.size() <= (size_t)vtbl_idx)
#if SH_RUNTIME_CODEGEN == 1 tmpvec.resize(vtbl_idx+1);
// Delete generated callgates tmpvec[vtbl_idx] = orig_entry;
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);
} }
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, 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) 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 && if (strcmp(hookmaniter->proto, proto) == 0 && hookmaniter->vtbl_offs == vtblofs &&
hookmaniter->thisptr_offs == thisptrofs) hookmaniter->vtbl_idx == vtblidx)
break; break;
} }
return hookmaniter; 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 // 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 (HookManInfoList::iterator hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookmaniter->ifaces.begin(); for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hookmaniter->vfnptrs.begin();
ifaceiter != hookmaniter->ifaces.end(); ++ifaceiter) vfnptr_iter != hookmaniter->vfnptrs.end(); ++vfnptr_iter)
{ for (HookManagerInfo::VfnPtr::IfaceListIter ifaceiter = vfnptr_iter->ifaces.begin();
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin(); ifaceiter != vfnptr_iter->ifaces.end(); ++ifaceiter)
hookiter != ifaceiter->hooks_pre.end(); ++hookiter) {
if (plug == hookiter->plug) for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin();
hookiter->paused = true; 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(); for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
hookiter != ifaceiter->hooks_post.end(); ++hookiter) hookiter != ifaceiter->hooks_post.end(); ++hookiter)
if (plug == hookiter->plug) if (plug == hookiter->plug)
hookiter->paused = true; hookiter->paused = true;
} }
} }
void CSourceHookImpl::UnpausePlugin(Plugin plug) 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 (HookManInfoList::iterator hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
for (std::list<HookManagerInfo::Iface>::iterator ifaceiter = hookmaniter->ifaces.begin(); for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hookmaniter->vfnptrs.begin();
ifaceiter != hookmaniter->ifaces.end(); ++ifaceiter) vfnptr_iter != hookmaniter->vfnptrs.end(); ++vfnptr_iter)
{ for (HookManagerInfo::VfnPtr::IfaceListIter ifaceiter = vfnptr_iter->ifaces.begin();
for (std::list<HookManagerInfo::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin(); ifaceiter != vfnptr_iter->ifaces.end(); ++ifaceiter)
hookiter != ifaceiter->hooks_pre.end(); ++hookiter) {
if (plug == hookiter->plug) for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_pre.begin();
hookiter->paused = false; 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(); for (std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
hookiter != ifaceiter->hooks_post.end(); ++hookiter) hookiter != ifaceiter->hooks_post.end(); ++hookiter)
if (plug == hookiter->plug) if (plug == hookiter->plug)
hookiter->paused = false; hookiter->paused = false;
} }
} }
void CSourceHookImpl::SetRes(META_RES res) 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 struct RemoveHookInfo
{ {
RemoveHookInfo(Plugin p, void *i, HookManagerPubFunc h, ISHDelegate *hd, bool ps) RemoveHookInfo(Plugin pplug, void *piface, HookManagerPubFunc phookman,
: plug(p), iface(i), hpf(h), handler(hd), post(ps) ISHDelegate *phandler, bool ppost)
: plug(pplug), iface(piface), hookman(phookman), handler(phandler), post(ppost)
{ {
} }
Plugin plug; Plugin plug;
void *iface; void *iface;
HookManagerPubFunc hpf; HookManagerPubFunc hookman;
ISHDelegate *handler; ISHDelegate *handler;
bool post; bool post;
}; };
@ -45,27 +46,30 @@ namespace SourceHook
*/ */
typedef std::list<HookManagerInfo> HookManInfoList; 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 Impl_CallClassList m_CallClasses; //!< A list of already generated callclasses
HookManInfoList m_HookMans; //!< A list of hook managers 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 * @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, 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); void ApplyCallClassPatch(CallClassInfo &cc, int vtbl_offs, int vtbl_idx, void *orig_entry);
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
META_RES m_Status, m_PrevRes, m_CurRes; META_RES m_Status, m_PrevRes, m_CurRes;
const void *m_OrigRet; const void *m_OrigRet;
@ -97,7 +101,7 @@ namespace SourceHook
* @param handler A pointer to a FastDelegate containing the hook handler * @param handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post 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. * @brief Removes a hook.
@ -106,12 +110,35 @@ namespace SourceHook
* *
* @param plug The unique identifier of the plugin that calls this function * @param plug The unique identifier of the plugin that calls this function
* @param iface The interface pointer * @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 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 handler A pointer to a FastDelegate containing the hook handler
* @param post Set to true if you want a post handler * @param post Set to true if you want a post handler
*/ */
bool RemoveHook(Plugin plug, void *iface, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post); 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 * @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 * @param iface The interface pointer
*/ */
void *GetCallClass(void *iface, size_t size); GenericCallClass *GetCallClass(void *iface);
/** /**
* @brief Release a callclass * @brief Release a callclass
* *
* @param ptr Pointer to the 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 void SetRes(META_RES res); //!< Sets the meta result
virtual META_RES GetPrevRes(); //!< Gets the meta result of the previously called handler 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)); 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[]) int main(int argc, char *argv[])
{ {
// Get an instance and call the functions using the pointer // Get an instance and call the functions using the pointer
// (otherwise the compiler optimizes away the vtable lookup) // (otherwise the compiler optimizes away the vtable lookup)
Test zLOL; Test zLOL;
Test zLOL2;
Test *zLOL_Ptr = &zLOL; Test *zLOL_Ptr = &zLOL;
Test *zLOL2_Ptr = &zLOL2;
g_SHPtr = &g_SHImpl; 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"); printf("TEST1.1: Calling F299\n");
zLOL_Ptr->F299(); zLOL_Ptr->F299();
printf("TEST1.2: Calling F299 through cc\n"); printf("TEST1.2: Calling F299 through cc\n");
cc->F299(); SH_CALL(cc, &Test::F299)();
printf("TEST1.3: Hooking it\n"); printf("TEST1.3: Hooking it\n");
SH_ADD_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler, false); SH_ADD_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler, false);
zLOL2_Ptr->F299();
printf("TEST1.4: Calling F299\n"); printf("TEST1.4: Calling F299\n");
zLOL_Ptr->F299(); zLOL_Ptr->F299();
printf("TEST1.5: Calling F299 through cc\n"); 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"); 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_pre_handler2, false);
SH_ADD_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_post_handler, true); SH_ADD_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_post_handler, true);
printf("TEST1.7: Calling F299\n"); printf("TEST1.7: Calling F299\n");
zLOL_Ptr->F299(); zLOL_Ptr->F299();
printf("TEST1.8: Calling F299 through cc\n"); printf("TEST1.8: Calling F299 through cc\n");
cc->F299(); SH_CALL(cc, &Test::F299)();
printf("TEST1.9: Removing pre hooks\n"); 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_handler, false);
SH_REMOVE_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler2, false); SH_REMOVE_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_pre_handler2, false);
printf("TEST1.10: Calling F299\n"); printf("TEST1.10: Calling F299\n");
zLOL_Ptr->F299(); zLOL_Ptr->F299();
printf("TEST1.11: Calling F299 through cc\n"); printf("TEST1.11: Calling F299 through cc\n");
cc->F299(); SH_CALL(cc, &Test::F299)();
printf("TEST1.12: Removing post hook\n"); printf("TEST1.12: Removing post hook\n");
SH_REMOVE_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_post_handler, true); SH_REMOVE_HOOK_STATICFUNC(Test, F299, zLOL_Ptr, Test_F299_post_handler, true);
printf("TEST1.13: Calling F299\n"); printf("TEST1.13: Calling F299\n");
zLOL_Ptr->F299(); zLOL_Ptr->F299();
printf("TEST1.14: Calling F299 through cc\n"); printf("TEST1.14: Calling F299 through cc\n");
cc->F299(); SH_CALL(cc, &Test::F299)();
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
printf("\n\n*************************************************\n\n"); printf("\n\n*************************************************\n\n");
printf("TEST2.1: Calling F16(155)\n"); printf("TEST2.1: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0); printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.2: Calling F16(155) through CC\n"); 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"); printf("TEST2.3: Hooking it\n");
SH_ADD_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_pre_handler, false); SH_ADD_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_pre_handler, false);
printf("TEST2.4: Calling F16(155)\n"); printf("TEST2.4: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0); printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.5: Calling F16(155) through CC\n"); 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"); printf("TEST2.6: Adding post hook\n");
SH_ADD_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_post_handler, true); SH_ADD_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_post_handler, true);
printf("TEST2.7: Calling F16(155)\n"); printf("TEST2.7: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0); printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.8: Calling F16(155) through CC\n"); printf("TEST2.8: 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");
printf("TEST2.XX.1: Pausing the plugin\n"); printf("TEST2.XX.1: Pausing the plugin\n");
g_SHImpl.PausePlugin(g_PLID); g_SHImpl.PausePlugin(g_PLID);
printf("TEST2.XX.2: Calling F16(155)\n"); printf("TEST2.XX.2: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0); printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.XX.3: Calling F16(155) through CC\n"); 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"); printf("TEST2.XX.4: Unpausing the plugin\n");
g_SHImpl.UnpausePlugin(g_PLID); g_SHImpl.UnpausePlugin(g_PLID);
printf("TEST2.XX.5: Calling F16(155)\n"); printf("TEST2.XX.5: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0); printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.XX.6: Calling F16(155) through CC\n"); 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"); printf("TEST2.9: Removing pre hook\n");
SH_REMOVE_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_pre_handler, false); SH_REMOVE_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_pre_handler, false);
printf("TEST2.10: Calling F16(155)\n"); printf("TEST2.10: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0); printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.11: Calling F16(155) through CC\n"); 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"); printf("TEST2.12: Removing post hook\n");
SH_REMOVE_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_post_handler, true); SH_REMOVE_HOOK_STATICFUNC(Test, F16, zLOL_Ptr, Test_F16_int_post_handler, true);
printf("TEST2.10: Calling F16(155)\n"); printf("TEST2.10: Calling F16(155)\n");
printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0); printf("Returned: %d\n", zLOL_Ptr->F16(155) ? 1 : 0);
printf("TEST2.11: Calling F16(155) through CC\n"); 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); SH_RELEASE_CALLCLASS(cc);