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-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
|
|
|
*/
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include "sourcehook_impl.h"
|
|
|
|
|
|
|
|
namespace SourceHook
|
|
|
|
{
|
|
|
|
CSourceHookImpl::CSourceHookImpl()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CSourceHookImpl::~CSourceHookImpl()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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-04-29 22:29:46 +02:00
|
|
|
for (hook_iter = iface_iter->name.begin(); hook_iter != iface_iter->name.end(); ++hook_iter) \
|
|
|
|
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-04-29 22:29:46 +02:00
|
|
|
if (hmil_iter->plug != plug)
|
|
|
|
continue;
|
|
|
|
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
|
|
|
|
vfnptr_iter != hmil_iter->vfnptrs.end(); ++vfnptr_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->ifaces.end(); ++iface_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
std::list<HookManagerInfo::VfnPtr::Iface::Hook>::iterator hook_iter;
|
2005-04-16 22:26:33 +02:00
|
|
|
TMP_CHECK_LIST(hooks_pre);
|
|
|
|
TMP_CHECK_LIST(hooks_post);
|
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
|
|
|
|
std::list<RemoveHookInfo> hookstoremove;
|
2005-04-29 22:29:46 +02:00
|
|
|
#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)
|
2005-04-16 18:55:53 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
|
|
|
|
vfnptr_iter != hmil_iter->vfnptrs.end(); ++vfnptr_iter)
|
2005-04-16 18:55:53 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
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);
|
|
|
|
}
|
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
|
|
|
|
|
|
|
for (std::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-04-29 22:29:46 +02:00
|
|
|
for (HookManInfoList::iterator hmil_iter = m_HookMans.begin(); hmil_iter != m_HookMans.end();
|
|
|
|
erase ? hmil_iter=m_HookMans.erase(hmil_iter) : ++hmil_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
if (hmil_iter->plug == plug)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
if (!hmil_iter->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-04-16 18:55:53 +02:00
|
|
|
// For each hook manager:
|
2005-04-29 22:29:46 +02:00
|
|
|
for (HookManInfoList::iterator 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-04-29 22:29:46 +02:00
|
|
|
hmil_iter->proto, hmil_iter->vtbl_offs, hmil_iter->vtbl_idx);
|
|
|
|
|
|
|
|
// 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!");
|
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
|
|
|
|
SH_ASSERT(newHookMan->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
|
|
|
|
SH_ASSERT(newHookMan->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
|
|
|
|
newHookMan->vfnptrs = hmil_iter->vfnptrs;
|
2005-04-16 18:55:53 +02:00
|
|
|
|
|
|
|
// Unregister the old one, register the new one
|
2005-04-29 22:29:46 +02:00
|
|
|
hmil_iter->func(HA_Unregister, NULL);
|
2005-04-16 18:55:53 +02:00
|
|
|
newHookMan->func(HA_Register, &(*newHookMan));
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::CompleteShutdown()
|
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
std::list<RemoveHookInfo> hookstoremove;
|
2005-04-29 22:29:46 +02:00
|
|
|
#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)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
for (HookManagerInfo::VfnPtrListIter vfnptr_iter = hmil_iter->vfnptrs.begin();
|
|
|
|
vfnptr_iter != hmil_iter->vfnptrs.end(); ++vfnptr_iter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
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);
|
|
|
|
}
|
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
|
|
|
|
|
|
|
for (std::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-04-16 18:55:53 +02:00
|
|
|
// 1) Get info about the hook manager
|
|
|
|
HookManagerInfo tmp;
|
|
|
|
if (myHookMan(HA_GetInfo, &tmp) != 0)
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-04-16 18:55:53 +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-04-29 22:29:46 +02:00
|
|
|
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)
|
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!
|
|
|
|
tmp.func = myHookMan;
|
2005-04-14 14:54:13 +02:00
|
|
|
tmp.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-04-29 22:29:46 +02:00
|
|
|
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!");
|
2005-04-14 14:54:13 +02:00
|
|
|
|
|
|
|
// Tell it to store the pointer if it's not already active
|
2005-04-29 22:29:46 +02:00
|
|
|
if (hookman->vfnptrs.empty())
|
2005-04-16 18:55:53 +02:00
|
|
|
hookman->func(HA_Register, &(*hookman));
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
void **cur_vtptr = *reinterpret_cast<void***>(
|
|
|
|
reinterpret_cast<char*>(adjustediface) + tmp.vtbl_offs);
|
|
|
|
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.vtbl_idx);
|
|
|
|
|
|
|
|
HookManagerInfo::VfnPtrListIter vfnptr_iter = std::find(
|
|
|
|
hookman->vfnptrs.begin(), hookman->vfnptrs.end(), cur_vfnptr);
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
if (vfnptr_iter == hookman->vfnptrs.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
// Add a new one
|
|
|
|
HookManagerInfo::VfnPtr vfp;
|
|
|
|
vfp.vfnptr = cur_vfnptr;
|
|
|
|
vfp.orig_entry = *reinterpret_cast<void**>(cur_vfnptr);
|
|
|
|
|
|
|
|
// Alter vtable entry
|
2005-05-01 20:04:18 +02:00
|
|
|
if (!SetMemAccess(cur_vtptr, sizeof(void*) * (tmp.vtbl_idx + 1), SH_MEM_READ | SH_MEM_WRITE))
|
|
|
|
return false;
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
*reinterpret_cast<void**>(cur_vfnptr) = *reinterpret_cast<void**>(hookman->hookfunc_vfnptr);
|
|
|
|
|
2005-05-01 20:04:18 +02:00
|
|
|
hookman->vfnptrs.push_back(vfp);
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
// Make vfnptr_iter point to the new element
|
|
|
|
vfnptr_iter = hookman->vfnptrs.end();
|
|
|
|
--vfnptr_iter;
|
2005-04-30 17:48:46 +02:00
|
|
|
|
|
|
|
// 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->cc.ptr == iface)
|
|
|
|
ApplyCallClassPatch(*cciter, tmp.vtbl_offs, tmp.vtbl_idx, vfnptr_iter->orig_entry);
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
2005-04-29 22:29:46 +02:00
|
|
|
|
|
|
|
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;
|
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-04-14 14:54:13 +02:00
|
|
|
if (post)
|
2005-04-29 22:29:46 +02:00
|
|
|
iface_iter->hooks_post.push_back(hookinfo);
|
2005-04-14 14:54:13 +02:00
|
|
|
else
|
2005-04-29 22:29:46 +02:00
|
|
|
iface_iter->hooks_pre.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)
|
|
|
|
{
|
|
|
|
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)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-16 18:55:53 +02:00
|
|
|
HookManagerInfo tmp;
|
|
|
|
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
|
|
|
|
HookManInfoList::iterator hookman = FindHookMan(m_HookMans.begin(), m_HookMans.end(),
|
2005-04-29 22:29:46 +02:00
|
|
|
tmp.proto, tmp.vtbl_offs, tmp.vtbl_idx);
|
2005-04-16 18:55:53 +02:00
|
|
|
if (hookman == m_HookMans.end())
|
2005-04-14 14:54:13 +02:00
|
|
|
return false;
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
void **cur_vtptr = *reinterpret_cast<void***>(
|
|
|
|
reinterpret_cast<char*>(adjustediface) + tmp.vtbl_offs);
|
|
|
|
void *cur_vfnptr = reinterpret_cast<void*>(cur_vtptr + tmp.vtbl_idx);
|
|
|
|
|
2005-05-01 11:55:51 +02:00
|
|
|
HookManagerInfo::VfnPtrListIter vfnptr_iter = std::find(hookman->vfnptrs.begin(), hookman->vfnptrs.end(), cur_vfnptr);
|
|
|
|
if (vfnptr_iter == hookman->vfnptrs.end())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (HookManagerInfo::VfnPtr::IfaceListIter iface_iter = vfnptr_iter->ifaces.begin();
|
|
|
|
iface_iter != vfnptr_iter->ifaces.end();)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-05-01 11:55:51 +02:00
|
|
|
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)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-05-01 11:55:51 +02:00
|
|
|
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())
|
|
|
|
{
|
|
|
|
iface_iter = vfnptr_iter->ifaces.erase(iface_iter);
|
|
|
|
if (vfnptr_iter->ifaces.empty())
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-05-01 11:55:51 +02:00
|
|
|
// Deactivate the hook
|
|
|
|
*reinterpret_cast<void**>(vfnptr_iter->vfnptr) = vfnptr_iter->orig_entry;
|
|
|
|
|
|
|
|
hookman->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)
|
|
|
|
if (cciter->cc.ptr == adjustediface)
|
|
|
|
RemoveCallClassPatch(*cciter, tmp.vtbl_offs, tmp.vtbl_idx);
|
|
|
|
|
|
|
|
if (hookman->vfnptrs.empty())
|
2005-04-29 22:29:46 +02:00
|
|
|
{
|
2005-05-01 11:55:51 +02:00
|
|
|
// Unregister the hook manager
|
|
|
|
hookman->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-04-29 22:29:46 +02:00
|
|
|
GenericCallClass *CSourceHookImpl::GetCallClass(void *iface)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
for (Impl_CallClassList::iterator cciter = m_CallClasses.begin(); cciter != m_CallClasses.end(); ++cciter)
|
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
if (cciter->cc.ptr == iface)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
|
|
|
++cciter->refcounter;
|
2005-04-29 22:29:46 +02:00
|
|
|
return &cciter->cc;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
CallClassInfo tmp;
|
2005-04-14 14:54:13 +02:00
|
|
|
tmp.refcounter = 1;
|
2005-04-29 22:29:46 +02:00
|
|
|
tmp.cc.ptr = iface;
|
2005-04-14 14:54:13 +02:00
|
|
|
|
2005-04-16 18:55:53 +02:00
|
|
|
for (HookManInfoList::iterator hookman = m_HookMans.begin(); hookman != m_HookMans.end(); ++hookman)
|
2005-04-29 22:29:46 +02:00
|
|
|
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);
|
|
|
|
|
2005-04-14 14:54:13 +02:00
|
|
|
m_CallClasses.push_back(tmp);
|
2005-04-29 22:29:46 +02:00
|
|
|
return &m_CallClasses.back().cc;
|
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
|
|
|
{
|
|
|
|
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)
|
|
|
|
m_CallClasses.erase(iter);
|
|
|
|
}
|
|
|
|
|
2005-04-29 22:29:46 +02:00
|
|
|
void CSourceHookImpl::ApplyCallClassPatch(CallClassInfo &cc, int vtbl_offs, int vtbl_idx, void *orig_entry)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
OrigFuncs &tmpvec = cc.cc.vt[vtbl_offs];
|
|
|
|
if (tmpvec.size() <= (size_t)vtbl_idx)
|
|
|
|
tmpvec.resize(vtbl_idx+1);
|
|
|
|
tmpvec[vtbl_idx] = orig_entry;
|
2005-04-14 14:54:13 +02:00
|
|
|
}
|
|
|
|
|
2005-04-30 17:48:46 +02:00
|
|
|
void CSourceHookImpl::RemoveCallClassPatch(CallClassInfo &cc, int vtbl_offs, int vtbl_idx)
|
|
|
|
{
|
|
|
|
OrigVTables::iterator iter = cc.cc.vt.find(vtbl_offs);
|
|
|
|
if (iter != cc.cc.vt.end())
|
|
|
|
{
|
|
|
|
if (iter->second.size() > (size_t)vtbl_idx)
|
|
|
|
{
|
|
|
|
iter->second[vtbl_idx] = 0;
|
|
|
|
// Free some memory if possible
|
|
|
|
OrigFuncs::reverse_iterator riter;
|
|
|
|
for (riter = iter->second.rbegin(); riter != iter->second.rend(); ++riter)
|
|
|
|
{
|
|
|
|
if (*riter != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
iter->second.resize(iter->second.size() - (riter - iter->second.rbegin()));
|
|
|
|
if (!iter->second.size())
|
|
|
|
cc.cc.vt.erase(iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-05-01 14:36:48 +02:00
|
|
|
HookManInfoList::iterator hookmaniter;
|
|
|
|
for (hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
|
2005-04-14 14:54:13 +02:00
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
if (strcmp(hookmaniter->proto, proto) == 0 && hookmaniter->vtbl_offs == vtblofs &&
|
|
|
|
hookmaniter->vtbl_idx == 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-04-16 22:26:33 +02:00
|
|
|
void CSourceHookImpl::PausePlugin(Plugin plug)
|
|
|
|
{
|
|
|
|
// 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-04-29 22:29:46 +02:00
|
|
|
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::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
|
|
|
|
hookiter != ifaceiter->hooks_post.end(); ++hookiter)
|
|
|
|
if (plug == hookiter->plug)
|
|
|
|
hookiter->paused = true;
|
|
|
|
}
|
2005-04-16 22:26:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::UnpausePlugin(Plugin plug)
|
|
|
|
{
|
2005-04-29 22:29:46 +02:00
|
|
|
// Go through all hook managers, all interfaces, and set the status of all hooks of this plugin to paused
|
2005-04-16 22:26:33 +02:00
|
|
|
for (HookManInfoList::iterator hookmaniter = m_HookMans.begin(); hookmaniter != m_HookMans.end(); ++hookmaniter)
|
2005-04-29 22:29:46 +02:00
|
|
|
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::VfnPtr::Iface::Hook>::iterator hookiter = ifaceiter->hooks_post.begin();
|
|
|
|
hookiter != ifaceiter->hooks_post.end(); ++hookiter)
|
|
|
|
if (plug == hookiter->plug)
|
|
|
|
hookiter->paused = false;
|
|
|
|
}
|
2005-04-16 22:26:33 +02:00
|
|
|
}
|
2005-04-14 14:54:13 +02:00
|
|
|
|
|
|
|
void CSourceHookImpl::SetRes(META_RES res)
|
|
|
|
{
|
|
|
|
m_CurRes = res;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES CSourceHookImpl::GetPrevRes()
|
|
|
|
{
|
|
|
|
return m_PrevRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES CSourceHookImpl::GetStatus()
|
|
|
|
{
|
|
|
|
return m_Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *CSourceHookImpl::GetOrigRet()
|
|
|
|
{
|
|
|
|
return m_OrigRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *CSourceHookImpl::GetOverrideRet()
|
|
|
|
{
|
|
|
|
return m_OverrideRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES &CSourceHookImpl::GetCurResRef()
|
|
|
|
{
|
|
|
|
return m_CurRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES &CSourceHookImpl::GetPrevResRef()
|
|
|
|
{
|
|
|
|
return m_PrevRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
META_RES &CSourceHookImpl::GetStatusRef()
|
|
|
|
{
|
|
|
|
return m_Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::SetOrigRet(const void *ptr)
|
|
|
|
{
|
|
|
|
m_OrigRet = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::SetOverrideRet(const void *ptr)
|
|
|
|
{
|
|
|
|
m_OverrideRet = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSourceHookImpl::SetIfacePtr(void *ptr)
|
|
|
|
{
|
|
|
|
m_IfacePtr = ptr;
|
|
|
|
}
|
|
|
|
void *CSourceHookImpl::GetIfacePtr()
|
|
|
|
{
|
|
|
|
return m_IfacePtr;
|
|
|
|
}
|
2005-04-22 16:29:25 +02:00
|
|
|
}
|