#include <cstring>
#include "Windows/Hid/DualShock.h"
#include "Windows/Hid/DualSense.h"
#include "Windows/Hid/HidInputDevice.h"
#include "Windows/Hid/HidCommon.h"
struct DualSenseInputReportUSB {
u8 reportId;
u8 lx;
u8 ly;
u8 rx;
u8 ry;
u8 l2_analog;
u8 r2_analog;
u8 frameCounter;
u8 buttons[3];
u8 pad[5];
s16 gyroscope[3];
s16 accelerometer[3];
};
#pragma pack(push, 1)
struct DualSenseInputReportBT {
u8 reportId;
u8 seq_tag;
u8 lx;
u8 ly;
u8 rx;
u8 ry;
u8 l2_analog;
u8 r2_analog;
u8 frameCounter;
u8 unknown_status[6];
u8 buttons[3];
u8 device_extra;
u8 pad[3];
s16 gyroscope[3];
s16 accelerometer[3];
};
#pragma pack(pop)
#pragma pack(push,1)
struct DualSenseOutputReport {
u8 reportId;
u8 flags1;
u8 flags2;
u8 rumbleRight;
u8 rumbleLeft;
u8 pad[2];
u8 muteLED;
u8 micMute;
u8 other[32];
u8 enableLightbar;
u8 fade;
u8 brightness;
u8 playerLights;
u8 lightbarRed;
u8 lightbarGreen;
u8 lightbarBlue;
};
static_assert(sizeof(DualSenseOutputReport) == 48);
static void FillDualSenseOutputReport(DualSenseOutputReport *report, bool lightsOn) {
report->reportId = 2;
report->flags1 = 0x0C;
report->flags2 = 0x15;
report->muteLED = 1;
report->playerLights = lightsOn ? 1 : 0;
report->enableLightbar = 1;
report->brightness = lightsOn ? 0 : 2;
report->lightbarRed = lightsOn ? LED_R : 0;
report->lightbarGreen = lightsOn ? LED_G : 0;
report->lightbarBlue = lightsOn ? LED_B : 0;
}
static bool SendReport(HANDLE handle, const DualSenseOutputReport &report, int outReportSize) {
if (outReportSize == sizeof(DualSenseOutputReport)) {
return WriteReport(handle, report);
} else if (outReportSize >= 547) {
_dbg_assert_(outReportSize == 547);
std::vector<uint8_t> buffer;
buffer.resize(78);
buffer[0] = 0x31;
buffer[1] = 0x02;
memcpy(&buffer[2], (uint8_t*)&report + 1, sizeof(DualSenseOutputReport) - 1);
buffer[3] = 0x15;
uint32_t crc = ComputeDualSenseBTCRC(buffer.data(), 74);
memcpy(buffer.data() + 74, &crc, 4);
return WriteReport(handle, buffer.data(), buffer.size());
} else {
ERROR_LOG(Log::System, "SendReport: Unexpected outReportSize: %d", outReportSize);
return false;
}
}
bool InitializeDualSense(HANDLE handle, int outReportSize) {
DualSenseOutputReport report{};
FillDualSenseOutputReport(&report, true);
return SendReport(handle, report, outReportSize);
}
bool ShutdownDualsense(HANDLE handle, int outReportSize) {
if (outReportSize != sizeof(DualSenseOutputReport)) {
return false;
}
DualSenseOutputReport report{};
FillDualSenseOutputReport(&report, false);
return SendReport(handle, report, outReportSize);
}
template<class T>
static void ReadReport(HIDControllerState* state, u32 *buttons, const T& 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;
const float accelScale = (1.0f / 8192.0f) * 9.81f;
state->accValid = true;
state->accelerometer[0] = -report.accelerometer[2] * accelScale;
state->accelerometer[1] = -report.accelerometer[0] * accelScale;
state->accelerometer[2] = report.accelerometer[1] * accelScale;
*buttons = 0;
memcpy(buttons, &report.buttons[0], 3);
}
bool ReadDualSenseInput(HANDLE handle, HIDControllerState *state, int inReportSize) {
if (inReportSize > 1024) {
return false;
}
BYTE inputReport[1024]{};
DWORD bytesRead = 0;
if (!ReadFile(handle, inputReport, inReportSize, &bytesRead, nullptr)) {
const u32 error = GetLastError();
return false;
}
if (bytesRead < 14) {
return false;
}
u32 buttons{};
if (inputReport[0] == 0x1 && inReportSize == 64) {
DualSenseInputReportUSB report;
memcpy(&report, inputReport, sizeof(report));
ReadReport(state, &buttons, report);
} else if (inputReport[0] == 0x31 && inReportSize >= 547) {
DualSenseInputReportBT report;
memcpy(&report, inputReport, sizeof(report));
ReadReport(state, &buttons, report);
} else if (inputReport[0] == 0x31 && inReportSize == 78) {
DualSenseInputReportUSB report;
memcpy(((uint8_t *)&report) + 1, inputReport + 2, sizeof(report) - 1);
ReadReport(state, &buttons, report);
} else if (inputReport[0] == 0x1 && inReportSize == 78) {
buttons = 0;
} else {
WARN_LOG(Log::System, "Unexpected: %02x type, %d size", inputReport[0], (int)inReportSize);
return true;
}
const u32 hat = buttons & 0xF;
buttons &= ~0xF;
buttons |= DecodePSHatSwitch(hat);
state->buttons = buttons;
return true;
}