input: try to add tony hawk RIDE Skateboard

This commit is contained in:
Megamouse 2023-08-26 15:47:52 +02:00
parent 6dcdf6ec76
commit 0bb5974ab6
24 changed files with 791 additions and 30 deletions

View File

@ -221,16 +221,23 @@ void fmt_class_string<fmt::buf_to_hexstring>::format(std::string& out, u64 arg)
const std::vector<u8> buf(_arg.buf, _arg.buf + _arg.len);
out.reserve(out.size() + (buf.size() * 3));
static constexpr char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
const bool use_linebreak = _arg.line_length > 0;
for (usz index = 0; index < buf.size(); index++)
{
if (index > 0)
{
if (use_linebreak && (index % _arg.line_length) == 0)
out += '\n';
else
out += ' ';
}
if (_arg.with_prefix)
out += "0x";
out += hex[buf[index] >> 4];
out += hex[buf[index] & 15];
if (((index + 1) % 16) == 0)
out += '\n';
else
out += ' ';
}
}

View File

@ -181,11 +181,13 @@ namespace fmt
struct buf_to_hexstring
{
buf_to_hexstring(const u8* buf, usz len)
: buf(buf), len(len) {}
buf_to_hexstring(const u8* buf, usz len, usz line_length = 16, bool with_prefix = false)
: buf(buf), len(len), line_length(line_length), with_prefix(with_prefix) {}
const u8* buf;
usz len;
usz line_length;
bool with_prefix;
};
struct string_hash

View File

@ -75,6 +75,7 @@ target_sources(rpcs3
Input/mm_joystick_handler.cpp
Input/pad_thread.cpp
Input/sdl_pad_handler.cpp
Input/skateboard_pad_handler.cpp
Input/xinput_pad_handler.cpp
)

View File

@ -137,6 +137,9 @@ void cellPad_NotifyStateChange(usz index, u32 /*state*/)
case CELL_PAD_PCLASS_TYPE_NAVIGATION:
product = input::get_product_info(input::product_type::ps_move_navigation);
break;
case CELL_PAD_PCLASS_TYPE_SKATEBOARD:
product = input::get_product_info(input::product_type::ride_skateboard);
break;
case CELL_PAD_PCLASS_TYPE_STANDARD:
default:
product = input::get_product_info(input::product_type::playstation_3_controller);
@ -301,8 +304,15 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
const u16 d1Initial = pad->m_digital_1;
const u16 d2Initial = pad->m_digital_2;
const auto set_value = [&btnChanged](u16& value, u16 new_value)
// Check if this pad is configured as a skateboard which ignores sticks and pressure button values.
// Curiously it maps infrared on the press value of the face buttons for some reason.
const bool use_piggyback = pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD;
const auto set_value = [&btnChanged, use_piggyback](u16& value, u16 new_value, bool is_piggyback = false)
{
if (use_piggyback && !is_piggyback)
return;
if (value != new_value)
{
btnChanged = true;
@ -315,7 +325,9 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
// here we check btns, and set pad accordingly,
// if something changed, set btnChanged
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
switch (button.m_offset)
{
case CELL_PAD_BTN_OFFSET_DIGITAL1:
{
if (button.m_pressed)
pad->m_digital_1 |= button.m_outKeyCode;
@ -335,8 +347,9 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
case CELL_PAD_CTRL_SELECT:
default: break;
}
break;
}
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
case CELL_PAD_BTN_OFFSET_DIGITAL2:
{
if (button.m_pressed)
pad->m_digital_2 |= button.m_outKeyCode;
@ -355,6 +368,30 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
case CELL_PAD_CTRL_L2: set_value(pad->m_press_L2, button.m_value); break;
default: break;
}
break;
}
case CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK:
{
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_PRESS_RIGHT: set_value(pad->m_press_right, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_LEFT: set_value(pad->m_press_left, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_UP: set_value(pad->m_press_up, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_DOWN: set_value(pad->m_press_down, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_TRIANGLE: set_value(pad->m_press_triangle, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_CIRCLE: set_value(pad->m_press_circle, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_CROSS: set_value(pad->m_press_cross, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_SQUARE: set_value(pad->m_press_square, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_L1: set_value(pad->m_press_L1, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_R1: set_value(pad->m_press_R1, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_L2: set_value(pad->m_press_L2, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_R2: set_value(pad->m_press_R2, button.m_value, true); break;
default: break;
}
break;
}
default:
break;
}
}
@ -376,10 +413,10 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
{
switch (sensor.m_offset)
{
case CELL_PAD_BTN_OFFSET_SENSOR_X: set_value(pad->m_sensor_x, sensor.m_value); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Y: set_value(pad->m_sensor_y, sensor.m_value); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Z: set_value(pad->m_sensor_z, sensor.m_value); break;
case CELL_PAD_BTN_OFFSET_SENSOR_G: set_value(pad->m_sensor_g, sensor.m_value); break;
case CELL_PAD_BTN_OFFSET_SENSOR_X: set_value(pad->m_sensor_x, sensor.m_value, true); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Y: set_value(pad->m_sensor_y, sensor.m_value, true); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Z: set_value(pad->m_sensor_z, sensor.m_value, true); break;
case CELL_PAD_BTN_OFFSET_SENSOR_G: set_value(pad->m_sensor_g, sensor.m_value, true); break;
default: break;
}
}
@ -446,8 +483,8 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
data->button[CELL_PAD_BTN_OFFSET_PRESS_CROSS] = pad->m_press_cross;
data->button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE] = pad->m_press_square;
data->button[CELL_PAD_BTN_OFFSET_PRESS_L1] = pad->m_press_L1;
data->button[CELL_PAD_BTN_OFFSET_PRESS_L2] = pad->m_press_L2;
data->button[CELL_PAD_BTN_OFFSET_PRESS_R1] = pad->m_press_R1;
data->button[CELL_PAD_BTN_OFFSET_PRESS_L2] = pad->m_press_L2;
data->button[CELL_PAD_BTN_OFFSET_PRESS_R2] = pad->m_press_R2;
}
else
@ -494,6 +531,7 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
default:
case CELL_PAD_PCLASS_TYPE_STANDARD:
case CELL_PAD_PCLASS_TYPE_NAVIGATION:
case CELL_PAD_PCLASS_TYPE_SKATEBOARD:
{
break;
}

View File

@ -304,6 +304,9 @@ usb_handler_thread::usb_handler_thread()
// EA Active 2 dongle for connecting wristbands & legband
check_device(0x21A4, 0xAC27, 0xAC27, "EA Active 2 Dongle");
// Tony Hawk RIDE Skateboard
check_device(0x12BA, 0x0400, 0x0400, "Tony Hawk RIDE Skateboard Controller");
}
libusb_free_device_list(list, 1);

View File

@ -454,19 +454,21 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr<Pad> pad, u8 player_id)
std::array<std::set<u32>, button::button_count> mapping = get_mapped_key_codes(pad_device, config);
u32 pclass_profile = 0x0;
u32 capabilities = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE;
for (const input::product_info& product : input::get_products_by_class(config->device_class_type))
{
if (product.vendor_id == config->vendor_id && product.product_id == config->product_id)
{
pclass_profile = product.pclass_profile;
capabilities = product.capabilites;
}
}
pad->Init
(
CELL_PAD_STATUS_DISCONNECTED,
CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE,
capabilities,
CELL_PAD_DEV_TYPE_STANDARD,
config->device_class_type,
pclass_profile,
@ -496,6 +498,16 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr<Pad> pad, u8 player_id)
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::select], CELL_PAD_CTRL_SELECT);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::ps], CELL_PAD_CTRL_PS);
if (pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD)
{
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, mapping[button::skateboard_ir_nose], CELL_PAD_CTRL_PRESS_TRIANGLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, mapping[button::skateboard_ir_tail], CELL_PAD_CTRL_PRESS_CIRCLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, mapping[button::skateboard_ir_left], CELL_PAD_CTRL_PRESS_CROSS);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, mapping[button::skateboard_ir_right], CELL_PAD_CTRL_PRESS_SQUARE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, mapping[button::skateboard_tilt_left], CELL_PAD_CTRL_PRESS_L1);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, mapping[button::skateboard_tilt_right], CELL_PAD_CTRL_PRESS_R1);
}
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, mapping[button::ls_left], mapping[button::ls_right]);
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, mapping[button::ls_down], mapping[button::ls_up]);
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, mapping[button::rs_left], mapping[button::rs_right]);
@ -557,6 +569,13 @@ std::array<std::set<u32>, PadHandlerBase::button::button_count> PadHandlerBase::
mapping[button::rs_up] = narrow_set(device->axis_code_right[3]);
mapping[button::ps] = FindKeyCodes<u32, u32>(button_list, cfg->ps);
mapping[button::skateboard_ir_nose] = FindKeyCodes<u32, u32>(button_list, cfg->ir_nose);
mapping[button::skateboard_ir_tail] = FindKeyCodes<u32, u32>(button_list, cfg->ir_tail);
mapping[button::skateboard_ir_left] = FindKeyCodes<u32, u32>(button_list, cfg->ir_left);
mapping[button::skateboard_ir_right] = FindKeyCodes<u32, u32>(button_list, cfg->ir_right);
mapping[button::skateboard_tilt_left] = FindKeyCodes<u32, u32>(button_list, cfg->tilt_left);
mapping[button::skateboard_tilt_right] = FindKeyCodes<u32, u32>(button_list, cfg->tilt_right);
mapping[button::pressure_intensity_button] = FindKeyCodes<u32, u32>(button_list, cfg->pressure_intensity_button);
return mapping;

