mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-11 00:56:37 +00:00
Merge pull request #12816 from LunaMoo/XVibrationCheat
Implement Xinput vibration CWCheat (PPSSPP specific 0xA code type)
This commit is contained in:
commit
cc916180b5
@ -24,6 +24,8 @@
|
||||
static int CheatEvent = -1;
|
||||
static CWCheatEngine *cheatEngine;
|
||||
static bool cheatsEnabled;
|
||||
using namespace SceCtrl;
|
||||
|
||||
void hleCheat(u64 userdata, int cyclesLate);
|
||||
|
||||
static inline std::string TrimString(const std::string &s) {
|
||||
@ -417,6 +419,8 @@ enum class CheatOp {
|
||||
MultiWrite,
|
||||
|
||||
CopyBytesFrom,
|
||||
Vibration,
|
||||
VibrationFromMemory,
|
||||
Delay,
|
||||
|
||||
Assert,
|
||||
@ -465,6 +469,12 @@ struct CheatOperation {
|
||||
int count;
|
||||
int type;
|
||||
} pointerCommands;
|
||||
struct {
|
||||
uint16_t vibrL;
|
||||
uint16_t vibrR;
|
||||
uint8_t vibrLTime;
|
||||
uint8_t vibrRTime;
|
||||
} vibrationValues;
|
||||
};
|
||||
};
|
||||
|
||||
@ -596,6 +606,25 @@ CheatOperation CWCheatEngine::InterpretNextCwCheat(const CheatCode &cheat, size_
|
||||
}
|
||||
return { CheatOp::Invalid };
|
||||
|
||||
case 0xA: // PPSSPP specific cheats
|
||||
switch (line1.part1 >> 24 & 0xF) {
|
||||
case 0x0: // 0x0 sets gamepad vibration by cheat parameters
|
||||
{
|
||||
CheatOperation op = { CheatOp::Vibration };
|
||||
op.vibrationValues.vibrL = line1.part1 & 0x0000FFFF;
|
||||
op.vibrationValues.vibrR = line1.part2 & 0x0000FFFF;
|
||||
op.vibrationValues.vibrLTime = (line1.part1 >> 16) & 0x000000FF;
|
||||
op.vibrationValues.vibrRTime = (line1.part2 >> 16) & 0x000000FF;
|
||||
return op;
|
||||
}
|
||||
case 0x1: // 0x1 reads value for gamepad vibration from memory
|
||||
addr = line1.part2;
|
||||
return { CheatOp::VibrationFromMemory, addr };
|
||||
// Place for other PPSSPP specific cheats
|
||||
default:
|
||||
return { CheatOp::Invalid };
|
||||
}
|
||||
|
||||
case 0xB: // Delay command.
|
||||
return { CheatOp::Delay, 0, 0, arg };
|
||||
|
||||
@ -864,6 +893,32 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat,
|
||||
}
|
||||
break;
|
||||
|
||||
case CheatOp::Vibration:
|
||||
if (op.vibrationValues.vibrL > 0) {
|
||||
SetLeftVibration(op.vibrationValues.vibrL);
|
||||
SetVibrationLeftDropout(op.vibrationValues.vibrLTime);
|
||||
}
|
||||
if (op.vibrationValues.vibrR > 0) {
|
||||
SetRightVibration(op.vibrationValues.vibrR);
|
||||
SetVibrationRightDropout(op.vibrationValues.vibrRTime);
|
||||
}
|
||||
break;
|
||||
|
||||
case CheatOp::VibrationFromMemory:
|
||||
if (Memory::IsValidAddress(op.addr) && Memory::IsValidAddress(op.addr + 0x4)) {
|
||||
uint16_t checkLeftVibration = Memory::Read_U16(op.addr);
|
||||
uint16_t checkRightVibration = Memory::Read_U16(op.addr + 0x2);
|
||||
if (checkLeftVibration > 0) {
|
||||
SetLeftVibration(checkLeftVibration);
|
||||
SetVibrationLeftDropout(Memory::Read_U8(op.addr + 0x4));
|
||||
}
|
||||
if (checkRightVibration > 0) {
|
||||
SetRightVibration(checkRightVibration);
|
||||
SetVibrationRightDropout(Memory::Read_U8(op.addr + 0x6));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CheatOp::Delay:
|
||||
// TODO: Not supported.
|
||||
break;
|
||||
|
@ -53,7 +53,6 @@ public:
|
||||
std::string CheatFilename();
|
||||
void Run();
|
||||
bool HasCheats();
|
||||
|
||||
void InvalidateICache(u32 addr, int size);
|
||||
private:
|
||||
u32 GetAddress(u32 value);
|
||||
|
@ -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,26 @@ void Register_sceCtrl_driver()
|
||||
{
|
||||
RegisterModule("sceCtrl_driver", ARRAY_SIZE(sceCtrl), sceCtrl);
|
||||
}
|
||||
|
||||
u16 sceCtrlGetRightVibration() {
|
||||
return rightVibration;
|
||||
}
|
||||
|
||||
u16 sceCtrlGetLeftVibration() {
|
||||
return leftVibration;
|
||||
}
|
||||
|
||||
namespace SceCtrl {
|
||||
void SetRightVibration(u16 rVibration) {
|
||||
rightVibration = rVibration;
|
||||
}
|
||||
void SetLeftVibration(u16 lVibration) {
|
||||
leftVibration = lVibration;
|
||||
}
|
||||
void SetVibrationRightDropout(u8 vibrationRDropout) {
|
||||
vibrationRightDropout = vibrationRDropout;
|
||||
}
|
||||
void SetVibrationLeftDropout(u8 vibrationLDropout) {
|
||||
vibrationLeftDropout = vibrationLDropout;
|
||||
}
|
||||
}
|
||||
|
@ -87,3 +87,13 @@ void __CtrlPeekAnalog(int stick, float *x, float *y);
|
||||
u32 __CtrlReadLatch();
|
||||
|
||||
void Register_sceCtrl_driver();
|
||||
|
||||
u16 sceCtrlGetRightVibration();
|
||||
u16 sceCtrlGetLeftVibration();
|
||||
|
||||
namespace SceCtrl {
|
||||
void SetLeftVibration(u16 lVibration);
|
||||
void SetRightVibration(u16 rVibration);
|
||||
void SetVibrationLeftDropout(u8 vibrationLDropout);
|
||||
void SetVibrationRightDropout(u8 vibrationRDropout);
|
||||
};
|
||||
|
@ -11,17 +11,23 @@
|
||||
#include "input/input_state.h"
|
||||
#include "input/keycodes.h"
|
||||
#include "XinputDevice.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HLE/sceCtrl.h"
|
||||
#include "Common/Timer.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;
|
||||
static int newVibrationTime = 0;
|
||||
|
||||
static void UnloadXInputDLL();
|
||||
|
||||
@ -65,6 +71,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 +98,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 +249,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 +267,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 +359,34 @@ void XinputDevice::ApplyButtons(int pad, const XINPUT_STATE &state) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void XinputDevice::ApplyVibration(int pad, XINPUT_VIBRATION &vibration) {
|
||||
if (PSP_IsInited()) {
|
||||
newVibrationTime = Common::Timer::GetTimeMs() >> 6;
|
||||
// We have to run PPSSPP_XInputSetState at time intervals
|
||||
// since it bugs otherwise with very high unthrottle speeds
|
||||
// and freezes at constant vibration or no vibration at all.
|
||||
if (abs(newVibrationTime - prevVibrationTime) >= 1) {
|
||||
if (GetUIState() == UISTATE_INGAME) {
|
||||
vibration.wLeftMotorSpeed = sceCtrlGetLeftVibration(); // use any value between 0-65535 here
|
||||
vibration.wRightMotorSpeed = sceCtrlGetRightVibration(); // use any value between 0-65535 here
|
||||
} else {
|
||||
vibration.wLeftMotorSpeed = 0;
|
||||
vibration.wRightMotorSpeed = 0;
|
||||
}
|
||||
|
||||
if ((prevVibration[pad].wLeftMotorSpeed != vibration.wLeftMotorSpeed || prevVibration[pad].wRightMotorSpeed != vibration.wRightMotorSpeed)) {
|
||||
PPSSPP_XInputSetState(pad, &vibration);
|
||||
prevVibration[pad] = vibration;
|
||||
}
|
||||
prevVibrationTime = newVibrationTime;
|
||||
}
|
||||
} else {
|
||||
DWORD dwResult = PPSSPP_XInputSetState(pad, &vibration);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
check_delay[pad] = 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 @@ public:
|
||||
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]{};
|
||||
int prevVibrationTime = 0;
|
||||
u32 prevButtons[4]{};
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user