From 574c1de9df13352bf4c9241b73eddf6dac6aba67 Mon Sep 17 00:00:00 2001 From: Alex van de Sandt Date: Sun, 28 Dec 2025 13:20:05 -0600 Subject: [PATCH] Start decoding bitfields with a macro --- Cargo.lock | 7 +++++ Cargo.toml | 1 + examples/dump_bitfield.rs | 22 ++++++++++++++ src/telemetry/bitfields.rs | 60 ++++++++++++++++++++++++++++++++++++++ src/telemetry/mod.rs | 2 ++ src/telemetry/sample.rs | 9 ++++-- 6 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 examples/dump_bitfield.rs create mode 100644 src/telemetry/bitfields.rs diff --git a/Cargo.lock b/Cargo.lock index b41d15f..eb8a397 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bit-iter" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c95b1159d450fcb44b73b8d7b7b739656061483a585bffe4b7484e617bf55e9" + [[package]] name = "bumpalo" version = "3.19.1" @@ -230,6 +236,7 @@ name = "irsdk" version = "0.1.0" dependencies = [ "aligned-vec", + "bit-iter", "bytemuck", "chrono", "claims", diff --git a/Cargo.toml b/Cargo.toml index 4979c3d..844f8f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] aligned-vec = "0.6.4" +bit-iter = "1.3.1" bytemuck = { version = "1.24.0", features = ["derive", "extern_crate_alloc"] } chrono = "0.4.42" indexmap = "2.12.1" diff --git a/examples/dump_bitfield.rs b/examples/dump_bitfield.rs new file mode 100644 index 0000000..cae4b6f --- /dev/null +++ b/examples/dump_bitfield.rs @@ -0,0 +1,22 @@ +use irsdk::{ + IbtFile, + telemetry::{Value, VarType}, +}; + +const USAGE: &str = "Usage: dump_bitfield "; + +fn main() { + let file_name = std::env::args().nth(1).expect(USAGE); + let var_name = std::env::args().nth(2).expect(USAGE); + let file = IbtFile::from_file(&file_name).expect("could not open IBT file"); + + let var = file.vars.var(&var_name).expect("unknown var"); + assert_eq!(var.ty, VarType::Bitfield); + + for sample in file.samples() { + let Value::Bitfield(bf) = sample.read_var(var) else { + panic!() + }; + println!("{bf:?}"); + } +} diff --git a/src/telemetry/bitfields.rs b/src/telemetry/bitfields.rs new file mode 100644 index 0000000..99def50 --- /dev/null +++ b/src/telemetry/bitfields.rs @@ -0,0 +1,60 @@ +#[derive(Clone, Copy, Debug)] +pub enum Bitfield { + EngineWarnings(EngineWarnings), + Unknown(u32), +} + +impl Bitfield { + pub(crate) fn parse(val: u32, unit: &str) -> Self { + match unit { + "irsdk_EngineWarnings" => Self::engine_warnings(val), + _ => Self::Unknown(val), + } + } + + fn engine_warnings(raw: u32) -> Self { + Self::EngineWarnings(EngineWarnings(raw)) + } +} + +/// Defines a tuple struct holding a `u32`, where each bit in the value may represent a different +/// "flag". An accessor for each flag is generated, which masks the value to extract the flag's +/// specified bit, returning `true` if the bit is set. A `Debug` impl is also generated. +macro_rules! bitfield { + ($name:ident { $($bit:literal => $field:ident),+ $(,)? }) => { + #[derive(Clone, Copy)] + pub struct $name(u32); + + impl $name { + $( + pub fn $field(&self) -> bool { + self.0 & (1 << $bit) > 0 + } + )+ + } + + impl ::core::fmt::Debug for $name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let entries = ::bit_iter::BitIter::from(self.0).map(|set_bit| match set_bit { + $($bit => stringify!($field),)+ + _ => "unknown", + }); + f.debug_set().entries(entries).finish() + } + } + }; +} + +bitfield! { + EngineWarnings { + 1 => water_temp, + 2 => fuel_pressure, + 3 => oil_pressure, + 4 => engine_stalled, + 5 => pit_speed_limiter, + 6 => rev_limiter, + 7 => oil_temp, + 8 => mandatory_repairs, + 9 => optional_repairs, + } +} diff --git a/src/telemetry/mod.rs b/src/telemetry/mod.rs index 45435bd..ded2713 100644 --- a/src/telemetry/mod.rs +++ b/src/telemetry/mod.rs @@ -1,8 +1,10 @@ +mod bitfields; mod enums; mod headers; mod sample; mod var; +pub use bitfields::{Bitfield, EngineWarnings}; pub use enums::{ CarLeftRight, Enum, PaceMode, PitServiceStatus, SessionState, TrackLocation, TrackSurface, TrackWetness, diff --git a/src/telemetry/sample.rs b/src/telemetry/sample.rs index 2eca7b9..0f5d8b5 100644 --- a/src/telemetry/sample.rs +++ b/src/telemetry/sample.rs @@ -2,7 +2,7 @@ use bytemuck::pod_collect_to_vec; use crate::{ aligned::align_cast, - telemetry::{Enum, VarHeader, VarType}, + telemetry::{Bitfield, Enum, VarHeader, VarType}, }; pub struct Sample<'data>(&'data [u8]); @@ -31,7 +31,10 @@ impl<'data> Sample<'data> { VarType::Char => Value::Char(slice[0] as char), VarType::Bool => Value::Bool(slice[0] != 0), VarType::Int => Value::Int(align_cast(slice)), - VarType::Bitfield => Value::Bitfield(align_cast(slice)), + VarType::Bitfield => { + let b = Bitfield::parse(align_cast(slice), var.unit.as_str()); + Value::Bitfield(b) + } VarType::Float => Value::Float(align_cast(slice)), VarType::Double => Value::Double(align_cast(slice)), } @@ -44,7 +47,7 @@ pub enum Value { Char(char), Bool(bool), Int(i32), - Bitfield(u32), + Bitfield(Bitfield), Float(f32), Double(f64), -- 2.51.2