diff --git a/src/controller/emulated.rs b/src/controller/emulated.rs index db0e55b..67dedde 100644 --- a/src/controller/emulated.rs +++ b/src/controller/emulated.rs @@ -18,6 +18,7 @@ mod sotp031201_p5b5; mod sotp031201_p5b7; mod vok00106; mod zkns001; +mod slph00051; const FFS_MOUNT: &str = "/tmp/ffs"; const ENDPOINT0: &str = "/tmp/ffs/ep0"; @@ -35,6 +36,7 @@ pub enum ControllerModel { SOTP031201P5B7, VOK00106, ZKNS001, + SLPH00051, } pub struct DeviceDescriptor { @@ -62,6 +64,11 @@ pub fn set_model(state: &ControllerState) -> Option { model = ControllerModel::ZKNS001; descriptors = (&zkns001::DEVICE_DESCRIPTOR, &zkns001::DESCRIPTORS, &zkns001::STRINGS); } + else if state.button_down && state.power == 0 { + model_name = "SLPH-00051"; + model = ControllerModel::SLPH00051; + descriptors = (&slph00051::DEVICE_DESCRIPTOR, &slph00051::DESCRIPTORS, &slph00051::STRINGS); + } else if state.button_d { model_name = "TCPP-20009"; model = ControllerModel::TCPP20009; @@ -136,6 +143,9 @@ pub fn set_state(state: &mut ControllerState, model: &ControllerModel) { ControllerModel::ZKNS001 => { zkns001::update_gadget(state); } + ControllerModel::SLPH00051 => { + slph00051::update_gadget(state); + } } } @@ -151,6 +161,9 @@ pub fn handle_ctrl_transfer(model: ControllerModel, data: &[u8]) { ControllerModel::ZKNS001 => { report = Some(&zkns001::HID_REPORT_DESCRIPTOR); } + ControllerModel::SLPH00051 => { + report = Some(&slph00051::HID_REPORT_DESCRIPTOR); + } _ => { report = None; } diff --git a/src/controller/emulated/slph00051.rs b/src/controller/emulated/slph00051.rs new file mode 100644 index 0000000..c8296af --- /dev/null +++ b/src/controller/emulated/slph00051.rs @@ -0,0 +1,198 @@ +use std::fs::File; +use std::io::{Write}; +use bitflags::bitflags; +use crate::controller::physical::ControllerState; +use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1}; + +pub const DESCRIPTORS: [u8; 80] = [0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, +0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, +0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x94, 0x00, +0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05, +0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x05, +0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, +0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x94, 0x00, +0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05, +0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x05]; +pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + +pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor{b_device_class: 0x0, b_device_sub_class: 0x0, id_vendor: 0x054C, id_product: 0x0268, bcd_device: 0x0100, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (PS1 Two Handle mode)", i_serial_number: "SLPH-00051"}; + +pub const HID_REPORT_DESCRIPTOR: [u8; 148] = [ + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Physical) + 0xA1, 0x02, // Collection (Application) + 0x85, 0x01, // Report ID (1) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + // NOTE: reserved byte + 0x75, 0x01, // Report Size (1) + 0x95, 0x13, // Report Count (19) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x01, // Physical Maximum (1) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x13, // Usage Maximum (0x13) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x01, // Report Size (1) + 0x95, 0x0D, // Report Count (13) + 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + // NOTE: 32 bit integer, where 0:18 are buttons and 19:31 are reserved + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection (Undefined) + 0x75, 0x08, // Report Size (8) + 0x95, 0x04, // Report Count (4) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0xFF, 0x00, // Physical Maximum (255) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x35, // Usage (Rz) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + // NOTE: four joysticks + 0xC0, // End Collection + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x75, 0x08, // Report Size (8) + 0x95, 0x27, // Report Count (39) + 0x09, 0x01, // Usage (Pointer) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x30, // Report Count (48) + 0x09, 0x01, // Usage (Pointer) + 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x75, 0x08, // Report Size (8) + 0x95, 0x30, // Report Count (48) + 0x09, 0x01, // Usage (Pointer) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + 0xA1, 0x02, // Collection (Application) + 0x85, 0x02, // Report ID (2) + 0x75, 0x08, // Report Size (8) + 0x95, 0x30, // Report Count (48) + 0x09, 0x01, // Usage (Pointer) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + 0xA1, 0x02, // Collection (Application) + 0x85, 0xEE, // Report ID (238) + 0x75, 0x08, // Report Size (8) + 0x95, 0x30, // Report Count (48) + 0x09, 0x01, // Usage (Pointer) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + 0xA1, 0x02, // Collection (Application) + 0x85, 0xEF, // Report ID (239) + 0x75, 0x08, // Report Size (8) + 0x95, 0x30, // Report Count (48) + 0x09, 0x01, // Usage (Pointer) + 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + 0xC0, // End Collection +]; + +bitflags! { + struct Buttons1: u8 { + const NONE = 0; + const SELECT = 1; + const L3 = 2; + const R3 = 4; + const START = 8; + const UP = 16; + const RIGHT = 32; + const DOWN = 64; + const LEFT = 128; + } + struct Buttons2: u8 { + const NONE = 0; + const L2 = 1; + const R2 = 2; + const L1 = 4; + const R1 = 8; + const TRIANGLE = 16; + const CIRCLE = 32; + const CROSS = 64; + const SQUARE = 128; + } +} + +pub fn update_gadget(state: &mut ControllerState) { + let mut buttons1 = Buttons1::UP | Buttons1::DOWN; + let mut buttons2 = Buttons2::NONE; + + // Calculate data for handles + match state.power { + 0 => { + buttons1.insert(Buttons1::LEFT | Buttons1::RIGHT); + } + 1 => { + buttons1.insert(Buttons1::RIGHT); + buttons2.insert(Buttons2::TRIANGLE); + } + 2 => { + buttons1.insert(Buttons1::RIGHT); + } + 3 => { + buttons1.insert(Buttons1::LEFT); + buttons2.insert(Buttons2::TRIANGLE); + } + 4 => { + buttons1.insert(Buttons1::LEFT); + } + _ => { + buttons2.insert(Buttons2::TRIANGLE); + } + } + match state.brake { + 0 => { + buttons2.insert(Buttons2::L2 | Buttons2::R1 | Buttons2::R2); + } + 1 => { + buttons2.insert(Buttons2::L1 | Buttons2::R1 | Buttons2::R2); + } + 2 => { + buttons2.insert(Buttons2::R1 | Buttons2::R2); + } + 3 => { + buttons2.insert(Buttons2::L1 | Buttons2::L2 | Buttons2::R2); + } + 4 => { + buttons2.insert(Buttons2::L2 | Buttons2::R2); + } + 5 => { + buttons2.insert(Buttons2::L1 | Buttons2::R2); + } + 6 => { + buttons2.insert(Buttons2::R2); + } + 7 => { + buttons2.insert(Buttons2::L1 | Buttons2::L2 | Buttons2::R1); + } + 8 => { + buttons2.insert(Buttons2::L2 | Buttons2::R1); + } + _ => () + } + + // Calculate data for buttons + if state.button_a { buttons2.insert(Buttons2::SQUARE) } + if state.button_b { buttons2.insert(Buttons2::CROSS) } + if state.button_c { buttons2.insert(Buttons2::CIRCLE) } + if state.button_start { buttons1.insert(Buttons1::START) } + if state.button_select { buttons1.insert(Buttons1::SELECT) } + + // Assemble data and send it to gadget + let data = [0x1, 0x0, buttons1.bits, buttons2.bits, 0x0, 0x0, 0x80, 0x80, 0x80, 0x80, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3, 0xEF, 0x14, 0x0, 0x0, 0x0, 0x0, 0x23, 0x1A, 0x77, 0x1, 0x81, 0x1, 0xFE, 0x1, 0xFE, 0x1, 0xFE, 0x1, 0xFE]; + if let Ok(mut file) = File::create(ENDPOINT1) { + file.write(&data).ok(); + } +} \ No newline at end of file