From f82598e42f6e2af7efa21a598d51704b830ea9a1 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Fri, 17 Jul 2026 23:11:12 +0200 Subject: [PATCH 1/5] build: Fix CMAKE_CXX_FLAGS --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ca6d68f..e8373f9d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ project(cemuMain) option(CEMU_CXX_FLAGS "Additional flags used for compiling Cemu source code") +separate_arguments(CEMU_CXX_FLAGS NATIVE_COMMAND ${CEMU_CXX_FLAGS}) if(CEMU_CXX_FLAGS) add_compile_options(${CEMU_CXX_FLAGS}) endif() From 3005cb7b46e3b9c45644d4105ae3a984f87336fb Mon Sep 17 00:00:00 2001 From: techmuse <44119098+techmuse8@users.noreply.github.com> Date: Fri, 17 Jul 2026 20:36:43 -0400 Subject: [PATCH 2/5] coreinit: Implement a few more DynLoad functions (#1990) --- src/Cafe/OS/RPL/rpl.cpp | 19 +++++++ src/Cafe/OS/RPL/rpl.h | 1 + src/Cafe/OS/RPL/rpl_structs.h | 1 + .../OS/libs/coreinit/coreinit_DynLoad.cpp | 54 ++++++++++++++++++- src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h | 21 ++++++++ 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp index 5cac94cf..b2eb4d0e 100644 --- a/src/Cafe/OS/RPL/rpl.cpp +++ b/src/Cafe/OS/RPL/rpl.cpp @@ -273,6 +273,13 @@ bool RPLLoader_ProcessHeaders(std::string_view moduleName, uint8* rplData, uint3 // init modulename rplLoaderContext->moduleName.assign(moduleName); + std::string fileName{moduleName}; + fileName.append(rplLoaderContext->IsRPX() ? ".rpx" : ".rpl"); + + // allocate modulename in PPC memory + rplLoaderContext->ppcName = coreinit_allocFromSysArea(fileName.size() + 1, 4); + memcpy(rplLoaderContext->ppcName.GetPtr(), fileName.data(), fileName.size() + 1); + // load CRC section uint32 crcTableExpectedSize = sectionCount * sizeof(uint32be); if (!RPLLoader_CheckBounds(rplLoaderContext, crcSection->fileOffset, crcTableExpectedSize)) @@ -2070,6 +2077,18 @@ uint32 RPLLoader_GetHandleByModuleName(const char* name) return RPL_INVALID_HANDLE; } +const std::string RPLLoader_GetModuleNameByHandle(uint32 handle) +{ + for (auto& dep : rplDependencyList) + { + if (dep->coreinitHandle == handle) + return dep->moduleName; + } + + return ""; + +} + uint32 RPLLoader_GetMaxTLSModuleIndex() { return rplLoader_currentTlsModuleIndex - 1; diff --git a/src/Cafe/OS/RPL/rpl.h b/src/Cafe/OS/RPL/rpl.h index 1f7205bc..4d670444 100644 --- a/src/Cafe/OS/RPL/rpl.h +++ b/src/Cafe/OS/RPL/rpl.h @@ -36,6 +36,7 @@ void RPLLoader_UpdateDependencies(); void RPLLoader_LoadCoreinit(); uint32 RPLLoader_GetHandleByModuleName(const char* name); +const std::string RPLLoader_GetModuleNameByHandle(uint32 handle); uint32 RPLLoader_GetMaxTLSModuleIndex(); bool RPLLoader_GetTLSDataByTLSIndex(sint16 tlsModuleIndex, uint8** tlsData, sint32* tlsSize); diff --git a/src/Cafe/OS/RPL/rpl_structs.h b/src/Cafe/OS/RPL/rpl_structs.h index 8dd126b0..578943d9 100644 --- a/src/Cafe/OS/RPL/rpl_structs.h +++ b/src/Cafe/OS/RPL/rpl_structs.h @@ -156,6 +156,7 @@ struct RPLModule MEMPTR regionMappingBase_text; // base destination address for text region MPTR regionMappingBase_data; // base destination address for data region MPTR regionMappingBase_loaderInfo; // base destination address for loaderInfo region + MEMPTR ppcName; // name of the module in PPC memory uint8* tempRegionPtr; uint32 tempRegionAllocSize; diff --git a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp index 5aa1fef5..45a2b71f 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp @@ -1,6 +1,7 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/HW/Espresso/PPCCallback.h" #include "Cafe/OS/RPL/rpl.h" +#include "Cafe/OS/RPL/rpl_structs.h" #include "Cafe/OS/libs/coreinit/coreinit_DynLoad.h" #include "Cafe/OS/libs/coreinit/coreinit_MEM.h" @@ -146,6 +147,54 @@ namespace coreinit return 0; } + uint32 OSDynLoad_GetModuleName(uint32 moduleHandle, char* nameBuf, sint32be* nameBufSize) + { + if (moduleHandle == 0xFFFFFFFF) + { + // main module + // Assassins Creed 4 has this handle hardcoded + moduleHandle = RPLLoader_GetMainModuleHandle(); + } + + const std::string moduleName = RPLLoader_GetModuleNameByHandle(moduleHandle); + std::strncpy(nameBuf, moduleName.c_str(), *nameBufSize); + + return 0; + + } + + sint32 OSDynLoad_GetNumberOfRPLs() + { + return RPLLoader_GetModuleCount(); + } + + uint32 OSDynLoad_GetRPLInfo(uint32 first, uint32 count, OSDynLoad_NotifyData* outInfos) + { + if (count == 0) + return 1; + RPLModule** modules = RPLLoader_GetModuleList(); + + for (uint32 i = first; i < count; i++) + { + outInfos[i].name = modules[i]->ppcName.GetMPTR(); + + outInfos[i].textAddr = modules[i]->regionMappingBase_text.GetBEValue(); + outInfos[i].textOffset = modules[i]->regionMappingBase_text.GetMPTR() - modules[i]->regionOrigAddr_text; + outInfos[i].textSize = modules[i]->regionSize_text; + + outInfos[i].dataAddr = modules[i]->regionMappingBase_data; + outInfos[i].dataOffset = modules[i]->regionMappingBase_data - modules[i]->regionOrigAddr_data; + outInfos[i].dataSize = modules[i]->regionSize_data; + + outInfos[i].readAddr = modules[i]->regionMappingBase_data; + outInfos[i].readOffset = modules[i]->regionMappingBase_data - modules[i]->regionOrigAddr_data; + outInfos[i].readSize = modules[i]->regionSize_data; + + } + + return 1; + } + void InitializeDynLoad() { cafeExportRegister("coreinit", OSDynLoad_SetAllocator, LogType::Placeholder); @@ -157,5 +206,8 @@ namespace coreinit cafeExportRegister("coreinit", OSDynLoad_Release, LogType::Placeholder); cafeExportRegister("coreinit", OSDynLoad_IsModuleLoaded, LogType::Placeholder); cafeExportRegister("coreinit", OSDynLoad_FindExport, LogType::Placeholder); + cafeExportRegister("coreinit", OSDynLoad_GetModuleName, LogType::Placeholder); + cafeExportRegister("coreinit", OSDynLoad_GetNumberOfRPLs, LogType::Placeholder); + cafeExportRegister("coreinit", OSDynLoad_GetRPLInfo, LogType::Placeholder); } -} \ No newline at end of file +} diff --git a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h index 0be8226c..ad1662e0 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h @@ -2,6 +2,23 @@ namespace coreinit { + struct OSDynLoad_NotifyData + { + MEMPTR name; + + uint32be textAddr; + uint32be textOffset; + uint32be textSize; + + uint32be dataAddr; + uint32be dataOffset; + uint32be dataSize; + + uint32be readAddr; + uint32be readOffset; + uint32be readSize; + }; + uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc); void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc); uint32 OSDynLoad_GetAllocator(betype* funcAlloc, betype* funcFree); @@ -14,5 +31,9 @@ namespace coreinit void OSDynLoad_Release(uint32 moduleHandle); uint32 OSDynLoad_FindExport(uint32 moduleHandle, uint32 isData, const char* exportName, betype* addrOut); + uint32 OSDynLoad_GetModuleName(uint32 moduleHandle, char* nameBuf, sint32be* nameBufSize); + sint32 OSDynLoad_GetNumberOfRPLs(); + uint32 OSDynLoad_GetRPLInfo(uint32 first, uint32 count, OSDynLoad_NotifyData* outInfos); + void InitializeDynLoad(); } \ No newline at end of file From 13d1826b4d018c2b6674f58b9b772e21ba084f61 Mon Sep 17 00:00:00 2001 From: primaviera <147349656+primaviera@users.noreply.github.com> Date: Sun, 19 Jul 2026 03:08:57 -0300 Subject: [PATCH 3/5] nn_fp: Improve Comment struct and fix invalid Mii name on Linux (#1986) --- src/Cafe/IOSU/legacy/iosu_fpd.cpp | 30 ++++++++++------------- src/Cafe/IOSU/legacy/iosu_fpd.h | 40 ++++++++++++++++++++----------- src/Cafe/OS/libs/nn_fp/nn_fp.cpp | 9 ++++--- src/Cemu/nex/nexFriends.cpp | 8 ++++--- src/Cemu/nex/nexFriends.h | 10 ++++---- src/util/helpers/StringHelpers.h | 15 +++++++++++- 6 files changed, 66 insertions(+), 46 deletions(-) diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.cpp b/src/Cafe/IOSU/legacy/iosu_fpd.cpp index 0445d309..b3f7e5d9 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.cpp +++ b/src/Cafe/IOSU/legacy/iosu_fpd.cpp @@ -68,7 +68,7 @@ namespace iosu std::atomic_uint64_t m_notificationQueueIndex{1}; }g_NotificationQueue; - struct + struct { bool isThreadStarted; bool isInitialized2; @@ -214,12 +214,9 @@ namespace iosu friendData->friendExtraData.gameKey.ukn08 = frd->presence.gameKey.ukn; NexPresenceToGameMode(&frd->presence, &friendData->friendExtraData.gameMode); - auto fixed_presence_msg = '\0' + frd->presence.msg; // avoid first character of comment from being cut off - friendData->friendExtraData.gameModeDescription.assignFromUTF8(fixed_presence_msg); - - auto fixed_comment = '\0' + frd->comment.commentString; // avoid first character of comment from being cut off - friendData->friendExtraData.comment.assignFromUTF8(fixed_comment); - + friendData->friendExtraData.gameModeDescription.assignFromUTF8(frd->presence.msg); + friendData->friendExtraData.comment.commentString.assignFromUTF8(frd->comment.commentString); + // set valid dates friendData->uknDate.year = 2018; friendData->uknDate.day = 1; @@ -293,7 +290,7 @@ namespace iosu memset(friendRequest, 0, sizeof(FriendRequest)); friendRequest->pid = frdReq->principalInfo.principalId; - + strncpy((char*)friendRequest->nnid, frdReq->principalInfo.nnid, sizeof(friendRequest->nnid)); friendRequest->nnid[sizeof(friendRequest->nnid) - 1] = '\0'; @@ -723,20 +720,19 @@ namespace iosu { if(numVecIn != 0 || numVecOut != 1) return FPResult_InvalidIPCParam; - std::vector myComment; + Comment outComment; if(g_fpd.nexFriendSession) { - if(vecOut->size != MY_COMMENT_LENGTH * sizeof(uint16be)) + if(vecOut->size != sizeof(Comment)) { cemuLog_log(LogType::Force, "GetMyComment: Unexpected output size"); return FPResult_InvalidIPCParam; } nexComment myNexComment; g_fpd.nexFriendSession->getMyComment(myNexComment); - myComment = StringHelpers::FromUtf8(myNexComment.commentString); + outComment.commentString.assignFromUTF8(myNexComment.commentString); } - myComment.insert(myComment.begin(), '\0'); - memcpy(vecOut->basePhys.GetPtr(), myComment.data(), MY_COMMENT_LENGTH * sizeof(uint16be)); + memcpy(vecOut->basePhys.GetPtr(), &outComment, sizeof(Comment)); return FPResult_Ok; } @@ -1166,10 +1162,9 @@ namespace iosu } IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); - auto utf8_comment = StringHelpers::ToUtf8(newComment, messageLength); nexComment temporaryComment; temporaryComment.ukn0 = 0; - temporaryComment.commentString = utf8_comment; + temporaryComment.commentString = StringHelpers::ToUtf8(newComment, messageLength-1); temporaryComment.ukn1 = 0; g_fpd.nexFriendSession->updateCommentAsync(temporaryComment, [cmd](NexFriends::RpcErrorCode result) { @@ -1456,7 +1451,7 @@ namespace iosu iosu::act::getAccountId(currentSlot, accountId); FFLData_t miiData; act::getMii(currentSlot, &miiData); - uint16 screenName[ACT_NICKNAME_LENGTH + 1] = { 0 }; + uint16 screenName[ACT_NICKNAME_SIZE] = { 0 }; act::getScreenname(currentSlot, screenName); uint32 countryCode = 0; act::getCountryIndex(currentSlot, &countryCode); @@ -1488,7 +1483,7 @@ namespace iosu const uint32_t hostIp = ((struct sockaddr_in*)addrs->ai_addr)->sin_addr.s_addr; freeaddrinfo(addrs); g_fpd.mtxFriendSession.lock(); - g_fpd.nexFriendSession = new NexFriends(hostIp, nexTokenResult.nexToken.port, "ridfebb9", myPid, nexTokenResult.nexToken.nexPassword, nexTokenResult.nexToken.token, accountId, (uint8*)&miiData, (wchar_t*)screenName, (uint8)countryCode, g_fpd.myPresence); + g_fpd.nexFriendSession = new NexFriends(hostIp, nexTokenResult.nexToken.port, "ridfebb9", myPid, nexTokenResult.nexToken.nexPassword, nexTokenResult.nexToken.token, accountId, (uint8*)&miiData, screenName, (uint8)countryCode, g_fpd.myPresence); g_fpd.nexFriendSession->setNotificationHandler(NotificationHandler); g_fpd.mtxFriendSession.unlock(); cemuLog_log(LogType::Force, "IOSU_FPD: Created friend server session"); @@ -1532,4 +1527,3 @@ namespace iosu } } - diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.h b/src/Cafe/IOSU/legacy/iosu_fpd.h index b1c30765..922f499e 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.h +++ b/src/Cafe/IOSU/legacy/iosu_fpd.h @@ -6,6 +6,14 @@ namespace iosu { namespace fpd { + static const int RELATIONSHIP_INVALID = 0; + static const int RELATIONSHIP_FRIENDREQUEST_OUT = 1; + static const int RELATIONSHIP_FRIENDREQUEST_IN = 2; + static const int RELATIONSHIP_FRIEND = 3; + + static const int GAMEMODE_MAX_MESSAGE_LENGTH = 0x80; // limit includes null-terminator character, so only 0x7F actual characters can be used + static const int MY_COMMENT_LENGTH = 0x11; // includes null-terminator character + struct FPDDate { /* +0x0 */ uint16be year; @@ -67,6 +75,14 @@ namespace iosu }; static_assert(sizeof(GameMode) == 0x2C); + struct Comment + { + /* +0x00 */ uint8 unk0; + /* +0x01 */ uint8 unk1; + /* +0x02 */ CafeWideString commentString; + }; + static_assert(sizeof(Comment) == 0x24); + struct FriendData { /* +0x000 */ uint8 type; // type (Non-Zero -> Friend, 0 -> Friend request ? ) @@ -83,23 +99,27 @@ namespace iosu // sub struct (the part above seems to be shared with friend requests) union { - struct + struct { /* +0x0A0 */ Profile profile; // this is returned for nn_fp.GetFriendProfile /* +0x0A4 */ uint32be ukn0A4; /* +0x0A8 */ GameKey gameKey; /* +0x0B8 */ GameMode gameMode; - /* +0x0E4 */ CafeWideString<0x82> gameModeDescription; + /* +0x0E4 */ uint8 unk0E4; + /* +0x0E5 */ uint8 unk0E5; + /* +0x0E6 */ CafeWideString gameModeDescription; + /* +0x1E6 */ uint8 unk1E6; + /* +0x1E7 */ uint8 unk1E7; /* +0x1E8 */ Profile profile1E8; // how does it differ from the one at 0xA0? Returned by GetFriendPresence /* +0x1EC */ uint8 isOnline; /* +0x1ED */ uint8 _padding1ED[3]; // some other sub struct? - /* +0x1F0 */ CafeWideString<0x12> comment; // pops up every few seconds in friend list + /* +0x1F0 */ Comment comment; // pops up every few seconds in friend list /* +0x214 */ uint32be _padding214; /* +0x218 */ FPDDate approvalTime; /* +0x220 */ FPDDate lastOnline; }friendExtraData; - struct + struct { /* +0x0A0 */ uint64be messageId; // guessed. If 0, then relationship is FRIENDSHIP_REQUEST_OUT, otherwise FRIENDSHIP_REQUEST_IN /* +0x0A8 */ uint8 ukn0A8; @@ -118,9 +138,9 @@ namespace iosu }; static_assert(sizeof(FriendData) == 0x228); static_assert(offsetof(FriendData, friendExtraData.gameKey) == 0x0A8); - static_assert(offsetof(FriendData, friendExtraData.gameModeDescription) == 0x0E4); + static_assert(offsetof(FriendData, friendExtraData.gameModeDescription) == 0x0E6); static_assert(offsetof(FriendData, friendExtraData.comment) == 0x1F0); - + static_assert(offsetof(FriendData, requestExtraData.messageId) == 0x0A0); static_assert(offsetof(FriendData, requestExtraData.comment) == 0x0AA); static_assert(offsetof(FriendData, requestExtraData.uknMessage) == 0x12C); @@ -206,14 +226,6 @@ namespace iosu }; static_assert(sizeof(FPDPreference) == 4); - static const int RELATIONSHIP_INVALID = 0; - static const int RELATIONSHIP_FRIENDREQUEST_OUT = 1; - static const int RELATIONSHIP_FRIENDREQUEST_IN = 2; - static const int RELATIONSHIP_FRIEND = 3; - - static const int GAMEMODE_MAX_MESSAGE_LENGTH = 0x80; // limit includes null-terminator character, so only 0x7F actual characters can be used - static const int MY_COMMENT_LENGTH = 0x12; - enum class FPD_REQUEST_ID { LoginAsync = 0x2775, diff --git a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp index 70122dd7..3c3321fc 100644 --- a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp +++ b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp @@ -484,7 +484,7 @@ namespace nn { FP_API_BASE(); auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyComment); - ipcCtx->AddOutput(myComment, iosu::fpd::MY_COMMENT_LENGTH * sizeof(uint16be)); + ipcCtx->AddOutput(myComment, sizeof(iosu::fpd::Comment)); return ipcCtx->Submit(std::move(ipcCtx)); } @@ -627,13 +627,13 @@ namespace nn { FP_API_BASE(); auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::UpdateCommentAsync); - uint32 commentLen = CafeStringHelpers::Length(newComment, iosu::fpd::MY_COMMENT_LENGTH-1); - if (commentLen >= iosu::fpd::MY_COMMENT_LENGTH-1) + uint32 commentLen = CafeStringHelpers::Length(newComment, iosu::fpd::MY_COMMENT_LENGTH); + if (commentLen >= iosu::fpd::MY_COMMENT_LENGTH) { cemuLog_log(LogType::Force, "UpdateCommentAsync: message too long"); return FPResult_InvalidIPCParam; } - ipcCtx->AddInput(newComment, sizeof(uint16be) * commentLen + 2); + ipcCtx->AddInput(newComment, sizeof(uint16be) * (commentLen + 1)); return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } @@ -840,4 +840,3 @@ namespace nn } } - diff --git a/src/Cemu/nex/nexFriends.cpp b/src/Cemu/nex/nexFriends.cpp index 36ba4a53..f598c2d9 100644 --- a/src/Cemu/nex/nexFriends.cpp +++ b/src/Cemu/nex/nexFriends.cpp @@ -1,7 +1,9 @@ +#include "Cafe/IOSU/legacy/iosu_act.h" #include "prudp.h" #include "nex.h" #include "nexFriends.h" #include "Cafe/CafeSystem.h" +#include "util/helpers/StringHelpers.h" static const int NOTIFICATION_SRV_FRIEND_OFFLINE = 0x0A; // the opposite event (friend online) is notified via _PRESENCE_CHANGE static const int NOTIFICATION_SRV_FRIEND_PRESENCE_CHANGE = 0x18; @@ -195,7 +197,7 @@ void nexFriends_protocolNotification_processRequest(nexServiceRequest_t* request request->nex->sendRequestResponse(request, 0x80000000, nullptr, 0); } -NexFriends::NexFriends(uint32 authServerIp, uint16 authServerPort, const char* accessKey, uint32 pid, const char* nexPassword, const char* nexToken, const char* nnid, uint8* miiData, const wchar_t* miiNickname, uint8 countryCode, nexPresenceV2& myPresence) +NexFriends::NexFriends(uint32 authServerIp, uint16 authServerPort, const char* accessKey, uint32 pid, const char* nexPassword, const char* nexToken, const char* nnid, uint8* miiData, const uint16* miiNickname, uint8 countryCode, nexPresenceV2& myPresence) : nexCon(nullptr) { memcpy(this->miiData, miiData, FFL_SIZE); @@ -203,7 +205,7 @@ NexFriends::NexFriends(uint32 authServerIp, uint16 authServerPort, const char* a this->pid = pid; this->countryCode = countryCode; this->myPresence = myPresence; - this->miiNickname = boost::nowide::narrow(miiNickname); + this->miiNickname = StringHelpers::ToUtf8FromLE(miiNickname, ACT_NICKNAME_LENGTH); cemu_assert_debug(this->miiNickname.size() <= 96-1); auth.serverIp = authServerIp; auth.port = authServerPort; @@ -511,7 +513,7 @@ void NexFriends::trackNotifications() { bool entryFound = false; for (auto& frqNew : list_friendReqIncoming) - { + { if (frqNew.principalInfo.principalId == frqPrevious.principalInfo.principalId) { entryFound = true; diff --git a/src/Cemu/nex/nexFriends.h b/src/Cemu/nex/nexFriends.h index 05cc433f..67bea22c 100644 --- a/src/Cemu/nex/nexFriends.h +++ b/src/Cemu/nex/nexFriends.h @@ -264,7 +264,7 @@ public: pb->writeU8(showGame); pb->writeU8(blockFriendRequests); } - + void readData(nexPacketBuffer* pb) override { showOnline = pb->readU8(); @@ -537,7 +537,7 @@ public: }; public: - NexFriends(uint32 authServerIp, uint16 authServerPort, const char* accessKey, uint32 pid, const char* nexPassword, const char* nexToken, const char* nnid, uint8* miiData, const wchar_t* miiNickname, uint8 countryCode, nexPresenceV2& myPresence); + NexFriends(uint32 authServerIp, uint16 authServerPort, const char* accessKey, uint32 pid, const char* nexPassword, const char* nexToken, const char* nnid, uint8* miiData, const uint16* miiNickname, uint8 countryCode, nexPresenceV2& myPresence); ~NexFriends(); @@ -586,7 +586,7 @@ private: void generateNotification(NOTIFICATION_TYPE notificationType, uint32 pid); void trackNotifications(); - + // notification-service handlers void processServerNotification_friendOffline(uint32 pid); void processServerNotification_presenceChange(uint32 pid, nexPresenceV2& presence); @@ -612,7 +612,7 @@ private: uint32 numFailedLogins; uint32 numSuccessfulLogins; // auth - struct + struct { uint32 serverIp; uint16 port; @@ -629,7 +629,7 @@ private: std::vector list_friends; std::vector list_friendReqOutgoing; std::vector list_friendReqIncoming; - struct + struct { std::vector list_friends; std::vector list_friendReqOutgoing; diff --git a/src/util/helpers/StringHelpers.h b/src/util/helpers/StringHelpers.h index fb858f4d..16b35c4b 100644 --- a/src/util/helpers/StringHelpers.h +++ b/src/util/helpers/StringHelpers.h @@ -110,6 +110,20 @@ namespace StringHelpers return ToUtf8(input.data(), input.size()); } + // convert little-endian wide string to utf8 string + static std::string ToUtf8FromLE(const uint16* ptr, size_t maxLength) + { + std::wstringstream result; + while (*ptr != 0 && maxLength > 0) + { + auto c = (uint16)*ptr; + result << static_cast(c); + ptr++; + maxLength--; + } + return boost::nowide::narrow(result.str()); + } + // convert utf8 string to Wii U big-endian wchar_t string static std::vector FromUtf8(std::string_view str) { @@ -285,4 +299,3 @@ namespace StringHelpers std::string_view m_str; }; }; - From 50b9e4ba1d4d7cf9821a9cd416378bb94e1ba0ca Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Wed, 22 Jul 2026 01:20:55 +0200 Subject: [PATCH 4/5] vulkan: crash on fence errors instead of possibly spamming the log --- src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index c700fbc2..95bc0fc6 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2163,8 +2163,7 @@ void VulkanRenderer::ProcessFinishedCommandBuffers() // not signaled break; } - cemuLog_log(LogType::Force, "vkGetFenceStatus returned unexpected error {}", (sint32)fenceStatus); - cemu_assert_debug(false); + UnrecoverableError(fmt::format("vkGetFenceStatus returned unexpected error {}", (sint32)fenceStatus).c_str()); } if (finishedCmdBuffers) { @@ -2183,7 +2182,7 @@ void VulkanRenderer::WaitForNextFinishedCommandBuffer() } else if (result != VK_SUCCESS) { - cemuLog_log(LogType::Force, "vkWaitForFences: Returned unhandled error {}", (sint32)result); + UnrecoverableError(fmt::format("vkWaitForFences: Returned unhandled error {}", (sint32)result).c_str()); } // process ProcessFinishedCommandBuffers(); From b8f2cf4b431df7c1669ec926a5ea8b9fc146f310 Mon Sep 17 00:00:00 2001 From: techmuse <44119098+techmuse8@users.noreply.github.com> Date: Thu, 23 Jul 2026 01:25:39 -0400 Subject: [PATCH 5/5] coreinit: Implement Dynload notify callbacks (#2003) --- src/Cafe/OS/RPL/rpl.cpp | 14 ++++ src/Cafe/OS/RPL/rpl.h | 1 + .../OS/libs/coreinit/coreinit_DynLoad.cpp | 81 +++++++++++++++++++ src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h | 15 +++- 4 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp index b2eb4d0e..384a54a2 100644 --- a/src/Cafe/OS/RPL/rpl.cpp +++ b/src/Cafe/OS/RPL/rpl.cpp @@ -2400,6 +2400,20 @@ RPLModule** RPLLoader_GetModuleList() return rplModuleList; } +RPLModule* RPLLoader_GetModuleByName(std::string_view name) +{ + std::string normalizedName = _RPLLoader_ExtractModuleNameFromPath(name); + RPLModule** modules = RPLLoader_GetModuleList(); + + for (uint32 i = 0; i < RPLLoader_GetModuleCount(); i++) + { + if (modules[i]->moduleName == normalizedName) + return modules[i]; + } + + return nullptr; +} + sint32 RPLLoader_GetModuleCount() { return rplModuleCount; diff --git a/src/Cafe/OS/RPL/rpl.h b/src/Cafe/OS/RPL/rpl.h index 4d670444..1d9f872c 100644 --- a/src/Cafe/OS/RPL/rpl.h +++ b/src/Cafe/OS/RPL/rpl.h @@ -47,6 +47,7 @@ uint32 RPLLoader_GetSDA2Base(); sint32 RPLLoader_GetModuleCount(); RPLModule** RPLLoader_GetModuleList(); +RPLModule* RPLLoader_GetModuleByName(std::string_view name); MEMPTR RPLLoader_AllocateCodeCaveMem(uint32 alignment, uint32 size); void RPLLoader_ReleaseCodeCaveMem(MEMPTR addr); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp index 45a2b71f..86a28e57 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp @@ -12,6 +12,8 @@ namespace coreinit MPTR _osDynLoadTLSFuncAlloc = MPTR_NULL; MPTR _osDynLoadTLSFuncFree = MPTR_NULL; + static std::vector notifyCallbacks; + uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc) { _osDynLoadFuncAlloc = allocFunc; @@ -109,6 +111,34 @@ namespace coreinit cemuLog_logDebug(LogType::Force, "OSDynLoad_Acquire() failed to load module '{}'", libName); return 0xFFFCFFE9; // module not found } + + for (const auto& cb : notifyCallbacks) + { + MEMPTR notifyData; + notifyData = (OSDynLoad_NotifyData*)OSDynLoad_AllocatorAlloc(sizeof(OSDynLoad_NotifyData), 4); + RPLModule* module = RPLLoader_GetModuleByName(tempLibName); + + if (!module) + break; + + notifyData->name = module->ppcName.GetMPTR(); + + notifyData->textAddr = module->regionMappingBase_text.GetBEValue(); + notifyData->textOffset = module->regionMappingBase_text.GetMPTR() - module->regionOrigAddr_text; + notifyData->textSize = module->regionSize_text; + + notifyData->dataAddr = module->regionMappingBase_data; + notifyData->dataOffset = module->regionMappingBase_data - module->regionOrigAddr_data; + notifyData->dataSize = module->regionSize_data; + + notifyData->readAddr = module->regionMappingBase_data; + notifyData->readOffset = module->regionMappingBase_data - module->regionOrigAddr_data; + notifyData->readSize = module->regionSize_data; + + PPCCoreCallback(cb.callback, moduleHandleOut, cb.userContext.GetMPTR(), 1, notifyData.GetMPTR()); + OSDynLoad_AllocatorFree(notifyData); + + } return 0; } @@ -121,6 +151,35 @@ namespace coreinit { if (moduleHandle == RPL_INVALID_HANDLE) return; + + for (const auto& cb : notifyCallbacks) + { + MEMPTR notifyData; + notifyData = (OSDynLoad_NotifyData*)OSDynLoad_AllocatorAlloc(sizeof(OSDynLoad_NotifyData), 4); + RPLModule* module = RPLLoader_GetModuleByName(RPLLoader_GetModuleNameByHandle(moduleHandle)); + + if (!module) + break; + + notifyData->name = module->ppcName.GetMPTR(); + + notifyData->textAddr = module->regionMappingBase_text.GetBEValue(); + notifyData->textOffset = module->regionMappingBase_text.GetMPTR() - module->regionOrigAddr_text; + notifyData->textSize = module->regionSize_text; + + notifyData->dataAddr = module->regionMappingBase_data; + notifyData->dataOffset = module->regionMappingBase_data - module->regionOrigAddr_data; + notifyData->dataSize = module->regionSize_data; + + notifyData->readAddr = module->regionMappingBase_data; + notifyData->readOffset = module->regionMappingBase_data - module->regionOrigAddr_data; + notifyData->readSize = module->regionSize_data; + + PPCCoreCallback(cb.callback, moduleHandle, cb.userContext.GetMPTR(), 2, notifyData.GetMPTR()); + OSDynLoad_AllocatorFree(notifyData); + + } + RPLLoader_RemoveDependency(moduleHandle); RPLLoader_UpdateDependencies(); } @@ -195,6 +254,25 @@ namespace coreinit return 1; } + uint32 OSDynLoad_AddNotifyCallback(MEMPTR notifyFn, MEMPTR userContext) + { + if (!notifyFn) + return 0xBAD1000E; // notify function pointer is null + + notifyCallbacks.emplace_back(notifyFn, userContext); + return 0; + } + + uint32 OSDynLoad_DelNotifyCallback(MEMPTR notifyFn, MEMPTR userContext) + { + std::erase_if(notifyCallbacks, [&](const NotifyCallbackEntry& entry) + { + return entry.callback.GetMPTR() == notifyFn.GetMPTR() && entry.userContext == userContext; + }); + + return 0; + } + void InitializeDynLoad() { cafeExportRegister("coreinit", OSDynLoad_SetAllocator, LogType::Placeholder); @@ -209,5 +287,8 @@ namespace coreinit cafeExportRegister("coreinit", OSDynLoad_GetModuleName, LogType::Placeholder); cafeExportRegister("coreinit", OSDynLoad_GetNumberOfRPLs, LogType::Placeholder); cafeExportRegister("coreinit", OSDynLoad_GetRPLInfo, LogType::Placeholder); + + cafeExportRegister("coreinit", OSDynLoad_AddNotifyCallback, LogType::Placeholder); + cafeExportRegister("coreinit", OSDynLoad_DelNotifyCallback, LogType::Placeholder); } } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h index ad1662e0..2789d3ae 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.h @@ -19,6 +19,16 @@ namespace coreinit uint32be readSize; }; + using OSDynLoadNotifyFunc = void (*)(uint32be* module, void *userContext, sint32 notifyReason, OSDynLoad_NotifyData *infos); + + struct NotifyCallbackEntry + { + MEMPTR callback; + MEMPTR userContext; + + NotifyCallbackEntry(MEMPTR func, MEMPTR context) : callback(func), userContext(context) {} + }; + uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc); void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc); uint32 OSDynLoad_GetAllocator(betype* funcAlloc, betype* funcFree); @@ -35,5 +45,8 @@ namespace coreinit sint32 OSDynLoad_GetNumberOfRPLs(); uint32 OSDynLoad_GetRPLInfo(uint32 first, uint32 count, OSDynLoad_NotifyData* outInfos); + uint32 OSDynLoad_AddNotifyCallback(MEMPTR notifyFn, MEMPTR userContext); + uint32 OSDynLoad_DelNotifyCallback(MEMPTR notifyFn, MEMPTR userContext); + void InitializeDynLoad(); -} \ No newline at end of file +}