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 598f67e8bb.

* 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
This commit is contained in:
Stephen Miller 2026-07-07 05:40:23 -05:00 committed by GitHub
parent 474fbe741d
commit 64d198190f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 3173 additions and 553 deletions

View File

@ -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/np_tus.h
src/core/libraries/np/trophy_ui.cpp src/core/libraries/np/trophy_ui.cpp
src/core/libraries/np/trophy_ui.h 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.cpp
src/core/libraries/np/np_party.h src/core/libraries/np/np_party.h
src/core/libraries/np/np_auth.cpp 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.h
src/core/libraries/np/np_web_api/np_web_api_internal.cpp 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_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 set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp

View File

@ -49,7 +49,7 @@
#include "core/libraries/np/np_trophy.h" #include "core/libraries/np/np_trophy.h"
#include "core/libraries/np/np_tus.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_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/pad/pad.h"
#include "core/libraries/playgo/playgo.h" #include "core/libraries/playgo/playgo.h"
#include "core/libraries/playgo/playgo_dialog.h" #include "core/libraries/playgo/playgo_dialog.h"

View File

@ -4,293 +4,359 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/network/http.h"
#include "core/libraries/network/http2.h" #include "core/libraries/network/http2.h"
#include <map>
namespace Libraries::Http2 { namespace Libraries::Http2 {
int PS4_SYSV_ABI _Z5dummyv() { std::map<s32, s32> 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2AddCookie() { s32 PS4_SYSV_ABI sceHttp2CookieExport() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2AddRequestHeader() { s32 PS4_SYSV_ABI sceHttp2CookieFlush() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2AuthCacheFlush() { s32 PS4_SYSV_ABI sceHttp2CookieImport() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2CookieExport() { s32 PS4_SYSV_ABI sceHttp2CreateCookieBox() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2CreateCookieBox() { s32 PS4_SYSV_ABI sceHttp2GetAutoRedirect() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2CreateRequestWithURL() { s32 PS4_SYSV_ABI sceHttp2GetCookie() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2CreateTemplate() { s32 PS4_SYSV_ABI sceHttp2GetCookieBox() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2DeleteCookieBox() { s32 PS4_SYSV_ABI sceHttp2GetCookieStats() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2DeleteRequest() { s32 PS4_SYSV_ABI sceHttp2GetMemoryPoolStats() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2DeleteTemplate() { s32 PS4_SYSV_ABI sceHttp2GetResponseContentLength() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; 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"); 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() { s32 PS4_SYSV_ABI sceHttp2Init(s32 net_id, s32 ssl_id, u64 pool_size, s32 max_requests) {
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) {
LOG_ERROR(Lib_Http2, "(DUMMY) called"); LOG_ERROR(Lib_Http2, "(DUMMY) called");
static int id = 0; s32 ctx_id = Libraries::Http::sceHttpInit(net_id, ssl_id, pool_size);
return ++id; 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2ReadDataAsync() { s32 PS4_SYSV_ABI sceHttp2RedirectCacheFlush() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2RedirectCacheFlush() { s32 PS4_SYSV_ABI sceHttp2RemoveRequestHeader() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SendRequest() { s32 PS4_SYSV_ABI sceHttp2SetAuthEnabled() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SendRequestAsync() { s32 PS4_SYSV_ABI sceHttp2SetAuthInfoCallback() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetAuthEnabled() { s32 PS4_SYSV_ABI sceHttp2SetAutoRedirect() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetAuthInfoCallback() { s32 PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetAutoRedirect() { s32 PS4_SYSV_ABI sceHttp2SetConnectTimeOut() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut() { s32 PS4_SYSV_ABI sceHttp2SetCookieBox() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetConnectTimeOut() { s32 PS4_SYSV_ABI sceHttp2SetCookieMaxNum() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetCookieBox() { s32 PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetCookieMaxNum() { s32 PS4_SYSV_ABI sceHttp2SetCookieMaxSize() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain() { s32 PS4_SYSV_ABI sceHttp2SetCookieRecvCallback() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetCookieMaxSize() { s32 PS4_SYSV_ABI sceHttp2SetCookieSendCallback() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetCookieRecvCallback() { s32 PS4_SYSV_ABI sceHttp2SetInflateGZIPEnabled() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetCookieSendCallback() { s32 PS4_SYSV_ABI sceHttp2SetMinSslVersion() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetMinSslVersion() { s32 PS4_SYSV_ABI sceHttp2SetRecvTimeOut() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetPreSendCallback() { s32 PS4_SYSV_ABI sceHttp2SetRedirectCallback() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; 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"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetRedirectCallback() { s32 PS4_SYSV_ABI sceHttp2SetResolveTimeOut() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetRequestContentLength() { s32 PS4_SYSV_ABI sceHttp2SetSendTimeOut() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetResolveRetry() { s32 PS4_SYSV_ABI sceHttp2SetSslCallback() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetResolveTimeOut() { s32 PS4_SYSV_ABI sceHttp2SetTimeOut() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetSendTimeOut() { s32 PS4_SYSV_ABI sceHttp2SslDisableOption() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetSslCallback() { s32 PS4_SYSV_ABI sceHttp2SslEnableOption() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SetTimeOut() { s32 PS4_SYSV_ABI sceHttp2Term() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttp2SslDisableOption() { s32 PS4_SYSV_ABI sceHttp2WaitAsync() {
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() {
LOG_ERROR(Lib_Http2, "(STUBBED) called"); LOG_ERROR(Lib_Http2, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
void RegisterLib(Core::Loader::SymbolsResolver* sym) { void RegisterLib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("AS45QoYHjc4", "libSceHttp2", 1, "libSceHttp2", _Z5dummyv);
LIB_FUNCTION("IZ-qjhRqvjk", "libSceHttp2", 1, "libSceHttp2", sceHttp2AbortRequest); LIB_FUNCTION("IZ-qjhRqvjk", "libSceHttp2", 1, "libSceHttp2", sceHttp2AbortRequest);
LIB_FUNCTION("flPxnowtvWY", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddCookie); LIB_FUNCTION("flPxnowtvWY", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddCookie);
LIB_FUNCTION("nrPfOE8TQu0", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddRequestHeader); LIB_FUNCTION("nrPfOE8TQu0", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddRequestHeader);

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "common/types.h" #include "common/types.h"
#include "core/libraries/network/ssl2.h"
namespace Core::Loader { namespace Core::Loader {
class SymbolsResolver; class SymbolsResolver;
@ -11,62 +12,38 @@ class SymbolsResolver;
namespace Libraries::Http2 { namespace Libraries::Http2 {
int PS4_SYSV_ABI _Z5dummyv(); enum OrbisHttp2HttpVersion : u32 {
int PS4_SYSV_ABI sceHttp2AbortRequest(); ORBIS_HTTP2_VERSION_1_0 = 1,
int PS4_SYSV_ABI sceHttp2AddCookie(); ORBIS_HTTP2_VERSION_1_1 = 2,
int PS4_SYSV_ABI sceHttp2AddRequestHeader(); ORBIS_HTTP2_VERSION_2_0 = 3,
int PS4_SYSV_ABI sceHttp2AuthCacheFlush(); };
int PS4_SYSV_ABI sceHttp2CookieExport();
int PS4_SYSV_ABI sceHttp2CookieFlush(); // TODO: Figure out the whole struct. What's here is based on libSceNpWebApi2 use.
int PS4_SYSV_ABI sceHttp2CookieImport(); struct OrbisHttp2PreSendCallbackData {
int PS4_SYSV_ABI sceHttp2CreateCookieBox(); void* unk;
int PS4_SYSV_ABI sceHttp2CreateRequestWithURL(); void* cert;
int PS4_SYSV_ABI sceHttp2CreateTemplate(); };
int PS4_SYSV_ABI sceHttp2DeleteCookieBox(); using OrbisHttp2PreSendCallback = PS4_SYSV_ABI s32 (*)(s32 request_id, s32 ssl_id,
int PS4_SYSV_ABI sceHttp2DeleteRequest(); OrbisHttp2PreSendCallbackData* data,
int PS4_SYSV_ABI sceHttp2DeleteTemplate(); void* user_arg);
int PS4_SYSV_ABI sceHttp2GetAllResponseHeaders();
int PS4_SYSV_ABI sceHttp2GetAuthEnabled(); s32 PS4_SYSV_ABI sceHttp2AbortRequest(s32 req_id);
int PS4_SYSV_ABI sceHttp2GetAutoRedirect(); s32 PS4_SYSV_ABI sceHttp2AddRequestHeader(s32 template_or_req_id, const char* name,
int PS4_SYSV_ABI sceHttp2GetCookie(); const char* value, u32 mode);
int PS4_SYSV_ABI sceHttp2GetCookieBox(); s32 PS4_SYSV_ABI sceHttp2CreateRequestWithURL(s32 tmpl_id, const char* method, const char* url,
int PS4_SYSV_ABI sceHttp2GetCookieStats(); u64 content_length);
int PS4_SYSV_ABI sceHttp2GetMemoryPoolStats(); s32 PS4_SYSV_ABI sceHttp2CreateTemplate(s32 ctx_id, const char* user_agent, s32 http_ver,
int PS4_SYSV_ABI sceHttp2GetResponseContentLength(); s32 auto_proxy_conf);
int PS4_SYSV_ABI sceHttp2GetStatusCode(); s32 PS4_SYSV_ABI sceHttp2DeleteRequest(s32 req_id);
int PS4_SYSV_ABI sceHttp2Init(int net_id, int ssl_id, size_t pool_size, int max_requests); s32 PS4_SYSV_ABI sceHttp2DeleteTemplate(s32 tmpl_id);
int PS4_SYSV_ABI sceHttp2ReadData(); s32 PS4_SYSV_ABI sceHttp2GetAllResponseHeaders(s32 req_id, char** header, u64* header_size);
int PS4_SYSV_ABI sceHttp2ReadDataAsync(); s32 PS4_SYSV_ABI sceHttp2GetStatusCode(s32 request_id, s32* status_code);
int PS4_SYSV_ABI sceHttp2RedirectCacheFlush(); s32 PS4_SYSV_ABI sceHttp2Init(s32 net_id, s32 ssl_id, u64 pool_size, s32 max_requests);
int PS4_SYSV_ABI sceHttp2RemoveRequestHeader(); s32 PS4_SYSV_ABI sceHttp2ReadData(s32 req_id, void* data, u64 size);
int PS4_SYSV_ABI sceHttp2SendRequest(); s32 PS4_SYSV_ABI sceHttp2SendRequest(s32 req_id, const void* data, u64 size);
int PS4_SYSV_ABI sceHttp2SendRequestAsync(); s32 PS4_SYSV_ABI sceHttp2SetPreSendCallback(s32 template_id, OrbisHttp2PreSendCallback cb_func,
int PS4_SYSV_ABI sceHttp2SetAuthEnabled(); void* user_arg);
int PS4_SYSV_ABI sceHttp2SetAuthInfoCallback(); s32 PS4_SYSV_ABI sceHttp2SetRequestContentLength(s32 req_id, u64 content_length);
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();
void RegisterLib(Core::Loader::SymbolsResolver* sym); void RegisterLib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Http2 } // namespace Libraries::Http2

View File

@ -390,6 +390,17 @@ s32 PS4_SYSV_ABI sceNpIntIsValidOnlineId(const OrbisNpOnlineId* id) {
return 1; 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, s32 PS4_SYSV_ABI sceNpCalloutInitCtx(OrbisNpCalloutContext* callout_ctx, const char* name,
u64 stack_size, s32 priority, u64 affinity_mask) { u64 stack_size, s32 priority, u64 affinity_mask) {
LOG_DEBUG(Lib_NpCommon, "ctx={:p} name={:p} stack={:#x} priority={} affinity={:#x}", 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("ss2xO9IJxKQ", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondTimedwait);
LIB_FUNCTION("uEwag-0YZPc", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexInit); LIB_FUNCTION("uEwag-0YZPc", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexInit);
LIB_FUNCTION("uMJFOA62mVU", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondSignal); LIB_FUNCTION("uMJFOA62mVU", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondSignal);
LIB_FUNCTION("Pglk7zFj0DI", "libSceNpCommon", 1, "libSceNpCommon", sceNpGetSdkVersion);
}; };
} // namespace Libraries::Np::NpCommon } // namespace Libraries::Np::NpCommon

View File

@ -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 sceNpGetSystemClockUsec(s64* usec);
s32 PS4_SYSV_ABI sceNpGetPlatformType(const OrbisNpId* np_id); s32 PS4_SYSV_ABI sceNpGetPlatformType(const OrbisNpId* np_id);
s32 PS4_SYSV_ABI sceNpIntIsValidOnlineId(const OrbisNpOnlineId* 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 PS4_SYSV_ABI sceNpCalloutInitCtx(OrbisNpCalloutContext* ctx, const char* name, u64 stack_size,
s32 priority, u64 affinity_mask); s32 priority, u64 affinity_mask);
s32 PS4_SYSV_ABI sceNpCalloutStartOnCtx(OrbisNpCalloutContext* ctx, OrbisNpCalloutEntry* callout, s32 PS4_SYSV_ABI sceNpCalloutStartOnCtx(OrbisNpCalloutContext* ctx, OrbisNpCalloutEntry* callout,

View File

@ -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_NOT_SIGNED_IN = 0x80553407;
constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_CONTENT_PARAMETER = 0x80553408; constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_CONTENT_PARAMETER = 0x80553408;
constexpr int ORBIS_NP_WEBAPI2_ERROR_ABORTED = 0x80553409; constexpr int ORBIS_NP_WEBAPI2_ERROR_ABORTED = 0x80553409;
constexpr int ORBIS_NP_WEBAPI2_USER_CONTEXT_ALREADY_EXIST = 0x8055340a; constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_ALREADY_EXIST = 0x8055340a;
constexpr int ORBIS_NP_WEBAPI2_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055340b; constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055340b;
constexpr int ORBIS_NP_WEBAPI2_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055340c; constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055340c;
constexpr int ORBIS_NP_WEBAPI2_HANDLE_NOT_FOUND = 0x8055340d; constexpr int ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND = 0x8055340d;
constexpr int ORBIS_NP_WEBAPI2_SIGNED_IN_USER_NOT_FOUND = 0x8055340e; constexpr int ORBIS_NP_WEBAPI2_ERROR_SIGNED_IN_USER_NOT_FOUND = 0x8055340e;
constexpr int ORBIS_NP_WEBAPI2_LIB_CONTEXT_BUSY = 0x8055340f; constexpr int ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_BUSY = 0x8055340f;
constexpr int ORBIS_NP_WEBAPI2_USER_CONTEXT_BUSY = 0x80553410; constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_BUSY = 0x80553410;
constexpr int ORBIS_NP_WEBAPI2_REQUEST_BUSY = 0x80553411; constexpr int ORBIS_NP_WEBAPI2_ERROR_REQUEST_BUSY = 0x80553411;
constexpr int ORBIS_NP_WEBAPI2_INVALID_HTTP_STATUS_CODE = 0x80553412; constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_HTTP_STATUS_CODE = 0x80553412;
constexpr int ORBIS_NP_WEBAPI2_PROHIBITED_HTTP_HEADER = 0x80553413; constexpr int ORBIS_NP_WEBAPI2_ERROR_PROHIBITED_HTTP_HEADER = 0x80553413;
constexpr int ORBIS_NP_WEBAPI2_PROHIBITED_FUNCTION_CALL = 0x80553414; constexpr int ORBIS_NP_WEBAPI2_ERROR_PROHIBITED_FUNCTION_CALL = 0x80553414;
constexpr int ORBIS_NP_WEBAPI2_MULTIPART_PART_NOT_FOUND = 0x80553415; constexpr int ORBIS_NP_WEBAPI2_ERROR_MULTIPART_PART_NOT_FOUND = 0x80553415;
constexpr int ORBIS_NP_WEBAPI2_PARAMETER_TOO_LONG = 0x80553416; constexpr int ORBIS_NP_WEBAPI2_ERROR_PARAMETER_TOO_LONG = 0x80553416;
constexpr int ORBIS_NP_WEBAPI2_HANDLE_BUSY = 0x80553417; constexpr int ORBIS_NP_WEBAPI2_ERROR_HANDLE_BUSY = 0x80553417;
constexpr int ORBIS_NP_WEBAPI2_LIB_CONTEXT_MAX = 0x80553418; constexpr int ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_MAX = 0x80553418;
constexpr int ORBIS_NP_WEBAPI2_USER_CONTEXT_MAX = 0x80553419; constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_MAX = 0x80553419;
constexpr int ORBIS_NP_WEBAPI2_AFTER_SEND = 0x8055341a; constexpr int ORBIS_NP_WEBAPI2_ERROR_AFTER_SEND = 0x8055341a;
constexpr int ORBIS_NP_WEBAPI2_TIMEOUT = 0x8055341b; constexpr int ORBIS_NP_WEBAPI2_ERROR_TIMEOUT = 0x8055341b;
constexpr int ORBIS_NP_WEBAPI2_PUSH_CONTEXT_NOT_FOUND = 0x8055341c; constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_CONTEXT_NOT_FOUND = 0x8055341c;
// NP Session Management Client (0x80553600 - 0x8055361e) // NP Session Management Client (0x80553600 - 0x8055361e)
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_ALREADY_INITIALIZED = 0x80553600; constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_ALREADY_INITIALIZED = 0x80553600;

View File

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

View File

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

View File

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

View File

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

View File

@ -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<s64>(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<void*>(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<s64>(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<u64>(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<u64>(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

View File

@ -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 <atomic>
#include <map>
#include <mutex>
#include <string>
#include <vector>
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<LibraryContextType>(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<LibraryContextType>(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<s32, UserContext*> user_contexts{};
std::map<s32, PushEventHandle*> push_event_handles{};
std::map<s32, PushEventFilter*> 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<s64, Request*> requests{};
std::map<s32, PushEventCallback*> push_event_callbacks{};
std::map<s64, PushEventPushContext*> push_contexts{};
std::map<s32, PushEventPushContextCallback*> 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<HttpRequestHeader*> http_headers{};
};
}; // namespace Libraries::Np::NpWebApi2

View File

@ -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 <map>
#include <mutex>
namespace Libraries::Np::NpWebApi2 {
std::recursive_mutex g_mutex{};
s32 g_current_lib_context_id{};
std::map<s32, LibraryContext*> 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<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(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<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(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<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(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<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(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<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(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<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(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<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(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

View File

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

View File

@ -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<std::vector<OrbisNpWebApi2PushEventExtdDataKey>> 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<OrbisNpWebApi2PushEventExtdDataKey> 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

View File

@ -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<OrbisNpWebApi2PushEventFilterParameter> filter_params{};
std::vector<std::vector<OrbisNpWebApi2PushEventExtdDataKey>> 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