diff options
| author | 2022-08-30 00:33:47 -0500 | |
|---|---|---|
| committer | 2022-09-07 01:04:00 -0500 | |
| commit | 19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e (patch) | |
| tree | f96054552ebe182e75fd77eabdcdf205a0dffe46 | |
| parent | core: nfp: Implement amiibo encryption (diff) | |
| download | yuzu-19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e.tar.gz yuzu-19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e.tar.xz yuzu-19a4e12e6ea6c3d06ee227f3ef1a6cbf93850f6e.zip | |
core: nfp: Implement Convert and RecreateApplicationArea, accuracy fixes
| -rw-r--r-- | src/core/hle/service/am/applets/applet_mii_edit_types.h | 2 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii.cpp | 32 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii_manager.cpp | 88 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii_manager.h | 9 | ||||
| -rw-r--r-- | src/core/hle/service/mii/types.h | 138 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/amiibo_crypto.cpp | 81 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/amiibo_crypto.h | 3 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/amiibo_types.h | 161 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.cpp | 95 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.h | 4 |
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 | ||
| 34 | struct MiiEditCharInfo { | 34 | struct MiiEditCharInfo { |
| 35 | Service::Mii::MiiInfo mii_info{}; | 35 | Service::Mii::CharInfo mii_info{}; |
| 36 | }; | 36 | }; |
| 37 | static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size."); | 37 | static_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 | ||
| 45 | MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { | 45 | CharInfo 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 | ||
| 412 | ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info, | 412 | ResultVal<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 | ||
| 422 | MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { | 422 | CharInfo 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 | ||
| 426 | MiiInfo MiiManager::BuildDefault(std::size_t index) { | 426 | CharInfo 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 | ||
| 430 | CharInfo 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 | |||
| 430 | ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { | 506 | ResultVal<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 | ||
| 444 | Result MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) { | 520 | Result 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 | ||
| 28 | private: | 29 | private: |
| 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 | }; |
| 87 | DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); | 87 | DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); |
| 88 | 88 | ||
| 89 | struct MiiInfo { | 89 | // nn::mii::CharInfo |
| 90 | struct 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 | }; |
| 143 | static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size."); | 144 | static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); |
| 144 | static_assert(std::has_unique_object_representations_v<MiiInfo>, | 145 | static_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 | ||
| 149 | struct MiiInfoElement { | 150 | struct 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 | }; |
| 155 | static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); | 156 | static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size."); |
| @@ -243,6 +244,131 @@ static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrec | |||
| 243 | static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, | 244 | static_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 | ||
| 251 | struct 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 | }; | ||
| 370 | static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size"); | ||
| 371 | |||
| 246 | struct MiiStoreData { | 372 | struct 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 | ||
| 17 | namespace Service::NFP::AmiiboCrypto { | 17 | namespace Service::NFP::AmiiboCrypto { |
| 18 | 18 | ||
| 19 | Service::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 | |||
| 89 | bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { | 19 | bool 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 | }; |
| 56 | static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); | 56 | static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size"); |
| 57 | 57 | ||
| 58 | /// Converts mii data from nintendo 3ds format to nintendo switch format | ||
| 59 | Service::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 |
| 62 | bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file); | 59 | bool 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 | |||
| 8 | namespace Service::NFP { | 10 | namespace Service::NFP { |
| 9 | enum class ServiceType : u32 { | 11 | enum class ServiceType : u32 { |
| 10 | User, | 12 | User, |
| @@ -74,13 +76,17 @@ using HashData = std::array<u8, 0x20>; | |||
| 74 | using ApplicationArea = std::array<u8, 0xD8>; | 76 | using ApplicationArea = std::array<u8, 0xD8>; |
| 75 | 77 | ||
| 76 | struct AmiiboDate { | 78 | struct 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 | }; |
| 85 | static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); | 91 | static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); |
| 86 | 92 | ||
| @@ -123,135 +129,20 @@ struct NTAG215Password { | |||
| 123 | }; | 129 | }; |
| 124 | static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); | 130 | static_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) |
| 130 | struct 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 | }; | ||
| 240 | static_assert(sizeof(AmiiboRegisterInfo) == 0x60, "AmiiboRegisterInfo is an invalid size"); | ||
| 241 | |||
| 242 | struct EncryptedAmiiboFile { | 133 | struct 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); | |||
| 24 | constexpr Result WrongDeviceState(ErrorModule::NFP, 73); | 24 | constexpr Result WrongDeviceState(ErrorModule::NFP, 73); |
| 25 | constexpr Result NfcDisabled(ErrorModule::NFP, 80); | 25 | constexpr Result NfcDisabled(ErrorModule::NFP, 80); |
| 26 | constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); | 26 | constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); |
| 27 | constexpr Result TagRemoved(ErrorModule::NFP, 97); | ||
| 27 | constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | 28 | constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); |
| 28 | constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); | 29 | constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); |
| 29 | constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); | 30 | constexpr 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 | ||
| 601 | void 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 | |||
| 600 | Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, | 629 | Module::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 { | |||
| 877 | Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { | 906 | Result 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 { | |||
| 918 | Result Module::Interface::OpenApplicationArea(u32 access_id) { | 951 | Result 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) { | |||
| 944 | Result Module::Interface::GetApplicationArea(ApplicationArea& data) const { | 980 | Result 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 { | |||
| 960 | Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) { | 999 | Result 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) { | |||
| 980 | Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) { | 1022 | Result 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 | ||
| 1047 | Result 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 | |||
| 1002 | u64 Module::Interface::GetHandle() const { | 1067 | u64 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 { | |||
| 55 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | 55 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); |
| 56 | 56 | ||
| 57 | struct RegisterInfo { | 57 | struct 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 | ||