Merge pull request #2011 from CookiePLMonster/transform-patches-recursive

Guard against nested SetTransform/MultiplyTransform calls
This commit is contained in:
Luke Usher 2020-10-26 18:01:11 +00:00 committed by GitHub
commit 1c465409c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 32 deletions

View File

@ -6459,40 +6459,66 @@ void CxbxImpl_SetTransform
LOG_FUNC_ARG(pMatrix)
LOG_FUNC_END;
State = EmuXB2PC_D3DTS(State);
State = EmuXB2PC_D3DTS(State);
HRESULT hRet = g_pD3DDevice->SetTransform(State, pMatrix);
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTransform");
}
// MultiplyTransform can call SetTransform, nested call detection is required
// Test case: 25 to Life
static thread_local uint32_t setTransformCount = 0;
// LTCG specific D3DDevice_SetTransform function...
// This uses a custom calling convention where parameter is passed in EAX, EDX
// Naked functions must not contain objects that would require unwinding
// so we cheat a bit by stashing the function body in a separate function
static void D3DDevice_SetTransform_0_Inner
(
D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
)
{
NestedPatchCounter call(setTransformCount);
__asm {
// Trampoline to guest code to remove the need for a GetTransform patch
mov eax, State
mov edx, pMatrix
call XB_TRMP(D3DDevice_SetTransform_0)
}
if (call.GetLevel() == 0) {
// Skip if this patch is called from MultiplyTransform
CxbxImpl_SetTransform(State, pMatrix);
}
}
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform_0)
(
)
{
D3DTRANSFORMSTATETYPE State;
CONST D3DMATRIX *pMatrix;
D3DTRANSFORMSTATETYPE State;
CONST D3DMATRIX *pMatrix;
// prologue
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
mov State, eax
mov pMatrix, edx
// Trampoline to guest code to remove the need for a GetTransform patch
call XB_TRMP(D3DDevice_SetTransform_0)
}
// prologue
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
mov State, eax
mov pMatrix, edx
}
CxbxImpl_SetTransform(State, pMatrix);
D3DDevice_SetTransform_0_Inner(State, pMatrix);
// epilogue
__asm {
mov esp, ebp
pop ebp
ret
}
// epilogue
__asm {
mov esp, ebp
pop ebp
ret
}
}
// ******************************************************************
@ -6504,9 +6530,14 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform)
CONST D3DMATRIX *pMatrix
)
{
// Trampoline to guest code to remove the need for a GetTransform patch
NestedPatchCounter call(setTransformCount);
// Trampoline to guest code to remove the need for a GetTransform patch
XB_TRMP(D3DDevice_SetTransform)(State, pMatrix);
CxbxImpl_SetTransform(State, pMatrix);
if (call.GetLevel() == 0) {
// Skip if this patch is called from MultiplyTransform
CxbxImpl_SetTransform(State, pMatrix);
}
}
// ******************************************************************
@ -6514,23 +6545,24 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform)
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_MultiplyTransform)
(
D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(State)
LOG_FUNC_ARG(pMatrix)
LOG_FUNC_END;
LOG_FUNC_BEGIN
LOG_FUNC_ARG(State)
LOG_FUNC_ARG(pMatrix)
LOG_FUNC_END;
NestedPatchCounter call(setTransformCount);
// Trampoline to guest code to remove the need for a GetTransform patch
// Trampoline to guest code to remove the need for a GetTransform patch
XB_TRMP(D3DDevice_MultiplyTransform)(State, pMatrix);
State = EmuXB2PC_D3DTS(State);
State = EmuXB2PC_D3DTS(State);
HRESULT hRet = g_pD3DDevice->MultiplyTransform(State, pMatrix);
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform");
HRESULT hRet = g_pD3DDevice->MultiplyTransform(State, pMatrix);
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform");
}
// ******************************************************************

View File

@ -56,4 +56,26 @@ void* GetXboxFunctionPointer(std::string functionName);
void VerifyHLEDataBase();
#endif
// A helper class to allow code patches to detect nested calls (patches called from patches)
class NestedPatchCounter
{
public:
explicit NestedPatchCounter(uint32_t& counter) noexcept
: m_counter(counter), m_level(counter)
{
m_counter++;
}
~NestedPatchCounter() noexcept
{
m_counter--;
}
uint32_t GetLevel() const noexcept { return m_level; }
public:
uint32_t& m_counter;
const uint32_t m_level;
};
#endif