Add conversion methods between chunk data

master
Trivernis 5 years ago
parent 273497cf40
commit bbe228707e

@ -1,17 +1,21 @@
use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; use byteorder::{BigEndian, ByteOrder};
use rand::AsByteSliceMut;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::fs::File; use std::fs::File;
use std::io::{BufReader, Read}; use std::io::{BufReader, Read};
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
pub const BDF_HDR: &[u8; 11] = b"BDF\x01RAINBOW"; pub const BDF_HDR: &[u8; 11] = b"BDF\x01RAINBOW";
pub const NULL_BYTES: &[u8; 4] = &[0u8; 4]; pub const NULL_BYTES: &[u8; 4] = &[0u8; 4];
pub const META_CHUNK_NAME: &str = "META";
pub const HTBL_CHUNK_NAME: &str = "HTBL";
pub const DTBL_CHUNK_NAME: &str = "DTBL";
pub struct BinaryDictionaryFile { pub struct BinaryDictionaryFile {
name: String, name: String,
reader: BufReader<File>, reader: BufReader<File>,
metadata: Option<MetaChunk>, metadata: Option<MetaChunk>,
lookup_table: Option<HashLookupTable>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -30,6 +34,11 @@ pub struct MetaChunk {
compression_method: Option<String>, compression_method: Option<String>,
} }
#[derive(Debug, Clone)]
pub struct HashLookupTable {
entries: HashMap<u32, HashEntry>,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct HashEntry { pub struct HashEntry {
id: u32, id: u32,
@ -44,22 +53,48 @@ pub struct DataEntry {
} }
impl BinaryDictionaryFile { impl BinaryDictionaryFile {
fn new(reader: BufReader<File>) -> Self { pub fn new(reader: BufReader<File>) -> Self {
Self { Self {
name: "".to_string(), name: "".to_string(),
metadata: None, metadata: None,
lookup_table: None,
reader, reader,
} }
} }
fn read_metadata(&mut self) -> Result<MetaChunk, Error> { pub fn read_metadata(&mut self) -> Result<&MetaChunk, Error> {
if !self.validate_header() { if !self.validate_header() {
return Err(Error::new(ErrorKind::InvalidData, "Invalid BDF Header!")); return Err(Error::new(ErrorKind::InvalidData, "invalid BDF Header"));
}
let meta_chunk: MetaChunk = self.next_chunk()?.try_into()?;
self.metadata = Some(meta_chunk);
if let Some(chunk) = &self.metadata {
Ok(&chunk)
} else {
Err(Error::new(
ErrorKind::Other,
"Failed to read self assigned metadata.",
))
} }
let meta_chunk = self.next_chunk().as_meta_chunk(); }
self.metadata = Some(meta_chunk.clone());
pub fn read_lookup_table(&mut self) -> Result<&HashLookupTable, Error> {
match &self.metadata {
None => self.read_metadata()?,
Some(t) => t,
};
let lookup_table: HashLookupTable = self.next_chunk()?.try_into()?;
self.lookup_table = Some(lookup_table);
Ok(meta_chunk) if let Some(chunk) = &self.lookup_table {
Ok(&chunk)
} else {
Err(Error::new(
ErrorKind::Other,
"failed to read self assigned chunk",
))
}
} }
fn validate_header(&mut self) -> bool { fn validate_header(&mut self) -> bool {
@ -69,52 +104,109 @@ impl BinaryDictionaryFile {
header == BDF_HDR.as_ref() header == BDF_HDR.as_ref()
} }
fn next_chunk(&mut self) -> GenericChunk { /// Returns the next chunk if one is available.
pub fn next_chunk(&mut self) -> Result<GenericChunk, Error> {
let mut length_raw = [0u8; 4]; let mut length_raw = [0u8; 4];
let _ = self.reader.read(&mut length_raw); let _ = self.reader.read_exact(&mut length_raw)?;
let length = BigEndian::read_u32(&mut length_raw); let length = BigEndian::read_u32(&mut length_raw);
let mut name_raw = [0u8; 4]; let mut name_raw = [0u8; 4];
let _ = self.reader.read(&mut name_raw); let _ = self.reader.read_exact(&mut name_raw)?;
let name = let name = String::from_utf8(name_raw.to_vec()).expect("Failed to parse name string.");
String::from_utf8(name_raw.to_vec()).expect("Failed to parse chunk name to string!");
let mut data = vec![0u8; length as usize]; let mut data = vec![0u8; length as usize];
let _ = self.reader.read(&mut data); let _ = self.reader.read_exact(&mut data)?;
let mut crc_raw = [0u8; 4]; let mut crc_raw = [0u8; 4];
let _ = self.reader.read(&mut crc_raw); let _ = self.reader.read_exact(&mut crc_raw)?;
let crc = BigEndian::read_u32(&mut crc_raw); let crc = BigEndian::read_u32(&mut crc_raw);
GenericChunk { Ok(GenericChunk {
length, length,
name, name,
data, data,
crc, crc,
} })
} }
} }
impl GenericChunk { impl TryFrom<GenericChunk> for MetaChunk {
fn as_meta_chunk(&self) -> MetaChunk { type Error = Error;
let mut chunk_count_raw = self.data[0..4].to_vec();
let mut entries_per_chunk = self.data[4..8].to_vec(); fn try_from(chunk: GenericChunk) -> Result<MetaChunk, Error> {
let mut total_number_of_entries = self.data[8..12].to_vec(); if &chunk.name != HTBL_CHUNK_NAME {
let mut compression_method_raw = self.data[12..16].to_vec(); return Err(Error::new(
let chunk_count = BigEndian::read_u32(&mut chunk_count_raw); ErrorKind::InvalidData,
let entries_per_chunk = BigEndian::read_u32(&mut entries_per_chunk); "chunk name doesn't match",
let entry_count = BigEndian::read_u32(&mut total_number_of_entries); ));
}
if chunk.data.len() < 16 {
return Err(Error::new(ErrorKind::InvalidData, "invalid chunk data"));
}
let chunk_count_raw = &chunk.data[0..4];
let entries_per_chunk = &chunk.data[4..8];
let total_number_of_entries = &chunk.data[8..12];
let compression_method_raw = chunk.data[12..16].to_vec();
let chunk_count = BigEndian::read_u32(chunk_count_raw);
let entries_per_chunk = BigEndian::read_u32(entries_per_chunk);
let entry_count = BigEndian::read_u32(total_number_of_entries);
let compression_method = if &compression_method_raw != NULL_BYTES { let compression_method = if &compression_method_raw != NULL_BYTES {
Some( Some(
String::from_utf8(compression_method_raw.to_vec()) String::from_utf8(compression_method_raw)
.expect("Failed to parse compression method from meta string"), .expect("Failed to parse compression method name!"),
) )
} else { } else {
None None
}; };
MetaChunk { Ok(MetaChunk {
chunk_count, chunk_count,
entries_per_chunk, entries_per_chunk,
entry_count, entry_count,
compression_method, compression_method,
})
}
}
impl HashLookupTable {
pub fn get_entry(&self, id: u32) -> Option<&HashEntry> {
self.entries.get(&id)
}
}
impl TryFrom<GenericChunk> for HashLookupTable {
type Error = Error;
fn try_from(chunk: GenericChunk) -> Result<HashLookupTable, Error> {
if &chunk.name != HTBL_CHUNK_NAME {
return Err(Error::new(
ErrorKind::InvalidData,
"chunk name doesn't match",
));
}
let mut hash_entries: HashMap<u32, HashEntry> = HashMap::new();
let mut position = 0;
while chunk.data.len() > (position + 12) {
let id_raw = &chunk.data[position..position + 4];
position += 4;
let output_length_raw = &chunk.data[position..position + 4];
position += 4;
let name_length_raw = &chunk.data[position..position + 4];
position += 4;
let id = BigEndian::read_u32(id_raw);
let output_length = BigEndian::read_u32(output_length_raw);
let name_length = BigEndian::read_u32(name_length_raw);
let name_raw = &chunk.data[position..position + name_length as usize];
let name =
String::from_utf8(name_raw.to_vec()).expect("Failed to parse hash function name!");
hash_entries.insert(
id,
HashEntry {
id,
output_length,
name,
},
);
} }
Ok(HashLookupTable {
entries: hash_entries,
})
} }
} }

Loading…
Cancel
Save