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,
})
}