diff --git a/pcsx2/SIO/Pad/PadDualshock2.cpp b/pcsx2/SIO/Pad/PadDualshock2.cpp index 7c8089aec4..4604ddae96 100644 --- a/pcsx2/SIO/Pad/PadDualshock2.cpp +++ b/pcsx2/SIO/Pad/PadDualshock2.cpp @@ -150,9 +150,12 @@ u8 PadDualshock2::Poll(u8 commandByte) return (buttons >> 8) & 0xff; case 4: this->vibrationMotors[1] = commandByte; + // Order is reversed here - SetPadVibrationIntensity takes large motor first, then small. PS2 orders small motor first, large motor second. InputManager::SetPadVibrationIntensity(this->unifiedSlot, - std::min(static_cast(this->vibrationMotors[0]) * GetVibrationScale(0) * (1.0f / 255.0f), 1.0f), - std::min(static_cast(this->vibrationMotors[1]) * GetVibrationScale(1) * (1.0f / 255.0f), 1.0f) + std::min(static_cast(this->vibrationMotors[1]) * GetVibrationScale(1) * (1.0f / 255.0f), 1.0f), + // Small motor on the PS2 is either on full power or zero power, it has no variable speed. If the game supplies any value here at all, + // the pad in turn supplies full power to the motor, or no power at all if zero. + std::min(static_cast((this->vibrationMotors[0] ? 0xff : 0)) * GetVibrationScale(0) * (1.0f / 255.0f), 1.0f) ); // PS1 mode: If the controller is still in digital mode, it is time to stop acknowledging. @@ -378,14 +381,27 @@ u8 PadDualshock2::Constant3(u8 commandByte) } } +// Set which byte of the poll command will correspond to a motor's power level. +// In all known cases, games never rearrange the motors. We've hard coded pad polls +// to always use the first vibration byte as small motor, and the second as big motor. +// There is no reason to rearrange these. Games never rearrange these. If someone does +// try to rearrange these, they should suffer. +// +// The return values for cases 3 and 4 are just to notify the pad module of what the mapping was, prior to this command. u8 PadDualshock2::VibrationMap(u8 commandByte) { + u8 ret = 0xff; + switch (commandBytesReceived) { case 3: - return 0x00; + ret = this->smallMotorLastConfig; + this->smallMotorLastConfig = commandByte; + return ret; case 4: - return 0x01; + ret = this->largeMotorLastConfig; + this->largeMotorLastConfig = commandByte; + return ret; case 8: g_Sio0.SetAcknowledge(false); return 0xff; @@ -787,6 +803,8 @@ bool PadDualshock2::Freeze(StateWrapper& sw) sw.Do(&vibrationScale); sw.Do(&pressureModifier); sw.Do(&buttonDeadzone); + sw.Do(&smallMotorLastConfig); + sw.Do(&largeMotorLastConfig); return !sw.HasError(); } diff --git a/pcsx2/SIO/Pad/PadDualshock2.h b/pcsx2/SIO/Pad/PadDualshock2.h index eaa1ff4308..ed2cd8ac10 100644 --- a/pcsx2/SIO/Pad/PadDualshock2.h +++ b/pcsx2/SIO/Pad/PadDualshock2.h @@ -96,6 +96,10 @@ private: // a way to simulate pressure sensitive controls. float pressureModifier; float buttonDeadzone; + // Used to store the last vibration mapping request the PS2 made for the small motor. + u8 smallMotorLastConfig = 0xff; + // Used to store the last vibration mapping request the PS2 made for the large motor. + u8 largeMotorLastConfig = 0xff; u8 Mystery(u8 commandByte); u8 ButtonQuery(u8 commandByte); diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index c22db935d2..6c79c5281c 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -37,7 +37,7 @@ enum class FreezeAction // [SAVEVERSION+] // This informs the auto updater that the users savestates will be invalidated. -static const u32 g_SaveVersion = (0x9A3B << 16) | 0x0000; +static const u32 g_SaveVersion = (0x9A3C << 16) | 0x0000; // the freezing data between submodules and core