#include "Windows/Hid/DualShock.h"
#include "Windows/Hid/HidInputDevice.h"
#include "Windows/Hid/HidCommon.h"
static const ButtonInputMapping g_psInputMappings[] = {
{PS_DPAD_UP, NKCODE_DPAD_UP},
{PS_DPAD_DOWN, NKCODE_DPAD_DOWN},
{PS_DPAD_LEFT, NKCODE_DPAD_LEFT},
{PS_DPAD_RIGHT, NKCODE_DPAD_RIGHT},
{PS_BTN_SQUARE, NKCODE_BUTTON_4},
{PS_BTN_TRIANGLE, NKCODE_BUTTON_3},
{PS_BTN_CIRCLE, NKCODE_BUTTON_1},
{PS_BTN_CROSS, NKCODE_BUTTON_2},
{PS_BTN_PS_BUTTON, NKCODE_HOME},
{PS_BTN_SHARE, NKCODE_BUTTON_9},
{PS_BTN_OPTIONS, NKCODE_BUTTON_10},
{PS_BTN_L1, NKCODE_BUTTON_7},
{PS_BTN_R1, NKCODE_BUTTON_8},
{PS_BTN_TOUCHPAD, NKCODE_BUTTON_11},
{PS_BTN_L3, NKCODE_BUTTON_THUMBL},
{PS_BTN_R3, NKCODE_BUTTON_THUMBR},
};
void GetPSButtonInputMappings(const ButtonInputMapping **mappings, size_t *size) {
*mappings = g_psInputMappings;
*size = ARRAY_SIZE(g_psInputMappings);
}
enum class DS4FeatureBits : u8 {
VOL_L = 0x10,
VOL_R = 0x20,
MIC_VOL = 0x40,
SPEAKER_VOL = 0x80,
RUMBLE = 0x1,
LIGHTBAR = 0x2,
FLASH = 0x4,
};
ENUM_CLASS_BITOPS(DS4FeatureBits);
struct DualshockOutputReport {
u8 reportId;
u8 featureBits;
u8 two;
u8 pad;
u8 rumbleRight;
u8 rumbleLeft;
u8 lightbarRed;
u8 lightbarGreen;
u8 lightbarBlue;
u8 padding[23];
};
static_assert(sizeof(DualshockOutputReport) == 32);
struct DualShockInputReport {
u8 lx;
u8 ly;
u8 rx;
u8 ry;
u8 buttons[3];
u8 l2_analog;
u8 r2_analog;
u8 pad[2];
u8 battery;
};
static void FillDualShockOutputReport(DualshockOutputReport *report, bool lightsOn) {
report->reportId = 0x05;
report->featureBits = (u8)(DS4FeatureBits::RUMBLE | DS4FeatureBits::LIGHTBAR | DS4FeatureBits::FLASH);
report->two = 2;
report->rumbleRight = 0;
report->rumbleLeft = 0;
report->lightbarRed = lightsOn ? LED_R : 0;
report->lightbarGreen = lightsOn ? LED_G : 0;
report->lightbarBlue = lightsOn ? LED_B : 0;
}
bool InitializeDualShock(HANDLE handle, int outReportSize) {
if (outReportSize != sizeof(DualshockOutputReport)) {
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
return false;
}
DualshockOutputReport report{};
FillDualShockOutputReport(&report, true);
return WriteReport(handle, report);
}
bool ShutdownDualShock(HANDLE handle, int outReportSize) {
if (outReportSize != sizeof(DualshockOutputReport)) {
WARN_LOG(Log::UI, "DS4 unexpected report size %d", outReportSize);
return false;
}
DualshockOutputReport report{};
FillDualShockOutputReport(&report, false);
return WriteReport(handle, report);
}
bool ReadDualShockInput(HANDLE handle, HIDControllerState *state, int inReportSize) {
if (inReportSize > 1024) {
return false;
}
BYTE inputReport[1024]{};
DWORD bytesRead = 0;
if (!ReadFile(handle, inputReport, inReportSize, &bytesRead, nullptr)) {
u32 error = GetLastError();
return false;
}
DualShockInputReport report{};
static_assert(sizeof(report) < sizeof(inputReport));
if (bytesRead < 14) {
return false;
}
int offset = 1;
int reportId;
if (inputReport[0] == 0xA1) {
offset = 2;
reportId = inputReport[1];
} else {
offset = 1;
reportId = inputReport[0];
}
memcpy(&report, inputReport + offset, sizeof(report));
state->stickAxes[HID_STICK_LX] = report.lx - 128;
state->stickAxes[HID_STICK_LY] = report.ly - 128;
state->stickAxes[HID_STICK_RX] = report.rx - 128;
state->stickAxes[HID_STICK_RY] = report.ry - 128;
state->triggerAxes[HID_TRIGGER_L2] = report.l2_analog;
state->triggerAxes[HID_TRIGGER_R2] = report.r2_analog;
state->accValid = false;
u32 buttons{};
int frameCounter = report.buttons[2] >> 2;
report.buttons[2] &= 3;
memcpy(&buttons, &report.buttons[0], 3);
buttons &= ~0xF;
buttons |= DecodePSHatSwitch(report.buttons[0] & 0xF);
state->buttons = buttons;
return true;
}