Skip to content
Open
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
8 changes: 8 additions & 0 deletions ps2xRuntime/include/ps2_call_list.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <cstdint>

// I know ugly, but will work for now.

#define PS2_SYSCALL_LIST(X) \
Expand Down Expand Up @@ -612,3 +614,9 @@
X(syMallocInit) \
X(syRtcInit) \
/* Game/middleware */

// Test hooks: override pad input for scePadRead.
#define PS2_TEST_HOOK_LIST(X) \
X(setPadOverrideState, (uint16_t buttons, uint8_t lx, uint8_t ly, \
uint8_t rx, uint8_t ry)) \
X(clearPadOverrideState, (void))
5 changes: 5 additions & 0 deletions ps2xRuntime/include/ps2_stubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ namespace ps2_stubs
PS2_STUB_LIST(PS2_DECLARE_STUB)
#undef PS2_DECLARE_STUB

#define PS2_DECLARE_TEST_HOOK(name, signature) void name signature;
PS2_TEST_HOOK_LIST(PS2_DECLARE_TEST_HOOK)
#undef PS2_DECLARE_TEST_HOOK

void syMalloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sndr_trans_func(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);

void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);

}

#endif // PS2_STUBS_H
206 changes: 206 additions & 0 deletions ps2xRuntime/src/lib/ps2_stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,200 @@
#include <unordered_set>
#include <filesystem>
#include <mutex>
#include <atomic>
#include <limits>

#include "raylib.h"
#include "stubs/helpers/ps2_stubs_helpers.inl"

namespace ps2_stubs
{
namespace
{
constexpr uint8_t kPadModeDualShock = 0x73;
constexpr uint8_t kPadAnalogCenter = 0x80;

constexpr uint16_t kPadBtnSelect = 1u << 0;
constexpr uint16_t kPadBtnL3 = 1u << 1;
constexpr uint16_t kPadBtnR3 = 1u << 2;
constexpr uint16_t kPadBtnStart = 1u << 3;
constexpr uint16_t kPadBtnUp = 1u << 4;
constexpr uint16_t kPadBtnRight = 1u << 5;
constexpr uint16_t kPadBtnDown = 1u << 6;
constexpr uint16_t kPadBtnLeft = 1u << 7;
constexpr uint16_t kPadBtnL2 = 1u << 8;
constexpr uint16_t kPadBtnR2 = 1u << 9;
constexpr uint16_t kPadBtnL1 = 1u << 10;
constexpr uint16_t kPadBtnR1 = 1u << 11;
constexpr uint16_t kPadBtnTriangle = 1u << 12;
constexpr uint16_t kPadBtnCircle = 1u << 13;
constexpr uint16_t kPadBtnCross = 1u << 14;
constexpr uint16_t kPadBtnSquare = 1u << 15;

struct PadInputState
{
uint16_t buttons = 0xFFFF; // active-low
uint8_t rx = kPadAnalogCenter;
uint8_t ry = kPadAnalogCenter;
uint8_t lx = kPadAnalogCenter;
uint8_t ly = kPadAnalogCenter;
};

std::mutex g_padOverrideMutex;
bool g_padOverrideEnabled = false;
PadInputState g_padOverrideState{};
bool g_padDebugCached = false;
bool g_padDebugEnabled = false;

uint8_t axisToByte(float axis)
{
axis = std::clamp(axis, -1.0f, 1.0f);
const float mapped = (axis + 1.0f) * 127.5f;
return static_cast<uint8_t>(std::lround(mapped));
}

bool padDebugEnabled()
{
if (!g_padDebugCached)
{
const char *env = std::getenv("PS2_PAD_DEBUG");
g_padDebugEnabled = (env && *env && std::strcmp(env, "0") != 0);
g_padDebugCached = true;
}
return g_padDebugEnabled;
}

void setButton(PadInputState &state, uint16_t mask, bool pressed)
{
if (pressed)
{
state.buttons = static_cast<uint16_t>(state.buttons & ~mask);
}
}

int findFirstGamepad()
{
for (int i = 0; i < 4; ++i)
{
if (IsGamepadAvailable(i))
{
return i;
}
}
return -1;
}

void applyGamepadState(PadInputState &state)
{
if (!IsWindowReady())
{
return;
}

const int gamepad = findFirstGamepad();
if (gamepad < 0)
{
return;
}

// Raylib mapping (PS2 -> raylib buttons/axes):
// D-Pad -> LEFT_FACE_*, Cross/Circle/Square/Triangle -> RIGHT_FACE_*
// L1/R1 -> TRIGGER_1, L2/R2 -> TRIGGER_2, L3/R3 -> THUMB
// Select/Start -> MIDDLE_LEFT/MIDDLE_RIGHT
state.lx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X));
state.ly = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y));
state.rx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X));
state.ry = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y));

