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
pub mod challenge;
pub mod replay;
use self::challenge::parse_challenge_header_xml;
use self::replay::parse_replay_xml;
use super::*;
use std::convert::TryInto;
use std::io;
use std::io::Read;
use std::{fs::File, num::ParseIntError};
const HEADER_START_TOKEN: &[u8] = "<header ".as_bytes();
const HEADER_END_TOKEN: &[u8] = "</header>".as_bytes();
const THUMBNAIL_START_TOKEN: &[u8] = &[0xFF, 0xD8, 0xFF];
const THUMBNAIL_END_TOKEN: &[u8] = &[0xFF, 0xD9];
#[derive(Debug)]
pub enum ParseError {
MissingGBXMagic,
FileTooShort,
HeaderNotFound,
ThumbnailNotFound,
XMLParseError(xml::reader::Error),
HeaderValueError(ParseIntError),
HeaderTryIntoEnumError(String),
IOError(io::Error),
Unknown,
}
fn find_window(buf: &[u8], needle: &[u8]) -> Option<usize> {
buf.windows(needle.len()).position(|w| w == needle)
}
pub fn parse_from_file(filename: &str) -> Result<GBX, ParseError> {
let mut buffer = Vec::new();
let mut f = File::open(filename).map_err(ParseError::IOError)?;
f.read_to_end(&mut buffer).map_err(ParseError::IOError)?;
let mut gbx = parse_from_buffer(&buffer)?;
gbx.origin = GBXOrigin::File {
path: String::from(filename),
};
Ok(gbx)
}
pub fn parse_from_buffer(buffer: &[u8]) -> Result<GBX, ParseError> {
if buffer.len() < 3 {
return Err(ParseError::FileTooShort);
}
if &buffer[0..3] != b"GBX" {
return Err(ParseError::MissingGBXMagic);
}
let binary_header = GBXBinaryHeader {
version: u16::from_le_bytes((&buffer[3..5]).try_into().unwrap()),
class_id: u32::from_le_bytes((&buffer[9..13]).try_into().unwrap()),
};
let header_start = find_window(buffer, HEADER_START_TOKEN).ok_or(ParseError::HeaderNotFound);
let header_end = find_window(buffer, HEADER_END_TOKEN)
.ok_or(ParseError::HeaderNotFound)
.map(|x| x + HEADER_END_TOKEN.len());
let thumbnail_start =
find_window(buffer, THUMBNAIL_START_TOKEN).ok_or(ParseError::ThumbnailNotFound);
let thumbnail_end = find_window(buffer, THUMBNAIL_END_TOKEN)
.ok_or(ParseError::ThumbnailNotFound)
.map(|x| x + THUMBNAIL_END_TOKEN.len());
let mut header_xml = Vec::new();
let mut challenge_header = Err(ParseError::HeaderNotFound);
let mut replay_header = Err(ParseError::HeaderNotFound);
let hs = *header_start.as_ref().unwrap_or(&0);
let he = *header_end.as_ref().unwrap_or(&0);
if header_start.is_ok() && header_end.is_ok() {
header_xml.extend_from_slice(&buffer[hs..he]);
challenge_header = parse_challenge_header_xml(&buffer[hs..he]);
replay_header = parse_replay_xml(&buffer[hs..he])
}
let header_xml = String::from_utf8(header_xml).unwrap();
let thumbnail = if let (Ok(ts), Ok(te)) = (&thumbnail_start, &thumbnail_end) {
let mut thumbnail_data = Vec::new();
thumbnail_data.extend_from_slice(&buffer[*ts..*te]);
Some(JPEGData(thumbnail_data))
} else {
None
};
Ok(GBX {
origin: GBXOrigin::Buffer,
filesize: buffer.len(),
header_length: he - hs,
header_start: hs,
thumbnail_length: if let (Ok(te), Ok(ts)) = (&thumbnail_end, &thumbnail_start) {
Some(*te - *ts)
} else {
None
},
thumbnail_start: thumbnail_start.ok(),
thumbnail,
challenge_header: challenge_header.ok(),
replay_header: replay_header.ok(),
header_xml,
bin_header: binary_header,
})
}