input_common: Create virtual amiibo driver
This commit is contained in:
		| @@ -76,6 +76,19 @@ enum class PollingError { | |||||||
|     Unknown, |     Unknown, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // Nfc reply from the controller | ||||||
|  | enum class NfcState { | ||||||
|  |     Success, | ||||||
|  |     NewAmiibo, | ||||||
|  |     WaitingForAmiibo, | ||||||
|  |     AmiiboRemoved, | ||||||
|  |     NotAnAmiibo, | ||||||
|  |     NotSupported, | ||||||
|  |     WrongDeviceState, | ||||||
|  |     WriteFailed, | ||||||
|  |     Unknown, | ||||||
|  | }; | ||||||
|  |  | ||||||
| // Ir camera reply from the controller | // Ir camera reply from the controller | ||||||
| enum class CameraError { | enum class CameraError { | ||||||
|     None, |     None, | ||||||
| @@ -202,6 +215,11 @@ struct CameraStatus { | |||||||
|     std::vector<u8> data{}; |     std::vector<u8> data{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct NfcStatus { | ||||||
|  |     NfcState state{}; | ||||||
|  |     std::vector<u8> data{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
| // List of buttons to be passed to Qt that can be translated | // List of buttons to be passed to Qt that can be translated | ||||||
| enum class ButtonNames { | enum class ButtonNames { | ||||||
|     Undefined, |     Undefined, | ||||||
| @@ -260,6 +278,7 @@ struct CallbackStatus { | |||||||
|     BatteryStatus battery_status{}; |     BatteryStatus battery_status{}; | ||||||
|     VibrationStatus vibration_status{}; |     VibrationStatus vibration_status{}; | ||||||
|     CameraStatus camera_status{}; |     CameraStatus camera_status{}; | ||||||
|  |     NfcStatus nfc_status{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Triggered once every input change | // Triggered once every input change | ||||||
| @@ -312,6 +331,14 @@ public: | |||||||
|     virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { |     virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { | ||||||
|         return CameraError::NotSupported; |         return CameraError::NotSupported; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     virtual NfcState SupportsNfc() { | ||||||
|  |         return NfcState::NotSupported; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) { | ||||||
|  |         return NfcState::NotSupported; | ||||||
|  |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// An abstract class template for a factory that can create input devices. | /// An abstract class template for a factory that can create input devices. | ||||||
|   | |||||||
| @@ -18,6 +18,8 @@ add_library(input_common STATIC | |||||||
|     drivers/touch_screen.h |     drivers/touch_screen.h | ||||||
|     drivers/udp_client.cpp |     drivers/udp_client.cpp | ||||||
|     drivers/udp_client.h |     drivers/udp_client.h | ||||||
|  |     drivers/virtual_amiibo.cpp | ||||||
|  |     drivers/virtual_amiibo.h | ||||||
|     helpers/stick_from_buttons.cpp |     helpers/stick_from_buttons.cpp | ||||||
|     helpers/stick_from_buttons.h |     helpers/stick_from_buttons.h | ||||||
|     helpers/touch_from_buttons.cpp |     helpers/touch_from_buttons.cpp | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								src/input_common/drivers/virtual_amiibo.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/input_common/drivers/virtual_amiibo.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-3.0-or-later | ||||||
|  |  | ||||||
|  | #include <cstring> | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include "common/fs/file.h" | ||||||
|  | #include "common/fs/fs.h" | ||||||
|  | #include "common/fs/path_util.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/settings.h" | ||||||
|  | #include "input_common/drivers/virtual_amiibo.h" | ||||||
|  |  | ||||||
|  | namespace InputCommon { | ||||||
|  | constexpr PadIdentifier identifier = { | ||||||
|  |     .guid = Common::UUID{}, | ||||||
|  |     .port = 0, | ||||||
|  |     .pad = 0, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} | ||||||
|  |  | ||||||
|  | VirtualAmiibo::~VirtualAmiibo() {} | ||||||
|  |  | ||||||
|  | Common::Input::PollingError VirtualAmiibo::SetPollingMode( | ||||||
|  |     [[maybe_unused]] const PadIdentifier& identifier_, | ||||||
|  |     const Common::Input::PollingMode polling_mode_) { | ||||||
|  |     polling_mode = polling_mode_; | ||||||
|  |  | ||||||
|  |     if (polling_mode == Common::Input::PollingMode::NFC) { | ||||||
|  |         if (state == State::Initialized) { | ||||||
|  |             state = State::WaitingForAmiibo; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if (state == State::AmiiboIsOpen) { | ||||||
|  |             CloseAmiibo(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Common::Input::PollingError::None; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Common::Input::NfcState VirtualAmiibo::SupportsNfc( | ||||||
|  |     [[maybe_unused]] const PadIdentifier& identifier_) { | ||||||
|  |     return Common::Input::NfcState::Success; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Common::Input::NfcState VirtualAmiibo::WriteNfcData( | ||||||
|  |     [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { | ||||||
|  |     const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, | ||||||
|  |                                          Common::FS::FileType::BinaryFile}; | ||||||
|  |  | ||||||
|  |     if (!amiibo_file.IsOpen()) { | ||||||
|  |         LOG_ERROR(Core, "Amiibo is already on use"); | ||||||
|  |         return Common::Input::NfcState::WriteFailed; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!amiibo_file.Write(data)) { | ||||||
|  |         LOG_ERROR(Service_NFP, "Error writting to file"); | ||||||
|  |         return Common::Input::NfcState::WriteFailed; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Common::Input::NfcState::Success; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { | ||||||
|  |     return state; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { | ||||||
|  |     const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, | ||||||
|  |                                          Common::FS::FileType::BinaryFile}; | ||||||
|  |  | ||||||
|  |     if (state != State::WaitingForAmiibo) { | ||||||
|  |         return Info::WrongDeviceState; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!amiibo_file.IsOpen()) { | ||||||
|  |         return Info::UnableToLoad; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     amiibo_data.resize(amiibo_size); | ||||||
|  |  | ||||||
|  |     if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) { | ||||||
|  |         return Info::NotAnAmiibo; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     file_path = filename; | ||||||
|  |     state = State::AmiiboIsOpen; | ||||||
|  |     SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); | ||||||
|  |     return Info::Success; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { | ||||||
|  |     state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo | ||||||
|  |                                                             : State::Initialized; | ||||||
|  |     SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); | ||||||
|  |     return Info::Success; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace InputCommon | ||||||
							
								
								
									
										61
									
								
								src/input_common/drivers/virtual_amiibo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/input_common/drivers/virtual_amiibo.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-3.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "input_common/input_engine.h" | ||||||
|  |  | ||||||
|  | namespace Common::FS { | ||||||
|  | class IOFile; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | namespace InputCommon { | ||||||
|  |  | ||||||
|  | class VirtualAmiibo final : public InputEngine { | ||||||
|  | public: | ||||||
|  |     enum class State { | ||||||
|  |         Initialized, | ||||||
|  |         WaitingForAmiibo, | ||||||
|  |         AmiiboIsOpen, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     enum class Info { | ||||||
|  |         Success, | ||||||
|  |         UnableToLoad, | ||||||
|  |         NotAnAmiibo, | ||||||
|  |         WrongDeviceState, | ||||||
|  |         Unknown, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     explicit VirtualAmiibo(std::string input_engine_); | ||||||
|  |     ~VirtualAmiibo() override; | ||||||
|  |  | ||||||
|  |     // Sets polling mode to a controller | ||||||
|  |     Common::Input::PollingError SetPollingMode( | ||||||
|  |         const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; | ||||||
|  |  | ||||||
|  |     Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) override; | ||||||
|  |  | ||||||
|  |     Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, | ||||||
|  |                                          const std::vector<u8>& data) override; | ||||||
|  |  | ||||||
|  |     State GetCurrentState() const; | ||||||
|  |  | ||||||
|  |     Info LoadAmiibo(const std::string& amiibo_file); | ||||||
|  |     Info CloseAmiibo(); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     static constexpr std::size_t amiibo_size = 0x21C; | ||||||
|  |     static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; | ||||||
|  |  | ||||||
|  |     std::string file_path{}; | ||||||
|  |     State state{State::Initialized}; | ||||||
|  |     std::vector<u8> amiibo_data; | ||||||
|  |     Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; | ||||||
|  | }; | ||||||
|  | } // namespace InputCommon | ||||||
| @@ -102,6 +102,17 @@ void InputEngine::SetCamera(const PadIdentifier& identifier, | |||||||
|     TriggerOnCameraChange(identifier, value); |     TriggerOnCameraChange(identifier, value); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) { | ||||||
|  |     { | ||||||
|  |         std::scoped_lock lock{mutex}; | ||||||
|  |         ControllerData& controller = controller_list.at(identifier); | ||||||
|  |         if (!configuring) { | ||||||
|  |             controller.nfc = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     TriggerOnNfcChange(identifier, value); | ||||||
|  | } | ||||||
|  |  | ||||||
| bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { | bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { | ||||||
|     std::scoped_lock lock{mutex}; |     std::scoped_lock lock{mutex}; | ||||||
|     const auto controller_iter = controller_list.find(identifier); |     const auto controller_iter = controller_list.find(identifier); | ||||||
| @@ -189,6 +200,18 @@ Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifi | |||||||
|     return controller.camera; |     return controller.camera; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const { | ||||||
|  |     std::scoped_lock lock{mutex}; | ||||||
|  |     const auto controller_iter = controller_list.find(identifier); | ||||||
|  |     if (controller_iter == controller_list.cend()) { | ||||||
|  |         LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), | ||||||
|  |                   identifier.pad, identifier.port); | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  |     const ControllerData& controller = controller_iter->second; | ||||||
|  |     return controller.nfc; | ||||||
|  | } | ||||||
|  |  | ||||||
| void InputEngine::ResetButtonState() { | void InputEngine::ResetButtonState() { | ||||||
|     for (const auto& controller : controller_list) { |     for (const auto& controller : controller_list) { | ||||||
|         for (const auto& button : controller.second.buttons) { |         for (const auto& button : controller.second.buttons) { | ||||||
| @@ -355,6 +378,20 @@ void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier, | ||||||
|  |                                      [[maybe_unused]] const Common::Input::NfcStatus& value) { | ||||||
|  |     std::scoped_lock lock{mutex_callback}; | ||||||
|  |     for (const auto& poller_pair : callback_list) { | ||||||
|  |         const InputIdentifier& poller = poller_pair.second; | ||||||
|  |         if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (poller.callback.on_change) { | ||||||
|  |             poller.callback.on_change(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, | bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, | ||||||
|                                          const PadIdentifier& identifier, EngineInputType type, |                                          const PadIdentifier& identifier, EngineInputType type, | ||||||
|                                          int index) const { |                                          int index) const { | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ enum class EngineInputType { | |||||||
|     Camera, |     Camera, | ||||||
|     HatButton, |     HatButton, | ||||||
|     Motion, |     Motion, | ||||||
|  |     Nfc, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| namespace std { | namespace std { | ||||||
| @@ -127,6 +128,17 @@ public: | |||||||
|         return Common::Input::CameraError::NotSupported; |         return Common::Input::CameraError::NotSupported; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Request nfc data from a controller | ||||||
|  |     virtual Common::Input::NfcState SupportsNfc([[maybe_unused]] const PadIdentifier& identifier) { | ||||||
|  |         return Common::Input::NfcState::NotSupported; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Writes data to an nfc tag | ||||||
|  |     virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, | ||||||
|  |                                                  [[maybe_unused]] const std::vector<u8>& data) { | ||||||
|  |         return Common::Input::NfcState::NotSupported; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Returns the engine name |     // Returns the engine name | ||||||
|     [[nodiscard]] const std::string& GetEngineName() const; |     [[nodiscard]] const std::string& GetEngineName() const; | ||||||
|  |  | ||||||
| @@ -183,6 +195,7 @@ public: | |||||||
|     Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; |     Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; | ||||||
|     BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; |     BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; | ||||||
|     Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; |     Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; | ||||||
|  |     Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; | ||||||
|  |  | ||||||
|     int SetCallback(InputIdentifier input_identifier); |     int SetCallback(InputIdentifier input_identifier); | ||||||
|     void SetMappingCallback(MappingCallback callback); |     void SetMappingCallback(MappingCallback callback); | ||||||
| @@ -195,6 +208,7 @@ protected: | |||||||
|     void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); |     void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); | ||||||
|     void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); |     void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); | ||||||
|     void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); |     void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); | ||||||
|  |     void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); | ||||||
|  |  | ||||||
|     virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { |     virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { | ||||||
|         return "Unknown"; |         return "Unknown"; | ||||||
| @@ -208,6 +222,7 @@ private: | |||||||
|         std::unordered_map<int, BasicMotion> motions; |         std::unordered_map<int, BasicMotion> motions; | ||||||
|         Common::Input::BatteryLevel battery{}; |         Common::Input::BatteryLevel battery{}; | ||||||
|         Common::Input::CameraStatus camera{}; |         Common::Input::CameraStatus camera{}; | ||||||
|  |         Common::Input::NfcStatus nfc{}; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); |     void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); | ||||||
| @@ -218,6 +233,7 @@ private: | |||||||
|                                const BasicMotion& value); |                                const BasicMotion& value); | ||||||
|     void TriggerOnCameraChange(const PadIdentifier& identifier, |     void TriggerOnCameraChange(const PadIdentifier& identifier, | ||||||
|                                const Common::Input::CameraStatus& value); |                                const Common::Input::CameraStatus& value); | ||||||
|  |     void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); | ||||||
|  |  | ||||||
|     bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, |     bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, | ||||||
|                                 const PadIdentifier& identifier, EngineInputType type, |                                 const PadIdentifier& identifier, EngineInputType type, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user