From 1c73dc9e1b824f4618e60704b2c1e6682b749ee0 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 30 Apr 2024 23:09:00 +0200 Subject: [PATCH] Implement proc_ui.rpl + stub SYSSwitchToEManual() to avoid softlocks - Full reimplementation of proc_ui.rpl with all 19 exports - Foreground/Background messages now go to the coreinit system message queue as they should (instead of using a hack where proc_ui receives them directly) - Add missing coreinit API needed by proc_ui: OSGetPFID(), OSGetUPID(), OSGetTitleID(), __OSCreateThreadType() - Use big-endian types in OSMessage - Flesh out the stubs for OSDriver_Register and OSDriver_Unregister a bit more since we need to call it from proc_ui. Similiar small tweaks to other coreinit API - Stub sysapp SYSSwitchToEManual() and _SYSSwitchToEManual() in such a way that they will trigger the expected background/foreground transition, avoiding softlocks in games that call these functions --- .gitignore | 1 + src/Cafe/OS/common/OSCommon.cpp | 2 +- src/Cafe/OS/libs/coreinit/coreinit.cpp | 22 - src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h | 6 + src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 2 +- src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp | 2 +- src/Cafe/OS/libs/coreinit/coreinit_Memory.h | 5 + .../libs/coreinit/coreinit_MessageQueue.cpp | 7 + .../OS/libs/coreinit/coreinit_MessageQueue.h | 19 +- src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp | 96 ++ src/Cafe/OS/libs/coreinit/coreinit_Misc.h | 20 + src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 19 +- src/Cafe/OS/libs/coreinit/coreinit_Thread.h | 8 +- src/Cafe/OS/libs/coreinit/coreinit_Time.cpp | 7 +- src/Cafe/OS/libs/coreinit/coreinit_Time.h | 3 +- src/Cafe/OS/libs/gx2/GX2_Misc.h | 2 + src/Cafe/OS/libs/nn_olv/nn_olv.cpp | 3 +- src/Cafe/OS/libs/proc_ui/proc_ui.cpp | 944 +++++++++++++++++- src/Cafe/OS/libs/proc_ui/proc_ui.h | 45 +- src/Cafe/OS/libs/sysapp/sysapp.cpp | 23 + src/Common/CafeString.h | 5 + 21 files changed, 1146 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index c10b38da..67a268aa 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ bin/sdcard/* bin/screenshots/* bin/dump/* bin/cafeLibs/* +bin/portable/* bin/keys.txt !bin/shaderCache/info.txt diff --git a/src/Cafe/OS/common/OSCommon.cpp b/src/Cafe/OS/common/OSCommon.cpp index a4410028..5297f201 100644 --- a/src/Cafe/OS/common/OSCommon.cpp +++ b/src/Cafe/OS/common/OSCommon.cpp @@ -221,5 +221,5 @@ void osLib_load() nsyskbd::nsyskbd_load(); swkbd::load(); camera::load(); - procui_load(); + proc_ui::load(); } diff --git a/src/Cafe/OS/libs/coreinit/coreinit.cpp b/src/Cafe/OS/libs/coreinit/coreinit.cpp index 660f874f..e18d0e8d 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit.cpp @@ -179,27 +179,6 @@ void coreinitExport_OSGetSharedData(PPCInterpreter_t* hCPU) osLib_returnFromFunction(hCPU, 1); } -typedef struct -{ - MPTR getDriverName; - MPTR ukn04; - MPTR onAcquiredForeground; - MPTR onReleaseForeground; - MPTR ukn10; -}OSDriverCallbacks_t; - -void coreinitExport_OSDriver_Register(PPCInterpreter_t* hCPU) -{ -#ifdef CEMU_DEBUG_ASSERT - cemuLog_log(LogType::Force, "OSDriver_Register(0x{:08x},0x{:08x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8]); -#endif - OSDriverCallbacks_t* driverCallbacks = (OSDriverCallbacks_t*)memory_getPointerFromVirtualOffset(hCPU->gpr[5]); - - // todo - - osLib_returnFromFunction(hCPU, 0); -} - namespace coreinit { sint32 OSGetCoreId() @@ -379,7 +358,6 @@ void coreinit_load() coreinit::miscInit(); osLib_addFunction("coreinit", "OSGetSharedData", coreinitExport_OSGetSharedData); osLib_addFunction("coreinit", "UCReadSysConfig", coreinitExport_UCReadSysConfig); - osLib_addFunction("coreinit", "OSDriver_Register", coreinitExport_OSDriver_Register); // async callbacks InitializeAsyncCallback(); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h index 0be8226c..2a3172c7 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h @@ -2,6 +2,12 @@ namespace coreinit { + enum class RplEntryReason + { + Loaded = 1, + Unloaded = 2, + }; + uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc); void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc); uint32 OSDynLoad_GetAllocator(betype* funcAlloc, betype* funcFree); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index a007f5ee..0ca8fb8e 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -837,7 +837,7 @@ namespace coreinit FSAsyncResult* FSGetAsyncResult(OSMessage* msg) { - return (FSAsyncResult*)memory_getPointerFromVirtualOffset(_swapEndianU32(msg->message)); + return (FSAsyncResult*)memory_getPointerFromVirtualOffset(msg->message); } sint32 __FSProcessAsyncResult(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, sint32 fsStatus, uint32 errHandling) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp index 4b147473..cff4ee2b 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp @@ -126,7 +126,7 @@ namespace coreinit return physicalAddr; } - void OSMemoryBarrier(PPCInterpreter_t* hCPU) + void OSMemoryBarrier() { // no-op } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Memory.h b/src/Cafe/OS/libs/coreinit/coreinit_Memory.h index cfb3ed06..0a212f61 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Memory.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Memory.h @@ -5,4 +5,9 @@ namespace coreinit void InitializeMemory(); void OSGetMemBound(sint32 memType, MPTR* offsetOutput, uint32* sizeOutput); + + void* OSBlockMove(MEMPTR dst, MEMPTR src, uint32 size, bool flushDC); + void* OSBlockSet(MEMPTR dst, uint32 value, uint32 size); + + void OSMemoryBarrier(); } \ No newline at end of file diff --git a/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.cpp b/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.cpp index 6e6a7bc1..cbcfa4d1 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.cpp @@ -3,6 +3,8 @@ namespace coreinit { + void UpdateSystemMessageQueue(); + void HandleReceivedSystemMessage(OSMessage* msg); SysAllocator g_systemMessageQueue; SysAllocator _systemMessageQueueArray; @@ -27,6 +29,9 @@ namespace coreinit bool OSReceiveMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags) { + bool isSystemMessageQueue = (msgQueue == g_systemMessageQueue); + if(isSystemMessageQueue) + UpdateSystemMessageQueue(); __OSLockScheduler(msgQueue); while (msgQueue->usedCount == (uint32be)0) { @@ -50,6 +55,8 @@ namespace coreinit if (!msgQueue->threadQueueSend.isEmpty()) msgQueue->threadQueueSend.wakeupSingleThreadWaitQueue(true); __OSUnlockScheduler(msgQueue); + if(isSystemMessageQueue) + HandleReceivedSystemMessage(msg); return true; } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.h b/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.h index 6741ab84..35fdc3e7 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.h @@ -3,12 +3,21 @@ namespace coreinit { + enum class SysMessageId : uint32 + { + MsgAcquireForeground = 0xFACEF000, + MsgReleaseForeground = 0xFACEBACC, + MsgExit = 0xD1E0D1E0, + HomeButtonDenied = 0xCCC0FFEE, + NetIoStartOrStop = 0xAAC0FFEE, + }; + struct OSMessage { - MPTR message; - uint32 data0; - uint32 data1; - uint32 data2; + uint32be message; + uint32be data0; + uint32be data1; + uint32be data2; }; struct OSMessageQueue @@ -36,5 +45,7 @@ namespace coreinit bool OSPeekMessage(OSMessageQueue* msgQueue, OSMessage* msg); sint32 OSSendMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags); + OSMessageQueue* OSGetSystemMessageQueue(); + void InitializeMessageQueue(); }; \ No newline at end of file diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp index 2d7468cf..e2b50661 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp @@ -1,5 +1,6 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/OS/libs/coreinit/coreinit_Misc.h" +#include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h" #include "Cafe/CafeSystem.h" #include "Cafe/Filesystem/fsc.h" #include @@ -371,6 +372,23 @@ namespace coreinit return true; } + uint32 OSGetPFID() + { + return 15; // hardcoded as game + } + + uint32 OSGetUPID() + { + return OSGetPFID(); + } + + uint64 s_currentTitleId; + + uint64 OSGetTitleID() + { + return s_currentTitleId; + } + uint32 s_sdkVersion; uint32 __OSGetProcessSDKVersion() @@ -470,9 +488,78 @@ namespace coreinit return 0; } + void OSReleaseForeground() + { + cemuLog_logDebug(LogType::Force, "OSReleaseForeground not implemented"); + } + + bool s_transitionToBackground = false; + bool s_transitionToForeground = false; + + void StartBackgroundForegroundTransition() + { + s_transitionToBackground = true; + s_transitionToForeground = true; + } + + // called at the beginning of OSReceiveMessage if the queue is the system message queue + void UpdateSystemMessageQueue() + { + if(!OSIsInterruptEnabled()) + return; + cemu_assert_debug(!__OSHasSchedulerLock()); + // normally syscall 0x2E is used to get the next message + // for now we just have some preliminary logic here to allow a fake transition to background & foreground + if(s_transitionToBackground) + { + // add transition to background message + OSMessage msg{}; + msg.data0 = stdx::to_underlying(SysMessageId::MsgReleaseForeground); + msg.data1 = 0; // 1 -> System is shutting down 0 -> Begin transitioning to background + OSMessageQueue* systemMessageQueue = coreinit::OSGetSystemMessageQueue(); + if(OSSendMessage(systemMessageQueue, &msg, 0)) + s_transitionToBackground = false; + return; + } + if(s_transitionToForeground) + { + // add transition to foreground message + OSMessage msg{}; + msg.data0 = stdx::to_underlying(SysMessageId::MsgAcquireForeground); + msg.data1 = 1; // ? + msg.data2 = 1; // ? + OSMessageQueue* systemMessageQueue = coreinit::OSGetSystemMessageQueue(); + if(OSSendMessage(systemMessageQueue, &msg, 0)) + s_transitionToForeground = false; + return; + } + } + + // called when OSReceiveMessage returns a message from the system message queue + void HandleReceivedSystemMessage(OSMessage* msg) + { + cemu_assert_debug(!__OSHasSchedulerLock()); + cemuLog_log(LogType::Force, "Receiving message: {:08x}", (uint32)msg->data0); + } + + uint32 OSDriver_Register(uint32 moduleHandle, sint32 priority, OSDriverInterface* driverCallbacks, sint32 driverId, uint32be* outUkn1, uint32be* outUkn2, uint32be* outUkn3) + { + cemuLog_logDebug(LogType::Force, "OSDriver_Register stubbed"); + return 0; + } + + uint32 OSDriver_Deregister(uint32 moduleHandle, sint32 driverId) + { + cemuLog_logDebug(LogType::Force, "OSDriver_Deregister stubbed"); + return 0; + } + void miscInit() { + s_currentTitleId = CafeSystem::GetForegroundTitleId(); s_sdkVersion = CafeSystem::GetForegroundTitleSDKVersion(); + s_transitionToBackground = false; + s_transitionToForeground = false; cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder); cafeExportRegister("coreinit", OSReport, LogType::Placeholder); @@ -480,6 +567,10 @@ namespace coreinit cafeExportRegister("coreinit", COSWarn, LogType::Placeholder); cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder); cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder); + + cafeExportRegister("coreinit", OSGetPFID, LogType::Placeholder); + cafeExportRegister("coreinit", OSGetUPID, LogType::Placeholder); + cafeExportRegister("coreinit", OSGetTitleID, LogType::Placeholder); cafeExportRegister("coreinit", __OSGetProcessSDKVersion, LogType::Placeholder); g_homeButtonMenuEnabled = true; // enabled by default @@ -489,6 +580,11 @@ namespace coreinit cafeExportRegister("coreinit", OSLaunchTitleByPathl, LogType::Placeholder); cafeExportRegister("coreinit", OSRestartGame, LogType::Placeholder); + + cafeExportRegister("coreinit", OSReleaseForeground, LogType::Placeholder); + + cafeExportRegister("coreinit", OSDriver_Register, LogType::Placeholder); + cafeExportRegister("coreinit", OSDriver_Deregister, LogType::Placeholder); } }; diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.h b/src/Cafe/OS/libs/coreinit/coreinit_Misc.h index 4a74d490..7abba92f 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.h @@ -2,9 +2,29 @@ namespace coreinit { + uint32 OSGetUPID(); + uint32 OSGetPFID(); + uint64 OSGetTitleID(); uint32 __OSGetProcessSDKVersion(); uint32 OSLaunchTitleByPathl(const char* path, uint32 pathLength, uint32 argc); uint32 OSRestartGame(uint32 argc, MEMPTR* argv); + void OSReleaseForeground(); + + void StartBackgroundForegroundTransition(); + + struct OSDriverInterface + { + MEMPTR getDriverName; + MEMPTR init; + MEMPTR onAcquireForeground; + MEMPTR onReleaseForeground; + MEMPTR done; + }; + static_assert(sizeof(OSDriverInterface) == 0x14); + + uint32 OSDriver_Register(uint32 moduleHandle, sint32 priority, OSDriverInterface* driverCallbacks, sint32 driverId, uint32be* outUkn1, uint32be* outUkn2, uint32be* outUkn3); + uint32 OSDriver_Deregister(uint32 moduleHandle, sint32 driverId); + void miscInit(); }; \ No newline at end of file diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 809d7be4..654e57a8 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -294,9 +294,9 @@ namespace coreinit __OSUnlockScheduler(); } - bool OSCreateThreadType(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop2, sint32 stackSize, sint32 priority, uint32 attr, OSThread_t::THREAD_TYPE threadType) + bool OSCreateThreadType(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop, sint32 stackSize, sint32 priority, uint32 attr, OSThread_t::THREAD_TYPE threadType) { - OSCreateThreadInternal(thread, entryPoint, memory_getVirtualOffsetFromPointer(stackTop2) - stackSize, stackSize, attr, threadType); + OSCreateThreadInternal(thread, entryPoint, memory_getVirtualOffsetFromPointer(stackTop) - stackSize, stackSize, attr, threadType); thread->context.gpr[3] = _swapEndianU32(numParam); // num arguments thread->context.gpr[4] = _swapEndianU32(memory_getVirtualOffsetFromPointer(ptrParam)); // arguments pointer __OSSetThreadBasePriority(thread, priority); @@ -317,9 +317,15 @@ namespace coreinit return true; } - bool OSCreateThread(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop2, sint32 stackSize, sint32 priority, uint32 attr) + bool OSCreateThread(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop, sint32 stackSize, sint32 priority, uint32 attr) { - return OSCreateThreadType(thread, entryPoint, numParam, ptrParam, stackTop2, stackSize, priority, attr, OSThread_t::THREAD_TYPE::TYPE_APP); + return OSCreateThreadType(thread, entryPoint, numParam, ptrParam, stackTop, stackSize, priority, attr, OSThread_t::THREAD_TYPE::TYPE_APP); + } + + // alias to OSCreateThreadType, similar to OSCreateThread, but with an additional parameter for the thread type + bool __OSCreateThreadType(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop, sint32 stackSize, sint32 priority, uint32 attr, OSThread_t::THREAD_TYPE threadType) + { + return OSCreateThreadType(thread, entryPoint, numParam, ptrParam, stackTop, stackSize, priority, attr, threadType); } bool OSRunThread(OSThread_t* thread, MPTR funcAddress, sint32 numParam, void* ptrParam) @@ -445,12 +451,12 @@ namespace coreinit return currentThread->specificArray[index].GetPtr(); } - void OSSetThreadName(OSThread_t* thread, char* name) + void OSSetThreadName(OSThread_t* thread, const char* name) { thread->threadName = name; } - char* OSGetThreadName(OSThread_t* thread) + const char* OSGetThreadName(OSThread_t* thread) { return thread->threadName.GetPtr(); } @@ -1371,6 +1377,7 @@ namespace coreinit { cafeExportRegister("coreinit", OSCreateThreadType, LogType::CoreinitThread); cafeExportRegister("coreinit", OSCreateThread, LogType::CoreinitThread); + cafeExportRegister("coreinit", __OSCreateThreadType, LogType::CoreinitThread); cafeExportRegister("coreinit", OSExitThread, LogType::CoreinitThread); cafeExportRegister("coreinit", OSGetCurrentThread, LogType::CoreinitThread); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h index e619d5b6..b401d96d 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h @@ -449,7 +449,7 @@ struct OSThread_t /* +0x578 */ sint32 alarmRelatedUkn; /* +0x57C */ std::array, 16> specificArray; /* +0x5BC */ betype type; - /* +0x5C0 */ MEMPTR threadName; + /* +0x5C0 */ MEMPTR threadName; /* +0x5C4 */ MPTR waitAlarm; // used only by OSWaitEventWithTimeout/OSSignalEvent ? /* +0x5C8 */ uint32 userStackPointer; @@ -505,6 +505,7 @@ namespace coreinit void* OSGetDefaultThreadStack(sint32 coreIndex, uint32& size); bool OSCreateThreadType(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop2, sint32 stackSize, sint32 priority, uint32 attr, OSThread_t::THREAD_TYPE threadType); + bool __OSCreateThreadType(OSThread_t* thread, MPTR entryPoint, sint32 numParam, void* ptrParam, void* stackTop, sint32 stackSize, sint32 priority, uint32 attr, OSThread_t::THREAD_TYPE threadType); void OSCreateThreadInternal(OSThread_t* thread, uint32 entryPoint, MPTR stackLowerBaseAddr, uint32 stackSize, uint8 affinityMask, OSThread_t::THREAD_TYPE threadType); bool OSRunThread(OSThread_t* thread, MPTR funcAddress, sint32 numParam, void* ptrParam); void OSExitThread(sint32 exitValue); @@ -519,8 +520,8 @@ namespace coreinit bool OSSetThreadPriority(OSThread_t* thread, sint32 newPriority); uint32 OSGetThreadAffinity(OSThread_t* thread); - void OSSetThreadName(OSThread_t* thread, char* name); - char* OSGetThreadName(OSThread_t* thread); + void OSSetThreadName(OSThread_t* thread, const char* name); + const char* OSGetThreadName(OSThread_t* thread); sint32 __OSResumeThreadInternal(OSThread_t* thread, sint32 resumeCount); sint32 OSResumeThread(OSThread_t* thread); @@ -530,6 +531,7 @@ namespace coreinit void OSSuspendThread(OSThread_t* thread); void OSSleepThread(OSThreadQueue* threadQueue); void OSWakeupThread(OSThreadQueue* threadQueue); + bool OSJoinThread(OSThread_t* thread, uint32be* exitValue); void OSTestThreadCancelInternal(); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp index 5a75b406..d6fc27b2 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp @@ -22,10 +22,9 @@ namespace coreinit osLib_returnFromFunction(hCPU, (uint32)osTime); } - void export_OSGetTime(PPCInterpreter_t* hCPU) + uint64 OSGetTime() { - uint64 osTime = coreinit_getOSTime(); - osLib_returnFromFunction64(hCPU, osTime); + return coreinit_getOSTime(); } void export_OSGetSystemTime(PPCInterpreter_t* hCPU) @@ -360,7 +359,7 @@ namespace coreinit void InitializeTimeAndCalendar() { - osLib_addFunction("coreinit", "OSGetTime", export_OSGetTime); + cafeExportRegister("coreinit", OSGetTime, LogType::Placeholder); osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime); osLib_addFunction("coreinit", "OSGetTick", export_OSGetTick); osLib_addFunction("coreinit", "OSGetSystemTick", export_OSGetSystemTick); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Time.h b/src/Cafe/OS/libs/coreinit/coreinit_Time.h index f5dcf22e..018e8eb7 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Time.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Time.h @@ -45,7 +45,8 @@ namespace coreinit }; void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct); - + uint64 OSGetTime(); + uint64 coreinit_getOSTime(); uint64 coreinit_getTimerTick(); diff --git a/src/Cafe/OS/libs/gx2/GX2_Misc.h b/src/Cafe/OS/libs/gx2/GX2_Misc.h index e6ac8010..38a728c1 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Misc.h +++ b/src/Cafe/OS/libs/gx2/GX2_Misc.h @@ -19,5 +19,7 @@ namespace GX2 void GX2SetTVBuffer(void* imageBuffePtr, uint32 imageBufferSize, E_TVRES tvResolutionMode, uint32 surfaceFormat, E_TVBUFFERMODE bufferMode); void GX2SetTVGamma(float gamma); + void GX2Invalidate(uint32 invalidationFlags, MPTR invalidationAddr, uint32 invalidationSize); + void GX2MiscInit(); }; \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp index 99c113c4..1916a18d 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp @@ -9,6 +9,7 @@ #include "Cafe/OS/libs/proc_ui/proc_ui.h" #include "Cafe/OS/libs/coreinit/coreinit_Time.h" +#include "Cafe/OS/libs/coreinit/coreinit_Misc.h" namespace nn { @@ -37,7 +38,7 @@ namespace nn void StubPostAppReleaseBackground(PPCInterpreter_t* hCPU) { coreinit::OSSleepTicks(ESPRESSO_TIMER_CLOCK * 2); // Sleep 2s - ProcUI_SendForegroundMessage(); + coreinit::StartBackgroundForegroundTransition(); } sint32 StubPostApp(void* pAnyPostParam) diff --git a/src/Cafe/OS/libs/proc_ui/proc_ui.cpp b/src/Cafe/OS/libs/proc_ui/proc_ui.cpp index 7de8691a..91d15af4 100644 --- a/src/Cafe/OS/libs/proc_ui/proc_ui.cpp +++ b/src/Cafe/OS/libs/proc_ui/proc_ui.cpp @@ -1,57 +1,905 @@ #include "Cafe/OS/common/OSCommon.h" +#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h" +#include "Cafe/OS/libs/coreinit/coreinit_Thread.h" +#include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h" +#include "Cafe/OS/libs/coreinit/coreinit_Misc.h" +#include "Cafe/OS/libs/coreinit/coreinit_Memory.h" +#include "Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.h" +#include "Cafe/OS/libs/coreinit/coreinit_Time.h" +#include "Cafe/OS/libs/coreinit/coreinit_FG.h" +#include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h" +#include "Cafe/OS/libs/gx2/GX2_Misc.h" +#include "Cafe/OS/RPL/rpl.h" +#include "Common/CafeString.h" #include "proc_ui.h" -#define PROCUI_STATUS_FOREGROUND 0 -#define PROCUI_STATUS_BACKGROUND 1 -#define PROCUI_STATUS_RELEASING 2 -#define PROCUI_STATUS_EXIT 3 +// proc_ui is a utility wrapper to help apps with the transition between foreground and background +// some games (like Xenoblades Chronicles X) bypass proc_ui.rpl and listen to OSGetSystemMessageQueue() directly -uint32 ProcUIProcessMessages() +using namespace coreinit; + +namespace proc_ui { - return PROCUI_STATUS_FOREGROUND; -} + enum class ProcUICoreThreadCommand + { + AcquireForeground = 0x0, + ReleaseForeground = 0x1, + Exit = 0x2, + NetIoStart = 0x3, + NetIoStop = 0x4, + HomeButtonDenied = 0x5, + Initial = 0x6 + }; + + struct ProcUIInternalCallbackEntry + { + coreinit::OSAlarm_t alarm; + uint64be tickDelay; + MEMPTR funcPtr; + MEMPTR userParam; + sint32be priority; + MEMPTR next; + }; + static_assert(sizeof(ProcUIInternalCallbackEntry) == 0x70); + + struct ProcUICallbackList + { + MEMPTR first; + }; + static_assert(sizeof(ProcUICallbackList) == 0x4); + + std::atomic_bool s_isInitialized; + bool s_isInForeground; + bool s_isInShutdown; + bool s_isForegroundProcess; + bool s_previouslyWasBlocking; + ProcUIStatus s_currentProcUIStatus; + MEMPTR s_saveCallback; // no param and no return value, set by ProcUIInit() + MEMPTR s_saveCallbackEx; // with custom param and return value, set by ProcUIInitEx() + MEMPTR s_saveCallbackExUserParam; + MEMPTR s_systemMessageQueuePtr; + SysAllocator s_eventStateMessageReceived; + SysAllocator s_eventWaitingBeforeReleaseForeground; + SysAllocator s_eventBackgroundThreadGotMessage; + // procUI core threads + uint32 s_coreThreadStackSize; + bool s_coreThreadsCreated; + std::atomic s_commandForCoreThread; + SysAllocator s_coreThreadArray[Espresso::CORE_COUNT]; + MEMPTR s_coreThreadStackPerCore[Espresso::CORE_COUNT]; + SysAllocator> s_coreThread0NameBuffer; + SysAllocator> s_coreThread1NameBuffer; + SysAllocator> s_coreThread2NameBuffer; + SysAllocator s_eventCoreThreadsNewCommandReady; + SysAllocator s_eventCoreThreadsCommandDone; + SysAllocator s_coreThreadRendezvousA; + SysAllocator s_coreThreadRendezvousB; + SysAllocator s_coreThreadRendezvousC; + // background thread + MEMPTR s_backgroundThreadStack; + SysAllocator s_backgroundThread; + // user defined heap + MEMPTR s_memoryPoolHeapPtr; + MEMPTR s_memAllocPtr; + MEMPTR s_memFreePtr; + // draw done release + bool s_drawDoneReleaseCalled; + // memory storage + MEMPTR s_bucketStorageBasePtr; + MEMPTR s_mem1StorageBasePtr; + // callbacks + ProcUICallbackList s_callbacksType0_AcquireForeground[Espresso::CORE_COUNT]; + ProcUICallbackList s_callbacksType1_ReleaseForeground[Espresso::CORE_COUNT]; + ProcUICallbackList s_callbacksType2_Exit[Espresso::CORE_COUNT]; + ProcUICallbackList s_callbacksType3_NetIoStart[Espresso::CORE_COUNT]; + ProcUICallbackList s_callbacksType4_NetIoStop[Espresso::CORE_COUNT]; + ProcUICallbackList s_callbacksType5_HomeButtonDenied[Espresso::CORE_COUNT]; + ProcUICallbackList* const s_CallbackTables[stdx::to_underlying(ProcUICallbackId::COUNT)] = + {s_callbacksType0_AcquireForeground, s_callbacksType1_ReleaseForeground, s_callbacksType2_Exit, s_callbacksType3_NetIoStart, s_callbacksType4_NetIoStop, s_callbacksType5_HomeButtonDenied}; + ProcUICallbackList s_backgroundCallbackList; + // driver + bool s_driverIsActive; + uint32be s_driverArgUkn1; + uint32be s_driverArgUkn2; + bool s_driverInBackground; + SysAllocator s_ProcUIDriver; + SysAllocator> s_ProcUIDriverName; -uint32 ProcUIInForeground(PPCInterpreter_t* hCPU) -{ - return 1; // true means application is in foreground -} + void* _AllocMem(uint32 size) + { + MEMPTR r{PPCCoreCallback(s_memAllocPtr, size)}; + return r.GetPtr(); + } -struct ProcUICallback -{ - MPTR callback; - void* data; - sint32 priority; + void _FreeMem(void* ptr) + { + PPCCoreCallback(s_memFreePtr.GetMPTR(), ptr); + } + + void ClearCallbacksWithoutMemFree() + { + for (sint32 coreIndex = 0; coreIndex < Espresso::CORE_COUNT; coreIndex++) + { + for (sint32 i = 0; i < stdx::to_underlying(ProcUICallbackId::COUNT); i++) + s_CallbackTables[i][coreIndex].first = nullptr; + } + s_backgroundCallbackList.first = nullptr; + } + + void ShutdownThreads() + { + if ( !s_coreThreadsCreated) + return; + s_commandForCoreThread = ProcUICoreThreadCommand::Initial; + coreinit::OSMemoryBarrier(); + OSSignalEvent(&s_eventCoreThreadsNewCommandReady); + for (sint32 coreIndex = 0; coreIndex < Espresso::CORE_COUNT; coreIndex++) + { + coreinit::OSJoinThread(&s_coreThreadArray[coreIndex], nullptr); + for (sint32 i = 0; i < stdx::to_underlying(ProcUICallbackId::COUNT); i++) + { + s_CallbackTables[i][coreIndex].first = nullptr; // memory is not cleanly released? + } + } + OSResetEvent(&s_eventCoreThreadsNewCommandReady); + for (sint32 coreIndex = 0; coreIndex < Espresso::CORE_COUNT; coreIndex++) + { + _FreeMem(s_coreThreadStackPerCore[coreIndex]); + s_coreThreadStackPerCore[coreIndex] = nullptr; + } + _FreeMem(s_backgroundThreadStack); + s_backgroundThreadStack = nullptr; + s_backgroundCallbackList.first = nullptr; // memory is not cleanly released? + s_coreThreadsCreated = false; + } + + void DoCallbackChain(ProcUIInternalCallbackEntry* entry) + { + while (entry) + { + uint32 r = PPCCoreCallback(entry->funcPtr, entry->userParam); + if ( r ) + cemuLog_log(LogType::APIErrors, "ProcUI: Callback returned error {}\n", r); + entry = entry->next; + } + } + + void AlarmDoBackgroundCallback(PPCInterpreter_t* hCPU) + { + coreinit::OSAlarm_t* arg = MEMPTR(hCPU->gpr[3]); + ProcUIInternalCallbackEntry* entry = (ProcUIInternalCallbackEntry*)arg; + uint32 r = PPCCoreCallback(entry->funcPtr, entry->userParam); + if ( r ) + cemuLog_log(LogType::APIErrors, "ProcUI: Background callback returned error {}\n", r); + osLib_returnFromFunction(hCPU, 0); // return type is void + } + + void StartBackgroundAlarms() + { + ProcUIInternalCallbackEntry* cb = s_backgroundCallbackList.first; + while(cb) + { + coreinit::OSCreateAlarm(&cb->alarm); + uint64 currentTime = coreinit::OSGetTime(); + coreinit::OSSetPeriodicAlarm(&cb->alarm, currentTime, cb->tickDelay, RPLLoader_MakePPCCallable(AlarmDoBackgroundCallback)); + cb = cb->next; + } + } + + void CancelBackgroundAlarms() + { + ProcUIInternalCallbackEntry* entry = s_backgroundCallbackList.first; + while (entry) + { + OSCancelAlarm(&entry->alarm); + entry = entry->next; + } + } + + void ProcUICoreThread(PPCInterpreter_t* hCPU) + { + uint32 coreIndex = hCPU->gpr[3]; + cemu_assert_debug(coreIndex == OSGetCoreId()); + while (true) + { + OSWaitEvent(&s_eventCoreThreadsNewCommandReady); + ProcUIInternalCallbackEntry* cbChain = nullptr; + cemuLog_logDebug(LogType::Force, "ProcUI: Core {} got command {}", coreIndex, (uint32)s_commandForCoreThread.load()); + auto cmd = s_commandForCoreThread.load(); + switch(cmd) + { + case ProcUICoreThreadCommand::Initial: + { + // signal to shut down thread + osLib_returnFromFunction(hCPU, 0); + return; + } + case ProcUICoreThreadCommand::AcquireForeground: + cbChain = s_callbacksType0_AcquireForeground[coreIndex].first; + break; + case ProcUICoreThreadCommand::ReleaseForeground: + cbChain = s_callbacksType1_ReleaseForeground[coreIndex].first; + break; + case ProcUICoreThreadCommand::Exit: + cbChain = s_callbacksType2_Exit[coreIndex].first; + break; + case ProcUICoreThreadCommand::NetIoStart: + cbChain = s_callbacksType3_NetIoStart[coreIndex].first; + break; + case ProcUICoreThreadCommand::NetIoStop: + cbChain = s_callbacksType4_NetIoStop[coreIndex].first; + break; + case ProcUICoreThreadCommand::HomeButtonDenied: + cbChain = s_callbacksType5_HomeButtonDenied[coreIndex].first; + break; + default: + cemu_assert_suspicious(); // invalid command + } + if(cmd == ProcUICoreThreadCommand::AcquireForeground) + { + if (coreIndex == 2) + CancelBackgroundAlarms(); + cbChain = s_callbacksType0_AcquireForeground[coreIndex].first; + } + else if(cmd == ProcUICoreThreadCommand::ReleaseForeground) + { + if (coreIndex == 2) + StartBackgroundAlarms(); + cbChain = s_callbacksType1_ReleaseForeground[coreIndex].first; + } + DoCallbackChain(cbChain); + OSWaitRendezvous(&s_coreThreadRendezvousA, 7); + if ( !coreIndex ) + { + OSInitRendezvous(&s_coreThreadRendezvousC); + OSResetEvent(&s_eventCoreThreadsNewCommandReady); + } + OSWaitRendezvous(&s_coreThreadRendezvousB, 7); + if ( !coreIndex ) + { + OSInitRendezvous(&s_coreThreadRendezvousA); + OSSignalEvent(&s_eventCoreThreadsCommandDone); + } + OSWaitRendezvous(&s_coreThreadRendezvousC, 7); + if ( !coreIndex ) + OSInitRendezvous(&s_coreThreadRendezvousB); + if (cmd == ProcUICoreThreadCommand::ReleaseForeground) + { + OSWaitEvent(&s_eventWaitingBeforeReleaseForeground); + OSReleaseForeground(); + } + } + osLib_returnFromFunction(hCPU, 0); + } + + void RecreateProcUICoreThreads() + { + ShutdownThreads(); + for (sint32 coreIndex = 0; coreIndex < Espresso::CORE_COUNT; coreIndex++) + { + s_coreThreadStackPerCore[coreIndex] = _AllocMem(s_coreThreadStackSize); + } + s_backgroundThreadStack = _AllocMem(s_coreThreadStackSize); + for (sint32 coreIndex = 0; coreIndex < Espresso::CORE_COUNT; coreIndex++) + { + __OSCreateThreadType(&s_coreThreadArray[coreIndex], RPLLoader_MakePPCCallable(ProcUICoreThread), coreIndex, nullptr, + (uint8*)s_coreThreadStackPerCore[coreIndex].GetPtr() + s_coreThreadStackSize, s_coreThreadStackSize, 16, + (1<assign("{SYS ProcUI Core 0}"); + s_coreThread1NameBuffer->assign("{SYS ProcUI Core 1}"); + s_coreThread2NameBuffer->assign("{SYS ProcUI Core 2}"); + OSSetThreadName(&s_coreThreadArray[0], s_coreThread0NameBuffer->c_str()); + OSSetThreadName(&s_coreThreadArray[1], s_coreThread1NameBuffer->c_str()); + OSSetThreadName(&s_coreThreadArray[2], s_coreThread2NameBuffer->c_str()); + s_coreThreadsCreated = true; + } + + void _SubmitCommandToCoreThreads(ProcUICoreThreadCommand cmd) + { + s_commandForCoreThread = cmd; + OSMemoryBarrier(); + OSResetEvent(&s_eventCoreThreadsCommandDone); + OSSignalEvent(&s_eventCoreThreadsNewCommandReady); + OSWaitEvent(&s_eventCoreThreadsCommandDone); + } + + void ProcUIInitInternal() + { + if( s_isInitialized.exchange(true) ) + return; + if (!s_memoryPoolHeapPtr) + { + // user didn't specify a custom heap, use default heap instead + s_memAllocPtr = gCoreinitData->MEMAllocFromDefaultHeap.GetMPTR(); + s_memFreePtr = gCoreinitData->MEMFreeToDefaultHeap.GetMPTR(); + } + OSInitEvent(&s_eventStateMessageReceived, OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_MANUAL); + OSInitEvent(&s_eventCoreThreadsNewCommandReady, OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_MANUAL); + OSInitEvent(&s_eventWaitingBeforeReleaseForeground, OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_MANUAL); + OSInitEvent(&s_eventCoreThreadsCommandDone, OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_MANUAL); + OSInitRendezvous(&s_coreThreadRendezvousA); + OSInitRendezvous(&s_coreThreadRendezvousB); + OSInitRendezvous(&s_coreThreadRendezvousC); + OSInitEvent(&s_eventBackgroundThreadGotMessage, OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, OSEvent::EVENT_MODE::MODE_MANUAL); + s_currentProcUIStatus = ProcUIStatus::Foreground; + s_drawDoneReleaseCalled = false; + s_isInForeground = true; + s_coreThreadStackSize = 0x2000; + s_systemMessageQueuePtr = coreinit::OSGetSystemMessageQueue(); + uint32 upid = coreinit::OSGetUPID(); + s_isForegroundProcess = upid == 2 || upid == 15; // either Wii U Menu or game title, both are RAMPID 7 (foreground process) + RecreateProcUICoreThreads(); + ClearCallbacksWithoutMemFree(); + } + + void ProcUIInit(MEMPTR callbackReadyToRelease) + { + s_saveCallback = callbackReadyToRelease; + s_saveCallbackEx = nullptr; + s_saveCallbackExUserParam = nullptr; + ProcUIInitInternal(); + } + + void ProcUIInitEx(MEMPTR callbackReadyToReleaseEx, MEMPTR userParam) + { + s_saveCallback = nullptr; + s_saveCallbackEx = callbackReadyToReleaseEx; + s_saveCallbackExUserParam = userParam; + ProcUIInitInternal(); + } + + void ProcUIShutdown() + { + if (!s_isInitialized.exchange(false)) + return; + if ( !s_isInForeground ) + CancelBackgroundAlarms(); + for (sint32 i = 0; i < Espresso::CORE_COUNT; i++) + OSSetThreadPriority(&s_coreThreadArray[i], 0); + _SubmitCommandToCoreThreads(ProcUICoreThreadCommand::Exit); + ProcUIClearCallbacks(); + ShutdownThreads(); + } + + bool ProcUIIsRunning() + { + return s_isInitialized; + } + + bool ProcUIInForeground() + { + return s_isInForeground; + } + + bool ProcUIInShutdown() + { + return s_isInShutdown; + } + + void AddCallbackInternal(void* funcPtr, void* userParam, uint64 tickDelay, sint32 priority, ProcUICallbackList& callbackList) + { + if ( __OSGetProcessSDKVersion() < 21102 ) + { + // in earlier COS versions it was possible/allowed to register a callback before initializing ProcUI + s_memAllocPtr = gCoreinitData->MEMAllocFromDefaultHeap.GetMPTR(); + s_memFreePtr = gCoreinitData->MEMFreeToDefaultHeap.GetMPTR(); + } + else if ( !s_isInitialized ) + { + cemuLog_log(LogType::Force, "ProcUI: Trying to register callback before init"); + cemu_assert_suspicious(); + } + ProcUIInternalCallbackEntry* entry = (ProcUIInternalCallbackEntry*)_AllocMem(sizeof(ProcUIInternalCallbackEntry)); + entry->funcPtr = funcPtr; + entry->userParam = userParam; + entry->tickDelay = tickDelay; + entry->priority = priority; + ProcUIInternalCallbackEntry* cur = callbackList.first; + cur = callbackList.first; + if (!cur || cur->priority > priority) + { + // insert as the first element + entry->next = cur; + callbackList.first = entry; + } + else + { + // find the correct position to insert + while (cur->next && cur->next->priority < priority) + cur = cur->next; + entry->next = cur->next; + cur->next = entry; + } + } + + void ProcUIRegisterCallbackCore(ProcUICallbackId callbackType, void* funcPtr, void* userParam, sint32 priority, uint32 coreIndex) + { + if(callbackType >= ProcUICallbackId::COUNT) + { + cemuLog_log(LogType::Force, "ProcUIRegisterCallback: Invalid callback type {}", stdx::to_underlying(callbackType)); + return; + } + if(callbackType != ProcUICallbackId::AcquireForeground) + priority = -priority; + AddCallbackInternal(funcPtr, userParam, priority, 0, s_CallbackTables[stdx::to_underlying(callbackType)][coreIndex]); + } + + void ProcUIRegisterCallback(ProcUICallbackId callbackType, void* funcPtr, void* userParam, sint32 priority) + { + ProcUIRegisterCallbackCore(callbackType, funcPtr, userParam, priority, OSGetCoreId()); + } + + void ProcUIRegisterBackgroundCallback(void* funcPtr, void* userParam, uint64 tickDelay) + { + AddCallbackInternal(funcPtr, userParam, 0, tickDelay, s_backgroundCallbackList); + } + + void FreeCallbackChain(ProcUICallbackList& callbackList) + { + ProcUIInternalCallbackEntry* entry = callbackList.first; + while (entry) + { + ProcUIInternalCallbackEntry* next = entry->next; + _FreeMem(entry); + entry = next; + } + callbackList.first = nullptr; + } + + void ProcUIClearCallbacks() + { + for (sint32 coreIndex = 0; coreIndex < Espresso::CORE_COUNT; coreIndex++) + { + for (sint32 i = 0; i < stdx::to_underlying(ProcUICallbackId::COUNT); i++) + { + FreeCallbackChain(s_CallbackTables[i][coreIndex]); + } + } + if (!s_isInForeground) + CancelBackgroundAlarms(); + FreeCallbackChain(s_backgroundCallbackList); + } + + void ProcUISetSaveCallback(void* funcPtr, void* userParam) + { + s_saveCallback = nullptr; + s_saveCallbackEx = funcPtr; + s_saveCallbackExUserParam = userParam; + } + + void ProcUISetCallbackStackSize(uint32 newStackSize) + { + s_coreThreadStackSize = newStackSize; + if( s_isInitialized ) + RecreateProcUICoreThreads(); + } + + uint32 ProcUICalcMemorySize(uint32 numCallbacks) + { + // each callback entry is 0x70 bytes with 0x14 bytes of allocator overhead (for ExpHeap). But for some reason proc_ui on 5.5.5 seems to reserve 0x8C0 bytes per callback? + uint32 stackReserveSize = (Espresso::CORE_COUNT + 1) * s_coreThreadStackSize; // 3 core threads + 1 message receive thread + uint32 callbackReserveSize = 0x8C0 * numCallbacks; + return stackReserveSize + callbackReserveSize + 100; + } + + void _MemAllocFromMemoryPool(PPCInterpreter_t* hCPU) + { + uint32 size = hCPU->gpr[3]; + MEMPTR r = MEMAllocFromExpHeapEx((MEMHeapHandle)s_memoryPoolHeapPtr.GetPtr(), size, 4); + osLib_returnFromFunction(hCPU, r.GetMPTR()); + } + + void _FreeToMemoryPoolExpHeap(PPCInterpreter_t* hCPU) + { + MEMPTR mem{hCPU->gpr[3]}; + MEMFreeToExpHeap((MEMHeapHandle)s_memoryPoolHeapPtr.GetPtr(), mem.GetPtr()); + osLib_returnFromFunction(hCPU, 0); + } + + sint32 ProcUISetMemoryPool(void* memBase, uint32 size) + { + s_memAllocPtr = RPLLoader_MakePPCCallable(_MemAllocFromMemoryPool); + s_memFreePtr = RPLLoader_MakePPCCallable(_FreeToMemoryPoolExpHeap); + s_memoryPoolHeapPtr = MEMCreateExpHeapEx(memBase, size, MEM_HEAP_OPTION_THREADSAFE); + return s_memoryPoolHeapPtr ? 0 : -1; + } + + void ProcUISetBucketStorage(void* memBase, uint32 size) + { + MEMPTR fgBase; + uint32be fgFreeSize; + OSGetForegroundBucketFreeArea((MPTR*)&fgBase, (MPTR*)&fgFreeSize); + if(fgFreeSize < size) + cemuLog_log(LogType::Force, "ProcUISetBucketStorage: Buffer size too small"); + s_bucketStorageBasePtr = memBase; + } + + void ProcUISetMEM1Storage(void* memBase, uint32 size) + { + MEMPTR memBound; + uint32be memBoundSize; + OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize); + if(memBoundSize < size) + cemuLog_log(LogType::Force, "ProcUISetMEM1Storage: Buffer size too small"); + s_mem1StorageBasePtr = memBase; + } + + void ProcUIDrawDoneRelease() + { + s_drawDoneReleaseCalled = true; + } + + OSMessage g_lastMsg; + + void ProcUI_BackgroundThread_ReceiveSingleMessage(PPCInterpreter_t* hCPU) + { + // the background thread receives messages in a loop until the title is either exited or foreground is acquired + while ( true ) + { + OSReceiveMessage(s_systemMessageQueuePtr, &g_lastMsg, OS_MESSAGE_BLOCK); // blocking receive + SysMessageId lastMsgId = static_cast((uint32)g_lastMsg.data0); + if(lastMsgId == SysMessageId::MsgExit || lastMsgId == SysMessageId::MsgAcquireForeground) + break; + else if (lastMsgId == SysMessageId::HomeButtonDenied) + { + cemu_assert_suspicious(); // Home button denied should not be sent to background app + } + else if ( lastMsgId == SysMessageId::NetIoStartOrStop ) + { + if (g_lastMsg.data1 ) + { + // NetIo start message + for (sint32 i = 0; i < Espresso::CORE_COUNT; i++) + DoCallbackChain(s_callbacksType3_NetIoStart[i].first); + } + else + { + // NetIo stop message + for (sint32 i = 0; i < Espresso::CORE_COUNT; i++) + DoCallbackChain(s_callbacksType4_NetIoStop[i].first); + } + } + else + { + cemuLog_log(LogType::Force, "ProcUI: BackgroundThread received invalid message 0x{:08x}", lastMsgId); + } + } + OSSignalEvent(&s_eventBackgroundThreadGotMessage); + osLib_returnFromFunction(hCPU, 0); + } + + // handle received message + // if the message is Exit this function returns false, in all other cases it returns true + bool ProcessSysMessage(OSMessage* msg) + { + SysMessageId lastMsgId = static_cast((uint32)msg->data0); + if ( lastMsgId == SysMessageId::MsgAcquireForeground ) + { + cemuLog_logDebug(LogType::Force, "ProcUI: Received Acquire Foreground message"); + s_isInShutdown = false; + _SubmitCommandToCoreThreads(ProcUICoreThreadCommand::AcquireForeground); + s_currentProcUIStatus = ProcUIStatus::Foreground; + s_isInForeground = true; + OSMemoryBarrier(); + OSSignalEvent(&s_eventStateMessageReceived); + return true; + } + else if (lastMsgId == SysMessageId::MsgExit) + { + cemuLog_logDebug(LogType::Force, "ProcUI: Received Exit message"); + s_isInShutdown = true; + _SubmitCommandToCoreThreads(ProcUICoreThreadCommand::Exit); + for (sint32 i = 0; i < Espresso::CORE_COUNT; i++) + FreeCallbackChain(s_callbacksType2_Exit[i]); + s_currentProcUIStatus = ProcUIStatus::Exit; + OSMemoryBarrier(); + OSSignalEvent(&s_eventStateMessageReceived); + return 0; + } + if (lastMsgId == SysMessageId::MsgReleaseForeground) + { + if (msg->data1 != 0) + { + cemuLog_logDebug(LogType::Force, "ProcUI: Received Release Foreground message as part of shutdown initiation"); + s_isInShutdown = true; + } + else + { + cemuLog_logDebug(LogType::Force, "ProcUI: Received Release Foreground message"); + } + s_currentProcUIStatus = ProcUIStatus::Releasing; + OSResetEvent(&s_eventStateMessageReceived); + // dont submit a command for the core threads yet, we need to wait for ProcUIDrawDoneRelease() + } + else if (lastMsgId == SysMessageId::HomeButtonDenied) + { + cemuLog_logDebug(LogType::Force, "ProcUI: Received Home Button Denied message"); + _SubmitCommandToCoreThreads(ProcUICoreThreadCommand::HomeButtonDenied); + } + else if ( lastMsgId == SysMessageId::NetIoStartOrStop ) + { + if (msg->data1 != 0) + { + cemuLog_logDebug(LogType::Force, "ProcUI: Received Net IO Start message"); + _SubmitCommandToCoreThreads(ProcUICoreThreadCommand::NetIoStart); + } + else + { + cemuLog_logDebug(LogType::Force, "ProcUI: Received Net IO Stop message"); + _SubmitCommandToCoreThreads(ProcUICoreThreadCommand::NetIoStop); + } + } + else + { + cemuLog_log(LogType::Force, "ProcUI: Received unknown message 0x{:08x}", (uint32)lastMsgId); + } + return true; + } + + ProcUIStatus ProcUIProcessMessages(bool isBlockingInBackground) + { + OSMessage msg; + if (!s_isInitialized) + { + cemuLog_logOnce(LogType::Force, "ProcUIProcessMessages: ProcUI not initialized"); + cemu_assert_suspicious(); + return ProcUIStatus::Foreground; + } + if ( !isBlockingInBackground && OSGetCoreId() != 2 ) + { + cemuLog_logOnce(LogType::Force, "ProcUIProcessMessages: Non-blocking call must run on core 2"); + } + if (s_previouslyWasBlocking && isBlockingInBackground ) + { + cemuLog_logOnce(LogType::Force, "ProcUIProcessMessages: Cannot switch to blocking mode when in background"); + } + s_currentProcUIStatus = s_isInForeground ? ProcUIStatus::Foreground : ProcUIStatus::Background; + if (s_drawDoneReleaseCalled) + { + s_isInForeground = false; + s_currentProcUIStatus = ProcUIStatus::Background; + _SubmitCommandToCoreThreads(ProcUICoreThreadCommand::ReleaseForeground); + OSResetEvent(&s_eventWaitingBeforeReleaseForeground); + if(s_saveCallback) + PPCCoreCallback(s_saveCallback); + if(s_saveCallbackEx) + PPCCoreCallback(s_saveCallbackEx, s_saveCallbackExUserParam); + if (s_isForegroundProcess && isBlockingInBackground) + { + // start background thread + __OSCreateThreadType(&s_backgroundThread, RPLLoader_MakePPCCallable(ProcUI_BackgroundThread_ReceiveSingleMessage), + 0, nullptr, (uint8*)s_backgroundThreadStack.GetPtr() + s_coreThreadStackSize, s_coreThreadStackSize, + 16, (1<<2), OSThread_t::THREAD_TYPE::TYPE_DRIVER); + OSResumeThread(&s_backgroundThread); + s_previouslyWasBlocking = true; + } + cemuLog_logDebug(LogType::Force, "ProcUI: Releasing foreground"); + OSSignalEvent(&s_eventWaitingBeforeReleaseForeground); + s_drawDoneReleaseCalled = false; + } + if (s_isInForeground || !isBlockingInBackground) + { + // non-blocking mode + if ( OSReceiveMessage(s_systemMessageQueuePtr, &msg, 0) ) + { + s_previouslyWasBlocking = false; + if ( !ProcessSysMessage(&msg) ) + return s_currentProcUIStatus; + // continue below, if we are now in background then ProcUIProcessMessages enters blocking mode + } + } + // blocking mode (if in background and param is true) + while (!s_isInForeground && isBlockingInBackground) + { + if ( !s_isForegroundProcess) + { + OSReceiveMessage(s_systemMessageQueuePtr, &msg, OS_MESSAGE_BLOCK); + s_previouslyWasBlocking = false; + if ( !ProcessSysMessage(&msg) ) + return s_currentProcUIStatus; + } + // this code should only run if the background thread was started? Maybe rearrange the code to make this more clear + OSWaitEvent(&s_eventBackgroundThreadGotMessage); + OSResetEvent(&s_eventBackgroundThreadGotMessage); + OSJoinThread(&s_backgroundThread, nullptr); + msg = g_lastMsg; // g_lastMsg is set by the background thread + s_previouslyWasBlocking = false; + if ( !ProcessSysMessage(&msg) ) + return s_currentProcUIStatus; + } + return s_currentProcUIStatus; + } + + ProcUIStatus ProcUISubProcessMessages(bool isBlockingInBackground) + { + if (isBlockingInBackground) + { + while (s_currentProcUIStatus == ProcUIStatus::Background) + OSWaitEvent(&s_eventStateMessageReceived); + } + return s_currentProcUIStatus; + } + + const char* ProcUIDriver_GetName() + { + s_ProcUIDriverName->assign("ProcUI"); + return s_ProcUIDriverName->c_str(); + } + + void ProcUIDriver_Init(/* parameters unknown */) + { + s_driverIsActive = true; + OSMemoryBarrier(); + } + + void ProcUIDriver_OnDone(/* parameters unknown */) + { + if (s_driverIsActive) + { + ProcUIShutdown(); + s_driverIsActive = false; + OSMemoryBarrier(); + } + } + + void StoreMEM1AndFGBucket() + { + if (s_mem1StorageBasePtr) + { + MEMPTR memBound; + uint32be memBoundSize; + OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize); + OSBlockMove(s_mem1StorageBasePtr.GetPtr(), memBound.GetPtr(), memBoundSize, true); + } + if (s_bucketStorageBasePtr) + { + MEMPTR memBound; + uint32be memBoundSize; + OSGetForegroundBucketFreeArea((MPTR*)memBound.GetBEPtr(), (MPTR*)&memBoundSize); + OSBlockMove(s_bucketStorageBasePtr.GetPtr(), memBound.GetPtr(), memBoundSize, true); + } + } + + void RestoreMEM1AndFGBucket() + { + if (s_mem1StorageBasePtr) + { + MEMPTR memBound; + uint32be memBoundSize; + OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize); + OSBlockMove(memBound.GetPtr(), s_mem1StorageBasePtr, memBoundSize, true); + GX2::GX2Invalidate(0x40, s_mem1StorageBasePtr.GetMPTR(), memBoundSize); + } + if (s_bucketStorageBasePtr) + { + MEMPTR memBound; + uint32be memBoundSize; + OSGetForegroundBucketFreeArea((MPTR*)memBound.GetBEPtr(), (MPTR*)&memBoundSize); + OSBlockMove(memBound.GetPtr(), s_bucketStorageBasePtr, memBoundSize, true); + GX2::GX2Invalidate(0x40, memBound.GetMPTR(), memBoundSize); + } + } + + void ProcUIDriver_OnAcquiredForeground(/* parameters unknown */) + { + if (s_driverInBackground) + { + ProcUIDriver_Init(); + s_driverInBackground = false; + } + else + { + RestoreMEM1AndFGBucket(); + s_driverIsActive = true; + OSMemoryBarrier(); + } + } + + void ProcUIDriver_OnReleaseForeground(/* parameters unknown */) + { + StoreMEM1AndFGBucket(); + s_driverIsActive = false; + OSMemoryBarrier(); + } + + sint32 rpl_entry(uint32 moduleHandle, RplEntryReason reason) + { + if ( reason == RplEntryReason::Loaded ) + { + s_ProcUIDriver->getDriverName = RPLLoader_MakePPCCallable([](PPCInterpreter_t* hCPU) {MEMPTR namePtr(ProcUIDriver_GetName()); osLib_returnFromFunction(hCPU, namePtr.GetMPTR()); }); + s_ProcUIDriver->init = RPLLoader_MakePPCCallable([](PPCInterpreter_t* hCPU) {ProcUIDriver_Init(); osLib_returnFromFunction(hCPU, 0); }); + s_ProcUIDriver->onAcquireForeground = RPLLoader_MakePPCCallable([](PPCInterpreter_t* hCPU) {ProcUIDriver_OnAcquiredForeground(); osLib_returnFromFunction(hCPU, 0); }); + s_ProcUIDriver->onReleaseForeground = RPLLoader_MakePPCCallable([](PPCInterpreter_t* hCPU) {ProcUIDriver_OnReleaseForeground(); osLib_returnFromFunction(hCPU, 0); }); + s_ProcUIDriver->done = RPLLoader_MakePPCCallable([](PPCInterpreter_t* hCPU) {ProcUIDriver_OnDone(); osLib_returnFromFunction(hCPU, 0); }); + + s_driverIsActive = false; + s_driverArgUkn1 = 0; + s_driverArgUkn2 = 0; + s_driverInBackground = false; + uint32be ukn3; + OSDriver_Register(moduleHandle, 200, &s_ProcUIDriver, 0, &s_driverArgUkn1, &s_driverArgUkn2, &ukn3); + if ( ukn3 ) + { + if ( OSGetForegroundBucket(nullptr, nullptr) ) + { + ProcUIDriver_Init(); + OSMemoryBarrier(); + return 0; + } + s_driverInBackground = true; + } + OSMemoryBarrier(); + } + else if ( reason == RplEntryReason::Unloaded ) + { + ProcUIDriver_OnDone(); + OSDriver_Deregister(moduleHandle, 0); + } + return 0; + } + + void reset() + { + // set variables to their initial state as if the RPL was just loaded + s_isInitialized = false; + s_isInShutdown = false; + s_isInForeground = false; // ProcUIInForeground returns false until ProcUIInit(Ex) is called + s_isForegroundProcess = true; + s_saveCallback = nullptr; + s_saveCallbackEx = nullptr; + s_systemMessageQueuePtr = nullptr; + ClearCallbacksWithoutMemFree(); + s_currentProcUIStatus = ProcUIStatus::Foreground; + s_bucketStorageBasePtr = nullptr; + s_mem1StorageBasePtr = nullptr; + s_drawDoneReleaseCalled = false; + s_previouslyWasBlocking = false; + // core threads + s_coreThreadStackSize = 0; + s_coreThreadsCreated = false; + s_commandForCoreThread = ProcUICoreThreadCommand::Initial; + // background thread + s_backgroundThreadStack = nullptr; + // user defined heap + s_memoryPoolHeapPtr = nullptr; + s_memAllocPtr = nullptr; + s_memFreePtr = nullptr; + // driver + s_driverIsActive = false; + s_driverInBackground = false; + } + + void load() + { + reset(); + + cafeExportRegister("proc_ui", ProcUIInit, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIInitEx, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIShutdown, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIIsRunning, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIInForeground, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIInShutdown, LogType::ProcUi); + + cafeExportRegister("proc_ui", ProcUIRegisterCallbackCore, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIRegisterCallback, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIRegisterBackgroundCallback, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIClearCallbacks, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUISetSaveCallback, LogType::ProcUi); + + cafeExportRegister("proc_ui", ProcUISetCallbackStackSize, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUICalcMemorySize, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUISetMemoryPool, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUISetBucketStorage, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUISetMEM1Storage, LogType::ProcUi); + + cafeExportRegister("proc_ui", ProcUIDrawDoneRelease, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUIProcessMessages, LogType::ProcUi); + cafeExportRegister("proc_ui", ProcUISubProcessMessages, LogType::ProcUi); + + // manually call rpl_entry for now + rpl_entry(-1, RplEntryReason::Loaded); + } }; -std::unordered_map g_Callbacks; - -uint32 ProcUIRegisterCallback(uint32 message, MPTR callback, void* data, sint32 priority) -{ - g_Callbacks.insert_or_assign(message, ProcUICallback{ .callback = callback, .data = data, .priority = priority }); - return 0; -} - -void ProcUI_SendBackgroundMessage() -{ - if (g_Callbacks.contains(PROCUI_STATUS_BACKGROUND)) - { - ProcUICallback& callback = g_Callbacks[PROCUI_STATUS_BACKGROUND]; - PPCCoreCallback(callback.callback, callback.data); - } -} - -void ProcUI_SendForegroundMessage() -{ - if (g_Callbacks.contains(PROCUI_STATUS_FOREGROUND)) - { - ProcUICallback& callback = g_Callbacks[PROCUI_STATUS_FOREGROUND]; - PPCCoreCallback(callback.callback, callback.data); - } -} - -void procui_load() -{ - cafeExportRegister("proc_ui", ProcUIRegisterCallback, LogType::ProcUi); - cafeExportRegister("proc_ui", ProcUIProcessMessages, LogType::ProcUi); - cafeExportRegister("proc_ui", ProcUIInForeground, LogType::ProcUi); -} \ No newline at end of file diff --git a/src/Cafe/OS/libs/proc_ui/proc_ui.h b/src/Cafe/OS/libs/proc_ui/proc_ui.h index 1cd04fb1..8de7bb4d 100644 --- a/src/Cafe/OS/libs/proc_ui/proc_ui.h +++ b/src/Cafe/OS/libs/proc_ui/proc_ui.h @@ -1,5 +1,44 @@ -void procui_load(); +namespace proc_ui +{ + enum class ProcUIStatus + { + Foreground = 0, + Background = 1, + Releasing = 2, + Exit = 3 + }; -void ProcUI_SendForegroundMessage(); -void ProcUI_SendBackgroundMessage(); \ No newline at end of file + enum class ProcUICallbackId + { + AcquireForeground = 0, + ReleaseForeground = 1, + Exit = 2, + NetIoStart = 3, + NetIoStop = 4, + HomeButtonDenied = 5, + COUNT = 6 + }; + + void ProcUIInit(MEMPTR callbackReadyToRelease); + void ProcUIInitEx(MEMPTR callbackReadyToReleaseEx, MEMPTR userParam); + void ProcUIShutdown(); + bool ProcUIIsRunning(); + bool ProcUIInForeground(); + bool ProcUIInShutdown(); + void ProcUIRegisterCallback(ProcUICallbackId callbackType, void* funcPtr, void* userParam, sint32 priority); + void ProcUIRegisterCallbackCore(ProcUICallbackId callbackType, void* funcPtr, void* userParam, sint32 priority, uint32 coreIndex); + void ProcUIRegisterBackgroundCallback(void* funcPtr, void* userParam, uint64 tickDelay); + void ProcUIClearCallbacks(); + void ProcUISetSaveCallback(void* funcPtr, void* userParam); + void ProcUISetCallbackStackSize(uint32 newStackSize); + uint32 ProcUICalcMemorySize(uint32 numCallbacks); + sint32 ProcUISetMemoryPool(void* memBase, uint32 size); + void ProcUISetBucketStorage(void* memBase, uint32 size); + void ProcUISetMEM1Storage(void* memBase, uint32 size); + void ProcUIDrawDoneRelease(); + ProcUIStatus ProcUIProcessMessages(bool isBlockingInBackground); + ProcUIStatus ProcUISubProcessMessages(bool isBlockingInBackground); + + void load(); +} \ No newline at end of file diff --git a/src/Cafe/OS/libs/sysapp/sysapp.cpp b/src/Cafe/OS/libs/sysapp/sysapp.cpp index 413d535a..ecaa940a 100644 --- a/src/Cafe/OS/libs/sysapp/sysapp.cpp +++ b/src/Cafe/OS/libs/sysapp/sysapp.cpp @@ -639,11 +639,34 @@ namespace sysapp return coreinit::OSRestartGame(argc, argv); } + struct EManualArgs + { + sysStandardArguments_t stdArgs; + uint64be titleId; + }; + static_assert(sizeof(EManualArgs) == 0x10); + + void _SYSSwitchToEManual(EManualArgs* args) + { + // the struct has the titleId at offset 8 and standard args at 0 (total size is most likely 0x10) + cemuLog_log(LogType::Force, "SYSSwitchToEManual called. Opening the manual is not supported"); + coreinit::StartBackgroundForegroundTransition(); + } + + void SYSSwitchToEManual() + { + EManualArgs args{}; + args.titleId = coreinit::OSGetTitleID(); + _SYSSwitchToEManual(&args); + } + void load() { cafeExportRegisterFunc(SYSClearSysArgs, "sysapp", "SYSClearSysArgs", LogType::Placeholder); cafeExportRegisterFunc(_SYSLaunchTitleByPathFromLauncher, "sysapp", "_SYSLaunchTitleByPathFromLauncher", LogType::Placeholder); cafeExportRegisterFunc(SYSRelaunchTitle, "sysapp", "SYSRelaunchTitle", LogType::Placeholder); + cafeExportRegister("sysapp", _SYSSwitchToEManual, LogType::Placeholder); + cafeExportRegister("sysapp", SYSSwitchToEManual, LogType::Placeholder); } } diff --git a/src/Common/CafeString.h b/src/Common/CafeString.h index 45a515b1..d902d721 100644 --- a/src/Common/CafeString.h +++ b/src/Common/CafeString.h @@ -20,6 +20,11 @@ class CafeString // fixed buffer size, null-terminated, PPC char return true; } + const char* c_str() + { + return (const char*)data; + } + uint8be data[N]; };