mii: Prepare Interface for new implementation

This commit is contained in:
german77 2023-09-11 00:58:46 -06:00
parent 571399930c
commit bd169f417f
6 changed files with 210 additions and 138 deletions

View file

@ -85,15 +85,18 @@ void MiiEdit::Execute() {
break; break;
case MiiEditAppletMode::CreateMii: case MiiEditAppletMode::CreateMii:
case MiiEditAppletMode::EditMii: { case MiiEditAppletMode::EditMii: {
Service::Mii::MiiManager mii_manager; Mii::CharInfo char_info{};
Mii::StoreData store_data{};
store_data.BuildBase(Mii::Gender::Male);
char_info.SetFromStoreData(store_data);
const MiiEditCharInfo char_info{ const MiiEditCharInfo edit_char_info{
.mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
? applet_input_v4.char_info.mii_info ? applet_input_v4.char_info.mii_info
: mii_manager.BuildBase(Mii::Gender::Male)}, : char_info},
}; };
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info); MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
break; break;
} }
default: default:

View file

@ -15,8 +15,8 @@ namespace Service::Mii {
class IDatabaseService final : public ServiceFramework<IDatabaseService> { class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public: public:
explicit IDatabaseService(Core::System& system_) explicit IDatabaseService(Core::System& system_, bool is_system_)
: ServiceFramework{system_, "IDatabaseService"} { : ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IDatabaseService::IsUpdated, "IsUpdated"}, {0, &IDatabaseService::IsUpdated, "IsUpdated"},
@ -53,34 +53,27 @@ public:
} }
private: private:
template <typename T>
std::vector<u8> SerializeArray(const std::vector<T>& values) {
std::vector<u8> out(values.size() * sizeof(T));
std::size_t offset{};
for (const auto& value : values) {
std::memcpy(out.data() + offset, &value, sizeof(T));
offset += sizeof(T);
}
return out;
}
void IsUpdated(HLERequestContext& ctx) { void IsUpdated(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const bool is_updated = manager.IsUpdated(metadata, source_flag);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter)); rb.Push<u8>(is_updated);
} }
void IsFullDatabase(HLERequestContext& ctx) { void IsFullDatabase(HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called"); LOG_DEBUG(Service_Mii, "called");
const bool is_full_database = manager.IsFullDatabase();
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(manager.IsFullDatabase()); rb.Push<u8>(is_full_database);
} }
void GetCount(HLERequestContext& ctx) { void GetCount(HLERequestContext& ctx) {
@ -89,57 +82,63 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const u32 mii_count = manager.GetCount(metadata, source_flag);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<u32>(manager.GetCount(source_flag)); rb.Push(mii_count);
} }
void Get(HLERequestContext& ctx) { void Get(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
const auto default_miis{manager.GetDefault(source_flag)}; u32 mii_count{};
if (default_miis.size() > 0) { std::vector<CharInfoElement> char_info_elements(output_size);
ctx.WriteBuffer(SerializeArray(default_miis)); Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
if (mii_count != 0) {
ctx.WriteBuffer(char_info_elements);
} }
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(result);
rb.Push<u32>(static_cast<u32>(default_miis.size())); rb.Push(mii_count);
} }
void Get1(HLERequestContext& ctx) { void Get1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
const auto default_miis{manager.GetDefault(source_flag)}; u32 mii_count{};
std::vector<CharInfo> char_info(output_size);
Result result = manager.Get(metadata, char_info, mii_count, source_flag);
std::vector<CharInfo> values; if (mii_count != 0) {
for (const auto& element : default_miis) { ctx.WriteBuffer(char_info);
values.emplace_back(element.char_info);
} }
ctx.WriteBuffer(SerializeArray(values));
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(result);
rb.Push<u32>(static_cast<u32>(default_miis.size())); rb.Push(mii_count);
} }
void UpdateLatest(HLERequestContext& ctx) { void UpdateLatest(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<CharInfo>()}; const auto char_info{rp.PopRaw<CharInfo>()};
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
CharInfo new_char_info{}; CharInfo new_char_info{};
const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)}; const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
if (result != ResultSuccess) { if (result.IsFailure()) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result); rb.Push(result);
return; return;
@ -152,7 +151,6 @@ private:
void BuildRandom(HLERequestContext& ctx) { void BuildRandom(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto age{rp.PopRaw<Age>()}; const auto age{rp.PopRaw<Age>()};
const auto gender{rp.PopRaw<Gender>()}; const auto gender{rp.PopRaw<Gender>()};
const auto race{rp.PopRaw<Race>()}; const auto race{rp.PopRaw<Race>()};
@ -162,46 +160,47 @@ private:
if (age > Age::All) { if (age > Age::All) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultInvalidArgument); rb.Push(ResultInvalidArgument);
LOG_ERROR(Service_Mii, "invalid age={}", age);
return; return;
} }
if (gender > Gender::All) { if (gender > Gender::All) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultInvalidArgument); rb.Push(ResultInvalidArgument);
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
return; return;
} }
if (race > Race::All) { if (race > Race::All) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultInvalidArgument); rb.Push(ResultInvalidArgument);
LOG_ERROR(Service_Mii, "invalid race={}", race);
return; return;
} }
CharInfo char_info{};
manager.BuildRandom(char_info, age, gender, race);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race)); rb.PushRaw<CharInfo>(char_info);
} }
void BuildDefault(HLERequestContext& ctx) { void BuildDefault(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto index{rp.Pop<u32>()}; const auto index{rp.Pop<u32>()};
LOG_DEBUG(Service_Mii, "called with index={}", index); LOG_INFO(Service_Mii, "called with index={}", index);
if (index > 5) { if (index > 5) {
LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
index);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultInvalidArgument); rb.Push(ResultInvalidArgument);
return; return;
} }
CharInfo char_info{};
manager.BuildDefault(char_info, index);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(manager.BuildDefault(index)); rb.PushRaw<CharInfo>(char_info);
} }
void GetIndex(HLERequestContext& ctx) { void GetIndex(HLERequestContext& ctx) {
@ -210,19 +209,21 @@ private:
LOG_DEBUG(Service_Mii, "called"); LOG_DEBUG(Service_Mii, "called");
u32 index{}; s32 index{};
const Result result = manager.GetIndex(metadata, info, index);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(manager.GetIndex(info, index)); rb.Push(result);
rb.Push(index); rb.Push(index);
} }
void SetInterfaceVersion(HLERequestContext& ctx) { void SetInterfaceVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
current_interface_version = rp.PopRaw<u32>(); const auto interface_version{rp.PopRaw<u32>()};
LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version); LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);
UNIMPLEMENTED_IF(current_interface_version != 1); manager.SetInterfaceVersion(metadata, interface_version);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -230,30 +231,27 @@ private:
void Convert(HLERequestContext& ctx) { void Convert(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
LOG_INFO(Service_Mii, "called"); LOG_INFO(Service_Mii, "called");
CharInfo char_info{};
manager.ConvertV3ToCharInfo(char_info, mii_v3);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3)); rb.PushRaw<CharInfo>(char_info);
} }
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const { MiiManager manager{};
return current_interface_version >= interface_version; DatabaseSessionMetadata metadata{};
} bool is_system{};
MiiManager manager;
u32 current_interface_version{};
u64 current_update_counter{};
}; };
class MiiDBModule final : public ServiceFramework<MiiDBModule> { class MiiDBModule final : public ServiceFramework<MiiDBModule> {
public: public:
explicit MiiDBModule(Core::System& system_, const char* name_) explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_)
: ServiceFramework{system_, name_} { : ServiceFramework{system_, name_}, is_system{is_system_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
@ -267,10 +265,12 @@ private:
void GetDatabaseService(HLERequestContext& ctx) { void GetDatabaseService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IDatabaseService>(system); rb.PushIpcInterface<IDatabaseService>(system, is_system);
LOG_DEBUG(Service_Mii, "called"); LOG_DEBUG(Service_Mii, "called");
} }
bool is_system{};
}; };
class MiiImg final : public ServiceFramework<MiiImg> { class MiiImg final : public ServiceFramework<MiiImg> {
@ -302,8 +302,10 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("mii:e", std::make_shared<MiiDBModule>(system, "mii:e")); server_manager->RegisterNamedService("mii:e",
server_manager->RegisterNamedService("mii:u", std::make_shared<MiiDBModule>(system, "mii:u")); std::make_shared<MiiDBModule>(system, "mii:e", true));
server_manager->RegisterNamedService("mii:u",
std::make_shared<MiiDBModule>(system, "mii:u", false));
server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system)); server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }

