swf: Fix compiling with lzma feature

This commit is contained in:
Mike Welsh 2019-10-11 23:02:20 -07:00
parent 160ec4d0c3
commit 1ab5211bfe
6 changed files with 93 additions and 31 deletions

View File

@ -26,4 +26,5 @@ default-features = false # can't use rayon on web
approx = "0.3.2"
[features]
default = ["minimp3"]
default = ["minimp3", "lzma"]
lzma = ["swf/lzma"]

View File

@ -71,15 +71,26 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
let swf_stream = swf::read::read_swf_header(&swf_data[..]).unwrap();
let header = swf_stream.header;
let mut reader = swf_stream.reader;
// Decompress the entire SWF in memory.
let mut data = Vec::with_capacity(swf_stream.uncompressed_length);
// Decompress the entire SWF in memory.
// Sometimes SWFs will have an incorrectly compressed stream,
// but will otherwise decompress fine up to the End tag.
// So just warn on this case and try to continue gracefully.
let data = if header.compression == swf::Compression::Lzma {
// TODO: The LZMA decoder is still funky.
// It always errors, and doesn't return all the data if you use read_to_end,
// but read_exact at least returns the data... why?
// Does the decoder need to be flushed somehow?
let mut data = vec![0u8; swf_stream.uncompressed_length];
let _ = reader.get_mut().read_exact(&mut data);
data
} else {
let mut data = Vec::with_capacity(swf_stream.uncompressed_length);
if let Err(e) = reader.get_mut().read_to_end(&mut data) {
log::error!("Error decompressing SWF, may be corrupt: {}", e);
}
data
};
let swf_len = data.len();

View File

@ -16,8 +16,8 @@ num-traits = "0.2"
libflate = {version = "0.1", optional = true}
log = "0.4"
flate2 = {version = "1.0", optional = true}
xz2 = {version = "0.1.5", optional = true}
xz2 = {version = "0.1.6", optional = true}
[features]
default = ["libflate"]
lzma-support = ["xz2"]
lzma = ["xz2"]

View File

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

View File

