1use core::fmt;
15use encoding::all::WINDOWS_1252;
16use encoding::{DecoderTrap, Encoding};
17use std::cmp::Ordering;
18use std::ffi::{c_void, CStr};
19use std::os::raw::c_char;
20use std::rc::Rc;
21use std::time::{Duration, Instant};
22use std::{slice, thread};
23use windows::Win32::Foundation::{
24 CloseHandle, GetLastError, HANDLE, HWND, LPARAM, WIN32_ERROR, WPARAM,
25};
26use windows::Win32::System::{Memory, Threading};
27use windows::Win32::UI::WindowsAndMessaging::{RegisterWindowMessageA, SendNotifyMessageA};
28
29pub mod flags;
30
31const IRSDK_MAX_BUFS: usize = 4;
32const IRSDK_MAX_STRING: usize = 32;
33const IRSDK_MAX_DESC: usize = 64;
35
36const HWND_BROADCAST: HWND = HWND(0xFFFF);
38
39pub const IRSDK_UNLIMITED_LAPS: i32 = 32767;
41
42pub const IRSDK_UNLIMITED_TIME: f64 = 604800.0;
44
45#[derive(Debug)]
48pub struct Client {
49 conn: Option<Rc<Connection>>,
50 session_id: i32,
53}
54impl Client {
55 pub fn new() -> Client {
57 Client {
58 conn: None,
59 session_id: 0,
60 }
61 }
62 unsafe fn connect(&mut self) -> bool {
65 match &self.conn {
66 Some(c) => c.connected(),
67 None => match Connection::new() {
68 Ok(c) => {
69 let result = c.connected();
70 self.conn = Some(Rc::new(c));
71 result
72 }
73 Err(_) => false,
74 },
75 }
76 }
77 pub unsafe fn session(&mut self) -> Option<Session> {
86 if !self.connect() {
87 None
88 } else {
89 let sid = self.session_id;
90 self.session_id += 1;
91 let mut s = Session {
92 session_id: sid,
93 conn: self.conn.as_ref().unwrap().clone(),
94 last_tick_count: -2,
95 data: bytes::BytesMut::new(),
96 expired: false,
97 };
98 let d = s.wait_for_data(Duration::from_millis(16));
99 if DataUpdateResult::Updated == d {
100 Some(s)
101 } else {
102 None
103 }
104 }
105 }
106 pub unsafe fn wait_for_session(&mut self, wait: Duration) -> Option<Session> {
115 match &self.conn {
118 Some(c) => {
119 c.wait_for_new_data(wait);
120 self.session()
121 }
122 None => {
123 let start = Instant::now();
124 let loop_wait = Duration::from_millis(1000);
125 loop {
126 let r = self.session();
127 if r.is_some() || start.elapsed() > wait {
128 return r;
129 }
130 thread::sleep(loop_wait);
134 }
135 }
136 }
137 }
138}
139impl Default for Client {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145#[derive(Debug, PartialEq)]
147pub enum DataUpdateResult {
148 Updated,
150 NoUpdate,
152 FailedToCopyRow,
155 SessionExpired,
157}
158
159#[derive(Debug)]
170pub struct Session {
171 session_id: i32,
172 conn: Rc<Connection>,
173 last_tick_count: i32,
174 data: bytes::BytesMut,
175 expired: bool,
176}
177impl Session {
178 pub unsafe fn connected(&self) -> bool {
183 !self.expired()
184 }
185 pub unsafe fn expired(&self) -> bool {
190 self.expired || (!self.conn.connected())
191 }
192 pub unsafe fn wait_for_data(&mut self, wait: Duration) -> DataUpdateResult {
198 let r = self.get_new_data();
199 if r == DataUpdateResult::NoUpdate {
200 self.conn.wait_for_new_data(wait);
201 self.get_new_data()
202 } else {
203 r
204 }
205 }
206 pub unsafe fn get_new_data(&mut self) -> DataUpdateResult {
211 if self.expired() {
212 self.expired = true;
213 return DataUpdateResult::SessionExpired;
214 }
215 let (buf_hdr, row) = self.conn.lastest();
216 match buf_hdr.tick_count.cmp(&self.last_tick_count) {
217 Ordering::Greater => {
218 for _tries in 0..2 {
219 let curr_tick_count = buf_hdr.tick_count;
220 self.data.clear();
221 self.data.extend_from_slice(row);
222 if curr_tick_count == buf_hdr.tick_count {
223 self.last_tick_count = curr_tick_count;
224 return DataUpdateResult::Updated;
225 }
226 }
227 DataUpdateResult::FailedToCopyRow
228 }
229 Ordering::Less => {
230 self.expired = true;
233 DataUpdateResult::SessionExpired
234 }
235 Ordering::Equal => DataUpdateResult::NoUpdate,
236 }
237 }
238 pub unsafe fn dump_vars(&self) {
244 for var_header in self.conn.variables() {
245 let var = Var {
246 hdr: *var_header,
247 session_id: self.session_id,
248 };
249 let value = self.var_value(&var);
250 println!(
251 "{:40} {:32}: {:?}: {}: {}: {:?}",
252 var.desc(),
253 var.name(),
254 var.var_type(),
255 var.count(),
256 var.hdr.count_as_time,
257 value,
258 );
259 }
260 }
261 pub unsafe fn find_var(&self, name: &str) -> Option<Var> {
269 for var_header in self.conn.variables() {
270 if var_header.has_name(name) {
271 return Some(Var {
272 hdr: *var_header,
273 session_id: self.session_id,
274 });
275 }
276 }
277 None
278 }
279 pub unsafe fn var_value(&self, var: &Var) -> Value {
285 assert_eq!(
286 var.session_id, self.session_id,
287 "programmer error, Var was issued by a different Session"
288 );
289 let var_offset = var.hdr.offset as usize;
291 assert!(
292 var.size() + var_offset <= self.data.len(),
293 "The value appears to be outside the buffer"
294 );
295
296 let x = self.data.as_ptr().add(var_offset);
297 if var.hdr.count == 1 {
298 match var.hdr.var_type {
299 VarType::Char => Value::Char(std::ptr::read_unaligned(x as *const u8)),
300 VarType::Bool => Value::Bool(std::ptr::read_unaligned(x as *const bool)),
301 VarType::Int => Value::Int(std::ptr::read_unaligned(x as *const i32)),
302 VarType::Bitfield => Value::Bitfield(std::ptr::read_unaligned(x as *const i32)),
303 VarType::Float => Value::Float(std::ptr::read_unaligned(x as *const f32)),
304 VarType::Double => Value::Double(std::ptr::read_unaligned(x as *const f64)),
305 _ => todo!(), }
307 } else {
308 let l = var.count();
309 match var.hdr.var_type {
310 VarType::Char => Value::Chars(slice::from_raw_parts(x, l)),
311 VarType::Bool => Value::Bools(slice::from_raw_parts(x as *const bool, l)),
312 VarType::Int => Value::Ints(slice::from_raw_parts(x as *const i32, l)),
313 VarType::Bitfield => Value::Bitfields(slice::from_raw_parts(x as *const i32, l)),
314 VarType::Float => Value::Floats(slice::from_raw_parts(x as *const f32, l)),
315 VarType::Double => Value::Doubles(slice::from_raw_parts(x as *const f64, l)),
316 _ => todo!(), }
318 }
319 }
320 pub unsafe fn value<'a, T>(&'a self, var: &Var) -> Result<T, T::Error>
327 where
328 T: TryFrom<Value<'a>, Error = Error>,
329 {
330 let v = self.var_value(var);
331 v.try_into()
332 }
333 pub unsafe fn session_info_update(&self) -> i32 {
339 (*self.conn.header).session_info_update
340 }
341 pub unsafe fn session_info(&self) -> String {
347 let bytes = self.conn.session_info();
348 WINDOWS_1252.decode(bytes, DecoderTrap::Replace).unwrap()
350 }
351
352 pub unsafe fn broadcast_msg(&self, msg: flags::BroadcastMsg) -> Result<(), WIN32_ERROR> {
361 let (cmd_msg_id, (var1, var2)) = msg.params();
362 let x = makelong(cmd_msg_id, var1);
363 let r = SendNotifyMessageA(
364 HWND_BROADCAST,
365 self.conn.broadcast_msg_id,
366 WPARAM(x as usize),
367 LPARAM(var2),
368 );
369 if r.as_bool() {
370 Ok(())
371 } else {
372 Err(GetLastError())
373 }
374 }
375}
376fn makelong(var1: i16, var2: i16) -> isize {
377 let x = ((var1 as u32) & 0xFFFF) | (((var2 as u32) & 0xFFFF) << 16);
379 x as isize
380}
381
382pub struct Var {
390 hdr: IrsdkVarHeader,
391 session_id: i32,
392}
393impl Var {
394 pub fn var_type(&self) -> VarType {
396 self.hdr.var_type
397 }
398 pub fn name(&self) -> &str {
399 self.hdr.name().unwrap()
400 }
401 pub fn desc(&self) -> &str {
402 self.hdr.desc().unwrap()
403 }
404 pub fn unit(&self) -> &str {
405 self.hdr.unit().unwrap()
406 }
407 pub fn count(&self) -> usize {
412 self.hdr.count as usize
413 }
414 fn size(&self) -> usize {
416 self.var_type().size() * self.count()
417 }
418}
419impl fmt::Debug for Var {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 write!(f, "{} {:?} ({})", self.name(), self.var_type(), self.desc())
422 }
423}
424
425#[derive(Debug)]
430struct Connection {
431 file_mapping: HANDLE,
432 shared_mem: *mut c_void,
433 header: *mut IrsdkHeader,
434 new_data: HANDLE,
435 broadcast_msg_id: u32,
436}
437impl Connection {
438 unsafe fn new() -> Result<Self, WIN32_ERROR> {
441 let file_mapping =
442 Memory::OpenFileMappingA(Memory::FILE_MAP_READ.0, false, "Local\\IRSDKMemMapFileName");
443 if file_mapping.is_invalid() {
444 return Err(GetLastError());
445 }
446 let shared_mem = Memory::MapViewOfFile(file_mapping, Memory::FILE_MAP_READ, 0, 0, 0);
447 if shared_mem.is_null() {
448 let e = Err(GetLastError());
449 CloseHandle(file_mapping);
450 return e;
451 }
452 let new_data = Threading::OpenEventA(
453 windows::Win32::Storage::FileSystem::SYNCHRONIZE.0,
454 false,
455 "Local\\IRSDKDataValidEvent",
456 );
457 if new_data.is_invalid() {
458 let e = Err(GetLastError());
459 Memory::UnmapViewOfFile(shared_mem);
460 CloseHandle(file_mapping);
461 return e;
462 }
463 let bc_id = RegisterWindowMessageA("IRSDK_BROADCASTMSG");
464 if bc_id == 0 {
465 let e = Err(GetLastError());
466 CloseHandle(new_data);
467 Memory::UnmapViewOfFile(shared_mem);
468 CloseHandle(file_mapping);
469 return e;
470 }
471 Ok(Connection {
472 file_mapping,
473 shared_mem,
474 header: shared_mem as *mut IrsdkHeader,
475 new_data,
476 broadcast_msg_id: bc_id,
477 })
478 }
479 unsafe fn connected(&self) -> bool {
480 (*self.header)
481 .status
482 .intersects(flags::StatusField::CONNECTED)
483 }
484 unsafe fn variables(&self) -> &[IrsdkVarHeader] {
485 let vhbase = self
486 .shared_mem
487 .add((*self.header).var_header_offset as usize)
488 as *const IrsdkVarHeader;
489 slice::from_raw_parts(vhbase, (*self.header).num_vars as usize)
490 }
491 unsafe fn buffers(&self) -> &[IrsdkBuf] {
492 let l = (*self.header).num_buf as usize;
493 assert!(l <= IRSDK_MAX_BUFS);
494 &(*self.header).var_buf[..l]
495 }
496 unsafe fn lastest(&self) -> (&IrsdkBuf, &[u8]) {
499 let b = self.buffers();
500 let mut latest = &b[0];
501 for buff in b {
502 if buff.tick_count > latest.tick_count {
503 latest = buff;
504 }
505 }
506 let buf_len = (*self.header).buf_len as usize;
507 let src = self.shared_mem.add(latest.buf_offset as usize);
508 return (latest, slice::from_raw_parts(src as *const u8, buf_len));
509 }
510 unsafe fn wait_for_new_data(&self, wait: Duration) {
511 Threading::WaitForSingleObject(self.new_data, wait.as_millis().try_into().unwrap());
512 }
513 unsafe fn session_info(&self) -> &[u8] {
514 let p = self
515 .shared_mem
516 .add((*self.header).session_info_offset as usize) as *const u8;
517 let mut bytes = std::slice::from_raw_parts(p, (*self.header).session_info_len as usize);
518 for i in 0..bytes.len() {
521 if bytes[i] == 0 {
522 bytes = &bytes[0..i];
523 break;
524 }
525 }
526 bytes
527 }
528}
529impl Drop for Connection {
530 fn drop(&mut self) {
531 unsafe {
532 CloseHandle(self.new_data);
533 Memory::UnmapViewOfFile(self.shared_mem);
534 windows::Win32::Foundation::CloseHandle(self.file_mapping);
535 }
536 }
537}
538
539#[repr(C)]
540#[derive(Debug)]
541struct IrsdkBuf {
542 tick_count: i32, buf_offset: i32, pad: [i32; 2], }
546
547#[repr(C)]
548#[derive(Debug)]
549struct IrsdkHeader {
550 ver: i32, status: flags::StatusField, tick_rate: i32, session_info_update: i32, session_info_len: i32, session_info_offset: i32, num_vars: i32, var_header_offset: i32, num_buf: i32, buf_len: i32, pad1: [i32; 2], var_buf: [IrsdkBuf; IRSDK_MAX_BUFS], }
568
569#[repr(C)]
570#[derive(Clone, Copy, Debug)]
571struct IrsdkVarHeader {
572 var_type: VarType, offset: i32, count: i32, count_as_time: u8,
577 pad: [i8; 3], name: [u8; IRSDK_MAX_STRING],
580 desc: [u8; IRSDK_MAX_DESC],
581 unit: [u8; IRSDK_MAX_STRING], }
583impl IrsdkVarHeader {
584 fn name(&self) -> Result<&str, std::str::Utf8Error> {
585 unsafe { CStr::from_ptr(self.name.as_ptr() as *const c_char).to_str() }
586 }
587 fn desc(&self) -> Result<&str, std::str::Utf8Error> {
588 unsafe { CStr::from_ptr(self.desc.as_ptr() as *const c_char).to_str() }
589 }
590 fn unit(&self) -> Result<&str, std::str::Utf8Error> {
591 unsafe { CStr::from_ptr(self.unit.as_ptr() as *const c_char).to_str() }
592 }
593 fn has_name(&self, n: &str) -> bool {
594 if n.len() > IRSDK_MAX_STRING {
595 return false;
596 }
597 let b = n.as_bytes();
598 for (i, item) in b.iter().enumerate() {
599 if *item != self.name[i] {
600 return false;
601 }
602 }
603 for i in b.len()..IRSDK_MAX_STRING {
604 if self.name[i] != 0 {
605 return false;
606 }
607 }
608 true
609 }
610}
611
612#[derive(Debug)]
615pub enum Error {
616 InvalidType,
617 InvalidEnumValue(i32),
618}
619
620#[derive(Clone, Copy, Debug, PartialEq)]
622pub enum VarType {
623 Char = 0,
625 Bool = 1,
626
627 Int = 2,
629 Bitfield = 3,
630 Float = 4,
631
632 Double = 5,
634
635 #[deprecated]
637 Etcount = 6,
638}
639impl VarType {
640 fn size(&self) -> usize {
641 match *self {
642 VarType::Char => 1,
643 VarType::Bool => 1,
644 VarType::Int => 4,
645 VarType::Bitfield => 4,
646 VarType::Float => 4,
647 VarType::Double => 8,
648 _ => todo!(), }
650 }
651}
652#[derive(Debug, Clone, Copy, PartialEq)]
654pub enum Value<'a> {
655 Char(u8),
656 Chars(&'a [u8]),
657 Bool(bool),
658 Bools(&'a [bool]),
659 Int(i32),
660 Ints(&'a [i32]),
661 Bitfield(i32),
662 Bitfields(&'a [i32]),
663 Float(f32),
664 Floats(&'a [f32]),
665 Double(f64),
666 Doubles(&'a [f64]),
667}
668
669impl<'a> Value<'a> {
670 pub fn as_f64(&self) -> Result<f64, Error> {
671 match *self {
672 Value::Double(f) => Ok(f),
673 _ => Err(Error::InvalidType),
674 }
675 }
676 pub fn as_f32(&self) -> Result<f32, Error> {
677 match *self {
678 Value::Float(f) => Ok(f),
679 _ => Err(Error::InvalidType),
680 }
681 }
682 pub fn as_i32(&self) -> Result<i32, Error> {
683 match *self {
684 Value::Int(f) => Ok(f),
685 Value::Bitfield(f) => Ok(f),
686 _ => Err(Error::InvalidType),
687 }
688 }
689 pub fn as_bool(&self) -> Result<bool, Error> {
690 match *self {
691 Value::Bool(f) => Ok(f),
692 _ => Err(Error::InvalidType),
693 }
694 }
695 pub fn as_u8(&self) -> Result<u8, Error> {
696 match *self {
697 Value::Char(f) => Ok(f),
698 _ => Err(Error::InvalidType),
699 }
700 }
701 pub fn as_f64s(&self) -> Result<&'a [f64], Error> {
702 match *self {
703 Value::Doubles(f) => Ok(f),
704 _ => Err(Error::InvalidType),
705 }
706 }
707 pub fn as_f32s(self) -> Result<&'a [f32], Error> {
708 match self {
709 Value::Floats(f) => Ok(f),
710 _ => Err(Error::InvalidType),
711 }
712 }
713 pub fn as_i32s(&self) -> Result<&'a [i32], Error> {
714 match *self {
715 Value::Ints(f) => Ok(f),
716 _ => Err(Error::InvalidType),
717 }
718 }
719 pub fn as_bools(&self) -> Result<&'a [bool], Error> {
720 match *self {
721 Value::Bools(f) => Ok(f),
722 _ => Err(Error::InvalidType),
723 }
724 }
725 pub fn as_u8s(&self) -> Result<&'a [u8], Error> {
726 match *self {
727 Value::Chars(f) => Ok(f),
728 _ => Err(Error::InvalidType),
729 }
730 }
731}
732
733impl TryFrom<Value<'_>> for bool {
734 type Error = Error;
735 fn try_from(v: Value) -> Result<Self, Self::Error> {
736 v.as_bool()
737 }
738}
739impl TryFrom<Value<'_>> for u8 {
740 type Error = Error;
741 fn try_from(v: Value) -> Result<Self, Self::Error> {
742 v.as_u8()
743 }
744}
745impl TryFrom<Value<'_>> for i32 {
746 type Error = Error;
747 fn try_from(v: Value) -> Result<Self, Self::Error> {
748 v.as_i32()
749 }
750}
751impl TryFrom<Value<'_>> for f32 {
752 type Error = Error;
753 fn try_from(v: Value) -> Result<Self, Self::Error> {
754 v.as_f32()
755 }
756}
757impl TryFrom<Value<'_>> for f64 {
758 type Error = Error;
759 fn try_from(value: Value) -> Result<Self, Self::Error> {
760 value.as_f64()
761 }
762}
763impl<'a> TryFrom<Value<'a>> for &'a [bool] {
764 type Error = Error;
765 fn try_from(v: Value<'a>) -> Result<Self, Self::Error> {
766 v.as_bools()
767 }
768}
769impl<'a> TryFrom<Value<'a>> for &'a [u8] {
770 type Error = Error;
771 fn try_from(v: Value<'a>) -> Result<Self, Self::Error> {
772 v.as_u8s()
773 }
774}
775impl<'a> TryFrom<Value<'a>> for &'a [i32] {
776 type Error = Error;
777 fn try_from(v: Value<'a>) -> Result<Self, Self::Error> {
778 v.as_i32s()
779 }
780}
781impl<'a> TryFrom<Value<'a>> for &'a [f32] {
782 type Error = Error;
783 fn try_from(v: Value<'a>) -> Result<Self, Self::Error> {
784 v.as_f32s()
785 }
786}
787impl<'a> TryFrom<Value<'a>> for &'a [f64] {
788 type Error = Error;
789 fn try_from(v: Value<'a>) -> Result<Self, Self::Error> {
790 v.as_f64s()
791 }
792}
793impl TryFrom<Value<'_>> for flags::EngineWarnings {
794 type Error = Error;
795 fn try_from(v: Value) -> Result<Self, Self::Error> {
796 Ok(Self::from_bits_truncate(v.as_i32()?))
797 }
798}
799impl TryFrom<Value<'_>> for flags::Flags {
800 type Error = Error;
801 fn try_from(v: Value) -> Result<Self, Self::Error> {
802 Ok(Self::from_bits_truncate(v.as_i32()? as u32))
803 }
804}
805impl TryFrom<Value<'_>> for flags::SessionState {
806 type Error = Error;
807 fn try_from(value: Value) -> Result<Self, Self::Error> {
808 let v = value.as_i32()?;
809 match num::FromPrimitive::from_i32(v) {
810 Some(t) => Ok(t),
811 None => Err(Error::InvalidEnumValue(v)),
812 }
813 }
814}
815impl TryFrom<Value<'_>> for flags::TrackLocation {
816 type Error = Error;
817 fn try_from(value: Value) -> Result<Self, Self::Error> {
818 let v = value.as_i32()?;
819 match num::FromPrimitive::from_i32(v) {
820 Some(t) => Ok(t),
821 None => Err(Error::InvalidEnumValue(v)),
822 }
823 }
824}
825impl TryFrom<Value<'_>> for flags::TrackSurface {
826 type Error = Error;
827 fn try_from(value: Value) -> Result<Self, Self::Error> {
828 let v = value.as_i32()?;
829 match num::FromPrimitive::from_i32(v) {
830 Some(t) => Ok(t),
831 None => Err(Error::InvalidEnumValue(v)),
832 }
833 }
834}
835impl TryFrom<Value<'_>> for flags::CarLeftRight {
836 type Error = Error;
837 fn try_from(value: Value) -> Result<Self, Self::Error> {
838 let v = value.as_i32()?;
839 match num::FromPrimitive::from_i32(v) {
840 Some(t) => Ok(t),
841 None => Err(Error::InvalidEnumValue(v)),
842 }
843 }
844}
845impl TryFrom<Value<'_>> for flags::CameraState {
846 type Error = Error;
847 fn try_from(v: Value) -> Result<Self, Self::Error> {
848 Ok(Self::from_bits_truncate(v.as_i32()?))
849 }
850}
851impl TryFrom<Value<'_>> for flags::PitSvcFlags {
852 type Error = Error;
853 fn try_from(v: Value) -> Result<Self, Self::Error> {
854 Ok(Self::from_bits_truncate(v.as_i32()?))
855 }
856}
857impl TryFrom<Value<'_>> for flags::PitSvcStatus {
858 type Error = Error;
859 fn try_from(value: Value) -> Result<Self, Self::Error> {
860 let v = value.as_i32()?;
861 match num::FromPrimitive::from_i32(v) {
862 Some(t) => Ok(t),
863 None => Err(Error::InvalidEnumValue(v)),
864 }
865 }
866}
867impl TryFrom<Value<'_>> for flags::PaceMode {
868 type Error = Error;
869 fn try_from(value: Value) -> Result<Self, Self::Error> {
870 let v = value.as_i32()?;
871 match num::FromPrimitive::from_i32(v) {
872 Some(t) => Ok(t),
873 None => Err(Error::InvalidEnumValue(v)),
874 }
875 }
876}
877impl TryFrom<Value<'_>> for flags::PaceFlags {
878 type Error = Error;
879 fn try_from(v: Value) -> Result<Self, Self::Error> {
880 Ok(Self::from_bits_truncate(v.as_i32()?))
881 }
882}
883
884#[cfg(test)]
885mod tests {
886
887 use std::ptr;
888
889 use crate::flags::StatusField;
890
891 use super::*;
892
893 #[test]
894 fn test_makelong() {
895 assert_eq!(makelong(0, 0), 0);
896 assert_eq!(makelong(0, 0x12), 0x00120000);
897 assert_eq!(makelong(0x12, 0), 0x12);
898 assert_eq!(makelong(0x12, 0x24), 0x00240012);
899 assert_eq!(makelong(-1, -1), 0xFFFFFFFF);
900 assert_eq!(makelong(0, -1), 0xFFFF0000);
901 assert_eq!(makelong(-1, 0), 0x0000FFFF);
902 assert_eq!(makelong(0x1234, 0x5678), 0x56781234);
903 let m = |a, b| makelong(a, b) as usize;
905 assert_eq!(m(0, 0), 0);
906 assert_eq!(m(0, 0x12), 0x00120000);
907 assert_eq!(m(0x12, 0), 0x12);
908 assert_eq!(m(0x12, 0x24), 0x00240012);
909 assert_eq!(m(-1, -1), 0xFFFFFFFF);
910 assert_eq!(m(0, -1), 0xFFFF0000);
911 assert_eq!(m(-1, 0), 0x0000FFFF);
912 assert_eq!(m(0x1234, 0x5678), 0x56781234);
913 }
914
915 #[test]
916 fn test_var_size() {
917 let f = |t, c| {
918 Var {
919 session_id: 0,
920 hdr: IrsdkVarHeader {
921 var_type: t,
922 offset: 0,
923 count: c,
924 count_as_time: 0,
925 pad: [0, 0, 0], name: [0; IRSDK_MAX_STRING],
927 desc: [0; IRSDK_MAX_DESC],
928 unit: [0; IRSDK_MAX_STRING],
929 },
930 }
931 };
932 assert_eq!(512, f(VarType::Double, 64).size());
933 assert_eq!(24, f(VarType::Double, 3).size());
934 assert_eq!(8, f(VarType::Double, 1).size());
935 assert_eq!(12, f(VarType::Float, 3).size());
936 assert_eq!(4, f(VarType::Float, 1).size());
937 assert_eq!(12, f(VarType::Int, 3).size());
938 assert_eq!(4, f(VarType::Int, 1).size());
939 assert_eq!(12, f(VarType::Bitfield, 3).size());
940 assert_eq!(4, f(VarType::Bitfield, 1).size());
941 assert_eq!(3, f(VarType::Char, 3).size());
942 assert_eq!(1, f(VarType::Char, 1).size());
943 assert_eq!(3, f(VarType::Bool, 3).size());
944 assert_eq!(1, f(VarType::Bool, 1).size());
945 }
946
947 #[test]
948 fn test_irsdk_var_header() {
949 let mut h = IrsdkVarHeader {
950 var_type: VarType::Float,
951 offset: 32,
952 count: 1,
953 count_as_time: 0,
954 pad: [0; 3],
955 name: [0; IRSDK_MAX_STRING],
956 desc: [0; IRSDK_MAX_DESC],
957 unit: [0; IRSDK_MAX_STRING],
958 };
959 h.name[0] = 'b' as u8;
961 h.name[1] = 'o' as u8;
962 h.name[2] = 'b' as u8;
963 assert_eq!(Ok("bob"), h.name());
964 assert!(h.has_name("bob"));
965 assert!(!h.has_name("alice"));
966 assert!(!h.has_name("bobby"));
967 }
968
969 #[test]
970 fn test_var_value() {
971 let b = || IrsdkBuf {
972 tick_count: 1,
973 buf_offset: 0,
974 pad: [0, 2],
975 };
976 let mut h = IrsdkHeader {
977 ver: 2,
978 status: StatusField::CONNECTED,
979 tick_rate: 60,
980 session_info_update: 1,
981 session_info_len: 0,
982 session_info_offset: 100,
983 num_vars: 0,
984 var_header_offset: 0,
985 num_buf: 3,
986 buf_len: 12,
987 pad1: [0; 2],
988 var_buf: [b(), b(), b(), b()],
989 };
990 let mut s = Session {
991 session_id: 1,
992 conn: Rc::new(Connection {
993 file_mapping: HANDLE::default(),
994 shared_mem: ptr::null_mut(),
995 header: ptr::addr_of_mut!(h),
996 new_data: HANDLE::default(),
997 broadcast_msg_id: 1,
998 }),
999 last_tick_count: 1,
1000 data: bytes::BytesMut::new(),
1001 expired: false,
1002 };
1003 s.data.extend_from_slice(&[55, 56, 57, 58, 1, 0, 1, 0]);
1005 s.data.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
1007 s.data
1009 .extend_from_slice(&[0x00, 0x00, 0x80, 0x3f, 0, 0, 0, 0xc0]);
1010 s.data
1012 .extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0xc0, 2, 0, 0, 0, 0, 0, 0xF0, 0x3F]);
1013 let v = |t, o, c| Var {
1014 hdr: IrsdkVarHeader {
1015 var_type: t,
1016 offset: o,
1017 count: c,
1018 count_as_time: 0,
1019 pad: [0; 3],
1020 name: [0; IRSDK_MAX_STRING],
1021 desc: [0; IRSDK_MAX_DESC],
1022 unit: [0; IRSDK_MAX_STRING],
1023 },
1024 session_id: 1,
1025 };
1026 unsafe {
1027 assert_eq!(s.var_value(&v(VarType::Char, 0, 1)), Value::Char(55));
1028 assert_eq!(s.var_value(&v(VarType::Char, 1, 1)), Value::Char(56));
1029 assert_eq!(s.var_value(&v(VarType::Bool, 4, 1)), Value::Bool(true));
1030 assert_eq!(s.var_value(&v(VarType::Bool, 5, 1)), Value::Bool(false));
1031 assert_eq!(s.var_value(&v(VarType::Int, 8, 1)), Value::Int(0x04030201));
1032 assert_eq!(
1033 s.var_value(&v(VarType::Bitfield, 8, 1)),
1034 Value::Bitfield(0x04030201)
1035 );
1036 assert_eq!(s.var_value(&v(VarType::Float, 16, 1)), Value::Float(1.0));
1037 assert_eq!(s.var_value(&v(VarType::Float, 20, 1)), Value::Float(-2.0));
1038 assert_eq!(s.var_value(&v(VarType::Double, 24, 1)), Value::Double(-2.0));
1039 assert_eq!(
1040 s.var_value(&v(VarType::Double, 32, 1)),
1041 Value::Double(1.0000000000000004)
1042 );
1043 assert_eq!(
1044 s.var_value(&v(VarType::Char, 0, 3)),
1045 Value::Chars(&[55, 56, 57])
1046 );
1047 assert_eq!(
1048 s.var_value(&v(VarType::Bool, 4, 4)),
1049 Value::Bools(&[true, false, true, false])
1050 );
1051 assert_eq!(
1052 s.var_value(&v(VarType::Int, 8, 2)),
1053 Value::Ints(&[0x04030201, 0x08070605])
1054 );
1055 assert_eq!(
1056 s.var_value(&v(VarType::Bitfield, 8, 2)),
1057 Value::Bitfields(&[0x04030201, 0x08070605])
1058 );
1059 assert_eq!(
1060 s.var_value(&v(VarType::Float, 16, 2)),
1061 Value::Floats(&[1.0, -2.0])
1062 );
1063 assert_eq!(
1064 s.var_value(&v(VarType::Double, 24, 2)),
1065 Value::Doubles(&[-2.0, 1.0000000000000004])
1066 );
1067 }
1068 }
1069 #[test]
1070 #[should_panic]
1071 fn test_cant_read_past_buffer() {
1072 let b = || IrsdkBuf {
1074 tick_count: 1,
1075 buf_offset: 0,
1076 pad: [0, 2],
1077 };
1078 let mut h = IrsdkHeader {
1079 ver: 2,
1080 status: StatusField::CONNECTED,
1081 tick_rate: 60,
1082 session_info_update: 1,
1083 session_info_len: 0,
1084 session_info_offset: 100,
1085 num_vars: 0,
1086 var_header_offset: 0,
1087 num_buf: 3,
1088 buf_len: 12,
1089 pad1: [0; 2],
1090 var_buf: [b(), b(), b(), b()],
1091 };
1092 let mut s = Session {
1093 session_id: 1,
1094 conn: Rc::new(Connection {
1095 file_mapping: HANDLE::default(),
1096 shared_mem: ptr::null_mut(),
1097 header: ptr::addr_of_mut!(h),
1098 new_data: HANDLE::default(),
1099 broadcast_msg_id: 1,
1100 }),
1101 last_tick_count: 1,
1102 data: bytes::BytesMut::new(),
1103 expired: false,
1104 };
1105 s.data.extend_from_slice(&[1, 2, 3, 4]);
1106 let v = Var {
1107 hdr: IrsdkVarHeader {
1108 var_type: VarType::Int,
1109 offset: 2,
1110 count: 1,
1111 count_as_time: 0,
1112 pad: [0; 3],
1113 name: [0; IRSDK_MAX_STRING],
1114 desc: [0; IRSDK_MAX_DESC],
1115 unit: [0; IRSDK_MAX_STRING],
1116 },
1117 session_id: 1,
1118 };
1119 unsafe {
1120 s.var_value(&v);
1121 }
1122 }
1123}