Skip to content

Instantly share code, notes, and snippets.

@MinusKelvin
Created October 1, 2020 09:42
Show Gist options
  • Save MinusKelvin/a9db4a5fe9755a8c7f763ed36acfafef to your computer and use it in GitHub Desktop.
Save MinusKelvin/a9db4a5fe9755a8c7f763ed36acfafef to your computer and use it in GitHub Desktop.
SCP Driver Interface rust version
//! Port of https://github.com/mogzol/ScpDriverInterface/
use winapi::um::setupapi::{
SetupDiDestroyDeviceInfoList,
SetupDiEnumDeviceInterfaces,
SetupDiGetClassDevsW,
SetupDiGetDeviceInterfaceDetailW,
SP_DEVICE_INTERFACE_DATA,
SP_DEVINFO_DATA,
DIGCF_PRESENT,
DIGCF_DEVICEINTERFACE
};
use winapi::shared::guiddef::GUID;
use winapi::um::ioapiset::DeviceIoControl;
use winapi::um::fileapi::{
CreateFileW,
OPEN_EXISTING,
};
use winapi::um::winnt::{
GENERIC_READ,
GENERIC_WRITE,
FILE_SHARE_READ,
FILE_SHARE_WRITE,
FILE_ATTRIBUTE_NORMAL,
HANDLE,
};
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
use winapi::um::handleapi::CloseHandle;
pub struct ScpBus {
handle: HANDLE
}
impl ScpBus {
pub fn new() -> Option<Self> {
unsafe { find().and_then(|p| get_handle(p)) }.map(|handle| ScpBus { handle })
}
pub fn plug_in(&self, num: u32) -> Option<Controller> {
let num_bytes = (num+1).to_ne_bytes();
let buffer = [
0x10,
0x00,
0x00,
0x00,
num_bytes[0],
num_bytes[1],
num_bytes[2],
num_bytes[3],
0, 0, 0, 0,
0, 0, 0, 0
];
unsafe {
let mut transfered = 0;
if DeviceIoControl(
self.handle,
0x2A4000,
buffer.as_ptr() as _,
buffer.len() as _,
std::ptr::null_mut(),
0,
&mut transfered,
std::ptr::null_mut()
) != 0 {
Some(Controller {
bus: self,
num: num+1,
state: ControllerState::default()
})
} else {
None
}
}
}
}
pub struct Controller<'a> {
bus: &'a ScpBus,
num: u32,
pub state: ControllerState
}
impl Controller<'_> {
pub fn update(&self) {
fn to_i16_bytes(axis_value: f32) -> [u8; 2] {
((axis_value.min(1.0).max(-1.0) * std::i16::MAX as f32) as i16).to_ne_bytes()
}
fn to_u8_bytes(axis_value: f32) -> u8 {
(axis_value.min(1.0).max(0.0) * std::u8::MAX as f32) as u8
}
fn to_bits(bools: [bool; 8]) -> u8 {
(bools[0] as u8) << 0 |
(bools[1] as u8) << 1 |
(bools[2] as u8) << 2 |
(bools[3] as u8) << 3 |
(bools[4] as u8) << 4 |
(bools[5] as u8) << 5 |
(bools[6] as u8) << 6 |
(bools[7] as u8) << 7
}
let num_bytes = self.num.to_ne_bytes();
let ls_x = to_i16_bytes(self.state.ls_x);
let ls_y = to_i16_bytes(self.state.ls_y);
let rs_x = to_i16_bytes(self.state.rs_x);
let rs_y = to_i16_bytes(self.state.rs_y);
let lt = to_u8_bytes(self.state.lt);
let rt = to_u8_bytes(self.state.rt);
let report = [
0x1C, 0, 0, 0,
num_bytes[0],
num_bytes[1],
num_bytes[2],
num_bytes[3],
0x00,
0x14,
to_bits([
self.state.up, self.state.down, self.state.left, self.state.right,
self.state.start, self.state.select, self.state.ls, self.state.rs
]),
to_bits([
self.state.lb, self.state.rb, false, false,
self.state.a, self.state.b, self.state.x, self.state.y
]),
lt,
rt,
ls_x[0], ls_x[1],
ls_y[0], ls_y[1],
rs_x[0], rs_x[1],
rs_y[0], rs_y[1],
0, 0, 0, 0, 0, 0
];
unsafe {
let mut transfered = 0;
DeviceIoControl(
self.bus.handle,
0x2A400C,
report.as_ptr() as _,
report.len() as _,
std::ptr::null_mut(),
0,
&mut transfered,
std::ptr::null_mut()
);
}
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct ControllerState {
pub up: bool,
pub down: bool,
pub left: bool,
pub right: bool,
pub a: bool,
pub b: bool,
pub x: bool,
pub y: bool,
pub start: bool,
pub select: bool,
pub ls: bool,
pub rs: bool,
pub lb: bool,
pub rb: bool,
pub ls_x: f32,
pub ls_y: f32,
pub rs_x: f32,
pub rs_y: f32,
pub lt: f32,
pub rt: f32
}
impl Drop for ScpBus {
fn drop(&mut self) {
unsafe {
CloseHandle(self.handle);
}
}
}
impl Drop for Controller<'_> {
fn drop(&mut self) {
let num_bytes = self.num.to_ne_bytes();
let buffer = [
0x10,
0x00,
0x00,
0x00,
num_bytes[0],
num_bytes[1],
num_bytes[2],
num_bytes[3],
0, 0, 0, 0,
0, 0, 0, 0
];
unsafe {
let mut transfered = 0;
DeviceIoControl(
self.bus.handle,
0x2A4004,
buffer.as_ptr() as _,
buffer.len() as _,
std::ptr::null_mut(),
0,
&mut transfered,
std::ptr::null_mut()
);
}
}
}
unsafe fn get_handle(path: Vec<u16>) -> Option<HANDLE> {
let h = CreateFileW(
path.as_ptr(),
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
std::ptr::null_mut(),
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
std::ptr::null_mut()
);
if h.is_null() {
None
} else {
Some(h)
}
}
unsafe fn find() -> Option<Vec<u16>> {
// {F679F562-3164-42CE-A4DB-E7DDBE723909}
const SCP_BUS_CLASS_GUID: GUID = GUID {
Data1: 0xF679F562,
Data2: 0x3164,
Data3: 0x42CE,
Data4: [0xA4, 0xDB, 0xE7, 0xDD, 0xBE, 0x72, 0x39, 0x09]
};
let mut device_interface_data = SP_DEVICE_INTERFACE_DATA::default();
device_interface_data.cbSize = std::mem::size_of::<SP_DEVICE_INTERFACE_DATA>() as _;
let device_info_set = SetupDiGetClassDevsW(
&SCP_BUS_CLASS_GUID,
std::ptr::null(),
std::ptr::null_mut(),
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
);
if SetupDiEnumDeviceInterfaces(
device_info_set,
std::ptr::null_mut(),
&SCP_BUS_CLASS_GUID,
0,
&mut device_interface_data
) != 0 {
let mut da = SP_DEVINFO_DATA::default();
da.cbSize = std::mem::size_of::<SP_DEVINFO_DATA>() as _;
let mut buffer_size = 0;
SetupDiGetDeviceInterfaceDetailW(
device_info_set,
&mut device_interface_data,
std::ptr::null_mut(),
0,
&mut buffer_size,
&mut da
);
let mut detail_data_buffer = vec![0; buffer_size as usize].into_boxed_slice();
for (d, &s) in detail_data_buffer.iter_mut().zip(8i32.to_ne_bytes().iter()) {
*d = s;
}
if SetupDiGetDeviceInterfaceDetailW(
device_info_set,
&mut device_interface_data,
detail_data_buffer.as_mut_ptr() as *mut _,
buffer_size,
&mut buffer_size,
&mut da
) != 0 {
let mut wide_str = vec![];
for chunk in detail_data_buffer[4..].chunks(2) {
wide_str.push(u16::from_ne_bytes([chunk[0], chunk[1]]));
}
SetupDiDestroyDeviceInfoList(device_info_set);
return Some(wide_str);
}
}
SetupDiDestroyDeviceInfoList(device_info_set);
None
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment