summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-12-18 09:08:44 -0500
committerGravatar Zach Hilman2019-04-25 08:07:57 -0400
commitdaf5b8c61b217632193d2777f8e5787d3c14e50a (patch)
treebb662788747e3ec4c2d2b4a6aff56870b062cb3e /src
parentcommon: Extract UUID to its own class (diff)
downloadyuzu-daf5b8c61b217632193d2777f8e5787d3c14e50a.tar.gz
yuzu-daf5b8c61b217632193d2777f8e5787d3c14e50a.tar.xz
yuzu-daf5b8c61b217632193d2777f8e5787d3c14e50a.zip
mii: Add MiiManager class to manage Mii database
Provides serialization/deserialization to the database in system save files, accessors for database state and proper handling of both major Mii formats (MiiInfo and MiiStoreData)
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp369
-rw-r--r--src/core/hle/service/mii/mii_manager.h253
2 files changed, 622 insertions, 0 deletions
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
new file mode 100644
index 000000000..25dfd8d48
--- /dev/null
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -0,0 +1,369 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7#include "common/assert.h"
8#include "common/file_util.h"
9#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/hle/service/mii/mii_manager.h"
12
13namespace Service::Mii {
14
15constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
16constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {'y', 'u', 'z', 'u', '\0'};
17
18// This value was retrieved from HW test
19constexpr MiiStoreData DEFAULT_MII = {
20 {
21 0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
22 0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
23 },
24 {'y', 'u', 'z', 'u', '\0'},
25 Common::UUID{1, 0},
26 0,
27 0,
28};
29
30// Default values taken from multiple real databases
31const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
32
33template <typename T, std::size_t s1, std::size_t s2>
34std::array<T, s2> ResizeArray(const std::array<T, s1>& in) {
35 std::array<T, s2> out{};
36 std::memcpy(out.data(), in.data(), sizeof(T) * std::min(s1, s2));
37 return out;
38}
39
40MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
41 MiiStoreBitFields bf{};
42 std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
43 return {
44 data.uuid,
45 ResizeArray<char16_t, 10, 11>(data.name),
46 static_cast<u8>(bf.font_region.Value()),
47 static_cast<u8>(bf.favorite_color.Value()),
48 static_cast<u8>(bf.gender.Value()),
49 static_cast<u8>(bf.height.Value()),
50 static_cast<u8>(bf.weight.Value()),
51 static_cast<u8>(bf.mii_type.Value()),
52 static_cast<u8>(bf.mii_region.Value()),
53 static_cast<u8>(bf.face_type.Value()),
54 static_cast<u8>(bf.face_color.Value()),
55 static_cast<u8>(bf.face_wrinkle.Value()),
56 static_cast<u8>(bf.face_makeup.Value()),
57 static_cast<u8>(bf.hair_type.Value()),
58 static_cast<u8>(bf.hair_color.Value()),
59 static_cast<bool>(bf.hair_flip.Value()),
60 static_cast<u8>(bf.eye_type.Value()),
61 static_cast<u8>(bf.eye_color.Value()),
62 static_cast<u8>(bf.eye_scale.Value()),
63 static_cast<u8>(bf.eye_aspect.Value()),
64 static_cast<u8>(bf.eye_rotate.Value()),
65 static_cast<u8>(bf.eye_x.Value()),
66 static_cast<u8>(bf.eye_y.Value()),
67 static_cast<u8>(bf.eyebrow_type.Value()),
68 static_cast<u8>(bf.eyebrow_color.Value()),
69 static_cast<u8>(bf.eyebrow_scale.Value()),
70 static_cast<u8>(bf.eyebrow_aspect.Value()),
71 static_cast<u8>(bf.eyebrow_rotate.Value()),
72 static_cast<u8>(bf.eyebrow_x.Value()),
73 static_cast<u8>(bf.eyebrow_y.Value()),
74 static_cast<u8>(bf.nose_type.Value()),
75 static_cast<u8>(bf.nose_scale.Value()),
76 static_cast<u8>(bf.nose_y.Value()),
77 static_cast<u8>(bf.mouth_type.Value()),
78 static_cast<u8>(bf.mouth_color.Value()),
79 static_cast<u8>(bf.mouth_scale.Value()),
80 static_cast<u8>(bf.mouth_aspect.Value()),
81 static_cast<u8>(bf.mouth_y.Value()),
82 static_cast<u8>(bf.facial_hair_color.Value()),
83 static_cast<u8>(bf.beard_type.Value()),
84 static_cast<u8>(bf.mustache_type.Value()),
85 static_cast<u8>(bf.mustache_scale.Value()),
86 static_cast<u8>(bf.mustache_y.Value()),
87 static_cast<u8>(bf.glasses_type.Value()),
88 static_cast<u8>(bf.glasses_color.Value()),
89 static_cast<u8>(bf.glasses_scale.Value()),
90 static_cast<u8>(bf.glasses_y.Value()),
91 static_cast<u8>(bf.mole_type.Value()),
92 static_cast<u8>(bf.mole_scale.Value()),
93 static_cast<u8>(bf.mole_x.Value()),
94 static_cast<u8>(bf.mole_y.Value()),
95 0x00,
96 };
97}
98MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
99 MiiStoreData out{};
100 out.name = ResizeArray<char16_t, 11, 10>(info.name);
101 out.uuid = info.uuid;
102
103 MiiStoreBitFields bf{};
104
105 bf.hair_type.Assign(info.hair_type);
106 bf.mole_type.Assign(info.mole_type);
107 bf.height.Assign(info.height);
108 bf.hair_flip.Assign(info.hair_flip);
109 bf.weight.Assign(info.weight);
110 bf.hair_color.Assign(info.hair_color);
111
112 bf.gender.Assign(info.gender);
113 bf.eye_color.Assign(info.eye_color);
114 bf.eyebrow_color.Assign(info.eyebrow_color);
115 bf.mouth_color.Assign(info.mouth_color);
116 bf.facial_hair_color.Assign(info.facial_hair_color);
117
118 bf.mii_type.Assign(info.mii_type);
119 bf.glasses_color.Assign(info.glasses_color);
120 bf.font_region.Assign(info.font_region);
121 bf.eye_type.Assign(info.eye_type);
122 bf.mii_region.Assign(info.mii_region);
123 bf.mouth_type.Assign(info.mouth_type);
124 bf.glasses_scale.Assign(info.glasses_scale);
125 bf.eye_y.Assign(info.eye_y);
126
127 bf.mustache_type.Assign(info.mustache_type);
128 bf.eyebrow_type.Assign(info.eyebrow_type);
129 bf.beard_type.Assign(info.beard_type);
130 bf.nose_type.Assign(info.nose_type);
131 bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
132 bf.nose_y.Assign(info.nose_y);
133 bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
134 bf.mouth_y.Assign(info.mouth_y);
135
136 bf.eye_rotate.Assign(info.eye_rotate);
137 bf.mustache_y.Assign(info.mustache_y);
138 bf.eye_aspect.Assign(info.eye_aspect_ratio);
139 bf.glasses_y.Assign(info.glasses_y);
140 bf.eye_scale.Assign(info.eye_scale);
141 bf.mole_x.Assign(info.mole_x);
142 bf.mole_y.Assign(info.mole_y);
143
144 bf.glasses_type.Assign(info.glasses_type);
145 bf.face_type.Assign(info.face_type);
146 bf.favorite_color.Assign(info.favorite_color);
147 bf.face_wrinkle.Assign(info.face_wrinkle);
148 bf.face_color.Assign(info.face_color);
149 bf.eye_x.Assign(info.eye_x);
150 bf.face_makeup.Assign(info.face_makeup);
151
152 bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
153 bf.eyebrow_scale.Assign(info.eyebrow_scale);
154 bf.eyebrow_y.Assign(info.eyebrow_y);
155 bf.eyebrow_x.Assign(info.eyebrow_x);
156 bf.mouth_scale.Assign(info.mouth_scale);
157 bf.nose_scale.Assign(info.nose_scale);
158 bf.mole_scale.Assign(info.mole_scale);
159 bf.mustache_scale.Assign(info.mustache_scale);
160
161 std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
162
163 return out;
164}
165
166std::u16string MiiInfo::Name() const {
167 return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
168}
169
170bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
171 return std::memcmp(&lhs, &rhs, sizeof(MiiInfo));
172}
173
174bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
175 return !operator==(lhs, rhs);
176}
177
178std::u16string MiiStoreData::Name() const {
179 return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
180}
181
182MiiManager::MiiManager() = default;
183
184MiiManager::~MiiManager() = default;
185
186MiiInfo MiiManager::CreateRandom(RandomParameters params) {
187 LOG_WARNING(Service_Mii,
188 "(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
189 params.unknown_1, params.unknown_2, params.unknown_3);
190
191 auto new_mii = DEFAULT_MII;
192
193 do {
194 new_mii.uuid = Common::UUID::Generate();
195 } while (IndexOf(new_mii.uuid) == INVALID_INDEX);
196
197 return ConvertStoreDataToInfo(new_mii);
198}
199
200MiiInfo MiiManager::CreateDefault(u32 index) {
201 auto new_mii = DEFAULT_MII;
202
203 do {
204 new_mii.uuid = Common::UUID::Generate();
205 } while (IndexOf(new_mii.uuid) == INVALID_INDEX);
206
207 ASSERT(index < MAX_MIIS);
208 database.miis[index] = new_mii;
209 std::stable_partition(database.miis.begin(), database.miis.end(),
210 [](const MiiStoreData& elem) { return elem.uuid; });
211
212 return ConvertStoreDataToInfo(new_mii);
213}
214
215bool MiiManager::Empty() const {
216 return Size() == 0;
217}
218
219bool MiiManager::Full() const {
220 return Size() == MAX_MIIS;
221}
222
223void MiiManager::Clear() {
224 std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
225}
226
227u32 MiiManager::Size() const {
228 return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
229 [](const MiiStoreData& elem) { return elem.uuid; }));
230}
231
232MiiInfo MiiManager::GetInfo(u32 index) const {
233 return ConvertStoreDataToInfo(GetStoreData(index));
234}
235
236MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
237 return {GetInfo(index), Source::Database};
238}
239
240MiiStoreData MiiManager::GetStoreData(u32 index) const {
241 return database.miis.at(index);
242}
243
244MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
245 return {GetStoreData(index), Source::Database};
246}
247
248bool MiiManager::Remove(Common::UUID uuid) {
249 const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
250 [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
251
252 if (iter == database.miis.end())
253 return false;
254
255 *iter = MiiStoreData{};
256 std::stable_partition(database.miis.begin(), database.miis.end(),
257 [](const MiiStoreData& elem) { return elem.uuid; });
258 return true;
259}
260
261u32 MiiManager::IndexOf(Common::UUID uuid) const {
262 const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
263 [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
264
265 if (iter == database.miis.end())
266 return INVALID_INDEX;
267
268 return static_cast<u32>(std::distance(database.miis.begin(), iter));
269}
270
271u32 MiiManager::IndexOf(MiiInfo info) const {
272 const auto iter =
273 std::find_if(database.miis.begin(), database.miis.end(), [info](const MiiStoreData& elem) {
274 return ConvertStoreDataToInfo(elem) == info;
275 });
276
277 if (iter == database.miis.end())
278 return INVALID_INDEX;
279
280 return static_cast<u32>(std::distance(database.miis.begin(), iter));
281}
282
283bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
284 const auto index = IndexOf(uuid);
285
286 if (index == INVALID_INDEX || new_index >= MAX_MIIS)
287 return false;
288
289 const auto moving = database.miis[index];
290 const auto replacing = database.miis[new_index];
291 if (replacing.uuid) {
292 database.miis[index] = replacing;
293 database.miis[new_index] = moving;
294 } else {
295 database.miis[index] = MiiStoreData{};
296 database.miis[new_index] = moving;
297 }
298
299 std::stable_partition(database.miis.begin(), database.miis.end(),
300 [](const MiiStoreData& elem) { return elem.uuid; });
301 return true;
302}
303
304bool MiiManager::AddOrReplace(MiiStoreData data) {
305 const auto index = IndexOf(data.uuid);
306
307 if (index == INVALID_INDEX) {
308 const auto size = Size();
309 if (size == MAX_MIIS)
310 return false;
311 database.miis[size] = data;
312 } else {
313 database.miis[index] = data;
314 }
315
316 return true;
317}
318
319void MiiManager::WriteToFile() {
320 const auto raw_path =
321 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
322 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
323 FileUtil::Delete(raw_path);
324
325 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
326
327 if (!FileUtil::CreateFullPath(path)) {
328 LOG_WARNING(Service_Mii,
329 "Failed to create full path of MiiDatabase.dat. Create the directory "
330 "nand/system/save/8000000000000030 to mitigate this "
331 "issue.");
332 return;
333 }
334
335 FileUtil::IOFile save(path, "wb");
336
337 if (!save.IsOpen()) {
338 LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
339 "made in current session will be saved.");
340 return;
341 }
342
343 save.Resize(sizeof(MiiDatabase));
344 save.WriteBytes(&database, sizeof(MiiDatabase));
345}
346
347void MiiManager::ReadFromFile() {
348 FileUtil::IOFile save(
349 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
350
351 if (!save.IsOpen()) {
352 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
353 "blank Mii database with no Miis.");
354 std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
355 return;
356 }
357
358 if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
359 LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
360 "Mii database with no Miis.");
361 std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
362 return;
363 }
364
365 std::stable_partition(database.miis.begin(), database.miis.end(),
366 [](const MiiStoreData& elem) { return elem.uuid; });
367}
368
369} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
new file mode 100644
index 000000000..069247cb6
--- /dev/null
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -0,0 +1,253 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/uuid.h"
10
11namespace Service::Mii {
12
13constexpr std::size_t MAX_MIIS = 100;
14constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
15
16struct RandomParameters {
17 u32 unknown_1;
18 u32 unknown_2;
19 u32 unknown_3;
20};
21static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
22
23enum class Source : u32 {
24 Database = 0,
25 Default = 1,
26 Account = 2,
27 Friend = 3,
28};
29
30struct MiiInfo {
31 Common::UUID uuid;
32 std::array<char16_t, 11> name;
33 u8 font_region;
34 u8 favorite_color;
35 u8 gender;
36 u8 height;
37 u8 weight;
38 u8 mii_type;
39 u8 mii_region;
40 u8 face_type;
41 u8 face_color;
42 u8 face_wrinkle;
43 u8 face_makeup;
44 u8 hair_type;
45 u8 hair_color;
46 bool hair_flip;
47 u8 eye_type;
48 u8 eye_color;
49 u8 eye_scale;
50 u8 eye_aspect_ratio;
51 u8 eye_rotate;
52 u8 eye_x;
53 u8 eye_y;
54 u8 eyebrow_type;
55 u8 eyebrow_color;
56 u8 eyebrow_scale;
57 u8 eyebrow_aspect_ratio;
58 u8 eyebrow_rotate;
59 u8 eyebrow_x;
60 u8 eyebrow_y;
61 u8 nose_type;
62 u8 nose_scale;
63 u8 nose_y;
64 u8 mouth_type;
65 u8 mouth_color;
66 u8 mouth_scale;
67 u8 mouth_aspect_ratio;
68 u8 mouth_y;
69 u8 facial_hair_color;
70 u8 beard_type;
71 u8 mustache_type;
72 u8 mustache_scale;
73 u8 mustache_y;
74 u8 glasses_type;
75 u8 glasses_color;
76 u8 glasses_scale;
77 u8 glasses_y;
78 u8 mole_type;
79 u8 mole_scale;
80 u8 mole_x;
81 u8 mole_y;
82 INSERT_PADDING_BYTES(1);
83
84 std::u16string Name() const;
85};
86static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
87
88bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
89bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
90
91#pragma pack(push, 4)
92struct MiiInfoElement {
93 MiiInfo info;
94 Source source;
95};
96static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
97
98struct MiiStoreBitFields {
99 union {
100 u32 word_0;
101
102 BitField<24, 8, u32> hair_type;
103 BitField<23, 1, u32> mole_type;
104 BitField<16, 7, u32> height;
105 BitField<15, 1, u32> hair_flip;
106 BitField<8, 7, u32> weight;
107 BitField<0, 7, u32> hair_color;
108 };
109
110 union {
111 u32 word_1;
112
113 BitField<31, 1, u32> gender;
114 BitField<24, 7, u32> eye_color;
115 BitField<16, 7, u32> eyebrow_color;
116 BitField<8, 7, u32> mouth_color;
117 BitField<0, 7, u32> facial_hair_color;
118 };
119
120 union {
121 u32 word_2;
122
123 BitField<31, 1, u32> mii_type;
124 BitField<24, 7, u32> glasses_color;
125 BitField<22, 2, u32> font_region;
126 BitField<16, 6, u32> eye_type;
127 BitField<14, 2, u32> mii_region;
128 BitField<8, 6, u32> mouth_type;
129 BitField<5, 3, u32> glasses_scale;
130 BitField<0, 5, u32> eye_y;
131 };
132
133 union {
134 u32 word_3;
135
136 BitField<29, 3, u32> mustache_type;
137 BitField<24, 5, u32> eyebrow_type;
138 BitField<21, 3, u32> beard_type;
139 BitField<16, 5, u32> nose_type;
140 BitField<13, 3, u32> mouth_aspect;
141 BitField<8, 5, u32> nose_y;
142 BitField<5, 3, u32> eyebrow_aspect;
143 BitField<0, 5, u32> mouth_y;
144 };
145
146 union {
147 u32 word_4;
148
149 BitField<29, 3, u32> eye_rotate;
150 BitField<24, 5, u32> mustache_y;
151 BitField<21, 3, u32> eye_aspect;
152 BitField<16, 5, u32> glasses_y;
153 BitField<13, 3, u32> eye_scale;
154 BitField<8, 5, u32> mole_x;
155 BitField<0, 5, u32> mole_y;
156 };
157
158 union {
159 u32 word_5;
160
161 BitField<24, 5, u32> glasses_type;
162 BitField<20, 4, u32> face_type;
163 BitField<16, 4, u32> favorite_color;
164 BitField<12, 4, u32> face_wrinkle;
165 BitField<8, 4, u32> face_color;
166 BitField<4, 4, u32> eye_x;
167 BitField<0, 4, u32> face_makeup;
168 };
169
170 union {
171 u32 word_6;
172
173 BitField<28, 4, u32> eyebrow_rotate;
174 BitField<24, 4, u32> eyebrow_scale;
175 BitField<20, 4, u32> eyebrow_y;
176 BitField<16, 4, u32> eyebrow_x;
177 BitField<12, 4, u32> mouth_scale;
178 BitField<8, 4, u32> nose_scale;
179 BitField<4, 4, u32> mole_scale;
180 BitField<0, 4, u32> mustache_scale;
181 };
182};
183static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
184
185struct MiiStoreData {
186 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
187 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
188 // not suitable for our uses.
189 std::array<u8, 0x1C> data;
190 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
191
192 std::array<char16_t, 10> name;
193 Common::UUID uuid;
194 u16 crc_1;
195 u16 crc_2;
196
197 std::u16string Name() const;
198};
199static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
200
201struct MiiStoreDataElement {
202 MiiStoreData data;
203 Source source;
204};
205static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
206
207struct MiiDatabase {
208 u32 magic; // 'NFDB'
209 std::array<MiiStoreData, MAX_MIIS> miis;
210 INSERT_PADDING_BYTES(1);
211 u8 count;
212 u16 crc;
213};
214static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
215#pragma pack(pop)
216
217// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
218// with providing an easy interface for HLE emulation of the mii service.
219class MiiManager {
220public:
221 MiiManager();
222 ~MiiManager();
223
224 MiiInfo CreateRandom(RandomParameters params);
225 MiiInfo CreateDefault(u32 index);
226
227 bool Empty() const;
228 bool Full() const;
229
230 void Clear();
231
232 u32 Size() const;
233
234 MiiInfo GetInfo(u32 index) const;
235 MiiInfoElement GetInfoElement(u32 index) const;
236 MiiStoreData GetStoreData(u32 index) const;
237 MiiStoreDataElement GetStoreDataElement(u32 index) const;
238
239 bool Remove(Common::UUID uuid);
240 u32 IndexOf(Common::UUID uuid) const;
241 u32 IndexOf(MiiInfo info) const;
242
243 bool Move(Common::UUID uuid, u32 new_index);
244 bool AddOrReplace(MiiStoreData data);
245
246private:
247 void WriteToFile();
248 void ReadFromFile();
249
250 MiiDatabase database;
251};
252
253}; // namespace Service::Mii