Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 35 additions & 9 deletions core/libs/prx/libSceNpWebApi2/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@

// The PSN web API has no service behind it: handles can be created, but requests fail.
static constexpr int SCE_NP_WEBAPI2_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80553402);
static constexpr int SCE_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND = static_cast<int>(0x80553404);
static constexpr int SCE_NP_WEBAPI2_ERROR_UNAVAILABLE = static_cast<int>(0x80553406);
static constexpr int SCE_NP_WEBAPI2_ERROR_ABORTED = static_cast<int>(0x80553409);
static constexpr int SCE_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND = static_cast<int>(0x8055340D);
static std::atomic<int> g_nextHandle{1};

struct NpWebApi2MemoryPoolStats {
Expand All @@ -19,8 +22,22 @@ struct NpWebApi2MemoryPoolStats {
int32_t reserved;
};

struct NpWebApi2PushEventHandle {
int libCtxId;
bool aborted;
};

static std::mutex g_poolsMutex;
static std::unordered_map<int, size_t> g_pools;
static std::unordered_map<int, NpWebApi2PushEventHandle> g_pushEventHandles;

static int FindPushEventHandle(int libCtxId, int handleId, NpWebApi2PushEventHandle*& handle) {
if (!g_pools.contains(libCtxId)) return SCE_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND;
const auto found = g_pushEventHandles.find(handleId);
if (found == g_pushEventHandles.end() || found->second.libCtxId != libCtxId) return SCE_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND;
handle = &found->second;
return 0;
}

extern "C" {

Expand Down Expand Up @@ -104,30 +121,38 @@ int APS5_VABI sceNpWebApi2GetMemoryPoolStats(int lib_ctx_id, NpWebApi2MemoryPool
}

int APS5_VABI sceNpWebApi2PushEventCreateFilter(int lib_ctx_id, int handle_id, const char* np_service_name, uint32_t np_service_label, const void* filter_param, size_t filter_param_num) {
(void)lib_ctx_id;
(void)handle_id;
(void)np_service_name;
(void)np_service_label;
(void)filter_param;
(void)filter_param_num;
std::lock_guard lock(g_poolsMutex);
NpWebApi2PushEventHandle* handle = nullptr;
if (const int result = FindPushEventHandle(lib_ctx_id, handle_id, handle); result != 0) return result;
if (handle->aborted) return SCE_NP_WEBAPI2_ERROR_ABORTED;
return g_nextHandle.fetch_add(1, std::memory_order_relaxed);
}

int APS5_VABI sceNpWebApi2PushEventCreateHandle(int lib_ctx_id) {
(void)lib_ctx_id;
return g_nextHandle.fetch_add(1, std::memory_order_relaxed);
std::lock_guard lock(g_poolsMutex);
if (!g_pools.contains(lib_ctx_id)) return SCE_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND;
const int handle_id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
g_pushEventHandles[handle_id] = {lib_ctx_id, false};
return handle_id;
}

int APS5_VABI sceNpWebApi2PushEventAbortHandle(int libCtxId, int handleId) {
(void)libCtxId;
(void)handleId;
NotImplemented_nid_no_patch(__func__);
std::lock_guard lock(g_poolsMutex);
NpWebApi2PushEventHandle* handle = nullptr;
if (const int result = FindPushEventHandle(libCtxId, handleId, handle); result != 0) return result;
handle->aborted = true;
return 0;
}

int APS5_VABI sceNpWebApi2PushEventDeleteHandle(int lib_ctx_id, int handle_id) {
(void)lib_ctx_id;
(void)handle_id;
std::lock_guard lock(g_poolsMutex);
NpWebApi2PushEventHandle* handle = nullptr;
if (const int result = FindPushEventHandle(lib_ctx_id, handle_id, handle); result != 0) return result;
g_pushEventHandles.erase(handle_id);
return 0;
}

Expand Down Expand Up @@ -163,6 +188,7 @@ int APS5_VABI sceNpWebApi2SendRequest(int64_t request_id, const void* data, size
int APS5_VABI sceNpWebApi2Terminate(int lib_ctx_id) {
std::lock_guard lock(g_poolsMutex);
g_pools.erase(lib_ctx_id);
std::erase_if(g_pushEventHandles, [&](const auto& handle) { return handle.second.libCtxId == lib_ctx_id; });
return 0;
}

Expand Down
33 changes: 33 additions & 0 deletions core/libs/tests/GuestNpWebApi2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ extern "C" {
int APS5_VABI sceNpWebApi2Initialize(int, std::size_t);
int APS5_VABI sceNpWebApi2Terminate(int);
int APS5_VABI sceNpWebApi2GetMemoryPoolStats(int, NpWebApi2MemoryPoolStats*);
int APS5_VABI sceNpWebApi2PushEventCreateHandle(int);
int APS5_VABI sceNpWebApi2PushEventAbortHandle(int, int);
int APS5_VABI sceNpWebApi2PushEventDeleteHandle(int, int);
int APS5_VABI sceNpWebApi2PushEventCreateFilter(int, int, const char*, std::uint32_t, const void*, std::size_t);
}

static constexpr int LibContextNotFound = static_cast<int>(0x80553404);
static constexpr int Aborted = static_cast<int>(0x80553409);
static constexpr int HandleNotFound = static_cast<int>(0x8055340D);

static void Require(bool value) { if (!value) std::abort(); }

template <typename TCall>
Expand All @@ -29,7 +37,32 @@ static bool Throws(TCall call) {
return false;
}

static void TestPushEventAbortHandle() {
const int context = sceNpWebApi2Initialize(1, 0x8000);
const int other = sceNpWebApi2Initialize(1, 0x8000);
const std::uint64_t filterParam[2]{};
const int handle = sceNpWebApi2PushEventCreateHandle(context);
const int kept = sceNpWebApi2PushEventCreateHandle(context);
Require(handle > 0 && kept > 0 && handle != kept);
Require(sceNpWebApi2PushEventCreateFilter(context, handle, nullptr, 0, filterParam, 1) > 0);
Require(sceNpWebApi2PushEventAbortHandle(context, handle) == 0);
Require(sceNpWebApi2PushEventCreateFilter(context, handle, nullptr, 0, filterParam, 1) == Aborted);
Require(sceNpWebApi2PushEventAbortHandle(context, handle) == 0);
Require(sceNpWebApi2PushEventCreateFilter(context, kept, nullptr, 0, filterParam, 1) > 0);
Require(sceNpWebApi2PushEventAbortHandle(other, handle) == HandleNotFound);
Require(sceNpWebApi2PushEventAbortHandle(context, 0x7FFFFFFF) == HandleNotFound);
Require(sceNpWebApi2PushEventAbortHandle(0x7FFFFFFF, handle) == LibContextNotFound);
Require(sceNpWebApi2PushEventCreateHandle(0x7FFFFFFF) == LibContextNotFound);
Require(sceNpWebApi2PushEventDeleteHandle(context, handle) == 0);
Require(sceNpWebApi2PushEventAbortHandle(context, handle) == HandleNotFound);
Require(sceNpWebApi2PushEventDeleteHandle(context, handle) == HandleNotFound);
Require(sceNpWebApi2Terminate(context) == 0);
Require(sceNpWebApi2PushEventAbortHandle(context, kept) == LibContextNotFound);
Require(sceNpWebApi2Terminate(other) == 0);
}

int main() {
TestPushEventAbortHandle();
const int context = sceNpWebApi2Initialize(1, 0x20000);
const int other = sceNpWebApi2Initialize(1, 0x8000);
Require(context > 0 && other > 0 && context != other);
Expand Down
5 changes: 4 additions & 1 deletion docs/dev/debt/libSceNpWebApi2.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@

### Unknown function info

- [sceNpWebApi2PushEventAbortHandle](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2, NID `1OLgvahaSco`) is a throwing stub. The two signed 32-bit handle arguments and return type follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/443f022450f6857256e780daa3d5a14029a748f7/src/core/libraries/np/np_web_api2/np_web_api2.cpp) (PS4); the PS5 signature and abort behavior are unverified. PPSA01736 imports it.
- [sceNpWebApi2GetMemoryPoolStats](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - imported by PPSA21703's Unity PSN plugin. The signature `(int libCtxId, stats*)` and the layout of the Http2 statistics above (measured on a PS5 for Http2, Ssl and Net) are assumed for this library, not measured. The pool size is the one given to `sceNpWebApi2Initialize`, and the peak and current use are 0. The error codes are unknown, so a null output or an unknown context throws

### Functional

- [sceNpWebApi2PushEventAbortHandle](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2, NID `1OLgvahaSco`) marks a push event handle as aborted, after which `sceNpWebApi2PushEventCreateFilter` on it returns `ABORTED` (`0x80553409`); callbacks already registered are left as they are. `sceNpWebApi2PushEventCreateHandle`, `sceNpWebApi2PushEventDeleteHandle` and `sceNpWebApi2PushEventCreateFilter` keep the handles per library context and `sceNpWebApi2Terminate` drops them. The signature, the order of the checks and the codes for an unknown context (`LIB_CONTEXT_NOT_FOUND`, `0x80553404`) or handle (`HANDLE_NOT_FOUND`, `0x8055340D`) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp#L112-L132) (PS4) and are unverified on the PS5; handle timeouts and the argument checks of `CreateFilter` are not modelled. PPSA01736 imports it
Loading