// 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, }; struct MiiInfo { Common::UUID 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."); 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; 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 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); private: void WriteToFile(); void ReadFromFile(); MiiStoreData CreateMiiWithUniqueUUID() const; void EnsureDatabasePartition(); MiiDatabase database; }; }; // namespace Service::Mii