#![warn(missing_docs)]
extern crate byteorder;
extern crate flate2;
extern crate lzma;
extern crate bit_range;
mod decoded_swf;
mod error;
use std::fs::File;
use std::path::Path;
pub use decoded_swf::DecodedSwf;
pub use error::Error;
use byteorder::{LittleEndian, ReadBytesExt};
use bit_range::BitRange;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Signature {
Uncompressed,
ZlibCompressed,
LzmaCompressed
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct SwfHeaders {
signature: Signature,
version: u8,
file_length: u32,
width: u32,
height: u32,
frame_rate: u16,
frame_count: u16
}
impl SwfHeaders {
pub fn open<T: AsRef<Path>>(path: T) -> Result<(Self, DecodedSwf), Error> {
Self::read_from(try!(File::open(path)))
}
pub fn read_from(mut file: File) -> Result<(Self, DecodedSwf), Error> {
let sig = match try!(file.read_u8()) as char {
'F' => Signature::Uncompressed,
'C' => Signature::ZlibCompressed,
'Z' => Signature::LzmaCompressed,
_ => return Err(Error::NotSwf)
};
match (try!(file.read_u8()), try!(file.read_u8())) {
(0x57, 0x53) => {},
_ => return Err(Error::NotSwf)
}
let version = try!(file.read_u8());
let file_length = try!(file.read_u32::<LittleEndian>());
let mut decoded = try!(DecodedSwf::decompress(file, sig));
let (width, height) = try!(parse_rect(&mut decoded));
let frame_rate_lower = try!(decoded.read_u8());
let frame_rate_upper = try!(decoded.read_u8());
if frame_rate_lower != 0 {
panic!("swf_headers: Decimal points in frame rates not yet supported");
}
let frame_rate = frame_rate_upper as u16;
let frame_count = try!(decoded.read_u16::<LittleEndian>());
Ok((SwfHeaders {
signature: sig,
version: version,
file_length: file_length,
width: width,
height: height,
frame_rate: frame_rate,
frame_count: frame_count
}, decoded))
}
pub fn signature(&self) -> Signature {
self.signature
}
pub fn version(&self) -> u8 {
self.version
}
pub fn file_length(&self) -> u32 {
self.file_length
}
pub fn dimensions_twips(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width / 20, self.height / 20)
}
pub fn frame_rate(&self) -> u16 {
self.frame_rate
}
pub fn frame_count(&self) -> u16 {
self.frame_count
}
}
fn parse_rect<T: ReadBytesExt>(file: &mut T) -> Result<(u32, u32), Error> {
let first_byte = try!(file.read_u8());
let nbits = ((first_byte >> 3) & 0b0001_1111) as u32;
let nbytes = (5 + nbits * 4) / 8;
let mut bytes = Vec::new();
bytes.push(first_byte);
for _ in 0..nbytes {
bytes.push(try!(file.read_u8()));
}
let width = bytes.get_bit_range(5+nbits..5+nbits*2);
let height = bytes.get_bit_range(5+nbits*3..5+nbits*4);
Ok((width, height))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_245() {
let (headers, _) = SwfHeaders::open("tests/245.swf").unwrap();
assert_eq!(headers.signature(), Signature::ZlibCompressed);
assert_eq!(headers.version(), 9);
assert_eq!(headers.file_length(), 849486);
assert_eq!(headers.dimensions_twips(), (6000, 6000));
assert_eq!(headers.dimensions(), (300, 300));
assert_eq!(headers.frame_rate(), 30);
assert_eq!(headers.frame_count(), 1);
}
#[test]
fn test_902() {
let (headers, _) = SwfHeaders::open("tests/902.swf").unwrap();
assert_eq!(headers.signature(), Signature::ZlibCompressed);
assert_eq!(headers.version(), 9);
assert_eq!(headers.file_length(), 2032206);
assert_eq!(headers.dimensions_twips(), (6000, 6000));
assert_eq!(headers.dimensions(), (300, 300));
assert_eq!(headers.frame_rate(), 30);
assert_eq!(headers.frame_count(), 1);
}
#[test]
fn test_submachine_1() {
let (headers, _) = SwfHeaders::open("tests/submachine_1.swf").unwrap();
assert_eq!(headers.signature(), Signature::ZlibCompressed);
assert_eq!(headers.version(), 9);
assert_eq!(headers.file_length(), 1781964);
assert_eq!(headers.dimensions_twips(), (8000, 8500));
assert_eq!(headers.dimensions(), (400, 425));
assert_eq!(headers.frame_rate(), 25);
assert_eq!(headers.frame_count(), 29);
}
#[test]
fn test_colourshift() {
let (headers, _) = SwfHeaders::open("tests/colourshift.swf").unwrap();
assert_eq!(headers.signature(), Signature::ZlibCompressed);
assert_eq!(headers.version(), 9);
assert_eq!(headers.file_length(), 189029);
assert_eq!(headers.dimensions_twips(), (12800, 9600));
assert_eq!(headers.dimensions(), (640, 480));
assert_eq!(headers.frame_rate(), 30);
assert_eq!(headers.frame_count(), 1);
}
}