coreinit: Improve accuracy of OSSwitchCoroutine

Fixes Injustice: Gods Among Us crashing during boot.
This commit is contained in:
Exzap 2024-03-27 14:44:51 +01:00
parent 5230fcab37
commit b0b2c25762

View File

@ -3,9 +3,13 @@
#include "Cafe/HW/Espresso/PPCState.h" #include "Cafe/HW/Espresso/PPCState.h"
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" #include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
#include "Cafe/HW/MMU/MMU.h" #include "Cafe/HW/MMU/MMU.h"
#include "Cafe/OS/RPL/rpl.h"
namespace coreinit namespace coreinit
{ {
static_assert(sizeof(OSCoroutine) == 0x180);
static uint32 s_PPCAddrOSSwitchCoroutineAfterOSLoadCoroutine = 0;
void coreinitExport_OSInitCoroutine(PPCInterpreter_t* hCPU) void coreinitExport_OSInitCoroutine(PPCInterpreter_t* hCPU)
{ {
@ -57,14 +61,30 @@ namespace coreinit
void coreinitExport_OSSwitchCoroutine(PPCInterpreter_t* hCPU) void coreinitExport_OSSwitchCoroutine(PPCInterpreter_t* hCPU)
{ {
OSCoroutine* coroutineCurrent = (OSCoroutine*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]); // OSSwitchCoroutine is a wrapper for OSSaveCoroutine + OSLoadCoroutine but it has side effects that we need to care about:
OSCoroutine* coroutineNext = (OSCoroutine*)memory_getPointerFromVirtualOffsetAllowNull(hCPU->gpr[4]); // r31 is saved and restored via the stack in OSSwitchCoroutine
// r4 is stored in the r31 field of coroutineCurrent. Injustice: Gods Among Us reads the r31 field and expects it to match coroutineCurrent (0x027183D4 @ EU v16)
OSCoroutine* coroutineCurrent = MEMPTR<OSCoroutine>(hCPU->gpr[3]);
OSCoroutine* coroutineNext = MEMPTR<OSCoroutine>(hCPU->gpr[4]);
hCPU->gpr[1] -= 0x10;
memory_writeU32(hCPU->gpr[1]+0xC, hCPU->gpr[31]);
memory_writeU32(hCPU->gpr[1]+0x14, hCPU->spr.LR);
hCPU->spr.LR = s_PPCAddrOSSwitchCoroutineAfterOSLoadCoroutine;
hCPU->gpr[31] = hCPU->gpr[4];
coreinitCoroutine_OSSaveCoroutine(coroutineCurrent, hCPU); coreinitCoroutine_OSSaveCoroutine(coroutineCurrent, hCPU);
if (coroutineNext != NULL) hCPU->gpr[3] = hCPU->gpr[31];
{ hCPU->gpr[4] = 1;
coreinitCoroutine_OSLoadCoroutine(coroutineNext, hCPU); coreinitCoroutine_OSLoadCoroutine(coroutineNext, hCPU);
hCPU->instructionPointer = hCPU->spr.LR;
} }
osLib_returnFromFunction(hCPU, 0);
void coreinitExport_OSSwitchCoroutineAfterOSLoadCoroutine(PPCInterpreter_t* hCPU)
{
// resuming after OSSaveCoroutine
hCPU->gpr[31] = memory_readU32(hCPU->gpr[1]+0xC);
hCPU->spr.LR = memory_readU32(hCPU->gpr[1]+0x14);
hCPU->gpr[1] += 0x10;
hCPU->instructionPointer = hCPU->spr.LR;
} }
void coreinitExport_OSSwitchFiberEx(PPCInterpreter_t* hCPU) void coreinitExport_OSSwitchFiberEx(PPCInterpreter_t* hCPU)
@ -96,5 +116,7 @@ namespace coreinit
osLib_addFunction("coreinit", "OSInitCoroutine", coreinitExport_OSInitCoroutine); osLib_addFunction("coreinit", "OSInitCoroutine", coreinitExport_OSInitCoroutine);
osLib_addFunction("coreinit", "OSSwitchCoroutine", coreinitExport_OSSwitchCoroutine); osLib_addFunction("coreinit", "OSSwitchCoroutine", coreinitExport_OSSwitchCoroutine);
osLib_addFunction("coreinit", "OSSwitchFiberEx", coreinitExport_OSSwitchFiberEx); osLib_addFunction("coreinit", "OSSwitchFiberEx", coreinitExport_OSSwitchFiberEx);
s_PPCAddrOSSwitchCoroutineAfterOSLoadCoroutine = RPLLoader_MakePPCCallable(coreinitExport_OSSwitchCoroutineAfterOSLoadCoroutine);
} }
} }