Files
wotbreplay-parser/src/models/data.rs
T

103 lines
2.7 KiB
Rust
Raw Normal View History

2023-05-03 15:25:33 +02:00
//! Models for `data.wotreplay` contents.
2023-04-29 19:05:40 +02:00
2023-05-03 15:25:33 +02:00
pub mod base_player_create;
2023-04-29 22:17:12 +02:00
pub mod entity_method;
pub mod packet;
pub mod payload;
2023-04-29 19:05:40 +02:00
use std::io::Read;
use byteorder::{LittleEndian, ReadBytesExt};
use serde::de::DeserializeOwned;
2023-04-29 22:17:12 +02:00
use serde::Serialize;
2023-04-29 19:05:40 +02:00
2023-04-29 22:17:12 +02:00
use crate::models::data::packet::Packet;
2023-04-29 19:05:40 +02:00
use crate::result::Result;
use crate::Error;
/// `data.wotreplay` root structure.
#[derive(Debug, Serialize)]
pub struct Data {
2023-04-29 22:17:12 +02:00
/// For example: `9.8.5_apple`.
2023-04-29 19:05:40 +02:00
pub client_version: String,
2023-04-30 19:32:35 +02:00
pub packets: Vec<Packet>,
2023-04-29 19:05:40 +02:00
}
impl Data {
pub fn from_reader(mut reader: impl Read) -> Result<Self> {
2023-04-29 22:17:12 +02:00
assert_magic(reader.read_u32::<LittleEndian>()?, 0x12345678)?;
// No idea what it is:
reader.read_u64::<LittleEndian>()?;
// Some sort of client hash, e.g.: `6CF2A9EFA5C52D6F6CE43A6D4A699C05`:
2023-04-30 22:13:51 +02:00
read_length_delimited(&mut reader)?;
2023-04-29 22:17:12 +02:00
2023-04-29 19:05:40 +02:00
let client_version = read_string(&mut reader)?;
2023-04-29 22:17:12 +02:00
// Some extra byte, no idea:
reader.read_u8()?;
2023-04-30 19:32:35 +02:00
let mut packets = Vec::new();
while let Some(packet) = Packet::from_reader(&mut reader)? {
packets.push(packet);
2023-04-29 22:17:12 +02:00
}
2023-04-29 19:05:40 +02:00
2023-04-30 19:32:35 +02:00
Ok(Self { client_version, packets })
2023-04-29 19:05:40 +02:00
}
}
2023-04-29 22:17:12 +02:00
#[inline]
fn assert_magic<T: Into<u32> + PartialEq>(actual: T, expected: T) -> Result {
if actual == expected {
Ok(())
} else {
2023-05-08 23:39:52 +02:00
Err(Error::InvalidMagic {
actual: actual.into(),
expected: expected.into(),
})
2023-04-29 19:05:40 +02:00
}
}
#[inline]
2023-04-30 22:13:51 +02:00
fn read_length_delimited(reader: &mut impl Read) -> Result<Vec<u8>> {
2023-04-29 19:05:40 +02:00
let length = reader.read_u8()? as usize;
let mut buffer = Vec::new();
buffer.resize(length, 0);
reader.read_exact(&mut buffer)?;
Ok(buffer)
}
#[inline]
fn read_string(reader: &mut impl Read) -> Result<String> {
2023-04-30 22:13:51 +02:00
Ok(String::from_utf8(read_length_delimited(reader)?)?)
2023-04-29 19:05:40 +02:00
}
#[inline]
fn read_pickled<T: DeserializeOwned>(reader: &mut impl Read, length: usize) -> Result<T> {
let mut buffer = Vec::new();
buffer.resize(length, 0);
reader.read_exact(&mut buffer)?;
Ok(serde_pickle::from_slice(&buffer, Default::default())?)
}
2023-04-30 22:13:51 +02:00
/// Read 2-byte length surrounded by the magical `0xFF` and `0x00`,
/// unless it's a simple 1-byte length.
///
2023-05-08 23:39:52 +02:00
/// # Context
///
/// It seems that for payloads larger than 254 bytes, Wargaming writes `00 <XX XX> FF`,
/// where the word in the middle is the actual length of a payload, and the `00` and `FF`
/// are magic values.
2023-04-30 22:13:51 +02:00
#[inline]
fn read_quirky_length(reader: &mut impl Read) -> Result<usize> {
match reader.read_u8()? {
0xFF => {
let length = reader.read_u16::<LittleEndian>()?;
assert_magic(reader.read_u8()?, 0x00)?;
Ok(length as usize)
}
length => Ok(length as usize),
}
}