mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-23 03:19:48 +00:00
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
This commit is contained in:
parent
c038e758ae
commit
1c73dc9e1b
1
.gitignore
vendored
1
.gitignore
vendored
@ -39,6 +39,7 @@ bin/sdcard/*
|
|||||||
bin/screenshots/*
|
bin/screenshots/*
|
||||||
bin/dump/*
|
bin/dump/*
|
||||||
bin/cafeLibs/*
|
bin/cafeLibs/*
|
||||||
|
bin/portable/*
|
||||||
bin/keys.txt
|
bin/keys.txt
|
||||||
|
|
||||||
!bin/shaderCache/info.txt
|
!bin/shaderCache/info.txt
|
||||||
|
@ -221,5 +221,5 @@ void osLib_load()
|
|||||||
nsyskbd::nsyskbd_load();
|
nsyskbd::nsyskbd_load();
|
||||||
swkbd::load();
|
swkbd::load();
|
||||||
camera::load();
|
camera::load();
|
||||||
procui_load();
|
proc_ui::load();
|
||||||
}
|
}
|
||||||
|
@ -179,27 +179,6 @@ void coreinitExport_OSGetSharedData(PPCInterpreter_t* hCPU)
|
|||||||
osLib_returnFromFunction(hCPU, 1);
|
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
|
namespace coreinit
|
||||||
{
|
{
|
||||||
sint32 OSGetCoreId()
|
sint32 OSGetCoreId()
|
||||||
@ -379,7 +358,6 @@ void coreinit_load()
|
|||||||
coreinit::miscInit();
|
coreinit::miscInit();
|
||||||
osLib_addFunction("coreinit", "OSGetSharedData", coreinitExport_OSGetSharedData);
|
osLib_addFunction("coreinit", "OSGetSharedData", coreinitExport_OSGetSharedData);
|
||||||
osLib_addFunction("coreinit", "UCReadSysConfig", coreinitExport_UCReadSysConfig);
|
osLib_addFunction("coreinit", "UCReadSysConfig", coreinitExport_UCReadSysConfig);
|
||||||
osLib_addFunction("coreinit", "OSDriver_Register", coreinitExport_OSDriver_Register);
|
|
||||||
|
|
||||||
// async callbacks
|
// async callbacks
|
||||||
InitializeAsyncCallback();
|
InitializeAsyncCallback();
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
{
|
{
|
||||||
|
enum class RplEntryReason
|
||||||
|
{
|
||||||
|
Loaded = 1,
|
||||||
|
Unloaded = 2,
|
||||||
|
};
|
||||||
|
|
||||||
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc);
|
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc);
|
||||||
void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc);
|
void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc);
|
||||||
uint32 OSDynLoad_GetAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree);
|
uint32 OSDynLoad_GetAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree);
|
||||||
|
@ -837,7 +837,7 @@ namespace coreinit
|
|||||||
|
|
||||||
FSAsyncResult* FSGetAsyncResult(OSMessage* msg)
|
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)
|
sint32 __FSProcessAsyncResult(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, sint32 fsStatus, uint32 errHandling)
|
||||||
|
@ -126,7 +126,7 @@ namespace coreinit
|
|||||||
return physicalAddr;
|
return physicalAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSMemoryBarrier(PPCInterpreter_t* hCPU)
|
void OSMemoryBarrier()
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,9 @@ namespace coreinit
|
|||||||
void InitializeMemory();
|
void InitializeMemory();
|
||||||
|
|
||||||
void OSGetMemBound(sint32 memType, MPTR* offsetOutput, uint32* sizeOutput);
|
void OSGetMemBound(sint32 memType, MPTR* offsetOutput, uint32* sizeOutput);
|
||||||
|
|
||||||
|
void* OSBlockMove(MEMPTR<void> dst, MEMPTR<void> src, uint32 size, bool flushDC);
|
||||||
|
void* OSBlockSet(MEMPTR<void> dst, uint32 value, uint32 size);
|
||||||
|
|
||||||
|
void OSMemoryBarrier();
|
||||||
}
|
}
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
{
|
{
|
||||||
|
void UpdateSystemMessageQueue();
|
||||||
|
void HandleReceivedSystemMessage(OSMessage* msg);
|
||||||
|
|
||||||
SysAllocator<OSMessageQueue> g_systemMessageQueue;
|
SysAllocator<OSMessageQueue> g_systemMessageQueue;
|
||||||
SysAllocator<OSMessage, 16> _systemMessageQueueArray;
|
SysAllocator<OSMessage, 16> _systemMessageQueueArray;
|
||||||
@ -27,6 +29,9 @@ namespace coreinit
|
|||||||
|
|
||||||
bool OSReceiveMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags)
|
bool OSReceiveMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags)
|
||||||
{
|
{
|
||||||
|
bool isSystemMessageQueue = (msgQueue == g_systemMessageQueue);
|
||||||
|
if(isSystemMessageQueue)
|
||||||
|
UpdateSystemMessageQueue();
|
||||||
__OSLockScheduler(msgQueue);
|
__OSLockScheduler(msgQueue);
|
||||||
while (msgQueue->usedCount == (uint32be)0)
|
while (msgQueue->usedCount == (uint32be)0)
|
||||||
{
|
{
|
||||||
@ -50,6 +55,8 @@ namespace coreinit
|
|||||||
if (!msgQueue->threadQueueSend.isEmpty())
|
if (!msgQueue->threadQueueSend.isEmpty())
|
||||||
msgQueue->threadQueueSend.wakeupSingleThreadWaitQueue(true);
|
msgQueue->threadQueueSend.wakeupSingleThreadWaitQueue(true);
|
||||||
__OSUnlockScheduler(msgQueue);
|
__OSUnlockScheduler(msgQueue);
|
||||||
|
if(isSystemMessageQueue)
|
||||||
|
HandleReceivedSystemMessage(msg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,12 +3,21 @@
|
|||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
{
|
{
|
||||||
|
enum class SysMessageId : uint32
|
||||||
|
{
|
||||||
|
MsgAcquireForeground = 0xFACEF000,
|
||||||
|
MsgReleaseForeground = 0xFACEBACC,
|
||||||
|
MsgExit = 0xD1E0D1E0,
|
||||||
|
HomeButtonDenied = 0xCCC0FFEE,
|
||||||
|
NetIoStartOrStop = 0xAAC0FFEE,
|
||||||
|
};
|
||||||
|
|
||||||
struct OSMessage
|
struct OSMessage
|
||||||
{
|
{
|
||||||
MPTR message;
|
uint32be message;
|
||||||
uint32 data0;
|
uint32be data0;
|
||||||
uint32 data1;
|
uint32be data1;
|
||||||
uint32 data2;
|
uint32be data2;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OSMessageQueue
|
struct OSMessageQueue
|
||||||
@ -36,5 +45,7 @@ namespace coreinit
|
|||||||
bool OSPeekMessage(OSMessageQueue* msgQueue, OSMessage* msg);
|
bool OSPeekMessage(OSMessageQueue* msgQueue, OSMessage* msg);
|
||||||
sint32 OSSendMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags);
|
sint32 OSSendMessage(OSMessageQueue* msgQueue, OSMessage* msg, uint32 flags);
|
||||||
|
|
||||||
|
OSMessageQueue* OSGetSystemMessageQueue();
|
||||||
|
|
||||||
void InitializeMessageQueue();
|
void InitializeMessageQueue();
|
||||||
};
|
};
|
@ -1,5 +1,6 @@
|
|||||||
#include "Cafe/OS/common/OSCommon.h"
|
#include "Cafe/OS/common/OSCommon.h"
|
||||||
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
||||||
|
#include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h"
|
||||||
#include "Cafe/CafeSystem.h"
|
#include "Cafe/CafeSystem.h"
|
||||||
#include "Cafe/Filesystem/fsc.h"
|
#include "Cafe/Filesystem/fsc.h"
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
@ -371,6 +372,23 @@ namespace coreinit
|
|||||||
return true;
|
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 s_sdkVersion;
|
||||||
|
|
||||||
uint32 __OSGetProcessSDKVersion()
|
uint32 __OSGetProcessSDKVersion()
|
||||||
@ -470,9 +488,78 @@ namespace coreinit
|
|||||||
return 0;
|
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()
|
void miscInit()
|
||||||
{
|
{
|
||||||
|
s_currentTitleId = CafeSystem::GetForegroundTitleId();
|
||||||
s_sdkVersion = CafeSystem::GetForegroundTitleSDKVersion();
|
s_sdkVersion = CafeSystem::GetForegroundTitleSDKVersion();
|
||||||
|
s_transitionToBackground = false;
|
||||||
|
s_transitionToForeground = false;
|
||||||
|
|
||||||
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
|
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
|
||||||
@ -480,6 +567,10 @@ namespace coreinit
|
|||||||
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
|
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSConsoleWrite, 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);
|
cafeExportRegister("coreinit", __OSGetProcessSDKVersion, LogType::Placeholder);
|
||||||
|
|
||||||
g_homeButtonMenuEnabled = true; // enabled by default
|
g_homeButtonMenuEnabled = true; // enabled by default
|
||||||
@ -489,6 +580,11 @@ namespace coreinit
|
|||||||
|
|
||||||
cafeExportRegister("coreinit", OSLaunchTitleByPathl, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSLaunchTitleByPathl, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSRestartGame, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSRestartGame, LogType::Placeholder);
|
||||||
|
|
||||||
|
cafeExportRegister("coreinit", OSReleaseForeground, LogType::Placeholder);
|
||||||
|
|
||||||
|
cafeExportRegister("coreinit", OSDriver_Register, LogType::Placeholder);
|
||||||
|
cafeExportRegister("coreinit", OSDriver_Deregister, LogType::Placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -2,9 +2,29 @@
|
|||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
{
|
{
|
||||||
|
uint32 OSGetUPID();
|
||||||
|
uint32 OSGetPFID();
|
||||||
|
uint64 OSGetTitleID();
|
||||||
uint32 __OSGetProcessSDKVersion();
|
uint32 __OSGetProcessSDKVersion();
|
||||||
uint32 OSLaunchTitleByPathl(const char* path, uint32 pathLength, uint32 argc);
|
uint32 OSLaunchTitleByPathl(const char* path, uint32 pathLength, uint32 argc);
|
||||||
uint32 OSRestartGame(uint32 argc, MEMPTR<char>* argv);
|
uint32 OSRestartGame(uint32 argc, MEMPTR<char>* argv);
|
||||||
|
|
||||||
|
void OSReleaseForeground();
|
||||||
|
|
||||||
|
void StartBackgroundForegroundTransition();
|
||||||
|
|
||||||
|
struct OSDriverInterface
|
||||||
|
{
|
||||||
|
MEMPTR<void> getDriverName;
|
||||||
|
MEMPTR<void> init;
|
||||||
|
MEMPTR<void> onAcquireForeground;
|
||||||
|
MEMPTR<void> onReleaseForeground;
|
||||||
|
MEMPTR<void> 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();
|
void miscInit();
|
||||||
};
|
};
|
@ -294,9 +294,9 @@ namespace coreinit
|
|||||||
__OSUnlockScheduler();
|
__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[3] = _swapEndianU32(numParam); // num arguments
|
||||||
thread->context.gpr[4] = _swapEndianU32(memory_getVirtualOffsetFromPointer(ptrParam)); // arguments pointer
|
thread->context.gpr[4] = _swapEndianU32(memory_getVirtualOffsetFromPointer(ptrParam)); // arguments pointer
|
||||||
__OSSetThreadBasePriority(thread, priority);
|
__OSSetThreadBasePriority(thread, priority);
|
||||||
@ -317,9 +317,15 @@ namespace coreinit
|
|||||||
return true;
|
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)
|
bool OSRunThread(OSThread_t* thread, MPTR funcAddress, sint32 numParam, void* ptrParam)
|
||||||
@ -445,12 +451,12 @@ namespace coreinit
|
|||||||
return currentThread->specificArray[index].GetPtr();
|
return currentThread->specificArray[index].GetPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSSetThreadName(OSThread_t* thread, char* name)
|
void OSSetThreadName(OSThread_t* thread, const char* name)
|
||||||
{
|
{
|
||||||
thread->threadName = name;
|
thread->threadName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* OSGetThreadName(OSThread_t* thread)
|
const char* OSGetThreadName(OSThread_t* thread)
|
||||||
{
|
{
|
||||||
return thread->threadName.GetPtr();
|
return thread->threadName.GetPtr();
|
||||||
}
|
}
|
||||||
@ -1371,6 +1377,7 @@ namespace coreinit
|
|||||||
{
|
{
|
||||||
cafeExportRegister("coreinit", OSCreateThreadType, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSCreateThreadType, LogType::CoreinitThread);
|
||||||
cafeExportRegister("coreinit", OSCreateThread, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSCreateThread, LogType::CoreinitThread);
|
||||||
|
cafeExportRegister("coreinit", __OSCreateThreadType, LogType::CoreinitThread);
|
||||||
cafeExportRegister("coreinit", OSExitThread, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSExitThread, LogType::CoreinitThread);
|
||||||
|
|
||||||
cafeExportRegister("coreinit", OSGetCurrentThread, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSGetCurrentThread, LogType::CoreinitThread);
|
||||||
|
@ -449,7 +449,7 @@ struct OSThread_t
|
|||||||
/* +0x578 */ sint32 alarmRelatedUkn;
|
/* +0x578 */ sint32 alarmRelatedUkn;
|
||||||
/* +0x57C */ std::array<MEMPTR<void>, 16> specificArray;
|
/* +0x57C */ std::array<MEMPTR<void>, 16> specificArray;
|
||||||
/* +0x5BC */ betype<THREAD_TYPE> type;
|
/* +0x5BC */ betype<THREAD_TYPE> type;
|
||||||
/* +0x5C0 */ MEMPTR<char> threadName;
|
/* +0x5C0 */ MEMPTR<const char> threadName;
|
||||||
/* +0x5C4 */ MPTR waitAlarm; // used only by OSWaitEventWithTimeout/OSSignalEvent ?
|
/* +0x5C4 */ MPTR waitAlarm; // used only by OSWaitEventWithTimeout/OSSignalEvent ?
|
||||||
|
|
||||||
/* +0x5C8 */ uint32 userStackPointer;
|
/* +0x5C8 */ uint32 userStackPointer;
|
||||||
@ -505,6 +505,7 @@ namespace coreinit
|
|||||||
void* OSGetDefaultThreadStack(sint32 coreIndex, uint32& size);
|
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* 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);
|
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);
|
bool OSRunThread(OSThread_t* thread, MPTR funcAddress, sint32 numParam, void* ptrParam);
|
||||||
void OSExitThread(sint32 exitValue);
|
void OSExitThread(sint32 exitValue);
|
||||||
@ -519,8 +520,8 @@ namespace coreinit
|
|||||||
bool OSSetThreadPriority(OSThread_t* thread, sint32 newPriority);
|
bool OSSetThreadPriority(OSThread_t* thread, sint32 newPriority);
|
||||||
uint32 OSGetThreadAffinity(OSThread_t* thread);
|
uint32 OSGetThreadAffinity(OSThread_t* thread);
|
||||||
|
|
||||||
void OSSetThreadName(OSThread_t* thread, char* name);
|
void OSSetThreadName(OSThread_t* thread, const char* name);
|
||||||
char* OSGetThreadName(OSThread_t* thread);
|
const char* OSGetThreadName(OSThread_t* thread);
|
||||||
|
|
||||||
sint32 __OSResumeThreadInternal(OSThread_t* thread, sint32 resumeCount);
|
sint32 __OSResumeThreadInternal(OSThread_t* thread, sint32 resumeCount);
|
||||||
sint32 OSResumeThread(OSThread_t* thread);
|
sint32 OSResumeThread(OSThread_t* thread);
|
||||||
@ -530,6 +531,7 @@ namespace coreinit
|
|||||||
void OSSuspendThread(OSThread_t* thread);
|
void OSSuspendThread(OSThread_t* thread);
|
||||||
void OSSleepThread(OSThreadQueue* threadQueue);
|
void OSSleepThread(OSThreadQueue* threadQueue);
|
||||||
void OSWakeupThread(OSThreadQueue* threadQueue);
|
void OSWakeupThread(OSThreadQueue* threadQueue);
|
||||||
|
bool OSJoinThread(OSThread_t* thread, uint32be* exitValue);
|
||||||
|
|
||||||
void OSTestThreadCancelInternal();
|
void OSTestThreadCancelInternal();
|
||||||
|
|
||||||
|
@ -22,10 +22,9 @@ namespace coreinit
|
|||||||
osLib_returnFromFunction(hCPU, (uint32)osTime);
|
osLib_returnFromFunction(hCPU, (uint32)osTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void export_OSGetTime(PPCInterpreter_t* hCPU)
|
uint64 OSGetTime()
|
||||||
{
|
{
|
||||||
uint64 osTime = coreinit_getOSTime();
|
return coreinit_getOSTime();
|
||||||
osLib_returnFromFunction64(hCPU, osTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void export_OSGetSystemTime(PPCInterpreter_t* hCPU)
|
void export_OSGetSystemTime(PPCInterpreter_t* hCPU)
|
||||||
@ -360,7 +359,7 @@ namespace coreinit
|
|||||||
|
|
||||||
void InitializeTimeAndCalendar()
|
void InitializeTimeAndCalendar()
|
||||||
{
|
{
|
||||||
osLib_addFunction("coreinit", "OSGetTime", export_OSGetTime);
|
cafeExportRegister("coreinit", OSGetTime, LogType::Placeholder);
|
||||||
osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime);
|
osLib_addFunction("coreinit", "OSGetSystemTime", export_OSGetSystemTime);
|
||||||
osLib_addFunction("coreinit", "OSGetTick", export_OSGetTick);
|
osLib_addFunction("coreinit", "OSGetTick", export_OSGetTick);
|
||||||
osLib_addFunction("coreinit", "OSGetSystemTick", export_OSGetSystemTick);
|
osLib_addFunction("coreinit", "OSGetSystemTick", export_OSGetSystemTick);
|
||||||
|
@ -45,7 +45,8 @@ namespace coreinit
|
|||||||
};
|
};
|
||||||
|
|
||||||
void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct);
|
void OSTicksToCalendarTime(uint64 ticks, OSCalendarTime_t* calenderStruct);
|
||||||
|
uint64 OSGetTime();
|
||||||
|
|
||||||
uint64 coreinit_getOSTime();
|
uint64 coreinit_getOSTime();
|
||||||
uint64 coreinit_getTimerTick();
|
uint64 coreinit_getTimerTick();
|
||||||
|
|
||||||
|
@ -19,5 +19,7 @@ namespace GX2
|
|||||||
void GX2SetTVBuffer(void* imageBuffePtr, uint32 imageBufferSize, E_TVRES tvResolutionMode, uint32 surfaceFormat, E_TVBUFFERMODE bufferMode);
|
void GX2SetTVBuffer(void* imageBuffePtr, uint32 imageBufferSize, E_TVRES tvResolutionMode, uint32 surfaceFormat, E_TVBUFFERMODE bufferMode);
|
||||||
void GX2SetTVGamma(float gamma);
|
void GX2SetTVGamma(float gamma);
|
||||||
|
|
||||||
|
void GX2Invalidate(uint32 invalidationFlags, MPTR invalidationAddr, uint32 invalidationSize);
|
||||||
|
|
||||||
void GX2MiscInit();
|
void GX2MiscInit();
|
||||||
};
|
};
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "Cafe/OS/libs/proc_ui/proc_ui.h"
|
#include "Cafe/OS/libs/proc_ui/proc_ui.h"
|
||||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||||
|
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
||||||
|
|
||||||
namespace nn
|
namespace nn
|
||||||
{
|
{
|
||||||
@ -37,7 +38,7 @@ namespace nn
|
|||||||
void StubPostAppReleaseBackground(PPCInterpreter_t* hCPU)
|
void StubPostAppReleaseBackground(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
coreinit::OSSleepTicks(ESPRESSO_TIMER_CLOCK * 2); // Sleep 2s
|
coreinit::OSSleepTicks(ESPRESSO_TIMER_CLOCK * 2); // Sleep 2s
|
||||||
ProcUI_SendForegroundMessage();
|
coreinit::StartBackgroundForegroundTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
sint32 StubPostApp(void* pAnyPostParam)
|
sint32 StubPostApp(void* pAnyPostParam)
|
||||||
|
@ -1,57 +1,905 @@
|
|||||||
#include "Cafe/OS/common/OSCommon.h"
|
#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"
|
#include "proc_ui.h"
|
||||||
|
|
||||||
#define PROCUI_STATUS_FOREGROUND 0
|
// proc_ui is a utility wrapper to help apps with the transition between foreground and background
|
||||||
#define PROCUI_STATUS_BACKGROUND 1
|
// some games (like Xenoblades Chronicles X) bypass proc_ui.rpl and listen to OSGetSystemMessageQueue() directly
|
||||||
#define PROCUI_STATUS_RELEASING 2
|
|
||||||
#define PROCUI_STATUS_EXIT 3
|
|
||||||
|
|
||||||
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<void> funcPtr;
|
||||||
|
MEMPTR<void> userParam;
|
||||||
|
sint32be priority;
|
||||||
|
MEMPTR<ProcUIInternalCallbackEntry> next;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ProcUIInternalCallbackEntry) == 0x70);
|
||||||
|
|
||||||
|
struct ProcUICallbackList
|
||||||
|
{
|
||||||
|
MEMPTR<ProcUIInternalCallbackEntry> 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<void> s_saveCallback; // no param and no return value, set by ProcUIInit()
|
||||||
|
MEMPTR<void> s_saveCallbackEx; // with custom param and return value, set by ProcUIInitEx()
|
||||||
|
MEMPTR<void> s_saveCallbackExUserParam;
|
||||||
|
MEMPTR<coreinit::OSMessageQueue> s_systemMessageQueuePtr;
|
||||||
|
SysAllocator<coreinit::OSEvent> s_eventStateMessageReceived;
|
||||||
|
SysAllocator<coreinit::OSEvent> s_eventWaitingBeforeReleaseForeground;
|
||||||
|
SysAllocator<coreinit::OSEvent> s_eventBackgroundThreadGotMessage;
|
||||||
|
// procUI core threads
|
||||||
|
uint32 s_coreThreadStackSize;
|
||||||
|
bool s_coreThreadsCreated;
|
||||||
|
std::atomic<ProcUICoreThreadCommand> s_commandForCoreThread;
|
||||||
|
SysAllocator<OSThread_t> s_coreThreadArray[Espresso::CORE_COUNT];
|
||||||
|
MEMPTR<void> s_coreThreadStackPerCore[Espresso::CORE_COUNT];
|
||||||
|
SysAllocator<CafeString<22>> s_coreThread0NameBuffer;
|
||||||
|
SysAllocator<CafeString<22>> s_coreThread1NameBuffer;
|
||||||
|
SysAllocator<CafeString<22>> s_coreThread2NameBuffer;
|
||||||
|
SysAllocator<coreinit::OSEvent> s_eventCoreThreadsNewCommandReady;
|
||||||
|
SysAllocator<coreinit::OSEvent> s_eventCoreThreadsCommandDone;
|
||||||
|
SysAllocator<coreinit::OSRendezvous> s_coreThreadRendezvousA;
|
||||||
|
SysAllocator<coreinit::OSRendezvous> s_coreThreadRendezvousB;
|
||||||
|
SysAllocator<coreinit::OSRendezvous> s_coreThreadRendezvousC;
|
||||||
|
// background thread
|
||||||
|
MEMPTR<void> s_backgroundThreadStack;
|
||||||
|
SysAllocator<OSThread_t> s_backgroundThread;
|
||||||
|
// user defined heap
|
||||||
|
MEMPTR<void> s_memoryPoolHeapPtr;
|
||||||
|
MEMPTR<void> s_memAllocPtr;
|
||||||
|
MEMPTR<void> s_memFreePtr;
|
||||||
|
// draw done release
|
||||||
|
bool s_drawDoneReleaseCalled;
|
||||||
|
// memory storage
|
||||||
|
MEMPTR<void> s_bucketStorageBasePtr;
|
||||||
|
MEMPTR<void> 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<OSDriverInterface> s_ProcUIDriver;
|
||||||
|
SysAllocator<CafeString<16>> s_ProcUIDriverName;
|
||||||
|
|
||||||
|
|
||||||
uint32 ProcUIInForeground(PPCInterpreter_t* hCPU)
|
void* _AllocMem(uint32 size)
|
||||||
{
|
{
|
||||||
return 1; // true means application is in foreground
|
MEMPTR<void> r{PPCCoreCallback(s_memAllocPtr, size)};
|
||||||
}
|
return r.GetPtr();
|
||||||
|
}
|
||||||
|
|
||||||
struct ProcUICallback
|
void _FreeMem(void* ptr)
|
||||||
{
|
{
|
||||||
MPTR callback;
|
PPCCoreCallback(s_memFreePtr.GetMPTR(), ptr);
|
||||||
void* data;
|
}
|
||||||
sint32 priority;
|
|
||||||
|
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<coreinit::OSAlarm_t>(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<<coreIndex), OSThread_t::THREAD_TYPE::TYPE_DRIVER);
|
||||||
|
OSResumeThread(&s_coreThreadArray[coreIndex]);
|
||||||
|
}
|
||||||
|
s_coreThread0NameBuffer->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<void> callbackReadyToRelease)
|
||||||
|
{
|
||||||
|
s_saveCallback = callbackReadyToRelease;
|
||||||
|
s_saveCallbackEx = nullptr;
|
||||||
|
s_saveCallbackExUserParam = nullptr;
|
||||||
|
ProcUIInitInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcUIInitEx(MEMPTR<void> callbackReadyToReleaseEx, MEMPTR<void> 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<void> r = MEMAllocFromExpHeapEx((MEMHeapHandle)s_memoryPoolHeapPtr.GetPtr(), size, 4);
|
||||||
|
osLib_returnFromFunction(hCPU, r.GetMPTR());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _FreeToMemoryPoolExpHeap(PPCInterpreter_t* hCPU)
|
||||||
|
{
|
||||||
|
MEMPTR<void> 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<void> 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<void> 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<SysMessageId>((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<SysMessageId>((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<void> memBound;
|
||||||
|
uint32be memBoundSize;
|
||||||
|
OSGetMemBound(1, (MPTR*)memBound.GetBEPtr(), (uint32*)&memBoundSize);
|
||||||
|
OSBlockMove(s_mem1StorageBasePtr.GetPtr(), memBound.GetPtr(), memBoundSize, true);
|
||||||
|
}
|
||||||
|
if (s_bucketStorageBasePtr)
|
||||||
|
{
|
||||||
|
MEMPTR<void> memBound;
|
||||||
|
uint32be memBoundSize;
|
||||||
|
OSGetForegroundBucketFreeArea((MPTR*)memBound.GetBEPtr(), (MPTR*)&memBoundSize);
|
||||||
|
OSBlockMove(s_bucketStorageBasePtr.GetPtr(), memBound.GetPtr(), memBoundSize, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreMEM1AndFGBucket()
|
||||||
|
{
|
||||||
|
if (s_mem1StorageBasePtr)
|
||||||
|
{
|
||||||
|
MEMPTR<void> 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<void> 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<const char> 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<uint32, ProcUICallback> 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);
|
|
||||||
}
|
|
@ -1,5 +1,44 @@
|
|||||||
|
|
||||||
void procui_load();
|
namespace proc_ui
|
||||||
|
{
|
||||||
|
enum class ProcUIStatus
|
||||||
|
{
|
||||||
|
Foreground = 0,
|
||||||
|
Background = 1,
|
||||||
|
Releasing = 2,
|
||||||
|
Exit = 3
|
||||||
|
};
|
||||||
|
|
||||||
void ProcUI_SendForegroundMessage();
|
enum class ProcUICallbackId
|
||||||
void ProcUI_SendBackgroundMessage();
|
{
|
||||||
|
AcquireForeground = 0,
|
||||||
|
ReleaseForeground = 1,
|
||||||
|
Exit = 2,
|
||||||
|
NetIoStart = 3,
|
||||||
|
NetIoStop = 4,
|
||||||
|
HomeButtonDenied = 5,
|
||||||
|
COUNT = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
void ProcUIInit(MEMPTR<void> callbackReadyToRelease);
|
||||||
|
void ProcUIInitEx(MEMPTR<void> callbackReadyToReleaseEx, MEMPTR<void> 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();
|
||||||
|
}
|
@ -639,11 +639,34 @@ namespace sysapp
|
|||||||
return coreinit::OSRestartGame(argc, argv);
|
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()
|
void load()
|
||||||
{
|
{
|
||||||
cafeExportRegisterFunc(SYSClearSysArgs, "sysapp", "SYSClearSysArgs", LogType::Placeholder);
|
cafeExportRegisterFunc(SYSClearSysArgs, "sysapp", "SYSClearSysArgs", LogType::Placeholder);
|
||||||
cafeExportRegisterFunc(_SYSLaunchTitleByPathFromLauncher, "sysapp", "_SYSLaunchTitleByPathFromLauncher", LogType::Placeholder);
|
cafeExportRegisterFunc(_SYSLaunchTitleByPathFromLauncher, "sysapp", "_SYSLaunchTitleByPathFromLauncher", LogType::Placeholder);
|
||||||
cafeExportRegisterFunc(SYSRelaunchTitle, "sysapp", "SYSRelaunchTitle", LogType::Placeholder);
|
cafeExportRegisterFunc(SYSRelaunchTitle, "sysapp", "SYSRelaunchTitle", LogType::Placeholder);
|
||||||
|
cafeExportRegister("sysapp", _SYSSwitchToEManual, LogType::Placeholder);
|
||||||
|
cafeExportRegister("sysapp", SYSSwitchToEManual, LogType::Placeholder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,11 @@ class CafeString // fixed buffer size, null-terminated, PPC char
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* c_str()
|
||||||
|
{
|
||||||
|
return (const char*)data;
|
||||||
|
}
|
||||||
|
|
||||||
uint8be data[N];
|
uint8be data[N];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user