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:
Albert Liu 2020-11-24 17:16:54 -08:00
parent 31b41b9ec0
commit 566993c6df
3 changed files with 131 additions and 16 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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)