refactor: make LZMA support an optional feature

This commit is contained in:
Mike Welsh 2018-06-10 11:58:08 -07:00
parent 120f94711a
commit 5a0e7a8fd3
4 changed files with 62 additions and 40 deletions

View File

@ -14,7 +14,8 @@ byteorder = "1.0"
flate2 = "1.0" flate2 = "1.0"
num-derive = "0.2" num-derive = "0.2"
num-traits = "0.2" num-traits = "0.2"
xz2 = "0.1.5" xz2 = {version = "0.1.5", optional = true}
[features] [features]
default = [] default = []
lzma-support = ["xz2"]

View File

@ -14,6 +14,7 @@ extern crate flate2;
#[macro_use] #[macro_use]
extern crate num_derive; extern crate num_derive;
extern crate num_traits; extern crate num_traits;
#[cfg(feature = "lzma-support")]
extern crate xz2; extern crate xz2;
pub mod avm1; pub mod avm1;

View File

@ -5,7 +5,6 @@ use flate2::read::ZlibDecoder;
use std::collections::HashSet; use std::collections::HashSet;
use std::io::{Error, ErrorKind, Read, Result}; use std::io::{Error, ErrorKind, Read, Result};
use types::*; use types::*;
use xz2::read::XzDecoder;
/// Reads SWF data from a stream. /// Reads SWF data from a stream.
pub fn read_swf<R: Read>(input: R) -> Result<Swf> { pub fn read_swf<R: Read>(input: R) -> Result<Swf> {
@ -24,22 +23,7 @@ fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<(Swf, Reader<Box<Re
let decompressed_input: Box<Read> = match compression { let decompressed_input: Box<Read> = match compression {
Compression::None => Box::new(input), Compression::None => Box::new(input),
Compression::Zlib => Box::new(ZlibDecoder::new(input)), Compression::Zlib => Box::new(ZlibDecoder::new(input)),
Compression::Lzma => { Compression::Lzma => make_lzma_reader(input)?,
// Flash uses a mangled LZMA header, so we have to massage it into the normal
// format.
use std::io::{Cursor, Write};
use xz2::stream::{Action, Stream};
use byteorder::WriteBytesExt;
input.read_u32::<LittleEndian>()?; // Compressed length
let mut lzma_properties = [0u8; 5];
input.read_exact(&mut lzma_properties)?;
let mut lzma_header = Cursor::new(Vec::with_capacity(13));
lzma_header.write_all(&lzma_properties)?;
lzma_header.write_u64::<LittleEndian>(uncompressed_length as u64)?;
let mut lzma_stream = Stream::new_lzma_decoder(u64::max_value())?;
lzma_stream.process(&lzma_header.into_inner(), &mut [0u8; 1], Action::Run)?;
Box::new(XzDecoder::new_stream(input, lzma_stream))
}
}; };
let mut reader = Reader::new(decompressed_input, version); let mut reader = Reader::new(decompressed_input, version);
@ -57,6 +41,29 @@ fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<(Swf, Reader<Box<Re
Ok((swf, reader)) Ok((swf, reader))
} }
#[cfg(feature = "lzma-support")]
fn make_lzma_reader<'a, R: Read + 'a>(mut input: R) -> Result<Box<Read + 'a>> {
// Flash uses a mangled LZMA header, so we have to massage it into the normal
// format.
use std::io::{Cursor, Write};
use xz2::stream::{Action, Stream};
use byteorder::WriteBytesExt;
input.read_u32::<LittleEndian>()?; // Compressed length
let mut lzma_properties = [0u8; 5];
input.read_exact(&mut lzma_properties)?;
let mut lzma_header = Cursor::new(Vec::with_capacity(13));
lzma_header.write_all(&lzma_properties)?;
lzma_header.write_u64::<LittleEndian>(uncompressed_length as u64)?;
let mut lzma_stream = Stream::new_lzma_decoder(u64::max_value())?;
lzma_stream.process(&lzma_header.into_inner(), &mut [0u8; 1], Action::Run)?;
Box::new(XzDecoder::new_stream(input, lzma_stream))
}
#[cfg(not(feature = "lzma-support"))]
fn make_lzma_reader<'a, R: Read + 'a>(_: R) -> Result<Box<Read + 'a>> {
Err(Error::new(ErrorKind::InvalidData, "Support for LZMA compressed SWFs is not enabled."))
}
pub trait SwfRead<R: Read> { pub trait SwfRead<R: Read> {
fn get_inner(&mut self) -> &mut R; fn get_inner(&mut self) -> &mut R;
@ -409,7 +416,7 @@ impl<R: Read> Reader<R> {
fn read_tag(&mut self) -> Result<Option<Tag>> { fn read_tag(&mut self) -> Result<Option<Tag>> {
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
let (tag_code, length) = self.read_tag_code_and_length()?; let (tag_code, length) = self.read_tag_code_and_length()?;
let mut tag_reader = Reader::new(self.input.by_ref().take(length as u64), self.version); let mut tag_reader = Reader::new(self.input.by_ref().take(length as u64), self.version);
@ -2669,10 +2676,12 @@ pub mod tests {
read_from_file("tests/swfs/zlib.swf").compression, read_from_file("tests/swfs/zlib.swf").compression,
Compression::Zlib Compression::Zlib
); );
assert_eq!( if cfg!(feature = "lzma-support") {
read_from_file("tests/swfs/lzma.swf").compression, assert_eq!(
Compression::Lzma read_from_file("tests/swfs/lzma.swf").compression,
); Compression::Lzma
);
}
} }
#[test] #[test]

View File

@ -9,7 +9,6 @@ use std::collections::HashSet;
use std::io::{Error, ErrorKind, Result, Write}; use std::io::{Error, ErrorKind, Result, Write};
use tag_codes::TagCode; use tag_codes::TagCode;
use types::*; use types::*;
use xz2::write::XzEncoder;
pub fn write_swf<W: Write>(swf: &Swf, mut output: W) -> Result<()> { pub fn write_swf<W: Write>(swf: &Swf, mut output: W) -> Result<()> {
let signature = match swf.compression { let signature = match swf.compression {
@ -52,22 +51,32 @@ pub fn write_swf<W: Write>(swf: &Swf, mut output: W) -> Result<()> {
// SWF format has a mangled LZMA header, so we have to do some magic to conver the // SWF format has a mangled LZMA header, so we have to do some magic to conver the
// standard LZMA header to SWF format. // standard LZMA header to SWF format.
// https://adobe.ly/2s8oYzn // https://adobe.ly/2s8oYzn
Compression::Lzma => { Compression::Lzma => write_lzma_swf(&mut output)?,
use xz2::stream::{Action, LzmaOptions, Stream};
let mut stream = Stream::new_lzma_encoder(&LzmaOptions::new_preset(9)?)?;
let mut lzma_header = [0; 13];
stream.process(&[], &mut lzma_header, Action::Run)?;
// Compressed length. We just write out a dummy value.
output.write_u32::<LittleEndian>(0xffffffff)?;
output.write_all(&lzma_header[0..5])?; // LZMA property bytes.
let mut encoder = XzEncoder::new_stream(&mut output, stream);
encoder.write_all(&swf_body)?;
}
}; };
Ok(()) Ok(())
} }
#[cfg(feature = "lzma-support")]
fn write_lzma_swf<W: Write>(mut output: W) -> Result<()> {
use xz2::write::XzEncoder;
use xz2::stream::{Action, LzmaOptions, Stream};
let mut stream = Stream::new_lzma_encoder(&LzmaOptions::new_preset(9)?)?;
let mut lzma_header = [0; 13];
stream.process(&[], &mut lzma_header, Action::Run)?;
// Compressed length. We just write out a dummy value.
output.write_u32::<LittleEndian>(0xffffffff)?;
output.write_all(&lzma_header[0..5])?; // LZMA property bytes.
let mut encoder = XzEncoder::new_stream(&mut output, stream);
encoder.write_all(&swf_body)?;
Ok(())
}
#[cfg(not(feature = "lzma-support"))]
fn write_lzma_swf<W: Write>(_: W) -> Result<()> {
Err(Error::new(ErrorKind::InvalidData, "Support for LZMA compressed SWFs is not enabled."))
}
pub trait SwfWrite<W: Write> { pub trait SwfWrite<W: Write> {
fn get_inner(&mut self) -> &mut W; fn get_inner(&mut self) -> &mut W;
@ -2691,10 +2700,12 @@ mod tests {
write_dummy_swf(Compression::Zlib).is_ok(), write_dummy_swf(Compression::Zlib).is_ok(),
"Failed to write zlib SWF." "Failed to write zlib SWF."
); );
assert!( if cfg!(feature = "lzma-support") {
write_dummy_swf(Compression::Lzma).is_ok(), assert!(
"Failed to write LZMA SWF." write_dummy_swf(Compression::Lzma).is_ok(),
); "Failed to write LZMA SWF."
);
}
} }
#[test] #[test]