1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
//! IFF is a binary-interchange format developed by Electronic Arts
//! for tagging binary data with a meaning. This file is made of out
//! of segments referred to as so called "chunks". This format is used
//! for mainly storing multimedia, eg. audio, video, midi, images.
//! 
//! This crate provides data-structures and wrappers to manipulate this
//! format quite easily by reading and decoding or writing and encoding
//! from or into file-streams.
//! 
//! # Examples
//! To decode all the chunks avialable from the given reader:
//! ```
//! use iffc::Decoder;
//! 
//! fn main() {
//!     let inp = std::io::Cursor::new(b"RIFF\x04\x00\x00\x00WAVE");
//!     let parser = Decoder::new(Box::new(inp));
//! 
//!     for chk in parser
//!     { println!("{:?}: {}", chk.0, chk.1.len()); }
//! }
//! ```
//! 
//! To encode chunks into a given writer:
//! ```
//! use iffc::{Encoder, Chunk};
//! 
//! fn main() {
//!     let out = std::io::Cursor::new(Vec::new());
//!     let deparser = Encoder::new(Box::new(out));
//! 
//!     deparser << Chunk(*b"RIFF", Box::new(*b"WAVE"));
//! }
//! ```
use std::io::{Read, Write, IoSlice, IoSliceMut};
use std::ops::{Shl};

/// An IFF chunk represents a single segment of a complete IFF
/// file. Note: Even though this structure is capable of stroing
/// data upto `usize` but IFF limits that to `u32` only.
/// 
/// `0` — four-byte identity of chunk.
/// `1` — byte-data encapsulated inside it.
#[derive(Debug, Eq, PartialEq)]
pub struct Chunk(pub [u8; 4], pub Box<[u8]>);

/// A structure which wraps a reader and parses IFF chunks and
/// behaves like an iterator which yields `IFFChunk` until
/// an entire-chunk can't be constructed.
pub struct Decoder(Box<dyn Read>);
impl Decoder
{ pub fn new(r: Box<dyn Read>) -> Self { Self(r) } }

/// A structure which wraps a writer and writes IFF chunks to it,
/// by using `<<` (shift-left) with an RHS of type `IFFChunk`, also
/// that operand can be chained.
pub struct Encoder(Box<dyn Write>);
impl Encoder
{ pub fn new(w: Box<dyn Write>) -> Self { Self(w) } }

impl Iterator for Decoder {
    type Item = Chunk;

    fn next(&mut self) -> Option<Self::Item> {
        let mut id   = [0u8; 4];
        let mut size = [0u8; 4];

        if let Err(_) = self.0.read_vectored(&mut [
            IoSliceMut::new(&mut id),
            IoSliceMut::new(&mut size)
        ]) { return None };

        let size = u32::from_le_bytes(size) as usize;
        let mut data = vec![0u8; size];
        
        match self.0.read(&mut data) {
            Ok(s) => if size != s || s == 0
                     { return None },
            Err(_) => { return None }
        };

        Some(Chunk(id, data.into_boxed_slice()))
    }
}

impl Shl<Chunk> for Encoder {
    type Output = Option<Self>;

    fn shl(self, chunk: Chunk) -> Option<Self> {
        let mut sel = self;
        
        match sel.0.write_vectored(&[
            IoSlice::new(&chunk.0),
            IoSlice::new(&(chunk.1.len() as u32)
                            .to_le_bytes()[..]),
            IoSlice::new(&chunk.1)
        ]) { Ok(_) => Some(Self(sel.0)),
             Err(_) => None }
    }
}