summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Narr the Reg2022-08-30 00:33:47 -0500
committerGravatar german772022-09-07 01:04:00 -0500
commit19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e (patch)
treef96054552ebe182e75fd77eabdcdf205a0dffe46 /src
parentcore: nfp: Implement amiibo encryption (diff)
downloadyuzu-19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e.tar.gz
yuzu-19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e.tar.xz
yuzu-19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e.zip
core: nfp: Implement Convert and RecreateApplicationArea, accuracy fixes
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit_types.h2
-rw-r--r--src/core/hle/service/mii/mii.cpp32
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp88
-rw-r--r--src/core/hle/service/mii/mii_manager.h9
-rw-r--r--src/core/hle/service/mii/types.h138
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp81
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h3
-rw-r--r--src/core/hle/service/nfp/amiibo_types.h161
-rw-r--r--src/core/hle/service/nfp/nfp.cpp95
-rw-r--r--src/core/hle/service/nfp/nfp.h4
10 files changed, 356 insertions, 257 deletions
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h
index 1b145b696..4705d019f 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit_types.h
+++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h
@@ -32,7 +32,7 @@ enum class MiiEditResult : u32 {
32}; 32};
33 33
34struct MiiEditCharInfo { 34struct MiiEditCharInfo {
35 Service::Mii::MiiInfo mii_info{}; 35 Service::Mii::CharInfo mii_info{};
36}; 36};
37static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size."); 37static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");
38 38
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index efb569993..390514fdc 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -43,7 +43,7 @@ public:
43 {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, 43 {20, nullptr, "IsBrokenDatabaseWithClearFlag"},
44 {21, &IDatabaseService::GetIndex, "GetIndex"}, 44 {21, &IDatabaseService::GetIndex, "GetIndex"},
45 {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, 45 {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
46 {23, nullptr, "Convert"}, 46 {23, &IDatabaseService::Convert, "Convert"},
47 {24, nullptr, "ConvertCoreDataToCharInfo"}, 47 {24, nullptr, "ConvertCoreDataToCharInfo"},
48 {25, nullptr, "ConvertCharInfoToCoreData"}, 48 {25, nullptr, "ConvertCharInfoToCoreData"},
49 {26, nullptr, "Append"}, 49 {26, nullptr, "Append"},
@@ -130,7 +130,7 @@ private:
130 return; 130 return;
131 } 131 }
132 132
133 std::vector<MiiInfo> values; 133 std::vector<CharInfo> values;
134 for (const auto& element : *result) { 134 for (const auto& element : *result) {
135 values.emplace_back(element.info); 135 values.emplace_back(element.info);
136 } 136 }
@@ -144,7 +144,7 @@ private:
144 144
145 void UpdateLatest(Kernel::HLERequestContext& ctx) { 145 void UpdateLatest(Kernel::HLERequestContext& ctx) {
146 IPC::RequestParser rp{ctx}; 146 IPC::RequestParser rp{ctx};
147 const auto info{rp.PopRaw<MiiInfo>()}; 147 const auto info{rp.PopRaw<CharInfo>()};
148 const auto source_flag{rp.PopRaw<SourceFlag>()}; 148 const auto source_flag{rp.PopRaw<SourceFlag>()};
149 149
150 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); 150 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
@@ -156,9 +156,9 @@ private:
156 return; 156 return;
157 } 157 }
158 158
159 IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; 159 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
160 rb.Push(ResultSuccess); 160 rb.Push(ResultSuccess);
161 rb.PushRaw<MiiInfo>(*result); 161 rb.PushRaw<CharInfo>(*result);
162 } 162 }
163 163
164 void BuildRandom(Kernel::HLERequestContext& ctx) { 164 void BuildRandom(Kernel::HLERequestContext& ctx) {
@@ -191,9 +191,9 @@ private:
191 return; 191 return;
192 } 192 }
193 193
194 IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; 194 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
195 rb.Push(ResultSuccess); 195 rb.Push(ResultSuccess);
196 rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race)); 196 rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race));
197 } 197 }
198 198
199 void BuildDefault(Kernel::HLERequestContext& ctx) { 199 void BuildDefault(Kernel::HLERequestContext& ctx) {
@@ -210,14 +210,14 @@ private:
210 return; 210 return;
211 } 211 }
212 212
213 IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; 213 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
214 rb.Push(ResultSuccess); 214 rb.Push(ResultSuccess);
215 rb.PushRaw<MiiInfo>(manager.BuildDefault(index)); 215 rb.PushRaw<CharInfo>(manager.BuildDefault(index));
216 } 216 }
217 217
218 void GetIndex(Kernel::HLERequestContext& ctx) { 218 void GetIndex(Kernel::HLERequestContext& ctx) {
219 IPC::RequestParser rp{ctx}; 219 IPC::RequestParser rp{ctx};
220 const auto info{rp.PopRaw<MiiInfo>()}; 220 const auto info{rp.PopRaw<CharInfo>()};
221 221
222 LOG_DEBUG(Service_Mii, "called"); 222 LOG_DEBUG(Service_Mii, "called");
223 223
@@ -239,6 +239,18 @@ private:
239 rb.Push(ResultSuccess); 239 rb.Push(ResultSuccess);
240 } 240 }
241 241
242 void Convert(Kernel::HLERequestContext& ctx) {
243 IPC::RequestParser rp{ctx};
244
245 const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
246
247 LOG_INFO(Service_Mii, "called");
248
249 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
250 rb.Push(ResultSuccess);
251 rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3));
252 }
253
242 constexpr bool IsInterfaceVersionSupported(u32 interface_version) const { 254 constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
243 return current_interface_version >= interface_version; 255 return current_interface_version >= interface_version;
244 } 256 }
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 544c92a00..97d1b948f 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -42,7 +42,7 @@ std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& i
42 return out; 42 return out;
43} 43}
44 44
45MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { 45CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
46 MiiStoreBitFields bf; 46 MiiStoreBitFields bf;
47 std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); 47 std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
48 48
@@ -409,8 +409,8 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
409 return static_cast<u32>(count); 409 return static_cast<u32>(count);
410} 410}
411 411
412ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info, 412ResultVal<CharInfo> MiiManager::UpdateLatest([[maybe_unused]] const CharInfo& info,
413 SourceFlag source_flag) { 413 SourceFlag source_flag) {
414 if ((source_flag & SourceFlag::Database) == SourceFlag::None) { 414 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
415 return ERROR_CANNOT_FIND_ENTRY; 415 return ERROR_CANNOT_FIND_ENTRY;
416 } 416 }
@@ -419,14 +419,90 @@ ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info
419 return ERROR_CANNOT_FIND_ENTRY; 419 return ERROR_CANNOT_FIND_ENTRY;
420} 420}
421 421
422MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { 422CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
423 return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); 423 return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
424} 424}
425 425
426MiiInfo MiiManager::BuildDefault(std::size_t index) { 426CharInfo MiiManager::BuildDefault(std::size_t index) {
427 return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); 427 return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
428} 428}
429 429
430CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
431 Service::Mii::MiiManager manager;
432 auto mii = manager.BuildDefault(0);
433
434 // Check if mii data exist
435 if (mii_v3.mii_name[0] == 0) {
436 return mii;
437 }
438
439 // TODO: We are ignoring a bunch of data from the mii_v3
440
441 mii.gender = static_cast<u8>(mii_v3.mii_information.gender);
442 mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color);
443 mii.height = mii_v3.height;
444 mii.build = mii_v3.build;
445
446 mii.font_region = mii_v3.region_information.character_set;
447 memcpy(mii.name.data(), mii_v3.mii_name.data(), 10);
448
449 mii.faceline_type = mii_v3.appearance_bits1.face_shape;
450 mii.faceline_color = mii_v3.appearance_bits1.skin_color;
451 mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles;
452 mii.faceline_make = mii_v3.appearance_bits2.makeup;
453
454 mii.hair_type = mii_v3.hair_style;
455 mii.hair_color = mii_v3.appearance_bits3.hair_color;
456 mii.hair_flip = mii_v3.appearance_bits3.flip_hair;
457
458 mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type);
459 mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color);
460 mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale);
461 mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch);
462 mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation);
463 mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing);
464 mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position);
465
466 mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style);
467 mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color);
468 mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale);
469 mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale);
470 mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation);
471 mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing);
472 mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position);
473
474 mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type);
475 mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale);
476 mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position);
477
478 mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type);
479 mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color);
480 mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale);
481 mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch);
482 mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position);
483
484 mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type);
485 mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale);
486 mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position);
487
488 mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type);
489 mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color);
490
491 mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type);
492 mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color);
493 mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale);
494 mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position);
495
496 mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled);
497 mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale);
498 mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position);
499 mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position);
500
501 // TODO: Validate mii data
502
503 return mii;
504}
505
430ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { 506ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
431 std::vector<MiiInfoElement> result; 507 std::vector<MiiInfoElement> result;
432 508
@@ -441,7 +517,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_
441 return result; 517 return result;
442} 518}
443 519
444Result MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) { 520Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) {
445 constexpr u32 INVALID_INDEX{0xFFFFFFFF}; 521 constexpr u32 INVALID_INDEX{0xFFFFFFFF};
446 522
447 index = INVALID_INDEX; 523 index = INVALID_INDEX;
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 6a286bd96..d847de0bd 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -19,11 +19,12 @@ public:
19 bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); 19 bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
20 bool IsFullDatabase() const; 20 bool IsFullDatabase() const;
21 u32 GetCount(SourceFlag source_flag) const; 21 u32 GetCount(SourceFlag source_flag) const;
22 ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag); 22 ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);
23 MiiInfo BuildRandom(Age age, Gender gender, Race race); 23 CharInfo BuildRandom(Age age, Gender gender, Race race);
24 MiiInfo BuildDefault(std::size_t index); 24 CharInfo BuildDefault(std::size_t index);
25 CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const;
25 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); 26 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
26 Result GetIndex(const MiiInfo& info, u32& index); 27 Result GetIndex(const CharInfo& info, u32& index);
27 28
28private: 29private:
29 const Common::UUID user_id{}; 30 const Common::UUID user_id{};
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h
index 45edbfeae..9e3247397 100644
--- a/src/core/hle/service/mii/types.h
+++ b/src/core/hle/service/mii/types.h
@@ -86,7 +86,8 @@ enum class SourceFlag : u32 {
86}; 86};
87DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); 87DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
88 88
89struct MiiInfo { 89// nn::mii::CharInfo
90struct CharInfo {
90 Common::UUID uuid; 91 Common::UUID uuid;
91 std::array<char16_t, 11> name; 92 std::array<char16_t, 11> name;
92 u8 font_region; 93 u8 font_region;
@@ -140,16 +141,16 @@ struct MiiInfo {
140 u8 mole_y; 141 u8 mole_y;
141 u8 padding; 142 u8 padding;
142}; 143};
143static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size."); 144static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
144static_assert(std::has_unique_object_representations_v<MiiInfo>, 145static_assert(std::has_unique_object_representations_v<CharInfo>,
145 "All bits of MiiInfo must contribute to its value."); 146 "All bits of CharInfo must contribute to its value.");
146 147
147#pragma pack(push, 4) 148#pragma pack(push, 4)
148 149
149struct MiiInfoElement { 150struct MiiInfoElement {
150 MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {} 151 MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {}
151 152
152 MiiInfo info{}; 153 CharInfo info{};
153 Source source{}; 154 Source source{};
154}; 155};
155static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); 156static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
@@ -243,6 +244,131 @@ static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrec
243static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, 244static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
244 "MiiStoreBitFields is not trivially copyable."); 245 "MiiStoreBitFields is not trivially copyable.");
245 246
247// This is nn::mii::Ver3StoreData
248// Based on citra HLE::Applets::MiiData and PretendoNetwork.
249// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
250// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
251struct Ver3StoreData {
252 u8 version;
253 union {
254 u8 raw;
255
256 BitField<0, 1, u8> allow_copying;
257 BitField<1, 1, u8> profanity_flag;
258 BitField<2, 2, u8> region_lock;
259 BitField<4, 2, u8> character_set;
260 } region_information;
261 u16_be mii_id;
262 u64_be system_id;
263 u32_be specialness_and_creation_date;
264 std::array<u8, 0x6> creator_mac;
265 u16_be padding;
266 union {
267 u16 raw;
268
269 BitField<0, 1, u16> gender;
270 BitField<1, 4, u16> birth_month;
271 BitField<5, 5, u16> birth_day;
272 BitField<10, 4, u16> favorite_color;
273 BitField<14, 1, u16> favorite;
274 } mii_information;
275 std::array<char16_t, 0xA> mii_name;
276 u8 height;
277 u8 build;
278 union {
279 u8 raw;
280
281 BitField<0, 1, u8> disable_sharing;
282 BitField<1, 4, u8> face_shape;
283 BitField<5, 3, u8> skin_color;
284 } appearance_bits1;
285 union {
286 u8 raw;
287
288 BitField<0, 4, u8> wrinkles;
289 BitField<4, 4, u8> makeup;
290 } appearance_bits2;
291 u8 hair_style;
292 union {
293 u8 raw;
294
295 BitField<0, 3, u8> hair_color;
296 BitField<3, 1, u8> flip_hair;
297 } appearance_bits3;
298 union {
299 u32 raw;
300
301 BitField<0, 6, u32> eye_type;
302 BitField<6, 3, u32> eye_color;
303 BitField<9, 4, u32> eye_scale;
304 BitField<13, 3, u32> eye_vertical_stretch;
305 BitField<16, 5, u32> eye_rotation;
306 BitField<21, 4, u32> eye_spacing;
307 BitField<25, 5, u32> eye_y_position;
308 } appearance_bits4;
309 union {
310 u32 raw;
311
312 BitField<0, 5, u32> eyebrow_style;
313 BitField<5, 3, u32> eyebrow_color;
314 BitField<8, 4, u32> eyebrow_scale;
315 BitField<12, 3, u32> eyebrow_yscale;
316 BitField<16, 4, u32> eyebrow_rotation;
317 BitField<21, 4, u32> eyebrow_spacing;
318 BitField<25, 5, u32> eyebrow_y_position;
319 } appearance_bits5;
320 union {
321 u16 raw;
322
323 BitField<0, 5, u16> nose_type;
324 BitField<5, 4, u16> nose_scale;
325 BitField<9, 5, u16> nose_y_position;
326 } appearance_bits6;
327 union {
328 u16 raw;
329
330 BitField<0, 6, u16> mouth_type;
331 BitField<6, 3, u16> mouth_color;
332 BitField<9, 4, u16> mouth_scale;
333 BitField<13, 3, u16> mouth_horizontal_stretch;
334 } appearance_bits7;
335 union {
336 u8 raw;
337
338 BitField<0, 5, u8> mouth_y_position;
339 BitField<5, 3, u8> mustache_type;
340 } appearance_bits8;
341 u8 allow_copying;
342 union {
343 u16 raw;
344
345 BitField<0, 3, u16> bear_type;
346 BitField<3, 3, u16> facial_hair_color;
347 BitField<6, 4, u16> mustache_scale;
348 BitField<10, 5, u16> mustache_y_position;
349 } appearance_bits9;
350 union {
351 u16 raw;
352
353 BitField<0, 4, u16> glasses_type;
354 BitField<4, 3, u16> glasses_color;
355 BitField<7, 4, u16> glasses_scale;
356 BitField<11, 5, u16> glasses_y_position;
357 } appearance_bits10;
358 union {
359 u16 raw;
360
361 BitField<0, 1, u16> mole_enabled;
362 BitField<1, 4, u16> mole_scale;
363 BitField<5, 5, u16> mole_x_position;
364 BitField<10, 5, u16> mole_y_position;
365 } appearance_bits11;
366
367 std::array<u16_le, 0xA> author_name;
368 INSERT_PADDING_BYTES(0x4);
369};
370static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
371
246struct MiiStoreData { 372struct MiiStoreData {
247 using Name = std::array<char16_t, 10>; 373 using Name = std::array<char16_t, 10>;
248 374
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
index 211e518b0..d9d0c8f62 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -16,76 +16,6 @@
16 16
17namespace Service::NFP::AmiiboCrypto { 17namespace Service::NFP::AmiiboCrypto {
18 18
19Service::Mii::MiiInfo AmiiboRegisterInfoToMii(const AmiiboRegisterInfo& mii_info) {
20
21 Service::Mii::MiiManager manager;
22 auto mii = manager.BuildDefault(0);
23
24 // TODO: We are ignoring a bunch of data from the amiibo mii
25
26 mii.gender = static_cast<u8>(mii_info.mii_information.gender);
27 mii.favorite_color = static_cast<u8>(mii_info.mii_information.favorite_color);
28 memcpy(mii.name.data(), mii_info.mii_name.data(), 10);
29 mii.height = mii_info.height;
30 mii.build = mii_info.build;
31
32 mii.faceline_type = mii_info.appearance_bits1.face_shape;
33 mii.faceline_color = mii_info.appearance_bits1.skin_color;
34 mii.faceline_wrinkle = mii_info.appearance_bits2.wrinkles;
35 mii.faceline_make = mii_info.appearance_bits2.makeup;
36
37 mii.hair_type = mii_info.hair_style;
38 mii.hair_color = mii_info.appearance_bits3.hair_color;
39 mii.hair_flip = mii_info.appearance_bits3.flip_hair;
40
41 mii.eye_type = static_cast<u8>(mii_info.appearance_bits4.eye_type);
42 mii.eye_color = static_cast<u8>(mii_info.appearance_bits4.eye_color);
43 mii.eye_scale = static_cast<u8>(mii_info.appearance_bits4.eye_scale);
44 mii.eye_aspect = static_cast<u8>(mii_info.appearance_bits4.eye_vertical_stretch);
45 mii.eye_rotate = static_cast<u8>(mii_info.appearance_bits4.eye_rotation);
46 mii.eye_x = static_cast<u8>(mii_info.appearance_bits4.eye_spacing);
47 mii.eye_y = static_cast<u8>(mii_info.appearance_bits4.eye_y_position);
48
49 mii.eyebrow_type = static_cast<u8>(mii_info.appearance_bits5.eyebrow_style);
50 mii.eyebrow_color = static_cast<u8>(mii_info.appearance_bits5.eyebrow_color);
51 mii.eyebrow_scale = static_cast<u8>(mii_info.appearance_bits5.eyebrow_scale);
52 mii.eyebrow_aspect = static_cast<u8>(mii_info.appearance_bits5.eyebrow_yscale);
53 mii.eyebrow_rotate = static_cast<u8>(mii_info.appearance_bits5.eyebrow_rotation);
54 mii.eyebrow_x = static_cast<u8>(mii_info.appearance_bits5.eyebrow_spacing);
55 mii.eyebrow_y = static_cast<u8>(mii_info.appearance_bits5.eyebrow_y_position);
56
57 mii.nose_type = static_cast<u8>(mii_info.appearance_bits6.nose_type);
58 mii.nose_scale = static_cast<u8>(mii_info.appearance_bits6.nose_scale);
59 mii.nose_y = static_cast<u8>(mii_info.appearance_bits6.nose_y_position);
60
61 mii.mouth_type = static_cast<u8>(mii_info.appearance_bits7.mouth_type);
62 mii.mouth_color = static_cast<u8>(mii_info.appearance_bits7.mouth_color);
63 mii.mouth_scale = static_cast<u8>(mii_info.appearance_bits7.mouth_scale);
64 mii.mouth_aspect = static_cast<u8>(mii_info.appearance_bits7.mouth_horizontal_stretch);
65 mii.mouth_y = static_cast<u8>(mii_info.appearance_bits8.mouth_y_position);
66
67 mii.mustache_type = static_cast<u8>(mii_info.appearance_bits8.mustache_type);
68 mii.mustache_scale = static_cast<u8>(mii_info.appearance_bits9.mustache_scale);
69 mii.mustache_y = static_cast<u8>(mii_info.appearance_bits9.mustache_y_position);
70
71 mii.beard_type = static_cast<u8>(mii_info.appearance_bits9.bear_type);
72 mii.beard_color = static_cast<u8>(mii_info.appearance_bits9.facial_hair_color);
73
74 mii.glasses_type = static_cast<u8>(mii_info.appearance_bits10.glasses_type);
75 mii.glasses_color = static_cast<u8>(mii_info.appearance_bits10.glasses_color);
76 mii.glasses_scale = static_cast<u8>(mii_info.appearance_bits10.glasses_scale);
77 mii.glasses_y = static_cast<u8>(mii_info.appearance_bits10.glasses_y_position);
78
79 mii.mole_type = static_cast<u8>(mii_info.appearance_bits11.mole_enabled);
80 mii.mole_scale = static_cast<u8>(mii_info.appearance_bits11.mole_scale);
81 mii.mole_x = static_cast<u8>(mii_info.appearance_bits11.mole_x_position);
82 mii.mole_y = static_cast<u8>(mii_info.appearance_bits11.mole_y_position);
83
84 // TODO: Validate mii data
85
86 return mii;
87}
88
89bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { 19bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
90 const auto& amiibo_data = ntag_file.user_memory; 20 const auto& amiibo_data = ntag_file.user_memory;
91 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); 21 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);
@@ -126,9 +56,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
126 if (amiibo_data.model_info.constant_value != 0x02) { 56 if (amiibo_data.model_info.constant_value != 0x02) {
127 return false; 57 return false;
128 } 58 }
129 if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001) { 59 // dynamic_lock value apparently is not constant
130 return false; 60 // ntag_file.dynamic_lock == 0x0F0001
131 }
132 if (ntag_file.CFG0 != 0x04000000U) { 61 if (ntag_file.CFG0 != 0x04000000U) {
133 return false; 62 return false;
134 } 63 }
@@ -348,16 +277,16 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
348 Common::FS::FileType::BinaryFile}; 277 Common::FS::FileType::BinaryFile};
349 278
350 if (!keys_file.IsOpen()) { 279 if (!keys_file.IsOpen()) {
351 LOG_ERROR(Core, "No keys detected"); 280 LOG_ERROR(Service_NFP, "No keys detected");
352 return false; 281 return false;
353 } 282 }
354 283
355 if (keys_file.Read(unfixed_info) != 1) { 284 if (keys_file.Read(unfixed_info) != 1) {
356 LOG_ERROR(Core, "Failed to read unfixed_info"); 285 LOG_ERROR(Service_NFP, "Failed to read unfixed_info");
357 return false; 286 return false;
358 } 287 }
359 if (keys_file.Read(locked_secret) != 1) { 288 if (keys_file.Read(locked_secret) != 1) {
360 LOG_ERROR(Core, "Failed to read locked-secret"); 289 LOG_ERROR(Service_NFP, "Failed to read locked-secret");
361 return false; 290 return false;
362 } 291 }
363 292
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
index bfba5dcb2..9b021a5eb 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.h
+++ b/src/core/hle/service/nfp/amiibo_crypto.h
@@ -55,9 +55,6 @@ struct DerivedKeys {
55}; 55};
56static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); 56static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size");
57 57
58/// Converts mii data from nintendo 3ds format to nintendo switch format
59Service::Mii::MiiInfo AmiiboRegisterInfoToMii(const AmiiboRegisterInfo& register_info);
60
61/// Validates that the amiibo file is not corrupted 58/// Validates that the amiibo file is not corrupted
62bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file); 59bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file);
63 60
diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/amiibo_types.h
index 49875cff4..bd0424ffd 100644
--- a/src/core/hle/service/nfp/amiibo_types.h
+++ b/src/core/hle/service/nfp/amiibo_types.h
@@ -5,6 +5,8 @@
5 5
6#include <array> 6#include <array>
7 7
8#include "core/hle/service/mii/types.h"
9
8namespace Service::NFP { 10namespace Service::NFP {
9enum class ServiceType : u32 { 11enum class ServiceType : u32 {
10 User, 12 User,
@@ -74,13 +76,17 @@ using HashData = std::array<u8, 0x20>;
74using ApplicationArea = std::array<u8, 0xD8>; 76using ApplicationArea = std::array<u8, 0xD8>;
75 77
76struct AmiiboDate { 78struct AmiiboDate {
77 union { 79 u16_be raw_date{};
78 u16_be raw{}; 80
79 81 u16 GetYear() const {
80 BitField<0, 5, u16> day; 82 return ((raw_date & 0xFE00) >> 9) + 2000;
81 BitField<5, 4, u16> month; 83 }
82 BitField<9, 7, u16> year; 84 u8 GetMonth() const {
83 }; 85 return ((raw_date & 0x01E0) >> 5) - 1;
86 }
87 u8 GetDay() const {
88 return raw_date & 0x001F;
89 }
84}; 90};
85static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); 91static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size");
86 92
@@ -123,135 +129,20 @@ struct NTAG215Password {
123}; 129};
124static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); 130static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
125 131
126// Based on citra HLE::Applets::MiiData and PretendoNetwork.
127// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
128// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
129#pragma pack(1) 132#pragma pack(1)
130struct AmiiboRegisterInfo {
131 u32_be mii_id;
132 u64_be system_id;
133 u32_be specialness_and_creation_date;
134 std::array<u8, 0x6> creator_mac;
135 u16_be padding;
136 union {
137 u16 raw;
138
139 BitField<0, 1, u16> gender;
140 BitField<1, 4, u16> birth_month;
141 BitField<5, 5, u16> birth_day;
142 BitField<10, 4, u16> favorite_color;
143 BitField<14, 1, u16> favorite;
144 } mii_information;
145 std::array<char16_t, 0xA> mii_name;
146 u8 height;
147 u8 build;
148 union {
149 u8 raw;
150
151 BitField<0, 1, u8> disable_sharing;
152 BitField<1, 4, u8> face_shape;
153 BitField<5, 3, u8> skin_color;
154 } appearance_bits1;
155 union {
156 u8 raw;
157
158 BitField<0, 4, u8> wrinkles;
159 BitField<4, 4, u8> makeup;
160 } appearance_bits2;
161 u8 hair_style;
162 union {
163 u8 raw;
164
165 BitField<0, 3, u8> hair_color;
166 BitField<3, 1, u8> flip_hair;
167 } appearance_bits3;
168 union {
169 u32 raw;
170
171 BitField<0, 6, u32> eye_type;
172 BitField<6, 3, u32> eye_color;
173 BitField<9, 4, u32> eye_scale;
174 BitField<13, 3, u32> eye_vertical_stretch;
175 BitField<16, 5, u32> eye_rotation;
176 BitField<21, 4, u32> eye_spacing;
177 BitField<25, 5, u32> eye_y_position;
178 } appearance_bits4;
179 union {
180 u32 raw;
181
182 BitField<0, 5, u32> eyebrow_style;
183 BitField<5, 3, u32> eyebrow_color;
184 BitField<8, 4, u32> eyebrow_scale;
185 BitField<12, 3, u32> eyebrow_yscale;
186 BitField<16, 4, u32> eyebrow_rotation;
187 BitField<21, 4, u32> eyebrow_spacing;
188 BitField<25, 5, u32> eyebrow_y_position;
189 } appearance_bits5;
190 union {
191 u16 raw;
192
193 BitField<0, 5, u16> nose_type;
194 BitField<5, 4, u16> nose_scale;
195 BitField<9, 5, u16> nose_y_position;
196 } appearance_bits6;
197 union {
198 u16 raw;
199
200 BitField<0, 6, u16> mouth_type;
201 BitField<6, 3, u16> mouth_color;
202 BitField<9, 4, u16> mouth_scale;
203 BitField<13, 3, u16> mouth_horizontal_stretch;
204 } appearance_bits7;
205 union {
206 u8 raw;
207
208 BitField<0, 5, u8> mouth_y_position;
209 BitField<5, 3, u8> mustache_type;
210 } appearance_bits8;
211 u8 allow_copying;
212 union {
213 u16 raw;
214
215 BitField<0, 3, u16> bear_type;
216 BitField<3, 3, u16> facial_hair_color;
217 BitField<6, 4, u16> mustache_scale;
218 BitField<10, 5, u16> mustache_y_position;
219 } appearance_bits9;
220 union {
221 u16 raw;
222
223 BitField<0, 4, u16> glasses_type;
224 BitField<4, 3, u16> glasses_color;
225 BitField<7, 4, u16> glasses_scale;
226 BitField<11, 5, u16> glasses_y_position;
227 } appearance_bits10;
228 union {
229 u16 raw;
230
231 BitField<0, 1, u16> mole_enabled;
232 BitField<1, 4, u16> mole_scale;
233 BitField<5, 5, u16> mole_x_position;
234 BitField<10, 5, u16> mole_y_position;
235 } appearance_bits11;
236
237 std::array<u16_le, 0xA> author_name;
238 INSERT_PADDING_BYTES(0x4);
239};
240static_assert(sizeof(AmiiboRegisterInfo) == 0x60, "AmiiboRegisterInfo is an invalid size");
241
242struct EncryptedAmiiboFile { 133struct EncryptedAmiiboFile {
243 u8 constant_value; // Must be A5 134 u8 constant_value; // Must be A5
244 u16 write_counter; // Number of times the amiibo has been written? 135 u16 write_counter; // Number of times the amiibo has been written?
245 INSERT_PADDING_BYTES(0x1); // Unknown 1 136 INSERT_PADDING_BYTES(0x1); // Unknown 1
246 AmiiboSettings settings; // Encrypted amiibo settings 137 AmiiboSettings settings; // Encrypted amiibo settings
247 HashData locked_hash; // Hash 138 HashData locked_hash; // Hash
248 AmiiboModelInfo model_info; // Encrypted amiibo model info 139 AmiiboModelInfo model_info; // Encrypted amiibo model info
249 HashData keygen_salt; // Salt 140 HashData keygen_salt; // Salt
250 HashData unfixed_hash; // Hash 141 HashData unfixed_hash; // Hash
251 AmiiboRegisterInfo owner_mii; // Encrypted Mii data 142 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
252 u64_be title_id; // Encrypted Game id 143 u64_be title_id; // Encrypted Game id
253 u16_be applicaton_write_counter; // Encrypted Counter 144 u16_be applicaton_write_counter; // Encrypted Counter
254 u32_be application_area_id; // Encrypted Game id 145 u32_be application_area_id; // Encrypted Game id
255 std::array<u8, 0x2> unknown; 146 std::array<u8, 0x2> unknown;
256 HashData hash; // Probably a SHA256-HMAC hash? 147 HashData hash; // Probably a SHA256-HMAC hash?
257 ApplicationArea application_area; // Encrypted Game data 148 ApplicationArea application_area; // Encrypted Game data
@@ -267,7 +158,7 @@ struct NTAG215File {
267 u16 write_counter; // Number of times the amiibo has been written? 158 u16 write_counter; // Number of times the amiibo has been written?
268 INSERT_PADDING_BYTES(0x1); // Unknown 1 159 INSERT_PADDING_BYTES(0x1); // Unknown 1
269 AmiiboSettings settings; 160 AmiiboSettings settings;
270 AmiiboRegisterInfo owner_mii; // Encrypted Mii data 161 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
271 u64_be title_id; 162 u64_be title_id;
272 u16_be applicaton_write_counter; // Encrypted Counter 163 u16_be applicaton_write_counter; // Encrypted Counter
273 u32_be application_area_id; 164 u32_be application_area_id;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 4dba05a6a..20fea87e6 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -24,6 +24,7 @@ constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
24constexpr Result WrongDeviceState(ErrorModule::NFP, 73); 24constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
25constexpr Result NfcDisabled(ErrorModule::NFP, 80); 25constexpr Result NfcDisabled(ErrorModule::NFP, 80);
26constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); 26constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
27constexpr Result TagRemoved(ErrorModule::NFP, 97);
27constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); 28constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
28constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); 29constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
29constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); 30constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
@@ -57,7 +58,7 @@ IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_)
57 {21, &IUser::GetNpadId, "GetNpadId"}, 58 {21, &IUser::GetNpadId, "GetNpadId"},
58 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, 59 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
59 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, 60 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
60 {24, nullptr, "RecreateApplicationArea"}, 61 {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
61 }; 62 };
62 RegisterHandlers(functions); 63 RegisterHandlers(functions);
63 64
@@ -597,6 +598,34 @@ void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
597 rb.PushCopyObjects(availability_change_event->GetReadableEvent()); 598 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
598} 599}
599 600
601void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) {
602 IPC::RequestParser rp{ctx};
603 const auto device_handle{rp.Pop<u64>()};
604 const auto access_id{rp.Pop<u32>()};
605 const auto data{ctx.ReadBuffer()};
606 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
607 device_handle, access_id, data.size());
608
609 if (state == State::NonInitialized) {
610 IPC::ResponseBuilder rb{ctx, 2};
611 rb.Push(ErrCodes::NfcDisabled);
612 return;
613 }
614
615 // TODO(german77): Loop through all interfaces
616 if (device_handle == nfp_interface.GetHandle()) {
617 const auto result = nfp_interface.RecreateApplicationArea(access_id, data);
618 IPC::ResponseBuilder rb{ctx, 2};
619 rb.Push(result);
620 return;
621 }
622
623 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
624
625 IPC::ResponseBuilder rb{ctx, 2};
626 rb.Push(ErrCodes::DeviceNotFound);
627}
628
600Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, 629Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
601 const char* name) 630 const char* name)
602 : ServiceFramework{system_, name}, module{std::move(module_)}, 631 : ServiceFramework{system_, name}, module{std::move(module_)},
@@ -621,14 +650,14 @@ bool Module::Interface::LoadAmiiboFile(const std::string& filename) {
621 Common::FS::FileType::BinaryFile}; 650 Common::FS::FileType::BinaryFile};
622 651
623 if (!amiibo_file.IsOpen()) { 652 if (!amiibo_file.IsOpen()) {
624 LOG_ERROR(Core, "Amiibo is already on use"); 653 LOG_ERROR(Service_NFP, "Amiibo is already on use");
625 return false; 654 return false;
626 } 655 }
627 656
628 // Workaround for files with missing password data 657 // Workaround for files with missing password data
629 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{}; 658 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{};
630 if (amiibo_file.Read(buffer) < tag_size_without_password) { 659 if (amiibo_file.Read(buffer) < tag_size_without_password) {
631 LOG_ERROR(Core, "Failed to read amiibo file"); 660 LOG_ERROR(Service_NFP, "Failed to read amiibo file");
632 return false; 661 return false;
633 } 662 }
634 memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); 663 memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File));
@@ -759,12 +788,12 @@ Result Module::Interface::Flush() {
759 bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id == 788 bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id ==
760 tag_data.model_info.character_id; 789 tag_data.model_info.character_id;
761 if (!is_uuid_equal || !is_character_equal) { 790 if (!is_uuid_equal || !is_character_equal) {
762 LOG_ERROR(Core, "Not the same amiibo"); 791 LOG_ERROR(Service_NFP, "Not the same amiibo");
763 return ErrCodes::WriteAmiiboFailed; 792 return ErrCodes::WriteAmiiboFailed;
764 } 793 }
765 794
766 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { 795 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
767 LOG_ERROR(Core, "Failed to encode data"); 796 LOG_ERROR(Service_NFP, "Failed to encode data");
768 return ErrCodes::WriteAmiiboFailed; 797 return ErrCodes::WriteAmiiboFailed;
769 } 798 }
770 799
@@ -830,13 +859,13 @@ Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
830 return ErrCodes::WrongDeviceState; 859 return ErrCodes::WrongDeviceState;
831 } 860 }
832 861
833 if (is_data_decoded) { 862 if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) {
834 const auto& settings = tag_data.settings; 863 const auto& settings = tag_data.settings;
835 // TODO: Validate this data 864 // TODO: Validate this data
836 common_info = { 865 common_info = {
837 .last_write_year = static_cast<u16>(settings.write_date.year.Value()), 866 .last_write_year = settings.write_date.GetYear(),
838 .last_write_month = static_cast<u8>(settings.write_date.month.Value()), 867 .last_write_month = settings.write_date.GetMonth(),
839 .last_write_day = static_cast<u8>(settings.write_date.day.Value()), 868 .last_write_day = settings.write_date.GetDay(),
840 .write_counter = settings.crc_counter, 869 .write_counter = settings.crc_counter,
841 .version = 1, 870 .version = 1,
842 .application_area_size = sizeof(ApplicationArea), 871 .application_area_size = sizeof(ApplicationArea),
@@ -877,10 +906,15 @@ Result Module::Interface::GetModelInfo(ModelInfo& model_info) const {
877Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { 906Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
878 if (device_state != DeviceState::TagMounted) { 907 if (device_state != DeviceState::TagMounted) {
879 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 908 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
909 if (device_state == DeviceState::TagRemoved) {
910 return ErrCodes::TagRemoved;
911 }
880 return ErrCodes::WrongDeviceState; 912 return ErrCodes::WrongDeviceState;
881 } 913 }
882 914
883 if (is_data_decoded) { 915 Service::Mii::MiiManager manager;
916
917 if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) {
884 const auto& settings = tag_data.settings; 918 const auto& settings = tag_data.settings;
885 919
886 // Amiibo name is u16 while the register info is u8. Figure out how to handle this properly 920 // Amiibo name is u16 while the register info is u8. Figure out how to handle this properly
@@ -891,10 +925,10 @@ Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
891 925
892 // TODO: Validate this data 926 // TODO: Validate this data
893 register_info = { 927 register_info = {
894 .mii_char_info = AmiiboCrypto::AmiiboRegisterInfoToMii(tag_data.owner_mii), 928 .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
895 .first_write_year = static_cast<u16>(settings.init_date.year.Value()), 929 .first_write_year = settings.init_date.GetYear(),
896 .first_write_month = static_cast<u8>(settings.init_date.month.Value()), 930 .first_write_month = settings.init_date.GetMonth(),
897 .first_write_day = static_cast<u8>(settings.init_date.day.Value()), 931 .first_write_day = settings.init_date.GetDay(),
898 .amiibo_name = amiibo_name, 932 .amiibo_name = amiibo_name,
899 .unknown = {}, 933 .unknown = {},
900 }; 934 };
@@ -903,7 +937,6 @@ Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
903 } 937 }
904 938
905 // Generate a generic answer 939 // Generate a generic answer
906 Service::Mii::MiiManager manager;
907 register_info = { 940 register_info = {
908 .mii_char_info = manager.BuildDefault(0), 941 .mii_char_info = manager.BuildDefault(0),
909 .first_write_year = 2022, 942 .first_write_year = 2022,
@@ -918,6 +951,9 @@ Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
918Result Module::Interface::OpenApplicationArea(u32 access_id) { 951Result Module::Interface::OpenApplicationArea(u32 access_id) {
919 if (device_state != DeviceState::TagMounted) { 952 if (device_state != DeviceState::TagMounted) {
920 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 953 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
954 if (device_state == DeviceState::TagRemoved) {
955 return ErrCodes::TagRemoved;
956 }
921 return ErrCodes::WrongDeviceState; 957 return ErrCodes::WrongDeviceState;
922 } 958 }
923 959
@@ -944,6 +980,9 @@ Result Module::Interface::OpenApplicationArea(u32 access_id) {
944Result Module::Interface::GetApplicationArea(ApplicationArea& data) const { 980Result Module::Interface::GetApplicationArea(ApplicationArea& data) const {
945 if (device_state != DeviceState::TagMounted) { 981 if (device_state != DeviceState::TagMounted) {
946 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 982 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
983 if (device_state == DeviceState::TagRemoved) {
984 return ErrCodes::TagRemoved;
985 }
947 return ErrCodes::WrongDeviceState; 986 return ErrCodes::WrongDeviceState;
948 } 987 }
949 988
@@ -960,6 +999,9 @@ Result Module::Interface::GetApplicationArea(ApplicationArea& data) const {
960Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) { 999Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
961 if (device_state != DeviceState::TagMounted) { 1000 if (device_state != DeviceState::TagMounted) {
962 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 1001 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
1002 if (device_state == DeviceState::TagRemoved) {
1003 return ErrCodes::TagRemoved;
1004 }
963 return ErrCodes::WrongDeviceState; 1005 return ErrCodes::WrongDeviceState;
964 } 1006 }
965 1007
@@ -980,6 +1022,9 @@ Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
980Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) { 1022Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
981 if (device_state != DeviceState::TagMounted) { 1023 if (device_state != DeviceState::TagMounted) {
982 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 1024 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
1025 if (device_state == DeviceState::TagRemoved) {
1026 return ErrCodes::TagRemoved;
1027 }
983 return ErrCodes::WrongDeviceState; 1028 return ErrCodes::WrongDeviceState;
984 } 1029 }
985 1030
@@ -999,6 +1044,26 @@ Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector
999 return ResultSuccess; 1044 return ResultSuccess;
1000} 1045}
1001 1046
1047Result Module::Interface::RecreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
1048 if (device_state != DeviceState::TagMounted) {
1049 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
1050 if (device_state == DeviceState::TagRemoved) {
1051 return ErrCodes::TagRemoved;
1052 }
1053 return ErrCodes::WrongDeviceState;
1054 }
1055
1056 if (data.size() != sizeof(ApplicationArea)) {
1057 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
1058 return ResultUnknown;
1059 }
1060
1061 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
1062 tag_data.application_area_id = access_id;
1063
1064 return ResultSuccess;
1065}
1066
1002u64 Module::Interface::GetHandle() const { 1067u64 Module::Interface::GetHandle() const {
1003 // Generate a handle based of the npad id 1068 // Generate a handle based of the npad id
1004 return static_cast<u64>(npad_id); 1069 return static_cast<u64>(npad_id);
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 3410dcfb0..6b979daba 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -55,7 +55,7 @@ struct ModelInfo {
55static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); 55static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
56 56
57struct RegisterInfo { 57struct RegisterInfo {
58 Service::Mii::MiiInfo mii_char_info; 58 Service::Mii::CharInfo mii_char_info;
59 u16 first_write_year; 59 u16 first_write_year;
60 u8 first_write_month; 60 u8 first_write_month;
61 u8 first_write_day; 61 u8 first_write_day;
@@ -96,6 +96,7 @@ public:
96 Result GetApplicationArea(ApplicationArea& data) const; 96 Result GetApplicationArea(ApplicationArea& data) const;
97 Result SetApplicationArea(const std::vector<u8>& data); 97 Result SetApplicationArea(const std::vector<u8>& data);
98 Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data); 98 Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
99 Result RecreateApplicationArea(u32 access_id, const std::vector<u8>& data);
99 100
100 u64 GetHandle() const; 101 u64 GetHandle() const;
101 DeviceState GetCurrentState() const; 102 DeviceState GetCurrentState() const;
@@ -152,6 +153,7 @@ private:
152 void GetNpadId(Kernel::HLERequestContext& ctx); 153 void GetNpadId(Kernel::HLERequestContext& ctx);
153 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); 154 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
154 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); 155 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
156 void RecreateApplicationArea(Kernel::HLERequestContext& ctx);
155 157
156 KernelHelpers::ServiceContext service_context; 158 KernelHelpers::ServiceContext service_context;
157 159