diff --git a/Cargo.toml b/Cargo.toml index f1961d7..29bf08c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,5 @@ num = "0.1.42" pretty_env_logger = "0.2.3" quick-xml = "0.12.1" rustc-hex = "1.0.0" +serde = "1.0.69" +serde_derive = "1.0.69" diff --git a/src/bin/kbinxml.rs b/src/bin/kbinxml.rs index 3c020d2..23e9670 100644 --- a/src/bin/kbinxml.rs +++ b/src/bin/kbinxml.rs @@ -6,15 +6,37 @@ extern crate minidom; extern crate pretty_env_logger; extern crate quick_xml; +#[macro_use] extern crate serde_derive; + use std::env; use std::fs::File; use std::io::{Cursor, Error as IoError, ErrorKind as IoErrorKind, Read, Write, stdout}; +use std::str; use failure::Fail; -use kbinxml::{KbinXml, Options}; +use kbinxml::{KbinXml, Options, to_bytes}; use minidom::Element; use quick_xml::Writer; +#[derive(Serialize)] +#[serde(rename = "test2")] +pub struct Testing2 { + hi: u16, + ho: i16, + vu: Vec, +} + +#[derive(Serialize)] +#[serde(rename = "test")] +pub struct Testing { + hi: u8, + ok: [u8; 3], + hhh: (u8, u8), + hhg: (u32, u32), + foo: String, + testing2: Testing2, +} + fn display_err(err: impl Fail) -> IoError { let mut fail: &Fail = &err; eprintln!("e: {}", err); @@ -97,19 +119,48 @@ fn main() -> std::io::Result<()> { let mut contents = Vec::new(); file.read_to_end(&mut contents)?; - let (element, encoding_original) = KbinXml::from_binary(&contents).map_err(display_err)?; - //println!("element: {:#?}", element); - let text_original = to_text(&element)?; - display_buf(&text_original)?; + if KbinXml::is_binary_xml(&contents) { + let (element, encoding_original) = KbinXml::from_binary(&contents).map_err(display_err)?; + //println!("element: {:#?}", element); + let text_original = to_text(&element)?; + display_buf(&text_original)?; - let options = Options::with_encoding(encoding_original); - let buf = KbinXml::to_binary_with_options(options, &element).map_err(display_err)?; - compare_slice(&buf, &contents); + let options = Options::with_encoding(encoding_original); + let buf = KbinXml::to_binary_with_options(options, &element).map_err(display_err)?; + compare_slice(&buf, &contents); + } else { + let contents = str::from_utf8(&contents).expect("Unable to interpret file contents as UTF-8"); + let element: Element = contents.parse().expect("Unable to construct DOM for input text XML"); + let options = Options::default(); + let buf = KbinXml::to_binary_with_options(options, &element).map_err(display_err)?; + eprintln!("data: {:02x?}", buf); + } + + /* let (element, encoding_new) = KbinXml::from_binary(&buf).map_err(display_err)?; let text_new = to_text(&element)?; assert_eq!(encoding_original, encoding_new); assert_eq!(text_original, text_new); + */ + } else { + let obj = Testing { + hi: 12, + ok: [12, 24, 48], + hhh: (55, 66), + hhg: (55, 66), + foo: "foobarbaz".to_string(), + testing2: Testing2 { + hi: 32423, + ho: 32000, + vu: vec![33, 255, 254], + }, + }; + let bytes = to_bytes(&obj).unwrap(); + eprintln!("bytes: {:02x?}", bytes); + + let mut file = File::create("testing.kbin")?; + file.write_all(&bytes)?; } Ok(()) } diff --git a/src/error.rs b/src/error.rs index aaba576..09c852f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -95,6 +95,9 @@ pub enum KbinErrorKind { #[fail(display = "Unable to convert from hexadecimal")] HexError, + + #[fail(display = "Missing type hint where one is required")] + MissingTypeHint, } impl fmt::Display for KbinError { diff --git a/src/lib.rs b/src/lib.rs index 138e126..1023678 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,11 +5,15 @@ extern crate encoding; extern crate minidom; extern crate num; extern crate rustc_hex; +extern crate serde; #[macro_use] extern crate failure; #[macro_use] extern crate lazy_static; #[macro_use] extern crate log; +#[cfg(test)] +#[macro_use] extern crate serde_derive; + use std::fmt::Write as FmtWrite; use std::io::{Cursor, Write}; @@ -26,6 +30,9 @@ mod node_types; mod options; mod sixbit; +//mod de; +mod ser; + use byte_buffer::{ByteBufferRead, ByteBufferWrite}; use compression::Compression; use node_types::StandardType; @@ -35,6 +42,7 @@ use sixbit::{pack_sixbit, unpack_sixbit}; pub use encoding_type::EncodingType; pub use error::{KbinError, KbinErrorKind, Result}; pub use options::Options; +pub use ser::to_bytes; const SIGNATURE: u8 = 0xA0; @@ -68,6 +76,10 @@ impl KbinXml { } } + pub fn is_binary_xml(input: &[u8]) -> bool { + input.len() > 2 && input[0] == SIGNATURE && input[1] == SIG_COMPRESSED + } + fn from_binary_internal(&mut self, stack: &mut Vec, input: &[u8]) -> Result<(Element, EncodingType)> { // Node buffer starts from the beginning. // Data buffer starts later after reading `len_data`. @@ -278,7 +290,7 @@ impl KbinXml { let size = (data.len() as u32) * (node_type.size as u32); data_buf.write_u32::(size).context(KbinErrorKind::DataWrite("binary node size"))?; - data_buf.write(&data).context(KbinErrorKind::DataWrite("binary"))?; + data_buf.write_all(&data).context(KbinErrorKind::DataWrite("binary"))?; data_buf.realign_writes(None)?; }, StandardType::String => { diff --git a/src/ser/error.rs b/src/ser/error.rs new file mode 100644 index 0000000..0bc7cbf --- /dev/null +++ b/src/ser/error.rs @@ -0,0 +1,59 @@ +use std::error::Error as StdError; +use std::fmt::{self, Display}; + +use failure::{Compat, Context, Fail}; +use serde::{de, ser}; + +use error::{KbinError, KbinErrorKind}; + +#[derive(Clone, Debug)] +pub enum Error { + Message(String), + + Wrapped(Compat), +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(StdError::description(self)) + } +} + +impl StdError for Error { + fn description(&self) -> &str { + match *self { + Error::Message(ref msg) => msg, + Error::Wrapped(ref err) => err.description(), + } + } +} + +impl From for Error { + fn from(inner: KbinError) -> Self { + Error::Wrapped(inner.compat()) + } +} + +impl From for Error { + fn from(inner: KbinErrorKind) -> Self { + Error::Wrapped(KbinError::from(inner).compat()) + } +} + +impl From> for Error { + fn from(inner: Context) -> Self { + Error::Wrapped(KbinError::from(inner).compat()) + } +} diff --git a/src/ser/mod.rs b/src/ser/mod.rs new file mode 100644 index 0000000..32f6eb3 --- /dev/null +++ b/src/ser/mod.rs @@ -0,0 +1,465 @@ +use std::io::{Cursor, Write}; +use std::mem; +use std::result::Result as StdResult; + +use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; +use failure::ResultExt; +use serde::ser::{self, Impossible, Serialize}; + +use byte_buffer::ByteBufferWrite; +use encoding_type::EncodingType; +use node_types::StandardType; +use sixbit::pack_sixbit; +use super::error::{KbinError, KbinErrorKind}; + +mod error; +mod tuple; + +use self::error::Error; +use self::tuple::Tuple; + +const SIGNATURE: u8 = 0xA0; + +const SIG_COMPRESSED: u8 = 0x42; + +const ARRAY_MASK: u8 = 1 << 6; // 1 << 6 = 64 + +pub type Result = StdResult; + +// Writing arrays should not be aligned after each write. Buffer realignment +// should be performed after Writing a single value. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum WriteMode { + Single, + Array, +} + +pub struct Serializer { + encoding: EncodingType, + + pub(crate) write_mode: WriteMode, + + pub(crate) node_buf: Cursor>, + pub(crate) data_buf: ByteBufferWrite, +} + +#[derive(Debug)] +pub struct TypeHint { + node_type: StandardType, + is_array: bool, + count: usize, +} + +impl TypeHint { + /* + fn new(node_type: StandardType, is_array: bool, count: usize) -> Self { + Self { node_type, is_array, count } + } + */ + + fn from_type(node_type: StandardType) -> Self { + Self { + node_type, + is_array: false, + count: 1, + } + } +} + +pub fn to_bytes(value: &T) -> Result> + where T: Serialize +{ + let mut serializer = Serializer { + encoding: EncodingType::SHIFT_JIS, + write_mode: WriteMode::Single, + node_buf: Cursor::new(Vec::new()), + data_buf: ByteBufferWrite::new(Vec::new()), + }; + value.serialize(&mut serializer)?; + + let output = serializer.finalize()?; + Ok(output) +} + +impl Serializer { + fn finalize(mut self) -> StdResult, KbinError> { + let mut header = Cursor::new(Vec::with_capacity(8)); + header.write_u8(SIGNATURE).context(KbinErrorKind::HeaderWrite("signature"))?; + header.write_u8(SIG_COMPRESSED).context(KbinErrorKind::HeaderWrite("compression"))?; + + let encoding = self.encoding.to_byte(); + header.write_u8(encoding).context(KbinErrorKind::HeaderWrite("encoding"))?; + header.write_u8(0xFF ^ encoding).context(KbinErrorKind::HeaderWrite("encoding negation"))?; + + self.node_buf.write_u8(StandardType::FileEnd.id | ARRAY_MASK).context(KbinErrorKind::DataWrite("file end"))?; + self.data_buf.realign_writes(None)?; + + let mut output = header.into_inner(); + + let node_buf = self.node_buf.into_inner(); + output.write_u32::(node_buf.len() as u32).context(KbinErrorKind::HeaderWrite("node buffer length"))?; + output.extend_from_slice(&node_buf); + + let data_buf = self.data_buf.into_inner(); + output.write_u32::(data_buf.len() as u32).context(KbinErrorKind::HeaderWrite("data buffer length"))?; + output.extend_from_slice(&data_buf); + + Ok(output) + } +} + +// `straight_impl` passes a single element array to `write_aligned` where +// `primitive_impl` will use `BigEndian` to populate a multi-element array for +// `write_aligned` +macro_rules! byte_impl { + ($inner_type:ident, $method:ident, $standard_type:ident $($cast:tt)*) => { + fn $method(self, value: $inner_type) -> Result { + debug!(concat!(stringify!($method), " => value: {}"), value); + + let node_type = StandardType::$standard_type; + match self.write_mode { + WriteMode::Single => { + let value = value $($cast)*; + self.data_buf.write_aligned(*node_type, &[value])?; + }, + WriteMode::Array => { + self.data_buf.write_u8(value $($cast)*).context(KbinErrorKind::DataWrite(node_type.name))?; + } + }; + + Ok(Some(TypeHint::from_type(node_type))) + } + } +} + +macro_rules! primitive_impl { + ($inner_type:ident, $method:ident, $write_method:ident, $standard_type:ident $($cast:tt)*) => { + fn $method(self, value: $inner_type) -> Result { + debug!(concat!(stringify!($method), " => value: {}"), value); + + let node_type = StandardType::$standard_type; + match self.write_mode { + WriteMode::Single => { + let mut buf = [0; ::std::mem::size_of::<$inner_type>()]; + BigEndian::$write_method(&mut buf, value); + self.data_buf.write_aligned(*node_type, &buf)?; + }, + WriteMode::Array => { + self.data_buf.$write_method::(value $($cast)*).context(KbinErrorKind::DataWrite(node_type.name))?; + } + }; + + Ok(Some(TypeHint::from_type(node_type))) + } + } +} + +impl<'a> ser::Serializer for &'a mut Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Tuple<'a>; + type SerializeTuple = Tuple<'a>; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Impossible; + + byte_impl!(bool, serialize_bool, Boolean as u8); + byte_impl!(u8, serialize_u8, U8); + byte_impl!(i8, serialize_i8, S8 as u8); + primitive_impl!(u16, serialize_u16, write_u16, U16); + primitive_impl!(i16, serialize_i16, write_i16, S16); + primitive_impl!(u32, serialize_u32, write_u32, U32); + primitive_impl!(i32, serialize_i32, write_i32, S32); + primitive_impl!(u64, serialize_u64, write_u64, U64); + primitive_impl!(i64, serialize_i64, write_i64, S64); + primitive_impl!(f32, serialize_f32, write_f32, Float); + primitive_impl!(f64, serialize_f64, write_f64, Double); + + /* + fn serialize_bool(self, value: bool) -> Result { + debug!("serialize_bool => value: {}", value); + let value = value as u8; + + let node_type = StandardType::Boolean; + match self.write_mode { + WriteMode::Single => self.data_buf.write_aligned(*node_type, &[value]) + .context(KbinErrorKind::DataWrite("bool"))?, + WriteMode::Array => self.data_buf.write_u8(value) + .context(KbinErrorKind::DataWrite("bool"))?, + }; + + Ok(TypeHint::from_type(node_type)) + } + + fn serialize_u8(self, value: u8) -> Result { + debug!("serialize_u8 => value: {}", value); + + let node_type = StandardType::U8; + match self.write_mode { + WriteMode::Single => self.data_buf.write_aligned(*node_type, &[value]) + .context(KbinErrorKind::DataWrite("bool"))?, + WriteMode::Array => self.data_buf.write_u8(value) + .context(KbinErrorKind::DataWrite("bool"))?, + }; + + Ok(TypeHint::from_type(node_type)) + } + + fn serialize_i8(self, value: i8) -> Result { + debug!("serialize_i8 => value: {}", value); + let hint = self.write(StandardType::S8, &[value as u8]).context(KbinErrorKind::DataWrite("i8"))?; + Ok(hint) + } + + fn serialize_u16(self, value: u16) -> Result { + debug!("serialize_u16 => value: {}", value); + self.data_buf.write_u16::(value).context(KbinErrorKind::DataWrite("u16"))?; + + Ok(TypeHint::from_type(StandardType::U16)) + } + + fn serialize_i16(self, value: i16) -> Result { + debug!("serialize_i16 => value: {}", value); + + let node_type = StandardType::S16; + match self.write_mode { + WriteMode::Single => { + let mut buf = [0; 2]; + BigEndian::write_i16(&mut buf, value); + self.data_buf.write_aligned(*node_type, &buf)?; + }, + WriteMode::Array => { + self.data_buf.write_i16::(value).context(KbinErrorKind::DataWrite(node_type.name))?; + } + }; + + Ok(TypeHint::from_type(node_type)) + } + + fn serialize_u32(self, value: u32) -> Result { + debug!("serialize_u32 => value: {}", value); + self.data_buf.write_u32::(value).context(KbinErrorKind::DataWrite("u32"))?; + + Ok(TypeHint::from_type(StandardType::U32)) + } + + fn serialize_i32(self, value: i32) -> Result { + debug!("serialize_i32 => value: {}", value); + self.data_buf.write_i32::(value).context(KbinErrorKind::DataWrite("i32"))?; + + Ok(TypeHint::from_type(StandardType::S32)) + } + + fn serialize_u64(self, value: u64) -> Result { + debug!("serialize_u64 => value: {}", value); + self.data_buf.write_u64::(value).context(KbinErrorKind::DataWrite("u64"))?; + + Ok(TypeHint::from_type(StandardType::U64)) + } + + fn serialize_i64(self, value: i64) -> Result { + debug!("serialize_i64 => value: {}", value); + self.data_buf.write_i64::(value).context(KbinErrorKind::DataWrite("i64"))?; + + Ok(TypeHint::from_type(StandardType::S64)) + } + + fn serialize_f32(self, value: f32) -> Result { + debug!("serialize_f32 => value: {}", value); + self.data_buf.write_f32::(value).context(KbinErrorKind::DataWrite("f32"))?; + + Ok(TypeHint::from_type(StandardType::Float)) + } + + fn serialize_f64(self, value: f64) -> Result { + debug!("serialize_f64 => value: {}", value); + self.data_buf.write_f64::(value).context(KbinErrorKind::DataWrite("f64"))?; + + Ok(Some(TypeHint::from_type(StandardType::Double))) + } + */ + + fn serialize_char(self, value: char) -> Result { + debug!("serialize_char => value: {}", value); + self.data_buf.write_str(self.encoding, &value.to_string())?; + + Ok(Some(TypeHint::from_type(StandardType::String))) + } + + fn serialize_str(self, value: &str) -> Result { + debug!("serialize_str => value: {}", value); + self.data_buf.write_str(self.encoding, value)?; + + Ok(Some(TypeHint::from_type(StandardType::String))) + } + + // Binary data is handled separately from other array types. + // Binary data should also be the only element of its node. + fn serialize_bytes(self, value: &[u8]) -> Result { + debug!("serialize_bytes => value: {:02x?}", value); + let node_type = StandardType::Binary; + let size = (value.len() as u32) * (node_type.size as u32); + self.data_buf.write_u32::(size).context(KbinErrorKind::DataWrite("binary node size"))?; + self.data_buf.write_all(value).context(KbinErrorKind::DataWrite("binary"))?; + self.data_buf.realign_writes(None)?; + + Ok(Some(TypeHint::from_type(node_type))) + } + + // TODO: Figure out a good way to serialize this + fn serialize_none(self) -> Result { + debug!("serialize_none"); + Ok(None) + } + + fn serialize_some(self, v: &T) -> Result + where T: ?Sized + Serialize + { + debug!("serialize_some"); + let hint = v.serialize(&mut *self)?; + Ok(hint) + } + + // TODO: Figure out a good way to serialize this + fn serialize_unit(self) -> Result { + debug!("serialize_unit"); + Ok(None) + } + + fn serialize_unit_struct(self, name: &'static str) -> Result { + debug!("serialize_unit_struct => name: {}", name); + let hint = name.serialize(&mut *self)?; + Ok(hint) + } + + fn serialize_unit_variant(self, name: &'static str, variant_index: u32, variant: &'static str) -> Result { + debug!("serialize_unit_variant => name: {}, variant_index: {}, variant: {}", name, variant_index, variant); + let hint = variant.serialize(&mut *self)?; + Ok(hint) + } + + fn serialize_newtype_struct(self, name: &'static str, value: &T) -> Result + where T: ?Sized + Serialize + { + debug!("serialize_newtype_struct => name: {}", name); + let hint = value.serialize(&mut *self)?; + Ok(hint) + } + + fn serialize_newtype_variant(self, name: &'static str, variant_index: u32, variant: &'static str, value: &T) -> Result + where T: ?Sized + Serialize + { + debug!("serialize_newtype_variant => name: {}, variant_index: {}, variant: {}", name, variant_index, variant); + variant.serialize(&mut *self)?; + let hint = value.serialize(&mut *self)?.map(|mut hint| { + hint.is_array = false; + hint + }); + Ok(hint) + } + + fn serialize_seq(self, len: Option) -> Result { + debug!("serialize_seq => len: {:?}", len); + let len = len.ok_or(Error::Message("unsized sequences not supported".to_string()))?; + Ok(Tuple::new(self, len)) + } + + fn serialize_tuple(self, len: usize) -> Result { + debug!("serialize_tuple => len: {}", len); + Ok(Tuple::new(self, len)) + } + + fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result { + debug!("serialize_tuple_struct => name: {}, len: {}", name, len); + Err(Error::Message("tuple struct not supported".to_string())) + } + + fn serialize_tuple_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result { + debug!("serialize_tuple_variant => name: {}, variant_index: {}, variant: {}, len: {}", name, variant_index, variant, len); + Err(Error::Message("tuple variant not supported".to_string())) + } + + fn serialize_map(self, len: Option) -> Result { + debug!("serialize_map => len: {:?}", len); + Ok(self) + } + + fn serialize_struct(self, name: &'static str, len: usize) -> Result { + debug!("serialize_struct => name: {}, len: {}", name, len); + + let node_type = StandardType::NodeStart; + self.node_buf.write_u8(node_type.id).context(KbinErrorKind::DataWrite(node_type.name))?; + pack_sixbit(&mut self.node_buf, name)?; + + Ok(self) + } + + fn serialize_struct_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result { + debug!("serialize_struct_variant => name: {}, variant_index: {}, variant: {}, len: {}", name, variant_index, variant, len); + Err(Error::Message("struct variant not supported".to_string())) + } +} + +impl<'a> ser::SerializeMap for &'a mut Serializer { + type Ok = Option; + type Error = Error; + + fn serialize_key(&mut self, key: &T) -> Result<()> + where T: ?Sized + Serialize + { + debug!("SerializeMap: serialize_key"); + let hint = key.serialize(&mut **self)?; + debug!("SerializeMap: serialize_key, hint: {:?}", hint); + Ok(()) + } + + fn serialize_value(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + debug!("SerializeMap: serialize_value"); + let hint = value.serialize(&mut **self)?; + debug!("SerializeMap: serialize_value, hint: {:?}", hint); + Ok(()) + } + + fn end(self) -> Result { + debug!("SerializeMap: end"); + Ok(None) + } +} + +impl<'a> ser::SerializeStruct for &'a mut Serializer { + type Ok = Option; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + let size = mem::size_of_val(value); + debug!("SerializeStruct: serialize_field, key: {}, value size: {}", key, size); + + let hint = value.serialize(&mut **self)?.ok_or(KbinErrorKind::MissingTypeHint)?; + let array_mask = if hint.is_array { ARRAY_MASK } else { 0 }; + debug!("SerializeStruct: serialize_field, key: {}, hint: {:?}", key, hint); + + self.node_buf.write_u8(hint.node_type.id | array_mask).context(KbinErrorKind::DataWrite(hint.node_type.name))?; + pack_sixbit(&mut self.node_buf, key)?; + + // TODO: Make sure this does not prematurely end nodes + self.node_buf.write_u8(StandardType::NodeEnd.id | ARRAY_MASK).context(KbinErrorKind::DataWrite("node end"))?; + + Ok(()) + } + + fn end(self) -> Result { + debug!("SerializeStruct: end"); + self.node_buf.write_u8(StandardType::NodeEnd.id | ARRAY_MASK).context(KbinErrorKind::DataWrite("node end"))?; + + trace!("SerializeStruct::end() => node_buf: {:02x?}", self.node_buf.get_ref()); + + Ok(Some(TypeHint::from_type(StandardType::NodeStart))) + } +} diff --git a/src/ser/tuple.rs b/src/ser/tuple.rs new file mode 100644 index 0000000..223a9be --- /dev/null +++ b/src/ser/tuple.rs @@ -0,0 +1,101 @@ +use std::io::{Seek, SeekFrom}; + +use byteorder::{BigEndian, WriteBytesExt}; +use failure::ResultExt; +use serde::ser::{Serialize, SerializeSeq, SerializeTuple}; + +use error::KbinErrorKind; +use node_types::StandardType; +use ser::{Error, Result, Serializer, TypeHint, WriteMode}; + +pub struct Tuple<'a> { + ser: &'a mut Serializer, + + size_index: u64, + node_type: StandardType, + len: usize, +} + +impl<'a> Tuple<'a> { + pub fn new(ser: &'a mut Serializer, len: usize) -> Self { + debug!("Tuple::new(len: {})", len); + + ser.write_mode = WriteMode::Array; + + let size_index = ser.data_buf.position(); + + // Estimate u32 for the total size of the tuple + debug!("big endian length: {:02x?}", u32::to_bytes(u32::to_be(len as u32))); + ser.data_buf.write_u32::(len as u32).expect("Unable to write size placeholder"); + + Self { + ser, + size_index, + node_type: StandardType::String, + len, + } + } + + fn find_standard_type(&self) -> StandardType { + debug!("find_standard_type => len: {}", self.len); + self.node_type + } +} + +impl<'a> SerializeTuple for Tuple<'a> { + type Ok = Option; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + debug!("SerializeTuple: serialize_element"); + let hint = value.serialize(&mut *self.ser)?.ok_or(KbinErrorKind::MissingTypeHint)?; + debug!("SerializeTuple: serialize_element, hint: {:?}", hint); + + self.node_type = hint.node_type; + + Ok(()) + } + + fn end(self) -> Result { + debug!("SerializeTuple: end"); + + self.ser.write_mode = WriteMode::Single; + self.ser.data_buf.realign_writes(None)?; + + let node_type = self.find_standard_type(); + let size = (self.len as u32) * (node_type.size as u32); + + // Update the size estimate from the constructor + if size as usize != self.len { + debug!("SerializeTuple: end, size correction: {}", size); + + let old_pos = self.ser.data_buf.position(); + self.ser.data_buf.seek(SeekFrom::Start(self.size_index)).context(KbinErrorKind::Seek)?; + self.ser.data_buf.write_u32::(size).context(KbinErrorKind::DataWrite("node size"))?; + self.ser.data_buf.seek(SeekFrom::Start(old_pos)).context(KbinErrorKind::Seek)?; + } + + // Taken care of by `SerializerStruct` + //self.ser.node_buf.write_u8(StandardType::NodeEnd.id | ARRAY_MASK).context(KbinErrorKind::DataWrite("node end"))?; + + Ok(Some(TypeHint { node_type, is_array: true, count: self.len })) + } +} + +// kbin only supports sized arrays, coerce sequence types to tuple processing +impl<'a> SerializeSeq for Tuple<'a> { + type Ok = Option; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where T: ?Sized + Serialize + { + ::serialize_element(self, value) + } + + fn end(self) -> Result { + ::end(self) + } +}