source_demo_tool/lib.rs
1pub mod demo_file;
2pub mod engine_types;
3pub mod event_data;
4
5mod parse_tools;
6pub mod protobuf_message;
7
8pub extern crate source_demo_tool_impl_proc_macros;
9
10/*
11#[cfg(test)]
12mod tests {
13 use std::{ fs::File, io::Write, collections::BTreeMap, };
14
15 use crate::demo_file::{
16 frame::Command,
17 packet::netmessage::NetMessage,
18 };
19 use super::*;
20
21 fn full_file_open_routine(filepath: &str, tag: &str) -> Result<(), String> {
22 let demo_file = match demo_file::DemoFile::open(filepath) {
23 Ok(df) => df,
24 Err(e) => return Err(format!("couldn't open test file: {e}"))
25 };
26
27 let output_first_100_frames = format!("{:#?}", &demo_file.frames[0..100]);
28 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-first_100_frames.txt").unwrap();
29 out_file.write_all(output_first_100_frames.as_bytes()).unwrap();
30
31 let out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-summary.txt").unwrap();
32 summarize_demo_file(&demo_file, out_file);
33
34 let header = format!("{:#?}", &demo_file.header);
35 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-header.txt").unwrap();
36 out_file.write_all(header.as_bytes()).unwrap();
37
38 let mut user_messages = Vec::new();
39 let mut game_events = Vec::new();
40
41 let mut first_1000_entity_data = Vec::new();
42 for frame in &demo_file.frames {
43 if let Command::Packet(packet) = &frame.command {
44 for netmsg in &packet.network_messages {
45 let msg = netmsg.message.as_ref().unwrap();
46 match msg {
47 NetMessage::PacketEntity(pe) => {
48 if first_1000_entity_data.len() == 1000 {
49 continue
50 }
51 first_1000_entity_data.push(pe);
52 },
53 NetMessage::UserMessage(um) => {
54 user_messages.push(um);
55 },
56 NetMessage::GameEvent(ge) => {
57 game_events.push(ge);
58 },
59 _ => {}
60 }
61 }
62 }
63 }
64
65 // raw entity data
66 let mut output_raw_entity_data = Vec::new();
67 for ed in first_1000_entity_data {
68 output_raw_entity_data.append(&mut ed.entity_data.clone().unwrap());
69 output_raw_entity_data.append(&mut [0xDE, 0xAD, 0xFF, 0xDE, 0xAD, 0xFF].to_vec());
70 }
71 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-first_1000_entity_data.raw").unwrap();
72 out_file.write_all(output_raw_entity_data.as_slice()).unwrap();
73
74 // sign on frames
75 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-signon_frames.txt").unwrap();
76 out_file.write_all(format!{"{:#?}", &demo_file.sign_on_frames}.as_bytes()).unwrap();
77
78 // user messages
79 let mut output_user_messages = Vec::new();
80 for um in user_messages {
81 output_user_messages.append(&mut um.msg_type.unwrap().to_le_bytes().to_vec());
82 output_user_messages.append(&mut um.msg_data.as_ref().unwrap().clone());
83 output_user_messages.append(&mut [0xDE, 0xAD, 0xFF, 0xDE, 0xAD, 0xFF].to_vec());
84 }
85 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-user_messages.raw").unwrap();
86 out_file.write_all(output_user_messages.as_slice()).unwrap();
87
88 // data tables
89 let output_data_tables = format!("{:#?}", demo_file.get_data_tables());
90 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-data_tables.txt").unwrap();
91 out_file.write_all(output_data_tables.as_bytes()).unwrap();
92
93 // game events
94 let output_game_events = format!("{:#?}", game_events);
95 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-game_events.txt").unwrap();
96 out_file.write_all(output_game_events.as_bytes()).unwrap();
97
98 // server info
99 let output_server_info = format!("{:#?}", demo_file.get_server_info().unwrap());
100 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-server_info.txt").unwrap();
101 out_file.write_all(output_server_info.as_bytes()).unwrap();
102
103 // sign on data summary
104 {
105 let mut command_counter = BTreeMap::new();
106 let mut message_counter = BTreeMap::new();
107 let mut class_info = None;
108 let mut server_info = None;
109 let mut game_event_list = None;
110 let mut set_con_var = None;
111 let mut set_view = None;
112 let mut sign_on_states = Vec::new();
113 let mut tick = None;
114 let mut voice_init = None;
115 let mut create_string_table_data_messages = Vec::new();
116 for f in &demo_file.sign_on_frames {
117 match &f.command {
118 Command::DataTables(_dt) => {
119 command_counter.entry("DataTables").and_modify(|e| {*e+=1}).or_insert(1);
120 },
121 Command::Packet(_pd) => {
122 command_counter.entry("Packet").and_modify(|e| {*e+=1}).or_insert(1);
123 },
124 Command::SignOn(pd) => {
125 command_counter.entry("SignOn").and_modify(|e| {*e+=1}).or_insert(1);
126
127 for msg in &pd.network_messages {
128 if let Some(nmsg) = &msg.message {
129 message_counter.entry(nmsg.to_string()).and_modify(|e| {*e+=1}).or_insert(1);
130 match &nmsg {
131 NetMessage::ClassInfo(ci) => class_info = Some(ci),
132 NetMessage::ServerInfo(si) => server_info = Some(si),
133 NetMessage::GameEventList(gel) => game_event_list = Some(gel),
134 NetMessage::SetConVar(scv) => set_con_var = Some(scv),
135 NetMessage::SetView(sv) => set_view = Some(sv),
136 NetMessage::SignOnState(sos) => sign_on_states.push(sos),
137 NetMessage::Tick(td) => tick = Some(td),
138 NetMessage::VoiceInit(vid) => voice_init = Some(vid),
139 NetMessage::CreateStringTable(cstd) => create_string_table_data_messages.push(cstd),
140 _ => {}
141 }
142
143 }
144 }
145 },
146 Command::Stop => {
147 command_counter.entry("Stop").and_modify(|e| {*e+=1}).or_insert(1);
148 },
149 Command::SyncTick => {
150 command_counter.entry("SyncTick").and_modify(|e| {*e+=1}).or_insert(1);
151 }
152 }
153 }
154
155 let output_sod_summary = format! {
156 "\
157 command_counter \
158 {:#?}\n\
159 message_counter \
160 {:#?}\n\
161 class_info \
162 {:#?}\n\
163 server_info \
164 {:#?}\n\
165 set_con_var \
166 {:#?}\n\
167 set_view \
168 {:#?}\n\
169 sign_on_state \
170 {:#?}\n\
171 tick \
172 {:#?}\n\
173 voice_init \
174 {:#?}\n\
175 "
176 , command_counter, message_counter, class_info, server_info, set_con_var, set_view,
177 sign_on_states, tick, voice_init
178 };
179 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-sign_on_data_summary.txt").unwrap();
180 out_file.write_all(output_sod_summary.as_bytes()).unwrap();
181
182 // game event list
183 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-game_event_list.txt").unwrap();
184 out_file.write_all(format!{"{:#?}", game_event_list}.as_bytes()).unwrap();
185
186 // create string table data
187 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-create_string_table.txt").unwrap();
188 out_file.write_all(format!{"{:#?}", create_string_table_data_messages}.as_bytes()).unwrap();
189 }
190
191 // dump full game events
192 let full_game_events = demo_file.get_full_game_events();
193 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-full_game_events.txt").unwrap();
194 out_file.write_all(format!{"{:#?}", &full_game_events}.as_bytes()).unwrap();
195
196 let mut game_event_counter = BTreeMap::new();
197 let mut game_event_id_name_map = BTreeMap::new();
198 for ev in &full_game_events {
199 game_event_counter.entry(&ev.event_id).and_modify(|e|{*e+=1}).or_insert(1);
200 game_event_id_name_map.insert(&ev.event_id, &ev.event_name);
201 }
202
203 let mut out_file = File::create("test_output/file_full_open-".to_owned() + tag + "-game_events_summary.txt").unwrap();
204 out_file.write_all(format!("{:>6}{:>42}{:>10}\n", "id", "name", "count").as_bytes()).unwrap();
205 for id in game_event_id_name_map {
206 out_file.write_all(format!("{:>6}{:>42}{:>10}\n", id.0, id.1, game_event_counter[id.0]).as_bytes()).unwrap();
207 }
208
209 Ok(())
210 }
211
212 #[test]
213 fn file_full_gotv_open1_full_gotv() -> Result<(), String> {
214 full_file_open_routine("assets/test_demos/full_gotv.dem", "full_gotv")
215 }
216
217 #[test]
218 fn file_full_gotv_open2_glitched() -> Result<(), String> {
219 full_file_open_routine("assets/test_demos/glitched_texture_r14_acor.dem", "glitched")
220 }
221
222 #[test]
223 fn file_full_gotv_open3_full_gotv() -> Result<(), String> {
224 full_file_open_routine("assets/test_demos/gamerlegion-vs-masonic-m1-vertigo.dem", "full_gotv3")
225 }
226
227 #[test]
228 fn file_full_gotv_open4_full_gotv() -> Result<(), String> {
229 full_file_open_routine("assets/test_demos/gamerlegion-vs-masonic-m3-ancient.dem", "full_gotv4")
230 }
231
232 fn summarize_demo_file(demo_file: &demo_file::DemoFile, mut out_file: File) {
233 let mut unknown_message_counter = BTreeMap::new();
234 let mut unknown_field_counter = BTreeMap::new();
235 let mut missing_field_counter = BTreeMap::new();
236 let mut invalid_or_corrupt_events = BTreeMap::new();
237 let mut frame_command_counter = BTreeMap::new();
238 let mut frame_player_slot_counter = BTreeMap::new();
239 let mut message_counter = BTreeMap::new();
240 let mut first_10_sub_warnings = Vec::new();
241 let mut user_message_counter = BTreeMap::new();
242
243 for frame in &demo_file.frames {
244 frame_command_counter.entry(frame.command.as_u8())
245 .and_modify(|e| {*e += 1 })
246 .or_insert(1);
247
248 frame_player_slot_counter.entry(&frame.player_slot)
249 .and_modify(|e| { *e += 1 })
250 .or_insert(1);
251
252 if let Command::Packet(p) = &frame.command {
253 for netmsg in &p.network_messages {
254 if let Some(msg) = &netmsg.message {
255 message_counter.entry(msg.to_string())
256 .and_modify(|e| { *e += 1 })
257 .or_insert(1);
258 if let NetMessage::UserMessage(umd) = &netmsg.message.as_ref().unwrap() {
259 user_message_counter.entry(umd.msg_type.unwrap())
260 .and_modify(|e|{*e+=1}).or_insert(1);
261 }
262 }
263
264 if let Some(msg_warns) = &netmsg.warnings {
265 let message_name = netmsg.message.as_ref().unwrap().to_string();
266 for field in &msg_warns.missing_fields {
267 missing_field_counter.entry((message_name.clone(), field.0, field.1))
268 .and_modify(|e| { *e += 1 })
269 .or_insert(1);
270 }
271 for field in &msg_warns.unknown_fields {
272 unknown_field_counter.entry((message_name.clone(), field.field_number))
273 .and_modify(|(_, counter)| { *counter += 1})
274 .or_insert((field.clone(), 1));
275 }
276 for field in &msg_warns.sub_warnings {
277 if first_10_sub_warnings.len() < 10 {
278 if !(field.missing_fields.is_empty() &&
279 field.unknown_fields.is_empty() &&
280 field.sub_warnings.is_empty()) {
281 first_10_sub_warnings.push((message_name.clone(), field));
282 }
283 }
284 }
285 }
286
287 if let Some(msg) = &netmsg.err {
288 type ErrT = demo_file::packet::ParseMessageErr;
289 match msg {
290 ErrT::InvalidOrCorrupt(ev) => { invalid_or_corrupt_events.entry(ev)
291 .and_modify(|e| {*e += 1 })
292 .or_insert(1); },
293 ErrT::UnknownCommand(n) => {
294 unknown_message_counter.entry(n)
295 .and_modify(|e| { *e += 1 })
296 .or_insert(1);
297 }
298 }
299 }
300 }
301 }
302 }
303
304 out_file.write_all(b"Unknown Messages -- msg_num: Count\n").unwrap();
305 out_file.write_all(format!("{:#?}", unknown_message_counter).as_bytes()).unwrap();
306 out_file.write_all(b"\n").unwrap();
307
308 out_file.write_all(b"Missing Fields -- (message_name, field_num, field_name): Count\n").unwrap();
309 out_file.write_all(format!("{:#?}", missing_field_counter).as_bytes()).unwrap();
310 out_file.write_all(b"\n").unwrap();
311
312 out_file.write_all(b"Unknown Fields -- (message_name, field_num): Count\n").unwrap();
313 out_file.write_all(format!("{:#?}", unknown_field_counter).as_bytes()).unwrap();
314 out_file.write_all(b"\n").unwrap();
315
316 out_file.write_all(b"Invalid or corrupt frame -- Event: Count\n").unwrap();
317 out_file.write_all(format!("{:#?}", invalid_or_corrupt_events).as_bytes()).unwrap();
318 out_file.write_all(b"\n").unwrap();
319
320 out_file.write_all(b"Frame Commands -- Command: Count\n").unwrap();
321 out_file.write_all(format!("{:#?}", frame_command_counter).as_bytes()).unwrap();
322 out_file.write_all(b"\n").unwrap();
323
324 out_file.write_all(b"Player Slot -- player_slot: Count\n").unwrap();
325 out_file.write_all(format!("{:#?}", frame_player_slot_counter).as_bytes()).unwrap();
326 out_file.write_all(b"\n").unwrap();
327
328 out_file.write_all(b"Messages -- message_name: Count\n").unwrap();
329 out_file.write_all(format!("{:#?}", message_counter).as_bytes()).unwrap();
330 out_file.write_all(b"\n").unwrap();
331
332 out_file.write_all(b"First 10 sub-warnings\n").unwrap();
333 out_file.write_all(format!("{:#?}", first_10_sub_warnings).as_bytes()).unwrap();
334 out_file.write_all(b"\n").unwrap();
335
336 out_file.write_all(b"UserMessages -- msg_type: Count\n").unwrap();
337 out_file.write_all(format!("{:#?}", user_message_counter).as_bytes()).unwrap();
338 out_file.write_all(b"\n").unwrap();
339
340 out_file.write_all(b"UserMessage warnings & errors\n").unwrap();
341 let user_messages = demo_file.get_user_messages();
342 let mut user_messages_warnings = Vec::new();
343 let mut user_messages_errors = Vec::new();
344 let mut first_10_sub_warnings = Vec::new();
345 for um in &user_messages {
346 if let Some(warns) = &um.warnings {
347 for sub_warn in &warns.sub_warnings {
348 if !(sub_warn.missing_fields.is_empty() && sub_warn.unknown_fields.is_empty()) {
349 first_10_sub_warnings.push(sub_warn);
350 }
351 }
352
353 if !(warns.missing_fields.is_empty() && warns.unknown_fields.is_empty()) {
354 user_messages_warnings.push(warns);
355 }
356
357 }
358
359 if let Some(err) = &um.err {
360 user_messages_errors.push(err);
361 }
362 }
363 out_file.write_all(format!("\
364 Warnings \
365 {:#?}\n\
366 Errors \
367 {:#?}\n\
368 First 10 sub warnings \
369 {:#?}\
370 ", user_messages_warnings, user_messages_errors, first_10_sub_warnings).as_bytes()).unwrap();
371 }
372}
373*/