code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <memory>

#include "common/logging/log.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#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/raw_data.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/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"

namespace Service::Mii {

class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
    explicit IDatabaseService(Core::System& system_, std::shared_ptr<MiiManager> mii_manager,
                              bool is_system_)
        : ServiceFramework{system_, "IDatabaseService"}, manager{mii_manager}, is_system{
                                                                                is_system_} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, D<&IDatabaseService::IsUpdated>, "IsUpdated"},
            {1, D<&IDatabaseService::IsFullDatabase>, "IsFullDatabase"},
            {2, D<&IDatabaseService::GetCount>, "GetCount"},
            {3, D<&IDatabaseService::Get>, "Get"},
            {4, D<&IDatabaseService::Get1>, "Get1"},
            {5, D<&IDatabaseService::UpdateLatest>, "UpdateLatest"},
            {6, D<&IDatabaseService::BuildRandom>, "BuildRandom"},
            {7, D<&IDatabaseService::BuildDefault>, "BuildDefault"},
            {8, D<&IDatabaseService::Get2>, "Get2"},
            {9, D<&IDatabaseService::Get3>, "Get3"},
            {10, D<&IDatabaseService::UpdateLatest1>, "UpdateLatest1"},
            {11, D<&IDatabaseService::FindIndex>, "FindIndex"},
            {12, D<&IDatabaseService::Move>, "Move"},
            {13, D<&IDatabaseService::AddOrReplace>, "AddOrReplace"},
            {14, D<&IDatabaseService::Delete>, "Delete"},
            {15, D<&IDatabaseService::DestroyFile>, "DestroyFile"},
            {16, D<&IDatabaseService::DeleteFile>, "DeleteFile"},
            {17, D<&IDatabaseService::Format>, "Format"},
            {18, nullptr, "Import"},
            {19, nullptr, "Export"},
            {20, D<&IDatabaseService::IsBrokenDatabaseWithClearFlag>, "IsBrokenDatabaseWithClearFlag"},
            {21, D<&IDatabaseService::GetIndex>, "GetIndex"},
            {22, D<&IDatabaseService::SetInterfaceVersion>, "SetInterfaceVersion"},
            {23, D<&IDatabaseService::Convert>, "Convert"},
            {24, D<&IDatabaseService::ConvertCoreDataToCharInfo>, "ConvertCoreDataToCharInfo"},
            {25, D<&IDatabaseService::ConvertCharInfoToCoreData>, "ConvertCharInfoToCoreData"},
            {26,  D<&IDatabaseService::Append>, "Append"},
        };
        // clang-format on

        RegisterHandlers(functions);

        m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>(
            "set:sys", true);
        manager->Initialize(metadata);
    }

private:
    Result IsUpdated(Out<bool> out_is_updated, SourceFlag source_flag) {
        LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);

        *out_is_updated = manager->IsUpdated(metadata, source_flag);

        R_SUCCEED();
    }

    Result IsFullDatabase(Out<bool> out_is_full_database) {
        LOG_DEBUG(Service_Mii, "called");

        *out_is_full_database = manager->IsFullDatabase();

        R_SUCCEED();
    }

    Result GetCount(Out<u32> out_mii_count, SourceFlag source_flag) {
        *out_mii_count = manager->GetCount(metadata, source_flag);

        LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag,
                  *out_mii_count);

        R_SUCCEED();
    }

    Result Get(Out<u32> out_mii_count, SourceFlag source_flag,
               OutArray<CharInfoElement, BufferAttr_HipcMapAlias> char_info_element_buffer) {
        const auto result =
            manager->Get(metadata, char_info_element_buffer, *out_mii_count, source_flag);

        LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag,
                 *out_mii_count);

        R_RETURN(result);
    }

    Result Get1(Out<u32> out_mii_count, SourceFlag source_flag,
                OutArray<CharInfo, BufferAttr_HipcMapAlias> char_info_buffer) {
        const auto result = manager->Get(metadata, char_info_buffer, *out_mii_count, source_flag);

        LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag,
                 *out_mii_count);

        R_RETURN(result);
    }

    Result UpdateLatest(Out<CharInfo> out_char_info, const CharInfo& char_info,
                        SourceFlag source_flag) {
        LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);

        R_RETURN(manager->UpdateLatest(metadata, *out_char_info, char_info, source_flag));
    }

    Result BuildRandom(Out<CharInfo> out_char_info, Age age, Gender gender, Race race) {
        LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race);

        R_UNLESS(age <= Age::All, ResultInvalidArgument);
        R_UNLESS(gender <= Gender::All, ResultInvalidArgument);
        R_UNLESS(race <= Race::All, ResultInvalidArgument);

        manager->BuildRandom(*out_char_info, age, gender, race);

        R_SUCCEED();
    }

    Result BuildDefault(Out<CharInfo> out_char_info, s32 index) {
        LOG_DEBUG(Service_Mii, "called with index={}", index);
        R_UNLESS(index < static_cast<s32>(RawData::DefaultMii.size()), ResultInvalidArgument);

        manager->BuildDefault(*out_char_info, index);

        R_SUCCEED();
    }

    Result Get2(Out<u32> out_mii_count, SourceFlag source_flag,
                OutArray<StoreDataElement, BufferAttr_HipcMapAlias> store_data_element_buffer) {
        const auto result =
            manager->Get(metadata, store_data_element_buffer, *out_mii_count, source_flag);

        LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag,
                 *out_mii_count);

        R_RETURN(result);
    }

    Result Get3(Out<u32> out_mii_count, SourceFlag source_flag,
                OutArray<StoreData, BufferAttr_HipcMapAlias> store_data_buffer) {
        const auto result = manager->Get(metadata, store_data_buffer, *out_mii_count, source_flag);

        LOG_INFO(Service_Mii, "called with source_flag={}, mii_count={}", source_flag,
                 *out_mii_count);

        R_RETURN(result);
    }

    Result UpdateLatest1(Out<StoreData> out_store_data, const StoreData& store_data,
                         SourceFlag source_flag) {
        LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
        R_UNLESS(is_system, ResultPermissionDenied);

        R_RETURN(manager->UpdateLatest(metadata, *out_store_data, store_data, source_flag));
    }

    Result FindIndex(Out<s32> out_index, Common::UUID create_id, bool is_special) {
        LOG_INFO(Service_Mii, "called with create_id={}, is_special={}",
                 create_id.FormattedString(), is_special);

        *out_index = manager->FindIndex(create_id, is_special);

        R_SUCCEED();
    }

    Result Move(Common::UUID create_id, s32 new_index) {
        LOG_INFO(Service_Mii, "called with create_id={}, new_index={}", create_id.FormattedString(),
                 new_index);
        R_UNLESS(is_system, ResultPermissionDenied);

        const u32 count = manager->GetCount(metadata, SourceFlag::Database);

        R_UNLESS(new_index >= 0 && new_index < static_cast<s32>(count), ResultInvalidArgument);

        R_RETURN(manager->Move(metadata, new_index, create_id));
    }

    Result AddOrReplace(const StoreData& store_data) {
        LOG_INFO(Service_Mii, "called");
        R_UNLESS(is_system, ResultPermissionDenied);

        const auto result = manager->AddOrReplace(metadata, store_data);

        R_RETURN(result);
    }

    Result Delete(Common::UUID create_id) {
        LOG_INFO(Service_Mii, "called, create_id={}", create_id.FormattedString());
        R_UNLESS(is_system, ResultPermissionDenied);

        R_RETURN(manager->Delete(metadata, create_id));
    }

    Result DestroyFile() {
        bool is_db_test_mode_enabled{};
        m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii",
                                            "is_db_test_mode_enabled");

        LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
        R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly);

        R_RETURN(manager->DestroyFile(metadata));
    }

    Result DeleteFile() {
        bool is_db_test_mode_enabled{};
        m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii",
                                            "is_db_test_mode_enabled");

        LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
        R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly);

        R_RETURN(manager->DeleteFile());
    }

    Result Format() {
        bool is_db_test_mode_enabled{};
        m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii",
                                            "is_db_test_mode_enabled");

        LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
        R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly);

        R_RETURN(manager->Format(metadata));
    }

    Result IsBrokenDatabaseWithClearFlag(Out<bool> out_is_broken_with_clear_flag) {
        LOG_DEBUG(Service_Mii, "called");
        R_UNLESS(is_system, ResultPermissionDenied);

        *out_is_broken_with_clear_flag = manager->IsBrokenWithClearFlag(metadata);

        R_SUCCEED();
    }

    Result GetIndex(Out<s32> out_index, const CharInfo& char_info) {
        LOG_DEBUG(Service_Mii, "called");

        R_RETURN(manager->GetIndex(metadata, char_info, *out_index));
    }

    Result SetInterfaceVersion(u32 interface_version) {
        LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);

        manager->SetInterfaceVersion(metadata, interface_version);

        R_SUCCEED();
    }

    Result Convert(Out<CharInfo> out_char_info, const Ver3StoreData& mii_v3) {
        LOG_INFO(Service_Mii, "called");

        R_RETURN(manager->ConvertV3ToCharInfo(*out_char_info, mii_v3));
    }

    Result ConvertCoreDataToCharInfo(Out<CharInfo> out_char_info, const CoreData& core_data) {
        LOG_INFO(Service_Mii, "called");

        R_RETURN(manager->ConvertCoreDataToCharInfo(*out_char_info, core_data));
    }

    Result ConvertCharInfoToCoreData(Out<CoreData> out_core_data, const CharInfo& char_info) {
        LOG_INFO(Service_Mii, "called");

        R_RETURN(manager->ConvertCharInfoToCoreData(*out_core_data, char_info));
    }

    Result Append(const CharInfo& char_info) {
        LOG_INFO(Service_Mii, "called");

        R_RETURN(manager->Append(metadata, char_info));
    }

    std::shared_ptr<MiiManager> manager = nullptr;
    DatabaseSessionMetadata metadata{};
    bool is_system{};

    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};

