// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/uuid.h" namespace Service::Mii { constexpr std::size_t MAX_MIIS{100}; constexpr u32 INVALID_INDEX{0xFFFFFFFF}; struct RandomParameters { u32 unknown_1{}; u32 unknown_2{}; u32 unknown_3{}; }; static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size."); enum class Source : u32 { Database = 0, Default = 1, Account = 2, Friend = 3, }; std::ostream& operator<<(std::ostream& os, Source source); struct MiiInfo { Common::UUID uuid{Common::INVALID_UUID}; std::array name{}; u8 font_region{}; u8 favorite_color{}; u8 gender{}; u8 height{}; u8 weight{}; u8 mii_type{}; u8 mii_region{}; u8 face_type{}; u8 face_color{}; u8 face_wrinkle{}; u8 face_makeup{}; u8 hair_type{}; u8 hair_color{}; bool hair_flip{}; u8 eye_type{}; u8 eye_color{}; u8 eye_scale{}; u8 eye_aspect_ratio{}; u8 eye_rotate{}; u8 eye_x{}; u8 eye_y{}; u8 eyebrow_type{}; u8 eyebrow_color{}; u8 eyebrow_scale{}; u8 eyebrow_aspect_ratio{}; u8 eyebrow_rotate{}; u8 eyebrow_x{}; u8 eyebrow_y{}; u8 nose_type{}; u8 nose_scale{}; u8 nose_y{}; u8 mouth_type{}; u8 mouth_color{}; u8 mouth_scale{}; u8 mouth_aspect_ratio{}; u8 mouth_y{}; u8 facial_hair_color{}; u8 beard_type{}; u8 mustache_type{}; u8 mustache_scale{}; u8 mustache_y{}; u8 glasses_type{}; u8 glasses_color{}; u8 glasses_scale{}; u8 glasses_y{}; u8 mole_type{}; u8 mole_scale{}; u8 mole_x{}; u8 mole_y{}; INSERT_PADDING_BYTES(1); std::u16string Name() const; }; static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size."); static_assert(std::has_unique_object_representations_v, "All bits of MiiInfo must contribute to its value."); bool operator==(const MiiInfo& lhs, const MiiInfo& rhs); bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs); #pragma pack(push, 4) struct MiiInfoElement { MiiInfo info{}; Source source{}; }; static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size."); struct MiiStoreBitFields { union { u32 word_0{}; BitField<24, 8, u32> hair_type; BitField<23, 1, u32> mole_type; BitField<16, 7, u32> height; BitField<15, 1, u32> hair_flip; BitField<8, 7, u32> weight; BitField<0, 7, u32> hair_color; }; union { u32 word_1{}; BitField<31, 1, u32> gender; BitField<24, 7, u32> eye_color; BitField<16, 7, u32> eyebrow_color; BitField<8, 7, u32> mouth_color; BitField<0, 7, u32> facial_hair_color; }; union { u32 word_2{}; BitField<31, 1, u32> mii_type; BitField<24, 7, u32> glasses_color; BitField<22, 2, u32> font_region; BitField<16, 6, u32> eye_type; BitField<14, 2, u32> mii_region; BitField<8, 6, u32> mouth_type; BitField<5, 3, u32> glasses_scale; BitField<0, 5, u32> eye_y; }; union { u32 word_3{}; BitField<29, 3, u32> mustache_type; BitField<24, 5, u32> eyebrow_type; BitField<21, 3, u32> beard_type; BitField<16, 5, u32> nose_type; BitField<13, 3, u32> mouth_aspect; BitField<8, 5, u32> nose_y; BitField<5, 3, u32> eyebrow_aspect; BitField<0, 5, u32> mouth_y; }; union { u32 word_4{}; BitField<29, 3, u32> eye_rotate; BitField<24, 5, u32> mustache_y; BitField<21, 3, u32> eye_aspect; BitField<16, 5, u32> glasses_y; BitField<13, 3, u32> eye_scale; BitField<8, 5, u32> mole_x; BitField<0, 5, u32> mole_y; }; union { u32 word_5{}; BitField<24, 5, u32> glasses_type; BitField<20, 4, u32> face_type; BitField<16, 4, u32> favorite_color; BitField<12, 4, u32> face_wrinkle; BitField<8, 4, u32> face_color; BitField<4, 4, u32> eye_x; BitField<0, 4, u32> face_makeup; }; union { u32 word_6{}; BitField<28, 4, u32> eyebrow_rotate; BitField<24, 4, u32> eyebrow_scale; BitField<20, 4, u32> eyebrow_y; BitField<16, 4, u32> eyebrow_x; BitField<12, 4, u32> mouth_scale; BitField<8, 4, u32> nose_scale; BitField<4, 4, u32> mole_scale; BitField<0, 4, u32> mustache_scale; }; }; static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size."); static_assert(std::is_trivially_copyable_v, "MiiStoreBitFields is not trivially copyable."); struct MiiStoreData { // This corresponds to the above structure MiiStoreBitFields. I did it like this because the // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is // not suitable for our uses. std::array data{}; static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); std::array name{}; Common::UUID uuid{Common::INVALID_UUID}; u16 crc_1{}; u16 crc_2{}; std::u16string Name() const; }; static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size."); struct MiiStoreDataElement { MiiStoreData data{}; Source source{}; }; static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size."); struct MiiDatabase { u32 magic{}; // 'NFDB' std::array miis{}; INSERT_PADDING_BYTES(1); u8 count{}; u16 crc{}; }; static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size."); #pragma pack(pop) // 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. class MiiManager { public: MiiManager(); ~MiiManager(); MiiInfo CreateRandom(RandomParameters params); MiiInfo CreateDefault(u32 index); bool CheckUpdatedFlag() const; void ResetUpdatedFlag(); bool IsTestModeEnabled() const; bool Empty() const; bool Full() const; void Clear(); u32 Size() const; MiiInfo GetInfo(u32 index) const; MiiInfoElement GetInfoElement(u32 index) const; MiiStoreData GetStoreData(u32 index) const; MiiStoreDataElement GetStoreDataElement(u32 index) const; bool Remove(Common::UUID uuid); u32 IndexOf(Common::UUID uuid) const; u32 IndexOf(const MiiInfo& info) const; bool Move(Common::UUID uuid, u32 new_index); bool AddOrReplace(const MiiStoreData& data); bool DestroyFile(); bool DeleteFile(); private: void WriteToFile(); void ReadFromFile(); MiiStoreData CreateMiiWithUniqueUUID() const; void EnsureDatabasePartition(); MiiDatabase database; bool updated_flag{}; bool is_test_mode_enabled{}; }; }; // namespace Service::Mii