1use std::path::Path;
2
3use memmap2::Mmap;
4use prost::Message;
5
6use crate::entity::{
7 ClassInfo, EntityContainer, FieldDecodeContext, SerializerContainer, StringTableContainer,
8};
9use crate::error::{Error, Result};
10use crate::io::{BitReader, ByteReader};
11
12use std::collections::HashMap;
13
14use super::command::{self, CmdHeader, dem, ge, svc};
15
16use boon_proto::proto::{
17 CDemoClassInfo, CDemoFileHeader, CDemoFileInfo, CDemoFullPacket, CDemoPacket, CDemoSendTables,
18 CMsgSource1LegacyGameEvent, CMsgSource1LegacyGameEventList, CitadelUserMessageIds,
19 CsvcMsgCreateStringTable, CsvcMsgPacketEntities, CsvcMsgServerInfo, CsvcMsgUpdateStringTable,
20 CsvcMsgUserMessage, EBaseUserMessages, ECitadelGameEvents,
21};
22
23const MAGIC: &[u8; 8] = b"PBDEMS2\0";
25const HEADER_SIZE: usize = 16;
27
28const DEFAULT_TICK_INTERVAL: f32 = 1.0 / 30.0;
31const DEFAULT_FULL_PACKET_INTERVAL: i32 = 1800;
33
34const BUF_SIZE: usize = 2 * 1024 * 1024;
36
37#[derive(Debug, Clone, serde::Serialize)]
39pub struct MessageInfo {
40 pub index: usize,
42 pub cmd: i32,
44 pub cmd_name: String,
46 pub tick: i32,
48 pub compressed: bool,
50 pub body_size: u32,
52 pub offset: usize,
54}
55
56pub struct Context {
63 pub serializers: SerializerContainer,
65 pub class_info: ClassInfo,
67 pub string_tables: StringTableContainer,
69 pub entities: EntityContainer,
71 pub tick_interval: f32,
73 pub full_packet_interval: i32,
75 pub tick: i32,
77}
78
79#[derive(Debug, Clone, serde::Serialize)]
81pub struct GameEvent {
82 pub tick: i32,
84 pub name: String,
86 pub msg_type: u32,
88 pub keys: Vec<(String, String)>,
90 #[serde(skip)]
92 pub payload: Vec<u8>,
93}
94
95struct EventDescriptor {
96 name: String,
97 field_names: Vec<String>,
98}
99
100fn format_event_key(key: &boon_proto::proto::c_msg_source1_legacy_game_event::KeyT) -> String {
101 if let Some(ref s) = key.val_string {
102 return s.clone();
103 }
104 if let Some(f) = key.val_float {
105 return f.to_string();
106 }
107 if let Some(l) = key.val_long {
108 return l.to_string();
109 }
110 if let Some(s) = key.val_short {
111 return s.to_string();
112 }
113 if let Some(b) = key.val_byte {
114 return b.to_string();
115 }
116 if let Some(b) = key.val_bool {
117 return b.to_string();
118 }
119 if let Some(u) = key.val_uint64 {
120 return u.to_string();
121 }
122 String::new()
123}
124
125enum Storage {
127 Mmap(Mmap),
128 Bytes(Vec<u8>),
129}
130
131impl AsRef<[u8]> for Storage {
132 fn as_ref(&self) -> &[u8] {
133 match self {
134 Storage::Mmap(m) => m,
135 Storage::Bytes(b) => b,
136 }
137 }
138}
139
140pub struct Parser {
142 storage: Storage,
143}
144
145impl Parser {
146 pub fn from_file(path: &Path) -> Result<Self> {
148 let file = std::fs::File::open(path)?;
149 let mmap = unsafe { Mmap::map(&file)? };
154 Ok(Self {
155 storage: Storage::Mmap(mmap),
156 })
157 }
158
159 pub fn from_bytes(bytes: Vec<u8>) -> Self {
164 Self {
165 storage: Storage::Bytes(bytes),
166 }
167 }
168
169 fn data(&self) -> &[u8] {
171 self.storage.as_ref()
172 }
173
174 pub fn verify(&self) -> Result<()> {
177 if self.data().len() < HEADER_SIZE {
178 return Err(Error::Parse {
179 context: "file too small for demo header".into(),
180 });
181 }
182
183 let mut magic = [0u8; 8];
184 magic.copy_from_slice(&self.data()[0..8]);
185 if &magic != MAGIC {
186 return Err(Error::InvalidMagic { got: magic });
187 }
188
189 Ok(())
190 }
191
192 fn read_cmd_header(reader: &mut ByteReader) -> Result<CmdHeader> {
193 let raw_cmd = reader.read_uvarint32()?;
194 let compress_flag = dem::IS_COMPRESSED;
195 let compressed = (raw_cmd & compress_flag) != 0;
196 let cmd = (raw_cmd & !compress_flag) as i32;
197 let tick_raw = reader.read_uvarint32()?;
198 let tick = tick_raw as i32;
199 let body_size = reader.read_uvarint32()?;
200 Ok(CmdHeader {
201 cmd,
202 tick,
203 compressed,
204 body_size,
205 })
206 }
207
208 fn read_cmd_body(reader: &mut ByteReader, header: &CmdHeader, buf: &mut Vec<u8>) -> Result<()> {
211 let raw = reader.read_bytes(header.body_size as usize)?;
212 if header.compressed {
213 let decompressed_len =
214 snap::raw::decompress_len(raw).map_err(|e| Error::Decompress(e.to_string()))?;
215 buf.clear();
216 buf.resize(decompressed_len, 0);
217 snap::raw::Decoder::new()
218 .decompress(raw, buf)
219 .map_err(|e| Error::Decompress(e.to_string()))?;
220 } else {
221 buf.clear();
222 buf.extend_from_slice(raw);
223 }
224 Ok(())
225 }
226
227 pub fn messages(&self) -> Result<Vec<MessageInfo>> {
230 self.verify()?;
231 let data = &self.data()[HEADER_SIZE..];
232 let mut reader = ByteReader::new(data);
233 let mut messages = Vec::new();
234 let mut index = 0;
235
236 while reader.remaining() > 0 {
237 let offset = reader.position() + HEADER_SIZE;
238 let header = match Self::read_cmd_header(&mut reader) {
239 Ok(h) => h,
240 Err(_) => break,
241 };
242
243 messages.push(MessageInfo {
244 index,
245 cmd: header.cmd,
246 cmd_name: command::command_name(header.cmd).to_string(),
247 tick: header.tick,
248 compressed: header.compressed,
249 body_size: header.body_size,
250 offset,
251 });
252
253 if header.cmd == dem::STOP {
255 index += 1;
256 continue;
257 }
258
259 if header.cmd == dem::FILE_INFO {
261 reader.skip(header.body_size as usize).ok();
262 break;
263 }
264
265 if reader.skip(header.body_size as usize).is_err() {
266 break;
267 }
268
269 index += 1;
270 }
271
272 Ok(messages)
273 }
274
275 pub fn file_header(&self) -> Result<CDemoFileHeader> {
277 self.verify()?;
278 let data = &self.data()[HEADER_SIZE..];
279 let mut reader = ByteReader::new(data);
280 let mut body_buf = Vec::with_capacity(BUF_SIZE);
281
282 while reader.remaining() > 0 {
283 let header = Self::read_cmd_header(&mut reader)?;
284
285 if header.cmd == dem::FILE_HEADER {
286 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
287 return CDemoFileHeader::decode(&body_buf[..]).map_err(Error::from);
288 }
289
290 if header.cmd == dem::STOP {
291 break;
292 }
293
294 reader.skip(header.body_size as usize)?;
295 }
296
297 Err(Error::Parse {
298 context: "DEM_FileHeader not found".into(),
299 })
300 }
301
302 pub fn file_info(&self) -> Result<CDemoFileInfo> {
304 self.verify()?;
305
306 let fileinfo_offset = u32::from_le_bytes([
308 self.data()[8],
309 self.data()[9],
310 self.data()[10],
311 self.data()[11],
312 ]) as usize;
313
314 let data = &self.data()[HEADER_SIZE..];
315 let mut reader = ByteReader::new(data);
316 reader.seek(fileinfo_offset.saturating_sub(HEADER_SIZE))?;
318
319 let header = Self::read_cmd_header(&mut reader)?;
320 if header.cmd != dem::FILE_INFO {
321 return Err(Error::Parse {
322 context: format!(
323 "expected DEM_FileInfo at offset {}, found command {}",
324 fileinfo_offset, header.cmd
325 ),
326 });
327 }
328
329 let mut body_buf = Vec::new();
330 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
331 CDemoFileInfo::decode(&body_buf[..]).map_err(Error::from)
332 }
333
334 pub fn events(&self, max_tick: Option<i32>) -> Result<Vec<GameEvent>> {
340 self.verify()?;
341 let data = &self.data()[HEADER_SIZE..];
342 let mut reader = ByteReader::new(data);
343 let mut body_buf = Vec::with_capacity(BUF_SIZE);
344 let mut packet_buf = vec![0u8; BUF_SIZE];
345 let mut events = Vec::new();
346 let mut descriptors: HashMap<i32, EventDescriptor> = HashMap::new();
347
348 while reader.remaining() > 0 {
349 let header = match Self::read_cmd_header(&mut reader) {
350 Ok(h) => h,
351 Err(_) => break,
352 };
353
354 if header.cmd == dem::STOP {
355 break;
356 }
357
358 if let Some(max) = max_tick
359 && header.tick > max
360 {
361 break;
362 }
363
364 match header.cmd {
365 dem::PACKET | dem::SIGNON_PACKET => {
366 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
367 let cmd = CDemoPacket::decode(&body_buf[..])?;
368 let pkt_data = cmd.data.unwrap_or_default();
369 Self::process_packet_events(
370 &pkt_data,
371 header.tick,
372 &mut descriptors,
373 &mut events,
374 &mut packet_buf,
375 )?;
376 }
377 dem::FULL_PACKET => {
378 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
379 let cmd = CDemoFullPacket::decode(&body_buf[..])?;
380 if let Some(packet) = cmd.packet {
381 let pkt_data = packet.data.unwrap_or_default();
382 Self::process_packet_events(
383 &pkt_data,
384 header.tick,
385 &mut descriptors,
386 &mut events,
387 &mut packet_buf,
388 )?;
389 }
390 }
391 _ => {
392 reader.skip(header.body_size as usize)?;
393 }
394 }
395 }
396
397 Ok(events)
398 }
399
400 fn process_packet_events(
402 pkt_data: &[u8],
403 tick: i32,
404 descriptors: &mut HashMap<i32, EventDescriptor>,
405 events: &mut Vec<GameEvent>,
406 packet_buf: &mut Vec<u8>,
407 ) -> Result<()> {
408 let mut br = BitReader::new(pkt_data);
409
410 while br.bits_remaining() > 8 {
411 let msg_type = br.read_ubitvar()?;
412 let size = br.read_uvarint32()? as usize;
413
414 if size > packet_buf.len() {
415 packet_buf.resize(size, 0);
416 }
417 br.read_bytes(&mut packet_buf[..size])?;
418 let msg_data = &packet_buf[..size];
419
420 match msg_type {
421 ge::SOURCE1_LEGACY_GAME_EVENT_LIST => {
422 let msg = CMsgSource1LegacyGameEventList::decode(msg_data)?;
423 for desc in msg.descriptors {
424 let eventid = desc.eventid.unwrap_or_default();
425 let name = desc.name.unwrap_or_default();
426 let field_names = desc
427 .keys
428 .iter()
429 .map(|k| k.name.clone().unwrap_or_default())
430 .collect();
431 descriptors.insert(eventid, EventDescriptor { name, field_names });
432 }
433 }
434 ge::SOURCE1_LEGACY_GAME_EVENT => {
435 let msg = CMsgSource1LegacyGameEvent::decode(msg_data)?;
436 let eventid = msg.eventid.unwrap_or_default();
437 let (name, keys) = if let Some(desc) = descriptors.get(&eventid) {
438 let keys: Vec<(String, String)> = desc
439 .field_names
440 .iter()
441 .zip(msg.keys.iter())
442 .map(|(fname, key)| (fname.clone(), format_event_key(key)))
443 .collect();
444 (desc.name.clone(), keys)
445 } else {
446 let name = msg
447 .event_name
448 .unwrap_or_else(|| format!("event_{}", eventid));
449 (name, Vec::new())
450 };
451 events.push(GameEvent {
452 tick,
453 name,
454 msg_type,
455 keys,
456 payload: msg_data.to_vec(),
457 });
458 }
459 svc::USER_MESSAGE => {
460 let msg = CsvcMsgUserMessage::decode(msg_data)?;
461 let inner_type = msg.msg_type.unwrap_or_default();
462 let name = command::user_message_name(inner_type);
463 let inner_payload = msg.msg_data.unwrap_or_default();
464 events.push(GameEvent {
465 tick,
466 name,
467 msg_type: inner_type as u32,
468 keys: Vec::new(),
469 payload: inner_payload,
470 });
471 }
472 _ => {
473 let t = msg_type as i32;
476 let name = if let Ok(e) = CitadelUserMessageIds::try_from(t) {
477 Some(e.as_str_name().to_string())
478 } else if let Ok(e) = ECitadelGameEvents::try_from(t) {
479 Some(e.as_str_name().to_string())
480 } else if let Ok(e) = EBaseUserMessages::try_from(t) {
481 Some(e.as_str_name().to_string())
482 } else {
483 None
484 };
485 if let Some(name) = name {
486 events.push(GameEvent {
487 tick,
488 name,
489 msg_type,
490 keys: Vec::new(),
491 payload: msg_data.to_vec(),
492 });
493 }
494 }
495 }
496 }
497
498 Ok(())
499 }
500
501 pub fn parse_send_tables(&self) -> Result<SerializerContainer> {
503 self.verify()?;
504 let data = &self.data()[HEADER_SIZE..];
505 let mut reader = ByteReader::new(data);
506 let mut body_buf = Vec::with_capacity(BUF_SIZE);
507
508 while reader.remaining() > 0 {
509 let header = Self::read_cmd_header(&mut reader)?;
510
511 if header.cmd == dem::SEND_TABLES {
512 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
513 let cmd = CDemoSendTables::decode(&body_buf[..])?;
514 return SerializerContainer::parse(cmd);
515 }
516
517 if header.cmd == dem::STOP || header.cmd == dem::SYNC_TICK {
518 break;
519 }
520
521 reader.skip(header.body_size as usize)?;
522 }
523
524 Err(Error::Parse {
525 context: "DEM_SendTables not found".into(),
526 })
527 }
528
529 pub fn parse_class_info(&self) -> Result<ClassInfo> {
531 self.verify()?;
532 let data = &self.data()[HEADER_SIZE..];
533 let mut reader = ByteReader::new(data);
534 let mut body_buf = Vec::with_capacity(BUF_SIZE);
535
536 while reader.remaining() > 0 {
537 let header = Self::read_cmd_header(&mut reader)?;
538
539 if header.cmd == dem::CLASS_INFO {
540 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
541 let cmd = CDemoClassInfo::decode(&body_buf[..])?;
542 return Ok(ClassInfo::parse(cmd));
543 }
544
545 if header.cmd == dem::STOP || header.cmd == dem::SYNC_TICK {
546 break;
547 }
548
549 reader.skip(header.body_size as usize)?;
550 }
551
552 Err(Error::Parse {
553 context: "DEM_ClassInfo not found".into(),
554 })
555 }
556
557 pub fn parse_init(&self) -> Result<Context> {
559 self.verify()?;
560 let data = &self.data()[HEADER_SIZE..];
561 let mut reader = ByteReader::new(data);
562
563 let mut packet_buf = vec![0u8; BUF_SIZE];
564 let mut body_buf = Vec::with_capacity(BUF_SIZE);
565
566 let mut serializers: Option<SerializerContainer> = None;
567 let mut class_info: Option<ClassInfo> = None;
568 let mut string_tables = StringTableContainer::new();
569 let mut tick_interval: f32 = 0.0;
570 let mut full_packet_interval: i32 = DEFAULT_FULL_PACKET_INTERVAL;
571
572 while reader.remaining() > 0 {
573 let header = Self::read_cmd_header(&mut reader)?;
574
575 if header.cmd == dem::SYNC_TICK {
576 reader.skip(header.body_size as usize)?;
577 break;
578 }
579
580 if header.cmd == dem::STOP {
581 break;
582 }
583
584 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
585
586 match header.cmd {
587 dem::SEND_TABLES => {
588 let cmd = CDemoSendTables::decode(&body_buf[..])?;
589 serializers = Some(SerializerContainer::parse(cmd)?);
590 }
591 dem::CLASS_INFO => {
592 let cmd = CDemoClassInfo::decode(&body_buf[..])?;
593 class_info = Some(ClassInfo::parse(cmd));
594 }
595 dem::PACKET | dem::SIGNON_PACKET => {
596 let cmd = CDemoPacket::decode(&body_buf[..])?;
597 let pkt_data = cmd.data.unwrap_or_default();
598 Self::process_packet_for_init(
599 &pkt_data,
600 &mut string_tables,
601 &mut tick_interval,
602 &mut full_packet_interval,
603 &mut packet_buf,
604 )?;
605 }
606 _ => {}
607 }
608 }
609
610 let serializers = serializers.ok_or_else(|| Error::Parse {
611 context: "DEM_SendTables not found during init".into(),
612 })?;
613 let class_info = class_info.ok_or_else(|| Error::Parse {
614 context: "DEM_ClassInfo not found during init".into(),
615 })?;
616
617 string_tables.update_instance_baselines(&class_info);
619
620 Ok(Context {
621 serializers,
622 class_info,
623 string_tables,
624 entities: EntityContainer::new(),
625 tick_interval,
626 full_packet_interval,
627 tick: -1,
628 })
629 }
630
631 fn process_packet_for_init(
633 pkt_data: &[u8],
634 string_tables: &mut StringTableContainer,
635 tick_interval: &mut f32,
636 full_packet_interval: &mut i32,
637 packet_buf: &mut Vec<u8>,
638 ) -> Result<()> {
639 let mut br = BitReader::new(pkt_data);
640
641 while br.bits_remaining() > 8 {
642 let msg_type = br.read_ubitvar()?;
643 let size = br.read_uvarint32()? as usize;
644
645 if size > packet_buf.len() {
647 packet_buf.resize(size, 0);
648 }
649 br.read_bytes(&mut packet_buf[..size])?;
650 let msg_data = &packet_buf[..size];
651
652 match msg_type {
653 svc::CREATE_STRING_TABLE => {
654 let msg = CsvcMsgCreateStringTable::decode(msg_data)?;
655 string_tables.handle_create(msg)?;
656 }
657 svc::UPDATE_STRING_TABLE => {
658 let msg = CsvcMsgUpdateStringTable::decode(msg_data)?;
659 string_tables.handle_update(msg)?;
660 }
661 svc::SERVER_INFO => {
662 let msg = CsvcMsgServerInfo::decode(msg_data)?;
663 if let Some(ti) = msg.tick_interval {
664 *tick_interval = ti;
665 let ratio = DEFAULT_TICK_INTERVAL / ti;
666 *full_packet_interval = DEFAULT_FULL_PACKET_INTERVAL * ratio as i32;
667 }
668 }
669 _ => {}
670 }
671 }
672
673 Ok(())
674 }
675
676 pub fn parse_to_tick(&self, target_tick: i32) -> Result<Context> {
682 let mut ctx = self.parse_init()?;
683 let data = &self.data()[HEADER_SIZE..];
684 let mut reader = ByteReader::new(data);
685
686 let mut packet_buf = vec![0u8; BUF_SIZE];
687 let mut body_buf = Vec::with_capacity(BUF_SIZE);
688 let mut fp_buf = Vec::with_capacity(256);
689 let mut field_decode_ctx = FieldDecodeContext::new(ctx.tick_interval);
690
691 let mut past_sync = false;
693 while reader.remaining() > 0 {
694 let header = Self::read_cmd_header(&mut reader)?;
695 if header.cmd == dem::SYNC_TICK {
696 reader.skip(header.body_size as usize)?;
697 past_sync = true;
698 break;
699 }
700 if header.cmd == dem::STOP {
701 return Ok(ctx);
702 }
703 reader.skip(header.body_size as usize)?;
704 }
705
706 if !past_sync {
707 return Ok(ctx);
708 }
709
710 let mut did_handle_last_full_packet = false;
712
713 while reader.remaining() > 0 {
714 let header = Self::read_cmd_header(&mut reader)?;
715
716 if header.tick > target_tick && header.cmd != dem::STOP {
717 break;
718 }
719
720 ctx.tick = header.tick;
721
722 if header.cmd == dem::STOP {
723 break;
724 }
725
726 let is_full_packet = header.cmd == dem::FULL_PACKET;
727 let distance = target_tick - header.tick;
728 let has_full_packet_ahead = distance > ctx.full_packet_interval + 100;
729
730 if is_full_packet {
731 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
732 let cmd = CDemoFullPacket::decode(&body_buf[..])?;
733
734 if let Some(st) = cmd.string_table {
736 ctx.string_tables.do_full_update(st);
737 ctx.string_tables.update_instance_baselines(&ctx.class_info);
738 }
739
740 if !has_full_packet_ahead {
742 if let Some(packet) = cmd.packet {
743 let pkt_data = packet.data.unwrap_or_default();
744 Self::process_packet_entities(
745 &pkt_data,
746 &mut ctx,
747 &mut field_decode_ctx,
748 &mut packet_buf,
749 &mut fp_buf,
750 )?;
751 }
752 did_handle_last_full_packet = true;
753 }
754
755 continue;
756 }
757
758 if !did_handle_last_full_packet {
759 reader.skip(header.body_size as usize)?;
760 continue;
761 }
762
763 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
764
765 match header.cmd {
766 dem::PACKET | dem::SIGNON_PACKET => {
767 let cmd = CDemoPacket::decode(&body_buf[..])?;
768 let pkt_data = cmd.data.unwrap_or_default();
769 Self::process_packet_entities(
770 &pkt_data,
771 &mut ctx,
772 &mut field_decode_ctx,
773 &mut packet_buf,
774 &mut fp_buf,
775 )?;
776 }
777 _ => {}
778 }
779 }
780
781 Ok(ctx)
782 }
783
784 pub fn run_to_end<F>(&self, mut on_tick: F) -> Result<()>
787 where
788 F: FnMut(&Context),
789 {
790 let mut ctx = self.parse_init()?;
791 let data = &self.data()[HEADER_SIZE..];
792 let mut reader = ByteReader::new(data);
793
794 let mut packet_buf = vec![0u8; BUF_SIZE];
795 let mut body_buf = Vec::with_capacity(BUF_SIZE);
796 let mut fp_buf = Vec::with_capacity(256);
797 let mut field_decode_ctx = FieldDecodeContext::new(ctx.tick_interval);
798
799 while reader.remaining() > 0 {
801 let header = Self::read_cmd_header(&mut reader)?;
802 if header.cmd == dem::SYNC_TICK {
803 reader.skip(header.body_size as usize)?;
804 break;
805 }
806 if header.cmd == dem::STOP {
807 return Ok(());
808 }
809 reader.skip(header.body_size as usize)?;
810 }
811
812 let mut last_tick: i32 = -1;
813
814 while reader.remaining() > 0 {
815 let header = Self::read_cmd_header(&mut reader)?;
816
817 if header.tick != last_tick && last_tick >= 0 {
819 on_tick(&ctx);
820 }
821 last_tick = header.tick;
822 ctx.tick = header.tick;
823
824 if header.cmd == dem::STOP {
825 if last_tick >= 0 {
827 on_tick(&ctx);
828 }
829 break;
830 }
831
832 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
833
834 match header.cmd {
835 dem::FULL_PACKET => {
836 let cmd = CDemoFullPacket::decode(&body_buf[..])?;
837
838 if let Some(st) = cmd.string_table {
839 ctx.string_tables.do_full_update(st);
840 ctx.string_tables.update_instance_baselines(&ctx.class_info);
841 }
842
843 if let Some(packet) = cmd.packet {
844 let pkt_data = packet.data.unwrap_or_default();
845 Self::process_packet_entities(
846 &pkt_data,
847 &mut ctx,
848 &mut field_decode_ctx,
849 &mut packet_buf,
850 &mut fp_buf,
851 )?;
852 }
853 }
854 dem::PACKET | dem::SIGNON_PACKET => {
855 let cmd = CDemoPacket::decode(&body_buf[..])?;
856 let pkt_data = cmd.data.unwrap_or_default();
857 Self::process_packet_entities(
858 &pkt_data,
859 &mut ctx,
860 &mut field_decode_ctx,
861 &mut packet_buf,
862 &mut fp_buf,
863 )?;
864 }
865 _ => {}
866 }
867 }
868
869 Ok(())
870 }
871
872 pub fn run_to_end_filtered<F>(
876 &self,
877 class_filter: &std::collections::HashSet<&str>,
878 mut on_tick: F,
879 ) -> Result<()>
880 where
881 F: FnMut(&Context),
882 {
883 let mut ctx = self.parse_init()?;
884 let data = &self.data()[HEADER_SIZE..];
885 let mut reader = ByteReader::new(data);
886
887 let mut packet_buf = vec![0u8; BUF_SIZE];
888 let mut body_buf = Vec::with_capacity(BUF_SIZE);
889 let mut fp_buf = Vec::with_capacity(256);
890 let mut field_decode_ctx = FieldDecodeContext::new(ctx.tick_interval);
891
892 while reader.remaining() > 0 {
894 let header = Self::read_cmd_header(&mut reader)?;
895 if header.cmd == dem::SYNC_TICK {
896 reader.skip(header.body_size as usize)?;
897 break;
898 }
899 if header.cmd == dem::STOP {
900 return Ok(());
901 }
902 reader.skip(header.body_size as usize)?;
903 }
904
905 let mut last_tick: i32 = -1;
906
907 while reader.remaining() > 0 {
908 let header = Self::read_cmd_header(&mut reader)?;
909
910 if header.tick != last_tick && last_tick >= 0 {
912 on_tick(&ctx);
913 }
914 last_tick = header.tick;
915 ctx.tick = header.tick;
916
917 if header.cmd == dem::STOP {
918 if last_tick >= 0 {
920 on_tick(&ctx);
921 }
922 break;
923 }
924
925 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
926
927 match header.cmd {
928 dem::FULL_PACKET => {
929 let cmd = CDemoFullPacket::decode(&body_buf[..])?;
930
931 if let Some(st) = cmd.string_table {
932 ctx.string_tables.do_full_update(st);
933 ctx.string_tables.update_instance_baselines(&ctx.class_info);
934 }
935
936 if let Some(packet) = cmd.packet {
937 let pkt_data = packet.data.unwrap_or_default();
938 Self::process_packet_entities_filtered(
939 &pkt_data,
940 &mut ctx,
941 &mut field_decode_ctx,
942 &mut packet_buf,
943 class_filter,
944 &mut fp_buf,
945 )?;
946 }
947 }
948 dem::PACKET | dem::SIGNON_PACKET => {
949 let cmd = CDemoPacket::decode(&body_buf[..])?;
950 let pkt_data = cmd.data.unwrap_or_default();
951 Self::process_packet_entities_filtered(
952 &pkt_data,
953 &mut ctx,
954 &mut field_decode_ctx,
955 &mut packet_buf,
956 class_filter,
957 &mut fp_buf,
958 )?;
959 }
960 _ => {}
961 }
962 }
963
964 Ok(())
965 }
966
967 pub fn run_to_end_with_events_filtered<F>(
971 &self,
972 class_filter: &std::collections::HashSet<&str>,
973 mut on_tick: F,
974 ) -> Result<()>
975 where
976 F: FnMut(&Context, &[GameEvent]),
977 {
978 let mut ctx = self.parse_init()?;
979 let data = &self.data()[HEADER_SIZE..];
980 let mut reader = ByteReader::new(data);
981
982 let mut packet_buf = vec![0u8; BUF_SIZE];
983 let mut event_packet_buf = vec![0u8; BUF_SIZE];
984 let mut body_buf = Vec::with_capacity(BUF_SIZE);
985 let mut fp_buf = Vec::with_capacity(256);
986 let mut field_decode_ctx = FieldDecodeContext::new(ctx.tick_interval);
987
988 let mut descriptors: HashMap<i32, EventDescriptor> = HashMap::new();
989 let mut tick_events: Vec<GameEvent> = Vec::new();
990
991 while reader.remaining() > 0 {
993 let header = Self::read_cmd_header(&mut reader)?;
994 if header.cmd == dem::SYNC_TICK {
995 reader.skip(header.body_size as usize)?;
996 break;
997 }
998 if header.cmd == dem::STOP {
999 return Ok(());
1000 }
1001 reader.skip(header.body_size as usize)?;
1002 }
1003
1004 let mut last_tick: i32 = -1;
1005
1006 while reader.remaining() > 0 {
1007 let header = Self::read_cmd_header(&mut reader)?;
1008
1009 if header.tick != last_tick && last_tick >= 0 {
1011 on_tick(&ctx, &tick_events);
1012 tick_events.clear();
1013 }
1014 last_tick = header.tick;
1015 ctx.tick = header.tick;
1016
1017 if header.cmd == dem::STOP {
1018 if last_tick >= 0 {
1020 on_tick(&ctx, &tick_events);
1021 }
1022 break;
1023 }
1024
1025 Self::read_cmd_body(&mut reader, &header, &mut body_buf)?;
1026
1027 match header.cmd {
1028 dem::FULL_PACKET => {
1029 let cmd = CDemoFullPacket::decode(&body_buf[..])?;
1030
1031 if let Some(st) = cmd.string_table {
1032 ctx.string_tables.do_full_update(st);
1033 ctx.string_tables.update_instance_baselines(&ctx.class_info);
1034 }
1035
1036 if let Some(packet) = cmd.packet {
1037 let pkt_data = packet.data.unwrap_or_default();
1038 Self::process_packet_entities_filtered(
1039 &pkt_data,
1040 &mut ctx,
1041 &mut field_decode_ctx,
1042 &mut packet_buf,
1043 class_filter,
1044 &mut fp_buf,
1045 )?;
1046 Self::process_packet_events(
1047 &pkt_data,
1048 header.tick,
1049 &mut descriptors,
1050 &mut tick_events,
1051 &mut event_packet_buf,
1052 )?;
1053 }
1054 }
1055 dem::PACKET | dem::SIGNON_PACKET => {
1056 let cmd = CDemoPacket::decode(&body_buf[..])?;
1057 let pkt_data = cmd.data.unwrap_or_default();
1058 Self::process_packet_entities_filtered(
1059 &pkt_data,
1060 &mut ctx,
1061 &mut field_decode_ctx,
1062 &mut packet_buf,
1063 class_filter,
1064 &mut fp_buf,
1065 )?;
1066 Self::process_packet_events(
1067 &pkt_data,
1068 header.tick,
1069 &mut descriptors,
1070 &mut tick_events,
1071 &mut event_packet_buf,
1072 )?;
1073 }
1074 _ => {}
1075 }
1076 }
1077
1078 Ok(())
1079 }
1080
1081 fn process_packet_entities(
1083 pkt_data: &[u8],
1084 ctx: &mut Context,
1085 field_decode_ctx: &mut FieldDecodeContext,
1086 packet_buf: &mut Vec<u8>,
1087 fp_buf: &mut Vec<crate::entity::field_path::FieldPath>,
1088 ) -> Result<()> {
1089 let mut br = BitReader::new(pkt_data);
1090
1091 while br.bits_remaining() > 8 {
1092 let msg_type = br.read_ubitvar()?;
1093 let size = br.read_uvarint32()? as usize;
1094
1095 if size > packet_buf.len() {
1096 packet_buf.resize(size, 0);
1097 }
1098 br.read_bytes(&mut packet_buf[..size])?;
1099 let msg_data = &packet_buf[..size];
1100
1101 match msg_type {
1102 svc::CREATE_STRING_TABLE => {
1103 let msg = CsvcMsgCreateStringTable::decode(msg_data)?;
1104 if ctx.string_tables.handle_create(msg)? {
1105 ctx.string_tables.update_instance_baselines(&ctx.class_info);
1106 }
1107 }
1108 svc::UPDATE_STRING_TABLE => {
1109 let msg = CsvcMsgUpdateStringTable::decode(msg_data)?;
1110 if ctx.string_tables.handle_update(msg)? {
1111 ctx.string_tables.update_instance_baselines(&ctx.class_info);
1112 }
1113 }
1114 svc::SERVER_INFO => {
1115 let msg = CsvcMsgServerInfo::decode(msg_data)?;
1116 if let Some(ti) = msg.tick_interval {
1117 ctx.tick_interval = ti;
1118 field_decode_ctx.tick_interval = ti;
1119 let ratio = DEFAULT_TICK_INTERVAL / ti;
1120 ctx.full_packet_interval = DEFAULT_FULL_PACKET_INTERVAL * ratio as i32;
1121 }
1122 }
1123 svc::PACKET_ENTITIES => {
1124 let msg = CsvcMsgPacketEntities::decode(msg_data)?;
1125 ctx.entities.handle_packet_entities(
1126 msg,
1127 &ctx.class_info,
1128 &ctx.serializers,
1129 &ctx.string_tables,
1130 field_decode_ctx,
1131 fp_buf,
1132 )?;
1133 }
1134 _ => {}
1135 }
1136 }
1137
1138 Ok(())
1139 }
1140
1141 fn process_packet_entities_filtered(
1143 pkt_data: &[u8],
1144 ctx: &mut Context,
1145 field_decode_ctx: &mut FieldDecodeContext,
1146 packet_buf: &mut Vec<u8>,
1147 class_filter: &std::collections::HashSet<&str>,
1148 fp_buf: &mut Vec<crate::entity::field_path::FieldPath>,
1149 ) -> Result<()> {
1150 let mut br = BitReader::new(pkt_data);
1151
1152 while br.bits_remaining() > 8 {
1153 let msg_type = br.read_ubitvar()?;
1154 let size = br.read_uvarint32()? as usize;
1155
1156 if size > packet_buf.len() {
1157 packet_buf.resize(size, 0);
1158 }
1159 br.read_bytes(&mut packet_buf[..size])?;
1160 let msg_data = &packet_buf[..size];
1161
1162 match msg_type {
1163 svc::CREATE_STRING_TABLE => {
1164 let msg = CsvcMsgCreateStringTable::decode(msg_data)?;
1165 if ctx.string_tables.handle_create(msg)? {
1166 ctx.string_tables.update_instance_baselines(&ctx.class_info);
1167 }
1168 }
1169 svc::UPDATE_STRING_TABLE => {
1170 let msg = CsvcMsgUpdateStringTable::decode(msg_data)?;
1171 if ctx.string_tables.handle_update(msg)? {
1172 ctx.string_tables.update_instance_baselines(&ctx.class_info);
1173 }
1174 }
1175 svc::SERVER_INFO => {
1176 let msg = CsvcMsgServerInfo::decode(msg_data)?;
1177 if let Some(ti) = msg.tick_interval {
1178 ctx.tick_interval = ti;
1179 field_decode_ctx.tick_interval = ti;
1180 let ratio = DEFAULT_TICK_INTERVAL / ti;
1181 ctx.full_packet_interval = DEFAULT_FULL_PACKET_INTERVAL * ratio as i32;
1182 }
1183 }
1184 svc::PACKET_ENTITIES => {
1185 let msg = CsvcMsgPacketEntities::decode(msg_data)?;
1186 ctx.entities.handle_packet_entities_filtered(
1187 msg,
1188 &ctx.class_info,
1189 &ctx.serializers,
1190 &ctx.string_tables,
1191 field_decode_ctx,
1192 class_filter,
1193 fp_buf,
1194 )?;
1195 }
1196 _ => {}
1197 }
1198 }
1199
1200 Ok(())
1201 }
1202}