core: hid: Add nfc support to emulated controller
This commit is contained in:
		| @@ -131,13 +131,16 @@ void EmulatedController::LoadDevices() { | ||||
|     battery_params[RightIndex].Set("battery", true); | ||||
|  | ||||
|     camera_params = Common::ParamPackage{"engine:camera,camera:1"}; | ||||
|     nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; | ||||
|  | ||||
|     output_params[LeftIndex] = left_joycon; | ||||
|     output_params[RightIndex] = right_joycon; | ||||
|     output_params[2] = camera_params; | ||||
|     output_params[3] = nfc_params; | ||||
|     output_params[LeftIndex].Set("output", true); | ||||
|     output_params[RightIndex].Set("output", true); | ||||
|     output_params[2].Set("output", true); | ||||
|     output_params[3].Set("output", true); | ||||
|  | ||||
|     LoadTASParams(); | ||||
|  | ||||
| @@ -155,6 +158,7 @@ void EmulatedController::LoadDevices() { | ||||
|     std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), | ||||
|                    Common::Input::CreateDevice<Common::Input::InputDevice>); | ||||
|     camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params); | ||||
|     nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params); | ||||
|     std::transform(output_params.begin(), output_params.end(), output_devices.begin(), | ||||
|                    Common::Input::CreateDevice<Common::Input::OutputDevice>); | ||||
|  | ||||
| @@ -284,6 +288,16 @@ void EmulatedController::ReloadInput() { | ||||
|         camera_devices->ForceUpdate(); | ||||
|     } | ||||
|  | ||||
|     if (nfc_devices) { | ||||
|         if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { | ||||
|             nfc_devices->SetCallback({ | ||||
|                 .on_change = | ||||
|                     [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, | ||||
|             }); | ||||
|             nfc_devices->ForceUpdate(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Use a common UUID for TAS | ||||
|     static constexpr Common::UUID TAS_UUID = Common::UUID{ | ||||
|         {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; | ||||
| @@ -339,6 +353,8 @@ void EmulatedController::UnloadInput() { | ||||
|     for (auto& stick : tas_stick_devices) { | ||||
|         stick.reset(); | ||||
|     } | ||||
|     camera_devices.reset(); | ||||
|     nfc_devices.reset(); | ||||
| } | ||||
|  | ||||
| void EmulatedController::EnableConfiguration() { | ||||
| @@ -903,6 +919,25 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback | ||||
|     TriggerOnChange(ControllerTriggerType::IrSensor, true); | ||||
| } | ||||
|  | ||||
| void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { | ||||
|     std::unique_lock lock{mutex}; | ||||
|     controller.nfc_values = TransformToNfc(callback); | ||||
|  | ||||
|     if (is_configuring) { | ||||
|         lock.unlock(); | ||||
|         TriggerOnChange(ControllerTriggerType::Nfc, false); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     controller.nfc_state = { | ||||
|         controller.nfc_values.state, | ||||
|         controller.nfc_values.data, | ||||
|     }; | ||||
|  | ||||
|     lock.unlock(); | ||||
|     TriggerOnChange(ControllerTriggerType::Nfc, true); | ||||
| } | ||||
|  | ||||
| bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | ||||
|     if (device_index >= output_devices.size()) { | ||||
|         return false; | ||||
| @@ -980,6 +1015,10 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | ||||
| bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | ||||
|     LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); | ||||
|     auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | ||||
|     auto& nfc_output_device = output_devices[3]; | ||||
|  | ||||
|     nfc_output_device->SetPollingMode(polling_mode); | ||||
|  | ||||
|     return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; | ||||
| } | ||||
|  | ||||
| @@ -1000,6 +1039,32 @@ bool EmulatedController::SetCameraFormat( | ||||
|                camera_format)) == Common::Input::CameraError::None; | ||||
| } | ||||
|  | ||||
| bool EmulatedController::HasNfc() const { | ||||
|     const auto& nfc_output_device = output_devices[3]; | ||||
|  | ||||
|     switch (npad_type) { | ||||
|     case NpadStyleIndex::JoyconRight: | ||||
|     case NpadStyleIndex::JoyconDual: | ||||
|     case NpadStyleIndex::ProController: | ||||
|         break; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     const bool has_virtual_nfc = | ||||
|         npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld; | ||||
|     const bool is_virtual_nfc_supported = | ||||
|         nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported; | ||||
|  | ||||
|     return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); | ||||
| } | ||||
|  | ||||
| bool EmulatedController::WriteNfc(const std::vector<u8>& data) { | ||||
|     auto& nfc_output_device = output_devices[3]; | ||||
|  | ||||
|     return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; | ||||
| } | ||||
|  | ||||
| void EmulatedController::SetLedPattern() { | ||||
|     for (auto& device : output_devices) { | ||||
|         if (!device) { | ||||
| @@ -1363,6 +1428,11 @@ const CameraState& EmulatedController::GetCamera() const { | ||||
|     return controller.camera_state; | ||||
| } | ||||
|  | ||||
| const NfcState& EmulatedController::GetNfc() const { | ||||
|     std::scoped_lock lock{mutex}; | ||||
|     return controller.nfc_state; | ||||
| } | ||||
|  | ||||
| NpadColor EmulatedController::GetNpadColor(u32 color) { | ||||
|     return { | ||||
|         .r = static_cast<u8>((color >> 16) & 0xFF), | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|  | ||||
| namespace Core::HID { | ||||
| const std::size_t max_emulated_controllers = 2; | ||||
| const std::size_t output_devices = 3; | ||||
| const std::size_t output_devices_size = 4; | ||||
| struct ControllerMotionInfo { | ||||
|     Common::Input::MotionStatus raw_status{}; | ||||
|     MotionInput emulated{}; | ||||
| @@ -37,7 +37,8 @@ using TriggerDevices = | ||||
| using BatteryDevices = | ||||
|     std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||||
| using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; | ||||
| using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>; | ||||
| using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; | ||||
| using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; | ||||
|  | ||||
| using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; | ||||
| using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; | ||||
| @@ -45,7 +46,8 @@ using ControllerMotionParams = std::array<Common::ParamPackage, Settings::Native | ||||
| using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; | ||||
| using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||||
| using CameraParams = Common::ParamPackage; | ||||
| using OutputParams = std::array<Common::ParamPackage, output_devices>; | ||||
| using NfcParams = Common::ParamPackage; | ||||
| using OutputParams = std::array<Common::ParamPackage, output_devices_size>; | ||||
|  | ||||
| using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | ||||
| using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; | ||||
| @@ -55,6 +57,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native | ||||
| using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; | ||||
| using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; | ||||
| using CameraValues = Common::Input::CameraStatus; | ||||
| using NfcValues = Common::Input::NfcStatus; | ||||
| using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; | ||||
|  | ||||
| struct AnalogSticks { | ||||
| @@ -80,6 +83,11 @@ struct CameraState { | ||||
|     std::size_t sample{}; | ||||
| }; | ||||
|  | ||||
| struct NfcState { | ||||
|     Common::Input::NfcState state{}; | ||||
|     std::vector<u8> data{}; | ||||
| }; | ||||
|  | ||||
| struct ControllerMotion { | ||||
|     Common::Vec3f accel{}; | ||||
|     Common::Vec3f gyro{}; | ||||
| @@ -107,6 +115,7 @@ struct ControllerStatus { | ||||
|     BatteryValues battery_values{}; | ||||
|     VibrationValues vibration_values{}; | ||||
|     CameraValues camera_values{}; | ||||
|     NfcValues nfc_values{}; | ||||
|  | ||||
|     // Data for HID serices | ||||
|     HomeButtonState home_button_state{}; | ||||
| @@ -119,6 +128,7 @@ struct ControllerStatus { | ||||
|     ControllerColors colors_state{}; | ||||
|     BatteryLevelState battery_state{}; | ||||
|     CameraState camera_state{}; | ||||
|     NfcState nfc_state{}; | ||||
| }; | ||||
|  | ||||
| enum class ControllerTriggerType { | ||||
| @@ -130,6 +140,7 @@ enum class ControllerTriggerType { | ||||
|     Battery, | ||||
|     Vibration, | ||||
|     IrSensor, | ||||
|     Nfc, | ||||
|     Connected, | ||||
|     Disconnected, | ||||
|     Type, | ||||
| @@ -315,6 +326,9 @@ public: | ||||
|     /// Returns the latest camera status from the controller | ||||
|     const CameraState& GetCamera() const; | ||||
|  | ||||
|     /// Returns the latest ntag status from the controller | ||||
|     const NfcState& GetNfc() const; | ||||
|  | ||||
|     /** | ||||
|      * Sends a specific vibration to the output device | ||||
|      * @return true if vibration had no errors | ||||
| @@ -341,6 +355,12 @@ public: | ||||
|      */ | ||||
|     bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); | ||||
|  | ||||
|     /// Returns true if the device has nfc support | ||||
|     bool HasNfc() const; | ||||
|  | ||||
|     /// Returns true if the nfc tag was written | ||||
|     bool WriteNfc(const std::vector<u8>& data); | ||||
|  | ||||
|     /// Returns the led pattern corresponding to this emulated controller | ||||
|     LedPattern GetLedPattern() const; | ||||
|  | ||||
| @@ -424,6 +444,12 @@ private: | ||||
|      */ | ||||
|     void SetCamera(const Common::Input::CallbackStatus& callback); | ||||
|  | ||||
|     /** | ||||
|      * Updates the nfc status of the controller | ||||
|      * @param callback A CallbackStatus containing the nfc status | ||||
|      */ | ||||
|     void SetNfc(const Common::Input::CallbackStatus& callback); | ||||
|  | ||||
|     /** | ||||
|      * Converts a color format from bgra to rgba | ||||
|      * @param color in bgra format | ||||
| @@ -458,6 +484,7 @@ private: | ||||
|     TriggerParams trigger_params; | ||||
|     BatteryParams battery_params; | ||||
|     CameraParams camera_params; | ||||
|     NfcParams nfc_params; | ||||
|     OutputParams output_params; | ||||
|  | ||||
|     ButtonDevices button_devices; | ||||
| @@ -466,6 +493,7 @@ private: | ||||
|     TriggerDevices trigger_devices; | ||||
|     BatteryDevices battery_devices; | ||||
|     CameraDevices camera_devices; | ||||
|     NfcDevices nfc_devices; | ||||
|     OutputDevices output_devices; | ||||
|  | ||||
|     // TAS related variables | ||||
|   | ||||
| @@ -287,6 +287,20 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu | ||||
|     return camera; | ||||
| } | ||||
|  | ||||
| Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) { | ||||
|     Common::Input::NfcStatus nfc{}; | ||||
|     switch (callback.type) { | ||||
|     case Common::Input::InputType::Nfc: | ||||
|         nfc = callback.nfc_status; | ||||
|         break; | ||||
|     default: | ||||
|         LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return nfc; | ||||
| } | ||||
|  | ||||
| void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { | ||||
|     const auto& properties = analog.properties; | ||||
|     float& raw_value = analog.raw_value; | ||||
|   | ||||
| @@ -84,6 +84,14 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu | ||||
|  */ | ||||
| Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); | ||||
|  | ||||
| /** | ||||
|  * Converts raw input data into a valid nfc status. | ||||
|  * | ||||
|  * @param callback Supported callbacks: Nfc. | ||||
|  * @return A valid CameraObject object. | ||||
|  */ | ||||
| Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); | ||||
|  | ||||
| /** | ||||
|  * Converts raw analog data into a valid analog value | ||||
|  * @param analog An analog object containing raw data and properties | ||||
|   | ||||
		Reference in New Issue
	
	Block a user