summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp80
-rw-r--r--src/core/hle/service/mii/mii_manager.h3
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp12
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h4
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1093
-rw-r--r--src/core/hle/service/nfp/nfp.h161
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp572
-rw-r--r--src/core/hle/service/nfp/nfp_device.h97
-rw-r--r--src/core/hle/service/nfp/nfp_result.h21
-rw-r--r--src/core/hle/service/nfp/nfp_types.h (renamed from src/core/hle/service/nfp/amiibo_types.h)79
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp634
-rw-r--r--src/core/hle/service/nfp/nfp_user.h44
13 files changed, 1542 insertions, 1263 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c17662323..7fd2d0276 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -523,9 +523,12 @@ add_library(core STATIC
523 hle/service/nfc/nfc.h 523 hle/service/nfc/nfc.h
524 hle/service/nfp/amiibo_crypto.cpp 524 hle/service/nfp/amiibo_crypto.cpp
525 hle/service/nfp/amiibo_crypto.h 525 hle/service/nfp/amiibo_crypto.h
526 hle/service/nfp/amiibo_types.h
527 hle/service/nfp/nfp.cpp 526 hle/service/nfp/nfp.cpp
528 hle/service/nfp/nfp.h 527 hle/service/nfp/nfp.h
528 hle/service/nfp/nfp_device.cpp
529 hle/service/nfp/nfp_device.h
530 hle/service/nfp/nfp_result.h
531 hle/service/nfp/nfp_types.h
529 hle/service/nfp/nfp_user.cpp 532 hle/service/nfp/nfp_user.cpp
530 hle/service/nfp/nfp_user.h 533 hle/service/nfp/nfp_user.h
531 hle/service/ngct/ngct.cpp 534 hle/service/ngct/ngct.cpp
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index c484a9c8d..3e92152ec 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -427,12 +427,12 @@ 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
430CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { 430CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {
431 Service::Mii::MiiManager manager; 431 Service::Mii::MiiManager manager;
432 auto mii = manager.BuildDefault(0); 432 auto mii = manager.BuildDefault(0);
433 433
434 // Check if mii data exist 434 // Check if mii data exist
435 if (mii_v3.mii_name[0] == 0) { 435 if (mii_v3.version == 0) {
436 return mii; 436 return mii;
437 } 437 }
438 438
@@ -443,8 +443,8 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
443 mii.height = mii_v3.height; 443 mii.height = mii_v3.height;
444 mii.build = mii_v3.build; 444 mii.build = mii_v3.build;
445 445
446 memset(mii.name.data(), 0, sizeof(mii.name)); 446 memset(mii.name.data(), 0, mii.name.size());
447 memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name)); 447 memcpy(mii.name.data(), mii_v3.mii_name.data(), mii_v3.mii_name.size());
448 mii.font_region = mii_v3.region_information.character_set; 448 mii.font_region = mii_v3.region_information.character_set;
449 449
450 mii.faceline_type = mii_v3.appearance_bits1.face_shape; 450 mii.faceline_type = mii_v3.appearance_bits1.face_shape;
@@ -504,6 +504,78 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
504 return mii; 504 return mii;
505} 505}
506 506
507Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
508 Service::Mii::MiiManager manager;
509 Ver3StoreData mii_v3{};
510
511 // TODO: We are ignoring a bunch of data from the mii_v3
512
513 mii_v3.version = 1;
514 mii_v3.mii_information.gender.Assign(mii.gender);
515 mii_v3.mii_information.favorite_color.Assign(mii.favorite_color);
516 mii_v3.height = mii.height;
517 mii_v3.build = mii.build;
518
519 memcpy(mii_v3.mii_name.data(), mii.name.data(), mii.name.size());
520 mii_v3.region_information.character_set.Assign(mii.font_region);
521
522 mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type);
523 mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color);
524 mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle);
525 mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make);
526
527 mii_v3.hair_style = mii.hair_type;
528 mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color);
529 mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip);
530
531 mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type);
532 mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color);
533 mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
534 mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
535 mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
536 mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x);
537 mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
538
539 mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type);
540 mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color);
541 mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale);
542 mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect);
543 mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
544 mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x);
545 mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y);
546
547 mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type);
548 mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale);
549 mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y);
550
551 mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type);
552 mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color);
553 mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
554 mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
555 mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
556
557 mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type);
558 mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale);
559 mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y);
560
561 mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type);
562 mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color);
563
564 mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type);
565 mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color);
566 mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale);
567 mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y);
568
569 mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type);
570 mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale);
571 mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
572 mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
573
574 // TODO: Validate mii_v3 data
575
576 return mii_v3;
577}
578
507ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { 579ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
508 std::vector<MiiInfoElement> result; 580 std::vector<MiiInfoElement> result;
509 581
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index d847de0bd..b68fdd54c 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -22,7 +22,8 @@ public:
22 ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag); 22 ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);
23 CharInfo BuildRandom(Age age, Gender gender, Race race); 23 CharInfo BuildRandom(Age age, Gender gender, Race race);
24 CharInfo BuildDefault(std::size_t index); 24 CharInfo BuildDefault(std::size_t index);
25 CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const; 25 CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
26 Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
26 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); 27 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
27 Result GetIndex(const CharInfo& info, u32& index); 28 Result GetIndex(const CharInfo& info, u32& index);
28 29
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
index 31dd3a307..c87da5ae4 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -20,13 +20,13 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
20 const auto& amiibo_data = ntag_file.user_memory; 20 const auto& amiibo_data = ntag_file.user_memory;
21 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);
22 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); 22 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container);
23 LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter); 23 LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter));
24 24
25 LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); 25 LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
26 LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); 26 LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
27 LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); 27 LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
28 LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); 28 LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number);
29 LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series); 29 LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
30 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value); 30 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value);
31 31
32 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); 32 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock);
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
index af7335912..e3fa3d07e 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.h
+++ b/src/core/hle/service/nfp/amiibo_crypto.h
@@ -5,7 +5,7 @@
5 5
6#include <array> 6#include <array>
7 7
8#include "core/hle/service/nfp/amiibo_types.h" 8#include "core/hle/service/nfp/nfp_types.h"
9 9
10struct mbedtls_md_context_t; 10struct mbedtls_md_context_t;
11 11
@@ -22,7 +22,7 @@ using HmacKey = std::array<u8, 0x10>;
22using DrgbOutput = std::array<u8, 0x20>; 22using DrgbOutput = std::array<u8, 0x20>;
23 23
24struct HashSeed { 24struct HashSeed {
25 u16 magic; 25 u16_be magic;
26 std::array<u8, 0xE> padding; 26 std::array<u8, 0xE> padding;
27 std::array<u8, 0x8> uuid1; 27 std::array<u8, 0x8> uuid1;
28 std::array<u8, 0x8> uuid2; 28 std::array<u8, 0x8> uuid2;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 037b86653..0cb55ca49 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -1,1098 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include <atomic>
6
7#include "common/fs/file.h"
8#include "common/fs/path_util.h"
9#include "common/logging/log.h" 4#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/core.h"
12#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h"
15#include "core/hle/ipc_helpers.h" 5#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/k_event.h"
17#include "core/hle/service/mii/mii_manager.h"
18#include "core/hle/service/nfp/amiibo_crypto.h"
19#include "core/hle/service/nfp/nfp.h" 6#include "core/hle/service/nfp/nfp.h"
20#include "core/hle/service/nfp/nfp_user.h" 7#include "core/hle/service/nfp/nfp_user.h"
21 8
22namespace Service::NFP { 9namespace Service::NFP {
23namespace ErrCodes {
24constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
25constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
26constexpr Result NfcDisabled(ErrorModule::NFP, 80);
27constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
28constexpr Result TagRemoved(ErrorModule::NFP, 97);
29constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
30constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
31constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
32} // namespace ErrCodes
33
34IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_)
35 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name},
36 nfp_interface{nfp_interface_} {
37 static const FunctionInfo functions[] = {
38 {0, &IUser::Initialize, "Initialize"},
39 {1, &IUser::Finalize, "Finalize"},
40 {2, &IUser::ListDevices, "ListDevices"},
41 {3, &IUser::StartDetection, "StartDetection"},
42 {4, &IUser::StopDetection, "StopDetection"},
43 {5, &IUser::Mount, "Mount"},
44 {6, &IUser::Unmount, "Unmount"},
45 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
46 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
47 {9, &IUser::SetApplicationArea, "SetApplicationArea"},
48 {10, &IUser::Flush, "Flush"},
49 {11, nullptr, "Restore"},
50 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
51 {13, &IUser::GetTagInfo, "GetTagInfo"},
52 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
53 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
54 {16, &IUser::GetModelInfo, "GetModelInfo"},
55 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
56 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
57 {19, &IUser::GetState, "GetState"},
58 {20, &IUser::GetDeviceState, "GetDeviceState"},
59 {21, &IUser::GetNpadId, "GetNpadId"},
60 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
61 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
62 {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
63 };
64 RegisterHandlers(functions);
65
66 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
67}
68
69void IUser::Initialize(Kernel::HLERequestContext& ctx) {
70 LOG_INFO(Service_NFC, "called");
71
72 state = State::Initialized;
73
74 // TODO(german77): Loop through all interfaces
75 nfp_interface.Initialize();
76
77 IPC::ResponseBuilder rb{ctx, 2, 0};
78 rb.Push(ResultSuccess);
79}
80
81void IUser::Finalize(Kernel::HLERequestContext& ctx) {
82 LOG_INFO(Service_NFP, "called");
83
84 state = State::NonInitialized;
85
86 // TODO(german77): Loop through all interfaces
87 nfp_interface.Finalize();
88
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(ResultSuccess);
91}
92
93void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
94 LOG_INFO(Service_NFP, "called");
95
96 if (state == State::NonInitialized) {
97 IPC::ResponseBuilder rb{ctx, 2};
98 rb.Push(ErrCodes::NfcDisabled);
99 return;
100 }
101
102 std::vector<u64> devices;
103
104 // TODO(german77): Loop through all interfaces
105 devices.push_back(nfp_interface.GetHandle());
106
107 if (devices.size() == 0) {
108 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ErrCodes::DeviceNotFound);
110 return;
111 }
112
113 ctx.WriteBuffer(devices);
114
115 IPC::ResponseBuilder rb{ctx, 3};
116 rb.Push(ResultSuccess);
117 rb.Push(static_cast<s32>(devices.size()));
118}
119
120void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
121 IPC::RequestParser rp{ctx};
122 const auto device_handle{rp.Pop<u64>()};
123 const auto nfp_protocol{rp.Pop<s32>()};
124 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
125
126 if (state == State::NonInitialized) {
127 IPC::ResponseBuilder rb{ctx, 2};
128 rb.Push(ErrCodes::NfcDisabled);
129 return;
130 }
131
132 // TODO(german77): Loop through all interfaces
133 if (device_handle == nfp_interface.GetHandle()) {
134 const auto result = nfp_interface.StartDetection(nfp_protocol);
135 IPC::ResponseBuilder rb{ctx, 2};
136 rb.Push(result);
137 return;
138 }
139
140 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
141
142 IPC::ResponseBuilder rb{ctx, 2};
143 rb.Push(ErrCodes::DeviceNotFound);
144}
145
146void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
147 IPC::RequestParser rp{ctx};
148 const auto device_handle{rp.Pop<u64>()};
149 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
150
151 if (state == State::NonInitialized) {
152 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(ErrCodes::NfcDisabled);
154 return;
155 }
156
157 // TODO(german77): Loop through all interfaces
158 if (device_handle == nfp_interface.GetHandle()) {
159 const auto result = nfp_interface.StopDetection();
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(result);
162 return;
163 }
164
165 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
166
167 IPC::ResponseBuilder rb{ctx, 2};
168 rb.Push(ErrCodes::DeviceNotFound);
169}
170
171void IUser::Mount(Kernel::HLERequestContext& ctx) {
172 IPC::RequestParser rp{ctx};
173 const auto device_handle{rp.Pop<u64>()};
174 const auto model_type{rp.PopEnum<ModelType>()};
175 const auto mount_target{rp.PopEnum<MountTarget>()};
176 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
177 model_type, mount_target);
178
179 if (state == State::NonInitialized) {
180 IPC::ResponseBuilder rb{ctx, 2};
181 rb.Push(ErrCodes::NfcDisabled);
182 return;
183 }
184
185 // TODO(german77): Loop through all interfaces
186 if (device_handle == nfp_interface.GetHandle()) {
187 const auto result = nfp_interface.Mount();
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(result);
190 return;
191 }
192
193 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
194
195 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(ErrCodes::DeviceNotFound);
197}
198
199void IUser::Unmount(Kernel::HLERequestContext& ctx) {
200 IPC::RequestParser rp{ctx};
201 const auto device_handle{rp.Pop<u64>()};
202 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
203
204 if (state == State::NonInitialized) {
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(ErrCodes::NfcDisabled);
207 return;
208 }
209
210 // TODO(german77): Loop through all interfaces
211 if (device_handle == nfp_interface.GetHandle()) {
212 const auto result = nfp_interface.Unmount();
213 IPC::ResponseBuilder rb{ctx, 2};
214 rb.Push(result);
215 return;
216 }
217
218 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
219
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(ErrCodes::DeviceNotFound);
222}
223
224void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
225 IPC::RequestParser rp{ctx};
226 const auto device_handle{rp.Pop<u64>()};
227 const auto access_id{rp.Pop<u32>()};
228 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle,
229 access_id);
230
231 if (state == State::NonInitialized) {
232 IPC::ResponseBuilder rb{ctx, 2};
233 rb.Push(ErrCodes::NfcDisabled);
234 return;
235 }
236
237 // TODO(german77): Loop through all interfaces
238 if (device_handle == nfp_interface.GetHandle()) {
239 const auto result = nfp_interface.OpenApplicationArea(access_id);
240 IPC::ResponseBuilder rb{ctx, 2};
241 rb.Push(result);
242 return;
243 }
244
245 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
246
247 IPC::ResponseBuilder rb{ctx, 2};
248 rb.Push(ErrCodes::DeviceNotFound);
249}
250
251void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
252 IPC::RequestParser rp{ctx};
253 const auto device_handle{rp.Pop<u64>()};
254 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
255
256 if (state == State::NonInitialized) {
257 IPC::ResponseBuilder rb{ctx, 2};
258 rb.Push(ErrCodes::NfcDisabled);
259 return;
260 }
261
262 // TODO(german77): Loop through all interfaces
263 if (device_handle == nfp_interface.GetHandle()) {
264 ApplicationArea data{};
265 const auto result = nfp_interface.GetApplicationArea(data);
266 ctx.WriteBuffer(data);
267 IPC::ResponseBuilder rb{ctx, 3};
268 rb.Push(result);
269 rb.Push(static_cast<u32>(data.size()));
270 return;
271 }
272
273 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
274
275 IPC::ResponseBuilder rb{ctx, 2};
276 rb.Push(ErrCodes::DeviceNotFound);
277}
278
279void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
280 IPC::RequestParser rp{ctx};
281 const auto device_handle{rp.Pop<u64>()};
282 const auto data{ctx.ReadBuffer()};
283 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle,
284 data.size());
285
286 if (state == State::NonInitialized) {
287 IPC::ResponseBuilder rb{ctx, 2};
288 rb.Push(ErrCodes::NfcDisabled);
289 return;
290 }
291
292 // TODO(german77): Loop through all interfaces
293 if (device_handle == nfp_interface.GetHandle()) {
294 const auto result = nfp_interface.SetApplicationArea(data);
295 IPC::ResponseBuilder rb{ctx, 2};
296 rb.Push(result);
297 return;
298 }
299
300 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
301
302 IPC::ResponseBuilder rb{ctx, 2};
303 rb.Push(ErrCodes::DeviceNotFound);
304}
305
306void IUser::Flush(Kernel::HLERequestContext& ctx) {
307 IPC::RequestParser rp{ctx};
308 const auto device_handle{rp.Pop<u64>()};
309 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
310
311 if (state == State::NonInitialized) {
312 IPC::ResponseBuilder rb{ctx, 2};
313 rb.Push(ErrCodes::NfcDisabled);
314 return;
315 }
316
317 // TODO(german77): Loop through all interfaces
318 if (device_handle == nfp_interface.GetHandle()) {
319 const auto result = nfp_interface.Flush();
320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(result);
322 return;
323 }
324
325 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
326
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ErrCodes::DeviceNotFound);
329}
330
331void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
332 IPC::RequestParser rp{ctx};
333 const auto device_handle{rp.Pop<u64>()};
334 const auto access_id{rp.Pop<u32>()};
335 const auto data{ctx.ReadBuffer()};
336 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
337 device_handle, access_id, data.size());
338
339 if (state == State::NonInitialized) {
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(ErrCodes::NfcDisabled);
342 return;
343 }
344
345 // TODO(german77): Loop through all interfaces
346 if (device_handle == nfp_interface.GetHandle()) {
347 const auto result = nfp_interface.CreateApplicationArea(access_id, data);
348 IPC::ResponseBuilder rb{ctx, 2};
349 rb.Push(result);
350 return;
351 }
352
353 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
354
355 IPC::ResponseBuilder rb{ctx, 2};
356 rb.Push(ErrCodes::DeviceNotFound);
357}
358
359void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx};
361 const auto device_handle{rp.Pop<u64>()};
362 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
363
364 if (state == State::NonInitialized) {
365 IPC::ResponseBuilder rb{ctx, 2};
366 rb.Push(ErrCodes::NfcDisabled);
367 return;
368 }
369
370 // TODO(german77): Loop through all interfaces
371 if (device_handle == nfp_interface.GetHandle()) {
372 TagInfo tag_info{};
373 const auto result = nfp_interface.GetTagInfo(tag_info);
374 ctx.WriteBuffer(tag_info);
375 IPC::ResponseBuilder rb{ctx, 2};
376 rb.Push(result);
377 return;
378 }
379
380 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
381
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(ErrCodes::DeviceNotFound);
384}
385
386void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
387 IPC::RequestParser rp{ctx};
388 const auto device_handle{rp.Pop<u64>()};
389 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
390
391 if (state == State::NonInitialized) {
392 IPC::ResponseBuilder rb{ctx, 2};
393 rb.Push(ErrCodes::NfcDisabled);
394 return;
395 }
396
397 // TODO(german77): Loop through all interfaces
398 if (device_handle == nfp_interface.GetHandle()) {
399 RegisterInfo register_info{};
400 const auto result = nfp_interface.GetRegisterInfo(register_info);
401 ctx.WriteBuffer(register_info);
402 IPC::ResponseBuilder rb{ctx, 2};
403 rb.Push(result);
404 return;
405 }
406
407 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
408
409 IPC::ResponseBuilder rb{ctx, 2};
410 rb.Push(ErrCodes::DeviceNotFound);
411}
412
413void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
414 IPC::RequestParser rp{ctx};
415 const auto device_handle{rp.Pop<u64>()};
416 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
417
418 if (state == State::NonInitialized) {
419 IPC::ResponseBuilder rb{ctx, 2};
420 rb.Push(ErrCodes::NfcDisabled);
421 return;
422 }
423
424 // TODO(german77): Loop through all interfaces
425 if (device_handle == nfp_interface.GetHandle()) {
426 CommonInfo common_info{};
427 const auto result = nfp_interface.GetCommonInfo(common_info);
428 ctx.WriteBuffer(common_info);
429 IPC::ResponseBuilder rb{ctx, 2};
430 rb.Push(result);
431 return;
432 }
433
434 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
435
436 IPC::ResponseBuilder rb{ctx, 2};
437 rb.Push(ErrCodes::DeviceNotFound);
438}
439
440void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
441 IPC::RequestParser rp{ctx};
442 const auto device_handle{rp.Pop<u64>()};
443 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
444
445 if (state == State::NonInitialized) {
446 IPC::ResponseBuilder rb{ctx, 2};
447 rb.Push(ErrCodes::NfcDisabled);
448 return;
449 }
450
451 // TODO(german77): Loop through all interfaces
452 if (device_handle == nfp_interface.GetHandle()) {
453 ModelInfo model_info{};
454 const auto result = nfp_interface.GetModelInfo(model_info);
455 ctx.WriteBuffer(model_info);
456 IPC::ResponseBuilder rb{ctx, 2};
457 rb.Push(result);
458 return;
459 }
460
461 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
462
463 IPC::ResponseBuilder rb{ctx, 2};
464 rb.Push(ErrCodes::DeviceNotFound);
465}
466
467void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
468 IPC::RequestParser rp{ctx};
469 const auto device_handle{rp.Pop<u64>()};
470 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
471
472 if (state == State::NonInitialized) {
473 IPC::ResponseBuilder rb{ctx, 2};
474 rb.Push(ErrCodes::NfcDisabled);
475 return;
476 }
477
478 // TODO(german77): Loop through all interfaces
479 if (device_handle == nfp_interface.GetHandle()) {
480 IPC::ResponseBuilder rb{ctx, 2, 1};
481 rb.Push(ResultSuccess);
482 rb.PushCopyObjects(nfp_interface.GetActivateEvent());
483 return;
484 }
485
486 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
487
488 IPC::ResponseBuilder rb{ctx, 2};
489 rb.Push(ErrCodes::DeviceNotFound);
490}
491
492void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
493 IPC::RequestParser rp{ctx};
494 const auto device_handle{rp.Pop<u64>()};
495 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
496
497 if (state == State::NonInitialized) {
498 IPC::ResponseBuilder rb{ctx, 2};
499 rb.Push(ErrCodes::NfcDisabled);
500 return;
501 }
502
503 // TODO(german77): Loop through all interfaces
504 if (device_handle == nfp_interface.GetHandle()) {
505 IPC::ResponseBuilder rb{ctx, 2, 1};
506 rb.Push(ResultSuccess);
507 rb.PushCopyObjects(nfp_interface.GetDeactivateEvent());
508 return;
509 }
510
511 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
512
513 IPC::ResponseBuilder rb{ctx, 2};
514 rb.Push(ErrCodes::DeviceNotFound);
515}
516
517void IUser::GetState(Kernel::HLERequestContext& ctx) {
518 LOG_DEBUG(Service_NFC, "called");
519
520 IPC::ResponseBuilder rb{ctx, 3, 0};
521 rb.Push(ResultSuccess);
522 rb.PushEnum(state);
523}
524
525void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
526 IPC::RequestParser rp{ctx};
527 const auto device_handle{rp.Pop<u64>()};
528 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
529
530 // TODO(german77): Loop through all interfaces
531 if (device_handle == nfp_interface.GetHandle()) {
532 IPC::ResponseBuilder rb{ctx, 3};
533 rb.Push(ResultSuccess);
534 rb.PushEnum(nfp_interface.GetCurrentState());
535 return;
536 }
537
538 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
539
540 IPC::ResponseBuilder rb{ctx, 2};
541 rb.Push(ErrCodes::DeviceNotFound);
542}
543
544void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
545 IPC::RequestParser rp{ctx};
546 const auto device_handle{rp.Pop<u64>()};
547 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
548
549 if (state == State::NonInitialized) {
550 IPC::ResponseBuilder rb{ctx, 2};
551 rb.Push(ErrCodes::NfcDisabled);
552 return;
553 }
554
555 // TODO(german77): Loop through all interfaces
556 if (device_handle == nfp_interface.GetHandle()) {
557 IPC::ResponseBuilder rb{ctx, 3};
558 rb.Push(ResultSuccess);
559 rb.PushEnum(nfp_interface.GetNpadId());
560 return;
561 }
562
563 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
564
565 IPC::ResponseBuilder rb{ctx, 2};
566 rb.Push(ErrCodes::DeviceNotFound);
567}
568
569void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
570 IPC::RequestParser rp{ctx};
571 const auto device_handle{rp.Pop<u64>()};
572 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
573
574 // TODO(german77): Loop through all interfaces
575 if (device_handle == nfp_interface.GetHandle()) {
576 IPC::ResponseBuilder rb{ctx, 3};
577 rb.Push(ResultSuccess);
578 rb.Push(sizeof(ApplicationArea));
579 return;
580 }
581
582 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
583
584 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(ErrCodes::DeviceNotFound);
586}
587
588void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
589 LOG_DEBUG(Service_NFP, "(STUBBED) called");
590
591 if (state == State::NonInitialized) {
592 IPC::ResponseBuilder rb{ctx, 2};
593 rb.Push(ErrCodes::NfcDisabled);
594 return;
595 }
596
597 IPC::ResponseBuilder rb{ctx, 2, 1};
598 rb.Push(ResultSuccess);
599 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
600}
601
602void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) {
603 IPC::RequestParser rp{ctx};
604 const auto device_handle{rp.Pop<u64>()};
605 const auto access_id{rp.Pop<u32>()};
606 const auto data{ctx.ReadBuffer()};
607 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
608 device_handle, access_id, data.size());
609
610 if (state == State::NonInitialized) {
611 IPC::ResponseBuilder rb{ctx, 2};
612 rb.Push(ErrCodes::NfcDisabled);
613 return;
614 }
615
616 // TODO(german77): Loop through all interfaces
617 if (device_handle == nfp_interface.GetHandle()) {
618 const auto result = nfp_interface.RecreateApplicationArea(access_id, data);
619 IPC::ResponseBuilder rb{ctx, 2};
620 rb.Push(result);
621 return;
622 }
623
624 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
625
626 IPC::ResponseBuilder rb{ctx, 2};
627 rb.Push(ErrCodes::DeviceNotFound);
628}
629
630Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
631 const char* name)
632 : ServiceFramework{system_, name}, module{std::move(module_)},
633 npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} {
634 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
635 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
636}
637
638Module::Interface::~Interface() = default;
639
640void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
641 LOG_DEBUG(Service_NFP, "called");
642
643 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
644 rb.Push(ResultSuccess);
645 rb.PushIpcInterface<IUser>(*this, system);
646}
647
648bool Module::Interface::LoadAmiiboFile(const std::string& filename) {
649 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
650 const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
651 Common::FS::FileType::BinaryFile};
652
653 if (!amiibo_file.IsOpen()) {
654 LOG_ERROR(Service_NFP, "Amiibo is already on use");
655 return false;
656 }
657
658 // Workaround for files with missing password data
659 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{};
660 if (amiibo_file.Read(buffer) < tag_size_without_password) {
661 LOG_ERROR(Service_NFP, "Failed to read amiibo file");
662 return false;
663 }
664 memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File));
665
666 if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
667 LOG_INFO(Service_NFP, "Invalid amiibo");
668 return false;
669 }
670
671 file_path = filename;
672 return true;
673}
674
675bool Module::Interface::LoadAmiibo(const std::string& filename) {
676 if (device_state != DeviceState::SearchingForTag) {
677 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
678 return false;
679 }
680
681 if (!LoadAmiiboFile(filename)) {
682 return false;
683 }
684
685 device_state = DeviceState::TagFound;
686 activate_event->GetWritableEvent().Signal();
687 return true;
688}
689
690void Module::Interface::CloseAmiibo() {
691 LOG_INFO(Service_NFP, "Remove amiibo");
692 device_state = DeviceState::TagRemoved;
693 is_data_decoded = false;
694 is_application_area_initialized = false;
695 encrypted_tag_data = {};
696 tag_data = {};
697 deactivate_event->GetWritableEvent().Signal();
698}
699
700Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const {
701 return activate_event->GetReadableEvent();
702}
703
704Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const {
705 return deactivate_event->GetReadableEvent();
706}
707
708void Module::Interface::Initialize() {
709 device_state = DeviceState::Initialized;
710 is_data_decoded = false;
711 is_application_area_initialized = false;
712 encrypted_tag_data = {};
713 tag_data = {};
714}
715
716void Module::Interface::Finalize() {
717 if (device_state == DeviceState::TagMounted) {
718 Unmount();
719 }
720 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
721 StopDetection();
722 }
723 device_state = DeviceState::Unaviable;
724}
725
726Result Module::Interface::StartDetection(s32 protocol_) {
727 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
728
729 // TODO(german77): Add callback for when nfc data is available
730
731 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
732 npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
733 device_state = DeviceState::SearchingForTag;
734 protocol = protocol_;
735 return ResultSuccess;
736 }
737
738 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
739 return ErrCodes::WrongDeviceState;
740}
741
742Result Module::Interface::StopDetection() {
743 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
744 npad_device->SetPollingMode(Common::Input::PollingMode::Active);
745
746 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
747 CloseAmiibo();
748 return ResultSuccess;
749 }
750 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
751 device_state = DeviceState::Initialized;
752 return ResultSuccess;
753 }
754
755 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
756 return ErrCodes::WrongDeviceState;
757}
758
759Result Module::Interface::Flush() {
760 // Ignore write command if we can't encrypt the data
761 if (!is_data_decoded) {
762 return ResultSuccess;
763 }
764
765 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
766 EncryptedNTAG215File tmp_encrypted_tag_data{};
767 const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
768 Common::FS::FileType::BinaryFile};
769
770 if (!amiibo_file.IsOpen()) {
771 LOG_ERROR(Core, "Amiibo is already on use");
772 return ErrCodes::WriteAmiiboFailed;
773 }
774
775 // Workaround for files with missing password data
776 std::array<u8, sizeof(EncryptedNTAG215File)> buffer{};
777 if (amiibo_file.Read(buffer) < tag_size_without_password) {
778 LOG_ERROR(Core, "Failed to read amiibo file");
779 return ErrCodes::WriteAmiiboFailed;
780 }
781 memcpy(&tmp_encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File));
782
783 if (!AmiiboCrypto::IsAmiiboValid(tmp_encrypted_tag_data)) {
784 LOG_INFO(Service_NFP, "Invalid amiibo");
785 return ErrCodes::WriteAmiiboFailed;
786 }
787
788 bool is_uuid_equal = memcmp(tmp_encrypted_tag_data.uuid.data(), tag_data.uuid.data(), 8) == 0;
789 bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id ==
790 tag_data.model_info.character_id;
791 if (!is_uuid_equal || !is_character_equal) {
792 LOG_ERROR(Service_NFP, "Not the same amiibo");
793 return ErrCodes::WriteAmiiboFailed;
794 }
795
796 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
797 LOG_ERROR(Service_NFP, "Failed to encode data");
798 return ErrCodes::WriteAmiiboFailed;
799 }
800
801 // Return to the start of the file
802 if (!amiibo_file.Seek(0)) {
803 LOG_ERROR(Service_NFP, "Error writing to file");
804 return ErrCodes::WriteAmiiboFailed;
805 }
806
807 if (!amiibo_file.Write(encrypted_tag_data)) {
808 LOG_ERROR(Service_NFP, "Error writing to file");
809 return ErrCodes::WriteAmiiboFailed;
810 }
811
812 return ResultSuccess;
813}
814
815Result Module::Interface::Mount() {
816 if (device_state != DeviceState::TagFound) {
817 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
818 return ErrCodes::WrongDeviceState;
819 }
820 10
821 is_data_decoded = AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data); 11class IUserManager final : public ServiceFramework<IUserManager> {
822 LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded); 12public:
823 13 explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} {
824 is_application_area_initialized = false; 14 // clang-format off
825 device_state = DeviceState::TagMounted; 15 static const FunctionInfo functions[] = {
826 return ResultSuccess; 16 {0, &IUserManager::CreateUserInterface, "CreateUserInterface"},
827}
828
829Result Module::Interface::Unmount() {
830 if (device_state != DeviceState::TagMounted) {
831 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
832 return ErrCodes::WrongDeviceState;
833 }
834
835 is_data_decoded = false;
836 is_application_area_initialized = false;
837 device_state = DeviceState::TagFound;
838 return ResultSuccess;
839}
840
841Result Module::Interface::GetTagInfo(TagInfo& tag_info) const {
842 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
843 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
844 return ErrCodes::WrongDeviceState;
845 }
846
847 tag_info = {
848 .uuid = encrypted_tag_data.uuid,
849 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()),
850 .protocol = protocol,
851 .tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type),
852 };
853
854 return ResultSuccess;
855}
856
857Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
858 if (device_state != DeviceState::TagMounted) {
859 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
860 return ErrCodes::WrongDeviceState;
861 }
862
863 if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) {
864 const auto& settings = tag_data.settings;
865 // TODO: Validate this data
866 common_info = {
867 .last_write_year = settings.write_date.GetYear(),
868 .last_write_month = settings.write_date.GetMonth(),
869 .last_write_day = settings.write_date.GetDay(),
870 .write_counter = settings.crc_counter,
871 .version = 1,
872 .application_area_size = sizeof(ApplicationArea),
873 };
874 return ResultSuccess;
875 }
876
877 // Generate a generic answer
878 common_info = {
879 .last_write_year = 2022,
880 .last_write_month = 2,
881 .last_write_day = 7,
882 .write_counter = 0,
883 .version = 1,
884 .application_area_size = sizeof(ApplicationArea),
885 };
886 return ResultSuccess;
887}
888
889Result Module::Interface::GetModelInfo(ModelInfo& model_info) const {
890 if (device_state != DeviceState::TagMounted) {
891 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
892 return ErrCodes::WrongDeviceState;
893 }
894
895 const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
896 model_info = {
897 .character_id = model_info_data.character_id,
898 .character_variant = model_info_data.character_variant,
899 .amiibo_type = model_info_data.amiibo_type,
900 .model_number = model_info_data.model_number,
901 .series = model_info_data.series,
902 .constant_value = model_info_data.constant_value,
903 };
904 return ResultSuccess;
905}
906
907Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
908 if (device_state != DeviceState::TagMounted) {
909 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
910 if (device_state == DeviceState::TagRemoved) {
911 return ErrCodes::TagRemoved;
912 }
913 return ErrCodes::WrongDeviceState;
914 }
915
916 Service::Mii::MiiManager manager;
917
918 if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) {
919 const auto& settings = tag_data.settings;
920
921 // TODO: Validate this data
922 register_info = {
923 .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
924 .first_write_year = settings.init_date.GetYear(),
925 .first_write_month = settings.init_date.GetMonth(),
926 .first_write_day = settings.init_date.GetDay(),
927 .amiibo_name = GetAmiiboName(settings),
928 .font_region = {},
929 }; 17 };
18 // clang-format on
930 19
931 return ResultSuccess; 20 RegisterHandlers(functions);
932 } 21 }
933 22
934 // Generate a generic answer 23private:
935 register_info = { 24 void CreateUserInterface(Kernel::HLERequestContext& ctx) {
936 .mii_char_info = manager.BuildDefault(0), 25 LOG_DEBUG(Service_NFP, "called");
937 .first_write_year = 2022,
938 .first_write_month = 2,
939 .first_write_day = 7,
940 .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0},
941 .font_region = {},
942 };
943 return ResultSuccess;
944}
945 26
946Result Module::Interface::OpenApplicationArea(u32 access_id) { 27 if (user_interface == nullptr) {
947 if (device_state != DeviceState::TagMounted) { 28 user_interface = std::make_shared<IUser>(system);
948 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
949 if (device_state == DeviceState::TagRemoved) {
950 return ErrCodes::TagRemoved;
951 } 29 }
952 return ErrCodes::WrongDeviceState;
953 }
954
955 // Fallback for lack of amiibo keys
956 if (!is_data_decoded) {
957 LOG_WARNING(Service_NFP, "Application area is not initialized");
958 return ErrCodes::ApplicationAreaIsNotInitialized;
959 }
960 30
961 if (tag_data.settings.settings.appdata_initialized == 0) { 31 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
962 LOG_WARNING(Service_NFP, "Application area is not initialized"); 32 rb.Push(ResultSuccess);
963 return ErrCodes::ApplicationAreaIsNotInitialized; 33 rb.PushIpcInterface<IUser>(user_interface);
964 }
965
966 if (tag_data.application_area_id != access_id) {
967 LOG_WARNING(Service_NFP, "Wrong application area id");
968 return ErrCodes::WrongApplicationAreaId;
969 }
970
971 is_application_area_initialized = true;
972 return ResultSuccess;
973}
974
975Result Module::Interface::GetApplicationArea(ApplicationArea& data) const {
976 if (device_state != DeviceState::TagMounted) {
977 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
978 if (device_state == DeviceState::TagRemoved) {
979 return ErrCodes::TagRemoved;
980 }
981 return ErrCodes::WrongDeviceState;
982 }
983
984 if (!is_application_area_initialized) {
985 LOG_ERROR(Service_NFP, "Application area is not initialized");
986 return ErrCodes::ApplicationAreaIsNotInitialized;
987 }
988
989 data = tag_data.application_area;
990
991 return ResultSuccess;
992}
993
994Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
995 if (device_state != DeviceState::TagMounted) {
996 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
997 if (device_state == DeviceState::TagRemoved) {
998 return ErrCodes::TagRemoved;
999 }
1000 return ErrCodes::WrongDeviceState;
1001 }
1002
1003 if (!is_application_area_initialized) {
1004 LOG_ERROR(Service_NFP, "Application area is not initialized");
1005 return ErrCodes::ApplicationAreaIsNotInitialized;
1006 }
1007
1008 if (data.size() != sizeof(ApplicationArea)) {
1009 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
1010 return ResultUnknown;
1011 }
1012
1013 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
1014 return ResultSuccess;
1015}
1016
1017Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
1018 if (device_state != DeviceState::TagMounted) {
1019 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
1020 if (device_state == DeviceState::TagRemoved) {
1021 return ErrCodes::TagRemoved;
1022 }
1023 return ErrCodes::WrongDeviceState;
1024 }
1025
1026 if (tag_data.settings.settings.appdata_initialized != 0) {
1027 LOG_ERROR(Service_NFP, "Application area already exist");
1028 return ErrCodes::ApplicationAreaExist;
1029 }
1030
1031 if (data.size() != sizeof(ApplicationArea)) {
1032 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
1033 return ResultUnknown;
1034 }
1035
1036 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
1037 tag_data.application_area_id = access_id;
1038
1039 return ResultSuccess;
1040}
1041
1042Result Module::Interface::RecreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
1043 if (device_state != DeviceState::TagMounted) {
1044 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
1045 if (device_state == DeviceState::TagRemoved) {
1046 return ErrCodes::TagRemoved;
1047 }
1048 return ErrCodes::WrongDeviceState;
1049 }
1050
1051 if (data.size() != sizeof(ApplicationArea)) {
1052 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
1053 return ResultUnknown;
1054 }
1055
1056 std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea));
1057 tag_data.application_area_id = access_id;
1058
1059 return ResultSuccess;
1060}
1061
1062u64 Module::Interface::GetHandle() const {
1063 // Generate a handle based of the npad id
1064 return static_cast<u64>(npad_id);
1065}
1066
1067DeviceState Module::Interface::GetCurrentState() const {
1068 return device_state;
1069}
1070
1071Core::HID::NpadIdType Module::Interface::GetNpadId() const {
1072 // Return first connected npad id as a workaround for lack of a single nfc interface per
1073 // controller
1074 return system.HIDCore().GetFirstNpadId();
1075}
1076
1077AmiiboName Module::Interface::GetAmiiboName(const AmiiboSettings& settings) const {
1078 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
1079 AmiiboName amiibo_name{};
1080
1081 // Convert from big endian to little endian
1082 for (std::size_t i = 0; i < amiibo_name_length; i++) {
1083 settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
1084 } 34 }
1085 35
1086 // Convert from utf16 to utf8 36 std::shared_ptr<IUser> user_interface;
1087 const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); 37};
1088 memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size());
1089
1090 return amiibo_name;
1091}
1092 38
1093void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 39void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
1094 auto module = std::make_shared<Module>(); 40 std::make_shared<IUserManager>(system)->InstallAsService(service_manager);
1095 std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
1096} 41}
1097 42
1098} // namespace Service::NFP 43} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 0de0b48e7..a25c362b8 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -3,170 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <array>
7#include <vector>
8
9#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/mii/types.h"
12#include "core/hle/service/nfp/amiibo_types.h"
13#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
14 7
15namespace Kernel {
16class KEvent;
17class KReadableEvent;
18} // namespace Kernel
19
20namespace Core::HID {
21enum class NpadIdType : u32;
22} // namespace Core::HID
23
24namespace Service::NFP { 8namespace Service::NFP {
25using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
26
27struct TagInfo {
28 TagUuid uuid;
29 u8 uuid_length;
30 INSERT_PADDING_BYTES(0x15);
31 s32 protocol;
32 u32 tag_type;
33 INSERT_PADDING_BYTES(0x30);
34};
35static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
36
37struct CommonInfo {
38 u16 last_write_year;
39 u8 last_write_month;
40 u8 last_write_day;
41 u16 write_counter;
42 u16 version;
43 u32 application_area_size;
44 INSERT_PADDING_BYTES(0x34);
45};
46static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
47
48struct ModelInfo {
49 u16 character_id;
50 u8 character_variant;
51 AmiiboType amiibo_type;
52 u16 model_number;
53 AmiiboSeries series;
54 u8 constant_value; // Must be 02
55 INSERT_PADDING_BYTES(0x38); // Unknown
56};
57static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
58
59struct RegisterInfo {
60 Service::Mii::CharInfo mii_char_info;
61 u16 first_write_year;
62 u8 first_write_month;
63 u8 first_write_day;
64 AmiiboName amiibo_name;
65 u8 font_region;
66 INSERT_PADDING_BYTES(0x7A);
67};
68static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
69
70class Module final {
71public:
72 class Interface : public ServiceFramework<Interface> {
73 public:
74 explicit Interface(std::shared_ptr<Module> module_, Core::System& system_,
75 const char* name);
76 ~Interface() override;
77
78 void CreateUserInterface(Kernel::HLERequestContext& ctx);
79 bool LoadAmiibo(const std::string& filename);
80 bool LoadAmiiboFile(const std::string& filename);
81 void CloseAmiibo();
82
83 void Initialize();
84 void Finalize();
85
86 Result StartDetection(s32 protocol_);
87 Result StopDetection();
88 Result Mount();
89 Result Unmount();
90 Result Flush();
91
92 Result GetTagInfo(TagInfo& tag_info) const;
93 Result GetCommonInfo(CommonInfo& common_info) const;
94 Result GetModelInfo(ModelInfo& model_info) const;
95 Result GetRegisterInfo(RegisterInfo& register_info) const;
96
97 Result OpenApplicationArea(u32 access_id);
98 Result GetApplicationArea(ApplicationArea& data) const;
99 Result SetApplicationArea(const std::vector<u8>& data);
100 Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
101 Result RecreateApplicationArea(u32 access_id, const std::vector<u8>& data);
102
103 u64 GetHandle() const;
104 DeviceState GetCurrentState() const;
105 Core::HID::NpadIdType GetNpadId() const;
106
107 Kernel::KReadableEvent& GetActivateEvent() const;
108 Kernel::KReadableEvent& GetDeactivateEvent() const;
109
110 protected:
111 std::shared_ptr<Module> module;
112
113 private:
114 AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
115
116 const Core::HID::NpadIdType npad_id;
117
118 bool is_data_decoded{};
119 bool is_application_area_initialized{};
120 s32 protocol;
121 std::string file_path{};
122 Kernel::KEvent* activate_event;
123 Kernel::KEvent* deactivate_event;
124 DeviceState device_state{DeviceState::Unaviable};
125 KernelHelpers::ServiceContext service_context;
126
127 NTAG215File tag_data{};
128 EncryptedNTAG215File encrypted_tag_data{};
129 };
130};
131
132class IUser final : public ServiceFramework<IUser> {
133public:
134 explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
135
136private:
137 void Initialize(Kernel::HLERequestContext& ctx);
138 void Finalize(Kernel::HLERequestContext& ctx);
139 void ListDevices(Kernel::HLERequestContext& ctx);
140 void StartDetection(Kernel::HLERequestContext& ctx);
141 void StopDetection(Kernel::HLERequestContext& ctx);
142 void Mount(Kernel::HLERequestContext& ctx);
143 void Unmount(Kernel::HLERequestContext& ctx);
144 void OpenApplicationArea(Kernel::HLERequestContext& ctx);
145 void GetApplicationArea(Kernel::HLERequestContext& ctx);
146 void SetApplicationArea(Kernel::HLERequestContext& ctx);
147 void Flush(Kernel::HLERequestContext& ctx);
148 void CreateApplicationArea(Kernel::HLERequestContext& ctx);
149 void GetTagInfo(Kernel::HLERequestContext& ctx);
150 void GetRegisterInfo(Kernel::HLERequestContext& ctx);
151 void GetCommonInfo(Kernel::HLERequestContext& ctx);
152 void GetModelInfo(Kernel::HLERequestContext& ctx);
153 void AttachActivateEvent(Kernel::HLERequestContext& ctx);
154 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
155 void GetState(Kernel::HLERequestContext& ctx);
156 void GetDeviceState(Kernel::HLERequestContext& ctx);
157 void GetNpadId(Kernel::HLERequestContext& ctx);
158 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
159 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
160 void RecreateApplicationArea(Kernel::HLERequestContext& ctx);
161
162 KernelHelpers::ServiceContext service_context;
163
164 // TODO(german77): We should have a vector of interfaces
165 Module::Interface& nfp_interface;
166
167 State state{State::NonInitialized};
168 Kernel::KEvent* availability_change_event;
169};
170 9
171void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 10void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
172 11
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
new file mode 100644
index 000000000..da7daae88
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -0,0 +1,572 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5#include <atomic>
6
7#include "common/fs/file.h"
8#include "common/fs/path_util.h"
9#include "common/input.h"
10#include "common/logging/log.h"
11#include "common/string_util.h"
12#include "common/tiny_mt.h"
13#include "core/core.h"
14#include "core/hid/emulated_controller.h"
15#include "core/hid/hid_core.h"
16#include "core/hid/hid_types.h"
17#include "core/hle/ipc_helpers.h"
18#include "core/hle/kernel/k_event.h"
19#include "core/hle/service/mii/mii_manager.h"
20#include "core/hle/service/nfp/amiibo_crypto.h"
21#include "core/hle/service/nfp/nfp.h"
22#include "core/hle/service/nfp/nfp_device.h"
23#include "core/hle/service/nfp/nfp_result.h"
24#include "core/hle/service/nfp/nfp_user.h"
25
26namespace Service::NFP {
27NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
28 KernelHelpers::ServiceContext& service_context_,
29 Kernel::KEvent* availability_change_event_)
30 : npad_id{npad_id_}, system{system_}, service_context{service_context_},
31 availability_change_event{availability_change_event_} {
32 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
33 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
34 npad_device = system.HIDCore().GetEmulatedController(npad_id);
35
36 Core::HID::ControllerUpdateCallback engine_callback{
37 .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
38 .is_npad_service = false,
39 };
40 is_controller_set = true;
41 callback_key = npad_device->SetCallback(engine_callback);
42}
43
44NfpDevice::~NfpDevice() {
45 if (is_controller_set) {
46 npad_device->DeleteCallback(callback_key);
47 is_controller_set = false;
48 }
49};
50
51void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
52 if (type == Core::HID::ControllerTriggerType::Connected ||
53 type == Core::HID::ControllerTriggerType::Disconnected) {
54 availability_change_event->GetWritableEvent().Signal();
55 return;
56 }
57
58 if (type != Core::HID::ControllerTriggerType::Nfc) {
59 return;
60 }
61
62 if (!npad_device->IsConnected()) {
63 return;
64 }
65
66 const auto nfc_status = npad_device->GetNfc();
67 switch (nfc_status.state) {
68 case Common::Input::NfcState::NewAmiibo:
69 LoadAmiibo(nfc_status.data);
70 break;
71 case Common::Input::NfcState::AmiiboRemoved:
72 if (device_state != DeviceState::SearchingForTag) {
73 CloseAmiibo();
74 }
75 break;
76 default:
77 break;
78 }
79}
80
81bool NfpDevice::LoadAmiibo(const std::vector<u8>& data) {
82 if (device_state != DeviceState::SearchingForTag) {
83 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
84 return false;
85 }
86
87 if (data.size() != sizeof(EncryptedNTAG215File)) {
88 LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size());
89 return false;
90 }
91
92 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
93
94 if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
95 LOG_INFO(Service_NFP, "Invalid amiibo");
96 return false;
97 }
98
99 device_state = DeviceState::TagFound;
100 activate_event->GetWritableEvent().Signal();
101 return true;
102}
103
104void NfpDevice::CloseAmiibo() {
105 LOG_INFO(Service_NFP, "Remove amiibo");
106
107 if (device_state == DeviceState::TagMounted) {
108 Unmount();
109 }
110
111 device_state = DeviceState::TagRemoved;
112 encrypted_tag_data = {};
113 tag_data = {};
114 deactivate_event->GetWritableEvent().Signal();
115}
116
117Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const {
118 return activate_event->GetReadableEvent();
119}
120
121Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const {
122 return deactivate_event->GetReadableEvent();
123}
124
125void NfpDevice::Initialize() {
126 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
127 encrypted_tag_data = {};
128 tag_data = {};
129}
130
131void NfpDevice::Finalize() {
132 if (device_state == DeviceState::TagMounted) {
133 Unmount();
134 }
135 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
136 StopDetection();
137 }
138 device_state = DeviceState::Unavailable;
139}
140
141Result NfpDevice::StartDetection(s32 protocol_) {
142 // TODO(german77): Add callback for when nfc data is available
143
144 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
145 npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
146 device_state = DeviceState::SearchingForTag;
147 protocol = protocol_;
148 return ResultSuccess;
149 }
150
151 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
152 return WrongDeviceState;
153}
154
155Result NfpDevice::StopDetection() {
156 npad_device->SetPollingMode(Common::Input::PollingMode::Active);
157
158 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
159 CloseAmiibo();
160 return ResultSuccess;
161 }
162 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
163 device_state = DeviceState::Initialized;
164 return ResultSuccess;
165 }
166
167 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
168 return WrongDeviceState;
169}
170
171Result NfpDevice::Flush() {
172 auto& settings = tag_data.settings;
173
174 if (settings.write_date.raw_date != settings.write_date.raw_date) {
175 // TODO: Read current system date
176 settings.write_date.SetYear(2022);
177 settings.write_date.SetMonth(9);
178 settings.write_date.SetDay(9);
179 settings.crc_counter++;
180 // TODO: Find how to calculate the crc check
181 // settings.crc = CalculateCRC(settings);
182 }
183
184 tag_data.write_counter++;
185
186 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
187 LOG_ERROR(Service_NFP, "Failed to encode data");
188 return WriteAmiiboFailed;
189 }
190
191 std::vector<u8> data(sizeof(encrypted_tag_data));
192 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
193
194 if (!npad_device->WriteNfc(data)) {
195 LOG_ERROR(Service_NFP, "Error writing to file");
196 return WriteAmiiboFailed;
197 }
198
199 is_data_moddified = false;
200
201 return ResultSuccess;
202}
203
204Result NfpDevice::Mount() {
205 if (device_state != DeviceState::TagFound) {
206 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
207 return WrongDeviceState;
208 }
209
210 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
211 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
212 return CorruptedData;
213 }
214
215 device_state = DeviceState::TagMounted;
216 return ResultSuccess;
217}
218
219Result NfpDevice::Unmount() {
220 if (device_state != DeviceState::TagMounted) {
221 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
222 return WrongDeviceState;
223 }
224
225 // Save data before unloading the amiibo
226 if (is_data_moddified) {
227 Flush();
228 }
229
230 device_state = DeviceState::TagFound;
231 return ResultSuccess;
232}
233
234Result NfpDevice::GetTagInfo(TagInfo& tag_info) const {
235 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
236 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
237 return WrongDeviceState;
238 }
239
240 tag_info = {
241 .uuid = encrypted_tag_data.uuid,
242 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()),
243 .protocol = protocol,
244 .tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type),
245 };
246
247 return ResultSuccess;
248}
249
250Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const {
251 if (device_state != DeviceState::TagMounted) {
252 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
253 return WrongDeviceState;
254 }
255
256 const auto& settings = tag_data.settings;
257 const u32 application_area_size =
258 tag_data.settings.settings.appdata_initialized == 0 ? 0 : sizeof(ApplicationArea);
259
260 // TODO: Validate this data
261 common_info = {
262 .last_write_date =
263 {
264 settings.write_date.GetYear(),
265 settings.write_date.GetMonth(),
266 settings.write_date.GetDay(),
267 },
268 .write_counter = tag_data.write_counter,
269 .version = 1,
270 .application_area_size = application_area_size,
271 };
272 return ResultSuccess;
273}
274
275Result NfpDevice::GetModelInfo(ModelInfo& model_info) const {
276 if (device_state != DeviceState::TagMounted) {
277 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
278 return WrongDeviceState;
279 }
280
281 const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
282 model_info = {
283 .character_id = model_info_data.character_id,
284 .character_variant = model_info_data.character_variant,
285 .amiibo_type = model_info_data.amiibo_type,
286 .model_number = model_info_data.model_number,
287 .series = model_info_data.series,
288 };
289 return ResultSuccess;
290}
291
292Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
293 if (device_state != DeviceState::TagMounted) {
294 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
295 if (device_state == DeviceState::TagRemoved) {
296 return TagRemoved;
297 }
298 return WrongDeviceState;
299 }
300
301 if (tag_data.settings.settings.amiibo_initialized == 0) {
302 return RegistrationIsNotInitialized;
303 }
304
305 Service::Mii::MiiManager manager;
306 const auto& settings = tag_data.settings;
307
308 // TODO: Validate this data
309 register_info = {
310 .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
311 .creation_date =
312 {
313 settings.init_date.GetYear(),
314 settings.init_date.GetMonth(),
315 settings.init_date.GetDay(),
316 },
317 .amiibo_name = GetAmiiboName(settings),
318 .font_region = {},
319 };
320
321 return ResultSuccess;
322}
323
324Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) {
325 if (device_state != DeviceState::TagMounted) {
326 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
327 if (device_state == DeviceState::TagRemoved) {
328 return TagRemoved;
329 }
330 return WrongDeviceState;
331 }
332
333 Service::Mii::MiiManager manager;
334 auto& settings = tag_data.settings;
335
336 // TODO: Read current system date
337 settings.init_date.SetYear(2022);
338 settings.init_date.SetMonth(9);
339 settings.init_date.SetDay(9);
340 settings.write_date.SetYear(2022);
341 settings.write_date.SetMonth(9);
342 settings.write_date.SetDay(9);
343 settings.crc_counter++;
344 // TODO: Find how to calculate the crc check
345 // settings.crc = CalculateCRC(settings);
346
347 SetAmiiboName(settings, amiibo_name);
348 tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0));
349 settings.settings.amiibo_initialized.Assign(1);
350
351 return Flush();
352}
353
354Result NfpDevice::RestoreAmiibo() {
355 // TODO: Load amiibo from backup on system
356 LOG_ERROR(Service_NFP, "Not Implemented");
357 return ResultSuccess;
358}
359
360Result NfpDevice::DeleteAllData() {
361 const auto result = DeleteApplicationArea();
362 if (result.IsError()) {
363 return result;
364 }
365
366 if (device_state != DeviceState::TagMounted) {
367 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
368 if (device_state == DeviceState::TagRemoved) {
369 return TagRemoved;
370 }
371 return WrongDeviceState;
372 }
373
374 Common::TinyMT rng{};
375 rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii));
376 tag_data.settings.settings.amiibo_initialized.Assign(0);
377
378 return Flush();
379}
380
381Result NfpDevice::OpenApplicationArea(u32 access_id) {
382 if (device_state != DeviceState::TagMounted) {
383 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
384 if (device_state == DeviceState::TagRemoved) {
385 return TagRemoved;
386 }
387 return WrongDeviceState;
388 }
389
390 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
391 LOG_WARNING(Service_NFP, "Application area is not initialized");
392 return ApplicationAreaIsNotInitialized;
393 }
394
395 if (tag_data.application_area_id != access_id) {
396 LOG_WARNING(Service_NFP, "Wrong application area id");
397 return WrongApplicationAreaId;
398 }
399
400 return ResultSuccess;
401}
402
403Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
404 if (device_state != DeviceState::TagMounted) {
405 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
406 if (device_state == DeviceState::TagRemoved) {
407 return TagRemoved;
408 }
409 return WrongDeviceState;
410 }
411
412 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
413 LOG_ERROR(Service_NFP, "Application area is not initialized");
414 return ApplicationAreaIsNotInitialized;
415 }
416
417 if (data.size() > sizeof(ApplicationArea)) {
418 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
419 return ResultUnknown;
420 }
421
422 memcpy(data.data(), tag_data.application_area.data(), data.size());
423
424 return ResultSuccess;
425}
426
427Result NfpDevice::SetApplicationArea(const std::vector<u8>& data) {
428 if (device_state != DeviceState::TagMounted) {
429 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
430 if (device_state == DeviceState::TagRemoved) {
431 return TagRemoved;
432 }
433 return WrongDeviceState;
434 }
435
436 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
437 LOG_ERROR(Service_NFP, "Application area is not initialized");
438 return ApplicationAreaIsNotInitialized;
439 }
440
441 if (data.size() > sizeof(ApplicationArea)) {
442 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
443 return ResultUnknown;
444 }
445
446 Common::TinyMT rng{};
447 rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea));
448 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
449
450 tag_data.applicaton_write_counter++;
451 is_data_moddified = true;
452
453 return ResultSuccess;
454}
455
456Result NfpDevice::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
457 if (device_state != DeviceState::TagMounted) {
458 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
459 if (device_state == DeviceState::TagRemoved) {
460 return TagRemoved;
461 }
462 return WrongDeviceState;
463 }
464
465 if (tag_data.settings.settings.appdata_initialized.Value() != 0) {
466 LOG_ERROR(Service_NFP, "Application area already exist");
467 return ApplicationAreaExist;
468 }
469
470 return RecreateApplicationArea(access_id, data);
471}
472
473Result NfpDevice::RecreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
474 if (device_state != DeviceState::TagMounted) {
475 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
476 if (device_state == DeviceState::TagRemoved) {
477 return TagRemoved;
478 }
479 return WrongDeviceState;
480 }
481
482 if (data.size() > sizeof(ApplicationArea)) {
483 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
484 return ResultUnknown;
485 }
486
487 Common::TinyMT rng{};
488 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
489 // HW seems to fill excess data with garbage
490 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
491 sizeof(ApplicationArea) - data.size());
492
493 // TODO: Investigate why the title id needs to be moddified
494 tag_data.title_id = system.GetCurrentProcessProgramID();
495 tag_data.title_id = tag_data.title_id | 0x30000000ULL;
496 tag_data.settings.settings.appdata_initialized.Assign(1);
497 tag_data.application_area_id = access_id;
498 tag_data.applicaton_write_counter++;
499 tag_data.unknown = {};
500
501 return Flush();
502}
503
504Result NfpDevice::DeleteApplicationArea() {
505 if (device_state != DeviceState::TagMounted) {
506 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
507 if (device_state == DeviceState::TagRemoved) {
508 return TagRemoved;
509 }
510 return WrongDeviceState;
511 }
512
513 Common::TinyMT rng{};
514 rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea));
515 rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64));
516 rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32));
517 tag_data.settings.settings.appdata_initialized.Assign(0);
518 tag_data.applicaton_write_counter++;
519 tag_data.unknown = {};
520
521 return Flush();
522}
523
524u64 NfpDevice::GetHandle() const {
525 // Generate a handle based of the npad id
526 return static_cast<u64>(npad_id);
527}
528
529u32 NfpDevice::GetApplicationAreaSize() const {
530 // Investigate if this value is really constant
531 return sizeof(ApplicationArea);
532}
533
534DeviceState NfpDevice::GetCurrentState() const {
535 return device_state;
536}
537
538Core::HID::NpadIdType NfpDevice::GetNpadId() const {
539 return npad_id;
540}
541
542AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const {
543 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
544 AmiiboName amiibo_name{};
545
546 // Convert from big endian to little endian
547 for (std::size_t i = 0; i < amiibo_name_length; i++) {
548 settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
549 }
550
551 // Convert from utf16 to utf8
552 const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data());
553 memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size());
554
555 return amiibo_name;
556}
557
558void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) {
559 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
560
561 // Convert from utf8 to utf16
562 const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data());
563 memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(),
564 amiibo_name_utf16.size() * sizeof(char16_t));
565
566 // Convert from little endian to big endian
567 for (std::size_t i = 0; i < amiibo_name_length; i++) {
568 settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]);
569 }
570}
571
572} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
new file mode 100644
index 000000000..53cc0833f
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -0,0 +1,97 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <vector>
8
9#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/mii/types.h"
12#include "core/hle/service/nfp/nfp_types.h"
13#include "core/hle/service/service.h"
14
15namespace Kernel {
16class KEvent;
17class KReadableEvent;
18} // namespace Kernel
19
20namespace Core {
21class System;
22} // namespace Core
23
24namespace Core::HID {
25class EmulatedController;
26enum class ControllerTriggerType;
27enum class NpadIdType : u32;
28} // namespace Core::HID
29
30namespace Service::NFP {
31class NfpDevice {
32public:
33 NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
34 KernelHelpers::ServiceContext& service_context_,
35 Kernel::KEvent* availability_change_event_);
36 ~NfpDevice();
37
38 void Initialize();
39 void Finalize();
40
41 Result StartDetection(s32 protocol_);
42 Result StopDetection();
43 Result Mount();
44 Result Unmount();
45 Result Flush();
46
47 Result GetTagInfo(TagInfo& tag_info) const;
48 Result GetCommonInfo(CommonInfo& common_info) const;
49 Result GetModelInfo(ModelInfo& model_info) const;
50 Result GetRegisterInfo(RegisterInfo& register_info) const;
51
52 Result SetNicknameAndOwner(const AmiiboName& amiibo_name);
53 Result RestoreAmiibo();
54 Result DeleteAllData();
55
56 Result OpenApplicationArea(u32 access_id);
57 Result GetApplicationArea(std::vector<u8>& data) const;
58 Result SetApplicationArea(const std::vector<u8>& data);
59 Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
60 Result RecreateApplicationArea(u32 access_id, const std::vector<u8>& data);
61 Result DeleteApplicationArea();
62
63 u64 GetHandle() const;
64 u32 GetApplicationAreaSize() const;
65 DeviceState GetCurrentState() const;
66 Core::HID::NpadIdType GetNpadId() const;
67
68 Kernel::KReadableEvent& GetActivateEvent() const;
69 Kernel::KReadableEvent& GetDeactivateEvent() const;
70
71private:
72 void NpadUpdate(Core::HID::ControllerTriggerType type);
73 bool LoadAmiibo(const std::vector<u8>& data);
74 void CloseAmiibo();
75
76 AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
77 void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
78
79 bool is_controller_set{};
80 int callback_key;
81 const Core::HID::NpadIdType npad_id;
82 Core::System& system;
83 Core::HID::EmulatedController* npad_device = nullptr;
84 KernelHelpers::ServiceContext& service_context;
85 Kernel::KEvent* activate_event = nullptr;
86 Kernel::KEvent* deactivate_event = nullptr;
87 Kernel::KEvent* availability_change_event = nullptr;
88
89 bool is_data_moddified{};
90 s32 protocol{};
91 DeviceState device_state{DeviceState::Unavailable};
92
93 NTAG215File tag_data{};
94 EncryptedNTAG215File encrypted_tag_data{};
95};
96
97} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h
new file mode 100644
index 000000000..15bc02b15
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_result.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::NFP {
9
10constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
11constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
12constexpr Result NfcDisabled(ErrorModule::NFP, 80);
13constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
14constexpr Result TagRemoved(ErrorModule::NFP, 97);
15constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120);
16constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
17constexpr Result CorruptedData(ErrorModule::NFP, 144);
18constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
19constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
20
21} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/nfp_types.h
index bf2de811a..d58657a21 100644
--- a/src/core/hle/service/nfp/amiibo_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -27,7 +27,7 @@ enum class DeviceState : u32 {
27 TagFound, 27 TagFound,
28 TagRemoved, 28 TagRemoved,
29 TagMounted, 29 TagMounted,
30 Unaviable, 30 Unavailable,
31 Finalized, 31 Finalized,
32}; 32};
33 33
@@ -36,6 +36,7 @@ enum class ModelType : u32 {
36}; 36};
37 37
38enum class MountTarget : u32 { 38enum class MountTarget : u32 {
39 None,
39 Rom, 40 Rom,
40 Ram, 41 Ram,
41 All, 42 All,
@@ -76,18 +77,36 @@ enum class AmiiboSeries : u8 {
76using TagUuid = std::array<u8, 10>; 77using TagUuid = std::array<u8, 10>;
77using HashData = std::array<u8, 0x20>; 78using HashData = std::array<u8, 0x20>;
78using ApplicationArea = std::array<u8, 0xD8>; 79using ApplicationArea = std::array<u8, 0xD8>;
80using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
79 81
80struct AmiiboDate { 82struct AmiiboDate {
81 u16 raw_date{}; 83 u16_be raw_date{};
84
85 u16 DateRaw() const {
86 return static_cast<u16>(raw_date);
87 }
82 88
83 u16 GetYear() const { 89 u16 GetYear() const {
84 return static_cast<u16>(((raw_date & 0xFE00) >> 9) + 2000); 90 return static_cast<u16>(((DateRaw() & 0xFE00) >> 9) + 2000);
85 } 91 }
86 u8 GetMonth() const { 92 u8 GetMonth() const {
87 return static_cast<u8>(((raw_date & 0x01E0) >> 5) - 1); 93 return static_cast<u8>(((DateRaw() & 0x01E0) >> 5) - 1);
88 } 94 }
89 u8 GetDay() const { 95 u8 GetDay() const {
90 return static_cast<u8>(raw_date & 0x001F); 96 return static_cast<u8>(DateRaw() & 0x001F);
97 }
98
99 void SetYear(u16 year) {
100 raw_date = DateRaw() & ~0xFE00;
101 raw_date |= static_cast<u16_be>((year - 2000) << 9);
102 }
103 void SetMonth(u8 month) {
104 raw_date = DateRaw() & ~0x01E0;
105 raw_date |= static_cast<u16_be>((month + 1) << 5);
106 }
107 void SetDay(u8 day) {
108 raw_date = DateRaw() & ~0x001F;
109 raw_date |= static_cast<u16_be>(day);
91 } 110 }
92}; 111};
93static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); 112static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size");
@@ -134,7 +153,7 @@ static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid siz
134#pragma pack(1) 153#pragma pack(1)
135struct EncryptedAmiiboFile { 154struct EncryptedAmiiboFile {
136 u8 constant_value; // Must be A5 155 u8 constant_value; // Must be A5
137 u16 write_counter; // Number of times the amiibo has been written? 156 u16_be write_counter; // Number of times the amiibo has been written?
138 INSERT_PADDING_BYTES(0x1); // Unknown 1 157 INSERT_PADDING_BYTES(0x1); // Unknown 1
139 AmiiboSettings settings; // Encrypted amiibo settings 158 AmiiboSettings settings; // Encrypted amiibo settings
140 HashData hmac_tag; // Hash 159 HashData hmac_tag; // Hash
@@ -157,7 +176,7 @@ struct NTAG215File {
157 u32 compability_container; // Defines available memory 176 u32 compability_container; // Defines available memory
158 HashData hmac_data; // Hash 177 HashData hmac_data; // Hash
159 u8 constant_value; // Must be A5 178 u8 constant_value; // Must be A5
160 u16 write_counter; // Number of times the amiibo has been written? 179 u16_be write_counter; // Number of times the amiibo has been written?
161 INSERT_PADDING_BYTES(0x1); // Unknown 1 180 INSERT_PADDING_BYTES(0x1); // Unknown 1
162 AmiiboSettings settings; 181 AmiiboSettings settings;
163 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data 182 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
@@ -194,4 +213,50 @@ static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an
194static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, 213static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
195 "EncryptedNTAG215File must be trivially copyable."); 214 "EncryptedNTAG215File must be trivially copyable.");
196 215
216struct TagInfo {
217 TagUuid uuid;
218 u8 uuid_length;
219 INSERT_PADDING_BYTES(0x15);
220 s32 protocol;
221 u32 tag_type;
222 INSERT_PADDING_BYTES(0x30);
223};
224static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
225
226struct WriteDate {
227 u16 year;
228 u8 month;
229 u8 day;
230};
231static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size");
232
233struct CommonInfo {
234 WriteDate last_write_date;
235 u16 write_counter;
236 u8 version;
237 INSERT_PADDING_BYTES(0x1);
238 u32 application_area_size;
239 INSERT_PADDING_BYTES(0x34);
240};
241static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
242
243struct ModelInfo {
244 u16 character_id;
245 u8 character_variant;
246 AmiiboType amiibo_type;
247 u16 model_number;
248 AmiiboSeries series;
249 INSERT_PADDING_BYTES(0x39); // Unknown
250};
251static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
252
253struct RegisterInfo {
254 Service::Mii::CharInfo mii_char_info;
255 WriteDate creation_date;
256 AmiiboName amiibo_name;
257 u8 font_region;
258 INSERT_PADDING_BYTES(0x7A);
259};
260static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
261
197} // namespace Service::NFP 262} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 2d7b156cf..f8f1975db 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -1,18 +1,644 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include <atomic>
6
7#include "common/logging/log.h"
8#include "core/core.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "core/hid/hid_types.h"
12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_event.h"
14#include "core/hle/service/mii/mii_manager.h"
15#include "core/hle/service/nfp/nfp_device.h"
16#include "core/hle/service/nfp/nfp_result.h"
4#include "core/hle/service/nfp/nfp_user.h" 17#include "core/hle/service/nfp/nfp_user.h"
5 18
6namespace Service::NFP { 19namespace Service::NFP {
7 20
8NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_) 21IUser::IUser(Core::System& system_)
9 : Interface(std::move(module_), system_, "nfp:user") { 22 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} {
10 static const FunctionInfo functions[] = { 23 static const FunctionInfo functions[] = {
11 {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, 24 {0, &IUser::Initialize, "Initialize"},
25 {1, &IUser::Finalize, "Finalize"},
26 {2, &IUser::ListDevices, "ListDevices"},
27 {3, &IUser::StartDetection, "StartDetection"},
28 {4, &IUser::StopDetection, "StopDetection"},
29 {5, &IUser::Mount, "Mount"},
30 {6, &IUser::Unmount, "Unmount"},
31 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
32 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
33 {9, &IUser::SetApplicationArea, "SetApplicationArea"},
34 {10, &IUser::Flush, "Flush"},
35 {11, &IUser::Restore, "Restore"},
36 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
37 {13, &IUser::GetTagInfo, "GetTagInfo"},
38 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
39 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
40 {16, &IUser::GetModelInfo, "GetModelInfo"},
41 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
42 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
43 {19, &IUser::GetState, "GetState"},
44 {20, &IUser::GetDeviceState, "GetDeviceState"},
45 {21, &IUser::GetNpadId, "GetNpadId"},
46 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
47 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
48 {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
12 }; 49 };
13 RegisterHandlers(functions); 50 RegisterHandlers(functions);
51
52 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
53
54 for (u32 device_index = 0; device_index < 10; device_index++) {
55 devices[device_index] =
56 std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system,
57 service_context, availability_change_event);
58 }
59}
60
61void IUser::Initialize(Kernel::HLERequestContext& ctx) {
62 LOG_INFO(Service_NFC, "called");
63
64 state = State::Initialized;
65
66 for (auto& device : devices) {
67 device->Initialize();
68 }
69
70 IPC::ResponseBuilder rb{ctx, 2, 0};
71 rb.Push(ResultSuccess);
72}
73
74void IUser::Finalize(Kernel::HLERequestContext& ctx) {
75 LOG_INFO(Service_NFP, "called");
76
77 state = State::NonInitialized;
78
79 for (auto& device : devices) {
80 device->Finalize();
81 }
82
83 IPC::ResponseBuilder rb{ctx, 2};
84 rb.Push(ResultSuccess);
85}
86
87void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
88 LOG_INFO(Service_NFP, "called");
89
90 if (state == State::NonInitialized) {
91 IPC::ResponseBuilder rb{ctx, 2};
92 rb.Push(NfcDisabled);
93 return;
94 }
95
96 std::vector<u64> nfp_devices;
97 const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64);
98
99 for (auto& device : devices) {
100 if (nfp_devices.size() >= max_allowed_devices) {
101 continue;
102 }
103 if (device->GetCurrentState() != DeviceState::Unavailable) {
104 nfp_devices.push_back(device->GetHandle());
105 }
106 }
107
108 if (nfp_devices.size() == 0) {
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(DeviceNotFound);
111 return;
112 }
113
114 ctx.WriteBuffer(nfp_devices);
115
116 IPC::ResponseBuilder rb{ctx, 3};
117 rb.Push(ResultSuccess);
118 rb.Push(static_cast<s32>(nfp_devices.size()));
119}
120
121void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
122 IPC::RequestParser rp{ctx};
123 const auto device_handle{rp.Pop<u64>()};
124 const auto nfp_protocol{rp.Pop<s32>()};
125 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
126
127 if (state == State::NonInitialized) {
128 IPC::ResponseBuilder rb{ctx, 2};
129 rb.Push(NfcDisabled);
130 return;
131 }
132
133 auto device = GetNfpDevice(device_handle);
134
135 if (!device.has_value()) {
136 IPC::ResponseBuilder rb{ctx, 2};
137 rb.Push(DeviceNotFound);
138 return;
139 }
140
141 const auto result = device.value()->StartDetection(nfp_protocol);
142 IPC::ResponseBuilder rb{ctx, 2};
143 rb.Push(result);
144}
145
146void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
147 IPC::RequestParser rp{ctx};
148 const auto device_handle{rp.Pop<u64>()};
149 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
150
151 if (state == State::NonInitialized) {
152 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(NfcDisabled);
154 return;
155 }
156
157 auto device = GetNfpDevice(device_handle);
158
159 if (!device.has_value()) {
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(DeviceNotFound);
162 return;
163 }
164
165 const auto result = device.value()->StopDetection();
166 IPC::ResponseBuilder rb{ctx, 2};
167 rb.Push(result);
168}
169
170void IUser::Mount(Kernel::HLERequestContext& ctx) {
171 IPC::RequestParser rp{ctx};
172 const auto device_handle{rp.Pop<u64>()};
173 const auto model_type{rp.PopEnum<ModelType>()};
174 const auto mount_target{rp.PopEnum<MountTarget>()};
175 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
176 model_type, mount_target);
177
178 if (state == State::NonInitialized) {
179 IPC::ResponseBuilder rb{ctx, 2};
180 rb.Push(NfcDisabled);
181 return;
182 }
183
184 auto device = GetNfpDevice(device_handle);
185
186 if (!device.has_value()) {
187 IPC::ResponseBuilder rb{ctx, 2};
188 rb.Push(DeviceNotFound);
189 return;
190 }
191
192 const auto result = device.value()->Mount();
193 IPC::ResponseBuilder rb{ctx, 2};
194 rb.Push(result);
195}
196
197void IUser::Unmount(Kernel::HLERequestContext& ctx) {
198 IPC::RequestParser rp{ctx};
199 const auto device_handle{rp.Pop<u64>()};
200 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
201
202 if (state == State::NonInitialized) {
203 IPC::ResponseBuilder rb{ctx, 2};
204 rb.Push(NfcDisabled);
205 return;
206 }
207
208 auto device = GetNfpDevice(device_handle);
209
210 if (!device.has_value()) {
211 IPC::ResponseBuilder rb{ctx, 2};
212 rb.Push(DeviceNotFound);
213 return;
214 }
215
216 const auto result = device.value()->Unmount();
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(result);
14} 219}
15 220
16NFP_User::~NFP_User() = default; 221void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
222 IPC::RequestParser rp{ctx};
223 const auto device_handle{rp.Pop<u64>()};
224 const auto access_id{rp.Pop<u32>()};
225 LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id);
226
227 if (state == State::NonInitialized) {
228 IPC::ResponseBuilder rb{ctx, 2};
229 rb.Push(NfcDisabled);
230 return;
231 }
232
233 auto device = GetNfpDevice(device_handle);
234
235 if (!device.has_value()) {
236 IPC::ResponseBuilder rb{ctx, 2};
237 rb.Push(DeviceNotFound);
238 return;
239 }
240
241 const auto result = device.value()->OpenApplicationArea(access_id);
242 IPC::ResponseBuilder rb{ctx, 2};
243 rb.Push(result);
244}
245
246void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
247 IPC::RequestParser rp{ctx};
248 const auto device_handle{rp.Pop<u64>()};
249 const auto data_size = ctx.GetWriteBufferSize();
250 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
251
252 if (state == State::NonInitialized) {
253 IPC::ResponseBuilder rb{ctx, 2};
254 rb.Push(NfcDisabled);
255 return;
256 }
257
258 auto device = GetNfpDevice(device_handle);
259
260 if (!device.has_value()) {
261 IPC::ResponseBuilder rb{ctx, 2};
262 rb.Push(DeviceNotFound);
263 return;
264 }
265
266 std::vector<u8> data(data_size);
267 const auto result = device.value()->GetApplicationArea(data);
268 ctx.WriteBuffer(data);
269 IPC::ResponseBuilder rb{ctx, 3};
270 rb.Push(result);
271 rb.Push(static_cast<u32>(data_size));
272}
273
274void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
275 IPC::RequestParser rp{ctx};
276 const auto device_handle{rp.Pop<u64>()};
277 const auto data{ctx.ReadBuffer()};
278 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size());
279
280 if (state == State::NonInitialized) {
281 IPC::ResponseBuilder rb{ctx, 2};
282 rb.Push(NfcDisabled);
283 return;
284 }
285
286 auto device = GetNfpDevice(device_handle);
287
288 if (!device.has_value()) {
289 IPC::ResponseBuilder rb{ctx, 2};
290 rb.Push(DeviceNotFound);
291 return;
292 }
293
294 const auto result = device.value()->SetApplicationArea(data);
295 IPC::ResponseBuilder rb{ctx, 2};
296 rb.Push(result);
297}
298
299void IUser::Flush(Kernel::HLERequestContext& ctx) {
300 IPC::RequestParser rp{ctx};
301 const auto device_handle{rp.Pop<u64>()};
302 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
303
304 if (state == State::NonInitialized) {
305 IPC::ResponseBuilder rb{ctx, 2};
306 rb.Push(NfcDisabled);
307 return;
308 }
309
310 auto device = GetNfpDevice(device_handle);
311
312 if (!device.has_value()) {
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(DeviceNotFound);
315 return;
316 }
317
318 const auto result = device.value()->Flush();
319 IPC::ResponseBuilder rb{ctx, 2};
320 rb.Push(result);
321}
322
323void IUser::Restore(Kernel::HLERequestContext& ctx) {
324 IPC::RequestParser rp{ctx};
325 const auto device_handle{rp.Pop<u64>()};
326 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
327
328 if (state == State::NonInitialized) {
329 IPC::ResponseBuilder rb{ctx, 2};
330 rb.Push(NfcDisabled);
331 return;
332 }
333
334 auto device = GetNfpDevice(device_handle);
335
336 if (!device.has_value()) {
337 IPC::ResponseBuilder rb{ctx, 2};
338 rb.Push(DeviceNotFound);
339 return;
340 }
341
342 const auto result = device.value()->RestoreAmiibo();
343 IPC::ResponseBuilder rb{ctx, 2};
344 rb.Push(result);
345}
346
347void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
348 IPC::RequestParser rp{ctx};
349 const auto device_handle{rp.Pop<u64>()};
350 const auto access_id{rp.Pop<u32>()};
351 const auto data{ctx.ReadBuffer()};
352 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
353 access_id, data.size());
354
355 if (state == State::NonInitialized) {
356 IPC::ResponseBuilder rb{ctx, 2};
357 rb.Push(NfcDisabled);
358 return;
359 }
360
361 auto device = GetNfpDevice(device_handle);
362
363 if (!device.has_value()) {
364 IPC::ResponseBuilder rb{ctx, 2};
365 rb.Push(DeviceNotFound);
366 return;
367 }
368
369 const auto result = device.value()->CreateApplicationArea(access_id, data);
370 IPC::ResponseBuilder rb{ctx, 2};
371 rb.Push(result);
372}
373
374void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
375 IPC::RequestParser rp{ctx};
376 const auto device_handle{rp.Pop<u64>()};
377 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
378
379 if (state == State::NonInitialized) {
380 IPC::ResponseBuilder rb{ctx, 2};
381 rb.Push(NfcDisabled);
382 return;
383 }
384
385 auto device = GetNfpDevice(device_handle);
386
387 if (!device.has_value()) {
388 IPC::ResponseBuilder rb{ctx, 2};
389 rb.Push(DeviceNotFound);
390 return;
391 }
392
393 TagInfo tag_info{};
394 const auto result = device.value()->GetTagInfo(tag_info);
395 ctx.WriteBuffer(tag_info);
396 IPC::ResponseBuilder rb{ctx, 2};
397 rb.Push(result);
398}
399
400void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
401 IPC::RequestParser rp{ctx};
402 const auto device_handle{rp.Pop<u64>()};
403 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
404
405 if (state == State::NonInitialized) {
406 IPC::ResponseBuilder rb{ctx, 2};
407 rb.Push(NfcDisabled);
408 return;
409 }
410
411 auto device = GetNfpDevice(device_handle);
412
413 if (!device.has_value()) {
414 IPC::ResponseBuilder rb{ctx, 2};
415 rb.Push(DeviceNotFound);
416 return;
417 }
418
419 RegisterInfo register_info{};
420 const auto result = device.value()->GetRegisterInfo(register_info);
421 ctx.WriteBuffer(register_info);
422 IPC::ResponseBuilder rb{ctx, 2};
423 rb.Push(result);
424}
425
426void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
427 IPC::RequestParser rp{ctx};
428 const auto device_handle{rp.Pop<u64>()};
429 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
430
431 if (state == State::NonInitialized) {
432 IPC::ResponseBuilder rb{ctx, 2};
433 rb.Push(NfcDisabled);
434 return;
435 }
436
437 auto device = GetNfpDevice(device_handle);
438
439 if (!device.has_value()) {
440 IPC::ResponseBuilder rb{ctx, 2};
441 rb.Push(DeviceNotFound);
442 return;
443 }
444
445 CommonInfo common_info{};
446 const auto result = device.value()->GetCommonInfo(common_info);
447 ctx.WriteBuffer(common_info);
448 IPC::ResponseBuilder rb{ctx, 2};
449 rb.Push(result);
450}
451
452void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
453 IPC::RequestParser rp{ctx};
454 const auto device_handle{rp.Pop<u64>()};
455 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
456
457 if (state == State::NonInitialized) {
458 IPC::ResponseBuilder rb{ctx, 2};
459 rb.Push(NfcDisabled);
460 return;
461 }
462
463 auto device = GetNfpDevice(device_handle);
464
465 if (!device.has_value()) {
466 IPC::ResponseBuilder rb{ctx, 2};
467 rb.Push(DeviceNotFound);
468 return;
469 }
470
471 ModelInfo model_info{};
472 const auto result = device.value()->GetModelInfo(model_info);
473 ctx.WriteBuffer(model_info);
474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(result);
476}
477
478void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
479 IPC::RequestParser rp{ctx};
480 const auto device_handle{rp.Pop<u64>()};
481 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
482
483 if (state == State::NonInitialized) {
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(NfcDisabled);
486 return;
487 }
488
489 auto device = GetNfpDevice(device_handle);
490
491 if (!device.has_value()) {
492 IPC::ResponseBuilder rb{ctx, 2};
493 rb.Push(DeviceNotFound);
494 return;
495 }
496
497 IPC::ResponseBuilder rb{ctx, 2, 1};
498 rb.Push(ResultSuccess);
499 rb.PushCopyObjects(device.value()->GetActivateEvent());
500}
501
502void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
503 IPC::RequestParser rp{ctx};
504 const auto device_handle{rp.Pop<u64>()};
505 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
506
507 if (state == State::NonInitialized) {
508 IPC::ResponseBuilder rb{ctx, 2};
509 rb.Push(NfcDisabled);
510 return;
511 }
512
513 auto device = GetNfpDevice(device_handle);
514
515 if (!device.has_value()) {
516 IPC::ResponseBuilder rb{ctx, 2};
517 rb.Push(DeviceNotFound);
518 return;
519 }
520
521 IPC::ResponseBuilder rb{ctx, 2, 1};
522 rb.Push(ResultSuccess);
523 rb.PushCopyObjects(device.value()->GetDeactivateEvent());
524}
525
526void IUser::GetState(Kernel::HLERequestContext& ctx) {
527 LOG_DEBUG(Service_NFC, "called");
528
529 IPC::ResponseBuilder rb{ctx, 3, 0};
530 rb.Push(ResultSuccess);
531 rb.PushEnum(state);
532}
533
534void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
535 IPC::RequestParser rp{ctx};
536 const auto device_handle{rp.Pop<u64>()};
537 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
538
539 auto device = GetNfpDevice(device_handle);
540
541 if (!device.has_value()) {
542 IPC::ResponseBuilder rb{ctx, 2};
543 rb.Push(DeviceNotFound);
544 return;
545 }
546
547 IPC::ResponseBuilder rb{ctx, 3};
548 rb.Push(ResultSuccess);
549 rb.PushEnum(device.value()->GetCurrentState());
550}
551
552void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
553 IPC::RequestParser rp{ctx};
554 const auto device_handle{rp.Pop<u64>()};
555 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
556
557 if (state == State::NonInitialized) {
558 IPC::ResponseBuilder rb{ctx, 2};
559 rb.Push(NfcDisabled);
560 return;
561 }
562
563 auto device = GetNfpDevice(device_handle);
564
565 if (!device.has_value()) {
566 IPC::ResponseBuilder rb{ctx, 2};
567 rb.Push(DeviceNotFound);
568 return;
569 }
570
571 IPC::ResponseBuilder rb{ctx, 3};
572 rb.Push(ResultSuccess);
573 rb.PushEnum(device.value()->GetNpadId());
574}
575
576void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
577 IPC::RequestParser rp{ctx};
578 const auto device_handle{rp.Pop<u64>()};
579 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
580
581 auto device = GetNfpDevice(device_handle);
582
583 if (!device.has_value()) {
584 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(DeviceNotFound);
586 return;
587 }
588
589 IPC::ResponseBuilder rb{ctx, 3};
590 rb.Push(ResultSuccess);
591 rb.Push(device.value()->GetApplicationAreaSize());
592}
593
594void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
595 LOG_INFO(Service_NFP, "called");
596
597 if (state == State::NonInitialized) {
598 IPC::ResponseBuilder rb{ctx, 2};
599 rb.Push(NfcDisabled);
600 return;
601 }
602
603 IPC::ResponseBuilder rb{ctx, 2, 1};
604 rb.Push(ResultSuccess);
605 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
606}
607
608void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) {
609 IPC::RequestParser rp{ctx};
610 const auto device_handle{rp.Pop<u64>()};
611 const auto access_id{rp.Pop<u32>()};
612 const auto data{ctx.ReadBuffer()};
613 LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
614 access_id, data.size());
615
616 if (state == State::NonInitialized) {
617 IPC::ResponseBuilder rb{ctx, 2};
618 rb.Push(NfcDisabled);
619 return;
620 }
621
622 auto device = GetNfpDevice(device_handle);
623
624 if (!device.has_value()) {
625 IPC::ResponseBuilder rb{ctx, 2};
626 rb.Push(DeviceNotFound);
627 return;
628 }
629
630 const auto result = device.value()->RecreateApplicationArea(access_id, data);
631 IPC::ResponseBuilder rb{ctx, 2};
632 rb.Push(result);
633}
634
635std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) {
636 for (auto& device : devices) {
637 if (device->GetHandle() == handle) {
638 return device;
639 }
640 }
641 return std::nullopt;
642}
17 643
18} // namespace Service::NFP 644} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 519ff56ee..68c60ae82 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -3,14 +3,52 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/kernel_helpers.h"
6#include "core/hle/service/nfp/nfp.h" 7#include "core/hle/service/nfp/nfp.h"
8#include "core/hle/service/nfp/nfp_types.h"
7 9
8namespace Service::NFP { 10namespace Service::NFP {
11class NfpDevice;
9 12
10class NFP_User final : public Module::Interface { 13class IUser final : public ServiceFramework<IUser> {
11public: 14public:
12 explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_); 15 explicit IUser(Core::System& system_);
13 ~NFP_User() override; 16
17private:
18 void Initialize(Kernel::HLERequestContext& ctx);
19 void Finalize(Kernel::HLERequestContext& ctx);
20 void ListDevices(Kernel::HLERequestContext& ctx);
21 void StartDetection(Kernel::HLERequestContext& ctx);
22 void StopDetection(Kernel::HLERequestContext& ctx);
23 void Mount(Kernel::HLERequestContext& ctx);
24 void Unmount(Kernel::HLERequestContext& ctx);
25 void OpenApplicationArea(Kernel::HLERequestContext& ctx);
26 void GetApplicationArea(Kernel::HLERequestContext& ctx);
27 void SetApplicationArea(Kernel::HLERequestContext& ctx);
28 void Flush(Kernel::HLERequestContext& ctx);
29 void Restore(Kernel::HLERequestContext& ctx);
30 void CreateApplicationArea(Kernel::HLERequestContext& ctx);
31 void GetTagInfo(Kernel::HLERequestContext& ctx);
32 void GetRegisterInfo(Kernel::HLERequestContext& ctx);
33 void GetCommonInfo(Kernel::HLERequestContext& ctx);
34 void GetModelInfo(Kernel::HLERequestContext& ctx);
35 void AttachActivateEvent(Kernel::HLERequestContext& ctx);
36 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
37 void GetState(Kernel::HLERequestContext& ctx);
38 void GetDeviceState(Kernel::HLERequestContext& ctx);
39 void GetNpadId(Kernel::HLERequestContext& ctx);
40 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
41 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
42 void RecreateApplicationArea(Kernel::HLERequestContext& ctx);
43
44 std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
45
46 KernelHelpers::ServiceContext service_context;
47
48 std::array<std::shared_ptr<NfpDevice>, 10> devices{};
49
50 State state{State::NonInitialized};
51 Kernel::KEvent* availability_change_event;
14}; 52};
15 53
16} // namespace Service::NFP 54} // namespace Service::NFP