Преглед изворни кода

service: mii: Implement the rest of the service

german77 пре 2 година
родитељ
комит
2fb71aecb0
3 измењених фајлова са 751 додато и 103 уклоњено
  1. 329 33
      src/core/hle/service/mii/mii.cpp
  2. 362 48
      src/core/hle/service/mii/mii_manager.cpp
  3. 60 22
      src/core/hle/service/mii/mii_manager.h

+ 329 - 33
src/core/hle/service/mii/mii.cpp

@@ -8,6 +8,9 @@
 #include "core/hle/service/mii/mii.h"
 #include "core/hle/service/mii/mii_manager.h"
 #include "core/hle/service/mii/mii_result.h"
+#include "core/hle/service/mii/types/char_info.h"
+#include "core/hle/service/mii/types/store_data.h"
+#include "core/hle/service/mii/types/ver3_store_data.h"
 #include "core/hle/service/server_manager.h"
 #include "core/hle/service/service.h"
 
@@ -27,29 +30,31 @@ public:
             {5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
             {6, &IDatabaseService::BuildRandom, "BuildRandom"},
             {7, &IDatabaseService::BuildDefault, "BuildDefault"},
-            {8, nullptr, "Get2"},
-            {9, nullptr, "Get3"},
-            {10, nullptr, "UpdateLatest1"},
-            {11, nullptr, "FindIndex"},
-            {12, nullptr, "Move"},
-            {13, nullptr, "AddOrReplace"},
-            {14, nullptr, "Delete"},
-            {15, nullptr, "DestroyFile"},
-            {16, nullptr, "DeleteFile"},
-            {17, nullptr, "Format"},
+            {8, &IDatabaseService::Get2, "Get2"},
+            {9, &IDatabaseService::Get3, "Get3"},
+            {10, &IDatabaseService::UpdateLatest1, "UpdateLatest1"},
+            {11, &IDatabaseService::FindIndex, "FindIndex"},
+            {12, &IDatabaseService::Move, "Move"},
+            {13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
+            {14, &IDatabaseService::Delete, "Delete"},
+            {15, &IDatabaseService::DestroyFile, "DestroyFile"},
+            {16, &IDatabaseService::DeleteFile, "DeleteFile"},
+            {17, &IDatabaseService::Format, "Format"},
             {18, nullptr, "Import"},
             {19, nullptr, "Export"},
-            {20, nullptr, "IsBrokenDatabaseWithClearFlag"},
+            {20, &IDatabaseService::IsBrokenDatabaseWithClearFlag, "IsBrokenDatabaseWithClearFlag"},
             {21, &IDatabaseService::GetIndex, "GetIndex"},
             {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
             {23, &IDatabaseService::Convert, "Convert"},
-            {24, nullptr, "ConvertCoreDataToCharInfo"},
-            {25, nullptr, "ConvertCharInfoToCoreData"},
-            {26, nullptr, "Append"},
+            {24, &IDatabaseService::ConvertCoreDataToCharInfo, "ConvertCoreDataToCharInfo"},
+            {25, &IDatabaseService::ConvertCharInfoToCoreData, "ConvertCharInfoToCoreData"},
+            {26,  &IDatabaseService::Append, "Append"},
         };
         // clang-format on
 
         RegisterHandlers(functions);
+
+        manager.Initialize(metadata);
     }
 
 private:
@@ -80,10 +85,10 @@ private:
         IPC::RequestParser rp{ctx};
         const auto source_flag{rp.PopRaw<SourceFlag>()};
 
-        LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
-
         const u32 mii_count = manager.GetCount(metadata, source_flag);
 
+        LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count);
+
         IPC::ResponseBuilder rb{ctx, 3};
         rb.Push(ResultSuccess);
         rb.Push(mii_count);
@@ -94,16 +99,17 @@ private:
         const auto source_flag{rp.PopRaw<SourceFlag>()};
         const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()};
 
-        LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
-
         u32 mii_count{};
         std::vector<CharInfoElement> char_info_elements(output_size);
-        Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
+        const auto result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
 
         if (mii_count != 0) {
             ctx.WriteBuffer(char_info_elements);
         }
 
