1use std::{ffi::OsStr, fs::OpenOptions, io::Read, path::Path};
23
24use types::{DeltaDecoderTable, Demo};
25
26mod byte_writer;
27mod delta;
28mod nom_helper;
29mod utils;
30
31pub mod bit;
32pub mod demo_parser;
33pub mod demo_writer;
34pub mod error;
35pub mod netmsg_doer;
36pub mod types;
37
38pub use crate::bit::BitSliceCast;
40
41use crate::{demo_parser::parse_demo, error::DemoError, types::MessageDataParseMode};
42pub use utils::bitslice_to_string;
43
44pub extern crate bitvec;
49
50impl Demo {
51 pub fn parse_from_file(
53 path: impl AsRef<OsStr> + AsRef<Path>,
54 netmsg_parse_mode: MessageDataParseMode,
55 ) -> Result<Self, DemoError> {
56 let mut file = OpenOptions::new().read(true).open(path)?;
57 let mut bytes: Vec<u8> = vec![];
58
59 file.read_to_end(&mut bytes)?;
60
61 Self::parse_from_bytes(bytes.as_slice(), netmsg_parse_mode)
62 }
63
64 pub fn parse_from_bytes(
65 demo_bytes: &[u8],
66 netmsg_parse_mode: MessageDataParseMode,
67 ) -> Result<Self, DemoError> {
68 parse_demo(demo_bytes, netmsg_parse_mode)
69 .map_err(|_| DemoError::ParseError)
71 .map(|(_, x)| x)
72 }
73}
74
75pub fn open_demo(demo_path: impl AsRef<Path> + AsRef<OsStr>) -> Result<Demo, DemoError> {
82 Demo::parse_from_file(demo_path, types::MessageDataParseMode::Parse)
83}
84
85pub fn open_demo_from_bytes(demo_bytes: &[u8]) -> Result<Demo, DemoError> {
86 Demo::parse_from_bytes(demo_bytes, types::MessageDataParseMode::Parse)
87}
88
89#[macro_export]
93macro_rules! nbit_num {
94 ($num:expr, $bit:expr) => {{
95 use $crate::bit::BitWriter;
96
97 let mut writer = BitWriter::new();
98 writer.append_u32_range($num as u32, $bit);
99 writer.data
100 }};
101}
102
103#[macro_export]
105macro_rules! nbit_str {
106 ($name:expr) => {{
107 use $crate::bit::BitWriter;
108
109 let mut writer = BitWriter::new();
110 $name.as_bytes().iter().for_each(|s| writer.append_u8(*s));
111 writer.data
112 }};
113}
114
115#[cfg(test)]
116mod test {
117 use super::*;
118
119 #[test]
120 fn open() {
121 open_demo("./src/tests/demotest.dem").unwrap();
122 }
123
124 #[test]
125 fn open_without_netmessage() {
126 Demo::parse_from_file(
127 "./src/tests/demotest.dem",
128 types::MessageDataParseMode::Parse,
129 )
130 .unwrap();
131 }
132
133 #[test]
134 fn write_read() {
135 let dem = open_demo("./src/tests/demotest.dem").unwrap();
136 let a = dem.write_to_bytes();
137 let _dem = open_demo_from_bytes(&a).unwrap();
138 }
139
140 #[test]
141 fn read_a_lot() {
142 let folder = "./src/tests/";
143
144 std::fs::read_dir(folder)
145 .map(|res| {
146 res.filter_map(|entry| entry.ok()).for_each(|entry| {
147 let path = entry.path();
148
149 if path.is_dir() {
150 return;
151 }
152
153 if path.extension().map(|ext| ext != "dem").unwrap_or(false) {
154 return;
155 }
156
157 assert!(
160 open_demo(path.as_path()).is_ok(),
161 "error opening {}",
162 path.display()
163 )
164 })
165 })
166 .unwrap_or_else(|_| assert!(false));
167 }
168
169 #[test]
170 fn read_write_read_a_lot() {
171 let folder = "./src/tests/";
172
173 if let Ok(read_dir_res) = std::fs::read_dir(folder) {
174 read_dir_res
175 .filter_map(|entry| entry.ok())
176 .for_each(|entry| {
177 let path = entry.path();
178
179 if path.is_dir() {
180 return;
181 }
182
183 if path.extension().map(|ext| ext != "dem").unwrap_or(false) {
184 return;
185 }
186
187 let a = open_demo(path.as_path());
188
189 assert!(a.is_ok(), "error opening {}", path.display());
190
191 let a = a.unwrap();
192
193 let a_bytes = a.write_to_bytes();
194
195 let a_again = open_demo_from_bytes(&a_bytes);
196
197 assert!(
198 a_again.is_ok(),
199 "error parse write parse {}",
200 path.display()
201 )
202 })
203 } else {
204 panic!("cannot read dir for read_write_read_a_lot");
205 }
206 }
207}