mirror of
https://github.com/marcriera/ddgo-pnp-controller.git
synced 2025-04-20 02:19:27 +02:00
Compare commits
12 commits
Author | SHA1 | Date | |
---|---|---|---|
|
5d70ae00d4 | ||
|
a6b99f2817 | ||
|
122ce1a82f | ||
|
bd2213f14f | ||
|
8aa9a02c21 | ||
|
8ed2d1300c | ||
|
7011359bb6 | ||
|
8b443ccb5e | ||
|
b2daa5d017 | ||
|
f317be0f15 | ||
|
1f84543091 | ||
|
c6013f2b74 |
17 changed files with 1237 additions and 430 deletions
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ddgo-pnp-controller"
|
name = "ddgo-pnp-controller"
|
||||||
version = "1.0.0"
|
version = "1.2.1"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.3.2"
|
bitflags = "1.3.2"
|
||||||
evdev = "0.12.1"
|
evdev = "0.12.2"
|
||||||
|
|
32
README.md
32
README.md
|
@ -16,19 +16,33 @@ This mod allows you to use your Densha de GO! Plug & Play as a USB controller fo
|
||||||
4. Plug the USB drive to the OTG adapter, plug the adapter to the Plug & Play and plug the data cable to the adapter and a power source.
|
4. Plug the USB drive to the OTG adapter, plug the adapter to the Plug & Play and plug the data cable to the adapter and a power source.
|
||||||
5. Turn on the Plug & Play. The door lamp will turn on to show the installation has begun. Once it has finished, the lamp will turn off. Turn off the unit and remove everything.
|
5. Turn on the Plug & Play. The door lamp will turn on to show the installation has begun. Once it has finished, the lamp will turn off. Turn off the unit and remove everything.
|
||||||
|
|
||||||
|
## Uninstallation
|
||||||
|
|
||||||
|
To completely remove the mod, follow these steps. **You need the backup of the original kernel created during installation**.
|
||||||
|
|
||||||
|
1. Prepare the USB flash drive by formatting it with a single FAT32 partition.
|
||||||
|
2. Download the latest release from the [Releases](https://github.com/MarcRiera/ddgo-pnp-controller/releases) section.
|
||||||
|
3. Extract the content to the root of the USB drive.
|
||||||
|
4. Create an empty file named `revert` on the root of the USB drive.
|
||||||
|
5. Put the original (backup) files `uImage` and `mali.ko` into a folder named `BACKUP` on the root of the USB drive.
|
||||||
|
6. Plug the USB drive to the OTG adapter, plug the adapter to the Plug & Play and plug the data cable to the adapter and a power source.
|
||||||
|
7. Turn on the Plug & Play. The door lamp will turn on to show the uninstallation has begun. Once it has finished, the lamp will turn off. Turn off the unit and remove everything.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Connect the Plug & Play to a PC or console using the data cable. Press one of the following button combinations to select the controller you want to emulate:
|
Connect the Plug & Play to a PC or console using the data cable. Press one of the following button combinations to select the controller you want to emulate:
|
||||||
|
|
||||||
| Controller | Button combination | Notes |
|
| Controller | Button combination | Notes |
|
||||||
|-----------------------------------------|-----------------------|--------------------------------------------------|
|
|-----------------------------------------|--------------------------|--------------------------------------------------|
|
||||||
| One handle controller (Nintendo Switch) | UP | SELECT+START=HOME, SELECT+LEFT=L, SELECT+RIGHT=R |
|
| One handle controller (Nintendo Switch) | UP | SELECT+START=HOME, SELECT+LEFT=L, SELECT+RIGHT=R |
|
||||||
| Two handle controller (PC) | RIGHT | D-Pad is mapped to SELECT+ABCD |
|
| Two handle controller (PC) | RIGHT | D-Pad is mapped to SELECT+ABCD |
|
||||||
| Two handle controller "Type 2" (PS2) | D | |
|
| Two handle controller (PS1) | DOWN + Power handle at 0 | Hold D to disable handles and enable D-Pad |
|
||||||
| Shinkansen controller (PS2) | B | Power notches are mapped to P2-P4-P7-P10-P13 |
|
| Two handle controller "Type 2" (PS2) | D | |
|
||||||
| Multi Train Controller (PS2) - P4/B7 | C + Power handle at 0 | SELECT+A=A2, SELECT+D=ATS, SELECT+D-Pad=Reverser |
|
| Shinkansen controller (PS2) | B | Power notches are mapped to P2-P4-P7-P10-P13 |
|
||||||
| Multi Train Controller (PS2) - P4/B2-B6 | C + Power handle at 1 | SELECT+A=A2, SELECT+D=ATS, SELECT+D-Pad=Reverser |
|
| Multi Train Controller (PS2) - P4/B7 | C + Power handle at 0 | SELECT+A=A2, SELECT+D=ATS, SELECT+D-Pad=Reverser |
|
||||||
| Multi Train Controller (PS2) - P5/B5 | C + Power handle at 2 | SELECT+A=A2, SELECT+D=ATS, SELECT+D-Pad=Reverser |
|
| Multi Train Controller (PS2) - P4/B2-B7 | C + Power handle at 1 | SELECT+A=A2, SELECT+D=ATS, SELECT+D-Pad=Reverser |
|
||||||
|
| Multi Train Controller (PS2) - P5/B5 | C + Power handle at 2 | SELECT+A=A2, SELECT+D=ATS, SELECT+D-Pad=Reverser |
|
||||||
|
| Multi Train Controller (PS2) - P5/B7 | C + Power handle at 3 | SELECT+A=A2, SELECT+D=ATS, SELECT+D-Pad=Reverser |
|
||||||
|
|
||||||
Hold the buttons until the controller vibrates to confirm selection. If no button is pressed, you can play with the Plug & Play as usual.
|
Hold the buttons until the controller vibrates to confirm selection. If no button is pressed, you can play with the Plug & Play as usual.
|
||||||
|
|
||||||
|
|
19
dist/hook_magic_36558b710a.sh
vendored
19
dist/hook_magic_36558b710a.sh
vendored
|
@ -33,6 +33,25 @@ if ! mount -o remount,rw /; then
|
||||||
error_exit
|
error_exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Create backup folder
|
||||||
|
mkdir -p "${USB_ROOT}/BACKUP"
|
||||||
|
|
||||||
|
# Revert changes flag detected, restore original files
|
||||||
|
if [ -f "${USB_ROOT}/revert" ]; then
|
||||||
|
if [ ! -f "${USB_ROOT}/BACKUP/uImage" ] || [ ! -f "${USB_ROOT}/BACKUP/mali.ko" ]; then
|
||||||
|
echo "Backup not found, cannot revert."
|
||||||
|
error_exit
|
||||||
|
fi
|
||||||
|
cp "${USB_ROOT}/BACKUP/uImage" /tmp/boot/uImage
|
||||||
|
cp "${USB_ROOT}/BACKUP/mali.ko" /lib/modules/3.4.113/extra/mali.ko
|
||||||
|
rm /etc/init.d/S40usbotg
|
||||||
|
rm /usr/bin/rndis-gadget.sh
|
||||||
|
rm /usr/bin/ddgo-pnp-controller
|
||||||
|
rm "${USB_ROOT}/revert"
|
||||||
|
poweroff
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
# Backup original kernel
|
# Backup original kernel
|
||||||
if [ ! -f "${USB_ROOT}/BACKUP/uImage" ] && [ ! -f "/usr/bin/input_relay" ] && [ ! -f "/usr/bin/ddgo-pnp-controller" ]; then
|
if [ ! -f "${USB_ROOT}/BACKUP/uImage" ] && [ ! -f "/usr/bin/input_relay" ] && [ ! -f "/usr/bin/ddgo-pnp-controller" ]; then
|
||||||
if ! cp /tmp/boot/uImage "${USB_ROOT}/BACKUP/"; then
|
if ! cp /tmp/boot/uImage "${USB_ROOT}/BACKUP/"; then
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
pub mod physical;
|
|
||||||
pub mod emulated;
|
pub mod emulated;
|
||||||
|
pub mod physical;
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
use std::thread;
|
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::process::Command;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::thread;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::controller::physical::ControllerState;
|
use crate::controller::physical::ControllerState;
|
||||||
|
|
||||||
mod dgoc44u;
|
mod dgoc44u;
|
||||||
mod tcpp20009;
|
mod slph00051;
|
||||||
mod tcpp20011;
|
|
||||||
mod sotp031201_p4b7;
|
|
||||||
mod sotp031201_p4b2b7;
|
mod sotp031201_p4b2b7;
|
||||||
|
mod sotp031201_p4b7;
|
||||||
mod sotp031201_p5b5;
|
mod sotp031201_p5b5;
|
||||||
mod sotp031201_p5b7;
|
mod sotp031201_p5b7;
|
||||||
mod vok00106;
|
mod tcpp20009;
|
||||||
|
mod tcpp20011;
|
||||||
mod zkns001;
|
mod zkns001;
|
||||||
|
|
||||||
const FFS_MOUNT: &str = "/tmp/ffs";
|
const FFS_MOUNT: &str = "/tmp/ffs";
|
||||||
|
@ -33,8 +33,8 @@ pub enum ControllerModel {
|
||||||
SOTP031201P4B2B7,
|
SOTP031201P4B2B7,
|
||||||
SOTP031201P5B5,
|
SOTP031201P5B5,
|
||||||
SOTP031201P5B7,
|
SOTP031201P5B7,
|
||||||
VOK00106,
|
|
||||||
ZKNS001,
|
ZKNS001,
|
||||||
|
SLPH00051,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DeviceDescriptor {
|
pub struct DeviceDescriptor {
|
||||||
|
@ -55,49 +55,76 @@ pub fn set_model(state: &ControllerState) -> Option<ControllerModel> {
|
||||||
if state.button_right {
|
if state.button_right {
|
||||||
model_name = "DGOC44-U";
|
model_name = "DGOC44-U";
|
||||||
model = ControllerModel::DGOC44U;
|
model = ControllerModel::DGOC44U;
|
||||||
descriptors = (&dgoc44u::DEVICE_DESCRIPTOR, &dgoc44u::DESCRIPTORS, &dgoc44u::STRINGS);
|
descriptors = (
|
||||||
}
|
&dgoc44u::DEVICE_DESCRIPTOR,
|
||||||
else if state.button_up {
|
&dgoc44u::DESCRIPTORS,
|
||||||
|
&dgoc44u::STRINGS,
|
||||||
|
);
|
||||||
|
} else if state.button_up {
|
||||||
model_name = "ZKNS-001";
|
model_name = "ZKNS-001";
|
||||||
model = ControllerModel::ZKNS001;
|
model = ControllerModel::ZKNS001;
|
||||||
descriptors = (&zkns001::DEVICE_DESCRIPTOR, &zkns001::DESCRIPTORS, &zkns001::STRINGS);
|
descriptors = (
|
||||||
}
|
&zkns001::DEVICE_DESCRIPTOR,
|
||||||
else if state.button_d {
|
&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_name = "TCPP-20009";
|
||||||
model = ControllerModel::TCPP20009;
|
model = ControllerModel::TCPP20009;
|
||||||
descriptors = (&tcpp20009::DEVICE_DESCRIPTOR, &tcpp20009::DESCRIPTORS, &tcpp20009::STRINGS);
|
descriptors = (
|
||||||
}
|
&tcpp20009::DEVICE_DESCRIPTOR,
|
||||||
else if state.button_b {
|
&tcpp20009::DESCRIPTORS,
|
||||||
|
&tcpp20009::STRINGS,
|
||||||
|
);
|
||||||
|
} else if state.button_b {
|
||||||
model_name = "TCPP-20011";
|
model_name = "TCPP-20011";
|
||||||
model = ControllerModel::TCPP20011;
|
model = ControllerModel::TCPP20011;
|
||||||
descriptors = (&tcpp20011::DEVICE_DESCRIPTOR, &tcpp20011::DESCRIPTORS, &tcpp20011::STRINGS);
|
descriptors = (
|
||||||
}
|
&tcpp20011::DEVICE_DESCRIPTOR,
|
||||||
else if state.button_c && state.power == 0 {
|
&tcpp20011::DESCRIPTORS,
|
||||||
|
&tcpp20011::STRINGS,
|
||||||
|
);
|
||||||
|
} else if state.button_c && state.power == 0 {
|
||||||
model_name = "SOTP-031201 (P4/B7 mode)";
|
model_name = "SOTP-031201 (P4/B7 mode)";
|
||||||
model = ControllerModel::SOTP031201P4B7;
|
model = ControllerModel::SOTP031201P4B7;
|
||||||
descriptors = (&sotp031201_p4b7::DEVICE_DESCRIPTOR, &sotp031201_p4b7::DESCRIPTORS, &sotp031201_p4b7::STRINGS);
|
descriptors = (
|
||||||
}
|
&sotp031201_p4b7::DEVICE_DESCRIPTOR,
|
||||||
else if state.button_c && state.power == 1 {
|
&sotp031201_p4b7::DESCRIPTORS,
|
||||||
|
&sotp031201_p4b7::STRINGS,
|
||||||
|
);
|
||||||
|
} else if state.button_c && state.power == 1 {
|
||||||
model_name = "SOTP-031201 (P4/B2-B7 mode)";
|
model_name = "SOTP-031201 (P4/B2-B7 mode)";
|
||||||
model = ControllerModel::SOTP031201P4B2B7;
|
model = ControllerModel::SOTP031201P4B2B7;
|
||||||
descriptors = (&sotp031201_p4b2b7::DEVICE_DESCRIPTOR, &sotp031201_p4b2b7::DESCRIPTORS, &sotp031201_p4b2b7::STRINGS);
|
descriptors = (
|
||||||
}
|
&sotp031201_p4b2b7::DEVICE_DESCRIPTOR,
|
||||||
else if state.button_c && state.power == 2 {
|
&sotp031201_p4b2b7::DESCRIPTORS,
|
||||||
|
&sotp031201_p4b2b7::STRINGS,
|
||||||
|
);
|
||||||
|
} else if state.button_c && state.power == 2 {
|
||||||
model_name = "SOTP-031201 (P5/B5 mode)";
|
model_name = "SOTP-031201 (P5/B5 mode)";
|
||||||
model = ControllerModel::SOTP031201P5B5;
|
model = ControllerModel::SOTP031201P5B5;
|
||||||
descriptors = (&sotp031201_p5b5::DEVICE_DESCRIPTOR, &sotp031201_p5b5::DESCRIPTORS, &sotp031201_p5b5::STRINGS);
|
descriptors = (
|
||||||
}
|
&sotp031201_p5b5::DEVICE_DESCRIPTOR,
|
||||||
/* else if state.button_c && state.power == 3 {
|
&sotp031201_p5b5::DESCRIPTORS,
|
||||||
|
&sotp031201_p5b5::STRINGS,
|
||||||
|
);
|
||||||
|
} else if state.button_c && state.power == 3 {
|
||||||
model_name = "SOTP-031201 (P5/B7 mode)";
|
model_name = "SOTP-031201 (P5/B7 mode)";
|
||||||
model = ControllerModel::SOTP031201P5B7;
|
model = ControllerModel::SOTP031201P5B7;
|
||||||
descriptors = (&sotp031201_p5b7::DEVICE_DESCRIPTOR, &sotp031201_p5b7::DESCRIPTORS, &sotp031201_p5b7::STRINGS);
|
descriptors = (
|
||||||
}
|
&sotp031201_p5b7::DEVICE_DESCRIPTOR,
|
||||||
else if state.button_a {
|
&sotp031201_p5b7::DESCRIPTORS,
|
||||||
model_name = "VOK-00106";
|
&sotp031201_p5b7::STRINGS,
|
||||||
model = ControllerModel::VOK00106;
|
);
|
||||||
descriptors = (&vok00106::DEVICE_DESCRIPTOR, &vok00106::DESCRIPTORS, &vok00106::STRINGS);
|
} else {
|
||||||
} */
|
|
||||||
else {
|
|
||||||
println!("ddgo-pnp-controller: No controller selected, starting RNDIS gadget.");
|
println!("ddgo-pnp-controller: No controller selected, starting RNDIS gadget.");
|
||||||
Command::new("rndis-gadget.sh").output().ok();
|
Command::new("rndis-gadget.sh").output().ok();
|
||||||
return None;
|
return None;
|
||||||
|
@ -130,12 +157,12 @@ pub fn set_state(state: &mut ControllerState, model: &ControllerModel) {
|
||||||
ControllerModel::SOTP031201P5B7 => {
|
ControllerModel::SOTP031201P5B7 => {
|
||||||
sotp031201_p5b7::update_gadget(state);
|
sotp031201_p5b7::update_gadget(state);
|
||||||
}
|
}
|
||||||
ControllerModel::VOK00106 => {
|
|
||||||
vok00106::update_gadget(state);
|
|
||||||
}
|
|
||||||
ControllerModel::ZKNS001 => {
|
ControllerModel::ZKNS001 => {
|
||||||
zkns001::update_gadget(state);
|
zkns001::update_gadget(state);
|
||||||
}
|
}
|
||||||
|
ControllerModel::SLPH00051 => {
|
||||||
|
slph00051::update_gadget(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +178,9 @@ pub fn handle_ctrl_transfer(model: ControllerModel, data: &[u8]) {
|
||||||
ControllerModel::ZKNS001 => {
|
ControllerModel::ZKNS001 => {
|
||||||
report = Some(&zkns001::HID_REPORT_DESCRIPTOR);
|
report = Some(&zkns001::HID_REPORT_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
|
ControllerModel::SLPH00051 => {
|
||||||
|
report = Some(&slph00051::HID_REPORT_DESCRIPTOR);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
report = None;
|
report = None;
|
||||||
}
|
}
|
||||||
|
@ -163,25 +193,38 @@ pub fn handle_ctrl_transfer(model: ControllerModel, data: &[u8]) {
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else if data[1] == 9 {
|
match model {
|
||||||
|
ControllerModel::SLPH00051 => {
|
||||||
|
slph00051::handle_ctrl_transfer(data);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_gadget(model: &ControllerModel, (device, descriptors, strings): (&DeviceDescriptor, &[u8], &[u8])) {
|
fn init_gadget(
|
||||||
|
model: &ControllerModel,
|
||||||
|
(device, descriptors, strings): (&DeviceDescriptor, &[u8], &[u8]),
|
||||||
|
) {
|
||||||
// Init g_ffs kernel module
|
// Init g_ffs kernel module
|
||||||
Command::new("modprobe").arg("g_ffs")
|
Command::new("modprobe")
|
||||||
.arg(String::from("bDeviceClass=")+&device.b_device_class.to_string())
|
.arg("g_ffs")
|
||||||
.arg(String::from("bDeviceSubClass=")+&device.b_device_sub_class.to_string())
|
.arg(String::from("bDeviceClass=") + &device.b_device_class.to_string())
|
||||||
.arg(String::from("idVendor=")+&device.id_vendor.to_string())
|
.arg(String::from("bDeviceSubClass=") + &device.b_device_sub_class.to_string())
|
||||||
.arg(String::from("idProduct=")+&device.id_product.to_string())
|
.arg(String::from("idVendor=") + &device.id_vendor.to_string())
|
||||||
.arg(String::from("bcdDevice=")+&device.bcd_device.to_string())
|
.arg(String::from("idProduct=") + &device.id_product.to_string())
|
||||||
.arg(String::from("iManufacturer=")+device.i_manufacturer)
|
.arg(String::from("bcdDevice=") + &device.bcd_device.to_string())
|
||||||
.arg(String::from("iProduct=")+device.i_product)
|
.arg(String::from("iManufacturer=") + device.i_manufacturer)
|
||||||
.arg(String::from("iSerialNumber=")+device.i_serial_number)
|
.arg(String::from("iProduct=") + device.i_product)
|
||||||
.output().ok();
|
.arg(String::from("iSerialNumber=") + device.i_serial_number)
|
||||||
Command::new("mkdir").args(["-p",&FFS_MOUNT]).output().ok();
|
.output()
|
||||||
Command::new("mount").args(["-t","functionfs","ffs",&FFS_MOUNT]).output().ok();
|
.ok();
|
||||||
|
Command::new("mkdir").args(["-p", &FFS_MOUNT]).output().ok();
|
||||||
|
Command::new("mount")
|
||||||
|
.args(["-t", "functionfs", "ffs", &FFS_MOUNT])
|
||||||
|
.output()
|
||||||
|
.ok();
|
||||||
|
|
||||||
let controller_model = model.clone();
|
let controller_model = model.clone();
|
||||||
|
|
||||||
|
@ -210,14 +253,46 @@ fn init_gadget(model: &ControllerModel, (device, descriptors, strings): (&Device
|
||||||
// Init Android Gadget for old 3.4 kernel
|
// Init Android Gadget for old 3.4 kernel
|
||||||
let gadget = Path::new(&ANDROID_GADGET);
|
let gadget = Path::new(&ANDROID_GADGET);
|
||||||
if gadget.is_dir() {
|
if gadget.is_dir() {
|
||||||
fs::write(gadget.join(Path::new("bDeviceClass")), &device.b_device_class.to_string()).ok();
|
fs::write(
|
||||||
fs::write(gadget.join(Path::new("bDeviceSubClass")), &device.b_device_sub_class.to_string()).ok();
|
gadget.join(Path::new("bDeviceClass")),
|
||||||
fs::write(gadget.join(Path::new("idVendor")), format!("{:x}", &device.id_vendor)).ok();
|
&device.b_device_class.to_string(),
|
||||||
fs::write(gadget.join(Path::new("idProduct")), format!("{:x}", &device.id_product)).ok();
|
)
|
||||||
fs::write(gadget.join(Path::new("bcdDevice")), format!("{:x}", &device.bcd_device)).ok();
|
.ok();
|
||||||
fs::write(gadget.join(Path::new("iManufacturer")), &device.i_manufacturer.to_string()).ok();
|
fs::write(
|
||||||
fs::write(gadget.join(Path::new("iProduct")), &device.i_product.to_string()).ok();
|
gadget.join(Path::new("bDeviceSubClass")),
|
||||||
fs::write(gadget.join(Path::new("iSerial")), &device.i_serial_number.to_string()).ok();
|
&device.b_device_sub_class.to_string(),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
fs::write(
|
||||||
|
gadget.join(Path::new("idVendor")),
|
||||||
|
format!("{:x}", &device.id_vendor),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
fs::write(
|
||||||
|
gadget.join(Path::new("idProduct")),
|
||||||
|
format!("{:x}", &device.id_product),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
fs::write(
|
||||||
|
gadget.join(Path::new("bcdDevice")),
|
||||||
|
format!("{:x}", &device.bcd_device),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
fs::write(
|
||||||
|
gadget.join(Path::new("iManufacturer")),
|
||||||
|
&device.i_manufacturer.to_string(),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
fs::write(
|
||||||
|
gadget.join(Path::new("iProduct")),
|
||||||
|
&device.i_product.to_string(),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
fs::write(
|
||||||
|
gadget.join(Path::new("iSerial")),
|
||||||
|
&device.i_serial_number.to_string(),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
fs::write(gadget.join(Path::new("functions")), "ffs").ok();
|
fs::write(gadget.join(Path::new("functions")), "ffs").ok();
|
||||||
fs::write(gadget.join(Path::new("f_ffs/aliases")), "ffs").ok();
|
fs::write(gadget.join(Path::new("f_ffs/aliases")), "ffs").ok();
|
||||||
fs::write(gadget.join(Path::new("enable")), "1").ok();
|
fs::write(gadget.join(Path::new("enable")), "1").ok();
|
||||||
|
|
|
@ -1,53 +1,65 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Write};
|
use std::io::Write;
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 66] = [0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 66] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3F, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x05,
|
0x3F, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x05, 0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3F, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08,
|
||||||
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3F, 0x00,
|
0x00, 0x05,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 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 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: 0x0AE4, id_product: 0x0003, bcd_device: 0x0102, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play", i_serial_number: "DGOC-44U"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0x0,
|
||||||
|
b_device_sub_class: 0x0,
|
||||||
|
id_vendor: 0x0AE4,
|
||||||
|
id_product: 0x0003,
|
||||||
|
bcd_device: 0x0102,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play",
|
||||||
|
i_serial_number: "DGOC-44U",
|
||||||
|
};
|
||||||
|
|
||||||
pub const HID_REPORT_DESCRIPTOR: [u8; 63] = [
|
pub const HID_REPORT_DESCRIPTOR: [u8; 63] = [
|
||||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||||
0x09, 0x04, // Usage (Joystick)
|
0x09, 0x04, // Usage (Joystick)
|
||||||
0xA1, 0x01, // Collection (Application)
|
0xA1, 0x01, // Collection (Application)
|
||||||
0x09, 0x01, // Usage (Pointer)
|
0x09, 0x01, // Usage (Pointer)
|
||||||
0xA1, 0x00, // Collection (Physical)
|
0xA1, 0x00, // Collection (Physical)
|
||||||
0x09, 0x30, // Usage (X)
|
0x09, 0x30, // Usage (X)
|
||||||
0x09, 0x31, // Usage (Y)
|
0x09, 0x31, // Usage (Y)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||||
0x75, 0x08, // Report Size (8)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x95, 0x02, // Report Count (2)
|
0x95, 0x02, // Report Count (2)
|
||||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0xC0, // End Collection
|
0xC0, // End Collection
|
||||||
0x75, 0x08, // Report Size (8)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x95, 0x01, // Report Count (1)
|
0x95, 0x01, // Report Count (1)
|
||||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x05, 0x09, // Usage Page (Button)
|
0x05, 0x09, // Usage Page (Button)
|
||||||
0x19, 0x01, // Usage Minimum (0x01)
|
0x19, 0x01, // Usage Minimum (0x01)
|
||||||
0x29, 0x06, // Usage Maximum (0x06)
|
0x29, 0x06, // Usage Maximum (0x06)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
0x25, 0x01, // Logical Maximum (1)
|
0x25, 0x01, // Logical Maximum (1)
|
||||||
0x35, 0x00, // Physical Minimum (0)
|
0x35, 0x00, // Physical Minimum (0)
|
||||||
0x45, 0x01, // Physical Maximum (1)
|
0x45, 0x01, // Physical Maximum (1)
|
||||||
0x75, 0x01, // Report Size (1)
|
0x75, 0x01, // Report Size (1)
|
||||||
0x95, 0x06, // Report Count (6)
|
0x95, 0x06, // Report Count (6)
|
||||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x95, 0x02, // Report Count (2)
|
0x95, 0x02, // Report Count (2)
|
||||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x75, 0x08, // Report Size (8)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x95, 0x02, // Report Count (2)
|
0x95, 0x02, // Report Count (2)
|
||||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0xC0 // End Collection
|
0xC0, // End Collection
|
||||||
];
|
];
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00];
|
const POWER_NOTCHES: [u8; 6] = [0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00];
|
||||||
|
@ -76,16 +88,36 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
|
|
||||||
// Calculate data for buttons
|
// Calculate data for buttons
|
||||||
let mut buttons = Buttons::NONE;
|
let mut buttons = Buttons::NONE;
|
||||||
if state.button_a { buttons.insert(Buttons::A) }
|
if state.button_a {
|
||||||
if state.button_b { buttons.insert(Buttons::B) }
|
buttons.insert(Buttons::A)
|
||||||
if state.button_c { buttons.insert(Buttons::C) }
|
}
|
||||||
if state.button_d { buttons.insert(Buttons::D) }
|
if state.button_b {
|
||||||
if state.button_select { buttons.insert(Buttons::SELECT) }
|
buttons.insert(Buttons::B)
|
||||||
if state.button_start { buttons.insert(Buttons::START) }
|
}
|
||||||
if state.button_up { buttons.insert(Buttons::UP) }
|
if state.button_c {
|
||||||
if state.button_down { buttons.insert(Buttons::DOWN) }
|
buttons.insert(Buttons::C)
|
||||||
if state.button_left { buttons.insert(Buttons::LEFT) }
|
}
|
||||||
if state.button_right { buttons.insert(Buttons::RIGHT) }
|
if state.button_d {
|
||||||
|
buttons.insert(Buttons::D)
|
||||||
|
}
|
||||||
|
if state.button_select {
|
||||||
|
buttons.insert(Buttons::SELECT)
|
||||||
|
}
|
||||||
|
if state.button_start {
|
||||||
|
buttons.insert(Buttons::START)
|
||||||
|
}
|
||||||
|
if state.button_up {
|
||||||
|
buttons.insert(Buttons::UP)
|
||||||
|
}
|
||||||
|
if state.button_down {
|
||||||
|
buttons.insert(Buttons::DOWN)
|
||||||
|
}
|
||||||
|
if state.button_left {
|
||||||
|
buttons.insert(Buttons::LEFT)
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
buttons.insert(Buttons::RIGHT)
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to gadget
|
// Assemble data and send it to gadget
|
||||||
let data = [brake, power, 0, buttons.bits, 0, 0];
|
let data = [brake, power, 0, buttons.bits, 0, 0];
|
||||||
|
|
377
src/controller/emulated/slph00051.rs
Normal file
377
src/controller/emulated/slph00051.rs
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT0, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
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
|
||||||
|
];
|
||||||
|
|
||||||
|
const F2_REPORT: [u8; 64] = [
|
||||||
|
0xF2, 0xFF, 0xFF, 0x0, 0x0, 0x6, 0xF5, 0x48, 0xE2, 0x49, 0x0, 0x3, 0x50, 0x81, 0xD8, 0x1, 0x8A,
|
||||||
|
0x13, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x0,
|
||||||
|
0x0, 0x4, 0x0, 0x1, 0x2, 0x7, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
|
||||||
|
const F5_REPORT: [u8; 64] = [
|
||||||
|
0x1, 0x0, 0x0, 0x23, 0x6, 0x7C, 0xB9, 0xB, 0xE2, 0x49, 0x0, 0x3, 0x50, 0x81, 0xD8, 0x1, 0x8A,
|
||||||
|
0x13, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x0,
|
||||||
|
0x0, 0x4, 0x0, 0x1, 0x2, 0x7, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
];
|
||||||
|
|
||||||
|
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::NONE;
|
||||||
|
let mut buttons2 = Buttons2::NONE;
|
||||||
|
|
||||||
|
// If D is pressed, D-pad mode is active
|
||||||
|
if !state.button_d {
|
||||||
|
// Calculate data for handles
|
||||||
|
buttons1.insert(Buttons1::UP | Buttons1::DOWN);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// D-pad mode
|
||||||
|
if state.button_up {
|
||||||
|
buttons1.insert(Buttons1::UP)
|
||||||
|
}
|
||||||
|
if state.button_down {
|
||||||
|
buttons1.insert(Buttons1::DOWN)
|
||||||
|
}
|
||||||
|
if state.button_left {
|
||||||
|
buttons1.insert(Buttons1::LEFT)
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
buttons1.insert(Buttons1::RIGHT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
let btn_up = if buttons1.contains(Buttons1::UP) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_right = if buttons1.contains(Buttons1::RIGHT) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_down = if buttons1.contains(Buttons1::DOWN) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_left = if buttons1.contains(Buttons1::LEFT) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_l2 = if buttons2.contains(Buttons2::L2) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_r2 = if buttons2.contains(Buttons2::R2) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_l1 = if buttons2.contains(Buttons2::L1) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_r1 = if buttons2.contains(Buttons2::R1) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_triangle = if buttons2.contains(Buttons2::TRIANGLE) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_circle = if buttons2.contains(Buttons2::CIRCLE) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_cross = if buttons2.contains(Buttons2::CROSS) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
let btn_square = if buttons2.contains(Buttons2::SQUARE) {
|
||||||
|
0xFF
|
||||||
|
} else {
|
||||||
|
0x0
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
btn_up,
|
||||||
|
btn_right,
|
||||||
|
btn_down,
|
||||||
|
btn_left,
|
||||||
|
btn_l2,
|
||||||
|
btn_r2,
|
||||||
|
btn_l1,
|
||||||
|
btn_r1,
|
||||||
|
btn_triangle,
|
||||||
|
btn_circle,
|
||||||
|
btn_cross,
|
||||||
|
btn_square,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_ctrl_transfer(data: &[u8]) {
|
||||||
|
if data[1] == 1 && data[2] == 0xF2 {
|
||||||
|
// Init 1
|
||||||
|
if let Ok(mut file) = File::create(ENDPOINT0) {
|
||||||
|
file.write(&F2_REPORT).unwrap();
|
||||||
|
}
|
||||||
|
} else if data[1] == 1 && data[2] == 0xF5 {
|
||||||
|
// Init 2
|
||||||
|
if let Ok(mut file) = File::create(ENDPOINT0) {
|
||||||
|
file.write(&F5_REPORT).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,29 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 48] = [0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 48] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14];
|
];
|
||||||
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
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: 0x00, b_device_sub_class: 0x0, id_vendor: 0x0AE4, id_product: 0x0101, bcd_device: 0x0400, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (MTC P4/B2-B7 mode)", i_serial_number: "SOTP-031201"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0x00,
|
||||||
|
b_device_sub_class: 0x0,
|
||||||
|
id_vendor: 0x0AE4,
|
||||||
|
id_product: 0x0101,
|
||||||
|
bcd_device: 0x0400,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play (MTC P4/B2-B7 mode)",
|
||||||
|
i_serial_number: "SOTP-031201",
|
||||||
|
};
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0C];
|
const POWER_NOTCHES: [u8; 6] = [0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0C];
|
||||||
const BRAKE_NOTCHES: [u8; 10] = [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x02, 0x01];
|
const BRAKE_NOTCHES: [u8; 10] = [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x02, 0x01];
|
||||||
|
@ -68,16 +79,36 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
state.reverser = 0x40;
|
state.reverser = 0x40;
|
||||||
state.combo = true;
|
state.combo = true;
|
||||||
}
|
}
|
||||||
if !state.combo && state.button_a { buttons1.insert(Buttons1::A) }
|
if !state.combo && state.button_a {
|
||||||
if state.button_b { buttons1.insert(Buttons1::B) }
|
buttons1.insert(Buttons1::A)
|
||||||
if !state.combo && state.button_c { buttons1.insert(Buttons1::C) }
|
}
|
||||||
if state.button_d { buttons1.insert(Buttons1::D) }
|
if state.button_b {
|
||||||
if !state.combo && state.button_up { buttons2.insert(Buttons2::UP) }
|
buttons1.insert(Buttons1::B)
|
||||||
if !state.combo && state.button_down { buttons2.insert(Buttons2::DOWN) }
|
}
|
||||||
if !state.combo && state.button_left { buttons2.insert(Buttons2::LEFT) }
|
if state.button_c {
|
||||||
if state.button_right { buttons2.insert(Buttons2::RIGHT) }
|
buttons1.insert(Buttons1::C)
|
||||||
if state.button_start { buttons2.insert(Buttons2::START) }
|
}
|
||||||
if !state.combo && state.button_select_hold { buttons2.insert(Buttons2::SELECT) }
|
if !state.combo && state.button_d {
|
||||||
|
buttons1.insert(Buttons1::D)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_up {
|
||||||
|
buttons2.insert(Buttons2::UP)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_down {
|
||||||
|
buttons2.insert(Buttons2::DOWN)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_left {
|
||||||
|
buttons2.insert(Buttons2::LEFT)
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
buttons2.insert(Buttons2::RIGHT)
|
||||||
|
}
|
||||||
|
if state.button_start {
|
||||||
|
buttons2.insert(Buttons2::START)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_select_hold {
|
||||||
|
buttons2.insert(Buttons2::SELECT)
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to endpoint
|
// Assemble data and send it to endpoint
|
||||||
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 48] = [0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 48] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14];
|
];
|
||||||
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
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: 0x00, b_device_sub_class: 0x0, id_vendor: 0x0AE4, id_product: 0x0101, bcd_device: 0x0102, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (MTC P4/B7 mode)", i_serial_number: "SOTP-031201"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0x00,
|
||||||
|
b_device_sub_class: 0x0,
|
||||||
|
id_vendor: 0x0AE4,
|
||||||
|
id_product: 0x0101,
|
||||||
|
bcd_device: 0x0300,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play (MTC P4/B7 mode)",
|
||||||
|
i_serial_number: "SOTP-031201",
|
||||||
|
};
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0D];
|
const POWER_NOTCHES: [u8; 6] = [0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0D];
|
||||||
const BRAKE_NOTCHES: [u8; 10] = [0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01];
|
const BRAKE_NOTCHES: [u8; 10] = [0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01];
|
||||||
|
@ -68,16 +79,36 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
state.reverser = 0x40;
|
state.reverser = 0x40;
|
||||||
state.combo = true;
|
state.combo = true;
|
||||||
}
|
}
|
||||||
if !state.combo && state.button_a { buttons1.insert(Buttons1::A) }
|
if !state.combo && state.button_a {
|
||||||
if state.button_b { buttons1.insert(Buttons1::B) }
|
buttons1.insert(Buttons1::A)
|
||||||
if !state.combo && state.button_c { buttons1.insert(Buttons1::C) }
|
}
|
||||||
if state.button_d { buttons1.insert(Buttons1::D) }
|
if state.button_b {
|
||||||
if !state.combo && state.button_up { buttons2.insert(Buttons2::UP) }
|
buttons1.insert(Buttons1::B)
|
||||||
if !state.combo && state.button_down { buttons2.insert(Buttons2::DOWN) }
|
}
|
||||||
if !state.combo && state.button_left { buttons2.insert(Buttons2::LEFT) }
|
if state.button_c {
|
||||||
if state.button_right { buttons2.insert(Buttons2::RIGHT) }
|
buttons1.insert(Buttons1::C)
|
||||||
if state.button_start { buttons2.insert(Buttons2::START) }
|
}
|
||||||
if !state.combo && state.button_select_hold { buttons2.insert(Buttons2::SELECT) }
|
if !state.combo && state.button_d {
|
||||||
|
buttons1.insert(Buttons1::D)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_up {
|
||||||
|
buttons2.insert(Buttons2::UP)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_down {
|
||||||
|
buttons2.insert(Buttons2::DOWN)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_left {
|
||||||
|
buttons2.insert(Buttons2::LEFT)
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
buttons2.insert(Buttons2::RIGHT)
|
||||||
|
}
|
||||||
|
if state.button_start {
|
||||||
|
buttons2.insert(Buttons2::START)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_select_hold {
|
||||||
|
buttons2.insert(Buttons2::SELECT)
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to endpoint
|
// Assemble data and send it to endpoint
|
||||||
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 48] = [0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 48] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14];
|
];
|
||||||
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
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: 0x00, b_device_sub_class: 0x0, id_vendor: 0x1C06, id_product: 0x77A7, bcd_device: 0x0102, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (MTC P5/B5 mode)", i_serial_number: "SOTP-031201"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0x00,
|
||||||
|
b_device_sub_class: 0x0,
|
||||||
|
id_vendor: 0x1C06,
|
||||||
|
id_product: 0x77A7,
|
||||||
|
bcd_device: 0x0202,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play (MTC P5/B5 mode)",
|
||||||
|
i_serial_number: "SOTP-031201",
|
||||||
|
};
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C];
|
const POWER_NOTCHES: [u8; 6] = [0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C];
|
||||||
const BRAKE_NOTCHES: [u8; 10] = [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01];
|
const BRAKE_NOTCHES: [u8; 10] = [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01];
|
||||||
|
@ -68,16 +79,36 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
state.reverser = 0x10;
|
state.reverser = 0x10;
|
||||||
state.combo = true;
|
state.combo = true;
|
||||||
}
|
}
|
||||||
if !state.combo && state.button_a { buttons1.insert(Buttons1::A) }
|
if !state.combo && state.button_a {
|
||||||
if state.button_b { buttons1.insert(Buttons1::B) }
|
buttons1.insert(Buttons1::A)
|
||||||
if !state.combo && state.button_c { buttons1.insert(Buttons1::C) }
|
}
|
||||||
if state.button_d { buttons1.insert(Buttons1::D) }
|
if state.button_b {
|
||||||
if !state.combo && state.button_up { buttons2.insert(Buttons2::UP) }
|
buttons1.insert(Buttons1::B)
|
||||||
if !state.combo && state.button_down { buttons2.insert(Buttons2::DOWN) }
|
}
|
||||||
if !state.combo && state.button_left { buttons2.insert(Buttons2::LEFT) }
|
if state.button_c {
|
||||||
if state.button_right { buttons2.insert(Buttons2::RIGHT) }
|
buttons1.insert(Buttons1::C)
|
||||||
if state.button_start { buttons2.insert(Buttons2::START) }
|
}
|
||||||
if !state.combo && state.button_select_hold { buttons2.insert(Buttons2::SELECT) }
|
if !state.combo && state.button_d {
|
||||||
|
buttons1.insert(Buttons1::D)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_up {
|
||||||
|
buttons2.insert(Buttons2::UP)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_down {
|
||||||
|
buttons2.insert(Buttons2::DOWN)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_left {
|
||||||
|
buttons2.insert(Buttons2::LEFT)
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
buttons2.insert(Buttons2::RIGHT)
|
||||||
|
}
|
||||||
|
if state.button_start {
|
||||||
|
buttons2.insert(Buttons2::START)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_select_hold {
|
||||||
|
buttons2.insert(Buttons2::SELECT)
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to endpoint
|
// Assemble data and send it to endpoint
|
||||||
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 48] = [0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 48] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14];
|
];
|
||||||
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
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: 0x00, b_device_sub_class: 0x0, id_vendor: 0x0AE4, id_product: 0x0101, bcd_device: 0x0102, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (MTC P5/B7 mode)", i_serial_number: "SOTP-031201"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0x00,
|
||||||
|
b_device_sub_class: 0x0,
|
||||||
|
id_vendor: 0x0AE4,
|
||||||
|
id_product: 0x0101,
|
||||||
|
bcd_device: 0x0800,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play (MTC P5/B7 mode)",
|
||||||
|
i_serial_number: "SOTP-031201",
|
||||||
|
};
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E];
|
const POWER_NOTCHES: [u8; 6] = [0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E];
|
||||||
const BRAKE_NOTCHES: [u8; 10] = [0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01];
|
const BRAKE_NOTCHES: [u8; 10] = [0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01];
|
||||||
|
@ -68,16 +79,36 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
state.reverser = 0x40;
|
state.reverser = 0x40;
|
||||||
state.combo = true;
|
state.combo = true;
|
||||||
}
|
}
|
||||||
if !state.combo && state.button_a { buttons1.insert(Buttons1::A) }
|
if !state.combo && state.button_a {
|
||||||
if state.button_b { buttons1.insert(Buttons1::B) }
|
buttons1.insert(Buttons1::A)
|
||||||
if !state.combo && state.button_c { buttons1.insert(Buttons1::C) }
|
}
|
||||||
if state.button_d { buttons1.insert(Buttons1::D) }
|
if state.button_b {
|
||||||
if !state.combo && state.button_up { buttons2.insert(Buttons2::UP) }
|
buttons1.insert(Buttons1::B)
|
||||||
if !state.combo && state.button_down { buttons2.insert(Buttons2::DOWN) }
|
}
|
||||||
if !state.combo && state.button_left { buttons2.insert(Buttons2::LEFT) }
|
if state.button_c {
|
||||||
if state.button_right { buttons2.insert(Buttons2::RIGHT) }
|
buttons1.insert(Buttons1::C)
|
||||||
if state.button_start { buttons2.insert(Buttons2::START) }
|
}
|
||||||
if !state.combo && state.button_select_hold { buttons2.insert(Buttons2::SELECT) }
|
if !state.combo && state.button_d {
|
||||||
|
buttons1.insert(Buttons1::D)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_up {
|
||||||
|
buttons2.insert(Buttons2::UP)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_down {
|
||||||
|
buttons2.insert(Buttons2::DOWN)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_left {
|
||||||
|
buttons2.insert(Buttons2::LEFT)
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
buttons2.insert(Buttons2::RIGHT)
|
||||||
|
}
|
||||||
|
if state.button_start {
|
||||||
|
buttons2.insert(Buttons2::START)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_select_hold {
|
||||||
|
buttons2.insert(Buttons2::SELECT)
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to endpoint
|
// Assemble data and send it to endpoint
|
||||||
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
let data = [0x1, state.reverser + handle, buttons1.bits, buttons2.bits];
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 48] = [0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 48] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14];
|
];
|
||||||
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
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: 0xFF, b_device_sub_class: 0x4, id_vendor: 0x0AE4, id_product: 0x0004, bcd_device: 0x0102, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (Type 2 mode)", i_serial_number: "TCPP20010"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0xFF,
|
||||||
|
b_device_sub_class: 0x4,
|
||||||
|
id_vendor: 0x0AE4,
|
||||||
|
id_product: 0x0004,
|
||||||
|
bcd_device: 0x0102,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play (Type 2 mode)",
|
||||||
|
i_serial_number: "TCPP20010",
|
||||||
|
};
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00];
|
const POWER_NOTCHES: [u8; 6] = [0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00];
|
||||||
const BRAKE_NOTCHES: [u8; 10] = [0x79, 0x8A, 0x94, 0x9A, 0xA2, 0xA8, 0xAF, 0xB2, 0xB5, 0xB9];
|
const BRAKE_NOTCHES: [u8; 10] = [0x79, 0x8A, 0x94, 0x9A, 0xA2, 0xA8, 0xAF, 0xB2, 0xB5, 0xB9];
|
||||||
|
@ -36,23 +47,51 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
|
|
||||||
// Calculate data for buttons
|
// Calculate data for buttons
|
||||||
let mut buttons = Buttons::NONE;
|
let mut buttons = Buttons::NONE;
|
||||||
if state.button_a { buttons.insert(Buttons::A) }
|
if state.button_a {
|
||||||
if state.button_b { buttons.insert(Buttons::B) }
|
buttons.insert(Buttons::A)
|
||||||
if state.button_c { buttons.insert(Buttons::C) }
|
}
|
||||||
if state.button_d { buttons.insert(Buttons::D) }
|
if state.button_b {
|
||||||
if state.button_select { buttons.insert(Buttons::SELECT) }
|
buttons.insert(Buttons::B)
|
||||||
if state.button_start { buttons.insert(Buttons::START) }
|
}
|
||||||
|
if state.button_c {
|
||||||
|
buttons.insert(Buttons::C)
|
||||||
|
}
|
||||||
|
if state.button_d {
|
||||||
|
buttons.insert(Buttons::D)
|
||||||
|
}
|
||||||
|
if state.button_select {
|
||||||
|
buttons.insert(Buttons::SELECT)
|
||||||
|
}
|
||||||
|
if state.button_start {
|
||||||
|
buttons.insert(Buttons::START)
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate data for D-pad
|
// Calculate data for D-pad
|
||||||
let mut dpad: u8 = 0x8;
|
let mut dpad: u8 = 0x8;
|
||||||
if state.button_up { dpad = 0x0 }
|
if state.button_up {
|
||||||
if state.button_down { dpad = 0x4 }
|
dpad = 0x0
|
||||||
if state.button_left { dpad = 0x6 }
|
}
|
||||||
if state.button_right { dpad = 0x2 }
|
if state.button_down {
|
||||||
if state.button_up & state.button_left { dpad = 0x7 }
|
dpad = 0x4
|
||||||
if state.button_up & state.button_right { dpad = 0x1 }
|
}
|
||||||
if state.button_down & state.button_left { dpad = 0x5 }
|
if state.button_left {
|
||||||
if state.button_down & state.button_right { dpad = 0x3 }
|
dpad = 0x6
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
dpad = 0x2
|
||||||
|
}
|
||||||
|
if state.button_up & state.button_left {
|
||||||
|
dpad = 0x7
|
||||||
|
}
|
||||||
|
if state.button_up & state.button_right {
|
||||||
|
dpad = 0x1
|
||||||
|
}
|
||||||
|
if state.button_down & state.button_left {
|
||||||
|
dpad = 0x5
|
||||||
|
}
|
||||||
|
if state.button_down & state.button_right {
|
||||||
|
dpad = 0x3
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to endpoint
|
// Assemble data and send it to endpoint
|
||||||
let data = [0x1, brake, power, 0xFF, dpad, buttons.bits];
|
let data = [0x1, brake, power, 0xFF, dpad, buttons.bits];
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 48] = [0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 48] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14,
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x14];
|
];
|
||||||
pub const STRINGS: [u8; 16] = [0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
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: 0xFF, b_device_sub_class: 0x5, id_vendor: 0x0AE4, id_product: 0x0005, bcd_device: 0x0102, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (Shinkansen mode)", i_serial_number: "TCPP20011"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0xFF,
|
||||||
|
b_device_sub_class: 0x5,
|
||||||
|
id_vendor: 0x0AE4,
|
||||||
|
id_product: 0x0005,
|
||||||
|
bcd_device: 0x0102,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play (Shinkansen mode)",
|
||||||
|
i_serial_number: "TCPP20011",
|
||||||
|
};
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x12, 0x36, 0x5A, 0x90, 0xC6, 0xFB];
|
const POWER_NOTCHES: [u8; 6] = [0x12, 0x36, 0x5A, 0x90, 0xC6, 0xFB];
|
||||||
const BRAKE_NOTCHES: [u8; 10] = [0x1C, 0x38, 0x54, 0x70, 0x8B, 0xA7, 0xC3, 0xDF, 0xDF, 0xFB];
|
const BRAKE_NOTCHES: [u8; 10] = [0x1C, 0x38, 0x54, 0x70, 0x8B, 0xA7, 0xC3, 0xDF, 0xDF, 0xFB];
|
||||||
|
@ -36,23 +47,51 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
|
|
||||||
// Calculate data for buttons
|
// Calculate data for buttons
|
||||||
let mut buttons = Buttons::NONE;
|
let mut buttons = Buttons::NONE;
|
||||||
if state.button_a { buttons.insert(Buttons::A) }
|
if state.button_a {
|
||||||
if state.button_b { buttons.insert(Buttons::B) }
|
buttons.insert(Buttons::A)
|
||||||
if state.button_c { buttons.insert(Buttons::C) }
|
}
|
||||||
if state.button_d { buttons.insert(Buttons::D) }
|
if state.button_b {
|
||||||
if state.button_select { buttons.insert(Buttons::SELECT) }
|
buttons.insert(Buttons::B)
|
||||||
if state.button_start { buttons.insert(Buttons::START) }
|
}
|
||||||
|
if state.button_c {
|
||||||
|
buttons.insert(Buttons::C)
|
||||||
|
}
|
||||||
|
if state.button_d {
|
||||||
|
buttons.insert(Buttons::D)
|
||||||
|
}
|
||||||
|
if state.button_select {
|
||||||
|
buttons.insert(Buttons::SELECT)
|
||||||
|
}
|
||||||
|
if state.button_start {
|
||||||
|
buttons.insert(Buttons::START)
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate data for D-pad
|
// Calculate data for D-pad
|
||||||
let mut dpad: u8 = 0x8;
|
let mut dpad: u8 = 0x8;
|
||||||
if state.button_up { dpad = 0x0 }
|
if state.button_up {
|
||||||
if state.button_down { dpad = 0x4 }
|
dpad = 0x0
|
||||||
if state.button_left { dpad = 0x6 }
|
}
|
||||||
if state.button_right { dpad = 0x2 }
|
if state.button_down {
|
||||||
if state.button_up & state.button_left { dpad = 0x7 }
|
dpad = 0x4
|
||||||
if state.button_up & state.button_right { dpad = 0x1 }
|
}
|
||||||
if state.button_down & state.button_left { dpad = 0x5 }
|
if state.button_left {
|
||||||
if state.button_down & state.button_right { dpad = 0x3 }
|
dpad = 0x6
|
||||||
|
}
|
||||||
|
if state.button_right {
|
||||||
|
dpad = 0x2
|
||||||
|
}
|
||||||
|
if state.button_up & state.button_left {
|
||||||
|
dpad = 0x7
|
||||||
|
}
|
||||||
|
if state.button_up & state.button_right {
|
||||||
|
dpad = 0x1
|
||||||
|
}
|
||||||
|
if state.button_down & state.button_left {
|
||||||
|
dpad = 0x5
|
||||||
|
}
|
||||||
|
if state.button_down & state.button_right {
|
||||||
|
dpad = 0x3
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to endpoint
|
// Assemble data and send it to endpoint
|
||||||
let data = [brake, power, 0xFF, dpad, buttons.bits, 0x0];
|
let data = [brake, power, 0xFF, dpad, buttons.bits, 0x0];
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Write};
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 76] = [0x01, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|
||||||
0x09, 0x04, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00,
|
|
||||||
0x07, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00,
|
|
||||||
0x07, 0x05, 0x02, 0x02, 0x20, 0x00, 0x00,
|
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x01,
|
|
||||||
0x09, 0x04, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x00,
|
|
||||||
0x07, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00,
|
|
||||||
0x07, 0x05, 0x02, 0x02, 0x20, 0x00, 0x00,
|
|
||||||
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x01];
|
|
||||||
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: 0x02, b_device_sub_class: 0x0, id_vendor: 0x067B, id_product: 0x2303, bcd_device: 0x0102, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (Master Controller II mode)", i_serial_number: "VOK-00106"};
|
|
||||||
|
|
||||||
const POWER_NOTCHES: [&str; 6] = ["TSA50", "TSA55", "TSA65", "TSA75", "TSA85", "TSA95"];
|
|
||||||
const BRAKE_NOTCHES: [&str; 10] = ["TSA50", "TSA45", "TSA35", "TSA25", "TSA15", "TSA05", "TSE99", "TSB40", "TSB30", "TSB20"];
|
|
||||||
const BUTTON_A: [&str; 2] = ["TSX00", "TSX99"];
|
|
||||||
const BUTTON_B: [&str; 2] = ["TSY00", "TSY99"];
|
|
||||||
const BUTTON_C: [&str; 2] = ["TSZ00", "TSZ99"];
|
|
||||||
const BUTTON_S: [&str; 2] = ["TSK00", "TSK99"];
|
|
||||||
|
|
||||||
|
|
||||||
pub fn update_gadget(state: &mut ControllerState) {
|
|
||||||
// Calculate data for handles
|
|
||||||
let mut handle = POWER_NOTCHES[state.power as usize];
|
|
||||||
if state.brake > 0 {
|
|
||||||
handle = BRAKE_NOTCHES[state.brake as usize];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate data for buttons
|
|
||||||
let button_a = if state.button_a {BUTTON_A[1]} else {BUTTON_A[0]};
|
|
||||||
let button_b = if state.button_b {BUTTON_B[1]} else {BUTTON_B[0]};
|
|
||||||
let button_c = if state.button_c {BUTTON_C[1]} else {BUTTON_C[0]};
|
|
||||||
let button_s = if state.button_d {BUTTON_S[1]} else {BUTTON_S[0]};
|
|
||||||
|
|
||||||
if let Ok(mut file) = File::create(ENDPOINT1) {
|
|
||||||
file.write(handle.as_bytes()).ok();
|
|
||||||
file.write(&[0xD]).ok();
|
|
||||||
file.write(button_a.as_bytes()).ok();
|
|
||||||
file.write(&[0xD]).ok();
|
|
||||||
file.write(button_b.as_bytes()).ok();
|
|
||||||
file.write(&[0xD]).ok();
|
|
||||||
file.write(button_c.as_bytes()).ok();
|
|
||||||
file.write(&[0xD]).ok();
|
|
||||||
file.write(button_s.as_bytes()).ok();
|
|
||||||
file.write(&[0xD]).ok();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +1,79 @@
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Write};
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::controller::physical::ControllerState;
|
|
||||||
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
use crate::controller::emulated::{DeviceDescriptor, ENDPOINT1};
|
||||||
|
use crate::controller::physical::ControllerState;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
pub const DESCRIPTORS: [u8; 80] = [0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
pub const DESCRIPTORS: [u8; 80] = [
|
||||||
0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x5E, 0x00,
|
0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22,
|
||||||
0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05,
|
0x5E, 0x00, 0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05, 0x07, 0x05, 0x81, 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,
|
||||||
0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
|
0x5E, 0x00, 0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05, 0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x05,
|
||||||
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x5E, 0x00,
|
];
|
||||||
0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x05,
|
pub const STRINGS: [u8; 16] = [
|
||||||
0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x05];
|
0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
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: 0x33DD, id_product: 0x0001, bcd_device: 0x0106, i_manufacturer: "TAITO", i_product: "Densha de Go! Plug & Play (NS One Handle mode)", i_serial_number: "ZKNS-001"};
|
pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor {
|
||||||
|
b_device_class: 0x0,
|
||||||
|
b_device_sub_class: 0x0,
|
||||||
|
id_vendor: 0x33DD,
|
||||||
|
id_product: 0x0001,
|
||||||
|
bcd_device: 0x0106,
|
||||||
|
i_manufacturer: "TAITO",
|
||||||
|
i_product: "Densha de Go! Plug & Play (NS One Handle mode)",
|
||||||
|
i_serial_number: "ZKNS-001",
|
||||||
|
};
|
||||||
|
|
||||||
pub const HID_REPORT_DESCRIPTOR: [u8; 94] = [
|
pub const HID_REPORT_DESCRIPTOR: [u8; 94] = [
|
||||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||||
0x09, 0x05, // Usage (Game Pad)
|
0x09, 0x05, // Usage (Game Pad)
|
||||||
0xA1, 0x01, // Collection (Application)
|
0xA1, 0x01, // Collection (Application)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
0x25, 0x01, // Logical Maximum (1)
|
0x25, 0x01, // Logical Maximum (1)
|
||||||
0x35, 0x00, // Physical Minimum (0)
|
0x35, 0x00, // Physical Minimum (0)
|
||||||
0x45, 0x01, // Physical Maximum (1)
|
0x45, 0x01, // Physical Maximum (1)
|
||||||
0x75, 0x01, // Report Size (1)
|
0x75, 0x01, // Report Size (1)
|
||||||
0x95, 0x0E, // Report Count (14)
|
0x95, 0x0E, // Report Count (14)
|
||||||
0x05, 0x09, // Usage Page (Button)
|
0x05, 0x09, // Usage Page (Button)
|
||||||
0x19, 0x01, // Usage Minimum (0x01)
|
0x19, 0x01, // Usage Minimum (0x01)
|
||||||
0x29, 0x0E, // Usage Maximum (0x0E)
|
0x29, 0x0E, // Usage Maximum (0x0E)
|
||||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x95, 0x02, // Report Count (2)
|
0x95, 0x02, // Report Count (2)
|
||||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||||
0x25, 0x07, // Logical Maximum (7)
|
0x25, 0x07, // Logical Maximum (7)
|
||||||
0x46, 0x3B, 0x01, // Physical Maximum (315)
|
0x46, 0x3B, 0x01, // Physical Maximum (315)
|
||||||
0x75, 0x04, // Report Size (4)
|
0x75, 0x04, // Report Size (4)
|
||||||
0x95, 0x01, // Report Count (1)
|
0x95, 0x01, // Report Count (1)
|
||||||
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
|
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
|
||||||
0x09, 0x39, // Usage (Hat switch)
|
0x09, 0x39, // Usage (Hat switch)
|
||||||
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
||||||
0x65, 0x00, // Unit (None)
|
0x65, 0x00, // Unit (None)
|
||||||
0x95, 0x01, // Report Count (1)
|
0x95, 0x01, // Report Count (1)
|
||||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||||
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||||
0x09, 0x30, // Usage (X)
|
0x09, 0x30, // Usage (X)
|
||||||
0x09, 0x31, // Usage (Y)
|
0x09, 0x31, // Usage (Y)
|
||||||
0x09, 0x32, // Usage (Z)
|
0x09, 0x32, // Usage (Z)
|
||||||
0x09, 0x35, // Usage (Rz)
|
0x09, 0x35, // Usage (Rz)
|
||||||
0x75, 0x08, // Report Size (8)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x95, 0x04, // Report Count (4)
|
0x95, 0x04, // Report Count (4)
|
||||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x75, 0x08, // Report Size (8)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x95, 0x01, // Report Count (1)
|
0x95, 0x01, // Report Count (1)
|
||||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x0A, 0x4F, 0x48, // Usage (0x484F)
|
0x0A, 0x4F, 0x48, // Usage (0x484F)
|
||||||
0x75, 0x08, // Report Size (8)
|
0x75, 0x08, // Report Size (8)
|
||||||
0x95, 0x08, // Report Count (8)
|
0x95, 0x08, // Report Count (8)
|
||||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
0xB1,
|
||||||
0x0A, 0x4F, 0x48, // Usage (0x484F)
|
0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
0x0A, 0x4F, 0x48, // Usage (0x484F)
|
||||||
0xC0, // End Collection
|
0x91,
|
||||||
|
0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||||
|
0xC0, // End Collection
|
||||||
];
|
];
|
||||||
|
|
||||||
const POWER_NOTCHES: [u8; 6] = [0x80, 0x9F, 0xB7, 0xCE, 0xE6, 0xFF];
|
const POWER_NOTCHES: [u8; 6] = [0x80, 0x9F, 0xB7, 0xCE, 0xE6, 0xFF];
|
||||||
|
@ -109,27 +120,66 @@ pub fn update_gadget(state: &mut ControllerState) {
|
||||||
buttons2.insert(Buttons2::HOME);
|
buttons2.insert(Buttons2::HOME);
|
||||||
state.combo = true;
|
state.combo = true;
|
||||||
}
|
}
|
||||||
if state.button_a { buttons1.insert(Buttons1::Y) }
|
if state.button_a {
|
||||||
if state.button_b { buttons1.insert(Buttons1::B) }
|
buttons1.insert(Buttons1::Y)
|
||||||
if state.button_c { buttons1.insert(Buttons1::A) }
|
}
|
||||||
if state.button_d { buttons1.insert(Buttons1::X) }
|
if state.button_b {
|
||||||
if state.brake == 9 { buttons1.insert(Buttons1::ZL) }
|
buttons1.insert(Buttons1::B)
|
||||||
if !state.combo && state.button_start { buttons2.insert(Buttons2::START) }
|
}
|
||||||
if !state.combo && state.button_select_hold { buttons2.insert(Buttons2::SELECT) }
|
if state.button_c {
|
||||||
|
buttons1.insert(Buttons1::A)
|
||||||
|
}
|
||||||
|
if state.button_d {
|
||||||
|
buttons1.insert(Buttons1::X)
|
||||||
|
}
|
||||||
|
if state.brake == 9 {
|
||||||
|
buttons1.insert(Buttons1::ZL)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_start {
|
||||||
|
buttons2.insert(Buttons2::START)
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_select_hold {
|
||||||
|
buttons2.insert(Buttons2::SELECT)
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate data for D-pad
|
// Calculate data for D-pad
|
||||||
let mut dpad: u8 = 0xF;
|
let mut dpad: u8 = 0xF;
|
||||||
if state.button_up { dpad = 0x0 }
|
if state.button_up {
|
||||||
if state.button_down { dpad = 0x4 }
|
dpad = 0x0
|
||||||
if !state.combo && state.button_left { dpad = 0x6 }
|
}
|
||||||
if !state.combo && state.button_right { dpad = 0x2 }
|
if state.button_down {
|
||||||
if !state.combo && state.button_up & state.button_left { dpad = 0x7 }
|
dpad = 0x4
|
||||||
if !state.combo && state.button_up & state.button_right { dpad = 0x1 }
|
}
|
||||||
if !state.combo && state.button_down & state.button_left { dpad = 0x5 }
|
if !state.combo && state.button_left {
|
||||||
if !state.combo && state.button_down & state.button_right { dpad = 0x3 }
|
dpad = 0x6
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_right {
|
||||||
|
dpad = 0x2
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_up & state.button_left {
|
||||||
|
dpad = 0x7
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_up & state.button_right {
|
||||||
|
dpad = 0x1
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_down & state.button_left {
|
||||||
|
dpad = 0x5
|
||||||
|
}
|
||||||
|
if !state.combo && state.button_down & state.button_right {
|
||||||
|
dpad = 0x3
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble data and send it to gadget
|
// Assemble data and send it to gadget
|
||||||
let data = [buttons1.bits, buttons2.bits, dpad, 0x80, handle, 0x80, 0x80, 0x00];
|
let data = [
|
||||||
|
buttons1.bits,
|
||||||
|
buttons2.bits,
|
||||||
|
dpad,
|
||||||
|
0x80,
|
||||||
|
handle,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x00,
|
||||||
|
];
|
||||||
if let Ok(mut file) = File::create(ENDPOINT1) {
|
if let Ok(mut file) = File::create(ENDPOINT1) {
|
||||||
file.write(&data).ok();
|
file.write(&data).ok();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
|
||||||
use std::io::Result;
|
use std::io::Result;
|
||||||
use std::time::{Instant,Duration};
|
use std::io::Write;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use evdev::Device;
|
use evdev::Device;
|
||||||
use evdev::Key;
|
use evdev::Key;
|
||||||
|
@ -30,9 +30,34 @@ pub struct ControllerState {
|
||||||
|
|
||||||
const HOLD_DELAY: Duration = Duration::from_millis(750);
|
const HOLD_DELAY: Duration = Duration::from_millis(750);
|
||||||
|
|
||||||
const USED_KEYS: [Key; 26] = [Key::KEY_0, Key::KEY_1, Key::KEY_2, Key::KEY_3, Key::KEY_4, Key::KEY_5,
|
const USED_KEYS: [Key; 26] = [
|
||||||
Key::KEY_B, Key::KEY_C, Key::KEY_D, Key::KEY_E, Key::KEY_F, Key::KEY_G, Key::KEY_H, Key::KEY_I, Key::KEY_J, Key::KEY_P,
|
Key::KEY_0,
|
||||||
Key::KEY_SPACE, Key::KEY_ENTER, Key::KEY_A, Key::KEY_Z, Key::KEY_X, Key::KEY_S, Key::KEY_UP, Key::KEY_DOWN, Key::KEY_LEFT, Key::KEY_RIGHT];
|
Key::KEY_1,
|
||||||
|
Key::KEY_2,
|
||||||
|
Key::KEY_3,
|
||||||
|
Key::KEY_4,
|
||||||
|
Key::KEY_5,
|
||||||
|
Key::KEY_B,
|
||||||
|
Key::KEY_C,
|
||||||
|
Key::KEY_D,
|
||||||
|
Key::KEY_E,
|
||||||
|
Key::KEY_F,
|
||||||
|
Key::KEY_G,
|
||||||
|
Key::KEY_H,
|
||||||
|
Key::KEY_I,
|
||||||
|
Key::KEY_J,
|
||||||
|
Key::KEY_P,
|
||||||
|
Key::KEY_SPACE,
|
||||||
|
Key::KEY_ENTER,
|
||||||
|
Key::KEY_A,
|
||||||
|
Key::KEY_Z,
|
||||||
|
Key::KEY_X,
|
||||||
|
Key::KEY_S,
|
||||||
|
Key::KEY_UP,
|
||||||
|
Key::KEY_DOWN,
|
||||||
|
Key::KEY_LEFT,
|
||||||
|
Key::KEY_RIGHT,
|
||||||
|
];
|
||||||
|
|
||||||
pub fn init() -> Result<[Device; 2]> {
|
pub fn init() -> Result<[Device; 2]> {
|
||||||
let d1 = Device::open("/dev/input/event1")?;
|
let d1 = Device::open("/dev/input/event1")?;
|
||||||
|
@ -55,27 +80,90 @@ pub fn get_state(state: &mut ControllerState, dev: &[Device; 2]) {
|
||||||
fn read_input(controller: &mut ControllerState, key: Key, value: bool) {
|
fn read_input(controller: &mut ControllerState, key: Key, value: bool) {
|
||||||
// Save input status to object for processing
|
// Save input status to object for processing
|
||||||
match key {
|
match key {
|
||||||
Key::KEY_0=>if value {controller.power = 0},
|
Key::KEY_0 => {
|
||||||
Key::KEY_1=>if value {controller.power = 1},
|
if value {
|
||||||
Key::KEY_2=>if value {controller.power = 2},
|
controller.power = 0
|
||||||
Key::KEY_3=>if value {controller.power = 3},
|
}
|
||||||
Key::KEY_4=>if value {controller.power = 4},
|
}
|
||||||
Key::KEY_5=>if value {controller.power = 5},
|
Key::KEY_1 => {
|
||||||
Key::KEY_B=>if value {controller.brake = 0},
|
if value {
|
||||||
Key::KEY_C=>if value {controller.brake = 1},
|
controller.power = 1
|
||||||
Key::KEY_D=>if value {controller.brake = 2},
|
}
|
||||||
Key::KEY_E=>if value {controller.brake = 3},
|
}
|
||||||
Key::KEY_F=>if value {controller.brake = 4},
|
Key::KEY_2 => {
|
||||||
Key::KEY_G=>if value {controller.brake = 5},
|
if value {
|
||||||
Key::KEY_H=>if value {controller.brake = 6},
|
controller.power = 2
|
||||||
Key::KEY_I=>if value {controller.brake = 7},
|
}
|
||||||
Key::KEY_J=>if value {controller.brake = 8},
|
}
|
||||||
Key::KEY_P=>if value {controller.brake = 9},
|
Key::KEY_3 => {
|
||||||
Key::KEY_SPACE=> {
|
if value {
|
||||||
|
controller.power = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_4 => {
|
||||||
|
if value {
|
||||||
|
controller.power = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_5 => {
|
||||||
|
if value {
|
||||||
|
controller.power = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_B => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_C => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_D => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_E => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_F => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_G => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_H => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_I => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_J => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_P => {
|
||||||
|
if value {
|
||||||
|
controller.brake = 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::KEY_SPACE => {
|
||||||
if !controller.button_select && value {
|
if !controller.button_select && value {
|
||||||
controller.button_select_time = Some(Instant::now());
|
controller.button_select_time = Some(Instant::now());
|
||||||
}
|
} else if !value {
|
||||||
else if !value {
|
|
||||||
controller.button_select_time = None;
|
controller.button_select_time = None;
|
||||||
controller.combo = false;
|
controller.combo = false;
|
||||||
}
|
}
|
||||||
|
@ -84,23 +172,23 @@ fn read_input(controller: &mut ControllerState, key: Key, value: bool) {
|
||||||
if let Some(time) = controller.button_select_time {
|
if let Some(time) = controller.button_select_time {
|
||||||
controller.button_select_hold = time.elapsed() > HOLD_DELAY && !controller.combo;
|
controller.button_select_hold = time.elapsed() > HOLD_DELAY && !controller.combo;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Key::KEY_ENTER=>controller.button_start = value,
|
Key::KEY_ENTER => controller.button_start = value,
|
||||||
Key::KEY_A=>controller.button_a = value,
|
Key::KEY_A => controller.button_a = value,
|
||||||
Key::KEY_Z=>controller.button_b = value,
|
Key::KEY_Z => controller.button_b = value,
|
||||||
Key::KEY_X=>controller.button_c = value,
|
Key::KEY_X => controller.button_c = value,
|
||||||
Key::KEY_S=>controller.button_d = value,
|
Key::KEY_S => controller.button_d = value,
|
||||||
Key::KEY_UP=>controller.button_up = value,
|
Key::KEY_UP => controller.button_up = value,
|
||||||
Key::KEY_DOWN=>controller.button_down = value,
|
Key::KEY_DOWN => controller.button_down = value,
|
||||||
Key::KEY_LEFT=>controller.button_left = value,
|
Key::KEY_LEFT => controller.button_left = value,
|
||||||
Key::KEY_RIGHT=>controller.button_right = value,
|
Key::KEY_RIGHT => controller.button_right = value,
|
||||||
_=>(),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_lamp(status: bool) {
|
pub fn set_lamp(status: bool) {
|
||||||
if let Ok(mut out) = File::create("/sys/class/leds/led2/brightness") {
|
if let Ok(mut out) = File::create("/sys/class/leds/led2/brightness") {
|
||||||
out.write(if status {b"1"} else {b"0"}).ok();
|
out.write(if status { b"1" } else { b"0" }).ok();
|
||||||
}
|
}
|
||||||
/*else {
|
/*else {
|
||||||
println!("WARNING: Could not set door lamp status!")
|
println!("WARNING: Could not set door lamp status!")
|
||||||
|
@ -109,7 +197,7 @@ pub fn set_lamp(status: bool) {
|
||||||
|
|
||||||
pub fn set_rumble(status: bool) {
|
pub fn set_rumble(status: bool) {
|
||||||
if let Ok(mut out) = File::create("/sys/class/leds/led1/brightness") {
|
if let Ok(mut out) = File::create("/sys/class/leds/led1/brightness") {
|
||||||
out.write(if status {b"1"} else {b"0"}).ok();
|
out.write(if status { b"1" } else { b"0" }).ok();
|
||||||
}
|
}
|
||||||
/*else {
|
/*else {
|
||||||
println!("WARNING: Could not set rumble motor status!")
|
println!("WARNING: Could not set rumble motor status!")
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -2,10 +2,10 @@ mod controller;
|
||||||
|
|
||||||
use std::io::Result;
|
use std::io::Result;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::Duration;
|
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use controller::physical::{set_lamp,set_rumble};
|
use controller::physical::{set_lamp, set_rumble};
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
match controller::physical::init() {
|
match controller::physical::init() {
|
||||||
|
@ -18,7 +18,6 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
// Check selected controller model
|
// Check selected controller model
|
||||||
if let Some(controller_model) = controller::emulated::set_model(&controller_state) {
|
if let Some(controller_model) = controller::emulated::set_model(&controller_state) {
|
||||||
|
|
||||||
// Stop main game
|
// Stop main game
|
||||||
stop_game();
|
stop_game();
|
||||||
|
|
||||||
|
@ -43,12 +42,15 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result::Ok(());
|
return Result::Ok(());
|
||||||
},
|
}
|
||||||
Err(_e) => println!("ddgo-pnp-controller: ERROR: Could not read input devices! Exiting."),
|
Err(_e) => println!("ddgo-pnp-controller: ERROR: Could not read input devices! Exiting."),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_game() {
|
fn stop_game() {
|
||||||
Command::new("/etc/init.d/S99dgtype3").arg("stop").output().ok();
|
Command::new("/etc/init.d/S99dgtype3")
|
||||||
|
.arg("stop")
|
||||||
|
.output()
|
||||||
|
.ok();
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue