2005-04-14 14:54:13 +02:00
|
|
|
/* ======== SourceHook ========
|
2005-04-17 01:33:57 +02:00
|
|
|
* Copyright (C) 2004-2005 Metamod:Source Development Team
|
2005-04-14 14:54:13 +02:00
|
|
|
* No warranties of any kind
|
2005-04-17 01:33:57 +02:00
|
|
|
*
|
|
|
|
* License: zlib/libpng
|
|
|
|
*
|
|
|
|
* Author(s): Pavol "PM OnoTo" Marko
|
2005-07-07 20:21:32 +02:00
|
|
|
* Contributors: Scott "Damaged Soul" Ehlert
|
2005-04-14 14:54:13 +02:00
|
|
|
* ============================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2005-04-17 01:33:57 +02:00
|
|
|
* @file sourcehook.cpp
|
|
|
|
* @brief Contains the implementation of the SourceHook API
|
2005-04-14 14:54:13 +02:00
|
|
|
*/
|
|
|
|
|
2005-09-13 22:24:58 +02:00
|
|
|
#if defined __GNUC__
|
2005-09-13 22:21:58 +02:00
|
|
|
#include <stdint.h>
|
2005-09-13 22:24:58 +02:00
|
|
|
#endif
|
2005-10-03 19:20:22 +02:00
|
|
|
|
2005-04-14 14:54:13 +02:00
|
|
|
#include "sourcehook_impl.h"
|
2005-10-03 19:20:22 +02:00
|
|
|
#include "sh_memory.h"
|
2005-04-14 14:54:13 +02:00
|
|
|
|
|
|
|
namespace SourceHook
|
|
|
|
{
|
2005-09-13 20:20:37 +02:00
|
|
|
template<>
|
2005-09-13 22:21:58 +02:00
|
|
|
int SourceHook::HashFunction<int>(const int & k)
|
2005-09-13 20:20:37 +02:00
|
|
|
{
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
template<>
|
2005-09-13 22:21:58 +02:00
|
|
|
int SourceHook::Compare<int>(const int & k1, const int & k2)
|
2005-09-13 20:20:37 +02:00
|
|
|
{
|
|
|
|
if (k1 == k2)
|
|
|
|
return 0;
|
|
|
|
if (k1 > k2)
|
|
|
|
return 1;
|
|
|
|
if (k1 < k2)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-14 14:54:13 +02:00
|
|
|
CSourceHookImpl::CSourceHookImpl()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
CSourceHookImpl::~CSourceHookImpl()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-05-05 12:40:47 +02:00
|
|
|
int CSourceHookImpl::GetIfaceVersion()
|
|
|
|
{
|
|
|
|
return SH_IFACE_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CSourceHookImpl::GetImplVersion()
|
|
|
|
{
|
|
|
|
return SH_IMPL_VERSION;
|
|
|
|
}
|
|
|
|
|
2005-04-14 14:54:13 +02:00
|
|
|
bool CSourceHookImpl::IsPluginInUse(Plugin plug)
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// Iterate through all hook managers which are in this plugin
|
2005-04-29 22:29:46 +02:00
|
|
|
// Iterate through their vfnptrs, ifaces, hooks
|
2005-04-14 14:54:13 +02:00
|
|
|
// If a hook from an other plugin is found, return true
|
|
|
|
// Return false otherwise
|
2005-04-16 22:26:33 +02:00
|
|
|
#define TMP_CHECK_LIST(name) \
|
2005-10-03 19:20:22 +02:00
|
|
|
for (hook_iter = iface_iter->name.m_List.begin(); hook_iter != iface_iter->name.m_List.end(); ++hook_iter) \
|
2005-04-29 22:29:46 +02:00
|
|
|
if (hook_iter->plug == plug) \
|
2005-04-16 22:26:33 +02:00
|
|
|
return true;
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end(); ++hmil_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
if (hmil_iter->m_Plug != plug)
|
2005-04-29 22:29:46 +02:00
|
|
|
continue;
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != hmil_iter->m_VfnPtrs.end(); ++vfnptr_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->m_Ifaces.end(); ++iface_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
List<HookInfo>::iterator hook_iter;
|
|
|
|
TMP_CHECK_LIST(m_PreHooks);
|
|
|
|
TMP_CHECK_LIST(m_PostHooks);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-04-16 22:26:33 +02:00
|
|
|
#undef TMP_CHECK_LIST
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::UnloadPlugin(Plugin plug)
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// 1) Manually remove all hooks by this plugin
|
2005-08-12 19:33:31 +02:00
|
|
|
List<RemoveHookInfo> hookstoremove;
|
2005-07-02 12:09:40 +02:00
|
|
|
HookManInfoList::iterator hmil_iter;
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
#define TMP_CHECK_LIST(name, ispost) \
|
2005-10-03 19:20:22 +02:00
|
|
|
for (hook_iter = iface_iter->name.m_List.begin(); hook_iter != iface_iter->name.m_List.end(); ++hook_iter) \
|
2005-04-29 22:29:46 +02:00
|
|
|
if (hook_iter->plug == plug) \
|
2005-10-03 19:20:22 +02:00
|
|
|
hookstoremove.push_back(RemoveHookInfo(hook_iter->plug, iface_iter->m_Ptr, \
|
|
|
|
hook_iter->thisptr_offs, hmil_iter->m_Func, hook_iter->handler, ispost))
|
|
|
|
|
2005-07-02 12:09:40 +02:00
|
|
|
for (hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end(); ++hmil_iter)
|
2005-04-16 18:55:53 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != hmil_iter->m_VfnPtrs.end(); ++vfnptr_iter)
|
2005-04-16 18:55:53 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->m_Ifaces.end(); ++iface_iter)
|
2005-04-29 22:29:46 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
List<HookInfo>::iterator hook_iter;
|
|
|
|
TMP_CHECK_LIST(m_PreHooks, false);
|
|
|
|
TMP_CHECK_LIST(m_PostHooks, true);
|
2005-04-29 22:29:46 +02:00
|
|
|
}
|
2005-04-16 18:55:53 +02:00
|
|
|
}
|
|
|
|
}
|
2005-04-29 22:29:46 +02:00
|
|
|
#undef TMP_CHECK_LIST
|
2005-04-16 18:55:53 +02:00
|
|
|
|
2005-08-12 19:33:31 +02:00
|
|
|
for (List<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
|
2005-04-29 22:29:46 +02:00
|
|
|
RemoveHook(*rmiter);
|
2005-04-16 18:55:53 +02:00
|
|
|
|
|
|
|
// 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
|
|
|
|
// Delete all hook managers that are in this plugin
|
|
|
|
|
|
|
|
HookManInfoList tmphookmans;
|
2005-04-14 14:54:13 +02:00
|
|
|
bool erase = false;
|
2005-07-02 12:09:40 +02:00
|
|
|
for (hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end();
|
2005-04-29 22:29:46 +02:00
|
|
|
erase ? hmil_iter=m_HookMans.erase(hmil_iter) : ++hmil_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
if (hmil_iter->m_Plug == plug)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
if (!hmil_iter->m_VfnPtrs.empty())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// All hooks by this plugin are already removed
|
2005-04-29 22:29:46 +02:00
|
|
|
// So if there is a vfnptr, it has to be used by an other plugin
|
|
|
|
tmphookmans.push_back(*hmil_iter);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
erase = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
erase = false;
|
|
|
|
}
|
2005-10-03 19:20:22 +02:00
|
|
|
|
|
|
|
// For each hook manager that is used in an other plugin:
|
2005-07-02 12:09:40 +02:00
|
|
|
for (hmil_iter = tmphookmans.begin(); hmil_iter != tmphookmans.end(); ++hmil_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// Find a suitable hook manager in an other plugin
|
|
|
|
HookManInfoList::iterator newHookMan = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
|
2005-10-03 19:20:22 +02:00
|
|
|
hmil_iter->m_Proto, hmil_iter->m_VtblOffs, hmil_iter->m_VtblIdx);
|
2005-04-29 22:29:46 +02:00
|
|
|
|
|
|
|
// 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(),
|
2005-10-03 19:20:22 +02:00
|
|
|
("Could not find a suitable hook manager in an other plugin!"));
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
// AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo
|
2005-10-03 19:20:22 +02:00
|
|
|
SH_ASSERT(newHookMan->m_Plug != plug, ("New hook manager from same plugin!"));
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
// The first hook manager should be always used - so the new hook manager has to be empty
|
2005-10-03 19:20:22 +02:00
|
|
|
SH_ASSERT(newHookMan->m_VfnPtrs.empty(), ("New hook manager not empty!"));
|
2005-04-16 18:55:53 +02:00
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
// Move the vfnptrs from the old hook manager to the new one
|
2005-10-03 19:20:22 +02:00
|
|
|
newHookMan->m_VfnPtrs = hmil_iter->m_VfnPtrs;
|
2005-04-16 18:55:53 +02:00
|
|
|
|
|
|
|
// Unregister the old one, register the new one
|
2005-10-03 19:20:22 +02:00
|
|
|
hmil_iter->m_Func(HA_Unregister, NULL);
|
|
|
|
newHookMan->m_Func(HA_Register, &(*newHookMan));
|
2005-07-02 12:09:40 +02:00
|
|
|
|
|
|
|
// zOMG BAIL, here is part of what you wanted:
|
|
|
|
|
|
|
|
// Go through all vfnptrs in this hookman and patch them to point to the new manager's handler!
|
|
|
|
// or whatever
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = newHookMan->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != newHookMan->m_VfnPtrs.end(); ++vfnptr_iter)
|
2005-07-02 12:09:40 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
// And DEREFERENCE newHookMan->m_HookfuncVfnptr!
|
2005-07-02 12:09:40 +02:00
|
|
|
// otherwise it will be executing the vtable... had to find out the hard way
|
2005-10-03 19:20:22 +02:00
|
|
|
*reinterpret_cast<void**>(vfnptr_iter->m_Ptr) = *reinterpret_cast<void**>(newHookMan->m_HookfuncVfnptr);
|
2005-07-02 12:09:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// That should fix it, bail!
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-10 23:37:53 +01:00
|
|
|
void CSourceHookImpl::RemoveHookManager(Plugin plug, HookManagerPubFunc pubFunc)
|
|
|
|
{
|
|
|
|
// Moo!
|
|
|
|
|
|
|
|
CHookManagerInfo tmp;
|
|
|
|
if (pubFunc(HA_GetInfo, &tmp) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
HookManInfoList::iterator hmil_iter = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
|
|
|
|
tmp.m_Proto, tmp.m_VtblOffs, tmp.m_VtblIdx);
|
|
|
|
|
|
|
|
if (hmil_iter == m_HookMans.end())
|
|
|
|
{
|
|
|
|
// Moo ?
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool stillInUse = false;
|
|
|
|
List<RemoveHookInfo> hookstoremove;
|
|
|
|
|
|
|
|
// Iterate through all of the hook manager's hooks. Remove all hooks from the hookman's plugin.
|
|
|
|
#define TMP_CHECK_LIST(name, ispost) \
|
|
|
|
for (hook_iter = iface_iter->name.m_List.begin(); hook_iter != iface_iter->name.m_List.end(); ++hook_iter) \
|
|
|
|
if (hook_iter->plug == plug) \
|
|
|
|
hookstoremove.push_back(RemoveHookInfo(hook_iter->plug, iface_iter->m_Ptr, \
|
|
|
|
hook_iter->thisptr_offs, hmil_iter->m_Func, hook_iter->handler, ispost)); \
|
|
|
|
else \
|
|
|
|
stillInUse = true; \
|
|
|
|
|
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != hmil_iter->m_VfnPtrs.end(); ++vfnptr_iter)
|
|
|
|
{
|
|
|
|
for (CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->m_Ifaces.end(); ++iface_iter)
|
|
|
|
{
|
|
|
|
List<HookInfo>::iterator hook_iter;
|
|
|
|
TMP_CHECK_LIST(m_PreHooks, false);
|
|
|
|
TMP_CHECK_LIST(m_PostHooks, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#undef TMP_CHECK_LIST
|
|
|
|
|
|
|
|
for (List<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
|
|
|
|
RemoveHook(*rmiter);
|
|
|
|
|
|
|
|
CHookManagerInfo info = *hmil_iter;
|
|
|
|
|
|
|
|
// Unlink the hook manager from the list
|
|
|
|
m_HookMans.erase(hmil_iter);
|
|
|
|
|
|
|
|
// If there were any hooks from other plugins, find a new suitable hook manager.
|
|
|
|
if (stillInUse)
|
|
|
|
{
|
|
|
|
// Find a suitable hook manager in an other plugin
|
|
|
|
HookManInfoList::iterator newHookMan = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
|
|
|
|
info.m_Proto, info.m_VtblOffs, info.m_VtblIdx);
|
|
|
|
|
|
|
|
// 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!"));
|
|
|
|
|
|
|
|
// AddHook should make sure that every plugin only has _one_ hook manager for _one_ proto/vi/vo
|
|
|
|
SH_ASSERT(newHookMan->m_Plug != plug, ("New hook manager from same plugin!"));
|
|
|
|
|
|
|
|
// The first hook manager should be always used - so the new hook manager has to be empty
|
|
|
|
SH_ASSERT(newHookMan->m_VfnPtrs.empty(), ("New hook manager not empty!"));
|
|
|
|
|
|
|
|
// Move the vfnptrs from the old hook manager to the new one
|
|
|
|
newHookMan->m_VfnPtrs = info.m_VfnPtrs;
|
|
|
|
|
|
|
|
// Unregister the old one, register the new one
|
|
|
|
info.m_Func(HA_Unregister, NULL);
|
|
|
|
newHookMan->m_Func(HA_Register, &(*newHookMan));
|
|
|
|
|
|
|
|
// Go through all vfnptrs in this hookman and patch them to point to the new manager's handler!
|
|
|
|
// or whatever
|
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = newHookMan->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != newHookMan->m_VfnPtrs.end(); ++vfnptr_iter)
|
|
|
|
{
|
|
|
|
// And DEREFERENCE newHookMan->m_HookfuncVfnptr!
|
|
|
|
// otherwise it will be executing the vtable... had to find out the hard way
|
|
|
|
*reinterpret_cast<void**>(vfnptr_iter->m_Ptr) = *reinterpret_cast<void**>(newHookMan->m_HookfuncVfnptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-14 14:54:13 +02:00
|
|
|
void CSourceHookImpl::CompleteShutdown()
|
|
|
|
{
|
2005-08-12 19:33:31 +02:00
|
|
|
List<RemoveHookInfo> hookstoremove;
|
2005-04-29 22:29:46 +02:00
|
|
|
#define TMP_CHECK_LIST(name, ispost) \
|
2005-10-03 19:20:22 +02:00
|
|
|
for (hook_iter = iface_iter->name.m_List.begin(); hook_iter != iface_iter->name.m_List.end(); ++hook_iter) \
|
|
|
|
hookstoremove.push_back(RemoveHookInfo(hook_iter->plug, iface_iter->m_Ptr, \
|
|
|
|
hook_iter->thisptr_offs, hmil_iter->m_Func, hook_iter->handler, ispost))
|
2005-04-29 22:29:46 +02:00
|
|
|
for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end(); ++hmil_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != hmil_iter->m_VfnPtrs.end(); ++vfnptr_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->m_Ifaces.end(); ++iface_iter)
|
2005-04-29 22:29:46 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
List<HookInfo>::iterator hook_iter;
|
|
|
|
TMP_CHECK_LIST(m_PreHooks, false);
|
|
|
|
TMP_CHECK_LIST(m_PostHooks, true);
|
2005-04-29 22:29:46 +02:00
|
|
|
}
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
2005-04-29 22:29:46 +02:00
|
|
|
#undef TMP_CHECK_LIST
|
2005-04-16 18:55:53 +02:00
|
|
|
|
2005-08-12 19:33:31 +02:00
|
|
|
for (List<RemoveHookInfo>::iterator rmiter = hookstoremove.begin(); rmiter != hookstoremove.end(); ++rmiter)
|
2005-04-29 22:29:46 +02:00
|
|
|
RemoveHook(*rmiter);
|
2005-04-16 18:55:53 +02:00
|
|
|
|
|
|
|
m_HookMans.clear();
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
bool CSourceHookImpl::AddHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
void *adjustediface = reinterpret_cast<void*>(reinterpret_cast<char*>(iface) + thisptr_offs);
|
2005-12-10 23:37:53 +01:00
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// 1) Get info about the hook manager
|
2005-10-03 19:20:22 +02:00
|
|
|
CHookManagerInfo tmp;
|
2005-04-16 18:55:53 +02:00
|
|
|
if (myHookMan(HA_GetInfo, &tmp) != 0)
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-12-10 23:37:53 +01:00
|
|
|
void **cur_vtptr = *reinterpret_cast<void***>(
|
|
|
|
reinterpret_cast<char*>(adjustediface) + tmp.m_VtblOffs);
|
|
|
|
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.m_VtblIdx);
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
// 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
|
2005-04-29 22:29:46 +02:00
|
|
|
HookManInfoList::iterator hkmi_iter;
|
|
|
|
for (hkmi_iter = m_HookMans.begin(); hkmi_iter != m_HookMans.end(); ++hkmi_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-12-10 23:37:53 +01:00
|
|
|
if (hkmi_iter->m_Plug == plug && ProtosEquiv(hkmi_iter->m_Proto, tmp.m_Proto) &&
|
2005-10-03 19:20:22 +02:00
|
|
|
hkmi_iter->m_VtblOffs == tmp.m_VtblOffs && hkmi_iter->m_VtblIdx == tmp.m_VtblIdx)
|
2005-04-14 14:54:13 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-04-29 22:29:46 +02:00
|
|
|
if (hkmi_iter == m_HookMans.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
// No such hook manager from this plugin yet, add it!
|
2005-10-03 19:20:22 +02:00
|
|
|
tmp.m_Func = myHookMan;
|
|
|
|
tmp.m_Plug = plug;
|
2005-04-16 18:55:53 +02:00
|
|
|
m_HookMans.push_back(tmp);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// Then, search for a suitable hook manager (from the beginning)
|
2005-10-03 19:20:22 +02:00
|
|
|
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(), tmp.m_Proto,
|
|
|
|
tmp.m_VtblOffs, tmp.m_VtblIdx);
|
|
|
|
SH_ASSERT(hookman != m_HookMans.end(), ("No hookman found - but if there was none, we've just added one!"));
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-12-10 23:37:53 +01:00
|
|
|
// Check whether there is already an other hook manager for the same vfnptr but with other values before
|
|
|
|
// If yes, it means that the two are incompatible, so return false.
|
|
|
|
for (hkmi_iter = m_HookMans.begin(); hkmi_iter != m_HookMans.end(); ++hkmi_iter)
|
|
|
|
{
|
|
|
|
if (hkmi_iter != hookman)
|
|
|
|
{
|
|
|
|
if (hkmi_iter->m_VfnPtrs.find(cur_vfnptr) != hkmi_iter->m_VfnPtrs.end())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-14 14:54:13 +02:00
|
|
|
// Tell it to store the pointer if it's not already active
|
2005-10-03 19:20:22 +02:00
|
|
|
if (hookman->m_VfnPtrs.empty())
|
|
|
|
hookman->m_Func(HA_Register, &(*hookman));
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
CHookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->m_VfnPtrs.find(cur_vfnptr);
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
if (vfnptr_iter == hookman->m_VfnPtrs.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
// Add a new one
|
2005-10-03 19:20:22 +02:00
|
|
|
CVfnPtr vfp(cur_vfnptr);
|
2005-04-29 22:29:46 +02:00
|
|
|
|
|
|
|
// Alter vtable entry
|
2005-10-03 19:20:22 +02:00
|
|
|
if (!SetMemAccess(cur_vtptr, sizeof(void*) * (tmp.m_VtblIdx + 1), SH_MEM_READ | SH_MEM_WRITE))
|
2005-05-01 20:04:18 +02:00
|
|
|
return false;
|
2005-04-29 22:29:46 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
*reinterpret_cast<void**>(cur_vfnptr) = *reinterpret_cast<void**>(hookman->m_HookfuncVfnptr);
|
|
|
|
|
|
|
|
hookman->m_VfnPtrs.push_back(vfp);
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
// Make vfnptr_iter point to the new element
|
2005-10-03 19:20:22 +02:00
|
|
|
vfnptr_iter = hookman->m_VfnPtrs.end();
|
2005-04-29 22:29:46 +02:00
|
|
|
--vfnptr_iter;
|
2005-04-30 17:48:46 +02:00
|
|
|
|
|
|
|
// Now that it is done, check whether we have to update any callclasses
|
2005-10-03 19:20:22 +02:00
|
|
|
ApplyCallClassPatches(adjustediface, tmp.m_VtblOffs, tmp.m_VtblIdx, vfp.m_OrigEntry);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
2005-04-29 22:29:46 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.find(adjustediface);
|
|
|
|
if (iface_iter == vfnptr_iter->m_Ifaces.end())
|
2005-04-29 22:29:46 +02:00
|
|
|
{
|
|
|
|
// Add a new one
|
2005-10-03 19:20:22 +02:00
|
|
|
vfnptr_iter->m_Ifaces.push_back(CIface(adjustediface));
|
2005-04-29 22:29:46 +02:00
|
|
|
|
|
|
|
// Make iface_iter point to the new element
|
2005-10-03 19:20:22 +02:00
|
|
|
iface_iter = vfnptr_iter->m_Ifaces.end();
|
2005-04-29 22:29:46 +02:00
|
|
|
--iface_iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the hook
|
2005-10-03 19:20:22 +02:00
|
|
|
HookInfo hookinfo;
|
2005-04-14 14:54:13 +02:00
|
|
|
hookinfo.handler = handler;
|
|
|
|
hookinfo.plug = plug;
|
2005-04-16 22:26:33 +02:00
|
|
|
hookinfo.paused = false;
|
2005-05-05 12:40:47 +02:00
|
|
|
hookinfo.thisptr_offs = thisptr_offs;
|
2005-04-14 14:54:13 +02:00
|
|
|
if (post)
|
2005-10-03 19:20:22 +02:00
|
|
|
iface_iter->m_PostHooks.m_List.push_back(hookinfo);
|
2005-04-14 14:54:13 +02:00
|
|
|
else
|
2005-10-03 19:20:22 +02:00
|
|
|
iface_iter->m_PreHooks.m_List.push_back(hookinfo);
|
2005-04-14 14:54:13 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
bool CSourceHookImpl::RemoveHook(RemoveHookInfo info)
|
|
|
|
{
|
2005-05-05 12:40:47 +02:00
|
|
|
return RemoveHook(info.plug, info.iface, info.thisptr_offs, info.hookman, info.handler, info.post);
|
2005-04-29 22:29:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CSourceHookImpl::RemoveHook(Plugin plug, void *iface, int thisptr_offs, HookManagerPubFunc myHookMan, ISHDelegate *handler, bool post)
|
|
|
|
{
|
2005-05-05 12:40:47 +02:00
|
|
|
void *adjustediface = reinterpret_cast<void*>(reinterpret_cast<char*>(iface)+thisptr_offs);
|
2005-10-03 19:20:22 +02:00
|
|
|
CHookManagerInfo tmp;
|
2005-04-16 18:55:53 +02:00
|
|
|
if (myHookMan(HA_GetInfo, &tmp) != 0)
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
// Find the hook manager and the hook
|
2005-10-03 19:20:22 +02:00
|
|
|
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
|
|
|
|
tmp.m_Proto, tmp.m_VtblOffs, tmp.m_VtblIdx);
|
2005-04-16 18:55:53 +02:00
|
|
|
if (hookman == m_HookMans.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
if (!ModuleInMemory(reinterpret_cast<char*>(adjustediface) + tmp.m_VtblOffs,
|
|
|
|
sizeof(void*) * (tmp.m_VtblIdx + 1)))
|
2005-07-07 20:21:32 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
// The module the vtable was in is already unloaded.
|
|
|
|
hookman->m_VfnPtrs.clear();
|
|
|
|
hookman->m_Func(HA_Unregister, NULL);
|
2005-07-07 20:21:32 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
void **cur_vtptr = *reinterpret_cast<void***>(
|
2005-10-03 19:20:22 +02:00
|
|
|
reinterpret_cast<char*>(adjustediface) + tmp.m_VtblOffs);
|
|
|
|
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.m_VtblIdx);
|
2005-04-29 22:29:46 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
CHookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->m_VfnPtrs.find(cur_vfnptr);
|
2005-08-12 19:33:31 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
if (vfnptr_iter == hookman->m_VfnPtrs.end())
|
2005-05-01 11:55:51 +02:00
|
|
|
return false;
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->m_Ifaces.end();)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
List<HookInfo> &hooks =
|
|
|
|
post ? iface_iter->m_PostHooks.m_List : iface_iter->m_PreHooks.m_List;
|
2005-05-01 11:55:51 +02:00
|
|
|
|
|
|
|
bool erase;
|
2005-10-03 19:20:22 +02:00
|
|
|
for (List<HookInfo>::iterator hookiter = hooks.begin();
|
2005-10-05 23:51:02 +02:00
|
|
|
hookiter != hooks.end(); )
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-05-05 12:40:47 +02:00
|
|
|
erase = hookiter->plug == plug && hookiter->handler->IsEqual(handler) &&
|
|
|
|
hookiter->thisptr_offs == thisptr_offs;
|
2005-05-01 11:55:51 +02:00
|
|
|
if (erase)
|
2005-10-05 23:51:02 +02:00
|
|
|
{
|
2005-05-01 11:55:51 +02:00
|
|
|
hookiter->handler->DeleteThis(); // Make the _plugin_ delete the handler object
|
2005-10-05 23:51:02 +02:00
|
|
|
|
|
|
|
// Move all iterators pointing at this
|
|
|
|
List<HookInfo>::iterator oldhookiter = hookiter;
|
|
|
|
hookiter = hooks.erase(hookiter);
|
|
|
|
List<HookInfo>::iterator newhookiter = hookiter;
|
|
|
|
--newhookiter; // The hook loop will ++ it then
|
|
|
|
CHookList::CIter *pItIter;
|
|
|
|
for (pItIter = iface_iter->m_PreHooks.m_UsedIters; pItIter; pItIter = pItIter->m_pNext)
|
|
|
|
if (pItIter->m_Iter == oldhookiter)
|
|
|
|
pItIter->m_Iter = newhookiter;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
++hookiter;
|
2005-05-01 11:55:51 +02:00
|
|
|
}
|
2005-10-03 19:20:22 +02:00
|
|
|
if (iface_iter->m_PostHooks.m_List.empty() && iface_iter->m_PreHooks.m_List.empty())
|
2005-05-01 11:55:51 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
// There are no hooks on this iface anymore...
|
2005-10-12 22:25:15 +02:00
|
|
|
for (HookLoopInfoStack::iterator hli_iter = m_HLIStack.begin();
|
|
|
|
hli_iter != m_HLIStack.end(); ++hli_iter)
|
|
|
|
{
|
|
|
|
if (hli_iter->pCurIface == static_cast<IIface*>(&(*iface_iter)))
|
|
|
|
hli_iter->shouldContinue = false;
|
|
|
|
}
|
2005-10-03 19:20:22 +02:00
|
|
|
|
|
|
|
iface_iter = vfnptr_iter->m_Ifaces.erase(iface_iter);
|
|
|
|
if (vfnptr_iter->m_Ifaces.empty())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
// No ifaces at all -> Deactivate the hook
|
|
|
|
*reinterpret_cast<void**>(vfnptr_iter->m_Ptr) = vfnptr_iter->m_OrigEntry;
|
|
|
|
|
|
|
|
hookman->m_VfnPtrs.erase(vfnptr_iter);
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-05-01 11:55:51 +02:00
|
|
|
// Remove callclass patch
|
|
|
|
for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
|
2005-10-03 19:20:22 +02:00
|
|
|
if (cciter->m_Ptr == adjustediface)
|
|
|
|
cciter->RemoveCallClassPatch(tmp.m_VtblOffs, tmp.m_VtblIdx);
|
2005-05-01 11:55:51 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
if (hookman->m_VfnPtrs.empty())
|
2005-04-29 22:29:46 +02:00
|
|
|
{
|
2005-05-01 11:55:51 +02:00
|
|
|
// Unregister the hook manager
|
2005-10-03 19:20:22 +02:00
|
|
|
hookman->m_Func(HA_Unregister, NULL);
|
2005-04-29 22:29:46 +02:00
|
|
|
}
|
2005-05-01 11:55:51 +02:00
|
|
|
|
|
|
|
// Don't try to continue looping through ifaces
|
|
|
|
// - the list is already invalid
|
|
|
|
return true;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
2005-05-01 11:55:51 +02:00
|
|
|
else
|
|
|
|
++iface_iter;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
2005-05-01 11:55:51 +02:00
|
|
|
return true;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-05-05 12:40:47 +02:00
|
|
|
GenericCallClass *CSourceHookImpl::GetCallClass(void *iface, size_t size)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
|
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
if (cciter->m_Ptr == iface && cciter->m_ObjSize == size)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
++cciter->m_RefCounter;
|
|
|
|
return &(*cciter);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
// Make a new one
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
CCallClassImpl tmp(iface, size);
|
2005-05-05 12:40:47 +02:00
|
|
|
ApplyCallClassPatches(tmp);
|
2005-04-14 14:54:13 +02:00
|
|
|
m_CallClasses.push_back(tmp);
|
2005-10-03 19:20:22 +02:00
|
|
|
return &m_CallClasses.back();
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
void CSourceHookImpl::ReleaseCallClass(GenericCallClass *ptr)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-08-12 19:33:31 +02:00
|
|
|
Impl_CallClassList::iterator iter = m_CallClasses.find(ptr);
|
2005-04-14 14:54:13 +02:00
|
|
|
if (iter == m_CallClasses.end())
|
|
|
|
return;
|
2005-10-03 19:20:22 +02:00
|
|
|
--iter->m_RefCounter;
|
|
|
|
if (iter->m_RefCounter < 1)
|
2005-04-14 14:54:13 +02:00
|
|
|
m_CallClasses.erase(iter);
|
|
|
|
}
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
void CSourceHookImpl::ApplyCallClassPatches(CCallClassImpl &cc)
|
2005-05-05 12:40:47 +02:00
|
|
|
{
|
|
|
|
for (HookManInfoList::iterator hookman = m_HookMans.begin(); hookman != m_HookMans.end(); ++hookman)
|
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = hookman->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != hookman->m_VfnPtrs.end(); ++vfnptr_iter)
|
2005-05-05 12:40:47 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CVfnPtr::IfaceListIter iface_iter = vfnptr_iter->m_Ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->m_Ifaces.end(); ++iface_iter)
|
2005-05-05 12:40:47 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
if (iface_iter->m_Ptr >= cc.m_Ptr &&
|
|
|
|
iface_iter->m_Ptr < (reinterpret_cast<char*>(cc.m_Ptr) + cc.m_ObjSize))
|
2005-05-05 12:40:47 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
cc.ApplyCallClassPatch(static_cast<int>(reinterpret_cast<char*>(iface_iter->m_Ptr) -
|
|
|
|
reinterpret_cast<char*>(cc.m_Ptr)) + hookman->m_VtblOffs,
|
|
|
|
hookman->m_VtblIdx, vfnptr_iter->m_OrigEntry);
|
2005-05-05 12:40:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::ApplyCallClassPatches(void *ifaceptr, int vtbl_offs, int vtbl_idx, void *orig_entry)
|
|
|
|
{
|
|
|
|
for (Impl_CallClassList::iterator cc_iter = m_CallClasses.begin(); cc_iter != m_CallClasses.end();
|
|
|
|
++cc_iter)
|
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
if (ifaceptr >= cc_iter->m_Ptr &&
|
|
|
|
ifaceptr < (reinterpret_cast<char*>(cc_iter->m_Ptr) + cc_iter->m_ObjSize))
|
2005-05-05 12:40:47 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
cc_iter->ApplyCallClassPatch(static_cast<int>(reinterpret_cast<char*>(ifaceptr) -
|
|
|
|
reinterpret_cast<char*>(cc_iter->m_Ptr)) + vtbl_offs, vtbl_idx, orig_entry);
|
2005-05-05 12:40:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::RemoveCallClassPatches(void *ifaceptr, int vtbl_offs, int vtbl_idx)
|
|
|
|
{
|
|
|
|
for (Impl_CallClassList::iterator cc_iter = m_CallClasses.begin(); cc_iter != m_CallClasses.end();
|
|
|
|
++cc_iter)
|
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
if (ifaceptr >= cc_iter->m_Ptr &&
|
|
|
|
ifaceptr < (reinterpret_cast<char*>(cc_iter->m_Ptr) + cc_iter->m_ObjSize))
|
2005-04-30 17:48:46 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
cc_iter->RemoveCallClassPatch(vtbl_offs, vtbl_idx);
|
2005-04-30 17:48:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
CSourceHookImpl::HookManInfoList::iterator CSourceHookImpl::FindHookMan(HookManInfoList::iterator begin,
|
2005-04-29 22:29:46 +02:00
|
|
|
HookManInfoList::iterator end, const char *proto, int vtblofs, int vtblidx)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
HookManInfoList::iterator hookmaniter;
|
2005-05-01 14:36:48 +02:00
|
|
|
for (hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-12-10 23:37:53 +01:00
|
|
|
if (ProtosEquiv(hookmaniter->m_Proto, proto) && hookmaniter->m_VtblOffs == vtblofs &&
|
2005-10-03 19:20:22 +02:00
|
|
|
hookmaniter->m_VtblIdx == vtblidx)
|
2005-04-14 14:54:13 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-04-16 18:55:53 +02:00
|
|
|
return hookmaniter;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
void CSourceHookImpl::SetPluginPaused(Plugin plug, bool paused)
|
2005-04-16 22:26:33 +02:00
|
|
|
{
|
|
|
|
// 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)
|
2005-10-03 19:20:22 +02:00
|
|
|
for (CHookManagerInfo::VfnPtrListIter vfnptr_iter = hookmaniter->m_VfnPtrs.begin();
|
|
|
|
vfnptr_iter != hookmaniter->m_VfnPtrs.end(); ++vfnptr_iter)
|
|
|
|
for (CVfnPtr::IfaceListIter ifaceiter = vfnptr_iter->m_Ifaces.begin();
|
|
|
|
ifaceiter != vfnptr_iter->m_Ifaces.end(); ++ifaceiter)
|
2005-04-29 22:29:46 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
for (List<HookInfo>::iterator hookiter = ifaceiter->m_PreHooks.m_List.begin();
|
|
|
|
hookiter != ifaceiter->m_PreHooks.m_List.end(); ++hookiter)
|
2005-04-29 22:29:46 +02:00
|
|
|
if (plug == hookiter->plug)
|
2005-10-03 19:20:22 +02:00
|
|
|
hookiter->paused = paused;
|
2005-04-29 22:29:46 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
for (List<HookInfo>::iterator hookiter = ifaceiter->m_PostHooks.m_List.begin();
|
|
|
|
hookiter != ifaceiter->m_PostHooks.m_List.end(); ++hookiter)
|
2005-04-29 22:29:46 +02:00
|
|
|
if (plug == hookiter->plug)
|
2005-10-03 19:20:22 +02:00
|
|
|
hookiter->paused = paused;
|
2005-04-29 22:29:46 +02:00
|
|
|
}
|
2005-04-16 22:26:33 +02:00
|
|
|
}
|
2005-10-03 19:20:22 +02:00
|
|
|
void CSourceHookImpl::PausePlugin(Plugin plug)
|
|
|
|
{
|
|
|
|
SetPluginPaused(plug, true);
|
|
|
|
}
|
2005-04-16 22:26:33 +02:00
|
|
|
|
|
|
|
void CSourceHookImpl::UnpausePlugin(Plugin plug)
|
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
SetPluginPaused(plug, false);
|
2005-04-16 22:26:33 +02:00
|
|
|
}
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-10-12 22:25:15 +02:00
|
|
|
void CSourceHookImpl::HookLoopBegin(IIface *pIface)
|
|
|
|
{
|
|
|
|
HookLoopInfo hli;
|
|
|
|
hli.pCurIface = pIface;
|
|
|
|
hli.shouldContinue = true;
|
|
|
|
m_HLIStack.push(hli);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::HookLoopEnd()
|
|
|
|
{
|
|
|
|
m_HLIStack.pop();
|
|
|
|
}
|
|
|
|
|
2005-04-14 14:54:13 +02:00
|
|
|
void CSourceHookImpl::SetRes(META_RES res)
|
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
*m_HLIStack.front().pCurRes = res;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
META_RES CSourceHookImpl::GetPrevRes()
|
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
return *m_HLIStack.front().pPrevRes;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
META_RES CSourceHookImpl::GetStatus()
|
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
return *m_HLIStack.front().pStatus;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const void *CSourceHookImpl::GetOrigRet()
|
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
return m_HLIStack.front().pOrigRet;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const void *CSourceHookImpl::GetOverrideRet()
|
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
return m_HLIStack.front().pOverrideRet;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-10-12 22:25:15 +02:00
|
|
|
void *CSourceHookImpl::GetIfacePtr()
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
return *m_HLIStack.front().pIfacePtrPtr;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-10-12 22:25:15 +02:00
|
|
|
void CSourceHookImpl::SetCurResPtr(META_RES *mres)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
m_HLIStack.front().pCurRes = mres;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-10-12 22:25:15 +02:00
|
|
|
void CSourceHookImpl::SetPrevResPtr(META_RES *mres)
|
2005-05-05 12:40:47 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
m_HLIStack.front().pPrevRes = mres;
|
2005-05-05 12:40:47 +02:00
|
|
|
}
|
|
|
|
|
2005-10-12 22:25:15 +02:00
|
|
|
void CSourceHookImpl::SetStatusPtr(META_RES *mres)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
m_HLIStack.front().pStatus = mres;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-10-12 22:25:15 +02:00
|
|
|
void CSourceHookImpl::SetIfacePtrPtr(void **pp)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
m_HLIStack.front().pIfacePtrPtr = pp;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-10-12 22:25:15 +02:00
|
|
|
void CSourceHookImpl::SetOrigRetPtr(const void *ptr)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
m_HLIStack.front().pOrigRet = ptr;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
2005-10-12 22:25:15 +02:00
|
|
|
void CSourceHookImpl::SetOverrideRetPtr(const void *ptr)
|
2005-10-06 22:46:49 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
m_HLIStack.front().pOverrideRet = ptr;
|
2005-10-06 22:46:49 +02:00
|
|
|
}
|
2005-10-12 22:25:15 +02:00
|
|
|
bool CSourceHookImpl::ShouldContinue()
|
2005-10-06 22:46:49 +02:00
|
|
|
{
|
2005-10-12 22:25:15 +02:00
|
|
|
return m_HLIStack.front().shouldContinue;
|
2005-10-06 22:46:49 +02:00
|
|
|
}
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
////////////////////////////
|
|
|
|
// CCallClassImpl
|
|
|
|
////////////////////////////
|
|
|
|
CSourceHookImpl::CCallClassImpl::CCallClassImpl(void *ptr, size_t size)
|
|
|
|
: m_Ptr(ptr), m_ObjSize(size), m_RefCounter(1)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
CSourceHookImpl::CCallClassImpl::~CCallClassImpl()
|
|
|
|
{
|
|
|
|
}
|
2005-07-09 22:49:25 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
void *CSourceHookImpl::CCallClassImpl::GetThisPtr()
|
2005-07-07 23:07:43 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
return m_Ptr;
|
|
|
|
}
|
|
|
|
void *CSourceHookImpl::CCallClassImpl::GetOrigFunc(int vtbloffs, int vtblidx)
|
|
|
|
{
|
|
|
|
OrigVTables::iterator iter = m_VT.find(vtbloffs);
|
|
|
|
if (iter != m_VT.end() && iter->val.size() > (size_t)vtblidx)
|
|
|
|
{
|
|
|
|
return iter->val[vtblidx];
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-07-07 23:07:43 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
void CSourceHookImpl::CCallClassImpl::ApplyCallClassPatch(int vtbl_offs, int vtbl_idx, void *orig_entry)
|
|
|
|
{
|
|
|
|
OrigFuncs &tmpvec = m_VT[vtbl_offs];
|
|
|
|
if (tmpvec.size() <= (size_t)vtbl_idx)
|
|
|
|
tmpvec.resize(vtbl_idx+1);
|
|
|
|
tmpvec[vtbl_idx] = orig_entry;
|
|
|
|
}
|
|
|
|
void CSourceHookImpl::CCallClassImpl::RemoveCallClassPatch(int vtbl_offs, int vtbl_idx)
|
|
|
|
{
|
|
|
|
OrigVTables::iterator iter = m_VT.find(vtbl_offs);
|
|
|
|
if (iter != m_VT.end())
|
|
|
|
{
|
|
|
|
if (iter->val.size() > (size_t)vtbl_idx)
|
|
|
|
{
|
|
|
|
iter->val[vtbl_idx] = 0;
|
2005-07-07 23:07:43 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
OrigFuncs &of = iter->val;
|
|
|
|
OrigFuncs::iterator lastused = of.end();
|
|
|
|
for (OrigFuncs::iterator viter = of.begin(); viter != of.end(); ++viter)
|
|
|
|
{
|
|
|
|
if (*viter)
|
|
|
|
lastused = viter;
|
|
|
|
}
|
|
|
|
if (lastused == of.end())
|
|
|
|
{
|
|
|
|
// No used element => Remove the whole vector
|
|
|
|
m_VT.erase(iter);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
of.resize(lastused - of.begin() + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-07-07 23:07:43 +02:00
|
|
|
|
2005-12-10 23:37:53 +01:00
|
|
|
bool CSourceHookImpl::ProtosEquiv(const char *p1, const char *p2)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Old protos look like this:
|
|
|
|
|
|
|
|
0_void:
|
|
|
|
"attrib"
|
|
|
|
1_void:
|
|
|
|
"attrib|param1_type"
|
|
|
|
2_void:
|
|
|
|
"attrib|param1_type|param2_type
|
|
|
|
0:
|
|
|
|
"attrib|ret_type"
|
|
|
|
1:
|
|
|
|
"attrib|ret_type|param1_type"
|
|
|
|
2:
|
|
|
|
"attrib|ret_type|param2_type"
|
|
|
|
|
|
|
|
New protos are in fact pointers to the ProtoInfo sturcture (see sourcehook.h for details)
|
|
|
|
|
|
|
|
Old protos _never_ begin with a null character
|
|
|
|
New protos _always_ begin with a null character
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (*p1 && *p2) // Case1: Both old
|
|
|
|
{
|
|
|
|
// As in old versions
|
|
|
|
return strcmp(p1, p2) == 0;
|
|
|
|
}
|
|
|
|
else if (!*p1 && !*p2) // Case2: Both new
|
|
|
|
{
|
|
|
|
const ProtoInfo *pi1 = reinterpret_cast<const ProtoInfo*>(p1);
|
|
|
|
const ProtoInfo *pi2 = reinterpret_cast<const ProtoInfo*>(p2);
|
|
|
|
|
|
|
|
if (pi1->retTypeSize == pi2->retTypeSize &&
|
|
|
|
pi1->numOfParams == pi2->numOfParams)
|
|
|
|
{
|
|
|
|
// params[0] is 0 for "normal" functions and -1 for vararg functions
|
|
|
|
// params[1] is size of first parameter
|
|
|
|
// params[2] is size of second parameter ...
|
|
|
|
for (int i = 0; i <= pi1->numOfParams; ++i)
|
|
|
|
{
|
|
|
|
if (pi1->params[i] != pi2->params[i])
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // Case3: Mixed old/new
|
|
|
|
{
|
|
|
|
// Trust the user
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
////////////////////////////
|
|
|
|
// CHookManagerInfo
|
|
|
|
////////////////////////////
|
|
|
|
CSourceHookImpl::CHookManagerInfo::~CHookManagerInfo()
|
|
|
|
{
|
|
|
|
}
|
2005-07-07 23:07:43 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
IVfnPtr *CSourceHookImpl::CHookManagerInfo::FindVfnPtr(void *vfnptr)
|
|
|
|
{
|
|
|
|
VfnPtrListIter iter = m_VfnPtrs.find(vfnptr);
|
|
|
|
return iter == m_VfnPtrs.end() ? NULL : &(*iter);
|
|
|
|
}
|
|
|
|
void CSourceHookImpl::CHookManagerInfo::SetInfo(int vtbl_offs, int vtbl_idx, const char *proto)
|
|
|
|
{
|
|
|
|
m_VtblOffs = vtbl_offs;
|
|
|
|
m_VtblIdx = vtbl_idx;
|
|
|
|
m_Proto = proto;
|
|
|
|
}
|
|
|
|
void CSourceHookImpl::CHookManagerInfo::SetHookfuncVfnptr(void *hookfunc_vfnptr)
|
|
|
|
{
|
|
|
|
m_HookfuncVfnptr = hookfunc_vfnptr;
|
|
|
|
}
|
2005-07-07 23:07:43 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
////////////////////////////
|
|
|
|
// CVfnPtr
|
|
|
|
////////////////////////////
|
2005-07-07 23:07:43 +02:00
|
|
|
|
|
|
|
|
2005-12-10 23:37:53 +01:00
|
|
|
// If you get a crash here, the ptr passed is invalid
|
|
|
|
// This usually means a SH_DECL_MANUALHOOK* with wrong thisptroffs/vtbloffs/vtblidx
|
2005-10-03 19:20:22 +02:00
|
|
|
CSourceHookImpl::CVfnPtr::CVfnPtr(void *ptr) : m_Ptr(ptr), m_OrigEntry(*reinterpret_cast<void**>(ptr))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
CSourceHookImpl::CVfnPtr::~CVfnPtr()
|
|
|
|
{
|
2005-07-07 23:07:43 +02:00
|
|
|
}
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
void *CSourceHookImpl::CVfnPtr::GetVfnPtr()
|
2005-07-07 23:07:43 +02:00
|
|
|
{
|
2005-10-03 19:20:22 +02:00
|
|
|
return m_Ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *CSourceHookImpl::CVfnPtr::GetOrigEntry()
|
|
|
|
{
|
|
|
|
return m_OrigEntry;
|
|
|
|
}
|
|
|
|
|
|
|
|
IIface *CSourceHookImpl::CVfnPtr::FindIface(void *ptr)
|
|
|
|
{
|
|
|
|
IfaceListIter iter = m_Ifaces.find(ptr);
|
|
|
|
return iter == m_Ifaces.end() ? NULL : &(*iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////
|
|
|
|
// CIface
|
|
|
|
////////////////////////////
|
|
|
|
CSourceHookImpl::CIface::CIface(void *ptr) : m_Ptr(ptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
CSourceHookImpl::CIface::~CIface()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void *CSourceHookImpl::CIface::GetPtr()
|
|
|
|
{
|
|
|
|
return m_Ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
IHookList *CSourceHookImpl::CIface::GetPreHooks()
|
|
|
|
{
|
|
|
|
return &m_PreHooks;
|
|
|
|
}
|
|
|
|
IHookList *CSourceHookImpl::CIface::GetPostHooks()
|
|
|
|
{
|
|
|
|
return &m_PostHooks;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////
|
|
|
|
// CHookList
|
|
|
|
////////////////////////////
|
|
|
|
|
2005-10-05 23:51:02 +02:00
|
|
|
CSourceHookImpl::CHookList::CHookList() : m_FreeIters(NULL), m_UsedIters(NULL)
|
2005-10-03 19:20:22 +02:00
|
|
|
{
|
|
|
|
}
|
2005-10-05 23:51:02 +02:00
|
|
|
CSourceHookImpl::CHookList::CHookList(const CHookList &other) : m_List(other.m_List), m_FreeIters(NULL), m_UsedIters(NULL)
|
2005-10-03 19:20:22 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
CSourceHookImpl::CHookList::~CHookList()
|
|
|
|
{
|
|
|
|
while (m_FreeIters)
|
|
|
|
{
|
|
|
|
CIter *iter = m_FreeIters->m_pNext;
|
|
|
|
delete m_FreeIters;
|
|
|
|
m_FreeIters = iter;
|
|
|
|
}
|
2005-12-06 07:01:16 +01:00
|
|
|
while (m_UsedIters)
|
|
|
|
{
|
|
|
|
CIter *iter = m_UsedIters->m_pNext;
|
|
|
|
delete m_UsedIters;
|
|
|
|
m_UsedIters = iter;
|
|
|
|
}
|
2005-10-03 19:20:22 +02:00
|
|
|
}
|
|
|
|
IHookList::IIter *CSourceHookImpl::CHookList::GetIter()
|
|
|
|
{
|
2005-10-05 23:51:02 +02:00
|
|
|
CIter *ret;
|
2005-10-03 19:20:22 +02:00
|
|
|
if (m_FreeIters)
|
|
|
|
{
|
2005-10-05 23:51:02 +02:00
|
|
|
ret = m_FreeIters;
|
2005-10-03 19:20:22 +02:00
|
|
|
m_FreeIters = ret->m_pNext;
|
|
|
|
ret->GoToBegin();
|
|
|
|
}
|
2005-10-05 23:51:02 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = new CIter(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret->m_pNext = m_UsedIters;
|
|
|
|
ret->m_pPrev = NULL;
|
|
|
|
if (m_UsedIters)
|
|
|
|
m_UsedIters->m_pPrev = ret;
|
|
|
|
m_UsedIters = ret;
|
|
|
|
|
|
|
|
return ret;
|
2005-10-03 19:20:22 +02:00
|
|
|
}
|
|
|
|
void CSourceHookImpl::CHookList::ReleaseIter(IIter *pIter)
|
|
|
|
{
|
|
|
|
CIter *pIter2 = static_cast<CIter*>(pIter);
|
2005-10-05 23:51:02 +02:00
|
|
|
|
|
|
|
// Unlink from m_UsedIters
|
|
|
|
|
|
|
|
if (pIter2->m_pNext)
|
|
|
|
pIter2->m_pNext->m_pPrev = pIter2->m_pPrev;
|
|
|
|
if (pIter2->m_pPrev)
|
|
|
|
pIter2->m_pPrev->m_pNext = pIter2->m_pNext;
|
2005-10-06 22:46:49 +02:00
|
|
|
if (pIter2 == m_UsedIters)
|
|
|
|
m_UsedIters = NULL;
|
2005-10-05 23:51:02 +02:00
|
|
|
|
|
|
|
// Link to m_FreeIters
|
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
pIter2->m_pNext = m_FreeIters;
|
2005-10-05 23:51:02 +02:00
|
|
|
|
2005-10-03 19:20:22 +02:00
|
|
|
m_FreeIters = pIter2;
|
|
|
|
}
|
|
|
|
|
|
|
|
CSourceHookImpl::CHookList::CIter::CIter(CHookList *pList) : m_pList(pList), m_pNext(NULL)
|
|
|
|
{
|
|
|
|
GoToBegin();
|
|
|
|
}
|
|
|
|
CSourceHookImpl::CHookList::CIter::~CIter()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::CHookList::CIter::GoToBegin()
|
|
|
|
{
|
|
|
|
m_Iter = m_pList->m_List.begin();
|
|
|
|
SkipPaused();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CSourceHookImpl::CHookList::CIter::End()
|
|
|
|
{
|
2005-10-05 23:51:02 +02:00
|
|
|
if (!m_pList)
|
|
|
|
return false;
|
2005-10-03 19:20:22 +02:00
|
|
|
return m_Iter == m_pList->m_List.end();
|
|
|
|
}
|
|
|
|
void CSourceHookImpl::CHookList::CIter::Next()
|
|
|
|
{
|
2005-10-05 23:51:02 +02:00
|
|
|
if (!m_pList)
|
|
|
|
return;
|
2005-10-03 19:20:22 +02:00
|
|
|
++m_Iter;
|
|
|
|
SkipPaused();
|
|
|
|
}
|
2005-10-05 23:51:02 +02:00
|
|
|
void CSourceHookImpl::CHookList::CIter::Clear()
|
|
|
|
{
|
|
|
|
m_pList = NULL;
|
|
|
|
}
|
2005-10-03 19:20:22 +02:00
|
|
|
void CSourceHookImpl::CHookList::CIter::SkipPaused()
|
|
|
|
{
|
|
|
|
while (m_Iter != m_pList->m_List.end() && m_Iter->paused)
|
|
|
|
++m_Iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
ISHDelegate *CSourceHookImpl::CHookList::CIter::Handler()
|
|
|
|
{
|
|
|
|
return m_Iter->handler;
|
|
|
|
}
|
|
|
|
int CSourceHookImpl::CHookList::CIter::ThisPtrOffs()
|
|
|
|
{
|
|
|
|
return m_Iter->thisptr_offs;
|
2005-07-07 23:07:43 +02:00
|
|
|
}
|
2005-04-22 16:29:25 +02:00
|
|
|
}
|