mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2025-12-07 10:28:30 +00:00
Completly redone templates, added "vfnptrs"
--HG-- extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%4042
This commit is contained in:
parent
9bb40ae639
commit
bb2cb311ad
@ -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. -> :(
|
||||
@ -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
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user