mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-01 12:45:56 +00:00
Merge pull request #9797 from Terminev/feat/npwebapi-offline-requests
feat(libSceNpWebApi): add user contexts, requests and push event callbacks offline
This commit is contained in:
4 files changed
+371
-41
No files matched your search
@@ -1,51 +1,133 @@
|
||||
#include "prx/libSceNpWebApi/include/Api.hpp"
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libSceUserService/UserService.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80552902);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_INVALID_LIB_CONTEXT_ID = static_cast<int>(0x80552903);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND = static_cast<int>(0x80552904);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND = static_cast<int>(0x80552905);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND = static_cast<int>(0x80552906);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN = static_cast<int>(0x80552907);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_INVALID_CONTENT_PARAMETER = static_cast<int>(0x80552908);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_ABORTED = static_cast<int>(0x80552909);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_USER_CONTEXT_ALREADY_EXIST = static_cast<int>(0x8055290A);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND = static_cast<int>(0x8055290B);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND = static_cast<int>(0x8055290C);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND = static_cast<int>(0x8055290D);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND = static_cast<int>(0x8055290E);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_CALLBACK_NOT_FOUND = static_cast<int>(0x8055290F);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND = static_cast<int>(0x80552910);
|
||||
static constexpr int SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_MAX = static_cast<int>(0x8055291A);
|
||||
|
||||
static constexpr int SCE_NP_WEBAPI_HTTP_METHOD_DELETE = 3;
|
||||
static constexpr std::uint32_t SCE_NP_INVALID_SERVICE_LABEL = 0xFFFFFFFF;
|
||||
static constexpr std::uint32_t SDK_VERSION_1_00 = 0x01000000;
|
||||
static constexpr std::uint32_t SDK_VERSION_2_00 = 0x02000000;
|
||||
static constexpr std::uint32_t SDK_VERSION_2_50 = 0x02500000;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Request {
|
||||
bool aborted;
|
||||
};
|
||||
|
||||
struct UserContext {
|
||||
int userId;
|
||||
std::unordered_map<std::int64_t, Request> requests;
|
||||
std::unordered_set<int> pushEventCallbacks;
|
||||
std::unordered_set<int> servicePushEventCallbacks;
|
||||
};
|
||||
|
||||
struct LibraryContext {
|
||||
int libHttpCtxId;
|
||||
std::size_t poolSize;
|
||||
std::unordered_set<int> handles;
|
||||
std::unordered_set<int> pushEventFilters;
|
||||
std::unordered_map<int, UserContext> userContexts;
|
||||
};
|
||||
|
||||
struct UserContextLookup {
|
||||
int error;
|
||||
LibraryContext* library;
|
||||
UserContext* user;
|
||||
};
|
||||
|
||||
struct RequestLookup {
|
||||
int error;
|
||||
UserContext* user;
|
||||
Request* request;
|
||||
};
|
||||
|
||||
std::mutex contextsMutex;
|
||||
std::unordered_map<int, LibraryContext> contexts;
|
||||
int lastContext = 0;
|
||||
int lastObject = 0;
|
||||
int lastUserContext = 0;
|
||||
std::int64_t lastRequest = 0;
|
||||
|
||||
int nextObject() {
|
||||
lastObject = lastObject % 0xefffffff + 1;
|
||||
return lastObject;
|
||||
}
|
||||
|
||||
std::uint32_t compiledSdkVersion() {
|
||||
const auto* process = static_cast<const std::byte*>(ApplicationProcessParameters_nid_no_patch());
|
||||
std::uint32_t version;
|
||||
std::memcpy(&version, process + 16, sizeof(version));
|
||||
return version;
|
||||
}
|
||||
|
||||
UserContextLookup findUserContext(int titleUserCtxId) {
|
||||
const auto library = contexts.find(titleUserCtxId >> 16);
|
||||
if (library == contexts.end()) return {SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND, nullptr, nullptr};
|
||||
const auto user = library->second.userContexts.find(titleUserCtxId);
|
||||
if (user == library->second.userContexts.end()) return {SCE_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND, &library->second, nullptr};
|
||||
return {0, &library->second, &user->second};
|
||||
}
|
||||
|
||||
RequestLookup findRequest(std::int64_t requestId) {
|
||||
const auto lookup = findUserContext(static_cast<int>(requestId >> 32));
|
||||
if (lookup.error != 0) return {lookup.error, nullptr, nullptr};
|
||||
const auto request = lookup.user->requests.find(requestId);
|
||||
if (request == lookup.user->requests.end()) return {SCE_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND, lookup.user, nullptr};
|
||||
return {0, lookup.user, &request->second};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpWebApiAbortRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
int APS5_VABI sceNpWebApiAbortRequest(std::int64_t requestId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findRequest(requestId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
lookup.request->aborted = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceNpWebApiCreateContext(int libCtxId, const void* onlineId) {
|
||||
if (libCtxId >= 0x8000) return SCE_NP_WEBAPI_ERROR_INVALID_LIB_CONTEXT_ID;
|
||||
if (!onlineId) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto context = contexts.find(libCtxId);
|
||||
if (context == contexts.end()) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
auto& userContexts = context->second.userContexts;
|
||||
for (const auto& [id, user] : userContexts) {
|
||||
if (user.userId == USER_SERVICE_INITIAL_USER_ID) return SCE_NP_WEBAPI_ERROR_USER_CONTEXT_ALREADY_EXIST;
|
||||
}
|
||||
lastUserContext = lastUserContext % 0xffff + 1;
|
||||
const int titleUserCtxId = (libCtxId << 16) | lastUserContext;
|
||||
userContexts.emplace(titleUserCtxId, UserContext{USER_SERVICE_INITIAL_USER_ID, {}, {}, {}});
|
||||
return titleUserCtxId;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateHandle(int libCtxId) {
|
||||
@@ -67,14 +149,26 @@ int APS5_VABI sceNpWebApiCreatePushEventFilter(int libCtxId, const void* filterP
|
||||
return filter;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
int APS5_VABI sceNpWebApiCreateRequest(int titleUserCtxId, const char* apiGroup, const char* path, int method, const NpWebApiContentParameter* contentParameter, std::int64_t* requestId) {
|
||||
if (!apiGroup || !path) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
if (contentParameter && contentParameter->contentLength != 0 && !contentParameter->contentType) return SCE_NP_WEBAPI_ERROR_INVALID_CONTENT_PARAMETER;
|
||||
if (method > SCE_NP_WEBAPI_HTTP_METHOD_DELETE && compiledSdkVersion() >= SDK_VERSION_2_50) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findUserContext(titleUserCtxId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
lastRequest = lastRequest % 0xffffffff + 1;
|
||||
const std::int64_t base = static_cast<std::int64_t>(titleUserCtxId) << 32;
|
||||
std::int64_t id = base | lastRequest;
|
||||
while (lookup.user->requests.contains(id)) --id;
|
||||
if (id <= base) throw std::runtime_error("sceNpWebApiCreateRequest: every request id of the user context is in use");
|
||||
lookup.user->requests.emplace(id, Request{false});
|
||||
if (requestId) *requestId = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiCreateServicePushEventFilter(int libCtxId, int handleId, const char* npServiceName, std::uint32_t npServiceLabel, const void* filterParam, std::size_t filterParamNum) {
|
||||
(void)npServiceLabel;
|
||||
if (!npServiceName || !filterParam || filterParamNum == 0) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
if (npServiceLabel == SCE_NP_INVALID_SERVICE_LABEL && compiledSdkVersion() >= SDK_VERSION_2_00) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto context = contexts.find(libCtxId);
|
||||
if (context == contexts.end()) return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
@@ -82,8 +176,11 @@ int APS5_VABI sceNpWebApiCreateServicePushEventFilter(int libCtxId, int handleId
|
||||
return SCE_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiDeleteContext() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
int APS5_VABI sceNpWebApiDeleteContext(int titleUserCtxId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findUserContext(titleUserCtxId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
lookup.library->userContexts.erase(titleUserCtxId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -101,8 +198,11 @@ int APS5_VABI sceNpWebApiDeletePushEventFilter(int libCtxId, int filterId) {
|
||||
return context->second.pushEventFilters.erase(filterId) != 0 ? 0 : SCE_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiDeleteRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
int APS5_VABI sceNpWebApiDeleteRequest(std::int64_t requestId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findRequest(requestId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
lookup.user->requests.erase(requestId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -133,7 +233,7 @@ int APS5_VABI sceNpWebApiInitialize(int libHttpCtxId, std::size_t poolSize) {
|
||||
for (int attempt = 0; attempt < 0x7fff; ++attempt) {
|
||||
lastContext = lastContext % 0x7fff + 1;
|
||||
if (contexts.contains(lastContext)) continue;
|
||||
contexts[lastContext] = {libHttpCtxId, poolSize, {}, {}};
|
||||
contexts[lastContext] = {libHttpCtxId, poolSize, {}, {}, {}};
|
||||
return lastContext;
|
||||
}
|
||||
return SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_MAX;
|
||||
@@ -144,19 +244,40 @@ int APS5_VABI sceNpWebApiReadData() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiRegisterPushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceNpWebApiRegisterPushEventCallback(int titleUserCtxId, int filterId, void* callback, void* userArg) {
|
||||
(void)userArg;
|
||||
if (!callback && compiledSdkVersion() >= SDK_VERSION_1_00) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findUserContext(titleUserCtxId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
if (!lookup.library->pushEventFilters.contains(filterId) && compiledSdkVersion() >= SDK_VERSION_2_50) return SCE_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND;
|
||||
const auto callbackId = nextObject();
|
||||
lookup.user->pushEventCallbacks.insert(callbackId);
|
||||
return callbackId;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiRegisterServicePushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceNpWebApiRegisterServicePushEventCallback(int titleUserCtxId, int filterId, void* callback, void* userArg) {
|
||||
(void)filterId;
|
||||
(void)userArg;
|
||||
if (!callback && compiledSdkVersion() >= SDK_VERSION_1_00) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findUserContext(titleUserCtxId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
if (compiledSdkVersion() >= SDK_VERSION_2_50) return SCE_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND;
|
||||
if (!callback) return SCE_NP_WEBAPI_ERROR_INVALID_ARGUMENT;
|
||||
const auto callbackId = nextObject();
|
||||
lookup.user->servicePushEventCallbacks.insert(callbackId);
|
||||
return callbackId;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiSendRequest() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceNpWebApiSendRequest(std::int64_t requestId, const void* data, std::size_t dataSize) {
|
||||
(void)data;
|
||||
(void)dataSize;
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findRequest(requestId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
if (lookup.request->aborted) return SCE_NP_WEBAPI_ERROR_ABORTED;
|
||||
return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiTerminate(int libCtxId) {
|
||||
@@ -164,14 +285,18 @@ int APS5_VABI sceNpWebApiTerminate(int libCtxId) {
|
||||
return contexts.erase(libCtxId) != 0 ? 0 : SCE_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiUnregisterPushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceNpWebApiUnregisterPushEventCallback(int titleUserCtxId, int callbackId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findUserContext(titleUserCtxId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
return lookup.user->pushEventCallbacks.erase(callbackId) != 0 ? 0 : SCE_NP_WEBAPI_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiUnregisterServicePushEventCallback() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceNpWebApiUnregisterServicePushEventCallback(int titleUserCtxId, int callbackId) {
|
||||
std::lock_guard lock(contextsMutex);
|
||||
const auto lookup = findUserContext(titleUserCtxId);
|
||||
if (lookup.error != 0) return lookup.error;
|
||||
return lookup.user->servicePushEventCallbacks.erase(callbackId) != 0 ? 0 : SCE_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_CALLBACK_NOT_FOUND;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNpWebApiUtilityParseNpId() {
|
||||
|
||||
@@ -2,30 +2,38 @@
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
struct NpWebApiContentParameter {
|
||||
std::uint64_t contentLength;
|
||||
const char* contentType;
|
||||
std::uint8_t reserved[16];
|
||||
};
|
||||
|
||||
static_assert(sizeof(NpWebApiContentParameter) == 32);
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNpWebApiAbortRequest();
|
||||
int APS5_VABI sceNpWebApiCreateContext();
|
||||
int APS5_VABI sceNpWebApiAbortRequest(std::int64_t requestId);
|
||||
int APS5_VABI sceNpWebApiCreateContext(int libCtxId, const void* onlineId);
|
||||
int APS5_VABI sceNpWebApiCreateHandle(int libCtxId);
|
||||
int APS5_VABI sceNpWebApiCreatePushEventFilter(int libCtxId, const void* filterParam, std::size_t filterParamNum);
|
||||
int APS5_VABI sceNpWebApiCreateRequest();
|
||||
int APS5_VABI sceNpWebApiCreateRequest(int titleUserCtxId, const char* apiGroup, const char* path, int method, const NpWebApiContentParameter* contentParameter, std::int64_t* requestId);
|
||||
int APS5_VABI sceNpWebApiCreateServicePushEventFilter(int libCtxId, int handleId, const char* npServiceName, std::uint32_t npServiceLabel, const void* filterParam, std::size_t filterParamNum);
|
||||
int APS5_VABI sceNpWebApiDeleteContext();
|
||||
int APS5_VABI sceNpWebApiDeleteContext(int titleUserCtxId);
|
||||
int APS5_VABI sceNpWebApiDeleteHandle(int libCtxId, int handleId);
|
||||
int APS5_VABI sceNpWebApiDeletePushEventFilter(int libCtxId, int filterId);
|
||||
int APS5_VABI sceNpWebApiDeleteRequest();
|
||||
int APS5_VABI sceNpWebApiDeleteRequest(std::int64_t requestId);
|
||||
int APS5_VABI sceNpWebApiDeleteServicePushEventFilter(int libCtxId, int filterId);
|
||||
int APS5_VABI sceNpWebApiGetHttpResponseHeaderValue();
|
||||
int APS5_VABI sceNpWebApiGetHttpResponseHeaderValueLength();
|
||||
int APS5_VABI sceNpWebApiGetHttpStatusCode();
|
||||
int APS5_VABI sceNpWebApiInitialize(int libHttpCtxId, std::size_t poolSize);
|
||||
int APS5_VABI sceNpWebApiReadData();
|
||||
int APS5_VABI sceNpWebApiRegisterPushEventCallback();
|
||||
int APS5_VABI sceNpWebApiRegisterServicePushEventCallback();
|
||||
int APS5_VABI sceNpWebApiSendRequest();
|
||||
int APS5_VABI sceNpWebApiRegisterPushEventCallback(int titleUserCtxId, int filterId, void* callback, void* userArg);
|
||||
int APS5_VABI sceNpWebApiRegisterServicePushEventCallback(int titleUserCtxId, int filterId, void* callback, void* userArg);
|
||||
int APS5_VABI sceNpWebApiSendRequest(std::int64_t requestId, const void* data, std::size_t dataSize);
|
||||
int APS5_VABI sceNpWebApiTerminate(int libCtxId);
|
||||
int APS5_VABI sceNpWebApiUnregisterPushEventCallback();
|
||||
int APS5_VABI sceNpWebApiUnregisterServicePushEventCallback();
|
||||
int APS5_VABI sceNpWebApiUnregisterPushEventCallback(int titleUserCtxId, int callbackId);
|
||||
int APS5_VABI sceNpWebApiUnregisterServicePushEventCallback(int titleUserCtxId, int callbackId);
|
||||
int APS5_VABI sceNpWebApiUtilityParseNpId();
|
||||
|
||||
}
|
||||
@@ -1,18 +1,40 @@
|
||||
#include "ProcessParametersFixture.hpp"
|
||||
#include "prx/libSceNpWebApi/include/Api.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int InvalidArgument = static_cast<int>(0x80552902u);
|
||||
constexpr int InvalidLibContextId = static_cast<int>(0x80552903u);
|
||||
constexpr int LibContextNotFound = static_cast<int>(0x80552904u);
|
||||
constexpr int UserContextNotFound = static_cast<int>(0x80552905u);
|
||||
constexpr int RequestNotFound = static_cast<int>(0x80552906u);
|
||||
constexpr int NotSignedIn = static_cast<int>(0x80552907u);
|
||||
constexpr int InvalidContentParameter = static_cast<int>(0x80552908u);
|
||||
constexpr int Aborted = static_cast<int>(0x80552909u);
|
||||
constexpr int UserContextAlreadyExist = static_cast<int>(0x8055290Au);
|
||||
constexpr int PushEventFilterNotFound = static_cast<int>(0x8055290Bu);
|
||||
constexpr int PushEventCallbackNotFound = static_cast<int>(0x8055290Cu);
|
||||
constexpr int HandleNotFound = static_cast<int>(0x8055290Du);
|
||||
constexpr int ServicePushEventFilterNotFound = static_cast<int>(0x8055290Eu);
|
||||
constexpr int ServicePushEventCallbackNotFound = static_cast<int>(0x8055290Fu);
|
||||
constexpr int SignedInUserNotFound = static_cast<int>(0x80552910u);
|
||||
|
||||
constexpr int MethodGet = 0;
|
||||
constexpr int MethodPost = 1;
|
||||
constexpr int MethodPatch = 4;
|
||||
|
||||
alignas(16) std::array<std::byte, 0x40> process{};
|
||||
const char onlineId[20] = "player";
|
||||
const char filterParam[16] = {};
|
||||
const char body[] = "{}";
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (!condition) {
|
||||
std::fprintf(stderr, "NpWebApi: %s\n", message);
|
||||
@@ -20,9 +42,26 @@ void Require(bool condition, const char* message) {
|
||||
}
|
||||
}
|
||||
|
||||
void APS5_VABI PushEvent() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
void* PushEventCallback() {
|
||||
return reinterpret_cast<void*>(&PushEvent);
|
||||
}
|
||||
|
||||
void SetSdkVersion(std::uint32_t version) {
|
||||
std::memcpy(process.data() + 16, &version, sizeof(version));
|
||||
}
|
||||
|
||||
void InstallProcessParameters() {
|
||||
const std::uint64_t size = process.size();
|
||||
const std::uint32_t magic = 0x4942524f;
|
||||
std::memcpy(process.data(), &size, sizeof(size));
|
||||
std::memcpy(process.data() + 8, &magic, sizeof(magic));
|
||||
Require(ProcessParametersFixture::Install(process.data(), process.size()), "process parameters were not installed");
|
||||
}
|
||||
|
||||
void TestLibraryContexts() {
|
||||
const int context = sceNpWebApiInitialize(1, 0x10000);
|
||||
Require(context > 0 && context <= 0x7fff, "library context id out of range");
|
||||
const int other = sceNpWebApiInitialize(1, 0x10000);
|
||||
@@ -31,7 +70,6 @@ int main() {
|
||||
|
||||
const int handle = sceNpWebApiCreateHandle(context);
|
||||
Require(handle > 0 && static_cast<unsigned>(handle) < 0xf0000000u, "handle id out of range");
|
||||
const char filterParam[16] = {};
|
||||
Require(sceNpWebApiCreatePushEventFilter(context, nullptr, 1) == InvalidArgument, "push event filter without its parameter");
|
||||
Require(sceNpWebApiCreatePushEventFilter(context, filterParam, 0) == InvalidArgument, "push event filter with no parameters");
|
||||
const int filter = sceNpWebApiCreatePushEventFilter(context, filterParam, 1);
|
||||
@@ -40,6 +78,8 @@ int main() {
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(context, handle, nullptr, 0, filterParam, 1) == InvalidArgument, "service filter without a service name");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(0x7fff + 1, handle, "service", 0, filterParam, 1) == LibContextNotFound, "service filter of an unknown context");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(other, handle, "service", 0, filterParam, 1) == HandleNotFound, "service filter with another context's handle");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(context, handle, "service", 0xffffffffu, filterParam, 1) == InvalidArgument, "service filter with the invalid service label from SDK 2.00");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(0x7fff + 1, handle, "service", 0xffffffffu, filterParam, 1) == InvalidArgument, "the service label is checked before the library context");
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(context, handle, "service", 0, filterParam, 1) == SignedInUserNotFound, "service filter without a signed-in user");
|
||||
Require(sceNpWebApiDeleteServicePushEventFilter(context, 1) == ServicePushEventFilterNotFound, "no service filter exists");
|
||||
|
||||
@@ -53,5 +93,160 @@ int main() {
|
||||
Require(sceNpWebApiTerminate(context) == LibContextNotFound, "library context terminated twice");
|
||||
Require(sceNpWebApiCreateHandle(context) == LibContextNotFound, "handle of a terminated context");
|
||||
Require(sceNpWebApiTerminate(other) == 0, "terminate the second library context");
|
||||
}
|
||||
|
||||
void TestUserContexts() {
|
||||
const int context = sceNpWebApiInitialize(1, 0x10000);
|
||||
const int other = sceNpWebApiInitialize(1, 0x10000);
|
||||
Require(context > 0 && other > 0, "library contexts for user contexts");
|
||||
Require(sceNpWebApiTerminate(other) == 0, "terminate the unused library context");
|
||||
|
||||
Require(sceNpWebApiCreateContext(0x8000, onlineId) == InvalidLibContextId, "user context of a library context id above the range");
|
||||
Require(sceNpWebApiCreateContext(0x8000, nullptr) == InvalidLibContextId, "the library context id is checked before the online id");
|
||||
Require(sceNpWebApiCreateContext(context, nullptr) == InvalidArgument, "user context without an online id");
|
||||
Require(sceNpWebApiCreateContext(0, onlineId) == LibContextNotFound, "user context of library context 0");
|
||||
Require(sceNpWebApiCreateContext(other, onlineId) == LibContextNotFound, "user context of a terminated library context");
|
||||
|
||||
const int user = sceNpWebApiCreateContext(context, onlineId);
|
||||
Require(user > 0 && (user >> 16) == context && (user & 0xffff) != 0, "user context id holds its library context");
|
||||
Require(sceNpWebApiCreateContext(context, onlineId) == UserContextAlreadyExist, "a second user context for the same user");
|
||||
|
||||
Require(sceNpWebApiDeleteContext(user + 1) == UserContextNotFound, "delete an unknown user context");
|
||||
Require(sceNpWebApiDeleteContext(-1) == LibContextNotFound, "delete user context -1");
|
||||
Require(sceNpWebApiDeleteContext((other << 16) | 1) == LibContextNotFound, "delete a user context of a terminated library context");
|
||||
Require(sceNpWebApiDeleteContext(user) == 0, "delete the user context");
|
||||
Require(sceNpWebApiDeleteContext(user) == UserContextNotFound, "user context deleted twice");
|
||||
|
||||
const int next = sceNpWebApiCreateContext(context, onlineId);
|
||||
Require(next > 0 && next != user && (next >> 16) == context, "a new user context after the first was deleted");
|
||||
std::int64_t request = 0;
|
||||
Require(sceNpWebApiCreateRequest(next, "group", "/path", MethodGet, nullptr, &request) == 0, "request before termination");
|
||||
Require(sceNpWebApiTerminate(context) == 0, "terminate a library context with a user context");
|
||||
Require(sceNpWebApiDeleteContext(next) == LibContextNotFound, "user context of a terminated library context");
|
||||
Require(sceNpWebApiSendRequest(request, nullptr, 0) == LibContextNotFound, "request of a terminated library context");
|
||||
}
|
||||
|
||||
void TestRequests() {
|
||||
const int context = sceNpWebApiInitialize(1, 0x10000);
|
||||
const int user = sceNpWebApiCreateContext(context, onlineId);
|
||||
Require(user > 0, "user context for requests");
|
||||
|
||||
std::int64_t request = 0;
|
||||
Require(sceNpWebApiCreateRequest(user, nullptr, "/path", MethodGet, nullptr, &request) == InvalidArgument, "request without an API group");
|
||||
Require(sceNpWebApiCreateRequest(user, "group", nullptr, MethodGet, nullptr, &request) == InvalidArgument, "request without a path");
|
||||
const NpWebApiContentParameter untyped{sizeof(body), nullptr, {}};
|
||||
Require(sceNpWebApiCreateRequest(user, "group", "/path", MethodPost, &untyped, &request) == InvalidContentParameter, "content without a type");
|
||||
Require(sceNpWebApiCreateRequest(0, "group", "/path", MethodPost, &untyped, &request) == InvalidContentParameter, "content is checked before the user context");
|
||||
Require(sceNpWebApiCreateRequest(user, "group", "/path", MethodPatch, nullptr, &request) == InvalidArgument, "PATCH with SDK 2.50 or later");
|
||||
Require(sceNpWebApiCreateRequest(user + 1, "group", "/path", MethodGet, nullptr, &request) == UserContextNotFound, "request of an unknown user context");
|
||||
Require(sceNpWebApiCreateRequest(0, "group", "/path", MethodGet, nullptr, &request) == LibContextNotFound, "request of user context 0");
|
||||
Require(sceNpWebApiCreateRequest(-1, "group", "/path", MethodGet, nullptr, &request) == LibContextNotFound, "request of user context -1");
|
||||
Require(request == 0, "a failed request leaves the id alone");
|
||||
|
||||
const NpWebApiContentParameter empty{0, nullptr, {}};
|
||||
Require(sceNpWebApiCreateRequest(user, "group", "/path", MethodGet, &empty, nullptr) == 0, "empty content without a type and no id output");
|
||||
const NpWebApiContentParameter typed{sizeof(body), "application/json", {}};
|
||||
Require(sceNpWebApiCreateRequest(user, "group", "/path", MethodPost, &typed, &request) == 0, "create a request");
|
||||
Require(static_cast<int>(request >> 32) == user && (request & 0xffffffff) != 0, "request id holds its user context");
|
||||
std::int64_t second = 0;
|
||||
Require(sceNpWebApiCreateRequest(user, "group", "/path", MethodGet, nullptr, &second) == 0 && second != request && static_cast<int>(second >> 32) == user, "a second request");
|
||||
|
||||
Require(sceNpWebApiSendRequest(request, body, sizeof(body)) == NotSignedIn, "a request cannot be sent without a signed-in user");
|
||||
Require(sceNpWebApiSendRequest(request, body, sizeof(body)) == NotSignedIn, "a failed send can be retried");
|
||||
Require(sceNpWebApiSendRequest(request + 0x10000, nullptr, 0) == RequestNotFound, "send an unknown request");
|
||||
Require(sceNpWebApiSendRequest(static_cast<std::int64_t>(user + 1) << 32 | 1, nullptr, 0) == UserContextNotFound, "send a request of an unknown user context");
|
||||
Require(sceNpWebApiSendRequest(1, nullptr, 0) == LibContextNotFound, "send a request of library context 0");
|
||||
|
||||
Require(sceNpWebApiAbortRequest(request + 0x10000) == RequestNotFound, "abort an unknown request");
|
||||
Require(sceNpWebApiAbortRequest(-1) == LibContextNotFound, "abort request -1");
|
||||
Require(sceNpWebApiDeleteRequest(-1) == LibContextNotFound, "delete request -1");
|
||||
Require(sceNpWebApiAbortRequest(request) == 0, "abort the request");
|
||||
Require(sceNpWebApiAbortRequest(request) == 0, "abort the request twice");
|
||||
Require(sceNpWebApiSendRequest(request, body, sizeof(body)) == Aborted, "send an aborted request");
|
||||
Require(sceNpWebApiSendRequest(second, nullptr, 0) == NotSignedIn, "aborting one request leaves the other");
|
||||
|
||||
Require(sceNpWebApiDeleteRequest(request) == 0, "delete the request");
|
||||
Require(sceNpWebApiDeleteRequest(request) == RequestNotFound, "request deleted twice");
|
||||
Require(sceNpWebApiSendRequest(request, nullptr, 0) == RequestNotFound, "send a deleted request");
|
||||
Require(sceNpWebApiDeleteContext(user) == 0, "delete the user context with a request");
|
||||
Require(sceNpWebApiSendRequest(second, nullptr, 0) == UserContextNotFound, "request of a deleted user context");
|
||||
Require(sceNpWebApiDeleteRequest(second) == UserContextNotFound, "delete a request of a deleted user context");
|
||||
Require(sceNpWebApiTerminate(context) == 0, "terminate the request library context");
|
||||
}
|
||||
|
||||
void TestPushEventCallbacks() {
|
||||
const int context = sceNpWebApiInitialize(1, 0x10000);
|
||||
const int other = sceNpWebApiInitialize(1, 0x10000);
|
||||
const int user = sceNpWebApiCreateContext(context, onlineId);
|
||||
Require(user > 0, "user context for callbacks");
|
||||
const int filter = sceNpWebApiCreatePushEventFilter(context, filterParam, 1);
|
||||
const int otherFilter = sceNpWebApiCreatePushEventFilter(other, filterParam, 1);
|
||||
Require(filter > 0 && otherFilter > 0, "push event filters for callbacks");
|
||||
int argument = 0;
|
||||
|
||||
Require(sceNpWebApiRegisterPushEventCallback(user, filter, nullptr, &argument) == InvalidArgument, "push event callback without a function");
|
||||
Require(sceNpWebApiRegisterPushEventCallback(user + 1, filter, nullptr, &argument) == InvalidArgument, "the function is checked before the user context");
|
||||
Require(sceNpWebApiRegisterPushEventCallback(user + 1, filter, PushEventCallback(), &argument) == UserContextNotFound, "push event callback of an unknown user context");
|
||||
Require(sceNpWebApiRegisterPushEventCallback(user, otherFilter, PushEventCallback(), &argument) == PushEventFilterNotFound, "push event callback with another context's filter");
|
||||
const int callback = sceNpWebApiRegisterPushEventCallback(user, filter, PushEventCallback(), &argument);
|
||||
Require(callback > 0 && static_cast<unsigned>(callback) < 0xf0000000u, "push event callback id");
|
||||
Require(sceNpWebApiUnregisterPushEventCallback(user + 1, callback) == UserContextNotFound, "unregister through an unknown user context");
|
||||
Require(sceNpWebApiUnregisterServicePushEventCallback(user, callback) == ServicePushEventCallbackNotFound, "a push event callback is not a service callback");
|
||||
Require(sceNpWebApiUnregisterPushEventCallback(user, callback) == 0, "unregister the push event callback");
|
||||
Require(sceNpWebApiUnregisterPushEventCallback(user, callback) == PushEventCallbackNotFound, "push event callback unregistered twice");
|
||||
|
||||
Require(sceNpWebApiRegisterServicePushEventCallback(user, 1, nullptr, &argument) == InvalidArgument, "service callback without a function");
|
||||
Require(sceNpWebApiRegisterServicePushEventCallback(user + 1, 1, PushEventCallback(), &argument) == UserContextNotFound, "service callback of an unknown user context");
|
||||
Require(sceNpWebApiRegisterServicePushEventCallback(user, filter, PushEventCallback(), &argument) == ServicePushEventFilterNotFound, "a push event filter is not a service filter");
|
||||
Require(sceNpWebApiUnregisterServicePushEventCallback(user, 1) == ServicePushEventCallbackNotFound, "no service callback exists");
|
||||
|
||||
Require(sceNpWebApiTerminate(context) == 0 && sceNpWebApiTerminate(other) == 0, "terminate the callback library contexts");
|
||||
}
|
||||
|
||||
void TestOlderSdks() {
|
||||
const int context = sceNpWebApiInitialize(1, 0x10000);
|
||||
const int user = sceNpWebApiCreateContext(context, onlineId);
|
||||
Require(user > 0, "user context for older SDKs");
|
||||
int argument = 0;
|
||||
|
||||
SetSdkVersion(0x02000000);
|
||||
std::int64_t request = 0;
|
||||
Require(sceNpWebApiCreateRequest(user, "group", "/path", MethodPatch, nullptr, &request) == 0, "PATCH before SDK 2.50");
|
||||
Require(sceNpWebApiSendRequest(request, nullptr, 0) == NotSignedIn, "send a PATCH request");
|
||||
Require(sceNpWebApiRegisterPushEventCallback(user, 1, nullptr, &argument) == InvalidArgument, "push event callback without a function from SDK 1.00");
|
||||
const int callback = sceNpWebApiRegisterPushEventCallback(user, 1, PushEventCallback(), &argument);
|
||||
Require(callback > 0, "push event callback without a filter before SDK 2.50");
|
||||
Require(sceNpWebApiUnregisterPushEventCallback(user, callback) == 0, "unregister the unfiltered push event callback");
|
||||
const int service = sceNpWebApiRegisterServicePushEventCallback(user, 1, PushEventCallback(), &argument);
|
||||
Require(service > 0 && service != callback, "service callback without a filter before SDK 2.50");
|
||||
Require(sceNpWebApiUnregisterPushEventCallback(user, service) == PushEventCallbackNotFound, "a service callback is not a push event callback");
|
||||
Require(sceNpWebApiUnregisterServicePushEventCallback(user, service) == 0, "unregister the service callback");
|
||||
Require(sceNpWebApiUnregisterServicePushEventCallback(user, service) == ServicePushEventCallbackNotFound, "service callback unregistered twice");
|
||||
|
||||
SetSdkVersion(0x01000000);
|
||||
const int handle = sceNpWebApiCreateHandle(context);
|
||||
Require(sceNpWebApiCreateServicePushEventFilter(context, handle, "service", 0xffffffffu, filterParam, 1) == SignedInUserNotFound, "the invalid service label is accepted before SDK 2.00");
|
||||
|
||||
SetSdkVersion(0x00900000);
|
||||
const int unchecked = sceNpWebApiRegisterPushEventCallback(user, 1, nullptr, &argument);
|
||||
Require(unchecked > 0, "push event callback without a function before SDK 1.00");
|
||||
Require(sceNpWebApiUnregisterPushEventCallback(user, unchecked) == 0, "unregister the push event callback without a function");
|
||||
Require(sceNpWebApiRegisterServicePushEventCallback(user + 1, 1, nullptr, &argument) == UserContextNotFound, "the user context is checked before the service callback function before SDK 1.00");
|
||||
Require(sceNpWebApiRegisterServicePushEventCallback(user, 1, nullptr, &argument) == InvalidArgument, "service callback without a function before SDK 1.00");
|
||||
|
||||
SetSdkVersion(0x04508001);
|
||||
Require(sceNpWebApiTerminate(context) == 0, "terminate the older SDK library context");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
InstallProcessParameters();
|
||||
SetSdkVersion(0x04508001);
|
||||
TestLibraryContexts();
|
||||
TestUserContexts();
|
||||
TestRequests();
|
||||
TestPushEventCallbacks();
|
||||
TestOlderSdks();
|
||||
return 0;
|
||||
}
|
||||
@@ -3,4 +3,6 @@
|
||||
### Unknown function info
|
||||
|
||||
- [sceNpWebApiInitialize, sceNpWebApiTerminate, sceNpWebApiCreateHandle, sceNpWebApiDeleteHandle, sceNpWebApiCreatePushEventFilter, sceNpWebApiDeletePushEventFilter](../../../core/libs/prx/libSceNpWebApi/Export.cpp) (libSceNpWebApi, PS4) - signatures, id ranges (library contexts 1 to 0x7fff, handles and filters below 0xf0000000) and errors (`LIB_CONTEXT_NOT_FOUND`, `HANDLE_NOT_FOUND`, `PUSH_EVENT_FILTER_NOT_FOUND`, `INVALID_ARGUMENT` for a missing filter parameter) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api/np_web_api_internal.cpp); they are not checked on a console. The libHttp context and pool size are kept but not used, since no request is ever sent. Limbo (CUSA01664) creates a context, a handle and filters when its game starts.
|
||||
- [sceNpWebApiCreateServicePushEventFilter](../../../core/libs/prx/libSceNpWebApi/Export.cpp) (libSceNpWebApi, PS4) - a filter for a PSN service needs the signed-in user, and no user is signed in to PSN, so a valid call returns `SIGNED_IN_USER_NOT_FOUND` after the context and handle checks, as shadPS4 does with its network disabled. shadPS4 also rejects an invalid service label for titles built with SDK 2.00 or later; AnyPS5 has no compiled-SDK version for libraries yet, so such a call returns `SIGNED_IN_USER_NOT_FOUND` instead of `INVALID_ARGUMENT`. `sceNpWebApiDeleteServicePushEventFilter` therefore never finds a filter.
|
||||
- [sceNpWebApiCreateServicePushEventFilter](../../../core/libs/prx/libSceNpWebApi/Export.cpp) (libSceNpWebApi, PS4) - a filter for a PSN service needs the signed-in user, and no user is signed in to PSN, so a valid call returns `SIGNED_IN_USER_NOT_FOUND` after the context and handle checks, as shadPS4 does with its network disabled. `sceNpWebApiDeleteServicePushEventFilter` therefore never finds a filter.
|
||||
- [sceNpWebApiCreateContext, sceNpWebApiDeleteContext, sceNpWebApiCreateRequest, sceNpWebApiDeleteRequest, sceNpWebApiAbortRequest, sceNpWebApiSendRequest](../../../core/libs/prx/libSceNpWebApi/Export.cpp) (libSceNpWebApi, PS4) - the online id is not looked up: as in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api/np_web_api_internal.cpp), a user context belongs to the initial user, so a library context holds at most one. With no user signed in to PSN, `sceNpWebApiSendRequest` returns `NOT_SIGNED_IN` unless the request is aborted. Id layouts and the delete, abort and not-found results match an offline PS4 Pro probe on firmware 10.71 (#9797); other checks follow shadPS4. That homebrew probe, with a non-retail SDK version, accepted method 99, so `INVALID_ARGUMENT` for a method after DELETE from SDK 2.50 is unverified. `sceNpWebApiTerminate` also deletes user contexts and requests; shadPS4 stops after the first user context.
|
||||
- [sceNpWebApiRegisterPushEventCallback, sceNpWebApiUnregisterPushEventCallback, sceNpWebApiRegisterServicePushEventCallback, sceNpWebApiUnregisterServicePushEventCallback](../../../core/libs/prx/libSceNpWebApi/Export.cpp) (libSceNpWebApi, PS4) - registered callbacks are never called, since no push event arrives. A null function is rejected from SDK 1.00 and the filter must exist in the library context from SDK 2.50, as in shadPS4; this is not checked on a console. No service filter can be created, so from SDK 2.50 a service callback returns `SERVICE_PUSH_EVENT_FILTER_NOT_FOUND`. Callback ids come from the counter of handles and filters.
|
||||
Reference in new issue
Block a user