1use 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, 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), 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, }
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])?; 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])?; 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])?; 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])?; 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])?; 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])?; 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 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}