#include "SDL_internal.h"
#ifdef SDL_JOYSTICK_PS2
#include <libmtap.h>
#include <libpad.h>
#include <ps2_joystick_driver.h>
#include <stdio.h>
#include <stdlib.h>
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
#define PS2_MAX_PORT 2
#define PS2_MAX_SLOT 4
#define MAX_CONTROLLERS (PS2_MAX_PORT * PS2_MAX_SLOT)
#define PS2_ANALOG_STICKS 2
#define PS2_ANALOG_AXIS 2
#define PS2_BUTTONS 16
#define PS2_TOTAL_AXIS (PS2_ANALOG_STICKS * PS2_ANALOG_AXIS)
struct JoyInfo
{
uint8_t padBuf[256];
uint16_t btns;
uint8_t analog_state[PS2_TOTAL_AXIS];
uint8_t port;
uint8_t slot;
int8_t rumble_ready;
int8_t opened;
} __attribute__((aligned(64)));
static uint8_t enabled_pads = 0;
static struct JoyInfo joyInfo[MAX_CONTROLLERS];
static inline int16_t convert_u8_to_s16(uint8_t val)
{
if (val == 0) {
return -0x7fff;
}
return val * 0x0101 - 0x8000;
}
static inline uint8_t rumble_status(uint8_t index)
{
char actAlign[6];
int res;
struct JoyInfo *info = &joyInfo[index];
if (info->rumble_ready == 0) {
actAlign[0] = 0;
actAlign[1] = 1;
actAlign[2] = 0xff;
actAlign[3] = 0xff;
actAlign[4] = 0xff;
actAlign[5] = 0xff;
res = padSetActAlign(info->port, info->slot, actAlign);
info->rumble_ready = res <= 0 ? -1 : 1;
}
return info->rumble_ready == 1;
}
static bool PS2_JoystickInit(void)
{
uint32_t port = 0;
uint32_t slot = 0;
if (init_joystick_driver(true) < 0) {
return false;
}
for (port = 0; port < PS2_MAX_PORT; port++) {
mtapPortOpen(port);
}
for (slot = 0; slot < PS2_MAX_SLOT; slot++) {
for (port = 0; port < PS2_MAX_PORT; port++) {
struct JoyInfo *info = &joyInfo[enabled_pads];
if (padPortOpen(port, slot, (void *)info->padBuf) > 0) {
info->port = (uint8_t)port;
info->slot = (uint8_t)slot;
info->opened = 1;
enabled_pads++;
SDL_PrivateJoystickAdded(enabled_pads);
}
}
}
return (enabled_pads > 0);
}
static int PS2_JoystickGetCount(void)
{
return (int)enabled_pads;
}
static void PS2_JoystickDetect(void)
{
}
static bool PS2_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
{
return false;
}
static const char *PS2_JoystickGetDeviceName(int index)
{
if (index >= 0 && index < enabled_pads) {
return "PS2 Controller";
}
SDL_SetError("No joystick available with that index");
return NULL;
}
static const char *PS2_JoystickGetDevicePath(int index)
{
return NULL;
}
static int PS2_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
{
return -1;
}
static int PS2_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
}
static void PS2_JoystickSetDevicePlayerIndex(int device_index, int player_index)
{
}
static SDL_GUID PS2_JoystickGetDeviceGUID(int device_index)
{
const char *name = PS2_JoystickGetDeviceName(device_index);
return SDL_CreateJoystickGUIDForName(name);
}
static SDL_JoystickID PS2_JoystickGetDeviceInstanceID(int device_index)
{
return device_index + 1;
}
static void PS2_WaitPadReady(int port, int slot)
{
int state = padGetState(port, slot);
while ((state != PAD_STATE_STABLE) && (state != PAD_STATE_FINDCTP1)) {
SDL_Delay(1);
state = padGetState(port, slot);
}
}
static void PS2_InitializePad(int port, int slot)
{
int modes;
int i;
char actAlign[6];
PS2_WaitPadReady(port, slot);
modes = padInfoMode(port, slot, PAD_MODETABLE, -1);
for (i = 0; i < modes; i++) {
if (padInfoMode(port, slot, PAD_MODETABLE, i) == PAD_TYPE_DUALSHOCK) {
break;
}
}
if (i >= modes) {
return;
}
if (!padInfoMode(port, slot, PAD_MODECUREXID, 0)) {
return;
}
padSetMainMode(port, slot, PAD_MMODE_DUALSHOCK, PAD_MMODE_LOCK);
PS2_WaitPadReady(port, slot);
padEnterPressMode(port, slot);
PS2_WaitPadReady(port, slot);
if (padInfoAct(port, slot, -1, 0)) {
actAlign[0] = 0; actAlign[1] = 1; actAlign[2] = 0xff;
actAlign[3] = 0xff;
actAlign[4] = 0xff;
actAlign[5] = 0xff;
PS2_WaitPadReady(port, slot);
padSetActAlign(port, slot, actAlign);
}
PS2_WaitPadReady(port, slot);
}
static bool PS2_JoystickOpen(SDL_Joystick *joystick, int device_index)
{
struct JoyInfo *info = &joyInfo[device_index];
if (!info->opened) {
if (padPortOpen(info->port, info->slot, (void *)info->padBuf) > 0) {
info->opened = 1;
} else {
return false;
}
}
PS2_InitializePad(info->port, info->slot);
joystick->nbuttons = PS2_BUTTONS;
joystick->naxes = PS2_TOTAL_AXIS;
joystick->nhats = 0;
SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
return true;
}
static bool PS2_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
char actAlign[6];
int res;
int index = (int)(joystick->instance_id - 1);
struct JoyInfo *info = &joyInfo[index];
if (!rumble_status(index)) {
return false;
}
actAlign[0] = low_frequency_rumble >> 8; actAlign[1] = high_frequency_rumble >> 8; actAlign[2] = 0xff;
actAlign[3] = 0xff;
actAlign[4] = 0xff;
actAlign[5] = 0xff;
res = padSetActDirect(info->port, info->slot, actAlign);
return (res == 1);
}
static bool PS2_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left, Uint16 right)
{
return SDL_Unsupported();
}
static bool PS2_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
{
return SDL_Unsupported();
}
static bool PS2_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
{
return SDL_Unsupported();
}
static bool PS2_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
{
return SDL_Unsupported();
}
static void PS2_JoystickUpdate(SDL_Joystick *joystick)
{
uint8_t i;
uint8_t previous_axis, current_axis;
uint16_t mask, previous, current;
struct padButtonStatus buttons;
uint8_t all_axis[PS2_TOTAL_AXIS];
int index = (int)(joystick->instance_id - 1);
struct JoyInfo *info = &joyInfo[index];
int state = padGetState(info->port, info->slot);
Uint64 timestamp = SDL_GetTicksNS();
if (state != PAD_STATE_DISCONN && state != PAD_STATE_EXECCMD && state != PAD_STATE_ERROR) {
int ret = padRead(info->port, info->slot, &buttons); if (ret != 0) {
int32_t pressed_buttons = 0xffff ^ buttons.btns;
;
if (info->btns != pressed_buttons) {
for (i = 0; i < PS2_BUTTONS; i++) {
mask = (1 << i);
previous = info->btns & mask;
current = pressed_buttons & mask;
if (previous != current) {
SDL_SendJoystickButton(timestamp, joystick, i, (current != 0));
}
}
}
info->btns = pressed_buttons;
all_axis[0] = buttons.ljoy_h;
all_axis[1] = buttons.ljoy_v;
all_axis[2] = buttons.rjoy_h;
all_axis[3] = buttons.rjoy_v;
for (i = 0; i < PS2_TOTAL_AXIS; i++) {
previous_axis = info->analog_state[i];
current_axis = all_axis[i];
if (previous_axis != current_axis) {
SDL_SendJoystickAxis(timestamp, joystick, i, convert_u8_to_s16(current_axis));
}
info->analog_state[i] = current_axis;
}
}
}
}
static void PS2_JoystickClose(SDL_Joystick *joystick)
{
int index = (int)(joystick->instance_id - 1);
struct JoyInfo *info = &joyInfo[index];
padPortClose(info->port, info->slot);
info->opened = 0;
}
static void PS2_JoystickQuit(void)
{
deinit_joystick_driver(true);
}
static bool PS2_GetGamepadMapping(int device_index, SDL_GamepadMapping *out)
{
return false;
}
SDL_JoystickDriver SDL_PS2_JoystickDriver = {
PS2_JoystickInit,
PS2_JoystickGetCount,
PS2_JoystickDetect,
PS2_JoystickIsDevicePresent,
PS2_JoystickGetDeviceName,
PS2_JoystickGetDevicePath,
PS2_JoystickGetDeviceSteamVirtualGamepadSlot,
PS2_JoystickGetDevicePlayerIndex,
PS2_JoystickSetDevicePlayerIndex,
PS2_JoystickGetDeviceGUID,
PS2_JoystickGetDeviceInstanceID,
PS2_JoystickOpen,
PS2_JoystickRumble,
PS2_JoystickRumbleTriggers,
PS2_JoystickSetLED,
PS2_JoystickSendEffect,
PS2_JoystickSetSensorsEnabled,
PS2_JoystickUpdate,
PS2_JoystickClose,
PS2_JoystickQuit,
PS2_GetGamepadMapping,
};
#endif