+        LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
+                 output_size, mii_count);
+
         IPC::ResponseBuilder rb{ctx, 3};
         rb.Push(result);
         rb.Push(mii_count);
@@ -114,16 +120,17 @@ private:
         const auto source_flag{rp.PopRaw<SourceFlag>()};
         const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()};
 
-        LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
-
         u32 mii_count{};
         std::vector<CharInfo> char_info(output_size);
-        Result result = manager.Get(metadata, char_info, mii_count, source_flag);
+        const auto result = manager.Get(metadata, char_info, mii_count, source_flag);
 
         if (mii_count != 0) {
             ctx.WriteBuffer(char_info);
         }
 
+        LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
+                 output_size, mii_count);
+
         IPC::ResponseBuilder rb{ctx, 3};
         rb.Push(result);
         rb.Push(mii_count);
@@ -134,7 +141,7 @@ private:
         const auto char_info{rp.PopRaw<CharInfo>()};
         const auto source_flag{rp.PopRaw<SourceFlag>()};
 
-        LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
+        LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
 
         CharInfo new_char_info{};
         const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
@@ -146,7 +153,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
         rb.Push(ResultSuccess);
-        rb.PushRaw<CharInfo>(new_char_info);
+        rb.PushRaw(new_char_info);
     }
 
     void BuildRandom(HLERequestContext& ctx) {
@@ -180,14 +187,14 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
         rb.Push(ResultSuccess);
-        rb.PushRaw<CharInfo>(char_info);
+        rb.PushRaw(char_info);
     }
 
     void BuildDefault(HLERequestContext& ctx) {
         IPC::RequestParser rp{ctx};
         const auto index{rp.Pop<u32>()};
 
-        LOG_INFO(Service_Mii, "called with index={}", index);
+        LOG_DEBUG(Service_Mii, "called with index={}", index);
 
         if (index > 5) {
             IPC::ResponseBuilder rb{ctx, 2};
@@ -200,7 +207,240 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
         rb.Push(ResultSuccess);
-        rb.PushRaw<CharInfo>(char_info);
+        rb.PushRaw(char_info);
+    }
+
+    void Get2(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto source_flag{rp.PopRaw<SourceFlag>()};
+        const auto output_size{ctx.GetWriteBufferNumElements<StoreDataElement>()};
+
+        u32 mii_count{};
+        std::vector<StoreDataElement> store_data_elements(output_size);
+        const auto result = manager.Get(metadata, store_data_elements, mii_count, source_flag);
+
+        if (mii_count != 0) {
+            ctx.WriteBuffer(store_data_elements);
+        }
+
+        LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
+                 output_size, mii_count);
+
+        IPC::ResponseBuilder rb{ctx, 3};
+        rb.Push(result);
+        rb.Push(mii_count);
+    }
+
+    void Get3(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto source_flag{rp.PopRaw<SourceFlag>()};
+        const auto output_size{ctx.GetWriteBufferNumElements<StoreData>()};
+
+        u32 mii_count{};
+        std::vector<StoreData> store_data(output_size);
+        const auto result = manager.Get(metadata, store_data, mii_count, source_flag);
+
+        if (mii_count != 0) {
+            ctx.WriteBuffer(store_data);
+        }
+
+        LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
+                 output_size, mii_count);
+
+        IPC::ResponseBuilder rb{ctx, 3};
+        rb.Push(result);
+        rb.Push(mii_count);
+    }
+
+    void UpdateLatest1(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto store_data{rp.PopRaw<StoreData>()};
+        const auto source_flag{rp.PopRaw<SourceFlag>()};
+
+        LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
+
+        Result result = ResultSuccess;
+        if (!is_system) {
+            result = ResultPermissionDenied;
+        }
+
+        StoreData new_store_data{};
+        if (result.IsSuccess()) {
+            result = manager.UpdateLatest(metadata, new_store_data, store_data, source_flag);
+        }
+
+        if (result.IsFailure()) {
+            IPC::ResponseBuilder rb{ctx, 2};
+            rb.Push(result);
+            return;
+        }
+
+        IPC::ResponseBuilder rb{ctx, 2 + sizeof(StoreData) / sizeof(u32)};
+        rb.Push(ResultSuccess);
+        rb.PushRaw<StoreData>(new_store_data);
+    }
+
+    void FindIndex(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto create_id{rp.PopRaw<Common::UUID>()};
+        const auto is_special{rp.PopRaw<bool>()};
+
+        LOG_INFO(Service_Mii, "called with create_id={}, is_special={}",
+                 create_id.FormattedString(), is_special);
+
+        s32 index = manager.FindIndex(create_id, is_special);
+
+        IPC::ResponseBuilder rb{ctx, 3};
+        rb.Push(ResultSuccess);
+        rb.Push(index);
+    }
+
+    void Move(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto create_id{rp.PopRaw<Common::UUID>()};
+        const auto new_index{rp.PopRaw<s32>()};
+
+        LOG_INFO(Service_Mii, "called with create_id={}, new_index={}", create_id.FormattedString(),
+                 new_index);
+
+        Result result = ResultSuccess;
+
+        if (!is_system) {
+            result = ResultPermissionDenied;
+        }
+
+        if (result.IsSuccess()) {
+            const u32 count = manager.GetCount(metadata, SourceFlag::Database);
+            if (new_index < 0 || new_index >= static_cast<s32>(count)) {
+                result = ResultInvalidArgument;
+            }
+        }
+
+        if (result.IsSuccess()) {
+            result = manager.Move(metadata, new_index, create_id);
+        }
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+    }
+
+    void AddOrReplace(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto store_data{rp.PopRaw<StoreData>()};
+
+        LOG_INFO(Service_Mii, "called");
+
+        Result result = ResultSuccess;
+
+        if (!is_system) {
+            result = ResultPermissionDenied;
+        }
+
+        if (result.IsSuccess()) {
+            result = manager.AddOrReplace(metadata, store_data);
+        }
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+    }
+
+    void Delete(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto create_id{rp.PopRaw<Common::UUID>()};
+
+        LOG_INFO(Service_Mii, "called, create_id={}", create_id.FormattedString());
+
+        Result result = ResultSuccess;
+
+        if (!is_system) {
+            result = ResultPermissionDenied;
+        }
+
+        if (result.IsSuccess()) {
+            result = manager.Delete(metadata, create_id);
+        }
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+    }
+
+    void DestroyFile(HLERequestContext& ctx) {
+        // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
+        bool is_db_test_mode_enabled = false;
+
+        LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
+
+        Result result = ResultSuccess;
+
+        if (!is_db_test_mode_enabled) {
+            result = ResultTestModeOnly;
+        }
+
+        if (result.IsSuccess()) {
+            result = manager.DestroyFile(metadata);
+        }
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+    }
+
+    void DeleteFile(HLERequestContext& ctx) {
+        // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
+        bool is_db_test_mode_enabled = false;
+
+        LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
+
+        Result result = ResultSuccess;
+
+        if (!is_db_test_mode_enabled) {
+            result = ResultTestModeOnly;
+        }
+
+        if (result.IsSuccess()) {
+            result = manager.DeleteFile();
+        }
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+    }
+
+    void Format(HLERequestContext& ctx) {
+        // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
+        bool is_db_test_mode_enabled = false;
+
+        LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
+
+        Result result = ResultSuccess;
+
+        if (!is_db_test_mode_enabled) {
+            result = ResultTestModeOnly;
+        }
+
+        if (result.IsSuccess()) {
+            result = manager.Format(metadata);
+        }
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+    }
+
+    void IsBrokenDatabaseWithClearFlag(HLERequestContext& ctx) {
+        LOG_DEBUG(Service_Mii, "called");
+
+        bool is_broken_with_clear_flag{};
+        Result result = ResultSuccess;
+
+        if (!is_system) {
+            result = ResultPermissionDenied;
+        }
+
+        if (result.IsSuccess()) {
+            is_broken_with_clear_flag = manager.IsBrokenWithClearFlag(metadata);
+        }
+
+        IPC::ResponseBuilder rb{ctx, 3};
+        rb.Push(result);
+        rb.Push<u8>(is_broken_with_clear_flag);
     }
 
     void GetIndex(HLERequestContext& ctx) {
@@ -236,13 +476,53 @@ private:
         LOG_INFO(Service_Mii, "called");
 
         CharInfo char_info{};
-        manager.ConvertV3ToCharInfo(char_info, mii_v3);
+        const auto result = manager.ConvertV3ToCharInfo(char_info, mii_v3);
 
         IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
-        rb.Push(ResultSuccess);
+        rb.Push(result);
+        rb.PushRaw<CharInfo>(char_info);
+    }
+
+    void ConvertCoreDataToCharInfo(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto core_data{rp.PopRaw<CoreData>()};
+
+        LOG_INFO(Service_Mii, "called");
+
+        CharInfo char_info{};
+        const auto result = manager.ConvertCoreDataToCharInfo(char_info, core_data);
+
+        IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
+        rb.Push(result);
         rb.PushRaw<CharInfo>(char_info);
     }
 
