From cfde68d8d540d26b891d7cd79fc2b38c99b72ea2 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Tue, 14 May 2019 08:55:49 +0100 Subject: [PATCH 1/2] Selectively unpatch Fiber functions, fixes Futurama without breaking DOA games --- src/core/hle/Patches.cpp | 46 ++++++++++++++++++++++++--- src/core/kernel/exports/EmuKrnlMm.cpp | 8 ++++- src/core/kernel/support/EmuFS.cpp | 7 +++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index 8002b1fb6..1d3d8d1a3 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -45,6 +45,7 @@ const uint32_t PATCH_ALWAYS = 1 << 0; const uint32_t PATCH_HLE_D3D = 1 << 1; const uint32_t PATCH_HLE_DSOUND = 1 << 2; const uint32_t PATCH_HLE_OHCI = 1 << 3; +const uint32_t PATCH_IS_FIBER = 1 << 4; #define PATCH_ENTRY(Name, Func, Flags) \ { Name, { &Func, Flags} } @@ -369,9 +370,9 @@ std::map g_PatchTable = { PATCH_ENTRY("XInputSetState", XTL::EMUPATCH(XInputSetState), PATCH_HLE_OHCI), // XAPI - PATCH_ENTRY("ConvertThreadToFiber", XTL::EMUPATCH(ConvertThreadToFiber), PATCH_ALWAYS), - PATCH_ENTRY("CreateFiber", XTL::EMUPATCH(CreateFiber), PATCH_ALWAYS), - PATCH_ENTRY("DeleteFiber", XTL::EMUPATCH(DeleteFiber), PATCH_ALWAYS), + PATCH_ENTRY("ConvertThreadToFiber", XTL::EMUPATCH(ConvertThreadToFiber), PATCH_IS_FIBER), + PATCH_ENTRY("CreateFiber", XTL::EMUPATCH(CreateFiber), PATCH_IS_FIBER), + PATCH_ENTRY("DeleteFiber", XTL::EMUPATCH(DeleteFiber), PATCH_IS_FIBER), PATCH_ENTRY("GetExitCodeThread", XTL::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS), PATCH_ENTRY("GetThreadPriority", XTL::EMUPATCH(GetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("OutputDebugStringA", XTL::EMUPATCH(OutputDebugStringA), PATCH_ALWAYS), @@ -379,7 +380,7 @@ std::map g_PatchTable = { PATCH_ENTRY("SetThreadPriority", XTL::EMUPATCH(SetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("SetThreadPriorityBoost", XTL::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS), PATCH_ENTRY("SignalObjectAndWait", XTL::EMUPATCH(SignalObjectAndWait), PATCH_ALWAYS), - PATCH_ENTRY("SwitchToFiber", XTL::EMUPATCH(SwitchToFiber), PATCH_ALWAYS), + PATCH_ENTRY("SwitchToFiber", XTL::EMUPATCH(SwitchToFiber), PATCH_IS_FIBER), PATCH_ENTRY("XMountMUA", XTL::EMUPATCH(XMountMUA), PATCH_ALWAYS), PATCH_ENTRY("XMountMURootA", XTL::EMUPATCH(XMountMURootA), PATCH_ALWAYS), PATCH_ENTRY("XSetProcessQuantumLength", XTL::EMUPATCH(XSetProcessQuantumLength), PATCH_ALWAYS), @@ -389,6 +390,38 @@ std::map g_PatchTable = { std::unordered_map g_FunctionHooks; +inline bool TitleRequiresUnpatchedFibers() +{ + static bool detected = false; + static bool result = false; + + // Prevent running the check every time this function is called + if (detected) { + return result; + } + + // Array of known games that require the fiber unpatch hack + DWORD titleIds[] = { + 0x46490002, // Futurama PAL + 0x56550008, // Futurama NTSC + 0 + }; + + DWORD* pTitleId = &titleIds[0]; + while (*pTitleId != 0) { + if (g_pCertificate->dwTitleId == *pTitleId) { + result = true; + break; + } + + pTitleId++; + } + + detected = true; + return result; +} + + // NOTE: EmuInstallPatch do not get to be in XbSymbolDatabase, do the patches in Cxbx project only. inline void EmuInstallPatch(std::string FunctionName, xbaddr FunctionAddr) { @@ -414,6 +447,11 @@ inline void EmuInstallPatch(std::string FunctionName, xbaddr FunctionAddr) return; } + if ((patch.flags & PATCH_IS_FIBER) && TitleRequiresUnpatchedFibers()) { + printf("HLE: %s: Skipped (Game requires unpatched Fibers)\n", FunctionName.c_str()); + return; + } + g_FunctionHooks[FunctionName].Install((void*)(FunctionAddr), (void*)patch.patchFunc); printf("HLE: %s Patched\n", FunctionName.c_str()); } diff --git a/src/core/kernel/exports/EmuKrnlMm.cpp b/src/core/kernel/exports/EmuKrnlMm.cpp index 26a53b8b2..51e734370 100644 --- a/src/core/kernel/exports/EmuKrnlMm.cpp +++ b/src/core/kernel/exports/EmuKrnlMm.cpp @@ -167,6 +167,10 @@ XBSYSAPI EXPORTNUM(169) xboxkrnl::PVOID NTAPI xboxkrnl::MmCreateKernelStack PVOID addr = (PVOID)g_VMManager.AllocateSystemMemory(DebuggerThread ? DebuggerType : StackType, XBOX_PAGE_READWRITE, NumberOfBytes, true); + // Since this is creating a stack (which counts DOWN) we must return the *end* of the address range, not the start + // Test cases: DOA3, Futurama + addr = (PVOID)((uint32_t)addr + NumberOfBytes + PAGE_SIZE); + RETURN(addr); } @@ -184,8 +188,10 @@ XBSYSAPI EXPORTNUM(170) xboxkrnl::VOID NTAPI xboxkrnl::MmDeleteKernelStack LOG_FUNC_ARG(StackLimit) LOG_FUNC_END; + ULONG actualStackSize = ((VAddr)StackBase - (VAddr)StackLimit) + PAGE_SIZE; + g_VMManager.DeallocateSystemMemory(IS_SYSTEM_ADDRESS(StackBase) ? StackType : DebuggerType, - (VAddr)StackBase, (VAddr)StackBase - (VAddr)StackLimit + PAGE_SIZE); + (VAddr)StackBase - (VAddr)actualStackSize, actualStackSize); } // ****************************************************************** diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index 887019ff4..b78915c46 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -627,7 +627,12 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData) // Fixup the TIB self pointer : NewPcr->NtTib.Self = XbTib; // Set the stack base - TODO : Verify this, doesn't look right? - NewPcr->NtTib.StackBase = pNewTLS; + NewPcr->NtTib.StackBase = pNewTLS; + + // Write the Xbox stack base to the Host, allows ConvertThreadToFiber to work correctly + // Test case: DOA3 + __writefsdword(TIB_StackBase, (DWORD)NewPcr->NtTib.StackBase); + __writefsdword(TIB_StackLimit, (DWORD)NewPcr->NtTib.StackLimit); } // Set flat address of this PCR From 561530a6c16bc29c141aa81e8b797b140cbb2963 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Tue, 14 May 2019 21:15:00 +0100 Subject: [PATCH 2/2] add HACK: note --- src/core/hle/Patches.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index 1d3d8d1a3..b361de58c 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -446,7 +446,10 @@ inline void EmuInstallPatch(std::string FunctionName, xbaddr FunctionAddr) printf("HLE: %s: Skipped (LLE OHCI Enabled)\n", FunctionName.c_str()); return; } - + + // HACK: Some titles require unpatched Fibers, otherwise they enter an infinte loop + // while others require patched Fibers, otherwise they outright crash + // This is caused by limitations of Direct Code Execution and Cxbx-R's threading model if ((patch.flags & PATCH_IS_FIBER) && TitleRequiresUnpatchedFibers()) { printf("HLE: %s: Skipped (Game requires unpatched Fibers)\n", FunctionName.c_str()); return;