@ -29,7 +29,21 @@ pub fn read_swf<R: Read>(input: R) -> Result<Swf> {
let mut reader = swf_stream.reader;
// Decompress all of SWF into memory at once.
let mut data = if header.compression == Compression::Lzma {
// TODO: The LZMA decoder is still funky.
// It always errors, and doesn't return all the data if you use read_to_end,
// but read_exact at least returns the data... why?
// Does the decoder need to be flushed somehow?
let mut data = vec![0u8; swf_stream.uncompressed_length];
let _ = reader.get_mut().read_exact(&mut data);
data
} else {
let mut data = Vec::with_capacity(swf_stream.uncompressed_length);
if let Err(e) = reader.get_mut().read_to_end(&mut data) {
log::error!("Error decompressing SWF, may be corrupt: {}", e);
}
data
};
let version = header.version;
// Some SWF streams may not be compressed correctly,
@ -66,7 +80,10 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<SwfStream<'a>>
// Read SWF header.
let compression = Reader::read_compression_type(&mut input)?;
let version = input.read_u8()?;
let uncompressed_length = input.read_u32::<LittleEndian>()?;
// Uncompressed length includes the 4-byte header and 4-byte uncompressed length itself,
// subtract it here.
let uncompressed_length = input.read_u32::<LittleEndian>()? - 8;
// Now the SWF switches to a compressed stream.
let decompressed_input: Box<dyn Read> = match compression {
@ -87,7 +104,7 @@ pub fn read_swf_header<'a, R: Read + 'a>(mut input: R) -> Result<SwfStream<'a>>
version
);
}
make_lzma_reader(input)?
make_lzma_reader(input, uncompressed_length)?
}
};
@ -129,26 +146,57 @@ fn make_zlib_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<dyn Read + 'a>> {
))
}
#[cfg(feature = "lzma-support")]
fn make_lzma_reader<'a, R: Read + 'a>(mut input: R) -> Result<Box<dyn Read + 'a>> {
// Flash uses a mangled LZMA header, so we have to massage it into the normal
// format.
#[cfg(feature = "lzma")]
fn make_lzma_reader<'a, R: Read + 'a>(
mut input: R,
uncompressed_length: u32,
) -> Result<Box<dyn Read + 'a>> {
use byteorder::WriteBytesExt;
use std::io::{Cursor, Write};
use xz2::stream::{Action, Stream};
input.read_u32::<LittleEndian>()?; // Compressed length
use xz2::{
read::XzDecoder,
stream::{Action, Stream},
};
// Flash uses a mangled LZMA header, so we have to massage it into the normal format.
// https://helpx.adobe.com/flash-player/kb/exception-thrown-you-decompress-lzma-compressed.html
// LZMA SWF header:
// Bytes 0..3: ZWS header
// Byte 3: SWF version
// Bytes 4..8: Uncompressed length
// Bytes 8..12: Compressed length
// Bytes 12..17: LZMA properties
//
// LZMA standard header
// Bytes 0..5: LZMA properties
// Bytes 5..13: Uncompressed length
// Read compressed length
let _ = input.read_u32::<LittleEndian>()?;
// Read LZMA propreties to decoder
let mut lzma_properties = [0u8; 5];
input.read_exact(&mut lzma_properties)?;
// Rearrange above into LZMA format
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)?;
lzma_header.write_u64::<LittleEndian>(uncompressed_length.into())?;
// Create LZMA decoder stream and write header
let mut lzma_stream = Stream::new_lzma_decoder(u64::max_value()).unwrap();
lzma_stream
.process(&lzma_header.into_inner(), &mut [0u8; 1], Action::Run)
.unwrap();
// Decoder is ready
Ok(Box::new(XzDecoder::new_stream(input, lzma_stream)))
}
#[cfg(not(feature = "lzma-support"))]
fn make_lzma_reader<'a, R: Read + 'a>(_input: R) -> Result<Box<dyn Read + 'a>> {
#[cfg(not(feature = "lzma"))]
fn make_lzma_reader<'a, R: Read + 'a>(
_input: R,
_uncompressed_length: u32,
) -> Result<Box<dyn Read + 'a>> {
Err(Error::unsupported(
"Support for Zlib compressed SWFs is not enabled.",
))
@ -2862,7 +2910,7 @@ pub mod tests {
read_from_file("tests/swfs/zlib.swf").header.compression,
Compression::Zlib
);
if cfg!(feature = "lzma-support") {
if cfg!(feature = "lzma") {
assert_eq!(
read_from_file("tests/swfs/lzma.swf").header.compression,
Compression::Lzma

View File

@ -104,13 +104,15 @@ fn write_zlib_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
))
}
#[cfg(feature = "lzma-support")]
#[cfg(feature = "lzma")]
fn write_lzma_swf<W: Write>(mut output: W, swf_body: &[u8]) -> Result<()> {
use xz2::stream::{Action, LzmaOptions, Stream};
use xz2::write::XzEncoder;
let mut stream = Stream::new_lzma_encoder(&LzmaOptions::new_preset(9)?)?;
use xz2::{
stream::{Action, LzmaOptions, Stream},
write::XzEncoder,
};
let mut stream = Stream::new_lzma_encoder(&LzmaOptions::new_preset(9).unwrap()).unwrap();
let mut lzma_header = [0; 13];
stream.process(&[], &mut lzma_header, Action::Run)?;
stream.process(&[], &mut lzma_header, Action::Run).unwrap();
// Compressed length. We just write out a dummy value.
output.write_u32::<LittleEndian>(0xffffffff)?;
output.write_all(&lzma_header[0..5])?; // LZMA property bytes.
@ -119,7 +121,7 @@ fn write_lzma_swf<W: Write>(mut output: W, swf_body: &[u8]) -> Result<()> {
Ok(())
}
#[cfg(not(feature = "lzma-support"))]
#[cfg(not(feature = "lzma"))]
fn write_lzma_swf<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
Err(Error::unsupported(
"Support for LZMA compressed SWFs is not enabled.",
@ -2792,7 +2794,7 @@ mod tests {
write_dummy_swf(Compression::Zlib).is_ok(),
"Failed to write zlib SWF."
);
if cfg!(feature = "lzma-support") {
if cfg!(feature = "lzma") {
assert!(
write_dummy_swf(Compression::Lzma).is_ok(),
"Failed to write LZMA SWF."