Skip to main content

source2_demo/parser/
mod.rs

1mod context;
2mod demo;
3mod observer;
4
5pub use context::*;
6pub use demo::runner::*;
7pub use observer::*;
8
9use crate::error::*;
10use crate::proto::*;
11use crate::reader::*;
12use std::cell::RefCell;
13use std::rc::Rc;
14
15use crate::parser::demo::DemoCommands;
16use crate::try_observers;
17#[cfg(feature = "dota")]
18use std::collections::VecDeque;
19
20pub struct Parser<'a, R = SliceReader<'a>>
21where
22    R: BitsReader + MessageReader,
23{
24    pub(crate) reader: R,
25    pub(crate) field_reader: FieldReader,
26
27    pub(crate) observers: Vec<Rc<RefCell<dyn Observer + 'a>>>,
28    pub(crate) observer_masks: Vec<Interests>,
29    pub(crate) global_mask: Interests,
30
31    #[cfg(feature = "dota")]
32    pub(crate) combat_log: VecDeque<CMsgDotaCombatLogEntry>,
33
34    pub(crate) prologue_completed: bool,
35    pub(crate) skip_deltas: bool,
36
37    pub(crate) replay_info: CDemoFileInfo,
38    pub(crate) last_tick: u32,
39    pub(crate) context: Context,
40
41    _phantom: std::marker::PhantomData<&'a ()>,
42}
43
44impl<'a> Parser<'a, SliceReader<'a>> {
45    pub fn new(replay: &'a [u8]) -> Result<Self, ParserError> {
46        let mut reader = SliceReader::new(replay);
47
48        if replay.len() < 16 || reader.read_bytes(8) != b"PBDEMS2\0" {
49            return Err(ParserError::WrongMagic);
50        };
51
52        reader.read_bytes(8);
53
54        let replay_info = reader.read_replay_info()?;
55        let last_tick = replay_info.playback_ticks() as u32;
56
57        reader.seek(16);
58
59        Ok(Parser {
60            reader,
61            field_reader: FieldReader::default(),
62
63            observers: Vec::default(),
64            observer_masks: Vec::default(),
65            global_mask: Interests::empty(),
66
67            #[cfg(feature = "dota")]
68            combat_log: VecDeque::default(),
69
70            prologue_completed: false,
71            skip_deltas: false,
72
73            context: Context::new(replay_info.clone()),
74
75            replay_info,
76            last_tick,
77            _phantom: std::marker::PhantomData,
78        })
79    }
80
81    #[inline]
82    pub fn from_slice(replay: &'a [u8]) -> Result<Self, ParserError> {
83        Self::new(replay)
84    }
85}
86
87impl<S> Parser<'static, SeekableReader<S>>
88where
89    S: std::io::Read + std::io::Seek,
90{
91    pub fn from_reader(reader: S) -> Result<Self, ParserError> {
92        let mut reader = SeekableReader::new(reader)
93            .map_err(|e| ParserError::IoError(e.to_string()))?;
94
95        let magic = reader.read_bytes(8);
96        if magic != b"PBDEMS2\0" {
97            return Err(ParserError::WrongMagic);
98        }
99
100        reader.read_bytes(8);
101
102        let replay_info = Self::read_file_info_from_reader(&mut reader)?;
103        let last_tick = replay_info.playback_ticks() as u32;
104
105        reader.seek(16);
106
107        Ok(Parser {
108            reader,
109            field_reader: FieldReader::default(),
110            observers: Vec::default(),
111            observer_masks: Vec::default(),
112            global_mask: Interests::empty(),
113
114            #[cfg(feature = "dota")]
115            combat_log: VecDeque::default(),
116
117            prologue_completed: false,
118            skip_deltas: false,
119
120            context: Context::new(replay_info.clone()),
121            
122            replay_info,
123            last_tick,
124            _phantom: std::marker::PhantomData,
125        })
126    }
127
128    fn read_file_info_from_reader(reader: &mut SeekableReader<S>) -> Result<CDemoFileInfo, ParserError> {
129        reader.seek(8);
130        let offset_bytes = reader.read_bytes(4);
131        let offset = u32::from_le_bytes([offset_bytes[0], offset_bytes[1], offset_bytes[2], offset_bytes[3]]) as usize;
132
133        reader.seek(offset);
134
135        if let Some(msg) = reader.read_next_message()? {
136            Ok(CDemoFileInfo::decode(msg.buf.as_slice())?)
137        } else {
138            Err(ParserError::ReplayEncodingError)
139        }
140    }
141}
142
143impl<'a, R> Parser<'a, R>
144where
145    R: BitsReader + MessageReader,
146{
147    pub fn context(&self) -> &Context {
148        &self.context
149    }
150
151    pub fn replay_info(&self) -> &CDemoFileInfo {
152        &self.replay_info
153    }
154
155    /// Registers a new observer and returns `Rc<RefCell<T>>` of it.
156    /// Observer struct must implement Observer and Default traits.
157    pub fn register_observer<T>(&mut self) -> Rc<RefCell<T>>
158    where
159        T: Observer + Default + 'a,
160    {
161        let rc = Rc::new(RefCell::new(T::default()));
162        let mask = rc.borrow().interests();
163        self.global_mask |= mask;
164        self.observer_masks.push(mask);
165        self.observers.push(rc.clone());
166        rc.clone()
167    }
168
169    #[inline]
170    fn anyone_interested(&self, flag: Interests) -> bool {
171        self.global_mask.intersects(flag)
172    }
173
174    pub(crate) fn prologue(&mut self) -> Result<(), ParserError> {
175        if self.prologue_completed && self.context.tick != u32::MAX {
176            return Ok(());
177        }
178
179        while let Some(message) = self.reader.read_next_message()? {
180            if self.prologue_completed
181                && (message.msg_type == EDemoCommands::DemSendTables
182                    || message.msg_type == EDemoCommands::DemClassInfo)
183            {
184                continue;
185            }
186
187            self.on_demo_command(message.msg_type, message.buf.as_slice())?;
188
189            if message.msg_type == EDemoCommands::DemSyncTick {
190                self.prologue_completed = true;
191                break;
192            }
193        }
194
195        Ok(())
196    }
197
198    pub(crate) fn on_demo_command(
199        &mut self,
200        msg_type: EDemoCommands,
201        msg: &[u8],
202    ) -> Result<(), ParserError> {
203        match msg_type {
204            EDemoCommands::DemSendTables => {
205                self.dem_send_tables(CDemoSendTables::decode(msg)?)?;
206            }
207            EDemoCommands::DemClassInfo => {
208                self.dem_class_info(CDemoClassInfo::decode(msg)?)?;
209            }
210            EDemoCommands::DemPacket | EDemoCommands::DemSignonPacket => {
211                self.dem_packet(CDemoPacket::decode(msg)?)?;
212            }
213            EDemoCommands::DemFullPacket => self.dem_full_packet(CDemoFullPacket::decode(msg)?)?,
214            EDemoCommands::DemStringTables => {
215                self.dem_string_tables(CDemoStringTables::decode(msg)?)?
216            }
217            EDemoCommands::DemStop => {
218                self.dem_stop()?;
219            }
220            _ => {}
221        };
222
223        try_observers!(self, DEMO, on_demo_command(&self.context, msg_type, msg))?;
224        Ok(())
225    }
226}
227
228impl<S> Parser<'static, SeekableReader<S>>
229where
230    S: std::io::Read + std::io::Seek,
231{
232    #[cfg(feature = "deadlock")]
233    pub fn deadlock_match_details(&mut self) -> Result<CMsgMatchMetaDataContents, ParserError> {
234        self.reader.read_deadlock_match_details()
235    }
236}
237
238impl<'a> Parser<'a, SliceReader<'a>> {
239    #[cfg(feature = "deadlock")]
240    pub fn deadlock_match_details(&mut self) -> Result<CMsgMatchMetaDataContents, ParserError> {
241        self.reader.read_deadlock_match_details()
242    }
243}