swf: Use lzma-rs for LZMA SWFs (fix #405)
Pure Rust decoder that functions on desktop and wasm. Enable lzma feature by default. TODO: Switch to lzma-rs streaming API when stable on crates.io. Currently decodes entire stream at once.
This commit is contained in:
parent
beed570475
commit
7cf217a911
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
header,
|
||||
|
|
|
@ -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"]
|
||||
default = ["flate2", "lzma"]
|
||||
lzma = ["lzma-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;
|
||||
|
|
|
@ -156,11 +156,9 @@ 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::{
|
||||
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::<LittleEndian>()?;
|
||||
|
||||
// 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::<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)))
|
||||
Ok(Box::new(io::Cursor::new(output)))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "lzma"))]
|
||||
|
|
|
@ -72,7 +72,11 @@ 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 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<W: Write>(_output: W, _swf_body: &[u8]) -> Result<()> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "lzma")]
|
||||
fn write_lzma_swf<W: Write>(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::<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)?;
|
||||
fn write_lzma_swf<W: Write>(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::<LittleEndian>(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(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue