From 64d198190fa05d37cbb1fa29c50f967c376fce55 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Tue, 7 Jul 2026 05:40:23 -0500 Subject: [PATCH] Lib.NpWebApi2: Library implementation (#4677) * Fix error definitions * sceNpWebApi2Initialize, sceNpWebApi2InitializeForPresence, sceNpWebApi2IntInitialize, sceNpWebApi2IntInitialize2 Defines classes for various existing objects in the library, and adds support for creating library context objects. * sceNpGetSdkVersion * sceNpWebApi2CreateUserContext * Clang * More clang * sceNpWebApi2GetMemoryPoolStats * stuff * sceNpWebApi2CreateRequest, sceNpWebApi2CreateMultipartRequest * sceNpWebApi2AddHttpRequestHeader * Why bother with pointers when deque exists? * sceNpWebApi2SendRequest * sceNpWebApi2AbortRequest, sceNpWebApi2DeleteRequest * Stub libSceHttp2 calls using libSceHttp Also some bug fixes to make sure requests actually work properly. * Clean things I'm not doing yet Probably done enough to be worth pushing, as now most games just complain from missing endpoints. * Update np_web_api2_context.h * sceNpWebApi2DeleteUserContext * sceNpWebApi2SetRequestTimeout * sceNpWebApi2GetHttpResponseHeaderValue, sceNpWebApi2GetHttpResponseHeaderValueLength * sceNpWebApi2ReadData * Revert "Clean things I'm not doing yet" This reverts commit 598f67e8bb6c090d80b216fb43e0c43c346718d9. * sceNpWebApi2PushEventCreateHandle * Misc stuff * Started work on sceNpWebApi2PushEventCreateFilter The actual filter doesn't create yet * Some touchups * Define base callback for checking push events Will fill out once push event stuff is done * Finish sceNpWebApi2PushEventCreateFilter * Define remaining parts * Logging adjustments, copy filter param extd data keys * sceNpWebApi2PushEventRegisterCallback * Store data_key vectors So they're properly deleted on PushEventFilter destructors (which will be important when I implement the proper functions for that) * sceNpWebApi2PushEventRegisterPushContextCallback * sceNpWebApi2PushEventUnregisterCallback, sceNpWebApi2PushEventUnregisterPushContextCallback * sceNpWebApi2PushEventAbortHandle * sceNpWebApi2PushEventDeleteFilter * sceNpWebApi2PushEventDeleteHandle * Clang * sceNpWebApi2PushEventSetHandleTimeout * sceNpWebApi2AddWebTraceTag * sceNpWebApi2PushEventCreatePushContext, sceNpWebApi2PushEventStartPushContextCallback * Clang * sceNpWebApi2PushEventDeletePushContext * sceNpWebApi2Terminate * Slight fix to sceNpWebApi2DeleteUserContext * sceNpWebApi2CheckTimeout --- CMakeLists.txt | 10 +- src/core/libraries/libs.cpp | 2 +- src/core/libraries/network/http2.cpp | 274 +++-- src/core/libraries/network/http2.h | 89 +- src/core/libraries/np/np_common.cpp | 12 + src/core/libraries/np/np_common.h | 1 + src/core/libraries/np/np_error.h | 38 +- src/core/libraries/np/np_web_api2.cpp | 331 ------- src/core/libraries/np/np_web_api2.h | 40 - .../libraries/np/np_web_api2/np_web_api2.cpp | 522 ++++++++++ .../libraries/np/np_web_api2/np_web_api2.h | 102 ++ .../np/np_web_api2/np_web_api2_context.cpp | 630 ++++++++++++ .../np/np_web_api2/np_web_api2_context.h | 463 +++++++++ .../np/np_web_api2/np_web_api2_internal.cpp | 932 ++++++++++++++++++ .../np/np_web_api2/np_web_api2_internal.h | 48 + .../np/np_web_api2/np_web_api2_push_event.cpp | 64 ++ .../np/np_web_api2/np_web_api2_push_event.h | 168 ++++ 17 files changed, 3173 insertions(+), 553 deletions(-) delete mode 100644 src/core/libraries/np/np_web_api2.cpp delete mode 100644 src/core/libraries/np/np_web_api2.h create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2.cpp create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2.h create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2_context.cpp create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2_context.h create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2_internal.h create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2_push_event.cpp create mode 100644 src/core/libraries/np/np_web_api2/np_web_api2_push_event.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 943969101..fc7ad361a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -649,8 +649,6 @@ set(NP_LIBS src/core/libraries/np/np_error.h src/core/libraries/np/np_tus.h src/core/libraries/np/trophy_ui.cpp src/core/libraries/np/trophy_ui.h - src/core/libraries/np/np_web_api2.cpp - src/core/libraries/np/np_web_api2.h src/core/libraries/np/np_party.cpp src/core/libraries/np/np_party.h src/core/libraries/np/np_auth.cpp @@ -685,6 +683,14 @@ set(NP_LIBS src/core/libraries/np/np_error.h src/core/libraries/np/np_web_api/np_web_api.h src/core/libraries/np/np_web_api/np_web_api_internal.cpp src/core/libraries/np/np_web_api/np_web_api_internal.h + src/core/libraries/np/np_web_api2/np_web_api2.cpp + src/core/libraries/np/np_web_api2/np_web_api2.h + src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp + src/core/libraries/np/np_web_api2/np_web_api2_internal.h + src/core/libraries/np/np_web_api2/np_web_api2_context.cpp + src/core/libraries/np/np_web_api2/np_web_api2_context.h + src/core/libraries/np/np_web_api2/np_web_api2_push_event.cpp + src/core/libraries/np/np_web_api2/np_web_api2_push_event.h ) set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 4e61b87a2..830180dba 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -49,7 +49,7 @@ #include "core/libraries/np/np_trophy.h" #include "core/libraries/np/np_tus.h" #include "core/libraries/np/np_web_api/np_web_api.h" -#include "core/libraries/np/np_web_api2.h" +#include "core/libraries/np/np_web_api2/np_web_api2.h" #include "core/libraries/pad/pad.h" #include "core/libraries/playgo/playgo.h" #include "core/libraries/playgo/playgo_dialog.h" diff --git a/src/core/libraries/network/http2.cpp b/src/core/libraries/network/http2.cpp index d71c04aed..cdaac4f9a 100644 --- a/src/core/libraries/network/http2.cpp +++ b/src/core/libraries/network/http2.cpp @@ -4,293 +4,359 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/network/http.h" #include "core/libraries/network/http2.h" +#include + namespace Libraries::Http2 { -int PS4_SYSV_ABI _Z5dummyv() { +std::map requests_to_connections{}; + +s32 PS4_SYSV_ABI sceHttp2AbortRequest(s32 req_id) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpAbortRequest(req_id); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to get abort HTTP request, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2AddCookie() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2AbortRequest() { +s32 PS4_SYSV_ABI sceHttp2AddRequestHeader(s32 template_or_req_id, const char* name, + const char* value, u32 mode) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpAddRequestHeader(template_or_req_id, name, value, mode); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to add HTTP response header, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2AuthCacheFlush() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2AddCookie() { +s32 PS4_SYSV_ABI sceHttp2CookieExport() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2AddRequestHeader() { +s32 PS4_SYSV_ABI sceHttp2CookieFlush() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2AuthCacheFlush() { +s32 PS4_SYSV_ABI sceHttp2CookieImport() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2CookieExport() { +s32 PS4_SYSV_ABI sceHttp2CreateCookieBox() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2CookieFlush() { +s32 PS4_SYSV_ABI sceHttp2CreateRequestWithURL(s32 tmpl_id, const char* method, const char* url, + u64 content_length) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + // Http1 has a connection thing to go through first + s32 conn_id = Libraries::Http::sceHttpCreateConnectionWithURL(tmpl_id, url, true); + if (conn_id < 0) { + LOG_ERROR(Lib_Http2, "Failed to create HTTP connection, error = {:#x}", conn_id); + return conn_id; + } + s32 req_id = + Libraries::Http::sceHttpCreateRequestWithURL2(conn_id, method, url, content_length); + if (req_id < 0) { + LOG_ERROR(Lib_Http2, "Failed to create HTTP request, error = {:#x}", req_id); + return req_id; + } + requests_to_connections[req_id] = conn_id; + return req_id; +} + +s32 PS4_SYSV_ABI sceHttp2CreateTemplate(s32 ctx_id, const char* user_agent, s32 http_ver, + s32 auto_proxy_conf) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 tmpl_id = + Libraries::Http::sceHttpCreateTemplate(ctx_id, user_agent, http_ver, auto_proxy_conf); + if (tmpl_id < 0) { + LOG_ERROR(Lib_Http2, "Failed to create HTTP template, error = {:#x}", tmpl_id); + } + return tmpl_id; +} + +s32 PS4_SYSV_ABI sceHttp2DeleteCookieBox() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2CookieImport() { +s32 PS4_SYSV_ABI sceHttp2DeleteRequest(s32 req_id) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpDeleteRequest(req_id); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to delete HTTP request, error = {:#x}", result); + return result; + } + s32 conn_id = requests_to_connections[req_id]; + result = Libraries::Http::sceHttpDeleteConnection(conn_id); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to delete HTTP connection, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2DeleteTemplate(s32 tmpl_id) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpDeleteTemplate(tmpl_id); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to delete HTTP template, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2GetAllResponseHeaders(s32 req_id, char** header, u64* header_size) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpGetAllResponseHeaders(req_id, header, header_size); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to get HTTP response headers, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2GetAuthEnabled() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2CreateCookieBox() { +s32 PS4_SYSV_ABI sceHttp2GetAutoRedirect() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2CreateRequestWithURL() { +s32 PS4_SYSV_ABI sceHttp2GetCookie() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2CreateTemplate() { +s32 PS4_SYSV_ABI sceHttp2GetCookieBox() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2DeleteCookieBox() { +s32 PS4_SYSV_ABI sceHttp2GetCookieStats() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2DeleteRequest() { +s32 PS4_SYSV_ABI sceHttp2GetMemoryPoolStats() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2DeleteTemplate() { +s32 PS4_SYSV_ABI sceHttp2GetResponseContentLength() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2GetAllResponseHeaders() { +s32 PS4_SYSV_ABI sceHttp2GetStatusCode(s32 req_id, s32* status_code) { LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; + s32 result = Libraries::Http::sceHttpGetStatusCode(req_id, status_code); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to get HTTP status code, error = {:#x}", result); + } + return result; } -int PS4_SYSV_ABI sceHttp2GetAuthEnabled() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2GetAutoRedirect() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2GetCookie() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2GetCookieBox() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2GetCookieStats() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2GetMemoryPoolStats() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2GetResponseContentLength() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2GetStatusCode() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2Init(int net_id, int ssl_id, size_t pool_size, int max_requests) { +s32 PS4_SYSV_ABI sceHttp2Init(s32 net_id, s32 ssl_id, u64 pool_size, s32 max_requests) { LOG_ERROR(Lib_Http2, "(DUMMY) called"); - static int id = 0; - return ++id; + s32 ctx_id = Libraries::Http::sceHttpInit(net_id, ssl_id, pool_size); + if (ctx_id < 0) { + LOG_ERROR(Lib_Http2, "Failed to init HTTP context, error = {:#x}", ctx_id); + } + return ctx_id; } -int PS4_SYSV_ABI sceHttp2ReadData() { +s32 PS4_SYSV_ABI sceHttp2ReadData(s32 req_id, void* data, u64 size) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpReadData(req_id, data, size); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to read HTTP data, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2ReadDataAsync() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2ReadDataAsync() { +s32 PS4_SYSV_ABI sceHttp2RedirectCacheFlush() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2RedirectCacheFlush() { +s32 PS4_SYSV_ABI sceHttp2RemoveRequestHeader() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2RemoveRequestHeader() { +s32 PS4_SYSV_ABI sceHttp2SendRequest(s32 req_id, const void* data, u64 size) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpSendRequest(req_id, data, size); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to send HTTP request, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2SendRequestAsync() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SendRequest() { +s32 PS4_SYSV_ABI sceHttp2SetAuthEnabled() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SendRequestAsync() { +s32 PS4_SYSV_ABI sceHttp2SetAuthInfoCallback() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetAuthEnabled() { +s32 PS4_SYSV_ABI sceHttp2SetAutoRedirect() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetAuthInfoCallback() { +s32 PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetAutoRedirect() { +s32 PS4_SYSV_ABI sceHttp2SetConnectTimeOut() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut() { +s32 PS4_SYSV_ABI sceHttp2SetCookieBox() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetConnectTimeOut() { +s32 PS4_SYSV_ABI sceHttp2SetCookieMaxNum() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetCookieBox() { +s32 PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetCookieMaxNum() { +s32 PS4_SYSV_ABI sceHttp2SetCookieMaxSize() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain() { +s32 PS4_SYSV_ABI sceHttp2SetCookieRecvCallback() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetCookieMaxSize() { +s32 PS4_SYSV_ABI sceHttp2SetCookieSendCallback() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetCookieRecvCallback() { +s32 PS4_SYSV_ABI sceHttp2SetInflateGZIPEnabled() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetCookieSendCallback() { +s32 PS4_SYSV_ABI sceHttp2SetMinSslVersion() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetInflateGZIPEnabled() { +s32 PS4_SYSV_ABI sceHttp2SetPreSendCallback(s32 template_id, OrbisHttp2PreSendCallback cb_func, + void* user_arg) { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetMinSslVersion() { +s32 PS4_SYSV_ABI sceHttp2SetRecvTimeOut() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetPreSendCallback() { +s32 PS4_SYSV_ABI sceHttp2SetRedirectCallback() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetRecvTimeOut() { +s32 PS4_SYSV_ABI sceHttp2SetRequestContentLength(s32 req_id, u64 content_length) { + LOG_ERROR(Lib_Http2, "(STUBBED) called"); + s32 result = Libraries::Http::sceHttpSetRequestContentLength(req_id, content_length); + if (result < 0) { + LOG_ERROR(Lib_Http2, "Failed to set HTTP request content length, error = {:#x}", result); + } + return result; +} + +s32 PS4_SYSV_ABI sceHttp2SetResolveRetry() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetRedirectCallback() { +s32 PS4_SYSV_ABI sceHttp2SetResolveTimeOut() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetRequestContentLength() { +s32 PS4_SYSV_ABI sceHttp2SetSendTimeOut() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetResolveRetry() { +s32 PS4_SYSV_ABI sceHttp2SetSslCallback() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetResolveTimeOut() { +s32 PS4_SYSV_ABI sceHttp2SetTimeOut() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetSendTimeOut() { +s32 PS4_SYSV_ABI sceHttp2SslDisableOption() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetSslCallback() { +s32 PS4_SYSV_ABI sceHttp2SslEnableOption() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SetTimeOut() { +s32 PS4_SYSV_ABI sceHttp2Term() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttp2SslDisableOption() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2SslEnableOption() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2Term() { - LOG_ERROR(Lib_Http2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttp2WaitAsync() { +s32 PS4_SYSV_ABI sceHttp2WaitAsync() { LOG_ERROR(Lib_Http2, "(STUBBED) called"); return ORBIS_OK; } void RegisterLib(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("AS45QoYHjc4", "libSceHttp2", 1, "libSceHttp2", _Z5dummyv); LIB_FUNCTION("IZ-qjhRqvjk", "libSceHttp2", 1, "libSceHttp2", sceHttp2AbortRequest); LIB_FUNCTION("flPxnowtvWY", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddCookie); LIB_FUNCTION("nrPfOE8TQu0", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddRequestHeader); diff --git a/src/core/libraries/network/http2.h b/src/core/libraries/network/http2.h index 99359518a..8c30345cf 100644 --- a/src/core/libraries/network/http2.h +++ b/src/core/libraries/network/http2.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "core/libraries/network/ssl2.h" namespace Core::Loader { class SymbolsResolver; @@ -11,62 +12,38 @@ class SymbolsResolver; namespace Libraries::Http2 { -int PS4_SYSV_ABI _Z5dummyv(); -int PS4_SYSV_ABI sceHttp2AbortRequest(); -int PS4_SYSV_ABI sceHttp2AddCookie(); -int PS4_SYSV_ABI sceHttp2AddRequestHeader(); -int PS4_SYSV_ABI sceHttp2AuthCacheFlush(); -int PS4_SYSV_ABI sceHttp2CookieExport(); -int PS4_SYSV_ABI sceHttp2CookieFlush(); -int PS4_SYSV_ABI sceHttp2CookieImport(); -int PS4_SYSV_ABI sceHttp2CreateCookieBox(); -int PS4_SYSV_ABI sceHttp2CreateRequestWithURL(); -int PS4_SYSV_ABI sceHttp2CreateTemplate(); -int PS4_SYSV_ABI sceHttp2DeleteCookieBox(); -int PS4_SYSV_ABI sceHttp2DeleteRequest(); -int PS4_SYSV_ABI sceHttp2DeleteTemplate(); -int PS4_SYSV_ABI sceHttp2GetAllResponseHeaders(); -int PS4_SYSV_ABI sceHttp2GetAuthEnabled(); -int PS4_SYSV_ABI sceHttp2GetAutoRedirect(); -int PS4_SYSV_ABI sceHttp2GetCookie(); -int PS4_SYSV_ABI sceHttp2GetCookieBox(); -int PS4_SYSV_ABI sceHttp2GetCookieStats(); -int PS4_SYSV_ABI sceHttp2GetMemoryPoolStats(); -int PS4_SYSV_ABI sceHttp2GetResponseContentLength(); -int PS4_SYSV_ABI sceHttp2GetStatusCode(); -int PS4_SYSV_ABI sceHttp2Init(int net_id, int ssl_id, size_t pool_size, int max_requests); -int PS4_SYSV_ABI sceHttp2ReadData(); -int PS4_SYSV_ABI sceHttp2ReadDataAsync(); -int PS4_SYSV_ABI sceHttp2RedirectCacheFlush(); -int PS4_SYSV_ABI sceHttp2RemoveRequestHeader(); -int PS4_SYSV_ABI sceHttp2SendRequest(); -int PS4_SYSV_ABI sceHttp2SendRequestAsync(); -int PS4_SYSV_ABI sceHttp2SetAuthEnabled(); -int PS4_SYSV_ABI sceHttp2SetAuthInfoCallback(); -int PS4_SYSV_ABI sceHttp2SetAutoRedirect(); -int PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut(); -int PS4_SYSV_ABI sceHttp2SetConnectTimeOut(); -int PS4_SYSV_ABI sceHttp2SetCookieBox(); -int PS4_SYSV_ABI sceHttp2SetCookieMaxNum(); -int PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain(); -int PS4_SYSV_ABI sceHttp2SetCookieMaxSize(); -int PS4_SYSV_ABI sceHttp2SetCookieRecvCallback(); -int PS4_SYSV_ABI sceHttp2SetCookieSendCallback(); -int PS4_SYSV_ABI sceHttp2SetInflateGZIPEnabled(); -int PS4_SYSV_ABI sceHttp2SetMinSslVersion(); -int PS4_SYSV_ABI sceHttp2SetPreSendCallback(); -int PS4_SYSV_ABI sceHttp2SetRecvTimeOut(); -int PS4_SYSV_ABI sceHttp2SetRedirectCallback(); -int PS4_SYSV_ABI sceHttp2SetRequestContentLength(); -int PS4_SYSV_ABI sceHttp2SetResolveRetry(); -int PS4_SYSV_ABI sceHttp2SetResolveTimeOut(); -int PS4_SYSV_ABI sceHttp2SetSendTimeOut(); -int PS4_SYSV_ABI sceHttp2SetSslCallback(); -int PS4_SYSV_ABI sceHttp2SetTimeOut(); -int PS4_SYSV_ABI sceHttp2SslDisableOption(); -int PS4_SYSV_ABI sceHttp2SslEnableOption(); -int PS4_SYSV_ABI sceHttp2Term(); -int PS4_SYSV_ABI sceHttp2WaitAsync(); +enum OrbisHttp2HttpVersion : u32 { + ORBIS_HTTP2_VERSION_1_0 = 1, + ORBIS_HTTP2_VERSION_1_1 = 2, + ORBIS_HTTP2_VERSION_2_0 = 3, +}; + +// TODO: Figure out the whole struct. What's here is based on libSceNpWebApi2 use. +struct OrbisHttp2PreSendCallbackData { + void* unk; + void* cert; +}; +using OrbisHttp2PreSendCallback = PS4_SYSV_ABI s32 (*)(s32 request_id, s32 ssl_id, + OrbisHttp2PreSendCallbackData* data, + void* user_arg); + +s32 PS4_SYSV_ABI sceHttp2AbortRequest(s32 req_id); +s32 PS4_SYSV_ABI sceHttp2AddRequestHeader(s32 template_or_req_id, const char* name, + const char* value, u32 mode); +s32 PS4_SYSV_ABI sceHttp2CreateRequestWithURL(s32 tmpl_id, const char* method, const char* url, + u64 content_length); +s32 PS4_SYSV_ABI sceHttp2CreateTemplate(s32 ctx_id, const char* user_agent, s32 http_ver, + s32 auto_proxy_conf); +s32 PS4_SYSV_ABI sceHttp2DeleteRequest(s32 req_id); +s32 PS4_SYSV_ABI sceHttp2DeleteTemplate(s32 tmpl_id); +s32 PS4_SYSV_ABI sceHttp2GetAllResponseHeaders(s32 req_id, char** header, u64* header_size); +s32 PS4_SYSV_ABI sceHttp2GetStatusCode(s32 request_id, s32* status_code); +s32 PS4_SYSV_ABI sceHttp2Init(s32 net_id, s32 ssl_id, u64 pool_size, s32 max_requests); +s32 PS4_SYSV_ABI sceHttp2ReadData(s32 req_id, void* data, u64 size); +s32 PS4_SYSV_ABI sceHttp2SendRequest(s32 req_id, const void* data, u64 size); +s32 PS4_SYSV_ABI sceHttp2SetPreSendCallback(s32 template_id, OrbisHttp2PreSendCallback cb_func, + void* user_arg); +s32 PS4_SYSV_ABI sceHttp2SetRequestContentLength(s32 req_id, u64 content_length); void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Http2 \ No newline at end of file diff --git a/src/core/libraries/np/np_common.cpp b/src/core/libraries/np/np_common.cpp index e95cf3322..59a258edd 100644 --- a/src/core/libraries/np/np_common.cpp +++ b/src/core/libraries/np/np_common.cpp @@ -390,6 +390,17 @@ s32 PS4_SYSV_ABI sceNpIntIsValidOnlineId(const OrbisNpOnlineId* id) { return 1; } +void PS4_SYSV_ABI sceNpGetSdkVersion(char* version_buf) { + LOG_DEBUG(Lib_NpCommon, "called"); + // Real library has no parameter checks, so I guess this is purely for internal use? + Libraries::Kernel::SwVersionStruct sw_version{}; + sw_version.struct_size = 0x28; + Libraries::Kernel::sceKernelGetSystemSwVersion(&sw_version); + u32 sw_hex = sw_version.hex_representation; + snprintf(version_buf, 8, "%d.%02d", ((sw_hex >> 0x18) & 0xf) + ((sw_hex >> 0x1c) * 10), + ((sw_hex >> 0x10) & 0xf) + (((sw_hex >> 0x14) & 0xf) * 10)); +} + s32 PS4_SYSV_ABI sceNpCalloutInitCtx(OrbisNpCalloutContext* callout_ctx, const char* name, u64 stack_size, s32 priority, u64 affinity_mask) { LOG_DEBUG(Lib_NpCommon, "ctx={:p} name={:p} stack={:#x} priority={} affinity={:#x}", @@ -600,6 +611,7 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("ss2xO9IJxKQ", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondTimedwait); LIB_FUNCTION("uEwag-0YZPc", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexInit); LIB_FUNCTION("uMJFOA62mVU", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondSignal); + LIB_FUNCTION("Pglk7zFj0DI", "libSceNpCommon", 1, "libSceNpCommon", sceNpGetSdkVersion); }; } // namespace Libraries::Np::NpCommon diff --git a/src/core/libraries/np/np_common.h b/src/core/libraries/np/np_common.h index 7ff5a81c0..9f28d1649 100644 --- a/src/core/libraries/np/np_common.h +++ b/src/core/libraries/np/np_common.h @@ -62,6 +62,7 @@ u32 PS4_SYSV_ABI sceNpJoinThread(Libraries::Kernel::PthreadT thread, void** ret) s32 PS4_SYSV_ABI sceNpGetSystemClockUsec(s64* usec); s32 PS4_SYSV_ABI sceNpGetPlatformType(const OrbisNpId* np_id); s32 PS4_SYSV_ABI sceNpIntIsValidOnlineId(const OrbisNpOnlineId* id); +void PS4_SYSV_ABI sceNpGetSdkVersion(char* version_buf); s32 PS4_SYSV_ABI sceNpCalloutInitCtx(OrbisNpCalloutContext* ctx, const char* name, u64 stack_size, s32 priority, u64 affinity_mask); s32 PS4_SYSV_ABI sceNpCalloutStartOnCtx(OrbisNpCalloutContext* ctx, OrbisNpCalloutEntry* callout, diff --git a/src/core/libraries/np/np_error.h b/src/core/libraries/np/np_error.h index c2fe511d1..ccbe74c76 100644 --- a/src/core/libraries/np/np_error.h +++ b/src/core/libraries/np/np_error.h @@ -553,25 +553,25 @@ constexpr int ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND = 0x80553406; constexpr int ORBIS_NP_WEBAPI2_ERROR_NOT_SIGNED_IN = 0x80553407; constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_CONTENT_PARAMETER = 0x80553408; constexpr int ORBIS_NP_WEBAPI2_ERROR_ABORTED = 0x80553409; -constexpr int ORBIS_NP_WEBAPI2_USER_CONTEXT_ALREADY_EXIST = 0x8055340a; -constexpr int ORBIS_NP_WEBAPI2_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055340b; -constexpr int ORBIS_NP_WEBAPI2_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055340c; -constexpr int ORBIS_NP_WEBAPI2_HANDLE_NOT_FOUND = 0x8055340d; -constexpr int ORBIS_NP_WEBAPI2_SIGNED_IN_USER_NOT_FOUND = 0x8055340e; -constexpr int ORBIS_NP_WEBAPI2_LIB_CONTEXT_BUSY = 0x8055340f; -constexpr int ORBIS_NP_WEBAPI2_USER_CONTEXT_BUSY = 0x80553410; -constexpr int ORBIS_NP_WEBAPI2_REQUEST_BUSY = 0x80553411; -constexpr int ORBIS_NP_WEBAPI2_INVALID_HTTP_STATUS_CODE = 0x80553412; -constexpr int ORBIS_NP_WEBAPI2_PROHIBITED_HTTP_HEADER = 0x80553413; -constexpr int ORBIS_NP_WEBAPI2_PROHIBITED_FUNCTION_CALL = 0x80553414; -constexpr int ORBIS_NP_WEBAPI2_MULTIPART_PART_NOT_FOUND = 0x80553415; -constexpr int ORBIS_NP_WEBAPI2_PARAMETER_TOO_LONG = 0x80553416; -constexpr int ORBIS_NP_WEBAPI2_HANDLE_BUSY = 0x80553417; -constexpr int ORBIS_NP_WEBAPI2_LIB_CONTEXT_MAX = 0x80553418; -constexpr int ORBIS_NP_WEBAPI2_USER_CONTEXT_MAX = 0x80553419; -constexpr int ORBIS_NP_WEBAPI2_AFTER_SEND = 0x8055341a; -constexpr int ORBIS_NP_WEBAPI2_TIMEOUT = 0x8055341b; -constexpr int ORBIS_NP_WEBAPI2_PUSH_CONTEXT_NOT_FOUND = 0x8055341c; +constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_ALREADY_EXIST = 0x8055340a; +constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055340b; +constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055340c; +constexpr int ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND = 0x8055340d; +constexpr int ORBIS_NP_WEBAPI2_ERROR_SIGNED_IN_USER_NOT_FOUND = 0x8055340e; +constexpr int ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_BUSY = 0x8055340f; +constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_BUSY = 0x80553410; +constexpr int ORBIS_NP_WEBAPI2_ERROR_REQUEST_BUSY = 0x80553411; +constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_HTTP_STATUS_CODE = 0x80553412; +constexpr int ORBIS_NP_WEBAPI2_ERROR_PROHIBITED_HTTP_HEADER = 0x80553413; +constexpr int ORBIS_NP_WEBAPI2_ERROR_PROHIBITED_FUNCTION_CALL = 0x80553414; +constexpr int ORBIS_NP_WEBAPI2_ERROR_MULTIPART_PART_NOT_FOUND = 0x80553415; +constexpr int ORBIS_NP_WEBAPI2_ERROR_PARAMETER_TOO_LONG = 0x80553416; +constexpr int ORBIS_NP_WEBAPI2_ERROR_HANDLE_BUSY = 0x80553417; +constexpr int ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_MAX = 0x80553418; +constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_MAX = 0x80553419; +constexpr int ORBIS_NP_WEBAPI2_ERROR_AFTER_SEND = 0x8055341a; +constexpr int ORBIS_NP_WEBAPI2_ERROR_TIMEOUT = 0x8055341b; +constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_CONTEXT_NOT_FOUND = 0x8055341c; // NP Session Management Client (0x80553600 - 0x8055361e) constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_ALREADY_INITIALIZED = 0x80553600; diff --git a/src/core/libraries/np/np_web_api2.cpp b/src/core/libraries/np/np_web_api2.cpp deleted file mode 100644 index e7489ccba..000000000 --- a/src/core/libraries/np/np_web_api2.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/emulator_settings.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/libs.h" -#include "core/libraries/np/np_error.h" -#include "core/libraries/np/np_web_api2.h" -#include "core/libraries/system/userservice.h" - -namespace Libraries::Np::NpWebApi2 { - -s32 PS4_SYSV_ABI sceNpWebApi2AbortRequest(s64 request_id) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, request_id = {:#x}", request_id); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2AddHttpRequestHeader() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2AddMultipartPart() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2AddWebTraceTag() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -void PS4_SYSV_ABI sceNpWebApi2CheckTimeout() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); -} - -s32 PS4_SYSV_ABI sceNpWebApi2CreateMultipartRequest() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2CreateRequest() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2CreateUserContext(s32 lib_ctx_id, - UserService::OrbisUserServiceUserId user_id) { - if (lib_ctx_id >= 0x8000) { - return ORBIS_NP_WEBAPI2_ERROR_INVALID_LIB_CONTEXT_ID; - } - if (user_id == UserService::ORBIS_USER_SERVICE_USER_ID_INVALID) { - return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; - } - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, lib_ctx_id = {:#x}, user_id = {:#x}", lib_ctx_id, - user_id); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2DeleteRequest(s64 request_id) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, request_id = {:#x}", request_id); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2DeleteUserContext(s32 user_ctx_id) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, user_ctx_id = {:#x}", user_ctx_id); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2GetHttpResponseHeaderValue() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2GetHttpResponseHeaderValueLength() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2GetMemoryPoolStats(s32 lib_ctx_id, - OrbisNpWebApi2MemoryPoolStats* stats) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, lib_ctx_id = {:#x}", lib_ctx_id); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2Initialize(s32 lib_http_ctx_id, u64 pool_size) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", - lib_http_ctx_id, pool_size); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2InitializeForPresence(s32 lib_http_ctx_id, u64 pool_size) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", - lib_http_ctx_id, pool_size); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2IntCreateRequest() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize(const OrbisNpWebApi2IntInitializeArgs* args) { - if (args == nullptr || args->struct_size != sizeof(OrbisNpWebApi2IntInitializeArgs)) { - return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; - } - LOG_ERROR(Lib_NpWebApi2, - "(STUBBED) called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = '{}'", - args->lib_http_ctx_id, args->pool_size, args->name); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize2(const OrbisNpWebApi2IntInitialize2Args* args) { - if (args == nullptr || args->struct_size != sizeof(OrbisNpWebApi2IntInitialize2Args)) { - return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; - } - LOG_ERROR(Lib_NpWebApi2, - "(STUBBED) called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = '{}', " - "group = {:#x}", - args->lib_http_ctx_id, args->pool_size, args->name, args->push_config_group); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2IntPushEventCreateCtxIndFilter() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventAbortHandle() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventCreateFilter() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventCreateHandle() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventCreatePushContext() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventDeleteFilter() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventDeleteHandle() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventDeletePushContext() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventRegisterCallback() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventRegisterPushContextCallback() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventSetHandleTimeout() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventStartPushContextCallback() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventUnregisterCallback() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2PushEventUnregisterPushContextCallback() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2ReadData(s64 request_id, void* data, u64 size) { - if (data == nullptr || size == 0) { - return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; - } - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, request_id = {:#x}, size = {:#x}", request_id, - size); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2SendMultipartRequest() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2SendRequest() { - if (!EmulatorSettings.IsShadNetEnabled()) { - LOG_INFO(Lib_NpWebApi2, "called, returning shadNet signed out."); - return ORBIS_NP_WEBAPI2_ERROR_NOT_SIGNED_IN; - } - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2SetMultipartContentType() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2SetRequestTimeout(s64 request_id, u32 timeout) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, request_id = {:#x}, timeout = {}", request_id, - timeout); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceNpWebApi2Terminate(s32 lib_ctx_id) { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called, lib_ctx_id = {:#x}", lib_ctx_id); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI Func_A9A31C5F6FBA6620() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI Func_03D22863300D2B73() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI Func_97296F7578AAD541() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI Func_E0DF39A36F087DB9() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; -} - -void RegisterLib(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("zpiPsH7dbFQ", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2AbortRequest); - LIB_FUNCTION("egOOvrnF6mI", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2AddHttpRequestHeader); - LIB_FUNCTION("Io7kh1LHDoM", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2AddMultipartPart); - LIB_FUNCTION("MgsTa76wlEk", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2AddWebTraceTag); - LIB_FUNCTION("3Tt9zL3tkoc", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2CheckTimeout); - LIB_FUNCTION("+nz1Vq-NrDA", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2CreateMultipartRequest); - LIB_FUNCTION("3EI-OSJ65Xc", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2CreateRequest); - LIB_FUNCTION("sk54bi6FtYM", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2CreateUserContext); - LIB_FUNCTION("vvzWO-DvG1s", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2DeleteRequest); - LIB_FUNCTION("9X9+cneTGUU", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2DeleteUserContext); - LIB_FUNCTION("hksbskNToEA", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2GetHttpResponseHeaderValue); - LIB_FUNCTION("HwP3aM+c85c", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2GetHttpResponseHeaderValueLength); - LIB_FUNCTION("Xweb+naPZ8Y", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2GetMemoryPoolStats); - LIB_FUNCTION("+o9816YQhqQ", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2Initialize); - LIB_FUNCTION("dowMWFgowXY", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2InitializeForPresence); - LIB_FUNCTION("qmINYLuqzaA", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2IntCreateRequest); - LIB_FUNCTION("zXaFo7euxsQ", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2IntInitialize); - LIB_FUNCTION("9KSGFMRnp3k", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2IntInitialize2); - LIB_FUNCTION("2hlBNB96saE", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2IntPushEventCreateCtxIndFilter); - LIB_FUNCTION("1OLgvahaSco", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventAbortHandle); - LIB_FUNCTION("MsaFhR+lPE4", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventCreateFilter); - LIB_FUNCTION("WV1GwM32NgY", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventCreateHandle); - LIB_FUNCTION("NNVf18SlbT8", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventCreatePushContext); - LIB_FUNCTION("KJdPcOGmK58", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventDeleteFilter); - LIB_FUNCTION("fIATVMo4Y1w", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventDeleteHandle); - LIB_FUNCTION("QafxeZM3WK4", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventDeletePushContext); - LIB_FUNCTION("fY3QqeNkF8k", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventRegisterCallback); - LIB_FUNCTION("lxtHJMwBsaU", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventRegisterPushContextCallback); - LIB_FUNCTION("KWkc6Q3tjXc", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventSetHandleTimeout); - LIB_FUNCTION("AAj9X+4aGYA", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventStartPushContextCallback); - LIB_FUNCTION("hOnIlcGrO6g", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventUnregisterCallback); - LIB_FUNCTION("PmyrbbJSFz0", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2PushEventUnregisterPushContextCallback); - LIB_FUNCTION("OOY9+ObfKec", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2ReadData); - LIB_FUNCTION("NKCwS8+5Fx8", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2SendMultipartRequest); - LIB_FUNCTION("lQOCF84lvzw", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2SendRequest); - LIB_FUNCTION("bltDCAskmfE", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2SetMultipartContentType); - LIB_FUNCTION("TjAutbrkr60", "libSceNpWebApi2", 1, "libSceNpWebApi2", - sceNpWebApi2SetRequestTimeout); - LIB_FUNCTION("bEvXpcEk200", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2Terminate); - LIB_FUNCTION("qaMcX2+6ZiA", "libSceNpWebApi2", 1, "libSceNpWebApi2", Func_A9A31C5F6FBA6620); - LIB_FUNCTION("A9IoYzANK3M", "libSceNpWebApi2AsyncRestricted", 1, "libSceNpWebApi2", - Func_03D22863300D2B73); - LIB_FUNCTION("lylvdXiq1UE", "libSceNpWebApi2AsyncRestricted", 1, "libSceNpWebApi2", - Func_97296F7578AAD541); - LIB_FUNCTION("4N85o28Ifbk", "libSceNpWebApi2AsyncRestricted", 1, "libSceNpWebApi2", - Func_E0DF39A36F087DB9); -}; - -} // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2.h b/src/core/libraries/np/np_web_api2.h deleted file mode 100644 index 1e970d08e..000000000 --- a/src/core/libraries/np/np_web_api2.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Np::NpWebApi2 { - -struct OrbisNpWebApi2IntInitializeArgs { - s32 lib_http_ctx_id; - s32 reserved; - u64 pool_size; - char* name; - u64 struct_size; -}; - -struct OrbisNpWebApi2IntInitialize2Args { - s32 lib_http_ctx_id; - s32 reserved; - u64 pool_size; - char* name; - u32 push_config_group; - s32 reserved2; - u64 struct_size; -}; - -struct OrbisNpWebApi2MemoryPoolStats { - u64 pool_size; - u64 max_inuse_size; - u64 current_inuse_size; - s32 reserved; -}; - -void RegisterLib(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2.cpp b/src/core/libraries/np/np_web_api2/np_web_api2.cpp new file mode 100644 index 000000000..7bcbdf040 --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2.cpp @@ -0,0 +1,522 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/emulator_settings.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np/np_error.h" +#include "core/libraries/np/np_manager.h" +#include "core/libraries/np/np_types.h" +#include "core/libraries/np/np_web_api2/np_web_api2.h" +#include "core/libraries/np/np_web_api2/np_web_api2_internal.h" +#include "core/libraries/system/userservice.h" + +namespace Libraries::Np::NpWebApi2 { + +s32 PS4_SYSV_ABI sceNpWebApi2AbortRequest(s64 request_id) { + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}", request_id); + return abortRequest(request_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2AddHttpRequestHeader(s64 request_id, const char* field_name, + const char* field_value) { + if (!field_name || !field_value) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, field_name = {}, field_value = {}", + request_id, field_name, field_value); + return addHttpRequestHeader(request_id, field_name, field_value); +} + +s32 PS4_SYSV_ABI sceNpWebApi2AddMultipartPart() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApi2AddWebTraceTag(s64 request_id, const char* value) { + if (!value) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, value = {}", request_id, value); + return addHttpRequestHeader(request_id, "X-Psn-WebTrace-Tag", value); +} + +void PS4_SYSV_ABI sceNpWebApi2CheckTimeout() { + LOG_TRACE(Lib_NpWebApi2, "called"); + return checkTimeout(); +} + +s32 PS4_SYSV_ABI sceNpWebApi2CreateMultipartRequest(s32 user_ctx_id, const char* api_group, + const char* path, const char* method, + s64* request_id) { + if (!api_group || !path || !method) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + + LOG_INFO(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, api_group = {}, path = {}, method = {}", + user_ctx_id, api_group, path, method); + s64 temp_request_id = 0; + s32 result = + createRequest(user_ctx_id, api_group, path, method, nullptr, true, &temp_request_id); + if (result == 0) { + LOG_INFO(Lib_NpWebApi2, "created request_id = {:#x}", temp_request_id); + if (request_id) { + *request_id = temp_request_id; + } + } + return result; +} + +s32 PS4_SYSV_ABI sceNpWebApi2CreateRequest(s32 user_ctx_id, const char* api_group, const char* path, + const char* method, + const OrbisNpWebApi2ContentParameter* content_parameter, + s64* request_id) { + if (!api_group || !path || !method) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + if (content_parameter && content_parameter->content_length != 0 && + !content_parameter->content_type) { + LOG_ERROR(Lib_NpWebApi2, "Invalid content parameter"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_CONTENT_PARAMETER; + } + + LOG_INFO(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, api_group = {}, path = {}, method = {}", + user_ctx_id, api_group, path, method); + s64 temp_request_id = 0; + s32 result = createRequest(user_ctx_id, api_group, path, method, content_parameter, false, + &temp_request_id); + if (result == 0) { + LOG_INFO(Lib_NpWebApi2, "created request_id = {:#x}", temp_request_id); + if (request_id) { + *request_id = temp_request_id; + } + } + return result; +} + +s32 PS4_SYSV_ABI sceNpWebApi2CreateUserContext(s32 lib_ctx_id, + UserService::OrbisUserServiceUserId user_id) { + if (lib_ctx_id >= 0x8000) { + LOG_ERROR(Lib_NpWebApi2, "Invalid library context id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_LIB_CONTEXT_ID; + } + if (user_id == UserService::ORBIS_USER_SERVICE_USER_ID_INVALID) { + LOG_ERROR(Lib_NpWebApi2, "Invalid user id"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_DEBUG(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}, user_id = {}", lib_ctx_id, user_id); + s32 user_ctx_id = createUserContext(lib_ctx_id, user_id); + if (user_ctx_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created user_ctx_id = {:#x}", user_ctx_id); + } + return user_ctx_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2DeleteRequest(s64 request_id) { + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}", request_id); + return deleteRequest(request_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2DeleteUserContext(s32 user_ctx_id) { + LOG_INFO(Lib_NpWebApi2, "called, user_ctx_id = {:#x}", user_ctx_id); + return deleteUserContext(user_ctx_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2GetHttpResponseHeaderValue(s64 request_id, const char* field_name, + u64* field_value_length) { + if (!field_name || !field_value_length) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + s32 result = getHttpResponseHeaderData(request_id, field_name, nullptr, 0, field_value_length); + if (result >= 0) { + LOG_INFO(Lib_NpWebApi2, + "on request_id = {:#x}, field_name = {} returned field_value_length = {:#x}", + request_id, field_name, *field_value_length); + } + return result; +} + +s32 PS4_SYSV_ABI sceNpWebApi2GetHttpResponseHeaderValueLength(s64 request_id, + const char* field_name, char* value, + u64 value_size) { + if (!field_name || !value || value_size == 0) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, field_name = {}", request_id, field_name); + return getHttpResponseHeaderData(request_id, field_name, value, value_size, nullptr); +} + +s32 PS4_SYSV_ABI sceNpWebApi2GetMemoryPoolStats(s32 lib_ctx_id, + OrbisNpWebApi2MemoryPoolStats* stats) { + LOG_INFO(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}", lib_ctx_id); + return getMemoryPoolStats(lib_ctx_id, stats); +} + +s32 PS4_SYSV_ABI sceNpWebApi2Initialize(s32 lib_http_ctx_id, u64 pool_size) { + LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", lib_http_ctx_id, + pool_size); + + // Uses a sceLncUtilGetAppStatus check to enable debug mode. For now, default to normal. + s32 ctx_id = createLibraryContext(lib_http_ctx_id, 1, pool_size, nullptr); + if (ctx_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); + } + return ctx_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2InitializeForPresence(s32 lib_http_ctx_id, u64 pool_size) { + LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", lib_http_ctx_id, + pool_size); + + s32 ctx_id = createLibraryContext(lib_http_ctx_id, 3, pool_size, nullptr); + if (ctx_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); + } + return ctx_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2IntCreateRequest() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize(const OrbisNpWebApi2IntInitializeArgs* args) { + if (args == nullptr || args->struct_size != sizeof(OrbisNpWebApi2IntInitializeArgs)) { + LOG_ERROR(Lib_NpWebApi2, "Invalid arguments"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = {}", + args->lib_http_ctx_id, args->pool_size, args->name ? args->name : "(null)"); + + s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, 2, args->pool_size, args->name); + if (ctx_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); + } + return ctx_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize2(const OrbisNpWebApi2IntInitialize2Args* args) { + + if (args == nullptr || args->struct_size != sizeof(OrbisNpWebApi2IntInitialize2Args)) { + LOG_ERROR(Lib_NpWebApi2, "Invalid arguments"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_DEBUG( + Lib_NpWebApi2, + "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = {}, push_config_group = {:#x}", + args->lib_http_ctx_id, args->pool_size, args->name ? args->name : "(null)", + args->push_config_group); + + s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, 2, args->pool_size, args->name); + if (ctx_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); + } + return ctx_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2IntPushEventCreateCtxIndFilter() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventAbortHandle(s32 lib_ctx_id, s32 handle_id) { + LOG_INFO(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}, handle_id = {:#x}", lib_ctx_id, handle_id); + return abortPushEventHandle(lib_ctx_id, handle_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventCreateFilter( + s32 lib_ctx_id, s32 handle_id, const char* np_service_name, + OrbisNpServiceLabel np_service_label, + const OrbisNpWebApi2PushEventFilterParameter* filter_param, u64 filter_param_num) { + if ((np_service_name && np_service_label == ORBIS_NP_INVALID_SERVICE_LABEL) || !filter_param || + filter_param_num == 0) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_DEBUG(Lib_NpWebApi2, + "called, lib_ctx_id = {:#x}, handle_id = {:#x}, np_service_name = {}, " + "np_service_label = {:#x}, filter_param_num = {:#x}", + lib_ctx_id, handle_id, np_service_name ? np_service_name : "(null)", + static_cast(np_service_label), filter_param_num); + for (u64 i = 0; i < filter_param_num; i++) { + LOG_DEBUG(Lib_NpWebApi2, "filter_param[{}].data_type.val = {}", i, + filter_param[i].data_type.val); + LOG_DEBUG(Lib_NpWebApi2, "filter_param[{}].extd_data_key_num = {}", i, + filter_param[i].extd_data_key_num); + for (u64 j = 0; j < filter_param[i].extd_data_key_num && filter_param[i].extd_data_key; + j++) { + LOG_DEBUG(Lib_NpWebApi2, "filter_param[{}].extd_data_key[{}].val = {}", i, j, + filter_param[i].extd_data_key[j].val); + } + } + s32 filter_id = createPushEventFilter(lib_ctx_id, handle_id, np_service_name, np_service_label, + filter_param, filter_param_num, false); + if (filter_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created filter_id = {:#x}", filter_id); + } + return filter_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventCreateHandle(s32 lib_ctx_id) { + LOG_DEBUG(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}", lib_ctx_id); + s32 handle_id = createPushEventHandle(lib_ctx_id); + if (handle_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created handle_id = {:#x}", handle_id); + } + return handle_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventCreatePushContext( + s32 user_ctx_id, OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + if (!push_ctx_id) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_DEBUG(Lib_NpWebApi2, "called, user_ctx_id = {:#x}", user_ctx_id); + s32 result = createPushContext(user_ctx_id, push_ctx_id); + if (result == 0) { + s64 raw_id{}; + std::memcpy(&raw_id, push_ctx_id, sizeof(s64)); + LOG_INFO(Lib_NpWebApi2, "created push_ctx_id = {:#x}", raw_id); + } + return result; +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventDeleteFilter(s32 lib_ctx_id, s32 filter_id) { + LOG_INFO(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}, filter_id = {:#x}", lib_ctx_id, filter_id); + return deletePushEventFilter(lib_ctx_id, filter_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventDeleteHandle(s32 lib_ctx_id, s32 handle_id) { + LOG_INFO(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}, handle_id = {:#x}", lib_ctx_id, handle_id); + return deletePushEventHandle(lib_ctx_id, handle_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventDeletePushContext( + s32 user_ctx_id, OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + if (!push_ctx_id) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + s64 raw_id{}; + std::memcpy(&raw_id, push_ctx_id, sizeof(s64)); + LOG_INFO(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, push_ctx_id = {:#x}", user_ctx_id, + raw_id); + return deletePushContext(user_ctx_id, push_ctx_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventRegisterCallback(s32 user_ctx_id, s32 filter_id, + OrbisNpWebApi2PushEventCallback cb_func, + void* user_arg) { + LOG_DEBUG(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, filter_id = {:#x}", user_ctx_id, + filter_id); + if (!cb_func) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + s32 callback_id = registerPushEventCallback(user_ctx_id, filter_id, cb_func, user_arg); + if (callback_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created callback_id = {:#x}", callback_id); + } + return callback_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventRegisterPushContextCallback( + s32 user_ctx_id, s32 filter_id, OrbisNpWebApi2PushEventPushContextCallback cb_func, + void* user_arg) { + LOG_DEBUG(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, filter_id = {:#x}", user_ctx_id, + filter_id); + if (!cb_func) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + s32 callback_id = registerPushContextCallback(user_ctx_id, filter_id, cb_func, user_arg); + if (callback_id > 0) { + LOG_INFO(Lib_NpWebApi2, "created callback_id = {:#x}", callback_id); + } + return callback_id; +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventSetHandleTimeout(s32 lib_ctx_id, s32 handle_id, u32 timeout) { + LOG_INFO(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}, handle_id = {:#x}, timeout = {}", + lib_ctx_id, handle_id, timeout); + return setHandleTimeout(lib_ctx_id, handle_id, timeout); +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventStartPushContextCallback( + s32 user_ctx_id, const OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + if (!push_ctx_id) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + s64 raw_id{}; + std::memcpy(&raw_id, push_ctx_id, sizeof(s64)); + LOG_INFO(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, push_ctx_id = {:#x}", user_ctx_id, + raw_id); + return startPushContextCallback(user_ctx_id, push_ctx_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventUnregisterCallback(s32 user_ctx_id, s32 callback_id) { + LOG_INFO(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, callback_id = {:#x}", user_ctx_id, + callback_id); + return unregisterPushEventCallback(user_ctx_id, callback_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2PushEventUnregisterPushContextCallback(s32 user_ctx_id, + s32 callback_id) { + LOG_INFO(Lib_NpWebApi2, "called, user_ctx_id = {:#x}, callback_id = {:#x}", user_ctx_id, + callback_id); + return unregisterPushContextCallback(user_ctx_id, callback_id); +} + +s32 PS4_SYSV_ABI sceNpWebApi2ReadData(s64 request_id, void* data, u64 size) { + if (!data || size == 0) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, size = {:#x}", request_id, size); + return readData(request_id, data, size); +} + +s32 PS4_SYSV_ABI +sceNpWebApi2SendMultipartRequest(s64 request_id, s32 part_index, void* data, u64 data_size, + OrbisNpWebApi2ResponseInformationOption* resp_info_option) { + if (part_index <= 0 || !data || data_size == 0) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + + LOG_WARNING(Lib_NpWebApi2, "called, request_id = {:#x}, part_index = {}, data_size = {:#x}", + request_id, part_index, data_size); + return sendRequest(request_id, part_index, data, data_size, resp_info_option); +} + +s32 PS4_SYSV_ABI +sceNpWebApi2SendRequest(s64 request_id, void* data, u64 data_size, + OrbisNpWebApi2ResponseInformationOption* resp_info_option) { + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, data_size = {:#x}", request_id, data_size); + return sendRequest(request_id, 0, data, data_size, resp_info_option); +} + +s32 PS4_SYSV_ABI sceNpWebApi2SetMultipartContentType() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpWebApi2SetRequestTimeout(s64 request_id, u32 timeout) { + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, timeout = {}", request_id, timeout); + return setRequestTimeout(request_id, timeout); +} + +s32 PS4_SYSV_ABI sceNpWebApi2Terminate(s32 lib_ctx_id) { + LOG_INFO(Lib_NpWebApi2, "called, lib_ctx_id = {:#x}", lib_ctx_id); + return terminateLibraryContext(lib_ctx_id); +} + +s32 PS4_SYSV_ABI Func_A9A31C5F6FBA6620() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_03D22863300D2B73() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_97296F7578AAD541() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_E0DF39A36F087DB9() { + LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterLib(Core::Loader::SymbolsResolver* sym) { + Libraries::Np::NpManager::RegisterNpCallback("npwebapi2_push", processPushEvents); + + LIB_FUNCTION("zpiPsH7dbFQ", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2AbortRequest); + LIB_FUNCTION("egOOvrnF6mI", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2AddHttpRequestHeader); + LIB_FUNCTION("Io7kh1LHDoM", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2AddMultipartPart); + LIB_FUNCTION("MgsTa76wlEk", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2AddWebTraceTag); + LIB_FUNCTION("3Tt9zL3tkoc", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2CheckTimeout); + LIB_FUNCTION("+nz1Vq-NrDA", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2CreateMultipartRequest); + LIB_FUNCTION("3EI-OSJ65Xc", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2CreateRequest); + LIB_FUNCTION("sk54bi6FtYM", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2CreateUserContext); + LIB_FUNCTION("vvzWO-DvG1s", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2DeleteRequest); + LIB_FUNCTION("9X9+cneTGUU", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2DeleteUserContext); + LIB_FUNCTION("hksbskNToEA", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2GetHttpResponseHeaderValue); + LIB_FUNCTION("HwP3aM+c85c", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2GetHttpResponseHeaderValueLength); + LIB_FUNCTION("Xweb+naPZ8Y", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2GetMemoryPoolStats); + LIB_FUNCTION("+o9816YQhqQ", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2Initialize); + LIB_FUNCTION("dowMWFgowXY", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2InitializeForPresence); + LIB_FUNCTION("qmINYLuqzaA", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2IntCreateRequest); + LIB_FUNCTION("zXaFo7euxsQ", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2IntInitialize); + LIB_FUNCTION("9KSGFMRnp3k", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2IntInitialize2); + LIB_FUNCTION("2hlBNB96saE", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2IntPushEventCreateCtxIndFilter); + LIB_FUNCTION("1OLgvahaSco", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventAbortHandle); + LIB_FUNCTION("MsaFhR+lPE4", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventCreateFilter); + LIB_FUNCTION("WV1GwM32NgY", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventCreateHandle); + LIB_FUNCTION("NNVf18SlbT8", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventCreatePushContext); + LIB_FUNCTION("KJdPcOGmK58", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventDeleteFilter); + LIB_FUNCTION("fIATVMo4Y1w", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventDeleteHandle); + LIB_FUNCTION("QafxeZM3WK4", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventDeletePushContext); + LIB_FUNCTION("fY3QqeNkF8k", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventRegisterCallback); + LIB_FUNCTION("lxtHJMwBsaU", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventRegisterPushContextCallback); + LIB_FUNCTION("KWkc6Q3tjXc", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventSetHandleTimeout); + LIB_FUNCTION("AAj9X+4aGYA", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventStartPushContextCallback); + LIB_FUNCTION("hOnIlcGrO6g", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventUnregisterCallback); + LIB_FUNCTION("PmyrbbJSFz0", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2PushEventUnregisterPushContextCallback); + LIB_FUNCTION("OOY9+ObfKec", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2ReadData); + LIB_FUNCTION("NKCwS8+5Fx8", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2SendMultipartRequest); + LIB_FUNCTION("lQOCF84lvzw", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2SendRequest); + LIB_FUNCTION("bltDCAskmfE", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2SetMultipartContentType); + LIB_FUNCTION("TjAutbrkr60", "libSceNpWebApi2", 1, "libSceNpWebApi2", + sceNpWebApi2SetRequestTimeout); + LIB_FUNCTION("bEvXpcEk200", "libSceNpWebApi2", 1, "libSceNpWebApi2", sceNpWebApi2Terminate); + LIB_FUNCTION("qaMcX2+6ZiA", "libSceNpWebApi2", 1, "libSceNpWebApi2", Func_A9A31C5F6FBA6620); + LIB_FUNCTION("A9IoYzANK3M", "libSceNpWebApi2AsyncRestricted", 1, "libSceNpWebApi2", + Func_03D22863300D2B73); + LIB_FUNCTION("lylvdXiq1UE", "libSceNpWebApi2AsyncRestricted", 1, "libSceNpWebApi2", + Func_97296F7578AAD541); + LIB_FUNCTION("4N85o28Ifbk", "libSceNpWebApi2AsyncRestricted", 1, "libSceNpWebApi2", + Func_E0DF39A36F087DB9); +}; + +} // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2.h b/src/core/libraries/np/np_web_api2/np_web_api2.h new file mode 100644 index 000000000..01816bcc4 --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2.h @@ -0,0 +1,102 @@ +// 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_types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Np::NpWebApi2 { + +struct OrbisNpWebApi2IntInitializeArgs { + s32 lib_http_ctx_id; + s32 reserved; + u64 pool_size; + const char* name; + u64 struct_size; +}; + +struct OrbisNpWebApi2IntInitialize2Args { + s32 lib_http_ctx_id; + s32 reserved; + u64 pool_size; + const char* name; + u32 push_config_group; + s32 reserved2; + u64 struct_size; +}; + +struct OrbisNpWebApi2ContentParameter { + u64 content_length; + const char* content_type; + u8 reserved[16]; +}; + +struct OrbisNpWebApi2MemoryPoolStats { + u64 pool_size; + u64 max_inuse_size; + u64 current_inuse_size; + s32 reserved; +}; + +struct OrbisNpWebApi2ResponseInformationOption { + s32 http_status; + s32 reserved; + char* error_object; + u64 error_object_size; + u64 response_data_size; +}; + +struct OrbisNpWebApi2PushEventDataType { + char val[65]; +}; + +struct OrbisNpWebApi2PushEventExtdDataKey { + char val[33]; +}; + +struct OrbisNpWebApi2PushEventFilterParameter { + OrbisNpWebApi2PushEventDataType data_type; + OrbisNpWebApi2PushEventExtdDataKey* extd_data_key; + u64 extd_data_key_num; +}; + +struct OrbisNpWebApi2PushEventExtdData { + OrbisNpWebApi2PushEventExtdDataKey extd_data_key; + char* data; + u64 data_len; +}; + +using OrbisNpWebApi2PushEventCallback = void PS4_SYSV_ABI (*)( + s32 user_ctx_id, s32 callback_id, const char* np_service_name, + OrbisNpServiceLabel np_service_label, const OrbisNpPeerAddressA* to, + const OrbisNpOnlineId* to_online_id, const OrbisNpPeerAddressA* from, + const OrbisNpOnlineId* from_online_id, const OrbisNpWebApi2PushEventDataType* data_type, + const char* data, u64 data_len, const OrbisNpWebApi2PushEventExtdData* extd_data, + u64 extd_data_num, void* user_arg); + +enum OrbisNpWebApi2PushEventPushContextCallbackType { + Unknown = -1, + Received = 0, + Dropped = 1, +}; + +struct OrbisNpWebApi2PushEventPushContextId { + char uuid[37]; +}; + +using OrbisNpWebApi2PushEventPushContextCallback = void PS4_SYSV_ABI (*)( + s32 user_ctx_id, s32 callback_id, const OrbisNpWebApi2PushEventPushContextId* push_ctx_id, + OrbisNpWebApi2PushEventPushContextCallbackType cb_type, const char* np_service_name, + OrbisNpServiceLabel np_service_label, const OrbisNpPeerAddressA* to, + const OrbisNpOnlineId* to_online_id, const OrbisNpPeerAddressA* from, + const OrbisNpOnlineId* from_online_id, const OrbisNpWebApi2PushEventDataType* data_type, + const char* data, u64 data_len, const OrbisNpWebApi2PushEventExtdData* extd_data, + u64 extd_data_num, void* user_arg); + +void RegisterLib(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_context.cpp b/src/core/libraries/np/np_web_api2/np_web_api2_context.cpp new file mode 100644 index 000000000..1424ff054 --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2_context.cpp @@ -0,0 +1,630 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/network/http.h" +#include "core/libraries/network/http2.h" +#include "core/libraries/np/np_common.h" +#include "core/libraries/np/np_error.h" +#include "core/libraries/np/np_handler.h" +#include "core/libraries/np/np_web_api2/np_web_api2_context.h" + +namespace Libraries::Np::NpWebApi2 { + +u32 g_current_push_event_handle_id{}; +u32 g_current_push_event_filter_id{}; +u32 g_current_push_event_callback_id{}; +u32 g_current_push_context_callback_id{}; +u32 g_current_user_context_id{}; +u64 g_current_request_id{}; + +void LibraryContext::CheckTimeout() { + u64 time = Libraries::Kernel::sceKernelGetProcessTime(); + std::scoped_lock lk{this->lock}; + for (auto& [user_ctx_id, user_ctx] : this->user_contexts) { + user_ctx->CheckTimeout(); + } + for (auto& [handle_id, handle] : this->push_event_handles) { + handle->CheckTimeout(time); + } +} + +s32 LibraryContext::CreateUserContext(Libraries::UserService::OrbisUserServiceUserId user_id) { + std::scoped_lock lk{this->lock}; + if (this->user_contexts.size() >= 0x10000) { + LOG_ERROR(Lib_NpWebApi2, "Too many user contexts"); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_MAX; + } + + s32 actual_user_ctx_id = 0; + do { + g_current_user_context_id++; + if (g_current_user_context_id >= 0x10000) { + g_current_user_context_id = 1; + } + actual_user_ctx_id = (this->id << 0x10) | g_current_user_context_id; + } while (this->user_contexts.contains(actual_user_ctx_id)); + + this->user_contexts[actual_user_ctx_id] = new UserContext(this, actual_user_ctx_id, user_id); + return actual_user_ctx_id; +} + +s32 LibraryContext::CreatePushEventHandle() { + std::scoped_lock lk{this->lock}; + if (g_current_push_event_handle_id + 1 < 0xf0000000) { + ++g_current_push_event_handle_id; + } else { + g_current_push_event_handle_id = 1; + } + + s32 new_handle_id = g_current_push_event_handle_id; + this->push_event_handles[new_handle_id] = new PushEventHandle(new_handle_id); + return new_handle_id; +} + +PushEventHandle* LibraryContext::GetPushEventHandle(s32 handle_id) { + std::scoped_lock lk{lock}; + if (!push_event_handles.contains(handle_id)) { + return nullptr; + } + PushEventHandle* handle = push_event_handles[handle_id]; + handle->AddUser(); + return handle; +} + +s32 LibraryContext::DeletePushEventHandle(PushEventHandle* handle) { + this->Lock(); + s32 handle_id = handle->GetId(); + if (handle->IsDeleted()) { + this->Unlock(); + LOG_ERROR(Lib_NpWebApi2, "handle with id {:#x} is already deleted", handle_id); + return ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND; + } + + handle->MarkForDeletion(); + handle->Abort(); + + while (handle->IsBusy()) { + handle->RemoveUser(); + this->Unlock(); + Libraries::Kernel::sceKernelUsleep(50000); + this->Lock(); + handle->AddUser(); + } + + if (push_event_handles.contains(handle_id)) { + push_event_handles.erase(handle_id); + } + delete handle; + + return ORBIS_OK; +} + +s32 LibraryContext::CreatePushEventFilter( + s32 handle_id, const char* np_service_name, OrbisNpServiceLabel np_service_label, + const OrbisNpWebApi2PushEventFilterParameter* filter_param, u64 filter_param_num, + bool internal) { + std::scoped_lock lk{this->lock}; + if (!this->push_event_handles.contains(handle_id)) { + LOG_ERROR(Lib_NpWebApi2, "No push event handle with id {:#x}", handle_id); + return ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND; + } + PushEventHandle* handle = this->push_event_handles[handle_id]; + handle->AddUser(); + + if (g_current_push_event_filter_id + 1 < 0xf0000000) { + ++g_current_push_event_filter_id; + } else { + g_current_push_event_filter_id = 1; + } + + s32 new_filter_id = g_current_push_event_filter_id; + this->push_event_filters[new_filter_id] = + new PushEventFilter(new_filter_id, np_service_name, np_service_label, internal); + PushEventFilter* filter = this->push_event_filters[new_filter_id]; + s32 result = filter->Initialize(handle, filter_param, filter_param_num); + if (result < 0) { + // Failed to initialize filter. + delete filter; + this->push_event_filters.erase(new_filter_id); + handle->RemoveUser(); + return result; + } + handle->RemoveUser(); + return new_filter_id; +} + +UserContext* LibraryContext::GetUserContext(s32 user_ctx_id) { + std::scoped_lock lk{this->lock}; + if (!this->user_contexts.contains(user_ctx_id)) { + return nullptr; + } + + UserContext* user_ctx = this->user_contexts.at(user_ctx_id); + if (user_ctx->IsDeleted()) { + return nullptr; + } + user_ctx->AddUser(); + return user_ctx; +} + +UserContext* LibraryContext::GetUserContextByUserId( + Libraries::UserService::OrbisUserServiceUserId user_id) { + if (user_id == Libraries::UserService::ORBIS_USER_SERVICE_USER_ID_INVALID) { + return nullptr; + } + + std::scoped_lock lk{this->lock}; + for (auto& [user_ctx_id, user_ctx] : this->user_contexts) { + if (user_ctx->GetUserId() == user_id) { + user_ctx->AddUser(); + return user_ctx; + } + } + + return nullptr; +} + +s32 PS4_SYSV_ABI internalPreSendCallback(s32 http_request_id, s32 ssl_id, + Libraries::Http2::OrbisHttp2PreSendCallbackData* data, + void* user_arg) { + s64 user_ctx_id = reinterpret_cast(user_arg); + LOG_ERROR(Lib_NpWebApi2, + "Unimplemented, http_request_id = {:#x}, ssl_id = {:#x}, user_ctx_id = {:#x}", + http_request_id, ssl_id, user_ctx_id); + return ORBIS_OK; +} + +s32 UserContext::Initialize() { + const char* name = this->parent_ctx->GetName(); + + char sw_version[8]; + std::memset(sw_version, 0, sizeof(sw_version)); + Libraries::Np::NpCommon::sceNpGetSdkVersion(sw_version); + + char user_agent_buf[0x40]; + std::memset(user_agent_buf, 0, sizeof(user_agent_buf)); + if (name) { + std::snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s (%s)", sw_version, + name); + } else { + std::snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s", sw_version); + } + this->user_agent = std::string{user_agent_buf}; + + s32 http_ctx_id = this->parent_ctx->GetHttpCtxId(); + http_template_id = Libraries::Http2::sceHttp2CreateTemplate( + http_ctx_id, user_agent_buf, + Libraries::Http2::OrbisHttp2HttpVersion::ORBIS_HTTP2_VERSION_2_0, 0); + if (http_template_id < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to create HTTP template, error = {:#x}", http_template_id); + return http_template_id; + } + + s32 result = Libraries::Http2::sceHttp2SetPreSendCallback( + http_template_id, internalPreSendCallback, reinterpret_cast(this->id)); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to set pre-send callback, error = {:#x}", result); + return result; + } + + return result; +} + +void UserContext::CheckTimeout() { + parent_ctx->Lock(); + for (auto& [request_id, request] : requests) { + request->CheckTimeout(); + } + parent_ctx->Unlock(); +} + +s32 UserContext::CreatePushEventCallback(s32 filter_id, OrbisNpWebApi2PushEventCallback cb_func, + void* user_arg) { + this->Lock(); + if (g_current_push_event_callback_id + 1 < 0xf0000000) { + ++g_current_push_event_callback_id; + } else { + g_current_push_event_callback_id = 1; + } + + s32 new_callback_id = g_current_push_event_callback_id; + this->push_event_callbacks[new_callback_id] = + new PushEventCallback(new_callback_id, filter_id, cb_func, user_arg, false); + this->Unlock(); + return new_callback_id; +} + +s32 UserContext::DeletePushEventCallback(s32 callback_id) { + this->Lock(); + if (!this->push_event_callbacks.contains(callback_id)) { + this->Unlock(); + LOG_ERROR(Lib_NpWebApi2, "No push event callback with id {:#x}", callback_id); + return ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND; + } + + PushEventCallback* callback = this->push_event_callbacks[callback_id]; + while (callback->is_busy) { + this->Unlock(); + Libraries::Kernel::sceKernelUsleep(100000); + this->Lock(); + } + + delete callback; + this->push_event_callbacks.erase(callback_id); + this->Unlock(); + return ORBIS_OK; +} + +void UserContext::CreatePushContext(OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + this->Lock(); + while (this->push_contexts_busy) { + this->Unlock(); + Libraries::Kernel::sceKernelUsleep(10000); + this->Lock(); + } + this->push_contexts_busy = true; + PushEventPushContext* push_ctx = new PushEventPushContext(5000, this); + push_ctx->Initialize(); + OrbisNpWebApi2PushEventPushContextId* new_id = push_ctx->GetId(); + std::memcpy(push_ctx_id, new_id, sizeof(*push_ctx_id)); + + s64 raw_id = push_ctx->GetFakeId(); + this->push_contexts[raw_id] = push_ctx; + this->push_contexts_busy = false; + this->Unlock(); +} + +PushEventPushContext* UserContext::GetPushContext( + const OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + parent_ctx->Lock(); + s64 raw_id{}; + std::memcpy(&raw_id, push_ctx_id, sizeof(s64)); + if (!push_contexts.contains(raw_id)) { + return nullptr; + } + PushEventPushContext* push_ctx = push_contexts[raw_id]; + parent_ctx->Unlock(); + return push_ctx; +} + +void UserContext::DeletePushContext(PushEventPushContext* push_ctx) { + this->Lock(); + while (this->push_contexts_busy) { + this->Unlock(); + Libraries::Kernel::sceKernelUsleep(10000); + this->Lock(); + } + this->push_contexts_busy = true; + + while (push_ctx->CallbackRunning()) { + this->Unlock(); + Libraries::Kernel::sceKernelUsleep(100000); + this->Lock(); + } + + s64 raw_id = push_ctx->GetFakeId(); + this->push_contexts.erase(raw_id); + delete push_ctx; + this->push_contexts_busy = false; + this->Unlock(); +} + +s32 UserContext::CreatePushContextCallback(s32 filter_id, + OrbisNpWebApi2PushEventPushContextCallback cb_func, + void* user_arg) { + this->Lock(); + if (g_current_push_context_callback_id + 1 < 0xf0000000) { + ++g_current_push_context_callback_id; + } else { + g_current_push_context_callback_id = 1; + } + + s32 new_callback_id = g_current_push_context_callback_id; + this->push_context_callbacks[new_callback_id] = + new PushEventPushContextCallback(new_callback_id, filter_id, cb_func, user_arg, false); + this->Unlock(); + return new_callback_id; +} + +s32 UserContext::DeletePushContextCallback(s32 callback_id) { + this->Lock(); + if (!this->push_context_callbacks.contains(callback_id)) { + this->Unlock(); + LOG_ERROR(Lib_NpWebApi2, "No push event push context callback with id {:#x}", callback_id); + return ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND; + } + + PushEventPushContextCallback* callback = this->push_context_callbacks[callback_id]; + while (callback->is_busy) { + this->Unlock(); + Libraries::Kernel::sceKernelUsleep(100000); + this->Lock(); + } + + delete callback; + this->push_context_callbacks.erase(callback_id); + this->Unlock(); + return ORBIS_OK; +} + +s32 UserContext::CreateRequest(const char* api_group, const char* path, const char* method, + const OrbisNpWebApi2ContentParameter* content_parameter, + bool multipart, Request** request) { + this->Lock(); + s64 actual_request_id = 0; + do { + g_current_request_id++; + if (g_current_request_id >> 0x20 != 0) { + g_current_request_id = 1; + } + actual_request_id = (static_cast(this->id) << 0x20) | g_current_request_id; + } while (this->requests.contains(actual_request_id)); + + if (content_parameter) { + this->requests[actual_request_id] = + new Request(this->parent_ctx, actual_request_id, api_group, path, method, multipart, + content_parameter); + } else { + this->requests[actual_request_id] = + new Request(this->parent_ctx, actual_request_id, api_group, path, method, multipart); + } + *request = this->requests[actual_request_id]; + this->Unlock(); + return ORBIS_OK; +} + +Request* UserContext::GetRequest(s64 request_id) { + this->Lock(); + if (!this->requests.contains(request_id)) { + this->Unlock(); + return nullptr; + } + + Request* request = this->requests[request_id]; + request->AddUser(); + this->Unlock(); + return request; +} + +bool UserContext::HasBusyRequests() { + this->Lock(); + for (auto& [request_id, request] : this->requests) { + request->AddUser(); + bool busy = request->IsBusy(); + request->RemoveUser(); + if (busy) { + this->Unlock(); + return true; + } + } + this->Unlock(); + return false; +} + +void UserContext::AbortAllRequests() { + this->Lock(); + for (auto& [request_id, request] : this->requests) { + request->Abort(); + } + this->Unlock(); +} + +void UserContext::Delete() { + if (this->http_template_id != 0) { + Libraries::Http2::sceHttp2DeleteTemplate(this->http_template_id); + } + this->parent_ctx->RemoveUserContext(this->id); + this->RemoveUser(); + + for (auto& [request_id, request] : this->requests) { + request->Delete(nullptr); + } + this->requests.clear(); + + for (auto& [push_ctx_id, push_ctx] : this->push_contexts) { + delete push_ctx; + } + this->push_contexts.clear(); + + for (auto& [callback_id, callback] : this->push_event_callbacks) { + delete callback; + } + this->push_event_callbacks.clear(); + + for (auto& [callback_id, callback] : this->push_context_callbacks) { + delete callback; + } + this->push_context_callbacks.clear(); + delete this; +} + +static bool IsInternalHeader(const char* header_name) { + std::string lower_name{header_name}; + std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), + [](char c) { return std::tolower(c); }); + // These headers are all skipped if the request's library context + // was not created through the IntInitialize functions. + return lower_name.compare("content-type") == 0 || lower_name.compare("user-agent") == 0 || + lower_name.compare("authorization") == 0 || + lower_name.compare("x-psn-np-title-id") == 0 || + lower_name.compare("x-psn-np-title-token") == 0 || + lower_name.compare("x-psn-request-id") == 0 || + lower_name.compare("x-psn-sdk-ver") == 0 || + lower_name.compare("x-psn-debug-settings") == 0 || lower_name.compare("npdebug") == 0 || + lower_name.compare("x-psn-trc-check-notification-enabled") == 0 || + lower_name.compare("x-psn-webtrace-enabled") == 0 || + lower_name.compare("x-psn-npwebapi2-context") == 0 || + lower_name.compare("x-psn-fake-rate-limit-enabled") == 0 || + lower_name.compare("x-psn-fake-rate-limit-targets") == 0; +} + +s32 Request::AddHttpRequestHeader(const char* field_name, const char* field_value) { + if (!this->parent_ctx->IsInternal() && IsInternalHeader(field_name)) { + LOG_WARNING(Lib_NpWebApi2, "This is not an internal context, skipping field name {}", + field_name); + return ORBIS_OK; + } + + this->Lock(); + this->http_headers.emplace_back(new HttpRequestHeader(field_name, field_value)); + this->Unlock(); + return ORBIS_OK; +} + +s32 Request::CreateHttpRequest(s32 http_template_id, const char* url) { + const char* method = this->method.empty() ? "UNKNOWN" : this->method.data(); + s32 http_request_id = Libraries::Http2::sceHttp2CreateRequestWithURL(http_template_id, method, + url, this->content_length); + if (http_request_id < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to create Http2 request, error = {:#x}", http_request_id); + return http_request_id; + } + this->http_request_id = http_request_id; + s32 result = Libraries::Http2::sceHttp2AddRequestHeader(http_request_id, "Content-Type", + this->content_type.data(), 0); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to add Content-Type request header, error = {:#x}", + result); + return result; + } + + UserContext* user_ctx = this->parent_ctx->GetUserContext(this->id >> 0x20); + const std::string bearer = NpHandler::GetInstance().GetBearerToken(user_ctx->GetUserId()); + if (!bearer.empty()) { + const std::string auth_value = "Bearer " + bearer; + result = Libraries::Http2::sceHttp2AddRequestHeader(http_request_id, "Authorization", + auth_value.data(), 0); + } + if (bearer.empty() || result < 0) { + LOG_WARNING(Lib_NpWebApi2, "Failed to add Authorization request header"); + } + + // This would go on to add several other PSN-specific headers. + // As shadNet does not appear to use them, the logic is skipped for now. + + for (HttpRequestHeader* header : this->http_headers) { + s32 result = Libraries::Http2::sceHttp2AddRequestHeader( + http_request_id, header->field_name.data(), header->field_value.data(), 0); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, + "Failed to add request header, name = {}, value = {}, error = {:#x}", + header->field_name.data(), header->field_value.data(), result); + user_ctx->RemoveUser(); + return result; + } + } + user_ctx->RemoveUser(); + return result; +} + +s32 Request::SendHttpRequest(void* data, u64 data_size) { + s32 result = 0; + if (this->content_length != 0 && this->sent_data == 0) { + // Real library seems to do some calculations, some parts including IPC calls. + // Not entirely sure how accurate this is in practice. + result = Libraries::Http2::sceHttp2SetRequestContentLength(this->http_request_id, + this->content_length); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to set content length, error = {:#x}", result); + return result; + } + } + if (!data || data_size == 0 || this->content_length == 0) { + result = Libraries::Http2::sceHttp2SendRequest(this->http_request_id, nullptr, 0); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to send request, error = {:#x}", result); + return result; + } + } + + u64 actual_size = std::min(data_size, this->content_length - this->sent_data); + if (this->content_length <= this->sent_data) { + // Nothing left to send + return result; + } + result = Libraries::Http2::sceHttp2SendRequest(this->http_request_id, data, actual_size); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to send request, error = {:#x}", result); + return result; + } + this->sent_data += actual_size; + return result; +} + +s32 Request::GetAllHttpResponseHeaders() { + char* header = nullptr; + u64 size = 0; + s32 result = + Libraries::Http2::sceHttp2GetAllResponseHeaders(this->http_request_id, &header, &size); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to get response headers, error = {:#x}", result); + return result; + } + this->http_response_headers = header; + this->http_response_header_size = size; + return result; +} + +s32 Request::ParseHttpResponseHeaders(const char* field_name, char* value, u64 value_size, + u64* value_size_out) { + const char* temp_val{}; + u64 temp_size{}; + s32 result = Libraries::Http::sceHttpParseResponseHeader(this->http_response_headers, + this->http_response_header_size, + field_name, &temp_val, &temp_size); + if (result >= 0) { + if (value) { + std::memset(value, 0, value_size); + u64 size = std::min(value_size, temp_size); + std::memcpy(value, temp_val, size); + } + if (value_size_out) { + *value_size_out = temp_size; + } + result = ORBIS_OK; + } + return result; +} + +s32 Request::Abort() { + this->Lock(); + s32 result = 0; + if (this->Aborted()) { + // Nothing to do. + this->Unlock(); + return result; + } + this->aborted = true; + + // Real library has multiple states, we don't since our logic is simpler. + if (this->send_state == 3) { + // In the middle of an Http request, that needs aborting. + result = Libraries::Http2::sceHttp2AbortRequest(this->http_request_id); + if (result < 0) { + LOG_ERROR(Lib_NpWebApi2, "Failed to abort http request, error = {:#x}", result); + } + } + this->Unlock(); + return result; +} + +s32 Request::Delete(s32* http_request) { + for (HttpRequestHeader* header : this->http_headers) { + delete header; + } + this->http_headers.clear(); + + if (!http_request && this->http_request_id) { + Libraries::Http2::sceHttp2DeleteRequest(this->http_request_id); + } else { + *http_request = this->http_request_id; + } + while (this->user_count > 0) { + Libraries::Kernel::sceKernelUsleep(50000); + } + delete this; + return ORBIS_OK; +} + +}; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_context.h b/src/core/libraries/np/np_web_api2/np_web_api2_context.h new file mode 100644 index 000000000..17055d597 --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2_context.h @@ -0,0 +1,463 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/time.h" +#include "core/libraries/np/np_web_api2/np_web_api2.h" +#include "core/libraries/np/np_web_api2/np_web_api2_internal.h" +#include "core/libraries/np/np_web_api2/np_web_api2_push_event.h" +#include "core/libraries/system/userservice.h" + +#include +#include +#include +#include +#include + +namespace Libraries::Np::NpWebApi2 { + +class UserContext; +class Request; + +class LibraryContext { +public: + LibraryContext(s32 ctx_id, s32 type, s32 http_ctx_id, u64 pool_size) + : id(ctx_id), type(static_cast(type)), http_ctx_id(http_ctx_id), + pool_size(pool_size) {} + LibraryContext(s32 ctx_id, s32 type, s32 http_ctx_id, u64 pool_size, const char* name) + : id(ctx_id), type(static_cast(type)), http_ctx_id(http_ctx_id), + pool_size(pool_size), name(name) {} + + void Lock() { + lock.lock(); + } + + void Unlock() { + lock.unlock(); + } + + void AddUser() { + std::scoped_lock lk{lock}; + user_count++; + } + + void RemoveUser() { + std::scoped_lock lk{lock}; + user_count--; + } + + s32 GetId() { + return id; + } + + s32 GetHttpCtxId() { + return http_ctx_id; + } + + u64 GetPoolSize() { + return pool_size; + } + + const char* GetName() { + return name.empty() ? nullptr : name.data(); + } + + bool IsDebug() { + return type == LibraryContextType::Debug; + } + + bool IsNormal() { + return type == LibraryContextType::Normal; + } + + bool IsInternal() { + return type == LibraryContextType::Internal; + } + + bool IsPresence() { + return type == LibraryContextType::Presence; + } + + bool IsDeleted() { + return deleting; + } + + void MarkForDeletion() { + deleting = true; + } + + bool IsBusy() { + std::scoped_lock lk{lock}; + return user_count > 1; + } + + void CheckTimeout(); + + s32 CreatePushEventHandle(); + PushEventHandle* GetPushEventHandle(s32 handle_id); + + void SetHandleEndTime(s32 handle_id) { + std::scoped_lock lk{lock}; + if (push_event_handles.contains(handle_id)) { + auto& handle = push_event_handles[handle_id]; + handle->SetEndTime(); + } + } + + void AbortAllPushEventHandles() { + std::scoped_lock lk{lock}; + for (auto& [handle_id, handle] : push_event_handles) { + abortPushEventHandle(id, handle_id); + } + } + + bool HasBusyPushEventHandles() { + std::scoped_lock lk{lock}; + for (auto& [handle_id, handle] : push_event_handles) { + if (handle->IsBusy()) { + return true; + } + } + return false; + } + + s32 DeletePushEventHandle(PushEventHandle* handle); + + void DeleteAllPushEventHandles() { + for (auto& [handle_id, handle] : push_event_handles) { + delete handle; + } + push_event_handles.clear(); + } + + s32 CreatePushEventFilter(s32 handle_id, const char* np_service_name, + OrbisNpServiceLabel np_service_label, + const OrbisNpWebApi2PushEventFilterParameter* filter_param, + u64 filter_param_num, bool internal); + + PushEventFilter* GetPushEventFilter(s32 filter_id) { + std::scoped_lock lk{lock}; + if (!push_event_filters.contains(filter_id)) { + return nullptr; + } + return push_event_filters[filter_id]; + } + + void DeletePushEventFilter(PushEventFilter* filter) { + std::scoped_lock lk{lock}; + s32 filter_id = filter->GetId(); + if (push_event_filters.contains(filter_id)) { + push_event_filters.erase(filter_id); + } + delete filter; + } + + void DeleteAllPushEventFilters() { + for (auto& [filter_id, filter] : push_event_filters) { + delete filter; + } + push_event_filters.clear(); + } + + s32 CreateUserContext(Libraries::UserService::OrbisUserServiceUserId user_id); + UserContext* GetUserContext(s32 user_ctx_id); + UserContext* GetUserContextByUserId(Libraries::UserService::OrbisUserServiceUserId user_id); + + void RemoveUserContext(s32 user_ctx_id) { + std::scoped_lock lk{lock}; + if (user_contexts.contains(user_ctx_id)) { + user_contexts.erase(user_ctx_id); + } + } + + void DeleteAllUserContexts() { + std::scoped_lock lk{lock}; + for (auto& [user_ctx_id, user_ctx] : user_contexts) { + deleteUserContext(user_ctx_id); + } + user_contexts.clear(); + } + +private: + s32 id{}; + s32 http_ctx_id{}; + s32 user_count{}; + enum LibraryContextType : s32 { + Debug = 0, + Normal = 1, + Internal = 2, + Presence = 3, + } type; + u64 pool_size{}; + bool deleting{}; + std::recursive_mutex lock{}; + std::string name{}; + std::map user_contexts{}; + std::map push_event_handles{}; + std::map push_event_filters{}; +}; + +class UserContext { +public: + UserContext(LibraryContext* parent, s32 user_ctx_id, + Libraries::UserService::OrbisUserServiceUserId user_id) + : parent_ctx(parent), id(user_ctx_id), user_id(user_id) {} + + void Lock() { + parent_ctx->Lock(); + } + + void Unlock() { + parent_ctx->Unlock(); + } + + void AddUser() { + Lock(); + user_count++; + Unlock(); + } + + void RemoveUser() { + Lock(); + user_count--; + Unlock(); + } + + bool IsDeleted() { + return deleting; + } + + void MarkForDeletion() { + deleting = true; + } + + bool IsBusy() { + parent_ctx->Lock(); + s32 users = user_count; + parent_ctx->Unlock(); + // Assumes caller is a user + return users > 1; + } + + s32 GetId() { + return id; + } + + Libraries::UserService::OrbisUserServiceUserId GetUserId() { + return user_id; + } + + s32 GetHttpTemplateId() { + return http_template_id; + } + + s32 Initialize(); + + void CheckTimeout(); + + s32 CreatePushEventCallback(s32 filter_id, OrbisNpWebApi2PushEventCallback cb_func, + void* user_arg); + s32 DeletePushEventCallback(s32 callback_id); + + void CreatePushContext(OrbisNpWebApi2PushEventPushContextId* push_ctx_id); + PushEventPushContext* GetPushContext(const OrbisNpWebApi2PushEventPushContextId* push_ctx_id); + void DeletePushContext(PushEventPushContext* push_ctx_id); + + s32 CreatePushContextCallback(s32 filter_id, OrbisNpWebApi2PushEventPushContextCallback cb_func, + void* user_arg); + s32 DeletePushContextCallback(s32 callback_id); + + s32 CreateRequest(const char* api_group, const char* path, const char* method, + const OrbisNpWebApi2ContentParameter* content_parameter, bool multipart, + Request** request); + Request* GetRequest(s64 request_id); + bool HasBusyRequests(); + void AbortAllRequests(); + + void RemoveRequest(s64 request_id) { + parent_ctx->Lock(); + if (requests.contains(request_id)) { + requests.erase(request_id); + } + parent_ctx->Unlock(); + } + + void Delete(); + +private: + s32 id{}; + Libraries::UserService::OrbisUserServiceUserId user_id{}; + s32 user_count{}; + s32 http_template_id{}; + bool deleting{}; + bool push_contexts_busy{}; + LibraryContext* parent_ctx{}; + std::string user_agent{}; + std::map requests{}; + std::map push_event_callbacks{}; + std::map push_contexts{}; + std::map push_context_callbacks{}; +}; + +class Request { +public: + Request(LibraryContext* parent, s64 request_id, const char* api_group, const char* path, + const char* method, bool multipart) + : parent_ctx(parent), id(request_id), api_group(api_group), path(path), method(method), + multipart_supported(multipart) {} + Request(LibraryContext* parent, s64 request_id, const char* api_group, const char* path, + const char* method, bool multipart, + const OrbisNpWebApi2ContentParameter* content_parameter) + : parent_ctx(parent), id(request_id), api_group(api_group), path(path), method(method), + multipart_supported(multipart), content_type(content_parameter->content_type), + content_length(content_parameter->content_length) {} + + void Lock() { + parent_ctx->Lock(); + } + + void Unlock() { + parent_ctx->Unlock(); + } + + void AddUser() { + Lock(); + user_count++; + Unlock(); + } + + void RemoveUser() { + Lock(); + user_count--; + Unlock(); + } + + s64 GetId() { + return id; + } + + s32 GetHttpRequestId() { + return http_request_id; + } + + bool IsMultipart() { + return multipart_supported; + } + + bool HasSent() { + return sent; + } + + void MarkSent() { + sent = true; + } + + bool Aborted() { + return aborted; + } + + bool Expired() { + return expired; + } + + bool OutOfData() { + return content_length == sent_data; + } + + void SetEndTime() { + if (timeout != 0 && end_time == 0) { + end_time = Libraries::Kernel::sceKernelGetProcessTime() + timeout; + } + } + + void ClearEndTime() { + end_time = 0; + } + + std::string& GetApiGroup() { + return api_group; + } + + std::string& GetPath() { + return path; + } + + std::string& GetMethod() { + return method; + } + + std::string& GetContentType() { + return content_type; + } + + void SetState(s32 state) { + send_state = state; + } + + bool IsDeleted() { + return deleting; + } + + void MarkForDeletion() { + deleting = true; + } + + bool IsBusy() { + parent_ctx->Lock(); + s32 users = user_count; + parent_ctx->Unlock(); + // Assumes caller is a user + return users > 1; + } + + s32 AddHttpRequestHeader(const char* field_name, const char* field_value); + + void SetTimeout(u32 new_timeout) { + this->timeout = new_timeout; + } + + void CheckTimeout() { + u64 time = Libraries::Kernel::sceKernelGetProcessTime(); + if (!expired && end_time != 0 && end_time < time) { + expired = true; + Abort(); + } + } + + s32 CreateHttpRequest(s32 http_template_id, const char* url); + s32 SendHttpRequest(void* data, u64 data_size); + s32 GetAllHttpResponseHeaders(); + s32 ParseHttpResponseHeaders(const char* field_name, char* value, u64 value_size, + u64* value_size_out); + s32 Abort(); + s32 Delete(s32* http_request_id); + +private: + s64 id{}; + u64 content_length{}; + char* http_response_headers{}; + u64 http_response_header_size{}; + s32 user_count{}; + s32 http_request_id{}; + u32 timeout{}; + s32 sent_data{}; + s32 send_state{}; + bool multipart_supported{}; + bool sent{}; + bool aborted{}; + bool expired{}; + bool deleting{}; + u64 end_time{}; + LibraryContext* parent_ctx{}; + std::string api_group{}; + std::string path{}; + std::string method{}; + std::string content_type{}; + + struct HttpRequestHeader { + std::string field_name; + std::string field_value; + }; + std::vector http_headers{}; +}; + +}; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp b/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp new file mode 100644 index 000000000..eacdd1251 --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp @@ -0,0 +1,932 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/emulator_settings.h" +#include "core/libraries/kernel/time.h" +#include "core/libraries/network/http2.h" +#include "core/libraries/np/np_error.h" +#include "core/libraries/np/np_types.h" +#include "core/libraries/np/np_web_api2/np_web_api2_context.h" +#include "core/libraries/np/np_web_api2/np_web_api2_internal.h" +#include "core/libraries/system/userservice.h" + +#include +#include + +namespace Libraries::Np::NpWebApi2 { + +std::recursive_mutex g_mutex{}; +s32 g_current_lib_context_id{}; +std::map g_lib_contexts{}; +u64 g_last_timeout_check{}; + +s32 createLibraryContext(s32 http_ctx_id, s32 type, u64 pool_size, const char* name) { + std::scoped_lock lk{g_mutex}; + + if (g_lib_contexts.size() >= 0x8000) { + LOG_ERROR(Lib_NpWebApi2, "Too many library contexts"); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_MAX; + } + + do { + g_current_lib_context_id++; + if (g_current_lib_context_id >= 0x8000) { + g_current_lib_context_id = 1; + } + } while (g_lib_contexts.contains(g_current_lib_context_id)); + + if (!name) { + g_lib_contexts[g_current_lib_context_id] = + new LibraryContext(g_current_lib_context_id, type, http_ctx_id, pool_size); + } else { + g_lib_contexts[g_current_lib_context_id] = + new LibraryContext(g_current_lib_context_id, type, http_ctx_id, pool_size, name); + } + return g_current_lib_context_id; +} + +LibraryContext* getLibraryContext(s32 lib_ctx_id) { + std::scoped_lock lk{g_mutex}; + + if (!g_lib_contexts.contains(lib_ctx_id)) { + return nullptr; + } + LibraryContext* lib_ctx = g_lib_contexts.at(lib_ctx_id); + if (lib_ctx->IsDeleted()) { + return nullptr; + } + lib_ctx->AddUser(); + return lib_ctx; +} + +s32 getMemoryPoolStats(s32 lib_ctx_id, OrbisNpWebApi2MemoryPoolStats* stats) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + if (stats) { + memset(stats, 0, sizeof(*stats)); + stats->pool_size = lib_ctx->GetPoolSize(); + } + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 createPushEventHandle(s32 lib_ctx_id) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + s32 result = lib_ctx->CreatePushEventHandle(); + lib_ctx->RemoveUser(); + return result; +} + +s32 setHandleTimeout(s32 lib_ctx_id, s32 handle_id, u32 timeout) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + PushEventHandle* handle = lib_ctx->GetPushEventHandle(handle_id); + if (!handle) { + LOG_ERROR(Lib_NpWebApi2, "No handle with id {:#x}", handle_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND; + } + + lib_ctx->Lock(); + handle->SetTimeout(timeout); + handle->RemoveUser(); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 abortPushEventHandle(s32 lib_ctx_id, s32 handle_id) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + PushEventHandle* handle = lib_ctx->GetPushEventHandle(handle_id); + if (!handle) { + LOG_ERROR(Lib_NpWebApi2, "No handle with id {:#x}", handle_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND; + } + + lib_ctx->Lock(); + handle->Abort(); + lib_ctx->Unlock(); + handle->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 deletePushEventHandle(s32 lib_ctx_id, s32 handle_id) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + PushEventHandle* handle = lib_ctx->GetPushEventHandle(handle_id); + if (!handle) { + LOG_ERROR(Lib_NpWebApi2, "No handle with id {:#x}", handle_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND; + } + + s32 result = lib_ctx->DeletePushEventHandle(handle); + lib_ctx->RemoveUser(); + return result; +} + +s32 createPushEventFilter(s32 lib_ctx_id, s32 handle_id, const char* np_service_name, + OrbisNpServiceLabel np_service_label, + const OrbisNpWebApi2PushEventFilterParameter* filter_param, + u64 filter_param_num, bool internal) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + lib_ctx->SetHandleEndTime(handle_id); + s32 result = lib_ctx->CreatePushEventFilter(handle_id, np_service_name, np_service_label, + filter_param, filter_param_num, internal); + lib_ctx->RemoveUser(); + return result; +} + +s32 deletePushEventFilter(s32 lib_ctx_id, s32 filter_id) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + PushEventFilter* filter = lib_ctx->GetPushEventFilter(filter_id); + if (!filter) { + LOG_ERROR(Lib_NpWebApi2, "No filter with id {:#x}", filter_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_FILTER_NOT_FOUND; + } + + lib_ctx->DeletePushEventFilter(filter); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 terminateLibraryContext(s32 lib_ctx_id) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + lib_ctx->Lock(); + lib_ctx->DeleteAllUserContexts(); + lib_ctx->AbortAllPushEventHandles(); + + if (lib_ctx->IsDeleted()) { + LOG_ERROR(Lib_NpWebApi2, "Library context with id {:#x} is already deleted", lib_ctx_id); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + lib_ctx->MarkForDeletion(); + while (lib_ctx->IsBusy() || lib_ctx->HasBusyPushEventHandles()) { + lib_ctx->Unlock(); + Libraries::Kernel::sceKernelUsleep(50000); + lib_ctx->Lock(); + } + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + + g_mutex.lock(); + g_lib_contexts.erase(lib_ctx_id); + g_mutex.unlock(); + + lib_ctx->DeleteAllPushEventHandles(); + lib_ctx->DeleteAllPushEventFilters(); + delete lib_ctx; + + return ORBIS_OK; +} + +s32 createUserContext(s32 lib_ctx_id, Libraries::UserService::OrbisUserServiceUserId user_id) { + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context with id {:#x}", lib_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContextByUserId(user_id); + if (user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "User context already exists for user id {}", user_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_ALREADY_EXIST; + } + + s32 user_ctx_id = lib_ctx->CreateUserContext(user_id); + if (user_ctx_id < 0) { + // user_ctx_id contains an error code, return it. + lib_ctx->RemoveUser(); + return user_ctx_id; + } + + user_ctx = lib_ctx->GetUserContext(user_ctx_id); + user_ctx->RemoveUser(); + + s32 result = user_ctx->Initialize(); + if (result < 0) { + // Failed to initialize user_ctx, destroy it. + lib_ctx->RemoveUserContext(user_ctx_id); + delete user_ctx; + } + + lib_ctx->RemoveUser(); + return user_ctx_id; +} + +s32 deleteUserContext(s32 user_ctx_id) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + lib_ctx->Lock(); + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + user_ctx->AbortAllRequests(); + if (user_ctx->IsDeleted()) { + LOG_ERROR(Lib_NpWebApi2, "User context with id {:#x} is already deleted", user_ctx_id); + user_ctx->RemoveUser(); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + user_ctx->MarkForDeletion(); + while (user_ctx->IsBusy() || user_ctx->HasBusyRequests()) { + lib_ctx->Unlock(); + Libraries::Kernel::sceKernelUsleep(50000); + lib_ctx->Lock(); + } + + user_ctx->Delete(); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 registerPushEventCallback(s32 user_ctx_id, s32 filter_id, + OrbisNpWebApi2PushEventCallback cb_func, void* user_arg) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (!lib_ctx->GetPushEventFilter(filter_id)) { + LOG_ERROR(Lib_NpWebApi2, "No push event filter with id {:#x}", filter_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_FILTER_NOT_FOUND; + } + + s32 result = user_ctx->CreatePushEventCallback(filter_id, cb_func, user_arg); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 unregisterPushEventCallback(s32 user_ctx_id, s32 callback_id) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + s32 result = user_ctx->DeletePushEventCallback(callback_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 createPushContext(s32 user_ctx_id, OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + user_ctx->CreatePushContext(push_ctx_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 startPushContextCallback(s32 user_ctx_id, + const OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + PushEventPushContext* push_ctx = user_ctx->GetPushContext(push_ctx_id); + if (!push_ctx) { + s64 raw_id{}; + std::memcpy(&raw_id, push_ctx_id, sizeof(s64)); + LOG_ERROR(Lib_NpWebApi2, "No push context with id {}", raw_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_PUSH_CONTEXT_NOT_FOUND; + } + + push_ctx->Start(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 deletePushContext(s32 user_ctx_id, const OrbisNpWebApi2PushEventPushContextId* push_ctx_id) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + PushEventPushContext* push_ctx = user_ctx->GetPushContext(push_ctx_id); + if (!push_ctx) { + s64 raw_id{}; + std::memcpy(&raw_id, push_ctx_id, sizeof(s64)); + LOG_ERROR(Lib_NpWebApi2, "No push context with id {}", raw_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_PUSH_CONTEXT_NOT_FOUND; + } + + user_ctx->DeletePushContext(push_ctx); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +s32 registerPushContextCallback(s32 user_ctx_id, s32 filter_id, + OrbisNpWebApi2PushEventPushContextCallback cb_func, + void* user_arg) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (!lib_ctx->GetPushEventFilter(filter_id)) { + LOG_ERROR(Lib_NpWebApi2, "No push event filter with id {:#x}", filter_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_FILTER_NOT_FOUND; + } + + s32 result = user_ctx->CreatePushContextCallback(filter_id, cb_func, user_arg); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 unregisterPushContextCallback(s32 user_ctx_id, s32 callback_id) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + s32 result = user_ctx->DeletePushContextCallback(callback_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +void processPushEvents() { + // LOG_ERROR(Lib_NpWebApi2, "(STUBBED)"); +} + +s32 createRequest(s32 user_ctx_id, const char* api_group, const char* path, const char* method, + const OrbisNpWebApi2ContentParameter* content_parameter, bool multipart, + s64* request_id) { + LibraryContext* lib_ctx = getLibraryContext(user_ctx_id >> 0x10); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for user context id {:#x}", user_ctx_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context with id {:#x}", user_ctx_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + if (std::strlen(api_group) >= 63) { + LOG_ERROR(Lib_NpWebApi2, "API group is too long"); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_PARAMETER_TOO_LONG; + } + + Request* request{}; + s32 result = + user_ctx->CreateRequest(api_group, path, method, content_parameter, multipart, &request); + if (result < 0) { + // Request creation failed + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + if (request) { + delete request; + } + return result; + } + + if (request_id) { + *request_id = request->GetId(); + } + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 addHttpRequestHeader(s64 request_id, const char* field_name, const char* field_value) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + if (request->HasSent()) { + LOG_ERROR(Lib_NpWebApi2, "Request is already sent"); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_AFTER_SEND; + } + + s32 result = request->AddHttpRequestHeader(field_name, field_value); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 setRequestTimeout(s64 request_id, u32 timeout) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + request->SetTimeout(timeout); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_OK; +} + +static s32 checkRequestStatus(Request* request, UserContext* user_ctx, LibraryContext* lib_ctx) { + if (request->Expired()) { + LOG_ERROR(Lib_NpWebApi2, "Request timed out"); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_TIMEOUT; + } + + if (request->Aborted()) { + LOG_ERROR(Lib_NpWebApi2, "Request aborted"); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_ABORTED; + } + return ORBIS_OK; +} + +s32 sendRequest(s64 request_id, s32 part_index, void* data, u64 data_size, + OrbisNpWebApi2ResponseInformationOption* resp_info_option) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + request->SetEndTime(); + if (request->IsMultipart()) { + if (part_index == 0) { + LOG_ERROR(Lib_NpWebApi2, "part_index 0 is prohibited for multipart requests"); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_PROHIBITED_FUNCTION_CALL; + } + // TODO: Multipart logic + } + + if (!request->HasSent()) { + request->MarkSent(); + } + + request->Lock(); + request->SetState(0); + s32 result = checkRequestStatus(request, user_ctx, lib_ctx); + request->Unlock(); + if (result != 0) { + // Aborted or timed out. + return result; + } + + if (!EmulatorSettings.IsShadNetEnabled()) { + LOG_INFO(Lib_NpWebApi2, "Cannot send request, you are not signed in to shadNet"); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_NOT_SIGNED_IN; + } + + if (request->GetHttpRequestId() == 0) { + std::string base_url = EmulatorSettings.GetShadNetWebApiServer(); + s32 template_id = user_ctx->GetHttpTemplateId(); + // Technically this should be base_url + api_group + path, + // but shadNet doesn't seem to use the api_group for endpoints? + std::string full_url = base_url + request->GetPath(); + result = request->CreateHttpRequest(template_id, full_url.data()); + if (result < 0) { + // Underlying request creation failed, return. + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; + } + } + + request->Lock(); + request->SetState(3); + result = checkRequestStatus(request, user_ctx, lib_ctx); + request->Unlock(); + if (result != 0) { + // Aborted or timed out. + return result; + } + + if (request->IsMultipart()) { + // TODO: Multipart sends + // result = request->SendMultipartHttpRequest(data, data_size, part_index); + } else { + result = request->SendHttpRequest(data, data_size); + } + + if (result >= 0 && request->OutOfData()) { + result = request->GetAllHttpResponseHeaders(); + if (result >= 0) { + s32 http_request_id = request->GetHttpRequestId(); + s32 http_status{}; + result = Libraries::Http2::sceHttp2GetStatusCode(http_request_id, &http_status); + if (result >= 0) { + if (resp_info_option) { + resp_info_option->http_status = http_status; + } + if (http_status >= 400) { + result = http_status | 0x82f00000; + } + } else { + LOG_ERROR(Lib_NpWebApi2, "Failed to get http status code, error = {:#x}", result); + } + } + } + + request->Lock(); + request->SetState(0); + s32 result2 = checkRequestStatus(request, user_ctx, lib_ctx); + request->Unlock(); + if (result2 != 0) { + // Aborted or timed out. + return result2; + } + + request->ClearEndTime(); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 getHttpResponseHeaderData(s64 request_id, const char* field_name, char* value, u64 value_size, + u64* value_size_out) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + s32 result = request->ParseHttpResponseHeaders(field_name, value, value_size, value_size_out); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 readData(s64 request_id, void* data, u64 size) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + request->SetEndTime(); + + request->Lock(); + request->SetState(3); + s32 result = checkRequestStatus(request, user_ctx, lib_ctx); + request->Unlock(); + if (result != 0) { + // Aborted or timed out. + return result; + } + + s32 http_req_id = request->GetHttpRequestId(); + s32 result2 = Libraries::Http2::sceHttp2ReadData(http_req_id, data, size); + + request->Lock(); + request->SetState(0); + result = checkRequestStatus(request, user_ctx, lib_ctx); + request->Unlock(); + if (result != 0) { + // Aborted or timed out. + return result; + } + + request->ClearEndTime(); + + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result2; +} + +s32 abortRequest(s64 request_id) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + request->RemoveUser(); + s32 result = request->Abort(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + +s32 deleteRequest(s64 request_id) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + lib_ctx->Lock(); + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + if (request->IsDeleted()) { + // Already marked for deletion + LOG_ERROR(Lib_NpWebApi2, "Request with id {:#x} is already being deleted", request_id); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + request->MarkForDeletion(); + request->Abort(); + while (request->IsBusy()) { + lib_ctx->Unlock(); + Libraries::Kernel::sceKernelUsleep(50000); + lib_ctx->Lock(); + } + user_ctx->RemoveRequest(request_id); + request->RemoveUser(); + + s32 http_request_id = 0; + request->Delete(&http_request_id); + + user_ctx->RemoveUser(); + lib_ctx->Unlock(); + lib_ctx->RemoveUser(); + if (http_request_id != 0) { + Libraries::Http2::sceHttp2DeleteRequest(http_request_id); + } + return ORBIS_OK; +} + +void checkTimeout() { + u64 current_time = Libraries::Kernel::sceKernelGetProcessTime(); + if (current_time < g_last_timeout_check + 1000) { + // Too soon, skip check. + return; + } + + std::scoped_lock lk{g_mutex}; + g_last_timeout_check = current_time; + for (auto& [lib_ctx_id, lib_ctx] : g_lib_contexts) { + lib_ctx->CheckTimeout(); + } +} + +}; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_internal.h b/src/core/libraries/np/np_web_api2/np_web_api2_internal.h new file mode 100644 index 000000000..53d75cd4b --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2_internal.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/np/np_web_api2/np_web_api2.h" +#include "core/libraries/system/userservice.h" + +namespace Libraries::Np::NpWebApi2 { + +s32 createLibraryContext(s32 http_ctx_id, s32 type, u64 pool_size, const char* name); +s32 getMemoryPoolStats(s32 lib_ctx_id, OrbisNpWebApi2MemoryPoolStats* stats); +s32 createPushEventHandle(s32 lib_ctx_id); +s32 setHandleTimeout(s32 lib_ctx_id, s32 handle_id, u32 timeout); +s32 abortPushEventHandle(s32 lib_ctx_id, s32 handle_id); +s32 deletePushEventHandle(s32 lib_ctx_id, s32 handle_id); +s32 createPushEventFilter(s32 lib_ctx_id, s32 handle_id, const char* np_service_name, + OrbisNpServiceLabel np_service_label, + const OrbisNpWebApi2PushEventFilterParameter* filter_param, + u64 filter_param_num, bool internal); +s32 deletePushEventFilter(s32 lib_ctx_id, s32 filter_id); +s32 terminateLibraryContext(s32 lib_ctx_id); +s32 createUserContext(s32 lib_ctx_id, Libraries::UserService::OrbisUserServiceUserId user_id); +s32 deleteUserContext(s32 user_ctx_id); +s32 registerPushEventCallback(s32 user_ctx_id, s32 filter_id, + OrbisNpWebApi2PushEventCallback cb_func, void* user_arg); +s32 unregisterPushEventCallback(s32 user_ctx_id, s32 callback_id); +s32 createPushContext(s32 user_ctx_id, OrbisNpWebApi2PushEventPushContextId* push_ctx_id); +s32 startPushContextCallback(s32 user_ctx_id, + const OrbisNpWebApi2PushEventPushContextId* push_ctx_id); +s32 deletePushContext(s32 user_ctx_id, const OrbisNpWebApi2PushEventPushContextId* push_ctx_id); +s32 registerPushContextCallback(s32 user_ctx_id, s32 filter_id, + OrbisNpWebApi2PushEventPushContextCallback cb_func, void* user_arg); +s32 unregisterPushContextCallback(s32 user_ctx_id, s32 callback_id); +void processPushEvents(); +s32 createRequest(s32 user_ctx_id, const char* api_group, const char* path, const char* method, + const OrbisNpWebApi2ContentParameter* content_parameter, bool multipart, + s64* request_id); +s32 addHttpRequestHeader(s64 request_id, const char* field_name, const char* field_value); +s32 setRequestTimeout(s64 request_id, u32 timeout); +s32 sendRequest(s64 request_id, s32 part_index, void* data, u64 data_size, + OrbisNpWebApi2ResponseInformationOption* resp_info_option); +s32 getHttpResponseHeaderData(s64 request_id, const char* field_name, char* value, u64 value_size, + u64* value_size_out); +s32 readData(s64 request_id, void* data, u64 size); +s32 abortRequest(s64 request_id); +s32 deleteRequest(s64 request_id); +void checkTimeout(); + +}; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_push_event.cpp b/src/core/libraries/np/np_web_api2/np_web_api2_push_event.cpp new file mode 100644 index 000000000..ece4023c0 --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2_push_event.cpp @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/np/np_error.h" +#include "core/libraries/np/np_web_api2/np_web_api2_push_event.h" + +namespace Libraries::Np::NpWebApi2 { + +s64 g_current_push_event_push_context_id{}; + +s32 PushEventFilter::Initialize(PushEventHandle* handle, + const OrbisNpWebApi2PushEventFilterParameter* filter_param, + u64 filter_param_num) { + if (handle->IsDeleted()) { + LOG_ERROR(Lib_NpWebApi2, "handle {:#x} is invalid", handle->GetId()); + return ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND; + } + if (handle->IsTimedout()) { + LOG_ERROR(Lib_NpWebApi2, "handle {:#x} has timed out", handle->GetId()); + return ORBIS_NP_WEBAPI2_ERROR_TIMEOUT; + } + if (handle->IsAborted()) { + LOG_ERROR(Lib_NpWebApi2, "handle {:#x} was aborted", handle->GetId()); + return ORBIS_NP_WEBAPI2_ERROR_ABORTED; + } + + std::vector> copy_storage{}; + for (u64 i = 0; i < filter_param_num; i++) { + OrbisNpWebApi2PushEventFilterParameter new_param{}; + memcpy(&new_param, &filter_param[i], sizeof(OrbisNpWebApi2PushEventFilterParameter)); + if (filter_param[i].extd_data_key != nullptr && filter_param[i].extd_data_key_num != 0) { + std::vector data_keys{}; + for (u64 j = 0; j < filter_param[i].extd_data_key_num; j++) { + OrbisNpWebApi2PushEventExtdDataKey new_key{}; + memcpy(&new_key, &filter_param[i].extd_data_key[j], + sizeof(OrbisNpWebApi2PushEventExtdDataKey)); + data_keys.emplace_back(new_key); + } + new_param.extd_data_key = data_keys.data(); + copy_storage.emplace_back(data_keys); + } + this->filter_params.emplace_back(new_param); + } + + return ORBIS_OK; +} + +void PushEventPushContext::Initialize() { + // In the real library, there's a whole process of registering this with NpManager's Push2 API. + // For now, we'll just create the uuid for this push context. + + // As for the ID itself, for now we'll cheat a bit. + // Real library does a mostly random string, but it's easier to just use an actual index. + ++g_current_push_event_push_context_id; + if (g_current_push_event_push_context_id < 0) { + g_current_push_event_push_context_id = 1; + } + s64 new_id = g_current_push_event_push_context_id; + std::memset(&this->id, 0, sizeof(this->id)); + std::memcpy(&this->id, &new_id, sizeof(s64)); +} + +}; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_push_event.h b/src/core/libraries/np/np_web_api2/np_web_api2_push_event.h new file mode 100644 index 000000000..b4270cda7 --- /dev/null +++ b/src/core/libraries/np/np_web_api2/np_web_api2_push_event.h @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/kernel/time.h" +#include "core/libraries/np/np_types.h" +#include "core/libraries/np/np_web_api2/np_web_api2.h" + +namespace Libraries::Np::NpWebApi2 { + +class UserContext; + +class PushEventHandle { +public: + PushEventHandle(s32 new_id) : id(new_id) {} + + s32 GetId() { + return id; + } + + void SetTimeout(u32 new_timeout) { + timeout = new_timeout; + } + + void AddUser() { + user_count++; + } + + void RemoveUser() { + user_count--; + } + + void Abort() { + aborted = true; + } + + bool IsAborted() { + return aborted; + } + + void MarkForDeletion() { + deleting = true; + } + + bool IsDeleted() { + return deleting; + } + + void SetEndTime() { + if (timeout != 0) { + end_time = Libraries::Kernel::sceKernelGetProcessTime() + timeout; + } + } + + void ClearEndTime() { + end_time = 0; + } + + u64 GetEndTime() { + return end_time; + } + + bool IsTimedout() { + return timed_out; + } + + void CheckTimeout(u64 time) { + if (!timed_out && end_time != 0 && end_time < time) { + timed_out = true; + Abort(); + } + } + + bool IsBusy() { + return user_count > 1; + } + +private: + s32 id{}; + u32 timeout{}; + s32 user_count{}; + bool aborted{}; + bool deleting{}; + bool timed_out{}; + u64 end_time{}; +}; + +class PushEventFilter { +public: + PushEventFilter(s32 new_id, const char* service_name, OrbisNpServiceLabel service_label, + bool is_internal) + : id(new_id), np_service_name(service_name ? service_name : ""), + np_service_label(service_label), internal(is_internal) {} + + s32 Initialize(PushEventHandle* handle, + const OrbisNpWebApi2PushEventFilterParameter* filter_param, + u64 filter_param_num); + + s32 GetId() { + return id; + } + +private: + s32 id{}; + OrbisNpServiceLabel np_service_label{}; + bool internal{}; + std::vector filter_params{}; + std::vector> data_key_copy_storage{}; + std::string np_service_name{}; +}; + +struct PushEventCallback { + s32 id; + s32 filter_id; + OrbisNpWebApi2PushEventCallback cb_func; + void* user_arg; + bool is_busy; +}; + +class PushEventPushContext { +public: + PushEventPushContext(u32 new_timeout, UserContext* user_ctx) + : timeout(new_timeout), parent_user_ctx(user_ctx) {} + + void Initialize(); + + OrbisNpWebApi2PushEventPushContextId* GetId() { + return &id; + } + + // Returns the actual numerical id currently stored in id. + s64 GetFakeId() { + s64 fake_id{}; + std::memcpy(&fake_id, &id, sizeof(s64)); + return fake_id; + } + + void Start() { + started = true; + } + + void SetState(s32 new_state) { + state = new_state; + } + + bool CallbackRunning() { + return state == 2; + } + +private: + s32 state{}; + u32 timeout{}; + UserContext* parent_user_ctx{}; + u64 end_time{}; + u64 start_time{}; + OrbisNpWebApi2PushEventPushContextId id{}; + bool started{}; +}; + +struct PushEventPushContextCallback { + s32 id; + s32 filter_id; + OrbisNpWebApi2PushEventPushContextCallback cb_func; + void* user_arg; + bool is_busy; +}; + +}; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file