This commit is contained in:
georgemoralis 2025-07-04 07:20:20 +00:00 committed by GitHub
commit e57926ad36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 399 additions and 63 deletions

3
.gitmodules vendored
View file

@ -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

View file

@ -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")

View file

@ -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",

View file

@ -230,3 +230,7 @@ if (APPLE)
add_subdirectory(MoltenVK)
endif()
endif()
#nlohmann json
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(json)

1
externals/json vendored Submodule

@ -0,0 +1 @@
Subproject commit 568b708fd46deeb23a959381393a7564f1586588

View file

@ -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;

View file

@ -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

View file

@ -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()) {

View file

@ -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";

View file

@ -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,

View file

@ -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);

View file

@ -3,6 +3,7 @@
#pragma once
#include <core/libraries/system/userservice.h>
#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;

View file

@ -5,6 +5,7 @@
#include <imgui.h>
#include <magic_enum/magic_enum.hpp>
#include <core/libraries/system/userservice.h>
#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;
};

View file

@ -11,8 +11,6 @@ class SymbolsResolver;
}
namespace Libraries::ErrorDialog {
using OrbisUserServiceUserId = s32;
struct Param;
CommonDialog::Error PS4_SYSV_ABI sceErrorDialogClose();

View file

@ -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) {

View file

@ -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();

View file

@ -3,6 +3,7 @@
#pragma once
#include <core/libraries/system/userservice.h>
#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];
};

View file

@ -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;

View file

@ -3,6 +3,7 @@
#pragma once
#include <core/libraries/system/userservice.h>
#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();

View file

@ -4,6 +4,7 @@
#include <unordered_map>
#include <pugixml.hpp>
#include <core/libraries/system/userservice.h>
#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;

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <core/libraries/system/userservice.h>
#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)

View file

@ -3,6 +3,7 @@
#pragma once
#include <core/libraries/system/userservice.h>
#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);

View file

@ -3,6 +3,7 @@
#include "remoteplay.h"
#include <core/libraries/system/userservice.h>
#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;
}

View file

@ -3,6 +3,7 @@
#pragma once
#include <core/libraries/system/userservice.h>
#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();

View file

@ -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();

View file

@ -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");

View file

@ -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();

View file

@ -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);

View file

@ -3,6 +3,7 @@
#pragma once
#include <core/libraries/system/userservice.h>
#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);

72
src/core/user_account.cpp Normal file
View file

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <filesystem>
#include <common/io_file.h>
#include <common/path_util.h>
#include <nlohmann/json.hpp>
#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<u32, user_account> user_account::GetUserAccounts(const std::string& base_dir) {
std::map<u32, user_account> 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();
}
}

40
src/core/user_account.h Normal file
View file

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <charconv>
#include <map>
#include <string>
#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<u32, user_account> 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;
};

BIN
src/images/users_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -7,6 +7,7 @@
#include "system_error"
#include "unordered_map"
#include <core/user_account.h>
#include <fmt/core.h>
#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<std::string> game_args{};

View file

@ -5,6 +5,7 @@
#include "system_error"
#include "unordered_map"
#include <core/user_account.h>
#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;

View file

@ -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));
}

View file

@ -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));

View file

@ -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 <QDesktopServices>
#include <QEvent>
#include <QGuiApplication>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QInputDialog>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPushButton>
#include <QRegularExpressionValidator>
#include <QScreen>
#include <common/path_util.h>
#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<int>(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<int>(QGuiApplication::primaryScreen()->size().height() * 0.6));
resize(preferred_size.boundedTo(max_size));
}

View file

@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QDialog>
#include <QTableWidget>
#include <core/user_account.h>
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<u32, user_account> m_user_list;
};

View file

@ -37,6 +37,7 @@
<file>images/fullscreen_icon.png</file>
<file>images/refreshlist_icon.png</file>
<file>images/favorite_icon.png</file>
<file>images/trophy_icon.png</file>
<file>images/trophy_icon.png</file>
<file>images/users_icon.png</file>
</qresource>
</RCC>