+    void ConvertCharInfoToCoreData(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto char_info{rp.PopRaw<CharInfo>()};
+
+        LOG_INFO(Service_Mii, "called");
+
+        CoreData core_data{};
+        const auto result = manager.ConvertCharInfoToCoreData(core_data, char_info);
+
+        IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)};
+        rb.Push(result);
+        rb.PushRaw<CoreData>(core_data);
+    }
+
+    void Append(HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const auto char_info{rp.PopRaw<CharInfo>()};
+
+        LOG_INFO(Service_Mii, "called");
+
+        const auto result = manager.Append(metadata, char_info);
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+    }
+
     MiiManager manager{};
     DatabaseSessionMetadata metadata{};
     bool is_system{};
@@ -267,7 +547,7 @@ private:
         rb.Push(ResultSuccess);
         rb.PushIpcInterface<IDatabaseService>(system, is_system);
 
-        LOG_DEBUG(Service_Mii, "called");
+        LOG_CRITICAL(Service_Mii, "called");
     }
 
     bool is_system{};
@@ -278,9 +558,9 @@ public:
     explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} {
         // clang-format off
         static const FunctionInfo functions[] = {
-            {0, nullptr, "Initialize"},
+            {0, &MiiImg::Initialize, "Initialize"},
             {10, nullptr, "Reload"},
-            {11, nullptr, "GetCount"},
+            {11, &MiiImg::GetCount, "GetCount"},
             {12, nullptr, "IsEmpty"},
             {13, nullptr, "IsFull"},
             {14, nullptr, "GetAttribute"},
@@ -297,6 +577,22 @@ public:
 
         RegisterHandlers(functions);
     }
+
+private:
+    void Initialize(HLERequestContext& ctx) {
+        LOG_CRITICAL(Service_Mii, "called");
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(ResultSuccess);
+    }
+
+    void GetCount(HLERequestContext& ctx) {
+        LOG_CRITICAL(Service_Mii, "called");
+
+        IPC::ResponseBuilder rb{ctx, 3};
+        rb.Push(ResultSuccess);
+        rb.Push(0);
+    }
 };
 
 void LoopProcess(Core::System& system) {
@@ -310,4 +606,4 @@ void LoopProcess(Core::System& system) {
     ServerManager::RunServer(std::move(server_manager));
 }
 
-} // namespace Service::Mii
+} // namespace Service::Mii

+ 362 - 48
src/core/hle/service/mii/mii_manager.cpp

@@ -1,38 +1,63 @@
 // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-#include <cstring>
-#include <random>
-
-#include "common/assert.h"
 #include "common/logging/log.h"
-#include "common/string_util.h"
-
-#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/mii/mii_database_manager.h"
 #include "core/hle/service/mii/mii_manager.h"
 #include "core/hle/service/mii/mii_result.h"
 #include "core/hle/service/mii/mii_util.h"
+#include "core/hle/service/mii/types/char_info.h"
 #include "core/hle/service/mii/types/core_data.h"
 #include "core/hle/service/mii/types/raw_data.h"
+#include "core/hle/service/mii/types/store_data.h"
+#include "core/hle/service/mii/types/ver3_store_data.h"
 
 namespace Service::Mii {
 constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
 
 MiiManager::MiiManager() {}
 
+Result MiiManager::Initialize(DatabaseSessionMetadata& metadata) {
+    database_manager.MountSaveData();
+    database_manager.Initialize(metadata, is_broken_with_clear_flag);
+    return ResultSuccess;
+}
+
+void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
+    StoreData store_data{};
+    store_data.BuildDefault(index);
+    out_char_info.SetFromStoreData(store_data);
+}
+
+void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const {
+    StoreData store_data{};
+    store_data.BuildBase(gender);
+    out_char_info.SetFromStoreData(store_data);
+}
+
+void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const {
+    StoreData store_data{};
+    store_data.BuildRandom(age, gender, race);
+    out_char_info.SetFromStoreData(store_data);
+}
+
+bool MiiManager::IsFullDatabase() const {
+    return database_manager.IsFullDatabase();
+}
+
+void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) const {
+    metadata.interface_version = version;
+}
+
 bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
     if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
         return false;
     }
 
