diff --git a/src/xenia/kernel/util/xuserdata.h b/src/xenia/kernel/util/xuserdata.h new file mode 100644 index 000000000..794882b6d --- /dev/null +++ b/src/xenia/kernel/util/xuserdata.h @@ -0,0 +1,234 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2024 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_UTIL_XUSERDATA_H_ +#define XENIA_KERNEL_UTIL_XUSERDATA_H_ + +#include "xenia/base/byte_stream.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { + +enum class X_USER_DATA_TYPE : uint8_t { + CONTENT = 0, + INT32 = 1, + INT64 = 2, + DOUBLE = 3, + WSTRING = 4, + FLOAT = 5, + BINARY = 6, + DATETIME = 7, + UNSET = 0xFF, +}; + +struct X_USER_DATA { + X_USER_DATA_TYPE type; + + union { + be s32; + be s64; + be u32; + be f64; + struct { + be size; + be ptr; + } unicode; + be f32; + struct { + be size; + be ptr; + } binary; + be filetime; + }; +}; +static_assert_size(X_USER_DATA, 16); + +class DataByteStream : public ByteStream { + public: + DataByteStream(uint32_t ptr, uint8_t* data, size_t data_length, + size_t offset = 0) + : ByteStream(data, data_length, offset), ptr_(ptr) {} + + uint32_t ptr() const { return static_cast(ptr_ + offset()); } + + private: + uint32_t ptr_; +}; + +class UserData { + public: + union Key { + uint32_t value; + struct { + uint32_t id : 14; + uint32_t unk : 2; + uint32_t size : 12; + uint32_t type : 4; + }; + }; + + UserData(){}; + UserData(X_USER_DATA_TYPE type) { data_.type = type; } + + virtual void Append(X_USER_DATA* data, DataByteStream* stream) { + data->type = data_.type; + } + + virtual std::vector Serialize() const { + return std::vector(); + } + virtual void Deserialize(std::vector) {} + + private: + X_USER_DATA data_ = {}; +}; + +class Int32UserData : public UserData { + public: + Int32UserData(int32_t value) + : UserData(X_USER_DATA_TYPE::INT32), value_(value) {} + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + data->s32 = value_; + } + + private: + int32_t value_; +}; + +class Uint32UserData : public UserData { + public: + Uint32UserData(uint32_t value) + : UserData(X_USER_DATA_TYPE::INT32), value_(value) {} + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + data->u32 = value_; + } + + private: + uint32_t value_; +}; + +class Int64UserData : public UserData { + public: + Int64UserData(int64_t value) + : UserData(X_USER_DATA_TYPE::INT64), value_(value) {} + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + data->s64 = value_; + } + + private: + int64_t value_; +}; + +class FloatUserData : public UserData { + public: + FloatUserData(float value) + : UserData(X_USER_DATA_TYPE::FLOAT), value_(value) {} + + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + data->f32 = value_; + } + + private: + float value_; +}; + +class DoubleUserData : public UserData { + public: + DoubleUserData(double value) + : UserData(X_USER_DATA_TYPE::DOUBLE), value_(value) {} + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + data->f64 = value_; + } + + private: + double value_; +}; + +class UnicodeUserData : public UserData { + public: + UnicodeUserData(const std::u16string& value) + : UserData(X_USER_DATA_TYPE::WSTRING), value_(value) {} + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + + if (value_.empty()) { + data->unicode.size = 0; + data->unicode.ptr = 0; + return; + } + + size_t count = value_.size() + 1; + size_t size = 2 * count; + assert_true(size <= std::numeric_limits::max()); + data->unicode.size = static_cast(size); + data->unicode.ptr = stream->ptr(); + auto buffer = + reinterpret_cast(&stream->data()[stream->offset()]); + stream->Advance(size); + copy_and_swap(buffer, (uint16_t*)value_.data(), count); + } + + private: + std::u16string value_; +}; + +class BinaryUserData : public UserData { + public: + BinaryUserData(const std::vector& value) + : UserData(X_USER_DATA_TYPE::BINARY), value_(value) {} + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + + if (value_.empty()) { + data->binary.size = 0; + data->binary.ptr = 0; + return; + } + + size_t size = value_.size(); + assert_true(size <= std::numeric_limits::max()); + data->binary.size = static_cast(size); + data->binary.ptr = stream->ptr(); + stream->Write(value_.data(), size); + } + + std::vector Serialize() const override { + return std::vector(value_.data(), value_.data() + value_.size()); + } + + void Deserialize(std::vector data) override { value_ = data; } + + private: + std::vector value_; +}; + +class DateTimeUserData : public UserData { + public: + DateTimeUserData(int64_t value) + : UserData(X_USER_DATA_TYPE::DATETIME), value_(value) {} + + void Append(X_USER_DATA* data, DataByteStream* stream) override { + UserData::Append(data, stream); + data->filetime = value_; + } + + private: + int64_t value_; +}; + +} // namespace kernel +} // namespace xe + +#endif diff --git a/src/xenia/kernel/xam/user_profile.cc b/src/xenia/kernel/xam/user_profile.cc index 4911051d0..b8b0ef4c9 100644 --- a/src/xenia/kernel/xam/user_profile.cc +++ b/src/xenia/kernel/xam/user_profile.cc @@ -32,74 +32,74 @@ UserProfile::UserProfile(uint8_t index) { // https://cs.rin.ru/forum/viewtopic.php?f=38&t=60668&hilit=gfwl+live&start=195 // https://github.com/arkem/py360/blob/master/py360/constants.py // XPROFILE_GAMER_YAXIS_INVERSION - AddSetting(std::make_unique(0x10040002, 0)); + AddSetting(std::make_unique(0x10040002, 0)); // XPROFILE_OPTION_CONTROLLER_VIBRATION - AddSetting(std::make_unique(0x10040003, 3)); + AddSetting(std::make_unique(0x10040003, 3)); // XPROFILE_GAMERCARD_ZONE - AddSetting(std::make_unique(0x10040004, 0)); + AddSetting(std::make_unique(0x10040004, 0)); // XPROFILE_GAMERCARD_REGION - AddSetting(std::make_unique(0x10040005, 0)); + AddSetting(std::make_unique(0x10040005, 0)); // XPROFILE_GAMERCARD_CRED - AddSetting(std::make_unique(0x10040006, 0xFA)); - // XPROFILE_GAMERCARD_REP - AddSetting(std::make_unique(0x5004000B, 0.0f)); + AddSetting(std::make_unique(0x10040006, 0xFA)); // XPROFILE_OPTION_VOICE_MUTED - AddSetting(std::make_unique(0x1004000C, 0)); + AddSetting(std::make_unique(0x1004000C, 3)); // XPROFILE_OPTION_VOICE_THRU_SPEAKERS - AddSetting(std::make_unique(0x1004000D, 0)); + AddSetting(std::make_unique(0x1004000D, 3)); // XPROFILE_OPTION_VOICE_VOLUME - AddSetting(std::make_unique(0x1004000E, 0x64)); - // XPROFILE_GAMERCARD_MOTTO - AddSetting(std::make_unique(0x402C0011, u"")); + AddSetting(std::make_unique(0x1004000E, 0x64)); // XPROFILE_GAMERCARD_TITLES_PLAYED - AddSetting(std::make_unique(0x10040012, 1)); + AddSetting(std::make_unique(0x10040012, 1)); // XPROFILE_GAMERCARD_ACHIEVEMENTS_EARNED - AddSetting(std::make_unique(0x10040013, 0)); + AddSetting(std::make_unique(0x10040013, 0)); // XPROFILE_GAMER_DIFFICULTY - AddSetting(std::make_unique(0x10040015, 0)); + AddSetting(std::make_unique(0x10040015, 0)); // XPROFILE_GAMER_CONTROL_SENSITIVITY - AddSetting(std::make_unique(0x10040018, 0)); + AddSetting(std::make_unique(0x10040018, 0)); // Preferred color 1 - AddSetting(std::make_unique(0x1004001D, 0xFFFF0000u)); + AddSetting(std::make_unique(0x1004001D, 0xFFFF0000u)); // Preferred color 2 - AddSetting(std::make_unique(0x1004001E, 0xFF00FF00u)); + AddSetting(std::make_unique(0x1004001E, 0xFF00FF00u)); // XPROFILE_GAMER_ACTION_AUTO_AIM - AddSetting(std::make_unique(0x10040022, 1)); + AddSetting(std::make_unique(0x10040022, 1)); // XPROFILE_GAMER_ACTION_AUTO_CENTER - AddSetting(std::make_unique(0x10040023, 0)); + AddSetting(std::make_unique(0x10040023, 0)); // XPROFILE_GAMER_ACTION_MOVEMENT_CONTROL - AddSetting(std::make_unique(0x10040024, 0)); + AddSetting(std::make_unique(0x10040024, 0)); // XPROFILE_GAMER_RACE_TRANSMISSION - AddSetting(std::make_unique(0x10040026, 0)); + AddSetting(std::make_unique(0x10040026, 0)); // XPROFILE_GAMER_RACE_CAMERA_LOCATION - AddSetting(std::make_unique(0x10040027, 0)); + AddSetting(std::make_unique(0x10040027, 0)); // XPROFILE_GAMER_RACE_BRAKE_CONTROL - AddSetting(std::make_unique(0x10040028, 0)); + AddSetting(std::make_unique(0x10040028, 0)); // XPROFILE_GAMER_RACE_ACCELERATOR_CONTROL - AddSetting(std::make_unique(0x10040029, 0)); + AddSetting(std::make_unique(0x10040029, 0)); // XPROFILE_GAMERCARD_TITLE_CRED_EARNED - AddSetting(std::make_unique(0x10040038, 0)); + AddSetting(std::make_unique(0x10040038, 0)); // XPROFILE_GAMERCARD_TITLE_ACHIEVEMENTS_EARNED - AddSetting(std::make_unique(0x10040039, 0)); + AddSetting(std::make_unique(0x10040039, 0)); - // If we set this, games will try to get it. + // XPROFILE_GAMERCARD_MOTTO + AddSetting(std::make_unique(0x402C0011, u"")); // XPROFILE_GAMERCARD_PICTURE_KEY AddSetting( - std::make_unique(0x4064000F, u"gamercard_picture_key")); + std::make_unique(0x4064000F, u"gamercard_picture_key")); + // XPROFILE_GAMERCARD_REP + AddSetting(std::make_unique(0x5004000B, 0.0f)); // XPROFILE_TITLE_SPECIFIC1 - AddSetting(std::make_unique(0x63E83FFF)); + AddSetting(std::make_unique(0x63E83FFF, std::vector())); // XPROFILE_TITLE_SPECIFIC2 - AddSetting(std::make_unique(0x63E83FFE)); + AddSetting(std::make_unique(0x63E83FFE, std::vector())); // XPROFILE_TITLE_SPECIFIC3 - AddSetting(std::make_unique(0x63E83FFD)); + AddSetting(std::make_unique(0x63E83FFD, std::vector())); } -void UserProfile::AddSetting(std::unique_ptr setting) { - Setting* previous_setting = setting.get(); - std::swap(settings_[setting->setting_id], previous_setting); +void UserProfile::AddSetting(std::unique_ptr setting) { + UserSetting* previous_setting = setting.get(); - if (setting->is_set && setting->is_title_specific()) { + std::swap(settings_[setting->GetSettingId()], previous_setting); + + if (setting->is_title_specific()) { SaveSetting(setting.get()); } @@ -118,40 +118,59 @@ void UserProfile::AddSetting(std::unique_ptr setting) { } } -UserProfile::Setting* UserProfile::GetSetting(uint32_t setting_id) { +UserSetting* UserProfile::GetSetting(uint32_t setting_id) { const auto& it = settings_.find(setting_id); if (it == settings_.end()) { return nullptr; } - UserProfile::Setting* setting = it->second; + + UserSetting* setting = it->second; if (setting->is_title_specific()) { // If what we have loaded in memory isn't for the title that is running // right now, then load it from disk. - if (kernel_state()->title_id() != setting->loaded_title_id) { - LoadSetting(setting); - } + LoadSetting(setting); } return setting; } -void UserProfile::LoadSetting(UserProfile::Setting* setting) { +void UserProfile::LoadSetting(UserSetting* setting) { if (setting->is_title_specific()) { - auto content_dir = + const std::filesystem::path content_dir = kernel_state()->content_manager()->ResolveGameUserContentPath(); - auto setting_id = fmt::format("{:08X}", setting->setting_id); - auto file_path = content_dir / setting_id; - auto file = xe::filesystem::OpenFile(file_path, "rb"); - if (file) { - fseek(file, 0, SEEK_END); - uint32_t input_file_size = static_cast(ftell(file)); - fseek(file, 0, SEEK_SET); - - std::vector serialized_data(input_file_size); - fread(serialized_data.data(), 1, serialized_data.size(), file); - fclose(file); - setting->Deserialize(serialized_data); - setting->loaded_title_id = kernel_state()->title_id(); + const std::string setting_id_str = + fmt::format("{:08X}", setting->GetSettingId()); + const std::filesystem::path file_path = content_dir / setting_id_str; + FILE* file = xe::filesystem::OpenFile(file_path, "rb"); + if (!file) { + return; } + + const uint32_t input_file_size = + static_cast(std::filesystem::file_size(file_path)); + + if (input_file_size < sizeof(X_USER_PROFILE_SETTING_HEADER)) { + fclose(file); + // Setting seems to be invalid, remove it. + std::filesystem::remove(file_path); + return; + } + + X_USER_PROFILE_SETTING_HEADER header; + fread(&header, sizeof(X_USER_PROFILE_SETTING_HEADER), 1, file); + if (header.setting_id != setting->GetSettingId()) { + // It's setting with different ID? Corrupted perhaps. + fclose(file); + std::filesystem::remove(file_path); + return; + } + + // TODO(Gliniak): Right now we only care about CONTENT, WSTRING, BINARY + setting->SetNewSettingHeader(&header); + setting->SetNewSettingSource(X_USER_PROFILE_SETTING_SOURCE::TITLE); + std::vector serialized_data(setting->GetSettingHeader()->size); + fread(serialized_data.data(), 1, serialized_data.size(), file); + fclose(file); + setting->GetSettingData()->Deserialize(serialized_data); } else { // Unsupported for now. Other settings aren't per-game and need to be // stored some other way. @@ -159,16 +178,32 @@ void UserProfile::LoadSetting(UserProfile::Setting* setting) { } } -void UserProfile::SaveSetting(UserProfile::Setting* setting) { - if (setting->is_title_specific()) { - auto serialized_setting = setting->Serialize(); - auto content_dir = +void UserProfile::SaveSetting(UserSetting* setting) { + if (setting->is_title_specific() && + setting->GetSettingSource() == X_USER_PROFILE_SETTING_SOURCE::TITLE) { + const std::filesystem::path content_dir = kernel_state()->content_manager()->ResolveGameUserContentPath(); + std::filesystem::create_directories(content_dir); - auto setting_id = fmt::format("{:08X}", setting->setting_id); - auto file_path = content_dir / setting_id; - auto file = xe::filesystem::OpenFile(file_path, "wb"); - fwrite(serialized_setting.data(), 1, serialized_setting.size(), file); + + const std::string setting_id_str = + fmt::format("{:08X}", setting->GetSettingId()); + std::filesystem::path file_path = content_dir / setting_id_str; + FILE* file = xe::filesystem::OpenFile(file_path, "wb"); + + if (!file) { + return; + } + + const std::vector serialized_setting = + setting->GetSettingData()->Serialize(); + const uint32_t serialized_setting_length = std::min( + kMaxSettingSize, static_cast(serialized_setting.size())); + + fwrite(setting->GetSettingHeader(), sizeof(X_USER_PROFILE_SETTING_HEADER), + 1, file); + // Writing data + fwrite(serialized_setting.data(), 1, serialized_setting_length, file); fclose(file); } else { // Unsupported for now. Other settings aren't per-game and need to be diff --git a/src/xenia/kernel/xam/user_profile.h b/src/xenia/kernel/xam/user_profile.h index 05ab4ed1d..0d226ed2f 100644 --- a/src/xenia/kernel/xam/user_profile.h +++ b/src/xenia/kernel/xam/user_profile.h @@ -17,212 +17,141 @@ #include #include "xenia/base/byte_stream.h" +#include "xenia/kernel/util/xuserdata.h" #include "xenia/xbox.h" namespace xe { namespace kernel { namespace xam { -struct X_USER_PROFILE_SETTING_DATA { - // UserProfile::Setting::Type. Appears to be 8-in-32 field, and the upper 24 - // are not always zeroed by the game. - uint8_t type; - uint8_t unk_1[3]; - xe::be unk_4; - // TODO(sabretooth): not sure if this is a union, but it seems likely. - // Haven't run into cases other than "binary data" yet. +constexpr uint32_t kMaxSettingSize = 0x03E8; + +enum class X_USER_PROFILE_SETTING_SOURCE : uint32_t { + NOT_SET = 0, + DEFAULT = 1, + TITLE = 2, + UNKNOWN = 3, +}; + +// Each setting contains 0x18 bytes long header +struct X_USER_PROFILE_SETTING_HEADER { + xe::be setting_id; + xe::be unknown_1; + xe::be setting_type; + char unknown_2[3]; + xe::be unknown_3; + union { - xe::be s32; - xe::be s64; - xe::be u32; - xe::be f64; - struct { - xe::be size; - xe::be ptr; - } unicode; - xe::be f32; - struct { - xe::be size; - xe::be ptr; - } binary; - xe::be filetime; + // Size is used only for types: CONTENT, WSTRING, BINARY + be size; + // Raw values that can be written. They do not need to be serialized. + be s32; + be s64; + be u32; + be f64; + be f32; }; }; -static_assert_size(X_USER_PROFILE_SETTING_DATA, 16); +static_assert_size(X_USER_PROFILE_SETTING_HEADER, 0x18); struct X_USER_PROFILE_SETTING { xe::be from; - xe::be unk04; union { xe::be user_index; xe::be xuid; }; xe::be setting_id; - xe::be unk14; union { - uint8_t data_bytes[sizeof(X_USER_PROFILE_SETTING_DATA)]; - X_USER_PROFILE_SETTING_DATA data; + uint8_t data_bytes[sizeof(X_USER_DATA)]; + X_USER_DATA data; }; }; static_assert_size(X_USER_PROFILE_SETTING, 40); +class UserSetting { + public: + template + UserSetting(uint32_t setting_id, T data) { + header_.setting_id = setting_id; + + setting_id_.value = setting_id; + CreateUserData(setting_id, data); + } + + static bool is_title_specific(uint32_t setting_id) { + return (setting_id & 0x3F00) == 0x3F00; + } + + bool is_title_specific() const { + return is_title_specific(setting_id_.value); + } + + const uint32_t GetSettingId() const { return setting_id_.value; } + const X_USER_PROFILE_SETTING_SOURCE GetSettingSource() const { + return created_by_; + } + const X_USER_PROFILE_SETTING_HEADER* GetSettingHeader() const { + return &header_; + } + UserData* GetSettingData() { return user_data_.get(); } + + void SetNewSettingSource(X_USER_PROFILE_SETTING_SOURCE new_source) { + created_by_ = new_source; + } + + void SetNewSettingHeader(X_USER_PROFILE_SETTING_HEADER* header) { + header_ = *header; + } + + private: + void CreateUserData(uint32_t setting_id, uint32_t data) { + header_.setting_type = static_cast(X_USER_DATA_TYPE::INT32); + header_.s64 = data; + user_data_ = std::make_unique(data); + } + void CreateUserData(uint32_t setting_id, int32_t data) { + header_.setting_type = static_cast(X_USER_DATA_TYPE::INT32); + header_.s32 = data; + user_data_ = std::make_unique(data); + } + void CreateUserData(uint32_t setting_id, float data) { + header_.setting_type = static_cast(X_USER_DATA_TYPE::FLOAT); + header_.f32 = data; + user_data_ = std::make_unique(data); + } + void CreateUserData(uint32_t setting_id, double data) { + header_.setting_type = static_cast(X_USER_DATA_TYPE::DOUBLE); + header_.f64 = data; + user_data_ = std::make_unique(data); + } + void CreateUserData(uint32_t setting_id, int64_t data) { + header_.setting_type = static_cast(X_USER_DATA_TYPE::INT64); + header_.s64 = data; + user_data_ = std::make_unique(data); + } + void CreateUserData(uint32_t setting_id, const std::u16string& data) { + header_.setting_type = static_cast(X_USER_DATA_TYPE::WSTRING); + header_.size = + std::min(kMaxSettingSize, static_cast((data.size() + 1) * 2)); + user_data_ = std::make_unique(data); + } + void CreateUserData(uint32_t setting_id, const std::vector& data) { + header_.setting_type = static_cast(X_USER_DATA_TYPE::BINARY); + header_.size = + std::min(kMaxSettingSize, static_cast(data.size())); + user_data_ = std::make_unique(data); + } + + X_USER_PROFILE_SETTING_SOURCE created_by_ = + X_USER_PROFILE_SETTING_SOURCE::DEFAULT; + + X_USER_PROFILE_SETTING_HEADER header_ = {}; + UserData::Key setting_id_ = {}; + std::unique_ptr user_data_ = nullptr; +}; + class UserProfile { public: - class SettingByteStream : public ByteStream { - public: - SettingByteStream(uint32_t ptr, uint8_t* data, size_t data_length, - size_t offset = 0) - : ByteStream(data, data_length, offset), ptr_(ptr) {} - - uint32_t ptr() const { return static_cast(ptr_ + offset()); } - - private: - uint32_t ptr_; - }; - struct Setting { - enum class Type { - CONTENT = 0, - INT32 = 1, - INT64 = 2, - DOUBLE = 3, - WSTRING = 4, - FLOAT = 5, - BINARY = 6, - DATETIME = 7, - UNSET = 0xFF, - }; - union Key { - uint32_t value; - struct { - uint32_t id : 14; - uint32_t unk : 2; - uint32_t size : 12; - uint32_t type : 4; - }; - }; - uint32_t setting_id; - Type type; - size_t size; - bool is_set; - uint32_t loaded_title_id; - Setting(uint32_t setting_id, Type type, size_t size, bool is_set) - : setting_id(setting_id), - type(type), - size(size), - is_set(is_set), - loaded_title_id(0) {} - virtual void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) { - data->type = static_cast(type); - } - virtual std::vector Serialize() const { - return std::vector(); - } - virtual void Deserialize(std::vector) {} - bool is_title_specific() const { return (setting_id & 0x3F00) == 0x3F00; } - }; - struct Int32Setting : public Setting { - Int32Setting(uint32_t setting_id, int32_t value) - : Setting(setting_id, Type::INT32, 4, true), value(value) {} - int32_t value; - void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) override { - Setting::Append(data, stream); - data->s32 = value; - } - }; - struct Int64Setting : public Setting { - Int64Setting(uint32_t setting_id, int64_t value) - : Setting(setting_id, Type::INT64, 8, true), value(value) {} - int64_t value; - void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) override { - Setting::Append(data, stream); - data->s64 = value; - } - }; - struct DoubleSetting : public Setting { - DoubleSetting(uint32_t setting_id, double value) - : Setting(setting_id, Type::DOUBLE, 8, true), value(value) {} - double value; - void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) override { - Setting::Append(data, stream); - data->f64 = value; - } - }; - struct UnicodeSetting : public Setting { - UnicodeSetting(uint32_t setting_id, const std::u16string& value) - : Setting(setting_id, Type::WSTRING, 8, true), value(value) {} - std::u16string value; - void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) override { - Setting::Append(data, stream); - if (value.empty()) { - data->unicode.size = 0; - data->unicode.ptr = 0; - } else { - size_t count = value.size() + 1; - size_t size = 2 * count; - assert_true(size <= std::numeric_limits::max()); - data->unicode.size = static_cast(size); - data->unicode.ptr = stream->ptr(); - auto buffer = - reinterpret_cast(&stream->data()[stream->offset()]); - stream->Advance(size); - xe::copy_and_swap(buffer, (uint16_t*)value.data(), count); - } - } - }; - struct FloatSetting : public Setting { - FloatSetting(uint32_t setting_id, float value) - : Setting(setting_id, Type::FLOAT, 4, true), value(value) {} - float value; - void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) override { - Setting::Append(data, stream); - data->f32 = value; - } - }; - struct BinarySetting : public Setting { - BinarySetting(uint32_t setting_id) - : Setting(setting_id, Type::BINARY, 8, false), value() {} - BinarySetting(uint32_t setting_id, const std::vector& value) - : Setting(setting_id, Type::BINARY, 8, true), value(value) {} - std::vector value; - void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) override { - Setting::Append(data, stream); - if (value.empty()) { - data->binary.size = 0; - data->binary.ptr = 0; - } else { - size_t size = value.size(); - assert_true(size <= std::numeric_limits::max()); - data->binary.size = static_cast(size); - data->binary.ptr = stream->ptr(); - stream->Write(value.data(), size); - } - } - std::vector Serialize() const override { - return std::vector(value.data(), value.data() + value.size()); - } - void Deserialize(std::vector data) override { - value = data; - is_set = true; - } - }; - struct DateTimeSetting : public Setting { - DateTimeSetting(uint32_t setting_id, int64_t value) - : Setting(setting_id, Type::DATETIME, 8, true), value(value) {} - int64_t value; - void Append(X_USER_PROFILE_SETTING_DATA* data, - SettingByteStream* stream) override { - Setting::Append(data, stream); - data->filetime = value; - } - }; - UserProfile(uint8_t index); uint64_t xuid() const { return xuid_; } @@ -230,19 +159,19 @@ class UserProfile { uint32_t signin_state() const { return 1; } uint32_t type() const { return 1 | 2; /* local | online profile? */ } - void AddSetting(std::unique_ptr setting); - Setting* GetSetting(uint32_t setting_id); + void AddSetting(std::unique_ptr setting); + UserSetting* GetSetting(uint32_t setting_id); std::map contexts_; private: uint64_t xuid_; std::string name_; - std::vector> setting_list_; - std::unordered_map settings_; + std::vector> setting_list_; + std::unordered_map settings_; - void LoadSetting(UserProfile::Setting*); - void SaveSetting(UserProfile::Setting*); + void LoadSetting(UserSetting*); + void SaveSetting(UserSetting*); }; } // namespace xam diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index d2b11c5ba..11f901dc6 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -199,11 +199,11 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, uint32_t needed_data_size = 0; for (uint32_t i = 0; i < setting_count; ++i) { needed_header_size += sizeof(X_USER_PROFILE_SETTING); - UserProfile::Setting::Key setting_key; + UserData::Key setting_key; setting_key.value = static_cast(setting_ids[i]); - switch (static_cast(setting_key.type)) { - case UserProfile::Setting::Type::WSTRING: - case UserProfile::Setting::Type::BINARY: + switch (static_cast(setting_key.type)) { + case X_USER_DATA_TYPE::WSTRING: + case X_USER_DATA_TYPE::BINARY: needed_data_size += setting_key.size; break; default: @@ -286,7 +286,7 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, out_header->settings_ptr = kernel_state()->memory()->HostToGuestVirtual(out_setting); - UserProfile::SettingByteStream out_stream( + DataByteStream out_stream( kernel_state()->memory()->HostToGuestVirtual(buffer), buffer, buffer_size, needed_header_size); for (uint32_t n = 0; n < setting_count; ++n) { @@ -294,27 +294,20 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, auto setting = user_profile->GetSetting(setting_id); std::memset(out_setting, 0, sizeof(X_USER_PROFILE_SETTING)); - out_setting->from = !setting || !setting->is_set ? 0 - : setting->is_title_specific() ? 2 - : 1; + out_setting->from = + !setting ? 0 : static_cast(setting->GetSettingSource()); if (xuids) { out_setting->xuid = user_profile->xuid(); } else { out_setting->xuid = -1; - out_setting->user_index = static_cast(user_index); + out_setting->user_index = user_index; } out_setting->setting_id = setting_id; if (setting) { - out_setting->from = 1; - out_setting->data.type = uint8_t(setting->type); - if (setting->is_set) { - if (setting->is_title_specific()) { - out_setting->from = 2; - } - - setting->Append(&out_setting->data, &out_stream); - } + out_setting->data.type = static_cast( + setting->GetSettingHeader()->setting_type.value); + setting->GetSettingData()->Append(&out_setting->data, &out_stream); } ++out_setting; } @@ -372,9 +365,8 @@ dword_result_t XamUserWriteProfileSettings_entry( for (uint32_t n = 0; n < setting_count; ++n) { const X_USER_PROFILE_SETTING& setting = settings[n]; - auto setting_type = - static_cast(setting.data.type); - if (setting_type == UserProfile::Setting::Type::UNSET) { + auto setting_type = static_cast(setting.data.type); + if (setting_type == X_USER_DATA_TYPE::UNSET) { continue; } @@ -385,21 +377,12 @@ dword_result_t XamUserWriteProfileSettings_entry( setting.data.type); switch (setting_type) { - case UserProfile::Setting::Type::CONTENT: - case UserProfile::Setting::Type::BINARY: { - UserProfile::Setting::Key setting_key; - setting_key.value = static_cast(setting.setting_id); - + case X_USER_DATA_TYPE::CONTENT: + case X_USER_DATA_TYPE::BINARY: { uint8_t* binary_ptr = kernel_state()->memory()->TranslateVirtual(setting.data.binary.ptr); size_t binary_size = setting.data.binary.size; - if (setting_key.size < binary_size) { - XELOGW( - "XamUserWriteProfileSettings: binary size > key size. Shrinking " - "binary size!"); - binary_size = setting_key.size; - } std::vector bytes; if (setting.data.binary.ptr) { // Copy provided data @@ -409,16 +392,19 @@ dword_result_t XamUserWriteProfileSettings_entry( // Data pointer was NULL, so just fill with zeroes bytes.resize(binary_size, 0); } - user_profile->AddSetting( - std::make_unique( - setting.setting_id, bytes)); + + auto user_setting = + std::make_unique(setting.setting_id, bytes); + + user_setting->SetNewSettingSource(X_USER_PROFILE_SETTING_SOURCE::TITLE); + user_profile->AddSetting(std::move(user_setting)); } break; - case UserProfile::Setting::Type::WSTRING: - case UserProfile::Setting::Type::DOUBLE: - case UserProfile::Setting::Type::FLOAT: - case UserProfile::Setting::Type::INT32: - case UserProfile::Setting::Type::INT64: - case UserProfile::Setting::Type::DATETIME: + case X_USER_DATA_TYPE::WSTRING: + case X_USER_DATA_TYPE::DOUBLE: + case X_USER_DATA_TYPE::FLOAT: + case X_USER_DATA_TYPE::INT32: + case X_USER_DATA_TYPE::INT64: + case X_USER_DATA_TYPE::DATETIME: default: { XELOGE("XamUserWriteProfileSettings: Unimplemented data type {}", setting_type);