source2_demo/parser/demo/writer/
mod.rs1mod baseline;
2mod entity;
3mod input;
4mod output;
5mod packet;
6mod packet_state;
7mod rewriter;
8mod run;
9mod string_table;
10
11use crate::entity::field::FieldValue;
12use crate::entity::{Entity, EntityEvents};
13use crate::error::ParserError;
14use crate::parser::Parser;
15use crate::reader::{BitsReader, MessageReader, SeekableReader, SliceReader};
16use crate::string_table::PackedStringTableState;
17use std::cell::RefCell;
18use std::io::{Seek, Write};
19use std::rc::Rc;
20
21use input::RawDemoMessage;
22pub use rewriter::{
23 rewrite_protobuf_message, DemoRewriter, MessageRewrite, PacketMessage, RewriteInterests,
24};
25
26const INSTANCE_BASELINE_TABLE: &str = "instancebaseline";
27
28pub struct DemoWriter<'a, R, W>
35where
36 R: BitsReader + MessageReader,
37 W: Write + Seek,
38{
39 parser: Parser<'a, R>,
40 writer: W,
41 string_table_rewrite_states: Vec<Option<PackedStringTableState>>,
42 rewriters: Vec<Box<dyn DemoRewriter + 'a>>,
43 rewriter_interests: RewriteInterests,
44 bytes_written: u64,
45 file_info_offset: Option<u64>,
46}
47
48impl<'a, R, W> DemoWriter<'a, R, W>
49where
50 R: BitsReader + MessageReader,
51 W: Write + Seek,
52{
53 pub fn new(parser: Parser<'a, R>, writer: W) -> Self {
55 Self {
56 parser,
57 writer,
58 string_table_rewrite_states: Vec::new(),
59 rewriters: Vec::new(),
60 rewriter_interests: RewriteInterests::empty(),
61 bytes_written: 0,
62 file_info_offset: None,
63 }
64 }
65
66 pub fn add_rewriter<T>(&mut self, rewriter: T) -> Rc<RefCell<T>>
72 where
73 T: DemoRewriter + 'a,
74 {
75 let rewriter = Rc::new(RefCell::new(rewriter));
76 self.rewriter_interests |= rewriter.borrow().interests();
77 self.rewriters.push(Box::new(rewriter.clone()));
78 rewriter
79 }
80
81 pub fn register_rewriter<T>(&mut self) -> Rc<RefCell<T>>
120 where
121 T: DemoRewriter + Default + 'a,
122 {
123 self.add_rewriter(T::default())
124 }
125
126 pub(crate) fn should_rewrite_entity(&mut self, event: EntityEvents, entity: &Entity) -> bool {
127 let ctx = &self.parser.context;
128 self.rewriters
129 .iter_mut()
130 .filter(|rewriter| {
131 rewriter
132 .interests()
133 .contains(RewriteInterests::ENTITY_FIELDS)
134 })
135 .all(|rewriter| rewriter.should_rewrite_entity(ctx, event, entity))
136 }
137
138 pub(crate) fn should_track_entity(&mut self, event: EntityEvents, entity: &Entity) -> bool {
139 let ctx = &self.parser.context;
140 self.rewriters
141 .iter_mut()
142 .filter(|rewriter| {
143 rewriter
144 .interests()
145 .contains(RewriteInterests::ENTITY_FIELDS)
146 })
147 .all(|rewriter| rewriter.should_track_entity(ctx, event, entity))
148 }
149
150 pub(crate) fn replace_entity_field(
151 &mut self,
152 event: EntityEvents,
153 entity: &Entity,
154 field_name: &str,
155 value: &FieldValue,
156 ) -> Option<FieldValue> {
157 let ctx = &self.parser.context;
158 self.rewriters
159 .iter_mut()
160 .filter(|rewriter| {
161 rewriter
162 .interests()
163 .contains(RewriteInterests::ENTITY_FIELDS)
164 })
165 .find_map(|rewriter| {
166 rewriter.replace_entity_field(ctx, event, entity, field_name, value)
167 })
168 }
169
170 fn has_rewriters(&self, interests: RewriteInterests) -> bool {
171 self.rewriter_interests.intersects(interests)
172 }
173
174 fn rewrites_entity_fields(&self) -> bool {
175 self.has_rewriters(RewriteInterests::ENTITY_FIELDS)
176 }
177
178 fn rewrites_string_table_entries(&self) -> bool {
179 self.has_rewriters(RewriteInterests::STRING_TABLE_ENTRIES)
180 }
181
182 fn needs_string_table_context(&self) -> bool {
183 self.rewrites_entity_fields() || self.rewrites_string_table_entries()
184 }
185
186 fn needs_packet_scan(&self) -> bool {
187 self.has_rewriters(RewriteInterests::PACKET_MESSAGE | RewriteInterests::PACKET_MESSAGES)
188 || self.rewrites_entity_fields()
189 || self.rewrites_string_table_entries()
190 || self.has_rewriters(
191 RewriteInterests::SVC_CREATE_STRING_TABLE
192 | RewriteInterests::SVC_UPDATE_STRING_TABLE,
193 )
194 }
195
196 fn needs_svc_packet_scan(&self) -> bool {
197 self.needs_string_table_context()
198 || self.has_rewriters(
199 RewriteInterests::SVC_CREATE_STRING_TABLE
200 | RewriteInterests::SVC_UPDATE_STRING_TABLE,
201 )
202 }
203
204 fn needs_packet_state(&self) -> bool {
205 self.needs_string_table_context()
206 }
207
208 fn needs_class_metadata(&self) -> bool {
209 self.rewrites_entity_fields()
210 }
211
212 fn needs_demo_string_table_scan(&self) -> bool {
213 self.needs_string_table_context()
214 || self.has_rewriters(RewriteInterests::DEMO_STRING_TABLES)
215 }
216
217 fn needs_demo_string_table_state(&self) -> bool {
218 self.rewrites_string_table_entries()
219 }
220
221 pub fn into_parts(self) -> (Parser<'a, R>, W) {
223 (self.parser, self.writer)
224 }
225}
226
227impl<'a, W> DemoWriter<'a, SliceReader<'a>, W>
228where
229 W: Write + Seek,
230{
231 pub fn from_slice(replay: &'a [u8], writer: W) -> Result<Self, ParserError> {
236 Ok(Self::new(Parser::from_slice(replay)?, writer))
237 }
238}
239
240impl<S, W> DemoWriter<'static, SeekableReader<S>, W>
241where
242 S: std::io::Read + std::io::Seek,
243 W: Write + Seek,
244{
245 pub fn from_reader(reader: S, writer: W) -> Result<Self, ParserError> {
250 Ok(Self::new(Parser::from_reader(reader)?, writer))
251 }
252}