From ae15e149eebb6baac786be28076b376d8f157eae Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Sun, 12 Apr 2020 15:08:46 +0200 Subject: [PATCH] Implement PPSSPP specific vibration cheat(Xinput) Syntax is: 0xA0NNLLLL 0x00MMRRRR where NN/MM is time vibration lasts LLLL/RRRR is the vibration power --- Core/CwCheat.cpp | 8 ++++++++ Core/CwCheat.h | 1 - Core/HLE/sceCtrl.cpp | 32 +++++++++++++++++++++++++++++++ Core/HLE/sceCtrl.h | 11 +++++++++++ Windows/PPSSPP.vcxproj | 2 +- Windows/XinputDevice.cpp | 41 ++++++++++++++++++++++++++++++++++++++-- Windows/XinputDevice.h | 7 +++++-- 7 files changed, 96 insertions(+), 6 deletions(-) diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index b50ed2ce8d01..7f618503d507 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -24,6 +24,7 @@ static int CheatEvent = -1; static CWCheatEngine *cheatEngine; static bool cheatsEnabled; +SceCtrl vibrationCheat; void hleCheat(u64 userdata, int cyclesLate); static inline std::string TrimString(const std::string &s) { @@ -596,6 +597,13 @@ CheatOperation CWCheatEngine::InterpretNextCwCheat(const CheatCode &cheat, size_ } return { CheatOp::Invalid }; + case 0xA: // Vibration command(PPSSPP specific) + vibrationCheat.SetLeftVibration(line1.part1 & 0x0000FFFF); + vibrationCheat.SetRightVibration(line1.part2 & 0x0000FFFF); + vibrationCheat.SetVibrationLeftDropout(line1.part1 >> 16 & 0x000000FF); + vibrationCheat.SetVibrationRightDropout(line1.part2 >> 16 & 0x000000FF); + return { CheatOp::Invalid }; + case 0xB: // Delay command. return { CheatOp::Delay, 0, 0, arg }; diff --git a/Core/CwCheat.h b/Core/CwCheat.h index 00c6134fd96e..578ffe7b98b1 100644 --- a/Core/CwCheat.h +++ b/Core/CwCheat.h @@ -53,7 +53,6 @@ class CWCheatEngine { std::string CheatFilename(); void Run(); bool HasCheats(); - void InvalidateICache(u32 addr, int size); private: u32 GetAddress(u32 value); diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 79888ebea50e..42fc548d38b4 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -87,6 +87,12 @@ static std::mutex ctrlMutex; static int ctrlTimer = -1; +static u16 leftVibration = 0; +static u16 rightVibration = 0; +// The higher the dropout, the longer Vibration will run +static u8 vibrationLeftDropout = 160; +static u8 vibrationRightDropout = 160; + // STATE END ////////////////////////////////////////////////////////////////////////// @@ -288,6 +294,10 @@ static void __CtrlVblank() { emuRapidFireFrames++; + // Reduce gamepad Vibration by set % each frame + leftVibration *= (float)vibrationLeftDropout / 256.0f; + rightVibration *= (float)vibrationRightDropout / 256.0f; + // This always runs, so make sure we're in vblank mode. if (ctrlCycle == 0) __CtrlDoSample(); @@ -563,3 +573,25 @@ void Register_sceCtrl_driver() { RegisterModule("sceCtrl_driver", ARRAY_SIZE(sceCtrl), sceCtrl); } + +u16 GetRightVibration() { + return rightVibration; +} + +u16 GetLeftVibration() { + return leftVibration; +} + +void SceCtrl::SetRightVibration(u16 rVibration) { + rightVibration = rVibration; +} +void SceCtrl::SetLeftVibration(u16 lVibration) { + leftVibration = lVibration; +} +void SceCtrl::SetVibrationRightDropout(u8 vibrationRDropout) { + vibrationRightDropout = vibrationRDropout; + +} +void SceCtrl::SetVibrationLeftDropout(u8 vibrationLDropout) { + vibrationLeftDropout = vibrationLDropout; +} diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index 0c2d757ddeb5..ab82291feaac 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -87,3 +87,14 @@ void __CtrlPeekAnalog(int stick, float *x, float *y); u32 __CtrlReadLatch(); void Register_sceCtrl_driver(); + +u16 GetRightVibration(); +u16 GetLeftVibration(); + +class SceCtrl { +public: + void SetLeftVibration(u16 lVibration); + void SetRightVibration(u16 rVibration); + void SetVibrationLeftDropout(u8 vibrationLDropout); + void SetVibrationRightDropout(u8 vibrationRDropout); +}; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 1792fb969b44..682bd43233f4 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -1027,4 +1027,4 @@ - + \ No newline at end of file diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index c297533a18cb..a948c3ab3b50 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -11,14 +11,18 @@ #include "input/input_state.h" #include "input/keycodes.h" #include "XinputDevice.h" +#include "Core/Core.h" +#include "Core/HLE/sceCtrl.h" // Utilities to dynamically load XInput. Adapted from SDL. #if !PPSSPP_PLATFORM(UWP) typedef DWORD (WINAPI *XInputGetState_t) (DWORD dwUserIndex, XINPUT_STATE* pState); +typedef DWORD (WINAPI *XInputSetState_t) (DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); static XInputGetState_t PPSSPP_XInputGetState = NULL; +static XInputSetState_t PPSSPP_XInputSetState = NULL; static DWORD PPSSPP_XInputVersion = 0; static HMODULE s_pXInputDLL = 0; static int s_XInputDLLRefCount = 0; @@ -65,6 +69,17 @@ static int LoadXInputDLL() { return -1; } + /* Let's try the name first, then fall back to a non-Ex version (xinput9_1_0.dll doesn't have Ex) */ + PPSSPP_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetStateEx"); + if (!PPSSPP_XInputSetState) { + PPSSPP_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetState"); + } + + if (!PPSSPP_XInputSetState) { + UnloadXInputDLL(); + return -1; + } + return 0; } @@ -81,6 +96,7 @@ static void UnloadXInputDLL() { static int LoadXInputDLL() { return 0; } static void UnloadXInputDLL() {} #define PPSSPP_XInputGetState XInputGetState +#define PPSSPP_XInputSetState XInputSetState #endif #ifndef XUSER_MAX_COUNT @@ -231,11 +247,13 @@ int XinputDevice::UpdateState() { for (int i = 0; i < XUSER_MAX_COUNT; i++) { XINPUT_STATE state; ZeroMemory(&state, sizeof(XINPUT_STATE)); + XINPUT_VIBRATION vibration; + ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION)); if (check_delay[i]-- > 0) continue; DWORD dwResult = PPSSPP_XInputGetState(i, &state); if (dwResult == ERROR_SUCCESS) { - UpdatePad(i, state); + UpdatePad(i, state, vibration); anySuccess = true; } else { check_delay[i] = 30; @@ -247,13 +265,14 @@ int XinputDevice::UpdateState() { return anySuccess ? UPDATESTATE_SKIP_PAD : 0; } -void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state) { +void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATION &vibration) { static bool notified = false; if (!notified) { notified = true; KeyMap::NotifyPadConnected("Xbox 360 Pad"); } ApplyButtons(pad, state); + ApplyVibration(pad, vibration); const float STICK_DEADZONE = g_Config.fXInputAnalogDeadzone; const int STICK_INV_MODE = g_Config.iXInputAnalogInverseMode; @@ -338,3 +357,21 @@ void XinputDevice::ApplyButtons(int pad, const XINPUT_STATE &state) { } } } + + +void XinputDevice::ApplyVibration(int pad, XINPUT_VIBRATION &vibration) { + if (PSP_IsInited()) { + vibration.wLeftMotorSpeed = GetLeftVibration(); // use any value between 0-65535 here + vibration.wRightMotorSpeed = GetRightVibration(); // use any value between 0-65535 here + if (prevVibration[pad].wLeftMotorSpeed != vibration.wLeftMotorSpeed || prevVibration[pad].wRightMotorSpeed != vibration.wRightMotorSpeed) { + PPSSPP_XInputSetState(pad, &vibration); + prevVibration[pad] = vibration; + } + } else { + DWORD dwResult = PPSSPP_XInputSetState(pad, &vibration); + if (dwResult != ERROR_SUCCESS) { + check_delay[pad] = 30; + } + } +} + diff --git a/Windows/XinputDevice.h b/Windows/XinputDevice.h index 4d0510498e0f..c1cac288870d 100644 --- a/Windows/XinputDevice.h +++ b/Windows/XinputDevice.h @@ -2,7 +2,7 @@ #include "InputDevice.h" #include "Xinput.h" - +#include "Core/HLE/sceCtrl.h" class XinputDevice final : public InputDevice { public: @@ -11,9 +11,12 @@ class XinputDevice final : public InputDevice { virtual int UpdateState() override; private: - void UpdatePad(int pad, const XINPUT_STATE &state); + void UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATION &vibration); void ApplyButtons(int pad, const XINPUT_STATE &state); + void ApplyVibration(int pad, XINPUT_VIBRATION &vibration); int check_delay[4]{}; XINPUT_STATE prevState[4]{}; + XINPUT_VIBRATION prevVibration[4]{}; + u32 prevButtons[4]{}; };