diff --git a/examples/dump_file.rs b/examples/dump_file.rs index db16068..97fc682 100644 --- a/examples/dump_file.rs +++ b/examples/dump_file.rs @@ -6,5 +6,5 @@ fn main() { .expect("Usage: dump_file "); let file = IbtFile::from_file(&file_name).unwrap(); - dbg!(&file.header, &file.disk_sub_header); + dbg!(&file.header, &file.disk_sub_header, &file.var_headers); } diff --git a/src/file.rs b/src/file.rs index 2f08a08..2f7737c 100644 --- a/src/file.rs +++ b/src/file.rs @@ -5,7 +5,7 @@ use saphyr::LoadableYamlNode; use crate::{ raw, - telemetry::{DiskSubHeader, Header}, + telemetry::{DiskSubHeader, Header, VarHeader}, }; #[derive(Clone, Debug)] @@ -14,6 +14,8 @@ pub struct IbtFile { pub header: Header, pub disk_sub_header: DiskSubHeader, + + pub var_headers: Vec, } impl IbtFile { @@ -28,10 +30,19 @@ impl IbtFile { ); let sub_header = DiskSubHeader::from_raw(&raw_sub_header); + let var_headers_offset = raw_header.var_header_offset as usize; + let var_headers_len = raw::VAR_HEADER_SIZE * raw_header.num_vars as usize; + let vh_slice = &data[var_headers_offset..var_headers_offset + var_headers_len]; + let var_headers = raw::VarHeader::slice_from_fraw_bytes(vh_slice) + .iter() + .map(VarHeader::from_raw) + .collect(); + Ok(Self { data, header, disk_sub_header: sub_header, + var_headers, }) } diff --git a/src/raw.rs b/src/raw.rs index 85320ea..5146721 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -1,4 +1,4 @@ -use std::ffi::{c_double, c_int}; +use std::ffi::{c_char, c_double, c_int}; use bytemuck::{AnyBitPattern, Pod, Zeroable}; @@ -6,6 +6,8 @@ use bytemuck::{AnyBitPattern, Pod, Zeroable}; type time_t = i64; const IRSDK_MAX_BUFS: usize = 4; +const IRSDK_MAX_STRING: usize = 32; +const IRSDK_MAX_DESC: usize = 64; /// The alignment of the [`Header`] type, should always be 16 /// @@ -16,6 +18,7 @@ pub const ALIGNMENT: usize = std::mem::align_of::
(); pub const HEADER_SIZE: usize = std::mem::size_of::
(); pub const SUB_HEADER_SIZE: usize = std::mem::size_of::(); +pub const VAR_HEADER_SIZE: usize = std::mem::size_of::(); #[derive(Clone, Copy, Debug, AnyBitPattern)] #[repr(C, align(16))] @@ -30,31 +33,31 @@ pub struct Header { /// Incremented when session info changes pub session_info_update: c_int, /// Length in bytes of the session info string - session_info_len: c_int, + pub session_info_len: c_int, /// Session info, encoded in YAML - session_info_offset: c_int, + pub session_info_offset: c_int, /// Length of the array pointed to by varHeaderOffset - num_vars: c_int, + pub num_vars: c_int, /// Offset to the `VarHeader` array, which describes the variables in [`VarBuf`] - var_header_offset: c_int, + pub var_header_offset: c_int, /// Number of variable buffers - num_buf: c_int, + pub num_buf: c_int, /// Length of each variable buffer - buf_len: c_int, + pub buf_len: c_int, /// Offsets to each of the variable buffers - var_bufs: [VarBuf; IRSDK_MAX_BUFS], + pub var_bufs: [VarBuf; IRSDK_MAX_BUFS], } #[derive(Clone, Copy, Debug, Pod, Zeroable)] #[repr(C, align(16))] pub struct VarBuf { /// Which tick this buffer represents - tick_count: c_int, + pub tick_count: c_int, /// Offset from the header - buf_offset: c_int, + pub buf_offset: c_int, _pad: [c_int; 2], } @@ -73,18 +76,23 @@ pub struct DiskSubHeader { pub session_record_count: c_int, } +#[derive(Clone, Copy, Debug, AnyBitPattern)] +#[repr(C, align(16))] +pub struct VarHeader { + pub ty: c_int, + pub offset: c_int, + pub count: c_int, + pub count_as_time: c_int, + + pub name: [c_char; IRSDK_MAX_STRING], + pub desc: [c_char; IRSDK_MAX_DESC], + pub unit: [c_char; IRSDK_MAX_STRING], +} + impl Header { pub fn from_raw_bytes(bytes: &[u8]) -> Self { *bytemuck::from_bytes(bytes) } - - pub fn session_info_len(&self) -> i32 { - self.session_info_len - } - - pub fn session_info_offset(&self) -> i32 { - self.session_info_offset - } } impl DiskSubHeader { @@ -92,3 +100,9 @@ impl DiskSubHeader { *bytemuck::from_bytes(bytes) } } + +impl VarHeader { + pub fn slice_from_fraw_bytes(bytes: &[u8]) -> &[Self] { + bytemuck::cast_slice(bytes) + } +} diff --git a/src/telemetry.rs b/src/telemetry.rs index 44ea22b..d1595d9 100644 --- a/src/telemetry.rs +++ b/src/telemetry.rs @@ -1,4 +1,7 @@ -use std::time::Duration; +use std::{ + ffi::{CStr, c_char}, + time::Duration, +}; use chrono::{DateTime, Utc}; @@ -31,13 +34,35 @@ pub struct DiskSubHeader { pub lap_count: u32, } +#[derive(Clone, Copy, Debug)] +pub enum VarType { + Char, + Bool, + Int, + Bitfield, + Float, + Double, +} + +#[derive(Clone, Debug)] +pub struct VarHeader { + pub ty: VarType, + pub offset: usize, + pub count: usize, + pub count_as_time: bool, + + pub name: String, + pub description: String, + pub unit: String, +} + impl Header { pub fn from_raw(raw: &raw::Header) -> Self { Self { tick_rate: raw.tick_rate.try_into().unwrap(), session_info_update: raw.session_info_update.try_into().unwrap(), - session_info_len: raw.session_info_len().try_into().unwrap(), - session_info_offset: raw.session_info_offset().try_into().unwrap(), + session_info_len: raw.session_info_len.try_into().unwrap(), + session_info_offset: raw.session_info_offset.try_into().unwrap(), } } @@ -60,3 +85,34 @@ impl DiskSubHeader { } } } + +impl VarHeader { + pub fn from_raw(raw: &raw::VarHeader) -> Self { + let ty = match raw.ty { + 0 => VarType::Char, + 1 => VarType::Bool, + 2 => VarType::Int, + 3 => VarType::Bitfield, + 4 => VarType::Float, + 5 => VarType::Double, + _ => panic!("invalid var type: `{}`", raw.ty), + }; + + Self { + ty, + offset: raw.offset.try_into().unwrap(), + count: raw.count.try_into().unwrap(), + count_as_time: raw.count_as_time == 0, + name: string_from_c_chars(&raw.name), + description: string_from_c_chars(&raw.desc), + unit: string_from_c_chars(&raw.unit), + } + } +} + +// TODO: There may be some weird encoding on these strings +fn string_from_c_chars(buf: &[c_char]) -> String { + assert!(buf.contains(&0)); + let cstr = unsafe { CStr::from_ptr(buf.as_ptr()) }; + cstr.to_string_lossy().into_owned() +}