IStaticService::IStaticService(Core::System& system_, const char* name_,
                               std::shared_ptr<MiiManager> mii_manager, bool is_system_)
    : ServiceFramework{system_, name_}, manager{mii_manager}, is_system{is_system_} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, D<&IStaticService::GetDatabaseService>, "GetDatabaseService"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IStaticService::~IStaticService() = default;

Result IStaticService::GetDatabaseService(
    Out<SharedPointer<IDatabaseService>> out_database_service) {
    LOG_DEBUG(Service_Mii, "called");

    *out_database_service = std::make_shared<IDatabaseService>(system, manager, is_system);

    R_SUCCEED();
}

std::shared_ptr<MiiManager> IStaticService::GetMiiManager() {
    return manager;
}

class IImageDatabaseService final : public ServiceFramework<IImageDatabaseService> {
public:
    explicit IImageDatabaseService(Core::System& system_) : ServiceFramework{system_, "miiimg"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, D<&IImageDatabaseService::Initialize>, "Initialize"},
            {10, nullptr, "Reload"},
            {11, D<&IImageDatabaseService::GetCount>, "GetCount"},
            {12, nullptr, "IsEmpty"},
            {13, nullptr, "IsFull"},
            {14, nullptr, "GetAttribute"},
            {15, nullptr, "LoadImage"},
            {16, nullptr, "AddOrUpdateImage"},
            {17, nullptr, "DeleteImages"},
            {100, nullptr, "DeleteFile"},
            {101, nullptr, "DestroyFile"},
            {102, nullptr, "ImportFile"},
            {103, nullptr, "ExportFile"},
            {104, nullptr, "ForceInitialize"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    Result Initialize() {
        LOG_INFO(Service_Mii, "called");

        R_SUCCEED();
    }

    Result GetCount(Out<u32> out_count) {
        LOG_DEBUG(Service_Mii, "called");

        *out_count = 0;

        R_SUCCEED();
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);
    std::shared_ptr<MiiManager> manager = std::make_shared<MiiManager>();

    server_manager->RegisterNamedService(
        "mii:e", std::make_shared<IStaticService>(system, "mii:e", manager, true));
    server_manager->RegisterNamedService(
        "mii:u", std::make_shared<IStaticService>(system, "mii:u", manager, false));
    server_manager->RegisterNamedService("miiimg", std::make_shared<IImageDatabaseService>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"

namespace Core {
class System;
}

namespace Service::Mii {
class MiiManager;
class IDatabaseService;

class IStaticService final : public ServiceFramework<IStaticService> {
public:
    explicit IStaticService(Core::System& system_, const char* name_,
                            std::shared_ptr<MiiManager> mii_manager, bool is_system_);
    ~IStaticService() override;

    std::shared_ptr<MiiManager> GetMiiManager();

private:
    Result GetDatabaseService(Out<SharedPointer<IDatabaseService>> out_database_service);

    std::shared_ptr<MiiManager> manager{nullptr};
    bool is_system{};
};

void LoopProcess(Core::System& system);

} // namespace Service::Mii

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/mii/mii_database.h"
#include "core/hle/service/mii/mii_result.h"
#include "core/hle/service/mii/mii_util.h"

namespace Service::Mii {

u8 NintendoFigurineDatabase::GetDatabaseLength() const {
    return database_length;
}

bool NintendoFigurineDatabase::IsFull() const {
    return database_length >= MaxDatabaseLength;
}

StoreData NintendoFigurineDatabase::Get(std::size_t index) const {
    StoreData store_data = miis.at(index);

    // This hack is to make external database dumps compatible
    store_data.SetDeviceChecksum();

    return store_data;
}

u32 NintendoFigurineDatabase::GetCount(const DatabaseSessionMetadata& metadata) const {
    if (magic == MiiMagic) {
        return GetDatabaseLength();
    }

    u32 mii_count{};
    for (std::size_t index = 0; index < mii_count; ++index) {
        const auto& store_data = Get(index);
        if (!store_data.IsSpecial()) {
            mii_count++;
        }
    }

    return mii_count;
}

bool NintendoFigurineDatabase::GetIndexByCreatorId(u32& out_index,
                                                   const Common::UUID& create_id) const {
    for (std::size_t index = 0; index < database_length; ++index) {
        if (miis[index].GetCreateId() == create_id) {
            out_index = static_cast<u32>(index);
            return true;
        }
    }

    return false;
}

Result NintendoFigurineDatabase::Move(u32 current_index, u32 new_index) {
    if (current_index == new_index) {
        return ResultNotUpdated;
    }

    const StoreData store_data = miis[current_index];

    if (new_index > current_index) {
        // Shift left
        const u32 index_diff = new_index - current_index;
        for (std::size_t i = 0; i < index_diff; i++) {
            miis[current_index + i] = miis[current_index + i + 1];
        }
    } else {
        // Shift right
        const u32 index_diff = current_index - new_index;
        for (std::size_t i = 0; i < index_diff; i++) {
            miis[current_index - i] = miis[current_index - i - 1];
        }
    }

    miis[new_index] = store_data;
    crc = GenerateDatabaseCrc();
    return ResultSuccess;
}

void NintendoFigurineDatabase::Replace(u32 index, const StoreData& store_data) {
    miis[index] = store_data;
    crc = GenerateDatabaseCrc();
}

void NintendoFigurineDatabase::Add(const StoreData& store_data) {
    miis[database_length] = store_data;
    database_length++;
    crc = GenerateDatabaseCrc();
}

void NintendoFigurineDatabase::Delete(u32 index) {
    // Shift left
    const s32 new_database_size = database_length - 1;
    if (static_cast<s32>(index) < new_database_size) {
        for (std::size_t i = index; i < static_cast<std::size_t>(new_database_size); i++) {
            miis[i] = miis[i + 1];
        }
    }

    database_length = static_cast<u8>(new_database_size);
    crc = GenerateDatabaseCrc();
}

void NintendoFigurineDatabase::CleanDatabase() {
    miis = {};
    version = 1;
    magic = DatabaseMagic;
    database_length = 0;
    crc = GenerateDatabaseCrc();
}

void NintendoFigurineDatabase::CorruptCrc() {
    crc = GenerateDatabaseCrc();
    crc = ~crc;
}

Result NintendoFigurineDatabase::CheckIntegrity() {
    if (magic != DatabaseMagic) {
        return ResultInvalidDatabaseSignature;
    }

    if (version != 1) {
        return ResultInvalidDatabaseVersion;
    }

    if (crc != GenerateDatabaseCrc()) {
        return ResultInvalidDatabaseChecksum;
    }

    if (database_length >= MaxDatabaseLength) {
        return ResultInvalidDatabaseLength;
    }

    return ResultSuccess;
}

u16 NintendoFigurineDatabase::GenerateDatabaseCrc() {
    return MiiUtil::CalculateCrc16(&magic, sizeof(NintendoFigurineDatabase) - sizeof(crc));
}

} // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"
#include "core/hle/service/mii/types/store_data.h"

namespace Service::Mii {

constexpr std::size_t MaxDatabaseLength{100};
constexpr u32 MiiMagic{0xa523b78f};
constexpr u32 DatabaseMagic{0x4244464e}; // NFDB

class NintendoFigurineDatabase {
public:
    /// Returns the total mii count.
    u8 GetDatabaseLength() const;

    /// Returns true if database is full.
    bool IsFull() const;

    /// Returns the mii of the specified index.
    StoreData Get(std::size_t index) const;

    /// Returns the total mii count. Ignoring special mii.
    u32 GetCount(const DatabaseSessionMetadata& metadata) const;

    /// Returns the index of a mii. If the mii isn't found returns false.
    bool GetIndexByCreatorId(u32& out_index, const Common::UUID& create_id) const;

    /// Moves the location of a specific mii.
    Result Move(u32 current_index, u32 new_index);

    /// Replaces mii with new data.
    void Replace(u32 index, const StoreData& store_data);

    /// Adds a new mii to the end of the database.
    void Add(const StoreData& store_data);

    /// Removes mii from database and shifts left the remainding data.
    void Delete(u32 index);

    /// Deletes all contents with a fresh database
    void CleanDatabase();

    /// Intentionally sets a bad checksum
    void CorruptCrc();

    /// Returns success if database is valid otherwise returns the corresponding error code.
    Result CheckIntegrity();

private:
    /// Returns the checksum of the database
    u16 GenerateDatabaseCrc();

    u32 magic{}; // 'NFDB'
    std::array<StoreData, MaxDatabaseLength> miis{};
    u8 version{};
    u8 database_length{};
    u16 crc{};
};
static_assert(sizeof(NintendoFigurineDatabase) == 0x1A98,
              "NintendoFigurineDatabase has incorrect size.");

}; // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"

#include "core/hle/service/mii/mii_database_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/store_data.h"

namespace Service::Mii {
const char* DbFileName = "MiiDatabase.dat";

DatabaseManager::DatabaseManager() {}

Result DatabaseManager::MountSaveData() {
    if (!is_save_data_mounted) {
        system_save_dir =
            Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000030";
        if (is_test_db) {
            system_save_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
                              "system/save/8000000000000031";
        }

        // mount point should be "mii:"

        if (!Common::FS::CreateDirs(system_save_dir)) {
            return ResultUnknown;
        }
    }

    is_save_data_mounted = true;
    return ResultSuccess;
}

Result DatabaseManager::Initialize(DatabaseSessionMetadata& metadata, bool& is_database_broken) {
    is_database_broken = false;
    if (!is_save_data_mounted) {
        return ResultInvalidArgument;
    }

    database.CleanDatabase();
    update_counter++;
    metadata.update_counter = update_counter;

    const Common::FS::IOFile db_file{system_save_dir / DbFileName, Common::FS::FileAccessMode::Read,
                                     Common::FS::FileType::BinaryFile};

    if (!db_file.IsOpen()) {
        return SaveDatabase();
    }

    if (Common::FS::GetSize(system_save_dir / DbFileName) != sizeof(NintendoFigurineDatabase)) {
        is_database_broken = true;
    }

    if (db_file.Read(database) != 1) {
        is_database_broken = true;
    }

    if (is_database_broken) {
        // Dragons happen here for simplicity just clean the database
        LOG_ERROR(Service_Mii, "Mii database is corrupted");
        database.CleanDatabase();
        return ResultUnknown;
    }

    const auto result = database.CheckIntegrity();

    if (result.IsError()) {
        LOG_ERROR(Service_Mii, "Mii database is corrupted 0x{:0x}", result.raw);
        database.CleanDatabase();
        return ResultSuccess;
    }

    LOG_INFO(Service_Mii, "Successfully loaded mii database. size={}",
             database.GetDatabaseLength());
    return ResultSuccess;
}

bool DatabaseManager::IsFullDatabase() const {
    return database.GetDatabaseLength() == MaxDatabaseLength;
}

bool DatabaseManager::IsModified() const {
    return is_moddified;
}

u64 DatabaseManager::GetUpdateCounter() const {
    return update_counter;
}

u32 DatabaseManager::GetCount(const DatabaseSessionMetadata& metadata) const {
    const u32 database_size = database.GetDatabaseLength();
    if (metadata.magic == MiiMagic) {
        return database_size;
    }

    // Special mii can't be used. Skip those.

    u32 mii_count{};
    for (std::size_t index = 0; index < database_size; ++index) {
        const auto& store_data = database.Get(index);
        if (store_data.IsSpecial()) {
            continue;
        }
        mii_count++;
    }

    return mii_count;
}

void DatabaseManager::Get(StoreData& out_store_data, std::size_t index,
                          const DatabaseSessionMetadata& metadata) const {
    if (metadata.magic == MiiMagic) {
        out_store_data = database.Get(index);
        return;
    }

    // The index refeers to the mii index without special mii.
    // Search on the database until we find it

    u32 virtual_index = 0;
    const u32 database_size = database.GetDatabaseLength();
    for (std::size_t i = 0; i < database_size; ++i) {
        const auto& store_data = database.Get(i);
        if (store_data.IsSpecial()) {
            continue;
        }
        if (virtual_index == index) {
            out_store_data = store_data;
            return;
        }
        virtual_index++;
    }

    // This function doesn't fail. It returns the first mii instead
    out_store_data = database.Get(0);
}

Result DatabaseManager::FindIndex(s32& out_index, const Common::UUID& create_id,
                                  bool is_special) const {
    u32 index{};
    const bool is_found = database.GetIndexByCreatorId(index, create_id);

    if (!is_found) {
        return ResultNotFound;
    }

    if (is_special) {
        out_index = index;
        return ResultSuccess;
    }

    if (database.Get(index).IsSpecial()) {
        return ResultNotFound;
    }

    out_index = 0;

    if (index < 1) {
        return ResultSuccess;
    }

    for (std::size_t i = 0; i < index; ++i) {
        if (database.Get(i).IsSpecial()) {
            continue;
        }
        out_index++;
    }
    return ResultSuccess;
}

Result DatabaseManager::FindIndex(const DatabaseSessionMetadata& metadata, u32& out_index,
                                  const Common::UUID& create_id) const {
    u32 index{};
    const bool is_found = database.GetIndexByCreatorId(index, create_id);

    if (!is_found) {
        return ResultNotFound;
    }

    if (metadata.magic == MiiMagic) {
        out_index = index;
        return ResultSuccess;
    }

    if (database.Get(index).IsSpecial()) {
        return ResultNotFound;
    }

    out_index = 0;

    if (index < 1) {
        return ResultSuccess;
    }

    // The index refeers to the mii index without special mii.
    // Search on the database until we find it

    for (std::size_t i = 0; i <= index; ++i) {
        const auto& store_data = database.Get(i);
        if (store_data.IsSpecial()) {
            continue;
        }
        out_index++;
    }
    return ResultSuccess;
}

Result DatabaseManager::FindMoveIndex(u32& out_index, u32 new_index,
                                      const Common::UUID& create_id) const {
    const auto database_size = database.GetDatabaseLength();

    if (database_size >= 1) {
        u32 virtual_index{};
        for (std::size_t i = 0; i < database_size; ++i) {
            const StoreData& store_data = database.Get(i);
            if (store_data.IsSpecial()) {
                continue;
            }
            if (virtual_index == new_index) {
                const bool is_found = database.GetIndexByCreatorId(out_index, create_id);
                if (!is_found) {
                    return ResultNotFound;
                }
                if (store_data.IsSpecial()) {
                    return ResultInvalidOperation;
                }
                return ResultSuccess;
            }
            virtual_index++;
        }
    }

    const bool is_found = database.GetIndexByCreatorId(out_index, create_id);
    if (!is_found) {
        return ResultNotFound;
    }
    const StoreData& store_data = database.Get(out_index);
    if (store_data.IsSpecial()) {
        return ResultInvalidOperation;
    }
    return ResultSuccess;
}

Result DatabaseManager::Move(DatabaseSessionMetadata& metadata, u32 new_index,
                             const Common::UUID& create_id) {
    u32 current_index{};
    if (metadata.magic == MiiMagic) {
        const bool is_found = database.GetIndexByCreatorId(current_index, create_id);
        if (!is_found) {
            return ResultNotFound;
        }
    } else {
        const auto result = FindMoveIndex(current_index, new_index, create_id);
        if (result.IsError()) {
            return result;
        }
    }

    const auto result = database.Move(current_index, new_index);
    if (result.IsFailure()) {
        return result;
    }

    is_moddified = true;
    update_counter++;
    metadata.update_counter = update_counter;
    return ResultSuccess;
}

Result DatabaseManager::AddOrReplace(DatabaseSessionMetadata& metadata,
                                     const StoreData& store_data) {
    if (store_data.IsValid() != ValidationResult::NoErrors) {
        return ResultInvalidStoreData;
    }
    if (metadata.magic != MiiMagic && store_data.IsSpecial()) {
        return ResultInvalidOperation;
    }

    u32 index{};
    const bool is_found = database.GetIndexByCreatorId(index, store_data.GetCreateId());
    if (is_found) {
        const StoreData& old_store_data = database.Get(index);

        if (store_data.IsSpecial() != old_store_data.IsSpecial()) {
            return ResultInvalidOperation;
        }

        database.Replace(index, store_data);
    } else {
        if (database.IsFull()) {
            return ResultDatabaseFull;
        }

        database.Add(store_data);
    }

    is_moddified = true;
    update_counter++;
    metadata.update_counter = update_counter;
    return ResultSuccess;
}

Result DatabaseManager::Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id) {
    u32 index{};
    const bool is_found = database.GetIndexByCreatorId(index, create_id);
    if (!is_found) {
        return ResultNotFound;
    }

    if (metadata.magic != MiiMagic) {
        const auto& store_data = database.Get(index);
        if (store_data.IsSpecial()) {
            return ResultInvalidOperation;
        }
    }

    database.Delete(index);

    is_moddified = true;
    update_counter++;
    metadata.update_counter = update_counter;
    return ResultSuccess;
}

Result DatabaseManager::Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info) {
    if (char_info.Verify() != ValidationResult::NoErrors) {
        return ResultInvalidCharInfo2;
    }
    if (char_info.GetType() == 1) {
        return ResultInvalidCharInfoType;
    }

    u32 index{};
    StoreData store_data{};

    // Loop until the mii we created is not on the database
    do {
        store_data.BuildWithCharInfo(char_info);
    } while (database.GetIndexByCreatorId(index, store_data.GetCreateId()));

    const Result result = store_data.Restore();

    if (result.IsSuccess() || result == ResultNotUpdated) {
        return AddOrReplace(metadata, store_data);
    }

    return result;
}

Result DatabaseManager::DestroyFile(DatabaseSessionMetadata& metadata) {
    database.CorruptCrc();

    is_moddified = true;
    update_counter++;
    metadata.update_counter = update_counter;

    const auto result = SaveDatabase();
    database.CleanDatabase();

    return result;
}

Result DatabaseManager::DeleteFile() {
    const bool result = Common::FS::RemoveFile(system_save_dir / DbFileName);
    // TODO: Return proper FS error here
    return result ? ResultSuccess : ResultUnknown;
}

void DatabaseManager::Format(DatabaseSessionMetadata& metadata) {
    database.CleanDatabase();
    is_moddified = true;
    update_counter++;
    metadata.update_counter = update_counter;
}

Result DatabaseManager::SaveDatabase() {
    // TODO: Replace unknown error codes with proper FS error codes when available

    if (!Common::FS::Exists(system_save_dir / DbFileName)) {
        if (!Common::FS::NewFile(system_save_dir / DbFileName)) {
            LOG_ERROR(Service_Mii, "Failed to create mii database");
            return ResultUnknown;
        }
    }

    const auto file_size = Common::FS::GetSize(system_save_dir / DbFileName);
    if (file_size != 0 && file_size != sizeof(NintendoFigurineDatabase)) {
        if (!Common::FS::RemoveFile(system_save_dir / DbFileName)) {
            LOG_ERROR(Service_Mii, "Failed to delete mii database");
            return ResultUnknown;
        }
        if (!Common::FS::NewFile(system_save_dir / DbFileName)) {
            LOG_ERROR(Service_Mii, "Failed to create mii database");
            return ResultUnknown;
        }
    }

    const Common::FS::IOFile db_file{system_save_dir / DbFileName,
                                     Common::FS::FileAccessMode::ReadWrite,
                                     Common::FS::FileType::BinaryFile};

    if (db_file.Write(database) != 1) {
        LOG_ERROR(Service_Mii, "Failed to save mii database");
        return ResultUnknown;
    }

    is_moddified = false;
    return ResultSuccess;
}

} // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/fs/fs.h"
#include "core/hle/result.h"
#include "core/hle/service/mii/mii_database.h"

namespace Service::Mii {
class CharInfo;
class StoreData;

class DatabaseManager {
public:
    DatabaseManager();
    Result MountSaveData();
    Result Initialize(DatabaseSessionMetadata& metadata, bool& is_database_broken);