View File

@ -98,6 +98,13 @@ protected:
rs_down,
rs_up,
skateboard_ir_nose,
skateboard_ir_tail,
skateboard_ir_left,
skateboard_ir_right,
skateboard_tilt_left,
skateboard_tilt_right,
pressure_intensity_button,
button_count

View File

@ -54,6 +54,13 @@ struct cfg_pad final : cfg::node
cfg::string l2{ this, "L2", "" };
cfg::string l3{ this, "L3", "" };
cfg::string ir_nose{ this, "IR Nose", "" };
cfg::string ir_tail{ this, "IR Tail", "" };
cfg::string ir_left{ this, "IR Left", "" };
cfg::string ir_right{ this, "IR Right", "" };
cfg::string tilt_left{ this, "Tilt Left", "" };
cfg::string tilt_right{ this, "Tilt Right", "" };
cfg_sensor motion_sensor_x{ this, "Motion Sensor X" };
cfg_sensor motion_sensor_y{ this, "Motion Sensor Y" };
cfg_sensor motion_sensor_z{ this, "Motion Sensor Z" };
@ -97,7 +104,7 @@ struct cfg_pad final : cfg::node
cfg::uint<0, 100> analog_lerp_factor{ this, "Analog Button Lerp Factor", 100 };
cfg::uint<0, 100> trigger_lerp_factor{ this, "Trigger Lerp Factor", 100 };
cfg::uint<CELL_PAD_PCLASS_TYPE_STANDARD, CELL_PAD_PCLASS_TYPE_NAVIGATION> device_class_type{ this, "Device Class Type", 0 };
cfg::uint<CELL_PAD_PCLASS_TYPE_STANDARD, CELL_PAD_PCLASS_TYPE_SKATEBOARD> device_class_type{ this, "Device Class Type", 0 };
cfg::uint<0, 65535> vendor_id{ this, "Vendor ID", 0 };
cfg::uint<0, 65535> product_id{ this, "Product ID", 0 };
};

View File

@ -13,6 +13,7 @@ void fmt_class_string<pad_handler>::format(std::string& out, u64 arg)
case pad_handler::ds3: return "DualShock 3";
case pad_handler::ds4: return "DualShock 4";
case pad_handler::dualsense: return "DualSense";
case pad_handler::skateboard: return "Skateboard";
#ifdef _WIN32
case pad_handler::xinput: return "XInput";
case pad_handler::mm: return "MMJoystick";

View File

@ -9,6 +9,7 @@ enum class pad_handler
ds3,
ds4,
dualsense,
skateboard,
#ifdef _WIN32
xinput,
mm,

View File

