From 0d71885c881984978ab8e0375975be3053a0a864 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 17 Oct 2023 04:41:22 +0200 Subject: [PATCH] nn_fp: Full rework of friend service --- src/Cafe/CafeSystem.cpp | 24 +- .../Interpreter/PPCInterpreterHLE.cpp | 2 +- src/Cafe/HW/Espresso/PPCState.h | 2 +- src/Cafe/IOSU/iosu_types_common.h | 14 +- src/Cafe/IOSU/kernel/iosu_kernel.cpp | 232 +- src/Cafe/IOSU/kernel/iosu_kernel.h | 7 +- src/Cafe/IOSU/legacy/iosu_act.cpp | 9 + src/Cafe/IOSU/legacy/iosu_act.h | 4 + src/Cafe/IOSU/legacy/iosu_fpd.cpp | 2105 ++++++++++------- src/Cafe/IOSU/legacy/iosu_fpd.h | 417 ++-- src/Cafe/IOSU/nn/iosu_nn_service.cpp | 129 +- src/Cafe/IOSU/nn/iosu_nn_service.h | 67 + src/Cafe/OS/RPL/rpl.cpp | 2 +- src/Cafe/OS/common/OSCommon.cpp | 5 +- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 86 +- src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp | 17 - src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp | 23 +- src/Cafe/OS/libs/h264_avc/H264Dec.cpp | 14 +- src/Cafe/OS/libs/nn_act/nn_act.cpp | 4 +- src/Cafe/OS/libs/nn_fp/nn_fp.cpp | 1269 +++++----- .../nn_olv/nn_olv_DownloadCommunityTypes.cpp | 4 +- .../OS/libs/nn_olv/nn_olv_InitializeTypes.cpp | 4 +- src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp | 10 +- .../nn_olv/nn_olv_UploadCommunityTypes.cpp | 4 +- .../nn_olv/nn_olv_UploadFavoriteTypes.cpp | 4 +- src/Cemu/Logging/CemuLogging.cpp | 1 + src/Cemu/Logging/CemuLogging.h | 1 + src/Cemu/napi/napi_act.cpp | 2 +- src/Cemu/nex/nex.cpp | 6 +- src/Cemu/nex/nexFriends.cpp | 211 +- src/Cemu/nex/nexFriends.h | 57 +- src/Common/CMakeLists.txt | 1 + src/Common/CafeString.h | 73 + src/Common/StackAllocator.h | 31 +- src/gui/MainWindow.cpp | 1 + src/util/helpers/StringHelpers.h | 1 + .../highresolutiontimer/HighResolutionTimer.h | 10 + 37 files changed, 2862 insertions(+), 1991 deletions(-) create mode 100644 src/Common/CafeString.h diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index a3f42791..3d06281e 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -526,6 +526,13 @@ namespace CafeSystem cemuLog_log(LogType::Force, "Platform: {}", platform); } + static std::vector s_iosuModules = + { + // entries in this list are ordered by initialization order. Shutdown in reverse order + iosu::kernel::GetModule(), + iosu::fpd::GetModule() + }; + // initialize all subsystems which are persistent and don't depend on a game running void Initialize() { @@ -550,14 +557,15 @@ namespace CafeSystem // allocate memory for all SysAllocators // must happen before COS module init, but also before iosu::kernel::Initialize() SysAllocatorContainer::GetInstance().Initialize(); - // init IOSU + // init IOSU modules + for(auto& module : s_iosuModules) + module->SystemLaunch(); + // init IOSU (deprecated manual init) iosuCrypto_init(); - iosu::kernel::Initialize(); iosu::fsa::Initialize(); iosuIoctl_init(); iosuAct_init_depr(); iosu::act::Initialize(); - iosu::fpd::Initialize(); iosu::iosuMcp_init(); iosu::mcp::Init(); iosu::iosuAcp_init(); @@ -593,11 +601,14 @@ namespace CafeSystem // if a title is running, shut it down if (sSystemRunning) ShutdownTitle(); - // shutdown persistent subsystems + // shutdown persistent subsystems (deprecated manual shutdown) iosu::odm::Shutdown(); iosu::act::Stop(); iosu::mcp::Shutdown(); iosu::fsa::Shutdown(); + // shutdown IOSU modules + for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) + (*it)->SystemExit(); s_initialized = false; } @@ -821,7 +832,8 @@ namespace CafeSystem void _LaunchTitleThread() { - // init + for(auto& module : s_iosuModules) + module->TitleStart(); cemu_initForGame(); // enter scheduler if (ActiveSettings::GetCPUMode() == CPUMode::MulticoreRecompiler) @@ -956,6 +968,8 @@ namespace CafeSystem nn::save::ResetToDefaultState(); coreinit::__OSDeleteAllActivePPCThreads(); RPLLoader_ResetState(); + for(auto it = s_iosuModules.rbegin(); it != s_iosuModules.rend(); ++it) + (*it)->TitleStop(); // stop time tracking iosu::pdm::Stop(); // reset Cemu subsystems diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp index 6aa1fcfa..24219e66 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp @@ -19,7 +19,7 @@ void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU) std::vector* sPPCHLETable{}; -HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall) +HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName) { if (!sPPCHLETable) sPPCHLETable = new std::vector(); diff --git a/src/Cafe/HW/Espresso/PPCState.h b/src/Cafe/HW/Espresso/PPCState.h index 85b2dc04..134f73a8 100644 --- a/src/Cafe/HW/Espresso/PPCState.h +++ b/src/Cafe/HW/Espresso/PPCState.h @@ -230,7 +230,7 @@ static inline float flushDenormalToZero(float f) typedef void(*HLECALL)(PPCInterpreter_t* hCPU); typedef sint32 HLEIDX; -HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall); +HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName); HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex); // HLE scheduler diff --git a/src/Cafe/IOSU/iosu_types_common.h b/src/Cafe/IOSU/iosu_types_common.h index 94c0cd2c..1f7f78f8 100644 --- a/src/Cafe/IOSU/iosu_types_common.h +++ b/src/Cafe/IOSU/iosu_types_common.h @@ -1,6 +1,9 @@ #pragma once using IOSMsgQueueId = uint32; +using IOSTimerId = uint32; + +static constexpr IOSTimerId IOSInvalidTimerId = 0xFFFFFFFF; // returned for syscalls // maybe also shared with IPC? @@ -19,4 +22,13 @@ enum IOS_ERROR : sint32 inline bool IOS_ResultIsError(const IOS_ERROR err) { return (err & 0x80000000) != 0; -} \ No newline at end of file +} + +class IOSUModule +{ + public: + virtual void SystemLaunch() {}; // CafeSystem is initialized + virtual void SystemExit() {}; // CafeSystem is shutdown + virtual void TitleStart() {}; // foreground title is launched + virtual void TitleStop() {}; // foreground title is closed +}; diff --git a/src/Cafe/IOSU/kernel/iosu_kernel.cpp b/src/Cafe/IOSU/kernel/iosu_kernel.cpp index 680170bc..52097698 100644 --- a/src/Cafe/IOSU/kernel/iosu_kernel.cpp +++ b/src/Cafe/IOSU/kernel/iosu_kernel.cpp @@ -1,6 +1,8 @@ #include "iosu_kernel.h" #include "util/helpers/fspinlock.h" +#include "util/helpers/helpers.h" #include "Cafe/OS/libs/coreinit/coreinit_IPC.h" +#include "util/highresolutiontimer/HighResolutionTimer.h" namespace iosu { @@ -8,6 +10,9 @@ namespace iosu { std::mutex sInternalMutex; + void IOS_DestroyResourceManagerForQueueId(IOSMsgQueueId msgQueueId); + void _IPCDestroyAllHandlesForMsgQueue(IOSMsgQueueId msgQueueId); + static void _assume_lock() { #ifdef CEMU_DEBUG_ASSERT @@ -84,6 +89,19 @@ namespace iosu return queueHandle; } + IOS_ERROR IOS_DestroyMessageQueue(IOSMsgQueueId msgQueueId) + { + std::unique_lock _l(sInternalMutex); + IOSMessageQueue* msgQueue = nullptr; + IOS_ERROR r = _IOS_GetMessageQueue(msgQueueId, msgQueue); + if (r != IOS_ERROR_OK) + return r; + msgQueue->msgArraySize = 0; + msgQueue->queueHandle = 0; + IOS_DestroyResourceManagerForQueueId(msgQueueId); + return IOS_ERROR_OK; + } + IOS_ERROR IOS_SendMessage(IOSMsgQueueId msgQueueId, IOSMessage message, uint32 flags) { std::unique_lock _l(sInternalMutex); @@ -146,6 +164,133 @@ namespace iosu return IOS_ERROR_OK; } + /* timer */ + + std::mutex sTimerMutex; + std::condition_variable sTimerCV; + std::atomic_bool sTimerThreadStop; + + struct IOSTimer + { + IOSMsgQueueId queueId; + uint32 message; + HRTick nextFire; + HRTick repeat; + bool isValid; + }; + + std::vector sTimers; + std::vector sTimersFreeHandles; + + auto sTimerSortComparator = [](const IOSTimerId& idA, const IOSTimerId& idB) + { + // order by nextFire, then by timerId to avoid duplicate keys + IOSTimer& timerA = sTimers[idA]; + IOSTimer& timerB = sTimers[idB]; + if (timerA.nextFire != timerB.nextFire) + return timerA.nextFire < timerB.nextFire; + return idA < idB; + }; + std::set sTimerByFireTime; + + IOSTimer& IOS_GetFreeTimer() + { + cemu_assert_debug(!sTimerMutex.try_lock()); // lock must be held by current thread + if (sTimersFreeHandles.empty()) + return sTimers.emplace_back(); + IOSTimerId timerId = sTimersFreeHandles.back(); + sTimersFreeHandles.pop_back(); + return sTimers[timerId]; + } + + void IOS_TimerSetNextFireTime(IOSTimer& timer, HRTick nextFire) + { + cemu_assert_debug(!sTimerMutex.try_lock()); // lock must be held by current thread + IOSTimerId timerId = &timer - sTimers.data(); + auto it = sTimerByFireTime.find(timerId); + if(it != sTimerByFireTime.end()) + sTimerByFireTime.erase(it); + timer.nextFire = nextFire; + if(nextFire != 0) + sTimerByFireTime.insert(timerId); + } + + void IOS_StopTimerInternal(IOSTimerId timerId) + { + cemu_assert_debug(!sTimerMutex.try_lock()); + IOS_TimerSetNextFireTime(sTimers[timerId], 0); + } + + IOS_ERROR IOS_CreateTimer(uint32 startMicroseconds, uint32 repeatMicroseconds, uint32 queueId, uint32 message) + { + std::unique_lock _l(sTimerMutex); + IOSTimer& timer = IOS_GetFreeTimer(); + timer.queueId = queueId; + timer.message = message; + HRTick nextFire = HighResolutionTimer::now().getTick() + HighResolutionTimer::microsecondsToTicks(startMicroseconds); + timer.repeat = HighResolutionTimer::microsecondsToTicks(repeatMicroseconds); + IOS_TimerSetNextFireTime(timer, nextFire); + timer.isValid = true; + sTimerCV.notify_one(); + return (IOS_ERROR)(&timer - sTimers.data()); + } + + IOS_ERROR IOS_StopTimer(IOSTimerId timerId) + { + std::unique_lock _l(sTimerMutex); + if (timerId >= sTimers.size() || !sTimers[timerId].isValid) + return IOS_ERROR_INVALID; + IOS_StopTimerInternal(timerId); + return IOS_ERROR_OK; + } + + IOS_ERROR IOS_DestroyTimer(IOSTimerId timerId) + { + std::unique_lock _l(sTimerMutex); + if (timerId >= sTimers.size() || !sTimers[timerId].isValid) + return IOS_ERROR_INVALID; + IOS_StopTimerInternal(timerId); + sTimers[timerId].isValid = false; + sTimersFreeHandles.push_back(timerId); + return IOS_ERROR_OK; + } + + void IOSTimerThread() + { + SetThreadName("IOS-Timer"); + std::unique_lock _l(sTimerMutex); + while (!sTimerThreadStop) + { + if (sTimerByFireTime.empty()) + { + sTimerCV.wait_for(_l, std::chrono::milliseconds(10000)); + continue; + } + IOSTimerId timerId = *sTimerByFireTime.begin(); + IOSTimer& timer = sTimers[timerId]; + HRTick now = HighResolutionTimer::now().getTick(); + if (now >= timer.nextFire) + { + if(timer.repeat == 0) + IOS_TimerSetNextFireTime(timer, 0); + else + IOS_TimerSetNextFireTime(timer, timer.nextFire + timer.repeat); + IOSMsgQueueId queueId = timer.queueId; + uint32 message = timer.message; + // fire timer + _l.unlock(); + IOSMessage msg; + IOS_SendMessage(queueId, message, 1); + _l.lock(); + continue; + } + else + { + sTimerCV.wait_for(_l, std::chrono::microseconds(HighResolutionTimer::ticksToMicroseconds(timer.nextFire - now))); + } + } + } + /* devices and IPC */ struct IOSResourceManager @@ -209,6 +354,23 @@ namespace iosu return IOS_ERROR_OK; } + void IOS_DestroyResourceManagerForQueueId(IOSMsgQueueId msgQueueId) + { + _assume_lock(); + // destroy all IPC handles associated with this queue + _IPCDestroyAllHandlesForMsgQueue(msgQueueId); + // destroy device resource manager + for (auto& it : sDeviceResources) + { + if (it.isSet && it.msgQueueId == msgQueueId) + { + it.isSet = false; + it.path.clear(); + it.msgQueueId = 0; + } + } + } + IOS_ERROR IOS_DeviceAssociateId(const char* devicePath, uint32 id) { // not yet implemented @@ -344,6 +506,22 @@ namespace iosu return IOS_ERROR_OK; } + void _IPCDestroyAllHandlesForMsgQueue(IOSMsgQueueId msgQueueId) + { + _assume_lock(); + for (auto& it : sActiveDeviceHandles) + { + if (it.isSet && it.msgQueueId == msgQueueId) + { + it.isSet = false; + it.path.clear(); + it.handleCheckValue = 0; + it.hasDispatchTargetHandle = false; + it.msgQueueId = 0; + } + } + } + IOS_ERROR _IPCAssignDispatchTargetHandle(IOSDevHandle devHandle, IOSDevHandle internalHandle) { std::unique_lock _lock(sInternalMutex); @@ -453,7 +631,6 @@ namespace iosu uint32 numIn = dispatchCmd->body.args[1]; uint32 numOut = dispatchCmd->body.args[2]; IPCIoctlVector* vec = MEMPTR(cmd.args[3]).GetPtr(); - // copy the vector array uint32 numVec = numIn + numOut; if (numVec <= 8) @@ -466,8 +643,23 @@ namespace iosu // reuse the original vector pointer cemuLog_log(LogType::Force, "Info: Ioctlv command with more than 8 vectors"); } - IOS_ERROR r = _IPCDispatchToResourceManager(dispatchCmd->body.devHandle, dispatchCmd); - return r; + return _IPCDispatchToResourceManager(dispatchCmd->body.devHandle, dispatchCmd); + } + + // normally COS kernel handles this, but currently we skip the IPC getting proxied through it + IOS_ERROR _IPCHandlerIn_TranslateVectorAddresses(IOSDispatchableCommand* dispatchCmd) + { + uint32 numIn = dispatchCmd->body.args[1]; + uint32 numOut = dispatchCmd->body.args[2]; + IPCIoctlVector* vec = MEMPTR(dispatchCmd->body.args[3]).GetPtr(); + for (uint32 i = 0; i < numIn + numOut; i++) + { + if (vec[i].baseVirt == nullptr && vec[i].size != 0) + return IOS_ERROR_INVALID; + // todo - check for valid pointer range + vec[i].basePhys = vec[i].baseVirt; + } + return IOS_ERROR_OK; } // called by COS directly @@ -494,7 +686,11 @@ namespace iosu r = _IPCHandlerIn_IOS_Ioctl(dispatchCmd); break; case IPCCommandId::IOS_IOCTLV: - r = _IPCHandlerIn_IOS_Ioctlv(dispatchCmd); + r = _IPCHandlerIn_TranslateVectorAddresses(dispatchCmd); + if(r < 0) + cemuLog_log(LogType::Force, "Ioctlv error"); + else + r = _IPCHandlerIn_IOS_Ioctlv(dispatchCmd); break; default: cemuLog_log(LogType::Force, "Invalid IPC command {}", (uint32)(IPCCommandId)cmd->cmdId); @@ -547,10 +743,34 @@ namespace iosu return IOS_ERROR_OK; } - void Initialize() + class : public ::IOSUModule { - _IPCInitDispatchablePool(); + void SystemLaunch() override + { + _IPCInitDispatchablePool(); + // start timer thread + sTimerThreadStop = false; + m_timerThread = std::thread(IOSTimerThread); + } + + void SystemExit() override + { + // stop timer thread + sTimerThreadStop = true; + sTimerCV.notify_one(); + m_timerThread.join(); + // reset resources + // todo + } + + std::thread m_timerThread; + }sIOSUModuleKernel; + + IOSUModule* GetModule() + { + return static_cast(&sIOSUModuleKernel); } + } } \ No newline at end of file diff --git a/src/Cafe/IOSU/kernel/iosu_kernel.h b/src/Cafe/IOSU/kernel/iosu_kernel.h index 2b82374e..0355c118 100644 --- a/src/Cafe/IOSU/kernel/iosu_kernel.h +++ b/src/Cafe/IOSU/kernel/iosu_kernel.h @@ -9,15 +9,20 @@ namespace iosu using IOSMessage = uint32; IOSMsgQueueId IOS_CreateMessageQueue(IOSMessage* messageArray, uint32 messageCount); + IOS_ERROR IOS_DestroyMessageQueue(IOSMsgQueueId msgQueueId); IOS_ERROR IOS_SendMessage(IOSMsgQueueId msgQueueId, IOSMessage message, uint32 flags); IOS_ERROR IOS_ReceiveMessage(IOSMsgQueueId msgQueueId, IOSMessage* messageOut, uint32 flags); + IOS_ERROR IOS_CreateTimer(uint32 startMicroseconds, uint32 repeatMicroseconds, uint32 queueId, uint32 message); + IOS_ERROR IOS_StopTimer(IOSTimerId timerId); + IOS_ERROR IOS_DestroyTimer(IOSTimerId timerId); + IOS_ERROR IOS_RegisterResourceManager(const char* devicePath, IOSMsgQueueId msgQueueId); IOS_ERROR IOS_DeviceAssociateId(const char* devicePath, uint32 id); IOS_ERROR IOS_ResourceReply(IPCCommandBody* cmd, IOS_ERROR result); void IPCSubmitFromCOS(uint32 ppcCoreIndex, IPCCommandBody* cmd); - void Initialize(); + IOSUModule* GetModule(); } } \ No newline at end of file diff --git a/src/Cafe/IOSU/legacy/iosu_act.cpp b/src/Cafe/IOSU/legacy/iosu_act.cpp index e7418e8f..ed3a69bd 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.cpp +++ b/src/Cafe/IOSU/legacy/iosu_act.cpp @@ -192,6 +192,15 @@ namespace iosu return true; } + // returns empty string if invalid + std::string getAccountId2(uint8 slot) + { + sint32 accountIndex = iosuAct_getAccountIndexBySlot(slot); + if (_actAccountData[accountIndex].isValid == false) + return {}; + return {_actAccountData[accountIndex].accountId}; + } + bool getMii(uint8 slot, FFLData_t* fflData) { sint32 accountIndex = iosuAct_getAccountIndexBySlot(slot); diff --git a/src/Cafe/IOSU/legacy/iosu_act.h b/src/Cafe/IOSU/legacy/iosu_act.h index 04dd579f..5336f519 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.h +++ b/src/Cafe/IOSU/legacy/iosu_act.h @@ -3,6 +3,8 @@ void iosuAct_init_depr(); bool iosuAct_isInitialized(); +#define ACT_ACCOUNTID_LENGTH (17) // includes '\0' + // Mii #define MII_FFL_STORAGE_SIZE (96) @@ -48,6 +50,8 @@ namespace iosu bool getScreenname(uint8 slot, uint16 screenname[ACT_NICKNAME_LENGTH]); bool getCountryIndex(uint8 slot, uint32* countryIndex); + std::string getAccountId2(uint8 slot); + const uint8 ACT_SLOT_CURRENT = 0xFE; void Initialize(); diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.cpp b/src/Cafe/IOSU/legacy/iosu_fpd.cpp index 4457d602..75bf0463 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.cpp +++ b/src/Cafe/IOSU/legacy/iosu_fpd.cpp @@ -1,4 +1,3 @@ -#include "iosu_ioctl.h" #include "iosu_act.h" #include "iosu_fpd.h" #include "Cemu/nex/nex.h" @@ -9,12 +8,10 @@ #include "config/ActiveSettings.h" #include "Cemu/napi/napi.h" #include "util/helpers/StringHelpers.h" -#include "Cafe/OS/libs/coreinit/coreinit.h" +#include "Cafe/IOSU/iosu_types_common.h" +#include "Cafe/IOSU/nn/iosu_nn_service.h" -uint32 memory_getVirtualOffsetFromPointer(void* ptr); // remove once we updated everything to MEMPTR - -SysAllocator _fpdAsyncLoginRetCode; -SysAllocator _fpdAsyncAddFriendRetCode; +#include "Common/CafeString.h" std::mutex g_friend_notification_mutex; std::vector< std::pair > g_friend_notifications; @@ -23,78 +20,114 @@ namespace iosu { namespace fpd { + using NotificationRunningId = uint64; + + struct NotificationEntry + { + NotificationEntry(uint64 index, NexFriends::NOTIFICATION_TYPE type, uint32 pid) : timestamp(std::chrono::steady_clock::now()), runningId(index), type(type), pid(pid) {} + std::chrono::steady_clock::time_point timestamp; + NotificationRunningId runningId; + NexFriends::NOTIFICATION_TYPE type; + uint32 pid; + }; + + class + { + public: + void TrackNotification(NexFriends::NOTIFICATION_TYPE type, uint32 pid) + { + std::unique_lock _l(m_mtxNotificationQueue); + m_notificationQueue.emplace_back(m_notificationQueueIndex++, type, pid); + } + + void RemoveExpired() + { + // remove entries older than 10 seconds + std::chrono::steady_clock::time_point expireTime = std::chrono::steady_clock::now() - std::chrono::seconds(10); + std::erase_if(m_notificationQueue, [expireTime](const auto& notification) { + return notification.timestamp < expireTime; + }); + } + + std::optional GetNextNotification(NotificationRunningId& previousRunningId) + { + std::unique_lock _l(m_mtxNotificationQueue); + auto it = std::lower_bound(m_notificationQueue.begin(), m_notificationQueue.end(), previousRunningId, [](const auto& notification, const auto& runningId) { + return notification.runningId <= runningId; + }); + size_t itIndex = it - m_notificationQueue.begin(); + if(it == m_notificationQueue.end()) + return std::nullopt; + previousRunningId = it->runningId; + return *it; + } + + private: + std::vector m_notificationQueue; + std::mutex m_mtxNotificationQueue; + std::atomic_uint64_t m_notificationQueueIndex{1}; + }g_NotificationQueue; struct { bool isThreadStarted; bool isInitialized2; NexFriends* nexFriendSession; - // notification handler - MPTR notificationFunc; - MPTR notificationCustomParam; - uint32 notificationMask; - // login callback - struct - { - MPTR func; - MPTR customParam; - }asyncLoginCallback; + std::mutex mtxFriendSession; + // session state + std::atomic_bool sessionStarted{false}; // current state nexPresenceV2 myPresence; }g_fpd = {}; - void notificationHandler(NexFriends::NOTIFICATION_TYPE type, uint32 pid) + void OverlayNotificationHandler(NexFriends::NOTIFICATION_TYPE type, uint32 pid) { cemuLog_logDebug(LogType::Force, "Friends::Notification {:02x} pid {:08x}", type, pid); - if(GetConfig().notification.friends) + if(!GetConfig().notification.friends) + return; + std::unique_lock lock(g_friend_notification_mutex); + std::string message; + if(type == NexFriends::NOTIFICATION_TYPE::NOTIFICATION_TYPE_ONLINE) { - std::unique_lock lock(g_friend_notification_mutex); - std::string message; - if(type == NexFriends::NOTIFICATION_TYPE::NOTIFICATION_TYPE_ONLINE) + g_friend_notifications.emplace_back("Connected to friend service", 5000); + if(g_fpd.nexFriendSession && g_fpd.nexFriendSession->getPendingFriendRequestCount() > 0) + g_friend_notifications.emplace_back(fmt::format("You have {} pending friend request(s)", g_fpd.nexFriendSession->getPendingFriendRequestCount()), 5000); + } + else + { + std::string msg_format; + switch(type) { - g_friend_notifications.emplace_back("Connected to friend service", 5000); - if(g_fpd.nexFriendSession && g_fpd.nexFriendSession->getPendingFriendRequestCount() > 0) - g_friend_notifications.emplace_back(fmt::format("You have {} pending friend request(s)", g_fpd.nexFriendSession->getPendingFriendRequestCount()), 5000); + case NexFriends::NOTIFICATION_TYPE_ONLINE: break; + case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGIN: msg_format = "{} is now online"; break; + case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGOFF: msg_format = "{} is now offline"; break; + case NexFriends::NOTIFICATION_TYPE_FRIEND_PRESENCE_CHANGE: break; + case NexFriends::NOTIFICATION_TYPE_ADDED_FRIEND: msg_format = "{} has been added to your friend list"; break; + case NexFriends::NOTIFICATION_TYPE_REMOVED_FRIEND: msg_format = "{} has been removed from your friend list"; break; + case NexFriends::NOTIFICATION_TYPE_ADDED_OUTGOING_REQUEST: break; + case NexFriends::NOTIFICATION_TYPE_REMOVED_OUTGOING_REQUEST: break; + case NexFriends::NOTIFICATION_TYPE_ADDED_INCOMING_REQUEST: msg_format = "{} wants to add you to his friend list"; break; + case NexFriends::NOTIFICATION_TYPE_REMOVED_INCOMING_REQUEST: break; + default: ; } - else + if (!msg_format.empty()) { - std::string msg_format; - switch(type) + std::string name = fmt::format("{:#x}", pid); + if (g_fpd.nexFriendSession) { - case NexFriends::NOTIFICATION_TYPE_ONLINE: break; - case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGIN: msg_format = "{} is now online"; break; - case NexFriends::NOTIFICATION_TYPE_FRIEND_LOGOFF: msg_format = "{} is now offline"; break; - case NexFriends::NOTIFICATION_TYPE_FRIEND_PRESENCE_CHANGE: break; - case NexFriends::NOTIFICATION_TYPE_ADDED_FRIEND: msg_format = "{} has been added to your friend list"; break; - case NexFriends::NOTIFICATION_TYPE_REMOVED_FRIEND: msg_format = "{} has been removed from your friend list"; break; - case NexFriends::NOTIFICATION_TYPE_ADDED_OUTGOING_REQUEST: break; - case NexFriends::NOTIFICATION_TYPE_REMOVED_OUTGOING_REQUEST: break; - case NexFriends::NOTIFICATION_TYPE_ADDED_INCOMING_REQUEST: msg_format = "{} wants to add you to his friend list"; break; - case NexFriends::NOTIFICATION_TYPE_REMOVED_INCOMING_REQUEST: break; - default: ; - } - - if (!msg_format.empty()) - { - std::string name = fmt::format("{:#x}", pid); - if (g_fpd.nexFriendSession) - { - const std::string tmp = g_fpd.nexFriendSession->getAccountNameByPid(pid); - if (!tmp.empty()) - name = tmp; - } - - g_friend_notifications.emplace_back(fmt::format(fmt::runtime(msg_format), name), 5000); + const std::string tmp = g_fpd.nexFriendSession->getAccountNameByPid(pid); + if (!tmp.empty()) + name = tmp; } + g_friend_notifications.emplace_back(fmt::format(fmt::runtime(msg_format), name), 5000); } } + } - if (g_fpd.notificationFunc == MPTR_NULL) - return; - uint32 notificationFlag = 1 << (type - 1); - if ( (notificationFlag&g_fpd.notificationMask) == 0 ) - return; - coreinitAsyncCallback_add(g_fpd.notificationFunc, 3, type, pid, g_fpd.notificationCustomParam); + void NotificationHandler(NexFriends::NOTIFICATION_TYPE type, uint32 pid) + { + OverlayNotificationHandler(type, pid); + g_NotificationQueue.TrackNotification(type, pid); } void convertMultiByteStringToBigEndianWidechar(const char* input, uint16be* output, sint32 maxOutputLength) @@ -107,7 +140,7 @@ namespace iosu output[beStr.size()] = '\0'; } - void convertFPDTimestampToDate(uint64 timestamp, fpdDate_t* fpdDate) + void convertFPDTimestampToDate(uint64 timestamp, FPDDate* fpdDate) { // if the timestamp is zero then still return a valid date if (timestamp == 0) @@ -128,7 +161,7 @@ namespace iosu fpdDate->year = (uint16)((timestamp >> 26)); } - uint64 convertDateToFPDTimestamp(fpdDate_t* fpdDate) + uint64 convertDateToFPDTimestamp(FPDDate* fpdDate) { uint64 t = 0; t |= (uint64)fpdDate->second; @@ -140,111 +173,33 @@ namespace iosu return t; } - void startFriendSession() + void NexPresenceToGameMode(nexPresenceV2* presence, GameMode* gameMode) { - cemu_assert(!g_fpd.nexFriendSession); - - NAPI::AuthInfo authInfo; - NAPI::NAPI_MakeAuthInfoFromCurrentAccount(authInfo); - NAPI::ACTGetNexTokenResult nexTokenResult = NAPI::ACT_GetNexToken_WithCache(authInfo, 0x0005001010001C00, 0x0000, 0x00003200); - if (nexTokenResult.isValid()) - { - // get values needed for friend session - uint32 myPid; - uint8 currentSlot = iosu::act::getCurrentAccountSlot(); - iosu::act::getPrincipalId(currentSlot, &myPid); - char accountId[256] = { 0 }; - iosu::act::getAccountId(currentSlot, accountId); - FFLData_t miiData; - act::getMii(currentSlot, &miiData); - uint16 screenName[ACT_NICKNAME_LENGTH + 1] = { 0 }; - act::getScreenname(currentSlot, screenName); - uint32 countryCode = 0; - act::getCountryIndex(currentSlot, &countryCode); - // init presence - g_fpd.myPresence.isOnline = 1; - g_fpd.myPresence.gameKey.titleId = CafeSystem::GetForegroundTitleId(); - g_fpd.myPresence.gameKey.ukn = CafeSystem::GetForegroundTitleVersion(); - - // Resolve potential domain to IP address - struct addrinfo hints = {0}, *addrs; - hints.ai_family = AF_INET; - - const int status = getaddrinfo(nexTokenResult.nexToken.host, NULL, &hints, &addrs); - if (status != 0) { -#if BOOST_OS_WINDOWS - cemuLog_log(LogType::Force, "IOSU_FPD: Failed to resolve hostname {}, {}", nexTokenResult.nexToken.host, gai_strerrorA(status)); -#else - cemuLog_log(LogType::Force, "IOSU_FPD: Failed to resolve hostname {}, {}", nexTokenResult.nexToken.host, gai_strerror(status)); -#endif - return; - } - - char addrstr[NI_MAXHOST]; - getnameinfo(addrs->ai_addr, addrs->ai_addrlen, addrstr, sizeof addrstr, NULL, 0, NI_NUMERICHOST); - cemuLog_log(LogType::Force, "IOSU_FPD: Resolved IP for hostname {}, {}", nexTokenResult.nexToken.host, addrstr); - - // start session - const uint32_t hostIp = ((struct sockaddr_in*)addrs->ai_addr)->sin_addr.s_addr; - freeaddrinfo(addrs); - 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->setNotificationHandler(notificationHandler); - cemuLog_log(LogType::Force, "IOSU_FPD: Created friend server session"); - } - else - { - cemuLog_logDebug(LogType::Force, "IOSU_FPD: Failed to acquire nex token for friend server"); - } + memset(gameMode, 0, sizeof(GameMode)); + gameMode->joinFlagMask = presence->joinFlagMask; + gameMode->matchmakeType = presence->joinAvailability; + gameMode->joinGameId = presence->gameId; + gameMode->joinGameMode = presence->gameMode; + gameMode->hostPid = presence->hostPid; + gameMode->groupId = presence->groupId; + memcpy(gameMode->appSpecificData, presence->appSpecificData, 0x14); } - void handleRequest_GetFriendList(iosuFpdCemuRequest_t* fpdCemuRequest, bool getAll) + void GameModeToNexPresence(GameMode* gameMode, nexPresenceV2* presence) { - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = 0; // zero entries returned - return; - } - - uint32 temporaryPidList[800]; - uint32 pidCount = 0; - g_fpd.nexFriendSession->getFriendPIDs(temporaryPidList, &pidCount, fpdCemuRequest->getFriendList.startIndex, std::min(sizeof(temporaryPidList) / sizeof(temporaryPidList[0]), fpdCemuRequest->getFriendList.maxCount), getAll); - uint32be* pidListOutput = fpdCemuRequest->getFriendList.pidList.GetPtr(); - if (pidListOutput) - { - for (uint32 i = 0; i < pidCount; i++) - pidListOutput[i] = temporaryPidList[i]; - } - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = pidCount; + memset(presence, 0, sizeof(nexPresenceV2)); + presence->joinFlagMask = gameMode->joinFlagMask; + presence->joinAvailability = (uint8)(uint32)gameMode->matchmakeType; + presence->gameId = gameMode->joinGameId; + presence->gameMode = gameMode->joinGameMode; + presence->hostPid = gameMode->hostPid; + presence->groupId = gameMode->groupId; + memcpy(presence->appSpecificData, gameMode->appSpecificData, 0x14); } - void handleRequest_GetFriendRequestList(iosuFpdCemuRequest_t* fpdCemuRequest) + void NexFriendToFPDFriendData(FriendData* friendData, nexFriend* frd) { - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = 0; // zero entries returned - return; - } - - uint32 temporaryPidList[800]; - uint32 pidCount = 0; - // get only incoming friend requests - g_fpd.nexFriendSession->getFriendRequestPIDs(temporaryPidList, &pidCount, fpdCemuRequest->getFriendList.startIndex, std::min(sizeof(temporaryPidList) / sizeof(temporaryPidList[0]), fpdCemuRequest->getFriendList.maxCount), true, false); - uint32be* pidListOutput = fpdCemuRequest->getFriendList.pidList.GetPtr(); - if (pidListOutput) - { - for (uint32 i = 0; i < pidCount; i++) - pidListOutput[i] = temporaryPidList[i]; - } - fpdCemuRequest->returnCode = 0; - fpdCemuRequest->resultU32.u32 = pidCount; - } - - void setFriendDataFromNexFriend(friendData_t* friendData, nexFriend* frd) - { - memset(friendData, 0, sizeof(friendData_t)); + memset(friendData, 0, sizeof(FriendData)); // setup friend data friendData->type = 1; // friend friendData->pid = frd->nnaInfo.principalInfo.principalId; @@ -253,13 +208,11 @@ namespace iosu // screenname convertMultiByteStringToBigEndianWidechar(frd->nnaInfo.principalInfo.mii.miiNickname, friendData->screenname, sizeof(friendData->screenname) / sizeof(uint16be)); - //friendData->friendExtraData.ukn0E4 = 0; friendData->friendExtraData.isOnline = frd->presence.isOnline != 0 ? 1 : 0; - friendData->friendExtraData.gameKeyTitleId = _swapEndianU64(frd->presence.gameKey.titleId); - friendData->friendExtraData.gameKeyUkn = frd->presence.gameKey.ukn; - - friendData->friendExtraData.statusMessage[0] = '\0'; + friendData->friendExtraData.gameKey.titleId = frd->presence.gameKey.titleId; + friendData->friendExtraData.gameKey.ukn08 = frd->presence.gameKey.ukn; + NexPresenceToGameMode(&frd->presence, &friendData->friendExtraData.gameMode); // set valid dates friendData->uknDate.year = 2018; @@ -269,19 +222,19 @@ namespace iosu friendData->uknDate.minute = 1; friendData->uknDate.second = 1; - friendData->friendExtraData.uknDate218.year = 2018; - friendData->friendExtraData.uknDate218.day = 1; - friendData->friendExtraData.uknDate218.month = 1; - friendData->friendExtraData.uknDate218.hour = 1; - friendData->friendExtraData.uknDate218.minute = 1; - friendData->friendExtraData.uknDate218.second = 1; + friendData->friendExtraData.approvalTime.year = 2018; + friendData->friendExtraData.approvalTime.day = 1; + friendData->friendExtraData.approvalTime.month = 1; + friendData->friendExtraData.approvalTime.hour = 1; + friendData->friendExtraData.approvalTime.minute = 1; + friendData->friendExtraData.approvalTime.second = 1; convertFPDTimestampToDate(frd->lastOnlineTimestamp, &friendData->friendExtraData.lastOnline); } - void setFriendDataFromNexFriendRequest(friendData_t* friendData, nexFriendRequest* frdReq, bool isIncoming) + void NexFriendRequestToFPDFriendData(FriendData* friendData, nexFriendRequest* frdReq, bool isIncoming) { - memset(friendData, 0, sizeof(friendData_t)); + memset(friendData, 0, sizeof(FriendData)); // setup friend data friendData->type = 0; // friend request friendData->pid = frdReq->principalInfo.principalId; @@ -292,7 +245,7 @@ namespace iosu convertMultiByteStringToBigEndianWidechar(frdReq->message.commentStr.c_str(), friendData->requestExtraData.comment, sizeof(friendData->requestExtraData.comment) / sizeof(uint16be)); - fpdDate_t expireDate; + FPDDate expireDate; convertFPDTimestampToDate(frdReq->message.expireTimestamp, &expireDate); bool isProvisional = frdReq->message.expireTimestamp == 0; @@ -301,10 +254,7 @@ namespace iosu //friendData->requestExtraData.ukn0A0 = 0; // if not set -> provisional friend request //friendData->requestExtraData.ukn0A4 = isProvisional ? 0 : 123; // no change? - friendData->requestExtraData.messageId = _swapEndianU64(frdReq->message.messageId); - - - //find the value for 'markedAsReceived' + friendData->requestExtraData.messageId = frdReq->message.messageId; ///* +0x0A8 */ uint8 ukn0A8; ///* +0x0A9 */ uint8 ukn0A9; // comment language? (guessed) @@ -332,9 +282,9 @@ namespace iosu convertFPDTimestampToDate(frdReq->message.expireTimestamp, &friendData->requestExtraData.uknData1); } - void setFriendRequestFromNexFriendRequest(friendRequest_t* friendRequest, nexFriendRequest* frdReq, bool isIncoming) + void NexFriendRequestToFPDFriendRequest(FriendRequest* friendRequest, nexFriendRequest* frdReq, bool isIncoming) { - memset(friendRequest, 0, sizeof(friendRequest_t)); + memset(friendRequest, 0, sizeof(FriendRequest)); friendRequest->pid = frdReq->principalInfo.principalId; @@ -355,715 +305,1170 @@ namespace iosu convertFPDTimestampToDate(frdReq->message.expireTimestamp, &friendRequest->expireDate); } - void handleRequest_GetFriendListEx(iosuFpdCemuRequest_t* fpdCemuRequest) + struct FPProfile { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - for (uint32 i = 0; i < fpdCemuRequest->getFriendListEx.count; i++) - { - uint32 pid = fpdCemuRequest->getFriendListEx.pidList[i]; - friendData_t* friendData = fpdCemuRequest->getFriendListEx.friendData.GetPtr() + i; - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - setFriendDataFromNexFriend(friendData, &frd); - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - setFriendDataFromNexFriendRequest(friendData, &frdReq, incoming); - continue; - } - fpdCemuRequest->returnCode = 0x80000000; - return; - } - } + uint8be country; + uint8be area; + uint16be unused; + }; + static_assert(sizeof(FPProfile) == 4); - void handleRequest_GetFriendRequestListEx(iosuFpdCemuRequest_t* fpdCemuRequest) + struct SelfPresence { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) + uint8be ukn[0x130]; // todo + }; + static_assert(sizeof(SelfPresence) == 0x130); + + struct SelfPlayingGame + { + uint8be ukn0[0x10]; + }; + static_assert(sizeof(SelfPlayingGame) == 0x10); + + static const auto FPResult_Ok = 0; + static const auto FPResult_InvalidIPCParam = BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_FP, 0x680); + static const auto FPResult_RequestFailed = BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // figure out proper error code + + class FPDService : public iosu::nn::IPCSimpleService + { + + struct NotificationAsyncRequest { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - for (uint32 i = 0; i < fpdCemuRequest->getFriendRequestListEx.count; i++) - { - uint32 pid = fpdCemuRequest->getFriendListEx.pidList[i]; - friendRequest_t* friendRequest = fpdCemuRequest->getFriendRequestListEx.friendRequest.GetPtr() + i; - nexFriendRequest frdReq; - bool incoming = false; - if (!g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + NotificationAsyncRequest(IPCCommandBody* cmd, uint32 maxNumEntries, FPDNotification* notificationsOut, uint32be* countOut) + : cmd(cmd), maxNumEntries(maxNumEntries), notificationsOut(notificationsOut), countOut(countOut) { - cemuLog_log(LogType::Force, "Failed to get friend request"); - fpdCemuRequest->returnCode = 0x80000000; + } + + IPCCommandBody* cmd; + uint32 maxNumEntries; + FPDNotification* notificationsOut; + uint32be* countOut; + }; + + struct FPDClient + { + bool hasLoggedIn{false}; + uint32 notificationMask{0}; + NotificationRunningId prevRunningId{0}; + std::vector notificationRequests; + }; + + // storage for async IPC requests + std::vector m_asyncLoginRequests; + std::vector m_clients; + + public: + FPDService() : iosu::nn::IPCSimpleService("/dev/fpd") {} + + std::string GetThreadName() override + { + return "IOSUModule::FPD"; + } + + void StartService() override + { + cemu_assert_debug(m_asyncLoginRequests.empty()); + } + + void StopService() override + { + m_asyncLoginRequests.clear(); + for(auto& it : m_clients) + delete it; + m_clients.clear(); + } + + void* CreateClientObject() override + { + FPDClient* client = new FPDClient(); + m_clients.push_back(client); + return client; + } + + void DestroyClientObject(void* clientObject) override + { + FPDClient* client = (FPDClient*)clientObject; + std::erase(m_clients, client); + delete client; + } + + void SendQueuedNotifications(FPDClient* client) + { + if (client->notificationRequests.empty()) return; + if (client->notificationRequests.size() > 1) + cemuLog_log(LogType::Force, "FPD: More than one simultanous notification query not supported"); + NotificationAsyncRequest& request = client->notificationRequests[0]; + uint32 numNotifications = 0; + while(numNotifications < request.maxNumEntries) + { + auto notification = g_NotificationQueue.GetNextNotification(client->prevRunningId); + if (!notification) + break; + uint32 flag = 1 << static_cast(notification->type); + if((client->notificationMask & flag) == 0) + continue; + request.notificationsOut[numNotifications].type = static_cast(notification->type); + request.notificationsOut[numNotifications].pid = notification->pid; + numNotifications++; } - setFriendRequestFromNexFriendRequest(friendRequest, &frdReq, incoming); - } - } - - typedef struct - { - MPTR funcMPTR; - MPTR customParam; - }fpAsyncCallback_t; - - typedef struct - { - nexPrincipalBasicInfo* principalBasicInfo; - uint32* pidList; - sint32 count; - friendBasicInfo_t* friendBasicInfo; - fpAsyncCallback_t fpCallback; - }getBasicInfoAsyncParams_t; - - SysAllocator _fpCallbackResultArray; // use a ring buffer of results to avoid overwriting the result when multiple callbacks are queued at the same time - sint32 fpCallbackResultIndex = 0; - - void handleFPCallback(fpAsyncCallback_t* fpCallback, uint32 resultCode) - { - fpCallbackResultIndex = (fpCallbackResultIndex + 1) % 32; - uint32* resultPtr = _fpCallbackResultArray.GetPtr() + fpCallbackResultIndex; - - *resultPtr = resultCode; - - coreinitAsyncCallback_add(fpCallback->funcMPTR, 2, memory_getVirtualOffsetFromPointer(resultPtr), fpCallback->customParam); - } - - void handleFPCallback2(MPTR funcMPTR, MPTR custom, uint32 resultCode) - { - fpCallbackResultIndex = (fpCallbackResultIndex + 1) % 32; - uint32* resultPtr = _fpCallbackResultArray.GetPtr() + fpCallbackResultIndex; - - *resultPtr = resultCode; - - coreinitAsyncCallback_add(funcMPTR, 2, memory_getVirtualOffsetFromPointer(resultPtr), custom); - } - - void handleResultCB_GetBasicInfoAsync(NexFriends* nexFriends, uint32 result, void* custom) - { - getBasicInfoAsyncParams_t* cbInfo = (getBasicInfoAsyncParams_t*)custom; - if (result != 0) - { - handleFPCallback(&cbInfo->fpCallback, 0x80000000); // todo - properly translate internal error to nn::fp error code - free(cbInfo->principalBasicInfo); - free(cbInfo->pidList); - free(cbInfo); - return; + if (numNotifications == 0) + return; + *request.countOut = numNotifications; + ServiceCallAsyncRespond(request.cmd, FPResult_Ok); + client->notificationRequests.erase(client->notificationRequests.begin()); } - // convert PrincipalBasicInfo into friendBasicInfo - for (sint32 i = 0; i < cbInfo->count; i++) + void TimerUpdate() override { - friendBasicInfo_t* basicInfo = cbInfo->friendBasicInfo + i; - nexPrincipalBasicInfo* principalBasicInfo = cbInfo->principalBasicInfo + i; + // called once a second while service is running + std::unique_lock _l(g_fpd.mtxFriendSession); + if (!g_fpd.nexFriendSession) + return; + g_fpd.nexFriendSession->update(); + while(!m_asyncLoginRequests.empty()) + { + if(g_fpd.nexFriendSession->isOnline()) + { + ServiceCallAsyncRespond(m_asyncLoginRequests.front(), FPResult_Ok); + m_asyncLoginRequests.erase(m_asyncLoginRequests.begin()); + } + else + break; + } + // handle notification responses + g_NotificationQueue.RemoveExpired(); + for(auto& client : m_clients) + SendQueuedNotifications(client); + } - memset(basicInfo, 0, sizeof(friendBasicInfo_t)); - basicInfo->pid = principalBasicInfo->principalId; - strcpy(basicInfo->nnid, principalBasicInfo->nnid); + uint32 ServiceCall(void* clientObject, uint32 requestId, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) override + { + // for /dev/fpd input and output vectors are swapped + std::swap(vecIn, vecOut); + std::swap(numVecIn, numVecOut); - convertMultiByteStringToBigEndianWidechar(principalBasicInfo->mii.miiNickname, basicInfo->screenname, sizeof(basicInfo->screenname) / sizeof(uint16be)); - memcpy(basicInfo->miiData, principalBasicInfo->mii.miiData, FFL_SIZE); + FPDClient* fpdClient = (FPDClient*)clientObject; + switch(static_cast(requestId)) + { + case FPD_REQUEST_ID::SetNotificationMask: + return CallHandler_SetNotificationMask(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetNotificationAsync: + return CallHandler_GetNotificationAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::SetLedEventMask: + cemuLog_logDebug(LogType::Force, "[/dev/fpd] SetLedEventMask is todo"); + return FPResult_Ok; + case FPD_REQUEST_ID::LoginAsync: + return CallHandler_LoginAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::HasLoggedIn: + return CallHandler_HasLoggedIn(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::IsOnline: + return CallHandler_IsOnline(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPrincipalId: + return CallHandler_GetMyPrincipalId(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyAccountId: + return CallHandler_GetMyAccountId(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyScreenName: + return CallHandler_GetMyScreenName(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyMii: + return CallHandler_GetMyMii(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyProfile: + return CallHandler_GetMyProfile(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPresence: + return CallHandler_GetMyPresence(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyComment: + return CallHandler_GetMyComment(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPreference: + return CallHandler_GetMyPreference(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetMyPlayingGame: + return CallHandler_GetMyPlayingGame(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendAccountId: + return CallHandler_GetFriendAccountId(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendScreenName: + return CallHandler_GetFriendScreenName(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendMii: + return CallHandler_GetFriendMii(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendPresence: + return CallHandler_GetFriendPresence(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendRelationship: + return CallHandler_GetFriendRelationship(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendList: + return CallHandler_GetFriendList_GetFriendListAll(fpdClient, vecIn, numVecIn, vecOut, numVecOut, false); + case FPD_REQUEST_ID::GetFriendListAll: + return CallHandler_GetFriendList_GetFriendListAll(fpdClient, vecIn, numVecIn, vecOut, numVecOut, true); + case FPD_REQUEST_ID::GetFriendRequestList: + return CallHandler_GetFriendRequestList(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendRequestListEx: + return CallHandler_GetFriendRequestListEx(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetBlackList: + return CallHandler_GetBlackList(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetFriendListEx: + return CallHandler_GetFriendListEx(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::UpdatePreferenceAsync: + return CallHandler_UpdatePreferenceAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::AddFriendRequestByPlayRecordAsync: + return CallHandler_AddFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::AcceptFriendRequestAsync: + return CallHandler_AcceptFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::DeleteFriendRequestAsync: + return CallHandler_DeleteFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::CancelFriendRequestAsync: + return CallHandler_CancelFriendRequestAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::MarkFriendRequestsAsReceivedAsync: + return CallHandler_MarkFriendRequestsAsReceivedAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::RemoveFriendAsync: + return CallHandler_RemoveFriendAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::DeleteFriendFlagsAsync: + return CallHandler_DeleteFriendFlagsAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetBasicInfoAsync: + return CallHandler_GetBasicInfoAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::CheckSettingStatusAsync: + return CallHandler_CheckSettingStatusAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::IsPreferenceValid: + return CallHandler_IsPreferenceValid(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::GetRequestBlockSettingAsync: + return CallHandler_GetRequestBlockSettingAsync(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::AddFriendAsyncByPid: + return CallHandler_AddFriendAsyncByPid(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + case FPD_REQUEST_ID::UpdateGameModeVariation1: + case FPD_REQUEST_ID::UpdateGameModeVariation2: + return CallHandler_UpdateGameMode(fpdClient, vecIn, numVecIn, vecOut, numVecOut); + default: + cemuLog_log(LogType::Force, "Unsupported service call {} to /dev/fpd", requestId); + return BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); + } + } - basicInfo->uknDate90.day = 1; - basicInfo->uknDate90.month = 1; - basicInfo->uknDate90.hour = 1; - basicInfo->uknDate90.minute = 1; - basicInfo->uknDate90.second = 1; + #define DeclareInputPtr(__Name, __T, __count, __vecIndex) if(sizeof(__T)*(__count) != vecIn[__vecIndex].size) { cemuLog_log(LogType::Force, "FPD: IPC buffer has incorrect size"); return FPResult_InvalidIPCParam;}; __T* __Name = ((__T*)vecIn[__vecIndex].basePhys.GetPtr()) + #define DeclareInput(__Name, __T, __vecIndex) if(sizeof(__T) != vecIn[__vecIndex].size) { cemuLog_log(LogType::Force, "FPD: IPC buffer has incorrect size"); return FPResult_InvalidIPCParam;}; __T __Name = *((__T*)vecIn[__vecIndex].basePhys.GetPtr()) + #define DeclareOutputPtr(__Name, __T, __count, __vecIndex) if(sizeof(__T)*(__count) != vecOut[__vecIndex].size) { cemuLog_log(LogType::Force, "FPD: IPC buffer has incorrect size"); return FPResult_InvalidIPCParam;}; __T* __Name = ((__T*)vecOut[__vecIndex].basePhys.GetPtr()) + + template + static nnResult WriteValueOutput(IPCIoctlVector* vec, const T& value) + { + if(vec->size != sizeof(T)) + return FPResult_InvalidIPCParam; + *(T*)vec->basePhys.GetPtr() = value; + return FPResult_Ok; + } + + nnResult CallHandler_SetNotificationMask(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + DeclareInput(notificationMask, uint32be, 0); + fpdClient->notificationMask = notificationMask; + return FPResult_Ok; + } + + nnResult CallHandler_GetNotificationAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 2) + return FPResult_InvalidIPCParam; + if((vecOut[0].size % sizeof(FPDNotification)) != 0 || vecOut[0].size < sizeof(FPDNotification)) + { + cemuLog_log(LogType::Force, "FPD GetNotificationAsync: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + DeclareOutputPtr(countOut, uint32be, 1, 1); + uint32 maxCount = vecOut[0].size / sizeof(FPDNotification); + DeclareOutputPtr(notificationList, FPDNotification, maxCount, 0); + fpdClient->notificationRequests.emplace_back(cmd, maxCount, notificationList, countOut); + SendQueuedNotifications(fpdClient); // if any notifications are queued, send them immediately + return FPResult_Ok; + } + + nnResult CallHandler_LoginAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!ActiveSettings::IsOnlineEnabled()) + { + // not online, fail immediately + return BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // todo + } + StartFriendSession(); + fpdClient->hasLoggedIn = true; + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + m_asyncLoginRequests.emplace_back(cmd); + return FPResult_Ok; + } + + nnResult CallHandler_HasLoggedIn(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + return WriteValueOutput(vecOut, fpdClient->hasLoggedIn ? 1 : 0); + } + + nnResult CallHandler_IsOnline(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + bool isOnline = g_fpd.nexFriendSession ? g_fpd.nexFriendSession->isOnline() : false; + return WriteValueOutput(vecOut, isOnline?1:0); + } + + nnResult CallHandler_GetMyPrincipalId(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + uint32 pid = 0; + iosu::act::getPrincipalId(slot, &pid); + return WriteValueOutput(vecOut, pid); + } + + nnResult CallHandler_GetMyAccountId(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + std::string accountId = iosu::act::getAccountId2(slot); + if(vecOut->size != ACT_ACCOUNTID_LENGTH) + { + cemuLog_log(LogType::Force, "GetMyAccountId: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + if(accountId.length() > ACT_ACCOUNTID_LENGTH-1) + { + cemuLog_log(LogType::Force, "GetMyAccountId: AccountID is too long"); + return FPResult_InvalidIPCParam; + } + if(accountId.empty()) + { + cemuLog_log(LogType::Force, "GetMyAccountId: AccountID is empty"); + return FPResult_InvalidIPCParam; // should return 0xC0C00800 ? + } + char* outputStr = (char*)vecOut->basePhys.GetPtr(); + memset(outputStr, 0, ACT_ACCOUNTID_LENGTH); + memcpy(outputStr, accountId.data(), accountId.length()); + return FPResult_Ok; + } + + nnResult CallHandler_GetMyScreenName(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + if(vecOut->size != ACT_NICKNAME_SIZE*sizeof(uint16be)) + { + cemuLog_log(LogType::Force, "GetMyScreenName: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + uint16 screenname[ACT_NICKNAME_SIZE]{0}; + bool r = iosu::act::getScreenname(slot, screenname); + if (!r) + { + cemuLog_log(LogType::Force, "GetMyScreenName: Screenname is empty"); + return FPResult_InvalidIPCParam; // should return 0xC0C00800 ? + } + uint16be* outputStr = (uint16be*)vecOut->basePhys.GetPtr(); + for(sint32 i = 0; i < ACT_NICKNAME_SIZE; i++) + outputStr[i] = screenname[i]; + return FPResult_Ok; + } + + nnResult CallHandler_GetMyMii(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + if(vecOut->size != FFL_SIZE) + { + cemuLog_log(LogType::Force, "GetMyMii: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + bool r = iosu::act::getMii(slot, (FFLData_t*)vecOut->basePhys.GetPtr()); + if (!r) + { + cemuLog_log(LogType::Force, "GetMyMii: Mii is empty"); + return FPResult_InvalidIPCParam; // should return 0xC0C00800 ? + } + return FPResult_Ok; + } + + nnResult CallHandler_GetMyProfile(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + FPProfile profile{0}; + // todo + cemuLog_log(LogType::Force, "GetMyProfile is todo"); + return WriteValueOutput(vecOut, profile); + } + + nnResult CallHandler_GetMyPresence(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + uint8 slot = iosu::act::getCurrentAccountSlot(); + SelfPresence selfPresence{0}; + cemuLog_log(LogType::Force, "GetMyPresence is todo"); + return WriteValueOutput(vecOut, selfPresence); + } + + nnResult CallHandler_GetMyComment(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + static constexpr uint32 MY_COMMENT_LENGTH = 0x12; // are comments utf16? Buffer length is 0x24 + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + if(vecOut->size != MY_COMMENT_LENGTH*sizeof(uint16be)) + { + cemuLog_log(LogType::Force, "GetMyComment: Unexpected output size"); + return FPResult_InvalidIPCParam; + } + std::basic_string myComment; + myComment.resize(MY_COMMENT_LENGTH); + memcpy(vecOut->basePhys.GetPtr(), myComment.data(), MY_COMMENT_LENGTH*sizeof(uint16be)); + return 0; + } + + nnResult CallHandler_GetMyPreference(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + FPDPreference selfPreference{0}; + if(g_fpd.nexFriendSession) + { + nexPrincipalPreference nexPreference; + g_fpd.nexFriendSession->getMyPreference(nexPreference); + selfPreference.showOnline = nexPreference.showOnline; + selfPreference.showGame = nexPreference.showGame; + selfPreference.blockFriendRequests = nexPreference.blockFriendRequests; + selfPreference.ukn = 0; + } + else + memset(&selfPreference, 0, sizeof(FPDPreference)); + return WriteValueOutput(vecOut, selfPreference); + } + + nnResult CallHandler_GetMyPlayingGame(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + SelfPlayingGame selfPlayingGame{0}; + cemuLog_log(LogType::Force, "GetMyPlayingGame is todo"); + return WriteValueOutput(vecOut, selfPlayingGame); + } + + nnResult CallHandler_GetFriendAccountId(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + // todo - online check + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(accountId, CafeString, count, 0); + memset(accountId, 0, ACT_ACCOUNTID_LENGTH * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + const uint32 pid = pidList[i]; + auto& nnidOutput = accountId[i]; + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + nnidOutput.assign(frd.nnaInfo.principalInfo.nnid); + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + nnidOutput.assign(frdReq.principalInfo.nnid); + continue; + } + cemuLog_log(LogType::Force, "GetFriendAccountId: PID {} not found", pid); + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendScreenName(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + static_assert(sizeof(CafeWideString) == 11*2); + if(numVecIn != 3 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareInput(replaceNonAscii, uint8be, 2); + DeclareOutputPtr(nameList, CafeWideString, count, 0); + uint8be* languageList = nullptr; + if(vecOut[1].size > 0) // languageList is optional + { + DeclareOutputPtr(_languageList, uint8be, count, 1); + languageList = _languageList; + } + memset(nameList, 0, ACT_NICKNAME_SIZE * sizeof(CafeWideString)); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + const uint32 pid = pidList[i]; + CafeWideString& screennameOutput = nameList[i]; + if (languageList) + languageList[i] = 0; // unknown + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + screennameOutput.assignFromUTF8(frd.nnaInfo.principalInfo.mii.miiNickname); + if (languageList) + languageList[i] = frd.nnaInfo.principalInfo.regionGuessed; + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + screennameOutput.assignFromUTF8(frdReq.principalInfo.mii.miiNickname); + if (languageList) + languageList[i] = frdReq.principalInfo.regionGuessed; + continue; + } + cemuLog_log(LogType::Force, "GetFriendScreenName: PID {} not found", pid); + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendMii(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(miiList, FFLData_t, count, 0); + memset(miiList, 0, sizeof(FFLData_t) * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + const uint32 pid = pidList[i]; + FFLData_t& miiOutput = miiList[i]; + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + memcpy(&miiOutput, frd.nnaInfo.principalInfo.mii.miiData, FFL_SIZE); + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + memcpy(&miiOutput, frdReq.principalInfo.mii.miiData, FFL_SIZE); + continue; + } + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendPresence(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(presenceList, FriendPresence, count, 0); + memset(presenceList, 0, sizeof(FriendPresence) * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + FriendPresence& presenceOutput = presenceList[i]; + const uint32 pid = pidList[i]; + nexFriend frd; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + presenceOutput.isOnline = frd.presence.isOnline ? 1 : 0; + presenceOutput.isValid = 1; + // todo - region and subregion + presenceOutput.gameMode.joinFlagMask = frd.presence.joinFlagMask; + presenceOutput.gameMode.matchmakeType = frd.presence.joinAvailability; + presenceOutput.gameMode.joinGameId = frd.presence.gameId; + presenceOutput.gameMode.joinGameMode = frd.presence.gameMode; + presenceOutput.gameMode.hostPid = frd.presence.hostPid; + presenceOutput.gameMode.groupId = frd.presence.groupId; + + memcpy(presenceOutput.gameMode.appSpecificData, frd.presence.appSpecificData, 0x14); + } + else + { + cemuLog_log(LogType::Force, "GetFriendPresence: PID {} not found", pid); + } + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendRelationship(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + // todo - check for valid session (same for all GetFriend* functions) + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(relationshipList, uint8be, count, 0); // correct? + for(uint32 i=0; igetFriendByPID(frd, pid)) + { + relationshipOutput = RELATIONSHIP_FRIEND; + continue; + } + else if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + if (incoming) + relationshipOutput = RELATIONSHIP_FRIENDREQUEST_IN; + else + relationshipOutput = RELATIONSHIP_FRIENDREQUEST_OUT; + } + } + } + return FPResult_Ok; + } + + nnResult CallHandler_GetFriendList_GetFriendListAll(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut, bool isAll) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(startIndex, uint32be, 0); + DeclareInput(maxCount, uint32be, 1); + if (maxCount * sizeof(FriendPID) != vecOut[0].size || vecOut[0].basePhys.IsNull()) + { + cemuLog_log(LogType::Force, "GetFriendListAll: pid list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if (!g_fpd.nexFriendSession) + return WriteValueOutput(vecOut+1, 0); + betype* pidList = (betype*)vecOut[0].basePhys.GetPtr(); + std::vector temporaryPidList; + temporaryPidList.resize(std::min(maxCount, 500)); + uint32 pidCount = 0; + g_fpd.nexFriendSession->getFriendPIDs(temporaryPidList.data(), &pidCount, startIndex, temporaryPidList.size(), isAll); + std::copy(temporaryPidList.begin(), temporaryPidList.begin() + pidCount, pidList); + return WriteValueOutput(vecOut+1, pidCount); + } + + nnResult CallHandler_GetFriendRequestList(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(startIndex, uint32be, 0); + DeclareInput(maxCount, uint32be, 1); + if(maxCount * sizeof(FriendPID) != vecOut[0].size || vecOut[0].basePhys.IsNull()) + { + cemuLog_log(LogType::Force, "GetFriendRequestList: pid list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if (!g_fpd.nexFriendSession) + return WriteValueOutput(vecOut+1, 0); + betype* pidList = (betype*)vecOut[0].basePhys.GetPtr(); + std::vector temporaryPidList; + temporaryPidList.resize(std::min(maxCount, 500)); + uint32 pidCount = 0; + g_fpd.nexFriendSession->getFriendRequestPIDs(temporaryPidList.data(), &pidCount, startIndex, temporaryPidList.size(), true, false); + std::copy(temporaryPidList.begin(), temporaryPidList.begin() + pidCount, pidList); + return WriteValueOutput(vecOut+1, pidCount); + } + + nnResult CallHandler_GetFriendRequestListEx(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, uint32be, count, 0); + DeclareOutputPtr(friendRequests, FriendRequest, count, 0); + memset(friendRequests, 0, sizeof(FriendRequest) * count); + if (!g_fpd.nexFriendSession) + return FPResult_Ok; + for(uint32 i=0; igetFriendRequestByPID(frdReq, &incoming, pidList[i])) + { + cemuLog_log(LogType::Force, "GetFriendRequestListEx: Failed to get friend request"); + return FPResult_RequestFailed; + } + NexFriendRequestToFPDFriendRequest(friendRequests + i, &frdReq, incoming); + } + return FPResult_Ok; + } + + nnResult CallHandler_GetBlackList(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 2) + return FPResult_InvalidIPCParam; + DeclareInput(startIndex, uint32be, 0); + DeclareInput(maxCount, uint32be, 1); + if(maxCount * sizeof(FriendPID) != vecOut[0].size) + { + cemuLog_log(LogType::Force, "GetBlackList: pid list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if (!g_fpd.nexFriendSession) + return WriteValueOutput(vecOut+1, 0); + betype* pidList = (betype*)vecOut[0].basePhys.GetPtr(); + // todo! + cemuLog_logDebug(LogType::Force, "GetBlackList is todo"); + uint32 countOut = 0; + + return WriteValueOutput(vecOut+1, countOut); + } + + nnResult CallHandler_GetFriendListEx(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if(numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, betype, count, 0); + if(count * sizeof(FriendPID) != vecIn[0].size) + { + cemuLog_log(LogType::Force, "GetFriendListEx: pid input list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + if(count * sizeof(FriendData) != vecOut[0].size) + { + cemuLog_log(LogType::Force, "GetFriendListEx: Friend output list buffer size is incorrect"); + return FPResult_InvalidIPCParam; + } + FriendData* friendOutput = (FriendData*)vecOut[0].basePhys.GetPtr(); + memset(friendOutput, 0, sizeof(FriendData) * count); + if (g_fpd.nexFriendSession) + { + for (uint32 i = 0; i < count; i++) + { + uint32 pid = pidList[i]; + FriendData* friendData = friendOutput + i; + nexFriend frd; + nexFriendRequest frdReq; + if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) + { + NexFriendToFPDFriendData(friendData, &frd); + continue; + } + bool incoming = false; + if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) + { + NexFriendRequestToFPDFriendData(friendData, &frdReq, incoming); + continue; + } + cemuLog_logDebug(LogType::Force, "GetFriendListEx: Failed to find friend or request with pid {}", pid); + memset(friendData, 0, sizeof(FriendData)); + } + } + return FPResult_Ok; + } + + static void NexBasicInfoToBasicInfo(const nexPrincipalBasicInfo& nexBasicInfo, FriendBasicInfo& basicInfo) + { + memset(&basicInfo, 0, sizeof(FriendBasicInfo)); + basicInfo.pid = nexBasicInfo.principalId; + strcpy(basicInfo.nnid, nexBasicInfo.nnid); + + convertMultiByteStringToBigEndianWidechar(nexBasicInfo.mii.miiNickname, basicInfo.screenname, sizeof(basicInfo.screenname) / sizeof(uint16be)); + memcpy(basicInfo.miiData, nexBasicInfo.mii.miiData, FFL_SIZE); + + basicInfo.uknDate90.day = 1; + basicInfo.uknDate90.month = 1; + basicInfo.uknDate90.hour = 1; + basicInfo.uknDate90.minute = 1; + basicInfo.uknDate90.second = 1; // unknown values not set: // ukn15 // ukn2E // ukn2F } - // success - handleFPCallback(&cbInfo->fpCallback, 0x00000000); - free(cbInfo->principalBasicInfo); - free(cbInfo->pidList); - free(cbInfo); - } - - void handleRequest_GetBasicInfoAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) + nnResult CallHandler_GetBasicInfoAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - sint32 count = fpdCemuRequest->getBasicInfo.count; - - nexPrincipalBasicInfo* principalBasicInfo = new nexPrincipalBasicInfo[count]; - uint32* pidList = (uint32*)malloc(sizeof(uint32)*count); - for (sint32 i = 0; i < count; i++) - pidList[i] = fpdCemuRequest->getBasicInfo.pidList[i]; - getBasicInfoAsyncParams_t* cbInfo = (getBasicInfoAsyncParams_t*)malloc(sizeof(getBasicInfoAsyncParams_t)); - cbInfo->principalBasicInfo = principalBasicInfo; - cbInfo->pidList = pidList; - cbInfo->count = count; - cbInfo->friendBasicInfo = fpdCemuRequest->getBasicInfo.basicInfo.GetPtr(); - cbInfo->fpCallback.funcMPTR = fpdCemuRequest->getBasicInfo.funcPtr; - cbInfo->fpCallback.customParam = fpdCemuRequest->getBasicInfo.custom; - g_fpd.nexFriendSession->requestPrincipleBaseInfoByPID(principalBasicInfo, pidList, count, handleResultCB_GetBasicInfoAsync, cbInfo); - } - - void handleResponse_addOrRemoveFriend(uint32 errorCode, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - g_fpd.nexFriendSession->requestGetAllInformation(); // refresh list - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_RemoveFriendAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - g_fpd.nexFriendSession->removeFriend(fpdCemuRequest->addOrRemoveFriend.pid, std::bind(handleResponse_addOrRemoveFriend, std::placeholders::_1, fpdCemuRequest->addOrRemoveFriend.funcPtr, fpdCemuRequest->addOrRemoveFriend.custom)); - } - - void handleResponse_MarkFriendRequestAsReceivedAsync(uint32 errorCode, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - handleFPCallback2(funcMPTR, custom, 0); - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_MarkFriendRequestAsReceivedAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - // convert messageId list to little endian - uint64 messageIds[100]; - sint32 count = 0; - for (uint32 i = 0; i < fpdCemuRequest->markFriendRequest.count; i++) - { - uint64 mid = _swapEndianU64(fpdCemuRequest->markFriendRequest.messageIdList.GetPtr()[i]); - if (mid == 0) + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidListBE, betype, count, 0); + DeclareOutputPtr(basicInfoList, FriendBasicInfo, count, 0); + if (!g_fpd.nexFriendSession) { - cemuLog_logDebug(LogType::Force, "MarkFriendRequestAsReceivedAsync - Invalid messageId"); - continue; + memset(basicInfoList, 0, sizeof(FriendBasicInfo) * sizeof(count)); + return FPResult_Ok; } - messageIds[count] = mid; - count++; - if (count >= sizeof(messageIds)/sizeof(messageIds[0])) - break; - } - // skipped for now - g_fpd.nexFriendSession->markFriendRequestsAsReceived(messageIds, count, std::bind(handleResponse_MarkFriendRequestAsReceivedAsync, std::placeholders::_1, fpdCemuRequest->markFriendRequest.funcPtr, fpdCemuRequest->markFriendRequest.custom)); - } - - void handleResponse_cancelMyFriendRequest(uint32 errorCode, uint32 pid, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_CancelFriendRequestAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - // find friend request with matching pid - nexFriendRequest frq; - bool isIncoming; - if (g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, fpdCemuRequest->cancelOrAcceptFriendRequest.messageId)) - { - g_fpd.nexFriendSession->removeFriend(frq.principalInfo.principalId, std::bind(handleResponse_cancelMyFriendRequest, std::placeholders::_1, frq.principalInfo.principalId, fpdCemuRequest->cancelOrAcceptFriendRequest.funcPtr, fpdCemuRequest->cancelOrAcceptFriendRequest.custom)); - } - else - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - } - - void handleResponse_acceptFriendRequest(uint32 errorCode, uint32 pid, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - g_fpd.nexFriendSession->requestGetAllInformation(); // refresh list - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_AcceptFriendRequestAsync(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - // find friend request with matching pid - nexFriendRequest frq; - bool isIncoming; - if (g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, fpdCemuRequest->cancelOrAcceptFriendRequest.messageId)) - { - g_fpd.nexFriendSession->acceptFriendRequest(fpdCemuRequest->cancelOrAcceptFriendRequest.messageId, std::bind(handleResponse_acceptFriendRequest, std::placeholders::_1, frq.principalInfo.principalId, fpdCemuRequest->cancelOrAcceptFriendRequest.funcPtr, fpdCemuRequest->cancelOrAcceptFriendRequest.custom)); - } - else - { - fpdCemuRequest->returnCode = 0x80000000; - return; - } - } - - void handleResponse_addFriendRequest(uint32 errorCode, MPTR funcMPTR, MPTR custom) - { - if (errorCode == 0) - { - handleFPCallback2(funcMPTR, custom, 0); - g_fpd.nexFriendSession->requestGetAllInformation(); // refresh list - } - else - handleFPCallback2(funcMPTR, custom, 0x80000000); - } - - void handleRequest_AddFriendRequest(iosuFpdCemuRequest_t* fpdCemuRequest) - { - fpdCemuRequest->returnCode = 0; - if (g_fpd.nexFriendSession == nullptr || g_fpd.nexFriendSession->isOnline() == false) - { - fpdCemuRequest->returnCode = 0x80000000; - return; + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + std::vector pidList; + std::copy(pidListBE, pidListBE + count, std::back_inserter(pidList)); + g_fpd.nexFriendSession->requestPrincipleBaseInfoByPID(pidList.data(), count, [cmd, basicInfoList, count](NexFriends::RpcErrorCode result, std::span basicInfo) -> void { + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + cemu_assert_debug(basicInfo.size() == count); + for(uint32 i = 0; i < count; i++) + NexBasicInfoToBasicInfo(basicInfo[i], basicInfoList[i]); + ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; } - uint16be* input = fpdCemuRequest->addFriendRequest.message.GetPtr(); - size_t inputLen = 0; - while (input[inputLen] != 0) - inputLen++; - std::string msg = StringHelpers::ToUtf8({ input, inputLen }); - - g_fpd.nexFriendSession->addFriendRequest(fpdCemuRequest->addFriendRequest.pid, msg.data(), std::bind(handleResponse_addFriendRequest, std::placeholders::_1, fpdCemuRequest->addFriendRequest.funcPtr, fpdCemuRequest->addFriendRequest.custom)); - } - - // called once a second to handle state checking and updates of the friends service - void iosuFpd_updateFriendsService() - { - if (g_fpd.nexFriendSession) + nnResult CallHandler_UpdatePreferenceAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) { - g_fpd.nexFriendSession->update(); + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInputPtr(newPreference, FPDPreference, 1, 0); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->updatePreferencesAsync(nexPrincipalPreference(newPreference->showOnline != 0 ? 1 : 0, newPreference->showGame != 0 ? 1 : 0, newPreference->blockFriendRequests != 0 ? 1 : 0), [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } - if (g_fpd.asyncLoginCallback.func != MPTR_NULL) + nnResult CallHandler_AddFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 2 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInputPtr(playRecord, RecentPlayRecordEx, 1, 0); + uint32 msgLength = vecIn[1].size/sizeof(uint16be); + DeclareInputPtr(msgBE, uint16be, msgLength, 1); + if(msgLength == 0 || msgBE[msgLength-1] != 0) { - if (g_fpd.nexFriendSession->isOnline()) - { - *_fpdAsyncLoginRetCode.GetPtr() = 0x00000000; - coreinitAsyncCallback_add(g_fpd.asyncLoginCallback.func, 2, _fpdAsyncLoginRetCode.GetMPTR(), g_fpd.asyncLoginCallback.customParam); - g_fpd.asyncLoginCallback.func = MPTR_NULL; - g_fpd.asyncLoginCallback.customParam = MPTR_NULL; - } + cemuLog_log(LogType::Force, "AddFriendRequestAsync: Message must contain at least a null-termination character and end with one"); + return FPResult_InvalidIPCParam; } + std::string msg = StringHelpers::ToUtf8({ msgBE, msgLength-1 }); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->addFriendRequest(playRecord->pid, msg.data(), [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; } - } - void iosuFpd_thread() - { - SetThreadName("iosuFpd_thread"); - while (true) + nnResult CallHandler_AcceptFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) { - uint32 returnValue = 0; // Ioctl return value - ioQueueEntry_t* ioQueueEntry = iosuIoctl_getNextWithTimeout(IOS_DEVICE_FPD, 1000); - if (ioQueueEntry == nullptr) + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(requestId, uint64be, 0); + nexFriendRequest frq; + bool isIncoming; + if (!g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, requestId)) + return FPResult_RequestFailed; + if(!isIncoming) { - iosuFpd_updateFriendsService(); - continue; + cemuLog_log(LogType::Force, "AcceptFriendRequestAsync: Trying to accept outgoing friend request"); + return FPResult_RequestFailed; } - if (ioQueueEntry->request == IOSU_FPD_REQUEST_CEMU) - { - iosuFpdCemuRequest_t* fpdCemuRequest = (iosuFpdCemuRequest_t*)ioQueueEntry->bufferVectors[0].buffer.GetPtr(); - if (fpdCemuRequest->requestCode == IOSU_FPD_INITIALIZE) - { - if (g_fpd.isInitialized2 == false) - { - if(ActiveSettings::IsOnlineEnabled()) - startFriendSession(); - - g_fpd.isInitialized2 = true; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_SET_NOTIFICATION_HANDLER) - { - g_fpd.notificationFunc = fpdCemuRequest->setNotificationHandler.funcPtr; - g_fpd.notificationCustomParam = fpdCemuRequest->setNotificationHandler.custom; - g_fpd.notificationMask = fpdCemuRequest->setNotificationHandler.notificationMask; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_LOGIN_ASYNC) - { - if (g_fpd.nexFriendSession) - { - g_fpd.asyncLoginCallback.func = fpdCemuRequest->loginAsync.funcPtr; - g_fpd.asyncLoginCallback.customParam = fpdCemuRequest->loginAsync.custom; - } - else - { - // offline mode - *_fpdAsyncLoginRetCode.GetPtr() = 0; // if we return 0x80000000 here then Splatoon softlocks during boot - coreinitAsyncCallback_add(fpdCemuRequest->loginAsync.funcPtr, 2, _fpdAsyncLoginRetCode.GetMPTR(), fpdCemuRequest->loginAsync.custom); - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_IS_ONLINE) - { - fpdCemuRequest->resultU32.u32 = g_fpd.nexFriendSession ? g_fpd.nexFriendSession->isOnline() : 0; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_IS_PREFERENCE_VALID) - { - fpdCemuRequest->resultU32.u32 = 1; // todo (if this returns 0, the friend app will show the first-time-setup screen and ask the user to configure preferences) - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_PRINCIPAL_ID) - { - uint8 slot = iosu::act::getCurrentAccountSlot(); - iosu::act::getPrincipalId(slot, &fpdCemuRequest->resultU32.u32); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_ACCOUNT_ID) - { - char* accountId = (char*)fpdCemuRequest->common.ptr.GetPtr(); - uint8 slot = iosu::act::getCurrentAccountSlot(); - if (iosu::act::getAccountId(slot, accountId) == false) - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - else - fpdCemuRequest->returnCode = 0; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_MII) - { - FFLData_t* fflData = (FFLData_t*)fpdCemuRequest->common.ptr.GetPtr(); - uint8 slot = iosu::act::getCurrentAccountSlot(); - if (iosu::act::getMii(slot, fflData) == false) - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - else - fpdCemuRequest->returnCode = 0; - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_MY_SCREENNAME) - { - uint16be* screennameOutput = (uint16be*)fpdCemuRequest->common.ptr.GetPtr(); - uint8 slot = iosu::act::getCurrentAccountSlot(); - uint16 screennameTemp[ACT_NICKNAME_LENGTH]; - if (iosu::act::getScreenname(slot, screennameTemp)) - { - for (sint32 i = 0; i < ACT_NICKNAME_LENGTH; i++) - { - screennameOutput[i] = screennameTemp[i]; - } - screennameOutput[ACT_NICKNAME_LENGTH] = '\0'; // length is ACT_NICKNAME_LENGTH+1 - fpdCemuRequest->returnCode = 0; - } - else - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - screennameOutput[0] = '\0'; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_ACCOUNT_ID) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendAccountId.count; i++) - { - char* nnidOutput = fpdCemuRequest->getFriendAccountId.accountIds.GetPtr()+i*17; - uint32 pid = fpdCemuRequest->getFriendAccountId.pidList[i]; - if (g_fpd.nexFriendSession == nullptr) - { - nnidOutput[0] = '\0'; - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - strcpy(nnidOutput, frd.nnaInfo.principalInfo.nnid); - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - strcpy(nnidOutput, frdReq.principalInfo.nnid); - continue; - } - nnidOutput[0] = '\0'; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_SCREENNAME) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendScreenname.count; i++) - { - uint16be* screennameOutput = fpdCemuRequest->getFriendScreenname.nameList.GetPtr()+i*11; - uint32 pid = fpdCemuRequest->getFriendScreenname.pidList[i]; - if(fpdCemuRequest->getFriendScreenname.languageList.IsNull() == false) - fpdCemuRequest->getFriendScreenname.languageList.GetPtr()[i] = 0; - if (g_fpd.nexFriendSession == nullptr) - { - screennameOutput[0] = '\0'; - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - convertMultiByteStringToBigEndianWidechar(frd.nnaInfo.principalInfo.mii.miiNickname, screennameOutput, 11); - if (fpdCemuRequest->getFriendScreenname.languageList.IsNull() == false) - fpdCemuRequest->getFriendScreenname.languageList.GetPtr()[i] = frd.nnaInfo.principalInfo.regionGuessed; - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - convertMultiByteStringToBigEndianWidechar(frdReq.principalInfo.mii.miiNickname, screennameOutput, 11); - if (fpdCemuRequest->getFriendScreenname.languageList.IsNull() == false) - fpdCemuRequest->getFriendScreenname.languageList.GetPtr()[i] = frdReq.principalInfo.regionGuessed; - continue; - } - screennameOutput[0] = '\0'; - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_MII) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendMii.count; i++) - { - uint8* miiOutput = fpdCemuRequest->getFriendMii.miiList + i * FFL_SIZE; - uint32 pid = fpdCemuRequest->getFriendMii.pidList[i]; - if (g_fpd.nexFriendSession == nullptr) - { - memset(miiOutput, 0, FFL_SIZE); - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - memcpy(miiOutput, frd.nnaInfo.principalInfo.mii.miiData, FFL_SIZE); - continue; - } - bool incoming = false; - if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - memcpy(miiOutput, frdReq.principalInfo.mii.miiData, FFL_SIZE); - continue; - } - memset(miiOutput, 0, FFL_SIZE); - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_PRESENCE) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendPresence.count; i++) - { - friendPresence_t* presenceOutput = (friendPresence_t*)(fpdCemuRequest->getFriendPresence.presenceList + i * sizeof(friendPresence_t)); - memset(presenceOutput, 0, sizeof(friendPresence_t)); - uint32 pid = fpdCemuRequest->getFriendPresence.pidList[i]; - if (g_fpd.nexFriendSession == nullptr) - { - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - presenceOutput->isOnline = frd.presence.isOnline ? 1 : 0; - presenceOutput->isValid = 1; - - presenceOutput->gameMode.joinFlagMask = frd.presence.joinFlagMask; - presenceOutput->gameMode.matchmakeType = frd.presence.joinAvailability; - presenceOutput->gameMode.joinGameId = frd.presence.gameId; - presenceOutput->gameMode.joinGameMode = frd.presence.gameMode; - presenceOutput->gameMode.hostPid = frd.presence.hostPid; - presenceOutput->gameMode.groupId = frd.presence.groupId; - - memcpy(presenceOutput->gameMode.appSpecificData, frd.presence.appSpecificData, 0x14); - - continue; - } - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_RELATIONSHIP) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - fpdCemuRequest->returnCode = 0; - for (sint32 i = 0; i < fpdCemuRequest->getFriendRelationship.count; i++) - { - uint8* relationshipOutput = (fpdCemuRequest->getFriendRelationship.relationshipList + i); - uint32 pid = fpdCemuRequest->getFriendRelationship.pidList[i]; - *relationshipOutput = RELATIONSHIP_INVALID; - if (g_fpd.nexFriendSession == nullptr) - { - continue; - } - nexFriend frd; - nexFriendRequest frdReq; - bool incoming; - if (g_fpd.nexFriendSession->getFriendByPID(frd, pid)) - { - *relationshipOutput = RELATIONSHIP_FRIEND; - continue; - } - else if (g_fpd.nexFriendSession->getFriendRequestByPID(frdReq, &incoming, pid)) - { - if(incoming) - *relationshipOutput = RELATIONSHIP_FRIENDREQUEST_IN; - else - *relationshipOutput = RELATIONSHIP_FRIENDREQUEST_OUT; - } - } - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_LIST) - { - handleRequest_GetFriendList(fpdCemuRequest, false); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIENDREQUEST_LIST) - { - handleRequest_GetFriendRequestList(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_LIST_ALL) - { - handleRequest_GetFriendList(fpdCemuRequest, true); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIEND_LIST_EX) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - handleRequest_GetFriendListEx(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_FRIENDREQUEST_LIST_EX) - { - if (g_fpd.nexFriendSession == nullptr) - { - fpdCemuRequest->returnCode = 0x80000000; // todo - proper error code - iosuIoctl_completeRequest(ioQueueEntry, returnValue); - return; - } - handleRequest_GetFriendRequestListEx(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_ADD_FRIEND) - { - // todo - figure out how this works - *_fpdAsyncAddFriendRetCode.GetPtr() = 0; - coreinitAsyncCallback_add(fpdCemuRequest->addOrRemoveFriend.funcPtr, 2, _fpdAsyncAddFriendRetCode.GetMPTR(), fpdCemuRequest->addOrRemoveFriend.custom); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_ADD_FRIEND_REQUEST) - { - handleRequest_AddFriendRequest(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_REMOVE_FRIEND_ASYNC) - { - handleRequest_RemoveFriendAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_MARK_FRIEND_REQUEST_AS_RECEIVED_ASYNC) - { - handleRequest_MarkFriendRequestAsReceivedAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_CANCEL_FRIEND_REQUEST_ASYNC) - { - handleRequest_CancelFriendRequestAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_ACCEPT_FRIEND_REQUEST_ASYNC) - { - handleRequest_AcceptFriendRequestAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_GET_BASIC_INFO_ASYNC) - { - handleRequest_GetBasicInfoAsync(fpdCemuRequest); - } - else if (fpdCemuRequest->requestCode == IOSU_FPD_UPDATE_GAMEMODE) - { - gameMode_t* gameMode = fpdCemuRequest->updateGameMode.gameMode.GetPtr(); - uint16be* gameModeMessage = fpdCemuRequest->updateGameMode.gameModeMessage.GetPtr(); - - g_fpd.myPresence.joinFlagMask = gameMode->joinFlagMask; - - g_fpd.myPresence.joinAvailability = (uint8)(uint32)gameMode->matchmakeType; - g_fpd.myPresence.gameId = gameMode->joinGameId; - g_fpd.myPresence.gameMode = gameMode->joinGameMode; - g_fpd.myPresence.hostPid = gameMode->hostPid; - g_fpd.myPresence.groupId = gameMode->groupId; - memcpy(g_fpd.myPresence.appSpecificData, gameMode->appSpecificData, 0x14); - - if (g_fpd.nexFriendSession) - { - g_fpd.nexFriendSession->updateMyPresence(g_fpd.myPresence); - } - - fpdCemuRequest->returnCode = 0; - } - else - cemu_assert_unimplemented(); - } - else - assert_dbg(); - iosuIoctl_completeRequest(ioQueueEntry, returnValue); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->acceptFriendRequest(requestId, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; } - return; + + nnResult CallHandler_DeleteFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + // reject incoming friend request + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(requestId, uint64be, 0); + nexFriendRequest frq; + bool isIncoming; + if (!g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, requestId)) + return FPResult_RequestFailed; + if(!isIncoming) + { + cemuLog_log(LogType::Force, "CancelFriendRequestAsync: Trying to block outgoing friend request"); + return FPResult_RequestFailed; + } + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->deleteFriendRequest(requestId, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_CancelFriendRequestAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + // retract outgoing friend request + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(requestId, uint64be, 0); + nexFriendRequest frq; + bool isIncoming; + if (!g_fpd.nexFriendSession->getFriendRequestByMessageId(frq, &isIncoming, requestId)) + return FPResult_RequestFailed; + if(isIncoming) + { + cemuLog_log(LogType::Force, "CancelFriendRequestAsync: Trying to cancel incoming friend request"); + return FPResult_RequestFailed; + } + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->removeFriend(frq.principalInfo.principalId, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_MarkFriendRequestsAsReceivedAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 2 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(requestIdsBE, uint64be, count, 0); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + // endian convert + std::vector requestIds; + std::copy(requestIdsBE, requestIdsBE + count, std::back_inserter(requestIds)); + g_fpd.nexFriendSession->markFriendRequestsAsReceived(requestIds.data(), requestIds.size(), [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_RemoveFriendAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(pid, uint32be, 0); + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + g_fpd.nexFriendSession->removeFriend(pid, [cmd](NexFriends::RpcErrorCode result){ + if (result != NexFriends::ERR_NONE) + return ServiceCallAsyncRespond(cmd, FPResult_RequestFailed); + return ServiceCallAsyncRespond(cmd, FPResult_Ok); + }); + return FPResult_Ok; + } + + nnResult CallHandler_DeleteFriendFlagsAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + std::unique_lock _l(g_fpd.mtxFriendSession); + if (numVecIn != 3 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(pid, uint32be, 0); + cemuLog_logDebug(LogType::Force, "DeleteFriendFlagsAsync is todo"); + return FPResult_Ok; + } + + nnResult CallHandler_CheckSettingStatusAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 0 || numVecOut != 1) + return FPResult_InvalidIPCParam; + if (vecOut[0].size != sizeof(uint8be)) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + IPCCommandBody* cmd = ServiceCallDelayCurrentResponse(); + + // for now we respond immediately + uint8 settingsStatus = 1; // todo - figure out what this status means + auto r = WriteValueOutput(vecOut, settingsStatus); + ServiceCallAsyncRespond(cmd, r); + cemuLog_log(LogType::Force, "CheckSettingStatusAsync is todo"); + + return FPResult_Ok; + } + + nnResult CallHandler_IsPreferenceValid(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 0 || numVecOut != 1) + return 0; + if (!g_fpd.nexFriendSession) + return 0; + // we currently automatically put the preferences into a valid state on session creation if they are not set yet + return WriteValueOutput(vecOut, 1); // if we return 0, the friend app will show the first time setup screen + } + + nnResult CallHandler_GetRequestBlockSettingAsync(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) // todo + { + if (numVecIn != 2 || numVecOut != 1) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(count, uint32be, 1); + DeclareInputPtr(pidList, betype, count, 0); + DeclareOutputPtr(settingList, uint8be, count, 0); + cemuLog_log(LogType::Force, "GetRequestBlockSettingAsync is todo"); + + for (uint32 i = 0; i < count; i++) + settingList[i] = 0; + // implementation is todo. Used by friend list app when adding a friend + // 0 means not blocked. Friend app will continue with GetBasicInformation() + // 1 means blocked. Friend app will continue with AddFriendAsync to add the user as a provisional friend + + return FPResult_Ok; + } + + nnResult CallHandler_AddFriendAsyncByPid(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 1 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInput(pid, uint32be, 0); + cemuLog_log(LogType::Force, "AddFriendAsyncByPid is todo"); + return FPResult_Ok; + } + + nnResult CallHandler_UpdateGameMode(FPDClient* fpdClient, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) + { + if (numVecIn != 2 || numVecOut != 0) + return FPResult_InvalidIPCParam; + if (!g_fpd.nexFriendSession) + return FPResult_RequestFailed; + DeclareInputPtr(gameMode, iosu::fpd::GameMode, 1, 0); + uint32 messageLength = vecIn[1].size / sizeof(uint16be); + if(messageLength == 0 || (vecIn[1].size%sizeof(uint16be)) != 0) + { + cemuLog_log(LogType::Force, "UpdateGameMode: Message must contain at least a null-termination character"); + return FPResult_InvalidIPCParam; + } + DeclareInputPtr(gameModeMessage, uint16be, messageLength, 1); + messageLength--; + GameModeToNexPresence(gameMode, &g_fpd.myPresence); + g_fpd.nexFriendSession->updateMyPresence(g_fpd.myPresence); + // todo - message + return FPResult_Ok; + } + + void StartFriendSession() + { + bool expected = false; + if (!g_fpd.sessionStarted.compare_exchange_strong(expected, true)) + return; + cemu_assert(!g_fpd.nexFriendSession); + NAPI::AuthInfo authInfo; + NAPI::NAPI_MakeAuthInfoFromCurrentAccount(authInfo); + NAPI::ACTGetNexTokenResult nexTokenResult = NAPI::ACT_GetNexToken_WithCache(authInfo, 0x0005001010001C00, 0x0000, 0x00003200); + if (!nexTokenResult.isValid()) + { + cemuLog_logDebug(LogType::Force, "IOSU_FPD: Failed to acquire nex token for friend server"); + g_fpd.myPresence.isOnline = 0; + return; + } + // get values needed for friend session + uint32 myPid; + uint8 currentSlot = iosu::act::getCurrentAccountSlot(); + iosu::act::getPrincipalId(currentSlot, &myPid); + char accountId[256] = { 0 }; + iosu::act::getAccountId(currentSlot, accountId); + FFLData_t miiData; + act::getMii(currentSlot, &miiData); + uint16 screenName[ACT_NICKNAME_LENGTH + 1] = { 0 }; + act::getScreenname(currentSlot, screenName); + uint32 countryCode = 0; + act::getCountryIndex(currentSlot, &countryCode); + // init presence + g_fpd.myPresence.isOnline = 1; + g_fpd.myPresence.gameKey.titleId = CafeSystem::GetForegroundTitleId(); + g_fpd.myPresence.gameKey.ukn = CafeSystem::GetForegroundTitleVersion(); + // resolve potential domain to IP address + struct addrinfo hints = {0}, *addrs; + hints.ai_family = AF_INET; + const int status = getaddrinfo(nexTokenResult.nexToken.host, NULL, &hints, &addrs); + if (status != 0) + { + cemuLog_log(LogType::Force, "IOSU_FPD: Failed to resolve hostname {}", nexTokenResult.nexToken.host); + return; + } + char addrstr[NI_MAXHOST]; + getnameinfo(addrs->ai_addr, addrs->ai_addrlen, addrstr, sizeof addrstr, NULL, 0, NI_NUMERICHOST); + cemuLog_log(LogType::Force, "IOSU_FPD: Resolved IP for hostname {}, {}", nexTokenResult.nexToken.host, addrstr); + // start session + 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->setNotificationHandler(NotificationHandler); + g_fpd.mtxFriendSession.unlock(); + cemuLog_log(LogType::Force, "IOSU_FPD: Created friend server session"); + } + + void StopFriendSession() + { + std::unique_lock _l(g_fpd.mtxFriendSession); + bool expected = true; + if (!g_fpd.sessionStarted.compare_exchange_strong(expected, false) ) + return; + delete g_fpd.nexFriendSession; + g_fpd.nexFriendSession = nullptr; + } + + private: + + + }; + + FPDService gFPDService; + + class : public ::IOSUModule + { + void TitleStart() override + { + gFPDService.Start(); + gFPDService.SetTimerUpdate(1000); // call TimerUpdate() once a second + } + void TitleStop() override + { + gFPDService.StopFriendSession(); + gFPDService.Stop(); + } + }sIOSUModuleNNFPD; + + IOSUModule* GetModule() + { + return static_cast(&sIOSUModuleNNFPD); } - void Initialize() - { - if (g_fpd.isThreadStarted) - return; - std::thread t1(iosuFpd_thread); - t1.detach(); - g_fpd.isThreadStarted = true; - } } } + diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.h b/src/Cafe/IOSU/legacy/iosu_fpd.h index bd52035c..79f524d6 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.h +++ b/src/Cafe/IOSU/legacy/iosu_fpd.h @@ -1,10 +1,12 @@ #pragma once +#include "Cafe/IOSU/iosu_types_common.h" +#include "Common/CafeString.h" namespace iosu { namespace fpd { - typedef struct + struct FPDDate { /* +0x0 */ uint16be year; /* +0x2 */ uint8 month; @@ -13,13 +15,61 @@ namespace iosu /* +0x5 */ uint8 minute; /* +0x6 */ uint8 second; /* +0x7 */ uint8 padding; - }fpdDate_t; + }; - static_assert(sizeof(fpdDate_t) == 8); + static_assert(sizeof(FPDDate) == 8); - typedef struct + struct RecentPlayRecordEx { - /* +0x000 */ uint8 type; // type(Non-Zero -> Friend, 0 -> Friend request ? ) + /* +0x00 */ uint32be pid; + /* +0x04 */ uint8 ukn04; + /* +0x05 */ uint8 ukn05; + /* +0x06 */ uint8 ukn06[0x22]; + /* +0x28 */ uint8 ukn28[0x22]; + /* +0x4A */ uint8 _uknOrPadding4A[6]; + /* +0x50 */ uint32be ukn50; + /* +0x54 */ uint32be ukn54; + /* +0x58 */ uint16be ukn58; + /* +0x5C */ uint8 _padding5C[4]; + /* +0x60 */ iosu::fpd::FPDDate date; + }; + + static_assert(sizeof(RecentPlayRecordEx) == 0x68, ""); + static_assert(offsetof(RecentPlayRecordEx, ukn06) == 0x06, ""); + static_assert(offsetof(RecentPlayRecordEx, ukn50) == 0x50, ""); + + struct GameKey + { + /* +0x00 */ uint64be titleId; + /* +0x08 */ uint16be ukn08; + /* +0x0A */ uint8 _padding0A[6]; + }; + static_assert(sizeof(GameKey) == 0x10); + + struct Profile + { + uint8be region; + uint8be regionSubcode; + uint8be platform; + uint8be ukn3; + }; + static_assert(sizeof(Profile) == 0x4); + + struct GameMode + { + /* +0x00 */ uint32be joinFlagMask; + /* +0x04 */ uint32be matchmakeType; + /* +0x08 */ uint32be joinGameId; + /* +0x0C */ uint32be joinGameMode; + /* +0x10 */ uint32be hostPid; + /* +0x14 */ uint32be groupId; + /* +0x18 */ uint8 appSpecificData[0x14]; + }; + static_assert(sizeof(GameMode) == 0x2C); + + struct FriendData + { + /* +0x000 */ uint8 type; // type (Non-Zero -> Friend, 0 -> Friend request ? ) /* +0x001 */ uint8 _padding1[7]; /* +0x008 */ uint32be pid; /* +0x00C */ char nnid[0x10 + 1]; @@ -29,43 +79,29 @@ namespace iosu /* +0x036 */ uint8 ukn036; /* +0x037 */ uint8 _padding037; /* +0x038 */ uint8 mii[0x60]; - /* +0x098 */ fpdDate_t uknDate; + /* +0x098 */ FPDDate uknDate; // sub struct (the part above seems to be shared with friend requests) union { struct { - /* +0x0A0 */ uint8 ukn0A0; // country code? - /* +0x0A1 */ uint8 ukn0A1; // country subcode? - /* +0x0A2 */ uint8 _paddingA2[2]; + /* +0x0A0 */ Profile profile; // this is returned for nn_fp.GetFriendProfile /* +0x0A4 */ uint32be ukn0A4; - /* +0x0A8 */ uint64 gameKeyTitleId; - /* +0x0B0 */ uint16be gameKeyUkn; - /* +0x0B2 */ uint8 _paddingB2[6]; - /* +0x0B8 */ uint32 ukn0B8; - /* +0x0BC */ uint32 ukn0BC; - /* +0x0C0 */ uint32 ukn0C0; - /* +0x0C4 */ uint32 ukn0C4; - /* +0x0C8 */ uint32 ukn0C8; - /* +0x0CC */ uint32 ukn0CC; - /* +0x0D0 */ uint8 appSpecificData[0x14]; - /* +0x0E4 */ uint8 ukn0E4; - /* +0x0E5 */ uint8 _paddingE5; - /* +0x0E6 */ uint16 uknStr[0x80]; // game mode description (could be larger) - /* +0x1E6 */ uint8 _padding1E6[0x1EC - 0x1E6]; + /* +0x0A8 */ GameKey gameKey; + /* +0x0B8 */ GameMode gameMode; + /* +0x0E4 */ CafeWideString<0x82> gameModeDescription; + /* +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 */ uint8 ukn1F0; - /* +0x1F1 */ uint8 _padding1F1; - /* +0x1F2 */ uint16be statusMessage[16 + 1]; // pops up every few seconds in friend list (ingame character name?) - /* +0x214 */ uint8 _padding214[4]; - /* +0x218 */ fpdDate_t uknDate218; - /* +0x220 */ fpdDate_t lastOnline; + /* +0x1F0 */ char comment[36]; // pops up every few seconds in friend list + /* +0x214 */ uint32be _padding214; + /* +0x218 */ FPDDate approvalTime; + /* +0x220 */ FPDDate lastOnline; }friendExtraData; struct { - /* +0x0A0 */ uint64 messageId; // guessed + /* +0x0A0 */ uint64be messageId; // guessed. If 0, then relationship is FRIENDSHIP_REQUEST_OUT, otherwise FRIENDSHIP_REQUEST_IN /* +0x0A8 */ uint8 ukn0A8; /* +0x0A9 */ uint8 ukn0A9; // comment language? (guessed) /* +0x0AA */ uint16be comment[0x40]; @@ -75,26 +111,23 @@ namespace iosu /* +0x150 */ uint64 gameKeyTitleId; /* +0x158 */ uint16be gameKeyUkn; /* +0x15A */ uint8 _padding[6]; - /* +0x160 */ fpdDate_t uknData0; - /* +0x168 */ fpdDate_t uknData1; + /* +0x160 */ FPDDate uknData0; + /* +0x168 */ FPDDate uknData1; }requestExtraData; }; - }friendData_t; - - static_assert(sizeof(friendData_t) == 0x228, ""); - static_assert(offsetof(friendData_t, nnid) == 0x00C, ""); - static_assert(offsetof(friendData_t, friendExtraData.gameKeyTitleId) == 0x0A8, ""); - static_assert(offsetof(friendData_t, friendExtraData.appSpecificData) == 0x0D0, ""); - static_assert(offsetof(friendData_t, friendExtraData.uknStr) == 0x0E6, ""); - static_assert(offsetof(friendData_t, friendExtraData.ukn1F0) == 0x1F0, ""); + }; + static_assert(sizeof(FriendData) == 0x228); + static_assert(offsetof(FriendData, friendExtraData.gameKey) == 0x0A8); + static_assert(offsetof(FriendData, friendExtraData.gameModeDescription) == 0x0E4); + static_assert(offsetof(FriendData, friendExtraData.comment) == 0x1F0); - static_assert(offsetof(friendData_t, requestExtraData.messageId) == 0x0A0, ""); - static_assert(offsetof(friendData_t, requestExtraData.comment) == 0x0AA, ""); - static_assert(offsetof(friendData_t, requestExtraData.uknMessage) == 0x12C, ""); - static_assert(offsetof(friendData_t, requestExtraData.gameKeyTitleId) == 0x150, ""); - static_assert(offsetof(friendData_t, requestExtraData.uknData1) == 0x168, ""); + static_assert(offsetof(FriendData, requestExtraData.messageId) == 0x0A0); + static_assert(offsetof(FriendData, requestExtraData.comment) == 0x0AA); + static_assert(offsetof(FriendData, requestExtraData.uknMessage) == 0x12C); + static_assert(offsetof(FriendData, requestExtraData.gameKeyTitleId) == 0x150); + static_assert(offsetof(FriendData, requestExtraData.uknData1) == 0x168); - typedef struct + struct FriendBasicInfo { /* +0x00 */ uint32be pid; /* +0x04 */ char nnid[0x11]; @@ -104,17 +137,17 @@ namespace iosu /* +0x2E */ uint8 ukn2E; // bool option /* +0x2F */ uint8 ukn2F; /* +0x30 */ uint8 miiData[0x60]; - /* +0x90 */ fpdDate_t uknDate90; - }friendBasicInfo_t; // size is 0x98 + /* +0x90 */ FPDDate uknDate90; + }; - static_assert(sizeof(friendBasicInfo_t) == 0x98, ""); - static_assert(offsetof(friendBasicInfo_t, nnid) == 0x04, ""); - static_assert(offsetof(friendBasicInfo_t, ukn15) == 0x15, ""); - static_assert(offsetof(friendBasicInfo_t, screenname) == 0x18, ""); - static_assert(offsetof(friendBasicInfo_t, ukn2E) == 0x2E, ""); - static_assert(offsetof(friendBasicInfo_t, miiData) == 0x30, ""); + static_assert(sizeof(FriendBasicInfo) == 0x98); + static_assert(offsetof(FriendBasicInfo, nnid) == 0x04); + static_assert(offsetof(FriendBasicInfo, ukn15) == 0x15); + static_assert(offsetof(FriendBasicInfo, screenname) == 0x18); + static_assert(offsetof(FriendBasicInfo, ukn2E) == 0x2E); + static_assert(offsetof(FriendBasicInfo, miiData) == 0x30); - typedef struct + struct FriendRequest { /* +0x000 */ uint32be pid; /* +0x004 */ uint8 nnid[17]; // guessed type @@ -125,7 +158,7 @@ namespace iosu /* +0x02E */ uint8 ukn2E; // bool option /* +0x02F */ uint8 ukn2F; // ukn /* +0x030 */ uint8 miiData[0x60]; - /* +0x090 */ fpdDate_t uknDate; + /* +0x090 */ FPDDate uknDate; /* +0x098 */ uint64 ukn98; /* +0x0A0 */ uint8 isMarkedAsReceived; /* +0x0A1 */ uint8 uknA1; @@ -136,216 +169,98 @@ namespace iosu /* +0x148 */ uint64 gameKeyTitleId; /* +0x150 */ uint16be gameKeyUkn; /* +0x152 */ uint8 _padding152[6]; - /* +0x158 */ fpdDate_t uknDate2; - /* +0x160 */ fpdDate_t expireDate; - }friendRequest_t; + /* +0x158 */ FPDDate uknDate2; + /* +0x160 */ FPDDate expireDate; + }; - static_assert(sizeof(friendRequest_t) == 0x168, ""); - static_assert(offsetof(friendRequest_t, uknDate) == 0x090, ""); - static_assert(offsetof(friendRequest_t, message) == 0x0A2, ""); - static_assert(offsetof(friendRequest_t, uknString2) == 0x124, ""); - static_assert(offsetof(friendRequest_t, gameKeyTitleId) == 0x148, ""); + static_assert(sizeof(FriendRequest) == 0x168); + static_assert(offsetof(FriendRequest, uknDate) == 0x090); + static_assert(offsetof(FriendRequest, message) == 0x0A2); + static_assert(offsetof(FriendRequest, uknString2) == 0x124); + static_assert(offsetof(FriendRequest, gameKeyTitleId) == 0x148); - typedef struct + struct FriendPresence { - /* +0x00 */ uint32be joinFlagMask; - /* +0x04 */ uint32be matchmakeType; - /* +0x08 */ uint32be joinGameId; - /* +0x0C */ uint32be joinGameMode; - /* +0x10 */ uint32be hostPid; - /* +0x14 */ uint32be groupId; - /* +0x18 */ uint8 appSpecificData[0x14]; - }gameMode_t; - - static_assert(sizeof(gameMode_t) == 0x2C, ""); - - typedef struct - { - gameMode_t gameMode; - /* +0x2C */ uint8 region; - /* +0x2D */ uint8 regionSubcode; - /* +0x2E */ uint8 platform; - /* +0x2F */ uint8 _padding2F; + GameMode gameMode; + /* +0x2C */ Profile profile; /* +0x30 */ uint8 isOnline; /* +0x31 */ uint8 isValid; /* +0x32 */ uint8 padding[2]; // guessed - }friendPresence_t; + }; + static_assert(sizeof(FriendPresence) == 0x34); + static_assert(offsetof(FriendPresence, isOnline) == 0x30); - static_assert(sizeof(friendPresence_t) == 0x34, ""); - static_assert(offsetof(friendPresence_t, region) == 0x2C, ""); - static_assert(offsetof(friendPresence_t, isOnline) == 0x30, ""); + struct FPDNotification + { + betype type; + betype pid; + }; + static_assert(sizeof(FPDNotification) == 8); + + struct FPDPreference + { + uint8be showOnline; // show online status to others + uint8be showGame; // show played game to others + uint8be blockFriendRequests; // block friend requests + uint8be ukn; // probably padding? + }; + 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; - typedef struct + static const int GAMEMODE_MAX_MESSAGE_LENGTH = 0x80; // limit includes null-terminator character, so only 0x7F actual characters can be used + + enum class FPD_REQUEST_ID { - uint32 requestCode; - union - { - struct - { - MEMPTR ptr; - }common; - struct - { - MPTR funcPtr; - MPTR custom; - }loginAsync; - struct - { - MEMPTR pidList; - uint32 startIndex; - uint32 maxCount; - }getFriendList; - struct - { - MEMPTR friendData; - MEMPTR pidList; - uint32 count; - }getFriendListEx; - struct - { - MEMPTR friendRequest; - MEMPTR pidList; - uint32 count; - }getFriendRequestListEx; - struct - { - uint32 pid; - MPTR funcPtr; - MPTR custom; - }addOrRemoveFriend; - struct - { - uint64 messageId; - MPTR funcPtr; - MPTR custom; - }cancelOrAcceptFriendRequest; - struct - { - uint32 pid; - MEMPTR message; - MPTR funcPtr; - MPTR custom; - }addFriendRequest; - struct - { - MEMPTR messageIdList; - uint32 count; - MPTR funcPtr; - MPTR custom; - }markFriendRequest; - struct - { - MEMPTR basicInfo; - MEMPTR pidList; - sint32 count; - MPTR funcPtr; - MPTR custom; - }getBasicInfo; - struct - { - uint32 notificationMask; - MPTR funcPtr; - MPTR custom; - }setNotificationHandler; - struct - { - MEMPTR accountIds; - MEMPTR pidList; - sint32 count; - }getFriendAccountId; - struct - { - MEMPTR nameList; - MEMPTR pidList; - sint32 count; - bool replaceNonAscii; - MEMPTR languageList; - }getFriendScreenname; - struct - { - uint8* miiList; - MEMPTR pidList; - sint32 count; - }getFriendMii; - struct - { - uint8* presenceList; - MEMPTR pidList; - sint32 count; - }getFriendPresence; - struct - { - uint8* relationshipList; - MEMPTR pidList; - sint32 count; - }getFriendRelationship; - struct - { - MEMPTR gameMode; - MEMPTR gameModeMessage; - }updateGameMode; - }; - - // output - uint32 returnCode; // return value - union - { - struct - { - uint32 numReturnedCount; - }resultGetFriendList; - struct - { - uint32 u32; - }resultU32; - }; - }iosuFpdCemuRequest_t; - - // custom dev/fpd protocol (Cemu only) - #define IOSU_FPD_REQUEST_CEMU (0xEE) - - // FPD request Cemu subcodes - enum - { - _IOSU_FPD_NONE, - IOSU_FPD_INITIALIZE, - IOSU_FPD_SET_NOTIFICATION_HANDLER, - IOSU_FPD_LOGIN_ASYNC, - IOSU_FPD_IS_ONLINE, - IOSU_FPD_IS_PREFERENCE_VALID, - - IOSU_FPD_UPDATE_GAMEMODE, - - IOSU_FPD_GET_MY_PRINCIPAL_ID, - IOSU_FPD_GET_MY_ACCOUNT_ID, - IOSU_FPD_GET_MY_MII, - IOSU_FPD_GET_MY_SCREENNAME, - - IOSU_FPD_GET_FRIEND_ACCOUNT_ID, - IOSU_FPD_GET_FRIEND_SCREENNAME, - IOSU_FPD_GET_FRIEND_MII, - IOSU_FPD_GET_FRIEND_PRESENCE, - IOSU_FPD_GET_FRIEND_RELATIONSHIP, - - IOSU_FPD_GET_FRIEND_LIST, - IOSU_FPD_GET_FRIENDREQUEST_LIST, - IOSU_FPD_GET_FRIEND_LIST_ALL, - IOSU_FPD_GET_FRIEND_LIST_EX, - IOSU_FPD_GET_FRIENDREQUEST_LIST_EX, - IOSU_FPD_ADD_FRIEND, - IOSU_FPD_ADD_FRIEND_REQUEST, - IOSU_FPD_REMOVE_FRIEND_ASYNC, - IOSU_FPD_CANCEL_FRIEND_REQUEST_ASYNC, - IOSU_FPD_ACCEPT_FRIEND_REQUEST_ASYNC, - IOSU_FPD_MARK_FRIEND_REQUEST_AS_RECEIVED_ASYNC, - IOSU_FPD_GET_BASIC_INFO_ASYNC, + LoginAsync = 0x2775, + HasLoggedIn = 0x2777, + IsOnline = 0x2778, + GetMyPrincipalId = 0x27D9, + GetMyAccountId = 0x27DA, + GetMyScreenName = 0x27DB, + GetMyMii = 0x27DC, + GetMyProfile = 0x27DD, + GetMyPreference = 0x27DE, + GetMyPresence = 0x27DF, + IsPreferenceValid = 0x27E0, + GetFriendList = 0x283D, + GetFriendListAll = 0x283E, + GetFriendAccountId = 0x283F, + GetFriendScreenName = 0x2840, + GetFriendPresence = 0x2845, + GetFriendRelationship = 0x2846, + GetFriendMii = 0x2841, + GetBlackList = 0x28A1, + GetFriendRequestList = 0x2905, + UpdateGameModeVariation1 = 0x2969, // there seem to be two different requestIds for the 2-param and 3-param version of UpdateGameMode, + UpdateGameModeVariation2 = 0x296A, // but the third parameter is never used and the same handler is used for both + AddFriendAsyncByPid = 0x29CD, + AddFriendAsyncByXXX = 0x29CE, // probably by name? + GetRequestBlockSettingAsync = 0x2B5D, + GetMyComment = 0x4EE9, + GetMyPlayingGame = 0x4EEA, + CheckSettingStatusAsync = 0x7596, + GetFriendListEx = 0x75F9, + GetFriendRequestListEx = 0x76C1, + UpdatePreferenceAsync = 0x7727, + RemoveFriendAsync = 0x7789, + DeleteFriendFlagsAsync = 0x778A, + AddFriendRequestByPlayRecordAsync = 0x778B, + CancelFriendRequestAsync = 0x778C, + AcceptFriendRequestAsync = 0x7851, + DeleteFriendRequestAsync = 0x7852, + MarkFriendRequestsAsReceivedAsync = 0x7854, + GetBasicInfoAsync = 0x7919, + SetLedEventMask = 0x9D0B, + SetNotificationMask = 0x15ff5, + GetNotificationAsync = 0x15FF6, }; - void Initialize(); + using FriendPID = uint32; + + IOSUModule* GetModule(); } } \ No newline at end of file diff --git a/src/Cafe/IOSU/nn/iosu_nn_service.cpp b/src/Cafe/IOSU/nn/iosu_nn_service.cpp index ade1fa2b..b3b2d4c9 100644 --- a/src/Cafe/IOSU/nn/iosu_nn_service.cpp +++ b/src/Cafe/IOSU/nn/iosu_nn_service.cpp @@ -1,5 +1,6 @@ #include "iosu_nn_service.h" #include "../kernel/iosu_kernel.h" +#include "util/helpers/helpers.h" using namespace iosu::kernel; @@ -7,6 +8,132 @@ namespace iosu { namespace nn { + /* IPCSimpleService */ + void IPCSimpleService::Start() + { + if (m_isRunning.exchange(true)) + return; + m_threadInitialized = false; + m_requestStop = false; + m_serviceThread = std::thread(&IPCSimpleService::ServiceThread, this); + while (!m_threadInitialized) std::this_thread::sleep_for(std::chrono::milliseconds(10)); + StartService(); + } + + void IPCSimpleService::Stop() + { + if (!m_isRunning.exchange(false)) + return; + m_requestStop = true; + StopService(); + if(m_timerId != IOSInvalidTimerId) + IOS_DestroyTimer(m_timerId); + m_timerId = IOSInvalidTimerId; + IOS_SendMessage(m_msgQueueId, 0, 0); // wake up thread + m_serviceThread.join(); + } + + void IPCSimpleService::ServiceThread() + { + if(!GetThreadName().empty()) + SetThreadName(GetThreadName().c_str()); + m_msgQueueId = IOS_CreateMessageQueue(_m_msgBuffer.GetPtr(), _m_msgBuffer.GetCount()); + cemu_assert(!IOS_ResultIsError((IOS_ERROR)m_msgQueueId)); + IOS_ERROR r = IOS_RegisterResourceManager(m_devicePath.c_str(), m_msgQueueId); + cemu_assert(!IOS_ResultIsError(r)); + m_threadInitialized = true; + while (true) + { + IOSMessage msg; + r = IOS_ReceiveMessage(m_msgQueueId, &msg, 0); + cemu_assert(!IOS_ResultIsError(r)); + if (msg == 0) + { + cemu_assert_debug(m_requestStop); + break; + } + else if(msg == 1) + { + TimerUpdate(); + continue; + } + IPCCommandBody* cmd = MEMPTR(msg).GetPtr(); + if (cmd->cmdId == IPCCommandId::IOS_OPEN) + { + void* clientObject = CreateClientObject(); + if(clientObject == nullptr) + { + cemuLog_log(LogType::Force, "IPCSimpleService[{}]: Maximum handle count reached or handle rejected", m_devicePath); + IOS_ResourceReply(cmd, IOS_ERROR_MAXIMUM_REACHED); + continue; + } + IOSDevHandle newHandle = GetFreeHandle(); + m_clientObjects[newHandle] = clientObject; + IOS_ResourceReply(cmd, (IOS_ERROR)newHandle); + continue; + } + else if (cmd->cmdId == IPCCommandId::IOS_CLOSE) + { + void* clientObject = GetClientObjectByHandle(cmd->devHandle); + if (clientObject) + DestroyClientObject(clientObject); + IOS_ResourceReply(cmd, IOS_ERROR_OK); + continue; + } + else if (cmd->cmdId == IPCCommandId::IOS_IOCTLV) + { + void* clientObject = GetClientObjectByHandle(cmd->devHandle); + if (!clientObject) + { + cemuLog_log(LogType::Force, "IPCSimpleService[{}]: Invalid IPC handle", m_devicePath); + IOS_ResourceReply(cmd, IOS_ERROR_INVALID); + continue; + } + uint32 requestId = cmd->args[0]; + uint32 numIn = cmd->args[1]; + uint32 numOut = cmd->args[2]; + IPCIoctlVector* vec = MEMPTR{ cmd->args[3] }.GetPtr(); + IPCIoctlVector* vecIn = vec + 0; // the ordering of vecIn/vecOut differs from IPCService + IPCIoctlVector* vecOut = vec + numIn; + m_delayResponse = false; + m_activeCmd = cmd; + uint32 result = ServiceCall(clientObject, requestId, vecIn, numIn, vecOut, numOut); + if (!m_delayResponse) + IOS_ResourceReply(cmd, (IOS_ERROR)result); + m_activeCmd = nullptr; + continue; + } + else + { + cemuLog_log(LogType::Force, "IPCSimpleService[{}]: Unsupported IPC cmdId {}", m_devicePath, (uint32)cmd->cmdId.value()); + cemu_assert_unimplemented(); + IOS_ResourceReply(cmd, IOS_ERROR_INVALID); + } + } + IOS_DestroyMessageQueue(m_msgQueueId); + m_threadInitialized = false; + } + + void IPCSimpleService::SetTimerUpdate(uint32 milliseconds) + { + if(m_timerId != IOSInvalidTimerId) + IOS_DestroyTimer(m_timerId); + m_timerId = IOS_CreateTimer(milliseconds * 1000, milliseconds * 1000, m_msgQueueId, 1); + } + + IPCCommandBody* IPCSimpleService::ServiceCallDelayCurrentResponse() + { + cemu_assert_debug(m_activeCmd); + m_delayResponse = true; + return m_activeCmd; + } + + void IPCSimpleService::ServiceCallAsyncRespond(IPCCommandBody* response, uint32 r) + { + IOS_ResourceReply(response, (IOS_ERROR)r); + } + + /* IPCService */ void IPCService::Start() { if (m_isRunning.exchange(true)) @@ -83,4 +210,4 @@ namespace iosu m_threadInitialized = false; } }; -}; \ No newline at end of file +}; diff --git a/src/Cafe/IOSU/nn/iosu_nn_service.h b/src/Cafe/IOSU/nn/iosu_nn_service.h index 7f06139f..d50a0794 100644 --- a/src/Cafe/IOSU/nn/iosu_nn_service.h +++ b/src/Cafe/IOSU/nn/iosu_nn_service.h @@ -8,6 +8,71 @@ namespace iosu { namespace nn { + // a simple service interface which wraps handle management and Ioctlv/IoctlvAsync + class IPCSimpleService + { + public: + IPCSimpleService(std::string_view devicePath) : m_devicePath(devicePath) {}; + virtual ~IPCSimpleService() {}; + + virtual void StartService() {}; + virtual void StopService() {}; + + virtual std::string GetThreadName() = 0; + + virtual void* CreateClientObject() = 0; + virtual void DestroyClientObject(void* clientObject) = 0; + virtual uint32 ServiceCall(void* clientObject, uint32 requestId, IPCIoctlVector* vecIn, uint32 numVecIn, IPCIoctlVector* vecOut, uint32 numVecOut) = 0; + virtual void TimerUpdate() {}; + + IPCCommandBody* ServiceCallDelayCurrentResponse(); + static void ServiceCallAsyncRespond(IPCCommandBody* response, uint32 r); + + void Start(); + void Stop(); + + void SetTimerUpdate(uint32 milliseconds); + + private: + void ServiceThread(); + + IOSDevHandle GetFreeHandle() + { + while(m_clientObjects.find(m_nextHandle) != m_clientObjects.end() || m_nextHandle == 0) + { + m_nextHandle++; + m_nextHandle &= 0x7FFFFFFF; + } + IOSDevHandle newHandle = m_nextHandle; + m_nextHandle++; + m_nextHandle &= 0x7FFFFFFF; + return newHandle; + } + + void* GetClientObjectByHandle(IOSDevHandle handle) const + { + auto it = m_clientObjects.find(handle); + if(it == m_clientObjects.end()) + return nullptr; + return it->second; + } + + std::string m_devicePath; + std::thread m_serviceThread; + std::atomic_bool m_requestStop{false}; + std::atomic_bool m_isRunning{false}; + std::atomic_bool m_threadInitialized{ false }; + std::unordered_map m_clientObjects; + IOSDevHandle m_nextHandle{1}; + IOSTimerId m_timerId{IOSInvalidTimerId}; + + IPCCommandBody* m_activeCmd{nullptr}; + bool m_delayResponse{false}; + + IOSMsgQueueId m_msgQueueId; + SysAllocator _m_msgBuffer; + }; + struct IPCServiceRequest { uint32be ukn00; @@ -23,6 +88,7 @@ namespace iosu uint32be nnResultCode; }; + // a complex service interface which wraps Ioctlv and adds an additional service channel, used by /dev/act, ? class IPCService { public: @@ -60,5 +126,6 @@ namespace iosu IOSMsgQueueId m_msgQueueId; SysAllocator _m_msgBuffer; }; + }; }; \ No newline at end of file diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp index 0e6d153f..f0703290 100644 --- a/src/Cafe/OS/RPL/rpl.cpp +++ b/src/Cafe/OS/RPL/rpl.cpp @@ -724,7 +724,7 @@ uint32 RPLLoader_MakePPCCallable(void(*ppcCallableExport)(PPCInterpreter_t* hCPU if (it != g_map_callableExports.end()) return it->second; // get HLE function index - sint32 functionIndex = PPCInterpreter_registerHLECall(ppcCallableExport); + sint32 functionIndex = PPCInterpreter_registerHLECall(ppcCallableExport, fmt::format("PPCCallback{:x}", (uintptr_t)ppcCallableExport)); MPTR codeAddr = memory_getVirtualOffsetFromPointer(RPLLoader_AllocateTrampolineCodeSpace(4)); uint32 opcode = (1 << 26) | functionIndex; memory_write(codeAddr, opcode); diff --git a/src/Cafe/OS/common/OSCommon.cpp b/src/Cafe/OS/common/OSCommon.cpp index 7e11ea13..5aedd197 100644 --- a/src/Cafe/OS/common/OSCommon.cpp +++ b/src/Cafe/OS/common/OSCommon.cpp @@ -85,6 +85,7 @@ void osLib_addFunctionInternal(const char* libraryName, const char* functionName uint32 funcHashA, funcHashB; osLib_generateHashFromName(libraryName, &libHashA, &libHashB); osLib_generateHashFromName(functionName, &funcHashA, &funcHashB); + std::string hleName = fmt::format("{}.{}", libraryName, functionName); // if entry already exists, update it for (auto& it : *s_osFunctionTable) { @@ -93,11 +94,11 @@ void osLib_addFunctionInternal(const char* libraryName, const char* functionName it.funcHashA == funcHashA && it.funcHashB == funcHashB) { - it.hleFunc = PPCInterpreter_registerHLECall(osFunction); + it.hleFunc = PPCInterpreter_registerHLECall(osFunction, hleName); return; } } - s_osFunctionTable->emplace_back(libHashA, libHashB, funcHashA, funcHashB, fmt::format("{}.{}", libraryName, functionName), PPCInterpreter_registerHLECall(osFunction)); + s_osFunctionTable->emplace_back(libHashA, libHashB, funcHashA, funcHashB, hleName, PPCInterpreter_registerHLECall(osFunction, hleName)); } extern "C" DLLEXPORT void osLib_registerHLEFunction(const char* libraryName, const char* functionName, void(*osFunction)(PPCInterpreter_t * hCPU)) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 916563c8..1e6eb92b 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -912,8 +912,8 @@ namespace coreinit sint32 FSOpenFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, FSFileHandleDepr_t* fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSOpenFileAsync(fsClient, fsCmdBlock, path, mode, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSOpenFileAsync(fsClient, fsCmdBlock, path, mode, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -941,8 +941,8 @@ namespace coreinit sint32 FSOpenFileEx(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, char* mode, uint32 createMode, uint32 openFlag, uint32 preallocSize, FSFileHandleDepr_t* fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSOpenFileExAsync(fsClient, fsCmdBlock, path, mode, createMode, openFlag, preallocSize, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSOpenFileExAsync(fsClient, fsCmdBlock, path, mode, createMode, openFlag, preallocSize, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -975,8 +975,8 @@ namespace coreinit sint32 FSCloseFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSCloseFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSCloseFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -1009,8 +1009,8 @@ namespace coreinit sint32 FSFlushFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errHandling) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSFlushFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSFlushFileAsync(fsClient, fsCmdBlock, fileHandle, errHandling, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errHandling); } @@ -1090,7 +1090,7 @@ namespace coreinit sint32 FSReadFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSReadFileAsync(fsClient, fsCmdBlock, dst, size, count, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1105,7 +1105,7 @@ namespace coreinit sint32 FSReadFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* dst, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSReadFileWithPosAsync(fsClient, fsCmdBlock, dst, size, count, filePos, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1183,7 +1183,7 @@ namespace coreinit sint32 FSWriteFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSWriteFileAsync(fsClient, fsCmdBlock, src, size, count, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1196,7 +1196,7 @@ namespace coreinit sint32 FSWriteFileWithPos(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, void* src, uint32 size, uint32 count, uint32 filePos, uint32 fileHandle, uint32 flag, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSWriteFileWithPosAsync(fsClient, fsCmdBlock, src, size, count, filePos, fileHandle, flag, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1228,7 +1228,7 @@ namespace coreinit { // used by games: Mario Kart 8 StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSSetPosFileAsync(fsClient, fsCmdBlock, fileHandle, filePos, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1259,7 +1259,7 @@ namespace coreinit sint32 FSGetPosFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32be* returnedFilePos, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSGetPosFileAsync(fsClient, fsCmdBlock, fileHandle, returnedFilePos, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1307,8 +1307,8 @@ namespace coreinit sint32 FSOpenDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, FSDirHandlePtr dirHandleOut, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSOpenDirAsync(fsClient, fsCmdBlock, path, dirHandleOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSOpenDirAsync(fsClient, fsCmdBlock, path, dirHandleOut, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1337,8 +1337,8 @@ namespace coreinit sint32 FSReadDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, FSDirEntry_t* dirEntryOut, uint32 errorMask, FSAsyncParamsNew_t* fsAsyncParams) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSReadDirAsync(fsClient, fsCmdBlock, dirHandle, dirEntryOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSReadDirAsync(fsClient, fsCmdBlock, dirHandle, dirEntryOut, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1367,8 +1367,8 @@ namespace coreinit sint32 FSCloseDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSCloseDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSCloseDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1400,8 +1400,8 @@ namespace coreinit sint32 FSRewindDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSDirHandle2 dirHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSRewindDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSRewindDirAsync(fsClient, fsCmdBlock, dirHandle, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1435,7 +1435,7 @@ namespace coreinit sint32 FSAppendFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 size, uint32 count, uint32 fileHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSAppendFileAsync(fsClient, fsCmdBlock, size, count, fileHandle, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1467,7 +1467,7 @@ namespace coreinit sint32 FSTruncateFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSFileHandle2 fileHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSTruncateFileAsync(fsClient, fsCmdBlock, fileHandle, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1531,7 +1531,7 @@ namespace coreinit sint32 FSRename(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* srcPath, char* dstPath, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); sint32 fsAsyncRet = FSRenameAsync(fsClient, fsCmdBlock, srcPath, dstPath, errorMask, asyncParams.GetPointer()); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1582,8 +1582,8 @@ namespace coreinit sint32 FSRemove(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint8* filePath, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSRemoveAsync(fsClient, fsCmdBlock, filePath, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSRemoveAsync(fsClient, fsCmdBlock, filePath, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1634,8 +1634,8 @@ namespace coreinit sint32 FSMakeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSMakeDirAsync(fsClient, fsCmdBlock, path, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSMakeDirAsync(fsClient, fsCmdBlock, path, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1683,8 +1683,8 @@ namespace coreinit sint32 FSChangeDir(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSChangeDirAsync(fsClient, fsCmdBlock, path, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSChangeDirAsync(fsClient, fsCmdBlock, path, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1718,8 +1718,8 @@ namespace coreinit sint32 FSGetCwd(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* dirPathOut, sint32 dirPathMaxLen, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetCwdAsync(fsClient, fsCmdBlock, dirPathOut, dirPathMaxLen, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetCwdAsync(fsClient, fsCmdBlock, dirPathOut, dirPathMaxLen, errorMask, &asyncParams); auto r = __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); return r; } @@ -1763,8 +1763,8 @@ namespace coreinit sint32 FSFlushQuota(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, char* path, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSFlushQuotaAsync(fsClient, fsCmdBlock, path, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSFlushQuotaAsync(fsClient, fsCmdBlock, path, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1821,8 +1821,8 @@ namespace coreinit sint32 FSGetStat(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSStat_t* statOut, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetStatAsync(fsClient, fsCmdBlock, path, statOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetStatAsync(fsClient, fsCmdBlock, path, statOut, errorMask, &asyncParams); sint32 ret = __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); return ret; } @@ -1858,8 +1858,8 @@ namespace coreinit sint32 FSGetStatFile(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, FSFileHandle2 fileHandle, FSStat_t* statOut, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetStatFileAsync(fsClient, fsCmdBlock, fileHandle, statOut, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetStatFileAsync(fsClient, fsCmdBlock, fileHandle, statOut, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1873,8 +1873,8 @@ namespace coreinit sint32 FSGetFreeSpaceSize(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, const char* path, FSLargeSize* returnedFreeSize, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSGetFreeSpaceSizeAsync(fsClient, fsCmdBlock, path, returnedFreeSize, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSGetFreeSpaceSizeAsync(fsClient, fsCmdBlock, path, returnedFreeSize, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } @@ -1908,8 +1908,8 @@ namespace coreinit sint32 FSIsEof(FSClient_t* fsClient, FSCmdBlock_t* fsCmdBlock, uint32 fileHandle, uint32 errorMask) { StackAllocator asyncParams; - __FSAsyncToSyncInit(fsClient, fsCmdBlock, asyncParams); - sint32 fsAsyncRet = FSIsEofAsync(fsClient, fsCmdBlock, fileHandle, errorMask, asyncParams); + __FSAsyncToSyncInit(fsClient, fsCmdBlock, &asyncParams); + sint32 fsAsyncRet = FSIsEofAsync(fsClient, fsCmdBlock, fileHandle, errorMask, &asyncParams); return __FSProcessAsyncResult(fsClient, fsCmdBlock, fsAsyncRet, errorMask); } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp b/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp index ef847f26..be3cb300 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp @@ -355,23 +355,6 @@ namespace coreinit IOS_ERROR _IPCDriver_SetupCmd_IOSIoctlv(IPCDriver& ipcDriver, IPCResourceBufferDescriptor* requestDescriptor, uint32 requestId, uint32 numIn, uint32 numOut, IPCIoctlVector* vec) { IPCCommandBody& cmdBody = requestDescriptor->resourcePtr->commandBody; - // verify input and output vectors - IPCIoctlVector* vecIn = vec; - IPCIoctlVector* vecOut = vec + numIn; - for (uint32 i = 0; i < numIn; i++) - { - if (vecIn[i].baseVirt == nullptr && vecIn[i].size != 0) - return IOS_ERROR_INVALID_ARG; - vecIn[i].basePhys = vecIn[i].baseVirt; - vecIn[i].baseVirt = nullptr; - } - for (uint32 i = 0; i < numOut; i++) - { - if (vecOut[i].baseVirt == nullptr && vecOut[i].size != 0) - return IOS_ERROR_INVALID_ARG; - vecOut[i].basePhys = vecOut[i].baseVirt; - vecOut[i].baseVirt = nullptr; - } // set args cmdBody.ppcVirt0 = MEMPTR(vec).GetMPTR(); cmdBody.args[0] = requestId; diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp index d5cd0018..2d7468cf 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp @@ -195,7 +195,28 @@ namespace coreinit else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && (formatStr[2] == 'x' || formatStr[2] == 'X'))) { formatStr += 3; - // number (64bit) + // double (64bit) + strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); + if ((formatStr - formatStart) < sizeof(tempFormat)) + tempFormat[(formatStr - formatStart)] = '\0'; + else + tempFormat[sizeof(tempFormat) - 1] = '\0'; + if (integerParamIndex & 1) + integerParamIndex++; + sint32 tempLen = sprintf(tempStr, tempFormat, PPCInterpreter_getCallParamU64(hCPU, integerParamIndex)); + integerParamIndex += 2; + for (sint32 i = 0; i < tempLen; i++) + { + if (writeIndex >= maxLength) + break; + strOut[writeIndex] = tempStr[i]; + writeIndex++; + } + } + else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && formatStr[2] == 'd')) + { + formatStr += 3; + // signed integer (64bit) strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); if ((formatStr - formatStart) < sizeof(tempFormat)) tempFormat[(formatStr - formatStart)] = '\0'; diff --git a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp index d88a29d4..024965fd 100644 --- a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp +++ b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp @@ -859,10 +859,10 @@ namespace H264 return H264DEC_STATUS::SUCCESS; } StackAllocator executeDoneEvent; - coreinit::OSInitEvent(executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::vector results; auto asyncTask = std::async(std::launch::async, _async_H264DECEnd, executeDoneEvent.GetPointer(), session, ctx, &results); - coreinit::OSWaitEvent(executeDoneEvent); + coreinit::OSWaitEvent(&executeDoneEvent); _ReleaseDecoderSession(session); if (!results.empty()) { @@ -977,9 +977,9 @@ namespace H264 StackAllocator stack_decodedFrameResult; for (sint32 i = 0; i < outputFrameCount; i++) - stack_resultPtrArray[i] = stack_decodedFrameResult + i; + stack_resultPtrArray[i] = &stack_decodedFrameResult + i; - H264DECFrameOutput* frameOutput = stack_decodedFrameResult + 0; + H264DECFrameOutput* frameOutput = &stack_decodedFrameResult + 0; memset(frameOutput, 0x00, sizeof(H264DECFrameOutput)); frameOutput->imagePtr = (uint8*)decodeResult.imageOutput; frameOutput->result = 100; @@ -1022,10 +1022,10 @@ namespace H264 return 0; } StackAllocator executeDoneEvent; - coreinit::OSInitEvent(executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&executeDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); H264AVCDecoder::DecodeResult decodeResult; - auto asyncTask = std::async(std::launch::async, _async_H264DECExecute, executeDoneEvent.GetPointer(), session, ctx, imageOutput , &decodeResult); - coreinit::OSWaitEvent(executeDoneEvent); + auto asyncTask = std::async(std::launch::async, _async_H264DECExecute, &executeDoneEvent, session, ctx, imageOutput , &decodeResult); + coreinit::OSWaitEvent(&executeDoneEvent); _ReleaseDecoderSession(session); if(decodeResult.frameReady) H264DoFrameOutputCallback(ctx, decodeResult); diff --git a/src/Cafe/OS/libs/nn_act/nn_act.cpp b/src/Cafe/OS/libs/nn_act/nn_act.cpp index 68109586..0fd9df5a 100644 --- a/src/Cafe/OS/libs/nn_act/nn_act.cpp +++ b/src/Cafe/OS/libs/nn_act/nn_act.cpp @@ -391,7 +391,7 @@ void nnActExport_GetMiiName(PPCInterpreter_t* hCPU) StackAllocator miiData; - uint32 r = nn::act::GetMiiEx(miiData, iosu::act::ACT_SLOT_CURRENT); + uint32 r = nn::act::GetMiiEx(&miiData, iosu::act::ACT_SLOT_CURRENT); // extract name sint32 miiNameLength = 0; for (sint32 i = 0; i < MII_FFL_NAME_LENGTH; i++) @@ -414,7 +414,7 @@ void nnActExport_GetMiiNameEx(PPCInterpreter_t* hCPU) StackAllocator miiData; - uint32 r = nn::act::GetMiiEx(miiData, slot); + uint32 r = nn::act::GetMiiEx(&miiData, slot); // extract name sint32 miiNameLength = 0; for (sint32 i = 0; i < MII_FFL_NAME_LENGTH; i++) diff --git a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp index e33b6369..53ab3eef 100644 --- a/src/Cafe/OS/libs/nn_fp/nn_fp.cpp +++ b/src/Cafe/OS/libs/nn_fp/nn_fp.cpp @@ -1,426 +1,554 @@ #include "Cafe/OS/common/OSCommon.h" -#include "Cafe/IOSU/legacy/iosu_ioctl.h" #include "Cafe/IOSU/legacy/iosu_act.h" #include "Cafe/IOSU/legacy/iosu_fpd.h" +#include "Cafe/IOSU/legacy/iosu_ioctl.h" // deprecated +#include "Cafe/IOSU/iosu_ipc_common.h" #include "Cafe/OS/libs/coreinit/coreinit_IOS.h" - -#define fpdPrepareRequest() \ -StackAllocator _buf_fpdRequest; \ -StackAllocator _buf_bufferVector; \ -iosu::fpd::iosuFpdCemuRequest_t* fpdRequest = _buf_fpdRequest.GetPointer(); \ -ioBufferVector_t* fpdBufferVector = _buf_bufferVector.GetPointer(); \ -memset(fpdRequest, 0, sizeof(iosu::fpd::iosuFpdCemuRequest_t)); \ -memset(fpdBufferVector, 0, sizeof(ioBufferVector_t)); \ -fpdBufferVector->buffer = (uint8*)fpdRequest; +#include "Cafe/OS/libs/coreinit/coreinit_IPC.h" +#include "Cafe/OS/libs/nn_common.h" +#include "util/ChunkedHeap/ChunkedHeap.h" +#include "Common/CafeString.h" namespace nn { namespace fp { + static const auto FPResult_OkZero = 0; + static const auto FPResult_Ok = BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_FP, 0); + static const auto FPResult_InvalidIPCParam = BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_FP, 0x680); + static const auto FPResult_RequestFailed = BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_FP, 0); // figure out proper error code - struct + struct { - bool isInitialized; + uint32 initCounter; bool isAdminMode; + bool isLoggedIn; + IOSDevHandle fpdHandle; + SysAllocator fpMutex; + SysAllocator g_fpdAllocatorSpace; + VHeap* fpBufferHeap{nullptr}; + // PPC buffers for async notification query + SysAllocator notificationCount; + SysAllocator notificationBuffer; + bool getNotificationCalled{false}; + // notification handler + MEMPTR notificationHandler{nullptr}; + MEMPTR notificationHandlerParam{nullptr}; }g_fp = { }; - void Initialize() + class { - if (g_fp.isInitialized == false) + public: + void Init() { - g_fp.isInitialized = true; - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_INITIALIZE; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); + std::unique_lock _l(m_mtx); + g_fp.fpBufferHeap = new VHeap(g_fp.g_fpdAllocatorSpace.GetPtr(), g_fp.g_fpdAllocatorSpace.GetByteSize()); } - } - - - - void export_IsInitialized(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "Called nn_fp.IsInitialized"); - osLib_returnFromFunction(hCPU, g_fp.isInitialized ? 1 : 0); - } - - void export_Initialize(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "Called nn_fp.Initialize"); - - Initialize(); - - osLib_returnFromFunction(hCPU, 0); - } - - void export_InitializeAdmin(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "Called nn_fp.InitializeAdmin"); - Initialize(); - g_fp.isAdminMode = true; - osLib_returnFromFunction(hCPU, 0); - } - - void export_IsInitializedAdmin(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "nn_fp.IsInitializedAdmin()"); - osLib_returnFromFunction(hCPU, g_fp.isInitialized ? 1 : 0); - } - - void export_SetNotificationHandler(PPCInterpreter_t* hCPU) - { - ppcDefineParamU32(notificationMask, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamMPTR(customParam, 2); - - cemuLog_logDebug(LogType::Force, "nn_fp.SetNotificationHandler(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_SET_NOTIFICATION_HANDLER; - fpdRequest->setNotificationHandler.notificationMask = notificationMask; - fpdRequest->setNotificationHandler.funcPtr = funcMPTR; - fpdRequest->setNotificationHandler.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, 0); - } - - void export_LoginAsync(PPCInterpreter_t* hCPU) - { - ppcDefineParamMPTR(funcPtr, 0); - ppcDefineParamMPTR(custom, 1); - cemuLog_logDebug(LogType::Force, "nn_fp.LoginAsync(0x{:08x},0x{:08x})", funcPtr, custom); - if (g_fp.isInitialized == false) + void Destroy() { - osLib_returnFromFunction(hCPU, 0xC0C00580); + std::unique_lock _l(m_mtx); + delete g_fp.fpBufferHeap; + } + + void* Allocate(uint32 size, uint32 alignment) + { + std::unique_lock _l(m_mtx); + void* p = g_fp.fpBufferHeap->alloc(size, 32); + uint32 heapSize, allocationSize, allocNum; + g_fp.fpBufferHeap->getStats(heapSize, allocationSize, allocNum); + return p; + } + + void Free(void* ptr) + { + std::unique_lock _l(m_mtx); + g_fp.fpBufferHeap->free(ptr); + } + + private: + std::mutex m_mtx; + }FPIpcBufferAllocator; + + class FPIpcContext { + static inline constexpr uint32 MAX_VEC_COUNT = 8; + public: + // use FP heap for this class + static void* operator new(size_t size) + { + return FPIpcBufferAllocator.Allocate(size, (uint32)alignof(FPIpcContext)); + } + + static void operator delete(void* ptr) + { + FPIpcBufferAllocator.Free(ptr); + } + + FPIpcContext(iosu::fpd::FPD_REQUEST_ID requestId) : m_requestId(requestId) + { + } + + ~FPIpcContext() + { + if(m_dataBuffer) + FPIpcBufferAllocator.Free(m_dataBuffer); + } + + void AddInput(void* ptr, uint32 size) + { + size_t vecIndex = GetVecInIndex(m_numVecIn); + m_vec[vecIndex].baseVirt = ptr; + m_vec[vecIndex].size = size; + m_numVecIn = m_numVecIn + 1; + } + + void AddOutput(void* ptr, uint32 size) + { + cemu_assert_debug(m_numVecIn == 0); // all outputs need to be added before any inputs + size_t vecIndex = GetVecOutIndex(m_numVecOut); + m_vec[vecIndex].baseVirt = ptr; + m_vec[vecIndex].size = size; + m_numVecOut = m_numVecOut + 1; + } + + uint32 Submit(std::unique_ptr owner) + { + InitSubmissionBuffer(); + // note: While generally, Ioctlv() usage has the order as input (app->IOSU) followed by output (IOSU->app), FP uses it the other way around + nnResult r = coreinit::IOS_Ioctlv(g_fp.fpdHandle, (uint32)m_requestId.value(), m_numVecOut, m_numVecIn, m_vec); + CopyBackOutputs(); + owner.reset(); + return r; + } + + nnResult SubmitAsync(std::unique_ptr owner, MEMPTR callbackFunc, MEMPTR callbackParam) + { + InitSubmissionBuffer(); + this->m_callbackFunc = callbackFunc; + this->m_callbackParam = callbackParam; + nnResult r = coreinit::IOS_IoctlvAsync(g_fp.fpdHandle, (uint32)m_requestId.value(), m_numVecOut, m_numVecIn, m_vec, MEMPTR(PPCInterpreter_makeCallableExportDepr(AsyncHandler)), MEMPTR(this)); + owner.release(); + return r; + } + + private: + size_t GetVecInIndex(uint8 inIndex) + { + return m_numVecOut + inIndex; + } + + size_t GetVecOutIndex(uint8 outIndex) + { + return outIndex; + } + + void InitSubmissionBuffer() + { + // allocate a chunk of memory to hold the input/output vectors and their data + uint32 vecOffset[MAX_VEC_COUNT]; + uint32 totalBufferSize = 0; + for(uint8 i=0; i(m_vec[vecIndex].baseVirt).GetPtr(), MEMPTR(m_vecOriginalAddress[vecIndex]).GetPtr(), m_vec[vecIndex].size); + } + } + + static void AsyncHandler(PPCInterpreter_t* hCPU) + { + ppcDefineParamU32(result, 0); + ppcDefineParamPtr(ipcCtx, FPIpcContext, 1); + ipcCtx->m_asyncResult = result; // store result in variable since FP callbacks pass a pointer to nnResult and not the value directly + ipcCtx->CopyBackOutputs(); + cemuLog_logDebug(LogType::Force, "[DBG] AsyncHandler BeforeCallback"); + PPCCoreCallback(ipcCtx->m_callbackFunc, &ipcCtx->m_asyncResult, ipcCtx->m_callbackParam); + cemuLog_logDebug(LogType::Force, "[DBG] AsyncHandler AfterCallback"); + delete ipcCtx; + osLib_returnFromFunction(hCPU, 0); + } + + void CopyBackOutputs() + { + if(m_numVecOut > 0) + { + // copy output from temporary output buffers to the original addresses + for(uint8 i=0; i m_requestId; + uint8be m_numVecIn{0}; + uint8be m_numVecOut{0}; + IPCIoctlVector m_vec[MAX_VEC_COUNT]; + MEMPTR m_vecOriginalAddress[MAX_VEC_COUNT]{}; + MEMPTR m_dataBuffer{nullptr}; + MEMPTR m_callbackFunc{nullptr}; + MEMPTR m_callbackParam{nullptr}; + betype m_asyncResult; + }; + + struct FPGlobalLock + { + FPGlobalLock() + { + coreinit::OSLockMutex(&g_fp.fpMutex); + } + ~FPGlobalLock() + { + coreinit::OSUnlockMutex(&g_fp.fpMutex); + } + }; + #define FP_API_BASE() if (g_fp.initCounter == 0) return 0xC0C00580; FPGlobalLock _fpLock; + #define FP_API_BASE_ZeroOnError() if (g_fp.initCounter == 0) return 0; FPGlobalLock _fpLock; + + nnResult Initialize() + { + FPGlobalLock _fpLock; + if (g_fp.initCounter == 0) + { + g_fp.fpdHandle = coreinit::IOS_Open("/dev/fpd", 0); + } + g_fp.initCounter++; + return FPResult_OkZero; + } + + uint32 IsInitialized() + { + FPGlobalLock _fpLock; + return g_fp.initCounter > 0 ? 1 : 0; + } + + nnResult InitializeAdmin(PPCInterpreter_t* hCPU) + { + FPGlobalLock _fpLock; + g_fp.isAdminMode = true; + return Initialize(); + } + + uint32 IsInitializedAdmin() + { + FPGlobalLock _fpLock; + return g_fp.initCounter > 0 ? 1 : 0; + } + + nnResult Finalize() + { + FPGlobalLock _fpLock; + if (g_fp.initCounter == 1) + { + g_fp.initCounter = 0; + g_fp.isAdminMode = false; + g_fp.isLoggedIn = false; + coreinit::IOS_Close(g_fp.fpdHandle); + g_fp.getNotificationCalled = false; + } + else if (g_fp.initCounter > 0) + g_fp.initCounter--; + return FPResult_OkZero; + } + + nnResult FinalizeAdmin() + { + return Finalize(); + } + + void GetNextNotificationAsync(); + + nnResult SetNotificationHandler(uint32 notificationMask, void* funcPtr, void* userParam) + { + FP_API_BASE(); + g_fp.notificationHandler = funcPtr; + g_fp.notificationHandlerParam = userParam; + StackAllocator notificationMaskBuf; notificationMaskBuf = notificationMask; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::SetNotificationMask); + ipcCtx->AddInput(¬ificationMaskBuf, sizeof(uint32be)); + nnResult r = ipcCtx->Submit(std::move(ipcCtx)); + if (NN_RESULT_IS_SUCCESS(r)) + { + // async query for notifications + GetNextNotificationAsync(); + } + return r; + } + + void GetNextNotificationAsyncHandler(PPCInterpreter_t* hCPU) + { + coreinit::OSLockMutex(&g_fp.fpMutex); + cemu_assert_debug(g_fp.getNotificationCalled); + g_fp.getNotificationCalled = false; + auto bufPtr = g_fp.notificationBuffer.GetPtr(); + uint32 count = g_fp.notificationCount->value(); + if (count == 0) + { + GetNextNotificationAsync(); + coreinit::OSUnlockMutex(&g_fp.fpMutex); + osLib_returnFromFunction(hCPU, 0); return; } - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_LOGIN_ASYNC; - fpdRequest->loginAsync.funcPtr = funcPtr; - fpdRequest->loginAsync.custom = custom; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); + // copy notifications to temporary buffer using std::copy + iosu::fpd::FPDNotification tempBuffer[256]; + std::copy(g_fp.notificationBuffer.GetPtr(), g_fp.notificationBuffer.GetPtr() + count, tempBuffer); + // call handler for each notification, but do it outside of the lock + void* notificationHandler = g_fp.notificationHandler; + void* notificationHandlerParam = g_fp.notificationHandlerParam; + coreinit::OSUnlockMutex(&g_fp.fpMutex); + iosu::fpd::FPDNotification* notificationBuffer = g_fp.notificationBuffer.GetPtr(); + for (uint32 i = 0; i < count; i++) + PPCCoreCallback(notificationHandler, (uint32)notificationBuffer[i].type, notificationBuffer[i].pid, notificationHandlerParam); + coreinit::OSLockMutex(&g_fp.fpMutex); + // query more notifications + GetNextNotificationAsync(); + coreinit::OSUnlockMutex(&g_fp.fpMutex); osLib_returnFromFunction(hCPU, 0); } - void export_HasLoggedIn(PPCInterpreter_t* hCPU) + void GetNextNotificationAsync() { - // Sonic All Star Racing needs this - cemuLog_logDebug(LogType::Force, "nn_fp.HasLoggedIn()"); - osLib_returnFromFunction(hCPU, 1); + if (g_fp.getNotificationCalled) + return; + g_fp.getNotificationCalled = true; + g_fp.notificationCount = 0; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetNotificationAsync); + ipcCtx->AddOutput(g_fp.notificationBuffer.GetPtr(), g_fp.notificationBuffer.GetByteSize()); + ipcCtx->AddOutput(g_fp.notificationCount.GetPtr(), sizeof(uint32be)); + cemu_assert_debug(g_fp.notificationBuffer.GetByteSize() == 0x800); + nnResult r = ipcCtx->SubmitAsync(std::move(ipcCtx), MEMPTR(PPCInterpreter_makeCallableExportDepr(GetNextNotificationAsyncHandler)), nullptr); } - void export_IsOnline(PPCInterpreter_t* hCPU) + nnResult LoginAsync(void* funcPtr, void* userParam) { - //cemuLog_logDebug(LogType::Force, "nn_fp.IsOnline();"); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_IS_ONLINE; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - cemuLog_logDebug(LogType::Force, "nn_fp.IsOnline() -> {}", fpdRequest->resultU32.u32); - - osLib_returnFromFunction(hCPU, fpdRequest->resultU32.u32); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::LoginAsync); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, userParam); } - void export_GetFriendList(PPCInterpreter_t* hCPU) + uint32 HasLoggedIn() { - ppcDefineParamMEMPTR(pidList, uint32be, 0); - ppcDefineParamMEMPTR(returnedCount, uint32be, 1); - ppcDefineParamU32(startIndex, 2); - ppcDefineParamU32(maxCount, 3); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendList(...)"); - //debug_printf("nn_fp.GetFriendList(0x%08x, 0x%08x, %d, %d)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_LIST; - - fpdRequest->getFriendList.pidList = pidList; - fpdRequest->getFriendList.startIndex = startIndex; - fpdRequest->getFriendList.maxCount = maxCount; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - *returnedCount = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE_ZeroOnError(); + // Sonic All Star Racing uses this + // and Monster Hunter 3 Ultimate needs this to return false at least once to initiate login and not get stuck + // this returns false until LoginAsync was called and has completed (?) even if the user is already logged in + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::HasLoggedIn); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf != 0 ? 1 : 0; } - void export_GetFriendRequestList(PPCInterpreter_t* hCPU) + uint32 IsOnline() { - // GetFriendRequestList__Q2_2nn2fpFPUiT1UiT3 - ppcDefineParamMEMPTR(pidList, uint32be, 0); - ppcDefineParamMEMPTR(returnedCount, uint32be, 1); - ppcDefineParamU32(startIndex, 2); - ppcDefineParamU32(maxCount, 3); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendRequestList(...)"); - //debug_printf("nn_fp.GetFriendList(0x%08x, 0x%08x, %d, %d)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIENDREQUEST_LIST; - - fpdRequest->getFriendList.pidList = pidList; - fpdRequest->getFriendList.startIndex = startIndex; - fpdRequest->getFriendList.maxCount = maxCount; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - *returnedCount = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE_ZeroOnError(); + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::IsOnline); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf != 0 ? 1 : 0; } - void export_GetFriendListAll(PPCInterpreter_t* hCPU) + nnResult GetFriendList(uint32be* pidList, uint32be* returnedCount, uint32 startIndex, uint32 maxCount) { - ppcDefineParamMEMPTR(pidList, uint32be, 0); - ppcDefineParamMEMPTR(returnedCount, uint32be, 1); - ppcDefineParamU32(startIndex, 2); - ppcDefineParamU32(maxCount, 3); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendListAll(...)"); - - //debug_printf("nn_fp.GetFriendListAll(0x%08x, 0x%08x, %d, %d)\n", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6]); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_LIST_ALL; - - fpdRequest->getFriendList.pidList = pidList; - fpdRequest->getFriendList.startIndex = startIndex; - fpdRequest->getFriendList.maxCount = maxCount; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - *returnedCount = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator startIndexBuf; startIndexBuf = startIndex; + StackAllocator maxCountBuf; maxCountBuf = maxCount; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendList); + ipcCtx->AddOutput(pidList, sizeof(uint32be) * maxCount); + ipcCtx->AddOutput(returnedCount, sizeof(uint32be)); + ipcCtx->AddInput(&startIndexBuf, sizeof(uint32be)); + ipcCtx->AddInput(&maxCountBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendListEx(PPCInterpreter_t* hCPU) + nnResult GetFriendRequestList(uint32be* pidList, uint32be* returnedCount, uint32 startIndex, uint32 maxCount) { - ppcDefineParamMEMPTR(friendData, iosu::fpd::friendData_t, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendListEx(...)"); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_LIST_EX; - - fpdRequest->getFriendListEx.friendData = friendData; - fpdRequest->getFriendListEx.pidList = pidList; - fpdRequest->getFriendListEx.count = count; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator startIndexBuf; startIndexBuf = startIndex; + StackAllocator maxCountBuf; maxCountBuf = maxCount; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendRequestList); + ipcCtx->AddOutput(pidList, sizeof(uint32be) * maxCount); + ipcCtx->AddOutput(returnedCount, sizeof(uint32be)); + ipcCtx->AddInput(&startIndexBuf, sizeof(uint32be)); + ipcCtx->AddInput(&maxCountBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendRequestListEx(PPCInterpreter_t* hCPU) + nnResult GetFriendListAll(uint32be* pidList, uint32be* returnedCount, uint32 startIndex, uint32 maxCount) { - ppcDefineParamMEMPTR(friendRequest, iosu::fpd::friendRequest_t, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendRequestListEx(...)"); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIENDREQUEST_LIST_EX; - - fpdRequest->getFriendRequestListEx.friendRequest = friendRequest; - fpdRequest->getFriendRequestListEx.pidList = pidList; - fpdRequest->getFriendRequestListEx.count = count; - - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator startIndexBuf; startIndexBuf = startIndex; + StackAllocator maxCountBuf; maxCountBuf = maxCount; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendListAll); + ipcCtx->AddOutput(pidList, sizeof(uint32be) * maxCount); + ipcCtx->AddOutput(returnedCount, sizeof(uint32be)); + ipcCtx->AddInput(&startIndexBuf, sizeof(uint32be)); + ipcCtx->AddInput(&maxCountBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetBasicInfoAsync(PPCInterpreter_t* hCPU) + nnResult GetFriendListEx(iosu::fpd::FriendData* friendData, uint32be* pidList, uint32 count) { - ppcDefineParamMEMPTR(basicInfo, iosu::fpd::friendBasicInfo_t, 0); - ppcDefineParamTypePtr(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - ppcDefineParamMPTR(funcMPTR, 3); - ppcDefineParamU32(customParam, 4); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_BASIC_INFO_ASYNC; - fpdRequest->getBasicInfo.basicInfo = basicInfo; - fpdRequest->getBasicInfo.pidList = pidList; - fpdRequest->getBasicInfo.count = count; - fpdRequest->getBasicInfo.funcPtr = funcMPTR; - fpdRequest->getBasicInfo.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector);; - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendListEx); + ipcCtx->AddOutput(friendData, sizeof(iosu::fpd::FriendData) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetMyPrincipalId(PPCInterpreter_t* hCPU) + nnResult GetFriendRequestListEx(iosu::fpd::FriendRequest* friendRequest, uint32be* pidList, uint32 count) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyPrincipalId()"); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_PRINCIPAL_ID; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - uint32 principalId = fpdRequest->resultU32.u32; - - osLib_returnFromFunction(hCPU, principalId); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendRequestListEx); + ipcCtx->AddOutput(friendRequest, sizeof(iosu::fpd::FriendRequest) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetMyAccountId(PPCInterpreter_t* hCPU) + nnResult GetBasicInfoAsync(iosu::fpd::FriendBasicInfo* basicInfo, uint32be* pidList, uint32 count, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyAccountId(0x{:08x})", hCPU->gpr[3]); - ppcDefineParamTypePtr(accountId, uint8, 0); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_ACCOUNT_ID; - fpdRequest->common.ptr = (void*)accountId; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetBasicInfoAsync); + ipcCtx->AddOutput(basicInfo, sizeof(iosu::fpd::FriendBasicInfo) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_GetMyScreenName(PPCInterpreter_t* hCPU) + uint32 GetMyPrincipalId() { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyScreenName(0x{:08x})", hCPU->gpr[3]); - ppcDefineParamTypePtr(screenname, uint16be, 0); - - screenname[0] = '\0'; - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_SCREENNAME; - fpdRequest->common.ptr = (void*)screenname; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE_ZeroOnError(); + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyPrincipalId); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf->value(); } - typedef struct + nnResult GetMyAccountId(uint8be* accountId) { - uint8 showOnline; // show online status to others - uint8 showGame; // show played game to others - uint8 blockFriendRequests; // block friend requests - }fpPerference_t; - - void export_GetMyPreference(PPCInterpreter_t* hCPU) - { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyPreference(0x{:08x}) - placeholder", hCPU->gpr[3]); - ppcDefineParamTypePtr(pref, fpPerference_t, 0); - - pref->showOnline = 1; - pref->showGame = 1; - pref->blockFriendRequests = 0; - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyAccountId); + ipcCtx->AddOutput(accountId, ACT_ACCOUNTID_LENGTH); + return ipcCtx->Submit(std::move(ipcCtx)); } - // GetMyPreference__Q2_2nn2fpFPQ3_2nn2fp10Preference - - void export_GetMyMii(PPCInterpreter_t* hCPU) + nnResult GetMyScreenName(uint16be* screenname) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetMyMii(0x{:08x})", hCPU->gpr[3]); - ppcDefineParamTypePtr(fflData, FFLData_t, 0); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_MY_MII; - fpdRequest->common.ptr = (void*)fflData; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyScreenName); + ipcCtx->AddOutput(screenname, ACT_NICKNAME_SIZE*sizeof(uint16)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendAccountId(PPCInterpreter_t* hCPU) + nnResult GetMyPreference(iosu::fpd::FPDPreference* myPreference) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendAccountId(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(accountIds, char, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_ACCOUNT_ID; - fpdRequest->getFriendAccountId.accountIds = accountIds; - fpdRequest->getFriendAccountId.pidList = pidList; - fpdRequest->getFriendAccountId.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyPreference); + ipcCtx->AddOutput(myPreference, sizeof(iosu::fpd::FPDPreference)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendScreenName(PPCInterpreter_t* hCPU) + nnResult GetMyMii(FFLData_t* fflData) { - // GetFriendScreenName__Q2_2nn2fpFPA11_wPCUiUibPUc - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendScreenName(0x{:08x},0x{:08x},0x{:08x},{},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); - ppcDefineParamMEMPTR(nameList, uint16be, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - ppcDefineParamU32(replaceNonAscii, 3); - ppcDefineParamMEMPTR(languageList, uint8, 4); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_SCREENNAME; - fpdRequest->getFriendScreenname.nameList = nameList; - fpdRequest->getFriendScreenname.pidList = pidList; - fpdRequest->getFriendScreenname.count = count; - fpdRequest->getFriendScreenname.replaceNonAscii = replaceNonAscii != 0; - fpdRequest->getFriendScreenname.languageList = languageList; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetMyMii); + ipcCtx->AddOutput(fflData, sizeof(FFLData_t)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendMii(PPCInterpreter_t* hCPU) + nnResult GetFriendAccountId(uint8be* accountIdArray, uint32be* pidList, uint32 count) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendMii(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(miiList, FFLData_t, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_MII; - fpdRequest->getFriendMii.miiList = (uint8*)miiList.GetPtr(); - fpdRequest->getFriendMii.pidList = pidList; - fpdRequest->getFriendMii.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + if (count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendAccountId); + ipcCtx->AddOutput(accountIdArray, ACT_ACCOUNTID_LENGTH * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendPresence(PPCInterpreter_t* hCPU) + nnResult GetFriendScreenName(uint16be* nameList, uint32be* pidList, uint32 count, uint8 replaceNonAscii, uint8be* languageList) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendPresence(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(presenceList, uint8, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_PRESENCE; - fpdRequest->getFriendPresence.presenceList = (uint8*)presenceList.GetPtr(); - fpdRequest->getFriendPresence.pidList = pidList; - fpdRequest->getFriendPresence.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + if (count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + StackAllocator replaceNonAsciiBuf; replaceNonAsciiBuf = replaceNonAscii; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendScreenName); + ipcCtx->AddOutput(nameList, ACT_NICKNAME_SIZE * sizeof(uint16be) * count); + ipcCtx->AddOutput(languageList, languageList ? sizeof(uint8be) * count : 0); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + ipcCtx->AddInput(&replaceNonAsciiBuf, sizeof(uint8be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_GetFriendRelationship(PPCInterpreter_t* hCPU) + nnResult GetFriendMii(FFLData_t* miiList, uint32be* pidList, uint32 count) { - cemuLog_logDebug(LogType::Force, "nn_fp.GetFriendRelationship(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(relationshipList, uint8, 0); - ppcDefineParamMEMPTR(pidList, uint32be, 1); - ppcDefineParamU32(count, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_RELATIONSHIP; - fpdRequest->getFriendRelationship.relationshipList = (uint8*)relationshipList.GetPtr(); - fpdRequest->getFriendRelationship.pidList = pidList; - fpdRequest->getFriendRelationship.count = count; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + if(count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendMii); + ipcCtx->AddOutput(miiList, sizeof(FFLData_t) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_IsJoinable(PPCInterpreter_t* hCPU) + nnResult GetFriendPresence(iosu::fpd::FriendPresence* presenceList, uint32be* pidList, uint32 count) { - ppcDefineParamTypePtr(presence, iosu::fpd::friendPresence_t, 0); - ppcDefineParamU64(joinMask, 2); + FP_API_BASE(); + if(count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendPresence); + ipcCtx->AddOutput(presenceList, sizeof(iosu::fpd::FriendPresence) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); + } + nnResult GetFriendRelationship(uint8* relationshipList, uint32be* pidList, uint32 count) + { + FP_API_BASE(); + if(count == 0) + return 0; + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetFriendRelationship); + ipcCtx->AddOutput(relationshipList, sizeof(uint8) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->Submit(std::move(ipcCtx)); + } + + uint32 IsJoinable(iosu::fpd::FriendPresence* presence, uint64 joinMask) + { if (presence->isValid == 0 || presence->isOnline == 0 || presence->gameMode.joinGameId == 0 || @@ -428,368 +556,237 @@ namespace nn presence->gameMode.groupId == 0 || presence->gameMode.joinGameMode >= 64 ) { - osLib_returnFromFunction(hCPU, 0); - return; + return 0; } uint32 joinGameMode = presence->gameMode.joinGameMode; uint64 joinModeMask = (1ULL<gameMode.joinFlagMask; if (joinFlagMask == 0) - { - osLib_returnFromFunction(hCPU, 0); - return; - } + return 0; if (joinFlagMask == 1) - { - osLib_returnFromFunction(hCPU, 1); - return; - } + return 1; if (joinFlagMask == 2) { - // check relationship + // check relationship uint8 relationship[1] = { 0 }; StackAllocator pidList; - pidList[0] = presence->gameMode.hostPid; - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_GET_FRIEND_RELATIONSHIP; - fpdRequest->getFriendRelationship.relationshipList = relationship; - fpdRequest->getFriendRelationship.pidList = pidList.GetPointer(); - fpdRequest->getFriendRelationship.count = 1; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - + pidList = presence->gameMode.hostPid; + GetFriendRelationship(relationship, &pidList, 1); if(relationship[0] == iosu::fpd::RELATIONSHIP_FRIEND) - osLib_returnFromFunction(hCPU, 1); - else - osLib_returnFromFunction(hCPU, 0); - return; + return 1; + return 0; } if (joinFlagMask == 0x65 || joinFlagMask == 0x66) { cemuLog_log(LogType::Force, "Unsupported friend invite"); } - - osLib_returnFromFunction(hCPU, 0); + return 0; } - void export_CheckSettingStatusAsync(PPCInterpreter_t* hCPU) + nnResult CheckSettingStatusAsync(uint8* status, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.CheckSettingStatusAsync(0x{:08x},0x{:08x},0x{:08x}) - placeholder", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamTypePtr(uknR3, uint8, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamU32(customParam, 2); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::CheckSettingStatusAsync); + ipcCtx->AddOutput(status, sizeof(uint8be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); + } - if (g_fp.isAdminMode == false) + uint32 IsPreferenceValid() + { + FP_API_BASE_ZeroOnError(); + StackAllocator resultBuf; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::IsPreferenceValid); + ipcCtx->AddOutput(&resultBuf, sizeof(uint32be)); + ipcCtx->Submit(std::move(ipcCtx)); + return resultBuf != 0 ? 1 : 0; + } + + nnResult UpdatePreferenceAsync(iosu::fpd::FPDPreference* newPreference, void* funcPtr, void* customParam) + { + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::UpdatePreferenceAsync); + ipcCtx->AddInput(newPreference, sizeof(iosu::fpd::FPDPreference)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); + } + + nnResult UpdateGameModeWithUnusedParam(iosu::fpd::GameMode* gameMode, uint16be* gameModeMessage, uint32 unusedParam) + { + FP_API_BASE(); + uint32 messageLen = CafeStringHelpers::Length(gameModeMessage, iosu::fpd::GAMEMODE_MAX_MESSAGE_LENGTH); + if(messageLen >= iosu::fpd::GAMEMODE_MAX_MESSAGE_LENGTH) { - - osLib_returnFromFunction(hCPU, 0xC0C00800); - return; + cemuLog_log(LogType::Force, "UpdateGameMode: message too long"); + return FPResult_InvalidIPCParam; } - - *uknR3 = 1; - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::UpdateGameModeVariation2); + ipcCtx->AddInput(gameMode, sizeof(iosu::fpd::GameMode)); + ipcCtx->AddInput(gameModeMessage, sizeof(uint16be) * (messageLen + 1)); + return ipcCtx->Submit(std::move(ipcCtx)); } - void export_IsPreferenceValid(PPCInterpreter_t* hCPU) + nnResult UpdateGameMode(iosu::fpd::GameMode* gameMode, uint16be* gameModeMessage) { - cemuLog_logDebug(LogType::Force, "nn_fp.IsPreferenceValid()"); - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_IS_PREFERENCE_VALID; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->resultU32.u32); + return UpdateGameModeWithUnusedParam(gameMode, gameModeMessage, 0); } - void export_UpdatePreferenceAsync(PPCInterpreter_t* hCPU) + nnResult GetRequestBlockSettingAsync(uint8* blockSettingList, uint32be* pidList, uint32 count, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.UpdatePreferenceAsync(0x{:08x},0x{:08x},0x{:08x}) - placeholder", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamTypePtr(uknR3, uint8, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamU32(customParam, 2); - - if (g_fp.isAdminMode == false) - { - - osLib_returnFromFunction(hCPU, 0xC0C00800); - return; - } - - //*uknR3 = 0; // seems to be 3 bytes (nn::fp::Preference const *) - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::GetRequestBlockSettingAsync); + ipcCtx->AddOutput(blockSettingList, sizeof(uint8be) * count); + ipcCtx->AddInput(pidList, sizeof(uint32be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_UpdateGameMode(PPCInterpreter_t* hCPU) + // overload of AddFriendAsync + nnResult AddFriendAsyncByPid(uint32 pid, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.UpdateGameMode(0x{:08x},0x{:08x},{})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]); - ppcDefineParamMEMPTR(gameMode, iosu::fpd::gameMode_t, 0); - ppcDefineParamMEMPTR(gameModeMessage, uint16be, 1); - ppcDefineParamU32(uknR5, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_UPDATE_GAMEMODE; - fpdRequest->updateGameMode.gameMode = gameMode; - fpdRequest->updateGameMode.gameModeMessage = gameModeMessage; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + StackAllocator pidBuf; pidBuf = pid; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::AddFriendAsyncByPid); + ipcCtx->AddInput(&pidBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_GetRequestBlockSettingAsync(PPCInterpreter_t* hCPU) + nnResult DeleteFriendFlagsAsync(uint32be* pidList, uint32 pidCount, uint32 ukn, void* funcPtr, void* customParam) { - ppcDefineParamTypePtr(settingList, uint8, 0); - ppcDefineParamTypePtr(pidList, uint32be, 1); - ppcDefineParamU32(pidCount, 2); - ppcDefineParamMPTR(funcMPTR, 3); - ppcDefineParamMPTR(customParam, 4); - - cemuLog_logDebug(LogType::Force, "GetRequestBlockSettingAsync(...) - todo"); - - for (uint32 i = 0; i < pidCount; i++) - settingList[i] = 0; - // 0 means not blocked. Friend app will continue with GetBasicInformation() - // 1 means blocked. Friend app will continue with AddFriendAsync to add the user as a provisional friend - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + // admin function? + FP_API_BASE(); + StackAllocator pidCountBuf; pidCountBuf = pidCount; + StackAllocator uknBuf; uknBuf = ukn; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::DeleteFriendFlagsAsync); + ipcCtx->AddInput(pidList, sizeof(uint32be) * pidCount); + ipcCtx->AddInput(&pidCountBuf, sizeof(uint32be)); + ipcCtx->AddInput(&uknBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_AddFriendAsync(PPCInterpreter_t* hCPU) + // overload of AddFriendRequestAsync + nnResult AddFriendRequestByPlayRecordAsync(iosu::fpd::RecentPlayRecordEx* playRecord, uint16be* message, void* funcPtr, void* customParam) { - // AddFriendAsync__Q2_2nn2fpFPCcPFQ2_2nn6ResultPv_vPv - ppcDefineParamU32(principalId, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamMPTR(customParam, 2); - -#ifdef CEMU_DEBUG_ASSERT - assert_dbg(); -#endif - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_ADD_FRIEND; - fpdRequest->addOrRemoveFriend.pid = principalId; - fpdRequest->addOrRemoveFriend.funcPtr = funcMPTR; - fpdRequest->addOrRemoveFriend.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::AddFriendRequestByPlayRecordAsync); + uint32 messageLen = 0; + while(message[messageLen] != 0) + messageLen++; + ipcCtx->AddInput(playRecord, sizeof(iosu::fpd::RecentPlayRecordEx)); + ipcCtx->AddInput(message, sizeof(uint16be) * (messageLen+1)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - - void export_DeleteFriendFlagsAsync(PPCInterpreter_t* hCPU) + nnResult RemoveFriendAsync(uint32 pid, void* funcPtr, void* customParam) { - cemuLog_logDebug(LogType::Force, "nn_fp.DeleteFriendFlagsAsync(...) - todo"); - ppcDefineParamU32(uknR3, 0); // example value: pointer - ppcDefineParamU32(uknR4, 1); // example value: 1 - ppcDefineParamU32(uknR5, 2); // example value: 1 - ppcDefineParamMPTR(funcMPTR, 3); - ppcDefineParamU32(customParam, 4); - - if (g_fp.isAdminMode == false) - { - osLib_returnFromFunction(hCPU, 0xC0C00800); - return; - } - - StackAllocator callbackResultCode; - - *callbackResultCode.GetPointer() = 0; - - hCPU->gpr[3] = callbackResultCode.GetMPTR(); - hCPU->gpr[4] = customParam; - PPCCore_executeCallbackInternal(funcMPTR); - - osLib_returnFromFunction(hCPU, 0); + FP_API_BASE(); + StackAllocator pidBuf; pidBuf = pid; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::RemoveFriendAsync); + ipcCtx->AddInput(&pidBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - - typedef struct + nnResult MarkFriendRequestsAsReceivedAsync(uint64be* messageIdList, uint32 count, void* funcPtr, void* customParam) { - /* +0x00 */ uint32be pid; - /* +0x04 */ uint8 ukn04; - /* +0x05 */ uint8 ukn05; - /* +0x06 */ uint8 ukn06[0x22]; - /* +0x28 */ uint8 ukn28[0x22]; - /* +0x4A */ uint8 _uknOrPadding4A[6]; - /* +0x50 */ uint32be ukn50; - /* +0x54 */ uint32be ukn54; - /* +0x58 */ uint16be ukn58; - /* +0x5C */ uint8 _padding5C[4]; - /* +0x60 */ iosu::fpd::fpdDate_t date; - }RecentPlayRecordEx_t; - - static_assert(sizeof(RecentPlayRecordEx_t) == 0x68, ""); - static_assert(offsetof(RecentPlayRecordEx_t, ukn06) == 0x06, ""); - static_assert(offsetof(RecentPlayRecordEx_t, ukn50) == 0x50, ""); - - void export_AddFriendRequestAsync(PPCInterpreter_t* hCPU) - { - ppcDefineParamTypePtr(playRecord, RecentPlayRecordEx_t, 0); - ppcDefineParamTypePtr(message, uint16be, 1); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - - uint8* uknData = (uint8*)playRecord; - - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_ADD_FRIEND_REQUEST; - fpdRequest->addFriendRequest.pid = playRecord->pid; - fpdRequest->addFriendRequest.message = message; - fpdRequest->addFriendRequest.funcPtr = funcMPTR; - fpdRequest->addFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator countBuf; countBuf = count; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::MarkFriendRequestsAsReceivedAsync); + ipcCtx->AddInput(messageIdList, sizeof(uint64be) * count); + ipcCtx->AddInput(&countBuf, sizeof(uint32be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_RemoveFriendAsync(PPCInterpreter_t* hCPU) + nnResult CancelFriendRequestAsync(uint64 requestId, void* funcPtr, void* customParam) { - ppcDefineParamU32(principalId, 0); - ppcDefineParamMPTR(funcMPTR, 1); - ppcDefineParamMPTR(customParam, 2); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_REMOVE_FRIEND_ASYNC; - fpdRequest->addOrRemoveFriend.pid = principalId; - fpdRequest->addOrRemoveFriend.funcPtr = funcMPTR; - fpdRequest->addOrRemoveFriend.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator requestIdBuf; requestIdBuf = requestId; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::CancelFriendRequestAsync); + ipcCtx->AddInput(&requestIdBuf, sizeof(uint64be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_MarkFriendRequestsAsReceivedAsync(PPCInterpreter_t* hCPU) + nnResult DeleteFriendRequestAsync(uint64 requestId, void* funcPtr, void* customParam) { - ppcDefineParamTypePtr(messageIdList, uint64, 0); - ppcDefineParamU32(count, 1); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_MARK_FRIEND_REQUEST_AS_RECEIVED_ASYNC; - fpdRequest->markFriendRequest.messageIdList = messageIdList; - fpdRequest->markFriendRequest.count = count; - fpdRequest->markFriendRequest.funcPtr = funcMPTR; - fpdRequest->markFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator requestIdBuf; requestIdBuf = requestId; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::DeleteFriendRequestAsync); + ipcCtx->AddInput(&requestIdBuf, sizeof(uint64be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } - void export_CancelFriendRequestAsync(PPCInterpreter_t* hCPU) + nnResult AcceptFriendRequestAsync(uint64 requestId, void* funcPtr, void* customParam) { - ppcDefineParamU64(frqMessageId, 0); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_CANCEL_FRIEND_REQUEST_ASYNC; - fpdRequest->cancelOrAcceptFriendRequest.messageId = frqMessageId; - fpdRequest->cancelOrAcceptFriendRequest.funcPtr = funcMPTR; - fpdRequest->cancelOrAcceptFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); - } - - void export_AcceptFriendRequestAsync(PPCInterpreter_t* hCPU) - { - ppcDefineParamU64(frqMessageId, 0); - ppcDefineParamMPTR(funcMPTR, 2); - ppcDefineParamMPTR(customParam, 3); - - fpdPrepareRequest(); - fpdRequest->requestCode = iosu::fpd::IOSU_FPD_ACCEPT_FRIEND_REQUEST_ASYNC; - fpdRequest->cancelOrAcceptFriendRequest.messageId = frqMessageId; - fpdRequest->cancelOrAcceptFriendRequest.funcPtr = funcMPTR; - fpdRequest->cancelOrAcceptFriendRequest.custom = customParam; - __depr__IOS_Ioctlv(IOS_DEVICE_FPD, IOSU_FPD_REQUEST_CEMU, 1, 1, fpdBufferVector); - - osLib_returnFromFunction(hCPU, fpdRequest->returnCode); + FP_API_BASE(); + StackAllocator requestIdBuf; requestIdBuf = requestId; + auto ipcCtx = std::make_unique(iosu::fpd::FPD_REQUEST_ID::AcceptFriendRequestAsync); + ipcCtx->AddInput(&requestIdBuf, sizeof(uint64be)); + return ipcCtx->SubmitAsync(std::move(ipcCtx), funcPtr, customParam); } void load() { - osLib_addFunction("nn_fp", "Initialize__Q2_2nn2fpFv", export_Initialize); - osLib_addFunction("nn_fp", "InitializeAdmin__Q2_2nn2fpFv", export_InitializeAdmin); - osLib_addFunction("nn_fp", "IsInitialized__Q2_2nn2fpFv", export_IsInitialized); - osLib_addFunction("nn_fp", "IsInitializedAdmin__Q2_2nn2fpFv", export_IsInitializedAdmin); + g_fp.initCounter = 0; + g_fp.isAdminMode = false; + g_fp.isLoggedIn = false; + g_fp.getNotificationCalled = false; + g_fp.notificationHandler = nullptr; + g_fp.notificationHandlerParam = nullptr; - osLib_addFunction("nn_fp", "SetNotificationHandler__Q2_2nn2fpFUiPFQ3_2nn2fp16NotificationTypeUiPv_vPv", export_SetNotificationHandler); + coreinit::OSInitMutex(&g_fp.fpMutex); + FPIpcBufferAllocator.Init(); - osLib_addFunction("nn_fp", "LoginAsync__Q2_2nn2fpFPFQ2_2nn6ResultPv_vPv", export_LoginAsync); - osLib_addFunction("nn_fp", "HasLoggedIn__Q2_2nn2fpFv", export_HasLoggedIn); + cafeExportRegisterFunc(Initialize, "nn_fp", "Initialize__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(InitializeAdmin, "nn_fp", "InitializeAdmin__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(IsInitialized, "nn_fp", "IsInitialized__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(IsInitializedAdmin, "nn_fp", "IsInitializedAdmin__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(Finalize, "nn_fp", "Finalize__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(FinalizeAdmin, "nn_fp", "FinalizeAdmin__Q2_2nn2fpFv", LogType::NN_FP); - osLib_addFunction("nn_fp", "IsOnline__Q2_2nn2fpFv", export_IsOnline); + cafeExportRegisterFunc(SetNotificationHandler, "nn_fp", "SetNotificationHandler__Q2_2nn2fpFUiPFQ3_2nn2fp16NotificationTypeUiPv_vPv", LogType::NN_FP); - osLib_addFunction("nn_fp", "GetFriendList__Q2_2nn2fpFPUiT1UiT3", export_GetFriendList); - osLib_addFunction("nn_fp", "GetFriendRequestList__Q2_2nn2fpFPUiT1UiT3", export_GetFriendRequestList); - osLib_addFunction("nn_fp", "GetFriendListAll__Q2_2nn2fpFPUiT1UiT3", export_GetFriendListAll); - osLib_addFunction("nn_fp", "GetFriendListEx__Q2_2nn2fpFPQ3_2nn2fp10FriendDataPCUiUi", export_GetFriendListEx); - osLib_addFunction("nn_fp", "GetFriendRequestListEx__Q2_2nn2fpFPQ3_2nn2fp13FriendRequestPCUiUi", export_GetFriendRequestListEx); - osLib_addFunction("nn_fp", "GetBasicInfoAsync__Q2_2nn2fpFPQ3_2nn2fp9BasicInfoPCUiUiPFQ2_2nn6ResultPv_vPv", export_GetBasicInfoAsync); + cafeExportRegisterFunc(LoginAsync, "nn_fp", "LoginAsync__Q2_2nn2fpFPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(HasLoggedIn, "nn_fp", "HasLoggedIn__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(IsOnline, "nn_fp", "IsOnline__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendList, "nn_fp", "GetFriendList__Q2_2nn2fpFPUiT1UiT3", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendRequestList, "nn_fp", "GetFriendRequestList__Q2_2nn2fpFPUiT1UiT3", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendListAll, "nn_fp", "GetFriendListAll__Q2_2nn2fpFPUiT1UiT3", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendListEx, "nn_fp", "GetFriendListEx__Q2_2nn2fpFPQ3_2nn2fp10FriendDataPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendRequestListEx, "nn_fp", "GetFriendRequestListEx__Q2_2nn2fpFPQ3_2nn2fp13FriendRequestPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetBasicInfoAsync, "nn_fp", "GetBasicInfoAsync__Q2_2nn2fpFPQ3_2nn2fp9BasicInfoPCUiUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); - osLib_addFunction("nn_fp", "GetMyPrincipalId__Q2_2nn2fpFv", export_GetMyPrincipalId); - osLib_addFunction("nn_fp", "GetMyAccountId__Q2_2nn2fpFPc", export_GetMyAccountId); - osLib_addFunction("nn_fp", "GetMyScreenName__Q2_2nn2fpFPw", export_GetMyScreenName); - osLib_addFunction("nn_fp", "GetMyMii__Q2_2nn2fpFP12FFLStoreData", export_GetMyMii); - osLib_addFunction("nn_fp", "GetMyPreference__Q2_2nn2fpFPQ3_2nn2fp10Preference", export_GetMyPreference); + cafeExportRegisterFunc(GetMyPrincipalId, "nn_fp", "GetMyPrincipalId__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(GetMyAccountId, "nn_fp", "GetMyAccountId__Q2_2nn2fpFPc", LogType::NN_FP); + cafeExportRegisterFunc(GetMyScreenName, "nn_fp", "GetMyScreenName__Q2_2nn2fpFPw", LogType::NN_FP); + cafeExportRegisterFunc(GetMyMii, "nn_fp", "GetMyMii__Q2_2nn2fpFP12FFLStoreData", LogType::NN_FP); + cafeExportRegisterFunc(GetMyPreference, "nn_fp", "GetMyPreference__Q2_2nn2fpFPQ3_2nn2fp10Preference", LogType::NN_FP); - osLib_addFunction("nn_fp", "GetFriendAccountId__Q2_2nn2fpFPA17_cPCUiUi", export_GetFriendAccountId); - osLib_addFunction("nn_fp", "GetFriendScreenName__Q2_2nn2fpFPA11_wPCUiUibPUc", export_GetFriendScreenName); - osLib_addFunction("nn_fp", "GetFriendMii__Q2_2nn2fpFP12FFLStoreDataPCUiUi", export_GetFriendMii); - osLib_addFunction("nn_fp", "GetFriendPresence__Q2_2nn2fpFPQ3_2nn2fp14FriendPresencePCUiUi", export_GetFriendPresence); - osLib_addFunction("nn_fp", "GetFriendRelationship__Q2_2nn2fpFPUcPCUiUi", export_GetFriendRelationship); - osLib_addFunction("nn_fp", "IsJoinable__Q2_2nn2fpFPCQ3_2nn2fp14FriendPresenceUL", export_IsJoinable); + cafeExportRegisterFunc(GetFriendAccountId, "nn_fp", "GetFriendAccountId__Q2_2nn2fpFPA17_cPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendScreenName, "nn_fp", "GetFriendScreenName__Q2_2nn2fpFPA11_wPCUiUibPUc", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendMii, "nn_fp", "GetFriendMii__Q2_2nn2fpFP12FFLStoreDataPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendPresence, "nn_fp", "GetFriendPresence__Q2_2nn2fpFPQ3_2nn2fp14FriendPresencePCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(GetFriendRelationship, "nn_fp", "GetFriendRelationship__Q2_2nn2fpFPUcPCUiUi", LogType::NN_FP); + cafeExportRegisterFunc(IsJoinable, "nn_fp", "IsJoinable__Q2_2nn2fpFPCQ3_2nn2fp14FriendPresenceUL", LogType::NN_FP); - osLib_addFunction("nn_fp", "CheckSettingStatusAsync__Q2_2nn2fpFPUcPFQ2_2nn6ResultPv_vPv", export_CheckSettingStatusAsync); - osLib_addFunction("nn_fp", "IsPreferenceValid__Q2_2nn2fpFv", export_IsPreferenceValid); - osLib_addFunction("nn_fp", "UpdatePreferenceAsync__Q2_2nn2fpFPCQ3_2nn2fp10PreferencePFQ2_2nn6ResultPv_vPv", export_UpdatePreferenceAsync); + cafeExportRegisterFunc(CheckSettingStatusAsync, "nn_fp", "CheckSettingStatusAsync__Q2_2nn2fpFPUcPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(IsPreferenceValid, "nn_fp", "IsPreferenceValid__Q2_2nn2fpFv", LogType::NN_FP); + cafeExportRegisterFunc(UpdatePreferenceAsync, "nn_fp", "UpdatePreferenceAsync__Q2_2nn2fpFPCQ3_2nn2fp10PreferencePFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(GetRequestBlockSettingAsync, "nn_fp", "GetRequestBlockSettingAsync__Q2_2nn2fpFPUcPCUiUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); - osLib_addFunction("nn_fp", "UpdateGameMode__Q2_2nn2fpFPCQ3_2nn2fp8GameModePCwUi", export_UpdateGameMode); - - osLib_addFunction("nn_fp", "GetRequestBlockSettingAsync__Q2_2nn2fpFPUcPCUiUiPFQ2_2nn6ResultPv_vPv", export_GetRequestBlockSettingAsync); - - osLib_addFunction("nn_fp", "AddFriendAsync__Q2_2nn2fpFPCcPFQ2_2nn6ResultPv_vPv", export_AddFriendAsync); - osLib_addFunction("nn_fp", "AddFriendRequestAsync__Q2_2nn2fpFPCQ3_2nn2fp18RecentPlayRecordExPCwPFQ2_2nn6ResultPv_vPv", export_AddFriendRequestAsync); - osLib_addFunction("nn_fp", "DeleteFriendFlagsAsync__Q2_2nn2fpFPCUiUiT2PFQ2_2nn6ResultPv_vPv", export_DeleteFriendFlagsAsync); - - osLib_addFunction("nn_fp", "RemoveFriendAsync__Q2_2nn2fpFUiPFQ2_2nn6ResultPv_vPv", export_RemoveFriendAsync); - osLib_addFunction("nn_fp", "MarkFriendRequestsAsReceivedAsync__Q2_2nn2fpFPCULUiPFQ2_2nn6ResultPv_vPv", export_MarkFriendRequestsAsReceivedAsync); - osLib_addFunction("nn_fp", "CancelFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", export_CancelFriendRequestAsync); - osLib_addFunction("nn_fp", "AcceptFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", export_AcceptFriendRequestAsync); + cafeExportRegisterFunc(UpdateGameModeWithUnusedParam, "nn_fp", "UpdateGameMode__Q2_2nn2fpFPCQ3_2nn2fp8GameModePCwUi", LogType::NN_FP); + cafeExportRegisterFunc(UpdateGameMode, "nn_fp", "UpdateGameMode__Q2_2nn2fpFPCQ3_2nn2fp8GameModePCw", LogType::NN_FP); + cafeExportRegisterFunc(AddFriendAsyncByPid, "nn_fp", "AddFriendAsync__Q2_2nn2fpFUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(AddFriendRequestByPlayRecordAsync, "nn_fp", "AddFriendRequestAsync__Q2_2nn2fpFPCQ3_2nn2fp18RecentPlayRecordExPCwPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(DeleteFriendFlagsAsync, "nn_fp", "DeleteFriendFlagsAsync__Q2_2nn2fpFPCUiUiT2PFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(RemoveFriendAsync, "nn_fp", "RemoveFriendAsync__Q2_2nn2fpFUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(MarkFriendRequestsAsReceivedAsync, "nn_fp", "MarkFriendRequestsAsReceivedAsync__Q2_2nn2fpFPCULUiPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(CancelFriendRequestAsync, "nn_fp", "CancelFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(DeleteFriendRequestAsync, "nn_fp", "DeleteFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); + cafeExportRegisterFunc(AcceptFriendRequestAsync, "nn_fp", "AcceptFriendRequestAsync__Q2_2nn2fpFULPFQ2_2nn6ResultPv_vPv", LogType::NN_FP); } } } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp index 8df14ce0..1bf2b37d 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp @@ -47,10 +47,10 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, DownloadCommunityDataList_AsyncRequest, std::ref(req), reqUrl, requestDoneEvent.GetPointer(), pOutList, pOutNum, numMaxList, pParam); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp index 0ae581e0..5e6dba7e 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp @@ -199,9 +199,9 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, MakeDiscoveryRequest_AsyncRequest, std::ref(req), requestUrl.c_str(), requestDoneEvent.GetPointer()); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp index e6cea082..309394e6 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp @@ -25,7 +25,7 @@ namespace nn // open archive g_offlineDBArchive = ZArchiveReader::OpenFromFile(ActiveSettings::GetUserDataPath("resources/miiverse/OfflineDB.zar")); if(!g_offlineDBArchive) - cemuLog_log(LogType::Force, "Failed to open resources/miiverse/OfflineDB.zar. Miiverse posts will not be available"); + cemuLog_log(LogType::Force, "Offline miiverse posts are not available"); g_offlineDBInitialized = true; } @@ -175,9 +175,9 @@ namespace nn return OLV_RESULT_SUCCESS; // the offlineDB doesn't contain any self posts StackAllocator doneEvent; - coreinit::OSInitEvent(doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); auto asyncTask = std::async(std::launch::async, _Async_OfflineDB_DownloadPostDataListParam_DownloadPostDataList, doneEvent.GetPointer(), downloadedTopicData, downloadedPostData, postCountOut, maxCount, param); - coreinit::OSWaitEvent(doneEvent); + coreinit::OSWaitEvent(&doneEvent); nnResult r = asyncTask.get(); return r; } @@ -204,9 +204,9 @@ namespace nn nnResult OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize) { StackAllocator doneEvent; - coreinit::OSInitEvent(doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&doneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); auto asyncTask = std::async(std::launch::async, _Async_OfflineDB_DownloadPostDataListParam_DownloadExternalImageData, doneEvent.GetPointer(), _this, imageDataOut, imageSizeOut, maxSize); - coreinit::OSWaitEvent(doneEvent); + coreinit::OSWaitEvent(&doneEvent); nnResult r = asyncTask.get(); return r; } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp index b76e6d63..179d66bd 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp @@ -54,9 +54,9 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, UploadCommunityData_AsyncRequest, std::ref(req), requestUrl, requestDoneEvent.GetPointer(), pOutData, pParam); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp index 7d9220fc..307004b9 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp @@ -44,9 +44,9 @@ namespace nn InitializeOliveRequest(req); StackAllocator requestDoneEvent; - coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); + coreinit::OSInitEvent(&requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL); std::future requestRes = std::async(std::launch::async, UploadFavoriteToCommunityData_AsyncRequest, std::ref(req), requestUrl, requestDoneEvent.GetPointer(), pOutData, pParam); - coreinit::OSWaitEvent(requestDoneEvent); + coreinit::OSWaitEvent(&requestDoneEvent); return requestRes.get(); } diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp index f8ce7265..6d596acf 100644 --- a/src/Cemu/Logging/CemuLogging.cpp +++ b/src/Cemu/Logging/CemuLogging.cpp @@ -43,6 +43,7 @@ const std::map g_logging_window_mapping {LogType::CoreinitMP, "Coreinit MP"}, {LogType::CoreinitThread, "Coreinit Thread"}, {LogType::NN_NFP, "nn::nfp"}, + {LogType::NN_FP, "nn::fp"}, {LogType::GX2, "GX2"}, {LogType::SoundAPI, "Audio"}, {LogType::InputAPI, "Input"}, diff --git a/src/Cemu/Logging/CemuLogging.h b/src/Cemu/Logging/CemuLogging.h index 7d6499fe..728c8b93 100644 --- a/src/Cemu/Logging/CemuLogging.h +++ b/src/Cemu/Logging/CemuLogging.h @@ -34,6 +34,7 @@ enum class LogType : sint32 NN_PDM = 21, NN_OLV = 23, NN_NFP = 13, + NN_FP = 24, TextureReadback = 29, diff --git a/src/Cemu/napi/napi_act.cpp b/src/Cemu/napi/napi_act.cpp index b395e2b7..9716c41e 100644 --- a/src/Cemu/napi/napi_act.cpp +++ b/src/Cemu/napi/napi_act.cpp @@ -358,7 +358,7 @@ namespace NAPI std::string_view port = tokenNode.child_value("port"); std::string_view token = tokenNode.child_value("token"); - std::memset(&result.nexToken, 0, sizeof(result.nexToken)); + memset(&result.nexToken, 0, sizeof(ACTNexToken)); if (host.size() > 15) cemuLog_log(LogType::Force, "NexToken response: host field too long"); if (nex_password.size() > 64) diff --git a/src/Cemu/nex/nex.cpp b/src/Cemu/nex/nex.cpp index 317b3877..d0857507 100644 --- a/src/Cemu/nex/nex.cpp +++ b/src/Cemu/nex/nex.cpp @@ -160,11 +160,10 @@ bool nexService::isMarkedForDestruction() void nexService::callMethod(uint8 protocolId, uint32 methodId, nexPacketBuffer* parameter, void(*nexServiceResponse)(nexService* nex, nexServiceResponse_t* serviceResponse), void* custom, bool callHandlerIfError) { - // add to queue queuedRequest_t queueRequest = { 0 }; queueRequest.protocolId = protocolId; queueRequest.methodId = methodId; - queueRequest.parameterData = std::vector(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); + queueRequest.parameterData.assign(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); queueRequest.nexServiceResponse = nexServiceResponse; queueRequest.custom = custom; queueRequest.callHandlerIfError = callHandlerIfError; @@ -175,11 +174,10 @@ void nexService::callMethod(uint8 protocolId, uint32 methodId, nexPacketBuffer* void nexService::callMethod(uint8 protocolId, uint32 methodId, nexPacketBuffer* parameter, std::function cb, bool callHandlerIfError) { - // add to queue queuedRequest_t queueRequest = { 0 }; queueRequest.protocolId = protocolId; queueRequest.methodId = methodId; - queueRequest.parameterData = std::vector(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); + queueRequest.parameterData.assign(parameter->getDataPtr(), parameter->getDataPtr() + parameter->getWriteIndex()); queueRequest.nexServiceResponse = nullptr; queueRequest.cb2 = cb; queueRequest.callHandlerIfError = callHandlerIfError; diff --git a/src/Cemu/nex/nexFriends.cpp b/src/Cemu/nex/nexFriends.cpp index cf169b72..4fae8143 100644 --- a/src/Cemu/nex/nexFriends.cpp +++ b/src/Cemu/nex/nexFriends.cpp @@ -274,8 +274,7 @@ void NexFriends::handleResponse_getAllInformation(nexServiceResponse_t* response return; } NexFriends* session = (NexFriends*)nexFriends; - - nexPrincipalPreference preference(&response->data); + session->myPreference = nexPrincipalPreference(&response->data); nexComment comment(&response->data); if (response->data.hasReadOutOfBounds()) return; @@ -290,29 +289,21 @@ void NexFriends::handleResponse_getAllInformation(nexServiceResponse_t* response uint32 friendCount = response->data.readU32(); session->list_friends.resize(friendCount); for (uint32 i = 0; i < friendCount; i++) - { session->list_friends[i].readData(&response->data); - } // friend requests (outgoing) uint32 friendRequestsOutCount = response->data.readU32(); if (response->data.hasReadOutOfBounds()) - { return; - } session->list_friendReqOutgoing.resize(friendRequestsOutCount); for (uint32 i = 0; i < friendRequestsOutCount; i++) - { session->list_friendReqOutgoing[i].readData(&response->data); - } // friend requests (incoming) uint32 friendRequestsInCount = response->data.readU32(); if (response->data.hasReadOutOfBounds()) return; session->list_friendReqIncoming.resize(friendRequestsInCount); for (uint32 i = 0; i < friendRequestsInCount; i++) - { session->list_friendReqIncoming[i].readData(&response->data); - } if (response->data.hasReadOutOfBounds()) return; // blacklist @@ -336,7 +327,7 @@ void NexFriends::handleResponse_getAllInformation(nexServiceResponse_t* response if (isPreferenceInvalid) { cemuLog_log(LogType::Force, "NEX: First time login into friend account, setting up default preferences"); - session->updatePreferences(nexPrincipalPreference(1, 1, 0)); + session->updatePreferencesAsync(nexPrincipalPreference(1, 1, 0), [](RpcErrorCode err){}); } if (session->firstInformationRequest == false) @@ -377,20 +368,27 @@ bool NexFriends::requestGetAllInformation(std::function cb) return true; } -void NexFriends::handleResponse_updatePreferences(nexServiceResponse_t* response, NexFriends* nexFriends, std::function cb) -{ - // todo -} - -bool NexFriends::updatePreferences(const nexPrincipalPreference& newPreferences) +bool NexFriends::updatePreferencesAsync(nexPrincipalPreference newPreferences, std::function cb) { uint8 tempNexBufferArray[1024]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); newPreferences.writeData(&packetBuffer); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 16, &packetBuffer, std::bind(handleResponse_updatePreferences, std::placeholders::_1, this, nullptr), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 16, &packetBuffer, [this, cb, newPreferences](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + this->myPreference = newPreferences; + return cb(NexFriends::ERR_NONE); + }, true); + // TEST return true; } +void NexFriends::getMyPreference(nexPrincipalPreference& preference) +{ + preference = myPreference; +} + bool NexFriends::addProvisionalFriendByPidGuessed(uint32 principalId) { uint8 tempNexBufferArray[512]; @@ -401,6 +399,7 @@ bool NexFriends::addProvisionalFriendByPidGuessed(uint32 principalId) return true; } +// returns true once connection is established and friend list data is available bool NexFriends::isOnline() { return isCurrentlyConnected && hasData; @@ -683,7 +682,7 @@ bool NexFriends::getFriendRequestByPID(nexFriendRequest& friendRequestData, bool { friendRequestData = it; if (isIncoming) - *isIncoming = false; + *isIncoming = false; return true; } } @@ -731,7 +730,7 @@ void addProvisionalFriendHandler(nexServiceResponse_t* nexResponse, std::functio } } -bool NexFriends::addProvisionalFriend(char* name, std::function cb) +bool NexFriends::addProvisionalFriend(char* name, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -754,12 +753,11 @@ void addFriendRequestHandler(nexServiceResponse_t* nexResponse, NexFriends* nexF { // todo: Properly handle returned error code cb(NexFriends::ERR_RPC_FAILED); - // refresh the list - nexFriends->requestGetAllInformation(); + nexFriends->requestGetAllInformation(); // refresh friend list and send add/remove notifications } } -void NexFriends::addFriendRequest(uint32 pid, const char* comment, std::function cb) +void NexFriends::addFriendRequest(uint32 pid, const char* comment, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -779,72 +777,31 @@ void NexFriends::addFriendRequest(uint32 pid, const char* comment, std::function nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 5, &packetBuffer, std::bind(addFriendRequestHandler, std::placeholders::_1, this, cb), true); } -typedef struct -{ - NEXFRIENDS_CALLBACK cb; - void* customParam; - NexFriends* nexFriends; - // command specific - struct - { - nexPrincipalBasicInfo* basicInfo; - sint32 count; - }principalBaseInfo; -}nexFriendsCallInfo_t; - -void NexFriends_handleResponse_requestPrincipleBaseInfoByPID(nexService* nex, nexServiceResponse_t* response) -{ - nexFriendsCallInfo_t* callInfo = (nexFriendsCallInfo_t*)response->custom; - if (response->isSuccessful == false) - { - // handle error case - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_RPC_FAILED, callInfo->customParam); - free(callInfo); - return; - } - // process result - uint32 count = response->data.readU32(); - if (count != callInfo->principalBaseInfo.count) - { - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_UNEXPECTED_RESULT, callInfo->customParam); - free(callInfo); - return; - } - for (uint32 i = 0; i < count; i++) - { - callInfo->principalBaseInfo.basicInfo[i].readData(&response->data); - } - if (response->data.hasReadOutOfBounds()) - { - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_UNEXPECTED_RESULT, callInfo->customParam); - free(callInfo); - return; - } - // callback - callInfo->cb(callInfo->nexFriends, NexFriends::ERR_NONE, callInfo->customParam); - free(callInfo); -} - -void NexFriends::requestPrincipleBaseInfoByPID(nexPrincipalBasicInfo* basicInfo, uint32* pidList, sint32 count, NEXFRIENDS_CALLBACK cb, void* customParam) +void NexFriends::requestPrincipleBaseInfoByPID(uint32* pidList, sint32 count, const std::function basicInfo)>& cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) - { - // not connected - cb(this, ERR_NOT_CONNECTED, customParam); - return; - } + return cb(ERR_NOT_CONNECTED, {}); uint8 tempNexBufferArray[512]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(count); for(sint32 i=0; iprincipalBaseInfo.basicInfo = basicInfo; - callInfo->principalBaseInfo.count = count; - callInfo->cb = cb; - callInfo->customParam = customParam; - callInfo->nexFriends = this; - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 17, &packetBuffer, NexFriends_handleResponse_requestPrincipleBaseInfoByPID, callInfo, true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 17, &packetBuffer, [cb, count](nexServiceResponse_t* response) + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED, {}); + // process result + uint32 resultCount = response->data.readU32(); + if (resultCount != count) + return cb(NexFriends::ERR_UNEXPECTED_RESULT, {}); + std::vector nexBasicInfo; + nexBasicInfo.resize(count); + for (uint32 i = 0; i < resultCount; i++) + nexBasicInfo[i].readData(&response->data); + if (response->data.hasReadOutOfBounds()) + return cb(NexFriends::ERR_UNEXPECTED_RESULT, {}); + return cb(NexFriends::ERR_NONE, nexBasicInfo); + }, true); } void genericFriendServiceNoResponseHandler(nexServiceResponse_t* nexResponse, std::function cb) @@ -858,7 +815,7 @@ void genericFriendServiceNoResponseHandler(nexServiceResponse_t* nexResponse, st } } -void NexFriends::removeFriend(uint32 pid, std::function cb) +void NexFriends::removeFriend(uint32 pid, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -869,10 +826,20 @@ void NexFriends::removeFriend(uint32 pid, std::function cb) uint8 tempNexBufferArray[512]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(pid); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, std::bind(genericFriendServiceNoResponseHandler, std::placeholders::_1, cb), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, [this, cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + { + cb(NexFriends::ERR_NONE); + this->requestGetAllInformation(); // refresh friend list and send add/remove notifications + return; + } + }, true); } -void NexFriends::cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb) +void NexFriends::cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) { @@ -883,53 +850,63 @@ void NexFriends::cancelOutgoingProvisionalFriendRequest(uint32 pid, std::functio uint8 tempNexBufferArray[512]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(pid); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, std::bind(genericFriendServiceNoResponseHandler, std::placeholders::_1, cb), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 4, &packetBuffer, [cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + return cb(NexFriends::ERR_NONE); + }, true); } -void NexFriends::acceptFriendRequest(uint64 messageId, std::function cb) +void NexFriends::acceptFriendRequest(uint64 messageId, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) - { - // not connected - cb(ERR_NOT_CONNECTED); - return; - } + return cb(ERR_NOT_CONNECTED); uint8 tempNexBufferArray[128]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU64(messageId); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 7, &packetBuffer, std::bind(genericFriendServiceNoResponseHandler, std::placeholders::_1, cb), true); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 7, &packetBuffer, [cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + return cb(NexFriends::ERR_NONE); + }, true); } -void markFriendRequestsAsReceivedHandler(nexServiceResponse_t* nexResponse, std::function cb) -{ - if (nexResponse->isSuccessful) - cb(0); - else - { - // todo: Properly handle returned error code - cb(NexFriends::ERR_RPC_FAILED); - } -} - -void NexFriends::markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb) +void NexFriends::deleteFriendRequest(uint64 messageId, std::function cb) { if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) - { - // not connected - cb(ERR_NOT_CONNECTED); - return; - } + return cb(ERR_NOT_CONNECTED); + uint8 tempNexBufferArray[128]; + nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); + packetBuffer.writeU64(messageId); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 8, &packetBuffer, [this, cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + cb(NexFriends::ERR_NONE); + this->requestGetAllInformation(); // refresh friend list and send add/remove notifications + }, true); +} + +void NexFriends::markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb) +{ + if (nexCon == nullptr || nexCon->getState() != nexService::STATE_CONNECTED) + return cb(ERR_NOT_CONNECTED); uint8 tempNexBufferArray[1024]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); packetBuffer.writeU32(count); for(sint32 i=0; icallMethod(NEX_PROTOCOL_FRIENDS_WIIU, 10, &packetBuffer, std::bind(markFriendRequestsAsReceivedHandler, std::placeholders::_1, cb), true); -} - -void genericFriendServiceNoResponseHandlerWithoutCB(nexServiceResponse_t* nexResponse) -{ - + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 10, &packetBuffer, [cb](nexServiceResponse_t* response) -> void + { + if (!response->isSuccessful) + return cb(NexFriends::ERR_RPC_FAILED); + else + return cb(NexFriends::ERR_NONE); + }, true); } void NexFriends::updateMyPresence(nexPresenceV2& myPresence) @@ -943,7 +920,7 @@ void NexFriends::updateMyPresence(nexPresenceV2& myPresence) uint8 tempNexBufferArray[1024]; nexPacketBuffer packetBuffer(tempNexBufferArray, sizeof(tempNexBufferArray), true); myPresence.writeData(&packetBuffer); - nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 13, &packetBuffer, std::bind(genericFriendServiceNoResponseHandlerWithoutCB, std::placeholders::_1), false); + nexCon->callMethod(NEX_PROTOCOL_FRIENDS_WIIU, 13, &packetBuffer, +[](nexServiceResponse_t* nexResponse){}, false); } void NexFriends::update() diff --git a/src/Cemu/nex/nexFriends.h b/src/Cemu/nex/nexFriends.h index 16548bf3..06c75110 100644 --- a/src/Cemu/nex/nexFriends.h +++ b/src/Cemu/nex/nexFriends.h @@ -248,9 +248,9 @@ public: nexPrincipalPreference(uint8 ukn0, uint8 ukn1, uint8 ukn2) { - this->ukn0 = ukn0; - this->ukn1 = ukn1; - this->ukn2 = ukn2; + this->showOnline = ukn0; + this->showGame = ukn1; + this->blockFriendRequests = ukn2; } nexPrincipalPreference(nexPacketBuffer* pb) @@ -260,21 +260,21 @@ public: void writeData(nexPacketBuffer* pb) const override { - pb->writeU8(ukn0); - pb->writeU8(ukn1); - pb->writeU8(ukn2); + pb->writeU8(showOnline); + pb->writeU8(showGame); + pb->writeU8(blockFriendRequests); } void readData(nexPacketBuffer* pb) override { - ukn0 = pb->readU8(); - ukn1 = pb->readU8(); - ukn2 = pb->readU8(); + showOnline = pb->readU8(); + showGame = pb->readU8(); + blockFriendRequests = pb->readU8(); } public: - uint8 ukn0; - uint8 ukn1; - uint8 ukn2; + uint8 showOnline; + uint8 showGame; + uint8 blockFriendRequests; }; class nexComment : public nexType @@ -505,13 +505,12 @@ public: uint32 type; std::string msg; }; -class NexFriends; - -typedef void (*NEXFRIENDS_CALLBACK)(NexFriends* nexFriends, uint32 result, void* custom); class NexFriends { public: + using RpcErrorCode = int; // replace with enum class later + static const int ERR_NONE = 0; static const int ERR_RPC_FAILED = 1; static const int ERR_UNEXPECTED_RESULT = 2; @@ -544,27 +543,29 @@ public: int getPendingFriendRequestCount(); bool requestGetAllInformation(); - bool requestGetAllInformation(std::function cb); bool addProvisionalFriendByPidGuessed(uint32 principalId); - void acceptFriendRequest(uint64 messageId, std::function cb); - bool isOnline(); + // synchronous API (returns immediately) + bool requestGetAllInformation(std::function cb); void getFriendPIDs(uint32* pidList, uint32* pidCount, sint32 offset, sint32 count, bool includeFriendRequests); void getFriendRequestPIDs(uint32* pidList, uint32* pidCount, sint32 offset, sint32 count, bool includeIncoming, bool includeOutgoing); bool getFriendByPID(nexFriend& friendData, uint32 pid); bool getFriendRequestByPID(nexFriendRequest& friendRequestData, bool* isIncoming, uint32 searchedPid); bool getFriendRequestByMessageId(nexFriendRequest& friendRequestData, bool* isIncoming, uint64 messageId); + bool isOnline(); + void getMyPreference(nexPrincipalPreference& preference); - bool addProvisionalFriend(char* name, std::function cb); - void addFriendRequest(uint32 pid, const char* comment, std::function cb); - - void requestPrincipleBaseInfoByPID(nexPrincipalBasicInfo* basicInfo, uint32* pidList, sint32 count, NEXFRIENDS_CALLBACK cb, void* customParam); - void removeFriend(uint32 pid, std::function cb); - void cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb); - void markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb); - + // asynchronous API (data has to be requested) + bool addProvisionalFriend(char* name, std::function cb); + void addFriendRequest(uint32 pid, const char* comment, std::function cb); + void requestPrincipleBaseInfoByPID(uint32* pidList, sint32 count, const std::function basicInfo)>& cb); + void removeFriend(uint32 pid, std::function cb); + void cancelOutgoingProvisionalFriendRequest(uint32 pid, std::function cb); + void markFriendRequestsAsReceived(uint64* messageIdList, sint32 count, std::function cb); + void acceptFriendRequest(uint64 messageId, std::function cb); + void deleteFriendRequest(uint64 messageId, std::function cb); // rejecting incoming friend request (differs from blocking friend requests) + bool updatePreferencesAsync(const nexPrincipalPreference newPreferences, std::function cb); void updateMyPresence(nexPresenceV2& myPresence); - bool updatePreferences(const nexPrincipalPreference& newPreferences); void setNotificationHandler(void(*notificationHandler)(NOTIFICATION_TYPE notificationType, uint32 pid)); @@ -578,7 +579,6 @@ private: static void handleResponse_acceptFriendRequest(nexService* nex, nexServiceResponse_t* response); static void handleResponse_getAllInformation(nexServiceResponse_t* response, NexFriends* nexFriends, std::function cb); - static void handleResponse_updatePreferences(nexServiceResponse_t* response, NexFriends* nexFriends, std::function cb); void generateNotification(NOTIFICATION_TYPE notificationType, uint32 pid); void trackNotifications(); @@ -618,6 +618,7 @@ private: }auth; // local friend state nexPresenceV2 myPresence; + nexPrincipalPreference myPreference; std::recursive_mutex mtx_lists; std::vector list_friends; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 7ed3d67a..9a764593 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(CemuCommon StackAllocator.h SysAllocator.cpp SysAllocator.h + CafeString.h version.h ) diff --git a/src/Common/CafeString.h b/src/Common/CafeString.h new file mode 100644 index 00000000..45a515b1 --- /dev/null +++ b/src/Common/CafeString.h @@ -0,0 +1,73 @@ +#pragma once +#include "betype.h" +#include "util/helpers/StringHelpers.h" + +/* Helper classes to represent CafeOS strings in emulated memory */ +template +class CafeString // fixed buffer size, null-terminated, PPC char +{ + public: + bool assign(std::string_view sv) + { + if (sv.size()+1 >= N) + { + memcpy(data, sv.data(), sv.size()-1); + data[sv.size()-1] = '\0'; + return false; + } + memcpy(data, sv.data(), sv.size()); + data[sv.size()] = '\0'; + return true; + } + + uint8be data[N]; +}; + +template +class CafeWideString // fixed buffer size, null-terminated, PPC wchar_t (16bit big-endian) +{ + public: + bool assign(const uint16be* input) + { + size_t i = 0; + while(input[i]) + { + if(i >= N-1) + { + data[N-1] = 0; + return false; + } + data[i] = input[i]; + i++; + } + data[i] = 0; + return true; + } + + bool assignFromUTF8(std::string_view sv) + { + std::basic_string beStr = StringHelpers::FromUtf8(sv); + if(beStr.length() > N-1) + { + memcpy(data, beStr.data(), (N-1)*sizeof(uint16be)); + data[N-1] = 0; + return false; + } + memcpy(data, beStr.data(), beStr.length()*sizeof(uint16be)); + data[beStr.length()] = '\0'; + return true; + } + + uint16be data[N]; +}; + +namespace CafeStringHelpers +{ + static uint32 Length(const uint16be* input, uint32 maxLength) + { + uint32 i = 0; + while(input[i] && i < maxLength) + i++; + return i; + } +}; diff --git a/src/Common/StackAllocator.h b/src/Common/StackAllocator.h index 750db13f..a69b7aaa 100644 --- a/src/Common/StackAllocator.h +++ b/src/Common/StackAllocator.h @@ -29,9 +29,36 @@ public: T* GetPointer() const { return m_ptr; } uint32 GetMPTR() const { return MEMPTR(m_ptr).GetMPTR(); } uint32 GetMPTRBE() const { return MEMPTR(m_ptr).GetMPTRBE(); } - - operator T*() const { return GetPointer(); } + + T* operator&() { return GetPointer(); } + explicit operator T*() const { return GetPointer(); } explicit operator uint32() const { return GetMPTR(); } + explicit operator bool() const { return *m_ptr != 0; } + + // for arrays (count > 1) allow direct access via [] operator + template + requires (c > 1) + T& operator[](const uint32 index) + { + return m_ptr[index]; + } + + // if count is 1, then allow direct value assignment via = operator + template + requires (c == 1) + T& operator=(const T& rhs) + { + *m_ptr = rhs; + return *m_ptr; + } + + // if count is 1, then allow == and != operators + template + requires (c == 1) + bool operator==(const T& rhs) const + { + return *m_ptr == rhs; + } private: static const uint32 kStaticMemOffset = 64; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c0d975ec..3156f2de 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -2198,6 +2198,7 @@ void MainWindow::RecreateMenu() debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitMP), _("&Coreinit MP API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitMP)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThread), _("&Coreinit Thread API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThread)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_NFP), _("&NN NFP"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::NN_NFP)); + debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_FP), _("&NN FP"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::NN_FP)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::GX2), _("&GX2 API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::GX2)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::SoundAPI), _("&Audio API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::SoundAPI)); debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::InputAPI), _("&Input API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::InputAPI)); diff --git a/src/util/helpers/StringHelpers.h b/src/util/helpers/StringHelpers.h index 24e70d49..54141808 100644 --- a/src/util/helpers/StringHelpers.h +++ b/src/util/helpers/StringHelpers.h @@ -2,6 +2,7 @@ #include "boost/nowide/convert.hpp" #include +// todo - move the Cafe/PPC specific parts to CafeString.h eventually namespace StringHelpers { // convert Wii U big-endian wchar_t string to utf8 string diff --git a/src/util/highresolutiontimer/HighResolutionTimer.h b/src/util/highresolutiontimer/HighResolutionTimer.h index 12dc0751..7a545c86 100644 --- a/src/util/highresolutiontimer/HighResolutionTimer.h +++ b/src/util/highresolutiontimer/HighResolutionTimer.h @@ -36,6 +36,16 @@ public: static HighResolutionTimer now(); static HRTick getFrequency(); + static HRTick microsecondsToTicks(uint64 microseconds) + { + return microseconds * m_freq / 1000000; + } + + static uint64 ticksToMicroseconds(HRTick ticks) + { + return ticks * 1000000 / m_freq; + } + private: HighResolutionTimer(uint64 timePoint) : m_timePoint(timePoint) {};