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 }