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:
parent
3c47dbb392
commit
3ea2f023e1
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user