boxcars/network/
attributes.rs

1use crate::bits::RlBits;
2use crate::errors::AttributeError;
3use crate::network::{
4    ActorId, ObjectId, ObjectIndex, Quaternion, Rotation, Vector3f, VersionTriplet,
5};
6use crate::parsing_utils::{decode_utf16, decode_windows1252};
7use bitter::{BitReader, LittleEndianReader};
8use encoding_rs::WINDOWS_1252;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub(crate) enum AttributeTag {
12    Boolean,
13    Byte,
14    AppliedDamage,
15    DamageState,
16    CamSettings,
17    ClubColors,
18    Demolish,
19    DemolishFx,
20    DemolishExtended,
21    Enum,
22    Explosion,
23    ExtendedExplosion,
24    FlaggedByte,
25    ActiveActor,
26    Float,
27    GameMode,
28    Int,
29    Int64,
30    Loadout,
31    TeamLoadout,
32    Location,
33    MusicStinger,
34    Pickup,
35    PickupNew,
36    PlayerHistoryKey,
37    QWordString,
38    Welded,
39    RigidBody,
40    Title,
41    TeamPaint,
42    NotImplemented,
43    String,
44    UniqueId,
45    Reservation,
46    PartyLeader,
47    PrivateMatchSettings,
48    LoadoutOnline,
49    LoadoutsOnline,
50    StatEvent,
51    RotationTag,
52    RepStatTitle,
53    PickupInfo,
54    Impulse,
55    ReplicatedBoost,
56    LogoData,
57}
58
59/// The attributes for updated actors in the network data.
60///
61/// The vast majority of attributes in the network data are rigid bodies. As a performance
62/// improvent, any attribute variant larger than the size of a rigid body is moved to the heap (ie:
63/// `Box::new`). This change increased throughput by 40%.
64#[derive(Debug, Clone, PartialEq, Serialize)]
65pub enum Attribute {
66    Boolean(bool),
67    Byte(u8),
68    AppliedDamage(AppliedDamage),
69    DamageState(DamageState),
70    CamSettings(Box<CamSettings>),
71    ClubColors(ClubColors),
72    Demolish(Box<Demolish>),
73    DemolishExtended(Box<DemolishExtended>),
74    DemolishFx(Box<DemolishFx>),
75    Enum(u16),
76    Explosion(Explosion),
77    ExtendedExplosion(ExtendedExplosion),
78    FlaggedByte(bool, u8),
79    ActiveActor(ActiveActor),
80    Float(f32),
81    GameMode(u8, u8),
82    Int(i32),
83
84    #[serde(serialize_with = "crate::serde_utils::display_it")]
85    Int64(i64),
86    Loadout(Box<Loadout>),
87    TeamLoadout(Box<TeamLoadout>),
88    Location(Vector3f),
89    MusicStinger(MusicStinger),
90    PlayerHistoryKey(u16),
91    Pickup(Pickup),
92    PickupNew(PickupNew),
93
94    #[serde(serialize_with = "crate::serde_utils::display_it")]
95    QWord(u64),
96    Welded(Welded),
97    Title(bool, bool, u32, u32, u32, u32, u32, bool),
98    TeamPaint(TeamPaint),
99    RigidBody(RigidBody),
100    String(String),
101    UniqueId(Box<UniqueId>),
102    Reservation(Box<Reservation>),
103    PartyLeader(Option<Box<UniqueId>>),
104    PrivateMatch(Box<PrivateMatchSettings>),
105    LoadoutOnline(Vec<Vec<Product>>),
106    LoadoutsOnline(LoadoutsOnline),
107    StatEvent(StatEvent),
108    Rotation(Rotation),
109    RepStatTitle(RepStatTitle),
110    PickupInfo(PickupInfo),
111    Impulse(Impulse),
112    ReplicatedBoost(ReplicatedBoost),
113    LogoData(LogoData),
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
117pub struct ActiveActor {
118    pub active: bool,
119    pub actor: ActorId,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
123pub struct CamSettings {
124    pub fov: f32,
125    pub height: f32,
126    pub angle: f32,
127    pub distance: f32,
128    pub stiffness: f32,
129    pub swivel: f32,
130    pub transition: Option<f32>,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
134pub struct ClubColors {
135    pub blue_flag: bool,
136    pub blue_color: u8,
137    pub orange_flag: bool,
138    pub orange_color: u8,
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
142pub struct AppliedDamage {
143    pub id: u8,
144    pub position: Vector3f,
145    pub damage_index: i32,
146    pub total_damage: i32,
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
150pub struct DamageState {
151    /// State of the dropshot tile (0 - undamaged, 1 - damaged, 2 - destroyed)
152    pub tile_state: u8,
153
154    /// True if damaged
155    pub damaged: bool,
156
157    /// Player actor that inflicted the damage
158    pub offender: ActorId,
159
160    /// Position of the ball at the time of the damage
161    pub ball_position: Vector3f,
162
163    /// True for the dropshot tile that was hit by the ball (center tile of the damage area)
164    pub direct_hit: bool,
165    pub unknown1: bool,
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
169pub struct Demolish {
170    pub attacker_flag: bool,
171    pub attacker: ActorId,
172    pub victim_flag: bool,
173    pub victim: ActorId,
174    pub attack_velocity: Vector3f,
175    pub victim_velocity: Vector3f,
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
179pub struct DemolishExtended {
180    pub attacker_pri: ActiveActor, // player replication info
181    pub self_demo: ActiveActor,
182    pub self_demolish: bool,
183    pub goal_explosion_owner: ActiveActor,
184    pub attacker: ActiveActor,
185    pub victim: ActiveActor,
186    pub attacker_velocity: Vector3f,
187    pub victim_velocity: Vector3f,
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
191pub struct DemolishFx {
192    pub custom_demo_flag: bool,
193    pub custom_demo_id: i32,
194    pub attacker_flag: bool,
195    pub attacker: ActorId,
196    pub victim_flag: bool,
197    pub victim: ActorId,
198    pub attack_velocity: Vector3f,
199    pub victim_velocity: Vector3f,
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
203pub struct Explosion {
204    pub flag: bool,
205    pub actor: ActorId,
206    pub location: Vector3f,
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
210pub struct ExtendedExplosion {
211    pub explosion: Explosion,
212    pub unknown1: bool,
213    pub secondary_actor: ActorId,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
217pub struct Loadout {
218    pub version: u8,
219    pub body: u32,
220    pub decal: u32,
221    pub wheels: u32,
222    pub rocket_trail: u32,
223    pub antenna: u32,
224    pub topper: u32,
225    pub unknown1: u32,
226    pub unknown2: Option<u32>,
227    pub engine_audio: Option<u32>,
228    pub trail: Option<u32>,
229    pub goal_explosion: Option<u32>,
230    pub banner: Option<u32>,
231    pub product_id: Option<u32>,
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
235pub struct TeamLoadout {
236    pub blue: Loadout,
237    pub orange: Loadout,
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
241pub struct StatEvent {
242    pub unknown1: bool,
243    pub object_id: i32,
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
247pub struct MusicStinger {
248    pub flag: bool,
249    pub cue: u32,
250    pub trigger: u8,
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
254pub struct Pickup {
255    pub instigator: Option<ActorId>,
256    pub picked_up: bool,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
260pub struct PickupNew {
261    pub instigator: Option<ActorId>,
262    pub picked_up: u8,
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
266pub struct Welded {
267    pub active: bool,
268    pub actor: ActorId,
269    pub offset: Vector3f,
270    pub mass: f32,
271    pub rotation: Rotation,
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
275pub struct TeamPaint {
276    pub team: u8,
277    pub primary_color: u8,
278    pub accent_color: u8,
279    pub primary_finish: u32,
280    pub accent_finish: u32,
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
284pub struct RigidBody {
285    pub sleeping: bool,
286    pub location: Vector3f,
287    pub rotation: Quaternion,
288    pub linear_velocity: Option<Vector3f>,
289    pub angular_velocity: Option<Vector3f>,
290}
291
292#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
293pub struct UniqueId {
294    pub system_id: u8,
295    pub remote_id: RemoteId,
296    pub local_id: u8,
297}
298
299#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize)]
300pub struct PsyNetId {
301    #[serde(serialize_with = "crate::serde_utils::display_it")]
302    pub online_id: u64,
303    pub unknown1: Vec<u8>,
304}
305
306#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize)]
307pub struct SwitchId {
308    #[serde(serialize_with = "crate::serde_utils::display_it")]
309    pub online_id: u64,
310    pub unknown1: Vec<u8>,
311}
312
313#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize)]
314pub struct Ps4Id {
315    #[serde(serialize_with = "crate::serde_utils::display_it")]
316    pub online_id: u64,
317    pub name: String,
318    pub unknown1: Vec<u8>,
319}
320
321#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
322pub enum RemoteId {
323    PlayStation(Ps4Id),
324    PsyNet(PsyNetId),
325    SplitScreen(u32),
326
327    #[serde(serialize_with = "crate::serde_utils::display_it")]
328    Steam(u64),
329    Switch(SwitchId),
330
331    #[serde(serialize_with = "crate::serde_utils::display_it")]
332    Xbox(u64),
333
334    #[serde(serialize_with = "crate::serde_utils::display_it")]
335    QQ(u64),
336    Epic(String),
337}
338
339#[derive(Debug, Clone, PartialEq, Serialize)]
340pub struct Reservation {
341    pub number: u32,
342    pub unique_id: UniqueId,
343    pub name: Option<String>,
344    pub unknown1: bool,
345    pub unknown2: bool,
346    pub unknown3: Option<u8>,
347}
348
349#[derive(Debug, Clone, PartialEq, Serialize)]
350pub struct PrivateMatchSettings {
351    pub mutators: String,
352    pub joinable_by: u32,
353    pub max_players: u32,
354    pub game_name: String,
355    pub password: String,
356    pub flag: bool,
357}
358
359#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
360pub struct Product {
361    pub unknown: bool,
362    pub object_ind: ObjectId,
363    pub value: ProductValue,
364}
365
366#[derive(Debug, Clone, PartialEq, Serialize)]
367pub struct LoadoutsOnline {
368    pub blue: Vec<Vec<Product>>,
369    pub orange: Vec<Vec<Product>>,
370    pub unknown1: bool,
371    pub unknown2: bool,
372}
373
374#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
375pub enum ProductValue {
376    NoColor,
377    Absent,
378    OldColor(u32),
379    NewColor(u32),
380    OldPaint(u32),
381    NewPaint(u32),
382    Title(String),
383    SpecialEdition(u32),
384    OldTeamEdition(u32),
385    NewTeamEdition(u32),
386}
387
388#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
389pub struct RepStatTitle {
390    pub unknown: bool,
391    pub name: String,
392    pub unknown2: bool,
393    pub index: u32,
394    pub value: u32,
395}
396
397#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
398pub struct PickupInfo {
399    pub active: bool,
400    pub actor: ActorId,
401    pub items_are_preview: bool,
402    pub unknown: bool,
403    pub unknown2: bool,
404}
405
406#[derive(Debug, Clone, PartialEq, Serialize)]
407pub struct Impulse {
408    pub compressed_rotation: i32,
409    pub speed: f32,
410}
411
412#[derive(Debug, Clone, PartialEq, Serialize)]
413pub struct ReplicatedBoost {
414    pub grant_count: u8,
415    pub boost_amount: u8,
416    pub unused1: u8,
417    pub unused2: u8,
418}
419
420#[derive(Debug, Clone, PartialEq, Serialize)]
421pub struct LogoData {
422    pub logo_id: u32,
423    pub swap_colors: bool,
424}
425
426#[derive(Debug, Clone, Copy, PartialEq, Eq)]
427pub(crate) struct ProductValueDecoder {
428    version: VersionTriplet,
429    color_ind: ObjectId,
430    painted_ind: ObjectId,
431    special_edition_ind: ObjectId,
432    team_edition_ind: ObjectId,
433    title_ind: ObjectId,
434}
435
436impl ProductValueDecoder {
437    pub fn create(version: VersionTriplet, object_index: &ObjectIndex) -> Self {
438        let color_ind = object_index
439            .primary_by_name("TAGame.ProductAttribute_UserColor_TA")
440            .unwrap_or_default();
441        let painted_ind = object_index
442            .primary_by_name("TAGame.ProductAttribute_Painted_TA")
443            .unwrap_or_default();
444        let title_ind = object_index
445            .primary_by_name("TAGame.ProductAttribute_TitleID_TA")
446            .unwrap_or_default();
447        let special_edition_ind = object_index
448            .primary_by_name("TAGame.ProductAttribute_SpecialEdition_TA")
449            .unwrap_or_default();
450        let team_edition_ind = object_index
451            .primary_by_name("TAGame.ProductAttribute_TeamEdition_TA")
452            .unwrap_or_default();
453
454        ProductValueDecoder {
455            version,
456            color_ind,
457            painted_ind,
458            title_ind,
459            special_edition_ind,
460            team_edition_ind,
461        }
462    }
463
464    pub fn decode(
465        &self,
466        bits: &mut LittleEndianReader<'_>,
467        obj_ind: ObjectId,
468        buf: &mut [u8],
469    ) -> Option<ProductValue> {
470        if obj_ind == self.color_ind {
471            if self.version >= VersionTriplet(868, 23, 8) {
472                bits.read_u32().map(ProductValue::NewColor)
473            } else {
474                bits.if_get(|b| {
475                    b.read_bits(31)
476                        .map(|x| x as u32)
477                        .map(ProductValue::OldColor)
478                })
479                .map(|x| x.unwrap_or(ProductValue::NoColor))
480            }
481        } else if obj_ind == self.painted_ind {
482            if self.version >= VersionTriplet(868, 18, 0) {
483                bits.read_bits(31)
484                    .map(|x| x as u32)
485                    .map(ProductValue::NewPaint)
486            } else {
487                bits.read_bits_max_computed(3, 14)
488                    .map(|x| x as u32)
489                    .map(ProductValue::OldPaint)
490            }
491        } else if obj_ind == self.title_ind {
492            decode_text(bits, buf).ok().map(ProductValue::Title)
493        } else if obj_ind == self.special_edition_ind {
494            bits.read_bits(31)
495                .map(|x| x as u32)
496                .map(ProductValue::SpecialEdition)
497        } else if obj_ind == self.team_edition_ind {
498            if self.version >= VersionTriplet(868, 18, 0) {
499                bits.read_bits(31)
500                    .map(|x| x as u32)
501                    .map(ProductValue::NewTeamEdition)
502            } else {
503                bits.read_bits_max_computed(3, 14)
504                    .map(|x| x as u32)
505                    .map(ProductValue::OldTeamEdition)
506            }
507        } else {
508            Some(ProductValue::Absent)
509        }
510    }
511}
512
513#[derive(Debug, Clone, Copy, PartialEq, Eq)]
514pub(crate) struct AttributeDecoder {
515    pub(crate) version: VersionTriplet,
516    pub(crate) product_decoder: ProductValueDecoder,
517    pub(crate) is_rl_223: bool,
518}
519
520impl AttributeDecoder {
521    pub fn decode(
522        &self,
523        tag: AttributeTag,
524        bits: &mut LittleEndianReader<'_>,
525        buf: &mut [u8],
526    ) -> Result<Attribute, AttributeError> {
527        match tag {
528            AttributeTag::Boolean => self.decode_boolean(bits),
529            AttributeTag::Byte => self.decode_byte(bits),
530            AttributeTag::AppliedDamage => self.decode_applied_damage(bits),
531            AttributeTag::DamageState => self.decode_damage_state(bits),
532            AttributeTag::CamSettings => self.decode_cam_settings(bits),
533            AttributeTag::ClubColors => self.decode_club_colors(bits),
534            AttributeTag::Demolish => self.decode_demolish(bits),
535            AttributeTag::DemolishExtended => self.decode_demolish_extended(bits),
536            AttributeTag::DemolishFx => self.decode_demolish_fx(bits),
537            AttributeTag::Enum => self.decode_enum(bits),
538            AttributeTag::Explosion => self.decode_explosion(bits),
539            AttributeTag::ExtendedExplosion => self.decode_extended_explosion(bits),
540            AttributeTag::ActiveActor => self.decode_active_actor(bits),
541            AttributeTag::FlaggedByte => self.decode_flagged_byte(bits),
542            AttributeTag::Float => self.decode_float(bits),
543            AttributeTag::GameMode => self.decode_game_mode(bits),
544            AttributeTag::Int => self.decode_int(bits),
545            AttributeTag::Int64 => self.decode_int64(bits),
546            AttributeTag::Loadout => self.decode_loadout(bits),
547            AttributeTag::TeamLoadout => self.decode_team_loadout(bits),
548            AttributeTag::Location => self.decode_location(bits),
549            AttributeTag::MusicStinger => self.decode_music_stinger(bits),
550            AttributeTag::Pickup => self.decode_pickup(bits),
551            AttributeTag::PickupNew => self.decode_pickup_new(bits),
552            AttributeTag::PlayerHistoryKey => self.decode_player_history_key(bits),
553            AttributeTag::QWordString => self.decode_qword_string(bits, buf),
554            AttributeTag::Welded => self.decode_welded(bits),
555            AttributeTag::RigidBody => self.decode_rigid_body(bits),
556            AttributeTag::Title => self.decode_title(bits),
557            AttributeTag::TeamPaint => self.decode_team_paint(bits),
558            AttributeTag::NotImplemented => self.decode_not_implemented(bits),
559            AttributeTag::String => self.decode_string(bits, buf),
560            AttributeTag::UniqueId => self.decode_unique_id(bits, buf),
561            AttributeTag::Reservation => self.decode_reservation(bits, buf),
562            AttributeTag::PartyLeader => self.decode_party_leader(bits, buf),
563            AttributeTag::PrivateMatchSettings => self.decode_private_match_settings(bits, buf),
564            AttributeTag::LoadoutOnline => self.decode_loadout_online(bits, buf),
565            AttributeTag::LoadoutsOnline => self.decode_loadouts_online(bits, buf),
566            AttributeTag::StatEvent => self.decode_stat_event(bits),
567            AttributeTag::RotationTag => self.decode_rotation(bits),
568            AttributeTag::RepStatTitle => self.decode_rep_stat_title(bits, buf),
569            AttributeTag::PickupInfo => self.decode_pickup_info(bits),
570            AttributeTag::Impulse => self.decode_impulse(bits),
571            AttributeTag::ReplicatedBoost => self.decode_replicated_boost(bits),
572            AttributeTag::LogoData => self.decode_logo_data(bits),
573        }
574    }
575
576    pub fn decode_byte(
577        &self,
578        bits: &mut LittleEndianReader<'_>,
579    ) -> Result<Attribute, AttributeError> {
580        bits.read_u8()
581            .map(Attribute::Byte)
582            .ok_or(AttributeError::NotEnoughDataFor("Byte"))
583    }
584
585    pub fn decode_player_history_key(
586        &self,
587        bits: &mut LittleEndianReader<'_>,
588    ) -> Result<Attribute, AttributeError> {
589        bits.read_bits(14)
590            .map(|x| x as u16)
591            .map(Attribute::PlayerHistoryKey)
592            .ok_or(AttributeError::NotEnoughDataFor("PlayerHistoryKey"))
593    }
594
595    fn _decode_flagged_byte(&self, bits: &mut LittleEndianReader<'_>) -> Option<Attribute> {
596        let b = bits.read_bit()?;
597        let data = bits.read_u8()?;
598        Some(Attribute::FlaggedByte(b, data))
599    }
600
601    pub fn decode_flagged_byte(
602        &self,
603        bits: &mut LittleEndianReader<'_>,
604    ) -> Result<Attribute, AttributeError> {
605        self._decode_flagged_byte(bits)
606            .ok_or(AttributeError::NotEnoughDataFor("FlaggedByte"))
607    }
608
609    pub fn decode_boolean(
610        &self,
611        bits: &mut LittleEndianReader<'_>,
612    ) -> Result<Attribute, AttributeError> {
613        bits.read_bit()
614            .map(Attribute::Boolean)
615            .ok_or(AttributeError::NotEnoughDataFor("Boolean"))
616    }
617
618    pub fn _decode_applied_damage(
619        &self,
620        bits: &mut LittleEndianReader<'_>,
621    ) -> Option<AppliedDamage> {
622        let id = bits.read_u8()?;
623        let position = Vector3f::decode(bits, self.version.net_version())?;
624        let damage_index = bits.read_i32()?;
625        let total_damage = bits.read_i32()?;
626        Some(AppliedDamage {
627            id,
628            position,
629            damage_index,
630            total_damage,
631        })
632    }
633
634    pub fn decode_applied_damage(
635        &self,
636        bits: &mut LittleEndianReader<'_>,
637    ) -> Result<Attribute, AttributeError> {
638        self._decode_applied_damage(bits)
639            .map(Attribute::AppliedDamage)
640            .ok_or(AttributeError::NotEnoughDataFor("Applied Damage"))
641    }
642
643    fn _decode_damage_state(&self, bits: &mut LittleEndianReader<'_>) -> Option<DamageState> {
644        let tile_state = bits.read_u8()?;
645        let damaged = bits.read_bit()?;
646        let offender = bits.read_i32().map(ActorId)?;
647        let ball_position = Vector3f::decode(bits, self.version.net_version())?;
648        let direct_hit = bits.read_bit()?;
649        let unknown1 = bits.read_bit()?;
650        Some(DamageState {
651            tile_state,
652            damaged,
653            offender,
654            ball_position,
655            direct_hit,
656            unknown1,
657        })
658    }
659
660    pub fn decode_damage_state(
661        &self,
662        bits: &mut LittleEndianReader<'_>,
663    ) -> Result<Attribute, AttributeError> {
664        self._decode_damage_state(bits)
665            .map(Attribute::DamageState)
666            .ok_or(AttributeError::NotEnoughDataFor("Damage State"))
667    }
668
669    fn _decode_cam_settings(&self, bits: &mut LittleEndianReader<'_>) -> Option<CamSettings> {
670        let fov = bits.read_f32()?;
671        let height = bits.read_f32()?;
672        let angle = bits.read_f32()?;
673        let distance = bits.read_f32()?;
674        let stiffness = bits.read_f32()?;
675        let swivel = bits.read_f32()?;
676        let transition = if self.version >= VersionTriplet(868, 20, 0) {
677            Some(bits.read_f32()?)
678        } else {
679            None
680        };
681
682        Some(CamSettings {
683            fov,
684            height,
685            angle,
686            distance,
687            stiffness,
688            swivel,
689            transition,
690        })
691    }
692
693    pub fn decode_cam_settings(
694        &self,
695        bits: &mut LittleEndianReader<'_>,
696    ) -> Result<Attribute, AttributeError> {
697        self._decode_cam_settings(bits)
698            .map(Box::new)
699            .map(Attribute::CamSettings)
700            .ok_or(AttributeError::NotEnoughDataFor("Cam Settings"))
701    }
702
703    fn _decode_club_colors(&self, bits: &mut LittleEndianReader<'_>) -> Option<ClubColors> {
704        let blue_flag = bits.read_bit()?;
705        let blue_color = bits.read_u8()?;
706        let orange_flag = bits.read_bit()?;
707        let orange_color = bits.read_u8()?;
708        Some(ClubColors {
709            blue_flag,
710            blue_color,
711            orange_flag,
712            orange_color,
713        })
714    }
715
716    pub fn decode_club_colors(
717        &self,
718        bits: &mut LittleEndianReader<'_>,
719    ) -> Result<Attribute, AttributeError> {
720        self._decode_club_colors(bits)
721            .map(Attribute::ClubColors)
722            .ok_or(AttributeError::NotEnoughDataFor("Club Colors"))
723    }
724
725    fn _decode_demolish(&self, bits: &mut LittleEndianReader<'_>) -> Option<Demolish> {
726        let attacker_flag = bits.read_bit()?;
727        let attacker = bits.read_i32().map(ActorId)?;
728        let victim_flag = bits.read_bit()?;
729        let victim = bits.read_i32().map(ActorId)?;
730        let attack_velocity = Vector3f::decode(bits, self.version.net_version())?;
731        let victim_velocity = Vector3f::decode(bits, self.version.net_version())?;
732        Some(Demolish {
733            attacker_flag,
734            attacker,
735            victim_flag,
736            victim,
737            attack_velocity,
738            victim_velocity,
739        })
740    }
741
742    pub fn decode_demolish(
743        &self,
744        bits: &mut LittleEndianReader<'_>,
745    ) -> Result<Attribute, AttributeError> {
746        self._decode_demolish(bits)
747            .map(Box::new)
748            .map(Attribute::Demolish)
749            .ok_or(AttributeError::NotEnoughDataFor("Demolish"))
750    }
751
752    pub fn _decode_demolish_fx(&self, bits: &mut LittleEndianReader<'_>) -> Option<DemolishFx> {
753        let custom_demo_flag = bits.read_bit()?;
754        let custom_demo_id = bits.read_i32()?;
755        let attacker_flag = bits.read_bit()?;
756        let attacker = bits.read_i32().map(ActorId)?;
757        let victim_flag = bits.read_bit()?;
758        let victim = bits.read_i32().map(ActorId)?;
759        let attack_velocity = Vector3f::decode(bits, self.version.net_version())?;
760        let victim_velocity = Vector3f::decode(bits, self.version.net_version())?;
761
762        Some(DemolishFx {
763            custom_demo_flag,
764            custom_demo_id,
765            attacker_flag,
766            attacker,
767            victim_flag,
768            victim,
769            attack_velocity,
770            victim_velocity,
771        })
772    }
773
774    pub fn decode_demolish_fx(
775        &self,
776        bits: &mut LittleEndianReader<'_>,
777    ) -> Result<Attribute, AttributeError> {
778        self._decode_demolish_fx(bits)
779            .map(Box::new)
780            .map(Attribute::DemolishFx)
781            .ok_or(AttributeError::NotEnoughDataFor("DemolishFx"))
782    }
783
784    pub fn _decode_demolish_extended(
785        &self,
786        bits: &mut LittleEndianReader<'_>,
787    ) -> Option<DemolishExtended> {
788        let attacker_pri = self._decode_active_actor(bits)?;
789        let self_demo = self._decode_active_actor(bits)?;
790        let self_demolish = bits.read_bit()?;
791        let goal_explosion_owner = self._decode_active_actor(bits)?;
792        let attacker = self._decode_active_actor(bits)?;
793        let victim = self._decode_active_actor(bits)?;
794        let attacker_velocity = Vector3f::decode(bits, self.version.net_version())?;
795        let victim_velocity = Vector3f::decode(bits, self.version.net_version())?;
796
797        Some(DemolishExtended {
798            attacker_pri,
799            self_demo,
800            self_demolish,
801            goal_explosion_owner,
802            attacker,
803            victim,
804            attacker_velocity,
805            victim_velocity,
806        })
807    }
808
809    pub fn decode_demolish_extended(
810        &self,
811        bits: &mut LittleEndianReader<'_>,
812    ) -> Result<Attribute, AttributeError> {
813        self._decode_demolish_extended(bits)
814            .map(Box::new)
815            .map(Attribute::DemolishExtended)
816            .ok_or(AttributeError::NotEnoughDataFor("DemolishExtended"))
817    }
818
819    pub fn decode_enum(
820        &self,
821        bits: &mut LittleEndianReader<'_>,
822    ) -> Result<Attribute, AttributeError> {
823        bits.read_bits(11)
824            .map(|x| x as u16)
825            .map(Attribute::Enum)
826            .ok_or(AttributeError::NotEnoughDataFor("Enum"))
827    }
828
829    pub fn decode_explosion(
830        &self,
831        bits: &mut LittleEndianReader<'_>,
832    ) -> Result<Attribute, AttributeError> {
833        decode_explosion(bits, self.version.net_version())
834            .map(Attribute::Explosion)
835            .ok_or(AttributeError::NotEnoughDataFor("Explosion"))
836    }
837
838    fn _decode_stat_event(&self, bits: &mut LittleEndianReader<'_>) -> Option<StatEvent> {
839        let unknown1 = bits.read_bit()?;
840        let object_id = bits.read_i32()?;
841        Some(StatEvent {
842            unknown1,
843            object_id,
844        })
845    }
846
847    pub fn decode_stat_event(
848        &self,
849        bits: &mut LittleEndianReader<'_>,
850    ) -> Result<Attribute, AttributeError> {
851        self._decode_stat_event(bits)
852            .map(Attribute::StatEvent)
853            .ok_or(AttributeError::NotEnoughDataFor("Stat Event"))
854    }
855
856    pub fn decode_rep_stat_title(
857        &self,
858        bits: &mut LittleEndianReader<'_>,
859        buf: &mut [u8],
860    ) -> Result<Attribute, AttributeError> {
861        let unknown = bits
862            .read_bit()
863            .ok_or(AttributeError::NotEnoughDataFor("RepStatTitle"))?;
864        let name = decode_text(bits, buf)?;
865        let unknown2 = bits
866            .read_bit()
867            .ok_or(AttributeError::NotEnoughDataFor("RepStatTitle"))?;
868        let index = bits
869            .read_u32()
870            .ok_or(AttributeError::NotEnoughDataFor("RepStatTitle"))?;
871        let value = bits
872            .read_u32()
873            .ok_or(AttributeError::NotEnoughDataFor("RepStatTitle"))?;
874        Ok(Attribute::RepStatTitle(RepStatTitle {
875            unknown,
876            name,
877            unknown2,
878            index,
879            value,
880        }))
881    }
882
883    pub fn decode_pickup_info(
884        &self,
885        bits: &mut LittleEndianReader<'_>,
886    ) -> Result<Attribute, AttributeError> {
887        let active = bits
888            .read_bit()
889            .ok_or(AttributeError::NotEnoughDataFor("PickupInfo"))?;
890        let actor = bits
891            .read_i32()
892            .map(ActorId)
893            .ok_or(AttributeError::NotEnoughDataFor("PickupInfo"))?;
894
895        let items_are_preview = bits
896            .read_bit()
897            .ok_or(AttributeError::NotEnoughDataFor("PickupInfo"))?;
898        let unknown = bits
899            .read_bit()
900            .ok_or(AttributeError::NotEnoughDataFor("PickupInfo"))?;
901        let unknown2 = bits
902            .read_bit()
903            .ok_or(AttributeError::NotEnoughDataFor("PickupInfo"))?;
904
905        Ok(Attribute::PickupInfo(PickupInfo {
906            active,
907            actor,
908            items_are_preview,
909            unknown,
910            unknown2,
911        }))
912    }
913
914    pub fn decode_impulse(
915        &self,
916        bits: &mut LittleEndianReader<'_>,
917    ) -> Result<Attribute, AttributeError> {
918        let compressed_rotation = bits
919            .read_i32()
920            .ok_or(AttributeError::NotEnoughDataFor("Impulse"))?;
921
922        let speed = bits
923            .read_f32()
924            .ok_or(AttributeError::NotEnoughDataFor("Impulse"))?;
925
926        Ok(Attribute::Impulse(Impulse {
927            compressed_rotation,
928            speed,
929        }))
930    }
931
932    pub fn decode_replicated_boost(
933        &self,
934        bits: &mut LittleEndianReader<'_>,
935    ) -> Result<Attribute, AttributeError> {
936        let grant_count = bits
937            .read_u8()
938            .ok_or(AttributeError::NotEnoughDataFor("ReplicatedBoost"))?;
939
940        let boost_amount = bits
941            .read_u8()
942            .ok_or(AttributeError::NotEnoughDataFor("ReplicatedBoost"))?;
943
944        let unused1 = bits
945            .read_u8()
946            .ok_or(AttributeError::NotEnoughDataFor("ReplicatedBoost"))?;
947
948        let unused2 = bits
949            .read_u8()
950            .ok_or(AttributeError::NotEnoughDataFor("ReplicatedBoost"))?;
951
952        Ok(Attribute::ReplicatedBoost(ReplicatedBoost {
953            grant_count,
954            boost_amount,
955            unused1,
956            unused2,
957        }))
958    }
959
960    fn _decode_extended_explosion(
961        &self,
962        bits: &mut LittleEndianReader<'_>,
963    ) -> Option<ExtendedExplosion> {
964        let explosion = decode_explosion(bits, self.version.net_version())?;
965        let unknown1 = bits.read_bit()?;
966        let secondary_actor = bits.read_i32().map(ActorId)?;
967        Some(ExtendedExplosion {
968            explosion,
969            unknown1,
970            secondary_actor,
971        })
972    }
973
974    pub fn decode_extended_explosion(
975        &self,
976        bits: &mut LittleEndianReader<'_>,
977    ) -> Result<Attribute, AttributeError> {
978        self._decode_extended_explosion(bits)
979            .map(Attribute::ExtendedExplosion)
980            .ok_or(AttributeError::NotEnoughDataFor("Extended Explosion"))
981    }
982
983    pub fn _decode_active_actor(&self, bits: &mut LittleEndianReader<'_>) -> Option<ActiveActor> {
984        bits.refill_lookahead();
985        if bits.lookahead_bits() < 33 {
986            return None;
987        }
988
989        let active = bits.peek_and_consume(1) == 1;
990        let actor = ActorId(bits.peek_and_consume(32) as i32);
991        Some(ActiveActor { active, actor })
992    }
993
994    pub fn decode_active_actor(
995        &self,
996        bits: &mut LittleEndianReader<'_>,
997    ) -> Result<Attribute, AttributeError> {
998        self._decode_active_actor(bits)
999            .map(Attribute::ActiveActor)
1000            .ok_or(AttributeError::NotEnoughDataFor("Active Actor"))
1001    }
1002
1003    pub fn decode_float(
1004        &self,
1005        bits: &mut LittleEndianReader<'_>,
1006    ) -> Result<Attribute, AttributeError> {
1007        bits.read_f32()
1008            .map(Attribute::Float)
1009            .ok_or(AttributeError::NotEnoughDataFor("Float"))
1010    }
1011
1012    pub fn decode_game_mode(
1013        &self,
1014        bits: &mut LittleEndianReader<'_>,
1015    ) -> Result<Attribute, AttributeError> {
1016        let init: u8 = if self.version < VersionTriplet(868, 12, 0) {
1017            2
1018        } else {
1019            8
1020        };
1021
1022        bits.read_bits(u32::from(init))
1023            .map(|x| x as u8)
1024            .map(|x| Attribute::GameMode(init, x))
1025            .ok_or(AttributeError::NotEnoughDataFor("Game Mode"))
1026    }
1027
1028    pub fn decode_int(
1029        &self,
1030        bits: &mut LittleEndianReader<'_>,
1031    ) -> Result<Attribute, AttributeError> {
1032        bits.read_i32()
1033            .map(Attribute::Int)
1034            .ok_or(AttributeError::NotEnoughDataFor("Int"))
1035    }
1036
1037    pub fn decode_int64(
1038        &self,
1039        bits: &mut LittleEndianReader<'_>,
1040    ) -> Result<Attribute, AttributeError> {
1041        bits.read_i64()
1042            .map(Attribute::Int64)
1043            .ok_or(AttributeError::NotEnoughDataFor("Int64"))
1044    }
1045
1046    pub fn decode_loadout(
1047        &self,
1048        bits: &mut LittleEndianReader<'_>,
1049    ) -> Result<Attribute, AttributeError> {
1050        decode_loadout(bits)
1051            .map(Box::new)
1052            .map(Attribute::Loadout)
1053            .ok_or(AttributeError::NotEnoughDataFor("Loadout"))
1054    }
1055
1056    pub fn decode_team_loadout(
1057        &self,
1058        bits: &mut LittleEndianReader<'_>,
1059    ) -> Result<Attribute, AttributeError> {
1060        let blue = decode_loadout(bits).ok_or(AttributeError::NotEnoughDataFor("Team Loadout"))?;
1061        let orange =
1062            decode_loadout(bits).ok_or(AttributeError::NotEnoughDataFor("Team Loadout"))?;
1063        Ok(Attribute::TeamLoadout(Box::new(TeamLoadout {
1064            blue,
1065            orange,
1066        })))
1067    }
1068
1069    pub fn decode_location(
1070        &self,
1071        bits: &mut LittleEndianReader<'_>,
1072    ) -> Result<Attribute, AttributeError> {
1073        Vector3f::decode(bits, self.version.net_version())
1074            .map(Attribute::Location)
1075            .ok_or(AttributeError::NotEnoughDataFor("Location"))
1076    }
1077
1078    fn _decode_music_stinger(&self, bits: &mut LittleEndianReader<'_>) -> Option<MusicStinger> {
1079        let flag = bits.read_bit()?;
1080        let cue = bits.read_u32()?;
1081        let trigger = bits.read_u8()?;
1082        Some(MusicStinger { flag, cue, trigger })
1083    }
1084
1085    pub fn decode_music_stinger(
1086        &self,
1087        bits: &mut LittleEndianReader<'_>,
1088    ) -> Result<Attribute, AttributeError> {
1089        self._decode_music_stinger(bits)
1090            .map(Attribute::MusicStinger)
1091            .ok_or(AttributeError::NotEnoughDataFor("Music Stinger"))
1092    }
1093
1094    pub fn decode_pickup(
1095        &self,
1096        bits: &mut LittleEndianReader<'_>,
1097    ) -> Result<Attribute, AttributeError> {
1098        let instigator = bits
1099            .if_get(LittleEndianReader::read_i32)
1100            .map(|x| x.map(ActorId))
1101            .ok_or(AttributeError::NotEnoughDataFor("Pickup"))?;
1102        let picked_up = bits
1103            .read_bit()
1104            .ok_or(AttributeError::NotEnoughDataFor("Pickup"))?;
1105        Ok(Attribute::Pickup(Pickup {
1106            instigator,
1107            picked_up,
1108        }))
1109    }
1110
1111    pub fn decode_pickup_new(
1112        &self,
1113        bits: &mut LittleEndianReader<'_>,
1114    ) -> Result<Attribute, AttributeError> {
1115        let instigator = bits
1116            .if_get(LittleEndianReader::read_i32)
1117            .map(|x| x.map(ActorId))
1118            .ok_or(AttributeError::NotEnoughDataFor("PickupNew"))?;
1119        let picked_up = bits
1120            .read_u8()
1121            .ok_or(AttributeError::NotEnoughDataFor("PickupNew"))?;
1122        Ok(Attribute::PickupNew(PickupNew {
1123            instigator,
1124            picked_up,
1125        }))
1126    }
1127
1128    pub fn decode_qword(
1129        &self,
1130        bits: &mut LittleEndianReader<'_>,
1131    ) -> Result<Attribute, AttributeError> {
1132        bits.read_u64()
1133            .map(Attribute::QWord)
1134            .ok_or(AttributeError::NotEnoughDataFor("QWord"))
1135    }
1136
1137    pub fn decode_qword_string(
1138        &self,
1139        bits: &mut LittleEndianReader<'_>,
1140        buf: &mut [u8],
1141    ) -> Result<Attribute, AttributeError> {
1142        if self.is_rl_223 {
1143            self.decode_string(bits, buf)
1144        } else {
1145            self.decode_qword(bits)
1146        }
1147    }
1148
1149    fn _decode_welded(&self, bits: &mut LittleEndianReader<'_>) -> Option<Welded> {
1150        let active = bits.read_bit()?;
1151        let actor = bits.read_i32().map(ActorId)?;
1152        let offset = Vector3f::decode(bits, self.version.net_version())?;
1153        let mass = bits.read_f32()?;
1154        let rotation = Rotation::decode(bits)?;
1155        Some(Welded {
1156            active,
1157            actor,
1158            offset,
1159            mass,
1160            rotation,
1161        })
1162    }
1163
1164    pub fn decode_welded(
1165        &self,
1166        bits: &mut LittleEndianReader<'_>,
1167    ) -> Result<Attribute, AttributeError> {
1168        self._decode_welded(bits)
1169            .map(Attribute::Welded)
1170            .ok_or(AttributeError::NotEnoughDataFor("Welded"))
1171    }
1172
1173    pub fn decode_rotation(
1174        &self,
1175        bits: &mut LittleEndianReader<'_>,
1176    ) -> Result<Attribute, AttributeError> {
1177        let rot = Rotation::decode(bits).ok_or(AttributeError::NotEnoughDataFor("Rotation"))?;
1178        Ok(Attribute::Rotation(rot))
1179    }
1180
1181    fn _decode_title(&self, bits: &mut LittleEndianReader<'_>) -> Option<Attribute> {
1182        let unknown1 = bits.read_bit()?;
1183        let unknown2 = bits.read_bit()?;
1184        let unknown3 = bits.read_u32()?;
1185        let unknown4 = bits.read_u32()?;
1186        let unknown5 = bits.read_u32()?;
1187        let unknown6 = bits.read_u32()?;
1188        let unknown7 = bits.read_u32()?;
1189        let unknown8 = bits.read_bit()?;
1190        Some(Attribute::Title(
1191            unknown1, unknown2, unknown3, unknown4, unknown5, unknown6, unknown7, unknown8,
1192        ))
1193    }
1194    pub fn decode_title(
1195        &self,
1196        bits: &mut LittleEndianReader<'_>,
1197    ) -> Result<Attribute, AttributeError> {
1198        self._decode_title(bits)
1199            .ok_or(AttributeError::NotEnoughDataFor("Title"))
1200    }
1201
1202    fn _decode_team_paint(&self, bits: &mut LittleEndianReader<'_>) -> Option<TeamPaint> {
1203        let team = bits.read_u8()?;
1204        let primary_color = bits.read_u8()?;
1205        let accent_color = bits.read_u8()?;
1206        let primary_finish = bits.read_u32()?;
1207        let accent_finish = bits.read_u32()?;
1208
1209        Some(TeamPaint {
1210            team,
1211            primary_color,
1212            accent_color,
1213            primary_finish,
1214            accent_finish,
1215        })
1216    }
1217
1218    pub fn decode_team_paint(
1219        &self,
1220        bits: &mut LittleEndianReader<'_>,
1221    ) -> Result<Attribute, AttributeError> {
1222        self._decode_team_paint(bits)
1223            .map(Attribute::TeamPaint)
1224            .ok_or(AttributeError::NotEnoughDataFor("Team Paint"))
1225    }
1226
1227    fn _decode_rigid_body(&self, bits: &mut LittleEndianReader<'_>) -> Option<RigidBody> {
1228        let sleeping = bits.read_bit()?;
1229        let location = Vector3f::decode(bits, self.version.net_version())?;
1230
1231        let rotation = if self.version.net_version() >= 7 {
1232            Quaternion::decode(bits)?
1233        } else {
1234            Quaternion::decode_compressed(bits)?
1235        };
1236
1237        let mut linear_velocity = None;
1238        let mut angular_velocity = None;
1239
1240        if !sleeping {
1241            linear_velocity = Some(Vector3f::decode(bits, self.version.net_version()))?;
1242            angular_velocity = Some(Vector3f::decode(bits, self.version.net_version()))?;
1243        }
1244
1245        Some(RigidBody {
1246            sleeping,
1247            location,
1248            rotation,
1249            linear_velocity,
1250            angular_velocity,
1251        })
1252    }
1253
1254    pub fn decode_rigid_body(
1255        &self,
1256        bits: &mut LittleEndianReader<'_>,
1257    ) -> Result<Attribute, AttributeError> {
1258        self._decode_rigid_body(bits)
1259            .map(Attribute::RigidBody)
1260            .ok_or(AttributeError::NotEnoughDataFor("Rigid Body"))
1261    }
1262
1263    pub fn decode_not_implemented(
1264        &self,
1265        _bits: &mut LittleEndianReader<'_>,
1266    ) -> Result<Attribute, AttributeError> {
1267        Err(AttributeError::Unimplemented)
1268    }
1269
1270    pub fn decode_string(
1271        &self,
1272        bits: &mut LittleEndianReader<'_>,
1273        buf: &mut [u8],
1274    ) -> Result<Attribute, AttributeError> {
1275        Ok(Attribute::String(decode_text(bits, buf)?))
1276    }
1277
1278    pub fn decode_unique_id(
1279        &self,
1280        bits: &mut LittleEndianReader<'_>,
1281        buf: &mut [u8],
1282    ) -> Result<Attribute, AttributeError> {
1283        decode_unique_id(bits, self.version.net_version(), buf)
1284            .map(Box::new)
1285            .map(Attribute::UniqueId)
1286    }
1287
1288    pub fn decode_reservation(
1289        &self,
1290        bits: &mut LittleEndianReader<'_>,
1291        buf: &mut [u8],
1292    ) -> Result<Attribute, AttributeError> {
1293        let component = "Reservation";
1294        let number = get_or!(bits.read_bits(3).map(|x| x as u32), component)?;
1295        let unique = decode_unique_id(bits, self.version.net_version(), buf)?;
1296        let name = if unique.system_id != 0 {
1297            Some(decode_text(bits, buf)?)
1298        } else if unique.remote_id != RemoteId::SplitScreen(0) {
1299            // A split screen of zero seems to mean rocket host replays,
1300            // which use null terminated string up to an arbitrary length.
1301            // https://github.com/Bakkes/CPPRP/blob/9a4ea97299247f0284b238c84fbdbb8c31ed7f3c/CPPRP/CPPBitReader.h#L245
1302            let mut result = String::new();
1303            while result.len() < 255 {
1304                match get_or!(bits.read_u8(), component)? {
1305                    0 => break,
1306                    c => result.push(c as char),
1307                }
1308            }
1309            Some(result)
1310        } else {
1311            None
1312        };
1313
1314        let unknown1 = get_or!(bits.read_bit(), component)?;
1315        let unknown2 = get_or!(bits.read_bit(), component)?;
1316        let mut unknown3 = None;
1317        if self.version >= VersionTriplet(868, 12, 0) {
1318            unknown3 = get_or!(
1319                bits.read_bits(6).map(|x| x as u32).map(|x| Some(x as u8)),
1320                component
1321            )?;
1322        };
1323
1324        Ok(Attribute::Reservation(Box::new(Reservation {
1325            number,
1326            unique_id: unique,
1327            name,
1328            unknown1,
1329            unknown2,
1330            unknown3,
1331        })))
1332    }
1333
1334    pub fn decode_party_leader(
1335        &self,
1336        bits: &mut LittleEndianReader<'_>,
1337        buf: &mut [u8],
1338    ) -> Result<Attribute, AttributeError> {
1339        if let Some(system_id) = bits.read_u8() {
1340            if system_id != 0 {
1341                let id = decode_unique_id_with_system_id(
1342                    bits,
1343                    self.version.net_version(),
1344                    system_id,
1345                    buf,
1346                )?;
1347                Ok(Attribute::PartyLeader(Some(Box::new(id))))
1348            } else {
1349                Ok(Attribute::PartyLeader(None))
1350            }
1351        } else {
1352            Err(AttributeError::NotEnoughDataFor("Party Leader"))
1353        }
1354    }
1355
1356    pub fn decode_private_match_settings(
1357        &self,
1358        bits: &mut LittleEndianReader<'_>,
1359        buf: &mut [u8],
1360    ) -> Result<Attribute, AttributeError> {
1361        let component = "Private Match";
1362        let mutators = decode_text(bits, buf)?;
1363        let joinable_by = get_or!(bits.read_u32(), component)?;
1364        let max_players = get_or!(bits.read_u32(), component)?;
1365        let game_name = decode_text(bits, buf)?;
1366        let password = decode_text(bits, buf)?;
1367        let flag = get_or!(bits.read_bit(), component)?;
1368
1369        Ok(Attribute::PrivateMatch(Box::new(PrivateMatchSettings {
1370            mutators,
1371            joinable_by,
1372            max_players,
1373            game_name,
1374            password,
1375            flag,
1376        })))
1377    }
1378
1379    pub fn decode_loadout_online(
1380        &self,
1381        bits: &mut LittleEndianReader<'_>,
1382        buf: &mut [u8],
1383    ) -> Result<Attribute, AttributeError> {
1384        self.inner_decode_online_loadout(bits, buf)
1385            .map(Attribute::LoadoutOnline)
1386            .ok_or(AttributeError::NotEnoughDataFor("Loadout Online"))
1387    }
1388
1389    fn _decode_loadouts_online(
1390        &self,
1391        bits: &mut LittleEndianReader<'_>,
1392        buf: &mut [u8],
1393    ) -> Option<LoadoutsOnline> {
1394        let blue = self.inner_decode_online_loadout(bits, buf)?;
1395        let orange = self.inner_decode_online_loadout(bits, buf)?;
1396        let unknown1 = bits.read_bit()?;
1397        let unknown2 = bits.read_bit()?;
1398        Some(LoadoutsOnline {
1399            blue,
1400            orange,
1401            unknown1,
1402            unknown2,
1403        })
1404    }
1405
1406    pub fn decode_loadouts_online(
1407        &self,
1408        bits: &mut LittleEndianReader<'_>,
1409        buf: &mut [u8],
1410    ) -> Result<Attribute, AttributeError> {
1411        self._decode_loadouts_online(bits, buf)
1412            .map(Attribute::LoadoutsOnline)
1413            .ok_or(AttributeError::NotEnoughDataFor("Loadouts online"))
1414    }
1415
1416    fn decode_product(&self, bits: &mut LittleEndianReader<'_>, buf: &mut [u8]) -> Option<Product> {
1417        let unknown = bits.read_bit()?;
1418        let obj_ind = ObjectId(bits.read_i32()?);
1419        let val = self.product_decoder.decode(bits, obj_ind, buf)?;
1420
1421        Some(Product {
1422            unknown,
1423            object_ind: obj_ind,
1424            value: val,
1425        })
1426    }
1427
1428    fn inner_decode_online_loadout(
1429        &self,
1430        bits: &mut LittleEndianReader<'_>,
1431        buf: &mut [u8],
1432    ) -> Option<Vec<Vec<Product>>> {
1433        if let Some(size) = bits.read_u8() {
1434            let mut res = Vec::with_capacity(size as usize);
1435            for _ in 0..size {
1436                if let Some(attribute_size) = bits.read_u8() {
1437                    let mut products = Vec::with_capacity(attribute_size as usize);
1438                    for _ in 0..attribute_size {
1439                        if let Some(product) = self.decode_product(bits, buf) {
1440                            products.push(product);
1441                        } else {
1442                            return None;
1443                        }
1444                    }
1445                    res.push(products);
1446                } else {
1447                    return None;
1448                }
1449            }
1450            Some(res)
1451        } else {
1452            None
1453        }
1454    }
1455
1456    fn decode_logo_data(
1457        &self,
1458        bits: &mut LittleEndianReader<'_>,
1459    ) -> Result<Attribute, AttributeError> {
1460        let logo_id = bits
1461            .read_u32()
1462            .ok_or(AttributeError::NotEnoughDataFor("LogoData"))?;
1463        let swap_colors = bits
1464            .read_bit()
1465            .ok_or(AttributeError::NotEnoughDataFor("LogoData"))?;
1466        Ok(Attribute::LogoData(LogoData {
1467            logo_id,
1468            swap_colors,
1469        }))
1470    }
1471}
1472
1473fn decode_explosion(bits: &mut LittleEndianReader<'_>, net_version: i32) -> Option<Explosion> {
1474    let flag = bits.read_bit()?;
1475    let actor = bits.read_i32().map(ActorId)?;
1476    let location = Vector3f::decode(bits, net_version)?;
1477    Some(Explosion {
1478        flag,
1479        actor,
1480        location,
1481    })
1482}
1483
1484fn decode_text(
1485    bits: &mut LittleEndianReader<'_>,
1486    buf: &mut [u8],
1487) -> Result<String, AttributeError> {
1488    use std::cmp::Ordering;
1489
1490    let size = bits
1491        .read_i32()
1492        .ok_or(AttributeError::NotEnoughDataFor("text string"))?;
1493
1494    // A zero length string for attributes is fine (this differs from the replay header where we
1495    // never see zero length strings)
1496    match size.cmp(&0) {
1497        Ordering::Equal => Ok(String::from("")),
1498        Ordering::Less => {
1499            let bytes = size
1500                .checked_mul(-2)
1501                .ok_or(AttributeError::TooBigString(size))? as usize;
1502            if bytes > buf.len() || !bits.read_bytes(&mut buf[..bytes]) {
1503                Err(AttributeError::TooBigString(size))
1504            } else if let Ok(x) = decode_utf16(&buf[..bytes]) {
1505                Ok(x)
1506            } else {
1507                Err(AttributeError::TooBigString(size))
1508            }
1509        }
1510        Ordering::Greater => {
1511            let bytes = size as usize;
1512            if bytes > buf.len() || !bits.read_bytes(&mut buf[..bytes]) {
1513                Err(AttributeError::TooBigString(size))
1514            } else if let Ok(x) = decode_windows1252(&buf[..bytes]) {
1515                Ok(x)
1516            } else {
1517                Err(AttributeError::TooBigString(size))
1518            }
1519        }
1520    }
1521}
1522
1523fn decode_loadout_specials(
1524    bits: &mut LittleEndianReader<'_>,
1525) -> Option<(Option<u32>, Option<u32>, Option<u32>)> {
1526    let engine_audio = bits.read_u32()?;
1527    let trail = bits.read_u32()?;
1528    let goal_explosion = bits.read_u32()?;
1529    Some((Some(engine_audio), Some(trail), Some(goal_explosion)))
1530}
1531
1532fn decode_loadout(bits: &mut LittleEndianReader<'_>) -> Option<Loadout> {
1533    let version = bits.read_u8()?;
1534    let body = bits.read_u32()?;
1535    let decal = bits.read_u32()?;
1536    let wheels = bits.read_u32()?;
1537    let rocket_trail = bits.read_u32()?;
1538    let antenna = bits.read_u32()?;
1539    let topper = bits.read_u32()?;
1540    let unknown1 = bits.read_u32()?;
1541    let unknown2 = if version > 10 {
1542        Some(bits.read_u32()?)
1543    } else {
1544        None
1545    };
1546
1547    let (engine_audio, trail, goal_explosion) = if version >= 16 {
1548        decode_loadout_specials(bits)?
1549    } else {
1550        (None, None, None)
1551    };
1552
1553    let banner = if version >= 17 {
1554        Some(bits.read_u32()?)
1555    } else {
1556        None
1557    };
1558
1559    let product_id = if version >= 19 {
1560        Some(bits.read_u32()?)
1561    } else {
1562        None
1563    };
1564
1565    if version >= 22 {
1566        let _ = bits.read_u32()?;
1567        let _ = bits.read_u32()?;
1568        let _ = bits.read_u32()?;
1569    }
1570
1571    Some(Loadout {
1572        version,
1573        body,
1574        decal,
1575        wheels,
1576        rocket_trail,
1577        antenna,
1578        topper,
1579        unknown1,
1580        unknown2,
1581        engine_audio,
1582        trail,
1583        goal_explosion,
1584        banner,
1585        product_id,
1586    })
1587}
1588
1589fn decode_unique_id(
1590    bits: &mut LittleEndianReader<'_>,
1591    net_version: i32,
1592    buf: &mut [u8],
1593) -> Result<UniqueId, AttributeError> {
1594    let system_id = bits
1595        .read_u8()
1596        .ok_or(AttributeError::NotEnoughDataFor("System id"))?;
1597    decode_unique_id_with_system_id(bits, net_version, system_id, buf)
1598}
1599
1600fn decode_unique_id_with_system_id(
1601    bits: &mut LittleEndianReader<'_>,
1602    net_version: i32,
1603    system_id: u8,
1604    buf: &mut [u8],
1605) -> Result<UniqueId, AttributeError> {
1606    let remote_id = match system_id {
1607        0 => bits
1608            .read_bits(24)
1609            .map(|x| x as u32)
1610            .ok_or(AttributeError::NotEnoughDataFor("SplitScreen"))
1611            .map(RemoteId::SplitScreen),
1612        1 => bits
1613            .read_u64()
1614            .ok_or(AttributeError::NotEnoughDataFor("Steam"))
1615            .map(RemoteId::Steam),
1616        2 => {
1617            let name_bytes_buf = &mut buf[..16];
1618            if !bits.read_bytes(name_bytes_buf) {
1619                return Err(AttributeError::NotEnoughDataFor("PS4 Name"));
1620            }
1621
1622            let name_bytes = name_bytes_buf
1623                .iter()
1624                .take_while(|&&x| x != 0)
1625                .cloned()
1626                .collect::<Vec<u8>>();
1627
1628            let (name, _) = WINDOWS_1252.decode_without_bom_handling(&name_bytes[..]);
1629            let name = name.to_string();
1630            let to_read = if net_version >= 1 { 16 } else { 8 };
1631
1632            let unknown1_buf = &mut buf[..to_read];
1633            if !bits.read_bytes(unknown1_buf) {
1634                return Err(AttributeError::NotEnoughDataFor("PS4 Unknown"));
1635            }
1636
1637            let online_id = bits
1638                .read_u64()
1639                .ok_or(AttributeError::NotEnoughDataFor("PS4 ID"))?;
1640
1641            Ok(RemoteId::PlayStation(Ps4Id {
1642                name,
1643                unknown1: unknown1_buf.to_vec(),
1644                online_id,
1645            }))
1646        }
1647        4 => bits
1648            .read_u64()
1649            .ok_or(AttributeError::NotEnoughDataFor("Xbox"))
1650            .map(RemoteId::Xbox),
1651        5 => bits
1652            .read_u64()
1653            .ok_or(AttributeError::NotEnoughDataFor("QQ ID"))
1654            .map(RemoteId::QQ),
1655        6 => {
1656            let online_id = bits
1657                .read_u64()
1658                .ok_or(AttributeError::NotEnoughDataFor("Switch ID"))?;
1659
1660            let unknown1_buf = &mut buf[..24];
1661            if !bits.read_bytes(unknown1_buf) {
1662                return Err(AttributeError::NotEnoughDataFor("Switch ID Unknown"));
1663            }
1664
1665            Ok(RemoteId::Switch(SwitchId {
1666                online_id,
1667                unknown1: unknown1_buf.to_vec(),
1668            }))
1669        }
1670        7 => {
1671            let online_id = bits
1672                .read_u64()
1673                .ok_or(AttributeError::NotEnoughDataFor("PsyNet ID"))?;
1674
1675            if net_version < 10 {
1676                let unknown1_buf = &mut buf[..24];
1677                if !bits.read_bytes(unknown1_buf) {
1678                    return Err(AttributeError::NotEnoughDataFor("PsyNet ID Unknown"));
1679                }
1680
1681                Ok(RemoteId::PsyNet(PsyNetId {
1682                    online_id,
1683                    unknown1: unknown1_buf.to_vec(),
1684                }))
1685            } else {
1686                Ok(RemoteId::PsyNet(PsyNetId {
1687                    online_id,
1688                    ..Default::default()
1689                }))
1690            }
1691        }
1692        11 => Ok(RemoteId::Epic(decode_text(bits, buf)?)),
1693        x => Err(AttributeError::UnrecognizedRemoteId(x)),
1694    }?;
1695
1696    let local_id = bits
1697        .read_u8()
1698        .ok_or(AttributeError::NotEnoughDataFor("UniqueId local_id"))?;
1699
1700    Ok(UniqueId {
1701        system_id,
1702        remote_id,
1703        local_id,
1704    })
1705}
1706
1707#[cfg(test)]
1708mod tests {
1709    use super::*;
1710
1711    #[test]
1712    fn test_size_of_rigid_body() {
1713        assert_eq!(::std::mem::size_of::<RigidBody>(), 64);
1714    }
1715
1716    #[test]
1717    fn test_size_of_attribute() {
1718        assert!(
1719            ::std::mem::size_of::<Attribute>()
1720                <= ::std::mem::size_of::<RigidBody>() + ::std::mem::size_of::<usize>()
1721        );
1722    }
1723}