    bool IsFullDatabase() const;
    bool IsModified() const;
    u64 GetUpdateCounter() const;

    void Get(StoreData& out_store_data, std::size_t index,
             const DatabaseSessionMetadata& metadata) const;
    u32 GetCount(const DatabaseSessionMetadata& metadata) const;

    Result FindIndex(s32& out_index, const Common::UUID& create_id, bool is_special) const;
    Result FindIndex(const DatabaseSessionMetadata& metadata, u32& out_index,
                     const Common::UUID& create_id) const;
    Result FindMoveIndex(u32& out_index, u32 new_index, const Common::UUID& create_id) const;

    Result Move(DatabaseSessionMetadata& metadata, u32 current_index,
                const Common::UUID& create_id);
    Result AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& out_store_data);
    Result Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id);
    Result Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info);

    Result DestroyFile(DatabaseSessionMetadata& metadata);
    Result DeleteFile();
    void Format(DatabaseSessionMetadata& metadata);

    Result SaveDatabase();

private:
    // This is the global value of
    // nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
    bool is_test_db{};

    bool is_moddified{};
    bool is_save_data_mounted{};
    u64 update_counter{};
    NintendoFigurineDatabase database{};

    std::filesystem::path system_save_dir{};
};

}; // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/logging/log.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 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 {
    u32 mii_count{};
    if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
        mii_count += DefaultMiiCount;
    }
    if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
        mii_count += database_manager.GetCount(metadata);
    }
    return mii_count;
}

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{};
    const bool is_special = metadata.magic == MiiMagic;
    const auto result = database_manager.FindIndex(index, char_info.GetCreateId(), is_special);

    if (result.IsError()) {
        index = -1;
    }

    if (index == -1) {
        return ResultNotFound;
    }

    out_index = index;
    return ResultSuccess;
}

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();
}

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

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

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{};
    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) 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].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) const {
    if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
        return BuildDefault(out_char_info, out_count, source_flag);
    }

    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) const {
    if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
        return ResultSuccess;
    }

    StoreData store_data{};

    for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
        if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
            return ResultInvalidArgumentSize;
        }

        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::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
                                SourceFlag source_flag) const {
    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::BuildDefault(std::span<StoreDataElement> out_elements, 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_elements.size() <= static_cast<std::size_t>(out_count)) {
            return ResultInvalidArgumentSize;
        }

        out_elements[out_count].store_data.BuildDefault(static_cast<u32>(index));
        out_elements[out_count].source = Source::Default;
        out_count++;
    }

    return ResultSuccess;
}

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


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <span>

