diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index dd4d3baf8b..5c7d9b3836 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -221,16 +221,23 @@ void fmt_class_string::format(std::string& out, u64 arg) const std::vector 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 += ' '; } } diff --git a/Utilities/StrUtil.h b/Utilities/StrUtil.h index 74df4eae07..3f5ddfd6df 100644 --- a/Utilities/StrUtil.h +++ b/Utilities/StrUtil.h @@ -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 diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index ebad9bfeb3..53429411ca 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -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 ) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index b960db944d..be14426db5 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -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; } diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp index da9cbb5f0f..b3b8b59413 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp @@ -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); diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 665bee4b63..4e62d1c451 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -454,19 +454,21 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad, u8 player_id) std::array, 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, 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, PadHandlerBase::button::button_count> PadHandlerBase:: mapping[button::rs_up] = narrow_set(device->axis_code_right[3]); mapping[button::ps] = FindKeyCodes(button_list, cfg->ps); + mapping[button::skateboard_ir_nose] = FindKeyCodes(button_list, cfg->ir_nose); + mapping[button::skateboard_ir_tail] = FindKeyCodes(button_list, cfg->ir_tail); + mapping[button::skateboard_ir_left] = FindKeyCodes(button_list, cfg->ir_left); + mapping[button::skateboard_ir_right] = FindKeyCodes(button_list, cfg->ir_right); + mapping[button::skateboard_tilt_left] = FindKeyCodes(button_list, cfg->tilt_left); + mapping[button::skateboard_tilt_right] = FindKeyCodes(button_list, cfg->tilt_right); + mapping[button::pressure_intensity_button] = FindKeyCodes(button_list, cfg->pressure_intensity_button); return mapping; diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 7ff5fa6b7e..5b164b9f25 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -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 diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index b2d34bf7c2..a4a03dee2e 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -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 device_class_type{ this, "Device Class Type", 0 }; + cfg::uint 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 }; }; diff --git a/rpcs3/Emu/Io/pad_config_types.cpp b/rpcs3/Emu/Io/pad_config_types.cpp index 9425939f47..370da062b8 100644 --- a/rpcs3/Emu/Io/pad_config_types.cpp +++ b/rpcs3/Emu/Io/pad_config_types.cpp @@ -13,6 +13,7 @@ void fmt_class_string::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"; diff --git a/rpcs3/Emu/Io/pad_config_types.h b/rpcs3/Emu/Io/pad_config_types.h index b7f04f8510..4d1c64eea8 100644 --- a/rpcs3/Emu/Io/pad_config_types.h +++ b/rpcs3/Emu/Io/pad_config_types.h @@ -9,6 +9,7 @@ enum class pad_handler ds3, ds4, dualsense, + skateboard, #ifdef _WIN32 xinput, mm, diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index abc7dac077..13c3fe8f34 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -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 diff --git a/rpcs3/Input/dualsense_pad_handler.cpp b/rpcs3/Input/dualsense_pad_handler.cpp index 80712f9021..dd17de8bd9 100644 --- a/rpcs3/Input/dualsense_pad_handler.cpp +++ b/rpcs3/Input/dualsense_pad_handler.cpp @@ -345,7 +345,7 @@ dualsense_pad_handler::DataStatus dualsense_pad_handler::get_data(DualSenseDevic std::array 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) { diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index c0f616aa3b..1db4233653 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -1359,6 +1359,16 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr 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); diff --git a/rpcs3/Input/hid_pad_handler.cpp b/rpcs3/Input/hid_pad_handler.cpp index f31c12e4c5..4d8a7f63e9 100644 --- a/rpcs3/Input/hid_pad_handler.cpp +++ b/rpcs3/Input/hid_pad_handler.cpp @@ -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::get_battery_color(u8 battery_level, u32 brightness) template class hid_pad_handler; template class hid_pad_handler; template class hid_pad_handler; +template class hid_pad_handler; diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index 283605bd31..38d8f51051 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -963,6 +963,16 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr 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)); diff --git a/rpcs3/Input/mm_joystick_handler.cpp b/rpcs3/Input/mm_joystick_handler.cpp index fbe9d14e5b..7e2de631cb 100644 --- a/rpcs3/Input/mm_joystick_handler.cpp +++ b/rpcs3/Input/mm_joystick_handler.cpp @@ -174,6 +174,13 @@ std::array, 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(cfg->ir_nose); + mapping[button::skateboard_ir_tail] = find_keys(cfg->ir_tail); + mapping[button::skateboard_ir_left] = find_keys(cfg->ir_left); + mapping[button::skateboard_ir_right] = find_keys(cfg->ir_right); + mapping[button::skateboard_tilt_left] = find_keys(cfg->tilt_left); + mapping[button::skateboard_tilt_right] = find_keys(cfg->tilt_right); + mapping[button::pressure_intensity_button] = find_keys(cfg->pressure_intensity_button); return mapping; diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 77187883b4..3e013b00a2 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -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(); break; + case pad_handler::skateboard: + cur_pad_handler = std::make_shared(); + break; #ifdef _WIN32 case pad_handler::xinput: cur_pad_handler = std::make_shared(); @@ -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 pad_thread::GetHandler(pad_handler type) return std::make_unique(); case pad_handler::dualsense: return std::make_unique(); + case pad_handler::skateboard: + return std::make_unique(); #ifdef _WIN32 case pad_handler::xinput: return std::make_unique(); diff --git a/rpcs3/Input/product_info.h b/rpcs3/Input/product_info.h index 9fb337fa27..eac07bf183 100644 --- a/rpcs3/Input/product_info.h +++ b/rpcs3/Input/product_info.h @@ -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) + }; + } } } }; diff --git a/rpcs3/Input/skateboard_pad_handler.cpp b/rpcs3/Input/skateboard_pad_handler.cpp new file mode 100644 index 0000000000..ab58806a4e --- /dev/null +++ b/rpcs3/Input/skateboard_pad_handler.cpp @@ -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 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 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(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(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 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(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& device) +{ + skateboard_device* skateboard_dev = static_cast(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 skateboard_pad_handler::get_button_values(const std::shared_ptr& device) +{ + std::unordered_map key_buf; + skateboard_device* dualsense_dev = static_cast(device.get()); + if (!dualsense_dev) + return key_buf; + + const std::array& buf = dualsense_dev->padData; + const skateboard_input_report* input = reinterpret_cast(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(device.get()); + if (!dev || !pad) + return; + + const std::array& buf = dev->padData; + const skateboard_input_report* input = reinterpret_cast(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& /*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(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 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()); +} diff --git a/rpcs3/Input/skateboard_pad_handler.h b/rpcs3/Input/skateboard_pad_handler.h new file mode 100644 index 0000000000..37520d55c5 --- /dev/null +++ b/rpcs3/Input/skateboard_pad_handler.h @@ -0,0 +1,189 @@ +#pragma once + +#include "hid_pad_handler.h" + +#include +#include + +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 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 data{}; + }; + + struct skateboard_feature_report + { + // 8 axis (0x2621), value range 0 to 255, 1 byte each (8 bytes total) + std::array data{}; + }; +} + +class skateboard_device : public HidDevice +{ +public: + bool skateboard_is_on = false; +}; + +class skateboard_pad_handler final : public hid_pad_handler +{ + 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& device) override; + std::unordered_map get_button_values(const std::shared_ptr& device) override; + pad_preview_values get_preview_values(const std::unordered_map& data) override; + void get_extended_info(const pad_ensemble& binding) override; + void apply_pad_data(const pad_ensemble& binding) override; +}; diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index afe710087c..0b9baabdcb 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -180,6 +180,7 @@ + @@ -923,6 +924,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index d758cdee03..cf1a8ca61c 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -166,6 +166,9 @@ {b3bba7ee-4f23-41b6-8ddf-e40f848015de} + + {bd1fd182-dc0d-45cb-bb9e-f68b2282d7eb} + @@ -1044,6 +1047,9 @@ Gui\settings + + Io\Skateboard + @@ -1223,6 +1229,9 @@ Gui\settings + + Io\Skateboard + diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 3311ea188b..bb0b5eca20 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -180,6 +180,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr 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::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(product.type)); break; } + case input::product_type::ride_skateboard: + { + ui->chooseProduct->addItem(tr("RIDE Skateboard", "Tony Hawk RIDE Skateboard Controller"), static_cast(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"); diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index ce20096ec9..378c088436 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -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 RPCS3 Wiki.").arg(gui::utils::get_link_style()); const QString evdev = tr("The evdev handler should work with any controller that has Linux support.
If your joystick is not being centered properly, read the RPCS3 Wiki 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.");