duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

negcon.cpp (15775B)


      1 // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com> and contributors.
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "negcon.h"
      5 #include "host.h"
      6 #include "system.h"
      7 
      8 #include "util/state_wrapper.h"
      9 
     10 #include "common/assert.h"
     11 #include "common/bitutils.h"
     12 #include "common/log.h"
     13 
     14 #include "IconsPromptFont.h"
     15 
     16 #include <array>
     17 #include <cmath>
     18 
     19 // Mapping of Button to index of corresponding bit in m_button_state
     20 static constexpr std::array<u8, static_cast<size_t>(NeGcon::Button::Count)> s_button_indices = {3, 4,  5,  6,
     21                                                                                                 7, 11, 12, 13};
     22 NeGcon::NeGcon(u32 index) : Controller(index)
     23 {
     24   m_axis_state.fill(0x00);
     25   m_axis_state[static_cast<u8>(Axis::Steering)] = 0x80;
     26 }
     27 
     28 NeGcon::~NeGcon() = default;
     29 
     30 ControllerType NeGcon::GetType() const
     31 {
     32   return ControllerType::NeGcon;
     33 }
     34 
     35 void NeGcon::Reset()
     36 {
     37   m_transfer_state = TransferState::Idle;
     38 }
     39 
     40 bool NeGcon::DoState(StateWrapper& sw, bool apply_input_state)
     41 {
     42   if (!Controller::DoState(sw, apply_input_state))
     43     return false;
     44 
     45   u16 button_state = m_button_state;
     46   sw.Do(&button_state);
     47   if (apply_input_state)
     48     m_button_state = button_state;
     49 
     50   sw.Do(&m_transfer_state);
     51   return true;
     52 }
     53 
     54 float NeGcon::GetBindState(u32 index) const
     55 {
     56   if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
     57       index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
     58   {
     59     float value = m_axis_state[static_cast<u32>(Axis::Steering)];
     60     value = value - 128.0f;
     61     value /= value < 0.0f ? 128.0f : 127.0f;
     62     value = std::clamp(value, -1.0f, 1.0f);
     63     if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)))
     64     {
     65       value *= -1.0f;
     66     }
     67     return std::max(0.0f, value);
     68   }
     69   else if (index >= static_cast<u32>(Button::Count))
     70   {
     71     // less one because of the two steering axes
     72     const u32 sub_index = index - (static_cast<u32>(Button::Count) + 1);
     73     if (sub_index >= m_axis_state.size())
     74       return 0.0f;
     75 
     76     return static_cast<float>(m_axis_state[sub_index]) * (1.0f / 255.0f);
     77   }
     78   else
     79   {
     80     const u32 bit = s_button_indices[index];
     81     return static_cast<float>(((m_button_state >> bit) & 1u) ^ 1u);
     82   }
     83 }
     84 
     85 static float apply_axis_modifier(float value, const NeGcon::AxisModifier& axis_modifier)
     86 {
     87   value = (value - axis_modifier.deadzone) / (axis_modifier.saturation - axis_modifier.deadzone);
     88   value = std::clamp(value, 0.0f, 1.0f);
     89   value = std::pow(value, std::exp(axis_modifier.linearity));
     90   return value;
     91 }
     92 
     93 static u8 get_scaled_value(float value, const NeGcon::AxisModifier& axis_modifier)
     94 {
     95   value = axis_modifier.scaling * axis_modifier.unit * value + axis_modifier.zero;
     96   return static_cast<u8>(std::clamp(std::round(value), 0.0f, 255.0f));
     97 }
     98 
     99 void NeGcon::SetBindState(u32 index, float value)
    100 {
    101   // Steering Axis: -1..1 -> 0..255
    102   if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
    103       index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
    104   {
    105     value = apply_axis_modifier(value, m_steering_modifier);
    106 
    107     m_half_axis_state[index - static_cast<u32>(Button::Count)] = std::clamp(value, 0.0f, 1.0f);
    108 
    109     float merged = m_half_axis_state[1] - m_half_axis_state[0];
    110     m_axis_state[static_cast<u32>(Axis::Steering)] = get_scaled_value(merged, m_steering_modifier);
    111   }
    112   else if (index >= static_cast<u32>(Button::Count))
    113   {
    114     // less one because of the two steering axes
    115     const u32 sub_index = index - (static_cast<u32>(Button::Count) + 1);
    116     if (sub_index >= m_axis_state.size())
    117       return;
    118 
    119     if (index >= (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::I)) &&
    120         index <= (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::L)))
    121     {
    122       const AxisModifier& axis_modifier =
    123         m_half_axis_modifiers[index - (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::I))];
    124       value = apply_axis_modifier(value, axis_modifier);
    125       m_axis_state[sub_index] = get_scaled_value(value, axis_modifier);
    126     }
    127     else
    128     {
    129       m_axis_state[sub_index] = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
    130     }
    131   }
    132   else if (index < static_cast<u32>(Button::Count))
    133   {
    134     const u16 bit = u16(1) << s_button_indices[static_cast<u8>(index)];
    135 
    136     if (value >= 0.5f)
    137     {
    138       if (m_button_state & bit)
    139         System::SetRunaheadReplayFlag();
    140 
    141       m_button_state &= ~bit;
    142     }
    143     else
    144     {
    145       if (!(m_button_state & bit))
    146         System::SetRunaheadReplayFlag();
    147 
    148       m_button_state |= bit;
    149     }
    150   }
    151 }
    152 
    153 u32 NeGcon::GetButtonStateBits() const
    154 {
    155   return m_button_state ^ 0xFFFF;
    156 }
    157 
    158 std::optional<u32> NeGcon::GetAnalogInputBytes() const
    159 {
    160   return m_axis_state[static_cast<size_t>(Axis::L)] << 24 | m_axis_state[static_cast<size_t>(Axis::II)] << 16 |
    161          m_axis_state[static_cast<size_t>(Axis::I)] << 8 | m_axis_state[static_cast<size_t>(Axis::Steering)];
    162 }
    163 
    164 void NeGcon::ResetTransferState()
    165 {
    166   m_transfer_state = TransferState::Idle;
    167 }
    168 
    169 bool NeGcon::Transfer(const u8 data_in, u8* data_out)
    170 {
    171   static constexpr u16 ID = 0x5A23;
    172 
    173   switch (m_transfer_state)
    174   {
    175     case TransferState::Idle:
    176     {
    177       *data_out = 0xFF;
    178 
    179       if (data_in == 0x01)
    180       {
    181         m_transfer_state = TransferState::Ready;
    182         return true;
    183       }
    184       return false;
    185     }
    186 
    187     case TransferState::Ready:
    188     {
    189       if (data_in == 0x42)
    190       {
    191         *data_out = Truncate8(ID);
    192         m_transfer_state = TransferState::IDMSB;
    193         return true;
    194       }
    195 
    196       *data_out = 0xFF;
    197       return false;
    198     }
    199 
    200     case TransferState::IDMSB:
    201     {
    202       *data_out = Truncate8(ID >> 8);
    203       m_transfer_state = TransferState::ButtonsLSB;
    204       return true;
    205     }
    206 
    207     case TransferState::ButtonsLSB:
    208     {
    209       *data_out = Truncate8(m_button_state);
    210       m_transfer_state = TransferState::ButtonsMSB;
    211       return true;
    212     }
    213 
    214     case TransferState::ButtonsMSB:
    215     {
    216       *data_out = Truncate8(m_button_state >> 8);
    217       m_transfer_state = TransferState::AnalogSteering;
    218       return true;
    219     }
    220 
    221     case TransferState::AnalogSteering:
    222     {
    223       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::Steering)]);
    224       m_transfer_state = TransferState::AnalogI;
    225       return true;
    226     }
    227 
    228     case TransferState::AnalogI:
    229     {
    230       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::I)]);
    231       m_transfer_state = TransferState::AnalogII;
    232       return true;
    233     }
    234 
    235     case TransferState::AnalogII:
    236     {
    237       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::II)]);
    238       m_transfer_state = TransferState::AnalogL;
    239       return true;
    240     }
    241 
    242     case TransferState::AnalogL:
    243     {
    244       *data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::L)]);
    245       m_transfer_state = TransferState::Idle;
    246       return false;
    247     }
    248 
    249     default:
    250     {
    251       UnreachableCode();
    252       return false;
    253     }
    254   }
    255 }
    256 
    257 std::unique_ptr<NeGcon> NeGcon::Create(u32 index)
    258 {
    259   return std::make_unique<NeGcon>(index);
    260 }
    261 
    262 static const Controller::ControllerBindingInfo s_binding_info[] = {
    263 #define BUTTON(name, display_name, icon_name, button, genb)                                                            \
    264   {                                                                                                                    \
    265     name, display_name, icon_name, static_cast<u32>(button), InputBindingInfo::Type::Button, genb                      \
    266   }
    267 #define AXIS(name, display_name, icon_name, halfaxis, genb)                                                            \
    268   {                                                                                                                    \
    269     name, display_name, icon_name, static_cast<u32>(NeGcon::Button::Count) + static_cast<u32>(halfaxis),               \
    270       InputBindingInfo::Type::HalfAxis, genb                                                                           \
    271   }
    272 
    273   // clang-format off
    274   BUTTON("Up", TRANSLATE_NOOP("NeGcon", "D-Pad Up"), ICON_PF_DPAD_UP, NeGcon::Button::Up, GenericInputBinding::DPadUp),
    275   BUTTON("Right", TRANSLATE_NOOP("NeGcon", "D-Pad Right"), ICON_PF_DPAD_RIGHT, NeGcon::Button::Right, GenericInputBinding::DPadRight),
    276   BUTTON("Down", TRANSLATE_NOOP("NeGcon", "D-Pad Down"), ICON_PF_DPAD_DOWN, NeGcon::Button::Down, GenericInputBinding::DPadDown),
    277   BUTTON("Left", TRANSLATE_NOOP("NeGcon", "D-Pad Left"), ICON_PF_DPAD_LEFT, NeGcon::Button::Left, GenericInputBinding::DPadLeft),
    278   BUTTON("Start", TRANSLATE_NOOP("NeGcon", "Start"), ICON_PF_START, NeGcon::Button::Start, GenericInputBinding::Start),
    279   BUTTON("A", TRANSLATE_NOOP("NeGcon", "A Button"), ICON_PF_BUTTON_A, NeGcon::Button::A, GenericInputBinding::Circle),
    280   BUTTON("B", TRANSLATE_NOOP("NeGcon", "B Button"), ICON_PF_BUTTON_B, NeGcon::Button::B, GenericInputBinding::Triangle),
    281   AXIS("I", TRANSLATE_NOOP("NeGcon", "I Button"), ICON_PF_BUTTON_ALT_1, NeGcon::HalfAxis::I, GenericInputBinding::R2),
    282   AXIS("II", TRANSLATE_NOOP("NeGcon", "II Button"), ICON_PF_BUTTON_ALT_2, NeGcon::HalfAxis::II, GenericInputBinding::L2),
    283   AXIS("L", TRANSLATE_NOOP("NeGcon", "Left Trigger"), ICON_PF_LEFT_TRIGGER_LT, NeGcon::HalfAxis::L, GenericInputBinding::L1),
    284   BUTTON("R", TRANSLATE_NOOP("NeGcon", "Right Trigger"), ICON_PF_RIGHT_TRIGGER_RT, NeGcon::Button::R, GenericInputBinding::R1),
    285   AXIS("SteeringLeft", TRANSLATE_NOOP("NeGcon", "Steering (Twist) Left"), ICON_PF_ANALOG_LEFT, NeGcon::HalfAxis::SteeringLeft, GenericInputBinding::LeftStickLeft),
    286   AXIS("SteeringRight", TRANSLATE_NOOP("NeGcon", "Steering (Twist) Right"), ICON_PF_ANALOG_RIGHT, NeGcon::HalfAxis::SteeringRight, GenericInputBinding::LeftStickRight),
    287 // clang-format on
    288 
    289 #undef AXIS
    290 #undef BUTTON
    291 };
    292 
    293 static const SettingInfo s_settings[] = {
    294   {SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATE_NOOP("NeGcon", "Steering Axis Deadzone"),
    295    TRANSLATE_NOOP("NeGcon", "Sets deadzone for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
    296    100.0f},
    297   {SettingInfo::Type::Float, "SteeringSaturation", TRANSLATE_NOOP("NeGcon", "Steering Axis Saturation"),
    298    TRANSLATE_NOOP("NeGcon", "Sets saturation for steering axis."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%",
    299    nullptr, 100.0f},
    300   {SettingInfo::Type::Float, "SteeringLinearity", TRANSLATE_NOOP("NeGcon", "Steering Axis Linearity"),
    301    TRANSLATE_NOOP("NeGcon", "Sets linearity for steering axis."), "0.00f", "-2.00f", "2.00f", "0.05f", "%.2f", nullptr,
    302    1.0f},
    303   {SettingInfo::Type::Float, "SteeringScaling", TRANSLATE_NOOP("NeGcon", "Steering Scaling"),
    304    TRANSLATE_NOOP("NeGcon", "Sets scaling for steering axis."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
    305    100.0f},
    306   {SettingInfo::Type::Float, "IDeadzone", TRANSLATE_NOOP("NeGcon", "I Button Deadzone"),
    307    TRANSLATE_NOOP("NeGcon", "Sets deadzone for button I."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
    308    100.0f},
    309   {SettingInfo::Type::Float, "ISaturation", TRANSLATE_NOOP("NeGcon", "I Button Saturation"),
    310    TRANSLATE_NOOP("NeGcon", "Sets saturation for button I."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%", nullptr,
    311    100.0f},
    312   {SettingInfo::Type::Float, "ILinearity", TRANSLATE_NOOP("NeGcon", "I Button Linearity"),
    313    TRANSLATE_NOOP("NeGcon", "Sets linearity for button I."), "0.00f", "-2.00f", "2.00f", "0.01f", "%.2f", nullptr,
    314    1.0f},
    315   {SettingInfo::Type::Float, "IScaling", TRANSLATE_NOOP("NeGcon", "I Scaling"),
    316    TRANSLATE_NOOP("NeGcon", "Sets scaling for button I."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
    317    100.0f},
    318   {SettingInfo::Type::Float, "IIDeadzone", TRANSLATE_NOOP("NeGcon", "II Button Deadzone"),
    319    TRANSLATE_NOOP("NeGcon", "Sets deadzone for button II."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
    320    100.0f},
    321   {SettingInfo::Type::Float, "IISaturation", TRANSLATE_NOOP("NeGcon", "II Button Saturation"),
    322    TRANSLATE_NOOP("NeGcon", "Sets saturation for button II."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%", nullptr,
    323    100.0f},
    324   {SettingInfo::Type::Float, "IILinearity", TRANSLATE_NOOP("NeGcon", "II Button Linearity"),
    325    TRANSLATE_NOOP("NeGcon", "Sets linearity for button II."), "0.00f", "-2.00f", "2.00f", "0.01f", "%.2f", nullptr,
    326    1.0f},
    327   {SettingInfo::Type::Float, "IIScaling", TRANSLATE_NOOP("NeGcon", "II Scaling"),
    328    TRANSLATE_NOOP("NeGcon", "Sets scaling for button II."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
    329    100.0f},
    330   {SettingInfo::Type::Float, "LDeadzone", TRANSLATE_NOOP("NeGcon", "Left Trigger Deadzone"),
    331    TRANSLATE_NOOP("NeGcon", "Sets deadzone for left trigger."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr,
    332    100.0f},
    333   {SettingInfo::Type::Float, "LSaturation", TRANSLATE_NOOP("NeGcon", "Left Trigger Saturation"),
    334    TRANSLATE_NOOP("NeGcon", "Sets saturation for left trigger."), "1.00f", "0.01f", "1.00f", "0.01f", "%.0f%%", nullptr,
    335    100.0f},
    336   {SettingInfo::Type::Float, "LLinearity", TRANSLATE_NOOP("NeGcon", "Left Trigger Linearity"),
    337    TRANSLATE_NOOP("NeGcon", "Sets linearity for left trigger."), "0.00f", "-2.00f", "2.00f", "0.01f", "%.2f", nullptr,
    338    1.0f},
    339   {SettingInfo::Type::Float, "LScaling", TRANSLATE_NOOP("NeGcon", "Left Trigger Scaling"),
    340    TRANSLATE_NOOP("NeGcon", "Sets scaling for left trigger."), "1.00f", "0.01f", "10.00f", "0.01f", "%.0f%%", nullptr,
    341    100.0f},
    342 };
    343 
    344 const Controller::ControllerInfo NeGcon::INFO = {
    345   ControllerType::NeGcon, "NeGcon",   TRANSLATE_NOOP("ControllerType", "NeGcon"),    ICON_PF_GAMEPAD,
    346   s_binding_info,         s_settings, Controller::VibrationCapabilities::NoVibration};
    347 
    348 void NeGcon::LoadSettings(SettingsInterface& si, const char* section, bool initial)
    349 {
    350   Controller::LoadSettings(si, section, initial);
    351   m_steering_modifier = {
    352     .deadzone = si.GetFloatValue(section, "SteeringDeadzone", DEFAULT_STEERING_MODIFIER.deadzone),
    353     .saturation = si.GetFloatValue(section, "SteeringSaturation", DEFAULT_STEERING_MODIFIER.saturation),
    354     .linearity = si.GetFloatValue(section, "SteeringLinearity", DEFAULT_STEERING_MODIFIER.linearity),
    355     .scaling = si.GetFloatValue(section, "SteeringScaling", DEFAULT_STEERING_MODIFIER.scaling),
    356     .zero = DEFAULT_STEERING_MODIFIER.zero,
    357     .unit = DEFAULT_STEERING_MODIFIER.unit,
    358   };
    359   m_half_axis_modifiers[0] = {
    360     .deadzone = si.GetFloatValue(section, "IDeadzone", DEFAULT_PEDAL_MODIFIER.deadzone),
    361     .saturation = si.GetFloatValue(section, "ISaturation", DEFAULT_PEDAL_MODIFIER.saturation),
    362     .linearity = si.GetFloatValue(section, "ILinearity", DEFAULT_PEDAL_MODIFIER.linearity),
    363     .scaling = si.GetFloatValue(section, "IScaling", DEFAULT_PEDAL_MODIFIER.scaling),
    364     .zero = DEFAULT_PEDAL_MODIFIER.zero,
    365     .unit = DEFAULT_PEDAL_MODIFIER.unit,
    366   };
    367   m_half_axis_modifiers[1] = {
    368     .deadzone = si.GetFloatValue(section, "IIDeadzone", DEFAULT_PEDAL_MODIFIER.deadzone),
    369     .saturation = si.GetFloatValue(section, "IISaturation", DEFAULT_PEDAL_MODIFIER.saturation),
    370     .linearity = si.GetFloatValue(section, "IILinearity", DEFAULT_PEDAL_MODIFIER.linearity),
    371     .scaling = si.GetFloatValue(section, "IIScaling", DEFAULT_PEDAL_MODIFIER.scaling),
    372     .zero = DEFAULT_PEDAL_MODIFIER.zero,
    373     .unit = DEFAULT_PEDAL_MODIFIER.unit,
    374   };
    375   m_half_axis_modifiers[2] = {
    376     .deadzone = si.GetFloatValue(section, "LDeadzone", DEFAULT_PEDAL_MODIFIER.deadzone),
    377     .saturation = si.GetFloatValue(section, "LSaturation", DEFAULT_PEDAL_MODIFIER.saturation),
    378     .linearity = si.GetFloatValue(section, "LLinearity", DEFAULT_PEDAL_MODIFIER.linearity),
    379     .scaling = si.GetFloatValue(section, "LScaling", DEFAULT_PEDAL_MODIFIER.scaling),
    380     .zero = DEFAULT_PEDAL_MODIFIER.zero,
    381     .unit = DEFAULT_PEDAL_MODIFIER.unit,
    382   };
    383 }