diff options
| author | 2022-10-02 14:28:43 -0700 | |
|---|---|---|
| committer | 2022-10-02 14:28:43 -0700 | |
| commit | 61399de5dbc64ada1c9e5ec3d84be76c215fdc0c (patch) | |
| tree | 72313f1b749d0771e38dbfcf539121a02d4b9ae0 | |
| parent | Merge pull request #8992 from Morph1984/vi-vsync-event (diff) | |
| parent | service: mii: Copy only valid name bytes (diff) | |
| download | yuzu-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
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 | ||
| 80 | enum 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 |
| 80 | enum class CameraError { | 93 | enum 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 | ||
| 218 | struct 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 |
| 206 | enum class ButtonNames { | 224 | enum 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 | ||
| 344 | void EmulatedController::EnableConfiguration() { | 360 | void 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 | ||
| 922 | void 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 | |||
| 906 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | 941 | bool 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) { | |||
| 980 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | 1015 | bool 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 | ||
| 1042 | bool 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 | |||
| 1062 | bool 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 | |||
| 1003 | void EmulatedController::SetLedPattern() { | 1068 | void 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 | ||
| 1431 | const NfcState& EmulatedController::GetNfc() const { | ||
| 1432 | std::scoped_lock lock{mutex}; | ||
| 1433 | return controller.nfc_state; | ||
| 1434 | } | ||
| 1435 | |||
| 1366 | NpadColor EmulatedController::GetNpadColor(u32 color) { | 1436 | NpadColor 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 | ||
| 21 | namespace Core::HID { | 21 | namespace Core::HID { |
| 22 | const std::size_t max_emulated_controllers = 2; | 22 | const std::size_t max_emulated_controllers = 2; |
| 23 | const std::size_t output_devices = 3; | 23 | const std::size_t output_devices_size = 4; |
| 24 | struct ControllerMotionInfo { | 24 | struct 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 = | |||
| 37 | using BatteryDevices = | 37 | using 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>; |
| 39 | using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; | 39 | using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; |
| 40 | using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>; | 40 | using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; |
| 41 | using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; | ||
| 41 | 42 | ||
| 42 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; | 43 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; |
| 43 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; | 44 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; |
| @@ -45,7 +46,8 @@ using ControllerMotionParams = std::array<Common::ParamPackage, Settings::Native | |||
| 45 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; | 46 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; |
| 46 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; | 47 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; |
| 47 | using CameraParams = Common::ParamPackage; | 48 | using CameraParams = Common::ParamPackage; |
| 48 | using OutputParams = std::array<Common::ParamPackage, output_devices>; | 49 | using NfcParams = Common::ParamPackage; |
| 50 | using OutputParams = std::array<Common::ParamPackage, output_devices_size>; | ||
| 49 | 51 | ||
| 50 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | 52 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; |
| 51 | using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; | 53 | using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; |
| @@ -55,6 +57,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native | |||
| 55 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; | 57 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; |
| 56 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; | 58 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; |
| 57 | using CameraValues = Common::Input::CameraStatus; | 59 | using CameraValues = Common::Input::CameraStatus; |
| 60 | using NfcValues = Common::Input::NfcStatus; | ||
| 58 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; | 61 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; |
| 59 | 62 | ||
| 60 | struct AnalogSticks { | 63 | struct AnalogSticks { |
| @@ -80,6 +83,11 @@ struct CameraState { | |||
| 80 | std::size_t sample{}; | 83 | std::size_t sample{}; |
| 81 | }; | 84 | }; |
| 82 | 85 | ||
| 86 | struct NfcState { | ||
| 87 | Common::Input::NfcState state{}; | ||
| 88 | std::vector<u8> data{}; | ||
| 89 | }; | ||
| 90 | |||
| 83 | struct ControllerMotion { | 91 | struct 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 | ||
| 124 | enum class ControllerTriggerType { | 134 | enum 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 | ||
| 290 | Common::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 | |||
| 290 | void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { | 304 | void 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 | |||
| 85 | Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); | 85 | Common::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 | */ | ||
| 93 | Common::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 | ||
| 430 | CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { | 430 | CharInfo 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 | ||
| 513 | Ver3StoreData 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 | |||
| 593 | bool 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 | |||
| 507 | ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { | 658 | ResultVal<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) { | |||
| 70 | NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | 73 | NTAG215File 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) { | |||
| 99 | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { | 103 | EncryptedNTAG215File 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 | ||
| 128 | u32 GetTagPassword(const TagUuid& uuid) { | 133 | u32 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 | ||
| 178 | void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, | 183 | void 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 | ||
| 10 | struct mbedtls_md_context_t; | 10 | struct mbedtls_md_context_t; |
| 11 | 11 | ||
| @@ -22,10 +22,12 @@ using HmacKey = std::array<u8, 0x10>; | |||
| 22 | using DrgbOutput = std::array<u8, 0x20>; | 22 | using DrgbOutput = std::array<u8, 0x20>; |
| 23 | 23 | ||
| 24 | struct HashSeed { | 24 | struct 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 | }; |
| 31 | static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); | 33 | static_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 | ||
| 22 | namespace Service::NFP { | 9 | namespace Service::NFP { |
| 23 | namespace ErrCodes { | ||
| 24 | constexpr Result DeviceNotFound(ErrorModule::NFP, 64); | ||
| 25 | constexpr Result WrongDeviceState(ErrorModule::NFP, 73); | ||
| 26 | constexpr Result NfcDisabled(ErrorModule::NFP, 80); | ||
| 27 | constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); | ||
| 28 | constexpr Result TagRemoved(ErrorModule::NFP, 97); | ||
| 29 | constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | ||
| 30 | constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); | ||
| 31 | constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); | ||
| 32 | } // namespace ErrCodes | ||
| 33 | |||
| 34 | IUser::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 | |||
| 69 | void 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 | |||
| 81 | void 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 | |||
| 93 | void 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 | |||
| 120 | void 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 | |||
| 146 | void 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 | |||
| 171 | void 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 | |||
| 199 | void 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 | |||
| 224 | void 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 | |||
| 251 | void 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 | |||
| 279 | void 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 | |||
| 306 | void 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 | |||
| 331 | void 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 | |||
| 359 | void 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 | |||
| 386 | void 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 | |||
| 413 | void 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 | |||
| 440 | void 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 | |||
| 467 | void 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 | |||
| 492 | void 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 | |||
| 517 | void 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 | |||
| 525 | void 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 | |||
| 544 | void 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 | |||
| 569 | void 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 | |||
| 588 | void 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 | |||
| 602 | void 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 | |||
| 630 | Module::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 | |||
| 638 | Module::Interface::~Interface() = default; | ||
| 639 | |||
| 640 | void 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 | |||
| 648 | bool 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 | |||
| 675 | bool 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 | |||
| 690 | void 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 | |||
| 700 | Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { | ||
| 701 | return activate_event->GetReadableEvent(); | ||
| 702 | } | ||
| 703 | |||
| 704 | Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const { | ||
| 705 | return deactivate_event->GetReadableEvent(); | ||
| 706 | } | ||
| 707 | |||
| 708 | void 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 | |||
| 716 | void 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 | |||
| 726 | Result 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 | |||
| 742 | Result 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 | |||
| 759 | Result 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 | |||
| 815 | Result 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); | 11 | class IUserManager final : public ServiceFramework<IUserManager> { |
| 822 | LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded); | 12 | public: |
| 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 | |||
| 829 | Result 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 | |||
| 841 | Result 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 | |||
| 857 | Result 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 | |||
| 889 | Result 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 | |||
| 907 | Result 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 | 23 | private: |
| 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 | ||
| 946 | Result 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 | |||
| 975 | Result 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 | |||
| 994 | Result 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 | |||
| 1017 | Result 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 | |||
| 1042 | Result 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 | |||
| 1062 | u64 Module::Interface::GetHandle() const { | ||
| 1063 | // Generate a handle based of the npad id | ||
| 1064 | return static_cast<u64>(npad_id); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | DeviceState Module::Interface::GetCurrentState() const { | ||
| 1068 | return device_state; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | Core::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 | |||
| 1077 | AmiiboName 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 | ||
| 1093 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { | 39 | void 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 | ||
| 15 | namespace Kernel { | ||
| 16 | class KEvent; | ||
| 17 | class KReadableEvent; | ||
| 18 | } // namespace Kernel | ||
| 19 | |||
| 20 | namespace Core::HID { | ||
| 21 | enum class NpadIdType : u32; | ||
| 22 | } // namespace Core::HID | ||
| 23 | |||
| 24 | namespace Service::NFP { | 8 | namespace Service::NFP { |
| 25 | using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; | ||
| 26 | |||
| 27 | struct 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 | }; | ||
| 35 | static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); | ||
| 36 | |||
| 37 | struct 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 | }; | ||
| 46 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 47 | |||
| 48 | struct 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 | }; | ||
| 57 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||
| 58 | |||
| 59 | struct 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 | }; | ||
| 68 | static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); | ||
| 69 | |||
| 70 | class Module final { | ||
| 71 | public: | ||
| 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 | |||
| 132 | class IUser final : public ServiceFramework<IUser> { | ||
| 133 | public: | ||
| 134 | explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); | ||
| 135 | |||
| 136 | private: | ||
| 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 | ||
| 171 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 10 | void 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 | |||
| 29 | namespace Service::NFP { | ||
| 30 | NfpDevice::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 | |||
| 50 | NfpDevice::~NfpDevice() { | ||
| 51 | if (!is_controller_set) { | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | npad_device->DeleteCallback(callback_key); | ||
| 55 | is_controller_set = false; | ||
| 56 | }; | ||
| 57 | |||
| 58 | void 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 | |||
| 88 | bool 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 | |||
| 112 | void 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 | |||
| 126 | Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { | ||
| 127 | return activate_event->GetReadableEvent(); | ||
| 128 | } | ||
| 129 | |||
| 130 | Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { | ||
| 131 | return deactivate_event->GetReadableEvent(); | ||
| 132 | } | ||
| 133 | |||
| 134 | void NfpDevice::Initialize() { | ||
| 135 | device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; | ||
| 136 | encrypted_tag_data = {}; | ||
| 137 | tag_data = {}; | ||
| 138 | } | ||
| 139 | |||
| 140 | void 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 | |||
| 150 | Result 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 | |||
| 162 | Result 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 | |||
| 178 | Result 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 | |||
| 222 | Result 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 | |||
| 238 | Result 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 | |||
| 256 | Result 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 | |||
| 272 | Result 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 | |||
| 303 | Result 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 | |||
| 320 | Result 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 | |||
| 357 | Result 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 | |||
| 387 | Result 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 | |||
| 406 | Result 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 | |||
| 427 | Result 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 | |||
| 456 | Result 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 | |||
| 490 | Result 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 | |||
| 531 | Result 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 | |||
| 548 | Result 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 | |||
| 584 | Result 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 | |||
| 609 | u64 NfpDevice::GetHandle() const { | ||
| 610 | // Generate a handle based of the npad id | ||
| 611 | return static_cast<u64>(npad_id); | ||
| 612 | } | ||
| 613 | |||
| 614 | u32 NfpDevice::GetApplicationAreaSize() const { | ||
| 615 | // Investigate if this value is really constant | ||
| 616 | return sizeof(ApplicationArea); | ||
| 617 | } | ||
| 618 | |||
| 619 | DeviceState NfpDevice::GetCurrentState() const { | ||
| 620 | return device_state; | ||
| 621 | } | ||
| 622 | |||
| 623 | Core::HID::NpadIdType NfpDevice::GetNpadId() const { | ||
| 624 | return npad_id; | ||
| 625 | } | ||
| 626 | |||
| 627 | AmiiboName 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 | |||
| 643 | void 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 | |||
| 657 | AmiiboDate 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 | |||
| 15 | namespace Kernel { | ||
| 16 | class KEvent; | ||
| 17 | class KReadableEvent; | ||
| 18 | } // namespace Kernel | ||
| 19 | |||
| 20 | namespace Core { | ||
| 21 | class System; | ||
| 22 | } // namespace Core | ||
| 23 | |||
| 24 | namespace Core::HID { | ||
| 25 | class EmulatedController; | ||
| 26 | enum class ControllerTriggerType; | ||
| 27 | enum class NpadIdType : u32; | ||
| 28 | } // namespace Core::HID | ||
| 29 | |||
| 30 | namespace Service::NFP { | ||
| 31 | class NfpDevice { | ||
| 32 | public: | ||
| 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 | |||
| 71 | private: | ||
| 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 | |||
| 8 | namespace Service::NFP { | ||
| 9 | |||
| 10 | constexpr Result DeviceNotFound(ErrorModule::NFP, 64); | ||
| 11 | constexpr Result WrongDeviceState(ErrorModule::NFP, 73); | ||
| 12 | constexpr Result NfcDisabled(ErrorModule::NFP, 80); | ||
| 13 | constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); | ||
| 14 | constexpr Result TagRemoved(ErrorModule::NFP, 97); | ||
| 15 | constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); | ||
| 16 | constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | ||
| 17 | constexpr Result CorruptedData(ErrorModule::NFP, 144); | ||
| 18 | constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); | ||
| 19 | constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); | ||
| 20 | constexpr 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 | ||
| 10 | namespace Service::NFP { | 11 | namespace 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 | ||
| 38 | enum class MountTarget : u32 { | 39 | enum 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 | ||
| 76 | using TagUuid = std::array<u8, 10>; | 78 | enum 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 | |||
| 87 | enum class TagProtocol : u32 { | ||
| 88 | None, | ||
| 89 | TypeA, // ISO14443A | ||
| 90 | TypeB, // ISO14443B | ||
| 91 | TypeF, // Sony Felica | ||
| 92 | }; | ||
| 93 | |||
| 94 | using UniqueSerialNumber = std::array<u8, 7>; | ||
| 95 | using LockBytes = std::array<u8, 2>; | ||
| 77 | using HashData = std::array<u8, 0x20>; | 96 | using HashData = std::array<u8, 0x20>; |
| 78 | using ApplicationArea = std::array<u8, 0xD8>; | 97 | using ApplicationArea = std::array<u8, 0xD8>; |
| 98 | using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; | ||
| 99 | |||
| 100 | struct TagUuid { | ||
| 101 | UniqueSerialNumber uid; | ||
| 102 | u8 nintendo_id; | ||
| 103 | LockBytes lock_bytes; | ||
| 104 | }; | ||
| 105 | static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); | ||
| 79 | 106 | ||
| 80 | struct AmiiboDate { | 107 | struct 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 | }; |
| 93 | static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); | 137 | static_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) |
| 135 | struct EncryptedAmiiboFile { | 179 | struct 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 | }; |
| 152 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); | 196 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); |
| 153 | 197 | ||
| 154 | struct NTAG215File { | 198 | struct 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 | |||
| 194 | static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, | 239 | static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, |
| 195 | "EncryptedNTAG215File must be trivially copyable."); | 240 | "EncryptedNTAG215File must be trivially copyable."); |
| 196 | 241 | ||
| 242 | struct 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 | }; | ||
| 251 | static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); | ||
| 252 | |||
| 253 | struct WriteDate { | ||
| 254 | u16 year; | ||
| 255 | u8 month; | ||
| 256 | u8 day; | ||
| 257 | }; | ||
| 258 | static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); | ||
| 259 | |||
| 260 | struct 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 | }; | ||
| 268 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 269 | |||
| 270 | struct 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 | }; | ||
| 278 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||
| 279 | |||
| 280 | struct 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 | }; | ||
| 287 | static_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 | ||
| 6 | namespace Service::NFP { | 19 | namespace Service::NFP { |
| 7 | 20 | ||
| 8 | NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_) | 21 | IUser::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 | |||
| 61 | void 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 | |||
| 74 | void 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 | |||
| 87 | void 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 | |||
| 121 | void 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 | |||
| 146 | void 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 | |||
| 170 | void 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 | |||
| 197 | void 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 | ||
| 16 | NFP_User::~NFP_User() = default; | 221 | void 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 | |||
| 246 | void 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 | |||
| 274 | void 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 | |||
| 299 | void 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 | |||
| 323 | void 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 | |||
| 347 | void 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 | |||
| 374 | void 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 | |||
| 400 | void 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 | |||
| 426 | void 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 | |||
| 452 | void 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 | |||
| 478 | void 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 | |||
| 502 | void 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 | |||
| 526 | void 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 | |||
| 534 | void 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 | |||
| 552 | void 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 | |||
| 576 | void 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 | |||
| 594 | void 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 | |||
| 608 | void 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 | |||
| 635 | std::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 | ||
| 8 | namespace Service::NFP { | 10 | namespace Service::NFP { |
| 11 | class NfpDevice; | ||
| 9 | 12 | ||
| 10 | class NFP_User final : public Module::Interface { | 13 | class IUser final : public ServiceFramework<IUser> { |
| 11 | public: | 14 | public: |
| 12 | explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_); | 15 | explicit IUser(Core::System& system_); |
| 13 | ~NFP_User() override; | 16 | |
| 17 | private: | ||
| 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 | |||
| 14 | namespace InputCommon { | ||
| 15 | constexpr PadIdentifier identifier = { | ||
| 16 | .guid = Common::UUID{}, | ||
| 17 | .port = 0, | ||
| 18 | .pad = 0, | ||
| 19 | }; | ||
| 20 | |||
| 21 | VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} | ||
| 22 | |||
| 23 | VirtualAmiibo::~VirtualAmiibo() = default; | ||
| 24 | |||
| 25 | Common::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 | |||
| 43 | Common::Input::NfcState VirtualAmiibo::SupportsNfc( | ||
| 44 | [[maybe_unused]] const PadIdentifier& identifier_) const { | ||
| 45 | return Common::Input::NfcState::Success; | ||
| 46 | } | ||
| 47 | |||
| 48 | Common::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 | |||
| 66 | VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { | ||
| 67 | return state; | ||
| 68 | } | ||
| 69 | |||
| 70 | VirtualAmiibo::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 | |||
| 94 | VirtualAmiibo::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 | |||
| 13 | namespace Common::FS { | ||
| 14 | class IOFile; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace InputCommon { | ||
| 18 | |||
| 19 | class VirtualAmiibo final : public InputEngine { | ||
| 20 | public: | ||
| 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 | |||
| 52 | private: | ||
| 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 | ||
| 105 | void 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 | |||
| 105 | bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { | 116 | bool 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 | ||
| 203 | Common::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 | |||
| 192 | void InputEngine::ResetButtonState() { | 215 | void 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 | ||
| 381 | void 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 | |||
| 358 | bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, | 395 | bool 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 | ||
| 47 | namespace std { | 48 | namespace 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 | ||
| 708 | class InputFromNfc final : public Common::Input::InputDevice { | ||
| 709 | public: | ||
| 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 | |||
| 743 | private: | ||
| 744 | const PadIdentifier identifier; | ||
| 745 | int callback_key; | ||
| 746 | InputEngine* input_engine; | ||
| 747 | }; | ||
| 748 | |||
| 708 | class OutputFromIdentifier final : public Common::Input::OutputDevice { | 749 | class OutputFromIdentifier final : public Common::Input::OutputDevice { |
| 709 | public: | 750 | public: |
| 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 | |||
| 730 | private: | 779 | private: |
| 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 | ||
| 1030 | std::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 | |||
| 981 | InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_) | 1042 | InputFactory::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 | ||
| 418 | VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { | ||
| 419 | return impl->virtual_amiibo.get(); | ||
| 420 | } | ||
| 421 | |||
| 422 | const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const { | ||
| 423 | return impl->virtual_amiibo.get(); | ||
| 424 | } | ||
| 425 | |||
| 405 | std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { | 426 | std::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; | |||
| 33 | class Keyboard; | 33 | class Keyboard; |
| 34 | class Mouse; | 34 | class Mouse; |
| 35 | class TouchScreen; | 35 | class TouchScreen; |
| 36 | class VirtualAmiibo; | ||
| 36 | struct MappingData; | 37 | struct 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 | ||
| 3254 | void GMainWindow::LoadAmiibo(const QString& filename) { | 3249 | void 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 | ||