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#[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 pub tile_state: u8,
153
154 pub damaged: bool,
156
157 pub offender: ActorId,
159
160 pub ball_position: Vector3f,
162
163 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, 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 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 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}