Created
October 1, 2020 09:42
-
-
Save MinusKelvin/a9db4a5fe9755a8c7f763ed36acfafef to your computer and use it in GitHub Desktop.
SCP Driver Interface rust version
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! 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