profile_manager: Load user icons, names, and UUIDs from system save
This commit is contained in:
parent
19c5cf9c63
commit
702622b8f1
11 changed files with 308 additions and 133 deletions
|
@ -2,6 +2,7 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/common_types.h"
|
||||
|
@ -33,9 +34,9 @@ struct UserData {
|
|||
};
|
||||
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
|
||||
|
||||
static std::string GetImagePath(const std::string& username) {
|
||||
return FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "users" + DIR_SEP + username +
|
||||
".jpg";
|
||||
static std::string GetImagePath(UUID uuid) {
|
||||
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
}
|
||||
|
||||
class IProfile final : public ServiceFramework<IProfile> {
|
||||
|
@ -49,15 +50,6 @@ public:
|
|||
{11, &IProfile::LoadImage, "LoadImage"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
ProfileBase profile_base{};
|
||||
if (profile_manager.GetProfileBase(user_id, profile_base)) {
|
||||
image = std::make_unique<FileUtil::IOFile>(
|
||||
GetImagePath(Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(profile_base.username.data()),
|
||||
profile_base.username.size())),
|
||||
"rb");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -111,13 +103,15 @@ private:
|
|||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (image == nullptr) {
|
||||
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
|
||||
|
||||
if (!image.IsOpen()) {
|
||||
ctx.WriteBuffer(backup_jpeg);
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
} else {
|
||||
const auto size = std::min<u32>(image->GetSize(), MAX_JPEG_IMAGE_SIZE);
|
||||
const auto size = std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE);
|
||||
std::vector<u8> buffer(size);
|
||||
image->ReadBytes(buffer.data(), buffer.size());
|
||||
image.ReadBytes(buffer.data(), buffer.size());
|
||||
|
||||
ctx.WriteBuffer(buffer.data(), buffer.size());
|
||||
rb.Push<u32>(buffer.size());
|
||||
|
@ -130,15 +124,16 @@ private:
|
|||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (image == nullptr)
|
||||
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
|
||||
|
||||
if (!image.IsOpen())
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
else
|
||||
rb.Push<u32>(std::min<u32>(image->GetSize(), MAX_JPEG_IMAGE_SIZE));
|
||||
rb.Push<u32>(std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE));
|
||||
}
|
||||
|
||||
const ProfileManager& profile_manager;
|
||||
UUID user_id; ///< The user id this profile refers to.
|
||||
std::unique_ptr<FileUtil::IOFile> image = nullptr;
|
||||
};
|
||||
|
||||
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
|
||||
|
|
|
@ -4,10 +4,27 @@
|
|||
|
||||
#include <random>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/file_util.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
struct UserRaw {
|
||||
UUID uuid;
|
||||
UUID uuid2;
|
||||
u64 timestamp;
|
||||
ProfileUsername username;
|
||||
INSERT_PADDING_BYTES(0x80);
|
||||
};
|
||||
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
|
||||
|
||||
struct ProfileDataRaw {
|
||||
INSERT_PADDING_BYTES(0x10);
|
||||
std::array<UserRaw, MAX_USERS> users;
|
||||
};
|
||||
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
|
||||
|
||||
// TODO(ogniK): Get actual error codes
|
||||
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
|
||||
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
|
||||
|
@ -23,15 +40,21 @@ const UUID& UUID::Generate() {
|
|||
}
|
||||
|
||||
ProfileManager::ProfileManager() {
|
||||
for (std::size_t i = 0; i < Settings::values.users.size(); ++i) {
|
||||
const auto& val = Settings::values.users[i];
|
||||
ASSERT(CreateNewUser(val.second, val.first).IsSuccess());
|
||||
}
|
||||
ParseUserSaveFile();
|
||||
|
||||
OpenUser(Settings::values.users[Settings::values.current_user].second);
|
||||
if (user_count == 0)
|
||||
CreateNewUser(UUID{}.Generate(), "yuzu");
|
||||
|
||||
auto current = Settings::values.current_user;
|
||||
if (!GetAllUsers()[current])
|
||||
current = 0;
|
||||
|
||||
OpenUser(GetAllUsers()[current]);
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager() = default;
|
||||
ProfileManager::~ProfileManager() {
|
||||
WriteUserSaveFile();
|
||||
}
|
||||
|
||||
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
|
||||
/// internal management of the users profiles
|
||||
|
@ -241,4 +264,70 @@ bool ProfileManager::CanSystemRegisterUser() const {
|
|||
// emulate qlaunch. Update this to dynamically change.
|
||||
}
|
||||
|
||||
bool ProfileManager::RemoveUser(UUID uuid) {
|
||||
auto index = GetUserIndex(uuid);
|
||||
if (index == boost::none) {
|
||||
return false;
|
||||
}
|
||||
|
||||
profiles[*index] = ProfileInfo{};
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
|
||||
auto index = GetUserIndex(uuid);
|
||||
if (profile_new.user_uuid == UUID(INVALID_UUID) || index == boost::none) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& profile = profiles[*index];
|
||||
profile.user_uuid = profile_new.user_uuid;
|
||||
profile.username = profile_new.username;
|
||||
profile.creation_time = profile_new.timestamp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProfileManager::ParseUserSaveFile() {
|
||||
FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/profiles.dat",
|
||||
"rb");
|
||||
|
||||
ProfileDataRaw data;
|
||||
save.Seek(0, SEEK_SET);
|
||||
if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw))
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < MAX_USERS; ++i) {
|
||||
const auto& user = data.users[i];
|
||||
|
||||
if (user.uuid != UUID(INVALID_UUID))
|
||||
AddUser({user.uuid, user.username, user.timestamp, {}, false});
|
||||
}
|
||||
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
}
|
||||
|
||||
void ProfileManager::WriteUserSaveFile() {
|
||||
ProfileDataRaw raw{};
|
||||
|
||||
for (std::size_t i = 0; i < MAX_USERS; ++i) {
|
||||
raw.users[i].username = profiles[i].username;
|
||||
raw.users[i].uuid2 = profiles[i].user_uuid;
|
||||
raw.users[i].uuid = profiles[i].user_uuid;
|
||||
raw.users[i].timestamp = profiles[i].creation_time;
|
||||
}
|
||||
|
||||
FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/profiles.dat",
|
||||
"rb");
|
||||
|
||||
save.Resize(sizeof(ProfileDataRaw));
|
||||
save.Seek(0, SEEK_SET);
|
||||
save.WriteBytes(&raw, sizeof(ProfileDataRaw));
|
||||
}
|
||||
|
||||
}; // namespace Service::Account
|
||||
|
|
|
@ -45,6 +45,15 @@ struct UUID {
|
|||
std::string Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
|
@ -108,7 +117,13 @@ public:
|
|||
|
||||
bool CanSystemRegisterUser() const;
|
||||
|
||||
bool RemoveUser(UUID uuid);
|
||||
bool SetProfileBase(UUID uuid, const ProfileBase& profile);
|
||||
|
||||
private:
|
||||
void ParseUserSaveFile();
|
||||
void WriteUserSaveFile();
|
||||
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
std::size_t user_count = 0;
|
||||
boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
|
@ -734,8 +736,10 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
|||
std::vector<u8> buffer(POP_LAUNCH_PARAMETER_BUFFER_SIZE);
|
||||
|
||||
std::memcpy(buffer.data(), header_data.data(), header_data.size());
|
||||
const auto current_uuid = Settings::values.users[Settings::values.current_user].second.uuid;
|
||||
std::memcpy(buffer.data() + header_data.size(), current_uuid.data(), sizeof(u128));
|
||||
|
||||
Account::ProfileManager profile_manager{};
|
||||
const auto uuid = profile_manager.GetAllUsers()[Settings::values.current_user].uuid;
|
||||
std::memcpy(buffer.data() + header_data.size(), uuid.data(), sizeof(u128));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <atomic>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
|
@ -116,7 +115,6 @@ struct Values {
|
|||
bool use_docked_mode;
|
||||
bool enable_nfc;
|
||||
int current_user;
|
||||
std::vector<std::pair<std::string, Service::Account::UUID>> users;
|
||||
int language_index;
|
||||
|
||||
// Controls
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue