mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-02 11:03:34 -06:00
856 lines
30 KiB
C++
856 lines
30 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <variant>
|
|
|
|
#include <core/user_settings.h>
|
|
#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/tls.h"
|
|
|
|
namespace Libraries::Np::NpManager {
|
|
|
|
static bool g_signed_in = false;
|
|
static s32 g_active_requests = 0;
|
|
static std::mutex g_request_mutex;
|
|
|
|
static std::map<std::string, std::function<void()>> g_np_callbacks;
|
|
static std::mutex g_np_callbacks_mutex;
|
|
|
|
// Internal types for storing request-related information
|
|
enum class NpRequestState {
|
|
None = 0,
|
|
Ready = 1,
|
|
Aborted = 2,
|
|
Complete = 3,
|
|
};
|
|
|
|
struct NpRequest {
|
|
NpRequestState state;
|
|
bool async;
|
|
s32 result;
|
|
};
|
|
|
|
static std::vector<NpRequest> g_requests;
|
|
|
|
s32 CreateNpRequest(bool async) {
|
|
if (g_active_requests == ORBIS_NP_MANAGER_REQUEST_LIMIT) {
|
|
return ORBIS_NP_ERROR_REQUEST_MAX;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = 0;
|
|
while (req_index < g_requests.size()) {
|
|
// Find first nonexistant request
|
|
if (g_requests[req_index].state == NpRequestState::None) {
|
|
// There is no request at this index, set the index to ready then break.
|
|
g_requests[req_index].state = NpRequestState::Ready;
|
|
g_requests[req_index].async = async;
|
|
break;
|
|
}
|
|
req_index++;
|
|
}
|
|
|
|
if (req_index == g_requests.size()) {
|
|
// There are no requests to replace.
|
|
NpRequest new_request{NpRequestState::Ready, async, 0};
|
|
g_requests.emplace_back(new_request);
|
|
}
|
|
|
|
// Offset by one, first returned ID is 0x20000001
|
|
g_active_requests++;
|
|
return req_index + ORBIS_NP_MANAGER_REQUEST_ID_OFFSET + 1;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpCreateRequest() {
|
|
LOG_DEBUG(Lib_NpManager, "called");
|
|
return CreateNpRequest(false);
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpCreateAsyncRequest(const OrbisNpCreateAsyncRequestParameter* param) {
|
|
LOG_DEBUG(Lib_NpManager, "called");
|
|
if (param == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (param->size != sizeof(OrbisNpCreateAsyncRequestParameter)) {
|
|
return ORBIS_NP_ERROR_INVALID_SIZE;
|
|
}
|
|
|
|
return CreateNpRequest(true);
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpCheckNpAvailability(s32 req_id, OrbisNpOnlineId* online_id) {
|
|
if (online_id == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, req_id = {:#x}, is_async = {}", req_id,
|
|
request.async);
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpCheckNpAvailabilityA(s32 req_id,
|
|
Libraries::UserService::OrbisUserServiceUserId user_id) {
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, req_id = {:#x}, is_async = {}", req_id,
|
|
request.async);
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpCheckNpReachability(s32 req_id,
|
|
Libraries::UserService::OrbisUserServiceUserId user_id) {
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, req_id = {:#x}, is_async = {}", req_id,
|
|
request.async);
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpCheckPlus(s32 req_id, const OrbisNpCheckPlusParameter* param,
|
|
OrbisNpCheckPlusResult* result) {
|
|
|
|
if (req_id == 0 || param == nullptr || result == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (param->size != sizeof(OrbisNpCheckPlusParameter)) {
|
|
return ORBIS_NP_ERROR_INVALID_SIZE;
|
|
}
|
|
|
|
if (param->features < 1 || param->features > 3) {
|
|
// TODO: If compiled SDK version is greater or equal to fw 3.50,
|
|
// error if param->features != 1 instead.
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager,
|
|
"(STUBBED) called, req_id = {:#x}, is_async = {}, param.features = {:#x}", req_id,
|
|
request.async, param->features);
|
|
|
|
// For now, set authorized to true to signal PS+ access.
|
|
result->authorized = true;
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountLanguage(s32 req_id, OrbisNpOnlineId* online_id,
|
|
OrbisNpLanguageCode* language) {
|
|
if (online_id == nullptr || language == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, req_id = {:#x}, is_async = {}", req_id,
|
|
request.async);
|
|
|
|
std::memset(language, 0, sizeof(OrbisNpLanguageCode));
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountLanguageA(s32 req_id,
|
|
Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpLanguageCode* language) {
|
|
if (language == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, req_id = {:#x}, user_id = {}, is_async = {}",
|
|
req_id, user_id, request.async);
|
|
|
|
std::memset(language, 0, sizeof(OrbisNpLanguageCode));
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetParentalControlInfo(s32 req_id, OrbisNpOnlineId* online_id, s8* age,
|
|
OrbisNpParentalControlInfo* info) {
|
|
if (online_id == nullptr || age == nullptr || info == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, req_id = {:#x}, is_async = {}", req_id,
|
|
request.async);
|
|
|
|
// TODO: Add to config?
|
|
*age = 13;
|
|
std::memset(info, 0, sizeof(OrbisNpParentalControlInfo));
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI
|
|
sceNpGetParentalControlInfoA(s32 req_id, Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
s8* age, OrbisNpParentalControlInfo* info) {
|
|
if (age == nullptr || info == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
auto& request = g_requests[req_index];
|
|
if (request.state == NpRequestState::Complete) {
|
|
request.result = ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
} else if (request.state == NpRequestState::Aborted) {
|
|
request.result = ORBIS_NP_ERROR_ABORTED;
|
|
return ORBIS_NP_ERROR_ABORTED;
|
|
}
|
|
|
|
request.state = NpRequestState::Complete;
|
|
if (!g_signed_in) {
|
|
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
|
// If the request is processed in some form, and it's an async request, then it returns OK.
|
|
if (request.async) {
|
|
return ORBIS_OK;
|
|
}
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, req_id = {:#x}, user_id = {}, is_async = {}",
|
|
req_id, user_id, request.async);
|
|
|
|
// TODO: Add to config?
|
|
*age = 13;
|
|
std::memset(info, 0, sizeof(OrbisNpParentalControlInfo));
|
|
|
|
request.result = ORBIS_OK;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpAbortRequest(s32 req_id) {
|
|
LOG_DEBUG(Lib_NpManager, "called req_id = {:#x}", req_id);
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
if (g_requests[req_index].state == NpRequestState::Complete) {
|
|
// If the request is already complete, abort is ignored.
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
g_requests[req_index].state = NpRequestState::Aborted;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpWaitAsync(s32 req_id, s32* result) {
|
|
if (result == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
if (!g_requests[req_index].async || g_requests[req_index].state == NpRequestState::Ready) {
|
|
return ORBIS_NP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
// Since we're not actually performing any sort of network request here,
|
|
// we can just set result based on the request and return.
|
|
*result = g_requests[req_index].result;
|
|
LOG_WARNING(Lib_NpManager, "called req_id = {:#x}, returning result = {:#x}", req_id,
|
|
static_cast<u32>(*result));
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpPollAsync(s32 req_id, s32* result) {
|
|
if (result == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
if (!g_requests[req_index].async || g_requests[req_index].state == NpRequestState::Ready) {
|
|
return ORBIS_NP_ERROR_INVALID_ID;
|
|
}
|
|
|
|
// Since we're not actually performing any sort of network request here,
|
|
// we can just set result based on the request and return.
|
|
*result = g_requests[req_index].result;
|
|
LOG_WARNING(Lib_NpManager, "called req_id = {:#x}, returning result = {:#x}", req_id,
|
|
static_cast<u32>(*result));
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpDeleteRequest(s32 req_id) {
|
|
LOG_DEBUG(Lib_NpManager, "called req_id = {:#x}", req_id);
|
|
|
|
std::scoped_lock lk{g_request_mutex};
|
|
|
|
s32 req_index = req_id - ORBIS_NP_MANAGER_REQUEST_ID_OFFSET - 1;
|
|
if (g_active_requests == 0 || g_requests.size() <= req_index ||
|
|
g_requests[req_index].state == NpRequestState::None) {
|
|
return ORBIS_NP_ERROR_REQUEST_NOT_FOUND;
|
|
}
|
|
|
|
g_active_requests--;
|
|
g_requests[req_index].state = NpRequestState::None;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountCountry(OrbisNpOnlineId* online_id,
|
|
OrbisNpCountryCode* country_code) {
|
|
if (online_id == nullptr || country_code == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
std::memset(country_code, 0, sizeof(OrbisNpCountryCode));
|
|
// TODO: get NP country code from config
|
|
std::memcpy(country_code->country_code, "us", 2);
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountCountryA(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpCountryCode* country_code) {
|
|
if (country_code == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
std::memset(country_code, 0, sizeof(OrbisNpCountryCode));
|
|
// TODO: get NP country code from config
|
|
std::memcpy(country_code->country_code, "us", 2);
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountDateOfBirth(OrbisNpOnlineId* online_id,
|
|
OrbisNpDate* date_of_birth) {
|
|
if (online_id == nullptr || date_of_birth == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
// TODO: maybe add to config?
|
|
date_of_birth->day = 1;
|
|
date_of_birth->month = 1;
|
|
date_of_birth->year = 2000;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpDate* date_of_birth) {
|
|
if (date_of_birth == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
|
|
// TODO: maybe add to config?
|
|
date_of_birth->day = 1;
|
|
date_of_birth->month = 1;
|
|
date_of_birth->year = 2000;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetGamePresenceStatus(OrbisNpOnlineId* online_id,
|
|
OrbisNpGamePresenseStatus* game_status) {
|
|
if (online_id == nullptr || game_status == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
*game_status =
|
|
g_signed_in ? OrbisNpGamePresenseStatus::Online : OrbisNpGamePresenseStatus::Offline;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetGamePresenceStatusA(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpGamePresenseStatus* game_status) {
|
|
if (game_status == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
*game_status =
|
|
g_signed_in ? OrbisNpGamePresenseStatus::Online : OrbisNpGamePresenseStatus::Offline;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id) {
|
|
LOG_DEBUG(Lib_NpManager, "called");
|
|
if (online_id == nullptr || account_id == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
*account_id = 0;
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
*account_id = 0xFEEDFACE;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetAccountIdA(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
u64* account_id) {
|
|
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
|
|
if (account_id == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
*account_id = 0;
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
*account_id = 0xFEEDFACE;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpId* np_id) {
|
|
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
|
|
if (np_id == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
memset(np_id, 0, sizeof(OrbisNpId));
|
|
strncpy(np_id->handle.data, UserManagement.GetDefaultUser().user_name.c_str(),
|
|
sizeof(np_id->handle.data));
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetOnlineId(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpOnlineId* online_id) {
|
|
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
|
|
if (online_id == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
memset(online_id, 0, sizeof(OrbisNpOnlineId));
|
|
strncpy(online_id->data, UserManagement.GetDefaultUser().user_name.c_str(),
|
|
sizeof(online_id->data));
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetNpReachabilityState(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpReachabilityState* state) {
|
|
if (state == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
*state =
|
|
g_signed_in ? OrbisNpReachabilityState::Reachable : OrbisNpReachabilityState::Unavailable;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpGetState(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
OrbisNpState* state) {
|
|
if (state == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
*state = g_signed_in ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
|
|
LOG_DEBUG(Lib_NpManager, "Signed {}", g_signed_in ? "in" : "out");
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI
|
|
sceNpGetUserIdByAccountId(u64 account_id, Libraries::UserService::OrbisUserServiceUserId* user_id) {
|
|
if (user_id == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!g_signed_in) {
|
|
return ORBIS_NP_ERROR_SIGNED_OUT;
|
|
}
|
|
*user_id = 1;
|
|
LOG_DEBUG(Lib_NpManager, "userid({}) = {}", account_id, *user_id);
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpHasSignedUp(Libraries::UserService::OrbisUserServiceUserId user_id,
|
|
bool* has_signed_up) {
|
|
LOG_DEBUG(Lib_NpManager, "called");
|
|
if (has_signed_up == nullptr) {
|
|
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
*has_signed_up = g_signed_in ? true : false;
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
struct NpStateCallbackForNpToolkit {
|
|
OrbisNpStateCallbackForNpToolkit func;
|
|
void* userdata;
|
|
};
|
|
|
|
NpStateCallbackForNpToolkit NpStateCbForNp;
|
|
|
|
struct NpStateCallback {
|
|
std::variant<OrbisNpStateCallback, OrbisNpStateCallbackA> func;
|
|
void* userdata;
|
|
};
|
|
|
|
NpStateCallback NpStateCb;
|
|
|
|
s32 PS4_SYSV_ABI sceNpCheckCallback() {
|
|
LOG_DEBUG(Lib_NpManager, "(STUBBED) called");
|
|
|
|
std::scoped_lock lk{g_np_callbacks_mutex};
|
|
|
|
for (auto i : g_np_callbacks) {
|
|
(i.second)();
|
|
}
|
|
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpCheckCallbackForLib() {
|
|
LOG_DEBUG(Lib_NpManager, "(STUBBED) called");
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpRegisterStateCallback(OrbisNpStateCallback callback, void* userdata) {
|
|
static s32 id = 0;
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, userdata = {}", userdata);
|
|
NpStateCb.func = callback;
|
|
NpStateCb.userdata = userdata;
|
|
|
|
return id;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpRegisterStateCallbackA(OrbisNpStateCallbackA callback, void* userdata) {
|
|
static s32 id = 0;
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called, userdata = {}", userdata);
|
|
NpStateCb.func = callback;
|
|
NpStateCb.userdata = userdata;
|
|
|
|
return id;
|
|
}
|
|
|
|
struct NpReachabilityStateCallback {
|
|
OrbisNpReachabilityStateCallback func;
|
|
void* userdata;
|
|
};
|
|
|
|
NpReachabilityStateCallback NpReachabilityCb;
|
|
|
|
s32 PS4_SYSV_ABI sceNpRegisterNpReachabilityStateCallback(OrbisNpReachabilityStateCallback callback,
|
|
void* userdata) {
|
|
static s32 id = 0;
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
|
NpReachabilityCb.func = callback;
|
|
NpReachabilityCb.userdata = userdata;
|
|
return id;
|
|
}
|
|
|
|
s32 PS4_SYSV_ABI sceNpRegisterStateCallbackForToolkit(OrbisNpStateCallbackForNpToolkit callback,
|
|
void* userdata) {
|
|
static s32 id = 0;
|
|
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
|
NpStateCbForNp.func = callback;
|
|
NpStateCbForNp.userdata = userdata;
|
|
return id;
|
|
}
|
|
|
|
void RegisterNpCallback(std::string key, std::function<void()> cb) {
|
|
std::scoped_lock lk{g_np_callbacks_mutex};
|
|
|
|
LOG_DEBUG(Lib_NpManager, "registering callback processing for {}", key);
|
|
|
|
g_np_callbacks.emplace(key, cb);
|
|
}
|
|
|
|
void DeregisterNpCallback(std::string key) {
|
|
std::scoped_lock lk{g_np_callbacks_mutex};
|
|
|
|
LOG_DEBUG(Lib_NpManager, "deregistering callback processing for {}", key);
|
|
|
|
g_np_callbacks.erase(key);
|
|
}
|
|
|
|
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
|
g_signed_in = EmulatorSettings.IsPSNSignedIn();
|
|
|
|
LIB_FUNCTION("GpLQDNKICac", "libSceNpManager", 1, "libSceNpManager", sceNpCreateRequest);
|
|
LIB_FUNCTION("eiqMCt9UshI", "libSceNpManager", 1, "libSceNpManager", sceNpCreateAsyncRequest);
|
|
LIB_FUNCTION("2rsFmlGWleQ", "libSceNpManager", 1, "libSceNpManager", sceNpCheckNpAvailability);
|
|
LIB_FUNCTION("8Z2Jc5GvGDI", "libSceNpManager", 1, "libSceNpManager", sceNpCheckNpAvailabilityA);
|
|
LIB_FUNCTION("KfGZg2y73oM", "libSceNpManager", 1, "libSceNpManager", sceNpCheckNpReachability);
|
|
LIB_FUNCTION("r6MyYJkryz8", "libSceNpManager", 1, "libSceNpManager", sceNpCheckPlus);
|
|
LIB_FUNCTION("KZ1Mj9yEGYc", "libSceNpManager", 1, "libSceNpManager", sceNpGetAccountLanguage);
|
|
LIB_FUNCTION("TPMbgIxvog0", "libSceNpManager", 1, "libSceNpManager", sceNpGetAccountLanguageA);
|
|
LIB_FUNCTION("ilwLM4zOmu4", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpGetParentalControlInfo);
|
|
LIB_FUNCTION("m9L3O6yst-U", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpGetParentalControlInfoA);
|
|
LIB_FUNCTION("OzKvTvg3ZYU", "libSceNpManager", 1, "libSceNpManager", sceNpAbortRequest);
|
|
LIB_FUNCTION("jyi5p9XWUSs", "libSceNpManager", 1, "libSceNpManager", sceNpWaitAsync);
|
|
LIB_FUNCTION("uqcPJLWL08M", "libSceNpManager", 1, "libSceNpManager", sceNpPollAsync);
|
|
LIB_FUNCTION("S7QTn72PrDw", "libSceNpManager", 1, "libSceNpManager", sceNpDeleteRequest);
|
|
|
|
LIB_FUNCTION("Ghz9iWDUtC4", "libSceNpManager", 1, "libSceNpManager", sceNpGetAccountCountry);
|
|
LIB_FUNCTION("JT+t00a3TxA", "libSceNpManager", 1, "libSceNpManager", sceNpGetAccountCountryA);
|
|
LIB_FUNCTION("8VBTeRf1ZwI", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpGetAccountDateOfBirth);
|
|
LIB_FUNCTION("q3M7XzBKC3s", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpGetAccountDateOfBirthA);
|
|
LIB_FUNCTION("IPb1hd1wAGc", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpGetGamePresenceStatus);
|
|
LIB_FUNCTION("oPO9U42YpgI", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpGetGamePresenceStatusA);
|
|
LIB_FUNCTION("e-ZuhGEoeC4", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpGetNpReachabilityState);
|
|
|
|
LIB_FUNCTION("a8R9-75u4iM", "libSceNpManager", 1, "libSceNpManager", sceNpGetAccountId);
|
|
LIB_FUNCTION("rbknaUjpqWo", "libSceNpManager", 1, "libSceNpManager", sceNpGetAccountIdA);
|
|
LIB_FUNCTION("p-o74CnoNzY", "libSceNpManager", 1, "libSceNpManager", sceNpGetNpId);
|
|
LIB_FUNCTION("XDncXQIJUSk", "libSceNpManager", 1, "libSceNpManager", sceNpGetOnlineId);
|
|
LIB_FUNCTION("eQH7nWPcAgc", "libSceNpManager", 1, "libSceNpManager", sceNpGetState);
|
|
LIB_FUNCTION("VgYczPGB5ss", "libSceNpManager", 1, "libSceNpManager", sceNpGetUserIdByAccountId);
|
|
LIB_FUNCTION("Oad3rvY-NJQ", "libSceNpManager", 1, "libSceNpManager", sceNpHasSignedUp);
|
|
LIB_FUNCTION("3Zl8BePTh9Y", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallback);
|
|
LIB_FUNCTION("JELHf4xPufo", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallbackForLib);
|
|
LIB_FUNCTION("VfRSmPmj8Q8", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpRegisterStateCallback);
|
|
LIB_FUNCTION("hw5KNqAAels", "libSceNpManager", 1, "libSceNpManager",
|
|
sceNpRegisterNpReachabilityStateCallback);
|
|
LIB_FUNCTION("JELHf4xPufo", "libSceNpManagerForToolkit", 1, "libSceNpManager",
|
|
sceNpCheckCallbackForLib);
|
|
LIB_FUNCTION("0c7HbXRKUt4", "libSceNpManagerForToolkit", 1, "libSceNpManager",
|
|
sceNpRegisterStateCallbackForToolkit);
|
|
|
|
LIB_FUNCTION("2rsFmlGWleQ", "libSceNpManagerCompat", 1, "libSceNpManager",
|
|
sceNpCheckNpAvailability);
|
|
LIB_FUNCTION("a8R9-75u4iM", "libSceNpManagerCompat", 1, "libSceNpManager", sceNpGetAccountId);
|
|
LIB_FUNCTION("KZ1Mj9yEGYc", "libSceNpManagerCompat", 1, "libSceNpManager",
|
|
sceNpGetAccountLanguage);
|
|
LIB_FUNCTION("Ghz9iWDUtC4", "libSceNpManagerCompat", 1, "libSceNpManager",
|
|
sceNpGetAccountCountry);
|
|
LIB_FUNCTION("8VBTeRf1ZwI", "libSceNpManagerCompat", 1, "libSceNpManager",
|
|
sceNpGetAccountDateOfBirth);
|
|
LIB_FUNCTION("IPb1hd1wAGc", "libSceNpManagerCompat", 1, "libSceNpManager",
|
|
sceNpGetGamePresenceStatus);
|
|
LIB_FUNCTION("ilwLM4zOmu4", "libSceNpManagerCompat", 1, "libSceNpManager",
|
|
sceNpGetParentalControlInfo);
|
|
};
|
|
|
|
} // namespace Libraries::Np::NpManager
|