mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2024-11-29 11:24:19 +01:00
9eb35c8830
--HG-- extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%40163
393 lines
10 KiB
C++
393 lines
10 KiB
C++
/* ======== SourceHook ========
|
|
* Copyright (C) 2004-2005 Metamod:Source Development Team
|
|
* No warranties of any kind
|
|
*
|
|
* License: zlib/libpng
|
|
*
|
|
* Author(s): Pavol "PM OnoTo" Marko
|
|
* ============================
|
|
*/
|
|
|
|
#ifndef __SOURCEHOOK_IMPL_H__
|
|
#define __SOURCEHOOK_IMPL_H__
|
|
|
|
#include "sourcehook.h"
|
|
#include "sh_list.h"
|
|
#include "sh_vector.h"
|
|
#include "sh_tinyhash.h"
|
|
#include "sh_stack.h"
|
|
|
|
namespace SourceHook
|
|
{
|
|
/**
|
|
* @brief The SourceHook implementation class
|
|
*/
|
|
class CSourceHookImpl : public ISourceHook
|
|
{
|
|
private:
|
|
/**
|
|
* @brief A hook can be removed if you have this information
|
|
*/
|
|
struct RemoveHookInfo
|
|
{
|
|
RemoveHookInfo(Plugin pplug, void *piface, int tpo, HookManagerPubFunc phookman,
|
|
ISHDelegate *phandler, bool ppost)
|
|
: plug(pplug), iface(piface), thisptr_offs(tpo),
|
|
hookman(phookman), handler(phandler), post(ppost)
|
|
{
|
|
}
|
|
|
|
Plugin plug;
|
|
void *iface;
|
|
int thisptr_offs;
|
|
HookManagerPubFunc hookman;
|
|
ISHDelegate *handler;
|
|
bool post;
|
|
};
|
|
|
|
struct HookInfo
|
|
{
|
|
ISHDelegate *handler; //!< Pointer to the handler
|
|
bool paused; //!< If true, the hook should not be executed
|
|
Plugin plug; //!< The owner plugin
|
|
int thisptr_offs; //!< This pointer offset
|
|
};
|
|
|
|
class CHookList : public IHookList
|
|
{
|
|
public:
|
|
List<HookInfo> m_List;
|
|
|
|
friend class CIter;
|
|
|
|
class CIter : public IHookList::IIter
|
|
{
|
|
friend class CHookList;
|
|
|
|
CHookList *m_pList;
|
|
|
|
void SkipPaused();
|
|
public:
|
|
|
|
List<HookInfo>::iterator m_Iter;
|
|
|
|
CIter(CHookList *pList);
|
|
|
|
virtual ~CIter();
|
|
|
|
void GoToBegin();
|
|
void Set(CIter *pOther);
|
|
|
|
bool End();
|
|
void Next();
|
|
ISHDelegate *Handler();
|
|
int ThisPtrOffs();
|
|
|
|
void Clear();
|
|
|
|
CIter *m_pNext; // When stored in m_FreeIters and m_UsedIters
|
|
CIter *m_pPrev; // Only used when stored in m_UsedIters
|
|
};
|
|
|
|
CIter *m_FreeIters;
|
|
CIter *m_UsedIters; // The last returned and not-yet-released iter is always m_UsedIters
|
|
|
|
// For recalls
|
|
bool m_Recall;
|
|
|
|
void SetRecallState(); // Sets the list into a state where the next returned
|
|
// iterator (from GetIter) will be a copy of the last
|
|
// returned iterator, incremented by one. This is used in Recalls.
|
|
// The hook resets this state automatically on:
|
|
// GetIter, ReleaseIter
|
|
|
|
CHookList();
|
|
CHookList(const CHookList &other);
|
|
virtual ~CHookList();
|
|
|
|
void operator=(const CHookList &other);
|
|
|
|
IIter *GetIter();
|
|
void ReleaseIter(IIter *pIter);
|
|
};
|
|
|
|
// I know, data hiding... But I'm a lazy bastard!
|
|
|
|
class CIface : public IIface
|
|
{
|
|
public:
|
|
void *m_Ptr;
|
|
CHookList m_PreHooks;
|
|
CHookList m_PostHooks;
|
|
public:
|
|
CIface(void *ptr);
|
|
virtual ~CIface();
|
|
|
|
void *GetPtr();
|
|
IHookList *GetPreHooks();
|
|
IHookList *GetPostHooks();
|
|
|
|
bool operator==(void *ptr)
|
|
{
|
|
return m_Ptr == ptr;
|
|
}
|
|
};
|
|
|
|
class CVfnPtr : public IVfnPtr
|
|
{
|
|
public:
|
|
typedef List<CIface> IfaceList;
|
|
typedef IfaceList::iterator IfaceListIter;
|
|
|
|
void *m_Ptr;
|
|
void *m_OrigEntry;
|
|
|
|
IfaceList m_Ifaces;
|
|
|
|
public:
|
|
CVfnPtr(void *ptr);
|
|
virtual ~CVfnPtr();
|
|
|
|
void *GetVfnPtr();
|
|
void *GetOrigEntry();
|
|
|
|
virtual IIface *FindIface(void *ptr);
|
|
|
|
bool operator==(void *ptr)
|
|
{
|
|
return m_Ptr == ptr;
|
|
}
|
|
};
|
|
|
|
class CHookManagerInfo : public IHookManagerInfo
|
|
{
|
|
public:
|
|
typedef List<CVfnPtr> VfnPtrList;
|
|
typedef VfnPtrList::iterator VfnPtrListIter;
|
|
|
|
Plugin m_Plug;
|
|
HookManagerPubFunc m_Func;
|
|
|
|
int m_VtblOffs;
|
|
int m_VtblIdx;
|
|
const char *m_Proto;
|
|
void *m_HookfuncVfnptr;
|
|
|
|
VfnPtrList m_VfnPtrs;
|
|
|
|
int m_HookFuncVersion;
|
|
|
|
public:
|
|
virtual ~CHookManagerInfo();
|
|
|
|
IVfnPtr *FindVfnPtr(void *vfnptr);
|
|
|
|
void SetInfo(int vtbl_offs, int vtbl_idx, const char *proto);
|
|
void SetHookfuncVfnptr(void *hookfunc_vfnptr);
|
|
};
|
|
/**
|
|
* @brief A list of CHookManagerInfo classes
|
|
*/
|
|
typedef List<CHookManagerInfo> HookManInfoList;
|
|
|
|
class CCallClassImpl : public GenericCallClass
|
|
{
|
|
public:
|
|
|
|
typedef SourceHook::CVector<void*> OrigFuncs;
|
|
typedef SourceHook::THash<int, OrigFuncs> OrigVTables;
|
|
|
|
void *m_Ptr; //!< Pointer to the actual object
|
|
size_t m_ObjSize; //!< Size of the instance
|
|
OrigVTables m_VT; //!< Info about vtables & functions
|
|
|
|
int m_RefCounter;
|
|
|
|
CCallClassImpl(void *ptr, size_t size);
|
|
virtual ~CCallClassImpl();
|
|
|
|
bool operator==(void *other)
|
|
{
|
|
return m_Ptr == other;
|
|
}
|
|
|
|
void *GetThisPtr();
|
|
void *GetOrigFunc(int vtbloffs, int vtblidx);
|
|
|
|
void ApplyCallClassPatch(int vtbl_offs, int vtbl_idx, void *orig_entry);
|
|
void RemoveCallClassPatch(int vtbl_offs, int vtbl_idx);
|
|
};
|
|
|
|
/**
|
|
* @brief A list of CallClass structures
|
|
*/
|
|
typedef List<CCallClassImpl> Impl_CallClassList;
|
|
|
|
Impl_CallClassList m_CallClasses; //!< A list of already generated callclasses
|
|
HookManInfoList m_HookMans; //!< A list of hook managers
|
|
|
|
struct HookLoopInfo
|
|
{
|
|
META_RES *pStatus;
|
|
META_RES *pPrevRes;
|
|
META_RES *pCurRes;
|
|
|
|
bool shouldContinue;
|
|
bool recall; //!< True if we're in a recall, eh.
|
|
|
|
IIface *pCurIface;
|
|
const void *pOrigRet;
|
|
void *pOverrideRet;
|
|
void **pIfacePtrPtr;
|
|
};
|
|
typedef CStack<HookLoopInfo> HookLoopInfoStack;
|
|
|
|
/**
|
|
* @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);
|
|
|
|
void ApplyCallClassPatches(CCallClassImpl &cc);
|
|
void ApplyCallClassPatches(void *ifaceptr, int vtbl_offs, int vtbl_idx, void *orig_entry);
|
|
void RemoveCallClassPatches(void *ifaceptr, int vtbl_offs, int vtbl_idx);
|
|
|
|
void SetPluginPaused(Plugin plug, bool paused);
|
|
|
|
bool ProtosEquiv(const char *p1, const char *p2);
|
|
|
|
HookLoopInfoStack m_HLIStack;
|
|
public:
|
|
CSourceHookImpl();
|
|
virtual ~CSourceHookImpl();
|
|
|
|
/**
|
|
* @brief Returns the interface version
|
|
*/
|
|
int GetIfaceVersion();
|
|
|
|
/**
|
|
* @brief Returns the implemnetation version
|
|
*/
|
|
int GetImplVersion();
|
|
|
|
/**
|
|
* @brief Make sure that a plugin is not used by any other plugins anymore, and unregister all its hook managers
|
|
*/
|
|
void UnloadPlugin(Plugin plug);
|
|
|
|
/**
|
|
* @brief Shut down the whole system, unregister all hook managers
|
|
*/
|
|
void CompleteShutdown();
|
|
|
|
/**
|
|
* @brief Add a hook.
|
|
*
|
|
* @return True if the function succeeded, false otherwise
|
|
*
|
|
* @param plug The unique identifier of the plugin that calls this function
|
|
* @param iface The interface pointer
|
|
* @param ifacesize The size of the class iface points to
|
|
* @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 AddHook(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 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 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
|
|
*
|
|
* @param plug The unique identifier of the plugin in question
|
|
*/
|
|
bool IsPluginInUse(Plugin plug);
|
|
|
|
/**
|
|
* @brief Pauses all hooks of a plugin
|
|
*
|
|
* @param plug The unique identifier of the plugin
|
|
*/
|
|
void PausePlugin(Plugin plug);
|
|
|
|
/**
|
|
* @brief Unpauses all hooks of a plugin
|
|
*
|
|
* @param plug The unique identifier of the plugin
|
|
*/
|
|
void UnpausePlugin(Plugin plug);
|
|
|
|
/**
|
|
* @brief Return a pointer to a callclass. Generate a new one if required.
|
|
*
|
|
* @param iface The interface pointer
|
|
* @param size Size of the class instance
|
|
*/
|
|
GenericCallClass *GetCallClass(void *iface, size_t size);
|
|
|
|
/**
|
|
* @brief Release a callclass
|
|
*
|
|
* @param ptr Pointer to the callclass
|
|
*/
|
|
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
|
|
virtual META_RES GetStatus(); //!< Gets the highest meta result
|
|
virtual const void *GetOrigRet(); //!< Gets the original result. If not in post function, undefined
|
|
virtual const void *GetOverrideRet(); //!< Gets the override result. If none is specified, NULL
|
|
virtual void *GetIfacePtr(); //!< Gets the interface pointer
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// For hook managers
|
|
void HookLoopBegin(IIface *pIface); //!< Should be called when a hook loop begins
|
|
void HookLoopEnd(); //!< Should be called when a hook loop exits
|
|
void SetCurResPtr(META_RES *mres); //!< Sets pointer to the current meta result
|
|
void SetPrevResPtr(META_RES *mres); //!< Sets pointer to previous meta result
|
|
void SetStatusPtr(META_RES *mres); //!< Sets pointer to the status variable
|
|
void SetIfacePtrPtr(void **pp); //!< Sets pointer to the interface this pointer
|
|
void SetOrigRetPtr(const void *ptr); //!< Sets the original return pointer
|
|
void SetOverrideRetPtr(void *ptr); //!< Sets the override result pointer
|
|
bool ShouldContinue(); //!< Returns false if the hook loop should exit
|
|
|
|
/**
|
|
* @brief Remove a hook manager. Auto-removes all hooks attached to it from plugin plug.
|
|
*
|
|
* @param plug The owner of the hook manager
|
|
* @param pubFunc The hook manager's info function
|
|
*/
|
|
virtual void RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc);
|
|
virtual void DoRecall(); //!< Initiates a recall sequence
|
|
virtual void *GetOverrideRetPtr(); //!< Returns the pointer set by SetOverrideRetPtr
|
|
|
|
virtual void *SetupHookLoop(META_RES *statusPtr, META_RES *prevResPtr, META_RES *curResPtr,
|
|
void **ifacePtrPtr, const void *origRetPtr, void *overrideRetPtr);
|
|
};
|
|
}
|
|
|
|
#endif
|
|
|