setButton(state, kPadBtnUp, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP));
setButton(state, kPadBtnDown, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN));
setButton(state, kPadBtnLeft, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT));
setButton(state, kPadBtnRight, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT));

setButton(state, kPadBtnCross, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN));
setButton(state, kPadBtnCircle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT));
setButton(state, kPadBtnSquare, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT));
setButton(state, kPadBtnTriangle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP));

setButton(state, kPadBtnL1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1));
setButton(state, kPadBtnR1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1));
setButton(state, kPadBtnL2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_2));
setButton(state, kPadBtnR2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_2));

setButton(state, kPadBtnL3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB));
setButton(state, kPadBtnR3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB));

setButton(state, kPadBtnSelect, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT));
setButton(state, kPadBtnStart, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT));
}

void applyKeyboardState(PadInputState &state, bool allowAnalog)
{
if (!IsWindowReady())
{
return;
}

// Keyboard mapping (PS2 -> keys):
// D-Pad: arrows, Square/Cross/Circle/Triangle: Z/X/C/V
// L1/R1: Q/E, L2/R2: 1/3, Start/Select: Enter/RightShift
// L3/R3: LeftCtrl/RightCtrl, Analog left: WASD
setButton(state, kPadBtnUp, IsKeyDown(KEY_UP));
setButton(state, kPadBtnDown, IsKeyDown(KEY_DOWN));
setButton(state, kPadBtnLeft, IsKeyDown(KEY_LEFT));
setButton(state, kPadBtnRight, IsKeyDown(KEY_RIGHT));

setButton(state, kPadBtnSquare, IsKeyDown(KEY_Z));
setButton(state, kPadBtnCross, IsKeyDown(KEY_X));
setButton(state, kPadBtnCircle, IsKeyDown(KEY_C));
setButton(state, kPadBtnTriangle, IsKeyDown(KEY_V));

setButton(state, kPadBtnL1, IsKeyDown(KEY_Q));
setButton(state, kPadBtnR1, IsKeyDown(KEY_E));
setButton(state, kPadBtnL2, IsKeyDown(KEY_ONE));
setButton(state, kPadBtnR2, IsKeyDown(KEY_THREE));

setButton(state, kPadBtnStart, IsKeyDown(KEY_ENTER));
setButton(state, kPadBtnSelect, IsKeyDown(KEY_RIGHT_SHIFT));
setButton(state, kPadBtnL3, IsKeyDown(KEY_LEFT_CONTROL));
setButton(state, kPadBtnR3, IsKeyDown(KEY_RIGHT_CONTROL));

if (!allowAnalog)
{
return;
}

float ax = 0.0f;
float ay = 0.0f;
if (IsKeyDown(KEY_D))
ax += 1.0f;
if (IsKeyDown(KEY_A))
ax -= 1.0f;
if (IsKeyDown(KEY_S))
ay += 1.0f;
if (IsKeyDown(KEY_W))
ay -= 1.0f;

if (ax != 0.0f || ay != 0.0f)
{
state.lx = axisToByte(ax);
state.ly = axisToByte(ay);
}
}

void fillPadStatus(uint8_t *data, const PadInputState &state)
{
std::memset(data, 0, 32);
data[1] = kPadModeDualShock;
data[2] = static_cast<uint8_t>(state.buttons & 0xFFu);
data[3] = static_cast<uint8_t>((state.buttons >> 8) & 0xFFu);
data[4] = state.rx;
data[5] = state.ry;
data[6] = state.lx;
data[7] = state.ly;
}
}

#include "stubs/ps2_stubs_libc.inl"
#include "stubs/ps2_stubs_ps2.inl"
#include "stubs/ps2_stubs_misc.inl"
Expand Down Expand Up @@ -72,4 +260,22 @@ namespace ps2_stubs
setReturnS32(ctx, -1); // Return error
}

void setPadOverrideState(uint16_t buttons, uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry)
{
std::lock_guard<std::mutex> lock(g_padOverrideMutex);
g_padOverrideEnabled = true;
g_padOverrideState.buttons = buttons;
g_padOverrideState.lx = lx;
g_padOverrideState.ly = ly;
g_padOverrideState.rx = rx;
g_padOverrideState.ry = ry;
}

void clearPadOverrideState()
{
std::lock_guard<std::mutex> lock(g_padOverrideMutex);
g_padOverrideEnabled = false;
g_padOverrideState = PadInputState{};
}

}
Loading