From e4820bcbc105e3d2dafe1f9dcfebf5f1add34e8e Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Mon, 23 Nov 2020 17:35:34 -0800 Subject: [PATCH 1/4] AnalogController: Delay manual mode toggle to transfer state reset --- src/core/analog_controller.cpp | 36 +++++++++++++++++++--------------- src/core/analog_controller.h | 2 ++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index dc0e9ac6e..f165cf0a3 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -102,22 +102,7 @@ void AnalogController::SetButtonState(Button button, bool pressed) { // analog toggle if (pressed) - { - if (m_analog_locked) - { - g_host_interface->AddFormattedOSDMessage( - 5.0f, - m_analog_mode ? g_host_interface->TranslateString("AnalogController", - "Controller %u is locked to analog mode by the game.") : - g_host_interface->TranslateString("AnalogController", - "Controller %u is locked to digital mode by the game."), - m_index + 1u); - } - else - { - SetAnalogMode(!m_analog_mode); - } - } + m_analog_toggle_queued = true; return; } @@ -158,6 +143,25 @@ float AnalogController::GetVibrationMotorStrength(u32 motor) void AnalogController::ResetTransferState() { + if (m_analog_toggle_queued) + { + if (m_analog_locked) + { + g_host_interface->AddFormattedOSDMessage( + 5.0f, + m_analog_mode ? + g_host_interface->TranslateString("AnalogController", "Controller %u is locked to analog mode by the game.") : + g_host_interface->TranslateString("AnalogController", "Controller %u is locked to digital mode by the game."), + m_index + 1u); + } + else + { + SetAnalogMode(!m_analog_mode); + } + + m_analog_toggle_queued = false; + } + m_state = State::Idle; } diff --git a/src/core/analog_controller.h b/src/core/analog_controller.h index 2cf96fae1..21cbe635f 100644 --- a/src/core/analog_controller.h +++ b/src/core/analog_controller.h @@ -149,6 +149,8 @@ private: std::array(Axis::Count)> m_axis_state{}; + bool m_analog_toggle_queued = false; + // buttons are active low u16 m_button_state = UINT16_C(0xFFFF); From 3f709d97b62063c8553392951260da7fe89d800c Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Wed, 25 Nov 2020 06:12:16 -0800 Subject: [PATCH 2/4] AnalogController: Send full analog response for reads in config mode --- src/core/analog_controller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index f165cf0a3..135cfdc2e 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -332,8 +332,8 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out) } *data_out = Truncate8(m_button_state >> 8); - m_state = m_analog_mode ? State::GetStateRightAxisX : State::Idle; - ack = m_analog_mode; + m_state = (m_analog_mode || m_configuration_mode) ? State::GetStateRightAxisX : State::Idle; + ack = m_analog_mode || m_configuration_mode; } break; From 31b41b9ec0daf5d44f9bb27551179921cc0a22f0 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Wed, 25 Nov 2020 06:20:12 -0800 Subject: [PATCH 3/4] AnalogController: Output zeros for 0x43 when already in config mode --- src/core/analog_controller.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index 135cfdc2e..d7c7ad0a3 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -350,10 +350,12 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out) case State::ConfigModeSetMode: { + // If 0x43 "enter/leave config mode" is called from within config mode, return all zeros Log_DebugPrintf("0x%02x(%s) config mode", data_in, data_in == 1 ? "enter" : "leave"); + bool prev_configuration_mode = m_configuration_mode; m_configuration_mode = (data_in == 1); - *data_out = Truncate8(m_button_state); - m_state = State::GetStateButtonsMSB; + *data_out = prev_configuration_mode ? 0x00 : Truncate8(m_button_state); + m_state = prev_configuration_mode ? State::Pad5Bytes : State::GetStateButtonsMSB; ack = true; } break; From 566993c6df936517ef00d960af3400ed71315de2 Mon Sep 17 00:00:00 2001 From: Albert Liu <45282415+ggrtk@users.noreply.github.com> Date: Tue, 24 Nov 2020 17:16:54 -0800 Subject: [PATCH 4/4] AnalogController: Improve DualShock rumble handling Fixes low vibration in Final Fantasy VIII, missing vibration in Armored Core Project Phantasma, etc. --- src/core/analog_controller.cpp | 124 +++++++++++++++++++++++++++++---- src/core/analog_controller.h | 21 ++++++ src/core/save_state_version.h | 2 +- 3 files changed, 131 insertions(+), 16 deletions(-) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index d7c7ad0a3..6e614b4e7 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -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(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(down) << static_cast(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(Axis::RightX)]), true, - State::GetStateRightAxisY); - FIXED_REPLY_STATE(State::GetStateRightAxisY, Truncate8(m_axis_state[static_cast(Axis::RightY)]), true, - State::GetStateLeftAxisX); - FIXED_REPLY_STATE(State::GetStateLeftAxisX, Truncate8(m_axis_state[static_cast(Axis::LeftX)]), true, - State::GetStateLeftAxisY); - FIXED_REPLY_STATE(State::GetStateLeftAxisY, Truncate8(m_axis_state[static_cast(Axis::LeftY)]), false, - State::Idle); + case State::GetStateRightAxisX: + { + if (m_rumble_unlocked) + SetMotorStateForConfigIndex(2, data_in); + + *data_out = Truncate8(m_axis_state[static_cast(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(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(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(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); diff --git a/src/core/analog_controller.h b/src/core/analog_controller.h index 21cbe635f..e658296b9 100644 --- a/src/core/analog_controller.h +++ b/src/core/analog_controller.h @@ -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(Axis::Count)> m_axis_state{}; + enum : u8 + { + LargeMotor = 0, + SmallMotor = 1 + }; + + std::array 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); diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index 1d5bfe1f7..8e8d90d67 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -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)