From ccddd6fb945213ec16e3344f9d9b7c469b6ead6e Mon Sep 17 00:00:00 2001 From: GodsWill Dev <56268269+Techx3@users.noreply.github.com> Date: Fri, 28 Aug 2026 20:27:49 -0400 Subject: [PATCH] user: make the local profile configurable (#347) user: make the local user ID configurable Co-authored-by: nmzik --- src/common/emulatorConfig.cpp | 5 ++ src/common/emulatorConfig.h | 9 +++ .../forms/configuration_edit_dialog.ui | 59 ++++++++++++------- src/launcher/include/configuration.h | 11 +++- src/launcher/src/configurationEditDialog.cpp | 7 +++ src/launcher/src/mainDialog.cpp | 1 + src/libs/controller.cpp | 4 +- src/libs/dialog.cpp | 10 ++-- src/libs/libUserService.cpp | 26 ++++---- src/main.cpp | 18 ++++++ 10 files changed, 108 insertions(+), 42 deletions(-) diff --git a/src/common/emulatorConfig.cpp b/src/common/emulatorConfig.cpp index 5f3995e4c..0a716e9b2 100644 --- a/src/common/emulatorConfig.cpp +++ b/src/common/emulatorConfig.cpp @@ -22,6 +22,7 @@ void Shutdown() { void Load(const ConfigOptions& cfg) { EXIT_IF(g_config == nullptr); EXIT_IF(cfg.user_name.empty() || cfg.user_name.size() > MAX_USER_NAME_LENGTH); + EXIT_IF(!IsConfiguredUserIdValid(cfg.user_id)); *g_config = cfg; } @@ -38,6 +39,10 @@ const std::string& GetUserName() { return g_config->user_name; } +int32_t GetUserId() { + return g_config->user_id; +} + PresentMode GetPresentMode() { return g_config->present_mode; } diff --git a/src/common/emulatorConfig.h b/src/common/emulatorConfig.h index 8586a5ee8..25a5fbb7b 100644 --- a/src/common/emulatorConfig.h +++ b/src/common/emulatorConfig.h @@ -34,11 +34,19 @@ using Keymap = std::vector; constexpr uint32_t DEFAULT_CONSOLE_LANGUAGE = 1; constexpr uint32_t MAX_CONSOLE_LANGUAGE = 29; constexpr std::size_t MAX_USER_NAME_LENGTH = 16; +constexpr int32_t DEFAULT_USER_ID = 1000; + +constexpr bool IsConfiguredUserIdValid(int32_t user_id) { + constexpr int32_t USER_ID_EVERYONE = 0xfe; + constexpr int32_t USER_ID_SYSTEM = 0xff; + return user_id >= 0 && user_id != USER_ID_EVERYONE && user_id != USER_ID_SYSTEM; +} struct ConfigOptions { uint32_t screen_width = 1280; uint32_t screen_height = 720; std::string user_name = "Kyty"; + int32_t user_id = DEFAULT_USER_ID; PresentMode present_mode = PresentMode::Fifo; bool fullscreen_enabled = false; uint32_t vblank_frequency = 60; @@ -70,6 +78,7 @@ void Load(const ConfigOptions& cfg); uint32_t GetScreenWidth(); uint32_t GetScreenHeight(); const std::string& GetUserName(); +int32_t GetUserId(); PresentMode GetPresentMode(); bool FullscreenEnabled(); uint32_t GetVblankFrequency(); diff --git a/src/launcher/forms/configuration_edit_dialog.ui b/src/launcher/forms/configuration_edit_dialog.ui index fc464417f..a6b381967 100644 --- a/src/launcher/forms/configuration_edit_dialog.ui +++ b/src/launcher/forms/configuration_edit_dialog.ui @@ -119,41 +119,58 @@ + + + User ID: + + + + + + + Local user ID exposed to games + + + 2147483647 + + + + Screen resolution: - + Window resolution - + Present mode: - + Vulkan swapchain presentation mode - + Vblank frequency: - + Virtual display refresh rate used for frame pacing @@ -169,56 +186,56 @@ - + Console language: - + Language - + Shader optimization type: - + Optimize shaders for code size or performance - + Shader log direction: - + Dump shaders to file or console window. If enabled may decrease emulator performance - + Shader log folder: - + Specify directory to dump shaders @@ -228,14 +245,14 @@ - + Command buffer dump folder: - + Specify directory to dump command buffers @@ -245,28 +262,28 @@ - + Printf direction: - + Print logs to file or console window. If enabled may decrease emulator performance - + Printf output file: - + Specify file to dump logs @@ -276,14 +293,14 @@ - + Profiler direction: - + Enable or disable profiler. If enabled may decrease emulator performance diff --git a/src/launcher/include/configuration.h b/src/launcher/include/configuration.h index b656f3e3d..7f5e667fb 100644 --- a/src/launcher/include/configuration.h +++ b/src/launcher/include/configuration.h @@ -2,6 +2,7 @@ #define LAUNCHER_INCLUDE_CONFIGURATION_H_ #include "common.h" +#include "common/emulatorConfig.h" #include #include @@ -91,6 +92,7 @@ public: Resolution screen_resolution = Resolution::R1280X720; QString user_name = "Kyty"; + int user_id = Config::DEFAULT_USER_ID; PresentMode present_mode = PresentMode::Fifo; bool fullscreen_enabled = false; int vblank_frequency = 60; @@ -116,6 +118,7 @@ public: void CopyEmulatorSettingsFrom(const Configuration& other) { screen_resolution = other.screen_resolution; user_name = other.user_name; + user_id = other.user_id; present_mode = other.present_mode; fullscreen_enabled = other.fullscreen_enabled; vblank_frequency = other.vblank_frequency; @@ -158,6 +161,7 @@ public: KYTY_CFG_SET(custom_settings); KYTY_CFG_SET(screen_resolution); KYTY_CFG_SET(user_name); + KYTY_CFG_SET(user_id); KYTY_CFG_SET(present_mode); KYTY_CFG_SET(fullscreen_enabled); KYTY_CFG_SET(vblank_frequency); @@ -186,7 +190,12 @@ public: KYTY_CFG_GET(game_path); KYTY_CFG_GET(custom_settings); KYTY_CFG_GET(screen_resolution); - user_name = s->value("user_name", user_name).toString(); + user_name = s->value("user_name", user_name).toString(); + bool user_id_ok = false; + auto saved_user_id = s->value("user_id", user_id).toInt(&user_id_ok); + user_id = user_id_ok && Config::IsConfiguredUserIdValid(saved_user_id) + ? saved_user_id + : Config::DEFAULT_USER_ID; KYTY_CFG_GET(present_mode); if (EnumToText(present_mode).isEmpty()) { present_mode = PresentMode::Fifo; diff --git a/src/launcher/src/configurationEditDialog.cpp b/src/launcher/src/configurationEditDialog.cpp index 80b8d1ac1..fc9d6d63f 100644 --- a/src/launcher/src/configurationEditDialog.cpp +++ b/src/launcher/src/configurationEditDialog.cpp @@ -156,6 +156,7 @@ static void ListInit(QComboBox* combo, T value) { void ConfigurationEditDialog::Init(const Configuration& info) { m_ui->lineEdit_user_name->setMaxLength(static_cast(Config::MAX_USER_NAME_LENGTH)); m_ui->lineEdit_user_name->setText(info.user_name); + m_ui->spinBox_user_id->setValue(info.user_id); ListInit(m_ui->comboBox_screen_resolution, info.screen_resolution); ListInit(m_ui->comboBox_present_mode, info.present_mode); m_ui->checkBox_fullscreen->setChecked(info.fullscreen_enabled); @@ -289,6 +290,7 @@ void ConfigurationEditDialog::moveEvent(QMoveEvent* event) { static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) { info.user_name = ui.lineEdit_user_name->text().trimmed(); + info.user_id = ui.spinBox_user_id->value(); info.screen_resolution = TextToEnum(ui.comboBox_screen_resolution->currentText()); info.present_mode = @@ -337,6 +339,11 @@ void ConfigurationEditDialog::save() { return; } m_ui->lineEdit_user_name->setText(user_name); + if (!Config::IsConfiguredUserIdValid(m_ui->spinBox_user_id->value())) { + QMessageBox::critical(this, tr("Save failed"), + tr("User ID cannot be 254 (everyone) or 255 (system)")); + return; + } update_info(); diff --git a/src/launcher/src/mainDialog.cpp b/src/launcher/src/mainDialog.cpp index 180ab133b..a9808a284 100644 --- a/src/launcher/src/mainDialog.cpp +++ b/src/launcher/src/mainDialog.cpp @@ -205,6 +205,7 @@ static QStringList CreateEmulatorArgs(const Configuration& info) { args << "--screen-width" << r.at(0); args << "--screen-height" << r.at(1); args << "--user-name" << info.user_name; + args << "--user-id" << QString::number(info.user_id); args << "--present-mode" << EnumToText(info.present_mode); if (info.fullscreen_enabled) { args << "--fullscreen"; diff --git a/src/libs/controller.cpp b/src/libs/controller.cpp index 4e570568d..cd019386b 100644 --- a/src/libs/controller.cpp +++ b/src/libs/controller.cpp @@ -2,6 +2,7 @@ #include "common/assert.h" #include "common/common.h" +#include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/stringUtils.h" #include "common/threads.h" @@ -358,13 +359,12 @@ int KYTY_SYSV_ABI PadInit() { } static bool PadOpenArgsAreValid(int user_id, int type, int index) { - constexpr int user_id_initial = 1000; constexpr int user_id_system = 0xff; constexpr int port_type_standard = 0; constexpr int port_type_special = 2; constexpr int port_type_remote = 16; const bool personal_port = - user_id == user_id_initial && (type == port_type_standard || type == port_type_special); + user_id == Config::GetUserId() && (type == port_type_standard || type == port_type_special); const bool system_remote_control = user_id == user_id_system && type == port_type_remote; return index == 0 && (personal_port || system_remote_control); } diff --git a/src/libs/dialog.cpp b/src/libs/dialog.cpp index 270ccca6e..39376c374 100644 --- a/src/libs/dialog.cpp +++ b/src/libs/dialog.cpp @@ -1,5 +1,6 @@ #include "libs/dialog.h" +#include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/stringUtils.h" #include "libs/errno.h" @@ -41,7 +42,6 @@ constexpr int LOGIN_RESULT_OK = 0; constexpr int LOGIN_MODE_ALL_USERS = 0; constexpr int LOGIN_MODE_NOT_LOGGED_IN = 1; constexpr int LOGIN_USER_INVALID = -1; -constexpr int LOGIN_USER_ID = 1000; constexpr int LOGIN_ERROR_NOT_INITIALIZED = static_cast(0x81340001u); constexpr int LOGIN_ERROR_ALREADY_INITIALIZED = static_cast(0x81340002u); @@ -67,7 +67,7 @@ static_assert(sizeof(LoginDialogResult) == 16); static_assert(sizeof(LoginDialogParam) == 64); static int g_login_status = LOGIN_STATUS_NONE; -static int g_login_selected_user = LOGIN_USER_ID; +static int g_login_selected_user = LOGIN_USER_INVALID; int KYTY_SYSV_ABI LoginDialogInitialize() { PRINT_NAME(); @@ -77,7 +77,7 @@ int KYTY_SYSV_ABI LoginDialogInitialize() { } g_login_status = LOGIN_STATUS_INITIALIZED; - g_login_selected_user = LOGIN_USER_ID; + g_login_selected_user = Config::GetUserId(); return OK; } @@ -90,7 +90,7 @@ int KYTY_SYSV_ABI LoginDialogTerminate() { } g_login_status = LOGIN_STATUS_NONE; - g_login_selected_user = LOGIN_USER_ID; + g_login_selected_user = LOGIN_USER_INVALID; return OK; } @@ -112,7 +112,7 @@ int KYTY_SYSV_ABI LoginDialogOpen(const void* param) { } g_login_selected_user = - p->initial_focus != LOGIN_USER_INVALID ? p->initial_focus : LOGIN_USER_ID; + p->initial_focus != LOGIN_USER_INVALID ? p->initial_focus : Config::GetUserId(); LOGF("\t size = %d\n" "\t mode = %d\n" diff --git a/src/libs/libUserService.cpp b/src/libs/libUserService.cpp index 4093ef170..e740f65a5 100644 --- a/src/libs/libUserService.cpp +++ b/src/libs/libUserService.cpp @@ -56,7 +56,7 @@ static KYTY_SYSV_ABI int UserServiceGetInitialUser(int* user_id) { EXIT_NOT_IMPLEMENTED(user_id == nullptr); - *user_id = 1000; + *user_id = Config::GetUserId(); return OK; } @@ -71,7 +71,7 @@ static KYTY_SYSV_ABI int UserServiceGetEvent(SceUserServiceEvent* event) { if (!logged_in) { logged_in = true; event->event_type = UserServiceEventTypeLogin; - event->user_id = 1000; + event->user_id = Config::GetUserId(); return OK; } @@ -83,7 +83,7 @@ static KYTY_SYSV_ABI int UserServiceGetLoginUserIdList(UserServiceLoginUserIdLis EXIT_NOT_IMPLEMENTED(user_id_list == nullptr); - user_id_list->user_id[0] = 1000; + user_id_list->user_id[0] = Config::GetUserId(); user_id_list->user_id[1] = -1; user_id_list->user_id[2] = -1; user_id_list->user_id[3] = -1; @@ -92,7 +92,7 @@ static KYTY_SYSV_ABI int UserServiceGetLoginUserIdList(UserServiceLoginUserIdLis } static KYTY_SYSV_ABI int UserServiceGetUserName(int user_id, char* name, size_t size) { - EXIT_NOT_IMPLEMENTED(user_id != 1000); + EXIT_NOT_IMPLEMENTED(user_id != Config::GetUserId()); EXIT_NOT_IMPLEMENTED(size < 5); const auto& user_name = Config::GetUserName(); @@ -109,7 +109,7 @@ static KYTY_SYSV_ABI int UserServiceGetUserNumber(int user_id, int32_t* number) if (number == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -124,7 +124,7 @@ static KYTY_SYSV_ABI int UserServiceGetGamePresets(int user_id, UserServiceGameP if (presets == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -145,7 +145,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityVibration(int user_id, int32 if (vibration == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -161,7 +161,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityTriggerEffect(int user_ if (trigger_effect == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -176,7 +176,7 @@ static KYTY_SYSV_ABI int UserServiceGetAgeLevel(int user_id, uint32_t* age_level if (age_level == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -192,7 +192,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityChatTranscription(int u if (chat_transcription == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -208,7 +208,7 @@ UserServiceGetAccessibilityPressAndHoldDelay(int user_id, int32_t* press_and_hol if (press_and_hold_delay == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -224,7 +224,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityZoomEnabled(int user_id if (zoom_enabled == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } @@ -240,7 +240,7 @@ static KYTY_SYSV_ABI int UserServiceGetAccessibilityZoomFollowFocus(int use if (zoom_follow_focus == nullptr) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } - if (user_id != 1000) { + if (user_id != Config::GetUserId()) { return USER_SERVICE_ERROR_NOT_LOGGED_IN; } diff --git a/src/main.cpp b/src/main.cpp index 6983f8c30..f6c0d2f55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,6 +49,8 @@ static void PrintUsage() { ::printf(" --screen-height Window height. Default: 720.\n"); ::printf( " --user-name Local user name (1-16 bytes). Default: Kyty.\n"); + ::printf(" --user-id Local user ID. Default: %d.\n", + Config::DEFAULT_USER_ID); ::printf(" --present-mode Fifo, Mailbox, or Immediate. Default: Fifo.\n"); ::printf(" --fullscreen Run in borderless desktop fullscreen.\n"); ::printf(" --vblank-frequency Virtual vblank frequency. Default: 60.\n"); @@ -125,6 +127,17 @@ static bool ParseConsoleLanguage(const std::string& value, uint32_t& out) { return true; } +static bool ParseUserId(const std::string& value, int32_t& out) { + int32_t user_id = 0; + auto [end, error] = std::from_chars(value.data(), value.data() + value.size(), user_id); + if (error != std::errc {} || end != value.data() + value.size() || + !Config::IsConfiguredUserIdValid(user_id)) { + return false; + } + out = user_id; + return true; +} + static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_help) { show_help = false; @@ -211,6 +224,11 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he return false; } options.config.user_name = value; + } else if (arg == "--user-id") { + if (!ParseUserId(value, options.config.user_id)) { + ::printf("invalid user ID: %s\n", value.c_str()); + return false; + } } else if (arg == "--present-mode") { if (!ParseEnum(value, options.config.present_mode)) { ::printf("invalid present mode: %s\n", value.c_str());