Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]{};
};