@ -133,6 +133,7 @@ enum
CELL_PAD_PCLASS_TYPE_DJ = 0x03,
CELL_PAD_PCLASS_TYPE_DANCEMAT = 0x04,
CELL_PAD_PCLASS_TYPE_NAVIGATION = 0x05,
CELL_PAD_PCLASS_TYPE_SKATEBOARD = 0x8001,
};
// Profile of a Standard Type Controller
@ -234,6 +235,9 @@ enum ButtonDataOffset
CELL_PAD_BTN_OFFSET_SENSOR_Y = 21,
CELL_PAD_BTN_OFFSET_SENSOR_Z = 22,
CELL_PAD_BTN_OFFSET_SENSOR_G = 23,
// Fake helpers
CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK,
};
enum CellPadPeriphGuitarBtnDataOffset
@ -318,6 +322,23 @@ static constexpr u16 DEFAULT_MOTION_Y = 399;
static constexpr u16 DEFAULT_MOTION_Z = 512;
static constexpr u16 DEFAULT_MOTION_G = 512;
// Fake helper enum
enum PressurePiggybackFlags : u32
{
CELL_PAD_CTRL_PRESS_RIGHT = CELL_PAD_BTN_OFFSET_PRESS_RIGHT,
CELL_PAD_CTRL_PRESS_LEFT = CELL_PAD_BTN_OFFSET_PRESS_LEFT,
CELL_PAD_CTRL_PRESS_UP = CELL_PAD_BTN_OFFSET_PRESS_UP,
CELL_PAD_CTRL_PRESS_DOWN = CELL_PAD_BTN_OFFSET_PRESS_DOWN,
CELL_PAD_CTRL_PRESS_TRIANGLE = CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE,
CELL_PAD_CTRL_PRESS_CIRCLE = CELL_PAD_BTN_OFFSET_PRESS_CIRCLE,
CELL_PAD_CTRL_PRESS_CROSS = CELL_PAD_BTN_OFFSET_PRESS_CROSS,
CELL_PAD_CTRL_PRESS_SQUARE = CELL_PAD_BTN_OFFSET_PRESS_SQUARE,
CELL_PAD_CTRL_PRESS_L1 = CELL_PAD_BTN_OFFSET_PRESS_L1,
CELL_PAD_CTRL_PRESS_R1 = CELL_PAD_BTN_OFFSET_PRESS_R1,
CELL_PAD_CTRL_PRESS_L2 = CELL_PAD_BTN_OFFSET_PRESS_L2,
CELL_PAD_CTRL_PRESS_R2 = CELL_PAD_BTN_OFFSET_PRESS_R2,
};
constexpr u32 special_button_offset = 666; // Must not conflict with other CELL offsets like ButtonDataOffset
enum special_button_value

View File

@ -345,7 +345,7 @@ dualsense_pad_handler::DataStatus dualsense_pad_handler::get_data(DualSenseDevic
std::array<u8, 128> buf{};
const int res = hid_read(device->hidDevice, buf.data(), 128);
const int res = hid_read(device->hidDevice, buf.data(), buf.size());
if (res == -1)
{

View File

@ -1359,6 +1359,16 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr<Pad> pad, u8 player
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_buttons(cfg->left), CELL_PAD_CTRL_LEFT);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_buttons(cfg->right), CELL_PAD_CTRL_RIGHT);
if (pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD)
{
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_buttons(cfg->ir_nose), CELL_PAD_CTRL_PRESS_TRIANGLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_buttons(cfg->ir_tail), CELL_PAD_CTRL_PRESS_CIRCLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_buttons(cfg->ir_left), CELL_PAD_CTRL_PRESS_CROSS);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_buttons(cfg->ir_right), CELL_PAD_CTRL_PRESS_SQUARE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_buttons(cfg->tilt_left), CELL_PAD_CTRL_PRESS_L1);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_buttons(cfg->tilt_right), CELL_PAD_CTRL_PRESS_R1);
}
m_dev->axis_left[0] = find_buttons(cfg->ls_right);
m_dev->axis_left[1] = find_buttons(cfg->ls_left);
m_dev->axis_left[2] = find_buttons(cfg->ls_up);

View File

@ -2,6 +2,7 @@
#include "ds3_pad_handler.h"
#include "ds4_pad_handler.h"
#include "dualsense_pad_handler.h"
#include "skateboard_pad_handler.h"
#include "util/logs.hpp"
#include "Utilities/Timer.h"
#include "Emu/System.h"
@ -267,3 +268,4 @@ u32 hid_pad_handler<Device>::get_battery_color(u8 battery_level, u32 brightness)
template class hid_pad_handler<ds3_device>;
template class hid_pad_handler<DS4Device>;
template class hid_pad_handler<DualSenseDevice>;
template class hid_pad_handler<skateboard_device>;

View File

@ -963,6 +963,16 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad, u8 player_i
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->r2), CELL_PAD_CTRL_R2);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->l2), CELL_PAD_CTRL_L2);
if (pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD)
{
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_nose), CELL_PAD_CTRL_PRESS_TRIANGLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_tail), CELL_PAD_CTRL_PRESS_CIRCLE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_left), CELL_PAD_CTRL_PRESS_CROSS);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_right), CELL_PAD_CTRL_PRESS_SQUARE);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_left), CELL_PAD_CTRL_PRESS_L1);
pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_right), CELL_PAD_CTRL_PRESS_R1);
}
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, find_keys(cfg->ls_left), find_keys(cfg->ls_right));
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, find_keys(cfg->ls_up), find_keys(cfg->ls_down));
pad->m_sticks.emplace_back(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, find_keys(cfg->rs_left), find_keys(cfg->rs_right));

View File

@ -174,6 +174,13 @@ std::array<std::set<u32>, PadHandlerBase::button::button_count> mm_joystick_hand
mapping[button::rs_down] = narrow_set(joy_device->axis_code_right[2]);
mapping[button::rs_up] = narrow_set(joy_device->axis_code_right[3]);
mapping[button::skateboard_ir_nose] = find_keys<u32>(cfg->ir_nose);
mapping[button::skateboard_ir_tail] = find_keys<u32>(cfg->ir_tail);
mapping[button::skateboard_ir_left] = find_keys<u32>(cfg->ir_left);
mapping[button::skateboard_ir_right] = find_keys<u32>(cfg->ir_right);
mapping[button::skateboard_tilt_left] = find_keys<u32>(cfg->tilt_left);
mapping[button::skateboard_tilt_right] = find_keys<u32>(cfg->tilt_right);
mapping[button::pressure_intensity_button] = find_keys<u32>(cfg->pressure_intensity_button);
return mapping;

View File

