input_common: Create virtual amiibo driver
This commit is contained in:
		| @@ -76,6 +76,19 @@ enum class PollingError { | ||||
|     Unknown, | ||||
| }; | ||||
|  | ||||
| // Nfc reply from the controller | ||||
| enum class NfcState { | ||||
|     Success, | ||||
|     NewAmiibo, | ||||
|     WaitingForAmiibo, | ||||
|     AmiiboRemoved, | ||||
|     NotAnAmiibo, | ||||
|     NotSupported, | ||||
|     WrongDeviceState, | ||||
|     WriteFailed, | ||||
|     Unknown, | ||||
| }; | ||||
|  | ||||
| // Ir camera reply from the controller | ||||
| enum class CameraError { | ||||
|     None, | ||||
| @@ -202,6 +215,11 @@ struct CameraStatus { | ||||
|     std::vector<u8> data{}; | ||||
| }; | ||||
|  | ||||
| struct NfcStatus { | ||||
|     NfcState state{}; | ||||
|     std::vector<u8> data{}; | ||||
| }; | ||||
|  | ||||
| // List of buttons to be passed to Qt that can be translated | ||||
| enum class ButtonNames { | ||||
|     Undefined, | ||||
| @@ -260,6 +278,7 @@ struct CallbackStatus { | ||||
|     BatteryStatus battery_status{}; | ||||
|     VibrationStatus vibration_status{}; | ||||
|     CameraStatus camera_status{}; | ||||
|     NfcStatus nfc_status{}; | ||||
| }; | ||||
|  | ||||
| // Triggered once every input change | ||||
| @@ -312,6 +331,14 @@ public: | ||||
|     virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { | ||||
|         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. | ||||
|   | ||||
| @@ -18,6 +18,8 @@ add_library(input_common STATIC | ||||
|     drivers/touch_screen.h | ||||
|     drivers/udp_client.cpp | ||||
|     drivers/udp_client.h | ||||
|     drivers/virtual_amiibo.cpp | ||||
|     drivers/virtual_amiibo.h | ||||
|     helpers/stick_from_buttons.cpp | ||||
|     helpers/stick_from_buttons.h | ||||
|     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); | ||||
| } | ||||
|  | ||||
| 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 { | ||||
|     std::scoped_lock lock{mutex}; | ||||
|     const auto controller_iter = controller_list.find(identifier); | ||||
| @@ -189,6 +200,18 @@ Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifi | ||||
|     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() { | ||||
|     for (const auto& controller : controller_list) { | ||||
|         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, | ||||
|                                          const PadIdentifier& identifier, EngineInputType type, | ||||
|                                          int index) const { | ||||
|   | ||||
| @@ -42,6 +42,7 @@ enum class EngineInputType { | ||||
|     Camera, | ||||
|     HatButton, | ||||
|     Motion, | ||||
|     Nfc, | ||||
| }; | ||||
|  | ||||
| namespace std { | ||||
| @@ -127,6 +128,17 @@ public: | ||||
|         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 | ||||
|     [[nodiscard]] const std::string& GetEngineName() const; | ||||
|  | ||||
| @@ -183,6 +195,7 @@ public: | ||||
|     Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; | ||||
|     BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; | ||||
|     Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; | ||||
|     Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; | ||||
|  | ||||
|     int SetCallback(InputIdentifier input_identifier); | ||||
|     void SetMappingCallback(MappingCallback callback); | ||||
| @@ -195,6 +208,7 @@ protected: | ||||
|     void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); | ||||
|     void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& 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 { | ||||
|         return "Unknown"; | ||||
| @@ -208,6 +222,7 @@ private: | ||||
|         std::unordered_map<int, BasicMotion> motions; | ||||
|         Common::Input::BatteryLevel battery{}; | ||||
|         Common::Input::CameraStatus camera{}; | ||||
|         Common::Input::NfcStatus nfc{}; | ||||
|     }; | ||||
|  | ||||
|     void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); | ||||
| @@ -218,6 +233,7 @@ private: | ||||
|                                const BasicMotion& value); | ||||
|     void TriggerOnCameraChange(const PadIdentifier& identifier, | ||||
|                                const Common::Input::CameraStatus& value); | ||||
|     void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); | ||||
|  | ||||
|     bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, | ||||
|                                 const PadIdentifier& identifier, EngineInputType type, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user