Skip to main content

source2_demo/reader/
msg.rs

1use crate::error::ParserError;
2#[cfg(feature = "deadlock")]
3use crate::proto::{
4    CCitadelUserMsgPostMatchDetails, CDemoPacket, CMsgMatchMetaDataContents, CitadelUserMessageIds,
5};
6use crate::proto::{CDemoFileInfo, EDemoCommands, Message};
7use crate::reader::bits::BitsReader;
8use crate::reader::slice::SliceReader;
9use crate::reader::seekable::SeekableReader;
10use std::io::{Read, Seek};
11
12pub struct OuterMessage {
13    pub msg_type: EDemoCommands,
14    pub tick: u32,
15    pub buf: Vec<u8>,
16}
17
18pub trait MessageReader {
19    fn read_next_message(&mut self) -> Result<Option<OuterMessage>, ParserError>;
20}
21
22pub trait ReplayInfoReader: MessageReader {
23    fn read_replay_info(&mut self) -> Result<CDemoFileInfo, ParserError>;
24
25    #[cfg(feature = "deadlock")]
26    fn read_deadlock_match_details(&mut self) -> Result<CMsgMatchMetaDataContents, ParserError>;
27}
28
29impl MessageReader for SliceReader<'_> {
30    #[inline]
31    fn read_next_message(&mut self) -> Result<Option<OuterMessage>, ParserError> {
32        if self.remaining_bytes() == 0 {
33            return Ok(None);
34        }
35
36        let cmd = self.read_var_u32() as i32;
37        let tick = self.read_var_u32();
38        let size = self.read_var_u32();
39
40        let msg_type = EDemoCommands::try_from(cmd & !(EDemoCommands::DemIsCompressed as i32))?;
41        let msg_compressed = cmd & EDemoCommands::DemIsCompressed as i32 != 0;
42
43        let buf = if msg_compressed {
44            let buf = self.read_bytes(size);
45            let mut decoder = snap::raw::Decoder::new();
46            decoder.decompress_vec(&buf)?
47        } else {
48            self.read_bytes(size)
49        };
50
51        Ok(Some(OuterMessage {
52            msg_type,
53            tick,
54            buf,
55        }))
56    }
57}
58
59impl<R: Read + Seek> ReplayInfoReader for SeekableReader<R> {
60    fn read_replay_info(&mut self) -> Result<CDemoFileInfo, ParserError> {
61        self.seek(8);
62        let offset_bytes = self.read_bytes(4);
63        let offset = u32::from_le_bytes([offset_bytes[0], offset_bytes[1], offset_bytes[2], offset_bytes[3]]) as usize;
64
65        self.seek(offset);
66
67        // Read the message
68        if let Some(msg) = self.read_next_message()? {
69            Ok(CDemoFileInfo::decode(msg.buf.as_slice())?)
70        } else {
71            Err(ParserError::ReplayEncodingError)
72        }
73    }
74
75    #[cfg(feature = "deadlock")]
76    fn read_deadlock_match_details(&mut self) -> Result<CMsgMatchMetaDataContents, ParserError> {
77        self.seek(16);
78
79        while let Some(message) = self.read_next_message()? {
80            if message.msg_type != EDemoCommands::DemPacket {
81                continue;
82            }
83
84            let packet = CDemoPacket::decode(message.buf.as_slice())?;
85            let mut packet_reader = SliceReader::new(packet.data());
86
87            while packet_reader.remaining_bytes() != 0 {
88                let msg_type = packet_reader.read_ubit_var() as i32;
89                let size = packet_reader.read_var_u32();
90                let packet_buf = packet_reader.read_bytes(size);
91
92                if msg_type == CitadelUserMessageIds::KEUserMsgPostMatchDetails as i32 {
93                    return Ok(CMsgMatchMetaDataContents::decode(
94                        CCitadelUserMsgPostMatchDetails::decode(packet_buf.as_slice())?
95                            .match_details(),
96                    )?);
97                }
98            }
99        }
100
101        Err(ParserError::MatchDetailsNotFound)
102    }
103}
104
105impl<'a> ReplayInfoReader for SliceReader<'a> {
106    fn read_replay_info(&mut self) -> Result<CDemoFileInfo, ParserError> {
107        let source_data = self.source_buffer;
108        let offset = u32::from_le_bytes(source_data[8..12].try_into().unwrap()) as usize;
109
110        if source_data.len() < offset {
111            return Err(ParserError::ReplayEncodingError);
112        }
113
114        let mut reader = SliceReader::new(&source_data[offset..]);
115        Ok(CDemoFileInfo::decode(
116            reader.read_next_message()?.unwrap().buf.as_slice(),
117        )?)
118    }
119
120    #[cfg(feature = "deadlock")]
121    fn read_deadlock_match_details(&mut self) -> Result<CMsgMatchMetaDataContents, ParserError> {
122        let source_data = self.source_buffer;
123        let mut temp_reader = SliceReader::new(source_data);
124        temp_reader.seek(16);
125        while let Some(message) = temp_reader.read_next_message()? {
126            if message.msg_type != EDemoCommands::DemPacket {
127                continue;
128            }
129
130            let packet = CDemoPacket::decode(message.buf.as_slice())?;
131            let mut packet_reader = SliceReader::new(packet.data());
132            while packet_reader.remaining_bytes() != 0 {
133                let msg_type = packet_reader.read_ubit_var() as i32;
134                let size = packet_reader.read_var_u32();
135                let packet_buf = packet_reader.read_bytes(size);
136
137                if msg_type == CitadelUserMessageIds::KEUserMsgPostMatchDetails as i32 {
138                    return Ok(CMsgMatchMetaDataContents::decode(
139                        CCitadelUserMsgPostMatchDetails::decode(packet_buf.as_slice())?
140                            .match_details(),
141                    )?);
142                }
143            }
144        }
145
146        Err(ParserError::ReplayEncodingError)
147    }
148}
149
150impl<R: Read + Seek> MessageReader for SeekableReader<R> {
151    #[inline]
152    fn read_next_message(&mut self) -> Result<Option<OuterMessage>, ParserError> {
153        self.refill();
154        if self.remaining_bytes() == 0 {
155            return Ok(None);
156        }
157
158        let cmd = self.read_var_u32() as i32;
159        let tick = self.read_var_u32();
160        let size = self.read_var_u32();
161
162        let msg_type = EDemoCommands::try_from(cmd & !(EDemoCommands::DemIsCompressed as i32))?;
163        let msg_compressed = cmd & EDemoCommands::DemIsCompressed as i32 != 0;
164
165        let raw_bytes = self.read_bytes(size);
166
167        let buf = if msg_compressed {
168            let mut decoder = snap::raw::Decoder::new();
169            let decompressed = decoder.decompress_vec(&raw_bytes)?;
170            
171            decompressed
172        } else {
173            raw_bytes
174        };
175
176        Ok(Some(OuterMessage {
177            msg_type,
178            tick,
179            buf,
180        }))
181    }
182}
183