@ -4,6 +4,7 @@
#include "ds3_pad_handler.h"
#include "ds4_pad_handler.h"
#include "dualsense_pad_handler.h"
#include "skateboard_pad_handler.h"
#ifdef _WIN32
#include "xinput_pad_handler.h"
#include "mm_joystick_handler.h"
@ -168,6 +169,9 @@ void pad_thread::Init()
case pad_handler::dualsense:
cur_pad_handler = std::make_shared<dualsense_pad_handler>();
break;
case pad_handler::skateboard:
cur_pad_handler = std::make_shared<skateboard_pad_handler>();
break;
#ifdef _WIN32
case pad_handler::xinput:
cur_pad_handler = std::make_shared<xinput_pad_handler>();
@ -558,7 +562,7 @@ void pad_thread::InitLddPad(u32 handle, const u32* port_status)
port_status ? *port_status : CELL_PAD_STATUS_CONNECTED | CELL_PAD_STATUS_ASSIGN_CHANGES | CELL_PAD_STATUS_CUSTOM_CONTROLLER,
CELL_PAD_CAPABILITY_PS3_CONFORMITY,
CELL_PAD_DEV_TYPE_LDD,
0, // CELL_PAD_PCLASS_TYPE_STANDARD
CELL_PAD_PCLASS_TYPE_STANDARD,
product.pclass_profile,
product.vendor_id,
product.product_id,
@ -609,6 +613,8 @@ std::shared_ptr<PadHandlerBase> pad_thread::GetHandler(pad_handler type)
return std::make_unique<ds4_pad_handler>();
case pad_handler::dualsense:
return std::make_unique<dualsense_pad_handler>();
case pad_handler::skateboard:
return std::make_unique<skateboard_pad_handler>();
#ifdef _WIN32
case pad_handler::xinput:
return std::make_unique<xinput_pad_handler>();

View File

@ -16,7 +16,8 @@ namespace input
harmonix_rockband_drum_kit,
harmonix_rockband_drum_kit_2,
rock_revolution_drum_kit,
ps_move_navigation
ps_move_navigation,
ride_skateboard
};
enum vendor_id
@ -38,6 +39,7 @@ namespace input
rock_revolution_drum_kit = 0x0300, // Rock Revolution Drum Controller
ps_move_navigation = 0x042F, // PlayStation Move navigation controller
dance_dance_revolution_mat = 0x1010, // Dance Dance Revolution Dance Mat Controller
ride_skateboard = 0x0400, // Tony Hawk RIDE Skateboard Controller
};
struct product_info
@ -46,6 +48,7 @@ namespace input
u16 vendor_id;
u16 product_id;
u32 pclass_profile; // See CELL_PAD_PCLASS_PROFILE flags
u32 capabilites; // See CELL_PAD_CAPABILITY flags
};
inline product_info get_product_info(product_type type)
@ -67,7 +70,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::konami_de,
.product_id = product_id::dance_dance_revolution_mat,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::dj_hero_turntable:
@ -88,7 +92,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::dj_hero_turntable,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::harmonix_rockband_drum_kit:
@ -106,7 +111,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::harmonix_rockband_drum_kit,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::harmonix_rockband_drum_kit_2:
@ -123,7 +129,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::harmonix_rockband_drum_kit_2,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::harmonix_rockband_guitar:
@ -148,7 +155,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::harmonix_rockband_guitar,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::red_octane_gh_drum_kit:
@ -164,7 +172,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::red_octane_gh_drum_kit,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::red_octane_gh_guitar:
@ -182,7 +191,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::red_octane_gh_guitar,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::rock_revolution_drum_kit:
@ -199,7 +209,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::rock_revolution_drum_kit,
.pclass_profile = profile
.pclass_profile = profile,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::ps_move_navigation:
@ -208,7 +219,18 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_corp,
.product_id = product_id::ps_move_navigation,
.pclass_profile = 0x0
.pclass_profile = 0x0,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::ride_skateboard:
{
return product_info{
.type = type,
.vendor_id = vendor_id::sony_cea,
.product_id = product_id::ride_skateboard,
.pclass_profile = 0x0,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
case product_type::playstation_3_controller:
@ -218,7 +240,8 @@ namespace input
.type = type,
.vendor_id = vendor_id::sony_corp,
.product_id = product_id::playstation_3_controller,
.pclass_profile = 0x0
.pclass_profile = 0x0,
.capabilites = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE
};
}
}
@ -275,6 +298,13 @@ namespace input
get_product_info(product_type::ps_move_navigation)
};
}
case CELL_PAD_PCLASS_TYPE_SKATEBOARD:
{
return
{
get_product_info(product_type::ride_skateboard)
};
}
}
}
};

View File

