mirror of
https://github.com/mbilker/kbinxml-rs.git
synced 2026-09-08 18:45:18 -05:00
lib: convert remaining functions to use failure
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use error::{KbinError, KbinErrorKind};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Compression {
|
||||
Compressed,
|
||||
@@ -5,11 +7,11 @@ pub enum Compression {
|
||||
}
|
||||
|
||||
impl Compression {
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
pub fn from_byte(byte: u8) -> Result<Self, KbinError> {
|
||||
match byte {
|
||||
0x42 => Some(Compression::Compressed),
|
||||
0x45 => Some(Compression::Uncompressed),
|
||||
_ => None,
|
||||
0x42 => Ok(Compression::Compressed),
|
||||
0x45 => Ok(Compression::Uncompressed),
|
||||
_ => Err(KbinErrorKind::UnknownCompression.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use error::{KbinError, KbinErrorKind};
|
||||
|
||||
use encoding::{DecoderTrap, Encoding};
|
||||
use encoding::all::{ASCII, EUC_JP, ISO_8859_1, WINDOWS_31J};
|
||||
|
||||
@@ -25,7 +27,7 @@ impl EncodingType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
pub fn from_byte(byte: u8) -> Result<Self, KbinError> {
|
||||
let val = match byte {
|
||||
0x00 => EncodingType::None,
|
||||
0x20 => EncodingType::ASCII,
|
||||
@@ -33,29 +35,31 @@ impl EncodingType {
|
||||
0x60 => EncodingType::EUC_JP,
|
||||
0x80 => EncodingType::SHIFT_JIS,
|
||||
0xA0 => EncodingType::UTF_8,
|
||||
_ => return None,
|
||||
_ => return Err(KbinErrorKind::UnknownEncoding.into()),
|
||||
};
|
||||
|
||||
Some(val)
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Decode bytes using the encoding definition from the `encoding` crate.
|
||||
///
|
||||
/// A `Some` value indicates an encoding should be used from the `encoding`
|
||||
/// crate. A `None` value indicates Rust's own UTF-8 handling should be used.
|
||||
pub fn decode_bytes(&self, input: Vec<u8>) -> String {
|
||||
const DECODER_FAIL: &str = "Unable to interpret string as alternate encoding";
|
||||
pub fn decode_bytes(&self, input: Vec<u8>) -> Result<String, KbinError> {
|
||||
let decoder_fail = |e| {
|
||||
format_err!("{}", e).context(KbinErrorKind::EncodingDecode)
|
||||
};
|
||||
|
||||
match *self {
|
||||
let result = match *self {
|
||||
EncodingType::None |
|
||||
EncodingType::UTF_8 => {
|
||||
String::from_utf8(input).expect("Unable to interpret string as UTF-8")
|
||||
},
|
||||
EncodingType::UTF_8 => String::from_utf8(input)?,
|
||||
|
||||
EncodingType::ASCII => ASCII.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL),
|
||||
EncodingType::ISO_8859_1 => ISO_8859_1.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL),
|
||||
EncodingType::EUC_JP => EUC_JP.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL),
|
||||
EncodingType::SHIFT_JIS => WINDOWS_31J.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL),
|
||||
}
|
||||
EncodingType::ASCII => ASCII.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?,
|
||||
EncodingType::ISO_8859_1 => ISO_8859_1.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?,
|
||||
EncodingType::EUC_JP => EUC_JP.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?,
|
||||
EncodingType::SHIFT_JIS => WINDOWS_31J.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
55
src/error.rs
55
src/error.rs
@@ -1,4 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use failure::{Backtrace, Context, Fail};
|
||||
|
||||
@@ -26,6 +27,54 @@ pub enum KbinErrorKind {
|
||||
|
||||
#[fail(display = "Unable to seek data buffer")]
|
||||
Seek,
|
||||
|
||||
#[fail(display = "Unable to read signature byte")]
|
||||
SignatureRead,
|
||||
|
||||
#[fail(display = "Unable to read compression byte")]
|
||||
CompressionRead,
|
||||
|
||||
#[fail(display = "Unknown compression value")]
|
||||
UnknownCompression,
|
||||
|
||||
#[fail(display = "Unable to read encoding byte")]
|
||||
EncodingRead,
|
||||
|
||||
#[fail(display = "Unable to read encoding negation byte")]
|
||||
EncodingNegationRead,
|
||||
|
||||
#[fail(display = "Unknown encoding")]
|
||||
UnknownEncoding,
|
||||
|
||||
#[fail(display = "Unable to read len_node")]
|
||||
LenNodeRead,
|
||||
|
||||
#[fail(display = "Unable to read len_data")]
|
||||
LenDataRead,
|
||||
|
||||
#[fail(display = "Unable to read node type")]
|
||||
NodeTypeRead,
|
||||
|
||||
#[fail(display = "Unable to read binary/string byte length")]
|
||||
BinaryLengthRead,
|
||||
|
||||
#[fail(display = "Unable to read array node length")]
|
||||
ArrayLengthRead,
|
||||
|
||||
#[fail(display = "Failed to write {} to output string", _0)]
|
||||
ByteParse(&'static str),
|
||||
|
||||
#[fail(display = "Unable to read sixbit string length")]
|
||||
SixbitLengthRead,
|
||||
|
||||
#[fail(display = "Unable to read sixbit string content")]
|
||||
SixbitRead,
|
||||
|
||||
#[fail(display = "Unable to interpret string as UTF-8")]
|
||||
Utf8,
|
||||
|
||||
#[fail(display = "Unable to interpret string as alternate encoding")]
|
||||
EncodingDecode,
|
||||
}
|
||||
|
||||
impl fmt::Display for KbinError {
|
||||
@@ -55,3 +104,9 @@ impl From<Context<KbinErrorKind>> for KbinError {
|
||||
KbinError { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for KbinError {
|
||||
fn from(inner: FromUtf8Error) -> KbinError {
|
||||
inner.context(KbinErrorKind::Utf8).into()
|
||||
}
|
||||
}
|
||||
|
||||
28
src/lib.rs
28
src/lib.rs
@@ -79,7 +79,7 @@ impl KbinXml {
|
||||
data.truncate(index + 1);
|
||||
trace!("data_buf_read_str => size: {}, data: 0x{:02x?}", data.len(), data);
|
||||
|
||||
Ok(encoding.decode_bytes(data))
|
||||
encoding.decode_bytes(data)
|
||||
}
|
||||
|
||||
fn data_buf_get(&mut self, data_buf: &mut Cursor<&[u8]>, size: u32) -> Result<Vec<u8>, KbinError> {
|
||||
@@ -159,25 +159,25 @@ impl KbinXml {
|
||||
// Data buffer starts later after reading `len_data`.
|
||||
let mut node_buf = Cursor::new(&input[..]);
|
||||
|
||||
let signature = node_buf.read_u8().expect("Unable to read signature byte");
|
||||
let signature = node_buf.read_u8().context(KbinErrorKind::SignatureRead)?;
|
||||
assert_eq!(signature, SIGNATURE);
|
||||
|
||||
// TODO: support uncompressed
|
||||
let compress_byte = node_buf.read_u8().expect("Unable to read compression byte");
|
||||
let compress_byte = node_buf.read_u8().context(KbinErrorKind::CompressionRead)?;
|
||||
assert_eq!(compress_byte, SIG_COMPRESSED);
|
||||
|
||||
let compressed = Compression::from_byte(compress_byte).expect("Unknown compression value");
|
||||
let compressed = Compression::from_byte(compress_byte)?;
|
||||
|
||||
let encoding_byte = node_buf.read_u8().expect("Unable to read encoding byte");
|
||||
let encoding_negation = node_buf.read_u8().expect("Unable to read encoding negation byte");
|
||||
let encoding = EncodingType::from_byte(encoding_byte).expect("Unknown encoding");
|
||||
let encoding_byte = node_buf.read_u8().context(KbinErrorKind::EncodingRead)?;
|
||||
let encoding_negation = node_buf.read_u8().context(KbinErrorKind::EncodingNegationRead)?;
|
||||
let encoding = EncodingType::from_byte(encoding_byte)?;
|
||||
assert_eq!(encoding_negation, 0xFF ^ encoding_byte);
|
||||
|
||||
info!("signature: 0x{:x}", signature);
|
||||
info!("compression: 0x{:x} ({:?})", compress_byte, compressed);
|
||||
info!("encoding: 0x{:x} ({:?})", encoding_byte, encoding);
|
||||
|
||||
let len_node = node_buf.read_u32::<BigEndian>().expect("Unable to read len_node");
|
||||
let len_node = node_buf.read_u32::<BigEndian>().context(KbinErrorKind::LenNodeRead)?;
|
||||
info!("len_node: {} (0x{:x})", len_node, len_node);
|
||||
|
||||
// We have read 8 bytes so far, so offset the start of the data buffer from
|
||||
@@ -192,14 +192,14 @@ impl KbinXml {
|
||||
trace!("offset_1: {}, offset_2: {}", self.offset_1, self.offset_2);
|
||||
}
|
||||
|
||||
let len_data = data_buf.read_u32::<BigEndian>().expect("Unable to read len_data");
|
||||
let len_data = data_buf.read_u32::<BigEndian>().context(KbinErrorKind::LenDataRead)?;
|
||||
info!("len_data: {} (0x{:x})", len_data, len_data);
|
||||
|
||||
let mut stack: Vec<Element> = Vec::new();
|
||||
{
|
||||
let node_buf_end = data_buf_start.into();
|
||||
while node_buf.position() < node_buf_end {
|
||||
let raw_node_type = node_buf.read_u8().expect("Unable to read node type");
|
||||
let raw_node_type = node_buf.read_u8().context(KbinErrorKind::NodeTypeRead)?;
|
||||
let is_array = raw_node_type & 64 == 64;
|
||||
let node_type = raw_node_type & !64;
|
||||
|
||||
@@ -224,7 +224,7 @@ impl KbinXml {
|
||||
_ => {},
|
||||
};
|
||||
|
||||
let name = unpack_sixbit(&mut node_buf);
|
||||
let name = unpack_sixbit(&mut node_buf)?;
|
||||
|
||||
if xml_type == KbinType::NodeStart {
|
||||
stack.push(Element::bare(name));
|
||||
@@ -256,10 +256,10 @@ impl KbinXml {
|
||||
let type_size = xml_type.size();
|
||||
let type_count = xml_type.count();
|
||||
let (is_array, size) = if type_count == -1 {
|
||||
(true, data_buf.read_u32::<BigEndian>().expect("Unable to read binary/string byte length"))
|
||||
(true, data_buf.read_u32::<BigEndian>().context(KbinErrorKind::BinaryLengthRead)?)
|
||||
} else if is_array {
|
||||
let node_size = type_size * type_count;
|
||||
let arr_count = data_buf.read_u32::<BigEndian>().expect("Unable to read array node length") / node_size as u32;
|
||||
let arr_count = data_buf.read_u32::<BigEndian>().context(KbinErrorKind::ArrayLengthRead)? / node_size as u32;
|
||||
to.set_attr("__count", arr_count);
|
||||
|
||||
let size = (node_size as u32) * arr_count;
|
||||
@@ -296,7 +296,7 @@ impl KbinXml {
|
||||
debug!("name: {}, string: {}", name, val);
|
||||
to.append_text_node(val);
|
||||
} else {
|
||||
let inner_value = xml_type.parse_bytes(&data);
|
||||
let inner_value = xml_type.parse_bytes(&data)?;
|
||||
debug!("name: {}, string: {}", name, inner_value);
|
||||
to.append_text_node(inner_value);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
use error::{KbinError, KbinErrorKind};
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
use failure::ResultExt;
|
||||
|
||||
trait KbinWrapperType<T> {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]);
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError>;
|
||||
}
|
||||
|
||||
macro_rules! number_impl {
|
||||
(integer; $($inner_type:ident),*) => {
|
||||
$(
|
||||
impl KbinWrapperType<$inner_type> for $inner_type {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> {
|
||||
trace!("KbinWrapperType<{}> => input: {:02x?}", stringify!($inner_type), input);
|
||||
|
||||
let mut data = [0; ::std::mem::size_of::<$inner_type>()];
|
||||
data.clone_from_slice(input);
|
||||
write!(output, "{}", $inner_type::from_be($inner_type::from_bytes(data)))
|
||||
.expect(concat!("Failed to write ", stringify!($inner_type), " to output string"));
|
||||
.context(KbinErrorKind::ByteParse(stringify!($inner_type)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
)*
|
||||
@@ -22,7 +28,7 @@ macro_rules! number_impl {
|
||||
(float; $($intermediate:ident => $inner_type:ident),*) => {
|
||||
$(
|
||||
impl KbinWrapperType<$inner_type> for $inner_type {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> {
|
||||
trace!("KbinWrapperType<{}> => input: {:02x?}", stringify!($inner_type), input);
|
||||
|
||||
let mut data = [0; ::std::mem::size_of::<$inner_type>()];
|
||||
@@ -30,7 +36,9 @@ macro_rules! number_impl {
|
||||
let bits = $intermediate::from_be($intermediate::from_bytes(data));
|
||||
|
||||
write!(output, "{:.6}", $inner_type::from_bits(bits))
|
||||
.expect(concat!("Failed to write ", stringify!($inner_type), " to output string"));
|
||||
.context(KbinErrorKind::ByteParse(stringify!($inner_type)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
)*
|
||||
@@ -41,7 +49,7 @@ number_impl!(integer; i8, u8, i16, u16, i32, u32, i64, u64);
|
||||
number_impl!(float; u32 => f32, u64 => f64);
|
||||
|
||||
impl KbinWrapperType<bool> for bool {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> {
|
||||
trace!("KbinWrapperType<bool> => input: {:02x?}", input);
|
||||
|
||||
let value = match input[0] {
|
||||
@@ -50,12 +58,14 @@ impl KbinWrapperType<bool> for bool {
|
||||
v => panic!("Unsupported value for boolean: {}", v),
|
||||
};
|
||||
output.push_str(value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Ip4;
|
||||
impl KbinWrapperType<Ip4> for Ip4 {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) {
|
||||
fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> {
|
||||
trace!("KbinWrapperType<Ip4> => input: {:02x?}", input);
|
||||
|
||||
if input.len() < 4 {
|
||||
@@ -63,18 +73,20 @@ impl KbinWrapperType<Ip4> for Ip4 {
|
||||
}
|
||||
|
||||
write!(output, "{}.{}.{}.{}", input[0], input[1], input[2], input[3])
|
||||
.expect("Failed to write IP address to output string");
|
||||
.context(KbinErrorKind::ByteParse("Ip4"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyConverter;
|
||||
impl KbinWrapperType<DummyConverter> for DummyConverter {
|
||||
fn from_kbin_bytes(_output: &mut String, _input: &[u8]) {}
|
||||
fn from_kbin_bytes(_output: &mut String, _input: &[u8]) -> Result<(), KbinError> { Ok(()) }
|
||||
}
|
||||
|
||||
struct InvalidConverter;
|
||||
impl KbinWrapperType<InvalidConverter> for InvalidConverter {
|
||||
fn from_kbin_bytes(_output: &mut String, input: &[u8]) {
|
||||
fn from_kbin_bytes(_output: &mut String, input: &[u8]) -> Result<(), KbinError> {
|
||||
panic!("Invalid kbin type converter called for input: {:02x?}", input);
|
||||
}
|
||||
}
|
||||
@@ -135,12 +147,12 @@ macro_rules! construct_types {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_array<T>(output: &mut String, input: &[u8], size: usize, count: usize, arr_count: usize)
|
||||
fn parse_array<T>(output: &mut String, input: &[u8], size: usize, count: usize, arr_count: usize) -> Result<(), KbinError>
|
||||
where T: KbinWrapperType<T>
|
||||
{
|
||||
{
|
||||
let first = &input[..size];
|
||||
T::from_kbin_bytes(output, first);
|
||||
T::from_kbin_bytes(output, first)?;
|
||||
}
|
||||
|
||||
let total_nodes = count * arr_count;
|
||||
@@ -149,11 +161,13 @@ macro_rules! construct_types {
|
||||
let end = (i + 1) * size;
|
||||
let data = &input[offset..end];
|
||||
output.push(' ');
|
||||
T::from_kbin_bytes(output, data);
|
||||
T::from_kbin_bytes(output, data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_bytes_inner<T>(&self, input: &[u8], name: &str, size: u8, count: i8) -> String
|
||||
fn parse_bytes_inner<T>(&self, input: &[u8], name: &str, size: u8, count: i8) -> Result<String, KbinError>
|
||||
where T: KbinWrapperType<T>
|
||||
{
|
||||
let type_size = (size as usize) * (count as usize);
|
||||
@@ -170,20 +184,20 @@ macro_rules! construct_types {
|
||||
// May have a node (i.e. Ip4) that is only a single count, but it
|
||||
// can be part of an array
|
||||
if arr_count == 1 {
|
||||
T::from_kbin_bytes(&mut result, input);
|
||||
T::from_kbin_bytes(&mut result, input)?;
|
||||
} else {
|
||||
Self::parse_array::<T>(&mut result, input, size as usize, count as usize, arr_count);
|
||||
Self::parse_array::<T>(&mut result, input, size as usize, count as usize, arr_count)?;
|
||||
}
|
||||
} else if count > 1 {
|
||||
Self::parse_array::<T>(&mut result, input, size as usize, count as usize, arr_count);
|
||||
Self::parse_array::<T>(&mut result, input, size as usize, count as usize, arr_count)?;
|
||||
} else {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn parse_bytes(&self, input: &[u8]) -> String {
|
||||
pub fn parse_bytes(&self, input: &[u8]) -> Result<String, KbinError> {
|
||||
match *self {
|
||||
$(
|
||||
KbinType::$konst => self.parse_bytes_inner::<$inner_type>(input, $name, $size, $count),
|
||||
|
||||
@@ -2,8 +2,11 @@ use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use failure::ResultExt;
|
||||
use num::{BigUint, FromPrimitive, ToPrimitive};
|
||||
|
||||
use error::{KbinError, KbinErrorKind};
|
||||
|
||||
static CHAR_MAP: &'static [u8] = b"0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
lazy_static! {
|
||||
@@ -42,10 +45,10 @@ pub fn pack_sixbit<T>(writer: &mut T, input: &str)
|
||||
writer.write_uint::<BigEndian>(bits, (input.len() * 6 + padding) / 8).expect("Unable to write sixbit contents");
|
||||
}
|
||||
|
||||
pub fn unpack_sixbit<T>(reader: &mut T) -> String
|
||||
pub fn unpack_sixbit<T>(reader: &mut T) -> Result<String, KbinError>
|
||||
where T: Read
|
||||
{
|
||||
let len = reader.read_u8().expect("Unable to read sixbit string length");
|
||||
let len = reader.read_u8().context(KbinErrorKind::SixbitLengthRead)?;
|
||||
let real_len = (f32::from(len * 6) / 8f32).ceil();
|
||||
let real_len = (real_len as u32) as usize;
|
||||
let padding = (8 - ((len * 6) % 8)) as usize;
|
||||
@@ -53,7 +56,7 @@ pub fn unpack_sixbit<T>(reader: &mut T) -> String
|
||||
debug!("sixbit_len: {}, real_len: {}, padding: {}", len, real_len, padding);
|
||||
|
||||
let mut buf = vec![0; real_len];
|
||||
reader.read_exact(&mut buf).expect("Unable to read sixbit string content");
|
||||
reader.read_exact(&mut buf).context(KbinErrorKind::SixbitRead)?;
|
||||
|
||||
let bits = BigUint::from_bytes_be(&buf);
|
||||
let bits = bits >> padding;
|
||||
@@ -76,5 +79,5 @@ pub fn unpack_sixbit<T>(reader: &mut T) -> String
|
||||
}).collect();
|
||||
|
||||
debug!("result: {}", result);
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user