#include "core/hle/result.h"
#include "core/hle/service/mii/mii_database_manager.h"
#include "core/hle/service/mii/mii_types.h"

namespace Service::Mii {
class CharInfo;
class CoreData;
class StoreData;
class Ver3StoreData;

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

    // 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 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) const;
    Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
               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) 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{};

    // This should be a global value
    bool is_broken_with_clear_flag{};
};

}; // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::Mii {

constexpr Result ResultInvalidArgument{ErrorModule::Mii, 1};
constexpr Result ResultInvalidArgumentSize{ErrorModule::Mii, 2};
constexpr Result ResultNotUpdated{ErrorModule::Mii, 3};
constexpr Result ResultNotFound{ErrorModule::Mii, 4};
constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5};
constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100};
constexpr Result ResultInvalidDatabaseChecksum{ErrorModule::Mii, 101};
constexpr Result ResultInvalidDatabaseSignature{ErrorModule::Mii, 103};
constexpr Result ResultInvalidDatabaseVersion{ErrorModule::Mii, 104};
constexpr Result ResultInvalidDatabaseLength{ErrorModule::Mii, 105};
constexpr Result ResultInvalidCharInfo2{ErrorModule::Mii, 107};
constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109};
constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202};
constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203};
constexpr Result ResultTestModeOnly{ErrorModule::Mii, 204};
constexpr Result ResultInvalidCharInfoType{ErrorModule::Mii, 205};

}; // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <type_traits>

#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/uuid.h"

namespace Service::Mii {

constexpr std::size_t MaxNameSize = 10;
constexpr u8 MaxHeight = 127;
constexpr u8 MaxBuild = 127;
constexpr u8 MaxType = 1;
constexpr u8 MaxRegionMove = 3;
constexpr u8 MaxEyeScale = 7;
constexpr u8 MaxEyeAspect = 6;
constexpr u8 MaxEyeRotate = 7;
constexpr u8 MaxEyeX = 12;
constexpr u8 MaxEyeY = 18;
constexpr u8 MaxEyebrowScale = 8;
constexpr u8 MaxEyebrowAspect = 6;
constexpr u8 MaxEyebrowRotate = 11;
constexpr u8 MaxEyebrowX = 12;
constexpr u8 MaxEyebrowY = 15;
constexpr u8 MaxNoseScale = 8;
constexpr u8 MaxNoseY = 18;
constexpr u8 MaxMouthScale = 8;
constexpr u8 MaxMoutAspect = 6;
constexpr u8 MaxMouthY = 18;
constexpr u8 MaxMustacheScale = 8;
constexpr u8 MaxMustacheY = 16;
constexpr u8 MaxGlassScale = 7;
constexpr u8 MaxGlassY = 20;
constexpr u8 MaxMoleScale = 8;
constexpr u8 MaxMoleX = 16;
constexpr u8 MaxMoleY = 30;
constexpr u8 MaxVer3CommonColor = 7;
constexpr u8 MaxVer3GlassType = 8;

enum class Age : u8 {
    Young,
    Normal,
    Old,
    All, // Default

    Max = All,
};

enum class Gender : u8 {
    Male,
    Female,
    All, // Default

    Max = Female,
};

enum class Race : u8 {
    Black,
    White,
    Asian,
    All, // Default

