summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hid/emulated_controller.cpp6
-rw-r--r--src/core/hid/emulated_controller.h11
-rw-r--r--src/core/hle/kernel/k_page_table.cpp5
-rw-r--r--src/core/hle/service/nfp/nfp.cpp916
-rw-r--r--src/core/hle/service/nfp/nfp.h242
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/main.cpp20
-rw-r--r--src/yuzu/main.ui2
8 files changed, 951 insertions, 257 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 2bee173b3..7e05666d6 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -885,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
885 return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); 885 return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
886} 886}
887 887
888bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
889 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
890 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
891 return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
892}
893
888void EmulatedController::SetLedPattern() { 894void EmulatedController::SetLedPattern() {
889 for (auto& device : output_devices) { 895 for (auto& device : output_devices) {
890 if (!device) { 896 if (!device) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index d8642c5b3..aa52f9572 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -299,16 +299,23 @@ public:
299 299
300 /** 300 /**
301 * Sends a specific vibration to the output device 301 * Sends a specific vibration to the output device
302 * @return returns true if vibration had no errors 302 * @return true if vibration had no errors
303 */ 303 */
304 bool SetVibration(std::size_t device_index, VibrationValue vibration); 304 bool SetVibration(std::size_t device_index, VibrationValue vibration);
305 305
306 /** 306 /**
307 * Sends a small vibration to the output device 307 * Sends a small vibration to the output device
308 * @return returns true if SetVibration was successfull 308 * @return true if SetVibration was successfull
309 */ 309 */
310 bool TestVibration(std::size_t device_index); 310 bool TestVibration(std::size_t device_index);
311 311
312 /**
313 * Sets the desired data to be polled from a controller
314 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
315 * @return true if SetPollingMode was successfull
316 */
317 bool SetPollingMode(Common::Input::PollingMode polling_mode);
318
312 /// Returns the led pattern corresponding to this emulated controller 319 /// Returns the led pattern corresponding to this emulated controller
313 LedPattern GetLedPattern() const; 320 LedPattern GetLedPattern() const;
314 321
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index cbb0fedde..88aa2a152 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -986,9 +986,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
986 VAddr cur_addr{addr}; 986 VAddr cur_addr{addr};
987 987
988 for (const auto& node : page_linked_list.Nodes()) { 988 for (const auto& node : page_linked_list.Nodes()) {
989 const std::size_t num_pages{(addr - cur_addr) / PageSize}; 989 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
990 if (const auto result{ 990 OperationType::Unmap)};
991 Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
992 result.IsError()) { 991 result.IsError()) {
993 return result; 992 return result;
994 } 993 }
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 761d0d3c6..513107715 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -7,6 +7,9 @@
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hid/emulated_controller.h"
11#include "core/hid/hid_core.h"
12#include "core/hid/hid_types.h"
10#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
12#include "core/hle/service/nfp/nfp.h" 15#include "core/hle/service/nfp/nfp.h"
@@ -14,343 +17,790 @@
14 17
15namespace Service::NFP { 18namespace Service::NFP {
16namespace ErrCodes { 19namespace ErrCodes {
17constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); 20constexpr ResultCode DeviceNotFound(ErrorModule::NFP, 64);
21constexpr ResultCode WrongDeviceState(ErrorModule::NFP, 73);
22constexpr ResultCode ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
23constexpr ResultCode ApplicationAreaExist(ErrorModule::NFP, 168);
18} // namespace ErrCodes 24} // namespace ErrCodes
19 25
20Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, 26constexpr u32 ApplicationAreaSize = 0xD8;
21 const char* name) 27
22 : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, 28IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_)
23 "NFP::IUser"} { 29 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name},
24 nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); 30 nfp_interface{nfp_interface_} {
25} 31 static const FunctionInfo functions[] = {
26 32 {0, &IUser::Initialize, "Initialize"},
27Module::Interface::~Interface() { 33 {1, &IUser::Finalize, "Finalize"},
28 service_context.CloseEvent(nfc_tag_load); 34 {2, &IUser::ListDevices, "ListDevices"},
29} 35 {3, &IUser::StartDetection, "StartDetection"},
30 36 {4, &IUser::StopDetection, "StopDetection"},
31class IUser final : public ServiceFramework<IUser> { 37 {5, &IUser::Mount, "Mount"},
32public: 38 {6, &IUser::Unmount, "Unmount"},
33 explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, 39 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
34 KernelHelpers::ServiceContext& service_context_) 40 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
35 : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, 41 {9, &IUser::SetApplicationArea, "SetApplicationArea"},
36 service_context{service_context_} { 42 {10, nullptr, "Flush"},
37 static const FunctionInfo functions[] = { 43 {11, nullptr, "Restore"},
38 {0, &IUser::Initialize, "Initialize"}, 44 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
39 {1, &IUser::Finalize, "Finalize"}, 45 {13, &IUser::GetTagInfo, "GetTagInfo"},
40 {2, &IUser::ListDevices, "ListDevices"}, 46 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
41 {3, &IUser::StartDetection, "StartDetection"}, 47 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
42 {4, &IUser::StopDetection, "StopDetection"}, 48 {16, &IUser::GetModelInfo, "GetModelInfo"},
43 {5, &IUser::Mount, "Mount"}, 49 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
44 {6, &IUser::Unmount, "Unmount"}, 50 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
45 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, 51 {19, &IUser::GetState, "GetState"},
46 {8, &IUser::GetApplicationArea, "GetApplicationArea"}, 52 {20, &IUser::GetDeviceState, "GetDeviceState"},
47 {9, nullptr, "SetApplicationArea"}, 53 {21, &IUser::GetNpadId, "GetNpadId"},
48 {10, nullptr, "Flush"}, 54 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
49 {11, nullptr, "Restore"}, 55 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
50 {12, nullptr, "CreateApplicationArea"}, 56 {24, nullptr, "RecreateApplicationArea"},
51 {13, &IUser::GetTagInfo, "GetTagInfo"}, 57 };
52 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, 58 RegisterHandlers(functions);
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, nullptr, "RecreateApplicationArea"},
63 };
64 RegisterHandlers(functions);
65 59
66 deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); 60 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
67 availability_change_event = 61}
68 service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent");
69 }
70 62
71 ~IUser() override { 63void IUser::Initialize(Kernel::HLERequestContext& ctx) {
72 service_context.CloseEvent(deactivate_event); 64 LOG_INFO(Service_NFC, "called");
73 service_context.CloseEvent(availability_change_event);
74 }
75 65
76private: 66 state = State::Initialized;
77 struct TagInfo {
78 std::array<u8, 10> uuid;
79 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
80 // mean something else
81 std::array<u8, 0x15> padding_1;
82 u32_le protocol;
83 u32_le tag_type;
84 std::array<u8, 0x2c> padding_2;
85 };
86 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
87 67
88 enum class State : u32 { 68 // TODO(german77): Loop through all interfaces
89 NonInitialized = 0, 69 nfp_interface.Initialize();
90 Initialized = 1,
91 };
92 70
93 enum class DeviceState : u32 { 71 IPC::ResponseBuilder rb{ctx, 2, 0};
94 Initialized = 0, 72 rb.Push(ResultSuccess);
95 SearchingForTag = 1, 73}
96 TagFound = 2,
97 TagRemoved = 3,
98 TagNearby = 4,
99 Unknown5 = 5,
100 Finalized = 6
101 };
102 74
103 struct CommonInfo { 75void IUser::Finalize(Kernel::HLERequestContext& ctx) {
104 u16_be last_write_year; 76 LOG_INFO(Service_NFP, "called");
105 u8 last_write_month;
106 u8 last_write_day;
107 u16_be write_counter;
108 u16_be version;
109 u32_be application_area_size;
110 INSERT_PADDING_BYTES(0x34);
111 };
112 static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
113 77
114 void Initialize(Kernel::HLERequestContext& ctx) { 78 state = State::NonInitialized;
115 LOG_DEBUG(Service_NFC, "called");
116 79
117 IPC::ResponseBuilder rb{ctx, 2, 0}; 80 // TODO(german77): Loop through all interfaces
118 rb.Push(ResultSuccess); 81 nfp_interface.Finalize();
119 82
120 state = State::Initialized; 83 IPC::ResponseBuilder rb{ctx, 2};
121 } 84 rb.Push(ResultSuccess);
85}
122 86
123 void GetState(Kernel::HLERequestContext& ctx) { 87void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
124 LOG_DEBUG(Service_NFC, "called"); 88 LOG_INFO(Service_NFP, "called");
125 89
126 IPC::ResponseBuilder rb{ctx, 3, 0}; 90 std::vector<u64> devices;
127 rb.Push(ResultSuccess); 91
128 rb.PushRaw<u32>(static_cast<u32>(state)); 92 // TODO(german77): Loop through all interfaces
93 devices.push_back(nfp_interface.GetHandle());
94
95 ctx.WriteBuffer(devices);
96
97 IPC::ResponseBuilder rb{ctx, 3};
98 rb.Push(ResultSuccess);
99 rb.Push(static_cast<s32>(devices.size()));
100}
101
102void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
103 IPC::RequestParser rp{ctx};
104 const auto device_handle{rp.Pop<u64>()};
105 const auto nfp_protocol{rp.Pop<s32>()};
106 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
107
108 // TODO(german77): Loop through all interfaces
109 if (device_handle == nfp_interface.GetHandle()) {
110 const auto result = nfp_interface.StartDetection(nfp_protocol);
111 IPC::ResponseBuilder rb{ctx, 2};
112 rb.Push(result);
113 return;
129 } 114 }
130 115
131 void ListDevices(Kernel::HLERequestContext& ctx) { 116 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
132 IPC::RequestParser rp{ctx};
133 const u32 array_size = rp.Pop<u32>();
134 LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
135 117
136 ctx.WriteBuffer(device_handle); 118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(ErrCodes::DeviceNotFound);
120}
137 121
138 IPC::ResponseBuilder rb{ctx, 3}; 122void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
139 rb.Push(ResultSuccess); 123 IPC::RequestParser rp{ctx};
140 rb.Push<u32>(1); 124 const auto device_handle{rp.Pop<u64>()};
125 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
126
127 // TODO(german77): Loop through all interfaces
128 if (device_handle == nfp_interface.GetHandle()) {
129 const auto result = nfp_interface.StopDetection();
130 IPC::ResponseBuilder rb{ctx, 2};
131 rb.Push(result);
132 return;
141 } 133 }
142 134
143 void GetNpadId(Kernel::HLERequestContext& ctx) { 135 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
144 IPC::RequestParser rp{ctx};
145 const u64 dev_handle = rp.Pop<u64>();
146 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
147 136
148 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(ResultSuccess); 138 rb.Push(ErrCodes::DeviceNotFound);
150 rb.Push<u32>(npad_id); 139}
140
141void IUser::Mount(Kernel::HLERequestContext& ctx) {
142 IPC::RequestParser rp{ctx};
143 const auto device_handle{rp.Pop<u64>()};
144 const auto model_type{rp.PopEnum<ModelType>()};
145 const auto mount_target{rp.PopEnum<MountTarget>()};
146 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
147 model_type, mount_target);
148
149 // TODO(german77): Loop through all interfaces
150 if (device_handle == nfp_interface.GetHandle()) {
151 const auto result = nfp_interface.Mount();
152 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(result);
154 return;
151 } 155 }
152 156
153 void AttachActivateEvent(Kernel::HLERequestContext& ctx) { 157 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
154 IPC::RequestParser rp{ctx};
155 const u64 dev_handle = rp.Pop<u64>();
156 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
157 158
158 IPC::ResponseBuilder rb{ctx, 2, 1}; 159 IPC::ResponseBuilder rb{ctx, 2};
159 rb.Push(ResultSuccess); 160 rb.Push(ErrCodes::DeviceNotFound);
160 rb.PushCopyObjects(nfp_interface.GetNFCEvent()); 161}
161 has_attached_handle = true; 162
163void IUser::Unmount(Kernel::HLERequestContext& ctx) {
164 IPC::RequestParser rp{ctx};
165 const auto device_handle{rp.Pop<u64>()};
166 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
167
168 // TODO(german77): Loop through all interfaces
169 if (device_handle == nfp_interface.GetHandle()) {
170 const auto result = nfp_interface.Unmount();
171 IPC::ResponseBuilder rb{ctx, 2};
172 rb.Push(result);
173 return;
162 } 174 }
163 175
164 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { 176 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
165 IPC::RequestParser rp{ctx};
166 const u64 dev_handle = rp.Pop<u64>();
167 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
168 177
169 IPC::ResponseBuilder rb{ctx, 2, 1}; 178 IPC::ResponseBuilder rb{ctx, 2};
170 rb.Push(ResultSuccess); 179 rb.Push(ErrCodes::DeviceNotFound);
171 rb.PushCopyObjects(deactivate_event->GetReadableEvent()); 180}
172 } 181
173 182void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
174 void StopDetection(Kernel::HLERequestContext& ctx) { 183 IPC::RequestParser rp{ctx};
175 LOG_DEBUG(Service_NFP, "called"); 184 const auto device_handle{rp.Pop<u64>()};
176 185 const auto access_id{rp.Pop<u32>()};
177 switch (device_state) { 186 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle,
178 case DeviceState::TagFound: 187 access_id);
179 case DeviceState::TagNearby: 188
180 deactivate_event->GetWritableEvent().Signal(); 189 // TODO(german77): Loop through all interfaces
181 device_state = DeviceState::Initialized; 190 if (device_handle == nfp_interface.GetHandle()) {
182 break; 191 const auto result = nfp_interface.OpenApplicationArea(access_id);
183 case DeviceState::SearchingForTag:
184 case DeviceState::TagRemoved:
185 device_state = DeviceState::Initialized;
186 break;
187 default:
188 break;
189 }
190 IPC::ResponseBuilder rb{ctx, 2}; 192 IPC::ResponseBuilder rb{ctx, 2};
191 rb.Push(ResultSuccess); 193 rb.Push(result);
194 return;
192 } 195 }
193 196
194 void GetDeviceState(Kernel::HLERequestContext& ctx) { 197 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
195 LOG_DEBUG(Service_NFP, "called"); 198
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ErrCodes::DeviceNotFound);
201}
202
203void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
204 IPC::RequestParser rp{ctx};
205 const auto device_handle{rp.Pop<u64>()};
206 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
196 207
208 // TODO(german77): Loop through all interfaces
209 if (device_handle == nfp_interface.GetHandle()) {
210 std::vector<u8> data{};
211 const auto result = nfp_interface.GetApplicationArea(data);
212 ctx.WriteBuffer(data);
197 IPC::ResponseBuilder rb{ctx, 3}; 213 IPC::ResponseBuilder rb{ctx, 3};
198 rb.Push(ResultSuccess); 214 rb.Push(result);
199 rb.Push<u32>(static_cast<u32>(device_state)); 215 rb.Push(static_cast<u32>(data.size()));
216 return;
200 } 217 }
201 218
202 void StartDetection(Kernel::HLERequestContext& ctx) { 219 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
203 LOG_DEBUG(Service_NFP, "called");
204 220
205 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { 221 IPC::ResponseBuilder rb{ctx, 2};
206 device_state = DeviceState::SearchingForTag; 222 rb.Push(ErrCodes::DeviceNotFound);
207 } 223}
224
225void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
226 IPC::RequestParser rp{ctx};
227 const auto device_handle{rp.Pop<u64>()};
228 const auto data{ctx.ReadBuffer()};
229 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle,
230 data.size());
231
232 // TODO(german77): Loop through all interfaces
233 if (device_handle == nfp_interface.GetHandle()) {
234 const auto result = nfp_interface.SetApplicationArea(data);
208 IPC::ResponseBuilder rb{ctx, 2}; 235 IPC::ResponseBuilder rb{ctx, 2};
209 rb.Push(ResultSuccess); 236 rb.Push(result);
237 return;
210 } 238 }
211 239
212 void GetTagInfo(Kernel::HLERequestContext& ctx) { 240 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
213 LOG_DEBUG(Service_NFP, "called");
214 241
242 IPC::ResponseBuilder rb{ctx, 2};
243 rb.Push(ErrCodes::DeviceNotFound);
244}
245
246void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
247 IPC::RequestParser rp{ctx};
248 const auto device_handle{rp.Pop<u64>()};
249 const auto access_id{rp.Pop<u32>()};
250 const auto data{ctx.ReadBuffer()};
251 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
252 device_handle, access_id, data.size());
253
254 // TODO(german77): Loop through all interfaces
255 if (device_handle == nfp_interface.GetHandle()) {
256 const auto result = nfp_interface.CreateApplicationArea(access_id, data);
215 IPC::ResponseBuilder rb{ctx, 2}; 257 IPC::ResponseBuilder rb{ctx, 2};
216 const auto& amiibo = nfp_interface.GetAmiiboBuffer(); 258 rb.Push(result);
217 const TagInfo tag_info{ 259 return;
218 .uuid = amiibo.uuid,
219 .uuid_length = static_cast<u8>(amiibo.uuid.size()),
220 .padding_1 = {},
221 .protocol = 1, // TODO(ogniK): Figure out actual values
222 .tag_type = 2,
223 .padding_2 = {},
224 };
225 ctx.WriteBuffer(tag_info);
226 rb.Push(ResultSuccess);
227 } 260 }
228 261
229 void Mount(Kernel::HLERequestContext& ctx) { 262 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
230 LOG_DEBUG(Service_NFP, "called"); 263
264 IPC::ResponseBuilder rb{ctx, 2};
265 rb.Push(ErrCodes::DeviceNotFound);
266}
267
268void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
269 IPC::RequestParser rp{ctx};
270 const auto device_handle{rp.Pop<u64>()};
271 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
231 272
232 device_state = DeviceState::TagNearby; 273 // TODO(german77): Loop through all interfaces
274 if (device_handle == nfp_interface.GetHandle()) {
275 TagInfo tag_info{};
276 const auto result = nfp_interface.GetTagInfo(tag_info);
277 ctx.WriteBuffer(tag_info);
233 IPC::ResponseBuilder rb{ctx, 2}; 278 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ResultSuccess); 279 rb.Push(result);
280 return;
235 } 281 }
236 282
237 void GetModelInfo(Kernel::HLERequestContext& ctx) { 283 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
238 LOG_DEBUG(Service_NFP, "called");
239 284
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(ErrCodes::DeviceNotFound);
287}
288
289void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
290 IPC::RequestParser rp{ctx};
291 const auto device_handle{rp.Pop<u64>()};
292 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
293
294 // TODO(german77): Loop through all interfaces
295 if (device_handle == nfp_interface.GetHandle()) {
296 RegisterInfo register_info{};
297 const auto result = nfp_interface.GetRegisterInfo(register_info);
298 ctx.WriteBuffer(register_info);
240 IPC::ResponseBuilder rb{ctx, 2}; 299 IPC::ResponseBuilder rb{ctx, 2};
241 const auto& amiibo = nfp_interface.GetAmiiboBuffer(); 300 rb.Push(result);
242 ctx.WriteBuffer(amiibo.model_info); 301 return;
243 rb.Push(ResultSuccess);
244 } 302 }
245 303
246 void Unmount(Kernel::HLERequestContext& ctx) { 304 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
247 LOG_DEBUG(Service_NFP, "called");
248 305
249 device_state = DeviceState::TagFound; 306 IPC::ResponseBuilder rb{ctx, 2};
307 rb.Push(ErrCodes::DeviceNotFound);
308}
250 309
310void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
311 IPC::RequestParser rp{ctx};
312 const auto device_handle{rp.Pop<u64>()};
313 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
314
315 // TODO(german77): Loop through all interfaces
316 if (device_handle == nfp_interface.GetHandle()) {
317 CommonInfo common_info{};
318 const auto result = nfp_interface.GetCommonInfo(common_info);
319 ctx.WriteBuffer(common_info);
251 IPC::ResponseBuilder rb{ctx, 2}; 320 IPC::ResponseBuilder rb{ctx, 2};
252 rb.Push(ResultSuccess); 321 rb.Push(result);
322 return;
253 } 323 }
254 324
255 void Finalize(Kernel::HLERequestContext& ctx) { 325 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
256 LOG_DEBUG(Service_NFP, "called"); 326
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ErrCodes::DeviceNotFound);
329}
257 330
258 device_state = DeviceState::Finalized; 331void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
332 IPC::RequestParser rp{ctx};
333 const auto device_handle{rp.Pop<u64>()};
334 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
259 335
336 // TODO(german77): Loop through all interfaces
337 if (device_handle == nfp_interface.GetHandle()) {
338 ModelInfo model_info{};
339 const auto result = nfp_interface.GetModelInfo(model_info);
340 ctx.WriteBuffer(model_info);
260 IPC::ResponseBuilder rb{ctx, 2}; 341 IPC::ResponseBuilder rb{ctx, 2};
261 rb.Push(ResultSuccess); 342 rb.Push(result);
343 return;
262 } 344 }
263 345
264 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { 346 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
265 LOG_WARNING(Service_NFP, "(STUBBED) called"); 347
348 IPC::ResponseBuilder rb{ctx, 2};
349 rb.Push(ErrCodes::DeviceNotFound);
350}
266 351
352void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
353 IPC::RequestParser rp{ctx};
354 const auto device_handle{rp.Pop<u64>()};
355 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
356
357 // TODO(german77): Loop through all interfaces
358 if (device_handle == nfp_interface.GetHandle()) {
267 IPC::ResponseBuilder rb{ctx, 2, 1}; 359 IPC::ResponseBuilder rb{ctx, 2, 1};
268 rb.Push(ResultSuccess); 360 rb.Push(ResultSuccess);
269 rb.PushCopyObjects(availability_change_event->GetReadableEvent()); 361 rb.PushCopyObjects(nfp_interface.GetActivateEvent());
362 return;
270 } 363 }
271 364
272 void GetRegisterInfo(Kernel::HLERequestContext& ctx) { 365 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
273 LOG_WARNING(Service_NFP, "(STUBBED) called");
274 366
275 // TODO(ogniK): Pull Mii and owner data from amiibo 367 IPC::ResponseBuilder rb{ctx, 2};
368 rb.Push(ErrCodes::DeviceNotFound);
369}
276 370
277 IPC::ResponseBuilder rb{ctx, 2}; 371void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
372 IPC::RequestParser rp{ctx};
373 const auto device_handle{rp.Pop<u64>()};
374 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
375
376 // TODO(german77): Loop through all interfaces
377 if (device_handle == nfp_interface.GetHandle()) {
378 IPC::ResponseBuilder rb{ctx, 2, 1};
278 rb.Push(ResultSuccess); 379 rb.Push(ResultSuccess);
380 rb.PushCopyObjects(nfp_interface.GetDeactivateEvent());
381 return;
279 } 382 }
280 383
281 void GetCommonInfo(Kernel::HLERequestContext& ctx) { 384 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
282 LOG_WARNING(Service_NFP, "(STUBBED) called");
283 385
284 // TODO(ogniK): Pull common information from amiibo 386 IPC::ResponseBuilder rb{ctx, 2};
387 rb.Push(ErrCodes::DeviceNotFound);
388}
285 389
286 CommonInfo common_info{}; 390void IUser::GetState(Kernel::HLERequestContext& ctx) {
287 common_info.application_area_size = 0; 391 LOG_DEBUG(Service_NFC, "called");
288 ctx.WriteBuffer(common_info);
289 392
290 IPC::ResponseBuilder rb{ctx, 2}; 393 IPC::ResponseBuilder rb{ctx, 3, 0};
394 rb.Push(ResultSuccess);
395 rb.PushEnum(state);
396}
397
398void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
399 IPC::RequestParser rp{ctx};
400 const auto device_handle{rp.Pop<u64>()};
401 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
402
403 // TODO(german77): Loop through all interfaces
404 if (device_handle == nfp_interface.GetHandle()) {
405 IPC::ResponseBuilder rb{ctx, 3};
291 rb.Push(ResultSuccess); 406 rb.Push(ResultSuccess);
407 rb.PushEnum(nfp_interface.GetCurrentState());
408 return;
292 } 409 }
293 410
294 void OpenApplicationArea(Kernel::HLERequestContext& ctx) { 411 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
295 LOG_WARNING(Service_NFP, "(STUBBED) called");
296 IPC::ResponseBuilder rb{ctx, 2};
297 rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA);
298 }
299 412
300 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { 413 IPC::ResponseBuilder rb{ctx, 2};
301 LOG_WARNING(Service_NFP, "(STUBBED) called"); 414 rb.Push(ErrCodes::DeviceNotFound);
302 // We don't need to worry about this since we can just open the file 415}
416
417void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
418 IPC::RequestParser rp{ctx};
419 const auto device_handle{rp.Pop<u64>()};
420 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
421
422 // TODO(german77): Loop through all interfaces
423 if (device_handle == nfp_interface.GetHandle()) {
303 IPC::ResponseBuilder rb{ctx, 3}; 424 IPC::ResponseBuilder rb{ctx, 3};
304 rb.Push(ResultSuccess); 425 rb.Push(ResultSuccess);
305 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub 426 rb.PushEnum(nfp_interface.GetNpadId());
427 return;
306 } 428 }
307 429
308 void GetApplicationArea(Kernel::HLERequestContext& ctx) { 430 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
309 LOG_WARNING(Service_NFP, "(STUBBED) called"); 431
432 IPC::ResponseBuilder rb{ctx, 2};
433 rb.Push(ErrCodes::DeviceNotFound);
434}
310 435
311 // TODO(ogniK): Pull application area from amiibo 436void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
437 IPC::RequestParser rp{ctx};
438 const auto device_handle{rp.Pop<u64>()};
439 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
312 440
441 // TODO(german77): Loop through all interfaces
442 if (device_handle == nfp_interface.GetHandle()) {
313 IPC::ResponseBuilder rb{ctx, 3}; 443 IPC::ResponseBuilder rb{ctx, 3};
314 rb.Push(ResultSuccess); 444 rb.Push(ResultSuccess);
315 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub 445 rb.Push(ApplicationAreaSize);
446 return;
316 } 447 }
317 448
318 Module::Interface& nfp_interface; 449 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
319 KernelHelpers::ServiceContext& service_context; 450
451 IPC::ResponseBuilder rb{ctx, 2};
452 rb.Push(ErrCodes::DeviceNotFound);
453}
454
455void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
456 LOG_DEBUG(Service_NFP, "(STUBBED) called");
320 457
321 bool has_attached_handle{}; 458 IPC::ResponseBuilder rb{ctx, 2, 1};
322 const u64 device_handle{0}; // Npad device 1 459 rb.Push(ResultSuccess);
323 const u32 npad_id{0}; // Player 1 controller 460 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
324 State state{State::NonInitialized}; 461}
325 DeviceState device_state{DeviceState::Initialized}; 462
326 Kernel::KEvent* deactivate_event; 463Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
327 Kernel::KEvent* availability_change_event; 464 const char* name)
328}; 465 : ServiceFramework{system_, name}, module{std::move(module_)},
466 npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} {
467 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
468 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
469}
470
471Module::Interface::~Interface() = default;
329 472
330void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 473void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
331 LOG_DEBUG(Service_NFP, "called"); 474 LOG_DEBUG(Service_NFP, "called");
332 475
333 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 476 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
334 rb.Push(ResultSuccess); 477 rb.Push(ResultSuccess);
335 rb.PushIpcInterface<IUser>(*this, system, service_context); 478 rb.PushIpcInterface<IUser>(*this, system);
336} 479}
337 480
338bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { 481bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
339 if (buffer.size() < sizeof(AmiiboFile)) { 482 if (device_state != DeviceState::SearchingForTag) {
483 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
484 return false;
485 }
486
487 constexpr auto tag_size = sizeof(NTAG215File);
488 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
489
490 std::vector<u8> amiibo_buffer = buffer;
491
492 if (amiibo_buffer.size() < tag_size_without_password) {
493 LOG_ERROR(Service_NFP, "Wrong file size {}", buffer.size());
494 return false;
495 }
496
497 // Ensure it has the correct size
498 if (amiibo_buffer.size() != tag_size) {
499 amiibo_buffer.resize(tag_size, 0);
500 }
501
502 LOG_INFO(Service_NFP, "Amiibo detected");
503 std::memcpy(&tag_data, buffer.data(), tag_size);
504
505 if (!IsAmiiboValid()) {
340 return false; 506 return false;
341 } 507 }
342 508
343 std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); 509 // This value can't be dumped from a tag. Generate it
344 nfc_tag_load->GetWritableEvent().Signal(); 510 tag_data.password.PWD = GetTagPassword(tag_data.uuid);
511
512 device_state = DeviceState::TagFound;
513 activate_event->GetWritableEvent().Signal();
345 return true; 514 return true;
346} 515}
347 516
348Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { 517void Module::Interface::CloseAmiibo() {
349 return nfc_tag_load->GetReadableEvent(); 518 LOG_INFO(Service_NFP, "Remove amiibo");
519 device_state = DeviceState::TagRemoved;
520 is_application_area_initialized = false;
521 application_area_id = 0;
522 application_area_data.clear();
523 deactivate_event->GetWritableEvent().Signal();
524}
525
526bool Module::Interface::IsAmiiboValid() const {
527 const auto& amiibo_data = tag_data.user_memory;
528 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", tag_data.lock_bytes);
529 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", tag_data.compability_container);
530 LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo_data.crypto_init);
531 LOG_DEBUG(Service_NFP, "write_count={}", amiibo_data.write_count);
532
533 LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
534 LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
535 LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
536 LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number);
537 LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
538 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.fixed);
539
540 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", tag_data.dynamic_lock);
541 LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", tag_data.CFG0);
542 LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", tag_data.CFG1);
543
544 // Check against all know constants on an amiibo binary
545 if (tag_data.lock_bytes != 0xE00F) {
546 return false;
547 }
548 if (tag_data.compability_container != 0xEEFF10F1U) {
549 return false;
550 }
551 if ((amiibo_data.crypto_init & 0xFF) != 0xA5) {
552 return false;
553 }
554 if (amiibo_data.model_info.fixed != 0x02) {
555 return false;
556 }
557 if ((tag_data.dynamic_lock & 0xFFFFFF) != 0x0F0001) {
558 return false;
559 }
560 if (tag_data.CFG0 != 0x04000000U) {
561 return false;
562 }
563 if (tag_data.CFG1 != 0x5F) {
564 return false;
565 }
566 return true;
567}
568
569Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const {
570 return activate_event->GetReadableEvent();
571}
572
573Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const {
574 return deactivate_event->GetReadableEvent();
575}
576
577void Module::Interface::Initialize() {
578 device_state = DeviceState::Initialized;
579}
580
581void Module::Interface::Finalize() {
582 device_state = DeviceState::Unaviable;
583 is_application_area_initialized = false;
584 application_area_id = 0;
585 application_area_data.clear();
586}
587
588ResultCode Module::Interface::StartDetection(s32 protocol_) {
589 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
590
591 // TODO(german77): Add callback for when nfc data is available
592
593 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
594 npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
595 device_state = DeviceState::SearchingForTag;
596 protocol = protocol_;
597 return ResultSuccess;
598 }
599
600 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
601 return ErrCodes::WrongDeviceState;
602}
603
604ResultCode Module::Interface::StopDetection() {
605 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
606 npad_device->SetPollingMode(Common::Input::PollingMode::Active);
607
608 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
609 CloseAmiibo();
610 return ResultSuccess;
611 }
612 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
613 device_state = DeviceState::Initialized;
614 return ResultSuccess;
615 }
616
617 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
618 return ErrCodes::WrongDeviceState;
619}
620
621ResultCode Module::Interface::Mount() {
622 if (device_state == DeviceState::TagFound) {
623 device_state = DeviceState::TagMounted;
624 return ResultSuccess;
625 }
626
627 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
628 return ErrCodes::WrongDeviceState;
629}
630
631ResultCode Module::Interface::Unmount() {
632 if (device_state == DeviceState::TagMounted) {
633 is_application_area_initialized = false;
634 application_area_id = 0;
635 application_area_data.clear();
636 device_state = DeviceState::TagFound;
637 return ResultSuccess;
638 }
639
640 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
641 return ErrCodes::WrongDeviceState;
642}
643
644ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const {
645 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
646 tag_info = {
647 .uuid = tag_data.uuid,
648 .uuid_length = static_cast<u8>(tag_data.uuid.size()),
649 .protocol = protocol,
650 .tag_type = static_cast<u32>(tag_data.user_memory.model_info.amiibo_type),
651 };
652 return ResultSuccess;
653 }
654
655 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
656 return ErrCodes::WrongDeviceState;
657}
658
659ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
660 if (device_state != DeviceState::TagMounted) {
661 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
662 return ErrCodes::WrongDeviceState;
663 }
664
665 // Read this data from the amiibo save file
666 common_info = {
667 .last_write_year = 2022,
668 .last_write_month = 2,
669 .last_write_day = 7,
670 .write_counter = tag_data.user_memory.write_count,
671 .version = 1,
672 .application_area_size = ApplicationAreaSize,
673 };
674 return ResultSuccess;
675}
676
677ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const {
678 if (device_state != DeviceState::TagMounted) {
679 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
680 return ErrCodes::WrongDeviceState;
681 }
682
683 model_info = tag_data.user_memory.model_info;
684 return ResultSuccess;
685}
686
687ResultCode Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
688 if (device_state != DeviceState::TagMounted) {
689 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
690 return ErrCodes::WrongDeviceState;
691 }
692
693 Service::Mii::MiiManager manager;
694
695 // Read this data from the amiibo save file
696 register_info = {
697 .mii_char_info = manager.BuildDefault(0),
698 .first_write_year = 2022,
699 .first_write_month = 2,
700 .first_write_day = 7,
701 .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0},
702 .unknown = {},
703 };
704 return ResultSuccess;
705}
706
707ResultCode Module::Interface::OpenApplicationArea(u32 access_id) {
708 if (device_state != DeviceState::TagMounted) {
709 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
710 return ErrCodes::WrongDeviceState;
711 }
712 if (AmiiboApplicationDataExist(access_id)) {
713 application_area_data = LoadAmiiboApplicationData(access_id);
714 application_area_id = access_id;
715 is_application_area_initialized = true;
716 }
717 if (!is_application_area_initialized) {
718 LOG_WARNING(Service_NFP, "Application area is not initialized");
719 return ErrCodes::ApplicationAreaIsNotInitialized;
720 }
721 return ResultSuccess;
722}
723
724ResultCode Module::Interface::GetApplicationArea(std::vector<u8>& data) const {
725 if (device_state != DeviceState::TagMounted) {
726 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
727 return ErrCodes::WrongDeviceState;
728 }
729 if (!is_application_area_initialized) {
730 LOG_ERROR(Service_NFP, "Application area is not initialized");
731 return ErrCodes::ApplicationAreaIsNotInitialized;
732 }
733
734 data = application_area_data;
735
736 return ResultSuccess;
737}
738
739ResultCode Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
740 if (device_state != DeviceState::TagMounted) {
741 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
742 return ErrCodes::WrongDeviceState;
743 }
744 if (!is_application_area_initialized) {
745 LOG_ERROR(Service_NFP, "Application area is not initialized");
746 return ErrCodes::ApplicationAreaIsNotInitialized;
747 }
748 application_area_data = data;
749 SaveAmiiboApplicationData(application_area_id, application_area_data);
750 return ResultSuccess;
751}
752
753ResultCode Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
754 if (device_state != DeviceState::TagMounted) {
755 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
756 return ErrCodes::WrongDeviceState;
757 }
758 if (AmiiboApplicationDataExist(access_id)) {
759 LOG_ERROR(Service_NFP, "Application area already exist");
760 return ErrCodes::ApplicationAreaExist;
761 }
762 application_area_data = data;
763 application_area_id = access_id;
764 SaveAmiiboApplicationData(application_area_id, application_area_data);
765 return ResultSuccess;
766}
767
768bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const {
769 // TODO(german77): Check if file exist
770 return false;
771}
772
773std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const {
774 // TODO(german77): Read file
775 std::vector<u8> data(ApplicationAreaSize);
776 return data;
777}
778
779void Module::Interface::SaveAmiiboApplicationData(u32 access_id,
780 const std::vector<u8>& data) const {
781 // TODO(german77): Save file
782}
783
784u64 Module::Interface::GetHandle() const {
785 // Generate a handle based of the npad id
786 return static_cast<u64>(npad_id);
787}
788
789DeviceState Module::Interface::GetCurrentState() const {
790 return device_state;
791}
792
793Core::HID::NpadIdType Module::Interface::GetNpadId() const {
794 return npad_id;
350} 795}
351 796
352const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { 797u32 Module::Interface::GetTagPassword(const TagUuid& uuid) const {
353 return amiibo; 798 // Verifiy that the generated password is correct
799 u32 password = 0xAA ^ (uuid[1] ^ uuid[3]);
800 password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8;
801 password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16;
802 password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24;
803 return password;
354} 804}
355 805
356void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 806void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 95c127efb..022f13b29 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -7,15 +7,132 @@
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9 9
10#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h" 11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/mii/mii_manager.h"
11#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
12 14
13namespace Kernel { 15namespace Kernel {
14class KEvent; 16class KEvent;
15} 17class KReadableEvent;
18} // namespace Kernel
19
20namespace Core::HID {
21enum class NpadIdType : u32;
22} // namespace Core::HID
16 23
17namespace Service::NFP { 24namespace Service::NFP {
18 25
26enum class ServiceType : u32 {
27 User,
28 Debug,
29 System,
30};
31
32enum class State : u32 {
33 NonInitialized,
34 Initialized,
35};
36
37enum class DeviceState : u32 {
38 Initialized,
39 SearchingForTag,
40 TagFound,
41 TagRemoved,
42 TagMounted,
43 Unaviable,
44 Finalized,
45};
46
47enum class ModelType : u32 {
48 Amiibo,
49};
50
51enum class MountTarget : u32 {
52 Rom,
53 Ram,
54 All,
55};
56
57enum class AmiiboType : u8 {
58 Figure,
59 Card,
60 Yarn,
61};
62
63enum class AmiiboSeries : u8 {
64 SuperSmashBros,
65 SuperMario,
66 ChibiRobo,
67 YoshiWoollyWorld,
68 Splatoon,
69 AnimalCrossing,
70 EightBitMario,
71 Skylanders,
72 Unknown8,
73 TheLegendOfZelda,
74 ShovelKnight,
75 Unknown11,
76 Kiby,
77 Pokemon,
78 MarioSportsSuperstars,
79 MonsterHunter,
80 BoxBoy,
81 Pikmin,
82 FireEmblem,
83 Metroid,
84 Others,
85 MegaMan,
86 Diablo
87};
88
89using TagUuid = std::array<u8, 10>;
90
91struct TagInfo {
92 TagUuid uuid;
93 u8 uuid_length;
94 INSERT_PADDING_BYTES(0x15);
95 s32 protocol;
96 u32 tag_type;
97 INSERT_PADDING_BYTES(0x30);
98};
99static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
100
101struct CommonInfo {
102 u16 last_write_year;
103 u8 last_write_month;
104 u8 last_write_day;
105 u16 write_counter;
106 u16 version;
107 u32 application_area_size;
108 INSERT_PADDING_BYTES(0x34);
109};
110static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
111
112struct ModelInfo {
113 u16 character_id;
114 u8 character_variant;
115 AmiiboType amiibo_type;
116 u16 model_number;
117 AmiiboSeries series;
118 u8 fixed; // Must be 02
119 INSERT_PADDING_BYTES(0x4); // Unknown
120 INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash
121 INSERT_PADDING_BYTES(0x14); // SHA256-HMAC
122};
123static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
124
125struct RegisterInfo {
126 Service::Mii::MiiInfo mii_char_info;
127 u16 first_write_year;
128 u8 first_write_month;
129 u8 first_write_day;
130 std::array<u8, 11> amiibo_name;
131 u8 unknown;
132 INSERT_PADDING_BYTES(0x98);
133};
134static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
135
19class Module final { 136class Module final {
20public: 137public:
21 class Interface : public ServiceFramework<Interface> { 138 class Interface : public ServiceFramework<Interface> {
@@ -24,34 +141,131 @@ public:
24 const char* name); 141 const char* name);
25 ~Interface() override; 142 ~Interface() override;
26 143
27 struct ModelInfo { 144 struct EncryptedAmiiboFile {
28 std::array<u8, 0x8> amiibo_identification_block; 145 u16 crypto_init; // Must be A5 XX
29 INSERT_PADDING_BYTES(0x38); 146 u16 write_count; // Number of times the amiibo has been written?
147 INSERT_PADDING_BYTES(0x20); // System crypts
148 INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash
149 ModelInfo model_info; // This struct is bigger than documentation
150 INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
151 INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
152 INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer
153 };
154 static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
155
156 struct NTAG215Password {
157 u32 PWD; // Password to allow write access
158 u16 PACK; // Password acknowledge reply
159 u16 RFUI; // Reserved for future use
30 }; 160 };
31 static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); 161 static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
32 162
33 struct AmiiboFile { 163 struct NTAG215File {
34 std::array<u8, 10> uuid; 164 TagUuid uuid; // Unique serial number
35 INSERT_PADDING_BYTES(0x4a); 165 u16 lock_bytes; // Set defined pages as read only
36 ModelInfo model_info; 166 u32 compability_container; // Defines available memory
167 EncryptedAmiiboFile user_memory; // Writable data
168 u32 dynamic_lock; // Dynamic lock
169 u32 CFG0; // Defines memory protected by password
170 u32 CFG1; // Defines number of verification attempts
171 NTAG215Password password; // Password data
37 }; 172 };
38 static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); 173 static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
39 174
40 void CreateUserInterface(Kernel::HLERequestContext& ctx); 175 void CreateUserInterface(Kernel::HLERequestContext& ctx);
41 bool LoadAmiibo(const std::vector<u8>& buffer); 176 bool LoadAmiibo(const std::vector<u8>& buffer);
42 Kernel::KReadableEvent& GetNFCEvent(); 177 void CloseAmiibo();
43 const AmiiboFile& GetAmiiboBuffer() const; 178
179 void Initialize();
180 void Finalize();
181
182 ResultCode StartDetection(s32 protocol_);
183 ResultCode StopDetection();
184 ResultCode Mount();
185 ResultCode Unmount();
186
187 ResultCode GetTagInfo(TagInfo& tag_info) const;
188 ResultCode GetCommonInfo(CommonInfo& common_info) const;
189 ResultCode GetModelInfo(ModelInfo& model_info) const;
190 ResultCode GetRegisterInfo(RegisterInfo& register_info) const;
191
192 ResultCode OpenApplicationArea(u32 access_id);
193 ResultCode GetApplicationArea(std::vector<u8>& data) const;
194 ResultCode SetApplicationArea(const std::vector<u8>& data);
195 ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
196
197 u64 GetHandle() const;
198 DeviceState GetCurrentState() const;
199 Core::HID::NpadIdType GetNpadId() const;
200
201 Kernel::KReadableEvent& GetActivateEvent() const;
202 Kernel::KReadableEvent& GetDeactivateEvent() const;
44 203
45 protected: 204 protected:
46 std::shared_ptr<Module> module; 205 std::shared_ptr<Module> module;
47 206
48 private: 207 private:
208 /// Validates that the amiibo file is not corrupted
209 bool IsAmiiboValid() const;
210
211 bool AmiiboApplicationDataExist(u32 access_id) const;
212 std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
213 void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
214
215 /// return password needed to allow write access to protected memory
216 u32 GetTagPassword(const TagUuid& uuid) const;
217
218 const Core::HID::NpadIdType npad_id;
219
220 DeviceState device_state{DeviceState::Unaviable};
49 KernelHelpers::ServiceContext service_context; 221 KernelHelpers::ServiceContext service_context;
50 Kernel::KEvent* nfc_tag_load; 222 Kernel::KEvent* activate_event;
51 AmiiboFile amiibo{}; 223 Kernel::KEvent* deactivate_event;
224 NTAG215File tag_data{};
225 s32 protocol;
226 bool is_application_area_initialized{};
227 u32 application_area_id;
228 std::vector<u8> application_area_data;
52 }; 229 };
53}; 230};
54 231
232class IUser final : public ServiceFramework<IUser> {
233public:
234 explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
235
236private:
237 void Initialize(Kernel::HLERequestContext& ctx);
238 void Finalize(Kernel::HLERequestContext& ctx);
239 void ListDevices(Kernel::HLERequestContext& ctx);
240 void StartDetection(Kernel::HLERequestContext& ctx);
241 void StopDetection(Kernel::HLERequestContext& ctx);
242 void Mount(Kernel::HLERequestContext& ctx);
243 void Unmount(Kernel::HLERequestContext& ctx);
244 void OpenApplicationArea(Kernel::HLERequestContext& ctx);
245 void GetApplicationArea(Kernel::HLERequestContext& ctx);
246 void SetApplicationArea(Kernel::HLERequestContext& ctx);
247 void CreateApplicationArea(Kernel::HLERequestContext& ctx);
248 void GetTagInfo(Kernel::HLERequestContext& ctx);
249 void GetRegisterInfo(Kernel::HLERequestContext& ctx);
250 void GetCommonInfo(Kernel::HLERequestContext& ctx);
251 void GetModelInfo(Kernel::HLERequestContext& ctx);
252 void AttachActivateEvent(Kernel::HLERequestContext& ctx);
253 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
254 void GetState(Kernel::HLERequestContext& ctx);
255 void GetDeviceState(Kernel::HLERequestContext& ctx);
256 void GetNpadId(Kernel::HLERequestContext& ctx);
257 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
258 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
259
260 KernelHelpers::ServiceContext service_context;
261
262 // TODO(german77): We should have a vector of interfaces
263 Module::Interface& nfp_interface;
264
265 State state{State::NonInitialized};
266 Kernel::KEvent* availability_change_event;
267};
268
55void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 269void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
56 270
57} // namespace Service::NFP 271} // namespace Service::NFP
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 9ee7992e7..d5ba86c03 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -77,13 +77,13 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
77 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 77 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
78 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 78 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
79 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 79 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
80 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
81 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 80 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
81 {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
82 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 82 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
83 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 83 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
84 {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
85 {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
86 {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 84 {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
85 {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
86 {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
87 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
88 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 88 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
89 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 3c2d7d080..e3fd38a02 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -934,7 +934,7 @@ void GMainWindow::InitializeHotkeys() {
934 hotkey_registry.LoadHotkeys(); 934 hotkey_registry.LoadHotkeys();
935 935
936 LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); 936 LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
937 LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo")); 937 LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo"));
938 LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); 938 LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu"));
939 LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); 939 LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation"));
940 LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); 940 LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation"));
@@ -2927,6 +2927,24 @@ void GMainWindow::OnLoadAmiibo() {
2927 return; 2927 return;
2928 } 2928 }
2929 2929
2930 Service::SM::ServiceManager& sm = system->ServiceManager();
2931 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
2932 if (nfc == nullptr) {
2933 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
2934 return;
2935 }
2936 const auto nfc_state = nfc->GetCurrentState();
2937 if (nfc_state == Service::NFP::DeviceState::TagFound ||
2938 nfc_state == Service::NFP::DeviceState::TagMounted) {
2939 nfc->CloseAmiibo();
2940 return;
2941 }
2942
2943 if (nfc_state != Service::NFP::DeviceState::SearchingForTag) {
2944 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
2945 return;
2946 }
2947
2930 is_amiibo_file_select_active = true; 2948 is_amiibo_file_select_active = true;
2931 const QString extensions{QStringLiteral("*.bin")}; 2949 const QString extensions{QStringLiteral("*.bin")};
2932 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); 2950 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 5719b2ee4..6ab95b9a5 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -266,7 +266,7 @@
266 <bool>false</bool> 266 <bool>false</bool>
267 </property> 267 </property>
268 <property name="text"> 268 <property name="text">
269 <string>Load &amp;Amiibo...</string> 269 <string>Load/Remove &amp;Amiibo...</string>
270 </property> 270 </property>
271 </action> 271 </action>
272 <action name="action_Report_Compatibility"> 272 <action name="action_Report_Compatibility">