Skip to main content

cdragon_prop/
parser.rs

1use std::any::Any;
2use std::io::Read;
3use nom::{
4    number::complete::{le_u8, le_i8, le_u16, le_i16, le_u32, le_i32, le_u64, le_i64, le_f32},
5    bytes::complete::{tag, take},
6    combinator::{map, flat_map, opt},
7    sequence::{pair, tuple},
8    multi::count,
9};
10use super::{
11    PropFile,
12    BinEntry,
13    BinEntryHeader,
14    data::*,
15    binvalue_map_keytype,
16    binvalue_map_type,
17};
18use cdragon_hashes::HashDef;
19use cdragon_utils::{
20    parsing::{ParseError, IResult, ReadArray},
21    parse_buf,
22};
23
24type Result<T> = std::result::Result<T, ParseError>;
25
26
27/// Trait satisfied by values that can be parsed from binary data
28pub(super) trait BinParsable where Self: Sized {
29    fn binparse(i: &[u8]) -> IResult<&[u8], Self>;
30}
31
32pub(super) fn binparse<T: BinParsable>(i: &[u8]) -> Result<T> {
33    match T::binparse(i) {
34        Ok((i, v)) => {
35            if !i.is_empty() {
36                Err(ParseError::TooMuchData)
37            } else {
38                Ok(v)
39            }
40        },
41        Err(e) => Err(e.into())
42    }
43}
44
45/// Similar to nom::multi::count, but get count from a parser
46fn length_count<I, O1, O2, F, G>(f: F, g: G) -> impl Fn(I) -> IResult<I, Vec<O2>>
47where
48  I: Clone + PartialEq,
49  F: Fn(I) -> IResult<I, O1>,
50  G: Fn(I) -> IResult<I, O2>,
51  O1: nom::ToUsize,
52{
53    move |i: I| {
54        let (i, n) = f(i)?;
55        let (i, v) = nom::multi::count(&g, n.to_usize())(i)?;
56        Ok((i, v))
57    }
58}
59
60
61macro_rules! impl_binparsable {
62    ($type:ty, $expr:expr) => {
63        impl BinParsable for $type {
64            fn binparse(i: &[u8]) -> IResult<&[u8], Self> { $expr(i) }
65        }
66    };
67    ($type:ty, =$parser:expr) => {
68        impl_binparsable!($type, map($parser, |v| Self(v)));
69    };
70    ($type:ty, =>($($parser:expr),* $(,)?)) => {
71        impl_binparsable!($type, map(tuple(($($parser,)*)), <$type>::from));
72    };
73}
74
75impl BinParsable for PropFile {
76    fn binparse(i: &[u8]) -> IResult<&[u8], Self> {
77        // Parse header
78        let (i, opt_ptch) = opt(tag("PTCH"))(i)?;
79        let (i, is_patch) = match opt_ptch {
80            Some(_) => {
81                let (i, header) = tuple((le_u32, le_u32))(i)?;
82                assert_eq!(header, (1, 0));
83                (i, true)
84            }
85            None => (i, false)
86        };
87
88        let (i, (_, version)) = tuple((tag("PROP"), le_u32))(i)?;
89        let (i, linked_files) =
90            if version >= 2 {
91                length_count(le_u32, parse_binstring)(i)?
92            } else {
93                (i, vec![])
94            };
95
96        let (i, entry_types) = length_count(le_u32, BinClassName::binparse)(i)?;
97        // Parse entries
98        let (i, entries) = {
99            let (mut i, mut entries) = (i, Vec::<BinEntry>::with_capacity(entry_types.len()));
100            for ctype in entry_types {
101                i = {
102                    let (i, entry) = parse_entry_from_type(i, ctype)?;
103                    entries.push(entry);
104                    i
105                }
106            }
107            (i, entries)
108        };
109
110        Ok((i, Self { version, is_patch, linked_files, entries }))
111    }
112}
113
114
115/// Scan entries from a bin file
116#[derive(Debug)]
117pub struct BinEntryScanner<R: Read> {
118    reader: R,
119    htypes_iter: std::vec::IntoIter<BinClassName>,
120    /// `true` if scanning a patch
121    ///
122    /// See [PropFile::is_patch] for details.
123    pub is_patch: bool,
124}
125
126impl<R: Read> BinEntryScanner<R> {
127    /// Create a scanner, parse the headers
128    pub fn new(mut reader: R) -> Result<Self> {
129        // Parse header
130        let (is_patch, version): (bool, u32) = {
131            let mut buf = [0u8; 4 + 4 + 4];  // maximum size needed
132            reader.read_exact(&mut buf[..8])?;
133            let is_patch = match parse_buf!(buf[..4], opt(tag("PTCH"))) {
134                Some(_) => {
135                    reader.read_exact(&mut buf[8..12])?;
136                    let header = parse_buf!(buf[4..12], tuple((le_u32, le_u32)));
137                    assert_eq!(header, (1, 0));
138                    reader.read_exact(&mut buf[..8])?;
139                    true
140                }
141                None => false
142            };
143
144            let (_, version) = parse_buf!(buf[..8], tuple((tag("PROP"), le_u32)));
145            (is_patch, version)
146        };
147
148        if version >= 2 {
149            // Skip linked files
150            let buf = reader.read_array::<4>()?;
151            let n = parse_buf!(buf, le_u32);
152            for _ in 0..n {
153                let buf = reader.read_array::<2>()?;
154                let n = parse_buf!(buf, le_u16);
155                std::io::copy(&mut reader.by_ref().take(n as u64), &mut std::io::sink())?;
156            }
157        };
158
159        // Parse entry types
160        let entry_types: Vec<BinClassName> = {
161            let buf = reader.read_array::<4>()?;
162            let n = parse_buf!(buf, le_u32);
163            let mut buf = Vec::<u8>::new();
164            reader.by_ref().take(4 * n as u64).read_to_end(&mut buf)?;
165            let entry_types = parse_buf!(buf, count(BinClassName::binparse, n as usize));
166            entry_types
167        };
168
169        Ok(Self { reader, htypes_iter: entry_types.into_iter(), is_patch })
170    }
171
172    /// Scan entries, allow to parse or skip each entry
173    ///
174    /// The result behaves provides `next()` but is not an `Iterator`.
175    pub fn scan(self) -> BinEntryScanScan<R> {
176        BinEntryScanScan {
177            reader: self.reader,
178            htypes_iter: self.htypes_iter,
179            length: None,
180        }
181    }
182
183    /// Scan entries, iterate on headers (path, type)
184    pub fn headers(self) -> BinEntryScanHeaders<R> {
185        BinEntryScanHeaders {
186            reader: self.reader,
187            htypes_iter: self.htypes_iter,
188        }
189    }
190
191    /// Scan entries, parse filtered ones
192    pub fn filter_parse<F>(self, f: F) -> BinEntryScanFilterParse<R, F>
193    where F: Fn(BinEntryPath, BinClassName) -> bool {
194        BinEntryScanFilterParse {
195            reader: self.reader,
196            htypes_iter: self.htypes_iter,
197            filter: f,
198        }
199    }
200
201    /// Parse entries, iterate on them
202    pub fn parse(self) -> BinEntryScanParse<R> {
203        BinEntryScanParse {
204            reader: self.reader,
205            htypes_iter: self.htypes_iter,
206        }
207    }
208}
209
210// Note: A trait alias would be better, but they are not available
211/// Item type for entry scanning
212pub type BinEntryScannerItem = Result<BinEntry>;
213
214
215/// Common methods for BinEntryScanner iterators
216trait BinEntryScan {
217    type Reader: Read;
218    type Output;
219
220    /// Read the next entry header, return the remaining length and the path
221    fn next_scan(reader: &mut Self::Reader) -> Result<(u32, BinEntryPath)> {
222        let buf = reader.read_array::<{4 + 4}>()?;
223        let (length, path) = parse_buf!(buf, tuple((le_u32, BinEntryPath::binparse)));
224        Ok((length - 4, path))  // path has been read, deduct it from length
225    }
226
227    /// Read entry fields
228    fn read_fields(reader: &mut Self::Reader, length: u32) -> Result<Vec<BinField>> {
229        let mut buf = Vec::<u8>::new();
230        reader.by_ref().take(length as u64).read_to_end(&mut buf)?;
231        let fields = parse_buf!(buf, length_count(le_u16, BinField::binparse));
232        Ok(fields)
233    }
234
235    /// Skip entry fields
236    fn skip_fields(reader: &mut Self::Reader, length: u32) -> Result<()> {
237        // There is no seek-like method implemented on &[u8]
238        //reader.seek(SeekFrom::Current(length as i64))?;
239        std::io::copy(&mut reader.by_ref().take(length as u64), &mut std::io::sink())?;
240        Ok(())
241    }
242
243    fn next_result(&mut self, ctype: BinClassName) -> Result<Self::Output>;
244}
245
246
247pub struct BinEntryScanHeaders<R>
248where R: Read {
249    reader: R,
250    htypes_iter: std::vec::IntoIter<BinClassName>,
251}
252
253impl<R: Read> BinEntryScan for BinEntryScanHeaders<R> {
254    type Reader = R;
255    type Output = (BinEntryPath, BinClassName);
256
257    fn next_result(&mut self, ctype: BinClassName) -> Result<Self::Output> {
258        let (length, path) = Self::next_scan(&mut self.reader)?;
259        Self::skip_fields(&mut self.reader, length)?;
260        Ok((path, ctype))
261    }
262}
263
264impl<R: Read> Iterator for BinEntryScanHeaders<R> {
265    type Item = Result<(BinEntryPath, BinClassName)>;
266
267    fn next(&mut self) -> Option<Self::Item> {
268        let ctype = self.htypes_iter.next()?;
269        Some(self.next_result(ctype))
270    }
271}
272
273
274pub struct BinEntryScanFilterParse<R, F>
275where R: Read, F: Fn(BinEntryPath, BinClassName) -> bool {
276    reader: R,
277    htypes_iter: std::vec::IntoIter<BinClassName>,
278    filter: F,
279}
280
281impl<R, F> BinEntryScan for BinEntryScanFilterParse<R, F>
282where R: Read, F: Fn(BinEntryPath, BinClassName) -> bool {
283    type Reader = R;
284    type Output = Option<BinEntry>;
285
286    fn next_result(&mut self, ctype: BinClassName) -> Result<Self::Output> {
287        let (length, path) = Self::next_scan(&mut self.reader)?;
288        if (self.filter)(path, ctype) {
289            let fields = Self::read_fields(&mut self.reader, length)?;
290            Ok(Some(BinEntry { path, ctype, fields }))
291        } else {
292            Self::skip_fields(&mut self.reader, length)?;
293            Ok(None)
294        }
295    }
296}
297
298impl<R, F> Iterator for BinEntryScanFilterParse<R, F>
299where R: Read, F: Fn(BinEntryPath, BinClassName) -> bool {
300    type Item = BinEntryScannerItem;
301
302    fn next(&mut self) -> Option<Self::Item> {
303        loop {
304            let ctype = self.htypes_iter.next()?;
305            match self.next_result(ctype) {
306                Ok(None) => continue,
307                Ok(Some(v)) => return Some(Ok(v)),
308                Err(e) => return Some(Err(e)),
309            }
310        }
311    }
312}
313
314
315pub struct BinEntryScanParse<R>
316where R: Read {
317    reader: R,
318    htypes_iter: std::vec::IntoIter<BinClassName>,
319}
320
321impl<R: Read> BinEntryScan for BinEntryScanParse<R> {
322    type Reader = R;
323    type Output = BinEntry;
324
325    fn next_result(&mut self, ctype: BinClassName) -> Result<Self::Output> {
326        let (length, path) = Self::next_scan(&mut self.reader)?;
327        let fields = Self::read_fields(&mut self.reader, length)?;
328        Ok(BinEntry { path, ctype, fields })
329    }
330}
331
332impl<R: Read> Iterator for BinEntryScanParse<R> {
333    type Item = BinEntryScannerItem;
334
335    fn next(&mut self) -> Option<Self::Item> {
336        let ctype = self.htypes_iter.next()?;
337        Some(self.next_result(ctype))
338    }
339}
340
341
342// Iterator-like
343//
344// It does NOT implemented `Iterator` but behaves similarly.
345pub struct BinEntryScanScan<R>
346where R: Read {
347    reader: R,
348    length: Option<u32>,
349    htypes_iter: std::vec::IntoIter<BinClassName>,
350}
351
352pub struct BinEntryScanItem<'a, R>
353where R: Read {
354    owner: &'a mut BinEntryScanScan<R>,
355    pub path: BinEntryPath,
356    pub ctype: BinClassName,
357}
358
359impl<'a, R> BinEntryScanItem<'a, R>
360where R: Read {
361    pub fn read(self) -> Result<BinEntry> {
362        self.owner.read_entry(self.path, self.ctype)
363    }
364}
365
366
367impl<R> BinEntryScan for BinEntryScanScan<R>
368where R: Read {
369    type Reader = R;
370    type Output = (u32, BinEntryPath, BinClassName);
371
372    fn next_result(&mut self, ctype: BinClassName) -> Result<Self::Output> {
373        let (length, path) = Self::next_scan(&mut self.reader)?;
374        Ok((length, path, ctype))
375    }
376}
377
378impl<R> BinEntryScanScan<R>
379where R: Read {
380    pub fn next(&mut self) -> Option<Result<BinEntryScanItem<'_, R>>> {
381        // Note: the entry is skipped and thus fails at the next iteration
382        if let Some(length) = self.length.take() {
383            if let Err(err) = Self::skip_fields(&mut self.reader, length) {
384                return Some(Err(err));
385            }
386        }
387        let ctype = self.htypes_iter.next()?;
388        match self.next_result(ctype) {
389            Ok((length, path, ctype)) => {
390                self.length = Some(length);
391                Some(Ok(BinEntryScanItem { owner: self, path, ctype }))
392            }
393            Err(err) => Some(Err(err)),
394        }
395    }
396
397    fn read_entry(&mut self, path: BinEntryPath, ctype: BinClassName) -> Result<BinEntry> {
398        // Double calls are not possible using public API
399        let length = self.length.take().unwrap();
400        let fields = Self::read_fields(&mut self.reader, length)?;
401        Ok(BinEntry { path, ctype, fields })
402    }
403}
404
405
406
407/// Parse a single BinEntry, starts at its header
408fn parse_entry_from_type(i: &[u8], ctype: BinClassName) -> IResult<&[u8], BinEntry> {
409    let (i, (_length, path)) = tuple((le_u32, BinEntryPath::binparse))(i)?;
410    parse_entry_from_header(i, (path, ctype))
411}
412
413/// Parse a single BinEntry, starts before its field count
414fn parse_entry_from_header(i: &[u8], (path, ctype): BinEntryHeader) -> IResult<&[u8], BinEntry> {
415    map(length_count(le_u16, BinField::binparse),
416        |fields| BinEntry { path, ctype, fields })(i)
417}
418
419fn parse_binstring(i: &[u8]) -> IResult<&[u8], String> {
420    map(flat_map(le_u16, take), |s| std::str::from_utf8(s).expect("invalid UTF-8 string in BIN").to_string())(i)
421}
422
423
424impl BinParsable for BinField {
425    fn binparse(i: &[u8]) -> IResult<&[u8], Self> {
426        let (i, (name, vtype)) = tuple((BinFieldName::binparse, BinType::binparse))(i)?;
427        let (i, value) = binvalue_map_type!(vtype, T, map(T::binparse, |v| { Box::new(v) as Box<dyn Any> })(i)?);
428        Ok((i, Self { name, vtype, value }))
429    }
430}
431
432impl_binparsable!(BinHashValue, map(le_u32, Self::from));
433impl_binparsable!(BinEntryPath, map(le_u32, Self::from));
434impl_binparsable!(BinClassName, map(le_u32, Self::from));
435impl_binparsable!(BinFieldName, map(le_u32, Self::from));
436impl_binparsable!(BinPathValue, map(le_u64, Self::from));
437
438impl_binparsable!(BinNone, map(take(6usize), |_| Self()));
439impl_binparsable!(BinBool, map(le_u8, |v| Self(v != 0u8)));
440impl_binparsable!(BinS8, =le_i8);
441impl_binparsable!(BinU8, =le_u8);
442impl_binparsable!(BinS16, =le_i16);
443impl_binparsable!(BinU16, =le_u16);
444impl_binparsable!(BinS32, =le_i32);
445impl_binparsable!(BinU32, =le_u32);
446impl_binparsable!(BinS64, =le_i64);
447impl_binparsable!(BinU64, =le_u64);
448impl_binparsable!(BinFloat, =le_f32);
449impl_binparsable!(BinVec2, =>(le_f32, le_f32));
450impl_binparsable!(BinVec3, =>(le_f32, le_f32, le_f32));
451impl_binparsable!(BinVec4, =>(le_f32, le_f32, le_f32, le_f32));
452impl_binparsable!(BinColor, map(tuple((le_u8, le_u8, le_u8, le_u8)), |t| Self { r: t.0, g: t.1, b: t.2, a: t.3 }));
453impl_binparsable!(BinMatrix, map(tuple((le_f32, le_f32, le_f32, le_f32,
454                                           le_f32, le_f32, le_f32, le_f32,
455                                           le_f32, le_f32, le_f32, le_f32,
456                                           le_f32, le_f32, le_f32, le_f32)),
457                                           |t| Self([
458                                           [t.0, t.1, t.2, t.3],
459                                           [t.4, t.5, t.6, t.7],
460                                           [t.8, t.9, t.10, t.11],
461                                           [t.12, t.13, t.14, t.15]])
462                                           ));
463
464impl BinParsable for BinList {
465    fn binparse(i: &[u8]) -> IResult<&[u8], Self> {
466        let (i, (vtype, _)) = tuple((BinType::binparse, le_u32))(i)?;
467        let (i, values) = binvalue_map_type!(vtype, T, map(length_count(le_u32, T::binparse), |v| { Box::new(v) as Box<dyn Any> })(i)?);
468        Ok((i, Self { vtype, values }))
469    }
470}
471
472impl BinParsable for BinStruct {
473    fn binparse(i: &[u8]) -> IResult<&[u8], Self> {
474        let (i, ctype) = BinClassName::binparse(i)?;
475        if ctype.is_null() {
476            Ok((i, Self { ctype, fields: vec![] }))
477        } else {
478            let (i, (_, fields)) = tuple((le_u32, length_count(le_u16, BinField::binparse)))(i)?;
479            Ok((i, Self { ctype, fields }))
480        }
481    }
482}
483
484impl BinParsable for BinEmbed {
485    fn binparse(i: &[u8]) -> IResult<&[u8], Self> {
486        let (i, ctype) = BinClassName::binparse(i)?;
487        if ctype.is_null() {
488            Ok((i, Self { ctype, fields: vec![] }))
489        } else {
490            let (i, (_, fields)) = tuple((le_u32, length_count(le_u16, BinField::binparse)))(i)?;
491            Ok((i, Self { ctype, fields }))
492        }
493    }
494}
495
496impl BinParsable for BinOption {
497    fn binparse(i: &[u8]) -> IResult<&[u8], Self> {
498        let (i, vtype) = BinType::binparse(i)?;
499        let (i, n) = le_u8(i)?;
500        let (i, value) = match n {
501            0 => (i, None),
502            1 => {
503                let (i, v) = binvalue_map_type!(vtype, T, map(T::binparse, |v| Box::new(v) as Box<dyn Any>)(i)?);
504                (i, Some(v))
505            }
506            _ => panic!("unexpected option count: {}", n),
507        };
508        Ok((i, Self { vtype, value }))
509    }
510}
511
512impl BinParsable for BinMap {
513    fn binparse(i: &[u8]) -> IResult<&[u8], Self> {
514        let (i, (ktype, vtype, _, n)) = tuple((BinType::binparse, BinType::binparse, le_u32, le_u32))(i)?;
515        let (i, values) =
516            binvalue_map_keytype!(
517                ktype, K, binvalue_map_type!(
518                    vtype, V, map(count(pair(K::binparse, V::binparse), n as usize), |v| {
519                        let v: Vec<(K, V)> = v.into_iter().collect();
520                        Box::new(v) as Box<dyn Any>
521                    })(i)?));
522        Ok((i, Self { ktype, vtype, values }))
523    }
524}
525
526impl_binparsable!(BinHash, =BinHashValue::binparse);
527impl_binparsable!(BinPath, =BinPathValue::binparse);
528impl_binparsable!(BinLink, =BinEntryPath::binparse);
529impl_binparsable!(BinFlag, map(le_u8, |v| Self(v != 0u8)));
530impl_binparsable!(BinString, =parse_binstring);
531impl_binparsable!(BinType, map(le_u8, |mut v| {
532    if v >= 0x80 {
533        v = v - 0x80 + BinType::List as u8;
534    }
535    Self::try_from(v).expect("invalid BIN type")
536}));
537