diff --git a/Cargo.toml b/Cargo.toml index 6ee57ca..8591583 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Matt Bilker "] [dependencies] byteorder = "1.2.3" encoding = "0.2" +failure = "0.1.1" lazy_static = "1.0.0" log = "0.4.1" minidom = "0.9.0" diff --git a/src/bin/kbinxml.rs b/src/bin/kbinxml.rs index 2a55969..99b69af 100644 --- a/src/bin/kbinxml.rs +++ b/src/bin/kbinxml.rs @@ -1,3 +1,4 @@ +extern crate failure; extern crate kbinxml; extern crate pretty_env_logger; extern crate quick_xml; @@ -6,9 +7,24 @@ use std::env; use std::fs::File; use std::io::{Cursor, Error as IoError, ErrorKind as IoErrorKind, Read, Write, stdout}; +use failure::Fail; use kbinxml::KbinXml; use quick_xml::Writer; +fn display_err(err: impl Fail) -> IoError { + let mut fail: &Fail = &err; + while let Some(cause) = fail.cause() { + eprintln!("Cause: {}", cause); + fail = cause; + } + + if let Some(backtrace) = err.cause().and_then(|cause| cause.backtrace()) { + eprintln!("{}", backtrace); + } + + IoError::new(IoErrorKind::Other, "Error parsing kbin") +} + fn main() -> std::io::Result<()> { pretty_env_logger::init(); @@ -19,7 +35,7 @@ fn main() -> std::io::Result<()> { let mut contents = Vec::new(); file.read_to_end(&mut contents)?; - let element = KbinXml::from_binary(&contents); + let element = KbinXml::from_binary(&contents).map_err(display_err)?; //println!("element: {:#?}", element); let inner = Cursor::new(Vec::new()); diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..de6ca1d --- /dev/null +++ b/src/error.rs @@ -0,0 +1,57 @@ +use std::fmt; + +use failure::{Backtrace, Context, Fail}; + +#[derive(Debug)] +pub struct KbinError { + inner: Context, +} + +#[derive(Debug, Fail)] +pub enum KbinErrorKind { + #[fail(display = "Unable to read data")] + DataRead, + + #[fail(display = "Unable to read data size")] + DataReadSize, + + #[fail(display = "Unable to read 1 byte data")] + DataReadOneByte, + + #[fail(display = "Unable to read 2 byte data")] + DataReadTwoByte, + + #[fail(display = "Unable to read aligned data from data buffer")] + DataReadAligned, + + #[fail(display = "Unable to seek data buffer")] + Seek, +} + +impl fmt::Display for KbinError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +impl Fail for KbinError { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl From for KbinError { + fn from(kind: KbinErrorKind) -> KbinError { + KbinError { inner: Context::new(kind) } + } +} + +impl From> for KbinError { + fn from(inner: Context) -> KbinError { + KbinError { inner } + } +} diff --git a/src/lib.rs b/src/lib.rs index 86031cc..5505f3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ extern crate encoding; extern crate minidom; extern crate num; +#[macro_use] extern crate failure; #[macro_use] extern crate lazy_static; #[macro_use] extern crate log; @@ -13,10 +14,12 @@ use std::fmt::Write; use std::io::{Cursor, Read, Seek, SeekFrom}; use byteorder::{BigEndian, ReadBytesExt}; +use failure::ResultExt; use minidom::Element; mod compression; mod encoding_type; +mod error; mod node_types; mod sixbit; @@ -25,6 +28,8 @@ use encoding_type::EncodingType; use node_types::KbinType; use sixbit::unpack_sixbit; +pub use error::{KbinError, KbinErrorKind}; + const SIGNATURE: u8 = 0xA0; const SIG_COMPRESSED: u8 = 0x42; @@ -52,19 +57,19 @@ impl KbinXml { data_buf.position() } - fn data_buf_read(&mut self, data_buf: &mut Cursor<&[u8]>) -> Vec { - let size = data_buf.read_u32::().expect("Unable to read data size"); + fn data_buf_read(&mut self, data_buf: &mut Cursor<&[u8]>) -> Result, KbinError> { + let size = data_buf.read_u32::().context(KbinErrorKind::DataReadSize)?; let mut data = vec![0; size as usize]; - data_buf.read_exact(&mut data).expect("Unable to read data"); + data_buf.read_exact(&mut data).context(KbinErrorKind::DataRead)?; trace!("data_buf_read => size: {}, data: 0x{:02x?}", data.len(), data); - self.data_buf_realign(data_buf, None); + self.data_buf_realign(data_buf, None)?; - data + Ok(data) } - fn data_buf_read_str(&mut self, data_buf: &mut Cursor<&[u8]>, encoding: EncodingType) -> String { - let mut data = self.data_buf_read(data_buf); + fn data_buf_read_str(&mut self, data_buf: &mut Cursor<&[u8]>, encoding: EncodingType) -> Result { + let mut data = self.data_buf_read(data_buf)?; // Remove trailing null bytes let mut index = data.len() - 1; @@ -74,17 +79,17 @@ impl KbinXml { data.truncate(index + 1); trace!("data_buf_read_str => size: {}, data: 0x{:02x?}", data.len(), data); - encoding.decode_bytes(data) + Ok(encoding.decode_bytes(data)) } - fn data_buf_get(&mut self, data_buf: &mut Cursor<&[u8]>, size: u32) -> Vec { + fn data_buf_get(&mut self, data_buf: &mut Cursor<&[u8]>, size: u32) -> Result, KbinError> { let mut data = vec![0; size as usize]; - data_buf.read_exact(&mut data).expect("Unable to read data"); + data_buf.read_exact(&mut data).context(KbinErrorKind::DataRead)?; - data + Ok(data) } - fn data_buf_get_aligned(&mut self, data_buf: &mut Cursor<&[u8]>, data_type: KbinType) -> Vec { + fn data_buf_get_aligned(&mut self, data_buf: &mut Cursor<&[u8]>, data_type: KbinType) -> Result, KbinError> { if self.offset_1 % 4 == 0 { self.offset_1 = self.data_buf_offset(data_buf); } @@ -97,26 +102,26 @@ impl KbinXml { trace!("data_buf_get_aligned => old_pos: {}, size: {}", old_pos, size); let (check_old, data) = match size { 1 => { - data_buf.seek(SeekFrom::Start(self.offset_1)).expect("Unable to seek data buffer"); + data_buf.seek(SeekFrom::Start(self.offset_1)).context(KbinErrorKind::Seek)?; - let data = data_buf.read_u8().expect("Unable to read 1 byte data"); + let data = data_buf.read_u8().context(KbinErrorKind::DataReadOneByte)?; self.offset_1 += 1; (true, vec![data]) }, 2 => { - data_buf.seek(SeekFrom::Start(self.offset_2)).expect("Unable to seek data buffer"); + data_buf.seek(SeekFrom::Start(self.offset_2)).context(KbinErrorKind::Seek)?; let mut data = vec![0; 2]; - data_buf.read_exact(&mut data).expect("Unable to read 2 byte data"); + data_buf.read_exact(&mut data).context(KbinErrorKind::DataReadTwoByte)?; self.offset_2 += 2; (true, data) }, size => { let mut data = vec![0; size as usize]; - data_buf.read_exact(&mut data).expect("Unable to read aligned data from data buffer"); - self.data_buf_realign(data_buf, None); + data_buf.read_exact(&mut data).context(KbinErrorKind::DataReadAligned)?; + self.data_buf_realign(data_buf, None)?; (false, data) }, @@ -124,30 +129,32 @@ impl KbinXml { if check_old { - data_buf.seek(SeekFrom::Start(old_pos)).expect("Unable to seek data buffer"); + data_buf.seek(SeekFrom::Start(old_pos)).context(KbinErrorKind::Seek)?; let trailing = max(self.offset_1, self.offset_2); trace!("data_buf_get_aligned => old_pos: {}, trailing: {}", old_pos, trailing); if old_pos < trailing { - data_buf.seek(SeekFrom::Start(trailing)).expect("Unable to seek data buffer"); - self.data_buf_realign(data_buf, None); + data_buf.seek(SeekFrom::Start(trailing)).context(KbinErrorKind::Seek)?; + self.data_buf_realign(data_buf, None)?; } } - data + Ok(data) } - fn data_buf_realign(&mut self, data_buf: &mut Cursor<&[u8]>, size: Option) { + fn data_buf_realign(&mut self, data_buf: &mut Cursor<&[u8]>, size: Option) -> Result<(), KbinError> { let size = size.unwrap_or(4); trace!("data_buf_realign => position: {}, size: {}", data_buf.position(), size); while data_buf.position() % size > 0 { - data_buf.seek(SeekFrom::Current(1)).expect("Unable to seek data buffer"); + data_buf.seek(SeekFrom::Current(1)).context(KbinErrorKind::Seek)?; } trace!("data_buf_realign => realigned to: {}", data_buf.position()); + + Ok(()) } - fn from_binary_internal(&mut self, input: &[u8]) -> Element { + fn from_binary_internal(&mut self, input: &[u8]) -> Result { // Node buffer starts from the beginning. // Data buffer starts later after reading `len_data`. let mut node_buf = Cursor::new(&input[..]); @@ -228,7 +235,7 @@ impl KbinXml { if let Some(to) = stack.last_mut() { match xml_type { KbinType::Attribute => { - let val = self.data_buf_read_str(&mut data_buf, encoding); + let val = self.data_buf_read_str(&mut data_buf, encoding)?; debug!("attr name: {}, val: {}", name, val); to.set_attr(name, val); }, @@ -239,7 +246,7 @@ impl KbinXml { KbinType::String => { to.set_attr("__type", xml_type.name()); - let val = self.data_buf_read_str(&mut data_buf, encoding); + let val = self.data_buf_read_str(&mut data_buf, encoding)?; debug!("name: {}, val: {}", name, val); to.append_text_node(val); }, @@ -269,12 +276,12 @@ impl KbinXml { size); let data = if is_array { - let data = self.data_buf_get(&mut data_buf, size); - self.data_buf_realign(&mut data_buf, None); + let data = self.data_buf_get(&mut data_buf, size)?; + self.data_buf_realign(&mut data_buf, None)?; data } else { - self.data_buf_get_aligned(&mut data_buf, xml_type) + self.data_buf_get_aligned(&mut data_buf, xml_type)? }; debug!("data: 0x{:02x?}", data); @@ -303,10 +310,10 @@ impl KbinXml { warn!("stack: {:#?}", stack); } stack.truncate(1); - stack.pop().expect("Stack must have root node") + Ok(stack.pop().expect("Stack must have root node")) } - pub fn from_binary(input: &[u8]) -> Element { + pub fn from_binary(input: &[u8]) -> Result { let mut kbinxml = KbinXml::new(); kbinxml.from_binary_internal(input) }