caff-archive 0.1.0

a library for manipulating CAFF archives
Documentation
use crate::prelude::*;

mod magic;
mod preview_image;
mod version;

use crate::Padding;
use magic::Magic;
use preview_image::PreviewImage;
use version::Version;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct Header {
  #[cfg_attr(test, map(|magic: Magic| Magic::default()))]
  pub archive_magic: Magic,
  pub archive_version: Version,
  pub format_identifier: [u8; 4],
  pub format_version: Version,
  pub key: Key,
  unknown: Padding<8>,
  pub preview_image: PreviewImage,
  padding: Padding<12>,
}

impl Header {
  pub fn read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
    reader.trace(|reader| {
      let archive_magic = Magic::read(reader)?;
      let archive_version = Version::read(reader)?;
      let mut format_identifier = [0u8; 4];
      reader.read_exact(&mut format_identifier)?;
      let format_version = Version::read(reader)?;
      let key = Key::read(reader)?;
      let unknown = Padding::read(reader)?;
      let preview_image = PreviewImage::read(reader)?;
      let padding = Padding::read(reader)?;

      let value = Self {
        archive_magic,
        archive_version,
        format_identifier,
        format_version,
        key,
        unknown,
        preview_image,
        padding,
      };

      #[cfg(feature = "logging")]
      log::debug!("read value={value:?}");

      Ok(value)
    })
  }

  pub fn write<W: Write + Seek>(&self, writer: &mut W) -> Result<()> {
    writer.trace(|writer| {
      let Self {
        archive_magic,
        archive_version,
        format_identifier,
        format_version,
        key: obfuscate_key,
        unknown,
        preview_image,
        padding,
      } = self;

      archive_magic.write(writer)?;
      archive_version.write(writer)?;
      writer.write_all(format_identifier)?;
      format_version.write(writer)?;
      obfuscate_key.write(writer)?;
      unknown.write(writer)?;
      preview_image.write(writer)?;
      padding.write(writer)?;

      Ok(())
    })
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use test_strategy::proptest;

  #[proptest]
  fn roundtrip(expected: Header) {
    let mut buffer = Cursor::new(Vec::new());

    expected.write(&mut buffer).unwrap();
    buffer.rewind().unwrap();
    let actual = Header::read(&mut buffer).unwrap();

    assert_eq!(expected, actual);
  }
}