From ee3a96011efd29f7f8e2b707b9021688343b459b Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Fri, 29 May 2020 21:33:55 -0700 Subject: [PATCH] Controller: Add NeGcon support --- src/core/CMakeLists.txt | 2 + src/core/controller.cpp | 19 ++++ src/core/negcon.cpp | 246 ++++++++++++++++++++++++++++++++++++++++ src/core/negcon.h | 78 +++++++++++++ src/core/settings.cpp | 8 +- src/core/types.h | 1 + 6 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 src/core/negcon.cpp create mode 100644 src/core/negcon.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c770f5f21..d044e9650 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -54,6 +54,8 @@ add_library(core memory_card.h namco_guncon.cpp namco_guncon.h + negcon.cpp + negcon.h pad.cpp pad.h playstation_mouse.cpp diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 12e147fcd..9c38cc864 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -4,6 +4,7 @@ #include "digital_controller.h" #include "namco_guncon.h" #include "playstation_mouse.h" +#include "negcon.h" Controller::Controller() = default; @@ -54,6 +55,9 @@ std::unique_ptr<Controller> Controller::Create(System* system, ControllerType ty case ControllerType::PlayStationMouse: return PlayStationMouse::Create(system); + case ControllerType::NeGcon: + return NeGcon::Create(); + case ControllerType::None: default: return {}; @@ -86,6 +90,9 @@ Controller::AxisList Controller::GetAxisNames(ControllerType type) case ControllerType::PlayStationMouse: return PlayStationMouse::StaticGetAxisNames(); + case ControllerType::NeGcon: + return NeGcon::StaticGetAxisNames(); + case ControllerType::None: default: return {}; @@ -108,6 +115,9 @@ Controller::ButtonList Controller::GetButtonNames(ControllerType type) case ControllerType::PlayStationMouse: return PlayStationMouse::StaticGetButtonNames(); + case ControllerType::NeGcon: + return NeGcon::StaticGetButtonNames(); + case ControllerType::None: default: return {}; @@ -130,6 +140,9 @@ u32 Controller::GetVibrationMotorCount(ControllerType type) case ControllerType::PlayStationMouse: return PlayStationMouse::StaticGetVibrationMotorCount(); + case ControllerType::NeGcon: + return NeGcon::StaticGetVibrationMotorCount(); + case ControllerType::None: default: return 0; @@ -152,6 +165,9 @@ std::optional<s32> Controller::GetAxisCodeByName(ControllerType type, std::strin case ControllerType::PlayStationMouse: return PlayStationMouse::StaticGetAxisCodeByName(axis_name); + case ControllerType::NeGcon: + return NeGcon::StaticGetAxisCodeByName(axis_name); + case ControllerType::None: default: return std::nullopt; @@ -174,6 +190,9 @@ std::optional<s32> Controller::GetButtonCodeByName(ControllerType type, std::str case ControllerType::PlayStationMouse: return PlayStationMouse::StaticGetButtonCodeByName(button_name); + case ControllerType::NeGcon: + return NeGcon::StaticGetButtonCodeByName(button_name); + case ControllerType::None: default: return std::nullopt; diff --git a/src/core/negcon.cpp b/src/core/negcon.cpp new file mode 100644 index 000000000..85d235c41 --- /dev/null +++ b/src/core/negcon.cpp @@ -0,0 +1,246 @@ +#include "negcon.h" +#include "common/assert.h" +#include "common/log.h" +#include "common/state_wrapper.h" +#include <array> +#include <cmath> + +NeGcon::NeGcon() +{ + m_axis_state.fill(0x00); + m_axis_state[static_cast<u8>(Axis::Steering)] = 0x80; +} + +NeGcon::~NeGcon() = default; + +ControllerType NeGcon::GetType() const +{ + return ControllerType::NeGcon; +} + +std::optional<s32> NeGcon::GetAxisCodeByName(std::string_view axis_name) const +{ + return StaticGetAxisCodeByName(axis_name); +} + +std::optional<s32> NeGcon::GetButtonCodeByName(std::string_view button_name) const +{ + return StaticGetButtonCodeByName(button_name); +} + +void NeGcon::Reset() +{ + m_transfer_state = TransferState::Idle; +} + +bool NeGcon::DoState(StateWrapper& sw) +{ + if (!Controller::DoState(sw)) + return false; + + sw.Do(&m_button_state); + sw.Do(&m_transfer_state); + return true; +} + +void NeGcon::SetAxisState(s32 axis_code, float value) +{ + if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count)) + return; + + // Steering Axis: -1..1 -> 0..255 + if (axis_code == static_cast<s32>(Axis::Steering)) + { + const u8 u8_value = static_cast<u8>(std::clamp(((value + 1.0f) / 2.0f) * 255.0f, 0.0f, 255.0f)); + + SetAxisState(static_cast<Axis>(axis_code), u8_value); + + return; + } + + // I, II, L: 0..1 -> 0..255 or -1..0 -> 0..255 to support negative axis ranges, + // e.g. if bound to analog stick instead of trigger + const u8 u8_value = static_cast<u8>(std::clamp(std::abs(value) * 255.0f, 0.0f, 255.0f)); + + SetAxisState(static_cast<Axis>(axis_code), u8_value); +} + +void NeGcon::SetAxisState(Axis axis, u8 value) +{ + m_axis_state[static_cast<u8>(axis)] = value; +} + +void NeGcon::SetButtonState(s32 button_code, bool pressed) +{ + if (button_code < 0 || button_code >= static_cast<s32>(Button::Count)) + return; + + SetButtonState(static_cast<Button>(button_code), pressed); +} + +void NeGcon::SetButtonState(Button button, bool pressed) +{ + // Mapping of Button to index of corresponding bit in m_button_state + static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {3, 4, 5, 6, 7, 11, 12, 13}; + + if (pressed) + m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]); + else + m_button_state |= u16(1) << indices[static_cast<u8>(button)]; +} + +void NeGcon::ResetTransferState() +{ + m_transfer_state = TransferState::Idle; +} + +bool NeGcon::Transfer(const u8 data_in, u8* data_out) +{ + static constexpr u16 ID = 0x5A23; + + switch (m_transfer_state) + { + case TransferState::Idle: + { + if (data_in == 0x42) + { + *data_out = Truncate8(ID); + m_transfer_state = TransferState::IDMSB; + return true; + } + else + { + *data_out = 0xFF; + return (data_in == 0x01); + } + } + + case TransferState::IDMSB: + { + *data_out = Truncate8(ID >> 8); + m_transfer_state = TransferState::ButtonsLSB; + return true; + } + + case TransferState::ButtonsLSB: + { + *data_out = Truncate8(m_button_state); + m_transfer_state = TransferState::ButtonsMSB; + return true; + } + + case TransferState::ButtonsMSB: + { + *data_out = Truncate8(m_button_state >> 8); + m_transfer_state = TransferState::AnalogSteering; + return true; + } + + case TransferState::AnalogSteering: + { + *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::Steering)]); + m_transfer_state = TransferState::AnalogI; + return true; + } + + case TransferState::AnalogI: + { + *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::I)]); + m_transfer_state = TransferState::AnalogII; + return true; + } + + case TransferState::AnalogII: + { + *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::II)]); + m_transfer_state = TransferState::AnalogL; + return true; + } + + case TransferState::AnalogL: + { + *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::L)]); + m_transfer_state = TransferState::Idle; + return false; + } + + default: + { + UnreachableCode(); + return false; + } + } +} + +std::unique_ptr<NeGcon> NeGcon::Create() +{ + return std::make_unique<NeGcon>(); +} + +std::optional<s32> NeGcon::StaticGetAxisCodeByName(std::string_view axis_name) +{ +#define AXIS(name) \ + if (axis_name == #name) \ + { \ + return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \ + } + + AXIS(Steering); + AXIS(I); + AXIS(II); + AXIS(L); + + return std::nullopt; + +#undef AXIS +} + +std::optional<s32> NeGcon::StaticGetButtonCodeByName(std::string_view button_name) +{ +#define BUTTON(name) \ + if (button_name == #name) \ + { \ + return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \ + } + + BUTTON(Up); + BUTTON(Down); + BUTTON(Left); + BUTTON(Right); + BUTTON(A); + BUTTON(B); + BUTTON(R); + BUTTON(Start); + + return std::nullopt; + +#undef BUTTON +} + +Controller::AxisList NeGcon::StaticGetAxisNames() +{ +#define A(n) \ + { \ + #n, static_cast <s32>(Axis::n) \ + } + + return {A(Steering), A(I), A(II), A(L)}; + +#undef A +} + +Controller::ButtonList NeGcon::StaticGetButtonNames() +{ +#define B(n) \ + { \ + #n, static_cast <s32>(Button::n) \ + } + + return {B(Up), B(Down), B(Left), B(Right), B(A), B(B), B(R), B(Start)}; +#undef B +} + +u32 NeGcon::StaticGetVibrationMotorCount() +{ + return 0; +} diff --git a/src/core/negcon.h b/src/core/negcon.h new file mode 100644 index 000000000..0ff3388ef --- /dev/null +++ b/src/core/negcon.h @@ -0,0 +1,78 @@ +#pragma once +#include "controller.h" +#include <array> +#include <memory> +#include <optional> +#include <string_view> + +class NeGcon final : public Controller +{ +public: + enum class Axis : u8 + { + Steering = 0, + I = 1, + II = 2, + L = 3, + Count + }; + + enum class Button : u8 + { + Start = 0, + Up = 1, + Right = 2, + Down = 3, + Left = 4, + R = 5, + B = 6, + A = 7, + Count + }; + + NeGcon(); + ~NeGcon() override; + + static std::unique_ptr<NeGcon> Create(); + static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name); + static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name); + static AxisList StaticGetAxisNames(); + static ButtonList StaticGetButtonNames(); + static u32 StaticGetVibrationMotorCount(); + + ControllerType GetType() const override; + std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override; + std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override; + + void Reset() override; + bool DoState(StateWrapper& sw) override; + + void SetAxisState(s32 axis_code, float value) override; + void SetButtonState(s32 button_code, bool pressed) override; + + void ResetTransferState() override; + bool Transfer(const u8 data_in, u8* data_out) override; + + void SetAxisState(Axis axis, u8 value); + void SetButtonState(Button button, bool pressed); + +private: + enum class TransferState : u8 + { + Idle, + IDMSB, + ButtonsLSB, + ButtonsMSB, + AnalogSteering, + AnalogI, + AnalogII, + AnalogL + }; + + std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state {}; + + // buttons are active low; bits 0-2, 8-10, 14-15 are not used and are always high + u16 m_button_state = UINT16_C(0xFFFF); + + TransferState m_transfer_state = TransferState::Idle; +}; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 5a433e353..153f7bf81 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -419,10 +419,10 @@ const char* Settings::GetAudioBackendDisplayName(AudioBackend backend) return s_audio_backend_display_names[static_cast<int>(backend)]; } -static std::array<const char*, 5> s_controller_type_names = { - {"None", "DigitalController", "AnalogController", "NamcoGunCon", "PlayStationMouse"}}; -static std::array<const char*, 5> s_controller_display_names = { - {"None", "Digital Controller", "Analog Controller (DualShock)", "Namco GunCon", "PlayStation Mouse"}}; +static std::array<const char*, 6> s_controller_type_names = { + {"None", "DigitalController", "AnalogController", "NamcoGunCon", "PlayStationMouse", "NeGcon"}}; +static std::array<const char*, 6> s_controller_display_names = { + {"None", "Digital Controller", "Analog Controller (DualShock)", "Namco GunCon", "PlayStation Mouse", "NeGcon"}}; std::optional<ControllerType> Settings::ParseControllerTypeName(const char* str) { diff --git a/src/core/types.h b/src/core/types.h index 8eea33f13..ca78133d5 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -89,6 +89,7 @@ enum class ControllerType AnalogController, NamcoGunCon, PlayStationMouse, + NeGcon, Count };