feat: add 2022 spec session packet parser

This commit is contained in:
Maciej Pędzich 2025-02-17 14:40:12 +01:00
parent 4b304fbd4e
commit dee766fb12
Signed by: maciejpedzich
GPG Key ID: CE4A303D84882F0D
4 changed files with 535 additions and 2 deletions

View File

@ -34,3 +34,377 @@ pub enum PacketId {
MotionEx = 13,
TimeTrial = 14,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(i8))]
pub enum MarshalZoneFlag {
Unknown = -1,
None = 0,
Green = 1,
Blue = 2,
Yellow = 3,
Red = 4,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum Weather {
Clear = 0,
LightCloud = 1,
Overcast = 2,
LightRain = 3,
HeavyRain = 4,
Storm = 5,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum SessionType {
Unknown = 0,
Practice1 = 1,
Practice2 = 2,
Practice3 = 3,
ShortPractice = 4,
Qualifying1 = 5,
Qualifying2 = 6,
Qualifying3 = 7,
ShortQualifying = 8,
OneShotQualifying = 9,
Race = 10,
Race2 = 11,
TimeTrial = 12,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(i8))]
pub enum TemperatureChange {
Up = 0,
Down = 1,
NoChange = 2,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(i8))]
pub enum TrackId {
Unknown = -1,
Melbourne = 0,
PaulRicard = 1,
Shanghai = 2,
Sakhir = 3,
Catalunya = 4,
Monaco = 5,
Montreal = 6,
Silverstone = 7,
Hockenheim = 8,
Hungaroring = 9,
Spa = 10,
Monza = 11,
Singapore = 12,
Suzuka = 13,
AbuDhabi = 14,
Texas = 15,
Brazil = 16,
Austria = 17,
Sochi = 18,
Mexico = 19,
Baku = 20,
SakhirShort = 21,
SilverstoneShort = 22,
TexasShort = 23,
SuzukaShort = 24,
Hanoi = 25,
Zandvoort = 26,
Imola = 27,
Portimao = 28,
Jeddah = 29,
Miami = 30,
LasVegas = 31,
Losail = 32,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum Formula {
F1Modern = 0,
F1Classic = 1,
F2 = 2,
F1Generic = 3,
Beta = 4,
Supercars = 5,
Esports = 6,
F22021 = 7,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum SafetyCarStatus {
None = 0,
Virtual = 1,
Full = 2,
FormationLap = 3,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum ForecastAccuracy {
Perfect = 0,
Approximate = 1,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum BrakingAssist {
Off = 0,
Low = 1,
Medium = 2,
High = 3,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum GearboxAssist {
Unknown = 0,
Manual = 1,
ManualWithSuggestedGear = 2,
Automatic = 3,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum DynamicRacingLine {
Off = 0,
CornersOnly = 1,
Full = 2,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum DynamicRacingLineType {
TwoDimensional = 0,
ThreeDimensional = 1,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum GameMode {
EventMode = 0,
GrandPrix = 3,
TimeTrial = 5,
Splitscreen = 6,
OnlineCustom = 7,
OnlineLeague = 8,
CareerInvitational = 11,
ChampionshipInvitational = 12,
Championship = 13,
OnlineChampionship = 14,
OnlineWeeklyEvent = 15,
Career2022 = 19,
Career2022Online = 20,
Benchmark = 127,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum Ruleset {
PracticeAndQualifying = 0,
Race = 1,
TimeTrial = 2,
TimeAttack = 4,
CheckpointChallenge = 6,
Autocross = 8,
Drift = 9,
AverageSpeedZone = 10,
RivalDuel = 11,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum SessionLength {
None = 0,
VeryShort = 2,
Short = 3,
Medium = 4,
MediumLong = 5,
Long = 6,
Full = 7,
}

View File

@ -2,7 +2,7 @@ pub mod constants;
pub mod packets;
use crate::constants::PacketId;
use crate::packets::F1PacketMotionData;
use crate::packets::{F1PacketMotionData, F1PacketSessionData};
use binrw::io::Cursor;
use binrw::{BinRead, BinReaderExt, BinResult};
@ -69,4 +69,6 @@ pub struct F1PacketHeader {
pub struct F1PacketBody {
#[br(if(packet_id == PacketId::Motion), args(packet_format))]
pub motion: Option<F1PacketMotionData>,
#[br(if(packet_id == PacketId::Session), args(packet_format))]
pub session: Option<F1PacketSessionData>,
}

View File

@ -1,7 +1,15 @@
pub mod motion;
pub mod session;
use crate::packets::motion::CarMotionData;
use crate::constants::{
BrakingAssist, DynamicRacingLine, DynamicRacingLineType, ForecastAccuracy,
Formula, GameMode, GearboxAssist, Ruleset, SafetyCarStatus, SessionLength,
SessionType, TrackId, Weather,
};
use crate::packets::session::{MarshalZone, WeatherForecastSample};
use binrw::BinRead;
use serde::{Deserialize, Serialize};
use std::string::FromUtf8Error;
@ -16,11 +24,119 @@ pub struct F1PacketMotionData {
/// Motion data for all cars on track.
#[br(count(22))]
pub car_motion_data: Vec<CarMotionData>,
/// Extra player car ONLY motion data (2022 format only).
/// Extra player car only motion data (2022 format only).
#[br(if(packet_format == 2022))]
pub motion_ex_data: Option<F1PacketMotionExData>,
}
#[allow(clippy::struct_excessive_bools)]
#[non_exhaustive]
#[derive(
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)]
#[br(
little,
import(packet_format: u16),
)]
pub struct F1PacketSessionData {
/// Current weather.
pub weather: Weather,
/// Track temperature in degrees Celsius.
pub track_temperature: i8,
/// Air temperature in degrees Celsius.
pub air_temperature: i8,
/// Total number of laps in this session.
pub total_laps: u8,
/// Track's length in metres.
pub track_length: u16,
/// Session's type.
pub session_type: SessionType,
/// Unique identifier of the track.
pub track_id: TrackId,
/// Formula of cars being raced.
pub formula: Formula,
/// Time left in the session in seconds.
pub session_time_left: u16,
/// Session's duration in seconds.
pub session_duration: u16,
/// Pit lane's speed limit in kilometres per hour.
pub pit_speed_limit: u8,
/// Whether the game is paused.
#[br(map(u8_to_bool))]
pub game_paused: bool,
/// Whether the player is spectating.
#[br(map(u8_to_bool))]
pub is_spectating: bool,
/// Index of the car being spectated.
#[br(map(u8_to_usize))]
pub spectator_car_index: usize,
/// Whether SLI Pro support is active.
#[br(map(u8_to_bool))]
pub sli_pro_native_support: bool,
/// Number of marshal zones to follow.
#[br(map(u8_to_usize))]
pub num_marshal_zones: usize,
/// List of up to 21 marshal zones.
#[br(count(21), args{ inner: (packet_format,) })]
pub marshal_zones: Vec<MarshalZone>,
/// Safety car deployment status.
pub safety_car_status: SafetyCarStatus,
/// Whether this game is online.
#[br(map(u8_to_bool))]
pub network_game: bool,
/// Number of weather samples to follow.
#[br(map(u8_to_usize))]
pub num_weather_forecast_samples: usize,
/// List of up to 56 weather forecast samples.
#[br(count(56), args{ inner: (packet_format,) })]
pub weather_forecast_samples: Vec<WeatherForecastSample>,
/// Weather forecast accuracy.
pub forecast_accuracy: ForecastAccuracy,
/// AI difficulty rating (0-110).
pub ai_difficulty: u8,
/// Identifier for season - persists across saves.
pub season_link_identifier: u32,
/// Identifier for weekend - persists across saves.
pub weekend_link_identifier: u32,
/// Identifier for session - persists across saves.
pub session_link_identifier: u32,
/// Ideal lap for the player to pit on for current strategy.
pub pit_stop_window_ideal_lap: u8,
/// The latest lap for the player to pit on for current strategy.
pub pit_stop_window_latest_lap: u8,
/// Predicted position for the player to rejoin at.
pub pit_stop_rejoin_position: u8,
/// Whether the steering assist is enabled.
#[br(map(u8_to_bool))]
pub steering_assist: bool,
/// Type of braking assist enabled.
pub braking_assist: BrakingAssist,
/// Type of gearbox assist enabled.
pub gearbox_assist: GearboxAssist,
/// Whether the pit assist is enabled.
#[br(map(u8_to_bool))]
pub pit_assist: bool,
/// Whether the pit release assist is enabled.
#[br(map(u8_to_bool))]
pub pit_release_assist: bool,
/// Whether the ERS assist is enabled.
#[br(map(u8_to_bool))]
pub ers_assist: bool,
/// Whether the DRS assist is enabled.
#[br(map(u8_to_bool))]
pub drs_assist: bool,
/// Type of the dynamic racing line assist.
pub dynamic_racing_line: DynamicRacingLine,
/// Type of the dynamic racing line (2D/3D).
pub dynamic_racing_line_type: DynamicRacingLineType,
/// Game mode's identifier.
pub game_mode: GameMode,
/// Ruleset's identifier.
pub ruleset: Ruleset,
/// Session's length.
pub session_length: SessionLength,
}
/// Extended motion data for player's car. Available as a:
/// - part of [`F1PacketMotionData`] in the 2022 format
/// - standalone packet from the 2023 format onwards

41
src/packets/session.rs Normal file
View File

@ -0,0 +1,41 @@
use crate::constants::{
MarshalZoneFlag, SessionType, TemperatureChange, Weather,
};
use binrw::BinRead;
use serde::{Deserialize, Serialize};
/// Section of the track supervised by marshals.
#[derive(
BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize,
)]
#[br(little, import(_packet_format: u16))]
pub struct MarshalZone {
/// Fraction (0..1) of way through the lap the marshal zone starts.
pub zone_start: f32,
/// Flag that's currently being waved in the marshal zone.
pub zone_flag: MarshalZoneFlag,
}
/// Weather forecast sample for a given session.
#[derive(
BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize,
)]
#[br(little, import(_packet_format: u16))]
pub struct WeatherForecastSample {
/// Session's type.
pub session_type: SessionType,
/// Time in minutes the forecast is for.
pub time_offset: u8,
/// Forecasted weather.
pub weather: Weather,
/// Track temperature in degrees Celsius.
pub track_temperature: i8,
/// Track temperature change.
pub track_temperature_change: TemperatureChange,
/// Air temperature in degrees Celsius.
pub air_temperature: i8,
/// Air temperature change.
pub air_temperature_change: TemperatureChange,
/// Chance of rain.
pub rain_percentage: u8,
}