View file

@ -16,45 +16,18 @@
#include "core/hle/service/mii/types/raw_data.h" #include "core/hle/service/mii/types/raw_data.h"
namespace Service::Mii { namespace Service::Mii {
namespace {
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
CharInfo ConvertStoreDataToInfo(const StoreData& data) { MiiManager::MiiManager() {}
CharInfo char_info{};
char_info.SetFromStoreData(data);
return char_info;
}
StoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
StoreData store_data{};
store_data.BuildRandom(age, gender, race);
return store_data;
}
StoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
StoreData store_data{};
store_data.BuildDefault(0);
return store_data;
}
} // namespace
MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {}
bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return false; return false;
} }
const bool result{current_update_counter != update_counter}; const auto metadata_update_counter = metadata.update_counter;
metadata.update_counter = update_counter;
current_update_counter = update_counter; return metadata_update_counter != update_counter;
return result;
} }
bool MiiManager::IsFullDatabase() const { bool MiiManager::IsFullDatabase() const {
@ -62,19 +35,19 @@ bool MiiManager::IsFullDatabase() const {
return false; return false;
} }
u32 MiiManager::GetCount(SourceFlag source_flag) const { u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
std::size_t count{}; u32 mii_count{};
if ((source_flag & SourceFlag::Database) != SourceFlag::None) { if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
mii_count += DefaultMiiCount;
}
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
// TODO(bunnei): We don't implement the Mii database, but when we do, update this // TODO(bunnei): We don't implement the Mii database, but when we do, update this
count += 0;
} }
if ((source_flag & SourceFlag::Default) != SourceFlag::None) { return mii_count;
count += DefaultMiiCount;
}
return static_cast<u32>(count);
} }
Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) { Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
const CharInfo& char_info, SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ResultNotFound; return ResultNotFound;
} }
@ -83,48 +56,117 @@ Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, Source
return ResultNotFound; return ResultNotFound;
} }
CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); StoreData store_data{};
store_data.BuildDefault(index);
out_char_info.SetFromStoreData(store_data);
} }
CharInfo MiiManager::BuildBase(Gender gender) { void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const {
const std::size_t index = gender == Gender::Female ? 1 : 0; StoreData store_data{};
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::BaseMii.at(index), user_id)); store_data.BuildBase(gender);
out_char_info.SetFromStoreData(store_data);
} }
CharInfo MiiManager::BuildDefault(std::size_t index) { void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const {
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); StoreData store_data{};
store_data.BuildRandom(age, gender, race);
out_char_info.SetFromStoreData(store_data);
} }
CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
CharInfo char_info{};
StoreData store_data{}; StoreData store_data{};
mii_v3.BuildToStoreData(store_data); mii_v3.BuildToStoreData(store_data);
char_info.SetFromStoreData(store_data); out_char_info.SetFromStoreData(store_data);
return char_info;
} }
std::vector<CharInfoElement> MiiManager::GetDefault(SourceFlag source_flag) { Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
std::vector<CharInfoElement> result; std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_elements, out_count, source_flag);
}
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
// Include default Mii at the end of the list
return BuildDefault(out_elements, out_count, source_flag);
}
Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
u32& out_count, SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return BuildDefault(out_char_info, out_count, source_flag);
}
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
// Include default Mii at the end of the list
return BuildDefault(out_char_info, out_count, source_flag);
}
Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) { if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return result; return ResultSuccess;
} }
for (std::size_t index = 0; index < DefaultMiiCount; index++) { StoreData store_data{};
result.emplace_back(BuildDefault(index), Source::Default);
for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
} }
return result; store_data.BuildDefault(static_cast<u32>(index));
out_elements[out_count].source = Source::Default;
out_elements[out_count].char_info.SetFromStoreData(store_data);
out_count++;
}
return ResultSuccess;
} }
Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) { Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return ResultSuccess;
}
StoreData store_data{};
for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
return ResultInvalidArgumentSize;
}
store_data.BuildDefault(static_cast<u32>(index));
out_char_info[out_count].SetFromStoreData(store_data);
out_count++;
}
return ResultSuccess;
}
Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
s32& out_index) {
if (char_info.Verify() != 0) {
return ResultInvalidCharInfo;
}
constexpr u32 INVALID_INDEX{0xFFFFFFFF}; constexpr u32 INVALID_INDEX{0xFFFFFFFF};
index = INVALID_INDEX; out_index = INVALID_INDEX;
// TODO(bunnei): We don't implement the Mii database, so we can't have an index // TODO(bunnei): We don't implement the Mii database, so we can't have an index
return ResultNotFound; return ResultNotFound;
} }
void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) {
metadata.interface_version = version;
}
} // namespace Service::Mii } // namespace Service::Mii

