From 1dc45ab6b3b18e09566bf16215b2f552e8da84d1 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 7 Feb 2026 21:05:47 +0200 Subject: [PATCH] npWebApi (#3878) * added function parameters * added logging * more logging * added error codes file * sceNpWebApiCreateExtdPushEventFilter some re * added np_web_api_internal * more np_web_api_internal definations * Initial types cleanup * Basic library context handling. Followed decomp closely, using standard library classes where possible to simplify code. * Fix params to sceNpWebApiCreateContext * Context logic fixes * User contexts * Clang * sceNpWebApiVshInitialize * Better initialization * Request creation logic * Some cleanup * sceNpWebApiAbortRequest, sceNpWebApiDeleteRequest * SendRequest functions * formatting * Update terminateContext and deleteUserContext Addressing some unimplemented bits now that I have requests and user contexts here. * Copyright * sceNpWebApiCreateHandle, sceNpWebApiDeleteHandle, sceNpWebApiAbortHandle also some bugfixing * Extended push event filter * abort handles in terminateContext * Other push event filter types * Register callbacks * unregister callbacks * oops * One final update to deleteContext * Logging changes * Bug fixes Fixes memory leaks, pretty sure these are the only places where that was an issue. * sceNpWebApiCheckTimeout * Handle and request timeouts * Oops * Push event filter parameters Tested with Assassin's Creed Unity, seems to be correct. * Service push event filter parameters Tested again with Assassin's Creed Unity, seems to work fine. Also fixed some code bugs I noticed, and removed an unnecessary part of my internal structs * Stub implementation for createUserContextWithOnlineId Might need a PSN check to be properly accurate, not sure. * added sceNpWebApiGetHttpStatusCode * opps * opss 2 * sceNpWebApiReadData * clang * Fix context ids, user context ids, and request ids Overlooked how these ids are actually calculated. * Additional PSN checks Seems creating any form of push event filter with an np service name fails when you're not connected to PSN. Not sure of the actual cause yet, but given the error code, it's related to sceNpManagerIntGetUserList. * compile fix --------- Co-authored-by: Stephen Miller Co-authored-by: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> --- CMakeLists.txt | 3 + src/common/elf_info.h | 1 + src/core/libraries/network/http.cpp | 4 +- src/core/libraries/network/http.h | 2 +- src/core/libraries/np/np_common.h | 2 +- src/core/libraries/np/np_matching2.cpp | 2 +- src/core/libraries/np/np_types.h | 18 +- src/core/libraries/np/np_web_api.cpp | 777 ++++++--- src/core/libraries/np/np_web_api.h | 214 +-- src/core/libraries/np/np_web_api_error.h | 36 + src/core/libraries/np/np_web_api_internal.cpp | 1534 +++++++++++++++++ src/core/libraries/np/np_web_api_internal.h | 301 ++++ 12 files changed, 2543 insertions(+), 351 deletions(-) create mode 100644 src/core/libraries/np/np_web_api_error.h create mode 100644 src/core/libraries/np/np_web_api_internal.cpp create mode 100644 src/core/libraries/np/np_web_api_internal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 254d7059c..8cef2df24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -598,6 +598,9 @@ set(NP_LIBS src/core/libraries/np/np_error.h src/core/libraries/np/trophy_ui.h src/core/libraries/np/np_web_api.cpp src/core/libraries/np/np_web_api.h + src/core/libraries/np/np_web_api_error.h + src/core/libraries/np/np_web_api_internal.cpp + src/core/libraries/np/np_web_api_internal.h src/core/libraries/np/np_web_api2.cpp src/core/libraries/np/np_web_api2.h src/core/libraries/np/np_party.cpp diff --git a/src/common/elf_info.h b/src/common/elf_info.h index 0b2589e95..0f2311cb0 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -75,6 +75,7 @@ class ElfInfo { std::filesystem::path game_folder{}; public: + static constexpr u32 FW_10 = 0x1000000; static constexpr u32 FW_15 = 0x1500000; static constexpr u32 FW_16 = 0x1600000; static constexpr u32 FW_17 = 0x1700000; diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index ebb10db68..8bc9b51f0 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -430,8 +430,8 @@ int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int return index + 1; } -int PS4_SYSV_ABI sceHttpReadData() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpReadData(s32 reqId, void* data, u64 size) { + LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {} size = {}", reqId, size); return ORBIS_OK; } diff --git a/src/core/libraries/network/http.h b/src/core/libraries/network/http.h index 2ad5e171f..d373fd290 100644 --- a/src/core/libraries/network/http.h +++ b/src/core/libraries/network/http.h @@ -91,7 +91,7 @@ int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, c int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer, int32_t* httpMinorVer, int32_t* responseCode, const char** reasonPhrase, u64* phraseLen); -int PS4_SYSV_ABI sceHttpReadData(); +int PS4_SYSV_ABI sceHttpReadData(s32 reqId, void* data, u64 size); int PS4_SYSV_ABI sceHttpRedirectCacheFlush(); int PS4_SYSV_ABI sceHttpRemoveRequestHeader(); int PS4_SYSV_ABI sceHttpRequestGetAllHeaders(); diff --git a/src/core/libraries/np/np_common.h b/src/core/libraries/np/np_common.h index 2fd4ecd7c..a130f9c1d 100644 --- a/src/core/libraries/np/np_common.h +++ b/src/core/libraries/np/np_common.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/libraries/np/np_matching2.cpp b/src/core/libraries/np/np_matching2.cpp index cf4faea39..423b84257 100644 --- a/src/core/libraries/np/np_matching2.cpp +++ b/src/core/libraries/np/np_matching2.cpp @@ -234,7 +234,7 @@ int PS4_SYSV_ABI sceNpMatching2CreateJoinRoomA(OrbisNpMatching2ContextId ctxId, OrbisNpMatching2RoomMemberDataInternalA me{ nullptr, 0, - {0xace104e, Libraries::Np::OrbisNpPlatformType::ORBIS_NP_PLATFORM_TYPE_PS4}, + {0xace104e, Libraries::Np::OrbisNpPlatformType::PS4}, onlineId, {0, 0, 0, 0}, 1, diff --git a/src/core/libraries/np/np_types.h b/src/core/libraries/np/np_types.h index cc37b5a3d..58c119bec 100644 --- a/src/core/libraries/np/np_types.h +++ b/src/core/libraries/np/np_types.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -46,18 +46,20 @@ struct OrbisNpIdToken { }; using OrbisNpServiceLabel = u32; +constexpr s32 ORBIS_NP_INVALID_SERVICE_LABEL = 0xFFFFFFFF; -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, +using OrbisNpAccountId = u64; +enum OrbisNpPlatformType : s32 { + None = 0, + PS3 = 1, + Vita = 2, + PS4 = 3, }; struct OrbisNpPeerAddressA { OrbisNpAccountId accountId; - OrbisNpPlatformType platformType; - u8 padding[4]; + OrbisNpPlatformType platform; + char padding[4]; }; }; // namespace Libraries::Np \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api.cpp b/src/core/libraries/np/np_web_api.cpp index db9d2f42a..e51b79a3c 100644 --- a/src/core/libraries/np/np_web_api.cpp +++ b/src/core/libraries/np/np_web_api.cpp @@ -1,155 +1,293 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/elf_info.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/np/np_web_api.h" +#include "core/libraries/np/np_web_api_error.h" +#include "core/libraries/np/np_web_api_internal.h" + +#include namespace Libraries::Np::NpWebApi { -s32 PS4_SYSV_ABI sceNpWebApiCreateContext() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); +static bool g_is_initialized = false; +static s32 g_active_library_contexts = 0; + +s32 PS4_SYSV_ABI sceNpWebApiCreateContext(s32 libCtxId, OrbisNpOnlineId* onlineId) { + if (libCtxId >= 0x8000) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_LIB_CONTEXT_ID; + } + if (onlineId == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + return createUserContextWithOnlineId(libCtxId, onlineId); +} + +s32 PS4_SYSV_ABI sceNpWebApiCreatePushEventFilter( + s32 libCtxId, const OrbisNpWebApiPushEventFilterParameter* pFilterParam, u64 filterParamNum) { + if (pFilterParam == nullptr || filterParamNum == 0) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_WARNING(Lib_NpWebApi, "called, libCtxId = {:#x}", libCtxId); + return createPushEventFilter(libCtxId, pFilterParam, filterParamNum); +} + +s32 PS4_SYSV_ABI sceNpWebApiCreateServicePushEventFilter( + s32 libCtxId, s32 handleId, const char* pNpServiceName, OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiServicePushEventFilterParameter* pFilterParam, u64 filterParamNum) { + if (pNpServiceName == nullptr || pFilterParam == nullptr || filterParamNum == 0) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + if (getCompiledSdkVersion() >= Common::ElfInfo::FW_20 && + npServiceLabel == ORBIS_NP_INVALID_SERVICE_LABEL) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_WARNING(Lib_NpWebApi, + "called, libCtxId = {:#x}, handleId = {:#x}, pNpServiceName = '{}', " + "npServiceLabel = {:#x}", + libCtxId, handleId, pNpServiceName, npServiceLabel); + return createServicePushEventFilter(libCtxId, handleId, pNpServiceName, npServiceLabel, + pFilterParam, filterParamNum); +} + +s32 PS4_SYSV_ABI sceNpWebApiDeletePushEventFilter(s32 libCtxId, s32 filterId) { + LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}"); + return deletePushEventFilter(libCtxId, filterId); +} + +s32 PS4_SYSV_ABI sceNpWebApiDeleteServicePushEventFilter(s32 libCtxId, s32 filterId) { + LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}"); + return deleteServicePushEventFilter(libCtxId, filterId); +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallback(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiExtdPushEventCallback cbFunc, + void* pUserArg) { + if (cbFunc == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, filterId = {:#x}, cbFunc = {}", + titleUserCtxId, filterId, fmt::ptr(cbFunc)); + return registerExtdPushEventCallback(titleUserCtxId, filterId, cbFunc, nullptr, pUserArg); +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterNotificationCallback(s32 titleUserCtxId, + OrbisNpWebApiNotificationCallback cbFunc, + void* pUserArg) { + if (cbFunc == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, cbFunc = {}", titleUserCtxId, + fmt::ptr(cbFunc)); + return registerNotificationCallback(titleUserCtxId, cbFunc, pUserArg); +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterPushEventCallback(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiPushEventCallback cbFunc, + void* pUserArg) { + if (getCompiledSdkVersion() >= Common::ElfInfo::FW_10 && cbFunc == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, filterId = {:#x}, cbFunc = {}", + titleUserCtxId, filterId, fmt::ptr(cbFunc)); + return registerPushEventCallback(titleUserCtxId, filterId, cbFunc, pUserArg); +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterServicePushEventCallback( + s32 titleUserCtxId, s32 filterId, OrbisNpWebApiServicePushEventCallback cbFunc, + void* pUserArg) { + if (getCompiledSdkVersion() >= Common::ElfInfo::FW_10 && cbFunc == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, filterId = {:#x}, cbFunc = {}", + titleUserCtxId, filterId, fmt::ptr(cbFunc)); + return registerServicePushEventCallback(titleUserCtxId, filterId, cbFunc, nullptr, nullptr, + pUserArg); +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterNotificationCallback(s32 titleUserCtxId) { + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}", titleUserCtxId); + return unregisterNotificationCallback(titleUserCtxId); +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterPushEventCallback(s32 titleUserCtxId, s32 callbackId) { + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, callbackId = {:#x}", titleUserCtxId, + callbackId); + return unregisterPushEventCallback(titleUserCtxId, callbackId); +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterServicePushEventCallback(s32 titleUserCtxId, s32 callbackId) { + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, callbackId = {:#x}", titleUserCtxId, + callbackId); + return unregisterServicePushEventCallback(titleUserCtxId, callbackId); +} + +s32 PS4_SYSV_ABI sceNpWebApiAbortHandle(s32 libCtxId, s32 handleId) { + LOG_INFO(Lib_NpWebApi, "called libCtxId = {:#x}, handleId = {:#x}", libCtxId, handleId); + return abortHandle(libCtxId, handleId); +} + +s32 PS4_SYSV_ABI sceNpWebApiAbortRequest(s64 requestId) { + LOG_INFO(Lib_NpWebApi, "called requestId = {:#x}", requestId); + return abortRequest(requestId); +} + +s32 PS4_SYSV_ABI sceNpWebApiAddHttpRequestHeader(s64 requestId, const char* pFieldName, + const char* pValue) { + LOG_ERROR(Lib_NpWebApi, + "called (STUBBED) : requestId = {:#x}, " + "pFieldName = '{}', pValue = '{}'", + requestId, (pFieldName ? pFieldName : "null"), (pValue ? pValue : "null")); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpWebApiCreatePushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpWebApiAddMultipartPart(s64 requestId, + const OrbisNpWebApiMultipartPartParameter* pParam, + s32* pIndex) { + LOG_INFO(Lib_NpWebApi, + "called (STUBBED) : requestId = {:#x}, " + "pParam = {}, pIndex = {}", + requestId, fmt::ptr(pParam), fmt::ptr(pIndex)); + if (pParam) { + LOG_ERROR(Lib_NpWebApi, " Part params: headerNum = {}, contentLength = {}", + pParam->headerNum, pParam->contentLength); + } return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpWebApiCreateServicePushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); +void PS4_SYSV_ABI sceNpWebApiCheckTimeout() { + LOG_TRACE(Lib_NpWebApi, "called"); + if (!g_is_initialized) { + return; + } + return checkTimeout(); +} + +s32 PS4_SYSV_ABI sceNpWebApiClearAllUnusedConnection(s32 userCtxId, + bool bRemainKeepAliveConnection) { + LOG_ERROR(Lib_NpWebApi, + "called (STUBBED) : userCtxId = {:#x}, " + "bRemainKeepAliveConnection = {}", + userCtxId, bRemainKeepAliveConnection); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpWebApiDeletePushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpWebApiClearUnusedConnection(s32 userCtxId, const char* pApiGroup, + bool bRemainKeepAliveConnection) { + LOG_ERROR(Lib_NpWebApi, + "called (STUBBED) : userCtxId = {:#x}, " + "pApiGroup = '{}', bRemainKeepAliveConnection = {}", + userCtxId, (pApiGroup ? pApiGroup : "null"), bRemainKeepAliveConnection); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpWebApiDeleteServicePushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiCreateContextA(s32 libCtxId, + Libraries::UserService::OrbisUserServiceUserId userId) { + if (libCtxId >= 0x8000) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_LIB_CONTEXT_ID; + } + if (userId == Libraries::UserService::ORBIS_USER_SERVICE_USER_ID_INVALID) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + return createUserContext(libCtxId, userId); } -s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiCreateExtdPushEventFilter( + s32 libCtxId, s32 handleId, const char* pNpServiceName, OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiExtdPushEventFilterParameter* pFilterParam, u64 filterParamNum) { + if ((pNpServiceName != nullptr && npServiceLabel == ORBIS_NP_INVALID_SERVICE_LABEL) || + pFilterParam == nullptr || filterParamNum == 0) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + LOG_INFO( + Lib_NpWebApi, + "called, libCtxId = {:#x}, handleId = {:#x}, pNpServiceName = '{}', npServiceLabel = {:#x}", + libCtxId, handleId, (pNpServiceName ? pNpServiceName : "null"), npServiceLabel); + return createExtendedPushEventFilter(libCtxId, handleId, pNpServiceName, npServiceLabel, + pFilterParam, filterParamNum, false); } -s32 PS4_SYSV_ABI sceNpWebApiRegisterNotificationCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiCreateHandle(s32 libCtxId) { + return createHandle(libCtxId); } -s32 PS4_SYSV_ABI sceNpWebApiRegisterPushEventCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiCreateMultipartRequest(s32 titleUserCtxId, const char* pApiGroup, + const char* pPath, + OrbisNpWebApiHttpMethod method, + s64* pRequestId) { + if (pApiGroup == nullptr || pPath == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + if (getCompiledSdkVersion() >= Common::ElfInfo::FW_25 && + method > OrbisNpWebApiHttpMethod::ORBIS_NP_WEBAPI_HTTP_METHOD_DELETE) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + LOG_INFO(Lib_NpWebApi, + "called titleUserCtxId = {:#x}, pApiGroup = '{}', pPath = '{}', method = {}", + titleUserCtxId, pApiGroup, pPath, magic_enum::enum_name(method)); + + return createRequest(titleUserCtxId, pApiGroup, pPath, method, nullptr, nullptr, pRequestId, + true); } -s32 PS4_SYSV_ABI sceNpWebApiRegisterServicePushEventCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiCreateRequest(s32 titleUserCtxId, const char* pApiGroup, + const char* pPath, OrbisNpWebApiHttpMethod method, + const OrbisNpWebApiContentParameter* pContentParameter, + s64* pRequestId) { + if (pApiGroup == nullptr || pPath == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + if (pContentParameter != nullptr && pContentParameter->contentLength != 0 && + pContentParameter->pContentType == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_CONTENT_PARAMETER; + } + + if (getCompiledSdkVersion() >= Common::ElfInfo::FW_25 && + method > OrbisNpWebApiHttpMethod::ORBIS_NP_WEBAPI_HTTP_METHOD_DELETE) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + LOG_INFO(Lib_NpWebApi, + "called titleUserCtxId = {:#x}, pApiGroup = '{}', pPath = '{}', method = {}", + titleUserCtxId, pApiGroup, pPath, magic_enum::enum_name(method)); + + return createRequest(titleUserCtxId, pApiGroup, pPath, method, pContentParameter, nullptr, + pRequestId, false); } -s32 PS4_SYSV_ABI sceNpWebApiUnregisterNotificationCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiDeleteContext(s32 titleUserCtxId) { + LOG_INFO(Lib_NpWebApi, "called titleUserCtxId = {:#x}", titleUserCtxId); + return deleteUserContext(titleUserCtxId); } -s32 PS4_SYSV_ABI sceNpWebApiUnregisterPushEventCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiDeleteExtdPushEventFilter(s32 libCtxId, s32 filterId) { + LOG_INFO(Lib_NpWebApi, "called libCtxId = {:#x}, filterId = {:#x}", libCtxId, filterId); + return deleteExtendedPushEventFilter(libCtxId, filterId); } -s32 PS4_SYSV_ABI sceNpWebApiUnregisterServicePushEventCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiDeleteHandle(s32 libCtxId, s32 handleId) { + LOG_INFO(Lib_NpWebApi, "called libCtxId = {:#x}, handleId = {:#x}", libCtxId, handleId); + return deleteHandle(libCtxId, handleId); } -s32 PS4_SYSV_ABI sceNpWebApiAbortHandle() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApiDeleteRequest(s64 requestId) { + LOG_INFO(Lib_NpWebApi, "called requestId = {:#x}", requestId); + return deleteRequest(requestId); } -s32 PS4_SYSV_ABI sceNpWebApiAbortRequest() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiAddHttpRequestHeader() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiAddMultipartPart() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiCheckTimeout() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiClearAllUnusedConnection() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiClearUnusedConnection() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiCreateContextA() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiCreateExtdPushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiCreateHandle() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiCreateMultipartRequest() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiCreateRequest() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiDeleteContext() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiDeleteExtdPushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiDeleteHandle() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiDeleteRequest() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiGetConnectionStats() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNpWebApiGetConnectionStats(s32 userCtxId, const char* pApiGroup, + OrbisNpWebApiConnectionStats* pStats) { + LOG_ERROR(Lib_NpWebApi, + "called (STUBBED) : userCtxId = {:#x}, " + "pApiGroup = '{}', pStats = {}", + userCtxId, (pApiGroup ? pApiGroup : "null"), fmt::ptr(pStats)); return ORBIS_OK; } @@ -158,135 +296,300 @@ s32 PS4_SYSV_ABI sceNpWebApiGetErrorCode() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValue() { +s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValue(s64 requestId, const char* pFieldName, + char* pValue, u64 valueSize) { + LOG_ERROR(Lib_NpWebApi, + "called (STUBBED) : requestId = {:#x}, " + "pFieldName = '{}', pValue = {}, valueSize = {}", + requestId, (pFieldName ? pFieldName : "null"), fmt::ptr(pValue), valueSize); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValueLength(s64 requestId, const char* pFieldName, + u64* pValueLength) { + LOG_ERROR(Lib_NpWebApi, + "called (STUBBED) : requestId = {:#x}, " + "pFieldName = '{}', pValueLength = {}", + requestId, (pFieldName ? pFieldName : "null"), fmt::ptr(pValueLength)); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetHttpStatusCode(s64 requestId, s32* out_status_code) { + LOG_ERROR(Lib_NpWebApi, "called : requestId = {:#x}", requestId); + // On newer SDKs, NULL output pointer is invalid + if (getCompiledSdkVersion() > Common::ElfInfo::FW_10 && out_status_code == nullptr) + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + s32 returncode = getHttpStatusCodeInternal(requestId, out_status_code); + return returncode; +} + +s32 PS4_SYSV_ABI sceNpWebApiGetMemoryPoolStats(s32 libCtxId, + OrbisNpWebApiMemoryPoolStats* pCurrentStat) { + LOG_ERROR(Lib_NpWebApi, "called (STUBBED) : libCtxId = {:#x}, pCurrentStat = {}", libCtxId, + fmt::ptr(pCurrentStat)); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiInitialize(s32 libHttpCtxId, u64 poolSize) { + LOG_INFO(Lib_NpWebApi, "called libHttpCtxId = {:#x}, poolSize = {:#x} bytes", libHttpCtxId, + poolSize); + if (!g_is_initialized) { + g_is_initialized = true; + s32 result = initializeLibrary(); + if (result < ORBIS_OK) { + return result; + } + } + + s32 result = createLibraryContext(libHttpCtxId, poolSize, nullptr, 0); + if (result >= ORBIS_OK) { + g_active_library_contexts++; + } + return result; +} + +s32 PS4_SYSV_ABI sceNpWebApiInitializeForPresence(s32 libHttpCtxId, u64 poolSize) { + LOG_INFO(Lib_NpWebApi, "called libHttpCtxId = {:#x}, poolSize = {:#x} bytes", libHttpCtxId, + poolSize); + if (!g_is_initialized) { + g_is_initialized = true; + s32 result = initializeLibrary(); + if (result < ORBIS_OK) { + return result; + } + } + + s32 result = createLibraryContext(libHttpCtxId, poolSize, nullptr, 3); + if (result >= ORBIS_OK) { + g_active_library_contexts++; + } + return result; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntCreateCtxIndExtdPushEventFilter( + s32 libCtxId, s32 handleId, const OrbisNpWebApiExtdPushEventFilterParameter* pFilterParam, + u64 filterParamNum) { + if (pFilterParam == nullptr || filterParamNum == 0) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, handleId = {:#x}", libCtxId, handleId); + return createExtendedPushEventFilter(libCtxId, handleId, nullptr, + ORBIS_NP_INVALID_SERVICE_LABEL, pFilterParam, + filterParamNum, true); +} + +s32 PS4_SYSV_ABI sceNpWebApiIntCreateRequest( + s32 titleUserCtxId, const char* pApiGroup, const char* pPath, OrbisNpWebApiHttpMethod method, + const OrbisNpWebApiContentParameter* pContentParameter, + const OrbisNpWebApiIntCreateRequestExtraArgs* pInternalArgs, s64* pRequestId) { + LOG_INFO(Lib_NpWebApi, "called"); + if (pApiGroup == nullptr || pPath == nullptr || + method > OrbisNpWebApiHttpMethod::ORBIS_NP_WEBAPI_HTTP_METHOD_PATCH) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + if (pContentParameter != nullptr && pContentParameter->contentLength != 0 && + pContentParameter->pContentType == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_CONTENT_PARAMETER; + } + + LOG_INFO(Lib_NpWebApi, + "called titleUserCtxId = {:#x}, pApiGroup = '{}', pPath = '{}', method = {}", + titleUserCtxId, pApiGroup, pPath, magic_enum::enum_name(method)); + + return createRequest(titleUserCtxId, pApiGroup, pPath, method, pContentParameter, pInternalArgs, + pRequestId, false); +} + +s32 PS4_SYSV_ABI sceNpWebApiIntCreateServicePushEventFilter( + s32 libCtxId, s32 handleId, const char* pNpServiceName, OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiServicePushEventFilterParameter* pFilterParam, u64 filterParamNum) { + if (pFilterParam == nullptr || filterParamNum == 0) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_WARNING(Lib_NpWebApi, + "called, libCtxId = {:#x}, handleId = {:#x}, pNpServiceName = '{}', " + "npServiceLabel = {:#x}", + libCtxId, handleId, (pNpServiceName ? pNpServiceName : "null"), npServiceLabel); + return createServicePushEventFilter(libCtxId, handleId, pNpServiceName, npServiceLabel, + pFilterParam, filterParamNum); +} + +s32 PS4_SYSV_ABI sceNpWebApiIntInitialize(const OrbisNpWebApiIntInitializeArgs* args) { + LOG_INFO(Lib_NpWebApi, "called"); + if (args == nullptr || args->structSize != sizeof(OrbisNpWebApiIntInitializeArgs)) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + if (!g_is_initialized) { + g_is_initialized = true; + s32 result = initializeLibrary(); + if (result < ORBIS_OK) { + return result; + } + } + + s32 result = createLibraryContext(args->libHttpCtxId, args->poolSize, args->name, 2); + if (result >= ORBIS_OK) { + g_active_library_contexts++; + } + return result; +} + +s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallback( + s32 titleUserCtxId, s32 filterId, OrbisNpWebApiInternalServicePushEventCallback cbFunc, + void* pUserArg) { + if (cbFunc == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, cbFunc = {}", titleUserCtxId, + fmt::ptr(cbFunc)); + return registerServicePushEventCallback(titleUserCtxId, filterId, nullptr, cbFunc, nullptr, + pUserArg); +} + +s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallbackA( + s32 titleUserCtxId, s32 filterId, OrbisNpWebApiInternalServicePushEventCallbackA cbFunc, + void* pUserArg) { + if (cbFunc == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, cbFunc = {}", titleUserCtxId, + fmt::ptr(cbFunc)); + return registerServicePushEventCallback(titleUserCtxId, filterId, nullptr, nullptr, cbFunc, + pUserArg); +} + +s32 PS4_SYSV_ABI sceNpWebApiReadData(s64 requestId, void* pData, u64 size) { + LOG_ERROR(Lib_NpWebApi, "called : requestId = {:#x}, pData = {}, size = {:#x}", requestId, + fmt::ptr(pData), size); + if (pData == nullptr || size == 0) + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + + return readDataInternal(requestId, pData, size); +} + +s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallbackA( + s32 titleUserCtxId, s32 filterId, OrbisNpWebApiExtdPushEventCallbackA cbFunc, void* pUserArg) { + if (cbFunc == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, cbFunc = {}", titleUserCtxId, + fmt::ptr(cbFunc)); + return registerExtdPushEventCallbackA(titleUserCtxId, filterId, cbFunc, pUserArg); +} + +s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest(s64 requestId, s32 partIndex, const void* pData, + u64 dataSize) { + if (partIndex <= 0 || pData == nullptr || dataSize == 0) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + LOG_INFO(Lib_NpWebApi, + "called, requestId = {:#x}, " + "partIndex = {:#x}, pData = {}, dataSize = {:#x}", + requestId, partIndex, fmt::ptr(pData), dataSize); + return sendRequest(requestId, partIndex, pData, dataSize, 0, nullptr); +} + +s32 PS4_SYSV_ABI +sceNpWebApiSendMultipartRequest2(s64 requestId, s32 partIndex, const void* pData, u64 dataSize, + OrbisNpWebApiResponseInformationOption* pRespInfoOption) { + if (partIndex <= 0 || pData == nullptr || dataSize == 0) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + LOG_INFO(Lib_NpWebApi, + "called, requestId = {:#x}, " + "partIndex = {:#x}, pData = {}, dataSize = {:#x}, pRespInfoOption = {}", + requestId, partIndex, fmt::ptr(pData), dataSize, fmt::ptr(pRespInfoOption)); + return sendRequest(requestId, partIndex, pData, dataSize, 1, pRespInfoOption); +} + +s32 PS4_SYSV_ABI sceNpWebApiSendRequest(s64 requestId, const void* pData, u64 dataSize) { + LOG_INFO(Lib_NpWebApi, "called, requestId = {:#x}, pData = {}, dataSize = {:#x}", requestId, + fmt::ptr(pData), dataSize); + return sendRequest(requestId, 0, pData, dataSize, 0, nullptr); +} + +s32 PS4_SYSV_ABI sceNpWebApiSendRequest2(s64 requestId, const void* pData, u64 dataSize, + OrbisNpWebApiResponseInformationOption* pRespInfoOption) { + LOG_INFO(Lib_NpWebApi, + "called, requestId = {:#x}, " + "pData = {}, dataSize = {:#x}, pRespInfoOption = {}", + requestId, fmt::ptr(pData), dataSize, fmt::ptr(pRespInfoOption)); + return sendRequest(requestId, 0, pData, dataSize, 1, pRespInfoOption); +} + +s32 PS4_SYSV_ABI sceNpWebApiSetHandleTimeout(s32 libCtxId, s32 handleId, u32 timeout) { + LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, handleId = {:#x}, timeout = {} ms", libCtxId, + handleId, timeout); + return setHandleTimeout(libCtxId, handleId, timeout); +} + +s32 PS4_SYSV_ABI sceNpWebApiSetMaxConnection(s32 libCtxId, s32 maxConnection) { + LOG_ERROR(Lib_NpWebApi, "called (STUBBED) : libCtxId = {:#x}, maxConnection = {}", libCtxId, + maxConnection); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSetMultipartContentType(s64 requestId, const char* pTypeName, + const char* pBoundary) { + LOG_ERROR(Lib_NpWebApi, + "called (STUBBED) : requestId = {:#x}, " + "pTypeName = '{}', pBoundary = '{}'", + requestId, (pTypeName ? pTypeName : "null"), (pBoundary ? pBoundary : "null")); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiSetRequestTimeout(s64 requestId, u32 timeout) { + LOG_INFO(Lib_NpWebApi, "called requestId = {:#x}, timeout = {} ms", requestId, timeout); + return setRequestTimeout(requestId, timeout); +} + +s32 PS4_SYSV_ABI sceNpWebApiTerminate(s32 libCtxId) { + LOG_INFO(Lib_NpWebApi, "called libCtxId = {:#x}", libCtxId); + s32 result = terminateContext(libCtxId); + if (result != ORBIS_OK) { + return result; + } + + g_active_library_contexts--; + if (g_active_library_contexts == 0) { + g_is_initialized = false; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApiUnregisterExtdPushEventCallback(s32 titleUserCtxId, s32 callbackId) { + LOG_INFO(Lib_NpWebApi, "called, titleUserCtxId = {:#x}, callbackId = {:#x}", titleUserCtxId, + callbackId); + return unregisterExtdPushEventCallback(titleUserCtxId, callbackId); +} + +s32 PS4_SYSV_ABI sceNpWebApiUtilityParseNpId(const char* pJsonNpId, + Libraries::Np::OrbisNpId* pNpId) { LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValueLength() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} +s32 PS4_SYSV_ABI sceNpWebApiVshInitialize(s32 libHttpCtxId, u64 poolSize) { + LOG_INFO(Lib_NpWebApi, "called libHttpCtxId = {:#x}, poolSize = {:#x} bytes", libHttpCtxId, + poolSize); + if (!g_is_initialized) { + g_is_initialized = true; + s32 result = initializeLibrary(); + if (result < ORBIS_OK) { + return result; + } + } -s32 PS4_SYSV_ABI sceNpWebApiGetHttpStatusCode() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiGetMemoryPoolStats() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiInitialize() { - LOG_ERROR(Lib_NpWebApi, "(DUMMY) called"); - static s32 id = 0; - return ++id; -} - -s32 PS4_SYSV_ABI sceNpWebApiInitializeForPresence() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiIntCreateCtxIndExtdPushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiIntCreateRequest() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiIntCreateServicePushEventFilter() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiIntInitialize() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallbackA() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiReadData() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallbackA() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest2() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSendRequest() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSendRequest2() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSetHandleTimeout() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSetMaxConnection() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSetMultipartContentType() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiSetRequestTimeout() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiTerminate() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiUnregisterExtdPushEventCallback() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiUtilityParseNpId() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApiVshInitialize() { - LOG_ERROR(Lib_NpWebApi, "(STUBBED) called"); - return ORBIS_OK; + s32 result = createLibraryContext(libHttpCtxId, poolSize, nullptr, 4); + if (result >= ORBIS_OK) { + g_active_library_contexts++; + } + return result; } s32 PS4_SYSV_ABI Func_064C4ED1EDBEB9E8() { diff --git a/src/core/libraries/np/np_web_api.h b/src/core/libraries/np/np_web_api.h index 6679662cb..8dd9441e0 100644 --- a/src/core/libraries/np/np_web_api.h +++ b/src/core/libraries/np/np_web_api.h @@ -1,9 +1,12 @@ -// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/types.h" +#include "core/libraries/np/np_common.h" +#include "core/libraries/np/np_types.h" +#include "core/libraries/system/userservice.h" namespace Core::Loader { class SymbolsResolver; @@ -11,106 +14,115 @@ class SymbolsResolver; namespace Libraries::Np::NpWebApi { -s32 PS4_SYSV_ABI sceNpWebApiCreateContext(); -s32 PS4_SYSV_ABI sceNpWebApiCreatePushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiCreateServicePushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiDeletePushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiDeleteServicePushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallback(); -s32 PS4_SYSV_ABI sceNpWebApiRegisterNotificationCallback(); -s32 PS4_SYSV_ABI sceNpWebApiRegisterPushEventCallback(); -s32 PS4_SYSV_ABI sceNpWebApiRegisterServicePushEventCallback(); -s32 PS4_SYSV_ABI sceNpWebApiUnregisterNotificationCallback(); -s32 PS4_SYSV_ABI sceNpWebApiUnregisterPushEventCallback(); -s32 PS4_SYSV_ABI sceNpWebApiUnregisterServicePushEventCallback(); -s32 PS4_SYSV_ABI sceNpWebApiAbortHandle(); -s32 PS4_SYSV_ABI sceNpWebApiAbortRequest(); -s32 PS4_SYSV_ABI sceNpWebApiAddHttpRequestHeader(); -s32 PS4_SYSV_ABI sceNpWebApiAddMultipartPart(); -s32 PS4_SYSV_ABI sceNpWebApiCheckTimeout(); -s32 PS4_SYSV_ABI sceNpWebApiClearAllUnusedConnection(); -s32 PS4_SYSV_ABI sceNpWebApiClearUnusedConnection(); -s32 PS4_SYSV_ABI sceNpWebApiCreateContextA(); -s32 PS4_SYSV_ABI sceNpWebApiCreateExtdPushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiCreateHandle(); -s32 PS4_SYSV_ABI sceNpWebApiCreateMultipartRequest(); -s32 PS4_SYSV_ABI sceNpWebApiCreateRequest(); -s32 PS4_SYSV_ABI sceNpWebApiDeleteContext(); -s32 PS4_SYSV_ABI sceNpWebApiDeleteExtdPushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiDeleteHandle(); -s32 PS4_SYSV_ABI sceNpWebApiDeleteRequest(); -s32 PS4_SYSV_ABI sceNpWebApiGetConnectionStats(); -s32 PS4_SYSV_ABI sceNpWebApiGetErrorCode(); -s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValue(); -s32 PS4_SYSV_ABI sceNpWebApiGetHttpResponseHeaderValueLength(); -s32 PS4_SYSV_ABI sceNpWebApiGetHttpStatusCode(); -s32 PS4_SYSV_ABI sceNpWebApiGetMemoryPoolStats(); -s32 PS4_SYSV_ABI sceNpWebApiInitialize(); -s32 PS4_SYSV_ABI sceNpWebApiInitializeForPresence(); -s32 PS4_SYSV_ABI sceNpWebApiIntCreateCtxIndExtdPushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiIntCreateRequest(); -s32 PS4_SYSV_ABI sceNpWebApiIntCreateServicePushEventFilter(); -s32 PS4_SYSV_ABI sceNpWebApiIntInitialize(); -s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallback(); -s32 PS4_SYSV_ABI sceNpWebApiIntRegisterServicePushEventCallbackA(); -s32 PS4_SYSV_ABI sceNpWebApiReadData(); -s32 PS4_SYSV_ABI sceNpWebApiRegisterExtdPushEventCallbackA(); -s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest(); -s32 PS4_SYSV_ABI sceNpWebApiSendMultipartRequest2(); -s32 PS4_SYSV_ABI sceNpWebApiSendRequest(); -s32 PS4_SYSV_ABI sceNpWebApiSendRequest2(); -s32 PS4_SYSV_ABI sceNpWebApiSetHandleTimeout(); -s32 PS4_SYSV_ABI sceNpWebApiSetMaxConnection(); -s32 PS4_SYSV_ABI sceNpWebApiSetMultipartContentType(); -s32 PS4_SYSV_ABI sceNpWebApiSetRequestTimeout(); -s32 PS4_SYSV_ABI sceNpWebApiTerminate(); -s32 PS4_SYSV_ABI sceNpWebApiUnregisterExtdPushEventCallback(); -s32 PS4_SYSV_ABI sceNpWebApiUtilityParseNpId(); -s32 PS4_SYSV_ABI sceNpWebApiVshInitialize(); -s32 PS4_SYSV_ABI Func_064C4ED1EDBEB9E8(); -s32 PS4_SYSV_ABI Func_0783955D4E9563DA(); -s32 PS4_SYSV_ABI Func_1A6D77F3FD8323A8(); -s32 PS4_SYSV_ABI Func_1E0693A26FE0F954(); -s32 PS4_SYSV_ABI Func_24A9B5F1D77000CF(); -s32 PS4_SYSV_ABI Func_24AAA6F50E4C2361(); -s32 PS4_SYSV_ABI Func_24D8853D6B47FC79(); -s32 PS4_SYSV_ABI Func_279B3E9C7C4A9DC5(); -s32 PS4_SYSV_ABI Func_28461E29E9F8D697(); -s32 PS4_SYSV_ABI Func_3C29624704FAB9E0(); -s32 PS4_SYSV_ABI Func_3F027804ED2EC11E(); -s32 PS4_SYSV_ABI Func_4066C94E782997CD(); -s32 PS4_SYSV_ABI Func_47C85356815DBE90(); -s32 PS4_SYSV_ABI Func_4FCE8065437E3B87(); -s32 PS4_SYSV_ABI Func_536280BE3DABB521(); -s32 PS4_SYSV_ABI Func_57A0E1BC724219F3(); -s32 PS4_SYSV_ABI Func_5819749C040B6637(); -s32 PS4_SYSV_ABI Func_6198D0C825E86319(); -s32 PS4_SYSV_ABI Func_61F2B9E8AB093743(); -s32 PS4_SYSV_ABI Func_6BC388E6113F0D44(); -s32 PS4_SYSV_ABI Func_7500F0C4F8DC2D16(); -s32 PS4_SYSV_ABI Func_75A03814C7E9039F(); -s32 PS4_SYSV_ABI Func_789D6026C521416E(); -s32 PS4_SYSV_ABI Func_7DED63D06399EFFF(); -s32 PS4_SYSV_ABI Func_7E55A2DCC03D395A(); -s32 PS4_SYSV_ABI Func_7E6C8F9FB86967F4(); -s32 PS4_SYSV_ABI Func_7F04B7D4A7D41E80(); -s32 PS4_SYSV_ABI Func_8E167252DFA5C957(); -s32 PS4_SYSV_ABI Func_95D0046E504E3B09(); -s32 PS4_SYSV_ABI Func_97284BFDA4F18FDF(); -s32 PS4_SYSV_ABI Func_99E32C1F4737EAB4(); -s32 PS4_SYSV_ABI Func_9CFF661EA0BCBF83(); -s32 PS4_SYSV_ABI Func_9EB0E1F467AC3B29(); -s32 PS4_SYSV_ABI Func_A2318FE6FBABFAA3(); -s32 PS4_SYSV_ABI Func_BA07A2E1BF7B3971(); -s32 PS4_SYSV_ABI Func_BD0803EEE0CC29A0(); -s32 PS4_SYSV_ABI Func_BE6F4E5524BB135F(); -s32 PS4_SYSV_ABI Func_C0D490EB481EA4D0(); -s32 PS4_SYSV_ABI Func_C175D392CA6D084A(); -s32 PS4_SYSV_ABI Func_CD0136AF165D2F2F(); -s32 PS4_SYSV_ABI Func_D1C0ADB7B52FEAB5(); -s32 PS4_SYSV_ABI Func_E324765D18EE4D12(); -s32 PS4_SYSV_ABI Func_E789F980D907B653(); -s32 PS4_SYSV_ABI Func_F9A32E8685627436(); +#define ORBIS_NP_WEBAPI_DEFAULT_CONNECTION_NUM 1 +#define ORBIS_NP_WEBAPI_MAX_CONNECTION_NUM 16 +#define ORBIS_NP_WEBAPI_PUSH_EVENT_DATA_TYPE_LEN_MAX 64 +#define ORBIS_NP_WEBAPI_EXTD_PUSH_EVENT_EXTD_DATA_KEY_LEN_MAX 32 + +struct OrbisNpWebApiPushEventDataType { + char val[ORBIS_NP_WEBAPI_PUSH_EVENT_DATA_TYPE_LEN_MAX + 1]; +}; + +struct OrbisNpWebApiExtdPushEventExtdDataKey { + char val[ORBIS_NP_WEBAPI_EXTD_PUSH_EVENT_EXTD_DATA_KEY_LEN_MAX + 1]; +}; + +struct OrbisNpWebApiPushEventFilterParameter { + OrbisNpWebApiPushEventDataType dataType; +}; + +struct OrbisNpWebApiServicePushEventFilterParameter { + OrbisNpWebApiPushEventDataType dataType; +}; + +struct OrbisNpWebApiExtdPushEventFilterParameter { + OrbisNpWebApiPushEventDataType dataType; + OrbisNpWebApiExtdPushEventExtdDataKey* pExtdDataKey; + u64 extdDataKeyNum; +}; + +struct OrbisNpWebApiExtdPushEventExtdData { + OrbisNpWebApiExtdPushEventExtdDataKey extdDataKey; + char* pData; + u64 dataLen; +}; + +struct OrbisNpWebApiHttpHeader { + char* pName; + char* pValue; +}; + +struct OrbisNpWebApiMultipartPartParameter { + OrbisNpWebApiHttpHeader* pHeaders; + u64 headerNum; + u64 contentLength; +}; + +enum OrbisNpWebApiHttpMethod : s32 { + ORBIS_NP_WEBAPI_HTTP_METHOD_GET, + ORBIS_NP_WEBAPI_HTTP_METHOD_POST, + ORBIS_NP_WEBAPI_HTTP_METHOD_PUT, + ORBIS_NP_WEBAPI_HTTP_METHOD_DELETE, + ORBIS_NP_WEBAPI_HTTP_METHOD_PATCH +}; + +struct OrbisNpWebApiContentParameter { + u64 contentLength; + const char* pContentType; + u8 reserved[16]; +}; + +struct OrbisNpWebApiResponseInformationOption { + s32 httpStatus; + char* pErrorObject; + u64 errorObjectSize; + u64 responseDataSize; +}; + +struct OrbisNpWebApiMemoryPoolStats { + u64 poolSize; + u64 maxInuseSize; + u64 currentInuseSize; + s32 reserved; +}; + +struct OrbisNpWebApiConnectionStats { + u32 max; + u32 used; + u32 unused; + u32 keepAlive; + u64 reserved; +}; + +struct OrbisNpWebApiIntInitializeArgs { + u32 libHttpCtxId; + u8 reserved[4]; + u64 poolSize; + const char* name; + u64 structSize; +}; + +struct OrbisNpWebApiIntCreateRequestExtraArgs { + void* unk_0; + void* unk_1; + void* unk_2; +}; + +using OrbisNpWebApiPushEventCallback = PS4_SYSV_ABI void (*)(); // dummy + +using OrbisNpWebApiExtdPushEventCallback = PS4_SYSV_ABI void (*)(); // dummy +using OrbisNpWebApiExtdPushEventCallbackA = PS4_SYSV_ABI void (*)( + s32 userCtxId, s32 callbackId, const char* pNpServiceName, OrbisNpServiceLabel npServiceLabel, + const OrbisNpPeerAddressA* pTo, const OrbisNpOnlineId* pToOnlineId, + const OrbisNpPeerAddressA* pFrom, const OrbisNpOnlineId* pFromOnlineId, + const OrbisNpWebApiPushEventDataType* pDataType, const char* pData, u64 dataLen, + const OrbisNpWebApiExtdPushEventExtdData* pExtdData, u64 extdDataNum, void* pUserArg); + +using OrbisNpWebApiServicePushEventCallback = PS4_SYSV_ABI void (*)(); // dummy +using OrbisNpWebApiInternalServicePushEventCallback = PS4_SYSV_ABI void (*)(); // dummy +using OrbisNpWebApiInternalServicePushEventCallbackA = PS4_SYSV_ABI void (*)(); // dummy + +using OrbisNpWebApiNotificationCallback = PS4_SYSV_ABI void (*)(); // dummy void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Np::NpWebApi \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api_error.h b/src/core/libraries/np/np_web_api_error.h new file mode 100644 index 000000000..c7f08224f --- /dev/null +++ b/src/core/libraries/np/np_web_api_error.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_NP_WEBAPI_ERROR_OUT_OF_MEMORY = 0x80552901; +constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT = 0x80552902; +constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_LIB_CONTEXT_ID = 0x80552903; +constexpr int ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND = 0x80552904; +constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND = 0x80552905; +constexpr int ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND = 0x80552906; +constexpr int ORBIS_NP_WEBAPI_ERROR_NOT_SIGNED_IN = 0x80552907; +constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_CONTENT_PARAMETER = 0x80552908; +constexpr int ORBIS_NP_WEBAPI_ERROR_ABORTED = 0x80552909; +constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_ALREADY_EXIST = 0x8055290a; +constexpr int ORBIS_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055290b; +constexpr int ORBIS_NP_WEBAPI_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055290c; +constexpr int ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND = 0x8055290d; +constexpr int ORBIS_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055290e; +constexpr int ORBIS_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055290f; +constexpr int ORBIS_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND = 0x80552910; +constexpr int ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_BUSY = 0x80552911; +constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_BUSY = 0x80552912; +constexpr int ORBIS_NP_WEBAPI_ERROR_REQUEST_BUSY = 0x80552913; +constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_HTTP_STATUS_CODE = 0x80552914; +constexpr int ORBIS_NP_WEBAPI_ERROR_PROHIBITED_HTTP_HEADER = 0x80552915; +constexpr int ORBIS_NP_WEBAPI_ERROR_PROHIBITED_FUNCTION_CALL = 0x80552916; +constexpr int ORBIS_NP_WEBAPI_ERROR_MULTIPART_PART_NOT_FOUND = 0x80552917; +constexpr int ORBIS_NP_WEBAPI_ERROR_PARAMETER_TOO_LONG = 0x80552918; +constexpr int ORBIS_NP_WEBAPI_ERROR_HANDLE_BUSY = 0x80552919; +constexpr int ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_MAX = 0x8055291a; +constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_MAX = 0x8055291b; +constexpr int ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055291c; +constexpr int ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055291d; +constexpr int ORBIS_NP_WEBAPI_ERROR_AFTER_SEND = 0x8055291e; +constexpr int ORBIS_NP_WEBAPI_ERROR_TIMEOUT = 0x8055291f; diff --git a/src/core/libraries/np/np_web_api_internal.cpp b/src/core/libraries/np/np_web_api_internal.cpp new file mode 100644 index 000000000..3c7557b72 --- /dev/null +++ b/src/core/libraries/np/np_web_api_internal.cpp @@ -0,0 +1,1534 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/config.h" +#include "common/elf_info.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/kernel/time.h" +#include "core/libraries/network/http.h" +#include "np_web_api_internal.h" + +#include + +namespace Libraries::Np::NpWebApi { + +static std::mutex g_global_mutex; +static std::map g_contexts; +static s32 g_library_context_count = 0; +static s32 g_user_context_count = 0; +static s32 g_handle_count = 0; +static s32 g_push_event_filter_count = 0; +static s32 g_service_push_event_filter_count = 0; +static s32 g_extended_push_event_filter_count = 0; +static s32 g_registered_callback_count = 0; +static s64 g_request_count = 0; +static u64 g_last_timeout_check = 0; +static s32 g_sdk_ver = 0; + +s32 initializeLibrary() { + return Kernel::sceKernelGetCompiledSdkVersion(&g_sdk_ver); +} + +s32 getCompiledSdkVersion() { + return g_sdk_ver; +} + +s32 createLibraryContext(s32 libHttpCtxId, u64 poolSize, const char* name, s32 type) { + std::scoped_lock lk{g_global_mutex}; + + g_library_context_count++; + if (g_library_context_count >= 0x8000) { + g_library_context_count = 1; + } + s32 ctx_id = g_library_context_count; + while (g_contexts.contains(ctx_id)) { + ctx_id--; + } + if (ctx_id <= 0) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_MAX; + } + + // Create new context + g_contexts[ctx_id] = new OrbisNpWebApiContext{}; + auto& new_context = g_contexts.at(ctx_id); + new_context->libCtxId = ctx_id; + new_context->libHttpCtxId = libHttpCtxId; + new_context->type = type; + new_context->userCount = 0; + new_context->terminated = false; + if (name != nullptr) { + new_context->name = std::string(name); + } + + return ctx_id; +} + +OrbisNpWebApiContext* findAndValidateContext(s32 libCtxId, s32 flag) { + std::scoped_lock lk{g_global_mutex}; + if (libCtxId < 1 || libCtxId >= 0x8000) { + return nullptr; + } + auto& context = g_contexts[libCtxId]; + std::scoped_lock lk2{context->contextLock}; + if (flag == 0 && context->terminated) { + return nullptr; + } + context->userCount++; + return context; +} + +void releaseContext(OrbisNpWebApiContext* context) { + std::scoped_lock lk{context->contextLock}; + context->userCount--; +} + +bool isContextTerminated(OrbisNpWebApiContext* context) { + std::scoped_lock lk{context->contextLock}; + return context->terminated; +} + +bool isContextBusy(OrbisNpWebApiContext* context) { + std::scoped_lock lk{context->contextLock}; + return context->userCount > 1; +} + +bool areContextHandlesBusy(OrbisNpWebApiContext* context) { + std::scoped_lock lk{context->contextLock}; + bool is_busy = false; + for (auto& handle : context->handles) { + if (handle.second->userCount > 0) { + return true; + } + } + return false; +} + +void lockContext(OrbisNpWebApiContext* context) { + context->contextLock.lock(); +} + +void unlockContext(OrbisNpWebApiContext* context) { + context->contextLock.unlock(); +} + +void markContextAsTerminated(OrbisNpWebApiContext* context) { + std::scoped_lock lk{context->contextLock}; + context->terminated = true; +} + +void checkContextTimeout(OrbisNpWebApiContext* context) { + u64 time = Kernel::sceKernelGetProcessTime(); + std::scoped_lock lk{context->contextLock}; + + for (auto& user_context : context->userContexts) { + checkUserContextTimeout(user_context.second); + } + + for (auto& value : context->timerHandles) { + auto& timer_handle = value.second; + if (!timer_handle->timedOut && timer_handle->handleTimeout != 0 && + timer_handle->handleEndTime < time) { + timer_handle->timedOut = true; + abortHandle(context->libCtxId, timer_handle->handleId); + } + } +} + +void checkTimeout() { + u64 time = Kernel::sceKernelGetProcessTime(); + if (time < g_last_timeout_check + 1000) { + return; + } + g_last_timeout_check = time; + std::scoped_lock lk{g_global_mutex}; + + for (auto& context : g_contexts) { + checkContextTimeout(context.second); + } +} + +s32 deleteContext(s32 libCtxId) { + std::scoped_lock lk{g_global_mutex}; + if (!g_contexts.contains(libCtxId)) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + auto& context = g_contexts[libCtxId]; + context->handles.clear(); + context->timerHandles.clear(); + context->pushEventFilters.clear(); + context->servicePushEventFilters.clear(); + context->extendedPushEventFilters.clear(); + + g_contexts.erase(libCtxId); + return ORBIS_OK; +} + +s32 terminateContext(s32 libCtxId) { + OrbisNpWebApiContext* ctx = findAndValidateContext(libCtxId); + if (ctx == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + if (g_sdk_ver < Common::ElfInfo::FW_40 && isContextBusy(ctx)) { + releaseContext(ctx); + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_BUSY; + } + + std::vector user_context_ids; + for (auto& user_context : ctx->userContexts) { + user_context_ids.emplace_back(user_context.first); + } + for (s32 user_context_id : user_context_ids) { + s32 result = deleteUserContext(user_context_id); + if (result != ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND || + g_sdk_ver < Common::ElfInfo::FW_40) { + return result; + } + } + + lockContext(ctx); + if (g_sdk_ver >= Common::ElfInfo::FW_40) { + for (auto& handle : ctx->handles) { + abortHandle(libCtxId, handle.first); + } + if (isContextTerminated(ctx)) { + unlockContext(ctx); + releaseContext(ctx); + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + markContextAsTerminated(ctx); + while (isContextBusy(ctx) || areContextHandlesBusy(ctx)) { + unlockContext(ctx); + Kernel::sceKernelUsleep(50000); + lockContext(ctx); + } + } + + unlockContext(ctx); + releaseContext(ctx); + return deleteContext(libCtxId); +} + +OrbisNpWebApiUserContext* findUserContextByUserId( + OrbisNpWebApiContext* context, Libraries::UserService::OrbisUserServiceUserId userId) { + if (userId == Libraries::UserService::ORBIS_USER_SERVICE_USER_ID_INVALID) { + return nullptr; + } + + std::scoped_lock lk{context->contextLock}; + for (auto& user_context : context->userContexts) { + if (user_context.second->userId == userId) { + user_context.second->userCount++; + return user_context.second; + } + } + return nullptr; +} + +OrbisNpWebApiUserContext* findUserContext(OrbisNpWebApiContext* context, s32 titleUserCtxId) { + std::scoped_lock lk{context->contextLock}; + if (!context->userContexts.contains(titleUserCtxId)) { + return nullptr; + } + OrbisNpWebApiUserContext* user_context = context->userContexts[titleUserCtxId]; + if (user_context->deleted) { + return nullptr; + } + user_context->userCount++; + return user_context; +} + +s32 createUserContextWithOnlineId(s32 libCtxId, OrbisNpOnlineId* onlineId) { + LOG_WARNING(Lib_NpWebApi, "called libCtxId = {}", libCtxId); + + Libraries::UserService::OrbisUserServiceUserId user_id = 0; + Libraries::UserService::sceUserServiceGetInitialUser(&user_id); + return createUserContext(libCtxId, user_id); +} + +s32 createUserContext(s32 libCtxId, Libraries::UserService::OrbisUserServiceUserId userId) { + LOG_INFO(Lib_NpWebApi, "libCtxId = {}, userId = {}", libCtxId, userId); + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContextByUserId(context, userId); + if (user_context != nullptr) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_ALREADY_EXIST; + } + + std::scoped_lock lk{context->contextLock}; + + // Create new user context + g_user_context_count++; + if (g_user_context_count >= 0x10000) { + g_user_context_count = 1; + } + s32 user_ctx_id = (libCtxId << 0x10) | g_user_context_count; + while (context->userContexts.contains(user_ctx_id)) { + user_ctx_id--; + } + if (user_ctx_id <= (libCtxId << 0x10)) { + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_MAX; + } + + context->userContexts[user_ctx_id] = new OrbisNpWebApiUserContext{}; + user_context = context->userContexts.at(user_ctx_id); + user_context->userCount = 0; + user_context->parentContext = context; + user_context->userId = userId; + user_context->userCtxId = user_ctx_id; + user_context->deleted = false; + + // TODO: Internal structs related to libSceHttp use are initialized here. + releaseContext(context); + return user_ctx_id; +} + +s32 registerNotificationCallback(s32 titleUserCtxId, OrbisNpWebApiNotificationCallback cbFunc, + void* pUserArg) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + lockContext(context); + user_context->notificationCallbackFunction = cbFunc; + user_context->pNotificationCallbackUserArgs = pUserArg; + unlockContext(context); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +s32 unregisterNotificationCallback(s32 titleUserCtxId) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + lockContext(context); + user_context->notificationCallbackFunction = nullptr; + user_context->pNotificationCallbackUserArgs = nullptr; + unlockContext(context); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +bool isUserContextBusy(OrbisNpWebApiUserContext* userContext) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + return userContext->userCount > 1; +} + +bool areUserContextRequestsBusy(OrbisNpWebApiUserContext* userContext) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + bool is_busy = false; + for (auto& request : userContext->requests) { + request.second->userCount++; + bool req_busy = isRequestBusy(request.second); + request.second->userCount--; + if (req_busy) { + return true; + } + } + return false; +} + +void releaseUserContext(OrbisNpWebApiUserContext* userContext) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + userContext->userCount--; +} + +void checkUserContextTimeout(OrbisNpWebApiUserContext* userContext) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + for (auto& request : userContext->requests) { + checkRequestTimeout(request.second); + } +} + +s32 deleteUserContext(s32 titleUserCtxId) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + lockContext(context); + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + unlockContext(context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (g_sdk_ver < Common::ElfInfo::FW_40) { + if (isUserContextBusy(user_context)) { + releaseUserContext(user_context); + unlockContext(context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_BUSY; + } + + if (areUserContextRequestsBusy(user_context)) { + releaseUserContext(user_context); + unlockContext(context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_BUSY; + } + } else { + for (auto& request : user_context->requests) { + abortRequestInternal(context, user_context, request.second); + } + + if (user_context->deleted) { + releaseUserContext(user_context); + unlockContext(context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + user_context->deleted = true; + while (isUserContextBusy(user_context) || areUserContextRequestsBusy(user_context)) { + unlockContext(context); + Kernel::sceKernelUsleep(50000); + lockContext(context); + } + } + + user_context->extendedPushEventCallbacks.clear(); + user_context->servicePushEventCallbacks.clear(); + user_context->pushEventCallbacks.clear(); + user_context->requests.clear(); + context->userContexts.erase(titleUserCtxId); + + unlockContext(context); + releaseContext(context); + return ORBIS_OK; +} + +s32 createRequest(s32 titleUserCtxId, const char* pApiGroup, const char* pPath, + OrbisNpWebApiHttpMethod method, + const OrbisNpWebApiContentParameter* pContentParameter, + const OrbisNpWebApiIntCreateRequestExtraArgs* pInternalArgs, s64* pRequestId, + bool isMultipart) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + lockContext(user_context->parentContext); + if (g_sdk_ver >= Common::ElfInfo::FW_40 && user_context->deleted) { + unlockContext(user_context->parentContext); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + g_request_count++; + if (g_request_count >> 0x20 != 0) { + g_request_count = 1; + } + + s64 user_ctx_id = static_cast(titleUserCtxId); + s32 request_id = (user_ctx_id << 0x20) | g_request_count; + while (user_context->requests.contains(request_id)) { + request_id--; + } + // Real library would hang if this assert fails. + ASSERT_MSG(request_id > user_ctx_id << 0x20, "Too many requests!"); + user_context->requests[request_id] = new OrbisNpWebApiRequest{}; + + auto& request = user_context->requests[request_id]; + request->parentContext = context; + request->userCount = 0; + request->requestId = request_id; + request->userMethod = method; + request->multipart = isMultipart; + request->aborted = false; + + if (pApiGroup != nullptr) { + request->userApiGroup = std::string(pApiGroup); + } + + if (pPath != nullptr) { + request->userPath = std::string(pPath); + } + + if (pContentParameter != nullptr) { + request->userContentLength = pContentParameter->contentLength; + if (pContentParameter->pContentType != nullptr) { + request->userContentType = std::string(pContentParameter->pContentType); + } + } + + if (pInternalArgs != nullptr) { + ASSERT_MSG(pInternalArgs->unk_0 == nullptr && pInternalArgs->unk_1 == nullptr && + pInternalArgs->unk_2 == nullptr, + "Internal arguments for requests not supported"); + } + + unlockContext(user_context->parentContext); + + if (pRequestId != nullptr) { + *pRequestId = request->requestId; + } + + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +OrbisNpWebApiRequest* findRequest(OrbisNpWebApiUserContext* userContext, s64 requestId) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + if (userContext->requests.contains(requestId)) { + return userContext->requests[requestId]; + } + + return nullptr; +} + +OrbisNpWebApiRequest* findRequestAndMarkBusy(OrbisNpWebApiUserContext* userContext, s64 requestId) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + if (userContext->requests.contains(requestId)) { + auto& request = userContext->requests[requestId]; + request->userCount++; + return request; + } + + return nullptr; +} + +bool isRequestBusy(OrbisNpWebApiRequest* request) { + std::scoped_lock lk{request->parentContext->contextLock}; + return request->userCount > 1; +} + +s32 setRequestTimeout(s64 requestId, u32 timeout) { + OrbisNpWebApiContext* context = findAndValidateContext(requestId >> 0x30); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, requestId >> 0x20); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiRequest* request = findRequestAndMarkBusy(user_context, requestId); + if (request == nullptr) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND; + } + + request->requestTimeout = timeout; + + releaseRequest(request); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +void startRequestTimer(OrbisNpWebApiRequest* request) { + if (request->requestTimeout != 0 && request->requestEndTime == 0) { + request->requestEndTime = Kernel::sceKernelGetProcessTime() + request->requestTimeout; + } +} + +void checkRequestTimeout(OrbisNpWebApiRequest* request) { + u64 time = Kernel::sceKernelGetProcessTime(); + if (!request->timedOut && request->requestEndTime != 0 && request->requestEndTime < time) { + request->timedOut = true; + abortRequest(request->requestId); + } +} + +s32 sendRequest(s64 requestId, s32 partIndex, const void* pData, u64 dataSize, s8 flag, + const OrbisNpWebApiResponseInformationOption* pRespInfoOption) { + OrbisNpWebApiContext* context = findAndValidateContext(requestId >> 0x30); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, requestId >> 0x20); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiRequest* request = findRequestAndMarkBusy(user_context, requestId); + if (request == nullptr) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND; + } + + startRequestTimer(request); + + // TODO: multipart logic + + if (g_sdk_ver >= Common::ElfInfo::FW_25 && !request->sent) { + request->sent = true; + } + + lockContext(context); + if (!request->timedOut && request->aborted) { + unlockContext(context); + releaseRequest(request); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_ABORTED; + } + + unlockContext(context); + + // Stubbing sceNpManagerIntGetSigninState call with a config check. + if (!Config::getPSNSignedIn()) { + releaseRequest(request); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_NOT_SIGNED_IN; + } + + LOG_ERROR(Lib_NpWebApi, + "(STUBBED) called, requestId = {:#x}, pApiGroup = '{}', pPath = '{}', pContentType = " + "'{}', method = {}, multipart = {}", + requestId, request->userApiGroup, request->userPath, request->userContentType, + magic_enum::enum_name(request->userMethod), request->multipart); + + releaseRequest(request); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +s32 abortRequestInternal(OrbisNpWebApiContext* context, OrbisNpWebApiUserContext* userContext, + OrbisNpWebApiRequest* request) { + if (context == nullptr || userContext == nullptr || request == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + std::scoped_lock lk{context->contextLock}; + if (request->aborted) { + return ORBIS_OK; + } + + request->aborted = true; + + // TODO: Should also abort any Np requests and Http requests tied to this request. + + return ORBIS_OK; +} + +s32 abortRequest(s64 requestId) { + OrbisNpWebApiContext* context = findAndValidateContext(requestId >> 0x30); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, requestId >> 0x20); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiRequest* request = findRequest(user_context, requestId); + if (request == nullptr) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND; + } + + s32 result = abortRequestInternal(context, user_context, request); + + releaseUserContext(user_context); + releaseContext(context); + return result; +} + +void releaseRequest(OrbisNpWebApiRequest* request) { + std::scoped_lock lk{request->parentContext->contextLock}; + request->userCount--; +} + +s32 deleteRequest(s64 requestId) { + OrbisNpWebApiContext* context = findAndValidateContext(requestId >> 0x30); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + lockContext(context); + OrbisNpWebApiUserContext* user_context = findUserContext(context, requestId >> 0x20); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiRequest* request = findRequestAndMarkBusy(user_context, requestId); + if (request == nullptr) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND; + } + + if (g_sdk_ver < Common::ElfInfo::FW_40 && isRequestBusy(request)) { + releaseRequest(request); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_REQUEST_BUSY; + } + + abortRequestInternal(context, user_context, request); + while (isRequestBusy(request)) { + unlockContext(context); + Kernel::sceKernelUsleep(50000); + lockContext(context); + } + + releaseRequest(request); + user_context->requests.erase(request->requestId); + + releaseUserContext(user_context); + unlockContext(context); + releaseContext(context); + return ORBIS_OK; +} + +s32 createHandleInternal(OrbisNpWebApiContext* context) { + g_handle_count++; + if (g_handle_count >= 0xf0000000) { + g_handle_count = 1; + } + + std::scoped_lock lk{context->contextLock}; + + s32 handle_id = g_handle_count; + context->handles[handle_id] = new OrbisNpWebApiHandle{}; + auto& handle = context->handles[handle_id]; + handle->handleId = handle_id; + handle->userCount = 0; + handle->aborted = false; + handle->deleted = false; + + if (g_sdk_ver >= Common::ElfInfo::FW_30) { + context->timerHandles[handle_id] = new OrbisNpWebApiTimerHandle{}; + auto& timer_handle = context->timerHandles[handle_id]; + timer_handle->handleId = handle_id; + timer_handle->timedOut = false; + timer_handle->handleTimeout = 0; + timer_handle->handleEndTime = 0; + } + + return handle_id; +} + +s32 createHandle(s32 libCtxId) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = createHandleInternal(context); + releaseContext(context); + return result; +} + +s32 setHandleTimeoutInternal(OrbisNpWebApiContext* context, s32 handleId, u32 timeout) { + std::scoped_lock lk{context->contextLock}; + if (!context->timerHandles.contains(handleId)) { + return ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND; + } + auto& handle = context->handles[handleId]; + handle->userCount++; + + auto& timer_handle = context->timerHandles[handleId]; + timer_handle->handleTimeout = timeout; + + handle->userCount--; + return ORBIS_OK; +} + +s32 setHandleTimeout(s32 libCtxId, s32 handleId, u32 timeout) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = setHandleTimeoutInternal(context, handleId, timeout); + releaseContext(context); + return result; +} + +void startHandleTimer(OrbisNpWebApiContext* context, s32 handleId) { + std::scoped_lock lk{context->contextLock}; + if (!context->timerHandles.contains(handleId)) { + return; + } + auto& timer_handle = context->timerHandles[handleId]; + if (timer_handle->handleTimeout == 0) { + return; + } + timer_handle->handleEndTime = Kernel::sceKernelGetProcessTime() + timer_handle->handleTimeout; +} + +void releaseHandle(OrbisNpWebApiContext* context, OrbisNpWebApiHandle* handle) { + if (handle != nullptr) { + std::scoped_lock lk{context->contextLock}; + handle->userCount--; + } +} + +s32 getHandle(OrbisNpWebApiContext* context, s32 handleId, OrbisNpWebApiHandle** handleOut) { + std::scoped_lock lk{context->contextLock}; + if (!context->handles.contains(handleId)) { + return ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND; + } + auto& handle = context->handles[handleId]; + handle->userCount++; + if (handleOut != nullptr) { + *handleOut = handle; + } + return ORBIS_OK; +} + +s32 abortHandle(s32 libCtxId, s32 handleId) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiHandle* handle; + s32 result = getHandle(context, handleId, &handle); + if (result == ORBIS_OK) { + std::scoped_lock lk{context->contextLock}; + handle->aborted = true; + // TODO: sceNpAsmClientAbortRequest call + releaseHandle(context, handle); + } + + releaseContext(context); + return result; +} + +s32 deleteHandleInternal(OrbisNpWebApiContext* context, s32 handleId) { + lockContext(context); + if (!context->handles.contains(handleId)) { + return ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND; + } + + auto& handle = context->handles[handleId]; + if (g_sdk_ver >= Common::ElfInfo::FW_40) { + if (handle->deleted) { + unlockContext(context); + return ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND; + } + handle->deleted = true; + unlockContext(context); + abortHandle(context->libCtxId, handleId); + lockContext(context); + handle->userCount++; + while (handle->userCount > 1) { + handle->userCount--; + unlockContext(context); + Kernel::sceKernelUsleep(50000); + lockContext(context); + handle->userCount++; + } + handle->userCount--; + } else if (handle->userCount > 0) { + unlockContext(context); + return ORBIS_NP_WEBAPI_ERROR_HANDLE_BUSY; + } + + context->handles.erase(handleId); + + if (g_sdk_ver >= Common::ElfInfo::FW_30 && context->timerHandles.contains(handleId)) { + auto& timer_handle = context->timerHandles[handleId]; + context->timerHandles.erase(handleId); + } + + unlockContext(context); + return ORBIS_OK; +} + +s32 deleteHandle(s32 libCtxId, s32 handleId) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = deleteHandleInternal(context, handleId); + releaseContext(context); + return result; +} + +s32 createPushEventFilterInternal(OrbisNpWebApiContext* context, + const OrbisNpWebApiPushEventFilterParameter* pFilterParam, + u64 filterParamNum) { + std::scoped_lock lk{context->contextLock}; + g_push_event_filter_count++; + if (g_push_event_filter_count >= 0xf0000000) { + g_push_event_filter_count = 1; + } + s32 filterId = g_push_event_filter_count; + + context->pushEventFilters[filterId] = new OrbisNpWebApiPushEventFilter{}; + auto& filter = context->pushEventFilters[filterId]; + filter->parentContext = context; + filter->filterId = filterId; + + if (pFilterParam != nullptr && filterParamNum != 0) { + for (u64 param_idx = 0; param_idx < filterParamNum; param_idx++) { + OrbisNpWebApiPushEventFilterParameter copy = OrbisNpWebApiPushEventFilterParameter{}; + memcpy(©, &pFilterParam[param_idx], sizeof(OrbisNpWebApiPushEventFilterParameter)); + filter->filterParams.emplace_back(copy); + } + } + return filterId; +} + +s32 createPushEventFilter(s32 libCtxId, const OrbisNpWebApiPushEventFilterParameter* pFilterParam, + u64 filterParamNum) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = createPushEventFilterInternal(context, pFilterParam, filterParamNum); + releaseContext(context); + return result; +} + +s32 deletePushEventFilterInternal(OrbisNpWebApiContext* context, s32 filterId) { + std::scoped_lock lk{context->contextLock}; + if (!context->pushEventFilters.contains(filterId)) { + return ORBIS_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND; + } + + context->pushEventFilters[filterId]->filterParams.clear(); + context->pushEventFilters.erase(filterId); + return ORBIS_OK; +} + +s32 deletePushEventFilter(s32 libCtxId, s32 filterId) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = deletePushEventFilterInternal(context, filterId); + releaseContext(context); + return result; +} + +s32 registerPushEventCallbackInternal(OrbisNpWebApiUserContext* userContext, s32 filterId, + OrbisNpWebApiPushEventCallback cbFunc, void* pUserArg) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + g_registered_callback_count++; + if (g_registered_callback_count >= 0xf0000000) { + g_registered_callback_count = 1; + } + s32 cbId = g_registered_callback_count; + + userContext->pushEventCallbacks[cbId] = new OrbisNpWebApiRegisteredPushEventCallback{}; + auto& cb = userContext->pushEventCallbacks[cbId]; + cb->callbackId = cbId; + cb->filterId = filterId; + cb->cbFunc = cbFunc; + cb->pUserArg = pUserArg; + + return cbId; +} + +s32 registerPushEventCallback(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiPushEventCallback cbFunc, void* pUserArg) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (g_sdk_ver >= Common::ElfInfo::FW_25 && !context->pushEventFilters.contains(filterId)) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND; + } + + s32 result = registerPushEventCallbackInternal(user_context, filterId, cbFunc, pUserArg); + releaseUserContext(user_context); + releaseContext(context); + return result; +} + +s32 unregisterPushEventCallback(s32 titleUserCtxId, s32 callbackId) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (!user_context->pushEventCallbacks.contains(callbackId)) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND; + } + + lockContext(context); + user_context->pushEventCallbacks.erase(callbackId); + unlockContext(context); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +s32 createServicePushEventFilterInternal( + OrbisNpWebApiContext* context, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiServicePushEventFilterParameter* pFilterParam, u64 filterParamNum) { + std::scoped_lock lk{context->contextLock}; + if (!context->handles.contains(handleId)) { + return ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND; + } + auto& handle = context->handles[handleId]; + handle->userCount++; + + if (pNpServiceName != nullptr && !Config::getPSNSignedIn()) { + // Seems sceNpManagerIntGetUserList fails? + LOG_DEBUG(Lib_NpWebApi, "Cannot create service push event while PSN is disabled"); + handle->userCount--; + return ORBIS_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND; + } + + g_service_push_event_filter_count++; + if (g_service_push_event_filter_count >= 0xf0000000) { + g_service_push_event_filter_count = 1; + } + s32 filterId = g_service_push_event_filter_count; + + context->servicePushEventFilters[filterId] = new OrbisNpWebApiServicePushEventFilter{}; + auto& filter = context->servicePushEventFilters[filterId]; + filter->parentContext = context; + filter->filterId = filterId; + + if (pNpServiceName == nullptr) { + filter->internal = true; + } else { + // TODO: if pNpServiceName is non-null, create an np request for this filter. + LOG_ERROR(Lib_NpWebApi, "Np behavior not handled"); + filter->npServiceName = std::string(pNpServiceName); + } + + filter->npServiceLabel = npServiceLabel; + + if (pFilterParam != nullptr && filterParamNum != 0) { + for (u64 param_idx = 0; param_idx < filterParamNum; param_idx++) { + OrbisNpWebApiServicePushEventFilterParameter copy = + OrbisNpWebApiServicePushEventFilterParameter{}; + memcpy(©, &pFilterParam[param_idx], + sizeof(OrbisNpWebApiServicePushEventFilterParameter)); + filter->filterParams.emplace_back(copy); + } + } + + handle->userCount--; + return filterId; +} + +s32 createServicePushEventFilter(s32 libCtxId, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiServicePushEventFilterParameter* pFilterParam, + u64 filterParamNum) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + startHandleTimer(context, handleId); + s32 result = createServicePushEventFilterInternal(context, handleId, pNpServiceName, + npServiceLabel, pFilterParam, filterParamNum); + releaseContext(context); + return result; +} + +s32 deleteServicePushEventFilterInternal(OrbisNpWebApiContext* context, s32 filterId) { + std::scoped_lock lk{context->contextLock}; + if (!context->servicePushEventFilters.contains(filterId)) { + return ORBIS_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND; + } + + context->servicePushEventFilters[filterId]->filterParams.clear(); + context->servicePushEventFilters.erase(filterId); + return ORBIS_OK; +} + +s32 deleteServicePushEventFilter(s32 libCtxId, s32 filterId) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = deleteServicePushEventFilterInternal(context, filterId); + releaseContext(context); + return result; +} + +s32 registerServicePushEventCallbackInternal( + OrbisNpWebApiUserContext* userContext, s32 filterId, + OrbisNpWebApiServicePushEventCallback cbFunc, + OrbisNpWebApiInternalServicePushEventCallback intCbFunc, + OrbisNpWebApiInternalServicePushEventCallbackA intCbFuncA, void* pUserArg) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + if (cbFunc == nullptr && intCbFunc == nullptr && intCbFuncA == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + + g_registered_callback_count++; + if (g_registered_callback_count >= 0xf0000000) { + g_registered_callback_count = 1; + } + s32 cbId = g_registered_callback_count; + + userContext->servicePushEventCallbacks[cbId] = + new OrbisNpWebApiRegisteredServicePushEventCallback{}; + auto& cb = userContext->servicePushEventCallbacks[cbId]; + cb->callbackId = cbId; + cb->filterId = filterId; + cb->cbFunc = cbFunc; + cb->internalCbFunc = intCbFunc; + cb->internalCbFuncA = intCbFuncA; + cb->pUserArg = pUserArg; + + return cbId; +} + +s32 registerServicePushEventCallback(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiServicePushEventCallback cbFunc, + OrbisNpWebApiInternalServicePushEventCallback intCbFunc, + OrbisNpWebApiInternalServicePushEventCallbackA intCbFuncA, + void* pUserArg) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (g_sdk_ver >= Common::ElfInfo::FW_25 && + !context->servicePushEventFilters.contains(filterId)) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND; + } + + s32 result = registerServicePushEventCallbackInternal(user_context, filterId, cbFunc, intCbFunc, + intCbFuncA, pUserArg); + releaseUserContext(user_context); + releaseContext(context); + return result; +} + +s32 unregisterServicePushEventCallback(s32 titleUserCtxId, s32 callbackId) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (!user_context->servicePushEventCallbacks.contains(callbackId)) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_CALLBACK_NOT_FOUND; + } + + lockContext(context); + user_context->servicePushEventCallbacks.erase(callbackId); + unlockContext(context); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +s32 createExtendedPushEventFilterInternal( + OrbisNpWebApiContext* context, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiExtdPushEventFilterParameter* pFilterParam, u64 filterParamNum, + bool internal) { + std::scoped_lock lk{context->contextLock}; + if (!context->handles.contains(handleId)) { + return ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND; + } + auto& handle = context->handles[handleId]; + handle->userCount++; + + if (pNpServiceName != nullptr && !Config::getPSNSignedIn()) { + // Seems sceNpManagerIntGetUserList fails? + LOG_DEBUG(Lib_NpWebApi, "Cannot create extended push event while PSN is disabled"); + handle->userCount--; + return ORBIS_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND; + } + + g_extended_push_event_filter_count++; + if (g_extended_push_event_filter_count >= 0xf0000000) { + g_extended_push_event_filter_count = 1; + } + s32 filterId = g_extended_push_event_filter_count; + + context->extendedPushEventFilters[filterId] = new OrbisNpWebApiExtendedPushEventFilter{}; + auto& filter = context->extendedPushEventFilters[filterId]; + filter->internal = internal; + filter->parentContext = context; + filter->filterId = filterId; + + if (pNpServiceName == nullptr) { + npServiceLabel = ORBIS_NP_INVALID_SERVICE_LABEL; + } else { + // TODO: if pNpServiceName is non-null, create an np request for this filter. + LOG_ERROR(Lib_NpWebApi, "Np behavior not handled"); + filter->npServiceName = std::string(pNpServiceName); + } + + filter->npServiceLabel = npServiceLabel; + + if (pFilterParam != nullptr && filterParamNum != 0) { + for (u64 param_idx = 0; param_idx < filterParamNum; param_idx++) { + OrbisNpWebApiExtdPushEventFilterParameter copy = + OrbisNpWebApiExtdPushEventFilterParameter{}; + memcpy(©, &pFilterParam[param_idx], + sizeof(OrbisNpWebApiExtdPushEventFilterParameter)); + filter->filterParams.emplace_back(copy); + // TODO: Every parameter is registered with an extended data filter through + // sceNpPushRegisterExtendedDataFilter + } + } + + handle->userCount--; + return filterId; +} + +s32 createExtendedPushEventFilter(s32 libCtxId, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiExtdPushEventFilterParameter* pFilterParam, + u64 filterParamNum, bool internal) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + startHandleTimer(context, handleId); + s32 result = createExtendedPushEventFilterInternal( + context, handleId, pNpServiceName, npServiceLabel, pFilterParam, filterParamNum, internal); + releaseContext(context); + return result; +} + +s32 deleteExtendedPushEventFilterInternal(OrbisNpWebApiContext* context, s32 filterId) { + std::scoped_lock lk{context->contextLock}; + if (!context->extendedPushEventFilters.contains(filterId)) { + return ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_FILTER_NOT_FOUND; + } + + context->extendedPushEventFilters[filterId]->filterParams.clear(); + context->extendedPushEventFilters.erase(filterId); + return ORBIS_OK; +} + +s32 deleteExtendedPushEventFilter(s32 libCtxId, s32 filterId) { + OrbisNpWebApiContext* context = findAndValidateContext(libCtxId); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = deleteExtendedPushEventFilterInternal(context, filterId); + releaseContext(context); + return result; +} + +s32 registerExtdPushEventCallbackInternal(OrbisNpWebApiUserContext* userContext, s32 filterId, + OrbisNpWebApiExtdPushEventCallback cbFunc, + OrbisNpWebApiExtdPushEventCallbackA cbFuncA, + void* pUserArg) { + std::scoped_lock lk{userContext->parentContext->contextLock}; + + g_registered_callback_count++; + if (g_registered_callback_count >= 0xf0000000) { + g_registered_callback_count = 1; + } + + if (cbFunc == nullptr && cbFuncA == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT; + } + s32 cbId = g_registered_callback_count; + + userContext->extendedPushEventCallbacks[cbId] = + new OrbisNpWebApiRegisteredExtendedPushEventCallback{}; + auto& cb = userContext->extendedPushEventCallbacks[cbId]; + cb->callbackId = cbId; + cb->filterId = filterId; + cb->cbFunc = cbFunc; + cb->cbFuncA = cbFuncA; + cb->pUserArg = pUserArg; + + return cbId; +} + +s32 registerExtdPushEventCallback(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiExtdPushEventCallback cbFunc, + OrbisNpWebApiExtdPushEventCallbackA cbFuncA, void* pUserArg) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (!context->extendedPushEventFilters.contains(filterId)) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_FILTER_NOT_FOUND; + } + + s32 result = + registerExtdPushEventCallbackInternal(user_context, filterId, cbFunc, cbFuncA, pUserArg); + releaseUserContext(user_context); + releaseContext(context); + return result; +} + +s32 registerExtdPushEventCallbackA(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiExtdPushEventCallbackA cbFunc, void* pUserArg) { + return registerExtdPushEventCallback(titleUserCtxId, filterId, nullptr, cbFunc, pUserArg); +} + +s32 unregisterExtdPushEventCallback(s32 titleUserCtxId, s32 callbackId) { + OrbisNpWebApiContext* context = findAndValidateContext(titleUserCtxId >> 0x10); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, titleUserCtxId); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (!user_context->extendedPushEventCallbacks.contains(callbackId)) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_CALLBACK_NOT_FOUND; + } + + lockContext(context); + user_context->extendedPushEventCallbacks.erase(callbackId); + unlockContext(context); + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI getHttpRequestIdFromRequest(OrbisNpWebApiRequest* request) + +{ + return request->requestId; +} + +s32 PS4_SYSV_ABI getHttpStatusCodeInternal(s64 requestId, s32* out_status_code) { + s32 status_code; + OrbisNpWebApiContext* context = findAndValidateContext(requestId >> 0x30); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, requestId >> 0x20); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiRequest* request = findRequest(user_context, requestId); + if (request == nullptr) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND; + } + + // Query HTTP layer + { + int32_t httpReqId = getHttpRequestIdFromRequest(request); + s32 err = Libraries::Http::sceHttpGetStatusCode(httpReqId, &status_code); + + if (out_status_code != nullptr) + *out_status_code = status_code; + + releaseRequest(request); + releaseUserContext(user_context); + releaseContext(context); + + return err; + } +} + +void PS4_SYSV_ABI setRequestEndTime(OrbisNpWebApiRequest* request) { + u64 time; + if ((request->requestTimeout != 0) && (request->requestEndTime == 0)) { + time = Libraries::Kernel::sceKernelGetProcessTime(); + request->requestEndTime = (u64)request->requestTimeout + time; + } +} + +void PS4_SYSV_ABI clearRequestEndTime(OrbisNpWebApiRequest* req) { + req->requestEndTime = 0; + return; +} + +bool PS4_SYSV_ABI hasRequestTimedOut(OrbisNpWebApiRequest* request) { + return request->timedOut; +} + +bool PS4_SYSV_ABI isRequestAborted(OrbisNpWebApiRequest* request) { + return request->aborted; +} + +void PS4_SYSV_ABI setRequestState(OrbisNpWebApiRequest* request, u8 state) { + request->requestState = state; +} + +u64 PS4_SYSV_ABI copyRequestData(OrbisNpWebApiRequest* request, void* data, u64 size) { + u64 readSize = 0; + + if (request->remainingData != 0) { + u64 remainingSize = request->remainingData - request->readOffset; + + if (remainingSize != 0) { + if (remainingSize < size) { + size = remainingSize; + } + memcpy(data, request->data + request->readOffset, size); + request->readOffset += static_cast(size); + readSize = size; + } + } + return readSize; +} + +s32 PS4_SYSV_ABI readDataInternal(s64 requestId, void* pData, u64 size) { + u32 offset; + s32 result; + u64 remainingSize; + u64 bytesCopied; + + OrbisNpWebApiContext* context = findAndValidateContext(requestId >> 0x30); + if (context == nullptr) { + return ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiUserContext* user_context = findUserContext(context, requestId >> 0x20); + if (user_context == nullptr) { + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND; + } + + OrbisNpWebApiRequest* request = findRequest(user_context, requestId); + if (request == nullptr) { + releaseUserContext(user_context); + releaseContext(context); + return ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND; + } + + setRequestEndTime(request); + + bytesCopied = copyRequestData(request, pData, size); + offset = (u32)bytesCopied; + remainingSize = size - offset; + + // If caller wants more data than buffered + if (remainingSize != 0) { + lockContext(context); + setRequestState(request, 5); // TODO add request states? + + if (!hasRequestTimedOut(request) && isRequestAborted(request)) { + unlockContext(context); + offset = ORBIS_NP_WEBAPI_ERROR_ABORTED; + } else { + unlockContext(context); + + int32_t httpReqId = getHttpRequestIdFromRequest(request); + int32_t httpRead = + Libraries::Http::sceHttpReadData(httpReqId, (u8*)pData + offset, remainingSize); + + if (httpRead < 0) + httpRead = 0; + + offset += httpRead; + } + } + + // Final state resolution + lockContext(context); + setRequestState(request, 0); + + if (hasRequestTimedOut(request)) { + result = ORBIS_NP_WEBAPI_ERROR_TIMEOUT; + } else if (isRequestAborted(request)) { + result = ORBIS_NP_WEBAPI_ERROR_ABORTED; + } else { + result = offset; + } + + unlockContext(context); + + // Cleanup + clearRequestEndTime(request); + releaseRequest(request); + releaseUserContext(user_context); + releaseContext(context); + + return result; +} + +}; // namespace Libraries::Np::NpWebApi \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api_internal.h b/src/core/libraries/np/np_web_api_internal.h new file mode 100644 index 000000000..571df0ab9 --- /dev/null +++ b/src/core/libraries/np/np_web_api_internal.h @@ -0,0 +1,301 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np/np_web_api.h" +#include "core/libraries/np/np_web_api_error.h" + +namespace Libraries::Np::NpWebApi { + +// Structs reference each other, so declare them before their contents. +struct OrbisNpWebApiContext; +struct OrbisNpWebApiUserContext; +struct OrbisNpWebApiRequest; +struct OrbisNpWebApiHandle; +struct OrbisNpWebApiTimerHandle; +struct OrbisNpWebApiPushEventFilter; +struct OrbisNpWebApiServicePushEventFilter; +struct OrbisNpWebApiExtendedPushEventFilter; +struct OrbisNpWebApiRegisteredPushEventCallback; +struct OrbisNpWebApiRegisteredServicePushEventCallback; +struct OrbisNpWebApiRegisteredExtendedPushEventCallback; + +struct OrbisNpWebApiContext { + s32 type; + s32 userCount; + s32 libCtxId; + s32 libHttpCtxId; + std::recursive_mutex contextLock; + std::map userContexts; + std::map handles; + std::map timerHandles; + std::map pushEventFilters; + std::map servicePushEventFilters; + std::map extendedPushEventFilters; + std::string name; + bool terminated; +}; + +struct OrbisNpWebApiUserContext { + OrbisNpWebApiContext* parentContext; + s32 userCount; + s32 userCtxId; + Libraries::UserService::OrbisUserServiceUserId userId; + std::map requests; + std::map pushEventCallbacks; + std::map servicePushEventCallbacks; + std::map extendedPushEventCallbacks; + bool deleted; + OrbisNpWebApiNotificationCallback notificationCallbackFunction; + void* pNotificationCallbackUserArgs; +}; + +struct OrbisNpWebApiRequest { + OrbisNpWebApiContext* parentContext; + s32 userCount; + s64 requestId; + std::string userApiGroup; + std::string userPath; + OrbisNpWebApiHttpMethod userMethod; + u64 userContentLength; + std::string userContentType; + bool multipart; + bool aborted; + bool sent; + u32 requestTimeout; + u64 requestEndTime; + bool timedOut; + // not sure Stephen + u8 requestState; + u64 remainingData; + u32 readOffset; + char data[64]; +}; + +struct OrbisNpWebApiHandle { + s32 handleId; + bool aborted; + bool deleted; + s32 userCount; +}; + +struct OrbisNpWebApiTimerHandle { + s32 handleId; + u32 handleTimeout; + u64 handleEndTime; + bool timedOut; +}; + +struct OrbisNpWebApiPushEventFilter { + s32 filterId; + std::vector filterParams; + OrbisNpWebApiContext* parentContext; +}; + +struct OrbisNpWebApiServicePushEventFilter { + s32 filterId; + bool internal; + std::vector filterParams; + std::string npServiceName; + OrbisNpServiceLabel npServiceLabel; + OrbisNpWebApiContext* parentContext; +}; + +struct OrbisNpWebApiExtendedPushEventFilter { + s32 filterId; + bool internal; + std::vector filterParams; + std::string npServiceName; + OrbisNpServiceLabel npServiceLabel; + OrbisNpWebApiContext* parentContext; +}; + +struct OrbisNpWebApiRegisteredPushEventCallback { + s32 callbackId; + s32 filterId; + OrbisNpWebApiPushEventCallback cbFunc; + void* pUserArg; +}; + +struct OrbisNpWebApiRegisteredServicePushEventCallback { + s32 callbackId; + s32 filterId; + OrbisNpWebApiServicePushEventCallback cbFunc; + OrbisNpWebApiInternalServicePushEventCallback internalCbFunc; + // Note: real struct stores both internal callbacks in one field + OrbisNpWebApiInternalServicePushEventCallbackA internalCbFuncA; + void* pUserArg; +}; + +struct OrbisNpWebApiRegisteredExtendedPushEventCallback { + s32 callbackId; + s32 filterId; + OrbisNpWebApiExtdPushEventCallback cbFunc; + // Note: real struct stores both callbacks in one field + OrbisNpWebApiExtdPushEventCallbackA cbFuncA; + void* pUserArg; +}; + +// General functions +s32 initializeLibrary(); // FUN_01001450 +s32 getCompiledSdkVersion(); // FUN_01001440 + +// Library context functions +s32 createLibraryContext(s32 libHttpCtxId, u64 poolSize, const char* name, + s32 type); // FUN_01006970 +OrbisNpWebApiContext* findAndValidateContext(s32 libCtxId, s32 flag = 0); // FUN_01006860 +void releaseContext(OrbisNpWebApiContext* context); // FUN_01006fc0 +bool isContextTerminated(OrbisNpWebApiContext* context); // FUN_01006910 +bool isContextBusy(OrbisNpWebApiContext* context); // FUN_01008a50 +bool areContextHandlesBusy(OrbisNpWebApiContext* context); // FUN_01008c20 +void lockContext(OrbisNpWebApiContext* context); // FUN_010072e0 +void unlockContext(OrbisNpWebApiContext* context); // FUN_010072f0 +void markContextAsTerminated(OrbisNpWebApiContext* context); // FUN_01008bf0 +void checkContextTimeout(OrbisNpWebApiContext* context); // FUN_01008ad0 +void checkTimeout(); // FUN_01003700 +s32 deleteContext(s32 libCtxId); // FUN_01006c70 +s32 terminateContext(s32 libCtxId); // FUN_010014b0 + +// User context functions +OrbisNpWebApiUserContext* findUserContextByUserId( + OrbisNpWebApiContext* context, + Libraries::UserService::OrbisUserServiceUserId userId); // FUN_010075c0 +OrbisNpWebApiUserContext* findUserContext(OrbisNpWebApiContext* context, + s32 userCtxId); // FUN_01007530 +s32 createUserContextWithOnlineId(s32 libCtxId, OrbisNpOnlineId* onlineId); // FUN_010016a0 +s32 createUserContext(s32 libCtxId, + Libraries::UserService::OrbisUserServiceUserId userId); // FUN_010015c0 +s32 registerNotificationCallback(s32 titleUserCtxId, OrbisNpWebApiNotificationCallback cbFunc, + void* pUserArg); // FUN_01003770 +s32 unregisterNotificationCallback(s32 titleUserCtxId); // FUN_01003800 +bool isUserContextBusy(OrbisNpWebApiUserContext* userContext); // FUN_0100ea40 +bool areUserContextRequestsBusy(OrbisNpWebApiUserContext* userContext); // FUN_0100d1f0 +void releaseUserContext(OrbisNpWebApiUserContext* userContext); // FUN_0100caa0 +void checkUserContextTimeout(OrbisNpWebApiUserContext* userContext); // FUN_0100ea90 +s32 deleteUserContext(s32 userCtxId); // FUN_01001710 + +// Request functions +s32 createRequest(s32 titleUserCtxId, const char* pApiGroup, const char* pPath, + OrbisNpWebApiHttpMethod method, + const OrbisNpWebApiContentParameter* pContentParameter, + const OrbisNpWebApiIntCreateRequestExtraArgs* pInternalArgs, s64* pRequestId, + bool isMultipart); // FUN_01001850 +OrbisNpWebApiRequest* findRequest(OrbisNpWebApiUserContext* userContext, + s64 requestId); // FUN_0100d3a0 +OrbisNpWebApiRequest* findRequestAndMarkBusy(OrbisNpWebApiUserContext* userContext, + s64 requestId); // FUN_0100d330 +bool isRequestBusy(OrbisNpWebApiRequest* request); // FUN_0100c1b0 +s32 setRequestTimeout(s64 requestId, u32 timeout); // FUN_01003610 +void startRequestTimer(OrbisNpWebApiRequest* request); // FUN_0100c0d0 +void checkRequestTimeout(OrbisNpWebApiRequest* request); // FUN_0100c130 +s32 sendRequest( + s64 requestId, s32 partIndex, const void* data, u64 dataSize, s8 flag, + const OrbisNpWebApiResponseInformationOption* pResponseInformationOption); // FUN_01001c50 +s32 abortRequestInternal(OrbisNpWebApiContext* context, OrbisNpWebApiUserContext* userContext, + OrbisNpWebApiRequest* request); // FUN_01001b70 +s32 abortRequest(s64 requestId); // FUN_01002c70 +void releaseRequest(OrbisNpWebApiRequest* request); // FUN_01009fb0 +s32 deleteRequest(s64 requestId); // FUN_010019a0 + +// Handle functions +s32 createHandleInternal(OrbisNpWebApiContext* context); // FUN_01007730 +s32 createHandle(s32 libCtxId); // FUN_01002ee0 +s32 setHandleTimeoutInternal(OrbisNpWebApiContext* context, s32 handleId, + u32 timeout); // FUN_01007ed0 +s32 setHandleTimeout(s32 libCtxId, s32 handleId, u32 timeout); // FUN_010036b0 +void startHandleTimer(OrbisNpWebApiContext* context, s32 handleId); // FUN_01007fd0 +void releaseHandle(OrbisNpWebApiContext* context, OrbisNpWebApiHandle* handle); // FUN_01007ea0 +s32 getHandle(OrbisNpWebApiContext* context, s32 handleId, + OrbisNpWebApiHandle** handleOut); // FUN_01007e20 +s32 abortHandle(s32 libCtxId, s32 handleId); // FUN_01003390 +s32 deleteHandleInternal(OrbisNpWebApiContext* context, s32 handleId); // FUN_01007a00 +s32 deleteHandle(s32 libCtxId, s32 handleId); // FUN_01002f20 + +// Push event filter functions +s32 createPushEventFilterInternal(OrbisNpWebApiContext* context, + const OrbisNpWebApiPushEventFilterParameter* pFilterParam, + u64 filterParamNum); // FUN_01008040 +s32 createPushEventFilter(s32 libCtxId, const OrbisNpWebApiPushEventFilterParameter* pFilterParam, + u64 filterParamNum); // FUN_01002d10 +s32 deletePushEventFilterInternal(OrbisNpWebApiContext* context, s32 filterId); // FUN_01008180 +s32 deletePushEventFilter(s32 libCtxId, s32 filterId); // FUN_01002d60 + +// Push event callback functions +s32 registerPushEventCallbackInternal(OrbisNpWebApiUserContext* userContext, s32 filterId, + OrbisNpWebApiPushEventCallback cbFunc, + void* userArg); // FUN_0100d450 +s32 registerPushEventCallback(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiPushEventCallback cbFunc, + void* pUserArg); // FUN_01002da0 +s32 unregisterPushEventCallback(s32 titleUserCtxId, s32 callbackId); // FUN_01002e50 + +// Service push event filter functions +s32 createServicePushEventFilterInternal( + OrbisNpWebApiContext* context, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiServicePushEventFilterParameter* pFilterParam, + u64 filterParamNum); // FUN_010082f0 +s32 createServicePushEventFilter(s32 libCtxId, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiServicePushEventFilterParameter* pFilterParam, + u64 filterParamNum); // FUN_01002f60 +s32 deleteServicePushEventFilterInternal(OrbisNpWebApiContext* context, + s32 filterId); // FUN_010084f0 +s32 deleteServicePushEventFilter(s32 libCtxId, s32 filterId); // FUN_01002fe0 + +// Service push event callback functions +s32 registerServicePushEventCallbackInternal( + OrbisNpWebApiUserContext* userContext, s32 filterId, + OrbisNpWebApiServicePushEventCallback cbFunc, + OrbisNpWebApiInternalServicePushEventCallback intCbFunc, + OrbisNpWebApiInternalServicePushEventCallbackA intCbFuncA, void* pUserArg); // FUN_0100d8c0 +s32 registerServicePushEventCallback(s32 titleUserCtxId, s32 filterId, + OrbisNpWebApiServicePushEventCallback cbFunc, + OrbisNpWebApiInternalServicePushEventCallback intCbFunc, + OrbisNpWebApiInternalServicePushEventCallbackA intCbFuncA, + void* pUserArg); // FUN_01003030 +s32 unregisterServicePushEventCallback(s32 titleUserCtxId, s32 callbackId); // FUN_010030f0 + +// Extended push event filter functions +s32 createExtendedPushEventFilterInternal( + OrbisNpWebApiContext* context, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiExtdPushEventFilterParameter* pFilterParam, u64 filterParamNum, + bool internal); // FUN_01008680 +s32 createExtendedPushEventFilter(s32 libCtxId, s32 handleId, const char* pNpServiceName, + OrbisNpServiceLabel npServiceLabel, + const OrbisNpWebApiExtdPushEventFilterParameter* pFilterParam, + u64 filterParamNum, bool internal); // FUN_01003180 +s32 deleteExtendedPushEventFilterInternal(OrbisNpWebApiContext* context, + s32 filterId); // FUN_01008880 +s32 deleteExtendedPushEventFilter(s32 libCtxId, s32 filterId); // FUN_01003200 + +// Extended push event callback functions +s32 registerExtdPushEventCallbackInternal(OrbisNpWebApiUserContext* userContext, s32 filterId, + OrbisNpWebApiExtdPushEventCallback cbFunc, + OrbisNpWebApiExtdPushEventCallbackA cbFuncA, + void* pUserArg); // FUN_0100df60 +s32 registerExtdPushEventCallback(s32 userCtxId, s32 filterId, + OrbisNpWebApiExtdPushEventCallback cbFunc, + OrbisNpWebApiExtdPushEventCallbackA cbFuncA, + void* pUserArg); // FUN_01003250 +s32 registerExtdPushEventCallbackA(s32 userCtxId, s32 filterId, + OrbisNpWebApiExtdPushEventCallbackA cbFunc, + void* pUserArg); // FUN_01003240 +s32 unregisterExtdPushEventCallback(s32 titleUserCtxId, s32 callbackId); // FUN_01003300 + +s32 PS4_SYSV_ABI getHttpStatusCodeInternal(s64 requestId, s32* out_status_code); +s32 PS4_SYSV_ABI getHttpRequestIdFromRequest(OrbisNpWebApiRequest* request); +s32 PS4_SYSV_ABI readDataInternal(s64 requestId, void* pData, u64 size); +void PS4_SYSV_ABI setRequestEndTime(OrbisNpWebApiRequest* request); +void PS4_SYSV_ABI clearRequestEndTime(OrbisNpWebApiRequest* req); +bool PS4_SYSV_ABI hasRequestTimedOut(OrbisNpWebApiRequest* request); +void PS4_SYSV_ABI setRequestState(OrbisNpWebApiRequest* request, u8 state); +u64 PS4_SYSV_ABI copyRequestData(OrbisNpWebApiRequest* request, void* data, u64 size); + +}; // namespace Libraries::Np::NpWebApi \ No newline at end of file