Skip to content

Commit

Permalink
Implement PPSSPP specific cheats:
Browse files Browse the repository at this point in the history
0xA0 vibration cheat(Xinput)
Syntax is: 0xA0NNLLLL 0x00MMRRRR
where NN/MM is time vibration lasts LLLL/RRRR is the vibration power

0xA1 read value for the vibration(Xinput) from game memory
Syntax is: 0xA1000000 0xNNNNNNNN
where NNNNNNNN is the address that stores following values at offset:
0xNNNNNNNN + 0x0 Left Vibration power,
0xNNNNNNNN + 0x2 Right Vibration Power
0xNNNNNNNN + 0x4 Left Vibration time
0xNNNNNNNN + 0x6 Right Vibration time

Left some room for other PPSSPP specific cheats(0xA2-0xAF)
  • Loading branch information
LunaMoo committed Apr 16, 2020
1 parent 7d3552a commit e999444
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 5 deletions.
55 changes: 55 additions & 0 deletions Core/CwCheat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ std::string gameTitle;
std::string activeCheatFile;
static CWCheatEngine *cheatEngine;
static bool cheatsEnabled;
using namespace SceCtrl;

void hleCheat(u64 userdata, int cyclesLate);

static inline std::string TrimString(const std::string &s) {
Expand Down Expand Up @@ -412,6 +414,8 @@ enum class CheatOp {
MultiWrite,

CopyBytesFrom,
Vibration,
VibrationFromMemory,
Delay,

Assert,
Expand Down Expand Up @@ -460,6 +464,12 @@ struct CheatOperation {
int count;
int type;
} pointerCommands;
struct {
uint16_t vibrL;
uint16_t vibrR;
uint8_t vibrLTime;
uint8_t vibrRTime;
} vibrationValues;
};
};

Expand Down Expand Up @@ -591,6 +601,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 };

Expand Down Expand Up @@ -859,6 +888,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;
Expand Down
1 change: 0 additions & 1 deletion Core/CwCheat.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class CWCheatEngine {
void CreateCheatFile();
void Run();
bool HasCheats();

void InvalidateICache(u32 addr, int size);
private:
u32 GetAddress(u32 value);
Expand Down
33 changes: 33 additions & 0 deletions Core/HLE/sceCtrl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
}
10 changes: 10 additions & 0 deletions Core/HLE/sceCtrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
56 changes: 54 additions & 2 deletions Windows/XinputDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
}

Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
}
}

7 changes: 5 additions & 2 deletions Windows/XinputDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "InputDevice.h"
#include "Xinput.h"

#include "Core/HLE/sceCtrl.h"

class XinputDevice final : public InputDevice {
public:
Expand All @@ -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]{};
int prevVibrationTime = 0;
u32 prevButtons[4]{};
};

0 comments on commit e999444

Please sign in to comment.