From 7bc18c8538f5317174f87f4164b3e19b899bd7ee Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 12 Feb 2025 21:47:10 +1000 Subject: [PATCH] DDGoController: Rewrite to support axis input And add virtual buttons for each of the possible states. --- data/resources/gamedb.yaml | 28 +++ src/common/bitutils.h | 4 + src/core/CMakeLists.txt | 2 + src/core/controller.cpp | 9 +- src/core/core.vcxproj | 2 + src/core/core.vcxproj.filters | 2 + src/core/ddgo_controller.cpp | 363 ++++++++++++++++++++++++++++++++ src/core/ddgo_controller.h | 124 +++++++++++ src/core/digital_controller.cpp | 36 ---- src/util/imgui_glyph_ranges.inl | 2 +- 10 files changed, 532 insertions(+), 40 deletions(-) create mode 100644 src/core/ddgo_controller.cpp create mode 100644 src/core/ddgo_controller.h diff --git a/data/resources/gamedb.yaml b/data/resources/gamedb.yaml index dff859a686..752955d1ac 100644 --- a/data/resources/gamedb.yaml +++ b/data/resources/gamedb.yaml @@ -36447,6 +36447,7 @@ SCPS-45166: name: "Densha De Go! (aka Densha De Go! / Densya De Go! - Let's Go by Train!)" controllers: - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36465,6 +36466,7 @@ SCPS-45167: name: "Densha De Go! (aka Densha De Go! / Densya De Go! - Let's Go by Train!)" controllers: - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36483,6 +36485,7 @@ SLPS-01150: name: "Densha de Go! (Japan)" controllers: - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36499,12 +36502,16 @@ SLPS-01150: linkCable: false SLPM-80166: name: "Densha de Go! (Japan) (Demo)" + controllers: + - DigitalController + - DDGoController metadata: genre: "** DEMO **" SLPM-86263: name: "Densha de Go! (Japan) (PlayStation the Best)" controllers: - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36524,6 +36531,7 @@ SLPM-86142: controllers: - AnalogController - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36540,10 +36548,18 @@ SLPM-86142: linkCable: false SLPM-80346: name: "Densha de Go! 2 (Japan) (Demo 1)" + controllers: + - AnalogController + - DigitalController + - DDGoController metadata: genre: "** DEMO **" SLPM-80406: name: "Densha de Go! 2 (Japan) (Demo 2)" + controllers: + - AnalogController + - DigitalController + - DDGoController metadata: genre: "** DEMO **" SLPM-86615: @@ -36551,6 +36567,7 @@ SLPM-86615: controllers: - AnalogController - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36570,6 +36587,7 @@ SLPM-86141: controllers: - AnalogController - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36589,6 +36607,7 @@ SLPM-86424: controllers: - AnalogController - DigitalController + - DDGoController metadata: publisher: "Taito" developer: "Taito" @@ -36605,6 +36624,10 @@ SLPM-86424: linkCable: false SLPM-80521: name: "Densha de Go! Nagoya Tetsudou-hen (Japan) (Demo)" + controllers: + - AnalogController + - DigitalController + - DDGoController metadata: genre: "** DEMO **" SLPM-86378: @@ -36612,6 +36635,7 @@ SLPM-86378: controllers: - AnalogController - DigitalController + - DDGoController codes: - SLPM-86378 - SLPM-86705 @@ -36631,6 +36655,10 @@ SLPM-86378: linkCable: false SLPM-80511: name: "Densha de Go! Professional Shiyou (Japan) (Taikenban)" + controllers: + - AnalogController + - DigitalController + - DDGoController SCPS-18003: name: "Depth (Japan)" controllers: diff --git a/src/common/bitutils.h b/src/common/bitutils.h index 72743050f5..c21e7acaf6 100644 --- a/src/common/bitutils.h +++ b/src/common/bitutils.h @@ -109,6 +109,10 @@ ALWAYS_INLINE static constexpr u64 BoolToUInt64(bool value) { return static_cast(value); } +ALWAYS_INLINE static constexpr float BoolToFloat(bool value) +{ + return static_cast(value); +} // Integer to boolean template diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 09b276e3c8..3212880ab0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -32,6 +32,8 @@ add_library(core cpu_pgxp.h cpu_types.cpp cpu_types.h + ddgo_controller.cpp + ddgo_controller.h digital_controller.cpp digital_controller.h dma.cpp diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 54b8eb80ec..bb4f2a54b1 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -1,9 +1,10 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "controller.h" #include "analog_controller.h" #include "analog_joystick.h" +#include "ddgo_controller.h" #include "digital_controller.h" #include "game_database.h" #include "guncon.h" @@ -35,7 +36,7 @@ static constexpr std::array Controller::Create(ControllerType type, u32 index) { case ControllerType::DigitalController: case ControllerType::PopnController: - case ControllerType::DDGoController: return DigitalController::Create(index, type); case ControllerType::AnalogController: @@ -144,6 +144,9 @@ std::unique_ptr Controller::Create(ControllerType type, u32 index) case ControllerType::NeGconRumble: return NeGconRumble::Create(index); + case ControllerType::DDGoController: + return DDGoController::Create(index); + case ControllerType::JogCon: return JogCon::Create(index); diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 773874fc1b..a53974a145 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -28,6 +28,7 @@ true + @@ -108,6 +109,7 @@ true + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index f935c97010..0f07ee6049 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -67,6 +67,7 @@ + @@ -144,6 +145,7 @@ + diff --git a/src/core/ddgo_controller.cpp b/src/core/ddgo_controller.cpp new file mode 100644 index 0000000000..2856353eb3 --- /dev/null +++ b/src/core/ddgo_controller.cpp @@ -0,0 +1,363 @@ +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +#include "ddgo_controller.h" +#include "host.h" +#include "system.h" + +#include "util/state_wrapper.h" + +#include "IconsPromptFont.h" + +#include "common/assert.h" +#include "common/bitutils.h" +#include "common/settings_interface.h" + +DDGoController::DDGoController(u32 index) : Controller(index) +{ +} + +DDGoController::~DDGoController() = default; + +ControllerType DDGoController::GetType() const +{ + return ControllerType::DDGoController; +} + +void DDGoController::Reset() +{ + m_transfer_state = TransferState::Idle; + m_power_transition_frames_remaining = 0; + UpdatePowerBits(); + m_brake_transition_frames_remaining = 0; + UpdateBrakeBits(); +} + +bool DDGoController::DoState(StateWrapper& sw, bool apply_input_state) +{ + if (!Controller::DoState(sw, apply_input_state)) + return false; + + u16 button_state = m_button_state; + u8 power_level = m_power_level; + u8 power_transition_frames_remaining = m_power_transition_frames_remaining; + u8 brake_level = m_brake_level; + u8 brake_transition_frames_remaining = m_brake_transition_frames_remaining; + sw.Do(&button_state); + sw.Do(&power_level); + sw.Do(&power_transition_frames_remaining); + sw.Do(&brake_level); + sw.Do(&brake_transition_frames_remaining); + + if (apply_input_state) + { + m_button_state = button_state; + m_power_level = power_level; + m_power_transition_frames_remaining = power_transition_frames_remaining; + m_brake_level = brake_level; + m_brake_transition_frames_remaining = brake_transition_frames_remaining; + UpdatePowerBits(); + UpdateBrakeBits(); + } + + sw.Do(&m_transfer_state); + return true; +} + +float DDGoController::GetBindState(u32 index) const +{ + if (index >= static_cast(Bind::VirtualButtonStart)) + { + if (index < static_cast(Bind::VirtualBrakeReleased)) + return BoolToFloat(m_power_level == (index - static_cast(Bind::VirtualPowerOff))); + else + return BoolToFloat(m_brake_level == (index - static_cast(Bind::VirtualBrakeReleased))); + } + + // don't show the buttons set by the level + static constexpr u16 REPORT_MASK = (1u << static_cast(Bind::Start)) | (1u << static_cast(Bind::Select)) | + (1u << static_cast(Bind::A)) | (1u << static_cast(Bind::B)) | + (1u << static_cast(Bind::C)); + return static_cast((((m_button_state ^ 0xFFFFu) & REPORT_MASK) >> index) & 1u); +} + +void DDGoController::SetBindState(u32 index, float value) +{ + if (index == static_cast(Bind::Power)) + { + value = (value < m_analog_deadzone) ? 0.0f : (value * m_analog_sensitivity); + SetPowerLevel(std::min(static_cast(value * MAX_POWER_LEVEL), MAX_POWER_LEVEL)); + return; + } + else if (index == static_cast(Bind::Brake)) + { + value = (value < m_analog_deadzone) ? 0.0f : (value * m_analog_sensitivity); + SetBrakeLevel(std::min(static_cast(value * MAX_BRAKE_LEVEL), MAX_BRAKE_LEVEL)); + return; + } + else if (index >= static_cast(Bind::VirtualButtonStart)) + { + // vbutton, only handle press + if (value < 0.5f) + return; + + if (index < static_cast(Bind::VirtualBrakeReleased)) + SetPowerLevel(index - static_cast(Bind::VirtualPowerOff)); + else + SetBrakeLevel(index - static_cast(Bind::VirtualBrakeReleased)); + + return; + } + + const bool pressed = (value >= 0.5f); + const u16 bit = u16(1) << static_cast(index); + if (pressed) + { + if (m_button_state & bit) + System::SetRunaheadReplayFlag(); + + m_button_state &= ~bit; + } + else + { + if (!(m_button_state & bit)) + System::SetRunaheadReplayFlag(); + + m_button_state |= bit; + } +} + +u32 DDGoController::GetButtonStateBits() const +{ + return m_button_state ^ 0xFFFF; +} + +void DDGoController::ResetTransferState() +{ + m_transfer_state = TransferState::Idle; +} + +bool DDGoController::Transfer(const u8 data_in, u8* data_out) +{ + static constexpr u16 ID = 0x5A41; + + switch (m_transfer_state) + { + case TransferState::Idle: + { + *data_out = 0xFF; + + if (data_in == 0x01) + { + m_transfer_state = TransferState::Ready; + + // handle transition time + if (m_power_transition_frames_remaining > 0) + { + m_power_transition_frames_remaining--; + UpdatePowerBits(); + } + + if (m_brake_transition_frames_remaining > 0) + { + m_brake_transition_frames_remaining--; + UpdateBrakeBits(); + } + + return true; + } + return false; + } + + case TransferState::Ready: + { + if (data_in == 0x42) + { + *data_out = Truncate8(ID); + m_transfer_state = TransferState::IDMSB; + return true; + } + + *data_out = 0xFF; + return false; + } + + case TransferState::IDMSB: + { + *data_out = Truncate8(ID >> 8); + m_transfer_state = TransferState::ButtonsLSB; + return true; + } + + case TransferState::ButtonsLSB: + { + *data_out = Truncate8(m_button_state & BUTTON_MASK); + m_transfer_state = TransferState::ButtonsMSB; + return true; + } + + case TransferState::ButtonsMSB: + *data_out = Truncate8((m_button_state & BUTTON_MASK) >> 8); + m_transfer_state = TransferState::Idle; + return false; + + default: + UnreachableCode(); + } +} + +void DDGoController::SetPowerLevel(u32 level) +{ + DebugAssert(level <= MAX_POWER_LEVEL); + if (m_power_level == level) + return; + + m_power_level = Truncate8(level); + m_power_transition_frames_remaining = m_power_transition_frames; + UpdatePowerBits(); + System::SetRunaheadReplayFlag(); +} + +void DDGoController::UpdatePowerBits() +{ +#define POWER_BITS(b0, b1, b2) \ + static_cast(~(((b0) ? (1u << static_cast(Bind::PowerBit0)) : 0u) | \ + ((b1) ? (1u << static_cast(Bind::PowerBit1)) : 0u) | \ + ((b2) ? (1u << static_cast(Bind::PowerBit2)) : 0u)) & \ + POWER_MASK) + + static constexpr std::array POWER_TABLE = {{ + POWER_BITS(0, 1, 1), // N + POWER_BITS(1, 0, 1), // P1 + POWER_BITS(0, 0, 1), // P2 + POWER_BITS(1, 1, 0), // P3 + POWER_BITS(0, 1, 0), // P4 + POWER_BITS(1, 0, 0), // P5 + POWER_BITS(0, 0, 0), // Transition + }}; + +#undef POWER_BITS + + const u32 idx = (m_power_transition_frames_remaining > 0) ? (MAX_POWER_LEVEL + 1) : m_power_level; + m_button_state = (m_button_state & ~POWER_MASK) | POWER_TABLE[idx]; +} + +void DDGoController::SetBrakeLevel(u32 level) +{ + DebugAssert(level <= MAX_BRAKE_LEVEL); + if (m_brake_level == level) + return; + + m_brake_level = Truncate8(level); + m_brake_transition_frames_remaining = m_brake_transition_frames; + UpdateBrakeBits(); + System::SetRunaheadReplayFlag(); +} + +void DDGoController::UpdateBrakeBits() +{ +#define BRAKE_BITS(b0, b1, b2, b3) \ + static_cast(~(((b0) ? (1u << static_cast(Bind::BrakeBit0)) : 0u) | \ + ((b1) ? (1u << static_cast(Bind::BrakeBit1)) : 0u) | \ + ((b2) ? (1u << static_cast(Bind::BrakeBit2)) : 0u) | \ + ((b3) ? (1u << static_cast(Bind::BrakeBit3)) : 0u)) & \ + BRAKE_MASK) + + static constexpr std::array BRAKE_TABLE = {{ + BRAKE_BITS(0, 1, 1, 1), // Released + BRAKE_BITS(1, 0, 1, 1), // B1 + BRAKE_BITS(0, 0, 1, 1), // B2 + BRAKE_BITS(1, 1, 0, 1), // B3 + BRAKE_BITS(0, 1, 0, 1), // B4 + BRAKE_BITS(1, 0, 0, 1), // B5 + BRAKE_BITS(0, 0, 0, 1), // B6 + BRAKE_BITS(1, 1, 1, 0), // B7 + BRAKE_BITS(0, 1, 1, 0), // B8 + BRAKE_BITS(0, 0, 0, 0), // Emergency + BRAKE_BITS(1, 1, 1, 1), // Transition + }}; + +#undef BRAKE_BITS + + const u32 idx = (m_brake_transition_frames_remaining > 0) ? (MAX_BRAKE_LEVEL + 1) : m_brake_level; + m_button_state = (m_button_state & ~BRAKE_MASK) | BRAKE_TABLE[idx]; +} + +void DDGoController::LoadSettings(const SettingsInterface& si, const char* section, bool initial) +{ + Controller::LoadSettings(si, section, initial); + m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f); + m_analog_sensitivity = + std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f); + m_power_transition_frames = static_cast(si.GetIntValue(section, "PowerTransitionFrames", 0)); + m_brake_transition_frames = static_cast(si.GetIntValue(section, "BrakeTransitionFrames", 0)); +} + +std::unique_ptr DDGoController::Create(u32 index) +{ + return std::make_unique(index); +} + +static const Controller::ControllerBindingInfo s_binding_info[] = { +// clang-format off +#define BUTTON(name, display_name, icon_name, button, genb) \ + {name, display_name, icon_name, static_cast(button), InputBindingInfo::Type::Button, genb} +#define AXIS(name, display_name, icon_name, axis, genb) \ + {name, display_name, icon_name, static_cast(axis), InputBindingInfo::Type::HalfAxis, genb} + + BUTTON("Select", TRANSLATE_NOOP("DDGoController", "Select"), ICON_PF_SELECT_SHARE, DDGoController::Bind::Select, GenericInputBinding::Select), + BUTTON("Start", TRANSLATE_NOOP("DDGoController", "Start"), ICON_PF_START, DDGoController::Bind::Start, GenericInputBinding::Start), + BUTTON("A", TRANSLATE_NOOP("DDGoController", "A"), ICON_PF_BUTTON_A, DDGoController::Bind::A, GenericInputBinding::Square), + BUTTON("B", TRANSLATE_NOOP("DDGoController", "B"), ICON_PF_BUTTON_B, DDGoController::Bind::B, GenericInputBinding::Cross), + BUTTON("C", TRANSLATE_NOOP("DDGoController", "C"), ICON_PF_BUTTON_C, DDGoController::Bind::C, GenericInputBinding::Circle), + BUTTON("VirtualPowerOff", TRANSLATE_NOOP("DDGoController", "Power Off"), ICON_PF_KEY_N, DDGoController::Bind::VirtualPowerOff, GenericInputBinding::Unknown), + BUTTON("VirtualPower1", TRANSLATE_NOOP("DDGoController", "Power 1"), ICON_PF_1, DDGoController::Bind::VirtualPower1, GenericInputBinding::Unknown), + BUTTON("VirtualPower2", TRANSLATE_NOOP("DDGoController", "Power 2"), ICON_PF_2, DDGoController::Bind::VirtualPower2, GenericInputBinding::Unknown), + BUTTON("VirtualPower3", TRANSLATE_NOOP("DDGoController", "Power 3"), ICON_PF_3, DDGoController::Bind::VirtualPower3, GenericInputBinding::Unknown), + BUTTON("VirtualPower4", TRANSLATE_NOOP("DDGoController", "Power 4"), ICON_PF_4, DDGoController::Bind::VirtualPower4, GenericInputBinding::Unknown), + BUTTON("VirtualPower5", TRANSLATE_NOOP("DDGoController", "Power 5"), ICON_PF_5, DDGoController::Bind::VirtualPower5, GenericInputBinding::Unknown), + BUTTON("VirtualBrakeReleased", TRANSLATE_NOOP("DDGoController", "Brake Released"), ICON_PF_KEY_R, DDGoController::Bind::VirtualBrakeReleased, GenericInputBinding::Unknown), + BUTTON("VirtualBrake1", TRANSLATE_NOOP("DDGoController", "Brake 1"), ICON_PF_1, DDGoController::Bind::VirtualBrake1, GenericInputBinding::Unknown), + BUTTON("VirtualBrake2", TRANSLATE_NOOP("DDGoController", "Brake 2"), ICON_PF_2, DDGoController::Bind::VirtualBrake2, GenericInputBinding::Unknown), + BUTTON("VirtualBrake3", TRANSLATE_NOOP("DDGoController", "Brake 3"), ICON_PF_3, DDGoController::Bind::VirtualBrake3, GenericInputBinding::Unknown), + BUTTON("VirtualBrake4", TRANSLATE_NOOP("DDGoController", "Brake 4"), ICON_PF_4, DDGoController::Bind::VirtualBrake4, GenericInputBinding::Unknown), + BUTTON("VirtualBrake5", TRANSLATE_NOOP("DDGoController", "Brake 5"), ICON_PF_5, DDGoController::Bind::VirtualBrake5, GenericInputBinding::Unknown), + BUTTON("VirtualBrake6", TRANSLATE_NOOP("DDGoController", "Brake 6"), ICON_PF_6, DDGoController::Bind::VirtualBrake6, GenericInputBinding::Unknown), + BUTTON("VirtualBrake7", TRANSLATE_NOOP("DDGoController", "Brake 7"), ICON_PF_7, DDGoController::Bind::VirtualBrake7, GenericInputBinding::Unknown), + BUTTON("VirtualBrake8", TRANSLATE_NOOP("DDGoController", "Brake 8"), ICON_PF_8, DDGoController::Bind::VirtualBrake8, GenericInputBinding::Unknown), + BUTTON("VirtualBrakeEmergency", TRANSLATE_NOOP("DDGoController", "Brake Emergency"), ICON_PF_KEY_E, DDGoController::Bind::VirtualBrakeEmergency, GenericInputBinding::Unknown), + + AXIS("Power", TRANSLATE_NOOP("DDGoController", "Power"), ICON_PF_KEY_P, DDGoController::Bind::Power, GenericInputBinding::L2), + AXIS("Brake", TRANSLATE_NOOP("DDGoController", "Brake"), ICON_PF_KEY_B, DDGoController::Bind::Brake, GenericInputBinding::R2), +// clang-format on + +#undef AXIS +#undef BUTTON +}; + +static const SettingInfo s_settings[] = { + {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATE_NOOP("DDGoController", "Analog Deadzone"), + TRANSLATE_NOOP("AnalogController", + "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."), + "0.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", nullptr, 100.0f}, + {SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATE_NOOP("DDGoController", "Analog Sensitivity"), + TRANSLATE_NOOP( + "AnalogController", + "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent " + "controllers, e.g. DualShock 4, Xbox One Controller."), + "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", nullptr, 100.0f}, + {SettingInfo::Type::Integer, "PowerTransitionFrames", TRANSLATE_NOOP("DDGoController", "Power Transition Frames"), + TRANSLATE_NOOP("DDGoController", "Sets the number of frames that the controller will report the " + "transitioning/inbetween state when changing power level."), + "10", "0", "255", "1", "%d", nullptr, 1.0f}, + {SettingInfo::Type::Integer, "BrakeTransitionFrames", TRANSLATE_NOOP("DDGoController", "Brake Transition Frames"), + TRANSLATE_NOOP("DDGoController", "Sets the number of frames that the controller will report the " + "transitioning/inbetween state when changing brake level."), + "10", "0", "255", "1", "%d", nullptr, 1.0f}}; + +const Controller::ControllerInfo DDGoController::INFO = {ControllerType::DDGoController, + "DDGoController", + TRANSLATE_NOOP("ControllerType", "Densha de Go! Controller"), + ICON_PF_GAMEPAD_ALT, + s_binding_info, + s_settings}; diff --git a/src/core/ddgo_controller.h b/src/core/ddgo_controller.h new file mode 100644 index 0000000000..a2e327e71c --- /dev/null +++ b/src/core/ddgo_controller.h @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin +// SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +#pragma once + +#include "controller.h" + +#include + +class DDGoController final : public Controller +{ +public: + enum class Axis : u8 + { + Count + }; + + enum class Bind : u8 + { + Select = 0, + Start = 3, + A = 15, + B = 14, + C = 13, + PowerBit0 = 12, + PowerBit1 = 7, + PowerBit2 = 5, + BrakeBit0 = 10, + BrakeBit1 = 8, + BrakeBit2 = 11, + BrakeBit3 = 9, + + // We have to sneak the power/brake axes in here because otherwise we go over the limit for the + // input overlay, which is 32 bindings. + Power = 1, + Brake = 2, + + VirtualButtonStart = 16, + VirtualPowerOff = VirtualButtonStart, + VirtualPower1, + VirtualPower2, + VirtualPower3, + VirtualPower4, + VirtualPower5, + VirtualBrakeReleased, + VirtualBrake1, + VirtualBrake2, + VirtualBrake3, + VirtualBrake4, + VirtualBrake5, + VirtualBrake6, + VirtualBrake7, + VirtualBrake8, + VirtualBrakeEmergency, + Count, + }; + + static const Controller::ControllerInfo INFO; + + DDGoController(u32 index); + ~DDGoController() override; + + static std::unique_ptr Create(u32 index); + + ControllerType GetType() const override; + + void Reset() override; + bool DoState(StateWrapper& sw, bool apply_input_state) override; + + float GetBindState(u32 index) const override; + void SetBindState(u32 index, float value) override; + u32 GetButtonStateBits() const override; + + void ResetTransferState() override; + bool Transfer(const u8 data_in, u8* data_out) override; + + void LoadSettings(const SettingsInterface& si, const char* section, bool initial) override; + +private: + enum class TransferState : u8 + { + Idle, + Ready, + IDMSB, + ButtonsLSB, + ButtonsMSB + }; + + static constexpr u16 BUTTON_MASK = static_cast(~((1u << 4) | (1u << 6))); + + static constexpr u16 POWER_MASK = + static_cast((1u << static_cast(Bind::PowerBit0)) | (1u << static_cast(Bind::PowerBit1)) | + (1u << static_cast(Bind::PowerBit2))); + + static constexpr u16 BRAKE_MASK = + static_cast((1u << static_cast(Bind::BrakeBit0)) | (1u << static_cast(Bind::BrakeBit1)) | + (1u << static_cast(Bind::BrakeBit2)) | (1u << static_cast(Bind::BrakeBit3))); + + static constexpr u32 MAX_POWER_LEVEL = 5; + static constexpr u32 MAX_BRAKE_LEVEL = 9; + + void SetPowerLevel(u32 level); + void UpdatePowerBits(); + + void SetBrakeLevel(u32 level); + void UpdateBrakeBits(); + + // buttons are active low + u16 m_button_state = UINT16_C(0xFFFF); + + TransferState m_transfer_state = TransferState::Idle; + + u8 m_power_level = 0; + u8 m_brake_level = MAX_BRAKE_LEVEL; + + u8 m_power_transition_frames_remaining = 0; + u8 m_brake_transition_frames_remaining = 0; + + u8 m_power_transition_frames = 10; + u8 m_brake_transition_frames = 10; + + float m_analog_deadzone = 0.0f; + float m_analog_sensitivity = 1.33f; +}; diff --git a/src/core/digital_controller.cpp b/src/core/digital_controller.cpp index 44357d7cb7..002ce0ba20 100644 --- a/src/core/digital_controller.cpp +++ b/src/core/digital_controller.cpp @@ -149,15 +149,9 @@ std::unique_ptr DigitalController::Create(u32 index, Controll static_cast(~((1u << static_cast(Button::Right)) | (1u << static_cast(Button::Down)) | (1u << static_cast(Button::Left)))); - // densha de go! controller - up/down grounded - static constexpr u16 DDGO_BUTTON_MASK = - static_cast(~((1u << static_cast(Button::Up)) | (1u << static_cast(Button::Down)))); - u16 mask = 0xFFFFu; if (type == ControllerType::PopnController) mask = POPN_BUTTON_MASK; - else if (type == ControllerType::DDGoController) - mask = DDGO_BUTTON_MASK; return std::make_unique(index, mask); } @@ -215,33 +209,3 @@ static const Controller::ControllerBindingInfo s_popn_binding_info[] = { const Controller::ControllerInfo DigitalController::INFO_POPN = { ControllerType::PopnController, "PopnController", TRANSLATE_NOOP("ControllerType", "Pop'n Controller"), ICON_PF_POPN_CONTROLLER, s_popn_binding_info, {}}; - -static const Controller::ControllerBindingInfo s_ddgo_binding_info[] = { -#define BUTTON(name, display_name, icon_name, button, genb) \ - {name, display_name, icon_name, static_cast(button), InputBindingInfo::Type::Button, genb} - - // clang-format off - BUTTON("Select", TRANSLATE_NOOP("DDGoController", "Select"), ICON_PF_SELECT_SHARE, DigitalController::Button::Select, GenericInputBinding::Select), - BUTTON("Start", TRANSLATE_NOOP("DDGoController", "Start"), ICON_PF_START, DigitalController::Button::Start, GenericInputBinding::Start), - BUTTON("A", TRANSLATE_NOOP("DDGoController", "A"), ICON_PF_BUTTON_SQUARE, DigitalController::Button::Square, GenericInputBinding::Square), - BUTTON("B", TRANSLATE_NOOP("DDGoController", "B"), ICON_PF_BUTTON_CROSS, DigitalController::Button::Cross, GenericInputBinding::Cross), - BUTTON("C", TRANSLATE_NOOP("DDGoController", "C"), ICON_PF_BUTTON_CIRCLE, DigitalController::Button::Circle, GenericInputBinding::Circle), - BUTTON("Power1", TRANSLATE_NOOP("DDGoController", "Power 1"), ICON_PF_BUTTON_TRIANGLE, DigitalController::Button::Triangle, GenericInputBinding::Triangle), - BUTTON("Power2", TRANSLATE_NOOP("DDGoController", "Power 2"), ICON_PF_DPAD_LEFT, DigitalController::Button::Left, GenericInputBinding::DPadLeft), - BUTTON("Power3", TRANSLATE_NOOP("DDGoController", "Power 3"), ICON_PF_DPAD_RIGHT, DigitalController::Button::Right, GenericInputBinding::DPadRight), - BUTTON("Brake1", TRANSLATE_NOOP("DDGoController", "Brake 1"), ICON_PF_LEFT_SHOULDER_L1, DigitalController::Button::L1, GenericInputBinding::L1), - BUTTON("Brake2", TRANSLATE_NOOP("DDGoController", "Brake 3"), ICON_PF_RIGHT_SHOULDER_R1, DigitalController::Button::R1, GenericInputBinding::R1), - BUTTON("Brake3", TRANSLATE_NOOP("DDGoController", "Brake 2"), ICON_PF_LEFT_TRIGGER_L2, DigitalController::Button::L2, GenericInputBinding::L2), - BUTTON("Brake4", TRANSLATE_NOOP("DDGoController", "Brake 4"), ICON_PF_RIGHT_TRIGGER_R2, DigitalController::Button::R2, GenericInputBinding::R2), -// clang-format on - -#undef BUTTON -}; - -const Controller::ControllerInfo DigitalController::INFO_DDGO = { - ControllerType::DDGoController, - "DDGoController", - TRANSLATE_NOOP("ControllerType", "Densha de Go! Controller"), - ICON_PF_GAMEPAD_ALT, - s_ddgo_binding_info, - {}}; diff --git a/src/util/imgui_glyph_ranges.inl b/src/util/imgui_glyph_ranges.inl index d04e2bcaf2..e07b1491c9 100644 --- a/src/util/imgui_glyph_ranges.inl +++ b/src/util/imgui_glyph_ranges.inl @@ -3,6 +3,6 @@ static constexpr ImWchar FA_ICON_RANGE[] = { 0xe06f,0xe070,0xe086,0xe086,0xf002,0xf002,0xf005,0xf005,0xf007,0xf007,0xf00c,0xf00e,0xf011,0xf013,0xf017,0xf017,0xf019,0xf019,0xf01c,0xf01c,0xf021,0xf021,0xf023,0xf023,0xf025,0xf026,0xf028,0xf028,0xf02e,0xf02e,0xf030,0xf030,0xf03a,0xf03a,0xf03d,0xf03e,0xf04a,0xf04c,0xf050,0xf050,0xf056,0xf056,0xf05e,0xf05e,0xf062,0xf063,0xf065,0xf067,0xf071,0xf071,0xf075,0xf075,0xf077,0xf078,0xf07b,0xf07c,0xf083,0xf085,0xf091,0xf091,0xf0ac,0xf0ae,0xf0b2,0xf0b2,0xf0c3,0xf0c3,0xf0c5,0xf0c5,0xf0c7,0xf0c9,0xf0cb,0xf0cb,0xf0d0,0xf0d0,0xf0dc,0xf0dc,0xf0e0,0xf0e0,0xf0e2,0xf0e2,0xf0e7,0xf0e8,0xf0eb,0xf0eb,0xf0f1,0xf0f1,0xf0f3,0xf0f3,0xf0fe,0xf0fe,0xf110,0xf110,0xf11b,0xf11c,0xf140,0xf140,0xf144,0xf144,0xf146,0xf146,0xf14a,0xf14a,0xf15b,0xf15d,0xf191,0xf192,0xf1ab,0xf1ab,0xf1c0,0xf1c0,0xf1c5,0xf1c5,0xf1de,0xf1de,0xf1e6,0xf1e6,0xf1eb,0xf1eb,0xf1f8,0xf1f8,0xf1fb,0xf1fc,0xf201,0xf201,0xf240,0xf240,0xf242,0xf242,0xf245,0xf245,0xf26c,0xf26c,0xf279,0xf279,0xf2c1,0xf2c1,0xf2d0,0xf2d0,0xf2db,0xf2db,0xf2f1,0xf2f2,0xf302,0xf302,0xf31e,0xf31e,0xf35d,0xf35d,0xf360,0xf360,0xf362,0xf362,0xf3fd,0xf3fd,0xf410,0xf410,0xf422,0xf422,0xf424,0xf424,0xf462,0xf462,0xf466,0xf466,0xf4ce,0xf4ce,0xf500,0xf500,0xf517,0xf517,0xf51f,0xf51f,0xf538,0xf538,0xf53f,0xf53f,0xf545,0xf545,0xf547,0xf548,0xf54c,0xf54c,0xf55b,0xf55b,0xf55d,0xf55d,0xf565,0xf565,0xf56e,0xf570,0xf575,0xf575,0xf5a2,0xf5a2,0xf5aa,0xf5aa,0xf5ae,0xf5ae,0xf5c7,0xf5c7,0xf5cb,0xf5cb,0xf5e7,0xf5e7,0xf5ee,0xf5ee,0xf61f,0xf61f,0xf65d,0xf65e,0xf6a9,0xf6a9,0xf6cf,0xf6cf,0xf70c,0xf70c,0xf70e,0xf70e,0xf78c,0xf78c,0xf794,0xf794,0xf7a0,0xf7a0,0xf7a4,0xf7a5,0xf7c2,0xf7c2,0xf807,0xf807,0xf815,0xf815,0xf818,0xf818,0xf84c,0xf84c,0xf853,0xf853,0xf87d,0xf87d,0xf8cc,0xf8cc,0x0,0x0 }; -static constexpr ImWchar PF_ICON_RANGE[] = { 0x2196,0x2199,0x219e,0x21a3,0x21b0,0x21b3,0x21ba,0x21c3,0x21c7,0x21ca,0x21d0,0x21d4,0x21e0,0x21e3,0x21e6,0x21e8,0x21ed,0x21ee,0x21f7,0x21f8,0x21fa,0x21fb,0x221a,0x221b,0x227a,0x227f,0x2284,0x2284,0x22bf,0x22c8,0x2349,0x2349,0x235e,0x235e,0x2360,0x2361,0x2364,0x2366,0x23b2,0x23b4,0x23cc,0x23cc,0x23ce,0x23ce,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243e,0x2446,0x2446,0x2460,0x246b,0x248f,0x248f,0x24f5,0x24fd,0x24ff,0x24ff,0x2717,0x2717,0x2753,0x2753,0x278a,0x278e,0x27fc,0x27fc,0xe000,0xe001,0xff21,0xff3a,0x1f52b,0x1f52b,0x0,0x0 }; +static constexpr ImWchar PF_ICON_RANGE[] = { 0x2196,0x2199,0x219e,0x21a3,0x21b0,0x21b3,0x21ba,0x21c3,0x21c7,0x21ca,0x21d0,0x21d4,0x21e0,0x21e3,0x21e6,0x21e8,0x21eb,0x21eb,0x21ed,0x21ee,0x21f7,0x21f8,0x21fa,0x21fb,0x221a,0x221b,0x227a,0x227f,0x2284,0x2284,0x22bf,0x22c8,0x2349,0x2349,0x235e,0x235e,0x2360,0x2361,0x2364,0x2366,0x23b2,0x23b4,0x23cc,0x23cc,0x23ce,0x23ce,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243e,0x2446,0x2446,0x2460,0x246b,0x248f,0x248f,0x24f5,0x24fd,0x24ff,0x24ff,0x2717,0x2717,0x2753,0x2753,0x278a,0x278e,0x27fc,0x27fc,0xe000,0xe001,0xff21,0xff3a,0x1f52b,0x1f52b,0x0,0x0 }; static constexpr ImWchar EMOJI_ICON_RANGE[] = { 0x2139,0x2139,0x23e9,0x23ea,0x23f8,0x23f8,0x26a0,0x26a0,0x1f4be,0x1f4be,0x1f4c2,0x1f4c2,0x1f4f7,0x1f4f8,0x1f504,0x1f504,0x1f507,0x1f507,0x1f509,0x1f50a,0x1f50d,0x1f50d,0x1f513,0x1f513,0x0,0x0 };