From e8d71712e7054748e7e18de9362de1f5a394b46b Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 19:46:49 -0500 Subject: input_common: Create virtual amiibo driver --- src/input_common/CMakeLists.txt | 2 + src/input_common/drivers/virtual_amiibo.cpp | 101 ++++++++++++++++++++++++++++ src/input_common/drivers/virtual_amiibo.h | 61 +++++++++++++++++ src/input_common/input_engine.cpp | 37 ++++++++++ src/input_common/input_engine.h | 16 +++++ 5 files changed, 217 insertions(+) create mode 100644 src/input_common/drivers/virtual_amiibo.cpp create mode 100644 src/input_common/drivers/virtual_amiibo.h (limited to 'src/input_common') 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 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 diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp new file mode 100644 index 000000000..8fadb1322 --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#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& 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 diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h new file mode 100644 index 000000000..5790e4a1f --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#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& 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 amiibo_data; + Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; +}; +} // 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, 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 { diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index f6b3c4610..9b8470c6f 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -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& 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 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, -- cgit v1.2.3 From da8864d00261894ebac8764786cd7fc51f8c566c Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 20:28:27 -0500 Subject: input_common: Enable virtual amiibo driver --- src/input_common/input_poller.cpp | 64 +++++++++++++++++++++++++++++++++++++++ src/input_common/input_poller.h | 10 ++++++ src/input_common/main.cpp | 21 +++++++++++++ src/input_common/main.h | 7 +++++ 4 files changed, 102 insertions(+) (limited to 'src/input_common') diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index ffb9b945e..a8eb1442b 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -705,6 +705,47 @@ private: InputEngine* input_engine; }; +class InputFromNfc final : public Common::Input::InputDevice { +public: + explicit InputFromNfc(PadIdentifier identifier_, InputEngine* input_engine_) + : identifier(identifier_), input_engine(input_engine_) { + UpdateCallback engine_callback{[this]() { OnChange(); }}; + const InputIdentifier input_identifier{ + .identifier = identifier, + .type = EngineInputType::Nfc, + .index = 0, + .callback = engine_callback, + }; + callback_key = input_engine->SetCallback(input_identifier); + } + + ~InputFromNfc() override { + input_engine->DeleteCallback(callback_key); + } + + Common::Input::NfcStatus GetStatus() const { + return input_engine->GetNfc(identifier); + } + + void ForceUpdate() override { + OnChange(); + } + + void OnChange() { + const Common::Input::CallbackStatus status{ + .type = Common::Input::InputType::Nfc, + .nfc_status = GetStatus(), + }; + + TriggerOnChange(status); + } + +private: + const PadIdentifier identifier; + int callback_key; + InputEngine* input_engine; +}; + class OutputFromIdentifier final : public Common::Input::OutputDevice { public: explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) @@ -727,6 +768,14 @@ public: return input_engine->SetCameraFormat(identifier, camera_format); } + Common::Input::NfcState SupportsNfc() override { + return input_engine->SupportsNfc(identifier); + } + + Common::Input::NfcState WriteNfcData(const std::vector& data) override { + return input_engine->WriteNfcData(identifier, data); + } + private: const PadIdentifier identifier; InputEngine* input_engine; @@ -978,6 +1027,18 @@ std::unique_ptr InputFactory::CreateCameraDevice( return std::make_unique(identifier, input_engine.get()); } +std::unique_ptr InputFactory::CreateNfcDevice( + const Common::ParamPackage& params) { + const PadIdentifier identifier = { + .guid = Common::UUID{params.Get("guid", "")}, + .port = static_cast(params.Get("port", 0)), + .pad = static_cast(params.Get("pad", 0)), + }; + + input_engine->PreSetController(identifier); + return std::make_unique(identifier, input_engine.get()); +} + InputFactory::InputFactory(std::shared_ptr input_engine_) : input_engine(std::move(input_engine_)) {} @@ -989,6 +1050,9 @@ std::unique_ptr InputFactory::Create( if (params.Has("camera")) { return CreateCameraDevice(params); } + if (params.Has("nfc")) { + return CreateNfcDevice(params); + } if (params.Has("button") && params.Has("axis")) { return CreateTriggerDevice(params); } 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: std::unique_ptr CreateCameraDevice( const Common::ParamPackage& params); + /** + * Creates a nfc device from the parameters given. + * @param params contains parameters for creating the device: + * - "guid": text string for identifying controllers + * - "port": port of the connected device + * - "pad": slot of the connected controller + * @returns a unique input device with the parameters specified + */ + std::unique_ptr CreateNfcDevice(const Common::ParamPackage& params); + std::shared_ptr input_engine; }; } // 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 @@ #include "input_common/drivers/tas_input.h" #include "input_common/drivers/touch_screen.h" #include "input_common/drivers/udp_client.h" +#include "input_common/drivers/virtual_amiibo.h" #include "input_common/helpers/stick_from_buttons.h" #include "input_common/helpers/touch_from_buttons.h" #include "input_common/input_engine.h" @@ -87,6 +88,15 @@ struct InputSubsystem::Impl { Common::Input::RegisterFactory(camera->GetEngineName(), camera_output_factory); + virtual_amiibo = std::make_shared("virtual_amiibo"); + virtual_amiibo->SetMappingCallback(mapping_callback); + virtual_amiibo_input_factory = std::make_shared(virtual_amiibo); + virtual_amiibo_output_factory = std::make_shared(virtual_amiibo); + Common::Input::RegisterFactory(virtual_amiibo->GetEngineName(), + virtual_amiibo_input_factory); + Common::Input::RegisterFactory(virtual_amiibo->GetEngineName(), + virtual_amiibo_output_factory); + #ifdef HAVE_SDL2 sdl = std::make_shared("sdl"); sdl->SetMappingCallback(mapping_callback); @@ -327,6 +337,7 @@ struct InputSubsystem::Impl { std::shared_ptr tas_input; std::shared_ptr udp_client; std::shared_ptr camera; + std::shared_ptr virtual_amiibo; std::shared_ptr keyboard_factory; std::shared_ptr mouse_factory; @@ -335,6 +346,7 @@ struct InputSubsystem::Impl { std::shared_ptr udp_client_input_factory; std::shared_ptr tas_input_factory; std::shared_ptr camera_input_factory; + std::shared_ptr virtual_amiibo_input_factory; std::shared_ptr keyboard_output_factory; std::shared_ptr mouse_output_factory; @@ -342,6 +354,7 @@ struct InputSubsystem::Impl { std::shared_ptr udp_client_output_factory; std::shared_ptr tas_output_factory; std::shared_ptr camera_output_factory; + std::shared_ptr virtual_amiibo_output_factory; #ifdef HAVE_SDL2 std::shared_ptr sdl; @@ -402,6 +415,14 @@ const Camera* InputSubsystem::GetCamera() const { return impl->camera.get(); } +VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { + return impl->virtual_amiibo.get(); +} + +const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const { + return impl->virtual_amiibo.get(); +} + std::vector InputSubsystem::GetInputDevices() const { return impl->GetInputDevices(); } 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; class Keyboard; class Mouse; class TouchScreen; +class VirtualAmiibo; struct MappingData; } // namespace InputCommon @@ -101,6 +102,12 @@ public: /// Retrieves the underlying camera input device. [[nodiscard]] const Camera* GetCamera() const; + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo(); + + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; + /** * Returns all available input devices that this Factory can create a new device with. * Each returned ParamPackage should have a `display` field used for display, a `engine` field -- cgit v1.2.3 From 3ce0ef04ddcb2420b61f8c6d22f8039fb7359856 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 24 Sep 2022 22:52:33 -0500 Subject: service: nfp: address comments --- src/input_common/drivers/virtual_amiibo.cpp | 4 ++-- src/input_common/drivers/virtual_amiibo.h | 2 +- src/input_common/input_engine.h | 3 ++- src/input_common/input_poller.cpp | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/input_common') diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 8fadb1322..0cd5129da 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -20,7 +20,7 @@ constexpr PadIdentifier identifier = { VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} -VirtualAmiibo::~VirtualAmiibo() {} +VirtualAmiibo::~VirtualAmiibo() = default; Common::Input::PollingError VirtualAmiibo::SetPollingMode( [[maybe_unused]] const PadIdentifier& identifier_, @@ -41,7 +41,7 @@ Common::Input::PollingError VirtualAmiibo::SetPollingMode( } Common::Input::NfcState VirtualAmiibo::SupportsNfc( - [[maybe_unused]] const PadIdentifier& identifier_) { + [[maybe_unused]] const PadIdentifier& identifier_) const { return Common::Input::NfcState::Success; } diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 5790e4a1f..9eac07544 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -39,7 +39,7 @@ public: 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 SupportsNfc(const PadIdentifier& identifier_) const override; Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, const std::vector& data) override; diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 9b8470c6f..cfbdb26bd 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -129,7 +129,8 @@ public: } // Request nfc data from a controller - virtual Common::Input::NfcState SupportsNfc([[maybe_unused]] const PadIdentifier& identifier) { + virtual Common::Input::NfcState SupportsNfc( + [[maybe_unused]] const PadIdentifier& identifier) const { return Common::Input::NfcState::NotSupported; } diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index a8eb1442b..75705b67e 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -768,7 +768,7 @@ public: return input_engine->SetCameraFormat(identifier, camera_format); } - Common::Input::NfcState SupportsNfc() override { + Common::Input::NfcState SupportsNfc() const override { return input_engine->SupportsNfc(identifier); } -- cgit v1.2.3