mirror of
https://github.com/bybrooklyn/openbitdo.git
synced 2026-03-19 04:12:56 -04:00
122 lines
5.1 KiB
Rust
122 lines
5.1 KiB
Rust
use std::env;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
fn main() {
|
|
let manifest_dir =
|
|
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("missing CARGO_MANIFEST_DIR"));
|
|
let spec_dir = manifest_dir.join("../../../spec");
|
|
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("missing OUT_DIR"));
|
|
|
|
let pid_csv = spec_dir.join("pid_matrix.csv");
|
|
let command_csv = spec_dir.join("command_matrix.csv");
|
|
|
|
println!("cargo:rerun-if-changed={}", pid_csv.display());
|
|
println!("cargo:rerun-if-changed={}", command_csv.display());
|
|
|
|
generate_pid_registry(&pid_csv, &out_dir.join("generated_pid_registry.rs"));
|
|
generate_command_registry(&command_csv, &out_dir.join("generated_command_registry.rs"));
|
|
}
|
|
|
|
fn generate_pid_registry(csv_path: &Path, out_path: &Path) {
|
|
let mut rdr = csv::Reader::from_path(csv_path).expect("failed to open pid_matrix.csv");
|
|
let mut out = String::new();
|
|
out.push_str("pub const PID_REGISTRY: &[crate::registry::PidRegistryRow] = &[\n");
|
|
|
|
for rec in rdr.records() {
|
|
let rec = rec.expect("invalid pid csv record");
|
|
let name = rec.get(0).expect("pid_name");
|
|
let pid: u16 = rec
|
|
.get(1)
|
|
.expect("pid_decimal")
|
|
.parse()
|
|
.expect("invalid pid decimal");
|
|
let support_level = match rec.get(5).expect("support_level") {
|
|
"full" => "crate::types::SupportLevel::Full",
|
|
"detect-only" => "crate::types::SupportLevel::DetectOnly",
|
|
other => panic!("unknown support_level {other}"),
|
|
};
|
|
let protocol_family = match rec.get(6).expect("protocol_family") {
|
|
"Standard64" => "crate::types::ProtocolFamily::Standard64",
|
|
"JpHandshake" => "crate::types::ProtocolFamily::JpHandshake",
|
|
"DInput" => "crate::types::ProtocolFamily::DInput",
|
|
"DS4Boot" => "crate::types::ProtocolFamily::DS4Boot",
|
|
"Unknown" => "crate::types::ProtocolFamily::Unknown",
|
|
other => panic!("unknown protocol_family {other}"),
|
|
};
|
|
|
|
out.push_str(&format!(
|
|
" crate::registry::PidRegistryRow {{ name: \"{name}\", pid: {pid}, support_level: {support_level}, protocol_family: {protocol_family} }},\n"
|
|
));
|
|
}
|
|
|
|
out.push_str("]\n;");
|
|
fs::write(out_path, out).expect("failed writing generated_pid_registry.rs");
|
|
}
|
|
|
|
fn generate_command_registry(csv_path: &Path, out_path: &Path) {
|
|
let mut rdr = csv::Reader::from_path(csv_path).expect("failed to open command_matrix.csv");
|
|
let mut out = String::new();
|
|
out.push_str("pub const COMMAND_REGISTRY: &[crate::registry::CommandRegistryRow] = &[\n");
|
|
|
|
for rec in rdr.records() {
|
|
let rec = rec.expect("invalid command csv record");
|
|
let id = rec.get(0).expect("command_id");
|
|
let safety_class = match rec.get(1).expect("safety_class") {
|
|
"SafeRead" => "crate::types::SafetyClass::SafeRead",
|
|
"SafeWrite" => "crate::types::SafetyClass::SafeWrite",
|
|
"UnsafeBoot" => "crate::types::SafetyClass::UnsafeBoot",
|
|
"UnsafeFirmware" => "crate::types::SafetyClass::UnsafeFirmware",
|
|
other => panic!("unknown safety_class {other}"),
|
|
};
|
|
let confidence = match rec.get(2).expect("confidence") {
|
|
"confirmed" => "crate::types::CommandConfidence::Confirmed",
|
|
"inferred" => "crate::types::CommandConfidence::Inferred",
|
|
other => panic!("unknown confidence {other}"),
|
|
};
|
|
let experimental_default = rec
|
|
.get(3)
|
|
.expect("experimental_default")
|
|
.parse::<bool>()
|
|
.expect("invalid experimental_default");
|
|
let report_id = parse_u8(rec.get(4).expect("report_id"));
|
|
let request_hex = rec.get(6).expect("request_hex");
|
|
let request = hex_to_bytes(request_hex);
|
|
let expected_response = rec.get(7).expect("expected_response");
|
|
|
|
out.push_str(&format!(
|
|
" crate::registry::CommandRegistryRow {{ id: crate::command::CommandId::{id}, safety_class: {safety_class}, confidence: {confidence}, experimental_default: {experimental_default}, report_id: {report_id}, request: &{request:?}, expected_response: \"{expected_response}\" }},\n"
|
|
));
|
|
}
|
|
|
|
out.push_str("]\n;");
|
|
fs::write(out_path, out).expect("failed writing generated_command_registry.rs");
|
|
}
|
|
|
|
fn parse_u8(value: &str) -> u8 {
|
|
if let Some(stripped) = value.strip_prefix("0x") {
|
|
u8::from_str_radix(stripped, 16).expect("invalid hex u8")
|
|
} else {
|
|
value.parse::<u8>().expect("invalid u8")
|
|
}
|
|
}
|
|
|
|
fn hex_to_bytes(hex: &str) -> Vec<u8> {
|
|
let hex = hex.trim();
|
|
if hex.len() % 2 != 0 {
|
|
panic!("hex length must be even: {hex}");
|
|
}
|
|
let mut bytes = Vec::with_capacity(hex.len() / 2);
|
|
let raw = hex.as_bytes();
|
|
for i in (0..raw.len()).step_by(2) {
|
|
let hi = (raw[i] as char)
|
|
.to_digit(16)
|
|
.unwrap_or_else(|| panic!("invalid hex: {hex}"));
|
|
let lo = (raw[i + 1] as char)
|
|
.to_digit(16)
|
|
.unwrap_or_else(|| panic!("invalid hex: {hex}"));
|
|
bytes.push(((hi << 4) | lo) as u8);
|
|
}
|
|
bytes
|
|
}
|