-    const auto metadata_update_counter = metadata.update_counter;
-    metadata.update_counter = update_counter;
-    return metadata_update_counter != update_counter;
-}
-
-bool MiiManager::IsFullDatabase() const {
-    // TODO(bunnei): We don't implement the Mii database, so it cannot be full
-    return false;
+    const u64 metadata_update_counter = metadata.update_counter;
+    const u64 database_update_counter = database_manager.GetUpdateCounter();
+    metadata.update_counter = database_update_counter;
+    return metadata_update_counter != database_update_counter;
 }
 
 u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
@@ -41,72 +66,343 @@ u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag sou
         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
+        mii_count += database_manager.GetCount(metadata);
     }
     return mii_count;
 }
 
-Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
-                                const CharInfo& char_info, SourceFlag source_flag) {
-    if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+Result MiiManager::Move(DatabaseSessionMetadata& metadata, u32 index,
+                        const Common::UUID& create_id) {
+    const auto result = database_manager.Move(metadata, index, create_id);
+
+    if (result.IsFailure()) {
+        return result;
+    }
+
+    if (!database_manager.IsModified()) {
+        return ResultNotUpdated;
+    }
+
+    return database_manager.SaveDatabase();
+}
+
+Result MiiManager::AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& store_data) {
+    const auto result = database_manager.AddOrReplace(metadata, store_data);
+
+    if (result.IsFailure()) {
+        return result;
+    }
+
+    if (!database_manager.IsModified()) {
+        return ResultNotUpdated;
+    }
+
+    return database_manager.SaveDatabase();
+}
+
+Result MiiManager::Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id) {
+    const auto result = database_manager.Delete(metadata, create_id);
+
+    if (result.IsFailure()) {
+        return result;
+    }
+
+    if (!database_manager.IsModified()) {
+        return ResultNotUpdated;
+    }
+
+    return database_manager.SaveDatabase();
+}
+
+s32 MiiManager::FindIndex(const Common::UUID& create_id, bool is_special) const {
+    s32 index{};
+    const auto result = database_manager.FindIndex(index, create_id, is_special);
+    if (result.IsError()) {
+        index = -1;
+    }
+    return index;
+}
+
+Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
+                            s32& out_index) const {
+    if (char_info.Verify() != ValidationResult::NoErrors) {
+        return ResultInvalidCharInfo;
+    }
+
+    s32 index{};
+    Result result = {};
+    // FindIndex(index);
+
+    if (result.IsError()) {
+        return ResultNotFound;
+    }
+
+    if (index == -1) {
         return ResultNotFound;
     }
 
-    // TODO(bunnei): We don't implement the Mii database, so we can't have an entry
-    return ResultNotFound;
+    out_index = index;
+    return ResultSuccess;
 }
 
-void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
-    StoreData store_data{};
-    store_data.BuildDefault(index);
-    out_char_info.SetFromStoreData(store_data);
+Result MiiManager::Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info) {
+    const auto result = database_manager.Append(metadata, char_info);
+
+    if (result.IsError()) {
+        return ResultNotFound;
+    }
+
+    if (!database_manager.IsModified()) {
+        return ResultNotUpdated;
+    }
+
+    return database_manager.SaveDatabase();
 }
 
