Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ bitflags = "2.13.0"
goblin = "0.10.7"
encoding_rs = "0.8.35"
codepage = "0.1.2"
flate2 = "1.1.9"
lzma-rs = "0.3.0"
flate2 = { version = "1.1.9", default-features = false, features = ["zlib-rs"] }
lzma-rust2 = { version = "0.18", default-features = false, features = ["std", "optimization"] }
bzip2 = "0.6.1"
sha1 = "0.11.0"
sha2 = "0.11.0"
Expand All @@ -44,3 +44,4 @@ chacha20 = { version = "0.10.1", features = ["xchacha"] }
pascalscript = "0.1.2"

[dev-dependencies]
lzma-rs = "0.3"
42 changes: 24 additions & 18 deletions src/decompress/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
use std::io::{Cursor, Read as _};

use flate2::read::ZlibDecoder;
use lzma_rs::decompress::{Options, UnpackedSize};
use lzma_rust2::LzmaReader;

use crate::{
error::Error,
Expand Down Expand Up @@ -358,24 +358,30 @@ fn decompress_zlib(raw: &[u8]) -> Result<Vec<u8>, Error> {

fn decompress_inno_lzma1(raw: &[u8]) -> Result<Vec<u8>, Error> {
// Inno's 5-byte LZMA1 properties header: byte 0 is
// `pb*45 + lp*9 + lc`, bytes 1..4 are LE dict_size. lzma-rs
// accepts this directly when configured with
// UnpackedSize::UseProvided(None) — that tells the decoder to
// skip the (absent) 8-byte uncompressed-size field and rely on
// the end-of-payload marker.
let mut input = Cursor::new(raw);
// `pb*45 + lp*9 + lc`, bytes 1..4 are LE dict_size. lzma-rust2's
// LzmaReader consumes a standard 13-byte LZMA-Alone header, so pad
// the 5-byte Inno header with the 8-byte unknown-size sentinel
// (u64::MAX) and rely on the end-of-payload marker — the same
// stream shape the nsis crate decodes.
let mut header = Vec::with_capacity(raw.len().saturating_add(8));
header.extend_from_slice(raw.get(..5).ok_or_else(|| Error::Decompress {
stream: "block (lzma1)",
source: std::io::Error::other("LZMA1 header too short"),
})?);
header.extend_from_slice(&u64::MAX.to_le_bytes());
header.extend_from_slice(raw.get(5..).unwrap_or(&[]));

let mut out = Vec::new();
let opts = Options {
unpacked_size: UnpackedSize::UseProvided(None),
..Options::default()
};
lzma_rs::lzma_decompress_with_options(&mut input, &mut out, &opts).map_err(|e| {
Error::Decompress {
stream: "block (lzma1)",
// lzma_rs::error::Error → io::Error via the Display impl;
// wrap manually so we keep the message.
source: std::io::Error::other(e.to_string()),
}
let mut reader =
LzmaReader::new_mem_limit(Cursor::new(header), u32::MAX, None).map_err(|e| {
Error::Decompress {
stream: "block (lzma1)",
source: std::io::Error::other(e.to_string()),
}
})?;
std::io::copy(&mut reader, &mut out).map_err(|e| Error::Decompress {
stream: "block (lzma1)",
source: e,
})?;
Ok(out)
}
Expand Down
31 changes: 19 additions & 12 deletions src/extract/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ pub(crate) fn decompress_chunk(
});
};
let mut input = io::BufReader::new(stream);
lzma_rs::lzma2_decompress(&mut input, &mut out).map_err(|e| Error::Decompress {
let mut reader = lzma_rust2::Lzma2Reader::new(&mut input, 32 * 1024 * 1024, None);
io::copy(&mut reader, &mut out).map_err(|e| Error::Decompress {
stream: "chunk LZMA2",
source: io::Error::other(e.to_string()),
})?;
Expand All @@ -256,19 +257,25 @@ pub(crate) fn decompress_chunk(
fn decompress_lzma1(compressed: &[u8], out: &mut Vec<u8>) -> Result<(), Error> {
// Inno's LZMA1 chunk format: [5-byte properties | raw stream].
// No 8-byte uncompressed-size field — same as the setup-0 outer
// block path. `UseProvided(None)` tells lzma-rs to skip the
// (absent) size field and trust the end-of-payload marker.
let mut input = io::BufReader::new(compressed);
let opts = lzma_rs::decompress::Options {
unpacked_size: lzma_rs::decompress::UnpackedSize::UseProvided(None),
memlimit: None,
allow_incomplete: false,
};
lzma_rs::lzma_decompress_with_options(&mut input, out, &opts).map_err(|e| {
Error::Decompress {
// block path. Pad with the 8-byte unknown-size sentinel (u64::MAX)
// so lzma-rust2's LzmaReader can consume the 13-byte LZMA-Alone
// header and rely on the end-of-payload marker.
let mut header = Vec::with_capacity(compressed.len().saturating_add(8));
header.extend_from_slice(compressed.get(..5).ok_or_else(|| Error::Decompress {
stream: "chunk LZMA1",
source: io::Error::other("LZMA1 header too short"),
})?);
header.extend_from_slice(&u64::MAX.to_le_bytes());
header.extend_from_slice(compressed.get(5..).unwrap_or(&[]));

let mut reader = lzma_rust2::LzmaReader::new_mem_limit(io::Cursor::new(header), u32::MAX, None)
.map_err(|e| Error::Decompress {
stream: "chunk LZMA1",
source: io::Error::other(e.to_string()),
}
})?;
io::copy(&mut reader, out).map_err(|e| Error::Decompress {
stream: "chunk LZMA1",
source: e,
})?;
Ok(())
}
Expand Down