From c0987ecc73821a7b8ef8d1b87a215b5a9c819808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 30 Jan 2026 22:42:37 +0100 Subject: [PATCH] Fake Matching2 (#3959) --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/np/np_manager.cpp | 87 +++ src/core/libraries/np/np_manager.h | 32 +- src/core/libraries/np/np_matching2.cpp | 812 +++++++++++++++++++++++++ src/core/libraries/np/np_matching2.h | 48 ++ src/core/libraries/np/np_types.h | 17 + 9 files changed, 994 insertions(+), 8 deletions(-) create mode 100644 src/core/libraries/np/np_matching2.cpp create mode 100644 src/core/libraries/np/np_matching2.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 08e8fe161..8e123f3d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,6 +584,8 @@ set(NP_LIBS src/core/libraries/np/np_error.h src/core/libraries/np/np_commerce.h src/core/libraries/np/np_manager.cpp src/core/libraries/np/np_manager.h + src/core/libraries/np/np_matching2.cpp + src/core/libraries/np/np_matching2.h src/core/libraries/np/np_score.cpp src/core/libraries/np/np_score.h src/core/libraries/np/np_trophy.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 85946e9c4..9a3fe0aa1 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -107,6 +107,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, NpCommon) \ SUB(Lib, NpCommerce) \ SUB(Lib, NpManager) \ + SUB(Lib, NpMatching2) \ SUB(Lib, NpScore) \ SUB(Lib, NpTrophy) \ SUB(Lib, NpTus) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 127377115..9e176c698 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -74,6 +74,7 @@ enum class Class : u8 { Lib_NpCommerce, ///< The LibSceNpCommerce implementation Lib_NpAuth, ///< The LibSceNpAuth implementation Lib_NpManager, ///< The LibSceNpManager implementation + Lib_NpMatching2, ///< The LibSceNpMatching2 implementation Lib_NpScore, ///< The LibSceNpScore implementation Lib_NpTrophy, ///< The LibSceNpTrophy implementation Lib_NpTus, ///< The LibSceNpTus implementation diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 7e8edccec..67c3d4b7d 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -35,6 +35,7 @@ #include "core/libraries/np/np_commerce.h" #include "core/libraries/np/np_common.h" #include "core/libraries/np/np_manager.h" +#include "core/libraries/np/np_matching2.h" #include "core/libraries/np/np_partner.h" #include "core/libraries/np/np_party.h" #include "core/libraries/np/np_profile_dialog.h" @@ -99,6 +100,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Np::NpCommerce::RegisterLib(sym); Libraries::Np::NpCommon::RegisterLib(sym); Libraries::Np::NpManager::RegisterLib(sym); + Libraries::Np::NpMatching2::RegisterLib(sym); Libraries::Np::NpScore::RegisterLib(sym); Libraries::Np::NpTrophy::RegisterLib(sym); Libraries::Np::NpWebApi::RegisterLib(sym); diff --git a/src/core/libraries/np/np_manager.cpp b/src/core/libraries/np/np_manager.cpp index ebc940bf3..229ae33af 100644 --- a/src/core/libraries/np/np_manager.cpp +++ b/src/core/libraries/np/np_manager.cpp @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include +#include #include "common/config.h" #include "common/logging/log.h" @@ -17,6 +19,9 @@ static bool g_signed_in = false; static s32 g_active_requests = 0; static std::mutex g_request_mutex; +static std::map> g_np_callbacks; +static std::mutex g_np_callbacks_mutex; + // Internal types for storing request-related information enum class NpRequestState { None = 0, @@ -665,6 +670,19 @@ s32 PS4_SYSV_ABI sceNpGetState(Libraries::UserService::OrbisUserServiceUserId us return ORBIS_OK; } +s32 PS4_SYSV_ABI +sceNpGetUserIdByAccountId(u64 account_id, Libraries::UserService::OrbisUserServiceUserId* user_id) { + if (user_id == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + if (!g_signed_in) { + return ORBIS_NP_ERROR_SIGNED_OUT; + } + *user_id = 1; + LOG_DEBUG(Lib_NpManager, "userid({}) = {}", account_id, *user_id); + return ORBIS_OK; +} + s32 PS4_SYSV_ABI sceNpHasSignedUp(Libraries::UserService::OrbisUserServiceUserId user_id, bool* has_signed_up) { LOG_DEBUG(Lib_NpManager, "called"); @@ -682,8 +700,22 @@ struct NpStateCallbackForNpToolkit { NpStateCallbackForNpToolkit NpStateCbForNp; +struct NpStateCallback { + std::variant func; + void* userdata; +}; + +NpStateCallback NpStateCb; + s32 PS4_SYSV_ABI sceNpCheckCallback() { LOG_DEBUG(Lib_NpManager, "(STUBBED) called"); + + std::scoped_lock lk{g_np_callbacks_mutex}; + + for (auto i : g_np_callbacks) { + (i.second)(); + } + return ORBIS_OK; } @@ -692,6 +724,40 @@ s32 PS4_SYSV_ABI sceNpCheckCallbackForLib() { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceNpRegisterStateCallback(OrbisNpStateCallback callback, void* userdata) { + static s32 id = 0; + LOG_ERROR(Lib_NpManager, "(STUBBED) called, userdata = {}", userdata); + NpStateCb.func = callback; + NpStateCb.userdata = userdata; + + return id; +} + +s32 PS4_SYSV_ABI sceNpRegisterStateCallbackA(OrbisNpStateCallbackA callback, void* userdata) { + static s32 id = 0; + LOG_ERROR(Lib_NpManager, "(STUBBED) called, userdata = {}", userdata); + NpStateCb.func = callback; + NpStateCb.userdata = userdata; + + return id; +} + +struct NpReachabilityStateCallback { + OrbisNpReachabilityStateCallback func; + void* userdata; +}; + +NpReachabilityStateCallback NpReachabilityCb; + +s32 PS4_SYSV_ABI sceNpRegisterNpReachabilityStateCallback(OrbisNpReachabilityStateCallback callback, + void* userdata) { + static s32 id = 0; + LOG_ERROR(Lib_NpManager, "(STUBBED) called"); + NpReachabilityCb.func = callback; + NpReachabilityCb.userdata = userdata; + return id; +} + s32 PS4_SYSV_ABI sceNpRegisterStateCallbackForToolkit(OrbisNpStateCallbackForNpToolkit callback, void* userdata) { static s32 id = 0; @@ -701,6 +767,22 @@ s32 PS4_SYSV_ABI sceNpRegisterStateCallbackForToolkit(OrbisNpStateCallbackForNpT return id; } +void RegisterNpCallback(std::string key, std::function cb) { + std::scoped_lock lk{g_np_callbacks_mutex}; + + LOG_DEBUG(Lib_NpManager, "registering callback processing for {}", key); + + g_np_callbacks.emplace(key, cb); +} + +void DeregisterNpCallback(std::string key) { + std::scoped_lock lk{g_np_callbacks_mutex}; + + LOG_DEBUG(Lib_NpManager, "deregistering callback processing for {}", key); + + g_np_callbacks.erase(key); +} + void RegisterLib(Core::Loader::SymbolsResolver* sym) { g_signed_in = Config::getPSNSignedIn(); @@ -739,9 +821,14 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("p-o74CnoNzY", "libSceNpManager", 1, "libSceNpManager", sceNpGetNpId); LIB_FUNCTION("XDncXQIJUSk", "libSceNpManager", 1, "libSceNpManager", sceNpGetOnlineId); LIB_FUNCTION("eQH7nWPcAgc", "libSceNpManager", 1, "libSceNpManager", sceNpGetState); + LIB_FUNCTION("VgYczPGB5ss", "libSceNpManager", 1, "libSceNpManager", sceNpGetUserIdByAccountId); LIB_FUNCTION("Oad3rvY-NJQ", "libSceNpManager", 1, "libSceNpManager", sceNpHasSignedUp); LIB_FUNCTION("3Zl8BePTh9Y", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallback); LIB_FUNCTION("JELHf4xPufo", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallbackForLib); + LIB_FUNCTION("VfRSmPmj8Q8", "libSceNpManager", 1, "libSceNpManager", + sceNpRegisterStateCallback); + LIB_FUNCTION("hw5KNqAAels", "libSceNpManager", 1, "libSceNpManager", + sceNpRegisterNpReachabilityStateCallback); LIB_FUNCTION("JELHf4xPufo", "libSceNpManagerForToolkit", 1, "libSceNpManager", sceNpCheckCallbackForLib); LIB_FUNCTION("0c7HbXRKUt4", "libSceNpManagerForToolkit", 1, "libSceNpManager", diff --git a/src/core/libraries/np/np_manager.h b/src/core/libraries/np/np_manager.h index 59864c173..078fa804a 100644 --- a/src/core/libraries/np/np_manager.h +++ b/src/core/libraries/np/np_manager.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "common/types.h" #include "core/libraries/np/np_error.h" #include "core/libraries/np/np_types.h" @@ -23,20 +25,28 @@ enum class OrbisNpState : u32 { SignedIn = 2, }; -using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)( - Libraries::UserService::OrbisUserServiceUserId userId, OrbisNpState state, void* userdata); - -enum class OrbisNpGamePresenseStatus { - Offline = 0, - Online = 1, -}; - enum class OrbisNpReachabilityState { Unavailable = 0, Available = 1, Reachable = 2, }; +using OrbisNpStateCallback = + PS4_SYSV_ABI void (*)(Libraries::UserService::OrbisUserServiceUserId userId, OrbisNpState state, + OrbisNpId* npId, void* userdata); +using OrbisNpStateCallbackA = PS4_SYSV_ABI void (*)( + Libraries::UserService::OrbisUserServiceUserId userId, OrbisNpState state, void* userdata); +using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)( + Libraries::UserService::OrbisUserServiceUserId userId, OrbisNpState state, void* userdata); +using OrbisNpReachabilityStateCallback = + PS4_SYSV_ABI void (*)(Libraries::UserService::OrbisUserServiceUserId userId, + OrbisNpReachabilityState state, void* userdata); + +enum class OrbisNpGamePresenseStatus { + Offline = 0, + Online = 1, +}; + struct OrbisNpCountryCode { char country_code[2]; char end; @@ -80,5 +90,11 @@ struct OrbisNpCreateAsyncRequestParameter { u8 padding[4]; }; +void RegisterNpCallback(std::string key, std::function cb); +void DeregisterNpCallback(std::string key); + +s32 PS4_SYSV_ABI sceNpGetOnlineId(Libraries::UserService::OrbisUserServiceUserId user_id, + OrbisNpOnlineId* online_id); + void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Np::NpManager diff --git a/src/core/libraries/np/np_matching2.cpp b/src/core/libraries/np/np_matching2.cpp new file mode 100644 index 000000000..cf4faea39 --- /dev/null +++ b/src/core/libraries/np/np_matching2.cpp @@ -0,0 +1,812 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/config.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np/np_manager.h" +#include "core/libraries/np/np_matching2.h" +#include "core/libraries/np/np_types.h" +#include "core/libraries/system/userservice.h" + +namespace Libraries::Np::NpMatching2 { + +static bool g_initialized = false; +static OrbisNpMatching2ContextId contextId = 1; + +struct NpMatching2ContextEvent { + OrbisNpMatching2ContextId contextId; + OrbisNpMatching2Event event; + OrbisNpMatching2EventCause cause; + int errorCode; +}; + +struct NpMatching2LobbyEvent { + OrbisNpMatching2ContextId contextId; + OrbisNpMatching2LobbyId lobbyId; + OrbisNpMatching2Event event; + void* data; +}; + +struct NpMatching2RoomEvent { + OrbisNpMatching2ContextId contextId; + OrbisNpMatching2RoomId roomId; + OrbisNpMatching2Event event; + void* data; +}; + +static std::mutex g_events_mutex; +static std::deque g_ctx_events; +static std::deque g_lobby_events; +static std::deque g_room_events; +static std::mutex g_responses_mutex; +static std::deque> g_responses; + +struct OrbisNpMatching2CreateContextParameter { + Libraries::Np::OrbisNpId* npId; + void* npCommunicationId; + void* npPassphrase; + Libraries::Np::OrbisNpServiceLabel serviceLabel; + u64 size; +}; + +static_assert(sizeof(OrbisNpMatching2CreateContextParameter) == 0x28); + +int PS4_SYSV_ABI sceNpMatching2CreateContext(const OrbisNpMatching2CreateContextParameter* param, + OrbisNpMatching2ContextId* ctxId) { + LOG_DEBUG(Lib_NpMatching2, "called, npId = {}, serviceLabel = {}, size = {}", + param->npId->handle.data, param->serviceLabel, param->size); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!param || param->size != 0x28 || !ctxId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + *ctxId = contextId++; + + return ORBIS_OK; +} + +struct OrbisNpMatching2CreateContextParameterA { + Libraries::UserService::OrbisUserServiceUserId userId; + Libraries::Np::OrbisNpServiceLabel serviceLabel; + u64 size; +}; + +static_assert(sizeof(OrbisNpMatching2CreateContextParameterA) == 16); + +int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContextParameterA* param, + OrbisNpMatching2ContextId* ctxId) { + LOG_DEBUG(Lib_NpMatching2, "called, userId = {}, serviceLabel = {}, size = {}", param->userId, + param->serviceLabel, param->size); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!param || param->size != 0x10 || !ctxId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + *ctxId = contextId++; + + return ORBIS_OK; +} + +using OrbisNpMatching2RequestCallback = PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId, + OrbisNpMatching2RequestId, + OrbisNpMatching2Event, int, void*, + void*); + +struct OrbisNpMatching2RequestOptParam { + OrbisNpMatching2RequestCallback callback; + void* arg; + u32 timeout; + u16 appId; + u8 dummy[2]; +}; + +static std::optional defaultRequestOptParam = std::nullopt; + +auto GetOptParam(OrbisNpMatching2RequestOptParam* requestOpt) { + return requestOpt ? *requestOpt + : (defaultRequestOptParam ? defaultRequestOptParam + : std::optional{}); +} + +struct OrbisNpMatching2CreateJoinRoomRequestA { + u16 maxSlot; + OrbisNpMatching2TeamId teamId; + u8 pad[5]; + OrbisNpMatching2Flags flags; + OrbisNpMatching2WorldId worldId; + OrbisNpMatching2LobbyId lobbyId; + void* roomPasswd; + void* passwdSlotMask; + void* groupConfig; + u64 groupConfigs; + void* joinGroupLabel; + Libraries::Np::OrbisNpAccountId* allowedUser; + u64 allowedUsers; + Libraries::Np::OrbisNpAccountId* blockedUser; + u64 blockedUsers; + void* internalBinAttr; + u64 internalBinAttrs; + void* externalSearchIntAttr; + u64 externalSearchIntAttrs; + void* externalSearchBinAttr; + u64 externalSearchBinAttrs; + void* externalBinAttr; + u64 externalBinAttrs; + void* memberInternalBinAttr; + u64 memberInternalBinAttrs; + void* signalingParam; +}; + +static_assert(sizeof(OrbisNpMatching2CreateJoinRoomRequestA) == 184); + +struct OrbisNpMatching2RoomDataInternal { + u16 publicSlots; + u16 privateSlots; + u16 openPublicSlots; + u16 openPrivateSlots; + u16 maxSlot; + OrbisNpMatching2ServerId serverId; + OrbisNpMatching2WorldId worldId; + OrbisNpMatching2LobbyId lobbyId; + OrbisNpMatching2RoomId roomId; + u64 passwdSlotMask; + u64 joinedSlotMask; + void* roomGroup; + u64 roomGroups; + OrbisNpMatching2Flags flags; + u8 pad[4]; + void* internalBinAttr; + u64 internalBinAttrs; +}; + +struct OrbisNpMatching2RoomMemberDataInternalA { + OrbisNpMatching2RoomMemberDataInternalA* next; + u64 joinDateTicks; + Libraries::Np::OrbisNpPeerAddressA user; + Libraries::Np::OrbisNpOnlineId onlineId; + u8 pad[4]; + OrbisNpMatching2RoomMemberId memberId; + OrbisNpMatching2TeamId teamId; + OrbisNpMatching2NatType natType; + OrbisNpMatching2Flags flags; + void* roomGroup; + void* roomMemberInternalBinAttr; + u64 roomMemberInternalBinAttrs; +}; + +struct OrbisNpMatching2RoomMemberDataInternalListA { + OrbisNpMatching2RoomMemberDataInternalA* members; + u64 membersNum; + OrbisNpMatching2RoomMemberDataInternalA* me; + OrbisNpMatching2RoomMemberDataInternalA* owner; +}; + +struct OrbisNpMatching2CreateJoinRoomResponseA { + OrbisNpMatching2RoomDataInternal* roomData; + OrbisNpMatching2RoomMemberDataInternalListA members; +}; + +int PS4_SYSV_ABI sceNpMatching2CreateJoinRoomA(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2CreateJoinRoomRequestA* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + LOG_DEBUG(Lib_NpMatching2, + "maxSlot = {}, teamId = {}, worldId = {}, lobbyId = {}, groupConfig = {}, " + "joinGroupLabel = {}", + request->maxSlot, request->teamId, request->worldId, request->lobbyId, + request->groupConfig, request->joinGroupLabel); + + static OrbisNpMatching2RequestId id = 10; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + std::scoped_lock lk{g_responses_mutex}; + auto reqIdCopy = *requestId; + auto requestCopy = *request; + g_responses.emplace_back([=]() { + Libraries::Np::OrbisNpOnlineId onlineId{}; + if (NpManager::sceNpGetOnlineId(1, &onlineId) != ORBIS_OK) { + return; + } + + OrbisNpMatching2RoomMemberDataInternalA me{ + nullptr, + 0, + {0xace104e, Libraries::Np::OrbisNpPlatformType::ORBIS_NP_PLATFORM_TYPE_PS4}, + onlineId, + {0, 0, 0, 0}, + 1, + requestCopy.teamId, + 1, + 0, + nullptr, + nullptr, + 0}; + OrbisNpMatching2RoomDataInternal room{requestCopy.maxSlot, + 0, + static_cast(requestCopy.maxSlot - 1u), + 0, + 15, + 0xac, + requestCopy.worldId, + requestCopy.lobbyId, + 0x10, + 0, + 0, + nullptr, + 0, + 0, + {0, 0, 0, 0}, + nullptr, + 0}; + OrbisNpMatching2CreateJoinRoomResponseA resp{&room, {&me, 1, &me, &me}}; + optParam->callback(ctxId, reqIdCopy, + ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM_A, 0, &resp, + optParam->arg); + }); + } + return ORBIS_OK; +} + +using OrbisNpMatching2ContextCallback = PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId contextId, + OrbisNpMatching2Event event, + OrbisNpMatching2EventCause cause, + int errorCode, void* userdata); + +std::function npMatching2ContextCallback = nullptr; + +int PS4_SYSV_ABI sceNpMatching2RegisterContextCallback(OrbisNpMatching2ContextCallback callback, + void* userdata) { + LOG_DEBUG(Lib_NpMatching2, "called, userdata = {}", userdata); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + + npMatching2ContextCallback = [callback, userdata](auto arg) { + callback(arg->contextId, arg->event, arg->cause, arg->errorCode, userdata); + }; + + return ORBIS_OK; +} + +using OrbisNpMatching2LobbyEventCallback = + PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId contextId, OrbisNpMatching2LobbyId lobbyId, + OrbisNpMatching2Event event, void* data, void* userdata); + +std::function npMatching2LobbyCallback = nullptr; + +int PS4_SYSV_ABI sceNpMatching2RegisterLobbyEventCallback( + OrbisNpMatching2ContextId ctxId, OrbisNpMatching2LobbyEventCallback callback, void* userdata) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, userdata); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + + npMatching2LobbyCallback = [callback, userdata](auto arg) { + callback(arg->contextId, arg->lobbyId, arg->event, arg->data, userdata); + }; + + return ORBIS_OK; +} + +using OrbisNpMatching2RoomEventCallback = PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId contextId, + OrbisNpMatching2RoomId roomId, + OrbisNpMatching2Event event, + void* data, void* userdata); + +std::function npMatching2RoomCallback = nullptr; + +int PS4_SYSV_ABI sceNpMatching2RegisterRoomEventCallback(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2RoomEventCallback callback, + void* userdata) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, userdata); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + + npMatching2RoomCallback = [callback, userdata](auto arg) { + callback(arg->contextId, arg->roomId, arg->event, arg->data, userdata); + }; + + return ORBIS_OK; +} + +struct OrbisNpMatching2SignalingEvent { + OrbisNpMatching2ContextId contextId; + OrbisNpMatching2RoomId roomId; + OrbisNpMatching2RoomMemberId roomMemberId; + OrbisNpMatching2Event event; + int errorCode; +}; + +using OrbisNpMatching2SignalingCallback = + PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId contextId, OrbisNpMatching2RoomId roomId, + OrbisNpMatching2RoomMemberId roomMemberId, OrbisNpMatching2Event event, + int errorCode, void* userdata); + +std::function npMatching2SignalingCallback = nullptr; + +int PS4_SYSV_ABI sceNpMatching2RegisterSignalingCallback(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2SignalingCallback callback, + void* userdata) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, userdata); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + + npMatching2SignalingCallback = [callback, userdata](auto arg) { + callback(arg->contextId, arg->roomId, arg->roomMemberId, arg->event, arg->errorCode, + userdata); + }; + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMatching2ContextStart(OrbisNpMatching2ContextId ctxId, u64 timeout) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, timeout = {}", ctxId, timeout); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + + std::scoped_lock lk{g_events_mutex}; + if (Config::getIsConnectedToNetwork() && Config::getPSNSignedIn()) { + g_ctx_events.emplace_back(ctxId, ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED, + ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0); + } else { + // error confirmed with a real console disconnected from the internet + constexpr int ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT = 0x804101e2; + g_ctx_events.emplace_back(ctxId, ORBIS_NP_MATCHING2_CONTEXT_EVENT_START_OVER, + ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ERROR, + ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT); + } + + return ORBIS_OK; +} + +void ProcessEvents() { + { + std::scoped_lock lk{g_events_mutex}; + + if (npMatching2ContextCallback) { + while (!g_ctx_events.empty()) { + npMatching2ContextCallback(&g_ctx_events.front()); + g_ctx_events.pop_front(); + } + } + if (npMatching2LobbyCallback) { + while (!g_lobby_events.empty()) { + npMatching2LobbyCallback(&g_lobby_events.front()); + g_lobby_events.pop_front(); + } + } + if (npMatching2RoomCallback) { + while (!g_room_events.empty()) { + npMatching2RoomCallback(&g_room_events.front()); + g_room_events.pop_front(); + } + } + } + + std::scoped_lock lk{g_responses_mutex}; + while (!g_responses.empty()) { + (g_responses.front())(); + g_responses.pop_front(); + } +} + +struct OrbisNpMatching2InitializeParameter { + u64 poolSize; + // +}; + +int PS4_SYSV_ABI sceNpMatching2Initialize(OrbisNpMatching2InitializeParameter* param) { + LOG_DEBUG(Lib_NpMatching2, "called"); + + if (g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_ALREADY_INITIALIZED; + } + + g_initialized = true; + Libraries::Np::NpManager::RegisterNpCallback("NpMatching2", ProcessEvents); + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMatching2Terminate() { + LOG_DEBUG(Lib_NpMatching2, "called"); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + + g_initialized = false; + Libraries::Np::NpManager::DeregisterNpCallback("NpMatching2"); + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMatching2SetDefaultRequestOptParam( + OrbisNpMatching2ContextId ctxId, OrbisNpMatching2RequestOptParam* requestOpt) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}", ctxId); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!requestOpt) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + defaultRequestOptParam = *requestOpt; + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMatching2GetServerId(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2ServerId* serverId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}", ctxId); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!serverId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + *serverId = 0xac; + + return ORBIS_OK; +} + +struct OrbisNpMatching2GetWorldInfoListRequest { + OrbisNpMatching2ServerId serverId; +}; + +struct OrbisNpMatching2World { + OrbisNpMatching2World* next; + OrbisNpMatching2WorldId worldId; + u32 lobbiesNum; + u32 maxLobbyMembersNum; + u32 lobbyMembersNum; + u32 roomsNum; + u32 roomMembersNum; + u8 pad[3]; +}; + +struct OrbisNpMatching2GetWorldInfoListResponse { + OrbisNpMatching2World* world; + u64 worldNum; +}; + +int PS4_SYSV_ABI sceNpMatching2GetWorldInfoList(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2GetWorldInfoListRequest* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, request.serverId = {}, requestOpt = {}", ctxId, + request ? request->serverId : 0xFFFF, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + static OrbisNpMatching2RequestId id = 1; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + auto reqIdCopy = *requestId; + std::scoped_lock lk{g_responses_mutex}; + g_responses.emplace_back([=]() { + OrbisNpMatching2World w{nullptr, 1, 10, 0, 10, 0, {}}; + OrbisNpMatching2GetWorldInfoListResponse resp{&w, 1}; + optParam->callback(ctxId, reqIdCopy, + ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST, 0, &resp, + optParam->arg); + }); + } + + return ORBIS_OK; +} + +struct OrbisNpMatching2PresenceOptionData { + u8 data[16]; + u64 len; +}; + +struct OrbisNpMatching2LeaveRoomRequest { + OrbisNpMatching2RoomId roomId; + OrbisNpMatching2PresenceOptionData optData; +}; + +int PS4_SYSV_ABI sceNpMatching2LeaveRoom(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2LeaveRoomRequest* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + static OrbisNpMatching2RequestId id = 500; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + std::scoped_lock lk{g_responses_mutex}; + auto reqIdCopy = *requestId; + g_responses.emplace_back([=]() { + optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM, 0, + nullptr, optParam->arg); + }); + } + + return ORBIS_OK; +} + +struct OrbisNpMatching2RangeFilter { + u32 start; + u32 max; +}; + +struct OrbisNpMatching2SearchRoomRequest { + int option; + OrbisNpMatching2WorldId worldId; + OrbisNpMatching2LobbyId lobbyId; + OrbisNpMatching2RangeFilter rangeFilter; + OrbisNpMatching2Flags flags1; + OrbisNpMatching2Flags flags2; + void* intFilter; + u64 intFilters; + void* binFilter; + u64 binFilters; + OrbisNpMatching2AttributeId* attr; + u64 attrs; +}; + +struct OrbisNpMatching2Range { + u32 start; + u32 total; + u32 results; + u8 pad[4]; +}; + +struct OrbisNpMatching2SearchRoomResponseA { + OrbisNpMatching2Range range; + void* roomDataExt; +}; + +int PS4_SYSV_ABI sceNpMatching2SearchRoom(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2SearchRoomRequest* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + static OrbisNpMatching2RequestId id = 1; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + std::scoped_lock lk{g_responses_mutex}; + auto reqIdCopy = *requestId; + auto requestCopy = *request; + g_responses.emplace_back([=]() { + OrbisNpMatching2SearchRoomResponseA resp{{0, 0, 0, {}}, nullptr}; + optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM_A, 0, + &resp, optParam->arg); + }); + } + + return ORBIS_OK; +} + +struct OrbisNpMatching2SetUserInfoRequest { + OrbisNpMatching2ServerId serverId; + u8 padding[6]; + void* userBinAttr; + u64 userBinAttrs; +}; + +int PS4_SYSV_ABI sceNpMatching2SetUserInfo(OrbisNpMatching2ContextId ctxId, + OrbisNpMatching2SetUserInfoRequest* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + static OrbisNpMatching2RequestId id = 100; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + std::scoped_lock lk{g_responses_mutex}; + auto reqIdCopy = *requestId; + g_responses.emplace_back([=]() { + optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_USER_INFO, 0, + nullptr, optParam->arg); + }); + } + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMatching2SendRoomMessage(OrbisNpMatching2ContextId ctxId, void* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + static OrbisNpMatching2RequestId id = 1000; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + std::scoped_lock lk{g_responses_mutex}; + auto reqIdCopy = *requestId; + g_responses.emplace_back([=]() { + optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_SEND_ROOM_MESSAGE, + 0, nullptr, optParam->arg); + }); + } + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMatching2SetRoomDataExternal(OrbisNpMatching2ContextId ctxId, void* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + static OrbisNpMatching2RequestId id = 800; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + std::scoped_lock lk{g_responses_mutex}; + auto reqIdCopy = *requestId; + g_responses.emplace_back([=]() { + optParam->callback(ctxId, reqIdCopy, + ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL, 0, nullptr, + optParam->arg); + }); + } + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceNpMatching2SetRoomDataInternal(OrbisNpMatching2ContextId ctxId, void* request, + OrbisNpMatching2RequestOptParam* requestOpt, + OrbisNpMatching2RequestId* requestId) { + LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt)); + + if (!g_initialized) { + return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED; + } + if (!request || !requestId) { + return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT; + } + + static OrbisNpMatching2RequestId id = 200; + *requestId = id++; + + if (auto optParam = GetOptParam(requestOpt); optParam) { + LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout, + optParam->appId); + std::scoped_lock lk{g_responses_mutex}; + auto reqIdCopy = *requestId; + g_responses.emplace_back([=]() { + optParam->callback(ctxId, reqIdCopy, + ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_INTERNAL, 0, nullptr, + optParam->arg); + }); + } + + return ORBIS_OK; +} + +void RegisterLib(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("10t3e5+JPnU", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2Initialize); + LIB_FUNCTION("Mqp3lJ+sjy4", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2Terminate); + LIB_FUNCTION("YfmpW719rMo", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2CreateContext); + LIB_FUNCTION("ajvzc8e2upo", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2CreateContextA); + LIB_FUNCTION("V6KSpKv9XJE", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2CreateJoinRoomA); + LIB_FUNCTION("fQQfP87I7hs", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2RegisterContextCallback); + LIB_FUNCTION("4Nj7u5B5yCA", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2RegisterLobbyEventCallback); + LIB_FUNCTION("p+2EnxmaAMM", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2RegisterRoomEventCallback); + LIB_FUNCTION("0UMeWRGnZKA", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2RegisterSignalingCallback); + LIB_FUNCTION("7vjNQ6Z1op0", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2ContextStart); + LIB_FUNCTION("LhCPctIICxQ", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2GetServerId); + LIB_FUNCTION("rJNPJqDCpiI", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2GetWorldInfoList); + LIB_FUNCTION("BD6kfx442Do", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2LeaveRoom); + LIB_FUNCTION("+8e7wXLmjds", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2SetDefaultRequestOptParam); + LIB_FUNCTION("VqZX7POg2Mk", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2SearchRoom); + LIB_FUNCTION("Iw2h0Jrrb5U", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2SendRoomMessage); + LIB_FUNCTION("meEjIdbjAA0", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2SetUserInfo); + LIB_FUNCTION("q7GK98-nYSE", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2SetRoomDataExternal); + LIB_FUNCTION("S9D8JSYIrjE", "libSceNpMatching2", 1, "libSceNpMatching2", + sceNpMatching2SetRoomDataInternal); +}; + +} // namespace Libraries::Np::NpMatching2 \ No newline at end of file diff --git a/src/core/libraries/np/np_matching2.h b/src/core/libraries/np/np_matching2.h new file mode 100644 index 000000000..6f7fca900 --- /dev/null +++ b/src/core/libraries/np/np_matching2.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Np::NpMatching2 { + +using OrbisNpMatching2AttributeId = u16; +using OrbisNpMatching2ContextId = u16; +using OrbisNpMatching2Event = u16; +using OrbisNpMatching2EventCause = u8; +using OrbisNpMatching2Flags = u32; +using OrbisNpMatching2LobbyId = u64; +using OrbisNpMatching2NatType = u8; +using OrbisNpMatching2RequestId = u16; +using OrbisNpMatching2RoomId = u64; +using OrbisNpMatching2RoomMemberId = u16; +using OrbisNpMatching2ServerId = u16; +using OrbisNpMatching2TeamId = u8; +using OrbisNpMatching2WorldId = u32; + +constexpr int ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED = 0x80550c01; +constexpr int ORBIS_NP_MATCHING2_ERROR_ALREADY_INITIALIZED = 0x80550c02; +constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT = 0x80550c15; + +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM = 0x0101; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM_A = 0x7101; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST = 0x0002; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM_A = 0x7106; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM = 0x0103; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SEND_ROOM_MESSAGE = 0x0108; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL = 0x0004; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_INTERNAL = 0x1106; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_USER_INFO = 0x0007; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_CONTEXT_EVENT_START_OVER = 0x6F01; +constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED = 0x6F02; + +constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ERROR = 10; +constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION = 11; + +void RegisterLib(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Np::NpMatching2 \ No newline at end of file diff --git a/src/core/libraries/np/np_types.h b/src/core/libraries/np/np_types.h index 8388ae47f..cc37b5a3d 100644 --- a/src/core/libraries/np/np_types.h +++ b/src/core/libraries/np/np_types.h @@ -9,6 +9,8 @@ // For structs and constants shared between multiple Np libraries. namespace Libraries::Np { +using OrbisNpAccountId = u64; + constexpr s32 ORBIS_NP_ONLINEID_MAX_LENGTH = 16; struct OrbisNpOnlineId { @@ -43,4 +45,19 @@ struct OrbisNpIdToken { u8 padding[7]; }; +using OrbisNpServiceLabel = u32; + +enum class OrbisNpPlatformType : s32 { + ORBIS_NP_PLATFORM_TYPE_NONE = 0, + ORBIS_NP_PLATFORM_TYPE_PS3 = 1, + ORBIS_NP_PLATFORM_TYPE_VITA = 2, + ORBIS_NP_PLATFORM_TYPE_PS4 = 3, +}; + +struct OrbisNpPeerAddressA { + OrbisNpAccountId accountId; + OrbisNpPlatformType platformType; + u8 padding[4]; +}; + }; // namespace Libraries::Np \ No newline at end of file