vault 11.0.1

A parser for Company of Heroes replay files.
Documentation
use crate::data::Span;
use byteorder::{LittleEndian, ReadBytesExt};
use nom::bytes::complete::{take, take_while};
use nom::combinator::{map, peek, verify};
use nom::error::ParseError;
use nom::multi::many_till;
use nom::number::complete::{le_u16, le_u32};
use nom::{IResult, ToUsize};
use std::io::Cursor;
use std::string::String;

pub fn verify_zero_u16(input: Span) -> IResult<Span, u16> {
    verify(le_u16, |n: &u16| *n == 0)(input)
}

pub fn verify_le_u32<'a>(expected: u32) -> impl FnMut(Span<'a>) -> IResult<Span<'a>, u32> {
    verify(le_u32, move |n: &u32| *n == expected)
}

pub fn parse_utf8_fixed<'a, E, T: ToUsize>(
    len: T,
) -> impl FnMut(Span<'a>) -> IResult<Span<'a>, String, E>
where
    E: ParseError<Span<'a>>,
{
    map(take(len), |s: Span| {
        String::from_utf8_lossy(s.fragment()).into_owned()
    })
}

pub fn parse_utf8_variable<'a, O, E, F>(
    mut f: F,
) -> impl FnMut(Span<'a>) -> IResult<Span<'a>, (O, String), E>
where
    E: ParseError<Span<'a>>,
    F: FnMut(Span<'a>) -> IResult<Span<'a>, O, E>,
    O: ToUsize + Copy,
{
    move |input: Span| {
        let (input, num) = f(input)?;
        let (input, res) = parse_utf8_fixed(num)(input)?;

        Ok((input, (num, res)))
    }
}

fn bytes_to_utf16(bytes: Span) -> String {
    let mut u16_vec = Vec::with_capacity(bytes.len() / 2);
    let mut cursor = Cursor::new(bytes.fragment());

    for _ in 1..=(bytes.len() / 2) {
        let val = cursor.read_u16::<LittleEndian>().unwrap();
        u16_vec.push(val);
    }

    String::from_utf16_lossy(&u16_vec)
}

pub fn parse_utf16_terminated(input: Span) -> IResult<Span, String> {
    map(
        many_till(le_u16, peek(verify(le_u16, |n: &u16| *n == 0))),
        |(u16s, _)| String::from_utf16_lossy(&u16s),
    )(input)
}

pub fn parse_utf16_fixed<'a, E, T>(len: T) -> impl FnMut(Span<'a>) -> IResult<Span<'a>, String, E>
where
    E: ParseError<Span<'a>>,
    T: ToUsize,
{
    let len = len.to_usize();
    let true_len = len * 2;

    map(take(true_len), bytes_to_utf16)
}

pub fn parse_utf16_variable<'a, O, E, F>(
    mut f: F,
) -> impl FnMut(Span<'a>) -> IResult<Span<'a>, (O, String), E>
where
    E: ParseError<Span<'a>>,
    F: FnMut(Span<'a>) -> IResult<Span<'a>, O, E>,
    O: ToUsize + Copy,
{
    move |input: Span| {
        let (input, num) = f(input)?;
        let (input, res) = parse_utf16_fixed(num)(input)?;

        Ok((input, (num, res)))
    }
}

pub fn take_zeroes(input: Span) -> IResult<Span, Span> {
    take_while(|n: u8| n == 0)(input)
}