@ -0,0 +1,379 @@
#include "stdafx.h"
#include "skateboard_pad_handler.h"
#include "Emu/Io/pad_config.h"
LOG_CHANNEL(skateboard_log, "Skateboard");
namespace
{
constexpr id_pair SKATEBOARD_ID_0 = {0x12BA, 0x0400}; // Tony Hawk RIDE Skateboard
enum button_flags : u16
{
square = 0x0001,
cross = 0x0002,
circle = 0x0004,
triangle = 0x0008,
select = 0x0100,
start = 0x0200,
ps = 0x1000,
};
enum dpad_states : u8
{
up = 0x00,
up_right = 0x01,
left = 0x02,
down_right = 0x03,
down = 0x04,
down_left = 0x05,
right = 0x06,
up_left = 0x07,
none = 0x0F,
};
// Default data if dongle is connected but the skateboard is turned off:
static constexpr std::array<u8, sizeof(skateboard_input_report)> not_connected_state = { 0x00, 0x00, 0x0F, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02 };
static constexpr std::array<u8, sizeof(skateboard_input_report)> disconnected_state = { 0x00, 0x00, 0x0F, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
}
skateboard_pad_handler::skateboard_pad_handler()
: hid_pad_handler<skateboard_device>(pad_handler::skateboard, {SKATEBOARD_ID_0})
{
// Unique names for the config files and our pad settings dialog
button_list =
{
{ skateboard_key_codes::none, "" },
{ skateboard_key_codes::left, "Left" },
{ skateboard_key_codes::right, "Right" },
{ skateboard_key_codes::up, "Up" },
{ skateboard_key_codes::down, "Down" },
{ skateboard_key_codes::cross, "Cross" },
{ skateboard_key_codes::square, "Square" },
{ skateboard_key_codes::circle, "Circle" },
{ skateboard_key_codes::triangle, "Triangle" },
{ skateboard_key_codes::start, "Start" },
{ skateboard_key_codes::select, "Select" },
{ skateboard_key_codes::ps, "PS" },
{ skateboard_key_codes::ir_left, "IR Left" },
{ skateboard_key_codes::ir_right, "IR Right" },
{ skateboard_key_codes::ir_nose, "IR Nose" },
{ skateboard_key_codes::ir_tail, "IR Tail" },
{ skateboard_key_codes::tilt_left, "Tilt Left" },
{ skateboard_key_codes::tilt_right, "Tilt Right" }
};
init_configs();
// Define border values
thumb_max = 255;
trigger_min = 0;
trigger_max = 255;
// Set capabilities
b_has_config = true;
b_has_rumble = false;
b_has_motion = true;
b_has_deadzones = false;
b_has_led = false;
b_has_rgb = false;
b_has_player_led = false;
b_has_battery = false;
b_has_pressure_intensity_button = false;
m_name_string = "Skateboard #";
m_max_devices = CELL_PAD_MAX_PORT_NUM;
m_trigger_threshold = trigger_max / 2;
m_thumb_threshold = thumb_max / 2;
}
skateboard_pad_handler::~skateboard_pad_handler()
{
}
void skateboard_pad_handler::init_config(cfg_pad* cfg)
{
if (!cfg) return;
// Set default button mapping
cfg->ls_left.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ls_down.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ls_right.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ls_up.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_left.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_down.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_right.def = ::at32(button_list, skateboard_key_codes::none);
cfg->rs_up.def = ::at32(button_list, skateboard_key_codes::none);
cfg->start.def = ::at32(button_list, skateboard_key_codes::start);
cfg->select.def = ::at32(button_list, skateboard_key_codes::select);
cfg->ps.def = ::at32(button_list, skateboard_key_codes::ps);
cfg->square.def = ::at32(button_list, skateboard_key_codes::square);
cfg->cross.def = ::at32(button_list, skateboard_key_codes::cross);
cfg->circle.def = ::at32(button_list, skateboard_key_codes::circle);
cfg->triangle.def = ::at32(button_list, skateboard_key_codes::triangle);
cfg->left.def = ::at32(button_list, skateboard_key_codes::left);
cfg->down.def = ::at32(button_list, skateboard_key_codes::down);
cfg->right.def = ::at32(button_list, skateboard_key_codes::right);
cfg->up.def = ::at32(button_list, skateboard_key_codes::up);
cfg->r1.def = ::at32(button_list, skateboard_key_codes::none);
cfg->r2.def = ::at32(button_list, skateboard_key_codes::none);
cfg->r3.def = ::at32(button_list, skateboard_key_codes::none);
cfg->l1.def = ::at32(button_list, skateboard_key_codes::none);
cfg->l2.def = ::at32(button_list, skateboard_key_codes::none);
cfg->l3.def = ::at32(button_list, skateboard_key_codes::none);
cfg->ir_nose.def = ::at32(button_list, skateboard_key_codes::ir_nose);
cfg->ir_tail.def = ::at32(button_list, skateboard_key_codes::ir_tail);
cfg->ir_left.def = ::at32(button_list, skateboard_key_codes::ir_left);
cfg->ir_right.def = ::at32(button_list, skateboard_key_codes::ir_right);
cfg->tilt_left.def = ::at32(button_list, skateboard_key_codes::tilt_left);
cfg->tilt_right.def = ::at32(button_list, skateboard_key_codes::tilt_right);
// Set default misc variables
cfg->lstickdeadzone.def = 40; // between 0 and 255
cfg->rstickdeadzone.def = 40; // between 0 and 255
cfg->ltriggerthreshold.def = 0; // between 0 and 255
cfg->rtriggerthreshold.def = 0; // between 0 and 255
// apply defaults
cfg->from_default();
}
void skateboard_pad_handler::check_add_device(hid_device* hidDevice, std::string_view path, std::wstring_view wide_serial)
{
if (!hidDevice)
{
return;
}
skateboard_device* device = nullptr;
for (auto& controller : m_controllers)
{
ensure(controller.second);
if (!controller.second->hidDevice)
{
device = controller.second.get();
break;
}
}
if (!device)
{
return;
}
if (hid_set_nonblocking(hidDevice, 1) == -1)
{
skateboard_log.error("check_add_device: hid_set_nonblocking failed! Reason: %s", hid_error(hidDevice));
hid_close(hidDevice);
return;
}
device->hidDevice = hidDevice;
device->path = path;
// Activate
if (send_output_report(device) == -1)
{
skateboard_log.error("check_add_device: send_output_report failed! Reason: %s", hid_error(hidDevice));
}
std::string serial;
for (wchar_t ch : wide_serial)
serial += static_cast<uchar>(ch);
skateboard_log.notice("Added device: serial='%s', path='%s'", serial, device->path);
}
skateboard_pad_handler::DataStatus skateboard_pad_handler::get_data(skateboard_device* device)
{
if (!device)
return DataStatus::ReadError;
std::array<u8, sizeof(skateboard_input_report)> buf{};
int res = hid_read(device->hidDevice, buf.data(), buf.size());
if (res == -1)
{
// looks like controller disconnected or read error
skateboard_log.error("get_data ReadError", device->path);
return DataStatus::ReadError;
}
if (res != static_cast<int>(sizeof(skateboard_input_report)))
return DataStatus::NoNewData;
if (std::memcmp(device->padData.data(), buf.data(), sizeof(skateboard_input_report)) == 0)
return DataStatus::NoNewData;
// Get the new data
memcpy(device->padData.data(), buf.data(), sizeof(skateboard_input_report));
// Check the skateboard's power state based on the input report
device->skateboard_is_on =
(std::memcmp(not_connected_state.data(), buf.data(), not_connected_state.size()) != 0 && // This usually means that the device hasn't been connected to the dongle yet.
std::memcmp(disconnected_state.data(), buf.data(), disconnected_state.size()) != 0); // This usually means that the device was disconnected from the dongle.
return DataStatus::NewData;
}
PadHandlerBase::connection skateboard_pad_handler::update_connection(const std::shared_ptr<PadDevice>& device)
{
skateboard_device* skateboard_dev = static_cast<skateboard_device*>(device.get());
if (!skateboard_dev || skateboard_dev->path.empty())
return connection::disconnected;
if (skateboard_dev->hidDevice == nullptr)
{
// try to reconnect
hid_device* dev = hid_open_path(skateboard_dev->path.c_str());
if (dev)
{
if (hid_set_nonblocking(dev, 1) == -1)
{
skateboard_log.error("Reconnecting Device %s: hid_set_nonblocking failed with error %s", skateboard_dev->path, hid_error(dev));
}
skateboard_dev->hidDevice = dev;
}
else
{
// nope, not there
skateboard_log.error("Device %s: disconnected", skateboard_dev->path);
return connection::disconnected;
}
}
if (get_data(skateboard_dev) == DataStatus::ReadError)
{
// this also can mean disconnected, either way deal with it on next loop and reconnect
hid_close(skateboard_dev->hidDevice);
skateboard_dev->hidDevice = nullptr;
return connection::no_data;
}
if (!skateboard_dev->skateboard_is_on)
{
// This means that the dongle is still connected, but the skateboard is turned off.
// There is no need to reconnect the hid device again, we just have to check the input report for proper data.
// The game should get the proper disconnected state anyway.
return connection::disconnected;
}
return connection::connected;
}
std::unordered_map<u64, u16> skateboard_pad_handler::get_button_values(const std::shared_ptr<PadDevice>& device)
{
std::unordered_map<u64, u16> key_buf;
skateboard_device* dualsense_dev = static_cast<skateboard_device*>(device.get());
if (!dualsense_dev)
return key_buf;
const std::array<u8, 64>& buf = dualsense_dev->padData;
const skateboard_input_report* input = reinterpret_cast<const skateboard_input_report*>(buf.data());
// D-Pad
key_buf[skateboard_key_codes::left] = (input->d_pad == dpad_states::left || input->d_pad == dpad_states::up_left || input->d_pad == dpad_states::down_left) ? 255 : 0;
key_buf[skateboard_key_codes::right] = (input->d_pad == dpad_states::right || input->d_pad == dpad_states::up_right || input->d_pad == dpad_states::down_right) ? 255 : 0;
key_buf[skateboard_key_codes::up] = (input->d_pad == dpad_states::up || input->d_pad == dpad_states::up_left || input->d_pad == dpad_states::up_right) ? 255 : 0;
key_buf[skateboard_key_codes::down] = (input->d_pad == dpad_states::down || input->d_pad == dpad_states::down_left || input->d_pad == dpad_states::down_right) ? 255 : 0;
// Face buttons
key_buf[skateboard_key_codes::cross] = (input->buttons & button_flags::cross) ? 255 : 0;
key_buf[skateboard_key_codes::square] = (input->buttons & button_flags::square) ? 255 : 0;
key_buf[skateboard_key_codes::circle] = (input->buttons & button_flags::circle) ? 255 : 0;
key_buf[skateboard_key_codes::triangle] = (input->buttons & button_flags::triangle) ? 255 : 0;
key_buf[skateboard_key_codes::start] = (input->buttons & button_flags::start) ? 255 : 0;
key_buf[skateboard_key_codes::select] = (input->buttons & button_flags::select) ? 255 : 0;
key_buf[skateboard_key_codes::ps] = (input->buttons & button_flags::ps) ? 255 : 0;
// Infrared
key_buf[skateboard_key_codes::ir_nose] = input->pressure_triangle;
key_buf[skateboard_key_codes::ir_tail] = input->pressure_circle;
key_buf[skateboard_key_codes::ir_left] = input->pressure_cross;
key_buf[skateboard_key_codes::ir_right] = input->pressure_square;
key_buf[skateboard_key_codes::tilt_left] = input->pressure_l1;
key_buf[skateboard_key_codes::tilt_right] = input->pressure_r1;
// NOTE: Axes X, Y, Z and RZ are always 128, which is the default anyway, so setting the values is omitted.
return key_buf;
}
void skateboard_pad_handler::get_extended_info(const pad_ensemble& binding)
{
const auto& device = binding.device;
const auto& pad = binding.pad;
skateboard_device* dev = static_cast<skateboard_device*>(device.get());
if (!dev || !pad)
return;
const std::array<u8, 64>& buf = dev->padData;
const skateboard_input_report* input = reinterpret_cast<const skateboard_input_report*>(buf.data());
pad->m_sensors[0].m_value = Clamp0To1023(input->large_axes[0]);
pad->m_sensors[1].m_value = Clamp0To1023(input->large_axes[1]);
pad->m_sensors[2].m_value = Clamp0To1023(input->large_axes[2]);
pad->m_sensors[3].m_value = Clamp0To1023(input->large_axes[3]);
}
pad_preview_values skateboard_pad_handler::get_preview_values(const std::unordered_map<u64, u16>& /*data*/)
{
// There is no proper user interface for skateboard values yet
return {};
}
int skateboard_pad_handler::send_output_report(skateboard_device* device)
{
if (!device || !device->hidDevice)
return -2;
const cfg_pad* config = device->config;
if (config == nullptr)
return -2; // hid_write returns -1 on error
// The output report contents are still unknown
skateboard_output_report report{};
return hid_write(device->hidDevice, report.data.data(), report.data.size());
}
void skateboard_pad_handler::apply_pad_data(const pad_ensemble& binding)
{
const auto& device = binding.device;
const auto& pad = binding.pad;
skateboard_device* dev = static_cast<skateboard_device*>(device.get());
if (!dev || !dev->hidDevice || !dev->config || !pad)
return;
dev->new_output_data = false;
if (dev->new_output_data)
{
if (send_output_report(dev) >= 0)
{
dev->new_output_data = false;
}
}
}
void skateboard_pad_handler::SetPadData(const std::string& padId, u8 player_id, u8 /*large_motor*/, u8 /*small_motor*/, s32 /*r*/, s32 /*g*/, s32 /*b*/, bool /*player_led*/, bool /*battery_led*/, u32 /*battery_led_brightness*/)
{
std::shared_ptr<skateboard_device> device = get_hid_device(padId);
if (device == nullptr || device->hidDevice == nullptr)
return;
device->player_id = player_id;
device->config = get_config(padId);
ensure(device->config);
// Disabled until needed
//send_output_report(device.get());
}

View File

@ -0,0 +1,189 @@
#pragma once
#include "hid_pad_handler.h"
#include <array>
#include <unordered_map>
namespace
{
// Descriptor
// 0x09, 0x05, // Usage (0x05)
// 0xA1, 0x01, // Collection (Application)
// 0x05, 0x09, // Usage Page (Button)
// 0x19, 0x01, // Usage Minimum (0x01)
// 0x29, 0x0D, // Usage Maximum (0x0D)
// 0x15, 0x00, // Logical Minimum (0)
// 0x25, 0x01, // Logical Maximum (1)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x0D, // Report Count (13)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x75, 0x03, // Report Size (3)
// 0x95, 0x01, // Report Count (1)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
// 0x09, 0x39, // Usage (Hat switch)
// 0x15, 0x00, // Logical Minimum (0)
// 0x25, 0x07, // Logical Maximum (7)
// 0x35, 0x00, // Physical Minimum (0)
// 0x46, 0x3B, 0x01, // Physical Maximum (315)
// 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
// 0x75, 0x04, // Report Size (4)
// 0x95, 0x01, // Report Count (1)
// 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
// 0x75, 0x04, // Report Size (4)
// 0x95, 0x01, // Report Count (1)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x09, 0x30, // Usage (X)
// 0x09, 0x31, // Usage (Y)
// 0x09, 0x32, // Usage (Z)
// 0x09, 0x35, // Usage (Rz)
// 0x15, 0x00, // Logical Minimum (0)
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
// 0x35, 0x00, // Physical Minimum (0)
// 0x46, 0xFF, 0x00, // Physical Maximum (255)
// 0x65, 0x00, // Unit (None)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x04, // Report Count (4)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
// 0x09, 0x20, // Usage (0x20)
// 0x09, 0x21, // Usage (0x21)
// 0x09, 0x22, // Usage (0x22)
// 0x09, 0x23, // Usage (0x23)
// 0x09, 0x24, // Usage (0x24)
// 0x09, 0x25, // Usage (0x25)
// 0x09, 0x26, // Usage (0x26)
// 0x09, 0x27, // Usage (0x27)
// 0x09, 0x28, // Usage (0x28)
// 0x09, 0x29, // Usage (0x29)
// 0x09, 0x2A, // Usage (0x2A)
// 0x09, 0x2B, // Usage (0x2B)
// 0x15, 0x00, // Logical Minimum (0)
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x0C, // Report Count (12)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x09, 0x2C, // Usage (0x2C)
// 0x09, 0x2D, // Usage (0x2D)
// 0x09, 0x2E, // Usage (0x2E)
// 0x09, 0x2F, // Usage (0x2F)
// 0x15, 0x00, // Logical Minimum (0)
// 0x26, 0xFF, 0x03, // Logical Maximum (1023)
// 0x35, 0x00, // Physical Minimum (0)
// 0x46, 0xFF, 0x03, // Physical Maximum (1023)
// 0x75, 0x10, // Report Size (16)
// 0x95, 0x04, // Report Count (4)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x0A, 0x21, 0x26, // Usage (0x2621)
// 0x15, 0x00, // Logical Minimum (0)
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
// 0x35, 0x00, // Physical Minimum (0)
// 0x46, 0xFF, 0x00, // Physical Maximum (255)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x08, // Report Count (8)
// 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
// 0x0A, 0x21, 0x26, // Usage (0x2621)
// 0x15, 0x00, // Logical Minimum (0)
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x08, // Report Count (8)
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
// 0xC0, // End Collection
#pragma pack(push, 1)
struct skateboard_input_report
{
// 13 buttons, value range 0 to 1, 13 bits + 3 bits padding (2 bytes total)
u16 buttons{};
// d-pad, value range 0 to 7 (8 directions), 4 bits + 4 bits padding (1 byte total)
u8 d_pad{};
// 4 axis (X, Y, Z, RZ), value range 0 to 255, 1 byte each (4 bytes total)
u8 axis_x{};
u8 axis_y{};
u8 axis_z{};
u8 axis_rz{};
// 12 axis (0x20 - 0x2B), value range 0 to 255, 1 byte each (12 bytes total)
// These 12 values match the pressure sensitivity values of the buttons (CELL_PAD_BTN_OFFSET_PRESS)
u8 pressure_right{}; // value for CELL_PAD_BTN_OFFSET_PRESS_RIGHT -> always 0 ?
u8 pressure_left{}; // value for CELL_PAD_BTN_OFFSET_PRESS_LEFT -> always 0 ?
u8 pressure_up{}; // value for CELL_PAD_BTN_OFFSET_PRESS_UP -> always 0 ?
u8 pressure_down{}; // value for CELL_PAD_BTN_OFFSET_PRESS_DOWN -> always 0 ?
u8 pressure_triangle{}; // value for CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE -> infrared nose
u8 pressure_circle{}; // value for CELL_PAD_BTN_OFFSET_PRESS_CIRCLE -> infrared tail
u8 pressure_cross{}; // value for CELL_PAD_BTN_OFFSET_PRESS_CROSS -> infrared left
u8 pressure_square{}; // value for CELL_PAD_BTN_OFFSET_PRESS_SQUARE -> infrared right
u8 pressure_l1{}; // value for CELL_PAD_BTN_OFFSET_PRESS_L1 -> tilt left (probably)
u8 pressure_r1{}; // value for CELL_PAD_BTN_OFFSET_PRESS_R1 -> tilt right (probably)
u8 pressure_l2{}; // value for CELL_PAD_BTN_OFFSET_PRESS_L2 -> always 0 ?
u8 pressure_r2{}; // value for CELL_PAD_BTN_OFFSET_PRESS_R2 -> always 0 ?
// 4 axis (0x2C - 0x2F), value range 0 to 1023, 2 bytes each (8 bytes total)
std::array<u16, 4> large_axes{};
};
#pragma pack(pop)
struct skateboard_output_report
{
// 8 axis (0x2621), value range 0 to 255, 1 byte each (8 bytes total)
std::array<u8, 8> data{};
};
struct skateboard_feature_report
{
// 8 axis (0x2621), value range 0 to 255, 1 byte each (8 bytes total)
std::array<u8, 8> data{};
};
}
class skateboard_device : public HidDevice
{
public:
bool skateboard_is_on = false;
};
class skateboard_pad_handler final : public hid_pad_handler<skateboard_device>
{
enum skateboard_key_codes
{
none = 0,
left,
right,
up,
down,
cross,
square,
circle,
triangle,
start,
select,
ps,
ir_nose,
ir_tail,
ir_left,
ir_right,
tilt_left,
tilt_right,
};
public:
skateboard_pad_handler();
~skateboard_pad_handler();
void SetPadData(const std::string& padId, u8 player_id, u8 large_motor, u8 small_motor, s32 r, s32 g, s32 b, bool player_led, bool battery_led, u32 battery_led_brightness) override;
void init_config(cfg_pad* cfg) override;
private:
DataStatus get_data(skateboard_device* device) override;
void check_add_device(hid_device* hidDevice, std::string_view path, std::wstring_view wide_serial) override;
int send_output_report(skateboard_device* device) override;
PadHandlerBase::connection update_connection(const std::shared_ptr<PadDevice>& device) override;
std::unordered_map<u64, u16> get_button_values(const std::shared_ptr<PadDevice>& device) override;
pad_preview_values get_preview_values(const std::unordered_map<u64, u16>& data) override;
void get_extended_info(const pad_ensemble& binding) override;
void apply_pad_data(const pad_ensemble& binding) override;
};

View File

@ -180,6 +180,7 @@
<ClCompile Include="Input\evdev_gun_handler.cpp" />
<ClCompile Include="Input\hid_pad_handler.cpp" />
<ClCompile Include="Input\sdl_pad_handler.cpp" />
<ClCompile Include="Input\skateboard_pad_handler.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="main_application.cpp" />
<ClCompile Include="Input\basic_keyboard_handler.cpp" />
@ -923,6 +924,7 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<ClInclude Include="Input\sdl_pad_handler.h" />
<ClInclude Include="Input\skateboard_pad_handler.h" />
<ClInclude Include="main_application.h" />
<ClInclude Include="Input/mm_joystick_handler.h" />
<CustomBuild Include="rpcs3qt\cg_disasm_window.h">

View File

@ -166,6 +166,9 @@
<Filter Include="CI">
<UniqueIdentifier>{b3bba7ee-4f23-41b6-8ddf-e40f848015de}</UniqueIdentifier>
</Filter>
<Filter Include="Io\Skateboard">
<UniqueIdentifier>{bd1fd182-dc0d-45cb-bb9e-f68b2282d7eb}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
@ -1044,6 +1047,9 @@
<ClCompile Include="rpcs3qt\localized_emu.cpp">
<Filter>Gui\settings</Filter>
</ClCompile>
<ClCompile Include="Input\skateboard_pad_handler.cpp">
<Filter>Io\Skateboard</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -1223,6 +1229,9 @@
<ClInclude Include="rpcs3qt\midi_creator.h">
<Filter>Gui\settings</Filter>
</ClInclude>
<ClInclude Include="Input\skateboard_pad_handler.h">
<Filter>Io\Skateboard</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">

View File

@ -180,6 +180,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
ui->chooseClass->addItem(tr("DJ"), u32{CELL_PAD_PCLASS_TYPE_DJ});
ui->chooseClass->addItem(tr("Dance Mat"), u32{CELL_PAD_PCLASS_TYPE_DANCEMAT});
ui->chooseClass->addItem(tr("Navigation"), u32{CELL_PAD_PCLASS_TYPE_NAVIGATION});
ui->chooseClass->addItem(tr("Skateboard"), u32{CELL_PAD_PCLASS_TYPE_SKATEBOARD});
connect(ui->chooseClass, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index)
{
@ -1382,6 +1383,7 @@ void pad_settings_dialog::ChangeHandler()
m_description = tooltips.gamepad_settings.null;
break;
case pad_handler::keyboard: m_description = tooltips.gamepad_settings.keyboard; break;
case pad_handler::skateboard: m_description = tooltips.gamepad_settings.skateboard; break;
#ifdef _WIN32
case pad_handler::xinput: m_description = tooltips.gamepad_settings.xinput; break;
case pad_handler::mm: m_description = tooltips.gamepad_settings.mmjoy; break;
@ -1446,6 +1448,7 @@ void pad_settings_dialog::ChangeHandler()
case pad_handler::ds3:
case pad_handler::ds4:
case pad_handler::dualsense:
case pad_handler::skateboard:
{
const QString name_string = qstr(m_handler->name_string());
for (usz i = 1; i <= m_handler->max_devices(); i++) // Controllers 1-n in GUI
@ -1672,6 +1675,11 @@ void pad_settings_dialog::HandleDeviceClassChange(u32 class_id) const
ui->chooseProduct->addItem(tr("PS Move Navigation", "PS Move Navigation Controller"), static_cast<int>(product.type));
break;
}
case input::product_type::ride_skateboard:
{
ui->chooseProduct->addItem(tr("RIDE Skateboard", "Tony Hawk RIDE Skateboard Controller"), static_cast<int>(product.type));
break;
}
}
}
}
@ -1892,6 +1900,7 @@ QString pad_settings_dialog::GetLocalizedPadHandler(const QString& original, pad
case pad_handler::ds3: return tr("DualShock 3");
case pad_handler::ds4: return tr("DualShock 4");
case pad_handler::dualsense: return tr("DualSense");
case pad_handler::skateboard: return tr("Skateboard");
#ifdef _WIN32
case pad_handler::xinput: return tr("XInput");
case pad_handler::mm: return tr("MMJoystick");

View File

@ -265,6 +265,7 @@ public:
const QString dualsense_windows = tr("The DualSense handler is recommended for official DualSense controllers.");
const QString dualsense_linux = tr("The DualSense handler is recommended for official DualSense controllers.");
const QString dualsense_other = tr("The DualSense handler is recommended for official DualSense controllers.");
const QString skateboard = tr("The Skateboard handler is recommended for official RIDE skateboard controllers.");
const QString xinput = tr("The XInput handler will work with Xbox controllers and many third-party PC-compatible controllers. Pressure sensitive buttons from SCP are supported when SCP's XInput1_3.dll is placed in the main RPCS3 directory. For more details, see the <a %0 href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a>.").arg(gui::utils::get_link_style());
const QString evdev = tr("The evdev handler should work with any controller that has Linux support.<br>If your joystick is not being centered properly, read the <a %0 href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a> for instructions.").arg(gui::utils::get_link_style());
const QString mmjoy = tr("The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them.");