Merge branch 'cemu-project:main' into main

This commit is contained in:
rcaridade145
2026-07-27 14:13:55 +01:00
committed by GitHub
13 changed files with 274 additions and 51 deletions

View File

@@ -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()

View File

@@ -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();

View File

@@ -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<uint16be> 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
}
}

View File

@@ -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<MY_COMMENT_LENGTH> 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<GAMEMODE_MAX_MESSAGE_LENGTH> 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,

View File

@@ -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;
@@ -2381,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;

View File

@@ -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);
@@ -46,6 +47,7 @@ uint32 RPLLoader_GetSDA2Base();
sint32 RPLLoader_GetModuleCount();
RPLModule** RPLLoader_GetModuleList();
RPLModule* RPLLoader_GetModuleByName(std::string_view name);
MEMPTR<void> RPLLoader_AllocateCodeCaveMem(uint32 alignment, uint32 size);
void RPLLoader_ReleaseCodeCaveMem(MEMPTR<void> addr);

View File

@@ -156,6 +156,7 @@ struct RPLModule
MEMPTR<void> 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<char> ppcName; // name of the module in PPC memory
uint8* tempRegionPtr;
uint32 tempRegionAllocSize;

View File

@@ -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"
@@ -11,6 +12,8 @@ namespace coreinit
MPTR _osDynLoadTLSFuncAlloc = MPTR_NULL;
MPTR _osDynLoadTLSFuncFree = MPTR_NULL;
static std::vector<NotifyCallbackEntry> notifyCallbacks;
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc)
{
_osDynLoadFuncAlloc = allocFunc;
@@ -108,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<OSDynLoad_NotifyData> 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;
}
@@ -120,6 +151,35 @@ namespace coreinit
{
if (moduleHandle == RPL_INVALID_HANDLE)
return;
for (const auto& cb : notifyCallbacks)
{
MEMPTR<OSDynLoad_NotifyData> 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();
}
@@ -146,6 +206,73 @@ 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;
}
uint32 OSDynLoad_AddNotifyCallback(MEMPTR<OSDynLoadNotifyFunc> notifyFn, MEMPTR<void> userContext)
{
if (!notifyFn)
return 0xBAD1000E; // notify function pointer is null
notifyCallbacks.emplace_back(notifyFn, userContext);
return 0;
}
uint32 OSDynLoad_DelNotifyCallback(MEMPTR<OSDynLoadNotifyFunc> notifyFn, MEMPTR<void> 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);
@@ -157,5 +284,11 @@ 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);
cafeExportRegister("coreinit", OSDynLoad_AddNotifyCallback, LogType::Placeholder);
cafeExportRegister("coreinit", OSDynLoad_DelNotifyCallback, LogType::Placeholder);
}
}
}

View File

@@ -2,6 +2,33 @@
namespace coreinit
{
struct OSDynLoad_NotifyData
{
MEMPTR<char> name;
uint32be textAddr;
uint32be textOffset;
uint32be textSize;
uint32be dataAddr;
uint32be dataOffset;
uint32be dataSize;
uint32be readAddr;
uint32be readOffset;
uint32be readSize;
};
using OSDynLoadNotifyFunc = void (*)(uint32be* module, void *userContext, sint32 notifyReason, OSDynLoad_NotifyData *infos);
struct NotifyCallbackEntry
{
MEMPTR<OSDynLoadNotifyFunc> callback;
MEMPTR<void> userContext;
NotifyCallbackEntry(MEMPTR<OSDynLoadNotifyFunc> func, MEMPTR<void> context) : callback(func), userContext(context) {}
};
uint32 OSDynLoad_SetAllocator(MPTR allocFunc, MPTR freeFunc);
void OSDynLoad_SetTLSAllocator(MPTR allocFunc, MPTR freeFunc);
uint32 OSDynLoad_GetAllocator(betype<MPTR>* funcAlloc, betype<MPTR>* funcFree);
@@ -14,5 +41,12 @@ namespace coreinit
void OSDynLoad_Release(uint32 moduleHandle);
uint32 OSDynLoad_FindExport(uint32 moduleHandle, uint32 isData, const char* exportName, betype<MPTR>* addrOut);
uint32 OSDynLoad_GetModuleName(uint32 moduleHandle, char* nameBuf, sint32be* nameBufSize);
sint32 OSDynLoad_GetNumberOfRPLs();
uint32 OSDynLoad_GetRPLInfo(uint32 first, uint32 count, OSDynLoad_NotifyData* outInfos);
uint32 OSDynLoad_AddNotifyCallback(MEMPTR<OSDynLoadNotifyFunc> notifyFn, MEMPTR<void> userContext);
uint32 OSDynLoad_DelNotifyCallback(MEMPTR<OSDynLoadNotifyFunc> notifyFn, MEMPTR<void> userContext);
void InitializeDynLoad();
}
}

View File

@@ -484,7 +484,7 @@ namespace nn
{
FP_API_BASE();
auto ipcCtx = std::make_unique<FPIpcContext>(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<FPIpcContext>(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
}
}

View File

@@ -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;

View File

@@ -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<nexFriend> list_friends;
std::vector<nexFriendRequest> list_friendReqOutgoing;
std::vector<nexFriendRequest> list_friendReqIncoming;
struct
struct
{
std::vector<nexFriend> list_friends;
std::vector<nexFriendRequest> list_friendReqOutgoing;

View File

@@ -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<wchar_t>(c);
ptr++;
maxLength--;
}
return boost::nowide::narrow(result.str());
}
// convert utf8 string to Wii U big-endian wchar_t string
static std::vector<uint16be> FromUtf8(std::string_view str)
{
@@ -285,4 +299,3 @@ namespace StringHelpers
std::string_view m_str;
};
};