-void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const {
+bool MiiManager::IsBrokenWithClearFlag(DatabaseSessionMetadata& metadata) {
+    const bool is_broken = is_broken_with_clear_flag;
+    if (is_broken_with_clear_flag) {
+        is_broken_with_clear_flag = false;
+        database_manager.Format(metadata);
+        database_manager.SaveDatabase();
+    }
+    return is_broken;
+}
+
+Result MiiManager::DestroyFile(DatabaseSessionMetadata& metadata) {
+    is_broken_with_clear_flag = true;
+    return database_manager.DestroyFile(metadata);
+}
+
+Result MiiManager::DeleteFile() {
+    return database_manager.DeleteFile();
+}
+
+Result MiiManager::Format(DatabaseSessionMetadata& metadata) {
+    database_manager.Format(metadata);
+
+    if (!database_manager.IsModified()) {
+        return ResultNotUpdated;
+    }
+    return database_manager.SaveDatabase();
+}
+
+Result MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
+    if (!mii_v3.IsValid()) {
+        return ResultInvalidCharInfo;
+    }
+
     StoreData store_data{};
-    store_data.BuildBase(gender);
+    mii_v3.BuildToStoreData(store_data);
+    const auto name = store_data.GetNickname();
+    if (!MiiUtil::IsFontRegionValid(store_data.GetFontRegion(), name.data)) {
+        store_data.SetInvalidName();
+    }
+
     out_char_info.SetFromStoreData(store_data);
+    return ResultSuccess;
 }
 
-void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const {
+Result MiiManager::ConvertCoreDataToCharInfo(CharInfo& out_char_info,
+                                             const CoreData& core_data) const {
+    if (core_data.IsValid() != ValidationResult::NoErrors) {
+        return ResultInvalidCharInfo;
+    }
+
     StoreData store_data{};
-    store_data.BuildRandom(age, gender, race);
+    store_data.BuildWithCoreData(core_data);
+    const auto name = store_data.GetNickname();
+    if (!MiiUtil::IsFontRegionValid(store_data.GetFontRegion(), name.data)) {
+        store_data.SetInvalidName();
+    }
+
     out_char_info.SetFromStoreData(store_data);
+    return ResultSuccess;
+}
+
+Result MiiManager::ConvertCharInfoToCoreData(CoreData& out_core_data,
+                                             const CharInfo& char_info) const {
+    if (char_info.Verify() != ValidationResult::NoErrors) {
+        return ResultInvalidCharInfo;
+    }
+
+    out_core_data.BuildFromCharInfo(char_info);
+    const auto name = out_core_data.GetNickname();
+    if (!MiiUtil::IsFontRegionValid(out_core_data.GetFontRegion(), name.data)) {
+        out_core_data.SetNickname(out_core_data.GetInvalidNickname());
+    }
+
+    return ResultSuccess;
 }
 
-void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
+Result MiiManager::UpdateLatest(const DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
+                                const CharInfo& char_info, SourceFlag source_flag) const {
+    if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+        return ResultNotFound;
+    }
+
+    if (metadata.IsInterfaceVersionSupported(1)) {
+        if (char_info.Verify() != ValidationResult::NoErrors) {
+            return ResultInvalidCharInfo;
+        }
+    }
+
+    u32 index{};
+    Result result = database_manager.FindIndex(metadata, index, char_info.GetCreateId());
+
+    if (result.IsError()) {
+        return result;
+    }
+
     StoreData store_data{};
-    mii_v3.BuildToStoreData(store_data);
+    database_manager.Get(store_data, index, metadata);
+
+    if (store_data.GetType() != char_info.GetType()) {
+        return ResultNotFound;
+    }
+
     out_char_info.SetFromStoreData(store_data);
+
+    if (char_info == out_char_info) {
+        return ResultNotUpdated;
+    }
+
+    return ResultSuccess;
+}
+
+Result MiiManager::UpdateLatest(const DatabaseSessionMetadata& metadata, StoreData& out_store_data,
+                                const StoreData& store_data, SourceFlag source_flag) const {
+    if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+        return ResultNotFound;
+    }
+
+    if (metadata.IsInterfaceVersionSupported(1)) {
+        if (store_data.IsValid() != ValidationResult::NoErrors) {
+            return ResultInvalidCharInfo;
+        }
+    }
+
+    u32 index{};
+    Result result = database_manager.FindIndex(metadata, index, store_data.GetCreateId());
+
+    if (result.IsError()) {
+        return result;
+    }
+
+    database_manager.Get(out_store_data, index, metadata);
+
+    if (out_store_data.GetType() != store_data.GetType()) {
+        return ResultNotFound;
+    }
+
+    if (store_data == out_store_data) {
+        return ResultNotUpdated;
+    }
+
+    return ResultSuccess;
 }
 
 Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
                        std::span<CharInfoElement> out_elements, u32& out_count,