    Max = All,
};

enum class HairType : u8 {
    NormalLong, // Default
    NormalShort,
    NormalMedium,
    NormalExtraLong,
    NormalLongBottom,
    NormalTwoPeaks,
    PartingLong,
    FrontLock,
    PartingShort,
    PartingExtraLongCurved,
    PartingExtraLong,
    PartingMiddleLong,
    PartingSquared,
    PartingLongBottom,
    PeaksTop,
    PeaksSquared,
    PartingPeaks,
    PeaksLongBottom,
    Peaks,
    PeaksRounded,
    PeaksSide,
    PeaksMedium,
    PeaksLong,
    PeaksRoundedLong,
    PartingFrontPeaks,
    PartingLongFront,
    PartingLongRounded,
    PartingFrontPeaksLong,
    PartingExtraLongRounded,
    LongRounded,
    NormalUnknown1,
    NormalUnknown2,
    NormalUnknown3,
    NormalUnknown4,
    NormalUnknown5,
    NormalUnknown6,
    DreadLocks,
    PlatedMats,
    Caps,
    Afro,
    PlatedMatsLong,
    Beanie,
    Short,
    ShortTopLongSide,
    ShortUnknown1,
    ShortUnknown2,
    MilitaryParting,
    Military,
    ShortUnknown3,
    ShortUnknown4,
    ShortUnknown5,
    ShortUnknown6,
    NoneTop,
    None,
    LongUnknown1,
    LongUnknown2,
    LongUnknown3,
    LongUnknown4,
    LongUnknown5,
    LongUnknown6,
    LongUnknown7,
    LongUnknown8,
    LongUnknown9,
    LongUnknown10,
    LongUnknown11,
    LongUnknown12,
    LongUnknown13,
    LongUnknown14,
    LongUnknown15,
    LongUnknown16,
    LongUnknown17,
    LongUnknown18,
    LongUnknown19,
    LongUnknown20,
    LongUnknown21,
    LongUnknown22,
    LongUnknown23,
    LongUnknown24,
    LongUnknown25,
    LongUnknown26,
    LongUnknown27,
    LongUnknown28,
    LongUnknown29,
    LongUnknown30,
    LongUnknown31,
    LongUnknown32,
    LongUnknown33,
    LongUnknown34,
    LongUnknown35,
    LongUnknown36,
    LongUnknown37,
    LongUnknown38,
    LongUnknown39,
    LongUnknown40,
    LongUnknown41,
    LongUnknown42,
    LongUnknown43,
    LongUnknown44,
    LongUnknown45,
    LongUnknown46,
    LongUnknown47,
    LongUnknown48,
    LongUnknown49,
    LongUnknown50,
    LongUnknown51,
    LongUnknown52,
    LongUnknown53,
    LongUnknown54,
    LongUnknown55,
    LongUnknown56,
    LongUnknown57,
    LongUnknown58,
    LongUnknown59,
    LongUnknown60,
    LongUnknown61,
    LongUnknown62,
    LongUnknown63,
    LongUnknown64,
    LongUnknown65,
    LongUnknown66,
    TwoMediumFrontStrandsOneLongBackPonyTail,
    TwoFrontStrandsLongBackPonyTail,
    PartingFrontTwoLongBackPonyTails,
    TwoFrontStrandsOneLongBackPonyTail,
    LongBackPonyTail,
    LongFrontTwoLongBackPonyTails,
    StrandsTwoShortSidedPonyTails,
    TwoMediumSidedPonyTails,
    ShortFrontTwoBackPonyTails,
    TwoShortSidedPonyTails,
    TwoLongSidedPonyTails,
    LongFrontTwoBackPonyTails,

    Max = LongFrontTwoBackPonyTails,
};

enum class MoleType : u8 {
    None, // Default
    OneDot,

    Max = OneDot,
};

enum class HairFlip : u8 {
    Left, // Default
    Right,

    Max = Right,
};

enum class CommonColor : u8 {
    // For simplicity common colors aren't listed
    Max = 99,
    Count = 100,
};

enum class FavoriteColor : u8 {
    Red, // Default
    Orange,
    Yellow,
    LimeGreen,
    Green,
    Blue,
    LightBlue,
    Pink,
    Purple,
    Brown,
    White,
    Black,

    Max = Black,
};

enum class EyeType : u8 {
    Normal, // Default
    NormalLash,
    WhiteLash,
    WhiteNoBottom,
    OvalAngledWhite,
    AngryWhite,
    DotLashType1,
    Line,
    DotLine,
    OvalWhite,
    RoundedWhite,
    NormalShadow,
    CircleWhite,
    Circle,
    CircleWhiteStroke,
    NormalOvalNoBottom,
    NormalOvalLarge,
    NormalRoundedNoBottom,
    SmallLash,
    Small,
    TwoSmall,
    NormalLongLash,
    WhiteTwoLashes,
    WhiteThreeLashes,
    DotAngry,
    DotAngled,
    Oval,
    SmallWhite,
    WhiteAngledNoBottom,
    WhiteAngledNoLeft,
    SmallWhiteTwoLashes,
    LeafWhiteLash,
    WhiteLargeNoBottom,
    Dot,
    DotLashType2,
    DotThreeLashes,
    WhiteOvalTop,
    WhiteOvalBottom,
    WhiteOvalBottomFlat,
    WhiteOvalTwoLashes,
    WhiteOvalThreeLashes,
    WhiteOvalNoBottomTwoLashes,
    DotWhite,
    WhiteOvalTopFlat,
    WhiteThinLeaf,
    StarThreeLashes,
    LineTwoLashes,
    CrowsFeet,
    WhiteNoBottomFlat,
    WhiteNoBottomRounded,
    WhiteSmallBottomLine,
    WhiteNoBottomLash,
    WhiteNoPartialBottomLash,
    WhiteOvalBottomLine,
    WhiteNoBottomLashTopLine,
    WhiteNoPartialBottomTwoLashes,
    NormalTopLine,
    WhiteOvalLash,
    RoundTired,
    WhiteLarge,

    Max = WhiteLarge,
};

enum class MouthType : u8 {
    Neutral, // Default
    NeutralLips,
    Smile,
    SmileStroke,
    SmileTeeth,
    LipsSmall,
    LipsLarge,
    Wave,
    WaveAngrySmall,
    NeutralStrokeLarge,
    TeethSurprised,
    LipsExtraLarge,
    LipsUp,
    NeutralDown,
    Surprised,
    TeethMiddle,
    NeutralStroke,
    LipsExtraSmall,
    Malicious,
    LipsDual,
    NeutralComma,
    NeutralUp,
    TeethLarge,
    WaveAngry,
    LipsSexy,
    SmileInverted,
    LipsSexyOutline,
    SmileRounded,
    LipsTeeth,
    NeutralOpen,
    TeethRounded,
    WaveAngrySmallInverted,
    NeutralCommaInverted,
    TeethFull,
    SmileDownLine,
    Kiss,

    Max = Kiss,
};

enum class FontRegion : u8 {
    Standard, // Default
    China,
    Korea,
    Taiwan,

    Max = Taiwan,
};

enum class FacelineType : u8 {
    Sharp, // Default
    Rounded,
    SharpRounded,
    SharpRoundedSmall,
    Large,
    LargeRounded,
    SharpSmall,
    Flat,
    Bump,
    Angular,
    FlatRounded,
    AngularSmall,

    Max = AngularSmall,
};

enum class FacelineColor : u8 {
    Beige, // Default
    WarmBeige,
    Natural,
    Honey,
    Chestnut,
    Porcelain,
    Ivory,
    WarmIvory,
    Almond,
    Espresso,

    Max = Espresso,
    Count = Max + 1,
};

enum class FacelineWrinkle : u8 {
    None, // Default
    TearTroughs,
    FacialPain,
    Cheeks,
    Folds,
    UnderTheEyes,
    SplitChin,
    Chin,
    BrowDroop,
    MouthFrown,
    CrowsFeet,
    FoldsCrowsFrown,

    Max = FoldsCrowsFrown,
};

enum class FacelineMake : u8 {
    None, // Default
    CheekPorcelain,
    CheekNatural,
    EyeShadowBlue,
    CheekBlushPorcelain,
    CheekBlushNatural,
    CheekPorcelainEyeShadowBlue,
    CheekPorcelainEyeShadowNatural,
    CheekBlushPorcelainEyeShadowEspresso,
    Freckles,
    LionsManeBeard,
    StubbleBeard,

    Max = StubbleBeard,
};

enum class EyebrowType : u8 {
    FlatAngledLarge, // Default
    LowArchRoundedThin,
    SoftAngledLarge,
    MediumArchRoundedThin,
    RoundedMedium,
    LowArchMedium,
    RoundedThin,
    UpThin,
    MediumArchRoundedMedium,
    RoundedLarge,
    UpLarge,
    FlatAngledLargeInverted,
    MediumArchFlat,
    AngledThin,
    HorizontalLarge,
    HighArchFlat,
    Flat,
    MediumArchLarge,
    LowArchThin,
    RoundedThinInverted,
    HighArchLarge,
    Hairy,
    Dotted,
    None,

    Max = None,
};

enum class NoseType : u8 {
    Normal, // Default
    Rounded,
    Dot,
    Arrow,
    Roman,
    Triangle,
    Button,
    RoundedInverted,
    Potato,
    Grecian,
    Snub,
    Aquiline,
    ArrowLeft,
    RoundedLarge,
    Hooked,
    Fat,
    Droopy,
    ArrowLarge,

    Max = ArrowLarge,
};

enum class BeardType : u8 {
    None,
    Goatee,
    GoateeLong,
    LionsManeLong,
    LionsMane,
    Full,

