Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
f6edcc2edThis file is pinned to the selected release. Download the original to open it in your design tool.
#include "utils/InputReport.h"
#include <iomanip>
#include <sstream>
namespace Doncon::Utils {
namespace {
uint8_t getHidHat(const InputState::Controller::DPad dpad) {
if (dpad.up && dpad.right) {
return 0x01;
}
if (dpad.down && dpad.right) {
return 0x03;
}
if (dpad.down && dpad.left) {
return 0x05;
}
if (dpad.up && dpad.left) {
return 0x07;
}
if (dpad.up) {
return 0x00;
}
if (dpad.right) {
return 0x02;
}
if (dpad.down) {
return 0x04;
}
if (dpad.left) {
return 0x06;
}
return 0x08;
}
} // namespace
usb_report_t InputReport::getSwitchReport(const InputState &state) {
const auto &controller = state.controller;
const auto &drum = state.drum;
m_switch_report.buttons = 0 //
| (controller.buttons.west ? (1 << 0) : 0) // Y
| (controller.buttons.south ? (1 << 1) : 0) // B
| (controller.buttons.east ? (1 << 2) : 0) // A
| (controller.buttons.north ? (1 << 3) : 0) // X
| (controller.buttons.l ? (1 << 4) : 0) // L
| (controller.buttons.r ? (1 << 5) : 0) // R
| (drum.ka_left.triggered ? (1 << 6) : 0) // ZL
| (drum.ka_right.triggered ? (1 << 7) : 0) // ZR
| (controller.buttons.select ? (1 << 8) : 0) // -
| (controller.buttons.start ? (1 << 9) : 0) // +
| (drum.don_left.triggered ? (1 << 10) : 0) // LS
| (drum.don_right.triggered ? (1 << 11) : 0) // RS
| (controller.buttons.home ? (1 << 12) : 0) // Home
| (controller.buttons.share ? (1 << 13) : 0); // Capture
m_switch_report.hat = getHidHat(controller.dpad);
return {.data = reinterpret_cast<uint8_t *>(&m_switch_report), .size = sizeof(hid_switch_report_t)};
}
usb_report_t InputReport::getPS3Report(const InputState &state) {
const auto &controller = state.controller;
const auto &drum = state.drum;
m_ps3_report.buttons1 = 0 //
| (controller.buttons.select ? (1 << 0) : 0) // Select
| (drum.don_left.triggered ? (1 << 1) : 0) // L3
| (drum.don_right.triggered ? (1 << 2) : 0) // R3
| (controller.buttons.start ? (1 << 3) : 0) // Start
| (controller.dpad.up ? (1 << 4) : 0) // Up
| (controller.dpad.right ? (1 << 5) : 0) // Right
| (controller.dpad.down ? (1 << 6) : 0) // Down
| (controller.dpad.left ? (1 << 7) : 0); // Left
m_ps3_report.buttons2 = 0 | (drum.ka_left.triggered ? (1 << 0) : 0) // L2
| (drum.ka_right.triggered ? (1 << 1) : 0) // R2
| (controller.buttons.l ? (1 << 2) : 0) // L1
| (controller.buttons.r ? (1 << 3) : 0) // R1
| (controller.buttons.north ? (1 << 4) : 0) // Triangle
| (controller.buttons.east ? (1 << 5) : 0) // Circle
| (controller.buttons.south ? (1 << 6) : 0) // Cross
| (controller.buttons.west ? (1 << 7) : 0); // Square
m_ps3_report.buttons3 = 0 | (controller.buttons.home ? (1 << 0) : 0); // Home
m_ps3_report.lt = (drum.ka_left.triggered ? 0xff : 0);
m_ps3_report.rt = (drum.ka_right.triggered ? 0xff : 0);
return {.data = reinterpret_cast<uint8_t *>(&m_ps3_report), .size = sizeof(hid_ps3_report_t)};
}
usb_report_t InputReport::getPS4Report(const InputState &state) {
const auto &controller = state.controller;
const auto &drum = state.drum;
m_ps4_report.buttons1 = getHidHat(controller.dpad) //
| (controller.buttons.west ? (1 << 4) : 0) // Square
| (controller.buttons.south ? (1 << 5) : 0) // Cross
| (controller.buttons.east ? (1 << 6) : 0) // Circle
| (controller.buttons.north ? (1 << 7) : 0); // Triangle
m_ps4_report.buttons2 = 0 //
| (controller.buttons.l ? (1 << 0) : 0) // L1
| (controller.buttons.r ? (1 << 1) : 0) // R1
| (drum.ka_left.triggered ? (1 << 2) : 0) // L2
| (drum.ka_right.triggered ? (1 << 3) : 0) // R2
| (controller.buttons.share ? (1 << 4) : 0) // Share
| (controller.buttons.start ? (1 << 5) : 0) // Option
| (drum.don_left.triggered ? (1 << 6) : 0) // L3
| (drum.don_right.triggered ? (1 << 7) : 0); // R3
m_ps4_report.buttons3 = (m_ps4_report_counter << 2) //
| (controller.buttons.home ? (1 << 0) : 0) // PS
| (controller.buttons.select ? (1 << 1) : 0); // T-Pad
m_ps4_report.lt = (drum.ka_left.triggered ? 0xFF : 0);
m_ps4_report.rt = (drum.ka_right.triggered ? 0xFF : 0);
// This method actually gets called more often than the report is sent,
// so counters are not consecutive ... let's see if this turns out to
// be a problem.
if (++m_ps4_report_counter > (UINT8_MAX >> 2)) {
m_ps4_report_counter = 0;
}
return {.data = reinterpret_cast<uint8_t *>(&m_ps4_report), .size = sizeof(hid_ps4_report_t)};
}
usb_report_t InputReport::getKeyboardReport(const InputState &state, InputReport::Player player) {
const auto &controller = state.controller;
const auto &drum = state.drum;
m_keyboard_report = {};
auto set_key = [&](const bool input, const uint8_t keycode) {
if (input) {
m_keyboard_report.keycodes[keycode / 8] |= 1 << (keycode % 8);
}
};
switch (player) {
case Player::One: {
set_key(drum.ka_left.triggered, HID_KEY_D);
set_key(drum.don_left.triggered, HID_KEY_F);
set_key(drum.don_right.triggered, HID_KEY_J);
set_key(drum.ka_right.triggered, HID_KEY_K);
} break;
case Player::Two: {
set_key(drum.ka_left.triggered, HID_KEY_C);
set_key(drum.don_left.triggered, HID_KEY_B);
set_key(drum.don_right.triggered, HID_KEY_N);
set_key(drum.ka_right.triggered, HID_KEY_COMMA);
} break;
}
set_key(controller.dpad.up, HID_KEY_ARROW_UP);
set_key(controller.dpad.down, HID_KEY_ARROW_DOWN);
set_key(controller.dpad.left, HID_KEY_ARROW_LEFT);
set_key(controller.dpad.right, HID_KEY_ARROW_RIGHT);
set_key(controller.buttons.north, HID_KEY_L);
set_key(controller.buttons.east, HID_KEY_BACKSPACE);
set_key(controller.buttons.south, HID_KEY_ENTER);
set_key(controller.buttons.west, HID_KEY_P);
set_key(controller.buttons.l, HID_KEY_Q);
set_key(controller.buttons.r, HID_KEY_E);
set_key(controller.buttons.start, HID_KEY_ESCAPE);
set_key(controller.buttons.select, HID_KEY_TAB);
// set_key(controller.buttons.home, );
// set_key(controller.buttons.share, );
return {.data = reinterpret_cast<uint8_t *>(&m_keyboard_report), .size = sizeof(hid_nkro_keyboard_report_t)};
}
usb_report_t InputReport::getXinputBaseReport(const InputState &state) {
const auto &controller = state.controller;
m_xinput_report.buttons1 = 0 //
| (controller.dpad.up ? (1 << 0) : 0) // Dpad Up
| (controller.dpad.down ? (1 << 1) : 0) // Dpad Down
| (controller.dpad.left ? (1 << 2) : 0) // Dpad Left
| (controller.dpad.right ? (1 << 3) : 0) // Dpad Right
| (controller.buttons.start ? (1 << 4) : 0) // Start
| (controller.buttons.select ? (1 << 5) : 0) // Select
| (false ? (1 << 6) : 0) // L3
| (false ? (1 << 7) : 0); // R3
m_xinput_report.buttons2 = 0 //
| (controller.buttons.l ? (1 << 0) : 0) // L1
| (controller.buttons.r ? (1 << 1) : 0) // R1
| (controller.buttons.home ? (1 << 2) : 0) // Guide
| (controller.buttons.south ? (1 << 4) : 0) // A
| (controller.buttons.east ? (1 << 5) : 0) // B
| (controller.buttons.west ? (1 << 6) : 0) // X
| (controller.buttons.north ? (1 << 7) : 0); // Y
return {.data = reinterpret_cast<uint8_t *>(&m_xinput_report), .size = sizeof(xinput_report_t)};
}
usb_report_t InputReport::getXinputDigitalReport(const InputState &state) {
const auto &drum = state.drum;
getXinputBaseReport(state);
m_xinput_report.buttons1 |= (drum.don_left.triggered ? (1 << 1) : 0) // Dpad Down
| (drum.ka_left.triggered ? (1 << 2) : 0); // Dpad Left
m_xinput_report.buttons2 |= (drum.don_right.triggered ? (1 << 4) : 0) // A
| (drum.ka_right.triggered ? (1 << 5) : 0); // B
return {.data = reinterpret_cast<uint8_t *>(&m_xinput_report), .size = sizeof(xinput_report_t)};
}
usb_report_t InputReport::getXinputAnalogReport(const InputState &state, InputReport::Player player) {
const auto &drum = state.drum;
getXinputBaseReport(state);
int16_t x = 0;
int16_t y = 0;
auto map_to_axis = [](uint16_t raw) { return static_cast<int16_t>(raw >> 1); };
if (drum.ka_left.analog > drum.don_left.analog) {
x = static_cast<int16_t>(-map_to_axis(drum.ka_left.analog));
} else {
x = map_to_axis(drum.don_left.analog);
}
if (drum.ka_right.analog > drum.don_right.analog) {
y = map_to_axis(drum.ka_right.analog);
} else {
y = static_cast<int16_t>(-map_to_axis(drum.don_right.analog));
}
switch (player) {
case Player::One:
m_xinput_report.lx = x;
m_xinput_report.ly = y;
break;
case Player::Two:
m_xinput_report.rx = x;
m_xinput_report.ry = y;
break;
}
return {.data = reinterpret_cast<uint8_t *>(&m_xinput_report), .size = sizeof(xinput_report_t)};
}
usb_report_t InputReport::getMidiReport(const InputState &state) {
const auto &drum = state.drum;
m_midi_report.status.acoustic_bass_drum = drum.don_left.triggered;
m_midi_report.status.electric_bass_drum = drum.don_right.triggered;
m_midi_report.status.drumsticks = drum.ka_left.triggered;
m_midi_report.status.side_stick = drum.ka_right.triggered;
auto convert_range = [](uint16_t in) {
const uint16_t out = in / 256;
return static_cast<uint8_t>(out > 127 ? 127 : out);
};
m_midi_report.velocity.acoustic_bass_drum = convert_range(drum.don_left.analog);
m_midi_report.velocity.electric_bass_drum = convert_range(drum.don_right.analog);
m_midi_report.velocity.drumsticks = convert_range(drum.ka_left.analog);
m_midi_report.velocity.side_stick = convert_range(drum.ka_right.analog);
return {.data = reinterpret_cast<uint8_t *>(&m_midi_report), .size = sizeof(midi_report_t)};
}
usb_report_t InputReport::getDebugReport(const InputState &state) {
const auto &drum = state.drum;
std::stringstream out;
auto bar = [](uint16_t val) { return std::string(val / 511, '#'); };
if (drum.don_left.triggered || drum.ka_left.triggered || drum.don_right.triggered || drum.ka_right.triggered) {
out << "(" << (drum.ka_left.triggered ? "*" : " ") << "( " //
<< std::setw(4) << drum.ka_left.raw << "[" << std::setw(8) << bar(drum.ka_left.raw) << "]" //
<< "(" << (drum.don_left.triggered ? "*" : " ") << "| " //
<< std::setw(4) << drum.don_left.raw << "[" << std::setw(8) << bar(drum.don_left.raw) << "]" //
<< "|" << (drum.don_right.triggered ? "*" : " ") << ") " //
<< std::setw(4) << drum.don_right.raw << "[" << std::setw(8) << bar(drum.don_right.raw) << "]" //
<< ")" << (drum.ka_right.triggered ? "*" : " ") << ") " //
<< std::setw(4) << drum.ka_right.raw << "[" << std::setw(8) << bar(drum.ka_right.raw) << "]" //
<< "\n";
}
m_debug_report = out.str();
return {.data = reinterpret_cast<uint8_t *>(m_debug_report.data()),
.size = static_cast<uint16_t>(m_debug_report.size() + 1)};
}
usb_report_t InputReport::getReport(const InputState &state, usb_mode_t mode) {
switch (mode) {
case USB_MODE_SWITCH_TATACON:
case USB_MODE_SWITCH_HORIPAD:
return getSwitchReport(state);
case USB_MODE_DUALSHOCK3:
return getPS3Report(state);
case USB_MODE_PS4_TATACON:
case USB_MODE_DUALSHOCK4:
return getPS4Report(state);
case USB_MODE_KEYBOARD_P1:
return getKeyboardReport(state, Player::One);
case USB_MODE_KEYBOARD_P2:
return getKeyboardReport(state, Player::Two);
case USB_MODE_XBOX360:
return getXinputDigitalReport(state);
case USB_MODE_XBOX360_ANALOG_P1:
return getXinputAnalogReport(state, Player::One);
case USB_MODE_XBOX360_ANALOG_P2:
return getXinputAnalogReport(state, Player::Two);
case USB_MODE_MIDI:
return getMidiReport(state);
case USB_MODE_DEBUG:
return getDebugReport(state);
}
return getDebugReport(state);
}
} // namespace Doncon::Utils