-                       SourceFlag source_flag) {
+                       SourceFlag source_flag) const {
     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
+    const auto mii_count = database_manager.GetCount(metadata);
+
+    for (std::size_t index = 0; index < mii_count; ++index) {
+        if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
+            return ResultInvalidArgumentSize;
+        }
+
+        StoreData store_data{};
+        database_manager.Get(store_data, index, metadata);
+
+        out_elements[out_count].source = Source::Database;
+        out_elements[out_count].char_info.SetFromStoreData(store_data);
+        out_count++;
+    }
 
     // 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) {
+                       u32& out_count, SourceFlag source_flag) const {
     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
+    const auto mii_count = database_manager.GetCount(metadata);
+
+    for (std::size_t index = 0; index < mii_count; ++index) {
+        if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
+            return ResultInvalidArgumentSize;
+        }
+
+        StoreData store_data{};
+        database_manager.Get(store_data, index, metadata);
+
+        out_char_info[out_count].SetFromStoreData(store_data);
+        out_count++;
+    }
 
     // Include default Mii at the end of the list
     return BuildDefault(out_char_info, out_count, source_flag);
 }
 
+Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
+                       std::span<StoreDataElement> out_elements, u32& out_count,
+                       SourceFlag source_flag) const {
+    if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+        return BuildDefault(out_elements, out_count, source_flag);
+    }
+
+    const auto mii_count = database_manager.GetCount(metadata);
+
+    for (std::size_t index = 0; index < mii_count; ++index) {
+        if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
+            return ResultInvalidArgumentSize;
+        }
+
+        StoreData store_data{};
+        database_manager.Get(store_data, index, metadata);
+
+        out_elements[out_count].store_data = store_data;
+        out_elements[out_count].source = Source::Database;
+        out_count++;
+    }
+
+    // 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<StoreData> out_store_data,
+                       u32& out_count, SourceFlag source_flag) const {
+    if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
+        return BuildDefault(out_store_data, out_count, source_flag);
+    }
+
+    const auto mii_count = database_manager.GetCount(metadata);
+
+    for (std::size_t index = 0; index < mii_count; ++index) {
+        if (out_store_data.size() <= static_cast<std::size_t>(out_count)) {
+            return ResultInvalidArgumentSize;
+        }
+
+        StoreData store_data{};
+        database_manager.Get(store_data, index, metadata);
+
+        out_store_data[out_count] = store_data;
+        out_count++;
+    }
+
+    // Include default Mii at the end of the list
+    return BuildDefault(out_store_data, out_count, source_flag);
+}
 Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
-                                SourceFlag source_flag) {
+                                SourceFlag source_flag) const {
     if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
         return ResultSuccess;
     }
@@ -129,7 +425,7 @@ Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& ou
 }
 
 Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
-                                SourceFlag source_flag) {
+                                SourceFlag source_flag) const {
     if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
         return ResultSuccess;
     }
@@ -150,23 +446,41 @@ Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_coun
     return ResultSuccess;
 }
 
-Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
-                            s32& out_index) {
-
-    if (char_info.Verify() != ValidationResult::NoErrors) {
-        return ResultInvalidCharInfo;
+Result MiiManager::BuildDefault(std::span<StoreDataElement> out_elements, u32& out_count,
+                                SourceFlag source_flag) const {
+    if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
+        return ResultSuccess;
     }
 
-    constexpr u32 INVALID_INDEX{0xFFFFFFFF};
+    for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
+        if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
+            return ResultInvalidArgumentSize;
+        }
 
-    out_index = INVALID_INDEX;
+        out_elements[out_count].store_data.BuildDefault(static_cast<u32>(index));
+        out_elements[out_count].source = Source::Default;
+        out_count++;
+    }
 
