diff --git a/src/header.rs b/src/header.rs index 383113a..018bff8 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,7 +1,7 @@ //! Structs and enums which are included in the header of SQP files. use byteorder::{ReadBytesExt, WriteBytesExt, LE}; -use std::io::{Cursor, Read, Write}; +use std::io::{self, Read, Write}; use crate::picture::Error; @@ -42,21 +42,26 @@ impl Default for Header { } impl Header { - pub fn to_bytes(&self) -> [u8; 19] { - let mut buf = Cursor::new(Vec::new()); - - buf.write_all(&self.magic).unwrap(); - buf.write_u32::(self.width).unwrap(); - buf.write_u32::(self.height).unwrap(); + /// Write the header into a byte stream implementing [`Write`]. + /// + /// Returns the number of bytes written. + pub fn write_into(&self, output: &mut W) -> Result { + let mut count = 0; + output.write_all(&self.magic)?; + output.write_u32::(self.width)?; + output.write_u32::(self.height)?; + count += 16; // Write compression info - buf.write_u8(self.compression_type.into()).unwrap(); - buf.write_u8(self.quality).unwrap(); + output.write_u8(self.compression_type.into())?; + output.write_u8(self.quality)?; + count += 2; // Write color format - buf.write_u8(self.color_format as u8).unwrap(); + output.write_u8(self.color_format as u8)?; + count += 1; - buf.into_inner().try_into().unwrap() + Ok(count) } /// Length of the header in bytes. @@ -65,13 +70,14 @@ impl Header { 19 } - /// Create a header from something implementing [`Read`]. - pub fn read_from(input: &mut T) -> Result { + /// Create a header from a byte stream implementing [`Read`]. + pub fn read_from(input: &mut R) -> Result { let mut magic = [0u8; 8]; input.read_exact(&mut magic).unwrap(); if magic != *b"dangoimg" { - return Err(Error::InvalidIdentifier(magic)); + let bad_id = String::from_utf8_lossy(&magic).into_owned(); + return Err(Error::InvalidIdentifier(bad_id)); } Ok(Header { diff --git a/src/picture.rs b/src/picture.rs index a97a0c6..e2b9bd1 100644 --- a/src/picture.rs +++ b/src/picture.rs @@ -13,14 +13,18 @@ use crate::{ operations::{add_rows, sub_rows}, }; +/// An error which occured while manipulating a [`SquishyPicture`]. #[derive(Error, Debug)] pub enum Error { - #[error("incorrect identifier, got {0:02X?}")] - InvalidIdentifier([u8; 8]), + /// The file signature was invalid. Must be "dangoimg". + #[error("incorrect signature, expected \"dangoimg\" got {0:?}")] + InvalidIdentifier(String), + /// Any I/O operation failed. #[error("io operation failed: {0}")] IoError(#[from] io::Error), + /// There was an error while compressing or decompressing. #[error("compression operation failed: {0}")] CompressionError(#[from] CompressionError), } @@ -146,8 +150,7 @@ impl SquishyPicture { let mut count = 0; // Write out the header - output.write_all(&self.header.to_bytes()).unwrap(); - count += self.header.len(); + count += self.header.write_into(&mut output)?; // Based on the compression type, modify the data accordingly let modified_data = match self.header.compression_type { @@ -241,6 +244,7 @@ impl SquishyPicture { } } +/// Decode a stream encoded as varints. fn decode_varint_stream(stream: &[u8]) -> Vec { let mut output = Vec::new(); let mut offset = 0; @@ -253,6 +257,10 @@ fn decode_varint_stream(stream: &[u8]) -> Vec { output } +/// Open an SQP from a given path. Convenience method around +/// [`SquishyPicture::decode`]. Returns a [`Result`]. +/// +/// If you are loading from memory, use [`SquishyPicture::decode`] instead. pub fn open>(path: P) -> Result { let input = File::open(path)?;