View file

@ -19,16 +19,24 @@ class MiiManager {
public: public:
MiiManager(); MiiManager();
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
bool IsFullDatabase() const; bool IsFullDatabase() const;
u32 GetCount(SourceFlag source_flag) const; u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
Result UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag); Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
CharInfo BuildRandom(Age age, Gender gender, Race race); const CharInfo& char_info, SourceFlag source_flag);
CharInfo BuildBase(Gender gender); Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements,
CharInfo BuildDefault(std::size_t index); u32& out_count, SourceFlag source_flag);
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
u32& out_count, SourceFlag source_flag);
void BuildDefault(CharInfo& out_char_info, u32 index) const;
void BuildBase(CharInfo& out_char_info, Gender gender) const;
void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const;
void ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const;
std::vector<CharInfoElement> GetDefault(SourceFlag source_flag); std::vector<CharInfoElement> GetDefault(SourceFlag source_flag);
Result GetIndex(const CharInfo& info, u32& index); Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
s32& out_index);
void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version);
struct MiiDatabase { struct MiiDatabase {
u32 magic{}; // 'NFDB' u32 magic{}; // 'NFDB'
@ -40,7 +48,10 @@ public:
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
private: private:
const Common::UUID user_id{}; Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
SourceFlag source_flag);
Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag);
u64 update_counter{}; u64 update_counter{};
}; };

View file

@ -165,4 +165,14 @@ struct DefaultMii {
}; };
static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size."); static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
struct DatabaseSessionMetadata {
u32 interface_version;
u32 magic;
u64 update_counter;
bool IsInterfaceVersionSupported(u32 version) const {
return version <= interface_version;
}
};
} // namespace Service::Mii } // namespace Service::Mii

View file

@ -680,12 +680,16 @@ Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const {
return ResultRegistrationIsNotInitialized; return ResultRegistrationIsNotInitialized;
} }
Service::Mii::MiiManager manager; Mii::CharInfo char_info{};
Mii::StoreData store_data{};
tag_data.owner_mii.BuildToStoreData(store_data);
char_info.SetFromStoreData(store_data);
const auto& settings = tag_data.settings; const auto& settings = tag_data.settings;
// TODO: Validate this data // TODO: Validate this data
register_info = { register_info = {
.mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), .mii_char_info = char_info,
.creation_date = settings.init_date.GetWriteDate(), .creation_date = settings.init_date.GetWriteDate(),
.amiibo_name = GetAmiiboName(settings), .amiibo_name = GetAmiiboName(settings),
.font_region = settings.settings.font_region, .font_region = settings.settings.font_region,