f1_game_packet_parser/packets/
mod.rs

1pub mod car_damage;
2pub mod car_setups;
3pub mod car_status;
4pub mod car_telemetry;
5pub mod event;
6pub mod final_classification;
7pub mod laps;
8pub mod lobby;
9pub mod motion;
10pub mod participants;
11pub mod session;
12pub mod session_history;
13pub mod time_trial;
14pub mod tyre_sets;
15
16use crate::constants::{
17    BrakingAssist, CarDamage, CarDamageRate, Collisions, CornerCuttingStringency,
18    DynamicRacingLine, DynamicRacingLineType, FlashbackLimit, ForecastAccuracy,
19    FormationLapExperience, Formula, GameMode, GearboxAssist, LowFuelMode, MfdPanelIndex,
20    PitStopExperience, RaceStarts, RecoveryMode, RedFlagIntensity, RuleSet,
21    SafetyCarExperience, SafetyCarIntensity, SafetyCarStatus, SessionLength, SpeedUnit,
22    SurfaceSimType, TemperatureUnit, TrackId, TyreTemperature, Weather, MAX_NUM_CARS,
23};
24use crate::packets::car_damage::CarDamageData;
25use crate::packets::car_setups::CarSetupData;
26use crate::packets::car_status::CarStatusData;
27use crate::packets::car_telemetry::CarTelemetryData;
28use crate::packets::event::EventDetails;
29use crate::packets::final_classification::FinalClassificationData;
30use crate::packets::laps::LapData;
31use crate::packets::lobby::LobbyInfoData;
32use crate::packets::motion::CarMotionData;
33use crate::packets::participants::ParticipantsData;
34use crate::packets::session::{
35    check_num_forecast_samples, get_forecast_samples_padding, MarshalZone,
36    WeatherForecastSample, MARSHAL_ZONE_RAW_SIZE, MAX_AI_DIFFICULTY,
37    MAX_NUM_MARSHAL_ZONES, MAX_NUM_SESSIONS,
38};
39use crate::packets::session_history::{
40    get_lap_history_raw_size, LapHistoryData, TyreStintHistoryData, MAX_NUM_LAPS,
41    MAX_NUM_TYRE_STINTS,
42};
43use crate::packets::time_trial::TimeTrialDataSet;
44use crate::packets::tyre_sets::{TyreSetData, NUM_TYRE_SETS};
45
46use binrw::BinRead;
47use serde::{Deserialize, Serialize};
48use std::error::Error;
49use std::fmt;
50use std::string::FromUtf8Error;
51
52/// The motion packet gives physics data for all the cars being driven.
53#[non_exhaustive]
54#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
55#[br(little, import(packet_format: u16))]
56pub struct F1PacketMotion {
57    /// Motion data for all cars on track. Should have a size of 22.
58    #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
59    pub data: Vec<CarMotionData>,
60    /// Extra player-car-only motion data.
61    /// Available only in the 2022 format.
62    #[br(if(packet_format == 2022))]
63    pub motion_ex: Option<F1PacketMotionEx>,
64}
65
66/// The session packet includes details about the current session in progress.
67/// ## Example
68#[allow(clippy::struct_excessive_bools)]
69#[non_exhaustive]
70#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
71#[br(little, import(packet_format: u16))]
72pub struct F1PacketSession {
73    /// Current weather.
74    pub weather: Weather,
75    /// Track temperature in degrees Celsius.
76    pub track_temperature: i8,
77    /// Air temperature in degrees Celsius.
78    pub air_temperature: i8,
79    /// Total number of laps in this session.
80    pub total_laps: u8,
81    /// Track's length in metres.
82    pub track_length: u16,
83    /// Session's type.
84    /// See [`session_type`](mod@crate::constants::session_type)
85    /// for possible values.
86    pub session_type: u8,
87    /// Unique identifier of the track.
88    pub track_id: TrackId,
89    /// Formula of cars being raced.
90    pub formula: Formula,
91    /// Time left in the session in seconds.
92    pub session_time_left: u16,
93    /// Session's duration in seconds.
94    pub session_duration: u16,
95    /// Pit lane's speed limit in kilometres per hour.
96    pub pit_speed_limit: u8,
97    /// Whether the game is paused.
98    #[br(try_map(u8_to_bool))]
99    pub game_paused: bool,
100    /// Whether the player is spectating.
101    #[br(try_map(u8_to_bool))]
102    pub is_spectating: bool,
103    /// Index of the car being spectated.
104    #[br(map(u8_to_usize))]
105    pub spectator_car_index: usize,
106    /// Whether SLI Pro support is active.
107    #[br(try_map(u8_to_bool))]
108    pub sli_pro_native_support: bool,
109    /// Number of marshal zones to follow.
110    #[br(
111        map(u8_to_usize),
112        assert(
113            num_marshal_zones <= MAX_NUM_MARSHAL_ZONES,
114            "Session packet has an invalid number of marshal zones: {}",
115            num_marshal_zones
116        )
117    )]
118    pub num_marshal_zones: usize,
119    /// List of marshal zones.
120    /// Should have a size equal to
121    /// [`num_marshal_zones`](field@crate::packets::F1PacketSession::num_marshal_zones).
122    #[br(
123        count(num_marshal_zones),
124        args{ inner: (packet_format,) },
125        pad_after(
126            (MAX_NUM_MARSHAL_ZONES - num_marshal_zones) * MARSHAL_ZONE_RAW_SIZE
127        )
128    )]
129    pub marshal_zones: Vec<MarshalZone>,
130    /// Safety car deployment status.
131    pub safety_car_status: SafetyCarStatus,
132    /// Whether this game is online.
133    #[br(try_map(u8_to_bool))]
134    pub network_game: bool,
135    /// Number of weather samples to follow.
136    #[br(
137        map(u8_to_usize),
138        assert(
139            check_num_forecast_samples(packet_format, num_weather_forecast_samples),
140            "Session packet has an invalid number of weather forecast samples: {}",
141            num_weather_forecast_samples
142        )
143    )]
144    pub num_weather_forecast_samples: usize,
145    /// List of up to weather forecast samples.
146    /// Should have a size equal to
147    /// [`num_weather_forecast_samples`](field@crate::packets::F1PacketSession::num_weather_forecast_samples).
148    #[br(
149        count(num_weather_forecast_samples),
150        args{ inner: (packet_format,) },
151        pad_after(
152            get_forecast_samples_padding(packet_format, num_weather_forecast_samples)
153        )
154    )]
155    pub weather_forecast_samples: Vec<WeatherForecastSample>,
156    /// Weather forecast accuracy.
157    pub forecast_accuracy: ForecastAccuracy,
158    /// AI difficulty rating in range `(0..=110)`.
159    #[br(
160        assert(
161            ai_difficulty <= MAX_AI_DIFFICULTY,
162            "Session packet has an invalid AI difficulty value: {}",
163            ai_difficulty
164        )
165    )]
166    pub ai_difficulty: u8,
167    /// Identifier for season - persists across saves.
168    pub season_link_identifier: u32,
169    /// Identifier for weekend - persists across saves.
170    pub weekend_link_identifier: u32,
171    /// Identifier for session - persists across saves.
172    pub session_link_identifier: u32,
173    /// Ideal lap for the player to pit on for current strategy.
174    pub pit_stop_window_ideal_lap: u8,
175    /// The latest lap for the player to pit on for current strategy.
176    pub pit_stop_window_latest_lap: u8,
177    /// Predicted position for the player to rejoin at.
178    pub pit_stop_rejoin_position: u8,
179    /// Whether the steering assist is enabled.
180    #[br(try_map(u8_to_bool))]
181    pub steering_assist: bool,
182    /// Type of braking assist enabled.
183    pub braking_assist: BrakingAssist,
184    /// Type of gearbox assist enabled.
185    pub gearbox_assist: GearboxAssist,
186    /// Whether the pit assist is enabled.
187    #[br(try_map(u8_to_bool))]
188    pub pit_assist: bool,
189    /// Whether the pit release assist is enabled.
190    #[br(try_map(u8_to_bool))]
191    pub pit_release_assist: bool,
192    /// Whether the ERS assist is enabled.
193    #[br(try_map(u8_to_bool))]
194    pub ers_assist: bool,
195    /// Whether the DRS assist is enabled.
196    #[br(try_map(u8_to_bool))]
197    pub drs_assist: bool,
198    /// Type of the dynamic racing line assist.
199    pub dynamic_racing_line: DynamicRacingLine,
200    /// Type of the dynamic racing line (2D/3D).
201    pub dynamic_racing_line_type: DynamicRacingLineType,
202    /// Game mode's identifier.
203    pub game_mode: GameMode,
204    /// Rule set's identifier.
205    pub rule_set: RuleSet,
206    /// Local time of day as minutes since midnight.
207    pub time_of_day: u32,
208    /// Session's length.
209    pub session_length: SessionLength,
210    /// Speed unit used by player 1.
211    /// Available from the 2023 format onwards.
212    #[br(if(packet_format >= 2023))]
213    pub speed_unit_lead_player: Option<SpeedUnit>,
214    /// Temperature unit used by player 1.
215    /// Available from the 2023 format onwards.
216    #[br(if(packet_format >= 2023))]
217    pub temperature_unit_lead_player: Option<TemperatureUnit>,
218    /// Speed unit used by player 2.
219    /// Available from the 2023 format onwards.
220    #[br(if(packet_format >= 2023))]
221    pub speed_unit_secondary_player: Option<SpeedUnit>,
222    /// Temperature unit used by player 2.
223    /// Available from the 2023 format onwards.
224    #[br(if(packet_format >= 2023))]
225    pub temperature_unit_secondary_player: Option<TemperatureUnit>,
226    /// Number of full safety cars called during the session.
227    /// Available from the 2023 format onwards.
228    #[br(if(packet_format >= 2023))]
229    pub num_safety_car_periods: u8,
230    /// Number of virtual safety cars called during the session.
231    /// Available from the 2023 format onwards.
232    #[br(if(packet_format >= 2023))]
233    pub num_virtual_safety_car_periods: u8,
234    /// Number of red flags called during the session.
235    /// Available from the 2023 format onwards.
236    #[br(if(packet_format >= 2023))]
237    pub num_red_flag_periods: u8,
238    /// Whether equal car performance is enabled.
239    /// Available from the 2024 format onwards.
240    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
241    pub equal_car_performance: bool,
242    /// Recovery mode assist.
243    /// Available from the 2024 format onwards.
244    #[br(if(packet_format >= 2024))]
245    pub recovery_mode: Option<RecoveryMode>,
246    /// Flashback limit type.
247    /// Available from the 2024 format onwards.
248    #[br(if(packet_format >= 2024))]
249    pub flashback_limit: Option<FlashbackLimit>,
250    /// Surface simulation type.
251    /// Available from the 2024 format onwards.
252    #[br(if(packet_format >= 2024))]
253    pub surface_sim_type: Option<SurfaceSimType>,
254    /// Low fuel driving difficulty.
255    /// Available from the 2024 format onwards.
256    #[br(if(packet_format >= 2024))]
257    pub low_fuel_mode: Option<LowFuelMode>,
258    /// Race starts assist.
259    /// Available from the 2024 format onwards.
260    #[br(if(packet_format >= 2024))]
261    pub race_starts: Option<RaceStarts>,
262    /// Tyre temperature simulation type.
263    /// Available from the 2024 format onwards.
264    #[br(if(packet_format >= 2024))]
265    pub tyre_temperature: Option<TyreTemperature>,
266    /// Whether the pit lane tyre simulation
267    /// (cold tyres and low grip right after a stop) is enabled.
268    /// Available from the 2024 format onwards.
269    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
270    pub pit_lane_tyre_sim: bool,
271    /// Car damage simulation type.
272    /// Available from the 2024 format onwards.
273    #[br(if(packet_format >= 2024))]
274    pub car_damage: Option<CarDamage>,
275    /// Car damage rate.
276    /// Available from the 2024 format onwards.
277    #[br(if(packet_format >= 2024))]
278    pub car_damage_rate: Option<CarDamageRate>,
279    /// Collision simulation type.
280    /// Available from the 2024 format onwards.
281    #[br(if(packet_format >= 2024))]
282    pub collisions: Option<Collisions>,
283    /// Whether collisions are disabled only for lap 1.
284    /// Available from the 2024 format onwards.
285    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
286    pub collisions_off_for_first_lap_only: bool,
287    /// Whether unsafe pit release is disabled in a multiplayer game.
288    /// Available from the 2024 format onwards.
289    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
290    pub mp_unsafe_pit_release_disabled: bool,
291    /// Whether collisions get disabled for griefing in a multiplayer game.
292    /// Available from the 2024 format onwards.
293    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
294    pub mp_collisions_off_for_griefing: bool,
295    /// Corner cutting stringency.
296    /// Available from the 2024 format onwards.
297    #[br(if(packet_format >= 2024))]
298    pub corner_cutting_stringency: Option<CornerCuttingStringency>,
299    /// Whether parc fermé rules are enabled.
300    /// Available from the 2024 format onwards.
301    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
302    pub parc_ferme_rules: bool,
303    /// Pit stop experience.
304    /// Available from the 2024 format onwards.
305    #[br(if(packet_format >= 2024))]
306    pub pit_stop_experience: Option<PitStopExperience>,
307    /// Safety car intensity.
308    /// Available from the 2024 format onwards.
309    #[br(if(packet_format >= 2024))]
310    pub safety_car_intensity: Option<SafetyCarIntensity>,
311    /// Safety car experience.
312    /// Available from the 2024 format onwards.
313    #[br(if(packet_format >= 2024))]
314    pub safety_car_experience: Option<SafetyCarExperience>,
315    /// Whether formation lap is enabled.
316    /// Available from the 2024 format onwards.
317    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
318    pub formation_lap: bool,
319    /// Formation lap experience.
320    /// Available from the 2024 format onwards.
321    #[br(if(packet_format >= 2024))]
322    pub formation_lap_experience: Option<FormationLapExperience>,
323    /// Red flag intensity.
324    /// Available from the 2024 format onwards.
325    #[br(if(packet_format >= 2024))]
326    pub red_flag_intensity: Option<RedFlagIntensity>,
327    /// Whether this single player game affects the license level.
328    /// Available from the 2024 format onwards.
329    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
330    pub affects_license_level_solo: bool,
331    /// Whether this multiplayer game affects the license level.
332    /// Available from the 2024 format onwards.
333    #[br(if(packet_format >= 2024), try_map(u8_to_bool))]
334    pub affects_license_level_mp: bool,
335    /// Number of sessions in the ongoing race weekend.
336    #[br(
337        map(u8_to_usize),
338        if(packet_format >= 2024),
339        assert(
340            num_sessions_in_weekend <= MAX_NUM_SESSIONS,
341            "Session packet has an invalid number of sessions in a weekend: {}",
342            num_sessions_in_weekend
343        )
344    )]
345    pub num_sessions_in_weekend: usize,
346    /// List of sessions that shows this weekend's structure.
347    /// Should have a size equal to
348    /// [`num_sessions_in_weekend`](field@crate::packets::F1PacketSession::num_sessions_in_weekend).
349    /// See [`session_type`](mod@crate::constants::session_type)
350    /// for possible values.
351    /// Available from the 2024 format onwards.
352    #[br(
353        if(packet_format >= 2024),
354        count(num_sessions_in_weekend),
355        pad_after(MAX_NUM_SESSIONS - num_sessions_in_weekend)
356    )]
357    pub weekend_structure: Vec<u8>,
358    /// Distance (in metres) around the track where sector 2 starts.
359    /// Available from the 2024 format onwards.
360    #[br(if(packet_format >= 2024))]
361    pub sector2_lap_distance_start: f32,
362    /// Distance (in metres) around the track where sector 3 starts.
363    /// Available from the 2024 format onwards.
364    #[br(if(packet_format >= 2024))]
365    pub sector3_lap_distance_start: f32,
366}
367
368/// Data about all the lap times of cars in the session.
369#[non_exhaustive]
370#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
371#[br(little, import(packet_format: u16))]
372pub struct F1PacketLaps {
373    /// Lap data for all cars on track. Should have a size of 22.
374    #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
375    pub data: Vec<LapData>,
376    /// Index of personal best car in time trial mode (255 if invalid).
377    #[br(map(u8_to_usize))]
378    pub time_trial_pb_car_index: usize,
379    /// Index of rival's car in time trial mode (255 if invalid).
380    #[br(map(u8_to_usize))]
381    pub time_trial_rival_car_index: usize,
382}
383
384/// Various notable events that happen during a session.
385#[non_exhaustive]
386#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
387#[br(little, import(packet_format: u16))]
388pub struct F1PacketEvent {
389    /// 4-letter event code.
390    #[br(
391        try_map(|bytes: [u8; 4]| String::from_utf8(bytes.to_vec())),
392        restore_position
393    )]
394    pub code: String,
395    /// Extra data for this event.
396    #[br(args(packet_format))]
397    pub details: EventDetails,
398}
399
400/// Data of participants in the session, mostly relevant for multiplayer.
401#[non_exhaustive]
402#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
403#[br(
404    little,
405    import(packet_format: u16),
406    assert(
407        num_active_cars <= MAX_NUM_CARS,
408        "Participants packet has an invalid number of cars: {}",
409        num_active_cars
410    )
411)]
412pub struct F1PacketParticipants {
413    /// Number of active cars in the session.
414    #[br(map(u8_to_usize))]
415    pub num_active_cars: usize,
416    /// Data for all participants.
417    /// Should have a size equal to
418    /// [`num_active_cars`](field@crate::packets::F1PacketParticipants::num_active_cars).
419    #[br(count(num_active_cars), args{ inner: (packet_format,) })]
420    pub data: Vec<ParticipantsData>,
421}
422
423/// Car setups for all cars in the race.
424/// In multiplayer games, other player cars will appear as blank.
425/// You will only be able to see your car setup and AI cars.
426#[non_exhaustive]
427#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
428#[br(little, import(packet_format: u16))]
429pub struct F1PacketCarSetups {
430    /// Setup data for all cars on track. Should have a size of 22.
431    #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
432    pub data: Vec<CarSetupData>,
433    /// Value of front wing after next pit stop - player only.
434    /// Available from the 2024 format onwards
435    #[br(if(packet_format >= 2024))]
436    pub next_front_wing_value: f32,
437}
438
439/// Telemetry (such as speed, DRS, throttle application, etc.)
440/// for all cars in the race.
441#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
442#[br(little, import(packet_format: u16))]
443pub struct F1PacketCarTelemetry {
444    /// Telemetry data for all cars on track. Should have a size of 22.
445    #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
446    pub data: Vec<CarTelemetryData>,
447    /// Index of currently open MFD panel for player 1.
448    pub mfd_panel_index: MfdPanelIndex,
449    /// Index of currently open MFD panel for player 2.
450    pub mfd_panel_index_secondary_player: MfdPanelIndex,
451    /// Suggested gear (0 if no gear suggested).
452    #[br(
453        assert(
454            (-1..=8).contains(&suggested_gear),
455            "Car telemetry entry has an invalid suggested gear value: {}",
456            suggested_gear
457        )
458    )]
459    pub suggested_gear: i8,
460}
461
462/// Car status data for each car in the race.
463#[non_exhaustive]
464#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
465#[br(little, import(packet_format: u16))]
466pub struct F1PacketCarStatus {
467    /// Car status data for all cars. Should have a size of 22.
468    #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
469    pub data: Vec<CarStatusData>,
470}
471
472/// Final classification confirmation at the end of a race.
473#[non_exhaustive]
474#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
475#[br(little, import(packet_format: u16))]
476pub struct F1PacketFinalClassification {
477    /// Number of cars in the final classification.
478    #[br(
479        map(u8_to_usize),
480        assert(
481            num_cars <= MAX_NUM_CARS,
482            "Final classification packet has an invalid number of cars: {}",
483            num_cars
484        )
485    )]
486    pub num_cars: usize,
487    /// Final classification data for all cars.
488    /// Should have a size equal to
489    /// [`num_cars`](field@crate::packets::F1PacketFinalClassification::num_cars).
490    #[br(count(num_cars), args{ inner: (packet_format,) })]
491    pub data: Vec<FinalClassificationData>,
492}
493
494/// Packet detailing all the players that are currently in a multiplayer lobby.
495#[non_exhaustive]
496#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
497#[br(little, import(packet_format: u16))]
498pub struct F1PacketLobby {
499    /// Number of players in the lobby.
500    #[br(
501        map(u8_to_usize),
502        assert(
503            num_players <= MAX_NUM_CARS,
504            "Lobby packet has an invalid number of players: {}",
505            num_players
506        )
507    )]
508    pub num_players: usize,
509    /// Lobby info data for all players.
510    /// Should have a size equal to
511    /// [`num_players`](field@crate::packets::F1PacketLobby::num_players).
512    #[br(count(num_players), args{ inner: (packet_format,) })]
513    pub data: Vec<LobbyInfoData>,
514}
515
516/// Car damage parameters for all cars in the session.
517#[non_exhaustive]
518#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
519#[br(little, import(packet_format: u16))]
520pub struct F1PacketCarDamage {
521    /// Car damage data. Should have a size of 22.
522    #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
523    pub data: Vec<CarDamageData>,
524}
525
526/// Packet detailing lap and tyre data history for a given driver in the session.
527#[non_exhaustive]
528#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
529#[br(little, import(packet_format: u16))]
530pub struct F1PacketSessionHistory {
531    /// Index of the car this packet refers to.
532    #[br(
533        map(u8_to_usize),
534        assert(
535            vehicle_index < MAX_NUM_CARS,
536            "Session history packet has an invalid vehicle index: {}",
537            vehicle_index
538        )
539    )]
540    pub vehicle_index: usize,
541    /// Number of laps in the data (including the current one).
542    #[br(
543        map(u8_to_usize),
544        assert(
545            num_laps <= MAX_NUM_LAPS,
546            "Session history packet has an invalid number of laps: {}",
547            num_laps
548        ),
549    )]
550    pub num_laps: usize,
551    /// Number of tyre stints in the data (including the current one).
552    #[br(
553        map(u8_to_usize),
554        assert(
555            num_tyre_stints <= MAX_NUM_TYRE_STINTS,
556            "Session history packet has an invalid number of tyre stints: {}",
557            num_tyre_stints
558        )
559    )]
560    pub num_tyre_stints: usize,
561    /// Number of the lap the best lap time was achieved on.
562    #[br(map(u8_to_usize))]
563    pub best_lap_time_lap_num: usize,
564    /// Number of the lap the best sector 1 time was achieved on.
565    #[br(map(u8_to_usize))]
566    pub best_sector1_lap_num: usize,
567    /// Number of the lap the best sector 2 time was achieved on.
568    #[br(map(u8_to_usize))]
569    pub best_sector2_lap_num: usize,
570    /// Number of the lap the best sector 3 time was achieved on.
571    #[br(map(u8_to_usize))]
572    pub best_sector3_lap_num: usize,
573    /// Lap history. Should have a size equal to
574    /// [`num_laps`](field@crate::packets::F1PacketSessionHistory::num_laps).
575    #[br(
576        count(num_laps),
577        args{ inner: (packet_format,) },
578        pad_after((MAX_NUM_LAPS - num_laps) * get_lap_history_raw_size(packet_format))
579    )]
580    pub lap_history_data: Vec<LapHistoryData>,
581    /// Tyre stint history.
582    /// Should have a size equal to
583    /// [`num_tyre_stints`](field@crate::packets::F1PacketSessionHistory::num_tyre_stints).
584    #[br(count(num_tyre_stints), args{ inner: (packet_format,) })]
585    pub tyre_stint_history_data: Vec<TyreStintHistoryData>,
586}
587
588/// In-depth details about tyre sets assigned to a vehicle during the session.
589/// Available from the 2023 format onwards.
590#[non_exhaustive]
591#[derive(BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize)]
592#[br(little, import(packet_format: u16))]
593pub struct F1PacketTyreSets {
594    /// Index of the car this packet relates to.
595    #[br(map(u8_to_usize))]
596    pub vehicle_index: usize,
597    /// 13 dry + 7 wet tyre sets.
598    #[br(count(NUM_TYRE_SETS), args{ inner: (packet_format,) })]
599    pub data: Vec<TyreSetData>,
600    /// Index of fitted tyre set.
601    #[br(
602        map(u8_to_usize),
603        assert(
604            fitted_index < NUM_TYRE_SETS,
605            "Tyre sets packet has an invalid fitted set index: {}",
606            fitted_index
607        )
608    )]
609    pub fitted_index: usize,
610}
611
612/// Extended motion data for player's car. Available as a:
613/// - part of [`F1PacketMotion`] in the 2022 format
614/// - standalone packet from the 2023 format onwards
615#[non_exhaustive]
616#[derive(BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize)]
617#[br(little, import(packet_format: u16))]
618pub struct F1PacketMotionEx {
619    /// Positions of suspension for each wheel.
620    /// See [`wheel_index`](mod@crate::constants::wheel_index)
621    /// for wheel order.
622    pub suspension_position: [f32; 4],
623    /// Velocity values of suspension for each wheel.
624    /// See [`wheel_index`](mod@crate::constants::wheel_index)
625    /// for wheel order.
626    pub suspension_velocity: [f32; 4],
627    /// Acceleration values of suspension for each wheel.
628    /// See [`wheel_index`](mod@crate::constants::wheel_index)
629    /// for wheel order.
630    pub suspension_acceleration: [f32; 4],
631    /// Speed of each wheel.
632    /// See [`wheel_index`](mod@crate::constants::wheel_index)
633    /// for wheel order.
634    pub wheel_speed: [f32; 4],
635    /// Slip ratio for each wheel.
636    /// See [`wheel_index`](mod@crate::constants::wheel_index)
637    /// for wheel order.
638    pub wheel_slip_ratio: [f32; 4],
639    /// Slip angles for each wheel.
640    /// See [`wheel_index`](mod@crate::constants::wheel_index)
641    /// for wheel order.
642    /// Available from the 2024 format onwards.
643    #[br(if(packet_format >= 2024))]
644    pub wheel_slip_angle: [f32; 4],
645    /// Lateral forces for each wheel.
646    /// See [`wheel_index`](mod@crate::constants::wheel_index)
647    /// for wheel order.
648    /// Available from the 2024 format onwards.
649    #[br(if(packet_format >= 2024))]
650    pub wheel_lat_force: [f32; 4],
651    /// Longitudinal forces for each wheel.
652    /// See [`wheel_index`](mod@crate::constants::wheel_index)
653    /// for wheel order.
654    /// Available from the 2024 format onwards.
655    #[br(if(packet_format >= 2024))]
656    pub wheel_long_force: [f32; 4],
657    /// Height of centre of gravity above ground.
658    /// Available from the 2024 format onwards.
659    #[br(if(packet_format >= 2024))]
660    pub height_of_cog_above_ground: f32,
661    /// X velocity in local space.
662    pub local_velocity_x: f32,
663    /// Y velocity in local space.
664    pub local_velocity_y: f32,
665    /// Z velocity in local space.
666    pub local_velocity_z: f32,
667    /// Angular velocity X component.
668    pub angular_velocity_x: f32,
669    /// Angular velocity Y component.
670    pub angular_velocity_y: f32,
671    /// Angular velocity Z component.
672    pub angular_velocity_z: f32,
673    /// Angular acceleration X component.
674    pub angular_acceleration_x: f32,
675    /// Angular acceleration Y component.
676    pub angular_acceleration_y: f32,
677    /// Angular acceleration Z component.
678    pub angular_acceleration_z: f32,
679    /// Current front wheels angle in radians.
680    pub front_wheels_angle: f32,
681    /// Vertical forces for each wheel.
682    /// See [`wheel_index`](mod@crate::constants::wheel_index)
683    /// for wheel order.
684    /// Available from the 2023 format onwards.
685    #[br(if(packet_format >= 2023))]
686    pub wheel_vert_force: [f32; 4],
687    /// Front plank edge height above road surface.
688    /// Available from the 2024 format onwards.
689    #[br(if(packet_format >= 2024))]
690    pub front_aero_height: f32,
691    /// Rear plank edge height above road surface.
692    /// Available from the 2024 format onwards.
693    #[br(if(packet_format >= 2024))]
694    pub rear_aero_height: f32,
695    /// Roll angle of the front suspension.
696    /// Available from the 2024 format onwards.
697    #[br(if(packet_format >= 2024))]
698    pub front_roll_angle: f32,
699    /// Roll angle of the rear suspension.
700    /// Available from the 2024 format onwards.
701    #[br(if(packet_format >= 2024))]
702    pub rear_roll_angle: f32,
703    /// Yaw angle of the chassis relative to the direction of motion - radians.
704    /// Available from the 2024 format onwards.
705    #[br(if(packet_format >= 2024))]
706    pub chassis_yaw: f32,
707}
708
709/// Extra information that's only relevant to time trial game mode.
710/// Available from the 2024 format onwards.
711#[non_exhaustive]
712#[derive(
713    BinRead, Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize,
714)]
715#[br(little, import(_packet_format: u16))]
716pub struct F1PacketTimeTrial {
717    /// Data set of player's best run this session.
718    pub player_session_best_data_set: TimeTrialDataSet,
719    /// Data set of player's personal best run.
720    pub personal_best_data_set: TimeTrialDataSet,
721    /// Data set of rival's best run.
722    pub rival_data_set: TimeTrialDataSet,
723}
724
725#[derive(Debug, PartialEq)]
726pub(crate) struct MapBoolError(u8);
727
728impl fmt::Display for MapBoolError {
729    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730        write!(f, "Invalid bool value: {}", self.0)
731    }
732}
733
734impl Error for MapBoolError {}
735
736pub(crate) fn u8_to_bool(value: u8) -> Result<bool, MapBoolError> {
737    match value {
738        0 => Ok(false),
739        1 => Ok(true),
740        _ => Err(MapBoolError(value)),
741    }
742}
743
744pub(crate) fn u8_to_usize(value: u8) -> usize {
745    value as usize
746}
747
748pub(crate) fn read_name(bytes: [u8; 48]) -> Result<String, FromUtf8Error> {
749    let first_nul_index =
750        bytes.iter().position(|&byte| byte == b'\0').unwrap_or(bytes.len());
751
752    String::from_utf8(bytes[..first_nul_index].to_vec())
753}