From 244aeb00dd42e9276dd499eb7318e8d28ede85f4 Mon Sep 17 00:00:00 2001 From: Arian Kordi Date: Fri, 26 Jun 2026 17:27:24 -0400 Subject: [PATCH 1/5] Properly export FSRewindDir method (used by FFLUtility: 000500001fd00400) --- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 3 +++ src/Cafe/OS/libs/coreinit/coreinit_FS.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 12ddb8df..433c83e9 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -570,6 +570,7 @@ namespace coreinit case FSA_CMD_OPERATION_TYPE::MAKEDIR: case FSA_CMD_OPERATION_TYPE::REMOVE: case FSA_CMD_OPERATION_TYPE::RENAME: + case FSA_CMD_OPERATION_TYPE::REWINDDIR: case FSA_CMD_OPERATION_TYPE::CLOSEDIR: case FSA_CMD_OPERATION_TYPE::READ: case FSA_CMD_OPERATION_TYPE::WRITE: @@ -2718,6 +2719,8 @@ namespace coreinit cafeExportRegister("coreinit", FSReadDir, LogType::CoreinitFile); cafeExportRegister("coreinit", FSCloseDirAsync, LogType::CoreinitFile); cafeExportRegister("coreinit", FSCloseDir, LogType::CoreinitFile); + cafeExportRegister("coreinit", FSRewindDirAsync, LogType::CoreinitFile); + cafeExportRegister("coreinit", FSRewindDir, LogType::CoreinitFile); // stat cafeExportRegister("coreinit", FSGetFreeSpaceSizeAsync, LogType::CoreinitFile); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.h b/src/Cafe/OS/libs/coreinit/coreinit_FS.h index bf12e33c..76dcac85 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.h @@ -292,6 +292,8 @@ namespace coreinit sint32 FSReadDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, FSDirEntry_t* dirEntryOut, uint32 errorMask, FSAsyncParams* fsAsyncParams); sint32 FSCloseDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask, FSAsyncParams* fsAsyncParams); sint32 FSCloseDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask); + sint32 FSRewindDirAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask, FSAsyncParams* fsAsyncParams); + sint32 FSRewindDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask); sint32 FSFlushQuotaAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask, FSAsyncParams* fsAsyncParams); sint32 FSFlushQuota(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask); From 1e1b2c14c51808d363652e2315afb366a2e38a4d Mon Sep 17 00:00:00 2001 From: Arian Kordi Date: Wed, 8 Jul 2026 19:08:05 -0400 Subject: [PATCH 2/5] Fix Mii Maker texture export + remove GX2SetColorBuffer assertion for linear special. --- src/Cafe/HW/Latte/Core/LatteTexture.cpp | 5 +++-- src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.cpp b/src/Cafe/HW/Latte/Core/LatteTexture.cpp index 43f64b2c..1d3c9686 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp @@ -1307,8 +1307,9 @@ LatteTexture::LatteTexture(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddre } } } - // determine if this texture should ever be mirrored to CPU RAM - if (this->tileMode == Latte::E_HWTILEMODE::TM_LINEAR_ALIGNED) + // determine if this texture should ever be mirrored to CPU RAM. + if (this->tileMode == Latte::E_HWTILEMODE::TM_LINEAR_ALIGNED || + this->tileMode == Latte::E_HWTILEMODE::TM_LINEAR_GENERAL) { this->enableReadback = true; } diff --git a/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp b/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp index 4ef37b18..7c25ae95 100644 --- a/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp @@ -176,8 +176,6 @@ void gx2Export_GX2SetColorBuffer(PPCInterpreter_t* hCPU) gx2WriteGather_submitU32AsBE(mmCB_COLOR0_SIZE - 0xA000 + hCPU->gpr[4]); gx2WriteGather_submitU32AsBE((uint32)colorBufferBE->reg_size); - cemu_assert_debug(tileMode != Latte::E_GX2TILEMODE::TM_LINEAR_SPECIAL); - // set mmCB_COLOR*_VIEW gx2WriteGather_submit( pm4HeaderType3(IT_SET_CONTEXT_REG, 2), From fa884c8684f8c0cd745828a71ed9183fe4f0f4e4 Mon Sep 17 00:00:00 2001 From: Arian Kordi Date: Thu, 9 Jul 2026 00:32:17 -0400 Subject: [PATCH 3/5] FSAddClientEx: Invoke SD card attach callback, so that Mii Maker knows it's present. --- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 46 ++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 433c83e9..940ad2ec 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -275,7 +275,37 @@ namespace coreinit cemuLog_log(LogType::Force, "FSShutdown called"); } - FS_RESULT FSAddClientEx(FSClient_t* fsClient, uint32 uknR4, uint32 errHandling) + /// Buffer for the notification passed to the FSAddClientEx callback. + /// Only the name at 0x18 ("sdcard") is read out of this from Mii Maker. + SysAllocator s_fsClientNotification; + // NOTE: Size is unknown, just using something generous. + + /// Notifies the guest that the SD card is present when they + /// register a callback via FSAddClientEx's r4 parameter. + static void __FSInvokeNotificationSDCard(MPTR notifyParamMPTR) + { + constexpr uint32 nameOffset = 0x1c; + + // The callback should be non-null before reaching this point. + MPTR callbackMPTR = memory_readU32(notifyParamMPTR + 0x00); + cemu_assert_debug(callbackMPTR != MPTR_NULL); + MPTR contextMPTR = memory_readU32(notifyParamMPTR + 0x04); // User object/context pointer. + + // Zero-initialize the notification. + uint8* pNotification = s_fsClientNotification.GetPtr(); + memset(pNotification, 0, s_fsClientNotification.GetByteSize()); + // Copy the string "sdcard" into the notification at 0x18. + // Mii Maker USA v50 compares this at 0202e6a4 + char* pMountName = reinterpret_cast(pNotification + nameOffset); + strcpy(pMountName, "sdcard"); + + // Invoke the user's callback. This happens synchronously in the same thread. + PPCCoreCallback(callbackMPTR, + 0 /* r3 = Unknown */, 1 /* r4 = Set to 0 if not present */, + s_fsClientNotification.GetMPTR(), contextMPTR); + } + + FS_RESULT FSAddClientEx(FSClient_t* fsClient, MPTR notifyParam, uint32 errHandling) { if (!sFSInitialized || sFSShutdown || !fsClient) { @@ -283,13 +313,13 @@ namespace coreinit return FS_RESULT::FATAL_ERROR; } FSLockMutex(); - if (uknR4 != 0) + if (notifyParam != MPTR_NULL) { - uint32 uknValue = memory_readU32(uknR4 + 0x00); - if (uknValue == 0) + MPTR callbackMPTR = memory_readU32(notifyParam + 0x00); + if (callbackMPTR == MPTR_NULL) { FSUnlockMutex(); - __FSErrorAndBlock("FSAddClientEx - unknown error"); + __FSErrorAndBlock("FSAddClientEx: Notification parameter was passed, but the inner callback is null."); return FS_RESULT::FATAL_ERROR; } } @@ -324,6 +354,12 @@ namespace coreinit fsClientBody->fsClientBodyNext = nullptr; } FSUnlockMutex(); + + // Emulate a notification that the SD card (always mounted) was + // attached, if the title (e.g. Mii Maker) specified a callback. + if (notifyParam != MPTR_NULL) + __FSInvokeNotificationSDCard(notifyParam); + return FS_RESULT::SUCCESS; } From 0ed2638da3cd6d176f2a4c9c632da1fc19792ba7 Mon Sep 17 00:00:00 2001 From: Arian Kordi Date: Thu, 9 Jul 2026 12:40:19 -0400 Subject: [PATCH 4/5] Revert "FSAddClientEx: Invoke SD card attach callback, so that Mii Maker knows it's present." This reverts commit fa884c8684f8c0cd745828a71ed9183fe4f0f4e4. --- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 46 +++-------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 940ad2ec..433c83e9 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -275,37 +275,7 @@ namespace coreinit cemuLog_log(LogType::Force, "FSShutdown called"); } - /// Buffer for the notification passed to the FSAddClientEx callback. - /// Only the name at 0x18 ("sdcard") is read out of this from Mii Maker. - SysAllocator s_fsClientNotification; - // NOTE: Size is unknown, just using something generous. - - /// Notifies the guest that the SD card is present when they - /// register a callback via FSAddClientEx's r4 parameter. - static void __FSInvokeNotificationSDCard(MPTR notifyParamMPTR) - { - constexpr uint32 nameOffset = 0x1c; - - // The callback should be non-null before reaching this point. - MPTR callbackMPTR = memory_readU32(notifyParamMPTR + 0x00); - cemu_assert_debug(callbackMPTR != MPTR_NULL); - MPTR contextMPTR = memory_readU32(notifyParamMPTR + 0x04); // User object/context pointer. - - // Zero-initialize the notification. - uint8* pNotification = s_fsClientNotification.GetPtr(); - memset(pNotification, 0, s_fsClientNotification.GetByteSize()); - // Copy the string "sdcard" into the notification at 0x18. - // Mii Maker USA v50 compares this at 0202e6a4 - char* pMountName = reinterpret_cast(pNotification + nameOffset); - strcpy(pMountName, "sdcard"); - - // Invoke the user's callback. This happens synchronously in the same thread. - PPCCoreCallback(callbackMPTR, - 0 /* r3 = Unknown */, 1 /* r4 = Set to 0 if not present */, - s_fsClientNotification.GetMPTR(), contextMPTR); - } - - FS_RESULT FSAddClientEx(FSClient_t* fsClient, MPTR notifyParam, uint32 errHandling) + FS_RESULT FSAddClientEx(FSClient_t* fsClient, uint32 uknR4, uint32 errHandling) { if (!sFSInitialized || sFSShutdown || !fsClient) { @@ -313,13 +283,13 @@ namespace coreinit return FS_RESULT::FATAL_ERROR; } FSLockMutex(); - if (notifyParam != MPTR_NULL) + if (uknR4 != 0) { - MPTR callbackMPTR = memory_readU32(notifyParam + 0x00); - if (callbackMPTR == MPTR_NULL) + uint32 uknValue = memory_readU32(uknR4 + 0x00); + if (uknValue == 0) { FSUnlockMutex(); - __FSErrorAndBlock("FSAddClientEx: Notification parameter was passed, but the inner callback is null."); + __FSErrorAndBlock("FSAddClientEx - unknown error"); return FS_RESULT::FATAL_ERROR; } } @@ -354,12 +324,6 @@ namespace coreinit fsClientBody->fsClientBodyNext = nullptr; } FSUnlockMutex(); - - // Emulate a notification that the SD card (always mounted) was - // attached, if the title (e.g. Mii Maker) specified a callback. - if (notifyParam != MPTR_NULL) - __FSInvokeNotificationSDCard(notifyParam); - return FS_RESULT::SUCCESS; } From 345ba088d1972f70374dcb98b4253cfadc632590 Mon Sep 17 00:00:00 2001 From: Arian Kordi Date: Thu, 9 Jul 2026 12:39:58 -0400 Subject: [PATCH 5/5] coreinit_FS: Invoke attach callback after FSAddClientEx to inform title that SD card was inserted. --- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 38 +++++++++++++++++------ src/Cafe/OS/libs/coreinit/coreinit_FS.h | 9 +++++- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 433c83e9..2634c2bf 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -275,7 +275,23 @@ namespace coreinit cemuLog_log(LogType::Force, "FSShutdown called"); } - FS_RESULT FSAddClientEx(FSClient_t* fsClient, uint32 uknR4, uint32 errHandling) + struct AttachCallbackData + { + /* +0x00 */ uint8 unk[0x1c]; + /* +0x1c */ char mountName[8]; + }; + + SysAllocator s_fsAttachCallbackData; + + void invokeAttachCallback(FSAttachParams& attachParams) + { + *s_fsAttachCallbackData = { .mountName = "sdcard" /* expected by Mii Maker */ }; + PPCCoreCallback(attachParams.userCallback, + 0 /* r3 = unknown */, 1 /* r4 = non-zero means device is attached */, + s_fsAttachCallbackData.GetMPTR(), attachParams.userContext); + } + + FS_RESULT FSAddClientEx(FSClient_t* fsClient, FSAttachParams* attachParams, uint32 errHandling) { if (!sFSInitialized || sFSShutdown || !fsClient) { @@ -283,15 +299,13 @@ namespace coreinit return FS_RESULT::FATAL_ERROR; } FSLockMutex(); - if (uknR4 != 0) + if (attachParams != nullptr && + attachParams->userCallback == nullptr) { - uint32 uknValue = memory_readU32(uknR4 + 0x00); - if (uknValue == 0) - { - FSUnlockMutex(); - __FSErrorAndBlock("FSAddClientEx - unknown error"); - return FS_RESULT::FATAL_ERROR; - } + FSUnlockMutex(); + // "FS: FSAddClient: attachParams->userCallback must be set.\n" + __FSErrorAndBlock("FSAddClientEx - unknown error"); + return FS_RESULT::FATAL_ERROR; } if (__FSIsClientRegistered(__FSGetClientBody(fsClient))) { @@ -324,12 +338,16 @@ namespace coreinit fsClientBody->fsClientBodyNext = nullptr; } FSUnlockMutex(); + + if (attachParams != nullptr) // invoke right before returning + invokeAttachCallback(*attachParams); + return FS_RESULT::SUCCESS; } FS_RESULT FSAddClient(FSClient_t* fsClient, uint32 errHandling) { - return FSAddClientEx(fsClient, 0, errHandling); + return FSAddClientEx(fsClient, nullptr, errHandling); } FS_RESULT FSDelClient(FSClient_t* fsClient, uint32 errHandling) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.h b/src/Cafe/OS/libs/coreinit/coreinit_FS.h index 76dcac85..286735fc 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.h @@ -18,6 +18,13 @@ struct FSAsyncParams }; static_assert(sizeof(FSAsyncParams) == 0xC); +struct FSAttachParams +{ + MEMPTR userCallback; + MEMPTR userContext; +}; +static_assert(sizeof(FSAttachParams) == 0x8); + namespace coreinit { struct FSCmdBlockBody; @@ -242,7 +249,7 @@ namespace coreinit sint32 __FSQueryInfoAsync(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* queryString, uint32 queryType, void* queryResult, uint32 errHandling, FSAsyncParams* fsAsyncParams); // coreinit exports - FS_RESULT FSAddClientEx(FSClient_t* fsClient, uint32 uknR4, uint32 errHandling); + FS_RESULT FSAddClientEx(FSClient_t* fsClient, FSAttachParams* attachParams, uint32 errHandling); FS_RESULT FSAddClient(FSClient_t* fsClient, uint32 errHandling); FS_RESULT FSDelClient(FSClient_t* fsClient, uint32 errHandling);