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  | 
