wotbreplay_parser/models/
data.rs

1//! Models for `data.wotreplay` contents.
2
3pub mod base_player_create;
4pub mod entity_method;
5pub mod packet;
6pub mod payload;
7
8use std::io::Read;
9
10use byteorder::{LittleEndian, ReadBytesExt};
11use serde::de::DeserializeOwned;
12use serde::Serialize;
13
14use crate::models::data::packet::Packet;
15use crate::result::Result;
16use crate::Error;
17
18/// `data.wotreplay` root structure.
19#[derive(Debug, Serialize)]
20pub struct Data {
21    /// For example: `9.8.5_apple`.
22    pub client_version: String,
23
24    pub packets: Vec<Packet>,
25}
26
27impl Data {
28    pub fn from_reader(mut reader: impl Read) -> Result<Self> {
29        assert_magic(reader.read_u32::<LittleEndian>()?, 0x12345678)?;
30
31        // No idea what it is:
32        reader.read_u64::<LittleEndian>()?;
33
34        // Some sort of client hash, e.g.: `6CF2A9EFA5C52D6F6CE43A6D4A699C05`:
35        read_length_delimited(&mut reader)?;
36
37        let client_version = read_string(&mut reader)?;
38
39        // Some extra byte, no idea:
40        reader.read_u8()?;
41
42        let mut packets = Vec::new();
43        while let Some(packet) = Packet::from_reader(&mut reader)? {
44            packets.push(packet);
45        }
46
47        Ok(Self { client_version, packets })
48    }
49}
50
51#[inline]
52fn assert_magic<T: Into<u32> + PartialEq>(actual: T, expected: T) -> Result {
53    if actual == expected {
54        Ok(())
55    } else {
56        Err(Error::InvalidMagic {
57            actual: actual.into(),
58            expected: expected.into(),
59        })
60    }
61}
62
63#[inline]
64fn read_length_delimited(reader: &mut impl Read) -> Result<Vec<u8>> {
65    let length = reader.read_u8()? as usize;
66    let mut buffer = vec![0; length];
67    reader.read_exact(&mut buffer)?;
68    Ok(buffer)
69}
70
71#[inline]
72fn read_string(reader: &mut impl Read) -> Result<String> {
73    Ok(String::from_utf8(read_length_delimited(reader)?)?)
74}
75
76#[inline]
77fn read_pickled<T: DeserializeOwned>(reader: &mut impl Read, length: usize) -> Result<T> {
78    let mut buffer = vec![0; length];
79    reader.read_exact(&mut buffer)?;
80    Ok(serde_pickle::from_slice(&buffer, Default::default())?)
81}
82
83/// Read 2-byte length surrounded by the magical `0xFF` and `0x00`,
84/// unless it's a simple 1-byte length.
85///
86/// # Context
87///
88/// It seems that for payloads larger than 254 bytes, Wargaming writes `00 <XX XX> FF`,
89/// where the word in the middle is the actual length of a payload, and the `00` and `FF`
90/// are magic values.
91#[inline]
92fn read_quirky_length(reader: &mut impl Read) -> Result<usize> {
93    match reader.read_u8()? {
94        0xFF => {
95            let length = reader.read_u16::<LittleEndian>()?;
96            assert_magic(reader.read_u8()?, 0x00)?;
97            Ok(length as usize)
98        }
99        length => Ok(length as usize),
100    }
101}