-    // TODO(bunnei): We don't implement the Mii database, so we can't have an index
-    return ResultNotFound;
+    return ResultSuccess;
 }
 
-void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) {
-    metadata.interface_version = version;
+Result MiiManager::BuildDefault(std::span<StoreData> out_char_info, u32& out_count,
+                                SourceFlag source_flag) const {
+    if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
+        return ResultSuccess;
+    }
+
+    for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
+        if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
+            return ResultInvalidArgumentSize;
+        }
+
+        out_char_info[out_count].BuildDefault(static_cast<u32>(index));
+        out_count++;
+    }
+
+    return ResultSuccess;
 }
 
 } // namespace Service::Mii

+ 60 - 22
src/core/hle/service/mii/mii_manager.h

@@ -3,47 +3,85 @@
 
 #pragma once
 
-#include <vector>
+#include <span>
 
 #include "core/hle/result.h"
+#include "core/hle/service/mii/mii_database_manager.h"
 #include "core/hle/service/mii/mii_types.h"
-#include "core/hle/service/mii/types/char_info.h"
-#include "core/hle/service/mii/types/store_data.h"
-#include "core/hle/service/mii/types/ver3_store_data.h"
 
 namespace Service::Mii {
+class CharInfo;
+class CoreData;
+class StoreData;
+class Ver3StoreData;
 
-// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
-// with providing an easy interface for HLE emulation of the mii service.
+struct CharInfoElement;
+struct StoreDataElement;
+
+// The Mii manager is responsible for handling mii operations along with providing an easy interface
+// for HLE emulation of the mii service.
 class MiiManager {
 public:
     MiiManager();
+    Result Initialize(DatabaseSessionMetadata& metadata);
 
-    bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
+    // Auto generated mii
+    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;
 
+    // Database operations
     bool IsFullDatabase() const;
+    void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) const;
+    bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
     u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
-    Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
-                        const CharInfo& char_info, SourceFlag source_flag);
+    Result Move(DatabaseSessionMetadata& metadata, u32 index, const Common::UUID& create_id);
+    Result AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& store_data);
+    Result Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id);
+    s32 FindIndex(const Common::UUID& create_id, bool is_special) const;
+    Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
+                    s32& out_index) const;
+    Result Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info);
+
+    // Test database operations
+    bool IsBrokenWithClearFlag(DatabaseSessionMetadata& metadata);
+    Result DestroyFile(DatabaseSessionMetadata& metadata);
+    Result DeleteFile();
+    Result Format(DatabaseSessionMetadata& metadata);
+
+    // Mii conversions
+    Result ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const;
+    Result ConvertCoreDataToCharInfo(CharInfo& out_char_info, const CoreData& core_data) const;
+    Result ConvertCharInfoToCoreData(CoreData& out_core_data, const CharInfo& char_info) const;
+    Result UpdateLatest(const DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
+                        const CharInfo& char_info, SourceFlag source_flag) const;
+    Result UpdateLatest(const DatabaseSessionMetadata& metadata, StoreData& out_store_data,
+                        const StoreData& store_data, SourceFlag source_flag) const;
+
+    // Overloaded getters
     Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements,
-               u32& out_count, SourceFlag source_flag);
+               u32& out_count, SourceFlag source_flag) 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);
-    Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
-                    s32& out_index);
-    void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version);
+               u32& out_count, SourceFlag source_flag) const;
+    Result Get(const DatabaseSessionMetadata& metadata, std::span<StoreDataElement> out_elements,
+               u32& out_count, SourceFlag source_flag) const;
+    Result Get(const DatabaseSessionMetadata& metadata, std::span<StoreData> out_store_data,
+               u32& out_count, SourceFlag source_flag) const;
 
 private:
     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);
+                        SourceFlag source_flag) const;
+    Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
+                        SourceFlag source_flag) const;
+    Result BuildDefault(std::span<StoreDataElement> out_char_info, u32& out_count,
+                        SourceFlag source_flag) const;
+    Result BuildDefault(std::span<StoreData> out_char_info, u32& out_count,
+                        SourceFlag source_flag) const;
+
+    DatabaseManager database_manager{};
 
-    u64 update_counter{};
+    // This should be a global value
+    bool is_broken_with_clear_flag{};
 };
 
 }; // namespace Service::Mii