    Min = Goatee,
    Max = Full,
};

enum class MustacheType : u8 {
    None,
    Walrus,
    Pencil,
    Horseshoe,
    Normal,
    Toothbrush,

    Min = Walrus,
    Max = Toothbrush,
};

enum class GlassType : u8 {
    None,
    Oval,
    Wayfarer,
    Rectangle,
    TopRimless,
    Rounded,
    Oversized,
    CatEye,
    Square,
    BottomRimless,
    SemiOpaqueRounded,
    SemiOpaqueCatEye,
    SemiOpaqueOval,
    SemiOpaqueRectangle,
    SemiOpaqueAviator,
    OpaqueRounded,
    OpaqueCatEye,
    OpaqueOval,
    OpaqueRectangle,
    OpaqueAviator,

    Max = OpaqueAviator,
    Count = Max + 1,
};

enum class BeardAndMustacheFlag : u32 {
    Beard = 1,
    Mustache,
    All = Beard | Mustache,
};
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);

enum class Source : u32 {
    Database = 0,
    Default = 1,
    Account = 2,
    Friend = 3,
};

enum class SourceFlag : u32 {
    None = 0,
    Database = 1 << 0,
    Default = 1 << 1,
};
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);

enum class ValidationResult : u32 {
    NoErrors = 0x0,
    InvalidBeardColor = 0x1,
    InvalidBeardType = 0x2,
    InvalidBuild = 0x3,
    InvalidEyeAspect = 0x4,
    InvalidEyeColor = 0x5,
    InvalidEyeRotate = 0x6,
    InvalidEyeScale = 0x7,
    InvalidEyeType = 0x8,
    InvalidEyeX = 0x9,
    InvalidEyeY = 0xa,
    InvalidEyebrowAspect = 0xb,
    InvalidEyebrowColor = 0xc,
    InvalidEyebrowRotate = 0xd,
    InvalidEyebrowScale = 0xe,
    InvalidEyebrowType = 0xf,
    InvalidEyebrowX = 0x10,
    InvalidEyebrowY = 0x11,
    InvalidFacelineColor = 0x12,
    InvalidFacelineMake = 0x13,
    InvalidFacelineWrinkle = 0x14,
    InvalidFacelineType = 0x15,
    InvalidColor = 0x16,
    InvalidFont = 0x17,
    InvalidGender = 0x18,
    InvalidGlassColor = 0x19,
    InvalidGlassScale = 0x1a,
    InvalidGlassType = 0x1b,
    InvalidGlassY = 0x1c,
    InvalidHairColor = 0x1d,
    InvalidHairFlip = 0x1e,
    InvalidHairType = 0x1f,
    InvalidHeight = 0x20,
    InvalidMoleScale = 0x21,
    InvalidMoleType = 0x22,
    InvalidMoleX = 0x23,
    InvalidMoleY = 0x24,
    InvalidMouthAspect = 0x25,
    InvalidMouthColor = 0x26,
    InvalidMouthScale = 0x27,
    InvalidMouthType = 0x28,
    InvalidMouthY = 0x29,
    InvalidMustacheScale = 0x2a,
    InvalidMustacheType = 0x2b,
    InvalidMustacheY = 0x2c,
    InvalidNoseScale = 0x2e,
    InvalidNoseType = 0x2f,
    InvalidNoseY = 0x30,
    InvalidRegionMove = 0x31,
    InvalidCreateId = 0x32,
    InvalidName = 0x33,
    InvalidChecksum = 0x34,
    InvalidType = 0x35,
};

struct Nickname {
    std::array<char16_t, MaxNameSize> data{};

    // Checks for null or dirty strings
    bool IsValid() const {
        if (data[0] == 0) {
            return false;
        }

        std::size_t index = 1;
        while (index < MaxNameSize && data[index] != 0) {
            index++;
        }
        while (index < MaxNameSize && data[index] == 0) {
            index++;
        }
        return index == MaxNameSize;
    }
};
static_assert(sizeof(Nickname) == 0x14, "Nickname is an invalid size");

