diff options
m--------- | externals/dynarmic | 0 | ||||
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc.cpp | 112 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc.h | 5 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc_su.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc_u0.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc_u1.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/acc/profile_manager.cpp | 175 | ||||
-rw-r--r-- | src/core/hle/service/acc/profile_manager.h | 111 |
9 files changed, 351 insertions, 60 deletions
diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 0118ee04f90faaff951989f3c2494bc6ffb70cf +Subproject 4f96c63025af34c1490c59f6729497b9866ffa3 diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index cceb1564b..4d39ba409 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -114,6 +114,8 @@ add_library(core STATIC hle/service/acc/acc_u0.h hle/service/acc/acc_u1.cpp hle/service/acc/acc_u1.h + hle/service/acc/profile_manager.cpp + hle/service/acc/profile_manager.h hle/service/am/am.cpp hle/service/am/am.h hle/service/am/applet_ae.cpp diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index f3c5b1b9c..e74379a24 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -3,7 +3,10 @@ // Refer to the license.txt file included. #include <array> +#include "common/common_types.h" #include "common/logging/log.h" +#include "common/swap.h" +#include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/acc/acc.h" #include "core/hle/service/acc/acc_aa.h" @@ -13,7 +16,6 @@ #include "core/settings.h" namespace Service::Account { - // TODO: RE this structure struct UserData { INSERT_PADDING_WORDS(1); @@ -25,19 +27,13 @@ struct UserData { }; static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); -struct ProfileBase { - u128 user_id; - u64 timestamp; - std::array<u8, 0x20> username; -}; -static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size"); - // TODO(ogniK): Generate a real user id based on username, md5(username) maybe? -static constexpr u128 DEFAULT_USER_ID{1ull, 0ull}; +static UUID DEFAULT_USER_ID{1ull, 0ull}; class IProfile final : public ServiceFramework<IProfile> { public: - explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) { + explicit IProfile(UUID user_id, ProfileManager& profile_manager) + : ServiceFramework("IProfile"), user_id(user_id), profile_manager(profile_manager) { static const FunctionInfo functions[] = { {0, &IProfile::Get, "Get"}, {1, &IProfile::GetBase, "GetBase"}, @@ -49,38 +45,31 @@ public: private: void Get(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); ProfileBase profile_base{}; - profile_base.user_id = user_id; - if (Settings::values.username.size() > profile_base.username.size()) { - std::copy_n(Settings::values.username.begin(), profile_base.username.size(), - profile_base.username.begin()); + std::array<u8, MAX_DATA> data{}; + if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 16}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(profile_base); } else { - std::copy(Settings::values.username.begin(), Settings::values.username.end(), - profile_base.username.begin()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code } - - IPC::ResponseBuilder rb{ctx, 16}; - rb.Push(RESULT_SUCCESS); - rb.PushRaw(profile_base); } void GetBase(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - - // TODO(Subv): Retrieve this information from somewhere. + LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); ProfileBase profile_base{}; - profile_base.user_id = user_id; - if (Settings::values.username.size() > profile_base.username.size()) { - std::copy_n(Settings::values.username.begin(), profile_base.username.size(), - profile_base.username.begin()); + if (profile_manager.GetProfileBase(user_id, profile_base)) { + IPC::ResponseBuilder rb{ctx, 16}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(profile_base); } else { - std::copy(Settings::values.username.begin(), Settings::values.username.end(), - profile_base.username.begin()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code } - IPC::ResponseBuilder rb{ctx, 16}; - rb.Push(RESULT_SUCCESS); - rb.PushRaw(profile_base); } void LoadImage(Kernel::HLERequestContext& ctx) { @@ -104,7 +93,8 @@ private: rb.Push<u32>(jpeg_size); } - u128 user_id; ///< The user id this profile refers to. + ProfileManager& profile_manager; + UUID user_id; ///< The user id this profile refers to. }; class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { @@ -141,44 +131,57 @@ private: }; void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_INFO(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(1); + rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount())); } void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + UUID user_id = rp.PopRaw<UUID>(); + LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(true); // TODO: Check when this is supposed to return true and when not + rb.Push(profile_manager->UserExists(user_id)); } void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - // TODO(Subv): There is only one user for now. - const std::vector<u128> user_ids = {DEFAULT_USER_ID}; - ctx.WriteBuffer(user_ids); + LOG_INFO(Service_ACC, "called"); + ctx.WriteBuffer(profile_manager->GetAllUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - // TODO(Subv): There is only one user for now. - const std::vector<u128> user_ids = {DEFAULT_USER_ID}; - ctx.WriteBuffer(user_ids); + LOG_INFO(Service_ACC, "called"); + ctx.WriteBuffer(profile_manager->GetOpenUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } +void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_ACC, "called"); + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<UUID>(profile_manager->GetLastOpennedUser()); +} + void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u128 user_id = rp.PopRaw<u128>(); + UUID user_id = rp.PopRaw<UUID>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IProfile>(user_id); - LOG_DEBUG(Service_ACC, "called user_id=0x{:016X}{:016X}", user_id[1], user_id[0]); + rb.PushIpcInterface<IProfile>(user_id, *profile_manager); + LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); +} + +void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(profile_manager->CanSystemRegisterUser()); } void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { @@ -194,15 +197,10 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo LOG_DEBUG(Service_ACC, "called"); } -void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(RESULT_SUCCESS); - rb.PushRaw(DEFAULT_USER_ID); -} - Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} + : ServiceFramework(name), module(std::move(module)) { + profile_manager = std::make_unique<ProfileManager>(); +} void InstallInterfaces(SM::ServiceManager& service_manager) { auto module = std::make_shared<Module>(); diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 88cabaa01..89d92c1c7 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -5,6 +5,7 @@ #pragma once #include "core/hle/service/service.h" +#include "profile_manager.h" namespace Service::Account { @@ -22,6 +23,10 @@ public: void GetProfile(Kernel::HLERequestContext& ctx); void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); + void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); + + private: + std::unique_ptr<ProfileManager> profile_manager{}; protected: std::shared_ptr<Module> module; diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 8b2a71f37..5973768be 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -15,7 +15,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(mod {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_SU::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, - {50, nullptr, "IsUserRegistrationRequestPermitted"}, + {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, nullptr, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, nullptr, "GetUserRegistrationNotifier"}, diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index d84c8b2e1..b6fe45dd8 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -15,7 +15,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(mod {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U0::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, - {50, nullptr, "IsUserRegistrationRequestPermitted"}, + {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, nullptr, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 0ceaf06b5..99e3f1ef6 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -15,7 +15,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(mod {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, {5, &ACC_U1::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, - {50, nullptr, "IsUserRegistrationRequestPermitted"}, + {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, nullptr, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, nullptr, "GetUserRegistrationNotifier"}, diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp new file mode 100644 index 000000000..8e7d7194c --- /dev/null +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -0,0 +1,175 @@ +#include "core/settings.h" +#include "profile_manager.h" + +namespace Service::Account { +// TODO(ogniK): Get actual error codes +constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); +constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); +constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); + +ProfileManager::ProfileManager() { + auto user_uuid = UUID{1, 0}; + CreateNewUser(user_uuid, Settings::values.username); + OpenUser(user_uuid); +} + +size_t ProfileManager::AddToProfiles(const ProfileInfo& user) { + if (user_count >= MAX_USERS) { + return -1; + } + profiles[user_count] = std::move(user); + return user_count++; +} + +bool ProfileManager::RemoveProfileAtIdx(size_t index) { + if (index >= MAX_USERS || index < 0 || index >= user_count) + return false; + profiles[index] = ProfileInfo{}; + if (index < user_count - 1) + for (size_t i = index; i < user_count - 1; i++) + profiles[i] = profiles[i + 1]; // Shift upper profiles down + user_count--; + return true; +} + +ResultCode ProfileManager::AddUser(ProfileInfo user) { + if (AddToProfiles(user) == -1) { + return ERROR_TOO_MANY_USERS; + } + return RESULT_SUCCESS; +} + +ResultCode ProfileManager::CreateNewUser(UUID uuid, std::array<u8, 0x20> username) { + if (user_count == MAX_USERS) + return ERROR_TOO_MANY_USERS; + if (!uuid) + return ERROR_ARGUMENT_IS_NULL; + if (username[0] == 0x0) + return ERROR_ARGUMENT_IS_NULL; + for (unsigned i = 0; i < user_count; i++) + if (uuid == profiles[i].user_uuid) + return ERROR_USER_ALREADY_EXISTS; + ProfileInfo prof_inf; + prof_inf.user_uuid = std::move(uuid); + prof_inf.username = std::move(username); + prof_inf.data = std::array<u8, MAX_DATA>(); + prof_inf.creation_time = 0x0; + prof_inf.is_open = false; + return AddUser(prof_inf); +} + +ResultCode ProfileManager::CreateNewUser(UUID uuid, std::string username) { + std::array<u8, 0x20> username_output; + if (username.size() > username_output.size()) + std::copy_n(username.begin(), username_output.size(), username_output.begin()); + else + std::copy(username.begin(), username.end(), username_output.begin()); + return CreateNewUser(uuid, std::move(username_output)); +} + +size_t ProfileManager::GetUserIndex(UUID uuid) { + if (!uuid) + return -1; + for (unsigned i = 0; i < user_count; i++) + if (profiles[i].user_uuid == uuid) + return i; + return -1; +} + +size_t ProfileManager::GetUserIndex(ProfileInfo user) { + return GetUserIndex(user.user_uuid); +} + +bool ProfileManager::GetProfileBase(size_t index, ProfileBase& profile) { + if (index >= MAX_USERS) { + profile.Invalidate(); + return false; + } + auto prof_info = profiles[index]; + profile.user_uuid = prof_info.user_uuid; + profile.username = prof_info.username; + profile.timestamp = prof_info.creation_time; + return true; +} + +bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) { + auto idx = GetUserIndex(uuid); + return GetProfileBase(idx, profile); +} + +bool ProfileManager::GetProfileBase(ProfileInfo user, ProfileBase& profile) { + return GetProfileBase(user.user_uuid, profile); +} + +size_t ProfileManager::GetUserCount() { + return user_count; +} + +bool ProfileManager::UserExists(UUID uuid) { + return (GetUserIndex(uuid) != -1); +} + +void ProfileManager::OpenUser(UUID uuid) { + auto idx = GetUserIndex(uuid); + if (idx == -1) + return; + profiles[idx].is_open = true; + last_openned_user = uuid; +} + +void ProfileManager::CloseUser(UUID uuid) { + auto idx = GetUserIndex(uuid); + if (idx == -1) + return; + profiles[idx].is_open = false; +} + +std::array<UUID, MAX_USERS> ProfileManager::GetAllUsers() { + std::array<UUID, MAX_USERS> output; + for (unsigned i = 0; i < user_count; i++) { + output[i] = profiles[i].user_uuid; + } + return output; +} + +std::array<UUID, MAX_USERS> ProfileManager::GetOpenUsers() { + std::array<UUID, MAX_USERS> output; + unsigned user_idx = 0; + for (unsigned i = 0; i < user_count; i++) { + if (profiles[i].is_open) { + output[i++] = profiles[i].user_uuid; + } + } + return output; +} + +const UUID& ProfileManager::GetLastOpennedUser() { + return last_openned_user; +} + +bool ProfileManager::GetProfileBaseAndData(size_t index, ProfileBase& profile, + std::array<u8, MAX_DATA>& data) { + if (GetProfileBase(index, profile)) { + std::memcpy(data.data(), profiles[index].data.data(), MAX_DATA); + return true; + } + return false; +} +bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, + std::array<u8, MAX_DATA>& data) { + auto idx = GetUserIndex(uuid); + return GetProfileBaseAndData(idx, profile, data); +} + +bool ProfileManager::GetProfileBaseAndData(ProfileInfo user, ProfileBase& profile, + std::array<u8, MAX_DATA>& data) { + return GetProfileBaseAndData(user.user_uuid, profile, data); +} + +bool ProfileManager::CanSystemRegisterUser() { + return false; // TODO(ogniK): Games shouldn't have + // access to user registration, when we + // emulate qlaunch. Update this to dynamically change. +} + +}; // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h new file mode 100644 index 000000000..64371ea16 --- /dev/null +++ b/src/core/hle/service/acc/profile_manager.h @@ -0,0 +1,111 @@ +#pragma once +#include <array> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/result.h" + +namespace Service::Account { +constexpr size_t MAX_USERS = 8; +constexpr size_t MAX_DATA = 128; + +struct UUID { + // UUIDs which are 0 are considered invalid! + u128 uuid{0, 0}; + UUID() = default; + explicit UUID(const u128& id) { + uuid[0] = id[0]; + uuid[1] = id[1]; + }; + explicit UUID(const u64& lo, const u64& hi) { + uuid[0] = lo; + uuid[1] = hi; + }; + operator bool() const { + return uuid[0] != 0x0 || uuid[1] != 0x0; + } + + bool operator==(const UUID& rhs) { + return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1]; + } + + bool operator!=(const UUID& rhs) { + return uuid[0] != rhs.uuid[0] || uuid[1] != rhs.uuid[1]; + } + + // TODO(ogniK): Properly generate uuids based on RFC-4122 + const UUID& Generate() { + uuid[0] = (static_cast<u64>(std::rand()) << 32) | std::rand(); + uuid[1] = (static_cast<u64>(std::rand()) << 32) | std::rand(); + return *this; + } + void Invalidate() { + uuid[0] = 0; + uuid[1] = 0; + } + std::string Format() { + return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); + } +}; +static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); + +/// This holds general information about a users profile. This is where we store all the information +/// based on a specific user +struct ProfileInfo { + UUID user_uuid; + std::array<u8, 0x20> username; + u64 creation_time; + std::array<u8, MAX_DATA> data; // TODO(ognik): Work out what this is + bool is_open; +}; + +struct ProfileBase { + UUID user_uuid; + u64_le timestamp; + std::array<u8, 0x20> username; + + const void Invalidate() { + user_uuid.Invalidate(); + timestamp = 0; + username.fill(0); + } +}; +static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size"); + +/// The profile manager is used for handling multiple user profiles at once. It keeps track of open +/// users, all the accounts registered on the "system" as well as fetching individual "ProfileInfo" +/// objects +class ProfileManager { +public: + ProfileManager(); // TODO(ogniK): Load from system save + ResultCode AddUser(ProfileInfo user); + ResultCode CreateNewUser(UUID uuid, std::array<u8, 0x20> username); + ResultCode CreateNewUser(UUID uuid, std::string username); + size_t GetUserIndex(UUID uuid); + size_t GetUserIndex(ProfileInfo user); + bool GetProfileBase(size_t index, ProfileBase& profile); + bool GetProfileBase(UUID uuid, ProfileBase& profile); + bool GetProfileBase(ProfileInfo user, ProfileBase& profile); + bool GetProfileBaseAndData(size_t index, ProfileBase& profile, std::array<u8, MAX_DATA>& data); + bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, std::array<u8, MAX_DATA>& data); + bool GetProfileBaseAndData(ProfileInfo user, ProfileBase& profile, + std::array<u8, MAX_DATA>& data); + size_t GetUserCount(); + bool UserExists(UUID uuid); + void OpenUser(UUID uuid); + void CloseUser(UUID uuid); + std::array<UUID, MAX_USERS> GetOpenUsers(); + std::array<UUID, MAX_USERS> GetAllUsers(); + const UUID& GetLastOpennedUser(); + + bool CanSystemRegisterUser(); + +private: + std::array<ProfileInfo, MAX_USERS> profiles{}; + size_t user_count = 0; + size_t AddToProfiles(const ProfileInfo& profile); + bool RemoveProfileAtIdx(size_t index); + UUID last_openned_user{0, 0}; +}; +using ProfileManagerPtr = std::unique_ptr<ProfileManager>; + +}; // namespace Service::Account |