cm_telemetry/f1/
f1_2022.rs

1use std::convert::TryFrom;
2use std::error::Error;
3use std::io::Cursor;
4
5use crate::{
6    TelemetryEvent,
7    TelemetryPacket,
8    f1::util::*,
9    f1::macros::*,
10};
11
12use binread::{BinRead, BinReaderExt};
13use num_enum::TryFromPrimitive;
14use bitflags::bitflags;
15
16/// F1_2022 implements the codemasters UDP telemetry protocol for "F1 22"
17/// See: https://answers.ea.com/t5/General-Discussion/F1-22-UDP-Specification/td-p/11551274
18/// Or: https://answers.ea.com/t5/General-Discussion/F1-22-UDP-Specification/td-p/11551274?attachment-id=657933
19
20pub enum F1_2022 {
21    Motion(Motion),
22    Session(Session),
23    LapData(LapData), 
24    Event(Event),
25    Participants(Participants),
26    CarSetup(CarSetup),
27    CarTelemetry(CarTelemetry),
28    CarStatus(CarStatus),
29    FinalClassification(FinalClassification),
30    LobbyInfo(LobbyInfo),
31    CarDamage(CarDamage),
32    SessionHistory(SessionHistory),
33}
34
35// HEADER
36#[derive(Debug, Default, BinRead)]
37pub struct Header
38{
39    pub packet_format: u16,             // 2022
40    pub game_major_version: u8,         // Game major version - "X.00"
41    pub game_minor_version: u8,         // Game minor version - "1.XX"
42    pub packet_version: u8,             // Version of this packet type, all start from 1
43    pub packet_id: u8,                  // Identifier for the packet type, see below
44    pub session_uid: u64,               // Unique identifier for the session
45    pub session_time: f32,              // Session timestamp
46    pub frame_identifier: u32,          // Identifier for the frame the data was retrieved on
47    pub player_car_index: u8,           // Index of player's car in the array
48    pub secondary_player_car_index: u8, // Index of secondary player's car in the array (splitscreen)
49                                        // 255 if no second player
50}
51
52// MOTION
53#[derive(Debug, BinRead)]
54pub struct Motion {
55    pub header: Header,
56
57    #[br(count = 22)]
58    pub car_motion_data: Vec<CarMotionData>,        // Data for all cars on track (22)
59
60    // Extra player car ONLY data
61    pub suspension_position: WheelValue<f32>,       // Note: All wheel arrays have the following order:
62    pub suspension_velocity: WheelValue<f32>,       // RL, RR, FL, FR
63    pub suspension_acceleration: WheelValue<f32>,   // RL, RR, FL, FR
64    pub wheel_speed: WheelValue<f32>,               // Speed of each wheel
65    pub wheel_slip: WheelValue<f32>,                // Slip ratio for each wheel
66    pub local_velocity: Coordinates<f32>,           // Velocity in local space
67    pub angular_velocity: Coordinates<f32>,         // Angular velocity
68    pub angular_acceleration: Coordinates<f32>,     // Angular acceleration
69    pub front_wheel_angle: f32,                     // Current front wheels angle in radians
70}
71
72player_data!(Motion, CarMotionData, car_motion_data);
73
74#[derive(Debug, Default, BinRead)]
75pub struct CarMotionData {
76    pub world_position: Coordinates<f32>,       // World space position
77    pub world_velocity: Coordinates<f32>,       // Velocity in world space
78    pub world_forward_dir: Coordinates<i16>,    // World space forward direction (normalised)
79    pub world_right_dir: Coordinates<i16>,      // World space right direction (normalised)
80    pub g_force_lateral: f32,                   // Lateral G-Force component
81    pub g_force_longitudinal: f32,              // Longitudinal G-Force component
82    pub g_force_vertical: f32,                  // Vertical G-Force component
83    pub yaw: f32,                               // Yaw angle in radians
84    pub pitch: f32,                             // Pitch angle in radians
85    pub roll: f32,                              // Roll angle in radians
86}
87
88// SESSION
89#[derive(Debug, BinRead)]
90pub struct Session {
91    pub header: Header,
92    pub weather: Weather,                                       // Weather - 0 = clear, 1 = light cloud, 2 = overcast
93                                                                // 3 = light rain, 4 = heavy rain, 5 = storm
94    pub track_temperature: i8,                                  // Track temp. in degrees celsius
95    pub air_temperature: i8,                                    // Air temp. in degrees celsius
96    pub total_laps: u8,                                         // Total number of laps in this race
97    pub track_length: u16,                                      // Track length in metres
98    pub session_type: SessionType, 	                            // 0 = unknown, 1 = P1, 2 = P2, 3 = P3, 4 = Short P
99                                                                // 5 = Q1, 6 = Q2, 7 = Q3, 8 = Short Q, 9 = OSQ
100                                            	                // 10 = R, 11 = R2, 12 = R3, 13 = Time Trial
101    pub track: Track,                                           // -1 for unknown, see appendix
102    pub formula: Formula,                                       // Formula, 0 = F1 Modern, 1 = F1 Classic, 2 = F2,
103                                                                // 3 = F1 Generic, 4 = Beta, 5 = Supercars
104                                                                // 6 = Esports, 7 = F2 2021
105    pub session_time_left: u16,                                 // Time left in session in seconds
106    pub session_duration: u16,                                  // Session duration in seconds
107    pub pit_speed_limit: u8,                                    // Pit speed limit in kilometres per hour
108    pub game_paused: u8,                                        // Whether the game is paused – network game only
109    pub is_spectating: u8,                                      // Whether the player is spectating
110    pub spectator_car_index: u8,                                // Index of the car being spectated
111    pub sli_pro_native_support: u8,                             // SLI Pro support, 0 = inactive, 1 = active
112    pub number_of_marshal_zones: u8,                            // Number of marshal zones to follow
113    #[br(count = 21)]
114    pub marshal_zones: Vec<MarshalZone>,                        // List of marshal zones – max 21
115    pub safety_car_status: SafetyCarStatus,                     // 0 = no safety car, 1 = full
116                                                                // 2 = virtual, 3 = formation lap
117    #[br(map = |x: u8| x > 0)]
118    pub network_game: bool,                                     // 0 = offline, 1 = online
119    pub number_of_weather_forecast_samples: u8,                 // Number of weather samples to follow
120    #[br(count = 56)]                                           //Array does not work due to size being > 32
121    pub weather_forecast_samples: Vec<WeatherForecastSample>,   // Array of weather forecast samples
122    pub forecast_accuracy: ForecastAccuracy,                    // 0 = Perfect, 1 = Approximate
123    pub ai_difficulty: u8,                                      // AI Difficulty rating – 0-110
124    pub season_link_identifier: u32,                            // Identifier for season - persists across saves
125    pub weekend_link_identifier: u32,                           // Identifier for weekend - persists across saves
126    pub session_link_identifier: u32,                           // Identifier for session - persists across saves
127    pub pit_stop_window_ideal_lap: u8,                          // Ideal lap to pit on for current strategy (player)
128    pub pit_stop_window_latest_lap: u8,                         // Latest lap to pit on for current strategy (player)
129    pub pit_stop_rejoin_position: u8,                           // Predicted position to rejoin at (player)
130    #[br(map = |x: u8| x > 0)]
131    pub steering_assist: bool,                                  // 0 = off, 1 = on
132    pub braking_assist: BrakingAssist,                          // 0 = off, 1 = low, 2 = medium, 3 = high
133    pub gearbox_assist: GearboxAssist,                          // 1 = manual, 2 = manual & suggested gear, 3 = auto
134    #[br(map = |x: u8| x > 0)]
135    pub pit_assist: bool,                                       // 0 = off, 1 = on
136    #[br(map = |x: u8| x > 0)]
137    pub pit_release_assist: bool,                               // 0 = off, 1 = on
138    #[br(map = |x: u8| x > 0)]
139    pub ers_assist: bool,                                       // 0 = off, 1 = on
140    #[br(map = |x: u8| x > 0)]
141    pub drs_assist: bool,                                       // 0 = off, 1 = on
142    pub dynamic_racing_line: RacingLine,                        // 0 = off, 1 = corners only, 2 = full
143    pub dynamic_racing_line_type: RacingLineType,               // 0 = 2D, 1 = 3D
144    pub game_mode: GameMode,                                    // Game mode id - see appendix
145    pub rule_set: RuleSet,                                      // Ruleset - see appendix
146    pub time_of_day: u32,                                       // Local time of day - minutes since midnight
147    pub session_length: SessionLength,                          // 0 = None, 2 = Very Short, 3 = Short, 4 = Medium
148                                                                // 5 = Medium Long, 6 = Long, 7 = Full
149}
150
151#[derive(Debug, Default, TryFromPrimitive)]
152#[repr(u8)]
153pub enum Weather {
154    #[default]
155    Clear,
156    LigthCloud,
157    Overcast,
158    LightRain,
159    HeavyRain,
160    Storm,
161    Unknown = 255,
162}
163
164binread_enum!(Weather, u8);
165
166#[derive(Debug, Default, TryFromPrimitive)]
167#[repr(u8)]
168pub enum SessionType {
169    #[default]
170    Unknown,
171    Practice1,
172    Practice2,
173    Practice3,
174    ShortPractice,
175    Qualifier1,
176    Qualifier2,
177    Qualifier3,
178    ShortQualifier,
179    OSQ,
180    Race,
181    Formula2Race,
182    R3,
183    TimeTrial,
184}
185
186binread_enum!(SessionType, u8);
187
188
189#[derive(Debug, Default, TryFromPrimitive)]
190#[repr(i8)]
191pub enum Track {
192    #[default]
193    Unknown = -1,
194    Melbourne,
195    PaulRicard,
196    Shanghai,
197    Sakhir,
198    Catalunya,
199    Monaco,
200    Montreal,
201    Silverstone,
202    Hockenheim,
203    Hungaroring,
204    Spa,
205    Monza,
206    Singapore,
207    Suzuka,
208    AbuDahbi,
209    Texas,
210    Brazil,
211    Austria,
212    Sochi,
213    Mexico,
214    Baku,
215    SakhirShort,
216    SilverstoneShort,
217    TexasShort,
218    SuzukaShort,
219    Hanoi,
220    Zandvoort,
221    Imola,
222    Portimao,
223    Jeddah,
224    Miami,
225}
226
227binread_enum!(Track, i8);
228
229#[derive(Debug, Default, TryFromPrimitive)]
230#[repr(u8)]
231pub enum Formula {
232    #[default]
233    F1Modern,
234    F1Classic,
235    F2,
236    F1Generic,
237    Beta,
238    Supercars,
239    Esports,
240    Unknown = 255,
241}
242
243binread_enum!(Formula, u8);
244
245
246#[derive(Debug, Default, BinRead)]
247pub struct MarshalZone {
248    pub zone_start: f32,        // Fraction (0..1) of way through the lap the marshal zone starts
249    pub zone_flag: ZoneFlag,    // -1 = invalid/unknown, 0 = none, 1 = green, 2 = blue, 3 = yellow, 4 = red
250}
251
252#[derive(Debug, Default, TryFromPrimitive)]
253#[repr(i8)]
254pub enum ZoneFlag {
255    #[default]
256    Unknown = -1,
257    None,
258    Green,
259    Blue,
260    Yellow,
261    Red,
262}
263
264binread_enum!(ZoneFlag, i8);
265
266#[derive(Debug, Default, TryFromPrimitive)]
267#[repr(u8)]
268pub enum SafetyCarStatus {
269    #[default]
270    NoSafetyCar,
271    FullSafetyCar,
272    VirtualSafetyCar,
273    FormationLap,
274    Unknown = 255,
275}
276
277binread_enum!(SafetyCarStatus, u8);
278
279#[derive(Debug, Default, BinRead)]
280pub struct WeatherForecastSample {
281    pub session_type: SessionType,                          // 0 = unknown, 1 = P1, 2 = P2, 3 = P3, 4 = Short P, 5 = Q1
282                                                            // 6 = Q2, 7 = Q3, 8 = Short Q, 9 = OSQ, 10 = R, 11 = R2
283                                                            // 12 = R3, 13 = Time Trial
284    pub time_offset: u8,                                    // Time in minutes the forecast is for
285    pub weather: Weather,                                   // Weather - 0 = clear, 1 = light cloud, 2 = overcast
286                                                            // 3 = light rain, 4 = heavy rain, 5 = storm
287    pub track_temperature: i8,                              // Track temp. in degrees Celsius
288    pub track_temperature_change: WeatherTemperatureTrend,  // Track temp. change – 0 = up, 1 = down, 2 = no change
289    pub air_temperature: i8,                                // Air temp. in degrees celsius
290    pub air_temperature_change: WeatherTemperatureTrend,    // Air temp. change – 0 = up, 1 = down, 2 = no change
291    pub rain_percentage: u8,                                // Rain percentage (0-100)
292}
293
294#[derive(Debug, Default, TryFromPrimitive)]
295#[repr(i8)]
296pub enum WeatherTemperatureTrend {
297    #[default]
298    Unknown = -1,
299    Up,
300    Down,
301    NoChange,
302}
303
304binread_enum!(WeatherTemperatureTrend, i8);
305
306#[derive(Debug, Default, TryFromPrimitive)]
307#[repr(u8)]
308pub enum ForecastAccuracy {
309    #[default]
310    Perfect,
311    Approximate,
312    Unknown = 255,
313}
314
315binread_enum!(ForecastAccuracy, u8);
316
317#[derive(Debug, Default, TryFromPrimitive)]
318#[repr(u8)]
319pub enum BrakingAssist {
320    #[default]
321    Off,
322    Low,
323    Medium,
324    High,
325    Unknown = 255,
326}
327
328binread_enum!(BrakingAssist, u8);
329
330#[derive(Debug, Default, TryFromPrimitive)]
331#[repr(u8)]
332pub enum GearboxAssist {
333    #[default]
334    Manual = 1,
335    ManualAndSuggest,
336    Auto,
337    Unknown = 255,
338}
339
340binread_enum!(GearboxAssist, u8);
341
342#[derive(Debug, Default, TryFromPrimitive)]
343#[repr(u8)]
344pub enum RacingLine {
345    #[default]
346    Off,
347    CornersOnly,
348    Full,
349    Unknown = 255,
350}
351
352binread_enum!(RacingLine, u8);
353
354#[derive(Debug, Default, TryFromPrimitive)]
355#[repr(u8)]
356pub enum RacingLineType {
357    #[default]
358    TwoD,
359    ThreeD,
360    Unknown = 255,
361}
362
363binread_enum!(RacingLineType, u8);
364
365#[derive(Debug, Default, TryFromPrimitive)]
366#[repr(u8)]
367pub enum GameMode {
368    #[default]
369    EventMode,
370    GrandPrix = 3,
371    TimeTrial = 5,
372    Splitscreen,
373    OnlineCustom,
374    OnlineLeague,
375    CareerInvitational = 11,
376    ChampionshipInvitational,
377    Championship,
378    OnlineChampionship,
379    OnlineWeeklyEvent,
380    Career22 = 19,
381    Career22Online,
382    Benchmark = 127,
383    Unknown = 255,
384}
385
386binread_enum!(GameMode, u8);
387
388#[derive(Debug, Default, TryFromPrimitive)]
389#[repr(u8)]
390pub enum RuleSet {
391    #[default]
392    PracticeAndQualifying,
393    Race,
394    TimeTrial,
395    TimeAttack = 4,
396    CheckpointChallenge = 6,
397    Autocross = 8,
398    Drift,
399    AverageSpeedZone,
400    RivalDuel,
401    Unknown = 255,
402}
403
404binread_enum!(RuleSet, u8);
405
406#[derive(Debug, Default, TryFromPrimitive)]
407#[repr(u8)]
408pub enum SessionLength {
409    #[default]
410    None,
411    VeryShort = 2,
412    Short,
413    Medium,
414    MediumLong,
415    Long,
416    Full,
417    Unknown = 255,
418}
419
420binread_enum!(SessionLength, u8);
421
422// LAP
423#[derive(Debug, BinRead)]
424pub struct LapData {
425    pub header: Header,
426    #[br(count = 22)]
427    pub laps: Vec<Lap>,                 // Lap data for all cars on track
428    pub	time_trial_pb_car_idx: u8,      // Index of Personal Best car in time trial (255 if invalid)
429    pub	time_trial_rival_car_idx: u8,   // Index of Rival car in time trial (255 if invalid)
430}
431
432player_data!(LapData, Lap, laps);
433
434#[derive(Debug, Default, BinRead)]
435pub struct Lap {
436    pub last_lap_time_ms: u32,                      // Last lap time in milliseconds
437    pub current_lap_time_ms: u32,                   // Current time around the lap in milliseconds
438    pub sector_time_ms: (u16, u16),                 // sector1, sector2 (no sector3 for some reason)
439    pub lap_distance: f32,                          // Distance vehicle is around current lap in metres – could
440                                                    // be negative if line hasn’t been crossed yet
441    pub total_distance: f32,                        // Total distance travelled in session in metres – could
442                                                    // be negative if line hasn’t been crossed yet
443    pub safety_car_delta: f32,                      // Delta in seconds for safety car
444    pub car_position: u8,                           // Car race position
445    pub current_lap_number: u8,                     // Current lap number
446    pub pit_status: PitStatus,                      // 0 = none, 1 = pitting, 2 = in pit area
447    pub num_pit_stops: u8,                          // Number of pit stops taken in this race
448    pub sector: Sector,                             // 0 = sector1, 1 = sector2, 2 = sector3
449    #[br(map = |x: u8| x > 0)]
450    pub current_lap_invalid: bool,                  // Current lap invalid - 0 = valid, 1 = invalid
451    pub penalties: u8,                              // Accumulated time penalties in seconds to be added
452    pub warnings: u8,                               // Accumulated number of warnings issued
453    pub num_unserved_drive_through_penalties: u8,   // Num drive through pens left to serve
454    pub num_unserved_stop_go_penalties: u8,         // Num stop go pens left to serve
455    pub grid_position: u8,                          // Grid position the vehicle started the race in
456    pub driver_status: DriverStatus,                // Status of driver - 0 = in garage, 1 = flying lap
457                                                    // 2 = in lap, 3 = out lap, 4 = on track
458    pub result_status: ResultStatus,                // Result status - 0 = invalid, 1 = inactive, 2 = active
459                                                    // 3 = finished, 4 = didnotfinish, 5 = disqualified
460                                                    // 6 = not classified, 7 = retired
461    #[br(map = |x: u8| x > 0)]
462    pub pit_lane_timer_active: bool,                // Pit lane timing, 0 = inactive, 1 = active
463    pub pit_lane_time_in_lane_ms: u16,              // If active, the current time spent in the pit lane in ms
464    pub pit_stop_timer_ms: u16,                     // Time of the actual pit stop in ms
465    pub pit_stop_should_serve_penalty: u8,          // Whether the car should serve a penalty at this stop
466}
467	           
468
469
470#[derive(Debug, Default, TryFromPrimitive)]
471#[repr(u8)]
472pub enum PitStatus {
473    #[default]
474    None,
475    Pitting,
476    InPitArea,
477    Unknown = 255,
478}
479
480binread_enum!(PitStatus, u8);
481
482#[derive(Debug, Default, TryFromPrimitive)]
483#[repr(u8)]
484pub enum Sector {
485    Sector1,
486    Sector2,
487    Sector3,
488    #[default]
489    Unknown = 255,
490}
491
492binread_enum!(Sector, u8);
493
494#[derive(Debug, Default, TryFromPrimitive)]
495#[repr(u8)]
496pub enum DriverStatus {
497    InGarage,
498    FlyingLap,
499    InLap,
500    OutLap,
501    OnTrack,
502    #[default]
503    Unknown = 255,
504}
505
506binread_enum!(DriverStatus, u8);
507
508#[derive(Debug, Default, TryFromPrimitive)]
509#[repr(u8)]
510pub enum ResultStatus {
511    Invalid,
512    Inactive,
513    Active,
514    Finished,
515    DidNotFinished,
516    Disqualified,
517    NotClassified,
518    Retired,
519    #[default]
520    Unknown = 255,
521}
522
523binread_enum!(ResultStatus, u8);
524
525// EVENT
526#[derive(Debug)]
527pub struct Event {
528    pub header: Header,
529    pub event_data_details: EventDataDetail,    // Event details - should be interpreted differently
530                                                // for each type  	
531}
532
533// Event is a bit more complicated since the event_data_details
534// depends on some of the packet details, so we cant simply derive and expect it to work
535impl binread::BinRead for Event {
536    type Args = ();
537    fn read_options<R: binread::io::Read + binread::io::Seek>(
538        reader: &mut R,
539        options: &binread::ReadOptions,
540        args: Self::Args,
541    ) -> binread::BinResult<Self> {
542        let header = Header::read_options(reader, options, args)?; // re-use Header BinRead default implementation.
543
544        // Read next 4 bytes for event string identification
545        let event_code_bytes = <[u8; 4]>::read_options(reader, options, args)?;
546        let event_code = std::str::from_utf8(&event_code_bytes).unwrap_or("UNKW");
547
548        let event_data_details = match event_code {
549            "SSTA" => EventDataDetail::SessionStarted,
550            "SEND" => EventDataDetail::SessionEnded,
551            "FTLP" => {
552                let idx = <u8>::read_options(reader, options, args)?;
553                let time = <f32>::read_options(reader, options, args)?;
554                EventDataDetail::FastestLap(idx, time)
555            }
556            "RTMT" => {
557                let idx = <u8>::read_options(reader, options, args)?;
558                EventDataDetail::Retirement(idx)
559            }
560            "DRSE" => EventDataDetail::DRSEnabled,
561            "DRSD" => EventDataDetail::DRSDisabled,
562            "TMPT" => {
563                let idx = <u8>::read_options(reader, options, args)?;
564                EventDataDetail::TeamMateInPits(idx)
565            }
566            "CHQF" => EventDataDetail::ChequeredFlag,
567            "RCWN" => {
568                let idx = <u8>::read_options(reader, options, args)?;
569                EventDataDetail::RaceWinner(idx)
570            }
571            "PENA" => {
572                let detail = PenaltyEventDetail::read_options(reader, options, args)?;
573                EventDataDetail::Penalty(detail)
574            }
575            "SPTP" => {
576                let detail = SpeedTrapDetail::read_options(reader, options, args)?;
577                EventDataDetail::SpeedTrap(detail)
578            }
579            "STLG" => {
580                let num_lights = <u8>::read_options(reader, options, args)?;
581                EventDataDetail::StartLights(num_lights)
582            }
583            "LGOT" => {
584                EventDataDetail::LightsOut
585            }
586            "DTSV" => {
587                let idx = <u8>::read_options(reader, options, args)?;
588                EventDataDetail::DriveThroughServed(idx)
589            }
590            "SGSV" => {
591                let idx = <u8>::read_options(reader, options, args)?;
592                EventDataDetail::StopGoServed(idx)
593            }
594            "FLBK" => {
595                let flashback_frame_identifier = <u32>::read_options(reader, options, args)?;
596                let flashback_session_time = <f32>::read_options(reader, options, args)?;
597                EventDataDetail::Flashback(flashback_frame_identifier, flashback_session_time)
598            }
599            "BUTN" => {
600                let button_status = ButtonFlags::from_bits(<u32>::read_options(reader, options, args)?).unwrap_or_default();
601                EventDataDetail::ButtonStatus(button_status)
602            }
603            _ => EventDataDetail::Unknown,
604        };
605
606        Ok(Event {
607            header,
608            event_data_details,
609        })
610    }
611}
612
613#[derive(Debug)]
614pub enum EventDataDetail {
615    SessionStarted, 
616    SessionEnded, 
617    FastestLap(u8, f32),        // vehicleIdx; Vehicle index of car achieving fastest lap
618                                // lapTime; Lap time is in seconds
619    Retirement(u8),             // vehicleIdx; Vehicle index of car retiring
620    DRSEnabled,
621    DRSDisabled,
622    TeamMateInPits(u8),         // vehicleIdx; Vehicle index of team mate
623    ChequeredFlag,
624    RaceWinner(u8),             // vehicleIdx; Vehicle index of the race winner
625    Penalty(PenaltyEventDetail),
626    SpeedTrap(SpeedTrapDetail),
627    StartLights(u8),            // numLights; Number of lights showing
628    LightsOut,
629    DriveThroughServed(u8),     // vehicleIdx; Vehicle index of the vehicle serving drive through
630    StopGoServed(u8),           // vehicleIdx; Vehicle index of the vehicle serving stop go
631    Flashback(u32, f32),        // flashbackFrameIdentifier; Frame identifier flashed back to
632                                // flashbackSessionTime; Session time flashed back to
633    ButtonStatus(ButtonFlags),  // buttonStatus; Bit flags specifying which buttons are being pressed
634                                // currently - see appendices
635    Unknown,                    // not part of the spec, added to satisfy match
636}
637
638bitflags! {
639    pub struct ButtonFlags: u32 {
640        const CROSS_OR_A        = 0x00000001;
641        const TRIANGLE_OR_Y     = 0x00000002;
642        const CIRCLE_OR_B       = 0x00000004;
643        const SQUARE_OR_X       = 0x00000008;
644        const D_PAD_LEFT        = 0x00000010;
645        const D_PAD_RIGHT       = 0x00000020;
646        const D_PAD_UP          = 0x00000040;
647        const D_PAD_DOWN        = 0x00000080;
648        const OPTIONS_OR_MENU   = 0x00000100;
649        const L1_OR_LB          = 0x00000200;
650        const R1_OR_RB          = 0x00000400;
651        const L2_OR_LT          = 0x00000800;
652        const R2_OR_RT          = 0x00001000;
653        const LEFT_STICK_CLICK  = 0x00002000;
654        const RIGHT_STICK_CLICK = 0x00004000;
655        const RIGHT_STICK_LEFT  = 0x00008000;
656        const RIGHT_STICK_RIGHT = 0x00010000;
657        const RIGHT_STICK_UP    = 0x00020000;
658        const RIGHT_STICK_DOWN  = 0x00040000;
659        const SPECIAL           = 0x00080000;
660        const UDP_ACTION_1      = 0x00100000;
661        const UDP_ACTION_2      = 0x00200000;
662        const UDP_ACTION_3      = 0x00400000;
663        const UDP_ACTION_4      = 0x00800000;
664        const UDP_ACTION_5      = 0x01000000;
665        const UDP_ACTION_6      = 0x02000000;
666        const UDP_ACTION_7      = 0x04000000;
667        const UDP_ACTION_8      = 0x08000000;
668        const UDP_ACTION_9      = 0x10000000;
669        const UDP_ACTION_10     = 0x20000000;
670        const UDP_ACTION_11     = 0x40000000;
671        const UDP_ACTION_12     = 0x80000000;
672    }
673}
674
675impl Default for ButtonFlags {
676    fn default() -> Self {
677        ButtonFlags::empty()
678    }
679}
680
681#[derive(Debug, Default, BinRead)]
682pub struct PenaltyEventDetail {
683    pub penalty_type: PenaltyType,          // Penalty type – see Appendices
684    pub infrigement_type: InfringementType, // Infringement type – see Appendices
685    pub vehicle_index: u8,                  // Vehicle index of the car the penalty is applied to
686    pub other_vehicle_index: u8,            // Vehicle index of the other car involved
687    pub time: u8,                           // Time gained, or time spent doing action in seconds
688    pub lap_number: u8,                     // Lap the penalty occurred on
689    pub places_gained: u8,                  // Number of places gained by this
690}
691
692#[derive(Debug, Default, TryFromPrimitive)]
693#[repr(u8)]
694pub enum PenaltyType {
695    DriveThrough,
696    StopGo,
697    GridPenalty,
698    PenaltyReminder,
699    TimePenalty,
700    Warning,
701    Disqualified,
702    RemovedFromFormationLap,
703    ParkedTooLongTimer,
704    TyreRegulations,
705    ThisLapInvalidated,
706    ThisAndNextLapInvalidated,
707    ThisLapInvalidatedWithNoReason,
708    ThisAndNextLapInvalidatedWithNoReason,
709    ThisAndPreviousLapInvalidated,
710    ThisAndPreviousLapInvalidatedWithNoReason,
711    Retired,
712    BlackFlagTimer,
713    #[default]
714    Unknown = 255,
715}
716
717binread_enum!(PenaltyType, u8);
718
719#[derive(Debug, Default, TryFromPrimitive)]
720#[repr(u8)]
721pub enum InfringementType {
722    BlockingBySlowDriving,
723    BlockingByWrongWayDriving,
724    ReversingOffTheStartLine,
725    BigCollision,
726    SmallCollision,
727    CollisionFailedToHandBackPositionSingle,
728    CollisionFailedToHandBackPositionMultiple,
729    CornerCuttingGainedTime,
730    CornerCuttingOvertakeSingle,
731    CornerCuttingOvertakeMultiple,
732    CrossedPitExitLane,
733    IgnoringBlueFlags,
734    IgnoringYellowFlags,
735    IgnoringDriveThrough,
736    TooManyDriveThroughs,
737    DriveThroughReminderServeWithinNLaps,
738    DriveThroughReminderServeThisLap,
739    PitLaneSpeeding,
740    ParkedForTooLong,
741    IgnoringTyreRegulations,
742    TooManyPenalties,
743    MultipleWarnings,
744    ApproachingDisqualification,
745    TyreRegulationsSelectSingle,
746    TyreRegulationsSelectMultiple,
747    LapInvalidatedCornerCutting,
748    LapInvalidatedRunningWide,
749    CornerCuttingRanWideGainedTimeMinor,
750    CornerCuttingRanWideGainedTimeSignificant,
751    CornerCuttingRanWideGainedTimeExtreme,
752    LapInvalidatedWallRiding,
753    LapInvalidatedFlashbackUsed,
754    LapInvalidatedResetToTrack,
755    BlockingThePitlane,
756    JumpStart,
757    SafetyCarToCarCollision,
758    SafetyCarIllegalOvertake,
759    SafetyCarExceedingAllowedPace,
760    VirtualSafetyCarExceedingAllowedPace,
761    FormationLapBelowAllowedSpeed,
762    FormationLapParking,
763    RetiredMechanicalFailure,
764    RetiredTerminallyDamaged,
765    SafetyCarFallingTooFarBack,
766    BlackFlagTimer,
767    UnservedStopGoPenalty,
768    UnservedDriveThroughPenalty,
769    EngineComponentChange,
770    GearboxChange,
771    ParcFermeChange,
772    LeagueGridPenalty,
773    RetryPenalty,
774    IllegalTimeGain,
775    MandatoryPitstop,
776    AttributeAssigned,
777    #[default]
778    Unknown = 255,
779}
780
781binread_enum!(InfringementType, u8);
782
783#[derive(Debug, Default, BinRead)]
784pub struct SpeedTrapDetail {
785    pub vehicle_index: u8,                      // Vehicle index of the vehicle triggering speed trap
786    pub speed: f32,                             // Top speed achieved in kilometres per hour
787    #[br(map = |x: u8| x > 0)]
788    pub is_overall_fastest_in_session: bool,    // Overall fastest speed in session = 1, otherwise 0
789    #[br(map = |x: u8| x > 0)]
790    pub is_driver_fastest_in_session: bool,     // Fastest speed for driver in session = 1, otherwise 0
791    pub fastest_vehicle_index_in_session: u8,   // Vehicle index of the vehicle that is the fastest
792                                                // in this session
793    pub fastest_speed_in_session: f32,          // Speed of the vehicle that is the fastest
794                                                // in this session
795}
796
797// PARTICIPANTS
798#[derive(Debug, BinRead)]
799pub struct Participants {
800    pub header: Header,
801    pub num_active_cars: u8,    // Number of active cars in the data – should match number of
802                                // cars on HUD
803    #[br(count = 22)]
804    pub participants_data: Vec<ParticipantsData>,
805}
806
807player_data!(Participants, ParticipantsData, participants_data);
808
809#[derive(Debug, Default, BinRead)]
810pub struct ParticipantsData {
811    #[br(map = |x: u8| x > 0)]
812    pub ai_controlled: bool,                    // Whether the vehicle is AI (1) or Human (0) controlled
813    pub driver: Driver,                         // Driver id - see appendix, 255 if network human
814    pub network_id: u8,                         // Network id – unique identifier for network players
815    pub team: Team,                             // Team id - see appendix
816    #[br(map = |x: u8| x > 1)]
817    pub my_team: bool,                          // My team flag – 1 = My Team, 0 = otherwise
818    pub race_number: u8,                        // Race number of the car
819    pub nationality: Nationality,               // Nationality of the driver
820    #[br(parse_with = participant_name_parser)]
821    pub name: String,                           // Name of participant in UTF-8 format – null terminated
822                                                // Will be truncated with … (U+2026) if too long
823    #[br(map = |x: u8| x > 1)]
824    pub your_telemetry_public: bool,            // The player's UDP setting, 0 = restricted, 1 = public
825}
826
827fn participant_name_parser<R: binread::io::Read + binread::io::Seek>(
828    reader: &mut R,
829    _: &binread::ReadOptions,
830    _: (),
831) -> binread::BinResult<String> {
832    let mut bytes: [u8; 48] = [0; 48]; // names for participants are 48 bytes wide
833    reader.read_exact(&mut bytes)?;
834
835    let driver_name = std::str::from_utf8(&bytes)
836        .unwrap_or("UNKW")
837        .trim_matches(char::from(0)); // trim any additional null-bytes
838
839    Ok(String::from(driver_name))
840}
841
842
843#[derive(Debug, Default, TryFromPrimitive)]
844#[repr(u8)]
845pub enum Driver {
846    CarlosSainz,
847    DaniilKvyat,
848    DanielRicciardo,
849    FernandoAlonso,
850    FelipeMassa,
851    KimiRaikkonen = 6,
852    LewisHamilton,
853    MaxVerstappen = 9,
854    NicoHulkenburg,
855    KevinMagnussen,
856    RomainGrosjean,
857    SebastianVettel,
858    SergioPerez,
859    ValtteriBottas,
860    EstebanOcon = 17,
861    LanceStroll = 19,
862    ArronBarnes,
863    MartinGiles,
864    AlexMurray,
865    LucasRoth,
866    IgorCorreia,
867    SophieLevasseur,
868    JonasSchiffer,
869    AlainForest,
870    JayLetourneau,
871    EstoSaari,
872    YasarAtiyeh,
873    CallistoCalabresi,
874    NaotaIzum,
875    HowardClarke,
876    WilheimKaufmann,
877    MarieLaursen,
878    FlavioNieves,
879    PeterBelousov,
880    KlimekMichalski,
881    SantiagoMoreno,
882    BenjaminCoppens,
883    NoahVisser,
884    GertWaldmuller,
885    JulianQuesada,
886    DanielJones,
887    ArtemMarkelov,
888    TadasukeMakino,
889    SeanGelael,
890    NyckDeVries,
891    JackAitken,
892    GeorgeRussell,
893    MaximilianGunther,
894    NireiFukuzumi,
895    LucaGhiotto,
896    LandoNorris,
897    SergioSetteCamara,
898    LouisDeletraz,
899    AntonioFuoco,
900    CharlesLeclerc,
901    PierreGasly,
902    AlexanderAlbon = 62,
903    NicholasLatifi,
904    DorianBoccolacci,
905    NikoKari,
906    RobertoMerhi,
907    ArjunMaini,
908    AlessioLorandi,
909    RubenMeijer,
910    RashidNair,
911    JackTremblay,
912    DevonButler,
913	LukasWeber,
914    AntonioGiovinazzi,
915    RobertKubica,
916    AlainProst,
917    AyrtonSenna,
918    NobuharuMatsushita,
919    NikitaMazepin,
920    GuanyaZhou,
921    MickSchumacher,
922    CallumIlott,
923    JuanManuelCorrea,
924    JordanKing,
925    MahaveerRaghunathan,
926    TatianaCalderon,
927    AnthoineHubert,
928    GuilianoAlesi,
929    RalphBoschung,
930    MichaelSchumacher,
931    DanTicktum,
932    MarcusArmstrong,
933    ChristianLundgaard,
934    YukiTsunoda,
935    JehanDaruvala,
936    GulhermeSamaia,
937    PedroPiquet,
938    FelipeDrugovich,
939    RobertSchwartzman,
940    RoyNissany,
941    MarinoSato,
942    AidanJackson,
943    CasperAkkerman,
944    JensonButton = 109,
945    DavidCoulthard,
946    NicoRosberg,
947    OscarPiastri,
948    LiamLawson,
949    JuriVips,
950    TheoPourchaire,
951    RichardVerschoor,
952    LirimZendeli,
953    DavidBeckmann,
954    AlessioDeledda = 121,
955    BentViscaal,
956    EnzoFittipaldi,
957    MarkWebber = 125,
958    JacquesVilleneuve,
959    JakeHughes,
960    FrederikVesti,
961    OlliCaldwell,
962    LoganSargeant,
963    CemBolukbasi,
964    AyumaIwasa,
965    ClementNovolak,
966    DennisHauger,
967    CalanWilliams,
968    JackDoohan,
969    AmauryCordeel,
970    MikaHakkinen,
971
972    #[default]
973    Unknown,
974    Human = 255, // Used for time trial "ghost" drivers that appear randomly
975}
976
977binread_enum!(Driver, u8);
978
979#[derive(Debug, Default, TryFromPrimitive)]
980#[repr(u8)]
981pub enum Team {
982    Mercedes,
983    Ferrari,
984    RedBullRacing,
985    Williams,
986    AstonMartin,
987    Alpine,
988    AlphaTauri,
989    Haas,
990    McLaren,
991    AlfaRomeo,
992    Mercedes2020 = 85,
993    Ferrari2020,
994    RedBull2020,
995    Williams2020,
996    RacingPoint2020,
997    Renault2020,
998    AlphaTauri2020,
999    Haas2020,
1000    McLaren2020,
1001    AlfaRomeo2020,
1002    AstonMartinDB11V12,
1003    AstonMartinVantageF1Edition,
1004    AstonMartinVantageSafetyCar,
1005    FerrariF8Tributo,
1006    FerrariRoma,
1007    McLaren720S,
1008    McLarenArtura,
1009    MercedesAMGGTBlackSeriesSafetyCar,
1010    MercedesAMGGTRPro,
1011    F1CustomTeam,
1012    Prema2021,
1013    UniVirtuosi2021,
1014    Carlin2021,
1015    Hitech2021,
1016    ArtGP2021,
1017    MPMotorsport2021,
1018    Charouz2021,
1019    Dams2021,
1020    Campos2021,
1021    BWT2021,
1022    Trident2021,
1023    MercedesAMGGTBlackSeries,
1024    Prema2022,
1025    Virtuosi2022,
1026    Carlin2022,
1027    Hitech2022,
1028    ArtGP2022,
1029    MPMotorsport2022,
1030    Charouz2022,
1031    Dams2022,
1032    Campos2022,
1033    VanAmersfoortRacing2022,
1034    Trident2022,
1035    #[default]
1036    Unknown = 255,
1037}
1038
1039binread_enum!(Team, u8);
1040
1041#[derive(Debug, Default, TryFromPrimitive)]
1042#[repr(u8)]
1043pub enum Nationality {
1044    #[default]
1045    Unknown,
1046    American,
1047    Argentinean,
1048    Australian,
1049    Austrian,
1050    Azerbaijani,
1051    Bahraini,
1052    Belgian,
1053    Bolivian,
1054    Brazilian,
1055    British,
1056    Bulgarian,
1057    Cameroonian,
1058    Canadian,
1059    Chilean,
1060    Chinese,
1061    Colombian,
1062    CostaRican,
1063    Croatian,
1064    Cypriot,
1065    Czech,
1066    Danish,
1067    Dutch,
1068    Ecuadorian,
1069    English,
1070    Emirian,
1071    Estonian,
1072    Finnish,
1073    French,
1074    German,
1075    Ghanaian,
1076    Greek,
1077    Guatemalan,
1078    Honduran,
1079    HongKonger,
1080    Hungarian,
1081    Icelander,
1082    Indian,
1083    Indonesian,
1084    Irish,
1085    Israeli,
1086    Italian,
1087    Jamaican,
1088    Japanese,
1089    Jordanian,
1090    Kuwaiti,
1091    Latvian,
1092    Lebanese,
1093    Lithuanian,
1094    Luxembourger,
1095    Malaysian,
1096    Maltese,
1097    Mexican,
1098    Monegasque,
1099    NewZealander,
1100    Nicaraguan,
1101    NorthernIrish,
1102    Norwegian,
1103    Omani,
1104    Pakistani,
1105    Panamanian,
1106    Paraguayan,
1107    Peruvian,
1108    Polish,
1109    Portuguese,
1110    Qatari,
1111    Romanian,
1112    Russian,
1113    Salvadoran,
1114    Saudi,
1115    Scottish,
1116    Serbian,
1117    Singaporean,
1118    Slovakian,
1119    Slovenian,
1120    SouthKorean,
1121    SouthAfrican,
1122    Spanish,
1123    Swedish,
1124    Swiss,
1125    Thai,
1126    Turkish,
1127    Uruguayan,
1128    Ukrainian,
1129    Venezuelan,
1130    Barbadian,
1131    Welsh,
1132    Vietnamese,
1133}
1134
1135binread_enum!(Nationality, u8);
1136
1137// CAR SETUP
1138#[derive(Debug, BinRead)]
1139pub struct CarSetup {
1140    pub header: Header,
1141    #[br(count = 22)]
1142    pub car_setup_data: Vec<CarSetupData>,
1143}
1144
1145player_data!(CarSetup, CarSetupData, car_setup_data);
1146
1147#[derive(Debug, Default, BinRead)]
1148pub struct CarSetupData {
1149    pub wing: FrontRearValue<u8>,               // Wing aero
1150    pub on_throttle: u8,                        // Differential adjustment on throttle (percentage)
1151    pub off_throttle: u8,                       // Differential adjustment off throttle (percentage)
1152    pub camber: FrontRearValue<f32>,            // Camber angle (suspension geometry)
1153    pub toe: FrontRearValue<f32>,               // Toe angle (suspension geometry)
1154    pub suspension: FrontRearValue<u8>,         // Suspension
1155    pub anti_roll_bar: FrontRearValue<u8>,      // Anti-roll bar
1156    pub suspension_height: FrontRearValue<u8>,  // Ride height
1157    pub brake_pressure: u8,                     // Brake pressure (percentage)
1158    pub brake_bias: u8,                         // Brake bias (percentage)
1159    pub type_pressure: WheelValue<f32>,         // Tyre pressure (PSI)
1160    pub ballast: u8,                            // Ballast
1161    pub fuel_load: f32,                         // Fuel load
1162}
1163
1164// CAR TELEMETRY
1165#[derive(Debug, BinRead)]
1166pub struct CarTelemetry {
1167    pub header: Header,
1168    #[br(count = 22)]
1169    pub car_telemetry_data: Vec<CarTelemetryData>,
1170    pub mfd_panel: MFDPanel,                                                                // Index of MFD panel open - 255 = MFD closed
1171                                                                                            // Single player, race – 0 = Car setup, 1 = Pits
1172                                                                                            // 2 = Damage, 3 =  Engine, 4 = Temperatures
1173                                                                                            // May vary depending on game mode
1174    pub mfd_panel_secondary_player: MFDPanel,                                               // See above
1175    #[br(map = |x: i8| if x == 0 { Gear::Unknown } else { Gear::try_from(x).unwrap() })]
1176    pub suggested_gear: Gear,                                                               // Suggested gear for the player (1-8)
1177                                                                                            // 0 if no gear suggested
1178}
1179
1180player_data!(CarTelemetry, CarTelemetryData, car_telemetry_data);
1181
1182#[derive(Debug, Default, BinRead)]
1183pub struct CarTelemetryData {
1184    pub speed: u16,                                 // Speed of car in kilometres per hour
1185    pub throttle: f32,                              // Amount of throttle applied (0.0 to 1.0)
1186    pub steer: f32,                                 // Steering (-1.0 (full lock left) to 1.0 (full lock right))
1187    pub brake: f32,                                 // Amount of brake applied (0.0 to 1.0)
1188    pub clutch: u8,                                 // Amount of clutch applied (0 to 100)
1189    #[br(map = |x: i8| Gear::try_from(x).unwrap())]
1190    pub gear: Gear,                                 // Gear selected (1-8, N=0, R=-1)
1191    pub engine_rpm: u16,                            // Engine RPM
1192    #[br(map = |x: u8| x > 0)]
1193    pub drs: bool,                                  // 0 = off, 1 = on
1194    pub rev_lights_percent: u8,                     // Rev lights indicator (percentage)
1195    pub rev_lights_bit_value: u16,                  // Rev lights (bit 0 = leftmost LED, bit 14 = rightmost LED)
1196    pub brake_temp: WheelValue<u16>,                // Brakes temperature (celsius)
1197    pub tyres_surface_temp: WheelValue<u8>,         // Tyres surface temperature (celsius)
1198    pub tyres_inner_temp: WheelValue<u8>,           // Tyres inner temperature (celsius)
1199    pub engine_temp: u16,                           // Engine temperature (celsius)
1200    pub tyres_pressure: WheelValue<f32>,            // Tyres pressure (PSI)
1201    #[br(parse_with = surface_type_parser)]
1202    pub surface_type: WheelValue<Surface>,          // Driving surface, see appendices
1203}
1204
1205#[derive(Debug, Default, TryFromPrimitive)]
1206#[repr(i8)]
1207pub enum Gear {
1208    Reverse = -1,
1209    Neutral,
1210    First,
1211    Second,
1212    Third,
1213    Fourth,
1214    Fifth,
1215    Sixth,
1216    Seventh,
1217    Eigth,
1218    #[default]
1219    Unknown = 127,
1220}
1221
1222binread_enum!(Gear, i8);
1223
1224#[derive(Debug, Default, TryFromPrimitive)]
1225#[repr(u8)]
1226pub enum Surface {
1227    Tarmac,
1228    RumbleStrip,
1229    Concrete,
1230    Rock,
1231    Gravel,
1232    Mud,
1233    Sand,
1234    Grass,
1235    Water,
1236    Cobblestone,
1237    Metal,
1238    Ridged,
1239    #[default]
1240    Unknown = 255,
1241}
1242
1243binread_enum!(Surface, u8);
1244
1245fn surface_type_parser<R: binread::io::Read + binread::io::Seek>(
1246    reader: &mut R,
1247    _: &binread::ReadOptions,
1248    _: (),
1249) -> binread::BinResult<WheelValue<Surface>> {
1250    let mut bytes: [u8; 4] = [0; 4];
1251    reader.read_exact(&mut bytes)?;
1252
1253    Ok(WheelValue::<Surface> {
1254        rear_left: Surface::try_from(bytes[0]).unwrap_or(Surface::Unknown),
1255        rear_right: Surface::try_from(bytes[1]).unwrap_or(Surface::Unknown),
1256        front_left: Surface::try_from(bytes[2]).unwrap_or(Surface::Unknown),
1257        front_right: Surface::try_from(bytes[3]).unwrap_or(Surface::Unknown),
1258    })
1259}
1260
1261#[derive(Debug, Default, TryFromPrimitive)]
1262#[repr(u8)]
1263pub enum MFDPanel {
1264    CarSetup,
1265    Pits,
1266    Damage,
1267    Engine,
1268    Temperatures,
1269    #[default]
1270    Unknown = 128,
1271    Closed = 255,
1272}
1273
1274binread_enum!(MFDPanel, u8);
1275
1276// CAR STATUS
1277
1278
1279#[derive(Debug, BinRead)]
1280pub struct CarStatus {
1281    pub header: Header,
1282    #[br(count = 22)]
1283    pub car_status_data: Vec<CarStatusData>,
1284}
1285
1286player_data!(CarStatus, CarStatusData, car_status_data);
1287
1288#[derive(Debug, Default, BinRead)]
1289pub struct CarStatusData {
1290    pub traction_control: u8,                                   // Traction control - 0 = off, 1 = medium, 2 = full
1291    #[br(map = |x: u8| x > 0)]
1292    pub anti_lock_brakes: bool,                                 // 0 (off) - 1 (on)
1293    pub fuel_mix: FuelMix,                                      // Fuel mix - 0 = lean, 1 = standard, 2 = rich, 3 = max
1294    pub front_brake_bias: u8,                                   // Front brake bias (percentage)
1295    #[br(map = |x: u8| x > 0)]
1296    pub pit_limiter_status: bool,                               // Pit limiter status - 0 = off, 1 = on
1297    pub fuel_in_tank: f32,                                      // Current fuel mass
1298    pub fuel_capacity: f32,                                     // Fuel capacity
1299    pub fuel_remaining_laps: f32,                               // Fuel remaining in terms of laps (value on MFD)
1300    pub max_rpm: u16,                                           // Cars max RPM, point of rev limiter
1301    pub idle_rpm: u16,                                          // Cars idle RPM
1302    pub max_gears: u8,                                          // Maximum number of gears
1303    #[br(map = |x: u8| x > 0)]
1304    pub drs_allowed: bool,                                      // 0 = not allowed, 1 = allowed
1305    #[br(map = |x: u16| if x > 0 { DRSActivationDistance::Distance(x) } else { DRSActivationDistance::NotAvailable })]
1306    pub drs_activation_distance: DRSActivationDistance,         // 0 = DRS not available, non-zero - DRS will be available
1307                                                                // in [X] metres
1308    pub tyres_compound: TyreCompound,                           // F1 Modern - 16 = C5, 17 = C4, 18 = C3, 19 = C2, 20 = C1
1309                                                                // 7 = inter, 8 = wet
1310                                                                // F1 Classic - 9 = dry, 10 = wet
1311                                                                // F2 – 11 = super soft, 12 = soft, 13 = medium, 14 = hard
1312                                                                // 15 = wet
1313    pub tyres_visual: TyreVisual,                               // F1 visual (can be different from actual compound)
1314                                                                // 16 = soft, 17 = medium, 18 = hard, 7 = inter, 8 = wet
1315                                                                // F1 Classic – same as above
1316                                                                // F2 ‘19, 15 = wet, 19 – super soft, 20 = soft
1317                                                                // 21 = medium , 22 = hard
1318    pub tyres_ages_lap: u8,                                     // Age in laps of the current set of tyres
1319    pub vehicle_fia_flag: FiaFlag,                              // -1 = invalid/unknown, 0 = none, 1 = green
1320                                                                // 2 = blue, 3 = yellow, 4 = red
1321    pub ers_data: ERS,                                          // ERS Data
1322    pub network_paused: u8,                                     // Whether the car is paused in a network game
1323}
1324
1325#[derive(Debug, Default, TryFromPrimitive)]
1326#[repr(u8)]
1327pub enum FuelMix {
1328    Lean,
1329    Standard,
1330    Rich,
1331    Max,
1332    #[default]
1333    Unknown,
1334}
1335
1336binread_enum!(FuelMix, u8);
1337
1338#[derive(Debug, Default)]
1339#[repr(u16)]
1340pub enum DRSActivationDistance {
1341    #[default]
1342    NotAvailable,
1343    Distance(u16),
1344}
1345
1346#[derive(Debug, Default, TryFromPrimitive)]
1347#[repr(u8)]
1348pub enum TyreCompound {
1349    Inter = 7,
1350    Wet,
1351    F1ClassicDry,
1352    F1ClassicWet,
1353    F2SuperSoft,
1354    F2Soft,
1355    F2Medium,
1356    F2Hard,
1357    F2Wet,
1358    C5,
1359    C4,
1360    C3,
1361    C2,
1362    C1,
1363    #[default]
1364    Unknown,
1365}
1366
1367binread_enum!(TyreCompound, u8);
1368
1369#[derive(Debug, Default, TryFromPrimitive)]
1370#[repr(u8)]
1371pub enum TyreVisual {
1372    Inter = 7,
1373    Wet,
1374    ClassicDry = 9,
1375    ClassicWet,
1376    F2Wet = 15,
1377    Soft,
1378    Medium,
1379    Hard,
1380    F2SuperSoft,
1381    F2Soft,
1382    F2Medium,
1383    F2Hard,
1384    #[default]
1385    Unknown = 255,
1386}
1387
1388binread_enum!(TyreVisual, u8);
1389
1390#[derive(Debug, Default, TryFromPrimitive)]
1391#[repr(i8)]
1392pub enum FiaFlag {
1393    #[default]
1394    Unknown = -1,
1395    None,
1396    Green,
1397    Blue,
1398    Yellow,
1399    Red,
1400}
1401
1402binread_enum!(FiaFlag, i8);
1403
1404#[derive(Debug, Default, BinRead)]
1405pub struct ERS {
1406    pub stored_energy: f32,             // ERS energy store in Joules
1407    pub deploy_mode: ERSDeployMode,     // ERS deployment mode, 0 = none, 1 = medium
1408                                        // 2 = hotlap, 3 = overtake
1409    pub harvested_this_lap_mguk: f32,   // ERS energy harvested this lap by MGU-K
1410    pub harvested_this_lap_mguh: f32,   // ERS energy harvested this lap by MGU-H
1411    pub deployed_this_lap: f32,         // ERS energy deployed this lap
1412}
1413
1414#[derive(Debug, Default, TryFromPrimitive)]
1415#[repr(u8)]
1416pub enum ERSDeployMode {
1417    None,
1418    Medium,
1419    Hotlap,
1420    Overtake,
1421    #[default]
1422    Unknown = 255,
1423}
1424
1425binread_enum!(ERSDeployMode, u8);
1426
1427// FINAL CLASSIFICATION
1428#[derive(Debug, BinRead)]
1429pub struct FinalClassification {
1430    pub header: Header,
1431    pub number_of_cars: u8,     // Number of cars in the final classification
1432    #[br(count = 22)]
1433    pub final_classification_data: Vec<FinalClassificationData>,
1434}
1435
1436player_data!(FinalClassification, FinalClassificationData, final_classification_data);
1437
1438#[derive(Debug, Default, BinRead)]
1439pub struct FinalClassificationData {
1440    pub position: u8,                           // Finishing position
1441    pub number_of_laps: u8,                     // Number of laps completed
1442    pub grid_position: u8,                      // Grid position of the car
1443    pub points: u8,                             // Number of points scored
1444    pub number_of_pit_stops: u8,                // Number of pit stops made
1445    pub result_status: ResultStatus,            // Result status - 0 = invalid, 1 = inactive, 2 = active
1446                                                // 3 = finished, 4 = didnotfinish, 5 = disqualified
1447                                                // 6 = not classified, 7 = retired
1448    pub best_lap_time_ms: u32,                  // Best lap time of the session in milliseconds
1449    pub total_race_time: f64,                   // Total race time in seconds without penalties
1450    pub penalties_time_s: u8,                   // Total penalties accumulated in seconds
1451    pub number_of_penalties: u8,                // Number of penalties applied to this driver
1452    pub number_of_tyre_stints: u8,              // Number of tyres stints up to maximum
1453    #[br(count = 8)]
1454    pub tyre_stints_actual: Vec<TyreCompound>,  // Actual tyres used by this driver
1455    #[br(count = 8)]
1456    pub tyre_stints_visual: Vec<TyreVisual>,    // Visual tyres used by this driver
1457    #[br(count = 8)]
1458    pub tyre_stints_end_laps: Vec<u8>,          // The lap number stints end on
1459}
1460
1461// LOBBY INFO
1462#[derive(Debug, BinRead)]
1463pub struct LobbyInfo {
1464    pub header: Header,
1465    pub number_of_players: u8,                  // Number of players in the lobby data
1466    #[br(count = 22)]
1467    pub lobby_players: Vec<LobbyInfoData>,
1468}
1469
1470player_data!(LobbyInfo, LobbyInfoData, lobby_players);
1471
1472impl LobbyInfo {
1473    pub fn players(self) -> Vec<LobbyInfoData> {
1474        let number_of_players = self.number_of_players as usize;
1475        self.lobby_players
1476            .into_iter()
1477            .take(number_of_players)
1478            .collect()
1479    }
1480}
1481
1482#[derive(Debug, Default, BinRead)]
1483pub struct LobbyInfoData {
1484    #[br(map = |x: u8| x > 0)]
1485    pub ai_controlled: bool,                    // Whether the vehicle is AI (1) or Human (0) controlled
1486    pub team: Team,                             // Team id - see appendix (255 if no team currently selected)
1487    pub nationality: Nationality,               // Nationality of the driver
1488    #[br(parse_with = participant_name_parser)]
1489    pub name: String,                           // Name of participant in UTF-8 format – null terminated
1490                                                // Will be truncated with ... (U+2026) if too long
1491    pub car_number: u8,                         // Car number of the player
1492    pub status: LobbyStatus,                    // 0 = not ready, 1 = ready, 2 = spectating
1493}      
1494
1495#[derive(Debug, Default, TryFromPrimitive)]
1496#[repr(u8)]
1497pub enum LobbyStatus {
1498    NotReady,
1499    Ready,
1500    Spectating,
1501    #[default]
1502    Unknown,
1503}
1504
1505binread_enum!(LobbyStatus, u8);
1506
1507// CAR DAMAGE
1508#[derive(Debug, BinRead)]
1509pub struct CarDamage {
1510    pub header: Header,
1511    #[br(count = 22)]
1512    pub car_damage_data: Vec<CarDamageData>,
1513}
1514
1515player_data!(CarDamage, CarDamageData, car_damage_data);
1516
1517#[derive(Debug, Default, BinRead)]
1518pub struct CarDamageData
1519{
1520    pub tyres_wear: WheelValue<u8>,     // Tyre wear (percentage)
1521    pub tyres_damage: WheelValue<u8>,   // Tyre damage (percentage)
1522    pub brakes_damage: WheelValue<u8>,  // Brakes damage (percentage)
1523    pub wing_damage: WingValue<u8>,     // Wing damage (percentage)
1524    pub floor_damage: u8,               // Floor damage (percentage)
1525    pub diffuser_damage: u8,            // Diffuser damage (percentage)
1526    pub sidepod_damage: u8,             // Sidepod damage (percentage)
1527    #[br(map = |x: u8| x > 0)]
1528    pub drs_fault: bool,                // Indicator for DRS fault, 0 = OK, 1 = fault
1529    #[br(map = |x: u8| x > 0)]
1530    pub ers_fault: bool,                // Indicator for ERS fault, 0 = OK, 1 = fault
1531    pub gear_box_damage: u8,            // Gear box damage (percentage)
1532    pub engine_damage: u8,              // Engine damage (percentage)
1533    pub engine_mguh_wear: u8,           // Engine wear MGU-H (percentage)
1534    pub engine_es_wear: u8,             // Engine wear ES (percentage)
1535    pub engine_ce_wear: u8,             // Engine wear CE (percentage)
1536    pub engine_ice_wear: u8,            // Engine wear ICE (percentage)
1537    pub engine_mguk_wear: u8,           // Engine wear MGU-K (percentage)
1538    pub engine_tc_wear: u8,             // Engine wear TC (percentage)
1539    #[br(map = |x: u8| x > 0)]
1540    pub engine_blown: bool,             // Engine blown, 0 = OK, 1 = fault
1541    #[br(map = |x: u8| x > 0)]
1542    pub engine_seized: bool,            // Engine seized, 0 = OK, 1 = fault
1543}
1544
1545// SESSION HISTORY
1546#[derive(Debug, BinRead)]
1547pub struct SessionHistory
1548{
1549    pub header: Header,                                         // Header
1550    pub car_index: u8,                                          // Index of the car this lap data relates to
1551    pub num_laps: u8,                                           // Num laps in the data (including current partial lap)
1552    pub num_tyre_stints: u8,                                    // Number of tyre stints in the data
1553    pub best_lap_time_lap_num: u8,                              // Lap the best lap time was achieved on
1554    pub best_sector1_lap_num: u8,                               // Lap the best Sector 1 time was achieved on
1555    pub best_sector2_lap_num: u8,                               // Lap the best Sector 2 time was achieved on
1556    pub best_sector3_lap_num: u8,                               // Lap the best Sector 3 time was achieved on
1557    #[br(count = 100)]
1558    pub lap_history_data: Vec<LapHistoryData>,                  // 100 laps of data max
1559    #[br(count = 8)]
1560    pub tyre_stints_history_data: Vec<TyreStintHistoryData>,
1561}
1562
1563#[derive(Debug, Default, BinRead)]
1564pub struct LapHistoryData
1565{
1566    pub lap_time_ms: u32,                   // Lap time in milliseconds
1567    pub sector_times_ms: (u16, u16, u16),   // Sector times in milliseconds
1568    
1569    #[br(parse_with = lap_valid_flags_aprser)]
1570    pub lap_valid_bit_flags: LapValidFlags, // 0x01 bit set-lap valid,      0x02 bit set-sector 1 valid
1571                                            // 0x04 bit set-sector 2 valid, 0x08 bit set-sector 3 valid
1572}
1573
1574bitflags! {
1575    pub struct LapValidFlags: u8 {
1576        const LAP_VALID         = 0x01;
1577        const SECTOR_1_VALID    = 0x02;
1578        const SECTOR_2_VALID    = 0x04;
1579        const SECTOR_3_VALID    = 0x08;
1580    }
1581}
1582
1583impl Default for LapValidFlags {
1584    fn default() -> Self {
1585        LapValidFlags::empty()
1586    }
1587}
1588
1589fn lap_valid_flags_aprser<R: binread::io::Read + binread::io::Seek>(
1590    reader: &mut R,
1591    _: &binread::ReadOptions,
1592    _: (),
1593) -> binread::BinResult<LapValidFlags> {
1594    let mut bytes: [u8; 1] = [0; 1];
1595    reader.read_exact(&mut bytes)?;
1596
1597    Ok(LapValidFlags::from_bits(bytes[0]).unwrap_or_default())
1598}
1599
1600#[derive(Debug, Default, BinRead)]
1601pub struct TyreStintHistoryData
1602{
1603    pub end_lap: u8,                            // Lap the tyre usage ends on (255 of current tyre)
1604    pub tyre_actual_compound: TyreCompound,     // Actual tyres used by this driver
1605    pub tyre_visual_compound: TyreVisual,       // Visual tyres used by this driver
1606}
1607
1608// PARSING
1609impl TelemetryEvent for F1_2022 {
1610    fn from_packet(packet: &TelemetryPacket) -> Result<F1_2022, Box<dyn Error>> {
1611        if packet.len() < 24 {
1612            return Err(Box::from("Packet is too small to contain a header"));
1613        }
1614
1615        let packet_id = packet[5]; // packet_id
1616        let mut reader = Cursor::new(packet);
1617        match packet_id {
1618            0 => {
1619                let data: Motion = reader.read_le()?;
1620                Ok(F1_2022::Motion(data))
1621            }
1622            1 => {
1623                let data: Session = reader.read_le()?;
1624                Ok(F1_2022::Session(data))
1625            }
1626            2 => {
1627                let data: LapData = reader.read_le()?;
1628                Ok(F1_2022::LapData(data))
1629            }
1630            3 => {
1631                let data: Event = reader.read_le()?;
1632                Ok(F1_2022::Event(data))
1633            }
1634            4 => {
1635                let data: Participants = reader.read_le()?;
1636                Ok(F1_2022::Participants(data))
1637            }
1638            5 => {
1639                let data: CarSetup = reader.read_le()?;
1640                Ok(F1_2022::CarSetup(data))
1641            }
1642            6 => {
1643                let data: CarTelemetry = reader.read_le()?;
1644                Ok(F1_2022::CarTelemetry(data))
1645            }
1646            7 => {
1647                let data: CarStatus = reader.read_le()?;
1648                Ok(F1_2022::CarStatus(data))
1649            }
1650            8 => {
1651                let data: FinalClassification = reader.read_le()?;
1652                Ok(F1_2022::FinalClassification(data))
1653            }
1654            9 => {
1655                let data: LobbyInfo = reader.read_le()?;
1656                Ok(F1_2022::LobbyInfo(data))
1657            }
1658            10 => {
1659                let data: CarDamage = reader.read_le()?;
1660                Ok(F1_2022::CarDamage(data))
1661            }
1662            11 => {
1663                let data: SessionHistory = reader.read_le()?;
1664                Ok(F1_2022::SessionHistory(data))
1665            }
1666            id => Err(Box::from(format!("Unknown packet type: {}", id))),
1667        }
1668    }
1669}