1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! This file contains all the scalars defined in the
//! specification file.
//!
//! The specification uses names like `WORD`, `SHORT`, etc. which
//! was resulted in some confusion while implementing this parser.
//! Therefore the parser uses the same types making it easy to
//! compare it to the specification.

use nom::{
    bytes::complete::take,
    combinator::{flat_map, map_res},
    number::complete::{le_i16, le_i32, le_u128, le_u16, le_u32, le_u8},
};

use super::errors::{ParseError, ParseResult};

pub type Byte = u8;
pub type Word = u16;
pub type Short = i16;
pub type Dword = u32;
pub type Long = i32;
pub type Float = f32;
pub type Double = f64;
pub type Qword = u64;
pub type Long64 = i64;
pub type Uuid = u128;

#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
pub struct Point {
    pub x: Long,
    pub y: Long,
}

#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
pub struct Size {
    pub width: Long,
    pub height: Long,
}

#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
pub struct Rect {
    pub point: Point,
    pub size: Size,
}

#[derive(Debug, Copy, Clone)]
pub struct Fixed(u16, u16);

#[derive(Debug, Copy, Clone)]
pub struct RGB {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
}

#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
pub struct Color {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
    pub alpha: u8,
}

#[inline]
pub fn byte(input: &[u8]) -> ParseResult<'_, Byte> {
    le_u8(input)
}

#[inline]
pub fn word(input: &[u8]) -> ParseResult<'_, Word> {
    le_u16(input)
}

#[inline]
pub fn short(input: &[u8]) -> ParseResult<'_, Short> {
    le_i16(input)
}

#[inline]
pub fn dword(input: &[u8]) -> ParseResult<'_, Dword> {
    le_u32(input)
}

#[inline]
pub fn long(input: &[u8]) -> ParseResult<'_, Long> {
    le_i32(input)
}

pub fn parse_point(input: &[u8]) -> ParseResult<'_, Point> {
    let (input, x) = long(input)?;
    let (input, y) = long(input)?;
    Ok((input, Point { x, y }))
}

pub fn parse_size(input: &[u8]) -> ParseResult<'_, Size> {
    let (input, width) = long(input)?;
    let (input, height) = long(input)?;
    Ok((input, Size { width, height }))
}

pub fn parse_rect(input: &[u8]) -> ParseResult<'_, Rect> {
    let (input, point) = parse_point(input)?;
    let (input, size) = parse_size(input)?;
    Ok((input, Rect { point, size }))
}

/// Parse a DWORD as size information and make sure the
/// parsed size no less than 4. The latter is important as
/// this function is used when parsing frames and chunks
/// where the size includes itself.
pub fn dword_size<'a>(input: &'a [u8], f: fn(Dword) -> ParseError<'a>) -> ParseResult<'a, Dword> {
    let (input, size) = dword(input)?;
    if size >= 4 {
        Ok((input, size))
    } else {
        Err(nom::Err::Failure(f(size)))
    }
}

pub fn parse_dword_as_usize(input: &[u8]) -> ParseResult<'_, usize> {
    let (input, size) = dword(input)?;
    let size = size
        .try_into()
        .map_err(|_| nom::Err::Failure(ParseError::DwordToUsize(size)))?;
    Ok((input, size))
}

pub fn parse_dword_as_u8<'a>(input: &'a [u8], e: ParseError<'a>) -> ParseResult<'a, u8> {
    let (input, size) = dword(input)?;
    let size = size.try_into().map_err(|_| nom::Err::Failure(e))?;
    Ok((input, size))
}

pub fn parse_string(input: &[u8]) -> ParseResult<'_, &str> {
    map_res(flat_map(word, take), std::str::from_utf8)(input)
}

pub fn parse_uuid(input: &[u8]) -> ParseResult<'_, Uuid> {
    le_u128(input)
}

pub fn fixed(input: &[u8]) -> ParseResult<'_, Fixed> {
    let (input, low) = le_u16(input)?;
    let (input, high) = le_u16(input)?;
    Ok((input, Fixed(high, low)))
}

pub fn parse_color(input: &[u8]) -> ParseResult<'_, Color> {
    let (input, red) = byte(input)?;
    let (input, green) = byte(input)?;
    let (input, blue) = byte(input)?;
    let (input, alpha) = byte(input)?;
    Ok((
        input,
        Color {
            red,
            green,
            blue,
            alpha,
        },
    ))
}

pub fn parse_rgb(input: &[u8]) -> ParseResult<'_, RGB> {
    let (input, red) = byte(input)?;
    let (input, green) = byte(input)?;
    let (input, blue) = byte(input)?;
    Ok((input, RGB { red, green, blue }))
}