source2_demo/reader/
msg.rs1use 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 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