1pub mod challenge;
5pub mod replay;
6
7use self::challenge::parse_challenge_header_xml;
8use self::replay::parse_replay_xml;
9
10use super::*;
11
12use std::convert::TryInto;
13use std::io;
14use std::io::Read;
15use std::{fs::File, num::ParseIntError};
16
17const HEADER_START_TOKEN: &[u8] = "<header ".as_bytes();
18const HEADER_END_TOKEN: &[u8] = "</header>".as_bytes();
19
20const THUMBNAIL_START_TOKEN: &[u8] = &[0xFF, 0xD8, 0xFF];
21const THUMBNAIL_END_TOKEN: &[u8] = &[0xFF, 0xD9];
22
23#[derive(Debug)]
24pub enum ParseError {
25 MissingGBXMagic,
26 FileTooShort,
27 HeaderNotFound,
28 ThumbnailNotFound,
29 XMLParseError(xml::reader::Error),
30 HeaderValueError(ParseIntError),
31 HeaderTryIntoEnumError(String),
32 IOError(io::Error),
33 Unknown,
34}
35
36fn find_window(buf: &[u8], needle: &[u8]) -> Option<usize> {
38 buf.windows(needle.len()).position(|w| w == needle)
39}
40
41pub fn parse_from_file(filename: &str) -> Result<GBX, ParseError> {
45 let mut buffer = Vec::new();
46 let mut f = File::open(filename).map_err(ParseError::IOError)?;
47 f.read_to_end(&mut buffer).map_err(ParseError::IOError)?;
48 let mut gbx = parse_from_buffer(&buffer)?;
49 gbx.origin = GBXOrigin::File {
50 path: String::from(filename),
51 };
52 Ok(gbx)
53}
54
55pub fn parse_from_buffer(buffer: &[u8]) -> Result<GBX, ParseError> {
63 if buffer.len() < 3 {
64 return Err(ParseError::FileTooShort);
65 }
66
67 if &buffer[0..3] != b"GBX" {
68 return Err(ParseError::MissingGBXMagic);
69 }
70
71 let binary_header = GBXBinaryHeader {
72 version: u16::from_le_bytes((&buffer[3..5]).try_into().unwrap()),
73 class_id: u32::from_le_bytes((&buffer[9..13]).try_into().unwrap()),
74 };
75
76 let header_start = find_window(buffer, HEADER_START_TOKEN).ok_or(ParseError::HeaderNotFound);
77 let header_end = find_window(buffer, HEADER_END_TOKEN)
78 .ok_or(ParseError::HeaderNotFound)
79 .map(|x| x + HEADER_END_TOKEN.len());
80
81 let thumbnail_start =
82 find_window(buffer, THUMBNAIL_START_TOKEN).ok_or(ParseError::ThumbnailNotFound);
83 let thumbnail_end = find_window(buffer, THUMBNAIL_END_TOKEN)
84 .ok_or(ParseError::ThumbnailNotFound)
85 .map(|x| x + THUMBNAIL_END_TOKEN.len());
86
87 let mut header_xml = Vec::new();
88 let mut challenge_header = Err(ParseError::HeaderNotFound);
89 let mut replay_header = Err(ParseError::HeaderNotFound);
90
91 let hs = *header_start.as_ref().unwrap_or(&0);
92 let he = *header_end.as_ref().unwrap_or(&0);
93 if header_start.is_ok() && header_end.is_ok() {
94 header_xml.extend_from_slice(&buffer[hs..he]);
95 challenge_header = parse_challenge_header_xml(&buffer[hs..he]);
96 replay_header = parse_replay_xml(&buffer[hs..he])
97 }
98 let header_xml = String::from_utf8(header_xml).unwrap();
99
100 let thumbnail = if let (Ok(ts), Ok(te)) = (&thumbnail_start, &thumbnail_end) {
101 let mut thumbnail_data = Vec::new();
102 thumbnail_data.extend_from_slice(&buffer[*ts..*te]);
103 Some(JPEGData(thumbnail_data))
104 } else {
105 None
106 };
107
108 Ok(GBX {
109 origin: GBXOrigin::Buffer,
110 filesize: buffer.len(),
111 header_length: he - hs,
112 header_start: hs,
113 thumbnail_length: if let (Ok(te), Ok(ts)) = (&thumbnail_end, &thumbnail_start) {
114 Some(*te - *ts)
115 } else {
116 None
117 },
118 thumbnail_start: thumbnail_start.ok(),
119 thumbnail,
120 challenge_header: challenge_header.ok(),
121 replay_header: replay_header.ok(),
122 header_xml,
123 bin_header: binary_header,
124 })
125}