mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 15:45:42 +00:00
AnalogController: Improve DualShock rumble handling
Fixes low vibration in Final Fantasy VIII, missing vibration in Armored Core Project Phantasma, etc.
This commit is contained in:
parent
31b41b9ec0
commit
566993c6df
|
@ -23,12 +23,12 @@ ControllerType AnalogController::GetType() const
|
|||
void AnalogController::Reset()
|
||||
{
|
||||
m_analog_mode = false;
|
||||
m_rumble_unlocked = false;
|
||||
m_legacy_rumble_unlocked = false;
|
||||
m_configuration_mode = false;
|
||||
m_command_param = 0;
|
||||
m_motor_state.fill(0);
|
||||
|
||||
ResetRumbleConfig();
|
||||
|
||||
if (m_auto_enable_analog)
|
||||
SetAnalogMode(true);
|
||||
}
|
||||
|
@ -48,6 +48,11 @@ bool AnalogController::DoState(StateWrapper& sw)
|
|||
sw.DoEx(&m_button_state, 44, static_cast<u16>(0xFFFF));
|
||||
sw.Do(&m_state);
|
||||
|
||||
sw.DoEx(&m_rumble_config, 45, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
|
||||
sw.DoEx(&m_rumble_config_large_motor_index, 45, -1);
|
||||
sw.DoEx(&m_rumble_config_small_motor_index, 45, -1);
|
||||
sw.DoEx(&m_analog_toggle_queued, 45, false);
|
||||
|
||||
MotorState motor_state = m_motor_state;
|
||||
sw.Do(&motor_state);
|
||||
|
||||
|
@ -157,6 +162,11 @@ void AnalogController::ResetTransferState()
|
|||
else
|
||||
{
|
||||
SetAnalogMode(!m_analog_mode);
|
||||
|
||||
// Manually toggling controller mode resets and disables rumble configuration
|
||||
ResetRumbleConfig();
|
||||
|
||||
// TODO: Mode switch detection (0x00 returned on certain commands instead of 0x5A)
|
||||
}
|
||||
|
||||
m_analog_toggle_queued = false;
|
||||
|
@ -216,6 +226,28 @@ u8 AnalogController::GetExtraButtonMaskLSB() const
|
|||
(static_cast<u8>(down) << static_cast<u8>(Button::Down)));
|
||||
}
|
||||
|
||||
void AnalogController::ResetRumbleConfig()
|
||||
{
|
||||
m_legacy_rumble_unlocked = false;
|
||||
|
||||
m_rumble_unlocked = false;
|
||||
m_rumble_config.fill(0xFF);
|
||||
|
||||
m_rumble_config_large_motor_index = -1;
|
||||
m_rumble_config_small_motor_index = -1;
|
||||
|
||||
SetMotorState(LargeMotor, 0);
|
||||
SetMotorState(SmallMotor, 0);
|
||||
}
|
||||
|
||||
void AnalogController::SetMotorStateForConfigIndex(int index, u8 value)
|
||||
{
|
||||
if (m_rumble_config_small_motor_index == index)
|
||||
SetMotorState(SmallMotor, ((value & 0x01) != 0) ? 255 : 0);
|
||||
else if (m_rumble_config_large_motor_index == index)
|
||||
SetMotorState(LargeMotor, value);
|
||||
}
|
||||
|
||||
bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
||||
{
|
||||
bool ack;
|
||||
|
@ -243,6 +275,23 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
|||
} \
|
||||
break;
|
||||
|
||||
#define REPLY_RUMBLE_CONFIG(state, index, ack_value, next_state) \
|
||||
case state: \
|
||||
{ \
|
||||
DebugAssert(index < m_rumble_config.size()); \
|
||||
*data_out = m_rumble_config[index]; \
|
||||
m_rumble_config[index] = data_in; \
|
||||
\
|
||||
if (data_in == 0x00) \
|
||||
m_rumble_config_small_motor_index = index; \
|
||||
else if (data_in == 0x01) \
|
||||
m_rumble_config_large_motor_index = index; \
|
||||
\
|
||||
m_state = next_state; \
|
||||
ack = ack_value; \
|
||||
} \
|
||||
break;
|
||||
|
||||
case State::Idle:
|
||||
{
|
||||
// ack when sent 0x01, send ID for 0x42
|
||||
|
@ -291,6 +340,8 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
|||
else if (m_configuration_mode && data_in == 0x4D)
|
||||
{
|
||||
m_rumble_unlocked = true;
|
||||
SetMotorState(LargeMotor, 0);
|
||||
SetMotorState(SmallMotor, 0);
|
||||
*data_out = Truncate8(GetID());
|
||||
m_state = State::UnlockRumbleIDMSB;
|
||||
ack = true;
|
||||
|
@ -309,11 +360,11 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
|||
case State::GetStateButtonsLSB:
|
||||
{
|
||||
if (m_rumble_unlocked)
|
||||
SetMotorState(1, ((data_in & 0x01) != 0) ? 255 : 0);
|
||||
SetMotorStateForConfigIndex(0, data_in);
|
||||
else if (data_in >= 0x40 && data_in <= 0x7F)
|
||||
m_legacy_rumble_unlocked = true;
|
||||
else
|
||||
SetMotorState(1, 0);
|
||||
SetMotorState(SmallMotor, 0);
|
||||
|
||||
*data_out = Truncate8(m_button_state) & GetExtraButtonMaskLSB();
|
||||
m_state = State::GetStateButtonsMSB;
|
||||
|
@ -324,10 +375,12 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
|||
case State::GetStateButtonsMSB:
|
||||
{
|
||||
if (m_rumble_unlocked)
|
||||
SetMotorState(0, data_in);
|
||||
{
|
||||
SetMotorStateForConfigIndex(1, data_in);
|
||||
}
|
||||
else if (m_legacy_rumble_unlocked)
|
||||
{
|
||||
SetMotorState(1, ((data_in & 0x01) != 0) ? 255 : 0);
|
||||
SetMotorState(SmallMotor, ((data_in & 0x01) != 0) ? 255 : 0);
|
||||
m_legacy_rumble_unlocked = false;
|
||||
}
|
||||
|
||||
|
@ -337,14 +390,49 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
|||
}
|
||||
break;
|
||||
|
||||
FIXED_REPLY_STATE(State::GetStateRightAxisX, Truncate8(m_axis_state[static_cast<u8>(Axis::RightX)]), true,
|
||||
State::GetStateRightAxisY);
|
||||
FIXED_REPLY_STATE(State::GetStateRightAxisY, Truncate8(m_axis_state[static_cast<u8>(Axis::RightY)]), true,
|
||||
State::GetStateLeftAxisX);
|
||||
FIXED_REPLY_STATE(State::GetStateLeftAxisX, Truncate8(m_axis_state[static_cast<u8>(Axis::LeftX)]), true,
|
||||
State::GetStateLeftAxisY);
|
||||
FIXED_REPLY_STATE(State::GetStateLeftAxisY, Truncate8(m_axis_state[static_cast<u8>(Axis::LeftY)]), false,
|
||||
State::Idle);
|
||||
case State::GetStateRightAxisX:
|
||||
{
|
||||
if (m_rumble_unlocked)
|
||||
SetMotorStateForConfigIndex(2, data_in);
|
||||
|
||||
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::RightX)]);
|
||||
m_state = State::GetStateRightAxisY;
|
||||
ack = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::GetStateRightAxisY:
|
||||
{
|
||||
if (m_rumble_unlocked)
|
||||
SetMotorStateForConfigIndex(3, data_in);
|
||||
|
||||
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::RightY)]);
|
||||
m_state = State::GetStateLeftAxisX;
|
||||
ack = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::GetStateLeftAxisX:
|
||||
{
|
||||
if (m_rumble_unlocked)
|
||||
SetMotorStateForConfigIndex(4, data_in);
|
||||
|
||||
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::LeftX)]);
|
||||
m_state = State::GetStateLeftAxisY;
|
||||
ack = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::GetStateLeftAxisY:
|
||||
{
|
||||
if (m_rumble_unlocked)
|
||||
SetMotorStateForConfigIndex(5, data_in);
|
||||
|
||||
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::LeftY)]);
|
||||
m_state = State::Idle;
|
||||
ack = false;
|
||||
}
|
||||
break;
|
||||
|
||||
ID_STATE_MSB(State::ConfigModeIDMSB, State::ConfigModeSetMode);
|
||||
|
||||
|
@ -438,7 +526,13 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
|||
FIXED_REPLY_STATE(State::Command4C4, 0x00, true, State::Command4C5);
|
||||
FIXED_REPLY_STATE(State::Command4C5, 0x00, false, State::Idle);
|
||||
|
||||
ID_STATE_MSB(State::UnlockRumbleIDMSB, State::Pad6Bytes);
|
||||
ID_STATE_MSB(State::UnlockRumbleIDMSB, State::GetSetRumble1);
|
||||
REPLY_RUMBLE_CONFIG(State::GetSetRumble1, 0, true, State::GetSetRumble2);
|
||||
REPLY_RUMBLE_CONFIG(State::GetSetRumble2, 1, true, State::GetSetRumble3);
|
||||
REPLY_RUMBLE_CONFIG(State::GetSetRumble3, 2, true, State::GetSetRumble4);
|
||||
REPLY_RUMBLE_CONFIG(State::GetSetRumble4, 3, true, State::GetSetRumble5);
|
||||
REPLY_RUMBLE_CONFIG(State::GetSetRumble5, 4, true, State::GetSetRumble6);
|
||||
REPLY_RUMBLE_CONFIG(State::GetSetRumble6, 5, false, State::Idle);
|
||||
|
||||
FIXED_REPLY_STATE(State::Pad6Bytes, 0x00, true, State::Pad5Bytes);
|
||||
FIXED_REPLY_STATE(State::Pad5Bytes, 0x00, true, State::Pad4Bytes);
|
||||
|
|
|
@ -99,6 +99,12 @@ private:
|
|||
GetAnalogMode5,
|
||||
GetAnalogMode6,
|
||||
UnlockRumbleIDMSB,
|
||||
GetSetRumble1,
|
||||
GetSetRumble2,
|
||||
GetSetRumble3,
|
||||
GetSetRumble4,
|
||||
GetSetRumble5,
|
||||
GetSetRumble6,
|
||||
Command46IDMSB,
|
||||
Command461,
|
||||
Command462,
|
||||
|
@ -132,6 +138,8 @@ private:
|
|||
void SetAnalogMode(bool enabled);
|
||||
void SetMotorState(u8 motor, u8 value);
|
||||
u8 GetExtraButtonMaskLSB() const;
|
||||
void ResetRumbleConfig();
|
||||
void SetMotorStateForConfigIndex(int index, u8 value);
|
||||
|
||||
u32 m_index;
|
||||
|
||||
|
@ -149,8 +157,21 @@ private:
|
|||
|
||||
std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{};
|
||||
|
||||
enum : u8
|
||||
{
|
||||
LargeMotor = 0,
|
||||
SmallMotor = 1
|
||||
};
|
||||
|
||||
std::array<u8, 6> m_rumble_config{};
|
||||
int m_rumble_config_large_motor_index = -1;
|
||||
int m_rumble_config_small_motor_index = -1;
|
||||
|
||||
bool m_analog_toggle_queued = false;
|
||||
|
||||
// TODO: Set this with command 0x4D and increase response length in digital mode accordingly
|
||||
u8 m_digital_mode_additional_bytes = 0;
|
||||
|
||||
// buttons are active low
|
||||
u16 m_button_state = UINT16_C(0xFFFF);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "types.h"
|
||||
|
||||
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
|
||||
static constexpr u32 SAVE_STATE_VERSION = 44;
|
||||
static constexpr u32 SAVE_STATE_VERSION = 45;
|
||||
static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42;
|
||||
|
||||
#pragma pack(push, 4)
|
||||
|
|
Loading…
Reference in a new issue