ddcore_rs/models/
replay.rs

1//
2// replay file models and utils
3//
4
5use std::{io::{Read, Seek, SeekFrom}, time::{SystemTime, Duration, UNIX_EPOCH}};
6use anyhow::{Result, bail};
7use num_derive::FromPrimitive;
8use num_traits::FromPrimitive;
9
10use super::spawnset::V3Enemies;
11
12type EntityId = i32;
13type PositionInt = [i16; 3];
14type PositionFloat = [f32; 3];
15type LeviathanData = i32;
16
17#[derive(Debug, Clone)]
18pub struct DfRpl2 {
19    pub header: DfRpl2Header,
20    pub data: ReplayData
21}
22
23#[derive(Debug, Clone)]
24pub struct ReplayData {
25    pub frames: Vec<ReplayFrame>,
26    pub entities: Vec<Entity>,
27}
28
29#[derive(Debug, Clone)]
30pub struct DfRpl2Header {
31    pub player_name: String,
32    pub funny_bytes: Vec<u8>
33}
34
35#[derive(Debug, Clone)]
36pub struct DdRpl {
37    pub header: DdRplHeader,
38    pub compressed_data: Option<Vec<u8>>,
39    pub data: Option<ReplayData>,
40    pub extra: Option<ExtraData>,
41}
42
43#[derive(Debug, Clone)]
44pub struct ExtraData {
45    pub homing: Vec<i32>,
46    pub homing_used: Vec<i32>,
47    pub starting_gems: i32,
48    pub starting_hand: u8,
49    pub look_speed: f32,
50    pub lvl2_time: f32,
51    pub lvl3_time: f32,
52    pub lvl4_time: f32,
53}
54
55#[derive(Debug, Clone)]
56pub struct DdRplHeader {
57    pub file_version: u32,
58    pub recorded_at: SystemTime,
59    pub time: f32,
60    pub starting_time: f32,
61    pub daggers_fired: u32,
62    pub death_type: i32,
63    pub gems: u32,
64    pub kills: u32,
65    pub daggers_hit: u32,
66    pub player_name: String,
67    pub player_id: i32,
68    pub spawnset_bin: Vec<u8>,
69    pub spawnset_hash: String,
70    pub compressed_data_len: u32,
71    pub spawnset: Option<crate::models::spawnset::Spawnset<V3Enemies>>
72}
73
74#[derive(Debug, Clone)]
75pub struct Entity {
76    pub id: EntityId,
77    pub entity_type: EntityType,
78}
79
80#[derive(Debug, Clone)]
81pub struct ReplayFrame {
82    pub events: Vec<ReplayEvent>
83}
84
85#[derive(Debug, Clone)]
86pub enum ReplayEvent {
87    Spawn(EntityData),
88    UpdateEntityPosition(EntityId, PositionInt),
89    UpdateEntityOrientation(EntityId, UpdateOrientationData),
90    UpdateEntityTarget(EntityId, [i16; 3]),
91    DaggerDewspawn(DaggerDespawnData),
92    EnemyHitWeakSpot(EnemyHitData),
93    EnemyHitArmor(EnemyHitData),
94    PlayerDeath(PlayerDeathData),
95    GemPickup,
96    Transmute(EntityId, TransmuteData),
97    EndFrame(ButtonData, MouseData),
98    EndReplay,
99}
100
101#[derive(Debug, Clone, FromPrimitive)]
102pub enum EntityType {
103    Dagger = 0x1,
104    Squid1 = 0x3,
105    Squid2 = 0x4,
106    Squid3 = 0x5,
107    Boid = 0x6, // Skulls and Spiderlings
108    Centipede = 0x7,
109    Spider1 = 0x8,
110    Spider2 = 0x9,
111    Egg = 0xA,
112    Leviathan = 0xB,
113    Gigapede = 0xC,
114    Thorn = 0xD,
115    Ghostpede = 0xF,
116}
117
118#[derive(Debug, Clone)]
119pub enum EntityData {
120    Dagger(DaggerData),
121    Squid1(SquidData),
122    Squid2(SquidData),
123    Squid3(SquidData),
124    Boid(BoidData), // Skulls and Spiderlings
125    Centipede(PedeData),
126    Spider1(SpiderData),
127    Spider2(SpiderData),
128    Egg(EggData),
129    Leviathan(LeviathanData),
130    Gigapede(PedeData),
131    Thorn(ThornData),
132    Ghostpede(PedeData),
133}
134
135#[derive(Debug, Clone)]
136pub struct ThornData {
137    pub a: i32,
138    pub position: PositionFloat,
139    pub rotation: f32,
140}
141
142#[derive(Debug, Clone)]
143pub struct EggData {
144    pub spider_spawner: EntityId,
145    pub funny1: [f32; 3],
146    pub funny2: [f32; 3],
147}
148
149#[derive(Debug, Clone)]
150pub struct SpiderData {
151    pub a: i32,
152    pub position: PositionFloat,
153}
154
155#[derive(Debug, Clone)]
156pub struct PedeData {
157    pub a: i32,
158    pub position: PositionFloat,
159    pub b: [f32; 3],
160    pub funny1: [f32; 3],
161    pub funny2: [f32; 3],
162    pub funny3: [f32; 3],
163}
164
165#[derive(Debug, Clone)]
166pub struct BoidData {
167    pub boid_type: BoidType,
168    pub spanwer: EntityId,
169    pub position: PositionInt,
170    pub funny1: [i16; 3],
171    pub funny2: [i16; 3],
172    pub funny3: [i16; 3],
173    pub funny4: [f32; 3],
174    pub speed: f32,
175}
176
177#[derive(Debug, Clone, FromPrimitive)]
178pub enum BoidType {
179    Skull1 = 1,
180    Skull2 = 2,
181    Skull3 = 3,
182    Skull4 = 5,
183    Spiderling = 4,
184}
185
186#[derive(Debug, Clone)]
187pub struct DaggerData {
188    pub a: i32,
189    pub position: PositionInt,
190    pub orientationa: [i16; 3],
191    pub orientationb: [i16; 3],
192    pub orientationc: [i16; 3],
193    pub b: u8,
194    pub dagger_level: DaggerLevel,
195}
196
197#[derive(Debug, Clone)]
198pub struct SquidData {
199    pub a: i32,
200    pub position: PositionFloat,
201    pub b: [f32; 3],
202    pub rotation: f32, // Radians
203}
204
205#[derive(Debug, Clone, FromPrimitive, PartialEq)]
206pub enum DaggerLevel {
207    Level0 = 0,
208    Level1,
209    Level2,
210    Level3,
211    Level4,
212    Level5,
213    Level6,
214    Level7,
215}
216
217#[derive(Debug, Clone)]
218pub struct UpdateOrientationData {
219    pub a: [i16; 3],
220    pub b: [i16; 3],
221    pub c: [i16; 3],
222}
223
224#[derive(Debug, Clone)]
225pub struct DaggerDespawnData {
226    pub dagger_id: i32,
227}
228
229#[derive(Debug, Clone)]
230pub struct PlayerDeathData {
231    pub death_type: i32,
232}
233
234#[derive(Debug, Clone)]
235pub struct EnemyHitData {
236    pub enemy_id: i32,
237    pub dagger_id: i32,
238    pub segment: i32,
239}
240
241#[derive(Debug, Clone)]
242pub struct TransmuteData {
243    pub a: [i16; 3],
244    pub b: [i16; 3],
245    pub c: [i16; 3],
246    pub d: [i16; 3],
247}
248
249#[derive(Debug, Clone)]
250pub struct ButtonData {
251    pub left: bool,
252    pub right: bool,
253    pub forward: bool,
254    pub backwards: bool,
255    pub jump: JumpButtonState,
256    pub shoot: MouseButtonState,
257    pub homing: MouseButtonState,
258}
259
260#[derive(Debug, Clone)]
261pub enum JumpButtonState {
262    NotPressed = 0,
263    Held,
264    JustPressed,
265}
266
267#[derive(Debug, Clone)]
268pub enum MouseButtonState {
269    NotPressed = 0,
270    Held,
271    Released,
272}
273
274#[derive(Debug, Clone)]
275pub struct MouseData {
276    pub x: i16,
277    pub y: i16,
278    pub look_speed: Option<f32>,
279}
280
281impl ReplayData {
282
283    #[allow(clippy::wildcard_in_or_patterns)]
284    pub fn from_reader<R: Read>(source: &mut R) -> Result<Self> {
285        use bytestream::*;
286        let mut decompressed = vec![];
287        libflate::zlib::Decoder::new(source)?.read_to_end(&mut decompressed)?;
288        let mut event_reader = &decompressed[..];
289
290
291        let mut next_entity_id: EntityId = 1;
292        let mut entities: Vec<Entity> = vec![];
293        let mut frames: Vec<ReplayFrame> = vec![];
294        let mut first = true;
295        let mut current_frame: Vec<ReplayEvent> = vec![];
296
297        loop {
298            let event_type = u8::read_from(&mut event_reader, ByteOrder::LittleEndian)?;
299
300            let event: ReplayEvent = match event_type {
301                0x0 => {
302                    let entity_type = u8::read_from(&mut event_reader, ByteOrder::LittleEndian)?;
303                    let entity_type: EntityType = FromPrimitive::from_u8(entity_type).unwrap();
304                    
305                    entities.push(Entity {
306                        id: next_entity_id,
307                        entity_type: entity_type.clone()
308                    });
309
310                    next_entity_id += 1;
311
312                    ReplayEvent::Spawn(match entity_type {
313                        EntityType::Dagger => EntityData::Dagger(DaggerData {
314                            a: read_entity_id(&mut event_reader)?,
315                            position: read3_i16(&mut event_reader)?,
316                            orientationa: read3_i16(&mut event_reader)?,
317                            orientationb: read3_i16(&mut event_reader)?,
318                            orientationc: read3_i16(&mut event_reader)?,
319                            b: u8::read_from(&mut event_reader, ByteOrder::LittleEndian)?,
320                            dagger_level: FromPrimitive::from_u8(u8::read_from(&mut event_reader, ByteOrder::LittleEndian)?).unwrap(),
321                        }),
322                        EntityType::Squid1 => EntityData::Squid1(SquidData {
323                            a: read_entity_id(&mut event_reader)?,
324                            position: read3_f32(&mut event_reader)?,
325                            b: read3_f32(&mut event_reader)?,
326                            rotation: read_f32(&mut event_reader)?
327                        }),
328                        EntityType::Squid2 => EntityData::Squid2(SquidData {
329                            a: read_entity_id(&mut event_reader)?,
330                            position: read3_f32(&mut event_reader)?,
331                            b: read3_f32(&mut event_reader)?,
332                            rotation: read_f32(&mut event_reader)?
333                        }),
334                        EntityType::Squid3 => EntityData::Squid3(SquidData {
335                            a: read_entity_id(&mut event_reader)?,
336                            position: read3_f32(&mut event_reader)?,
337                            b: read3_f32(&mut event_reader)?,
338                            rotation: read_f32(&mut event_reader)?
339                        }),
340                        EntityType::Boid => EntityData::Boid(BoidData {
341                            spanwer: read_entity_id(&mut event_reader)?,
342                            boid_type: FromPrimitive::from_u8(u8::read_from(&mut event_reader, ByteOrder::LittleEndian)?).unwrap(),
343                            position: read3_i16(&mut event_reader)?,
344                            funny1: read3_i16(&mut event_reader)?,
345                            funny2: read3_i16(&mut event_reader)?,
346                            funny3: read3_i16(&mut event_reader)?,
347                            funny4: read3_f32(&mut event_reader)?,
348                            speed: read_f32(&mut event_reader)?,
349                        }),
350                        EntityType::Centipede => EntityData::Centipede(PedeData {
351                            a: read_entity_id(&mut event_reader)?,
352                            position: read3_f32(&mut event_reader)?,
353                            b: read3_f32(&mut event_reader)?,
354                            funny1: read3_f32(&mut event_reader)?,
355                            funny2: read3_f32(&mut event_reader)?,
356                            funny3: read3_f32(&mut event_reader)?,
357                        }),
358                        EntityType::Gigapede => EntityData::Gigapede(PedeData {
359                            a: read_entity_id(&mut event_reader)?,
360                            position: read3_f32(&mut event_reader)?,
361                            b: read3_f32(&mut event_reader)?,
362                            funny1: read3_f32(&mut event_reader)?,
363                            funny2: read3_f32(&mut event_reader)?,
364                            funny3: read3_f32(&mut event_reader)?,
365                        }),
366                        EntityType::Ghostpede => EntityData::Ghostpede(PedeData {
367                            a: read_entity_id(&mut event_reader)?,
368                            position: read3_f32(&mut event_reader)?,
369                            b: read3_f32(&mut event_reader)?,
370                            funny1: read3_f32(&mut event_reader)?,
371                            funny2: read3_f32(&mut event_reader)?,
372                            funny3: read3_f32(&mut event_reader)?,
373                        }),
374                        EntityType::Spider1 => EntityData::Spider1(SpiderData {
375                            a: read_entity_id(&mut event_reader)?,
376                            position: read3_f32(&mut event_reader)?,
377                        }),
378                        EntityType::Spider2 => EntityData::Spider2(SpiderData {
379                            a: read_entity_id(&mut event_reader)?,
380                            position: read3_f32(&mut event_reader)?,
381                        }),
382                        EntityType::Egg => EntityData::Egg(EggData {
383                            spider_spawner: read_entity_id(&mut event_reader)?,
384                            funny1: read3_f32(&mut event_reader)?,
385                            funny2: read3_f32(&mut event_reader)?,
386                        }),
387                        EntityType::Thorn => EntityData::Thorn(ThornData {
388                            a: read_entity_id(&mut event_reader)?,
389                            position: read3_f32(&mut event_reader)?,
390                            rotation: read_f32(&mut event_reader)?,
391                        }),
392                        EntityType::Leviathan => EntityData::Leviathan(read_entity_id(&mut event_reader)?),
393                    })
394                },
395                0x1 => ReplayEvent::UpdateEntityPosition(
396                    read_entity_id(&mut event_reader)?,
397                    read3_i16(&mut event_reader)?
398                ),
399                0x2 => ReplayEvent::UpdateEntityOrientation(
400                    read_entity_id(&mut event_reader)?,
401                    UpdateOrientationData {
402                        a: read3_i16(&mut event_reader)?,
403                        b: read3_i16(&mut event_reader)?,
404                        c: read3_i16(&mut event_reader)?,
405                    }
406                ),
407                0x4 => ReplayEvent::UpdateEntityTarget(
408                    read_entity_id(&mut event_reader)?,
409                    read3_i16(&mut event_reader)?
410                ),
411                0x5 => {
412                    let a = i32::read_from(&mut event_reader, ByteOrder::LittleEndian)?;
413                    let b = i32::read_from(&mut event_reader, ByteOrder::LittleEndian)?;
414                    let c = i32::read_from(&mut event_reader, ByteOrder::LittleEndian)?;
415
416                    if a == 0 {
417                        ReplayEvent::PlayerDeath(PlayerDeathData {
418                            death_type: b
419                        })
420                    } else if b == 0 && c == 0 {
421                        ReplayEvent::DaggerDewspawn(DaggerDespawnData {
422                            dagger_id: a
423                        })
424                    } else if a < 0 {
425                        ReplayEvent::EnemyHitArmor(EnemyHitData {
426                            enemy_id: -a,
427                            dagger_id: b,
428                            segment: c
429                        })
430                    } else {
431                        ReplayEvent::EnemyHitWeakSpot(EnemyHitData {
432                            enemy_id: a,
433                            dagger_id: b,
434                            segment: c
435                        })
436                    }
437                },
438                0x6 => ReplayEvent::GemPickup,
439                0x7 => ReplayEvent::Transmute(
440                    read_entity_id(&mut event_reader)?,
441                    TransmuteData {
442                        a: read3_i16(&mut event_reader)?,
443                        b: read3_i16(&mut event_reader)?,
444                        c: read3_i16(&mut event_reader)?,
445                        d: read3_i16(&mut event_reader)?,
446                    }
447                ),
448                0x9 => {
449                    let buttons = ButtonData {
450                        left: bool::read_from(&mut event_reader, ByteOrder::LittleEndian)?,
451                        right: bool::read_from(&mut event_reader, ByteOrder::LittleEndian)?,
452                        forward: bool::read_from(&mut event_reader, ByteOrder::LittleEndian)?,
453                        backwards: bool::read_from(&mut event_reader, ByteOrder::LittleEndian)?,
454                        jump: read_jump(&mut event_reader)?,
455                        shoot: read_mouse_btn(&mut event_reader)?,
456                        homing: read_mouse_btn(&mut event_reader)?,
457                    };
458
459                    let mut mouse_data = MouseData {
460                        x: i16::read_from(&mut event_reader, ByteOrder::LittleEndian)?,
461                        y: i16::read_from(&mut event_reader, ByteOrder::LittleEndian)?,
462                        look_speed: None,
463                    };
464
465                    if first {
466                        mouse_data.look_speed = Some((500. / 3.) * read_f32(&mut event_reader)?);
467                        first = false;
468                    }
469
470                    if let Ok(funny) = u8::read_from(&mut event_reader, ByteOrder::LittleEndian) {
471                        if funny != 0xA {
472                            bail!("FUNNY BYTE!");
473                        }
474                    }
475
476                    ReplayEvent::EndFrame(
477                        buttons,
478                        mouse_data
479                    )
480                },
481                0xB | _ => ReplayEvent::EndReplay
482            };
483
484            current_frame.push(event.clone());
485            match event {
486                ReplayEvent::EndReplay => {
487                    frames.push(ReplayFrame {
488                        events: current_frame,
489                    });
490                    break;
491                },
492                ReplayEvent::EndFrame(_cool, _cool2) => {
493                    frames.push(ReplayFrame {
494                        events: current_frame,
495                    });
496                    current_frame = vec![];
497                }
498                _ => {},
499            }
500        }
501
502        Ok(Self {
503            frames,
504            entities
505        })
506    }
507}
508
509impl DdRplHeader {
510    pub fn create_spawnset(&mut self) -> Result<()> {
511        let bin_reader = self.spawnset_bin.clone();
512        let spawnset = crate::models::spawnset::Spawnset::<V3Enemies>::deserialize(&mut &bin_reader[..])?;
513        self.spawnset = Some(spawnset);
514        Ok(())
515    }
516}
517
518impl DdRpl {
519    pub fn calc_data(&mut self) -> Result<()> {
520        if let Some(compressed_data) = &self.compressed_data {
521            self.data = Some(ReplayData::from_reader(&mut &compressed_data[..])?);
522            self.compressed_data = None;
523            Ok(())
524        } else {
525            bail!("No compressed data");
526        }
527    }
528
529    pub fn create_extra(&mut self) -> Result<()> {
530        if self.data.is_none() {
531            self.calc_data()?;
532        }
533
534        if self.header.spawnset.is_none() {
535            self.header.create_spawnset()?;
536        }
537
538        let spawnset = self.header.spawnset.as_ref().unwrap().clone();
539        let mut old_extra = self.extra.as_ref().unwrap().clone();
540        if self.extra.is_none() {
541            old_extra = ExtraData {
542                homing: vec![],
543                homing_used: vec![],
544                starting_hand: 0,
545                starting_gems: 0,
546                lvl2_time: 0.,
547                lvl3_time: 0.,
548                lvl4_time: 0.,
549                look_speed: 0.,
550            };
551        }
552        let initial_hand = if spawnset.settings.is_some() { spawnset.settings.as_ref().unwrap().initial_hand } else { 0 };
553        let additional_gems = if spawnset.settings.is_some() { spawnset.settings.as_ref().unwrap().additional_gems } else { 0 };
554
555        old_extra.starting_hand = initial_hand;
556        old_extra.starting_gems = additional_gems;
557
558        let mut _gem_counter = old_extra.starting_gems;
559        let mut hand_level = old_extra.starting_hand;
560        #[allow(unused_assignments)]
561        let mut level_gems = 0;
562        let mut homing = 0;
563        let mut homing_used = 0;
564
565        match &old_extra.starting_hand {
566            3 => { level_gems = 70; homing = old_extra.starting_gems },
567            4 => { level_gems = 71; homing = old_extra.starting_gems },
568            _ => { level_gems = old_extra.starting_gems },
569        };
570
571        let mut homing_history = vec![];
572        let mut homing_used_history = vec![];
573
574        let data = self.data.as_ref().unwrap();
575
576        for (frame_count, frame) in data.frames.iter().enumerate() {
577            for event in &frame.events {
578                match event {
579                    ReplayEvent::EndFrame(_button_data, mouse_data) => {
580                        if mouse_data.look_speed.is_some() {
581                            old_extra.look_speed = mouse_data.look_speed.unwrap();
582                        }
583                    },
584                    ReplayEvent::GemPickup => {
585                        _gem_counter += 1;
586                        if hand_level <= 1 {
587                            level_gems += 1;
588                            if level_gems >= 10 {
589                                old_extra.lvl2_time = (frame_count + 1) as f32 / 60.;
590                                hand_level = 2;
591                                level_gems = 10;
592                            }
593                        } else if hand_level == 2 {
594                            level_gems += 1;
595                            if level_gems >= 70 {
596                                old_extra.lvl3_time = (frame_count + 1) as f32 / 60.;
597                                hand_level = 3;
598                                level_gems = 70;
599                            }
600                        } else if hand_level == 3 {
601                            homing += 1;
602                            if homing >= 150 {
603                                old_extra.lvl4_time = (frame_count + 1) as f32 / 60.;
604                                hand_level = 4;
605                                level_gems = 71;
606                                homing = 0;
607                            }
608                        } else {
609                            homing += 1;
610                        }
611                    },
612                    ReplayEvent::Spawn(EntityData::Dagger(dagger)) => {
613                        if dagger.dagger_level.eq(&DaggerLevel::Level6) {
614                            homing -= 1;
615                            homing_used += 1;
616                        }
617                    },
618                    _ => {},
619                }
620            }
621            
622            homing_history.push(homing);
623            homing_used_history.push(homing_used);
624        }
625
626        old_extra.homing = homing_history;
627        old_extra.homing_used = homing_used_history;
628
629        self.extra = Some(old_extra);
630
631        Ok(())
632    }
633
634    pub fn validate_reader<R: Read + Seek>(source: &mut R) -> Result<()> {
635        use bytestream::*;
636
637        source.read_exact(&mut [0u8; 0x6])?; // ddrpl.
638        let _file_version = u32::read_from(source, ByteOrder::LittleEndian)?;
639        let timestamp = u64::read_from(source, ByteOrder::LittleEndian)?;
640        let _timestamp = UNIX_EPOCH + Duration::from_secs(1455753600 + timestamp);
641        let _time = read_f32(source)?;
642        let _starting_time = read_f32(source)?;
643        let _daggers_fired = u32::read_from(source, ByteOrder::LittleEndian)?;
644        let _death_type = i32::read_from(source, ByteOrder::LittleEndian)?;
645        let _gems_collected = u32::read_from(source, ByteOrder::LittleEndian)?;
646        let _daggers_hit = u32::read_from(source, ByteOrder::LittleEndian)?;
647        let _kills = u32::read_from(source, ByteOrder::LittleEndian)?;
648        let _player_id = i32::read_from(source, ByteOrder::LittleEndian)?;
649        let username_len = u32::read_from(source, ByteOrder::LittleEndian)?;
650        let mut username = vec![0u8; username_len as usize];
651        source.read_exact(&mut username)?;
652        let _username = String::from_utf8(username)?;
653        source.read_exact(&mut [0u8; 10])?; // skip unknown
654        let mut spawnset_hash = [0u8; 16];
655        source.read_exact(&mut spawnset_hash)?;
656        let _spawnset_hash = crate::utils::md5_to_string_lower(&spawnset_hash);
657        let spawnset_len = u32::read_from(source, ByteOrder::LittleEndian)?;
658        let mut spawnset_bin = vec![0u8; spawnset_len as usize];
659        source.read_exact(&mut spawnset_bin)?;
660        let compressed_data_len = u32::read_from(source, ByteOrder::LittleEndian)?;
661        if compressed_data_len > 40000000 {
662            bail!("Replay data is too big");
663        }
664        source.seek(SeekFrom::Current(compressed_data_len as i64))?;
665        if source.read(&mut [0u8; 1])? != 0 {
666            bail!("Invalid Replay File");
667        }
668        Ok(())
669    }
670
671    pub fn validate_reader_output_bin<R: Read + Seek>(source: &mut R) -> Result<Vec<u8>> {
672        use bytestream::*;
673
674        source.read_exact(&mut [0u8; 0x6])?; // ddrpl.
675        let _file_version = u32::read_from(source, ByteOrder::LittleEndian)?;
676        let timestamp = u64::read_from(source, ByteOrder::LittleEndian)?;
677        let _timestamp = UNIX_EPOCH + Duration::from_secs(1455753600 + timestamp);
678        let _time = read_f32(source)?;
679        let _starting_time = read_f32(source)?;
680        let _daggers_fired = u32::read_from(source, ByteOrder::LittleEndian)?;
681        let _death_type = i32::read_from(source, ByteOrder::LittleEndian)?;
682        let _gems_collected = u32::read_from(source, ByteOrder::LittleEndian)?;
683        let _daggers_hit = u32::read_from(source, ByteOrder::LittleEndian)?;
684        let _kills = u32::read_from(source, ByteOrder::LittleEndian)?;
685        let _player_id = i32::read_from(source, ByteOrder::LittleEndian)?;
686        let username_len = u32::read_from(source, ByteOrder::LittleEndian)?;
687        let mut username = vec![0u8; username_len as usize];
688        source.read_exact(&mut username)?;
689        let _username = String::from_utf8(username)?;
690        source.read_exact(&mut [0u8; 10])?; // skip unknown
691        let mut spawnset_hash = [0u8; 16];
692        source.read_exact(&mut spawnset_hash)?;
693        let _spawnset_hash = crate::utils::md5_to_string_lower(&spawnset_hash);
694        let spawnset_len = u32::read_from(source, ByteOrder::LittleEndian)?;
695        let mut spawnset_bin = vec![0u8; spawnset_len as usize];
696        source.read_exact(&mut spawnset_bin)?;
697        let compressed_data_len = u32::read_from(source, ByteOrder::LittleEndian)?;
698        if compressed_data_len > 40000000 {
699            bail!("Replay data is too big");
700        }
701        source.seek(SeekFrom::Current(compressed_data_len as i64))?;
702        if source.read(&mut [0u8; 1])? != 0 {
703            bail!("Invalid Replay File");
704        }
705        source.seek(SeekFrom::Start(0))?;
706
707        let mut out = vec![];
708        source.read_to_end(&mut out)?;
709        Ok(out)
710    }
711
712    pub fn from_reader<R: Read>(source: &mut R) -> Result<Self> {
713        use bytestream::*;
714
715        source.read_exact(&mut [0u8; 0x6])?; // ddrpl.
716        let file_version = u32::read_from(source, ByteOrder::LittleEndian)?;
717        let timestamp = u64::read_from(source, ByteOrder::LittleEndian)?;
718        let timestamp = UNIX_EPOCH + Duration::from_secs(1455753600 + timestamp);
719        let time = read_f32(source)?;
720        let starting_time = read_f32(source)?;
721        let daggers_fired = u32::read_from(source, ByteOrder::LittleEndian)?;
722        let death_type = i32::read_from(source, ByteOrder::LittleEndian)?;
723        let gems_collected = u32::read_from(source, ByteOrder::LittleEndian)?;
724        let daggers_hit = u32::read_from(source, ByteOrder::LittleEndian)?;
725        let kills = u32::read_from(source, ByteOrder::LittleEndian)?;
726        let player_id = i32::read_from(source, ByteOrder::LittleEndian)?;
727        let username_len = u32::read_from(source, ByteOrder::LittleEndian)?;
728        let mut username = vec![0u8; username_len as usize];
729        source.read_exact(&mut username)?;
730        let username = String::from_utf8(username)?;
731        source.read_exact(&mut [0u8; 10])?; // skip unknown
732        let mut spawnset_hash = [0u8; 16];
733        source.read_exact(&mut spawnset_hash)?;
734        let spawnset_hash = crate::utils::md5_to_string_lower(&spawnset_hash);
735        let spawnset_len = u32::read_from(source, ByteOrder::LittleEndian)?;
736        let mut spawnset_bin = vec![0u8; spawnset_len as usize];
737        source.read_exact(&mut spawnset_bin)?;
738        let compressed_data_len = u32::read_from(source, ByteOrder::LittleEndian)?;
739
740        if compressed_data_len > 40000000 {
741            bail!("Replay data is too big");
742        }
743
744        let header = DdRplHeader {
745            player_name: username,
746            player_id,
747            spawnset_hash,
748            kills,
749            death_type,
750            daggers_hit,
751            daggers_fired,
752            file_version,
753            time,
754            starting_time,
755            gems: gems_collected,
756            recorded_at: timestamp,
757            spawnset_bin,
758            compressed_data_len,
759            spawnset: None,
760        };
761    
762        let mut compressed_data = vec![0u8; compressed_data_len as usize];
763        source.read_exact(&mut compressed_data[..])?;
764        let compressed_data = Some(compressed_data);
765        
766        if source.read(&mut [0u8; 1])? != 0 {
767            bail!("Invalid Replay File");
768        }
769
770        Ok(DdRpl {
771            header,
772            data: None,
773            compressed_data,
774            extra: None,
775        })
776    }
777}
778
779impl DfRpl2 {
780    pub fn from_reader<R: Read>(source: &mut R) -> Result<Self> {
781        use bytestream::*;
782
783        // Skip DF_RPL2
784        source.read_exact(&mut [0u8; 7])?;
785        let username_len = u16::read_from(source, ByteOrder::LittleEndian)?;
786        let mut username = vec![0u8; username_len as usize];
787        source.read_exact(&mut username)?;
788        let username = String::from_utf8(username)?;
789        let funny_bytes_len = u16::read_from(source, ByteOrder::LittleEndian)?;
790        let mut funny_bytes = vec![0u8; funny_bytes_len as usize];
791        source.read_exact(&mut funny_bytes)?;
792
793        let header = DfRpl2Header {
794            player_name: username,
795            funny_bytes
796        };
797
798        let data = ReplayData::from_reader(source)?;
799
800        Ok(DfRpl2 {
801            header,
802            data
803        })
804    }
805}
806
807fn read_jump<R: Read>(source: &mut R) -> Result<JumpButtonState> {
808    use bytestream::*;
809    let v = u8::read_from(source, ByteOrder::LittleEndian)?;
810    Ok(match v {
811        0 => JumpButtonState::NotPressed,
812        1 => JumpButtonState::Held,
813        2 => JumpButtonState::JustPressed,
814        _ => JumpButtonState::Held,
815    })
816}
817
818fn read_mouse_btn<R: Read>(source: &mut R) -> Result<MouseButtonState> {
819    use bytestream::*;
820    let v = u8::read_from(source, ByteOrder::LittleEndian)?;
821    Ok(match v {
822        0 => MouseButtonState::NotPressed,
823        1 => MouseButtonState::Held,
824        2 => MouseButtonState::Released,
825        _ => MouseButtonState::Held,
826    })
827}
828
829fn read_entity_id<R: Read>(source: &mut R) -> Result<i32> {
830    use bytestream::*;
831    Ok(i32::read_from(source, ByteOrder::LittleEndian)?)
832}
833
834fn read3_i16<R: Read>(source: &mut R) -> Result<[i16; 3]> {
835    use bytestream::*;
836    Ok([
837        i16::read_from(source, ByteOrder::LittleEndian)?,
838        i16::read_from(source, ByteOrder::LittleEndian)?,
839        i16::read_from(source, ByteOrder::LittleEndian)?,
840    ])
841}
842
843fn read_f32<R: Read>(source: &mut R) -> Result<f32> {
844    let mut buf = [0u8; 4];
845    source.read_exact(&mut buf)?;
846    Ok(f32::from_le_bytes(buf))
847}
848
849fn read3_f32<R: Read>(source: &mut R) -> Result<[f32; 3]> {
850    let mut b1 = [0u8; 4];
851    let mut b2 = [0u8; 4];
852    let mut b3 = [0u8; 4];
853    source.read_exact(&mut b1)?;
854    source.read_exact(&mut b2)?;
855    source.read_exact(&mut b3)?;
856    Ok([
857        f32::from_le_bytes(b1),
858        f32::from_le_bytes(b2),
859        f32::from_le_bytes(b3),
860    ])
861}