summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2022-10-02 14:28:43 -0700
committerGravatar GitHub2022-10-02 14:28:43 -0700
commit61399de5dbc64ada1c9e5ec3d84be76c215fdc0c (patch)
tree72313f1b749d0771e38dbfcf539121a02d4b9ae0
parentMerge pull request #8992 from Morph1984/vi-vsync-event (diff)
parentservice: mii: Copy only valid name bytes (diff)
downloadyuzu-61399de5dbc64ada1c9e5ec3d84be76c215fdc0c.tar.gz
yuzu-61399de5dbc64ada1c9e5ec3d84be76c215fdc0c.tar.xz
yuzu-61399de5dbc64ada1c9e5ec3d84be76c215fdc0c.zip
Merge pull request #8955 from german77/amiibo-rewrite
core: nfp: Rewrite implementation to remove direct access from the frontend
-rw-r--r--src/common/input.h27
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/hid/emulated_controller.cpp70
-rw-r--r--src/core/hid/emulated_controller.h34
-rw-r--r--src/core/hid/input_converter.cpp14
-rw-r--r--src/core/hid/input_converter.h8
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp161
-rw-r--r--src/core/hle/service/mii/mii_manager.h4
-rw-r--r--src/core/hle/service/nfc/nfc.cpp8
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp77
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h10
-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.cpp676
-rw-r--r--src/core/hle/service/nfp/nfp_device.h101
-rw-r--r--src/core/hle/service/nfp/nfp_result.h22
-rw-r--r--src/core/hle/service/nfp/nfp_types.h (renamed from src/core/hle/service/nfp/amiibo_types.h)116
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp634
-rw-r--r--src/core/hle/service/nfp/nfp_user.h44
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp101
-rw-r--r--src/input_common/drivers/virtual_amiibo.h61
-rw-r--r--src/input_common/input_engine.cpp37
-rw-r--r--src/input_common/input_engine.h17
-rw-r--r--src/input_common/input_poller.cpp64
-rw-r--r--src/input_common/input_poller.h10
-rw-r--r--src/input_common/main.cpp21
-rw-r--r--src/input_common/main.h7
-rw-r--r--src/yuzu/main.cpp51
29 files changed, 2303 insertions, 1333 deletions
diff --git a/src/common/input.h b/src/common/input.h
index 825b0d650..bfa0639f5 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -76,6 +76,19 @@ enum class PollingError {
76 Unknown, 76 Unknown,
77}; 77};
78 78
79// Nfc reply from the controller
80enum class NfcState {
81 Success,
82 NewAmiibo,
83 WaitingForAmiibo,
84 AmiiboRemoved,
85 NotAnAmiibo,
86 NotSupported,
87 WrongDeviceState,
88 WriteFailed,
89 Unknown,
90};
91
79// Ir camera reply from the controller 92// Ir camera reply from the controller
80enum class CameraError { 93enum class CameraError {
81 None, 94 None,
@@ -202,6 +215,11 @@ struct CameraStatus {
202 std::vector<u8> data{}; 215 std::vector<u8> data{};
203}; 216};
204 217
218struct NfcStatus {
219 NfcState state{};
220 std::vector<u8> data{};
221};
222
205// List of buttons to be passed to Qt that can be translated 223// List of buttons to be passed to Qt that can be translated
206enum class ButtonNames { 224enum class ButtonNames {
207 Undefined, 225 Undefined,
@@ -260,6 +278,7 @@ struct CallbackStatus {
260 BatteryStatus battery_status{}; 278 BatteryStatus battery_status{};
261 VibrationStatus vibration_status{}; 279 VibrationStatus vibration_status{};
262 CameraStatus camera_status{}; 280 CameraStatus camera_status{};
281 NfcStatus nfc_status{};
263}; 282};
264 283
265// Triggered once every input change 284// Triggered once every input change
@@ -312,6 +331,14 @@ public:
312 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { 331 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
313 return CameraError::NotSupported; 332 return CameraError::NotSupported;
314 } 333 }
334
335 virtual NfcState SupportsNfc() const {
336 return NfcState::NotSupported;
337 }
338
339 virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
340 return NfcState::NotSupported;
341 }
315}; 342};
316 343
317/// An abstract class template for a factory that can create input devices. 344/// An abstract class template for a factory that can create input devices.
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 81391f513..8e3fd4505 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -525,9 +525,12 @@ add_library(core STATIC
525 hle/service/nfc/nfc.h 525 hle/service/nfc/nfc.h
526 hle/service/nfp/amiibo_crypto.cpp 526 hle/service/nfp/amiibo_crypto.cpp
527 hle/service/nfp/amiibo_crypto.h 527 hle/service/nfp/amiibo_crypto.h
528 hle/service/nfp/amiibo_types.h
529 hle/service/nfp/nfp.cpp 528 hle/service/nfp/nfp.cpp
530 hle/service/nfp/nfp.h 529 hle/service/nfp/nfp.h
530 hle/service/nfp/nfp_device.cpp
531 hle/service/nfp/nfp_device.h
532 hle/service/nfp/nfp_result.h
533 hle/service/nfp/nfp_types.h
531 hle/service/nfp/nfp_user.cpp 534 hle/service/nfp/nfp_user.cpp
532 hle/service/nfp/nfp_user.h 535 hle/service/nfp/nfp_user.h
533 hle/service/ngct/ngct.cpp 536 hle/service/ngct/ngct.cpp
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 2cff279b1..e27d84734 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -131,13 +131,16 @@ void EmulatedController::LoadDevices() {
131 battery_params[RightIndex].Set("battery", true); 131 battery_params[RightIndex].Set("battery", true);
132 132
133 camera_params = Common::ParamPackage{"engine:camera,camera:1"}; 133 camera_params = Common::ParamPackage{"engine:camera,camera:1"};
134 nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
134 135
135 output_params[LeftIndex] = left_joycon; 136 output_params[LeftIndex] = left_joycon;
136 output_params[RightIndex] = right_joycon; 137 output_params[RightIndex] = right_joycon;
137 output_params[2] = camera_params; 138 output_params[2] = camera_params;
139 output_params[3] = nfc_params;
138 output_params[LeftIndex].Set("output", true); 140 output_params[LeftIndex].Set("output", true);
139 output_params[RightIndex].Set("output", true); 141 output_params[RightIndex].Set("output", true);
140 output_params[2].Set("output", true); 142 output_params[2].Set("output", true);
143 output_params[3].Set("output", true);
141 144
142 LoadTASParams(); 145 LoadTASParams();
143 146
@@ -155,6 +158,7 @@ void EmulatedController::LoadDevices() {
155 std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), 158 std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
156 Common::Input::CreateDevice<Common::Input::InputDevice>); 159 Common::Input::CreateDevice<Common::Input::InputDevice>);
157 camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params); 160 camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
161 nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params);
158 std::transform(output_params.begin(), output_params.end(), output_devices.begin(), 162 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
159 Common::Input::CreateDevice<Common::Input::OutputDevice>); 163 Common::Input::CreateDevice<Common::Input::OutputDevice>);
160 164
@@ -284,6 +288,16 @@ void EmulatedController::ReloadInput() {
284 camera_devices->ForceUpdate(); 288 camera_devices->ForceUpdate();
285 } 289 }
286 290
291 if (nfc_devices) {
292 if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) {
293 nfc_devices->SetCallback({
294 .on_change =
295 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
296 });
297 nfc_devices->ForceUpdate();
298 }
299 }
300
287 // Use a common UUID for TAS 301 // Use a common UUID for TAS
288 static constexpr Common::UUID TAS_UUID = Common::UUID{ 302 static constexpr Common::UUID TAS_UUID = Common::UUID{
289 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; 303 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -339,6 +353,8 @@ void EmulatedController::UnloadInput() {
339 for (auto& stick : tas_stick_devices) { 353 for (auto& stick : tas_stick_devices) {
340 stick.reset(); 354 stick.reset();
341 } 355 }
356 camera_devices.reset();
357 nfc_devices.reset();
342} 358}
343 359
344void EmulatedController::EnableConfiguration() { 360void EmulatedController::EnableConfiguration() {
@@ -903,6 +919,25 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
903 TriggerOnChange(ControllerTriggerType::IrSensor, true); 919 TriggerOnChange(ControllerTriggerType::IrSensor, true);
904} 920}
905 921
922void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
923 std::unique_lock lock{mutex};
924 controller.nfc_values = TransformToNfc(callback);
925
926 if (is_configuring) {
927 lock.unlock();
928 TriggerOnChange(ControllerTriggerType::Nfc, false);
929 return;
930 }
931
932 controller.nfc_state = {
933 controller.nfc_values.state,
934 controller.nfc_values.data,
935 };
936
937 lock.unlock();
938 TriggerOnChange(ControllerTriggerType::Nfc, true);
939}
940
906bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 941bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
907 if (device_index >= output_devices.size()) { 942 if (device_index >= output_devices.size()) {
908 return false; 943 return false;
@@ -980,6 +1015,10 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
980bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 1015bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
981 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); 1016 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
982 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1017 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1018 auto& nfc_output_device = output_devices[3];
1019
1020 nfc_output_device->SetPollingMode(polling_mode);
1021
983 return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; 1022 return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
984} 1023}
985 1024
@@ -1000,6 +1039,32 @@ bool EmulatedController::SetCameraFormat(
1000 camera_format)) == Common::Input::CameraError::None; 1039 camera_format)) == Common::Input::CameraError::None;
1001} 1040}
1002 1041
1042bool EmulatedController::HasNfc() const {
1043 const auto& nfc_output_device = output_devices[3];
1044
1045 switch (npad_type) {
1046 case NpadStyleIndex::JoyconRight:
1047 case NpadStyleIndex::JoyconDual:
1048 case NpadStyleIndex::ProController:
1049 break;
1050 default:
1051 return false;
1052 }
1053
1054 const bool has_virtual_nfc =
1055 npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld;
1056 const bool is_virtual_nfc_supported =
1057 nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported;
1058
1059 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
1060}
1061
1062bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1063 auto& nfc_output_device = output_devices[3];
1064
1065 return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
1066}
1067
1003void EmulatedController::SetLedPattern() { 1068void EmulatedController::SetLedPattern() {
1004 for (auto& device : output_devices) { 1069 for (auto& device : output_devices) {
1005 if (!device) { 1070 if (!device) {
@@ -1363,6 +1428,11 @@ const CameraState& EmulatedController::GetCamera() const {
1363 return controller.camera_state; 1428 return controller.camera_state;
1364} 1429}
1365 1430
1431const NfcState& EmulatedController::GetNfc() const {
1432 std::scoped_lock lock{mutex};
1433 return controller.nfc_state;
1434}
1435
1366NpadColor EmulatedController::GetNpadColor(u32 color) { 1436NpadColor EmulatedController::GetNpadColor(u32 color) {
1367 return { 1437 return {
1368 .r = static_cast<u8>((color >> 16) & 0xFF), 1438 .r = static_cast<u8>((color >> 16) & 0xFF),
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index c3aa8f9d3..319226bf8 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -20,7 +20,7 @@
20 20
21namespace Core::HID { 21namespace Core::HID {
22const std::size_t max_emulated_controllers = 2; 22const std::size_t max_emulated_controllers = 2;
23const std::size_t output_devices = 3; 23const std::size_t output_devices_size = 4;
24struct ControllerMotionInfo { 24struct ControllerMotionInfo {
25 Common::Input::MotionStatus raw_status{}; 25 Common::Input::MotionStatus raw_status{};
26 MotionInput emulated{}; 26 MotionInput emulated{};
@@ -37,7 +37,8 @@ using TriggerDevices =
37using BatteryDevices = 37using BatteryDevices =
38 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; 38 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
39using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; 39using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
40using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>; 40using NfcDevices = std::unique_ptr<Common::Input::InputDevice>;
41using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
41 42
42using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; 43using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
43using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; 44using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
@@ -45,7 +46,8 @@ using ControllerMotionParams = std::array<Common::ParamPackage, Settings::Native
45using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; 46using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
46using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; 47using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
47using CameraParams = Common::ParamPackage; 48using CameraParams = Common::ParamPackage;
48using OutputParams = std::array<Common::ParamPackage, output_devices>; 49using NfcParams = Common::ParamPackage;
50using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
49 51
50using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; 52using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
51using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; 53using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -55,6 +57,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native
55using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; 57using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
56using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; 58using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
57using CameraValues = Common::Input::CameraStatus; 59using CameraValues = Common::Input::CameraStatus;
60using NfcValues = Common::Input::NfcStatus;
58using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; 61using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
59 62
60struct AnalogSticks { 63struct AnalogSticks {
@@ -80,6 +83,11 @@ struct CameraState {
80 std::size_t sample{}; 83 std::size_t sample{};
81}; 84};
82 85
86struct NfcState {
87 Common::Input::NfcState state{};
88 std::vector<u8> data{};
89};
90
83struct ControllerMotion { 91struct ControllerMotion {
84 Common::Vec3f accel{}; 92 Common::Vec3f accel{};
85 Common::Vec3f gyro{}; 93 Common::Vec3f gyro{};
@@ -107,6 +115,7 @@ struct ControllerStatus {
107 BatteryValues battery_values{}; 115 BatteryValues battery_values{};
108 VibrationValues vibration_values{}; 116 VibrationValues vibration_values{};
109 CameraValues camera_values{}; 117 CameraValues camera_values{};
118 NfcValues nfc_values{};
110 119
111 // Data for HID serices 120 // Data for HID serices
112 HomeButtonState home_button_state{}; 121 HomeButtonState home_button_state{};
@@ -119,6 +128,7 @@ struct ControllerStatus {
119 ControllerColors colors_state{}; 128 ControllerColors colors_state{};
120 BatteryLevelState battery_state{}; 129 BatteryLevelState battery_state{};
121 CameraState camera_state{}; 130 CameraState camera_state{};
131 NfcState nfc_state{};
122}; 132};
123 133
124enum class ControllerTriggerType { 134enum class ControllerTriggerType {
@@ -130,6 +140,7 @@ enum class ControllerTriggerType {
130 Battery, 140 Battery,
131 Vibration, 141 Vibration,
132 IrSensor, 142 IrSensor,
143 Nfc,
133 Connected, 144 Connected,
134 Disconnected, 145 Disconnected,
135 Type, 146 Type,
@@ -315,6 +326,9 @@ public:
315 /// Returns the latest camera status from the controller 326 /// Returns the latest camera status from the controller
316 const CameraState& GetCamera() const; 327 const CameraState& GetCamera() const;
317 328
329 /// Returns the latest ntag status from the controller
330 const NfcState& GetNfc() const;
331
318 /** 332 /**
319 * Sends a specific vibration to the output device 333 * Sends a specific vibration to the output device
320 * @return true if vibration had no errors 334 * @return true if vibration had no errors
@@ -341,6 +355,12 @@ public:
341 */ 355 */
342 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); 356 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
343 357
358 /// Returns true if the device has nfc support
359 bool HasNfc() const;
360
361 /// Returns true if the nfc tag was written
362 bool WriteNfc(const std::vector<u8>& data);
363
344 /// Returns the led pattern corresponding to this emulated controller 364 /// Returns the led pattern corresponding to this emulated controller
345 LedPattern GetLedPattern() const; 365 LedPattern GetLedPattern() const;
346 366
@@ -425,6 +445,12 @@ private:
425 void SetCamera(const Common::Input::CallbackStatus& callback); 445 void SetCamera(const Common::Input::CallbackStatus& callback);
426 446
427 /** 447 /**
448 * Updates the nfc status of the controller
449 * @param callback A CallbackStatus containing the nfc status
450 */
451 void SetNfc(const Common::Input::CallbackStatus& callback);
452
453 /**
428 * Converts a color format from bgra to rgba 454 * Converts a color format from bgra to rgba
429 * @param color in bgra format 455 * @param color in bgra format
430 * @return NpadColor in rgba format 456 * @return NpadColor in rgba format
@@ -458,6 +484,7 @@ private:
458 TriggerParams trigger_params; 484 TriggerParams trigger_params;
459 BatteryParams battery_params; 485 BatteryParams battery_params;
460 CameraParams camera_params; 486 CameraParams camera_params;
487 NfcParams nfc_params;
461 OutputParams output_params; 488 OutputParams output_params;
462 489
463 ButtonDevices button_devices; 490 ButtonDevices button_devices;
@@ -466,6 +493,7 @@ private:
466 TriggerDevices trigger_devices; 493 TriggerDevices trigger_devices;
467 BatteryDevices battery_devices; 494 BatteryDevices battery_devices;
468 CameraDevices camera_devices; 495 CameraDevices camera_devices;
496 NfcDevices nfc_devices;
469 OutputDevices output_devices; 497 OutputDevices output_devices;
470 498
471 // TAS related variables 499 // TAS related variables
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 52fb69e9c..fe9915abe 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -287,6 +287,20 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu
287 return camera; 287 return camera;
288} 288}
289 289
290Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) {
291 Common::Input::NfcStatus nfc{};
292 switch (callback.type) {
293 case Common::Input::InputType::Nfc:
294 return callback.nfc_status;
295 break;
296 default:
297 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
298 break;
299 }
300
301 return nfc;
302}
303
290void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { 304void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
291 const auto& properties = analog.properties; 305 const auto& properties = analog.properties;
292 float& raw_value = analog.raw_value; 306 float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index 143c50cc0..b7eb6e660 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -85,6 +85,14 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
85Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); 85Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
86 86
87/** 87/**
88 * Converts raw input data into a valid nfc status.
89 *
90 * @param callback Supported callbacks: Nfc.
91 * @return A valid CameraObject object.
92 */
93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
94
95/**
88 * Converts raw analog data into a valid analog value 96 * Converts raw analog data into a valid analog value
89 * @param analog An analog object containing raw data and properties 97 * @param analog An analog object containing raw data and properties
90 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. 98 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index c484a9c8d..3a2fe938f 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -427,12 +427,11 @@ 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 if (!ValidateV3Info(mii_v3)) {
435 if (mii_v3.mii_name[0] == 0) {
436 return mii; 435 return mii;
437 } 436 }
438 437
@@ -443,8 +442,15 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
443 mii.height = mii_v3.height; 442 mii.height = mii_v3.height;
444 mii.build = mii_v3.build; 443 mii.build = mii_v3.build;
445 444
446 memset(mii.name.data(), 0, sizeof(mii.name)); 445 // Copy name until string terminator
447 memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name)); 446 mii.name = {};
447 for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
448 mii.name[index] = mii_v3.mii_name[index];
449 if (mii.name[index] == 0) {
450 break;
451 }
452 }
453
448 mii.font_region = mii_v3.region_information.character_set; 454 mii.font_region = mii_v3.region_information.character_set;
449 455
450 mii.faceline_type = mii_v3.appearance_bits1.face_shape; 456 mii.faceline_type = mii_v3.appearance_bits1.face_shape;
@@ -504,6 +510,151 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
504 return mii; 510 return mii;
505} 511}
506 512
513Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
514 Service::Mii::MiiManager manager;
515 Ver3StoreData mii_v3{};
516
517 // TODO: We are ignoring a bunch of data from the mii_v3
518
519 mii_v3.version = 1;
520 mii_v3.mii_information.gender.Assign(mii.gender);
521 mii_v3.mii_information.favorite_color.Assign(mii.favorite_color);
522 mii_v3.height = mii.height;
523 mii_v3.build = mii.build;
524
525 // Copy name until string terminator
526 mii_v3.mii_name = {};
527 for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
528 mii_v3.mii_name[index] = mii.name[index];
529 if (mii_v3.mii_name[index] == 0) {
530 break;
531 }
532 }
533
534 mii_v3.region_information.character_set.Assign(mii.font_region);
535
536 mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type);
537 mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color);
538 mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle);
539 mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make);
540
541 mii_v3.hair_style = mii.hair_type;
542 mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color);
543 mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip);
544
545 mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type);
546 mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color);
547 mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
548 mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
549 mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
550 mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x);
551 mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
552
553 mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type);
554 mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color);
555 mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale);
556 mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect);
557 mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
558 mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x);
559 mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y);
560
561 mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type);
562 mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale);
563 mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y);
564
565 mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type);
566 mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color);
567 mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
568 mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
569 mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
570
571 mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type);
572 mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale);
573 mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y);
574
575 mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type);
576 mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color);
577
578 mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type);
579 mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color);
580 mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale);
581 mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y);
582
583 mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type);
584 mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale);
585 mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
586 mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
587
588 // TODO: Validate mii_v3 data
589
590 return mii_v3;
591}
592
593bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
594 bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
595
596 is_valid = is_valid && (mii_v3.mii_name[0] != 0);
597
598 is_valid = is_valid && (mii_v3.mii_information.birth_month < 13);
599 is_valid = is_valid && (mii_v3.mii_information.birth_day < 32);
600 is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12);
601 is_valid = is_valid && (mii_v3.height < 128);
602 is_valid = is_valid && (mii_v3.build < 128);
603
604 is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12);
605 is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7);
606 is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12);
607 is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12);
608
609 is_valid = is_valid && (mii_v3.hair_style < 132);
610 is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8);
611
612 is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60);
613 is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6);
614 is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8);
615 is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7);
616 is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8);
617 is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13);
618 is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19);
619
620 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25);
621 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8);
622 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9);
623 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7);
624 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12);
625 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12);
626 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19);
627
628 is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18);
629 is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9);
630 is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19);
631
632 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36);
633 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5);
634 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9);
635 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7);
636 is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19);
637
638 is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6);
639 is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7);
640 is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17);
641
642 is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6);
643 is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8);
644
645 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9);
646 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6);
647 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8);
648 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21);
649
650 is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2);
651 is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9);
652 is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17);
653 is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31);
654
655 return is_valid;
656}
657
507ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { 658ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
508 std::vector<MiiInfoElement> result; 659 std::vector<MiiInfoElement> result;
509 660
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index d847de0bd..83ad3d343 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -22,7 +22,9 @@ 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;
27 bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
26 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); 28 ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
27 Result GetIndex(const CharInfo& info, u32& index); 29 Result GetIndex(const CharInfo& info, u32& index);
28 30
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 13a843a28..046c5f18f 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -106,10 +106,10 @@ public:
106 {1, &IUser::FinalizeOld, "FinalizeOld"}, 106 {1, &IUser::FinalizeOld, "FinalizeOld"},
107 {2, &IUser::GetStateOld, "GetStateOld"}, 107 {2, &IUser::GetStateOld, "GetStateOld"},
108 {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, 108 {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
109 {400, nullptr, "Initialize"}, 109 {400, &IUser::InitializeOld, "Initialize"},
110 {401, nullptr, "Finalize"}, 110 {401, &IUser::FinalizeOld, "Finalize"},
111 {402, nullptr, "GetState"}, 111 {402, &IUser::GetStateOld, "GetState"},
112 {403, nullptr, "IsNfcEnabled"}, 112 {403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"},
113 {404, nullptr, "ListDevices"}, 113 {404, nullptr, "ListDevices"},
114 {405, nullptr, "GetDeviceState"}, 114 {405, nullptr, "GetDeviceState"},
115 {406, nullptr, "GetNpadId"}, 115 {406, nullptr, "GetNpadId"},
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
index 31dd3a307..ce0bc3f75 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -20,13 +20,14 @@ 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}",
29 LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series); 29 static_cast<u16>(amiibo_data.model_info.model_number));
30 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); 31 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value);
31 32
32 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); 33 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock);
@@ -35,11 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
35 36
36 // Validate UUID 37 // Validate UUID
37 constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` 38 constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
38 if ((CT ^ ntag_file.uuid[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) { 39 if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) !=
40 ntag_file.uuid.uid[3]) {
39 return false; 41 return false;
40 } 42 }
41 if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) != 43 if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^
42 ntag_file.uuid[8]) { 44 ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) {
43 return false; 45 return false;
44 } 46 }
45 47
@@ -56,8 +58,9 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
56 if (amiibo_data.model_info.constant_value != 0x02) { 58 if (amiibo_data.model_info.constant_value != 0x02) {
57 return false; 59 return false;
58 } 60 }
59 // dynamic_lock value apparently is not constant 61 if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) {
60 // ntag_file.dynamic_lock == 0x0F0001 62 return false;
63 }
61 if (ntag_file.CFG0 != 0x04000000U) { 64 if (ntag_file.CFG0 != 0x04000000U) {
62 return false; 65 return false;
63 } 66 }
@@ -70,7 +73,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
70NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { 73NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
71 NTAG215File encoded_data{}; 74 NTAG215File encoded_data{};
72 75
73 memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, sizeof(encoded_data.uuid2)); 76 encoded_data.uid = nfc_data.uuid.uid;
77 encoded_data.nintendo_id = nfc_data.uuid.nintendo_id;
74 encoded_data.static_lock = nfc_data.static_lock; 78 encoded_data.static_lock = nfc_data.static_lock;
75 encoded_data.compability_container = nfc_data.compability_container; 79 encoded_data.compability_container = nfc_data.compability_container;
76 encoded_data.hmac_data = nfc_data.user_memory.hmac_data; 80 encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
@@ -82,10 +86,10 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
82 encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; 86 encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter;
83 encoded_data.application_area_id = nfc_data.user_memory.application_area_id; 87 encoded_data.application_area_id = nfc_data.user_memory.application_area_id;
84 encoded_data.unknown = nfc_data.user_memory.unknown; 88 encoded_data.unknown = nfc_data.user_memory.unknown;
85 encoded_data.hash = nfc_data.user_memory.hash; 89 encoded_data.unknown2 = nfc_data.user_memory.unknown2;
86 encoded_data.application_area = nfc_data.user_memory.application_area; 90 encoded_data.application_area = nfc_data.user_memory.application_area;
87 encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; 91 encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
88 memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), sizeof(encoded_data.uuid)); 92 encoded_data.lock_bytes = nfc_data.uuid.lock_bytes;
89 encoded_data.model_info = nfc_data.user_memory.model_info; 93 encoded_data.model_info = nfc_data.user_memory.model_info;
90 encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; 94 encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
91 encoded_data.dynamic_lock = nfc_data.dynamic_lock; 95 encoded_data.dynamic_lock = nfc_data.dynamic_lock;
@@ -99,8 +103,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
99EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { 103EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
100 EncryptedNTAG215File nfc_data{}; 104 EncryptedNTAG215File nfc_data{};
101 105
102 memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), sizeof(encoded_data.uuid2)); 106 nfc_data.uuid.uid = encoded_data.uid;
103 memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), sizeof(encoded_data.uuid)); 107 nfc_data.uuid.nintendo_id = encoded_data.nintendo_id;
108 nfc_data.uuid.lock_bytes = encoded_data.lock_bytes;
104 nfc_data.static_lock = encoded_data.static_lock; 109 nfc_data.static_lock = encoded_data.static_lock;
105 nfc_data.compability_container = encoded_data.compability_container; 110 nfc_data.compability_container = encoded_data.compability_container;
106 nfc_data.user_memory.hmac_data = encoded_data.hmac_data; 111 nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
@@ -112,7 +117,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
112 nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; 117 nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter;
113 nfc_data.user_memory.application_area_id = encoded_data.application_area_id; 118 nfc_data.user_memory.application_area_id = encoded_data.application_area_id;
114 nfc_data.user_memory.unknown = encoded_data.unknown; 119 nfc_data.user_memory.unknown = encoded_data.unknown;
115 nfc_data.user_memory.hash = encoded_data.hash; 120 nfc_data.user_memory.unknown2 = encoded_data.unknown2;
116 nfc_data.user_memory.application_area = encoded_data.application_area; 121 nfc_data.user_memory.application_area = encoded_data.application_area;
117 nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; 122 nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag;
118 nfc_data.user_memory.model_info = encoded_data.model_info; 123 nfc_data.user_memory.model_info = encoded_data.model_info;
@@ -127,10 +132,10 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
127 132
128u32 GetTagPassword(const TagUuid& uuid) { 133u32 GetTagPassword(const TagUuid& uuid) {
129 // Verifiy that the generated password is correct 134 // Verifiy that the generated password is correct
130 u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); 135 u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]);
131 password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; 136 password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8;
132 password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; 137 password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16;
133 password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; 138 password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24;
134 return password; 139 return password;
135} 140}
136 141
@@ -138,15 +143,13 @@ HashSeed GetSeed(const NTAG215File& data) {
138 HashSeed seed{ 143 HashSeed seed{
139 .magic = data.write_counter, 144 .magic = data.write_counter,
140 .padding = {}, 145 .padding = {},
141 .uuid1 = {}, 146 .uid_1 = data.uid,
142 .uuid2 = {}, 147 .nintendo_id_1 = data.nintendo_id,
148 .uid_2 = data.uid,
149 .nintendo_id_2 = data.nintendo_id,
143 .keygen_salt = data.keygen_salt, 150 .keygen_salt = data.keygen_salt,
144 }; 151 };
145 152
146 // Copy the first 8 bytes of uuid
147 memcpy(seed.uuid1.data(), data.uuid.data(), sizeof(seed.uuid1));
148 memcpy(seed.uuid2.data(), data.uuid.data(), sizeof(seed.uuid2));
149
150 return seed; 153 return seed;
151} 154}
152 155
@@ -165,8 +168,10 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
165 output.insert(output.end(), key.magic_bytes.begin(), 168 output.insert(output.end(), key.magic_bytes.begin(),
166 key.magic_bytes.begin() + key.magic_length); 169 key.magic_bytes.begin() + key.magic_length);
167 170
168 output.insert(output.end(), seed.uuid1.begin(), seed.uuid1.end()); 171 output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end());
169 output.insert(output.end(), seed.uuid2.begin(), seed.uuid2.end()); 172 output.emplace_back(seed.nintendo_id_1);
173 output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end());
174 output.emplace_back(seed.nintendo_id_2);
170 175
171 for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { 176 for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
172 output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); 177 output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
@@ -177,7 +182,6 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
177 182
178void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, 183void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
179 const std::vector<u8>& seed) { 184 const std::vector<u8>& seed) {
180
181 // Initialize context 185 // Initialize context
182 ctx.used = false; 186 ctx.used = false;
183 ctx.counter = 0; 187 ctx.counter = 0;
@@ -250,14 +254,15 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
250 reinterpret_cast<unsigned char*>(&out_data.settings)); 254 reinterpret_cast<unsigned char*>(&out_data.settings));
251 255
252 // Copy the rest of the data directly 256 // Copy the rest of the data directly
253 out_data.uuid2 = in_data.uuid2; 257 out_data.uid = in_data.uid;
258 out_data.nintendo_id = in_data.nintendo_id;
259 out_data.lock_bytes = in_data.lock_bytes;
254 out_data.static_lock = in_data.static_lock; 260 out_data.static_lock = in_data.static_lock;
255 out_data.compability_container = in_data.compability_container; 261 out_data.compability_container = in_data.compability_container;
256 262
257 out_data.constant_value = in_data.constant_value; 263 out_data.constant_value = in_data.constant_value;
258 out_data.write_counter = in_data.write_counter; 264 out_data.write_counter = in_data.write_counter;
259 265
260 out_data.uuid = in_data.uuid;
261 out_data.model_info = in_data.model_info; 266 out_data.model_info = in_data.model_info;
262 out_data.keygen_salt = in_data.keygen_salt; 267 out_data.keygen_salt = in_data.keygen_salt;
263 out_data.dynamic_lock = in_data.dynamic_lock; 268 out_data.dynamic_lock = in_data.dynamic_lock;
@@ -309,7 +314,7 @@ bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& t
309 // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! 314 // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
310 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; 315 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
311 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), 316 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
312 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid), 317 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
313 input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag)); 318 input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag));
314 319
315 // Regenerate data HMAC 320 // Regenerate data HMAC
@@ -350,7 +355,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t
350 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; 355 constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
351 constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; 356 constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START;
352 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), 357 mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
353 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid), 358 sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
354 input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag)); 359 input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag));
355 360
356 // Init mbedtls HMAC context 361 // Init mbedtls HMAC context
@@ -364,7 +369,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t
364 input_length2); // Data 369 input_length2); // Data
365 mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), 370 mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag),
366 sizeof(HashData)); // Tag HMAC 371 sizeof(HashData)); // Tag HMAC
367 mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uuid), 372 mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid),
368 input_length); 373 input_length);
369 mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data)); 374 mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data));
370 375
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
index af7335912..0175ced91 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,10 +22,12 @@ 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 UniqueSerialNumber uid_1;
28 std::array<u8, 0x8> uuid2; 28 u8 nintendo_id_1;
29 UniqueSerialNumber uid_2;
30 u8 nintendo_id_2;
29 std::array<u8, 0x20> keygen_salt; 31 std::array<u8, 0x20> keygen_salt;
30}; 32};
31static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); 33static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
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..0d4ffd3a5
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -0,0 +1,676 @@
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#include "core/hle/service/time/time_manager.h"
26#include "core/hle/service/time/time_zone_content_manager.h"
27#include "core/hle/service/time/time_zone_types.h"
28
29namespace Service::NFP {
30NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
31 KernelHelpers::ServiceContext& service_context_,
32 Kernel::KEvent* availability_change_event_)
33 : npad_id{npad_id_}, system{system_}, service_context{service_context_},
34 availability_change_event{availability_change_event_} {
35 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
36 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
37 npad_device = system.HIDCore().GetEmulatedController(npad_id);
38
39 Core::HID::ControllerUpdateCallback engine_callback{
40 .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
41 .is_npad_service = false,
42 };
43 is_controller_set = true;
44 callback_key = npad_device->SetCallback(engine_callback);
45
46 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
47 current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point;
48}
49
50NfpDevice::~NfpDevice() {
51 if (!is_controller_set) {
52 return;
53 }
54 npad_device->DeleteCallback(callback_key);
55 is_controller_set = false;
56};
57
58void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
59 if (type == Core::HID::ControllerTriggerType::Connected ||
60 type == Core::HID::ControllerTriggerType::Disconnected) {
61 availability_change_event->GetWritableEvent().Signal();
62 return;
63 }
64
65 if (type != Core::HID::ControllerTriggerType::Nfc) {
66 return;
67 }
68
69 if (!npad_device->IsConnected()) {
70 return;
71 }
72
73 const auto nfc_status = npad_device->GetNfc();
74 switch (nfc_status.state) {
75 case Common::Input::NfcState::NewAmiibo:
76 LoadAmiibo(nfc_status.data);
77 break;
78 case Common::Input::NfcState::AmiiboRemoved:
79 if (device_state != DeviceState::SearchingForTag) {
80 CloseAmiibo();
81 }
82 break;
83 default:
84 break;
85 }
86}
87
88bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
89 if (device_state != DeviceState::SearchingForTag) {
90 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
91 return false;
92 }
93
94 if (data.size() != sizeof(EncryptedNTAG215File)) {
95 LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size());
96 return false;
97 }
98
99 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
100
101 if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
102 LOG_INFO(Service_NFP, "Invalid amiibo");
103 return false;
104 }
105
106 device_state = DeviceState::TagFound;
107 deactivate_event->GetReadableEvent().Clear();
108 activate_event->GetWritableEvent().Signal();
109 return true;
110}
111
112void NfpDevice::CloseAmiibo() {
113 LOG_INFO(Service_NFP, "Remove amiibo");
114
115 if (device_state == DeviceState::TagMounted) {
116 Unmount();
117 }
118
119 device_state = DeviceState::TagRemoved;
120 encrypted_tag_data = {};
121 tag_data = {};
122 activate_event->GetReadableEvent().Clear();
123 deactivate_event->GetWritableEvent().Signal();
124}
125
126Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const {
127 return activate_event->GetReadableEvent();
128}
129
130Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const {
131 return deactivate_event->GetReadableEvent();
132}
133
134void NfpDevice::Initialize() {
135 device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
136 encrypted_tag_data = {};
137 tag_data = {};
138}
139
140void NfpDevice::Finalize() {
141 if (device_state == DeviceState::TagMounted) {
142 Unmount();
143 }
144 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
145 StopDetection();
146 }
147 device_state = DeviceState::Unavailable;
148}
149
150Result NfpDevice::StartDetection(s32 protocol_) {
151 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
152 npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
153 device_state = DeviceState::SearchingForTag;
154 protocol = protocol_;
155 return ResultSuccess;
156 }
157
158 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
159 return WrongDeviceState;
160}
161
162Result NfpDevice::StopDetection() {
163 npad_device->SetPollingMode(Common::Input::PollingMode::Active);
164
165 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
166 CloseAmiibo();
167 return ResultSuccess;
168 }
169 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
170 device_state = DeviceState::Initialized;
171 return ResultSuccess;
172 }
173
174 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
175 return WrongDeviceState;
176}
177
178Result NfpDevice::Flush() {
179 if (device_state != DeviceState::TagMounted) {
180 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
181 if (device_state == DeviceState::TagRemoved) {
182 return TagRemoved;
183 }
184 return WrongDeviceState;
185 }
186
187 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
188 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
189 return WrongDeviceState;
190 }
191
192 auto& settings = tag_data.settings;
193
194 const auto& current_date = GetAmiiboDate(current_posix_time);
195 if (settings.write_date.raw_date != current_date.raw_date) {
196 settings.write_date = current_date;
197 settings.crc_counter++;
198 // TODO: Find how to calculate the crc check
199 // settings.crc = CalculateCRC(settings);
200 }
201
202 tag_data.write_counter++;
203
204 if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
205 LOG_ERROR(Service_NFP, "Failed to encode data");
206 return WriteAmiiboFailed;
207 }
208
209 std::vector<u8> data(sizeof(encrypted_tag_data));
210 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
211
212 if (!npad_device->WriteNfc(data)) {
213 LOG_ERROR(Service_NFP, "Error writing to file");
214 return WriteAmiiboFailed;
215 }
216
217 is_data_moddified = false;
218
219 return ResultSuccess;
220}
221
222Result NfpDevice::Mount(MountTarget mount_target_) {
223 if (device_state != DeviceState::TagFound) {
224 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
225 return WrongDeviceState;
226 }
227
228 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
229 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
230 return CorruptedData;
231 }
232
233 device_state = DeviceState::TagMounted;
234 mount_target = mount_target_;
235 return ResultSuccess;
236}
237
238Result NfpDevice::Unmount() {
239 if (device_state != DeviceState::TagMounted) {
240 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
241 return WrongDeviceState;
242 }
243
244 // Save data before unloading the amiibo
245 if (is_data_moddified) {
246 Flush();
247 }
248
249 device_state = DeviceState::TagFound;
250 mount_target = MountTarget::None;
251 is_app_area_open = false;
252
253 return ResultSuccess;
254}
255
256Result NfpDevice::GetTagInfo(TagInfo& tag_info) const {
257 if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
258 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
259 return WrongDeviceState;
260 }
261
262 tag_info = {
263 .uuid = encrypted_tag_data.uuid.uid,
264 .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
265 .protocol = TagProtocol::TypeA,
266 .tag_type = TagType::Type2,
267 };
268
269 return ResultSuccess;
270}
271
272Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const {
273 if (device_state != DeviceState::TagMounted) {
274 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
275 if (device_state == DeviceState::TagRemoved) {
276 return TagRemoved;
277 }
278 return WrongDeviceState;
279 }
280
281 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
282 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
283 return WrongDeviceState;
284 }
285
286 const auto& settings = tag_data.settings;
287
288 // TODO: Validate this data
289 common_info = {
290 .last_write_date =
291 {
292 settings.write_date.GetYear(),
293 settings.write_date.GetMonth(),
294 settings.write_date.GetDay(),
295 },
296 .write_counter = tag_data.write_counter,
297 .version = 0,
298 .application_area_size = sizeof(ApplicationArea),
299 };
300 return ResultSuccess;
301}
302
303Result NfpDevice::GetModelInfo(ModelInfo& model_info) const {
304 if (device_state != DeviceState::TagMounted) {
305 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
306 return WrongDeviceState;
307 }
308
309 const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
310 model_info = {
311 .character_id = model_info_data.character_id,
312 .character_variant = model_info_data.character_variant,
313 .amiibo_type = model_info_data.amiibo_type,
314 .model_number = model_info_data.model_number,
315 .series = model_info_data.series,
316 };
317 return ResultSuccess;
318}
319
320Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
321 if (device_state != DeviceState::TagMounted) {
322 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
323 if (device_state == DeviceState::TagRemoved) {
324 return TagRemoved;
325 }
326 return WrongDeviceState;
327 }
328
329 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
330 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
331 return WrongDeviceState;
332 }
333
334 if (tag_data.settings.settings.amiibo_initialized == 0) {
335 return RegistrationIsNotInitialized;
336 }
337
338 Service::Mii::MiiManager manager;
339 const auto& settings = tag_data.settings;
340
341 // TODO: Validate this data
342 register_info = {
343 .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
344 .creation_date =
345 {
346 settings.init_date.GetYear(),
347 settings.init_date.GetMonth(),
348 settings.init_date.GetDay(),
349 },
350 .amiibo_name = GetAmiiboName(settings),
351 .font_region = {},
352 };
353
354 return ResultSuccess;
355}
356
357Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) {
358 if (device_state != DeviceState::TagMounted) {
359 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
360 if (device_state == DeviceState::TagRemoved) {
361 return TagRemoved;
362 }
363 return WrongDeviceState;
364 }
365
366 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
367 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
368 return WrongDeviceState;
369 }
370
371 Service::Mii::MiiManager manager;
372 auto& settings = tag_data.settings;
373
374 settings.init_date = GetAmiiboDate(current_posix_time);
375 settings.write_date = GetAmiiboDate(current_posix_time);
376 settings.crc_counter++;
377 // TODO: Find how to calculate the crc check
378 // settings.crc = CalculateCRC(settings);
379
380 SetAmiiboName(settings, amiibo_name);
381 tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0));
382 settings.settings.amiibo_initialized.Assign(1);
383
384 return Flush();
385}
386
387Result NfpDevice::RestoreAmiibo() {
388 if (device_state != DeviceState::TagMounted) {
389 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
390 if (device_state == DeviceState::TagRemoved) {
391 return TagRemoved;
392 }
393 return WrongDeviceState;
394 }
395
396 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
397 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
398 return WrongDeviceState;
399 }
400
401 // TODO: Load amiibo from backup on system
402 LOG_ERROR(Service_NFP, "Not Implemented");
403 return ResultSuccess;
404}
405
406Result NfpDevice::DeleteAllData() {
407 const auto result = DeleteApplicationArea();
408 if (result.IsError()) {
409 return result;
410 }
411
412 if (device_state != DeviceState::TagMounted) {
413 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
414 if (device_state == DeviceState::TagRemoved) {
415 return TagRemoved;
416 }
417 return WrongDeviceState;
418 }
419
420 Common::TinyMT rng{};
421 rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii));
422 tag_data.settings.settings.amiibo_initialized.Assign(0);
423
424 return Flush();
425}
426
427Result NfpDevice::OpenApplicationArea(u32 access_id) {
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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
437 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
438 return WrongDeviceState;
439 }
440
441 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
442 LOG_WARNING(Service_NFP, "Application area is not initialized");
443 return ApplicationAreaIsNotInitialized;
444 }
445
446 if (tag_data.application_area_id != access_id) {
447 LOG_WARNING(Service_NFP, "Wrong application area id");
448 return WrongApplicationAreaId;
449 }
450
451 is_app_area_open = true;
452
453 return ResultSuccess;
454}
455
456Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
466 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
467 return WrongDeviceState;
468 }
469
470 if (!is_app_area_open) {
471 LOG_ERROR(Service_NFP, "Application area is not open");
472 return WrongDeviceState;
473 }
474
475 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
476 LOG_ERROR(Service_NFP, "Application area is not initialized");
477 return ApplicationAreaIsNotInitialized;
478 }
479
480 if (data.size() > sizeof(ApplicationArea)) {
481 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
482 return ResultUnknown;
483 }
484
485 memcpy(data.data(), tag_data.application_area.data(), data.size());
486
487 return ResultSuccess;
488}
489
490Result NfpDevice::SetApplicationArea(std::span<const u8> data) {
491 if (device_state != DeviceState::TagMounted) {
492 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
493 if (device_state == DeviceState::TagRemoved) {
494 return TagRemoved;
495 }
496 return WrongDeviceState;
497 }
498
499 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
500 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
501 return WrongDeviceState;
502 }
503
504 if (!is_app_area_open) {
505 LOG_ERROR(Service_NFP, "Application area is not open");
506 return WrongDeviceState;
507 }
508
509 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
510 LOG_ERROR(Service_NFP, "Application area is not initialized");
511 return ApplicationAreaIsNotInitialized;
512 }
513
514 if (data.size() > sizeof(ApplicationArea)) {
515 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
516 return ResultUnknown;
517 }
518
519 Common::TinyMT rng{};
520 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
521 // HW seems to fill excess data with garbage
522 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
523 sizeof(ApplicationArea) - data.size());
524
525 tag_data.applicaton_write_counter++;
526 is_data_moddified = true;
527
528 return ResultSuccess;
529}
530
531Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) {
532 if (device_state != DeviceState::TagMounted) {
533 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
534 if (device_state == DeviceState::TagRemoved) {
535 return TagRemoved;
536 }
537 return WrongDeviceState;
538 }
539
540 if (tag_data.settings.settings.appdata_initialized.Value() != 0) {
541 LOG_ERROR(Service_NFP, "Application area already exist");
542 return ApplicationAreaExist;
543 }
544
545 return RecreateApplicationArea(access_id, data);
546}
547
548Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) {
549 if (device_state != DeviceState::TagMounted) {
550 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
551 if (device_state == DeviceState::TagRemoved) {
552 return TagRemoved;
553 }
554 return WrongDeviceState;
555 }
556
557 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
558 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
559 return WrongDeviceState;
560 }
561
562 if (data.size() > sizeof(ApplicationArea)) {
563 LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
564 return ResultUnknown;
565 }
566
567 Common::TinyMT rng{};
568 std::memcpy(tag_data.application_area.data(), data.data(), data.size());
569 // HW seems to fill excess data with garbage
570 rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
571 sizeof(ApplicationArea) - data.size());
572
573 // TODO: Investigate why the title id needs to be moddified
574 tag_data.title_id = system.GetCurrentProcessProgramID();
575 tag_data.title_id = tag_data.title_id | 0x30000000ULL;
576 tag_data.settings.settings.appdata_initialized.Assign(1);
577 tag_data.application_area_id = access_id;
578 tag_data.applicaton_write_counter++;
579 tag_data.unknown = {};
580
581 return Flush();
582}
583
584Result NfpDevice::DeleteApplicationArea() {
585 if (device_state != DeviceState::TagMounted) {
586 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
587 if (device_state == DeviceState::TagRemoved) {
588 return TagRemoved;
589 }
590 return WrongDeviceState;
591 }
592
593 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
594 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
595 return WrongDeviceState;
596 }
597
598 Common::TinyMT rng{};
599 rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea));
600 rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64));
601 rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32));
602 tag_data.settings.settings.appdata_initialized.Assign(0);
603 tag_data.applicaton_write_counter++;
604 tag_data.unknown = {};
605
606 return Flush();
607}
608
609u64 NfpDevice::GetHandle() const {
610 // Generate a handle based of the npad id
611 return static_cast<u64>(npad_id);
612}
613
614u32 NfpDevice::GetApplicationAreaSize() const {
615 // Investigate if this value is really constant
616 return sizeof(ApplicationArea);
617}
618
619DeviceState NfpDevice::GetCurrentState() const {
620 return device_state;
621}
622
623Core::HID::NpadIdType NfpDevice::GetNpadId() const {
624 return npad_id;
625}
626
627AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const {
628 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
629 AmiiboName amiibo_name{};
630
631 // Convert from big endian to little endian
632 for (std::size_t i = 0; i < amiibo_name_length; i++) {
633 settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
634 }
635
636 // Convert from utf16 to utf8
637 const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data());
638 memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size());
639
640 return amiibo_name;
641}
642
643void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) {
644 std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
645
646 // Convert from utf8 to utf16
647 const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data());
648 memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(),
649 amiibo_name_utf16.size() * sizeof(char16_t));
650
651 // Convert from little endian to big endian
652 for (std::size_t i = 0; i < amiibo_name_length; i++) {
653 settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]);
654 }
655}
656
657AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const {
658 const auto& time_zone_manager =
659 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
660 Time::TimeZone::CalendarInfo calendar_info{};
661 AmiiboDate amiibo_date{};
662
663 amiibo_date.SetYear(2000);
664 amiibo_date.SetMonth(1);
665 amiibo_date.SetDay(1);
666
667 if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) {
668 amiibo_date.SetYear(calendar_info.time.year);
669 amiibo_date.SetMonth(calendar_info.time.month);
670 amiibo_date.SetDay(calendar_info.time.day);
671 }
672
673 return amiibo_date;
674}
675
676} // 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..a5b72cf19
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -0,0 +1,101 @@
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(MountTarget mount_target);
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(std::span<const u8> data);
59 Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
60 Result RecreateApplicationArea(u32 access_id, std::span<const 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(std::span<const u8> data);
74 void CloseAmiibo();
75
76 AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
77 void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
78 AmiiboDate GetAmiiboDate(s64 posix_time) const;
79
80 bool is_controller_set{};
81 int callback_key;
82 const Core::HID::NpadIdType npad_id;
83 Core::System& system;
84 Core::HID::EmulatedController* npad_device = nullptr;
85 KernelHelpers::ServiceContext& service_context;
86 Kernel::KEvent* activate_event = nullptr;
87 Kernel::KEvent* deactivate_event = nullptr;
88 Kernel::KEvent* availability_change_event = nullptr;
89
90 bool is_data_moddified{};
91 bool is_app_area_open{};
92 s32 protocol{};
93 s64 current_posix_time{};
94 MountTarget mount_target{MountTarget::None};
95 DeviceState device_state{DeviceState::Unavailable};
96
97 NTAG215File tag_data{};
98 EncryptedNTAG215File encrypted_tag_data{};
99};
100
101} // 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..ac259e2ff
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_result.h
@@ -0,0 +1,22 @@
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);
20constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
21
22} // 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..dd4525b61 100644
--- a/src/core/hle/service/nfp/amiibo_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7 7
8#include "common/swap.h"
8#include "core/hle/service/mii/types.h" 9#include "core/hle/service/mii/types.h"
9 10
10namespace Service::NFP { 11namespace Service::NFP {
@@ -27,7 +28,7 @@ enum class DeviceState : u32 {
27 TagFound, 28 TagFound,
28 TagRemoved, 29 TagRemoved,
29 TagMounted, 30 TagMounted,
30 Unaviable, 31 Unavailable,
31 Finalized, 32 Finalized,
32}; 33};
33 34
@@ -36,6 +37,7 @@ enum class ModelType : u32 {
36}; 37};
37 38
38enum class MountTarget : u32 { 39enum class MountTarget : u32 {
40 None,
39 Rom, 41 Rom,
40 Ram, 42 Ram,
41 All, 43 All,
@@ -73,21 +75,63 @@ enum class AmiiboSeries : u8 {
73 Diablo, 75 Diablo,
74}; 76};
75 77
76using TagUuid = std::array<u8, 10>; 78enum class TagType : u32 {
79 None,
80 Type1, // ISO14443A RW 96-2k bytes 106kbit/s
81 Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
82 Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
83 Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
84 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
85};
86
87enum class TagProtocol : u32 {
88 None,
89 TypeA, // ISO14443A
90 TypeB, // ISO14443B
91 TypeF, // Sony Felica
92};
93
94using UniqueSerialNumber = std::array<u8, 7>;
95using LockBytes = std::array<u8, 2>;
77using HashData = std::array<u8, 0x20>; 96using HashData = std::array<u8, 0x20>;
78using ApplicationArea = std::array<u8, 0xD8>; 97using ApplicationArea = std::array<u8, 0xD8>;
98using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
99
100struct TagUuid {
101 UniqueSerialNumber uid;
102 u8 nintendo_id;
103 LockBytes lock_bytes;
104};
105static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size");
79 106
80struct AmiiboDate { 107struct AmiiboDate {
81 u16 raw_date{}; 108 u16 raw_date{};
82 109
110 u16 GetValue() const {
111 return Common::swap16(raw_date);
112 }
113
83 u16 GetYear() const { 114 u16 GetYear() const {
84 return static_cast<u16>(((raw_date & 0xFE00) >> 9) + 2000); 115 return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000);
85 } 116 }
86 u8 GetMonth() const { 117 u8 GetMonth() const {
87 return static_cast<u8>(((raw_date & 0x01E0) >> 5) - 1); 118 return static_cast<u8>((GetValue() & 0x01E0) >> 5);
88 } 119 }
89 u8 GetDay() const { 120 u8 GetDay() const {
90 return static_cast<u8>(raw_date & 0x001F); 121 return static_cast<u8>(GetValue() & 0x001F);
122 }
123
124 void SetYear(u16 year) {
125 const u16 year_converted = static_cast<u16>((year - 2000) << 9);
126 raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
127 }
128 void SetMonth(u8 month) {
129 const u16 month_converted = static_cast<u16>(month << 5);
130 raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted);
131 }
132 void SetDay(u8 day) {
133 const u16 day_converted = static_cast<u16>(day);
134 raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted);
91 } 135 }
92}; 136};
93static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); 137static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size");
@@ -117,7 +161,7 @@ struct AmiiboModelInfo {
117 u16 character_id; 161 u16 character_id;
118 u8 character_variant; 162 u8 character_variant;
119 AmiiboType amiibo_type; 163 AmiiboType amiibo_type;
120 u16 model_number; 164 u16_be model_number;
121 AmiiboSeries series; 165 AmiiboSeries series;
122 u8 constant_value; // Must be 02 166 u8 constant_value; // Must be 02
123 INSERT_PADDING_BYTES(0x4); // Unknown 167 INSERT_PADDING_BYTES(0x4); // Unknown
@@ -134,7 +178,7 @@ static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid siz
134#pragma pack(1) 178#pragma pack(1)
135struct EncryptedAmiiboFile { 179struct EncryptedAmiiboFile {
136 u8 constant_value; // Must be A5 180 u8 constant_value; // Must be A5
137 u16 write_counter; // Number of times the amiibo has been written? 181 u16_be write_counter; // Number of times the amiibo has been written?
138 INSERT_PADDING_BYTES(0x1); // Unknown 1 182 INSERT_PADDING_BYTES(0x1); // Unknown 1
139 AmiiboSettings settings; // Encrypted amiibo settings 183 AmiiboSettings settings; // Encrypted amiibo settings
140 HashData hmac_tag; // Hash 184 HashData hmac_tag; // Hash
@@ -146,18 +190,18 @@ struct EncryptedAmiiboFile {
146 u16_be applicaton_write_counter; // Encrypted Counter 190 u16_be applicaton_write_counter; // Encrypted Counter
147 u32_be application_area_id; // Encrypted Game id 191 u32_be application_area_id; // Encrypted Game id
148 std::array<u8, 0x2> unknown; 192 std::array<u8, 0x2> unknown;
149 HashData hash; // Probably a SHA256-HMAC hash? 193 std::array<u32, 0x8> unknown2;
150 ApplicationArea application_area; // Encrypted Game data 194 ApplicationArea application_area; // Encrypted Game data
151}; 195};
152static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); 196static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
153 197
154struct NTAG215File { 198struct NTAG215File {
155 std::array<u8, 0x2> uuid2; 199 LockBytes lock_bytes; // Tag UUID
156 u16 static_lock; // Set defined pages as read only 200 u16 static_lock; // Set defined pages as read only
157 u32 compability_container; // Defines available memory 201 u32 compability_container; // Defines available memory
158 HashData hmac_data; // Hash 202 HashData hmac_data; // Hash
159 u8 constant_value; // Must be A5 203 u8 constant_value; // Must be A5
160 u16 write_counter; // Number of times the amiibo has been written? 204 u16_be write_counter; // Number of times the amiibo has been written?
161 INSERT_PADDING_BYTES(0x1); // Unknown 1 205 INSERT_PADDING_BYTES(0x1); // Unknown 1
162 AmiiboSettings settings; 206 AmiiboSettings settings;
163 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data 207 Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
@@ -165,10 +209,11 @@ struct NTAG215File {
165 u16_be applicaton_write_counter; // Encrypted Counter 209 u16_be applicaton_write_counter; // Encrypted Counter
166 u32_be application_area_id; 210 u32_be application_area_id;
167 std::array<u8, 0x2> unknown; 211 std::array<u8, 0x2> unknown;
168 HashData hash; // Probably a SHA256-HMAC hash? 212 std::array<u32, 0x8> unknown2;
169 ApplicationArea application_area; // Encrypted Game data 213 ApplicationArea application_area; // Encrypted Game data
170 HashData hmac_tag; // Hash 214 HashData hmac_tag; // Hash
171 std::array<u8, 0x8> uuid; 215 UniqueSerialNumber uid; // Unique serial number
216 u8 nintendo_id; // Tag UUID
172 AmiiboModelInfo model_info; 217 AmiiboModelInfo model_info;
173 HashData keygen_salt; // Salt 218 HashData keygen_salt; // Salt
174 u32 dynamic_lock; // Dynamic lock 219 u32 dynamic_lock; // Dynamic lock
@@ -194,4 +239,51 @@ static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an
194static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, 239static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
195 "EncryptedNTAG215File must be trivially copyable."); 240 "EncryptedNTAG215File must be trivially copyable.");
196 241
242struct TagInfo {
243 UniqueSerialNumber uuid;
244 INSERT_PADDING_BYTES(0x3);
245 u8 uuid_length;
246 INSERT_PADDING_BYTES(0x15);
247 TagProtocol protocol;
248 TagType tag_type;
249 INSERT_PADDING_BYTES(0x30);
250};
251static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
252
253struct WriteDate {
254 u16 year;
255 u8 month;
256 u8 day;
257};
258static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size");
259
260struct CommonInfo {
261 WriteDate last_write_date;
262 u16 write_counter;
263 u8 version;
264 INSERT_PADDING_BYTES(0x1);
265 u32 application_area_size;
266 INSERT_PADDING_BYTES(0x34);
267};
268static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
269
270struct ModelInfo {
271 u16 character_id;
272 u8 character_variant;
273 AmiiboType amiibo_type;
274 u16 model_number;
275 AmiiboSeries series;
276 INSERT_PADDING_BYTES(0x39); // Unknown
277};
278static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
279
280struct RegisterInfo {
281 Service::Mii::CharInfo mii_char_info;
282 WriteDate creation_date;
283 AmiiboName amiibo_name;
284 u8 font_region;
285 INSERT_PADDING_BYTES(0x7A);
286};
287static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
288
197} // namespace Service::NFP 289} // 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..c61df9401 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(mount_target);
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
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 4b91b88ce..2cf9eb97f 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -18,6 +18,8 @@ add_library(input_common STATIC
18 drivers/touch_screen.h 18 drivers/touch_screen.h
19 drivers/udp_client.cpp 19 drivers/udp_client.cpp
20 drivers/udp_client.h 20 drivers/udp_client.h
21 drivers/virtual_amiibo.cpp
22 drivers/virtual_amiibo.h
21 helpers/stick_from_buttons.cpp 23 helpers/stick_from_buttons.cpp
22 helpers/stick_from_buttons.h 24 helpers/stick_from_buttons.h
23 helpers/touch_from_buttons.cpp 25 helpers/touch_from_buttons.cpp
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
new file mode 100644
index 000000000..0cd5129da
--- /dev/null
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <cstring>
5#include <fmt/format.h>
6
7#include "common/fs/file.h"
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/logging/log.h"
11#include "common/settings.h"
12#include "input_common/drivers/virtual_amiibo.h"
13
14namespace InputCommon {
15constexpr PadIdentifier identifier = {
16 .guid = Common::UUID{},
17 .port = 0,
18 .pad = 0,
19};
20
21VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
22
23VirtualAmiibo::~VirtualAmiibo() = default;
24
25Common::Input::PollingError VirtualAmiibo::SetPollingMode(
26 [[maybe_unused]] const PadIdentifier& identifier_,
27 const Common::Input::PollingMode polling_mode_) {
28 polling_mode = polling_mode_;
29
30 if (polling_mode == Common::Input::PollingMode::NFC) {
31 if (state == State::Initialized) {
32 state = State::WaitingForAmiibo;
33 }
34 } else {
35 if (state == State::AmiiboIsOpen) {
36 CloseAmiibo();
37 }
38 }
39
40 return Common::Input::PollingError::None;
41}
42
43Common::Input::NfcState VirtualAmiibo::SupportsNfc(
44 [[maybe_unused]] const PadIdentifier& identifier_) const {
45 return Common::Input::NfcState::Success;
46}
47
48Common::Input::NfcState VirtualAmiibo::WriteNfcData(
49 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
50 const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
51 Common::FS::FileType::BinaryFile};
52
53 if (!amiibo_file.IsOpen()) {
54 LOG_ERROR(Core, "Amiibo is already on use");
55 return Common::Input::NfcState::WriteFailed;
56 }
57
58 if (!amiibo_file.Write(data)) {
59 LOG_ERROR(Service_NFP, "Error writting to file");
60 return Common::Input::NfcState::WriteFailed;
61 }
62
63 return Common::Input::NfcState::Success;
64}
65
66VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
67 return state;
68}
69
70VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
71 const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
72 Common::FS::FileType::BinaryFile};
73
74 if (state != State::WaitingForAmiibo) {
75 return Info::WrongDeviceState;
76 }
77
78 if (!amiibo_file.IsOpen()) {
79 return Info::UnableToLoad;
80 }
81
82 amiibo_data.resize(amiibo_size);
83
84 if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) {
85 return Info::NotAnAmiibo;
86 }
87
88 file_path = filename;
89 state = State::AmiiboIsOpen;
90 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
91 return Info::Success;
92}
93
94VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
95 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
96 : State::Initialized;
97 SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}});
98 return Info::Success;
99}
100
101} // namespace InputCommon
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
new file mode 100644
index 000000000..9eac07544
--- /dev/null
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8#include <vector>
9
10#include "common/common_types.h"
11#include "input_common/input_engine.h"
12
13namespace Common::FS {
14class IOFile;
15}
16
17namespace InputCommon {
18
19class VirtualAmiibo final : public InputEngine {
20public:
21 enum class State {
22 Initialized,
23 WaitingForAmiibo,
24 AmiiboIsOpen,
25 };
26
27 enum class Info {
28 Success,
29 UnableToLoad,
30 NotAnAmiibo,
31 WrongDeviceState,
32 Unknown,
33 };
34
35 explicit VirtualAmiibo(std::string input_engine_);
36 ~VirtualAmiibo() override;
37
38 // Sets polling mode to a controller
39 Common::Input::PollingError SetPollingMode(
40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
43
44 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
45 const std::vector<u8>& data) override;
46
47 State GetCurrentState() const;
48
49 Info LoadAmiibo(const std::string& amiibo_file);
50 Info CloseAmiibo();
51
52private:
53 static constexpr std::size_t amiibo_size = 0x21C;
54 static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
55
56 std::string file_path{};
57 State state{State::Initialized};
58 std::vector<u8> amiibo_data;
59 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
60};
61} // namespace InputCommon
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 6ede0e4b0..61cfd0911 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -102,6 +102,17 @@ void InputEngine::SetCamera(const PadIdentifier& identifier,
102 TriggerOnCameraChange(identifier, value); 102 TriggerOnCameraChange(identifier, value);
103} 103}
104 104
105void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) {
106 {
107 std::scoped_lock lock{mutex};
108 ControllerData& controller = controller_list.at(identifier);
109 if (!configuring) {
110 controller.nfc = value;
111 }
112 }
113 TriggerOnNfcChange(identifier, value);
114}
115
105bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { 116bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
106 std::scoped_lock lock{mutex}; 117 std::scoped_lock lock{mutex};
107 const auto controller_iter = controller_list.find(identifier); 118 const auto controller_iter = controller_list.find(identifier);
@@ -189,6 +200,18 @@ Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifi
189 return controller.camera; 200 return controller.camera;
190} 201}
191 202
203Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const {
204 std::scoped_lock lock{mutex};
205 const auto controller_iter = controller_list.find(identifier);
206 if (controller_iter == controller_list.cend()) {
207 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
208 identifier.pad, identifier.port);
209 return {};
210 }
211 const ControllerData& controller = controller_iter->second;
212 return controller.nfc;
213}
214
192void InputEngine::ResetButtonState() { 215void InputEngine::ResetButtonState() {
193 for (const auto& controller : controller_list) { 216 for (const auto& controller : controller_list) {
194 for (const auto& button : controller.second.buttons) { 217 for (const auto& button : controller.second.buttons) {
@@ -355,6 +378,20 @@ void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
355 } 378 }
356} 379}
357 380
381void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier,
382 [[maybe_unused]] const Common::Input::NfcStatus& value) {
383 std::scoped_lock lock{mutex_callback};
384 for (const auto& poller_pair : callback_list) {
385 const InputIdentifier& poller = poller_pair.second;
386 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) {
387 continue;
388 }
389 if (poller.callback.on_change) {
390 poller.callback.on_change();
391 }
392 }
393}
394
358bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, 395bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
359 const PadIdentifier& identifier, EngineInputType type, 396 const PadIdentifier& identifier, EngineInputType type,
360 int index) const { 397 int index) const {
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index f6b3c4610..cfbdb26bd 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -42,6 +42,7 @@ enum class EngineInputType {
42 Camera, 42 Camera,
43 HatButton, 43 HatButton,
44 Motion, 44 Motion,
45 Nfc,
45}; 46};
46 47
47namespace std { 48namespace std {
@@ -127,6 +128,18 @@ public:
127 return Common::Input::CameraError::NotSupported; 128 return Common::Input::CameraError::NotSupported;
128 } 129 }
129 130
131 // Request nfc data from a controller
132 virtual Common::Input::NfcState SupportsNfc(
133 [[maybe_unused]] const PadIdentifier& identifier) const {
134 return Common::Input::NfcState::NotSupported;
135 }
136
137 // Writes data to an nfc tag
138 virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,
139 [[maybe_unused]] const std::vector<u8>& data) {
140 return Common::Input::NfcState::NotSupported;
141 }
142
130 // Returns the engine name 143 // Returns the engine name
131 [[nodiscard]] const std::string& GetEngineName() const; 144 [[nodiscard]] const std::string& GetEngineName() const;
132 145
@@ -183,6 +196,7 @@ public:
183 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; 196 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
184 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; 197 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
185 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; 198 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
199 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
186 200
187 int SetCallback(InputIdentifier input_identifier); 201 int SetCallback(InputIdentifier input_identifier);
188 void SetMappingCallback(MappingCallback callback); 202 void SetMappingCallback(MappingCallback callback);
@@ -195,6 +209,7 @@ protected:
195 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 209 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
196 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); 210 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
197 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); 211 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
212 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
198 213
199 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { 214 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
200 return "Unknown"; 215 return "Unknown";
@@ -208,6 +223,7 @@ private:
208 std::unordered_map<int, BasicMotion> motions; 223 std::unordered_map<int, BasicMotion> motions;
209 Common::Input::BatteryLevel battery{}; 224 Common::Input::BatteryLevel battery{};
210 Common::Input::CameraStatus camera{}; 225 Common::Input::CameraStatus camera{};
226 Common::Input::NfcStatus nfc{};
211 }; 227 };
212 228
213 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); 229 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
@@ -218,6 +234,7 @@ private:
218 const BasicMotion& value); 234 const BasicMotion& value);
219 void TriggerOnCameraChange(const PadIdentifier& identifier, 235 void TriggerOnCameraChange(const PadIdentifier& identifier,
220 const Common::Input::CameraStatus& value); 236 const Common::Input::CameraStatus& value);
237 void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
221 238
222 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, 239 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
223 const PadIdentifier& identifier, EngineInputType type, 240 const PadIdentifier& identifier, EngineInputType type,
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index ffb9b945e..75705b67e 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -705,6 +705,47 @@ private:
705 InputEngine* input_engine; 705 InputEngine* input_engine;
706}; 706};
707 707
708class InputFromNfc final : public Common::Input::InputDevice {
709public:
710 explicit InputFromNfc(PadIdentifier identifier_, InputEngine* input_engine_)
711 : identifier(identifier_), input_engine(input_engine_) {
712 UpdateCallback engine_callback{[this]() { OnChange(); }};
713 const InputIdentifier input_identifier{
714 .identifier = identifier,
715 .type = EngineInputType::Nfc,
716 .index = 0,
717 .callback = engine_callback,
718 };
719 callback_key = input_engine->SetCallback(input_identifier);
720 }
721
722 ~InputFromNfc() override {
723 input_engine->DeleteCallback(callback_key);
724 }
725
726 Common::Input::NfcStatus GetStatus() const {
727 return input_engine->GetNfc(identifier);
728 }
729
730 void ForceUpdate() override {
731 OnChange();
732 }
733
734 void OnChange() {
735 const Common::Input::CallbackStatus status{
736 .type = Common::Input::InputType::Nfc,
737 .nfc_status = GetStatus(),
738 };
739
740 TriggerOnChange(status);
741 }
742
743private:
744 const PadIdentifier identifier;
745 int callback_key;
746 InputEngine* input_engine;
747};
748
708class OutputFromIdentifier final : public Common::Input::OutputDevice { 749class OutputFromIdentifier final : public Common::Input::OutputDevice {
709public: 750public:
710 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) 751 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
@@ -727,6 +768,14 @@ public:
727 return input_engine->SetCameraFormat(identifier, camera_format); 768 return input_engine->SetCameraFormat(identifier, camera_format);
728 } 769 }
729 770
771 Common::Input::NfcState SupportsNfc() const override {
772 return input_engine->SupportsNfc(identifier);
773 }
774
775 Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {
776 return input_engine->WriteNfcData(identifier, data);
777 }
778
730private: 779private:
731 const PadIdentifier identifier; 780 const PadIdentifier identifier;
732 InputEngine* input_engine; 781 InputEngine* input_engine;
@@ -978,6 +1027,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
978 return std::make_unique<InputFromCamera>(identifier, input_engine.get()); 1027 return std::make_unique<InputFromCamera>(identifier, input_engine.get());
979} 1028}
980 1029
1030std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateNfcDevice(
1031 const Common::ParamPackage& params) {
1032 const PadIdentifier identifier = {
1033 .guid = Common::UUID{params.Get("guid", "")},
1034 .port = static_cast<std::size_t>(params.Get("port", 0)),
1035 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
1036 };
1037
1038 input_engine->PreSetController(identifier);
1039 return std::make_unique<InputFromNfc>(identifier, input_engine.get());
1040}
1041
981InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_) 1042InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
982 : input_engine(std::move(input_engine_)) {} 1043 : input_engine(std::move(input_engine_)) {}
983 1044
@@ -989,6 +1050,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
989 if (params.Has("camera")) { 1050 if (params.Has("camera")) {
990 return CreateCameraDevice(params); 1051 return CreateCameraDevice(params);
991 } 1052 }
1053 if (params.Has("nfc")) {
1054 return CreateNfcDevice(params);
1055 }
992 if (params.Has("button") && params.Has("axis")) { 1056 if (params.Has("button") && params.Has("axis")) {
993 return CreateTriggerDevice(params); 1057 return CreateTriggerDevice(params);
994 } 1058 }
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
index 4410a8415..d7db13ce4 100644
--- a/src/input_common/input_poller.h
+++ b/src/input_common/input_poller.h
@@ -222,6 +222,16 @@ private:
222 std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice( 222 std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
223 const Common::ParamPackage& params); 223 const Common::ParamPackage& params);
224 224
225 /**
226 * Creates a nfc device from the parameters given.
227 * @param params contains parameters for creating the device:
228 * - "guid": text string for identifying controllers
229 * - "port": port of the connected device
230 * - "pad": slot of the connected controller
231 * @returns a unique input device with the parameters specified
232 */
233 std::unique_ptr<Common::Input::InputDevice> CreateNfcDevice(const Common::ParamPackage& params);
234
225 std::shared_ptr<InputEngine> input_engine; 235 std::shared_ptr<InputEngine> input_engine;
226}; 236};
227} // namespace InputCommon 237} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 75a57b9fc..b2064ef95 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -11,6 +11,7 @@
11#include "input_common/drivers/tas_input.h" 11#include "input_common/drivers/tas_input.h"
12#include "input_common/drivers/touch_screen.h" 12#include "input_common/drivers/touch_screen.h"
13#include "input_common/drivers/udp_client.h" 13#include "input_common/drivers/udp_client.h"
14#include "input_common/drivers/virtual_amiibo.h"
14#include "input_common/helpers/stick_from_buttons.h" 15#include "input_common/helpers/stick_from_buttons.h"
15#include "input_common/helpers/touch_from_buttons.h" 16#include "input_common/helpers/touch_from_buttons.h"
16#include "input_common/input_engine.h" 17#include "input_common/input_engine.h"
@@ -87,6 +88,15 @@ struct InputSubsystem::Impl {
87 Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(), 88 Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
88 camera_output_factory); 89 camera_output_factory);
89 90
91 virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
92 virtual_amiibo->SetMappingCallback(mapping_callback);
93 virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
94 virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
95 Common::Input::RegisterFactory<Common::Input::InputDevice>(virtual_amiibo->GetEngineName(),
96 virtual_amiibo_input_factory);
97 Common::Input::RegisterFactory<Common::Input::OutputDevice>(virtual_amiibo->GetEngineName(),
98 virtual_amiibo_output_factory);
99
90#ifdef HAVE_SDL2 100#ifdef HAVE_SDL2
91 sdl = std::make_shared<SDLDriver>("sdl"); 101 sdl = std::make_shared<SDLDriver>("sdl");
92 sdl->SetMappingCallback(mapping_callback); 102 sdl->SetMappingCallback(mapping_callback);
@@ -327,6 +337,7 @@ struct InputSubsystem::Impl {
327 std::shared_ptr<TasInput::Tas> tas_input; 337 std::shared_ptr<TasInput::Tas> tas_input;
328 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 338 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
329 std::shared_ptr<Camera> camera; 339 std::shared_ptr<Camera> camera;
340 std::shared_ptr<VirtualAmiibo> virtual_amiibo;
330 341
331 std::shared_ptr<InputFactory> keyboard_factory; 342 std::shared_ptr<InputFactory> keyboard_factory;
332 std::shared_ptr<InputFactory> mouse_factory; 343 std::shared_ptr<InputFactory> mouse_factory;
@@ -335,6 +346,7 @@ struct InputSubsystem::Impl {
335 std::shared_ptr<InputFactory> udp_client_input_factory; 346 std::shared_ptr<InputFactory> udp_client_input_factory;
336 std::shared_ptr<InputFactory> tas_input_factory; 347 std::shared_ptr<InputFactory> tas_input_factory;
337 std::shared_ptr<InputFactory> camera_input_factory; 348 std::shared_ptr<InputFactory> camera_input_factory;
349 std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
338 350
339 std::shared_ptr<OutputFactory> keyboard_output_factory; 351 std::shared_ptr<OutputFactory> keyboard_output_factory;
340 std::shared_ptr<OutputFactory> mouse_output_factory; 352 std::shared_ptr<OutputFactory> mouse_output_factory;
@@ -342,6 +354,7 @@ struct InputSubsystem::Impl {
342 std::shared_ptr<OutputFactory> udp_client_output_factory; 354 std::shared_ptr<OutputFactory> udp_client_output_factory;
343 std::shared_ptr<OutputFactory> tas_output_factory; 355 std::shared_ptr<OutputFactory> tas_output_factory;
344 std::shared_ptr<OutputFactory> camera_output_factory; 356 std::shared_ptr<OutputFactory> camera_output_factory;
357 std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
345 358
346#ifdef HAVE_SDL2 359#ifdef HAVE_SDL2
347 std::shared_ptr<SDLDriver> sdl; 360 std::shared_ptr<SDLDriver> sdl;
@@ -402,6 +415,14 @@ const Camera* InputSubsystem::GetCamera() const {
402 return impl->camera.get(); 415 return impl->camera.get();
403} 416}
404 417
418VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
419 return impl->virtual_amiibo.get();
420}
421
422const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
423 return impl->virtual_amiibo.get();
424}
425
405std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 426std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
406 return impl->GetInputDevices(); 427 return impl->GetInputDevices();
407} 428}
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 9a969e747..ced252383 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -33,6 +33,7 @@ class Camera;
33class Keyboard; 33class Keyboard;
34class Mouse; 34class Mouse;
35class TouchScreen; 35class TouchScreen;
36class VirtualAmiibo;
36struct MappingData; 37struct MappingData;
37} // namespace InputCommon 38} // namespace InputCommon
38 39
@@ -101,6 +102,12 @@ public:
101 /// Retrieves the underlying camera input device. 102 /// Retrieves the underlying camera input device.
102 [[nodiscard]] const Camera* GetCamera() const; 103 [[nodiscard]] const Camera* GetCamera() const;
103 104
105 /// Retrieves the underlying virtual amiibo input device.
106 [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo();
107
108 /// Retrieves the underlying virtual amiibo input device.
109 [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;
110
104 /** 111 /**
105 * Returns all available input devices that this Factory can create a new device with. 112 * Returns all available input devices that this Factory can create a new device with.
106 * Each returned ParamPackage should have a `display` field used for display, a `engine` field 113 * Each returned ParamPackage should have a `display` field used for display, a `engine` field
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 612b8dbb8..c63ce3a30 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -105,12 +105,12 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
105#include "core/hle/kernel/k_process.h" 105#include "core/hle/kernel/k_process.h"
106#include "core/hle/service/am/am.h" 106#include "core/hle/service/am/am.h"
107#include "core/hle/service/filesystem/filesystem.h" 107#include "core/hle/service/filesystem/filesystem.h"
108#include "core/hle/service/nfp/nfp.h"
109#include "core/hle/service/sm/sm.h" 108#include "core/hle/service/sm/sm.h"
110#include "core/loader/loader.h" 109#include "core/loader/loader.h"
111#include "core/perf_stats.h" 110#include "core/perf_stats.h"
112#include "core/telemetry_session.h" 111#include "core/telemetry_session.h"
113#include "input_common/drivers/tas_input.h" 112#include "input_common/drivers/tas_input.h"
113#include "input_common/drivers/virtual_amiibo.h"
114#include "input_common/main.h" 114#include "input_common/main.h"
115#include "ui_main.h" 115#include "ui_main.h"
116#include "util/overlay_dialog.h" 116#include "util/overlay_dialog.h"
@@ -3219,21 +3219,16 @@ void GMainWindow::OnLoadAmiibo() {
3219 return; 3219 return;
3220 } 3220 }
3221 3221
3222 Service::SM::ServiceManager& sm = system->ServiceManager(); 3222 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3223 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); 3223
3224 if (nfc == nullptr) { 3224 // Remove amiibo if one is connected
3225 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); 3225 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) {
3226 return; 3226 virtual_amiibo->CloseAmiibo();
3227 }
3228 const auto nfc_state = nfc->GetCurrentState();
3229 if (nfc_state == Service::NFP::DeviceState::TagFound ||
3230 nfc_state == Service::NFP::DeviceState::TagMounted) {
3231 nfc->CloseAmiibo();
3232 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3227 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3233 return; 3228 return;
3234 } 3229 }
3235 3230
3236 if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { 3231 if (virtual_amiibo->GetCurrentState() != InputCommon::VirtualAmiibo::State::WaitingForAmiibo) {
3237 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); 3232 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
3238 return; 3233 return;
3239 } 3234 }
@@ -3252,24 +3247,30 @@ void GMainWindow::OnLoadAmiibo() {
3252} 3247}
3253 3248
3254void GMainWindow::LoadAmiibo(const QString& filename) { 3249void GMainWindow::LoadAmiibo(const QString& filename) {
3255 Service::SM::ServiceManager& sm = system->ServiceManager(); 3250 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
3256 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); 3251 const QString title = tr("Error loading Amiibo data");
3257 if (nfc == nullptr) {
3258 return;
3259 }
3260
3261 // Remove amiibo if one is connected 3252 // Remove amiibo if one is connected
3262 const auto nfc_state = nfc->GetCurrentState(); 3253 if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) {
3263 if (nfc_state == Service::NFP::DeviceState::TagFound || 3254 virtual_amiibo->CloseAmiibo();
3264 nfc_state == Service::NFP::DeviceState::TagMounted) {
3265 nfc->CloseAmiibo();
3266 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); 3255 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
3267 return; 3256 return;
3268 } 3257 }
3269 3258
3270 if (!nfc->LoadAmiibo(filename.toStdString())) { 3259 switch (virtual_amiibo->LoadAmiibo(filename.toStdString())) {
3271 QMessageBox::warning(this, tr("Error loading Amiibo data"), 3260 case InputCommon::VirtualAmiibo::Info::NotAnAmiibo:
3272 tr("Unable to load Amiibo data.")); 3261 QMessageBox::warning(this, title, tr("The selected file is not a valid amiibo"));
3262 break;
3263 case InputCommon::VirtualAmiibo::Info::UnableToLoad:
3264 QMessageBox::warning(this, title, tr("The selected file is already on use"));
3265 break;
3266 case InputCommon::VirtualAmiibo::Info::WrongDeviceState:
3267 QMessageBox::warning(this, title, tr("The current game is not looking for amiibos"));
3268 break;
3269 case InputCommon::VirtualAmiibo::Info::Unknown:
3270 QMessageBox::warning(this, title, tr("An unkown error occured"));
3271 break;
3272 default:
3273 break;
3273 } 3274 }
3274} 3275}
3275 3276