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 replace_entity_field(
139 &mut self,
140 event: EntityEvents,
141 entity: &Entity,
142 field_name: &str,
143 value: &FieldValue,
144 ) -> Option<FieldValue> {
145 let ctx = &self.parser.context;
146 self.rewriters
147 .iter_mut()
148 .filter(|rewriter| {
149 rewriter
150 .interests()
151 .contains(RewriteInterests::ENTITY_FIELDS)
152 })
153 .find_map(|rewriter| {
154 rewriter.replace_entity_field(ctx, event, entity, field_name, value)
155 })
156 }
157
158 fn has_rewriters(&self, interests: RewriteInterests) -> bool {
159 self.rewriter_interests.intersects(interests)
160 }
161
162 fn rewrites_entity_fields(&self) -> bool {
163 self.has_rewriters(RewriteInterests::ENTITY_FIELDS)
164 }
165
166 fn rewrites_string_table_entries(&self) -> bool {
167 self.has_rewriters(RewriteInterests::STRING_TABLE_ENTRIES)
168 }
169
170 fn needs_string_table_context(&self) -> bool {
171 self.rewrites_entity_fields() || self.rewrites_string_table_entries()
172 }
173
174 fn needs_packet_scan(&self) -> bool {
175 self.has_rewriters(RewriteInterests::PACKET_MESSAGE | RewriteInterests::PACKET_MESSAGES)
176 || self.rewrites_entity_fields()
177 || self.rewrites_string_table_entries()
178 || self.has_rewriters(
179 RewriteInterests::SVC_CREATE_STRING_TABLE
180 | RewriteInterests::SVC_UPDATE_STRING_TABLE,
181 )
182 }
183
184 fn needs_svc_packet_scan(&self) -> bool {
185 self.needs_string_table_context()
186 || self.has_rewriters(
187 RewriteInterests::SVC_CREATE_STRING_TABLE
188 | RewriteInterests::SVC_UPDATE_STRING_TABLE,
189 )
190 }
191
192 fn needs_packet_state(&self) -> bool {
193 self.needs_string_table_context()
194 }
195
196 fn needs_class_metadata(&self) -> bool {
197 self.rewrites_entity_fields()
198 }
199
200 fn needs_demo_string_table_scan(&self) -> bool {
201 self.needs_string_table_context()
202 || self.has_rewriters(RewriteInterests::DEMO_STRING_TABLES)
203 }
204
205 fn needs_demo_string_table_state(&self) -> bool {
206 self.rewrites_string_table_entries()
207 }
208
209 pub fn into_parts(self) -> (Parser<'a, R>, W) {
211 (self.parser, self.writer)
212 }
213}
214
215impl<'a, W> DemoWriter<'a, SliceReader<'a>, W>
216where
217 W: Write + Seek,
218{
219 pub fn from_slice(replay: &'a [u8], writer: W) -> Result<Self, ParserError> {
224 Ok(Self::new(Parser::from_slice(replay)?, writer))
225 }
226}
227
228impl<S, W> DemoWriter<'static, SeekableReader<S>, W>
229where
230 S: std::io::Read + std::io::Seek,
231 W: Write + Seek,
232{
233 pub fn from_reader(reader: S, writer: W) -> Result<Self, ParserError> {
238 Ok(Self::new(Parser::from_reader(reader)?, writer))
239 }
240}