struct DefaultMii {
    u32 face_type{};
    u32 face_color{};
    u32 face_wrinkle{};
    u32 face_makeup{};
    u32 hair_type{};
    u32 hair_color{};
    u32 hair_flip{};
    u32 eye_type{};
    u32 eye_color{};
    u32 eye_scale{};
    u32 eye_aspect{};
    u32 eye_rotate{};
    u32 eye_x{};
    u32 eye_y{};
    u32 eyebrow_type{};
    u32 eyebrow_color{};
    u32 eyebrow_scale{};
    u32 eyebrow_aspect{};
    u32 eyebrow_rotate{};
    u32 eyebrow_x{};
    u32 eyebrow_y{};
    u32 nose_type{};
    u32 nose_scale{};
    u32 nose_y{};
    u32 mouth_type{};
    u32 mouth_color{};
    u32 mouth_scale{};
    u32 mouth_aspect{};
    u32 mouth_y{};
    u32 mustache_type{};
    u32 beard_type{};
    u32 beard_color{};
    u32 mustache_scale{};
    u32 mustache_y{};
    u32 glasses_type{};
    u32 glasses_color{};
    u32 glasses_scale{};
    u32 glasses_y{};
    u32 mole_type{};
    u32 mole_scale{};
    u32 mole_x{};
    u32 mole_y{};
    u32 height{};
    u32 weight{};
    u32 gender{};
    u32 favorite_color{};
    u32 region_move{};
    u32 font_region{};
    u32 type{};
    Nickname nickname;
};
static_assert(sizeof(DefaultMii) == 0xd8, "DefaultMii 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


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <random>
#include <span>

#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"
#include "core/hle/service/mii/mii_types.h"

namespace Service::Mii {
class MiiUtil {
public:
    static u16 CalculateCrc16(const void* data, std::size_t size) {
        s32 crc{};
        for (std::size_t i = 0; i < size; i++) {
            crc ^= static_cast<const u8*>(data)[i] << 8;
            for (std::size_t j = 0; j < 8; j++) {
                crc <<= 1;
                if ((crc & 0x10000) != 0) {
                    crc = (crc ^ 0x1021) & 0xFFFF;
                }
            }
        }
        return Common::swap16(static_cast<u16>(crc));
    }

    static u16 CalculateDeviceCrc16(const Common::UUID& uuid, std::size_t data_size) {
        constexpr u16 magic{0x1021};
        s32 crc{};

        for (std::size_t i = 0; i < uuid.uuid.size(); i++) {
            for (std::size_t j = 0; j < 8; j++) {
                crc <<= 1;
                if ((crc & 0x10000) != 0) {
                    crc = crc ^ magic;
                }
            }
            crc ^= uuid.uuid[i];
        }

        // As much as this looks wrong this is what N's does

        for (std::size_t i = 0; i < data_size * 8; i++) {
            crc <<= 1;
            if ((crc & 0x10000) != 0) {
                crc = crc ^ magic;
            }
        }

        return Common::swap16(static_cast<u16>(crc));
    }

    static Common::UUID MakeCreateId() {
        return Common::UUID::MakeRandomRFC4122V4();
    }

    static Common::UUID GetDeviceId() {
        // This should be nn::settings::detail::GetMiiAuthorId()
        return Common::UUID::MakeDefault();
    }

    template <typename T>
    static T GetRandomValue(T min, T max) {
        std::random_device device;
        std::mt19937 gen(device());
        std::uniform_int_distribution<u64> distribution(static_cast<u64>(min),
                                                        static_cast<u64>(max));
        return static_cast<T>(distribution(gen));
    }

    template <typename T>
    static T GetRandomValue(T max) {
        return GetRandomValue<T>({}, max);
    }

    static bool IsFontRegionValid(FontRegion font, std::span<const char16_t> text) {
        // TODO: This function needs to check against the font tables
        return true;
    }
};
} // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/mii/types/char_info.h"
#include "core/hle/service/mii/types/store_data.h"

namespace Service::Mii {

void CharInfo::SetFromStoreData(const StoreData& store_data) {
    name = store_data.GetNickname();
    null_terminator = '\0';
    create_id = store_data.GetCreateId();
    font_region = store_data.GetFontRegion();
    favorite_color = store_data.GetFavoriteColor();
    gender = store_data.GetGender();
    height = store_data.GetHeight();
    build = store_data.GetBuild();
    type = store_data.GetType();
    region_move = store_data.GetRegionMove();
    faceline_type = store_data.GetFacelineType();
    faceline_color = store_data.GetFacelineColor();
    faceline_wrinkle = store_data.GetFacelineWrinkle();
    faceline_make = store_data.GetFacelineMake();
    hair_type = store_data.GetHairType();
    hair_color = store_data.GetHairColor();
    hair_flip = store_data.GetHairFlip();
    eye_type = store_data.GetEyeType();
    eye_color = store_data.GetEyeColor();
    eye_scale = store_data.GetEyeScale();
    eye_aspect = store_data.GetEyeAspect();
    eye_rotate = store_data.GetEyeRotate();
    eye_x = store_data.GetEyeX();
    eye_y = store_data.GetEyeY();
    eyebrow_type = store_data.GetEyebrowType();
    eyebrow_color = store_data.GetEyebrowColor();
    eyebrow_scale = store_data.GetEyebrowScale();
    eyebrow_aspect = store_data.GetEyebrowAspect();
    eyebrow_rotate = store_data.GetEyebrowRotate();
    eyebrow_x = store_data.GetEyebrowX();
    eyebrow_y = store_data.GetEyebrowY() + 3;
    nose_type = store_data.GetNoseType();
    nose_scale = store_data.GetNoseScale();
    nose_y = store_data.GetNoseY();
    mouth_type = store_data.GetMouthType();
    mouth_color = store_data.GetMouthColor();
    mouth_scale = store_data.GetMouthScale();
    mouth_aspect = store_data.GetMouthAspect();
    mouth_y = store_data.GetMouthY();
    beard_color = store_data.GetBeardColor();
    beard_type = store_data.GetBeardType();
    mustache_type = store_data.GetMustacheType();
    mustache_scale = store_data.GetMustacheScale();
    mustache_y = store_data.GetMustacheY();
    glass_type = store_data.GetGlassType();
    glass_color = store_data.GetGlassColor();
    glass_scale = store_data.GetGlassScale();
    glass_y = store_data.GetGlassY();
    mole_type = store_data.GetMoleType();
    mole_scale = store_data.GetMoleScale();
    mole_x = store_data.GetMoleX();
    mole_y = store_data.GetMoleY();
    padding = '\0';
}

ValidationResult CharInfo::Verify() const {
    if (!create_id.IsValid()) {
        return ValidationResult::InvalidCreateId;
    }
    if (!name.IsValid()) {
        return ValidationResult::InvalidName;
    }
    if (font_region > FontRegion::Max) {
        return ValidationResult::InvalidFont;
    }
    if (favorite_color > FavoriteColor::Max) {
        return ValidationResult::InvalidColor;
    }
    if (gender > Gender::Max) {
        return ValidationResult::InvalidGender;
    }
    if (height > MaxHeight) {
        return ValidationResult::InvalidHeight;
    }
    if (build > MaxBuild) {
        return ValidationResult::InvalidBuild;
    }
    if (type > MaxType) {
        return ValidationResult::InvalidType;
    }
    if (region_move > MaxRegionMove) {
        return ValidationResult::InvalidRegionMove;
    }
    if (faceline_type > FacelineType::Max) {
        return ValidationResult::InvalidFacelineType;
    }
    if (faceline_color > FacelineColor::Max) {
        return ValidationResult::InvalidFacelineColor;
    }
    if (faceline_wrinkle > FacelineWrinkle::Max) {
        return ValidationResult::InvalidFacelineWrinkle;
    }
    if (faceline_make > FacelineMake::Max) {
        return ValidationResult::InvalidFacelineMake;
    }
    if (hair_type > HairType::Max) {
        return ValidationResult::InvalidHairType;
    }
    if (hair_color > CommonColor::Max) {
        return ValidationResult::InvalidHairColor;
    }
    if (hair_flip > HairFlip::Max) {
        return ValidationResult::InvalidHairFlip;
    }
    if (eye_type > EyeType::Max) {
        return ValidationResult::InvalidEyeType;
    }
    if (eye_color > CommonColor::Max) {
        return ValidationResult::InvalidEyeColor;
    }
    if (eye_scale > MaxEyeScale) {
        return ValidationResult::InvalidEyeScale;
    }
    if (eye_aspect > MaxEyeAspect) {
        return ValidationResult::InvalidEyeAspect;
    }
    if (eye_rotate > MaxEyeX) {
        return ValidationResult::InvalidEyeRotate;
    }
    if (eye_x > MaxEyeX) {
        return ValidationResult::InvalidEyeX;
    }
    if (eye_y > MaxEyeY) {
        return ValidationResult::InvalidEyeY;
    }
    if (eyebrow_type > EyebrowType::Max) {
        return ValidationResult::InvalidEyebrowType;
    }
    if (eyebrow_color > CommonColor::Max) {
        return ValidationResult::InvalidEyebrowColor;
    }
    if (eyebrow_scale > MaxEyebrowScale) {
        return ValidationResult::InvalidEyebrowScale;
    }
    if (eyebrow_aspect > MaxEyebrowAspect) {
        return ValidationResult::InvalidEyebrowAspect;
    }
    if (eyebrow_rotate > MaxEyebrowRotate) {
        return ValidationResult::InvalidEyebrowRotate;
    }
    if (eyebrow_x > MaxEyebrowX) {
        return ValidationResult::InvalidEyebrowX;
    }
    if (eyebrow_y - 3 > MaxEyebrowY) {
        return ValidationResult::InvalidEyebrowY;
    }
    if (nose_type > NoseType::Max) {
        return ValidationResult::InvalidNoseType;
    }
    if (nose_scale > MaxNoseScale) {
        return ValidationResult::InvalidNoseScale;
    }
    if (nose_y > MaxNoseY) {
        return ValidationResult::InvalidNoseY;
    }
    if (mouth_type > MouthType::Max) {
        return ValidationResult::InvalidMouthType;
    }
    if (mouth_color > CommonColor::Max) {
        return ValidationResult::InvalidMouthColor;
    }
    if (mouth_scale > MaxMouthScale) {
        return ValidationResult::InvalidMouthScale;
    }
    if (mouth_aspect > MaxMoutAspect) {
        return ValidationResult::InvalidMouthAspect;
    }
    if (mouth_y > MaxMouthY) {
        return ValidationResult::InvalidMoleY;
    }
    if (beard_color > CommonColor::Max) {
        return ValidationResult::InvalidBeardColor;
    }
    if (beard_type > BeardType::Max) {
        return ValidationResult::InvalidBeardType;
    }
    if (mustache_type > MustacheType::Max) {
        return ValidationResult::InvalidMustacheType;
    }
    if (mustache_scale > MaxMustacheScale) {
        return ValidationResult::InvalidMustacheScale;
    }
    if (mustache_y > MaxMustacheY) {
        return ValidationResult::InvalidMustacheY;
    }
    if (glass_type > GlassType::Max) {
        return ValidationResult::InvalidGlassType;
    }
    if (glass_color > CommonColor::Max) {
        return ValidationResult::InvalidGlassColor;
    }
    if (glass_scale > MaxGlassScale) {
        return ValidationResult::InvalidGlassScale;
    }
    if (glass_y > MaxGlassY) {
        return ValidationResult::InvalidGlassY;
    }
    if (mole_type > MoleType::Max) {
        return ValidationResult::InvalidMoleType;
    }
    if (mole_scale > MaxMoleScale) {
        return ValidationResult::InvalidMoleScale;
    }
    if (mole_x > MaxMoleX) {
        return ValidationResult::InvalidMoleX;
    }
    if (mole_y > MaxMoleY) {
        return ValidationResult::InvalidMoleY;
    }
    return ValidationResult::NoErrors;
}

Common::UUID CharInfo::GetCreateId() const {
    return create_id;
}

Nickname CharInfo::GetNickname() const {
    return name;
}

FontRegion CharInfo::GetFontRegion() const {
    return font_region;
}

FavoriteColor CharInfo::GetFavoriteColor() const {
    return favorite_color;
}

Gender CharInfo::GetGender() const {
    return gender;
}

u8 CharInfo::GetHeight() const {
    return height;
}

u8 CharInfo::GetBuild() const {
    return build;
}

u8 CharInfo::GetType() const {
    return type;
}

u8 CharInfo::GetRegionMove() const {
    return region_move;
}

FacelineType CharInfo::GetFacelineType() const {
    return faceline_type;
}

FacelineColor CharInfo::GetFacelineColor() const {
    return faceline_color;
}

FacelineWrinkle CharInfo::GetFacelineWrinkle() const {
    return faceline_wrinkle;
}

FacelineMake CharInfo::GetFacelineMake() const {
    return faceline_make;
}

HairType CharInfo::GetHairType() const {
    return hair_type;
}

CommonColor CharInfo::GetHairColor() const {
    return hair_color;
}

HairFlip CharInfo::GetHairFlip() const {
    return hair_flip;
}

EyeType CharInfo::GetEyeType() const {
    return eye_type;
}

CommonColor CharInfo::GetEyeColor() const {
    return eye_color;
}

u8 CharInfo::GetEyeScale() const {
    return eye_scale;
}

u8 CharInfo::GetEyeAspect() const {
    return eye_aspect;
}

u8 CharInfo::GetEyeRotate() const {
    return eye_rotate;
}

u8 CharInfo::GetEyeX() const {
    return eye_x;
}

u8 CharInfo::GetEyeY() const {
    return eye_y;
}

EyebrowType CharInfo::GetEyebrowType() const {
    return eyebrow_type;
}

CommonColor CharInfo::GetEyebrowColor() const {
    return eyebrow_color;
}

u8 CharInfo::GetEyebrowScale() const {
    return eyebrow_scale;
}

u8 CharInfo::GetEyebrowAspect() const {
    return eyebrow_aspect;
}

u8 CharInfo::GetEyebrowRotate() const {
    return eyebrow_rotate;
}

u8 CharInfo::GetEyebrowX() const {
    return eyebrow_x;
}

u8 CharInfo::GetEyebrowY() const {
    return eyebrow_y;
}

NoseType CharInfo::GetNoseType() const {
    return nose_type;
}

u8 CharInfo::GetNoseScale() const {
    return nose_scale;
}

u8 CharInfo::GetNoseY() const {
    return nose_y;
}

MouthType CharInfo::GetMouthType() const {
    return mouth_type;
}

CommonColor CharInfo::GetMouthColor() const {
    return mouth_color;
}

u8 CharInfo::GetMouthScale() const {
    return mouth_scale;
}

u8 CharInfo::GetMouthAspect() const {
    return mouth_aspect;
}

u8 CharInfo::GetMouthY() const {
    return mouth_y;
}

CommonColor CharInfo::GetBeardColor() const {
    return beard_color;
}

BeardType CharInfo::GetBeardType() const {
    return beard_type;
}

MustacheType CharInfo::GetMustacheType() const {
    return mustache_type;
}

u8 CharInfo::GetMustacheScale() const {
    return mustache_scale;
}

u8 CharInfo::GetMustacheY() const {
    return mustache_y;
}

GlassType CharInfo::GetGlassType() const {
    return glass_type;
}

CommonColor CharInfo::GetGlassColor() const {
    return glass_color;
}

u8 CharInfo::GetGlassScale() const {
    return glass_scale;
}

u8 CharInfo::GetGlassY() const {
    return glass_y;
}

MoleType CharInfo::GetMoleType() const {
    return mole_type;
}

u8 CharInfo::GetMoleScale() const {
    return mole_scale;
}

u8 CharInfo::GetMoleX() const {
    return mole_x;
}

u8 CharInfo::GetMoleY() const {
    return mole_y;
}

bool CharInfo::operator==(const CharInfo& info) {
    bool is_identical = info.Verify() == ValidationResult::NoErrors;
    is_identical &= name.data == info.GetNickname().data;
    is_identical &= create_id == info.GetCreateId();
    is_identical &= font_region == info.GetFontRegion();
    is_identical &= favorite_color == info.GetFavoriteColor();
    is_identical &= gender == info.GetGender();
    is_identical &= height == info.GetHeight();
    is_identical &= build == info.GetBuild();
    is_identical &= type == info.GetType();
    is_identical &= region_move == info.GetRegionMove();
    is_identical &= faceline_type == info.GetFacelineType();
    is_identical &= faceline_color == info.GetFacelineColor();
    is_identical &= faceline_wrinkle == info.GetFacelineWrinkle();
    is_identical &= faceline_make == info.GetFacelineMake();
    is_identical &= hair_type == info.GetHairType();
    is_identical &= hair_color == info.GetHairColor();
    is_identical &= hair_flip == info.GetHairFlip();
    is_identical &= eye_type == info.GetEyeType();
    is_identical &= eye_color == info.GetEyeColor();
    is_identical &= eye_scale == info.GetEyeScale();
    is_identical &= eye_aspect == info.GetEyeAspect();
    is_identical &= eye_rotate == info.GetEyeRotate();
    is_identical &= eye_x == info.GetEyeX();
    is_identical &= eye_y == info.GetEyeY();
    is_identical &= eyebrow_type == info.GetEyebrowType();
    is_identical &= eyebrow_color == info.GetEyebrowColor();
    is_identical &= eyebrow_scale == info.GetEyebrowScale();
    is_identical &= eyebrow_aspect == info.GetEyebrowAspect();
    is_identical &= eyebrow_rotate == info.GetEyebrowRotate();
    is_identical &= eyebrow_x == info.GetEyebrowX();
    is_identical &= eyebrow_y == info.GetEyebrowY();
    is_identical &= nose_type == info.GetNoseType();
    is_identical &= nose_scale == info.GetNoseScale();
    is_identical &= nose_y == info.GetNoseY();
    is_identical &= mouth_type == info.GetMouthType();
    is_identical &= mouth_color == info.GetMouthColor();
    is_identical &= mouth_scale == info.GetMouthScale();
    is_identical &= mouth_aspect == info.GetMouthAspect();
    is_identical &= mouth_y == info.GetMouthY();
    is_identical &= beard_color == info.GetBeardColor();
    is_identical &= beard_type == info.GetBeardType();
    is_identical &= mustache_type == info.GetMustacheType();
    is_identical &= mustache_scale == info.GetMustacheScale();
    is_identical &= mustache_y == info.GetMustacheY();
    is_identical &= glass_type == info.GetGlassType();
    is_identical &= glass_color == info.GetGlassColor();
    is_identical &= glass_scale == info.GetGlassScale();
    is_identical &= glass_y == info.GetGlassY();
    is_identical &= mole_type == info.GetMoleType();
    is_identical &= mole_scale == info.GetMoleScale();
    is_identical &= mole_x == info.GetMoleX();
    is_identical &= mole_y == info.GetMoleY();
    return is_identical;
}

} // namespace Service::Mii


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/mii/mii_types.h"

namespace Service::Mii {
class StoreData;

// This is nn::mii::detail::CharInfoRaw
class CharInfo {
public:
    void SetFromStoreData(const StoreData& store_data_raw);

    ValidationResult Verify() const;

    Common::UUID GetCreateId() const;
    Nickname GetNickname() const;
    FontRegion GetFontRegion() const;
    FavoriteColor GetFavoriteColor() const;
    Gender GetGender() const;
    u8 GetHeight() const;
    u8 GetBuild() const;
    u8 GetType() const;
    u8 GetRegionMove() const;
    FacelineType GetFacelineType() const;
    FacelineColor GetFacelineColor() const;
    FacelineWrinkle GetFacelineWrinkle() const;
    FacelineMake GetFacelineMake() const;
    HairType GetHairType() const;
    CommonColor GetHairColor() const;
    HairFlip GetHairFlip() const;
    EyeType GetEyeType() const;
    CommonColor GetEyeColor() const;
    u8 GetEyeScale() const;
    u8 GetEyeAspect() const;
    u8 GetEyeRotate() const;
    u8 GetEyeX() const;
    u8 GetEyeY() const;
    EyebrowType GetEyebrowType() const;
    CommonColor GetEyebrowColor() const;
    u8 GetEyebrowScale() const;
    u8 GetEyebrowAspect() const;
    u8 GetEyebrowRotate() const;
    u8 GetEyebrowX() const;
    u8 GetEyebrowY() const;
    NoseType GetNoseType() const;
    u8 GetNoseScale() const;
    u8 GetNoseY() const;
    MouthType GetMouthType() const;
    CommonColor GetMouthColor() const;
    u8 GetMouthScale() const;
    u8 GetMouthAspect() const;
    u8 GetMouthY() const;
    CommonColor GetBeardColor() const;
    BeardType GetBeardType() const;
    MustacheType GetMustacheType() const;
    u8 GetMustacheScale() const;
    u8 GetMustacheY() const;
    GlassType GetGlassType() const;
    CommonColor GetGlassColor() const;
    u8 GetGlassScale() const;
    u8 GetGlassY() const;
    MoleType GetMoleType() const;
    u8 GetMoleScale() const;
    u8 GetMoleX() const;
    u8 GetMoleY() const;

    bool operator==(const CharInfo& info);

private:
    Common::UUID create_id{};
    Nickname name{};
    u16 null_terminator{};
    FontRegion font_region{};
    FavoriteColor favorite_color{};
    Gender gender{};
    u8 height{};
    u8 build{};
    u8 type{};
    u8 region_move{};
    FacelineType faceline_type{};
    FacelineColor faceline_color{};
    FacelineWrinkle faceline_wrinkle{};
    FacelineMake faceline_make{};
    HairType hair_type{};
    CommonColor hair_color{};
    HairFlip hair_flip{};
    EyeType eye_type{};
    CommonColor eye_color{};
    u8 eye_scale{};
    u8 eye_aspect{};
    u8 eye_rotate{};
    u8 eye_x{};
    u8 eye_y{};
    EyebrowType eyebrow_type{};
    CommonColor eyebrow_color{};
    u8 eyebrow_scale{};
    u8 eyebrow_aspect{};
    u8 eyebrow_rotate{};
    u8 eyebrow_x{};
    u8 eyebrow_y{};
    NoseType nose_type{};
    u8 nose_scale{};
    u8 nose_y{};
    MouthType mouth_type{};
    CommonColor mouth_color{};
    u8 mouth_scale{};
    u8 mouth_aspect{};
    u8 mouth_y{};
    CommonColor beard_color{};
    BeardType beard_type{};
    MustacheType mustache_type{};
    u8 mustache_scale{};
    u8 mustache_y{};
    GlassType glass_type{};
    CommonColor glass_color{};
    u8 glass_scale{};
    u8 glass_y{};
    MoleType mole_type{};
    u8 mole_scale{};
    u8 mole_x{};
    u8 mole_y{};
    u8 padding{};
};
static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<CharInfo>,
              "All bits of CharInfo must contribute to its value.");

struct CharInfoElement {
    CharInfo char_info{};
    Source source{};
};
static_assert(sizeof(CharInfoElement) == 0x5c, "CharInfoElement has incorrect size.");

}; // namespace Service::Mii