1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2025-03-21 12:28:56 +01:00

h-hello? new feature: RETURN_META_(VALUE_)NEWPARAMS now also works in post handlers!

--HG--
extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%40180
This commit is contained in:
Pavol Marko 2006-02-05 20:24:58 +00:00
parent 3c47dbb392
commit 3ea2f023e1
3 changed files with 176 additions and 6 deletions

View File

@ -617,12 +617,19 @@ namespace SourceHook
HookLoopInfo hli;
hli.pCurIface = pIface;
hli.shouldContinue = true;
hli.recall = false;
hli.recall = HookLoopInfo::Recall_No;
static_cast<CIface*>(pIface)->m_PreHooks.RQFlagReset();
static_cast<CIface*>(pIface)->m_PostHooks.RQFlagReset();
m_HLIStack.push(hli);
}
void CSourceHookImpl::HookLoopEnd()
{
// When in a post recall... make sure status is high enough so that it returns the orig ret.
if (m_HLIStack.size() > 1 && m_HLIStack.second().recall == HookLoopInfo::Recall_Post2)
*m_HLIStack.front().pStatus = MRES_SUPERCEDE; // THAT'LL TEACH THEM!
m_HLIStack.pop();
}
@ -643,6 +650,10 @@ namespace SourceHook
const void *CSourceHookImpl::GetOrigRet()
{
// When in a post recall... return the orig ret of the previous hookloop.
if (m_HLIStack.size() > 1 && m_HLIStack.second().recall == HookLoopInfo::Recall_Post2)
return m_HLIStack.second().pOrigRet;
return m_HLIStack.front().pOrigRet;
}
@ -724,7 +735,13 @@ namespace SourceHook
HookLoopInfo &other = m_HLIStack.second();
*statusPtr = *other.pStatus;
*prevResPtr = *other.pStatus;
hli.pOverrideRet = other.pOverrideRet;
// When the status is low so there's no override return value and we're in a post recall,
// give it the orig return value as override return value.
if (*statusPtr < MRES_OVERRIDE && other.recall == HookLoopInfo::Recall_Post1)
hli.pOverrideRet = const_cast<void*>(other.pOrigRet);
else // Otherwise, transfer override ret normally
hli.pOverrideRet = other.pOverrideRet;
}
else
hli.pOverrideRet = overrideRetPtr;
@ -740,6 +757,27 @@ namespace SourceHook
// actual recall is done and that we are back in the original handler which shall return
// immediately.
// Post-recalls:
// The second element on the stack has recall set to Recall_Post1.
// This means that we want to skip this part and the original function calling thing, so
// we store the status mres value, set status to MRES_SUPERCEDE, and return false.
// We also set the recall flag to Recall_Post2, so the next time the thing calls us, we
// can return true (so that the thing goes into post hooks).
if (m_HLIStack.size() > 1)
{
if (m_HLIStack.second().recall == HookLoopInfo::Recall_Post1)
{
m_HLIStack.front().temporaryStatus = *m_HLIStack.front().pStatus;
*m_HLIStack.front().pStatus = MRES_SUPERCEDE;
m_HLIStack.second().recall = HookLoopInfo::Recall_Post2;
return false;
}
else if (m_HLIStack.second().recall == HookLoopInfo::Recall_Post2)
{
*m_HLIStack.front().pStatus = m_HLIStack.front().temporaryStatus;
return m_HLIStack.front().shouldContinue;
}
}
return m_HLIStack.front().shouldContinue && !m_HLIStack.front().recall;
}
@ -747,11 +785,16 @@ namespace SourceHook
{
if (!m_HLIStack.empty())
{
m_HLIStack.front().recall = true;
CHookList *mlist = static_cast<CHookList*>(m_HLIStack.front().pCurIface->GetPreHooks());
// Also watch out for post recalls! Described at the beginning of sourcehook_impl.h
m_HLIStack.front().recall =
static_cast<CIface*>(m_HLIStack.front().pCurIface)->m_PostHooks.RQFlagGet() ?
HookLoopInfo::Recall_Post1 : HookLoopInfo::Recall_Pre;
CHookList *mlist = static_cast<CHookList*>(m_HLIStack.front().recall == HookLoopInfo::Recall_Pre ?
m_HLIStack.front().pCurIface->GetPreHooks() : m_HLIStack.front().pCurIface->GetPostHooks());
mlist->m_Recall = true;
// The hookfunc usually do this, but it won't have a chance to see it,
// The hookfunc usually does this, but it won't have a chance to see it,
// so for recalls, we update status here if it's required
if (*m_HLIStack.front().pCurRes > *m_HLIStack.front().pStatus)
*m_HLIStack.front().pStatus = *m_HLIStack.front().pCurRes;
@ -931,6 +974,8 @@ namespace SourceHook
}
IHookList::IIter *CSourceHookImpl::CHookList::GetIter()
{
m_RQFlag = true;
CIter *ret;
if (m_FreeIters)
{

View File

@ -116,6 +116,32 @@ Recalls
When this recurisvely called hookfunc returns, the macro returns what it returned
(using MRES_SUPERCEDE). CSourceHookImpl returns false from ShouldContinue so the original hook loop
is abandonned.
Post Recalls
People wanted to be able to use META_RETURN_(VALUE_)NEWPARAMS from post hooks as well. Crazy people!
Anyway, for this, we have to know where a hook handler is. Is it executing pre or post hooks at the moment?
The only way we can know this is watching when it calls CHookList::GetIter(). So CHookList gets a new variable:
m_RequestedFlag. It also gets two new functions: RQFlagReset() and RQFlagGet().
HookLoopBegin() calls RQFlagReset on both hooklists of the iface; then DoRecall() checks whether the postlist's
RQ flag is set. if yes, the hook loop is in post mode.
So, what a about a recall in post mode? The first ShouldContinue returns false and sets Status to supercede.
This way the pre hooks and the function call will be skipped. Then, then next ShouldContinue returns true, so we get
into the post hooks. HA!
Return Values in Post Recalls
The easy case is when we already have an override return value. In this case, the status register gets transferred,
and so does the override return pointer. So, basically, everything is ok.
However, what happens if we don't? ie. the status register is on MRES_IGNORED? In this case we'd have to transfer the
orig ret value. But we can't: There's no way to tell the hookfunc: "Use this as orig ret pointer". It uses its own.
So, we emulate it. GetOrigRet will return the orig ret pointer from the old hook loop. If still no one overrides it,
we'd have to return it. BUT! HOW TO DO THIS? Check out SH_RETURN(). First calls HookLoopEnd(), then decides whether
to return the override retval or the orig retval. But it doesn't ask for a new override return. So we give the function
the last orig return value as its new override return value; but leave status where it is so everything works, and in
HookLoopEnd we make sure that status is high enough so that the override return will be returned. crazy.
All this stuff could be much less complicated if I didn't try to preserve binary compatibility :)
*/
namespace SourceHook
@ -240,6 +266,7 @@ namespace SourceHook
// For recalls
bool m_Recall;
bool m_RQFlag;
void SetRecallState(); // Sets the list into a state where the next returned
// iterator (from GetIter) will be a copy of the last
@ -247,6 +274,8 @@ namespace SourceHook
// The hook resets this state automatically on:
// GetIter, ReleaseIter
void RQFlagReset() { m_RQFlag = false; }
bool RQFlagGet() { return m_RQFlag; }
CHookList();
CHookList(const CHookList &other);
virtual ~CHookList();
@ -452,12 +481,21 @@ namespace SourceHook
struct HookLoopInfo
{
enum RecallType
{
Recall_No=0,
Recall_Pre,
Recall_Post1,
Recall_Post2
};
META_RES *pStatus;
META_RES *pPrevRes;
META_RES *pCurRes;
META_RES temporaryStatus; //!< Stored during Post1 recall phase
bool shouldContinue;
bool recall; //!< True if we're in a recall, eh.
RecallType recall; //!< Specifies which kind of recall we're in.
IIface *pCurIface;
const void *pOrigRet;

View File

@ -19,8 +19,14 @@ namespace
MAKE_STATE_1(State_Func2, int);
MAKE_STATE_1(State_H1_Func2, int);
MAKE_STATE_1(State_H2_Func2, int);
MAKE_STATE_2(State_HP_Func2, int, int);
MAKE_STATE_2(State_Func22, int, int);
MAKE_STATE_2(State_H1_Func22, int, int);
MAKE_STATE_2(State_HP1_Func22, int, int);
MAKE_STATE_2(State_HP2_Func22, int, int);
struct Test
{
virtual void Func1(int a)
@ -37,6 +43,7 @@ namespace
// Overloaded version
virtual int Func2(int a, int b)
{
ADD_STATE(State_Func22(a, b));
return 0xDEADFC;
}
};
@ -65,14 +72,46 @@ namespace
static_cast<int (Test::*)(int)>(&Test::Func2), (a - 10));
}
int Handler2_Func2(int a)
{
ADD_STATE(State_H2_Func2(a));
RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, 0,
static_cast<int (Test::*)(int)>(&Test::Func2), (a - 10));
}
int HandlerPost_Func2(int a)
{
ADD_STATE(State_HP_Func2(a, META_RESULT_ORIG_RET(int)));
RETURN_META_VALUE(MRES_IGNORED, 0);
}
int Handler1_Func22(int a, int b)
{
ADD_STATE(State_H1_Func22(a, b));
RETURN_META_VALUE(MRES_IGNORED, 0);
}
int HandlerPost1_Func22(int a, int b)
{
ADD_STATE(State_HP1_Func22(a, b));
RETURN_META_VALUE_NEWPARAMS(MRES_IGNORED, 0, static_cast<int (Test::*)(int, int)>(&Test::Func2), (1, 2));
}
int HandlerPost1A_Func22(int a, int b)
{
ADD_STATE(State_HP1_Func22(a, b));
RETURN_META_VALUE_NEWPARAMS(MRES_OVERRIDE, 0, static_cast<int (Test::*)(int, int)>(&Test::Func2), (1, 2));
}
int HandlerPost2_Func22(int a, int b)
{
ADD_STATE(State_HP2_Func22(a, b));
RETURN_META_VALUE(MRES_IGNORED, 0);
}
SH_DECL_HOOK1_void(Test, Func1, SH_NOATTRIB, 0, int);
SH_DECL_HOOK1(Test, Func2, SH_NOATTRIB, 0, int, int);
SH_DECL_HOOK2(Test, Func2, SH_NOATTRIB, 1, int, int, int);
}
bool TestRecall(std::string &error)
@ -135,5 +174,53 @@ bool TestRecall(std::string &error)
CHECK_COND(a == 500, "Part 4.1");
// Func2, with other handler
SH_REMOVE_HOOK_STATICFUNC(Test, Func2, ptr, Handler1_Func2, false);
SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, Handler2_Func2, false);
a = ptr->Func2(77);
CHECK_STATES((&g_States,
new State_H2_Func2(77),
new State_Func2(67),
new State_HP_Func2(67, 1000), // 1000 because it's the ORIG_RET
NULL), "Part 4.2");
CHECK_COND(a == 1000, "Part 4.2.1"); // Should return 1000 as well.
// Func22 -> post recalls
// 1) WITH OVERRIDE
SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, Handler1_Func22, false);
SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, HandlerPost1A_Func22, true);
SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, HandlerPost2_Func22, true);
a = ptr->Func2(10, 11);
CHECK_STATES((&g_States,
new State_H1_Func22(10, 11),
new State_Func22(10, 11),
new State_HP1_Func22(10, 11),
new State_HP2_Func22(1, 2),
NULL), "Part 5");
CHECK_COND(a == 0, "Part 5.1");
// 2) WITH IGNORE
SH_REMOVE_HOOK_STATICFUNC(Test, Func2, ptr, HandlerPost1A_Func22, true);
SH_REMOVE_HOOK_STATICFUNC(Test, Func2, ptr, HandlerPost2_Func22, true);
SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, HandlerPost1_Func22, true);
SH_ADD_HOOK_STATICFUNC(Test, Func2, ptr, HandlerPost2_Func22, true);
a = ptr->Func2(10, 11);
CHECK_STATES((&g_States,
new State_H1_Func22(10, 11),
new State_Func22(10, 11),
new State_HP1_Func22(10, 11),
new State_HP2_Func22(1, 2),
NULL), "Part 5");
CHECK_COND(a == 0xDEADFC, "Part 5.1");
return true;
}