cm_telemetry/f1/
f1_2020.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;
14
15/// F1_2020 implements the codemasters UDP telemetry protocol for "F1 2020"
16/// see: https://forums.codemasters.com/topic/50942-f1-2020-udp-specification/
17pub enum F1_2020 {
18    Motion(Motion),
19    Session(Session),
20    LapData(LapData),
21    Event(Event),
22    Participants(Participants),
23    CarSetup(CarSetup),
24    CarTelemetry(CarTelemetry),
25    CarStatus(CarStatus),
26    FinalClassification(FinalClassification),
27    LobbyInfo(LobbyInfo),
28}
29
30#[derive(Debug, Default, BinRead)]
31pub struct Header {
32    pub packet_format: u16,
33    pub game_major_version: u8,
34    pub game_minor_version: u8,
35    pub packet_version: u8,
36    pub packet_id: u8,
37    pub session_uid: u64,
38    pub session_time: f32,
39    pub frame_identifier: u32,
40    pub player_car_index: u8,
41    pub secondary_player_car_index: u8,
42}
43
44#[derive(Debug, BinRead)]
45pub struct Motion {
46    pub header: Header,
47    #[br(count = 22)]
48    pub car_motion_data: Vec<CarMotionData>,
49    pub suspension_position: WheelValue<f32>,
50    pub suspension_velocity: WheelValue<f32>,
51    pub suspension_acceleration: WheelValue<f32>,
52    pub wheel_speed: WheelValue<f32>,
53    pub wheel_slip: WheelValue<f32>,
54    pub local_velocity: Coordinates<f32>,
55    pub angular_velocity: Coordinates<f32>,
56    pub angular_acceleration: Coordinates<f32>,
57    pub front_wheel_angle: f32,
58}
59
60player_data!(Motion, CarMotionData, car_motion_data);
61
62#[derive(Debug, Default, BinRead)]
63pub struct CarMotionData {
64    pub world_position: Coordinates<f32>,
65    pub world_velocity: Coordinates<f32>,
66    pub world_forward_dir: Coordinates<i16>,
67    pub world_right_dir: Coordinates<i16>,
68    pub g_force_lateral: f32,
69    pub g_force_longitudinal: f32,
70    pub g_force_vertical: f32,
71    pub yaw: f32,
72    pub pitch: f32,
73    pub roll: f32,
74}
75
76#[derive(Debug, BinRead)]
77pub struct Session {
78    pub header: Header,
79    pub weather: Weather,
80    pub track_temperature: i8,
81    pub air_temperature: i8,
82    pub total_laps: i8,
83    pub track_length: i16,
84    pub session_type: SessionType,
85    pub track: Track,
86    pub formula: Formula,
87    pub session_time_left: u16,
88    pub session_duration: u16,
89    pub pit_speed_limit: u8,
90    pub game_paused: u8,
91    pub is_spectating: u8,
92    pub spectator_car_index: u8,
93    pub sli_pro_native_support: u8,
94    pub number_of_marshal_zones: u8,
95    pub marshal_zones: [MarshalZone; 21],
96    pub safety_car_status: SafetyCarStatus,
97    #[br(map = |x: u8| x > 0)]
98    pub network_game: bool,
99    pub number_of_weather_forecast_samples: u8,
100    pub weather_forecast_samples: [WeatherForecastSample; 20],
101}
102
103impl Session {
104    pub fn current_weather_forecast_sample(&self) -> &WeatherForecastSample {
105        let mut current_weather_forecast_sample_index =
106            self.number_of_weather_forecast_samples as usize;
107        if current_weather_forecast_sample_index > 0 {
108            current_weather_forecast_sample_index -= 1;
109        }
110        &self.weather_forecast_samples[current_weather_forecast_sample_index]
111    }
112}
113
114#[derive(Debug, Default, TryFromPrimitive)]
115#[repr(u8)]
116pub enum Weather {
117    Clear,
118    LigthCloud,
119    Overcast,
120    LightRain,
121    HeavyRain,
122    Storm,
123    #[default]
124    Unknown = 255,
125}
126
127binread_enum!(Weather, u8);
128
129#[derive(Debug, Default, TryFromPrimitive)]
130#[repr(i8)]
131pub enum Track {
132    #[default]
133    Unknown = -1,
134    Melbourne,
135    PaulRicard,
136    Shanghai,
137    Sakhir,
138    Catalunya,
139    Monaco,
140    Montreal,
141    Silverstone,
142    Hockenheim,
143    Hungaroring,
144    Spa,
145    Monza,
146    Singapore,
147    Suzuka,
148    AbuDahbi,
149    Texas,
150    Brazil,
151    Austria,
152    Sochi,
153    Mexico,
154    Baku,
155    SakhirShort,
156    SilverstoneShort,
157    TexasShort,
158    SuzukaShort,
159    Hanoi,
160    Zandvoort,
161}
162
163binread_enum!(Track, i8);
164
165#[derive(Debug, Default, TryFromPrimitive)]
166#[repr(u8)]
167pub enum Formula {
168    F1Modern,
169    F1Classic,
170    F2,
171    F1Generic,
172    #[default]
173    Unknown = 255,
174}
175
176binread_enum!(Formula, u8);
177
178#[derive(Debug, Default, TryFromPrimitive)]
179#[repr(u8)]
180pub enum SafetyCarStatus {
181    NoSafetyCar,
182    FullSafetyCar,
183    VirtualSafetyCar,
184    #[default]
185    Unknown = 255,
186}
187
188binread_enum!(SafetyCarStatus, u8);
189
190#[derive(Debug, Default, BinRead)]
191pub struct MarshalZone {
192    pub zone_start: f32,
193    pub zone_flag: ZoneFlag,
194}
195
196#[derive(Debug, Default, TryFromPrimitive)]
197#[repr(i8)]
198pub enum ZoneFlag {
199    #[default]
200    Unknown = -1,
201    None,
202    Green,
203    Blue,
204    Yellow,
205    Red,
206}
207
208binread_enum!(ZoneFlag, i8);
209
210#[derive(Debug, Default, BinRead)]
211pub struct WeatherForecastSample {
212    pub session_type: SessionType,
213    pub time_offset: u8,
214    pub weather: Weather,
215    pub track_temperature: i8,
216    pub air_temperature: i8,
217}
218
219#[derive(Debug, Default, TryFromPrimitive)]
220#[repr(u8)]
221pub enum SessionType {
222    #[default]
223    Unknown,
224    Practice1,
225    Practice2,
226    Practice3,
227    ShortPractice,
228    Qualifier1,
229    Qualifier2,
230    Qualifier3,
231    ShortQualifier,
232    OSQ,
233    Race,
234    Formula2Race,
235    TimeTrial,
236}
237
238binread_enum!(SessionType, u8);
239
240#[derive(Debug, BinRead)]
241pub struct LapData {
242    pub header: Header,
243    #[br(count = 22)]
244    pub laps: Vec<Lap>,
245}
246
247player_data!(LapData, Lap, laps);
248
249#[derive(Debug, Default, BinRead)]
250pub struct Lap {
251    pub last_lap_time: f32,
252    pub current_lap_time: f32,
253    pub sector_time_ms: (u16, u16), // sector1, sector2 (no sector3 for some reason)
254    pub best_lap_time: f32,
255    pub best_lap_number: u8,
256    pub best_lap_sector_time: (u16, u16, u16), // sector1, sector2, sector3
257    pub best_overall_sector_time: (
258        BestOverallSectorTime,
259        BestOverallSectorTime,
260        BestOverallSectorTime,
261    ), // sector1, sector2, sector3
262    pub lap_distance: f32,
263    pub total_distance: f32,
264    pub safety_car_delta: f32,
265    pub car_position: u8,
266    pub current_lap_number: u8,
267    pub pit_status: PitStatus,
268    pub sector: Sector,
269    #[br(map = |x: u8| x > 0)]
270    pub current_lap_invalid: bool,
271    pub penalties: u8,
272    pub grid_position: u8,
273    pub driver_status: DriverStatus,
274    pub result_status: ResultStatus,
275}
276
277#[derive(Debug, Default, BinRead)]
278pub struct BestLapSectorTime {
279    pub sector1: u16,
280    pub sector2: u16,
281    pub sector3: u16,
282}
283
284#[derive(Debug, Default, BinRead)]
285pub struct BestOverallSectorTime {
286    pub sector_time: u16,
287    pub lap_number: u8,
288}
289
290#[derive(Debug, Default, TryFromPrimitive)]
291#[repr(u8)]
292pub enum PitStatus {
293    None,
294    Pitting,
295    InPitArea,
296    #[default]
297    Unknown = 255,
298}
299
300binread_enum!(PitStatus, u8);
301
302#[derive(Debug, Default, TryFromPrimitive)]
303#[repr(u8)]
304pub enum Sector {
305    Sector1,
306    Sector2,
307    Sector3,
308    #[default]
309    Unknown = 255,
310}
311
312binread_enum!(Sector, u8);
313
314#[derive(Debug, Default, TryFromPrimitive)]
315#[repr(u8)]
316pub enum DriverStatus {
317    InGarage,
318    FlyingLap,
319    InLap,
320    OutLap,
321    InTrack,
322    #[default]
323    Unknown = 255,
324}
325
326binread_enum!(DriverStatus, u8);
327
328#[derive(Debug, Default, TryFromPrimitive)]
329#[repr(u8)]
330pub enum ResultStatus {
331    Invalid,
332    Inactive,
333    Active,
334    Finished,
335    Disqualified,
336    NotClassified,
337    Retired,
338    MechanicalFailure,
339    #[default]
340    Unknown = 255,
341}
342
343binread_enum!(ResultStatus, u8);
344
345#[derive(Debug)]
346pub struct Event {
347    pub header: Header,
348    pub event_data_details: EventDataDetail,
349}
350
351// Event is a bit more complicated since the event_data_details
352// depends on some of the packet details, so we cant simply derive and expect it to work
353impl binread::BinRead for Event {
354    type Args = ();
355    fn read_options<R: binread::io::Read + binread::io::Seek>(
356        reader: &mut R,
357        options: &binread::ReadOptions,
358        args: Self::Args,
359    ) -> binread::BinResult<Self> {
360        let header = Header::read_options(reader, options, args)?; // re-use Header BinRead default implementation.
361
362        // Read next 4 bytes for event string identification
363        let event_code_bytes = <[u8; 4]>::read_options(reader, options, args)?;
364        let event_code = std::str::from_utf8(&event_code_bytes).unwrap_or("UNKW");
365
366        let event_data_details = match event_code {
367            "SSTA" => EventDataDetail::SessionStarted,
368            "SEND" => EventDataDetail::SessionEnded,
369            "FTLP" => {
370                let idx = <u8>::read_options(reader, options, args)?;
371                let time = <f32>::read_options(reader, options, args)?;
372                EventDataDetail::FastestLap(idx, time)
373            }
374            "RTMT" => {
375                let idx = <u8>::read_options(reader, options, args)?;
376                EventDataDetail::Retirement(idx)
377            }
378            "DRSE" => EventDataDetail::DRSEnabled,
379            "DRSD" => EventDataDetail::DRSDisabled,
380            "TMPT" => {
381                let idx = <u8>::read_options(reader, options, args)?;
382                EventDataDetail::TeamMateInPits(idx)
383            }
384            "CHQF" => EventDataDetail::ChequeredFlag,
385            "RCWN" => {
386                let idx = <u8>::read_options(reader, options, args)?;
387                EventDataDetail::RaceWinner(idx)
388            }
389            "PENA" => {
390                let detail = PenaltyEventDetail::read_options(reader, options, args)?;
391                EventDataDetail::Penalty(detail)
392            }
393            "SPTP" => {
394                let idx = <u8>::read_options(reader, options, args)?;
395                let speed = <f32>::read_options(reader, options, args)?;
396                EventDataDetail::SpeedTrap(idx, speed)
397            }
398            _ => EventDataDetail::Unknown,
399        };
400
401        Ok(Event {
402            header,
403            event_data_details,
404        })
405    }
406}
407
408#[derive(Debug)]
409pub enum EventDataDetail {
410    SessionStarted,
411    SessionEnded,
412    FastestLap(u8, f32), // time
413    Retirement(u8),      // car_index
414    DRSEnabled,
415    DRSDisabled,
416    TeamMateInPits(u8), // car_index
417    ChequeredFlag,
418    RaceWinner(u8), // car_index
419    Penalty(PenaltyEventDetail),
420    SpeedTrap(u8, f32), // car_index, speed
421    Unknown,            // not part of the spec, added to satisfy match
422}
423
424#[derive(Debug, Default, BinRead)]
425pub struct PenaltyEventDetail {
426    pub penalty_type: PenaltyType,
427    pub infrigement_type: InfringementType,
428    pub vehicle_index: u8,
429    pub other_vehicle_index: u8,
430    pub time: u8,
431    pub lap_number: u8,
432    pub places_gained: u8,
433}
434
435#[derive(Debug, Default, TryFromPrimitive)]
436#[repr(u8)]
437pub enum PenaltyType {
438    DriveThrough,
439    StopGo,
440    GridPenalty,
441    PenaltyReminder,
442    TimePenalty,
443    Warning,
444    Disqualified,
445    RemovedFromFormationLap,
446    ParkedTooLongTimer,
447    TyreRegulations,
448    ThisLapInvalidated,
449    ThisAndNextLapInvalidated,
450    ThisLapInvalidatedWithNoReason,
451    ThisAndNextLapInvalidatedWithNoReason,
452    ThisAndPreviousLapInvalidated,
453    ThisAndPreviousLapInvalidatedWithNoReason,
454    Retired,
455    BlackFlagTimer,
456    #[default]
457    Unknown = 255,
458}
459
460binread_enum!(PenaltyType, u8);
461
462#[derive(Debug, Default, TryFromPrimitive)]
463#[repr(u8)]
464pub enum InfringementType {
465    BlockingBySlowDriving,
466    BlockingByWrongWayDriving,
467    ReversingOffTheStartLine,
468    BigCollision,
469    SmallCollision,
470    CollisionFailedToHandBackPositionSingle,
471    CollisionFailedToHandBackPositionMultiple,
472    CornerCuttingGainedTime,
473    CornerCuttingOvertakeSingle,
474    CornerCuttingOvertakeMultiple,
475    CrossedPitExitLane,
476    IgnoringBlueFlags,
477    IgnoringYellowFlags,
478    IgnoringDriveThrough,
479    TooManyDriveThroughs,
480    DriveThroughReminderServeWithinNLaps,
481    DriveThroughReminderServeThisLap,
482    PitLaneSpeeding,
483    ParkedForTooLong,
484    IgnoringTyreRegulations,
485    TooManyPenalties,
486    MultipleWarnings,
487    ApproachingDisqualification,
488    TyreRegulationsSelectSingle,
489    TyreRegulationsSelectMultiple,
490    LapInvalidatedCornerCutting,
491    LapInvalidatedRunningWide,
492    CornerCuttingRanWideGainedTimeMinor,
493    CornerCuttingRanWideGainedTimeSignificant,
494    CornerCuttingRanWideGainedTimeExtreme,
495    LapInvalidatedWallRiding,
496    LapInvalidatedFlashbackUsed,
497    LapInvalidatedResetToTrack,
498    BlockingThePitlane,
499    JumpStart,
500    SafetyCarToCarCollision,
501    SafetyCarIllegalOvertake,
502    SafetyCarExceedingAllowedPace,
503    VirtualSafetyCarExceedingAllowedPace,
504    FormationLapBelowAllowedSpeed,
505    RetiredMechanicalFailure,
506    RetiredTerminallyDamaged,
507    SafetyCarFallingTooFarBack,
508    BlackFlagTimer,
509    UnservedStopGoPenalty,
510    UnservedDriveThroughPenalty,
511    EngineComponentChange,
512    GearboxChange,
513    LeagueGridPenalty,
514    RetryPenalty,
515    IllegalTimeGain,
516    MandatoryPitstop,
517    #[default]
518    Unknown = 255,
519}
520
521binread_enum!(InfringementType, u8);
522
523#[derive(Debug, BinRead)]
524pub struct Participants {
525    pub header: Header,
526    pub num_active_cars: u8,
527    #[br(count = 22)]
528    pub participants_data: Vec<ParticipantsData>,
529}
530
531player_data!(Participants, ParticipantsData, participants_data);
532
533#[derive(Debug, Default, BinRead)]
534pub struct ParticipantsData {
535    #[br(map = |x: u8| x > 0)]
536    pub ai_controlled: bool,
537    pub driver: Driver,
538    pub team: Team,
539    pub race_number: u8,
540    pub nationality: Nationality,
541    #[br(parse_with = participant_name_parser)]
542    pub name: String,
543    #[br(map = |x: u8| x > 1)]
544    pub your_telemetry_restricted: bool,
545}
546
547fn participant_name_parser<R: binread::io::Read + binread::io::Seek>(
548    reader: &mut R,
549    _: &binread::ReadOptions,
550    _: (),
551) -> binread::BinResult<String> {
552    let mut bytes: [u8; 48] = [0; 48]; // names for participants are 48 bytes wide
553    reader.read_exact(&mut bytes)?;
554
555    let driver_name = std::str::from_utf8(&bytes)
556        .unwrap_or("UNKW")
557        .trim_matches(char::from(0)); // trim any additional null-bytes
558
559    Ok(String::from(driver_name))
560}
561
562#[derive(Debug, Default, TryFromPrimitive)]
563#[repr(u8)]
564pub enum Driver {
565    CarlosSainz,
566    DaniilKvyat,
567    DanielRicciardo,
568    KimiRaikkonen = 6, // Kimi Räikkönen
569    LewisHamilton,
570    MaxVerstappen = 9,
571    NicoHulkenburg,
572    KevinMagnussen,
573    RomainGrosjean,
574    SebastianVettel,
575    SergioPerez,
576    ValtteriBottas,
577    EstebanOcon = 17,
578    LanceStroll = 19,
579    ArronBarnes,
580    MartinGiles,
581    AlexMurray,
582    LucasRoth,
583    IgorCorreia,
584    SophieLevasseur,
585    JonasSchiffer,
586    AlainForest,
587    JayLetourneau,
588    EstoSaari,
589    YasarAtiyeh,
590    CallistoCalabresi,
591    NaotaIzumi,
592    HowardClarke,
593    WilheimKaufmann,
594    MarieLaursen,
595    FlavioNieves,
596    PeterBelousovm,
597    KlimekMichalski,
598    SantiagoMoreno,
599    BenjaminCoppens,
600    NoahVisser,
601    GertWaldmuller,
602    JulianQuesada,
603    DanielJones,
604    ArtemMarkelov,
605    TadasukeMakino,
606    SeanGelael,
607    NyckDeVries,
608    JackAitken,
609    GeorgeRussell,
610    MaximilianGunther,
611    NireiFukuzumi,
612    LucaGhiotto,
613    LandoNorris,
614    SergioSetteCamara = 55, // Sérgio Sette Câmara
615    LouisDeletraz,          // Louis Delétraz
616    AntonioFuoco,
617    CharlesLeclerc,
618    PierreGasly,
619    AlexanderAlbon = 62,
620    NicholasLatifi,
621    DorianBoccolacci,
622    NikoKari,
623    RobertoMerhi,
624    ArjunMaini,
625    AlessioLorandi,
626    RubenMeijer,
627    RashidNair,
628    JackTremblay,
629    AntonioGiovinazzi = 74,
630    RobertKubica,
631    NobuharuMatsushita = 78,
632    NikitaMazepin,
633    GuanyaZhou,
634    MickSchumacher,
635    CallumIlott,
636    JuanManuel,
637    Correa,
638    JordanKing,
639    MahaveerRaghunathan,
640    TatianaCalderon,
641    AnthoineHubert,
642    GuilianoAlesi,
643    RalphBoschung,
644    MyDriver = 100,
645    #[default]
646    Unknown = 255, // Used for time trial "ghost" drivers that appear randomly
647}
648
649binread_enum!(Driver, u8);
650
651#[derive(Debug, Default, TryFromPrimitive)]
652#[repr(u8)]
653pub enum Team {
654    Mercedes,
655    Ferrari,
656    RedBullRacing,
657    Williams,
658    RacingPoint,
659    Renault,
660    AlphaTauri,
661    Haas,
662    McLaren,
663    AlfaRomeo,
664    McLaren1988,
665    McLaren1991,
666    Williams1992,
667    Ferrari1995,
668    Williams1996,
669    McLaren1998,
670    Ferrari2002,
671    Ferrari2004,
672    Renault2006,
673    Ferrari2007,
674    McLaren2008,
675    RedBull2010,
676    Ferrari1976,
677    ARTGrandPrix,
678    CamposVexatecRacing,
679    Carlin,
680    CharouzRacingSystem,
681    DAMS,
682    RussianTime,
683    MPMotorsport,
684    Pertamina,
685    McLaren1990,
686    Trident,
687    BWTArden,
688    McLaren1976,
689    Lotus1972,
690    Ferrari1979,
691    McLaren1982,
692    Williams2003,
693    Brawn2009,
694    Lotus1978,
695    F1GenericCar,
696    ArtGP19,
697    Campos19,
698    Carlin19,
699    SauberJuniorCharouz19,
700    Dams19,
701    UniVirtuosi19,
702    MPMotorsport19,
703    Prema19,
704    Trident19,
705    Arden19,
706    Benetton1994,
707    Benetton1995,
708    Ferrari2000,
709    Jordan1991,
710    Ferrari1990 = 63,
711    McLaren2010,
712    Ferrari2010,
713    #[default]
714    Unknown = 254,
715    MyTeam = 255,
716}
717
718binread_enum!(Team, u8);
719
720#[derive(Debug, Default, TryFromPrimitive)]
721#[repr(u8)]
722pub enum Nationality {
723    #[default]
724    Unknown,
725    American,
726    Argentinean,
727    Australian,
728    Austrian,
729    Azerbaijani,
730    Bahraini,
731    Belgian,
732    Bolivian,
733    Brazilian,
734    British,
735    Bulgarian,
736    Cameroonian,
737    Canadian,
738    Chilean,
739    Chinese,
740    Colombian,
741    CostaRican,
742    Croatian,
743    Cypriot,
744    Czech,
745    Danish,
746    Dutch,
747    Ecuadorian,
748    English,
749    Emirian,
750    Estonian,
751    Finnish,
752    French,
753    German,
754    Ghanaian,
755    Greek,
756    Guatemalan,
757    Honduran,
758    HongKonger,
759    Hungarian,
760    Icelander,
761    Indian,
762    Indonesian,
763    Irish,
764    Israeli,
765    Italian,
766    Jamaican,
767    Japanese,
768    Jordanian,
769    Kuwaiti,
770    Latvian,
771    Lebanese,
772    Lithuanian,
773    Luxembourger,
774    Malaysian,
775    Maltese,
776    Mexican,
777    Monegasque,
778    NewZealander,
779    Nicaraguan,
780    NorthKorean,
781    NorthernIrish,
782    Norwegian,
783    Omani,
784    Pakistani,
785    Panamanian,
786    Paraguayan,
787    Peruvian,
788    Polish,
789    Portuguese,
790    Qatari,
791    Romanian,
792    Russian,
793    Salvadoran,
794    Saudi,
795    Scottish,
796    Serbian,
797    Singaporean,
798    Slovakian,
799    Slovenian,
800    SouthKorean,
801    SouthAfrican,
802    Spanish,
803    Swedish,
804    Swiss,
805    Thai,
806    Turkish,
807    Uruguayan,
808    Ukrainian,
809    Venezuelan,
810    Welsh,
811    Barbadian,
812    Vietnamese,
813}
814
815binread_enum!(Nationality, u8);
816
817#[derive(Debug, BinRead)]
818pub struct CarSetup {
819    pub header: Header,
820    #[br(count = 22)]
821    pub car_setup_data: Vec<CarSetupData>,
822}
823
824#[derive(Debug, Default, BinRead)]
825pub struct CarSetupData {
826    pub wing: FrontRearValue<u8>,
827    pub on_throttle: u8,
828    pub off_throttle: u8,
829    pub camber: FrontRearValue<f32>,
830    pub toe: FrontRearValue<f32>,
831    pub suspension: FrontRearValue<u8>,
832    pub anti_roll_bar: FrontRearValue<u8>,
833    pub suspension_height: FrontRearValue<u8>,
834    pub brake_pressure: u8,
835    pub brake_bias: u8,
836    pub type_pressure: WheelValue<f32>,
837    pub ballast: u8,
838    pub fuel_load: f32,
839}
840
841player_data!(CarSetup, CarSetupData, car_setup_data);
842
843#[derive(Debug, BinRead)]
844pub struct CarTelemetry {
845    pub header: Header,
846    #[br(count = 22)]
847    pub car_telemetry_data: Vec<CarTelemetryData>,
848    pub button_status: u32,
849    pub mfd_panel: MFDPanel,
850    pub mfd_panel_secondary_player: MFDPanel,
851    #[br(map = |x: i8| if x == 0 { Gear::Unknown } else { Gear::try_from(x).unwrap() })]
852    pub suggested_gear: Gear,
853}
854
855player_data!(CarTelemetry, CarTelemetryData, car_telemetry_data);
856
857#[derive(Debug, Default, BinRead)]
858pub struct CarTelemetryData {
859    pub speed: u16,
860    pub throttle: f32,
861    pub steer: f32,
862    pub brake: f32,
863    pub clutch: u8,
864    #[br(map = |x: i8| Gear::try_from(x).unwrap())]
865    pub gear: Gear,
866    pub engine_rpm: u16,
867    #[br(map = |x: u8| x > 0)]
868    pub drs: bool,
869    pub rev_lights_percent: u8,
870    pub brake_temp: WheelValue<u16>,
871    pub tyres_surface_temp: WheelValue<u8>,
872    pub tyres_inner_temp: WheelValue<u8>,
873    pub engine_temp: u16,
874    pub tyres_pressure: WheelValue<f32>,
875    #[br(parse_with = surface_type_parser)]
876    pub surface_type: WheelValue<Surface>,
877}
878
879fn surface_type_parser<R: binread::io::Read + binread::io::Seek>(
880    reader: &mut R,
881    _: &binread::ReadOptions,
882    _: (),
883) -> binread::BinResult<WheelValue<Surface>> {
884    let mut bytes: [u8; 4] = [0; 4];
885    reader.read_exact(&mut bytes)?;
886
887    Ok(WheelValue::<Surface> {
888        rear_left: Surface::try_from(bytes[0]).unwrap_or(Surface::Unknown),
889        rear_right: Surface::try_from(bytes[1]).unwrap_or(Surface::Unknown),
890        front_left: Surface::try_from(bytes[2]).unwrap_or(Surface::Unknown),
891        front_right: Surface::try_from(bytes[3]).unwrap_or(Surface::Unknown),
892    })
893}
894
895#[derive(Debug, Default, TryFromPrimitive)]
896#[repr(i8)]
897pub enum Gear {
898    Reverse = -1,
899    Neutral,
900    First,
901    Second,
902    Third,
903    Fourth,
904    Fifth,
905    Sixth,
906    Seventh,
907    Eigth,
908    #[default]
909    Unknown = 127,
910}
911
912binread_enum!(Gear, i8);
913
914#[derive(Debug, Default, TryFromPrimitive)]
915#[repr(u8)]
916pub enum Surface {
917    Tarmac,
918    RumbleStrip,
919    Concrete,
920    Rock,
921    Gravel,
922    Mud,
923    Sand,
924    Grass,
925    Water,
926    Cobblestone,
927    Metal,
928    Ridged,
929    #[default]
930    Unknown = 255,
931}
932
933binread_enum!(Surface, u8);
934
935#[derive(Debug, Default, TryFromPrimitive)]
936#[repr(u8)]
937pub enum MFDPanel {
938    CarSetup,
939    Pits,
940    Damage,
941    Engine,
942    Temperatures,
943    #[default]
944    Unknown,
945    Closed = 255,
946}
947
948binread_enum!(MFDPanel, u8);
949
950#[derive(Debug, BinRead)]
951pub struct CarStatus {
952    pub header: Header,
953    #[br(count = 22)]
954    pub car_status_data: Vec<CarStatusData>,
955}
956
957player_data!(CarStatus, CarStatusData, car_status_data);
958
959#[derive(Debug, Default, BinRead)]
960pub struct CarStatusData {
961    pub traction_control: u8,
962    #[br(map = |x: u8| x > 0)]
963    pub anti_lock_brakes: bool,
964    pub fuel_mix: FuelMix,
965    pub front_brake_bias: u8,
966    #[br(map = |x: u8| x > 0)]
967    pub pit_limiter_status: bool,
968    pub fuel_in_tank: f32,
969    pub fuel_capacity: f32,
970    pub fuel_remaining_laps: f32,
971    pub max_rpm: u16,
972    pub idle_rpm: u16,
973    pub max_gears: u8,
974    pub drs_allowed: DRSAllowed,
975    #[br(map = |x: u16| if x > 0 { DRSActivationDistance::Distance(x) } else { DRSActivationDistance::NotAvailable })]
976    pub drs_activation_distance: DRSActivationDistance,
977    pub tyres_wear: WheelValue<u8>,
978    pub tyres_compound: TyreCompound,
979    pub tyres_visual: TyreVisual,
980    pub tyres_ages_lap: u8,
981    pub tyres_damage: WheelValue<u8>,
982    pub wing_damage: WingValue<u8>,
983    #[br(map = |x: u8| x > 0)]
984    pub drs_fault: bool,
985    pub engine_damage: u8,
986    pub gearbox_damage: u8,
987    pub vehicle_fia_flag: FiaFlag,
988    pub ers_data: ERS,
989}
990
991#[derive(Debug, Default, TryFromPrimitive)]
992#[repr(u8)]
993pub enum FuelMix {
994    Lean,
995    Standard,
996    Rich,
997    Max,
998    #[default]
999    Unknown,
1000}
1001
1002binread_enum!(FuelMix, u8);
1003
1004#[derive(Debug, Default, TryFromPrimitive)]
1005#[repr(u8)]
1006pub enum DRSAllowed {
1007    NotAllowed,
1008    Allowed,
1009    #[default]
1010    Unknown,
1011}
1012
1013binread_enum!(DRSAllowed, u8);
1014
1015#[derive(Debug, Default)]
1016#[repr(u16)]
1017pub enum DRSActivationDistance {
1018    #[default]
1019    NotAvailable,
1020    Distance(u16),
1021}
1022
1023#[derive(Debug, Default, TryFromPrimitive)]
1024#[repr(u8)]
1025pub enum TyreCompound {
1026    Inter = 7,
1027    Wet,
1028    F1ClassicDry,
1029    F1ClassicWet,
1030    F2SuperSoft,
1031    F2Soft,
1032    F2Medium,
1033    F2Hard,
1034    F2Wet,
1035    C5,
1036    C4,
1037    C3,
1038    C2,
1039    C1,
1040    #[default]
1041    Unknown,
1042}
1043
1044binread_enum!(TyreCompound, u8);
1045
1046#[derive(Debug, Default, TryFromPrimitive)]
1047#[repr(u8)]
1048pub enum TyreVisual {
1049    Inter = 7,
1050    Wet = 8,
1051    Soft = 16,
1052    Medium = 17,
1053    Hard = 18,
1054    #[default]
1055    Unknown = 255,
1056}
1057
1058binread_enum!(TyreVisual, u8);
1059
1060#[derive(Debug, Default, TryFromPrimitive)]
1061#[repr(i8)]
1062pub enum FiaFlag {
1063    #[default]
1064    Unknown = -1,
1065    None,
1066    Green,
1067    Blue,
1068    Yellow,
1069    Red,
1070}
1071
1072binread_enum!(FiaFlag, i8);
1073
1074#[derive(Debug, Default, BinRead)]
1075pub struct ERS {
1076    pub stored_energy: f32,
1077    pub deploy_mode: ERSDeployMode,
1078    pub harvested_this_lap_mguk: f32,
1079    pub harvested_this_lap_mguh: f32,
1080    pub deployed_this_lap: f32,
1081}
1082
1083#[derive(Debug, Default, TryFromPrimitive)]
1084#[repr(u8)]
1085pub enum ERSDeployMode {
1086    None,
1087    Medium,
1088    Overtake,
1089    Hotlap,
1090    #[default]
1091    Unknown = 255,
1092}
1093
1094binread_enum!(ERSDeployMode, u8);
1095
1096#[derive(Debug, BinRead)]
1097pub struct FinalClassification {
1098    pub header: Header,
1099    pub number_of_cars: u8,
1100    #[br(count = 22)]
1101    pub final_classification_data: Vec<FinalClassificationData>,
1102}
1103
1104player_data!(
1105    FinalClassification,
1106    FinalClassificationData,
1107    final_classification_data
1108);
1109
1110#[derive(Debug, Default, BinRead)]
1111pub struct FinalClassificationData {
1112    pub position: u8,
1113    pub number_of_laps: u8,
1114    pub grid_position: u8,
1115    pub points: u8,
1116    pub number_of_pit_stops: u8,
1117    pub result_status: ResultStatus,
1118    pub best_lap_time: f32,
1119    pub total_race_time: f64,
1120    pub penalties_time: u8,
1121    pub number_of_penalties: u8,
1122    pub number_of_tyre_stints: u8,
1123    #[br(count = 8)]
1124    pub tyre_stints_actual: Vec<TyreCompound>,
1125    #[br(count = 8)]
1126    pub tyre_stints_visual: Vec<TyreVisual>,
1127}
1128
1129#[derive(Debug, BinRead)]
1130pub struct LobbyInfo {
1131    pub header: Header,
1132    pub number_of_players: u8,
1133    #[br(count = 22)]
1134    pub lobby_players: Vec<LobbyInfoData>,
1135}
1136
1137player_data!(LobbyInfo, LobbyInfoData, lobby_players);
1138
1139impl LobbyInfo {
1140    pub fn players(self) -> Vec<LobbyInfoData> {
1141        let number_of_players = self.number_of_players as usize;
1142        self.lobby_players
1143            .into_iter()
1144            .take(number_of_players)
1145            .collect()
1146    }
1147}
1148
1149#[derive(Debug, Default, BinRead)]
1150pub struct LobbyInfoData {
1151    #[br(map = |x: u8| x > 0)]
1152    pub ai_controlled: bool,
1153    pub team: Team,
1154    pub nationality: Nationality,
1155    #[br(parse_with = participant_name_parser)]
1156    pub name: String,
1157    pub status: LobbyStatus,
1158}
1159
1160#[derive(Debug, Default, TryFromPrimitive)]
1161#[repr(u8)]
1162pub enum LobbyStatus {
1163    NotReady,
1164    Ready,
1165    Spectating,
1166    #[default]
1167    Unknown,
1168}
1169
1170binread_enum!(LobbyStatus, u8);
1171
1172impl TelemetryEvent for F1_2020 {
1173    fn from_packet(packet: &TelemetryPacket) -> Result<F1_2020, Box<dyn Error>> {
1174        if packet.len() < 24 {
1175            return Err(Box::from("Packet is too small to contain a header"));
1176        }
1177
1178        let packet_id = packet[5]; // packet_id
1179        let mut reader = Cursor::new(packet);
1180        match packet_id {
1181            0 => {
1182                let data: Motion = reader.read_le()?;
1183                Ok(F1_2020::Motion(data))
1184            }
1185            1 => {
1186                let data: Session = reader.read_le()?;
1187                Ok(F1_2020::Session(data))
1188            }
1189            2 => {
1190                let data: LapData = reader.read_le()?;
1191                Ok(F1_2020::LapData(data))
1192            }
1193            3 => {
1194                let data: Event = reader.read_le()?;
1195                Ok(F1_2020::Event(data))
1196            }
1197            4 => {
1198                let data: Participants = reader.read_le()?;
1199                Ok(F1_2020::Participants(data))
1200            }
1201            5 => {
1202                let data: CarSetup = reader.read_le()?;
1203                Ok(F1_2020::CarSetup(data))
1204            }
1205            6 => {
1206                let data: CarTelemetry = reader.read_le()?;
1207                Ok(F1_2020::CarTelemetry(data))
1208            }
1209            7 => {
1210                let data: CarStatus = reader.read_le()?;
1211                Ok(F1_2020::CarStatus(data))
1212            }
1213            8 => {
1214                let data: FinalClassification = reader.read_le()?;
1215                Ok(F1_2020::FinalClassification(data))
1216            }
1217            9 => {
1218                let data: LobbyInfo = reader.read_le()?;
1219                Ok(F1_2020::LobbyInfo(data))
1220            }
1221            id => Err(Box::from(format!("Unknown packet type: {}", id))),
1222        }
1223    }
1224}