profile_manager: Load user icons, names, and UUIDs from system save

This commit is contained in:
Zach Hilman 2018-10-10 21:49:20 -04:00
parent 19c5cf9c63
commit 702622b8f1
11 changed files with 308 additions and 133 deletions

View file

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

View file

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

View file

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

View file

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

View file

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