np: separate PSN emulation from host networking

Co-authored-by: Chai T <chaitukca@gmail.com>
This commit is contained in:
nmzikandChai T committed 2026-10-29 08:30:55 +01:00
1 parent 651ef73a03
commit 15452a5fee
12 files changed
+413 -78

No files matched your search

+4
View File
@@ -137,6 +137,10 @@ bool IsNetworkConnected() {
return g_config->network_connected;
}
bool PsnEmulationEnabled() {
return g_config->psn_emulation_enabled;
}
bool ShaderValidationEnabled() {
return g_config->shader_validation_enabled;
}
+2
View File
@@ -70,6 +70,7 @@ struct ConfigOptions {
bool pro_enabled = false;
bool amd_cpu_enabled = false;
bool network_connected = false;
bool psn_emulation_enabled = false;
uint32_t vblank_frequency = 60;
uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE;
bool vulkan_validation_enabled = false;
@@ -132,6 +133,7 @@ bool VrEnabled();
bool Ps5ProEnabled();
bool AmdCpuEnabled();
bool IsNetworkConnected();
bool PsnEmulationEnabled();
uint32_t GetVblankFrequency();
uint32_t GetConsoleLanguage();
bool VulkanValidationEnabled();
@@ -412,6 +412,11 @@
<property name="text"><string>Enable network connection</string></property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_psn_emulation">
<property name="text"><string>Emulate PSN (compatibility)</string></property>
</widget>
</item>
<item>
<spacer name="networkSpacer">
<property name="orientation"><enum>Qt::Vertical</enum></property>
+2
View File
@@ -128,6 +128,7 @@ public:
bool perf_overlay_enabled = false;
bool skip_notice_screen = false;
bool network_connected = false;
bool psn_emulation_enabled = false;
int vblank_frequency = 60;
int console_language = DEFAULT_CONSOLE_LANGUAGE;
bool vulkan_validation_enabled = false;
@@ -172,6 +173,7 @@ public:
perf_overlay_enabled = other.perf_overlay_enabled;
skip_notice_screen = other.skip_notice_screen;
network_connected = other.network_connected;
psn_emulation_enabled = other.psn_emulation_enabled;
vblank_frequency = other.vblank_frequency;
console_language = other.console_language;
vulkan_validation_enabled = other.vulkan_validation_enabled;
+2
View File
@@ -42,6 +42,7 @@ QVariantMap Configuration::GameSettings() const {
KYTY_CFG_SET(perf_overlay_enabled);
KYTY_CFG_SET(skip_notice_screen);
KYTY_CFG_SET(network_connected);
KYTY_CFG_SET(psn_emulation_enabled);
KYTY_CFG_SET(vblank_frequency);
KYTY_CFG_SET(console_language);
KYTY_CFG_SET(vulkan_validation_enabled);
@@ -97,6 +98,7 @@ void Configuration::ReadGameSettingsValues(const Settings& s) {
KYTY_CFG_GET(perf_overlay_enabled);
KYTY_CFG_GET(skip_notice_screen);
KYTY_CFG_GET(network_connected);
KYTY_CFG_GET(psn_emulation_enabled);
vblank_frequency = s.value("vblank_frequency", vblank_frequency).toInt();
console_language = s.value("console_language", console_language).toInt();
if (console_language < 0 || console_language > MAX_CONSOLE_LANGUAGE) {
@@ -298,6 +298,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
m_ui->checkBox_shader_cache->setChecked(info.shader_cache_enabled);
m_ui->checkBox_skip_notice_screen->setChecked(info.skip_notice_screen);
m_ui->checkBox_network_connected->setChecked(info.network_connected);
m_ui->checkBox_psn_emulation->setChecked(info.psn_emulation_enabled);
m_ui->checkBox_trophy_notifications->setChecked(info.trophy_enabled);
m_ui->spinBox_trophy_duration->setValue(info.trophy_duration_seconds);
m_ui->checkBox_perf_overlay->setChecked(info.perf_overlay_enabled);
@@ -534,6 +535,7 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui, boo
info.shader_cache_enabled = ui.checkBox_shader_cache->isChecked();
info.skip_notice_screen = ui.checkBox_skip_notice_screen->isChecked();
info.network_connected = ui.checkBox_network_connected->isChecked();
info.psn_emulation_enabled = ui.checkBox_psn_emulation->isChecked();
info.trophy_enabled = ui.checkBox_trophy_notifications->isChecked();
info.trophy_duration_seconds = ui.spinBox_trophy_duration->value();
info.perf_overlay_enabled = ui.checkBox_perf_overlay->isChecked();
+1
View File
@@ -276,6 +276,7 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
AddFlag(args, info.perf_overlay_enabled, "--perf-overlay");
AddFlag(args, info.skip_notice_screen, "--skip-notice-screen");
AddFlag(args, info.network_connected, "--network-connected");
AddFlag(args, info.psn_emulation_enabled, "--emulate-psn");
AddFlag(args, info.tessellation_enabled, "--tessellation");
AddFlag(args, info.occlusion_queries_enabled, "--occlusion-queries");
AddFlag(args, info.shader_cache_enabled, "--shader-cache");
+54 -32
View File
@@ -1685,12 +1685,34 @@ LIB_VERSION("NpAuth", 1, "NpAuth", 1, 1);
constexpr int NP_AUTH_MAX_REQUEST_NUM = 16;
constexpr int NP_AUTH_REQUEST_ID_OFFSET = 0x10000000;
constexpr int NP_AUTH_ERROR_INVALID_ARGUMENT = -2141912319; /* 0x80550301 */
constexpr int NP_AUTH_ERROR_INVALID_SIZE = -2141912318; /* 0x80550302 */
constexpr int NP_AUTH_ERROR_ABORTED = -2141912316; /* 0x80550304 */
constexpr int NP_AUTH_ERROR_REQUEST_MAX = -2141912315; /* 0x80550305 */
constexpr int NP_AUTH_ERROR_REQUEST_NOT_FOUND = -2141912314; /* 0x80550306 */
constexpr int NP_AUTH_ERROR_INVALID_ID = -2141912313; /* 0x80550307 */
constexpr int NP_AUTH_ERROR_SERVICE_DOWN = -2141912063; /* 0x80550401 */
constexpr int NP_ERROR_SIGNED_OUT = -2141913082; /* 0x80550006 */
struct NpAuthGetAuthorizationCodeParameterV3 {
size_t size;
int user_id;
uint8_t padding[4];
const void* client_id;
const char* scope;
};
struct NpAuthGetIdTokenParameterV3 {
size_t size;
int user_id;
uint8_t padding[4];
const void* client_id;
const void* client_secret;
const char* scope;
};
static_assert(sizeof(NpAuthGetAuthorizationCodeParameterV3) == 32);
static_assert(sizeof(NpAuthGetIdTokenParameterV3) == 40);
enum class NpAuthRequestState {
None = 0,
Ready,
@@ -1822,7 +1844,7 @@ static int np_auth_wait_or_poll_async(int req_id, int* result) {
return 0;
}
static int np_auth_complete_signed_out(NpAuthRequest* request) {
static int np_auth_complete_unavailable(NpAuthRequest* request, int user_id) {
if (request->state == NpAuthRequestState::Complete) {
request->result = NP_AUTH_ERROR_INVALID_ARGUMENT;
return NP_AUTH_ERROR_INVALID_ARGUMENT;
@@ -1833,9 +1855,11 @@ static int np_auth_complete_signed_out(NpAuthRequest* request) {
}
request->state = NpAuthRequestState::Complete;
request->result = NP_ERROR_SIGNED_OUT;
// Emulating sign-in cannot provide a real authorization code or ID token.
request->result = Network::NpManager::IsSignedIn(user_id) ? NP_AUTH_ERROR_SERVICE_DOWN
: NP_ERROR_SIGNED_OUT;
return request->async ? 0 : NP_ERROR_SIGNED_OUT;
return request->async ? 0 : request->result;
}
static int KYTY_SYSV_ABI NpAuthWaitAsync(int req_id, int* result) {
@@ -1856,8 +1880,9 @@ static int KYTY_SYSV_ABI NpAuthPollAsync(int req_id, int* result) {
return np_auth_wait_or_poll_async(req_id, result);
}
static int KYTY_SYSV_ABI NpAuthGetAuthorizationCodeV3(int req_id, const void* param,
void* auth_code, int* issuer_id) {
static int KYTY_SYSV_ABI NpAuthGetAuthorizationCodeV3(
int req_id, const NpAuthGetAuthorizationCodeParameterV3* param, void* auth_code,
int* issuer_id) {
PRINT_NAME();
LOGF("\t req_id = %d\n", req_id);
@@ -1865,9 +1890,17 @@ static int KYTY_SYSV_ABI NpAuthGetAuthorizationCodeV3(int req_id, const void* pa
LOGF("\t auth_code = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(auth_code));
LOGF("\t issuer_id = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(issuer_id));
if (auth_code != nullptr) {
memset(auth_code, 0, 136);
if (req_id <= 0 || param == nullptr || auth_code == nullptr) {
return NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (param->size < sizeof(*param)) {
return NP_AUTH_ERROR_INVALID_SIZE;
}
if (param->client_id == nullptr || param->scope == nullptr) {
return NP_AUTH_ERROR_INVALID_ARGUMENT;
}
memset(auth_code, 0, 136);
if (issuer_id != nullptr) {
*issuer_id = 0;
}
@@ -1877,46 +1910,35 @@ static int KYTY_SYSV_ABI NpAuthGetAuthorizationCodeV3(int req_id, const void* pa
if (request == nullptr) {
return NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
if (request->state == NpAuthRequestState::Aborted) {
// request->result = 0;
// return 0;
request->result = NP_AUTH_ERROR_ABORTED;
return NP_AUTH_ERROR_ABORTED;
}
// request->state = NpAuthRequestState::Complete;
// request->result = 0;
// return 0;
return np_auth_complete_signed_out(request);
return np_auth_complete_unavailable(request, param->user_id);
}
static int KYTY_SYSV_ABI NpAuthGetIdTokenV3(int req_id, const void* param, void* id_token) {
static int KYTY_SYSV_ABI NpAuthGetIdTokenV3(int req_id, const NpAuthGetIdTokenParameterV3* param,
void* id_token) {
PRINT_NAME();
LOGF("\t req_id = %d\n", req_id);
LOGF("\t param = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(param));
LOGF("\t id_token = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(id_token));
if (id_token != nullptr) {
memset(id_token, 0, 4104);
if (req_id <= 0 || param == nullptr || id_token == nullptr) {
return NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (param->size < sizeof(*param)) {
return NP_AUTH_ERROR_INVALID_SIZE;
}
if (param->client_id == nullptr || param->client_secret == nullptr || param->scope == nullptr) {
return NP_AUTH_ERROR_INVALID_ARGUMENT;
}
memset(id_token, 0, 4104);
std::scoped_lock lock(g_np_auth_request_mutex);
auto* request = np_auth_find_request(req_id);
if (request == nullptr) {
return NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
if (request->state == NpAuthRequestState::Aborted) {
// request->result = 0;
// return 0;
request->result = NP_AUTH_ERROR_ABORTED;
return NP_AUTH_ERROR_ABORTED;
}
// request->state = NpAuthRequestState::Complete;
// request->result = 0;
// return 0;
return np_auth_complete_signed_out(request);
return np_auth_complete_unavailable(request, param->user_id);
}
LIB_DEFINE(InitNet_1_NpAuth) {
+70 -46
View File
@@ -4002,6 +4002,11 @@ struct NpCountryCode {
char padding[1];
};
struct NpLanguageCode {
char code[36];
char padding[12];
};
struct NpAgeRestriction {
NpCountryCode country_code;
int8_t age;
@@ -4056,8 +4061,23 @@ constexpr int np_error_aborted = -2141913070; /* 0x8055
constexpr int np_error_request_max = -2141913069; /* 0x80550013 */
constexpr int np_error_request_not_found = -2141913068; /* 0x80550014 */
constexpr int np_error_invalid_id = -2141913067; /* 0x80550015 */
constexpr int np_error_network_not_available = -2141872086; /* 0x8055a02a */
constexpr int np_request_max = 128;
constexpr uint32_t np_state_signed_out = 1;
constexpr uint32_t np_state_signed_in = 2;
// A simulated account belongs only to the primary user and survives network loss.
bool IsSignedIn(int user_id) {
return Config::PsnEmulationEnabled() && user_id == Config::GetUserId();
}
static int np_reachability_result(int user_id) {
if (!IsSignedIn(user_id)) {
return np_error_signed_out;
}
// This is an explicit compatibility simulation, not a connection to PSN.
return NetCtl::NetCtlConnected() ? OK : np_error_network_not_available;
}
struct NpStateCallback {
NpStateCallbackA callback = nullptr;
@@ -4117,7 +4137,7 @@ static NpRequest* np_get_request_locked(int req_id) {
return &g_np_requests[index];
}
static int np_complete_signed_out_locked(NpRequest* request) {
static int np_complete_locked(NpRequest* request, int result) {
if (request->state == NpRequestState::Complete) {
request->result = np_error_invalid_argument;
return np_error_invalid_argument;
@@ -4128,9 +4148,9 @@ static int np_complete_signed_out_locked(NpRequest* request) {
}
request->state = NpRequestState::Complete;
request->result = np_error_signed_out;
request->result = result;
return (request->async ? OK : np_error_signed_out);
return (request->async ? OK : result);
}
int KYTY_SYSV_ABI NpCheckCallback() {
@@ -4144,7 +4164,9 @@ int KYTY_SYSV_ABI NpCheckCallback() {
}
}
for (const auto& entry: pending) {
entry.callback(Config::GetUserId(), np_state_signed_out, entry.userdata);
entry.callback(Config::GetUserId(),
IsSignedIn(Config::GetUserId()) ? np_state_signed_in : np_state_signed_out,
entry.userdata);
}
return OK;
@@ -4239,12 +4261,13 @@ int KYTY_SYSV_ABI NpGetNpId(int user_id, NpId* np_id) {
EXIT_NOT_IMPLEMENTED(np_id == nullptr);
// int s = snprintf(np_id->handle.data, 16, "Kyty");
// EXIT_NOT_IMPLEMENTED(s >= 16);
// np_id->handle.term = 0;
std::memset(np_id, 0, sizeof(*np_id));
// return OK;
if (IsSignedIn(user_id)) {
std::memcpy(np_id->handle.data, "Kyty", 4);
return OK;
}
return np_error_signed_out;
}
@@ -4255,12 +4278,13 @@ int KYTY_SYSV_ABI NpGetOnlineId(int user_id, NpOnlineId* online_id) {
EXIT_NOT_IMPLEMENTED(online_id == nullptr);
// int s = snprintf(online_id->data, 16, "Kyty");
// EXIT_NOT_IMPLEMENTED(s >= 16);
// online_id->term = 0;
std::memset(online_id, 0, sizeof(*online_id));
// return OK;
if (IsSignedIn(user_id)) {
std::memcpy(online_id->data, "Kyty", 4);
return OK;
}
return np_error_signed_out;
}
@@ -4271,10 +4295,13 @@ int KYTY_SYSV_ABI NpGetAccountIdA(int user_id, uint64_t* account_id) {
EXIT_NOT_IMPLEMENTED(account_id == nullptr);
// *account_id = 0x00000000feedfaceull;
*account_id = 0;
// return OK;
if (IsSignedIn(user_id)) {
*account_id = 0x00000000feedfaceull;
return OK;
}
return np_error_signed_out;
}
@@ -4287,10 +4314,12 @@ int KYTY_SYSV_ABI NpGetAccountCountryA(int user_id, void* country_code) {
auto* code = static_cast<NpCountryCode*>(country_code);
std::memset(code, 0, sizeof(*code));
// code->data[0] = '';
// code->data[1] = 'S';
// return OK;
if (IsSignedIn(user_id)) {
std::memcpy(code->data, "US", 2);
return OK;
}
return np_error_signed_out;
}
@@ -4305,11 +4334,14 @@ int KYTY_SYSV_ABI NpGetAccountAge(int req_id, int user_id, uint8_t* age) {
return np_error_invalid_argument;
}
// *age = 21;
*age = 0;
std::lock_guard lock(g_np_request_mutex);
auto* request = np_get_request_locked(req_id);
if (request == nullptr) {
return np_error_request_not_found;
}
// return OK;
return np_error_signed_out;
*age = IsSignedIn(user_id) ? 21 : 0;
return np_complete_locked(request, IsSignedIn(user_id) ? OK : np_error_signed_out);
}
int KYTY_SYSV_ABI NpGetAccountLanguage2(int req_id, int user_id, void* language_code) {
@@ -4325,11 +4357,17 @@ int KYTY_SYSV_ABI NpGetAccountLanguage2(int req_id, int user_id, void* language_
std::lock_guard lock(g_np_request_mutex);
if (np_get_request_locked(req_id) == nullptr) {
auto* request = np_get_request_locked(req_id);
if (request == nullptr) {
return np_error_request_not_found;
}
return np_error_signed_out;
auto* code = static_cast<NpLanguageCode*>(language_code);
std::memset(code, 0, sizeof(*code));
if (IsSignedIn(user_id)) {
std::memcpy(code->code, "en-US", 5);
}
return np_complete_locked(request, IsSignedIn(user_id) ? OK : np_error_signed_out);
}
int KYTY_SYSV_ABI NpCreateRequest() {
@@ -4428,8 +4466,9 @@ int KYTY_SYSV_ABI NpCheckNpAvailability(int req_id, const char* user, void* resu
return np_error_request_not_found;
}
// return OK;
return np_complete_signed_out_locked(request);
return np_complete_locked(request, std::strcmp(user, "Kyty") == 0
? np_reachability_result(Config::GetUserId())
: np_error_signed_out);
}
int KYTY_SYSV_ABI NpCheckNpReachability(int req_id, int user_id) {
@@ -4451,8 +4490,7 @@ int KYTY_SYSV_ABI NpCheckNpReachability(int req_id, int user_id) {
return np_error_request_not_found;
}
// return OK;
return np_complete_signed_out_locked(request);
return np_complete_locked(request, np_reachability_result(user_id));
}
int KYTY_SYSV_ABI NpPollAsync(int req_id, int* result) {
@@ -4509,23 +4547,10 @@ int KYTY_SYSV_ABI NpCheckPremium(int req_id, const NpCheckPremiumParameter* para
return np_error_request_not_found;
}
if (request->state == NpRequestState::Complete) {
request->result = np_error_invalid_argument;
return np_error_invalid_argument;
}
if (request->state == NpRequestState::Aborted) {
return np_error_aborted;
}
std::memset(result, 0, sizeof(*result));
// result->authorized = true;
// request->state = NpRequestState::Complete;
// request->result = OK;
// return OK;
return np_complete_signed_out_locked(request);
// The simulated account has no premium eligibility.
return np_complete_locked(request, np_reachability_result(param->user_id));
}
int KYTY_SYSV_ABI NpGetState(int user_id, uint32_t* state) {
@@ -4535,7 +4560,7 @@ int KYTY_SYSV_ABI NpGetState(int user_id, uint32_t* state) {
LOGF("\t user_id = %d\n", user_id);
*state = np_state_signed_out;
*state = IsSignedIn(user_id) ? np_state_signed_in : np_state_signed_out;
return OK;
}
@@ -4549,8 +4574,7 @@ int KYTY_SYSV_ABI NpGetNpReachabilityState(int user_id, uint32_t* state) {
LOGF("\t user_id = %d\n", user_id);
// *state = 2; // SCE_NP_REACHABILITY_STATE_REACHABLE
*state = 0; // SCE_NP_REACHABILITY_STATE_UNAVAILABLE
*state = np_reachability_result(user_id) == OK ? 2 : 0; // REACHABLE / UNAVAILABLE
return OK;
}
@@ -4564,7 +4588,7 @@ int KYTY_SYSV_ABI NpHasSignedUp(int user_id, bool* has_signed_up) {
LOGF("\t user_id = %d\n", user_id);
*has_signed_up = false;
*has_signed_up = IsSignedIn(user_id);
return OK;
}
+2
View File
@@ -182,6 +182,8 @@ struct NpCheckPremiumResult;
using NpStateCallbackA = KYTY_SYSV_ABI void (*)(int user_id, uint32_t state, void* userdata);
bool IsSignedIn(int user_id);
int KYTY_SYSV_ABI NpCheckCallback();
int KYTY_SYSV_ABI NpSetNpTitleId(const NpTitleId* title_id, const NpTitleSecret* title_secret);
int KYTY_SYSV_ABI NpSetContentRestriction(const NpContentRestriction* restriction);
+2
View File
@@ -81,6 +81,7 @@ static void PrintUsage() {
::printf(" --console-language <0-29> Console language. Default: 1 (English US).\n");
::printf(" --vulkan-validation Enable Vulkan validation.\n");
::printf(" --network-connected Use the host local network.\n");
::printf(" --emulate-psn Simulate PSN sign-in for compatibility.\n");
::printf(" --gpu-assisted-validation Bounds-check shader accesses on the GPU.\n"
" Implies --vulkan-validation; very slow.\n");
::printf(" --shader-validation Enable shader validation.\n");
@@ -209,6 +210,7 @@ static constexpr Flag FLAGS[] = {
{"--trinity", &ConfigOptions::pro_enabled},
{"--amd-cpu", &ConfigOptions::amd_cpu_enabled},
{"--network-connected", &ConfigOptions::network_connected},
{"--emulate-psn", &ConfigOptions::psn_emulation_enabled},
{"--vulkan-validation", &ConfigOptions::vulkan_validation_enabled},
{"--gpu-assisted-validation", &ConfigOptions::gpu_assisted_validation_enabled},
{"--shader-validation", &ConfigOptions::shader_validation_enabled},