diff --git a/Cargo.lock b/Cargo.lock index 1223e8e07..e3f8feaca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,6 +290,12 @@ dependencies = [ "serde", ] +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" + [[package]] name = "bumpalo" version = "3.4.0" @@ -794,6 +800,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + [[package]] name = "crc32fast" version = "1.2.1" @@ -2232,14 +2247,13 @@ dependencies = [ ] [[package]] -name = "lzma-sys" -version = "0.1.17" +name = "lzma-rs" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619" +checksum = "adc181f57e3b64bb860c476fe5751eb6f60e9fcf588b1447e24c0757c1c3101f" dependencies = [ - "cc", - "libc", - "pkg-config", + "byteorder", + "crc", ] [[package]] @@ -3844,10 +3858,10 @@ dependencies = [ "flate2", "libflate 1.0.3", "log", + "lzma-rs", "num-derive", "num-traits", "smallvec 1.5.1", - "xz2", ] [[package]] @@ -4789,12 +4803,3 @@ name = "xml-rs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" - -[[package]] -name = "xz2" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" -dependencies = [ - "lzma-sys", -] diff --git a/core/src/tag_utils.rs b/core/src/tag_utils.rs index 92754e323..7e654ad71 100644 --- a/core/src/tag_utils.rs +++ b/core/src/tag_utils.rs @@ -80,21 +80,10 @@ impl SwfMovie { // 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) { - return Err(format!("Error decompressing SWF, may be corrupt: {}", e).into()); - } - data - }; + let mut data = Vec::with_capacity(swf_stream.uncompressed_length); + if let Err(e) = reader.get_mut().read_to_end(&mut data) { + return Err(format!("Error decompressing SWF, may be corrupt: {}", e).into()); + } Ok(Self { header, diff --git a/swf/Cargo.toml b/swf/Cargo.toml index e6ecb1fa9..d6233f2d9 100644 --- a/swf/Cargo.toml +++ b/swf/Cargo.toml @@ -18,11 +18,11 @@ libflate = {version = "1.0", optional = true} log = "0.4" smallvec = "1.5.1" flate2 = {version = "1.0", optional = true} -xz2 = {version = "0.1.6", optional = true} +lzma-rs = {version = "0.1.3", optional = true } [dev-dependencies] approx = "0.4.0" [features] -default = ["flate2"] -lzma = ["xz2"] \ No newline at end of file +default = ["flate2", "lzma"] +lzma = ["lzma-rs"] diff --git a/swf/src/lib.rs b/swf/src/lib.rs index 1c31e6d13..ccb7acb96 100644 --- a/swf/src/lib.rs +++ b/swf/src/lib.rs @@ -16,8 +16,6 @@ extern crate libflate; #[macro_use] extern crate num_derive; extern crate num_traits; -#[cfg(feature = "lzma")] -extern crate xz2; pub mod avm1; pub mod avm2; diff --git a/swf/src/read.rs b/swf/src/read.rs index 7cae5e776..83907e3e6 100644 --- a/swf/src/read.rs +++ b/swf/src/read.rs @@ -156,11 +156,9 @@ fn make_lzma_reader<'a, R: Read + 'a>( mut input: R, uncompressed_length: u32, ) -> Result> { - use byteorder::WriteBytesExt; - use std::io::{Cursor, Write}; - use xz2::{ - read::XzDecoder, - stream::{Action, Stream}, + use lzma_rs::{ + decompress::{Options, UnpackedSize}, + lzma_decompress_with_options, }; // 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 @@ -174,27 +172,24 @@ fn make_lzma_reader<'a, R: Read + 'a>( // LZMA standard header // Bytes 0..5: LZMA properties // Bytes 5..13: Uncompressed length + // + // To deal with the mangled header, use lzma_rs options to anually provide uncompressed length. - // Read compressed length + // Read compressed length (ignored) let _ = input.read_u32::()?; - // Read LZMA propreties to decoder - let mut lzma_properties = [0u8; 5]; - input.read_exact(&mut lzma_properties)?; + // TODO: Switch to lzma-rs streaming API when stable. + let mut output = Vec::with_capacity(uncompressed_length as usize); + lzma_decompress_with_options( + &mut io::BufReader::new(input), + &mut output, + &Options { + unpacked_size: UnpackedSize::UseProvided(Some(uncompressed_length.into())), + }, + ) + .map_err(|_| Error::invalid_data("Unable to decompress LZMA SWF."))?; - // 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::(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))) + Ok(Box::new(io::Cursor::new(output))) } #[cfg(not(feature = "lzma"))] diff --git a/swf/src/write.rs b/swf/src/write.rs index 9152e854d..3de3a2240 100644 --- a/swf/src/write.rs +++ b/swf/src/write.rs @@ -72,7 +72,11 @@ pub fn write_swf(swf: &Swf, mut output: W) -> Result<()> { // SWF format has a mangled LZMA header, so we have to do some magic to convert the // standard LZMA header to SWF format. // https://adobe.ly/2s8oYzn - Compression::Lzma => write_lzma_swf(&mut output, &swf_body)?, + Compression::Lzma => { + write_lzma_swf(&mut output, &swf_body)?; + // 5 bytes of garbage data? + //output.write_all(&[0xFF, 0xB5, 0xE6, 0xF8, 0xCB])?; + } }; Ok(()) @@ -105,19 +109,16 @@ fn write_zlib_swf(_output: W, _swf_body: &[u8]) -> Result<()> { } #[cfg(feature = "lzma")] -fn write_lzma_swf(mut output: W, swf_body: &[u8]) -> Result<()> { - 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).unwrap(); - // Compressed length. We just write out a dummy value. - output.write_u32::(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)?; +fn write_lzma_swf(mut output: W, mut swf_body: &[u8]) -> Result<()> { + // Compress data using LZMA. + let mut data = vec![]; + lzma_rs::lzma_compress(&mut swf_body, &mut data)?; + + // Flash uses a mangled LZMA header, so we have to massage it into the SWF format. + // https://helpx.adobe.com/flash-player/kb/exception-thrown-you-decompress-lzma-compressed.html + output.write_u32::(data.len() as u32 - 13)?; // Compressed length (- 13 to not include lzma header) + output.write_all(&data[0..5])?; // LZMA properties + output.write_all(&data[13..])?; // Data Ok(()) }