Cemu/src/Cafe/GamePatch.cpp

475 lines
19 KiB
C++
Raw Normal View History

2022-08-22 22:21:23 +02:00
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
#include "CafeSystem.h"
void hleExport_breathOfTheWild_busyLoop(PPCInterpreter_t* hCPU)
{
uint32 queue7C = memory_readU32(hCPU->gpr[24] + 0x7C);
uint32 queue80b = memory_readU8(hCPU->gpr[24] + 0x80);
if (!(queue80b == 0 || hCPU->gpr[22] != 0 || queue7C > 0))
{
PPCInterpreter_relinquishTimeslice();
}
hCPU->gpr[6] = hCPU->gpr[29];
hCPU->instructionPointer += 4;
}
void hleExport_breathOfTheWild_busyLoop2(PPCInterpreter_t* hCPU)
{
uint32 queue7C = memory_readU32(hCPU->gpr[24] + 0x7C);
uint32 queue80b = memory_readU8(hCPU->gpr[24] + 0x80);
if (!(queue80b == 0 || hCPU->gpr[22] != 0 || queue7C > 0))
{
PPCInterpreter_relinquishTimeslice();
}
hCPU->gpr[12] = hCPU->gpr[29];
hCPU->instructionPointer += 4;
}
void hleExport_ffl_swapEndianFloatArray(PPCInterpreter_t* hCPU)
{
ppcDefineParamStructPtr(valueArray, uint32, 0);
ppcDefineParamS32(valueCount, 1);
for (sint32 i = 0; i < valueCount; i++)
{
valueArray[i] = _swapEndianU32(valueArray[i]);
}
osLib_returnFromFunction(hCPU, 0);
}
typedef struct
{
std::atomic<uint32be> count;
uint32be ownerThreadId;
uint32 ukn08;
}xcxCS_t;
void hleExport_xcx_enterCriticalSection(PPCInterpreter_t* hCPU)
{
ppcDefineParamStructPtr(xcxCS, xcxCS_t, 0);
uint32 threadId = coreinitThread_getCurrentThreadMPTRDepr(hCPU);
cemu_assert_debug(xcxCS->ukn08 != 0);
cemu_assert_debug(threadId);
if (xcxCS->ownerThreadId == (uint32be)threadId)
{
xcxCS->count.store(xcxCS->count.load() + 1);
osLib_returnFromFunction(hCPU, 0);
return;
}
// quick check
uint32be newCount = xcxCS->count.load() + 1;
uint32be expectedCount = 0;
if(xcxCS->count.compare_exchange_strong(expectedCount, newCount))
{
xcxCS->ownerThreadId = threadId;
osLib_returnFromFunction(hCPU, 0);
return;
}
// spinloop for a bit to reduce the time we occupy the scheduler lock (via PPCCore_switchToScheduler)
while (true)
{
for (sint32 i = 0; i < 50; i++)
{
if (xcxCS->count.compare_exchange_strong(expectedCount, newCount))
{
xcxCS->ownerThreadId = threadId;
osLib_returnFromFunction(hCPU, 0);
return;
}
_mm_pause();
}
PPCCore_switchToScheduler();
}
osLib_returnFromFunction(hCPU, 0);
}
bool mh3u_raceConditionWorkaround = true;
void hleExport_mh3u_raceConditionWorkaround(PPCInterpreter_t* hCPU) // new style HLE method, does not need entry in hle_load (but can only be reached via BL and not the usual HLE instruction)
{
uint8 b = memory_readU8(hCPU->gpr[3] + 0x3E5);
b ^= 1;
if (mh3u_raceConditionWorkaround)
{
b = 0;
mh3u_raceConditionWorkaround = false;
}
osLib_returnFromFunction(hCPU, b);
}
void hleExport_pmcs_yellowPaintStarCrashWorkaround(PPCInterpreter_t* hCPU)
{
hCPU->gpr[7] = hCPU->gpr[3] * 4;
MPTR parentLR = memory_readU32(hCPU->gpr[1] + 0x4C);
if (hCPU->gpr[3] >= 0x00800000)
{
hCPU->instructionPointer = parentLR;
hCPU->gpr[1] += 0x48;
hCPU->gpr[3] = 0;
return;
}
hCPU->instructionPointer = hCPU->spr.LR;
}
uint8 hleSignature_wwhd_0173B2A0[] = {0x8D,0x43,0x00,0x01,0x7C,0xC9,0x52,0x78,0x55,0x2C,0x15,0xBA,0x7C,0x0C,0x28,0x2E,0x54,0xC8,0xC2,0x3E,0x7D,0x06,0x02,0x78,0x42,0x00,0xFF,0xE8,0x7C,0xC3,0x30,0xF8};
void hle_scan(uint8* data, sint32 dataLength, char* hleFunctionName)
{
sint32 functionIndex = osLib_getFunctionIndex("hle", hleFunctionName);
if( functionIndex < 0 )
{
debug_printf("HLE function unknown\n");
return;
}
uint8* scanStart = memory_getPointerFromVirtualOffset(0x01000000);
uint8* scanEnd = scanStart + 0x0F000000 - dataLength;
uint8* scanCurrent = scanStart;
while( scanCurrent < scanEnd )
{
if( memcmp(scanCurrent, data, dataLength) == 0 )
{
uint32 offset = (uint32)(scanCurrent - scanStart) + 0x01000000;
debug_printf("HLE signature for '%s' found at 0x%08x\n", hleFunctionName, offset);
uint32 opcode = (1<<26)|(functionIndex+0x1000); // opcode for HLE: 0x1000 + FunctionIndex
memory_writeU32Direct(offset, opcode);
break;
}
scanCurrent += 4;
}
}
MPTR hle_locate(uint8* data, sint32 dataLength)
{
uint8* scanStart = memory_getPointerFromVirtualOffset(0x01000000);
uint8* scanEnd = scanStart + 0x0F000000 - dataLength;
uint8* scanCurrent = scanStart;
while( scanCurrent < scanEnd )
{
if( memcmp(scanCurrent, data, dataLength) == 0 )
{
return memory_getVirtualOffsetFromPointer(scanCurrent);
}
scanCurrent += 4;
}
return MPTR_NULL;
}
bool compareMasked(uint8* mem, uint8* compare, uint8* mask, sint32 length)
{
while( length )
{
uint8 m = *mask;
if( (*mem&m) != (*compare&m) )
return false;
mem++;
compare++;
mask++;
length--;
}
return true;
}
MPTR hle_locate(uint8* data, uint8* mask, sint32 dataLength)
{
uint8* scanStart = memory_getPointerFromVirtualOffset(MEMORY_CODEAREA_ADDR);
uint8* scanEnd = memory_getPointerFromVirtualOffset(RPLLoader_GetMaxCodeOffset() - dataLength);
uint8* scanCurrent = scanStart;
if( mask )
{
if (dataLength >= 4 && *(uint32*)mask == 0xFFFFFFFF)
{
// fast path
uint32 firstDword = *(uint32*)data;
while (scanCurrent < scanEnd)
{
if (*(uint32*)scanCurrent == firstDword && compareMasked(scanCurrent, data, mask, dataLength))
{
return memory_getVirtualOffsetFromPointer(scanCurrent);
}
scanCurrent += 4;
}
}
else
{
#ifdef CEMU_DEBUG_ASSERT
2022-08-22 22:21:23 +02:00
if (mask[0] != 0xFF)
assert_dbg();
#endif
uint8 firstByte = data[0];
while (scanCurrent < scanEnd)
{
if (scanCurrent[0] == firstByte && compareMasked(scanCurrent, data, mask, dataLength))
{
return memory_getVirtualOffsetFromPointer(scanCurrent);
}
scanCurrent += 4;
}
}
}
else
{
while( scanCurrent < scanEnd )
{
if( memcmp(scanCurrent, data, dataLength) == 0 )
{
return memory_getVirtualOffsetFromPointer(scanCurrent);
}
scanCurrent += 4;
}
}
return MPTR_NULL;
}
uint8 xcx_gpuHangDetection_degradeFramebuffer[] = {0x3B,0x39,0x00,0x01,0x28,0x19,0x4E,0x20,0x40,0x81,0x00,0x44};
uint8 xcx_framebufferReductionSignature[] = {0x80,0xC9,0x00,0x1C,0x38,0xA0,0x00,0x01,0x80,0x7E,0x00,0x80,0x80,0x9E,0x02,0xEC,0x80,0xE9,0x00,0x20,0x48,0x06,0x1E,0xD1,0x7E,0x73,0x1B,0x78};
uint8 xcx_framebufferReductionMask[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF};
uint8 botw_busyLoopSignature[] = {0x80,0xE6,0x00,0x00,0x2C,0x07,0x00,0x01,0x41,0x82,0xFF,0xF8,0x7D,0x00,0x30,0x28,0x2C,0x08,0x00,0x00,0x40,0x82,0xFF,0xF8,0x7C,0x00,0x30,0x6C,0x39,0x00,0x00,0x01,0x7D,0x00,0x31,0x2D,0x40,0x82,0xFF,0xE8 };
uint8 botw_busyLoopMask[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
uint8 botw_busyLoopSignature2[] = {0x80,0x0C,0x00,0x00,0x2C,0x00,0x00,0x01,0x41,0x82,0xFF,0xF8,0x7C,0xA0,0x60,0x28,0x2C,0x05,0x00,0x00,0x40,0x82,0xFF,0xF8,0x7C,0x00,0x60,0x6C,0x38,0x80,0x00,0x01,0x7C,0x80,0x61,0x2D,0x40,0x82,0xFF,0xE8};
uint8 botw_busyLoopMask2[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
uint8 botw_crashFuncSignature[] = { 0x94,0x21,0xFF,0xD8,0x7C,0x08,0x02,0xA6,0xBF,0x41,0x00,0x10,0x7C,0xC7,0x33,0x78,0x7C,0xBE,0x2B,0x78,0x90,0x01,0x00,0x2C,0x7C,0x9D,0x23,0x78,0x38,0x00,0x00,0x00,0x7F,0xC6,0xF3,0x78,0x38,0x81,0x00,0x0C,0x90,0x01,0x00,0x0C,0x38,0xA1,0x00,0x08 };
uint8 botw_crashFuncMask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
uint8 ffl_floatArrayEndianSwap[] = { 0x7C,0x08,0x02,0xA6,0x94,0x21,0xFF,0xE8,0x93,0xC1,0x00,0x10,0x7C,0x7E,0x1B,0x78,0x93,0xE1,0x00,0x14,0x93,0x81,0x00,0x08,0x7C,0x9F,0x23,0x78,0x93,0xA1,0x00,0x0C,0x90,0x01,0x00,0x1C,0x3B,0xA0,0x00,0x00,0x7C,0x1D,0xF8,0x40,0x40,0x80,0x00,0x20,0x57,0xBC,0x10,0x3A,0x7C,0x3E,0xE4,0x2E };
uint8 xcx_enterCriticalSectionSignature[] = { 0x94,0x21,0xFF,0xE0,0xBF,0x41,0x00,0x08,0x7C,0x08,0x02,0xA6,0x90,0x01,0x00,0x24,0x7C,0x7E,0x1B,0x78,0x80,0x1E,0x00,0x08,0x2C,0x00,0x00,0x00,0x41,0x82,0x00,0xC0,0x48,0x01,0xD7,0xA1,0x7C,0x7A,0x1B,0x79,0x41,0x82,0x00,0xB4,0x81,0x3E,0x00,0x04,0x7C,0x09,0xD0,0x40,0x40,0x82,0x00,0x2C,0x7D,0x20,0xF0,0x28,0x7C,0x00,0xF0,0x6C };
uint8 xcx_enterCriticalSectionMask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
uint8 smash4_softlockFixV0Signature[] = { 0x2C,0x03,0x00,0x00,0x41,0x82,0x00,0x20,0x38,0x60,0x00,0x0A,0x48,0x33,0xB8,0xAD,0x7F,0xA3,0xEB,0x78,0x7F,0xC4,0xF3,0x78,0x4B,0xFF,0xFF,0x09,0x2C,0x03,0x00,0x00 };
uint8 smash4_softlockFixV0Mask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF };
uint8 mh3u_raceConditionWorkaroundV0Signature[] = { 0x38,0x21,0x00,0x40,0x38,0x60,0x00,0x00,0x4E,0x80,0x00,0x20,0x80,0x7B,0xDB,0x9C,0x48,0x11,0x6B,0x81,0x2C,0x03,0x00,0x00 };
uint8 mh3u_raceConditionWorkaroundV0Mask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF };
uint8 pmcs_yellowPaintStarCrashV0Signature[] = { 0x94,0x21,0xFF,0xB8,0xBE,0x61,0x00,0x14,0x7C,0x08,0x02,0xA6,0x7C,0xB3,0x2B,0x78,0x90,0x01,0x00,0x4C,0x7C,0x9D,0x23,0x78,0x83,0x3D,0x00,0x0C,0x81,0x39,0x04,0xA8,0x54,0x67,0x10,0x3A,0x7F,0xC7,0x48,0x2E,0x83,0x1E,0x00,0xDC,0x82,0xF8,0x00,0x08,0x2C,0x17,0x00,0x00 };
//uint8 mh3u_raceConditionWorkaroundV0Mask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF };
uint8 bayo2_audioQueueFixSignature[] = { 0x80,0x03,0x00,0x3C,0x81,0x43,0x00,0x5C,0x81,0x83,0x00,0x40,0x55,0x48,0xB2,0xBE,0x3D,0x40,0x10,0x1D,0x7D,0x6C,0x42,0x14,0x39,0x4A,0x46,0xF0,0x7D,0x8B,0x00,0x50 };
uint8 bayo2_audioQueueFixMask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF };
uint8 sm3dw_dynFrameBufferResScale[] = { 0x94,0x21,0xFF,0xB8,0xBF,0x21,0x00,0x2C,0x7C,0x08,0x02,0xA6,0x90,0x01,0x00,0x4C,0x7C,0x7E,0x1B,0x78,0x81,0x7E,0x07,0xD8,0x38,0x80,0x00,0x02,0x38,0x6B,0x00,0x03 };
uint8 tww_waitFunc[] = { 0x7C,0x08,0x02,0xA6,0x94,0x21,0xFF,0xF0,0x93,0xE1,0x00,0x0C,0x7C,0x7F,0x1B,0x78,0x90,0x01,0x00,0x14,0x80,0x7F,0x02,0xE0,0x81,0x83,0x00,0x0C,0x80,0x0C,0x00,0x1C,0x7C,0x09,0x03,0xA6,0x38,0xA0,0x00,0x00,0x38,0x9F,0x03,0x68 };
static_assert(sizeof(xcx_enterCriticalSectionSignature) == sizeof(xcx_enterCriticalSectionMask), "xcx_enterCriticalSection signature and size mismatch");
static_assert(sizeof(bayo2_audioQueueFixSignature) == sizeof(bayo2_audioQueueFixMask), "bayo2_audioQueueFix signature and size mismatch");
uint8 cars3_avro_schema_incref[] = { 0x2C,0x03,0x00,0x00,0x94,0x21,0xFF,0xE8,0x41,0x82,0x00,0x40,0x39,0x03,0x00,0x08,0x39,0x41,0x00,0x08,0x91,0x01,0x00,0x08,0x7D,0x80,0x50,0x28,0x2C,0x0C,0xFF,0xFF,0x41,0x82,0x00,0x28,0x39,0x21,0x00,0x0C,0x38,0x0C,0x00,0x01,0x38,0xE0,0x00,0x01,0x91,0x01,0x00,0x0C,0x7C,0x00,0x49,0x2D };
sint32 hleIndex_h000000001 = -1;
sint32 hleIndex_h000000002 = -1;
sint32 hleIndex_h000000003 = -1;
sint32 hleIndex_h000000004 = -1;
/*
* Returns true for all HLE functions that do not jump to LR
* Used by recompiler to determine function code flow
*/
bool GamePatch_IsNonReturnFunction(uint32 hleIndex)
{
if (hleIndex == hleIndex_h000000001)
return true;
if (hleIndex == hleIndex_h000000002)
return true;
if (hleIndex == hleIndex_h000000003)
return false;
if (hleIndex == hleIndex_h000000004)
return false;
return false;
}
void GamePatch_scan()
{
MPTR hleAddr;
uint32 hleInstallStart = GetTickCount();
hleAddr = hle_locate(xcx_gpuHangDetection_degradeFramebuffer, NULL, sizeof(xcx_gpuHangDetection_degradeFramebuffer));
if( hleAddr )
{
#ifdef CEMU_DEBUG_ASSERT
2022-08-22 22:21:23 +02:00
forceLog_printf("HLE: XCX GPU hang detection");
#endif
// remove the ADDI r25, r25, 1 instruction
memory_writeU32(hleAddr, memory_readU32(hleAddr+4));
}
hleAddr = hle_locate(xcx_framebufferReductionSignature, xcx_framebufferReductionMask, sizeof(xcx_framebufferReductionSignature));
if( hleAddr )
{
#ifdef CEMU_DEBUG_ASSERT
2022-08-22 22:21:23 +02:00
forceLog_printf("HLE: Prevent XCX rendertarget reduction");
#endif
uint32 bl = memory_readU32(hleAddr+0x14);
uint32 func_isReductionBuffer = hleAddr + 0x14 + (bl&0x3FFFFFC);
// patch isReductionBuffer
memory_writeU32(func_isReductionBuffer, 0x38600000); // LI R3, 0
memory_writeU32(func_isReductionBuffer+4, 0x4E800020); // BLR
}
hleIndex_h000000001 = osLib_getFunctionIndex("hle", "h000000001");
hleAddr = hle_locate(botw_busyLoopSignature, botw_busyLoopMask, sizeof(botw_busyLoopSignature));
if (hleAddr)
{
#ifdef CEMU_DEBUG_ASSERT
2022-08-22 22:21:23 +02:00
forceLog_printf("HLE: Patch BotW busy loop 1 at 0x%08x", hleAddr);
#endif
sint32 functionIndex = hleIndex_h000000001;
uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
memory_writeU32Direct(hleAddr - 4, opcode);
}
hleIndex_h000000002 = osLib_getFunctionIndex("hle", "h000000002");
hleAddr = hle_locate(botw_busyLoopSignature2, botw_busyLoopMask2, sizeof(botw_busyLoopSignature2));
if (hleAddr)
{
#ifdef CEMU_DEBUG_ASSERT
2022-08-22 22:21:23 +02:00
forceLog_printf("HLE: Patch BotW busy loop 2 at 0x%08x", hleAddr);
#endif
sint32 functionIndex = hleIndex_h000000002;
uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
memory_writeU32Direct(hleAddr - 4, opcode);
}
// FFL library float array endian conversion
// original function needs invalid float values to remain intact between LFSX -> STFSX, which is not supported in recompiler mode
hleIndex_h000000003 = osLib_getFunctionIndex("hle", "h000000003");
hleAddr = hle_locate(ffl_floatArrayEndianSwap, NULL, sizeof(ffl_floatArrayEndianSwap));
if (hleAddr)
{
forceLogDebug_printf("HLE: Hook FFL float array endian swap function at 0x%08x", hleAddr);
sint32 functionIndex = hleIndex_h000000003;
uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
memory_writeU32Direct(hleAddr, opcode);
}
// XCX freeze workaround
//hleAddr = hle_locate(xcx_enterCriticalSectionSignature, xcx_enterCriticalSectionMask, sizeof(xcx_enterCriticalSectionSignature));
//if (hleAddr)
//{
// forceLogDebug_printf("HLE: Hook XCX enterCriticalSection function at 0x%08x", hleAddr);
// hleIndex_h000000004 = osLib_getFunctionIndex("hle", "h000000004");
// sint32 functionIndex = hleIndex_h000000004;
// uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex
// memory_writeU32Direct(hleAddr, opcode);
//}
// MH3U race condition (tested for EU+US 1.2)
hleAddr = hle_locate(mh3u_raceConditionWorkaroundV0Signature, mh3u_raceConditionWorkaroundV0Mask, sizeof(mh3u_raceConditionWorkaroundV0Mask));
if (hleAddr)
{
uint32 patchAddr = hleAddr + 0x10;
forceLog_printf("HLE: Patch MH3U race condition candidate at 0x%08x", patchAddr);
uint32 funcAddr = PPCInterpreter_makeCallableExportDepr(hleExport_mh3u_raceConditionWorkaround);
// set absolute jump
uint32 opc = 0x48000000;
opc |= PPC_OPC_LK;
opc |= PPC_OPC_AA;
opc |= funcAddr;
memory_writeU32(patchAddr, opc);
}
// Super Smash Bros softlock fix
// fixes random softlocks that can occur after matches
hleAddr = hle_locate(smash4_softlockFixV0Signature, smash4_softlockFixV0Mask, sizeof(smash4_softlockFixV0Signature));
if (hleAddr)
{
forceLogDebug_printf("Smash softlock fix: 0x%08x", hleAddr);
memory_writeU32(hleAddr+0x20, memory_readU32(hleAddr+0x1C));
}
// Color Splash Yellow paint star crash workaround
// fixes the crash at the beginning of the dream sequence cutscene after collecting the yellow paint star
hleAddr = hle_locate(pmcs_yellowPaintStarCrashV0Signature, nullptr, sizeof(pmcs_yellowPaintStarCrashV0Signature));
if (hleAddr)
{
forceLogDebug_printf("Color Splash crash fix: 0x%08x", hleAddr);
uint32 funcAddr = PPCInterpreter_makeCallableExportDepr(hleExport_pmcs_yellowPaintStarCrashWorkaround);
// set absolute jump
uint32 opc = 0x48000000;
opc |= PPC_OPC_LK;
opc |= PPC_OPC_AA;
opc |= funcAddr;
memory_writeU32(hleAddr+0x20, opc);
}
// Bayonetta 2 sound queue patch (fixes audio starting to loop infinitely when there is stutter)
hleAddr = hle_locate(bayo2_audioQueueFixSignature, bayo2_audioQueueFixMask, sizeof(bayo2_audioQueueFixSignature));
if (hleAddr)
{
// replace CMPL with CMP
forceLog_printf("Patching Bayonetta 2 audio bug at: 0x%08x", hleAddr+0x34);
uint32 opc = memory_readU32(hleAddr + 0x34);
opc &= ~(0x3FF << 1); // turn CMPL to CMP
memory_writeU32(hleAddr + 0x34, opc);
}
if (CafeSystem::GetRPXHashUpdated() == 0xb1c033dd) // Wind Waker US
{
uint32 p = memory_readU32(0x02813878);
if (p == 0x40800018)
{
debug_printf("HLE: TWW US dsp kill channel patch\n");
uint32 li = 0x18;
uint32 opcode = (li & 0x3FFFFFC) | (18 << 26); // replace BGE with B instruction
memory_writeU32(0x02813878, opcode);
}
}
else if (CafeSystem::GetRPXHashUpdated() == 0xCDC68ACD) // Wind Waker EU
{
uint32 p = memory_readU32(0x2814138);
if (p == 0x40800018)
{
debug_printf("HLE: TWW EU dsp kill channel patch\n");
uint32 li = 0x18;
uint32 opcode = (li & 0x3FFFFFC) | (18 << 26); // replace BGE with B instruction
memory_writeU32(0x02814138, opcode);
}
}
// disable SM3DW dynamic resolution scaling (fixes level 1-5 spamming lots of texture creates when gradually resizing framebuffer)
hleAddr = hle_locate(sm3dw_dynFrameBufferResScale, nullptr, sizeof(sm3dw_dynFrameBufferResScale));
if (hleAddr)
{
forceLog_printf("Patching SM3DW dynamic resolution scaling at: 0x%08x", hleAddr);
memory_writeU32(hleAddr, 0x4E800020); // BLR
}
// remove unnecessary lock from a wait function in TWW
// this resolves a deadlock in singlecore mode
hleAddr = hle_locate(tww_waitFunc, nullptr, sizeof(tww_waitFunc));
if (hleAddr)
{
forceLog_printf("Patching TWW race conditon at: 0x%08x", hleAddr);
// NOP calls to Lock/Unlock mutex
memory_writeU32(hleAddr + 0x34, 0x60000000);
memory_writeU32(hleAddr + 0x48, 0x60000000);
memory_writeU32(hleAddr + 0x50, 0x60000000);
memory_writeU32(hleAddr + 0x64, 0x60000000);
}
uint32 hleInstallEnd = GetTickCount();
forceLog_printf("HLE scan time: %dms", hleInstallEnd-hleInstallStart);
}
RunAtCemuBoot _loadGamePatchAPI([]()
{
osLib_addFunction("hle", "h000000001", hleExport_breathOfTheWild_busyLoop);
osLib_addFunction("hle", "h000000002", hleExport_breathOfTheWild_busyLoop2);
osLib_addFunction("hle", "h000000003", hleExport_ffl_swapEndianFloatArray);
osLib_addFunction("hle", "h000000004", hleExport_xcx_enterCriticalSection);
});