diff --git a/.gitmodules b/.gitmodules index 25b5d307b..716d3fb95 100644 --- a/.gitmodules +++ b/.gitmodules @@ -106,3 +106,6 @@ [submodule "externals/libusb"] path = externals/libusb url = https://github.com/libusb/libusb-cmake.git +[submodule "externals/json"] + path = externals/json + url = https://github.com/nlohmann/json.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 38532760d..a64fbadc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -801,6 +801,8 @@ set(CORE src/core/aerolib/stubs.cpp src/core/thread.h src/core/tls.cpp src/core/tls.h + src/core/user_account.cpp + src/core/user_account.h ) if (ARCHITECTURE STREQUAL "x86_64") @@ -1069,6 +1071,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/gui_settings.h src/qt_gui/settings.cpp src/qt_gui/settings.h + src/qt_gui/user_management_dialog.cpp + src/qt_gui/user_management_dialog.h src/qt_gui/sdl_event_wrapper.cpp src/qt_gui/sdl_event_wrapper.h ${EMULATOR} @@ -1112,7 +1116,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb nlohmann_json::nlohmann_json) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/REUSE.toml b/REUSE.toml index 4b1c94d21..ce4fc50a1 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -57,6 +57,7 @@ path = [ "src/images/refreshlist_icon.png", "src/images/settings_icon.png", "src/images/fullscreen_icon.png", + "src/images/users_icon.png", "src/images/stop_icon.png", "src/images/utils_icon.png", "src/images/shadPS4.icns", diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 89b0fbfdd..a283769b5 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -230,3 +230,7 @@ if (APPLE) add_subdirectory(MoltenVK) endif() endif() + +#nlohmann json +set(JSON_BuildTests OFF CACHE INTERNAL "") +add_subdirectory(json) \ No newline at end of file diff --git a/externals/json b/externals/json new file mode 160000 index 000000000..568b708fd --- /dev/null +++ b/externals/json @@ -0,0 +1 @@ +Subproject commit 568b708fd46deeb23a959381393a7564f1586588 diff --git a/src/common/config.cpp b/src/common/config.cpp index 4a764a4c6..15e33b689 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -75,6 +75,15 @@ static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; static std::string trophyKey; static bool isPSNSignedIn = false; +std::string userid{"00000001"}; +std::string selectedUserid{"00000001"}; + +std::string getDefaultUserId() { + return userid; +} +std::string getActiveUserId() { + return selectedUserid; +} // Gui static bool load_game_size = true; diff --git a/src/common/config.h b/src/common/config.h index 931fa68e2..fd1e7ca7c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -133,5 +133,7 @@ void setDefaultValues(); // todo: name and function location pending std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = ""); +std::string getDefaultUserId(); +std::string getActiveUserId(); -}; // namespace Config +}; // namespace Config \ No newline at end of file diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 3270c24dd..383e15b61 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -137,6 +137,7 @@ static auto UserPaths = [] { create_path(PathType::PatchesDir, user_dir / PATCHES_DIR); create_path(PathType::MetaDataDir, user_dir / METADATA_DIR); create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY); + create_path(PathType::HomeDir, user_dir / HOME_DIR); std::ofstream notice_file(user_dir / CUSTOM_TROPHY / "Notice.txt"); if (notice_file.is_open()) { diff --git a/src/common/path_util.h b/src/common/path_util.h index b8053a229..8418a74f5 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -27,6 +27,7 @@ enum class PathType { PatchesDir, // Where patches are stored. MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored. CustomTrophy, // Where custom files for trophies are stored. + HomeDir, // PS4 home directory }; constexpr auto PORTABLE_DIR = "user"; @@ -44,6 +45,7 @@ constexpr auto CHEATS_DIR = "cheats"; constexpr auto PATCHES_DIR = "patches"; constexpr auto METADATA_DIR = "game_data"; constexpr auto CUSTOM_TROPHY = "custom_trophy"; +constexpr auto HOME_DIR = "home"; // Filenames constexpr auto LOG_FILE = "shad_log.txt"; diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 646c28949..332386be2 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -29,10 +29,10 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(const s32 handle) { return AudioOut::sceAudioOutClose(handle); } -s32 PS4_SYSV_ABI -sceAudio3dAudioOutOpen(const OrbisAudio3dPortId port_id, const OrbisUserServiceUserId user_id, - s32 type, const s32 index, const u32 len, const u32 freq, - const AudioOut::OrbisAudioOutParamExtendedInformation param) { +s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen( + const OrbisAudio3dPortId port_id, const Libraries::UserService::OrbisUserServiceUserId user_id, + s32 type, const s32 index, const u32 len, const u32 freq, + const AudioOut::OrbisAudioOutParamExtendedInformation param) { LOG_INFO(Lib_Audio3d, "called, port_id = {}, user_id = {}, type = {}, index = {}, len = {}, freq = {}", port_id, user_id, type, index, len, freq); @@ -421,7 +421,7 @@ s32 PS4_SYSV_ABI sceAudio3dPortGetStatus() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAudio3dPortOpen(const OrbisUserServiceUserId user_id, +s32 PS4_SYSV_ABI sceAudio3dPortOpen(const Libraries::UserService::OrbisUserServiceUserId user_id, const OrbisAudio3dOpenParameters* parameters, OrbisAudio3dPortId* port_id) { LOG_INFO(Lib_Audio3d, "called, user_id = {}, parameters = {}, id = {}", user_id, diff --git a/src/core/libraries/audio3d/audio3d.h b/src/core/libraries/audio3d/audio3d.h index f4e9ada8a..23d22298e 100644 --- a/src/core/libraries/audio3d/audio3d.h +++ b/src/core/libraries/audio3d/audio3d.h @@ -15,8 +15,6 @@ class SymbolsResolver; namespace Libraries::Audio3d { -using OrbisUserServiceUserId = s32; - enum class OrbisAudio3dRate : u32 { ORBIS_AUDIO3D_RATE_48000 = 0, }; @@ -91,7 +89,8 @@ struct Audio3dState { }; s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle); -s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId port_id, OrbisUserServiceUserId user_id, +s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId port_id, + Libraries::UserService::OrbisUserServiceUserId user_id, s32 type, s32 index, u32 len, u32 freq, AudioOut::OrbisAudioOutParamExtendedInformation param); s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, void* ptr); @@ -127,7 +126,7 @@ s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId port_id, u32* qu u32* queue_available); s32 PS4_SYSV_ABI sceAudio3dPortGetState(); s32 PS4_SYSV_ABI sceAudio3dPortGetStatus(); -s32 PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId user_id, +s32 PS4_SYSV_ABI sceAudio3dPortOpen(Libraries::UserService::OrbisUserServiceUserId user_id, const OrbisAudio3dOpenParameters* parameters, OrbisAudio3dPortId* port_id); s32 PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId port_id, OrbisAudio3dBlocking blocking); diff --git a/src/core/libraries/game_live_streaming/gamelivestreaming.h b/src/core/libraries/game_live_streaming/gamelivestreaming.h index 468750fd1..08f26e554 100644 --- a/src/core/libraries/game_live_streaming/gamelivestreaming.h +++ b/src/core/libraries/game_live_streaming/gamelivestreaming.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Core::Loader { @@ -15,11 +16,11 @@ struct OrbisGameLiveStreamingStatus { bool isOnAir; u8 align[3]; u32 spectatorCounts; - s32 userId; + Libraries::UserService::OrbisUserServiceUserId userId; u8 reserved[60]; }; struct OrbisGameLiveStreamingStatus2 { - s32 userId; + Libraries::UserService::OrbisUserServiceUserId userId; bool isOnAir; u8 align[3]; u32 spectatorCounts; diff --git a/src/core/libraries/ime/error_dialog.cpp b/src/core/libraries/ime/error_dialog.cpp index 07580fe1d..a5a0223f3 100644 --- a/src/core/libraries/ime/error_dialog.cpp +++ b/src/core/libraries/ime/error_dialog.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" @@ -117,7 +118,7 @@ static ErrorDialogUi g_dialog_ui; struct Param { s32 size; s32 errorCode; - OrbisUserServiceUserId userId; + Libraries::UserService::OrbisUserServiceUserId userId; s32 _reserved; }; diff --git a/src/core/libraries/ime/error_dialog.h b/src/core/libraries/ime/error_dialog.h index 3e6651d4a..42b37da66 100644 --- a/src/core/libraries/ime/error_dialog.h +++ b/src/core/libraries/ime/error_dialog.h @@ -11,8 +11,6 @@ class SymbolsResolver; } namespace Libraries::ErrorDialog { -using OrbisUserServiceUserId = s32; - struct Param; CommonDialog::Error PS4_SYSV_ABI sceErrorDialogClose(); diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 1c61bc276..c14122bb0 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -247,7 +247,7 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* return ORBIS_OK; } -s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) { +s32 PS4_SYSV_ABI sceImeKeyboardClose(Libraries::UserService::OrbisUserServiceUserId userId) { LOG_INFO(Lib_Ime, "(STUBBED) called"); if (!g_keyboard_handler) { @@ -268,7 +268,8 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) { +s32 PS4_SYSV_ABI sceImeKeyboardOpen(Libraries::UserService::OrbisUserServiceUserId userId, + const OrbisImeKeyboardParam* param) { LOG_INFO(Lib_Ime, "called"); if (!param) { diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h index fcf381048..4a8c39c98 100644 --- a/src/core/libraries/ime/ime.h +++ b/src/core/libraries/ime/ime.h @@ -99,10 +99,11 @@ int PS4_SYSV_ABI sceImeFilterText(); int PS4_SYSV_ABI sceImeForTestFunction(); int PS4_SYSV_ABI sceImeGetPanelPositionAndForm(); s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height); -s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId); +s32 PS4_SYSV_ABI sceImeKeyboardClose(Libraries::UserService::OrbisUserServiceUserId userId); int PS4_SYSV_ABI sceImeKeyboardGetInfo(); int PS4_SYSV_ABI sceImeKeyboardGetResourceId(); -s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param); +s32 PS4_SYSV_ABI sceImeKeyboardOpen(Libraries::UserService::OrbisUserServiceUserId userId, + const OrbisImeKeyboardParam* param); int PS4_SYSV_ABI sceImeKeyboardOpenInternal(); int PS4_SYSV_ABI sceImeKeyboardSetMode(); int PS4_SYSV_ABI sceImeKeyboardUpdate(); diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h index 96f073dc5..227f8aeeb 100644 --- a/src/core/libraries/ime/ime_common.h +++ b/src/core/libraries/ime/ime_common.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" #include "core/libraries/rtc/rtc.h" @@ -141,7 +142,7 @@ struct OrbisImeKeycode { }; struct OrbisImeKeyboardResourceIdArray { - s32 userId; + Libraries::UserService::OrbisUserServiceUserId userId; u32 resourceId[5]; }; diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index bc920b5a9..54a33fc52 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -924,7 +924,7 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id, +int PS4_SYSV_ABI sceNpGetAccountCountryA(Libraries::UserService::OrbisUserServiceUserId user_id, OrbisNpCountryCode* country_code) { LOG_INFO(Lib_NpManager, "(STUBBED) called, user_id = {}", user_id); if (country_code == nullptr) { @@ -955,7 +955,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id) return SIGNEDIN_STATUS; } -int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) { +int 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; @@ -989,7 +990,8 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) { +int 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; @@ -1004,7 +1006,8 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id) { +int 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; @@ -1024,7 +1027,8 @@ int PS4_SYSV_ABI sceNpGetParentalControlInfoA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state) { +int PS4_SYSV_ABI sceNpGetState(Libraries::UserService::OrbisUserServiceUserId user_id, + OrbisNpState* state) { if (state == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } @@ -1043,7 +1047,8 @@ int PS4_SYSV_ABI sceNpGetUserIdByOnlineId() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up) { +int 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; diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 1078a9f3e..a3f12cac4 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Core::Loader { @@ -18,8 +19,6 @@ using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(s32 userId, Orbis constexpr int ORBIS_NP_ONLINEID_MAX_LENGTH = 16; -using OrbisUserServiceUserId = s32; - struct OrbisNpOnlineId { char data[ORBIS_NP_ONLINEID_MAX_LENGTH]; char term; @@ -221,26 +220,31 @@ int PS4_SYSV_ABI sceNpCreateRequest(); int PS4_SYSV_ABI sceNpDeleteRequest(int reqId); int PS4_SYSV_ABI sceNpGetAccountAge(); int PS4_SYSV_ABI sceNpGetAccountCountry(); -int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id, +int PS4_SYSV_ABI sceNpGetAccountCountryA(Libraries::UserService::OrbisUserServiceUserId user_id, OrbisNpCountryCode* country_code); int PS4_SYSV_ABI sceNpGetAccountDateOfBirth(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(); int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id); -int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id); +int PS4_SYSV_ABI sceNpGetAccountIdA(Libraries::UserService::OrbisUserServiceUserId user_id, + u64* account_id); int PS4_SYSV_ABI sceNpGetAccountLanguage(); int PS4_SYSV_ABI sceNpGetAccountLanguage2(); int PS4_SYSV_ABI sceNpGetAccountLanguageA(); int PS4_SYSV_ABI sceNpGetGamePresenceStatus(); int PS4_SYSV_ABI sceNpGetGamePresenceStatusA(); -int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id); +int PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId user_id, + OrbisNpId* np_id); int PS4_SYSV_ABI sceNpGetNpReachabilityState(); -int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id); +int PS4_SYSV_ABI sceNpGetOnlineId(Libraries::UserService::OrbisUserServiceUserId user_id, + OrbisNpOnlineId* online_id); int PS4_SYSV_ABI sceNpGetParentalControlInfo(); int PS4_SYSV_ABI sceNpGetParentalControlInfoA(); -int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state); +int PS4_SYSV_ABI sceNpGetState(Libraries::UserService::OrbisUserServiceUserId user_id, + OrbisNpState* state); int PS4_SYSV_ABI sceNpGetUserIdByAccountId(); int PS4_SYSV_ABI sceNpGetUserIdByOnlineId(); -int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up); +int PS4_SYSV_ABI sceNpHasSignedUp(Libraries::UserService::OrbisUserServiceUserId user_id, + bool* has_signed_up); int PS4_SYSV_ABI sceNpIdMapperAbortRequest(); int PS4_SYSV_ABI sceNpIdMapperAccountIdToNpId(); int PS4_SYSV_ABI sceNpIdMapperAccountIdToOnlineId(); diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index c0642f81c..cd0611f6b 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "common/logging/log.h" #include "common/path_util.h" #include "common/slot_vector.h" @@ -147,7 +148,8 @@ int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t user_id, +s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, + Libraries::UserService::OrbisUserServiceUserId userId, uint32_t service_label, uint64_t options) { ASSERT(options == 0ull); if (!context) { @@ -158,16 +160,16 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t return ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX; } - const auto& key = ContextKey{user_id, service_label}; + const auto& key = ContextKey{userId, service_label}; if (contexts_internal.contains(key)) { return ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS; } - const auto ctx_id = trophy_contexts.insert(user_id, service_label); + const auto ctx_id = trophy_contexts.insert(userId, service_label); *context = ctx_id.index + 1; contexts_internal[key].context_id = *context; - LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", *context, user_id, + LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", *context, userId, service_label); return ORBIS_OK; diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 59964fa58..3fd03ad4e 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/config.h" #include "common/logging/log.h" #include "common/singleton.h" @@ -156,7 +157,8 @@ int PS4_SYSV_ABI scePadGetFeatureReport() { return ORBIS_OK; } -int PS4_SYSV_ABI scePadGetHandle(s32 userId, s32 type, s32 index) { +int PS4_SYSV_ABI scePadGetHandle(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index) { if (userId == -1) { return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE; } @@ -249,7 +251,8 @@ int PS4_SYSV_ABI scePadMbusTerm() { return ORBIS_OK; } -int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam) { +int PS4_SYSV_ABI scePadOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, const OrbisPadOpenParam* pParam) { if (userId == -1) { return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE; } @@ -265,8 +268,8 @@ int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenP return 1; // dummy } -int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index, - const OrbisPadOpenExtParam* pParam) { +int PS4_SYSV_ABI scePadOpenExt(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, const OrbisPadOpenExtParam* pParam) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); if (Config::getUseSpecialPad()) { if (type != ORBIS_PAD_PORT_TYPE_SPECIAL) diff --git a/src/core/libraries/pad/pad.h b/src/core/libraries/pad/pad.h index 68943b460..c24a78b93 100644 --- a/src/core/libraries/pad/pad.h +++ b/src/core/libraries/pad/pad.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/enum.h" #include "common/types.h" @@ -276,7 +277,8 @@ int PS4_SYSV_ABI scePadGetExtControllerInformation(s32 handle, OrbisPadExtendedControllerInformation* pInfo); int PS4_SYSV_ABI scePadGetExtensionUnitInfo(); int PS4_SYSV_ABI scePadGetFeatureReport(); -int PS4_SYSV_ABI scePadGetHandle(s32 userId, s32 type, s32 index); +int PS4_SYSV_ABI scePadGetHandle(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index); int PS4_SYSV_ABI scePadGetIdleCount(); int PS4_SYSV_ABI scePadGetInfo(); int PS4_SYSV_ABI scePadGetInfoByPortType(); @@ -294,8 +296,10 @@ int PS4_SYSV_ABI scePadIsMoveReproductionModel(); int PS4_SYSV_ABI scePadIsValidHandle(); int PS4_SYSV_ABI scePadMbusInit(); int PS4_SYSV_ABI scePadMbusTerm(); -int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam); -int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index, const OrbisPadOpenExtParam* pParam); +int PS4_SYSV_ABI scePadOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, const OrbisPadOpenParam* pParam); +int PS4_SYSV_ABI scePadOpenExt(Libraries::UserService::OrbisUserServiceUserId userId, s32 type, + s32 index, const OrbisPadOpenExtParam* pParam); int PS4_SYSV_ABI scePadOpenExt2(); int PS4_SYSV_ABI scePadOutputReport(); int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num); diff --git a/src/core/libraries/remote_play/remoteplay.cpp b/src/core/libraries/remote_play/remoteplay.cpp index b7402173e..14ede107a 100644 --- a/src/core/libraries/remote_play/remoteplay.cpp +++ b/src/core/libraries/remote_play/remoteplay.cpp @@ -3,6 +3,7 @@ #include "remoteplay.h" +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" @@ -54,7 +55,8 @@ int PS4_SYSV_ABI sceRemoteplayGetConnectHistory() { return ORBIS_OK; } -int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus) { +int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus( + Libraries::UserService::OrbisUserServiceUserId userId, int* pStatus) { *pStatus = ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT; return ORBIS_OK; } diff --git a/src/core/libraries/remote_play/remoteplay.h b/src/core/libraries/remote_play/remoteplay.h index 979f73a9e..159d6ab58 100644 --- a/src/core/libraries/remote_play/remoteplay.h +++ b/src/core/libraries/remote_play/remoteplay.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Core::Loader { @@ -24,7 +25,8 @@ int PS4_SYSV_ABI sceRemoteplayDisconnect(); int PS4_SYSV_ABI sceRemoteplayGeneratePinCode(); int PS4_SYSV_ABI sceRemoteplayGetApMode(); int PS4_SYSV_ABI sceRemoteplayGetConnectHistory(); -int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus); +int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus( + Libraries::UserService::OrbisUserServiceUserId userId, int* pStatus); int PS4_SYSV_ABI sceRemoteplayGetConnectUserId(); int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo(); int PS4_SYSV_ABI sceRemoteplayGetOperationStatus(); diff --git a/src/core/libraries/share_play/shareplay.h b/src/core/libraries/share_play/shareplay.h index 8b1ad5f47..d71cafb6d 100644 --- a/src/core/libraries/share_play/shareplay.h +++ b/src/core/libraries/share_play/shareplay.h @@ -21,8 +21,8 @@ struct OrbisSharePlayConnectionInfo { int mode; Libraries::NpManager::OrbisNpOnlineId hostOnlineId; Libraries::NpManager::OrbisNpOnlineId visitorOnlineId; - s32 hostUserId; - s32 visitorUserId; + Libraries::UserService::OrbisUserServiceUserId hostUserId; + Libraries::UserService::OrbisUserServiceUserId visitorUserId; }; int PS4_SYSV_ABI sceSharePlayCrashDaemon(); diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index b4bf189ea..631ac6234 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -490,7 +490,7 @@ int PS4_SYSV_ABI sceUserServiceGetImeRunCount() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(int* user_id) { +s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(OrbisUserServiceUserId* user_id) { LOG_DEBUG(Lib_UserService, "called"); if (user_id == nullptr) { LOG_ERROR(Lib_UserService, "user_id is null"); @@ -1041,7 +1041,8 @@ int PS4_SYSV_ABI sceUserServiceGetTraditionalChineseInputType() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserColor* color) { +s32 PS4_SYSV_ABI sceUserServiceGetUserColor(OrbisUserServiceUserId user_id, + OrbisUserServiceUserColor* color) { // TODO fix me better LOG_DEBUG(Lib_UserService, "called user_id = {}", user_id); if (color == nullptr) { @@ -1067,7 +1068,8 @@ int PS4_SYSV_ABI sceUserServiceGetUserGroupNum() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceUserServiceGetUserName(int user_id, char* user_name, std::size_t size) { +s32 PS4_SYSV_ABI sceUserServiceGetUserName(OrbisUserServiceUserId user_id, char* user_name, + std::size_t size) { LOG_DEBUG(Lib_UserService, "called user_id = {} ,size = {} ", user_id, size); if (user_name == nullptr) { LOG_ERROR(Lib_UserService, "user_name is null"); diff --git a/src/core/libraries/system/userservice.h b/src/core/libraries/system/userservice.h index 66ac2b69d..35038b840 100644 --- a/src/core/libraries/system/userservice.h +++ b/src/core/libraries/system/userservice.h @@ -151,7 +151,7 @@ int PS4_SYSV_ABI sceUserServiceGetImeLastUnit(); int PS4_SYSV_ABI sceUserServiceGetImePointerMode(); int PS4_SYSV_ABI sceUserServiceGetImePredictiveTextEnabled(); int PS4_SYSV_ABI sceUserServiceGetImeRunCount(); -s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(int* user_id); +s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(OrbisUserServiceUserId* user_id); int PS4_SYSV_ABI sceUserServiceGetIPDLeft(); int PS4_SYSV_ABI sceUserServiceGetIPDRight(); int PS4_SYSV_ABI sceUserServiceGetIsFakePlus(); @@ -258,11 +258,13 @@ int PS4_SYSV_ABI sceUserServiceGetTopMenuLimitItem(); int PS4_SYSV_ABI sceUserServiceGetTopMenuNotificationFlag(); int PS4_SYSV_ABI sceUserServiceGetTopMenuTutorialFlag(); int PS4_SYSV_ABI sceUserServiceGetTraditionalChineseInputType(); -s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserColor* color); +s32 PS4_SYSV_ABI sceUserServiceGetUserColor(OrbisUserServiceUserId user_id, + OrbisUserServiceUserColor* color); int PS4_SYSV_ABI sceUserServiceGetUserGroupName(); int PS4_SYSV_ABI sceUserServiceGetUserGroupNameList(); int PS4_SYSV_ABI sceUserServiceGetUserGroupNum(); -s32 PS4_SYSV_ABI sceUserServiceGetUserName(int user_id, char* user_name, std::size_t size); +s32 PS4_SYSV_ABI sceUserServiceGetUserName(OrbisUserServiceUserId user_id, char* user_name, + std::size_t size); int PS4_SYSV_ABI sceUserServiceGetUserStatus(); int PS4_SYSV_ABI sceUserServiceGetVibrationEnabled(); int PS4_SYSV_ABI sceUserServiceGetVoiceRecognitionLastUsedOsk(); diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index da715b3bf..ac8f58959 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -291,8 +291,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio return ORBIS_OK; } -s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, - const void* param) { +s32 PS4_SYSV_ABI sceVideoOutOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 busType, + s32 index, const void* param) { LOG_INFO(Lib_VideoOut, "called"); ASSERT(busType == SCE_VIDEO_OUT_BUS_TYPE_MAIN); diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 7db09530b..575c52d74 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -3,6 +3,7 @@ #pragma once +#include #include "core/libraries/kernel/equeue.h" #include "core/libraries/videoout/buffer.h" @@ -12,8 +13,6 @@ class SymbolsResolver; namespace Libraries::VideoOut { -using SceUserServiceUserId = s32; // TODO move it to proper place - // SceVideoOutBusType constexpr int SCE_VIDEO_OUT_BUS_TYPE_MAIN = 0; // Main output constexpr int SCE_VIDEO_OUT_BUS_TYPE_AUX_SOCIAL_SCREEN = 5; // Aux output for social @@ -131,8 +130,8 @@ s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle); s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg); s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status); s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status); -s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, - const void* param); +s32 PS4_SYSV_ABI sceVideoOutOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 busType, + s32 index, const void* param); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data); diff --git a/src/core/user_account.cpp b/src/core/user_account.cpp new file mode 100644 index 000000000..88bdb1bf9 --- /dev/null +++ b/src/core/user_account.cpp @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "common/config.h" +#include "user_account.h" + +using json = nlohmann::json; + +user_account::user_account(const std::string& user_id) { + // Setting userId. + m_user_id = user_id; + + m_user_dir = + Common::FS::GetUserPathString(Common::FS::PathType::HomeDir) + "/" + m_user_id + "/"; + + Common::FS::IOFile userfile(m_user_dir + "localuser.json", Common::FS::FileAccessMode::Read); + if (userfile.IsOpen()) { + nlohmann::json jsonfile; + try { + jsonfile = nlohmann::json::parse(userfile.ReadString(userfile.GetSize())); + } catch (const nlohmann::json::parse_error& e) { + // TODO error code + } + userfile.Close(); + m_username = jsonfile.value("username", "shadps4"); + if (m_username.length() > 16) // max of 16 chars allowed + { + m_username = m_username.substr(0, 16); // substring 16 only characters to display + } + } +} + +std::map user_account::GetUserAccounts(const std::string& base_dir) { + std::map user_list; + for (const auto& entry : std::filesystem::directory_iterator(base_dir)) { + if (entry.is_directory()) { + std::string folder_name = entry.path().filename().string(); + const u32 key = check_user(folder_name); + if (key == 0) { + continue; + } + const std::filesystem::path account_file = entry.path() / "localuser.json"; + if (std::filesystem::exists(account_file)) { + user_list.emplace(key, user_account(folder_name)); + } + } + } + + return user_list; +} + +void user_account::createdDefaultUser() { + const auto& default_user_dir = + Common::FS::GetUserPath(Common::FS::PathType::HomeDir) / Config::getDefaultUserId(); + if (!std::filesystem::exists(default_user_dir)) { + std::filesystem::create_directory(default_user_dir); + Common::FS::IOFile userfile(default_user_dir / "localuser.json", + Common::FS::FileAccessMode::Write); + nlohmann::json jsonfile; + + // Assign values + jsonfile["username"] = "shadps4"; + + std::string jsonStr = jsonfile.dump(4); + userfile.WriteString(jsonStr); + userfile.Close(); + } +} diff --git a/src/core/user_account.h b/src/core/user_account.h new file mode 100644 index 000000000..5ed807a14 --- /dev/null +++ b/src/core/user_account.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include +#include +#include "common/types.h" + +class user_account { +public: + explicit user_account(const std::string& user_id = "00000001"); + + const std::string& GetUserId() const { + return m_user_id; + } + const std::string& GetUserDir() const { + return m_user_dir; + } + const std::string& GetUsername() const { + return m_username; + } + + static std::map GetUserAccounts(const std::string& base_dir); + static void createdDefaultUser(); + static u32 check_user(const std::string& user) { + u32 id = 0; + + if (user.size() == 8) { + std::from_chars(&user.front(), &user.back() + 1, id); + } + + return id; + } + +private: + std::string m_user_id; + std::string m_user_dir; + std::string m_username; +}; diff --git a/src/images/users_icon.png b/src/images/users_icon.png new file mode 100644 index 000000000..43e5ab73f Binary files /dev/null and b/src/images/users_icon.png differ diff --git a/src/main.cpp b/src/main.cpp index fe245d104..931709cdc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "system_error" #include "unordered_map" +#include #include #include "common/config.h" #include "common/memory_patcher.h" @@ -27,6 +28,8 @@ int main(int argc, char* argv[]) { const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::load(user_dir / "config.toml"); + user_account::createdDefaultUser(); + bool has_game_argument = false; std::string game_path; std::vector game_args{}; diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index b7de517e8..0fe2325f0 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -5,6 +5,7 @@ #include "system_error" #include "unordered_map" +#include #include "common/config.h" #include "common/memory_patcher.h" #include "core/file_sys/fs.h" @@ -32,6 +33,8 @@ int main(int argc, char* argv[]) { const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::load(user_dir / "config.toml"); + user_account::createdDefaultUser(); + bool has_command_line_argument = argc > 1; bool show_gui = false, has_game_argument = false; std::string game_path; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index f561bf392..2a5dfb2f9 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -27,6 +27,7 @@ #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif +#include "user_management_dialog.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); @@ -438,6 +439,11 @@ void MainWindow::CreateConnects() { settingsDialog->exec(); }); + connect(ui->userManagement, &QAction::triggered, this, [this]() { + user_manager_dialog user_manager(this); + user_manager.exec(); + }); + connect(ui->settingsButton, &QPushButton::clicked, this, [this]() { auto settingsDialog = new SettingsDialog(m_gui_settings, m_compat_info, this); @@ -1102,6 +1108,7 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite)); ui->trophyViewerAct->setIcon(RecolorIcon(ui->trophyViewerAct->icon(), isWhite)); ui->configureAct->setIcon(RecolorIcon(ui->configureAct->icon(), isWhite)); + ui->userManagement->setIcon(RecolorIcon(ui->userManagement->icon(), isWhite)); ui->addElfFolderAct->setIcon(RecolorIcon(ui->addElfFolderAct->icon(), isWhite)); } diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 4ce71013e..604a9dc91 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -32,6 +32,7 @@ public: #endif QAction* aboutAct; QAction* configureAct; + QAction* userManagement; QAction* setThemeDark; QAction* setThemeLight; QAction* setThemeGreen; @@ -155,6 +156,9 @@ public: configureAct = new QAction(MainWindow); configureAct->setObjectName("configureAct"); configureAct->setIcon(QIcon(":images/settings_icon.png")); + userManagement = new QAction(MainWindow); + userManagement->setObjectName("userManagement"); + userManagement->setIcon(QIcon(":images/users_icon.png")); setThemeDark = new QAction(MainWindow); setThemeDark->setObjectName("setThemeDark"); setThemeDark->setCheckable(true); @@ -329,6 +333,7 @@ public: menuGame_List_Mode->addAction(setlistModeGridAct); menuGame_List_Mode->addAction(setlistElfAct); menuSettings->addAction(configureAct); + menuSettings->addAction(userManagement); menuSettings->addAction(gameInstallPathAct); menuSettings->addAction(menuUtils->menuAction()); menuUtils->addAction(downloadCheatsPatchesAct); @@ -355,6 +360,8 @@ public: #endif aboutAct->setText(QCoreApplication::translate("MainWindow", "About shadPS4", nullptr)); configureAct->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr)); + userManagement->setText( + QCoreApplication::translate("MainWindow", "User Management", nullptr)); #if QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip) menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr)); diff --git a/src/qt_gui/user_management_dialog.cpp b/src/qt_gui/user_management_dialog.cpp new file mode 100644 index 000000000..0fa9986d9 --- /dev/null +++ b/src/qt_gui/user_management_dialog.cpp @@ -0,0 +1,126 @@ +#include "user_management_dialog.h" +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/config.h" +#include "user_management_dialog.h" + +user_manager_dialog::user_manager_dialog(QWidget* parent) : QDialog(parent) { + setWindowTitle(tr("User Manager")); + setMinimumSize(QSize(500, 400)); + setModal(true); + + Init(); +} + +void user_manager_dialog::Init() { + // Table + m_table = new QTableWidget(this); + m_table->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->setContextMenuPolicy(Qt::CustomContextMenu); + m_table->setColumnCount(2); + m_table->setCornerButtonEnabled(false); + m_table->setAlternatingRowColors(true); + m_table->setHorizontalHeaderLabels(QStringList() << tr("User ID") << tr("User Name")); + m_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + m_table->horizontalHeader()->setStretchLastSection(true); + m_table->horizontalHeader()->setDefaultSectionSize(150); + m_table->installEventFilter(this); + + QPushButton* push_remove_user = new QPushButton(tr("&Delete User"), this); + push_remove_user->setAutoDefault(false); + + QPushButton* push_create_user = new QPushButton(tr("&Create User"), this); + push_create_user->setAutoDefault(false); + + QPushButton* push_edit_user = new QPushButton(tr("&Edit User"), this); + push_edit_user->setAutoDefault(false); + + QPushButton* push_close = new QPushButton(tr("&Close"), this); + push_close->setAutoDefault(false); + + // Button Layout + QHBoxLayout* hbox_buttons = new QHBoxLayout(); + hbox_buttons->addWidget(push_create_user); + hbox_buttons->addWidget(push_edit_user); + hbox_buttons->addWidget(push_remove_user); + hbox_buttons->addStretch(); + hbox_buttons->addWidget(push_close); + + // Main Layout + QVBoxLayout* vbox_main = new QVBoxLayout(); + vbox_main->setAlignment(Qt::AlignCenter); + vbox_main->addWidget(m_table); + vbox_main->addLayout(hbox_buttons); + setLayout(vbox_main); + + // get active user + m_active_user = Config::getActiveUserId(); + RefreshTable(); +} +void user_manager_dialog::RefreshTable() { + + // For indicating logged-in user. + QFont bold_font; + bold_font.setBold(true); + + m_user_list.clear(); + const auto& home_dir = Common::FS::GetUserPathString(Common::FS::PathType::HomeDir); + m_user_list = user_account::GetUserAccounts(home_dir); + + // Clear and then repopulate the table with the list gathered above. + m_table->setRowCount(static_cast(m_user_list.size())); + + int row = 0; + for (auto& [id, account] : m_user_list) { + QTableWidgetItem* user_id_item = + new QTableWidgetItem(QString::fromStdString(account.GetUserId())); + user_id_item->setData(Qt::UserRole, id); + user_id_item->setFlags(user_id_item->flags() & ~Qt::ItemIsEditable); + m_table->setItem(row, 0, user_id_item); + + QTableWidgetItem* username_item = + new QTableWidgetItem(QString::fromStdString(account.GetUsername())); + username_item->setData(Qt::UserRole, id); + username_item->setFlags(username_item->flags() & ~Qt::ItemIsEditable); + m_table->setItem(row, 1, username_item); + + // make bold the user that is active + if (m_active_user.starts_with(account.GetUserId())) { + user_id_item->setFont(bold_font); + username_item->setFont(bold_font); + } + ++row; + } + + // GUI resizing + m_table->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); + m_table->verticalHeader()->resizeSections(QHeaderView::ResizeToContents); + + const QSize table_size(m_table->verticalHeader()->width() + + m_table->horizontalHeader()->length() + m_table->frameWidth() * 2, + m_table->horizontalHeader()->height() + + m_table->verticalHeader()->length() + m_table->frameWidth() * 2); + + const QSize preferred_size = + minimumSize().expandedTo(sizeHint() - m_table->sizeHint() + table_size).expandedTo(size()); + const QSize max_size(preferred_size.width(), + static_cast(QGuiApplication::primaryScreen()->size().height() * 0.6)); + + resize(preferred_size.boundedTo(max_size)); +} diff --git a/src/qt_gui/user_management_dialog.h b/src/qt_gui/user_management_dialog.h new file mode 100644 index 000000000..3c6848a4b --- /dev/null +++ b/src/qt_gui/user_management_dialog.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +class user_manager_dialog : public QDialog { + Q_OBJECT + +public: + explicit user_manager_dialog(QWidget* parent = nullptr); + +private: + void Init(); + void RefreshTable(); + + QTableWidget* m_table = nullptr; + std::string m_active_user; + std::map m_user_list; +}; \ No newline at end of file diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 707fc89b0..1c08e7fa6 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -37,6 +37,7 @@ images/fullscreen_icon.png images/refreshlist_icon.png images/favorite_icon.png - images/trophy_icon.png + images/trophy_icon.png + images/users_icon.png