use crate::{
decompress::{self, CompressionMode},
error::Error,
installer::NsisInstaller,
nsis::entry::{Entry, EntryIter},
opcode,
strings::NsisString,
};
pub struct ExtractedFile<'a> {
installer: &'a NsisInstaller<'a>,
entry: Entry<'a>,
}
impl<'a> ExtractedFile<'a> {
pub fn name(&self) -> Result<NsisString, Error> {
self.installer.read_string(self.entry.offset(1))
}
#[inline]
pub fn overwrite_flags(&self) -> i32 {
self.entry.offset(0)
}
#[inline]
pub fn data_block_offset(&self) -> u32 {
self.entry.offset(2) as u32
}
pub fn datetime(&self) -> Option<(u32, u32)> {
let lo = self.entry.offset(3);
let hi = self.entry.offset(4);
if lo == 0 && hi == 0 {
None
} else {
Some((lo as u32, hi as u32))
}
}
pub fn is_compressed(&self) -> bool {
let Some((is_compressed, _)) = self.length_prefix() else {
return false;
};
is_compressed
}
pub fn data(&self) -> &[u8] {
let Some((_, size)) = self.length_prefix() else {
return &[];
};
let source = self.data_source();
let offset = self.source_offset() + 4; let end = offset + size as usize;
if end <= source.len() {
&source[offset..end]
} else {
&[]
}
}
pub fn decompress(&self) -> Result<Vec<u8>, Error> {
let Some((is_compressed, size)) = self.length_prefix() else {
return Err(Error::TooShort {
expected: 4,
actual: 0,
context: "file data length prefix",
});
};
let source = self.data_source();
let offset = self.source_offset() + 4;
let end = offset + size as usize;
if end > source.len() {
return Err(Error::TooShort {
expected: end,
actual: source.len(),
context: "file data payload",
});
}
let payload = &source[offset..end];
if !is_compressed {
return Ok(payload.to_vec());
}
let max_output = (size as usize * 10).max(64 * 1024 * 1024);
decompress::decompress_block(
payload,
self.installer.compression(),
max_output,
Some(max_output),
)
}
#[inline]
pub fn entry(&self) -> &Entry<'a> {
&self.entry
}
fn data_source(&self) -> &[u8] {
if self.installer.compression_mode() == CompressionMode::Solid {
self.installer.solid_data()
} else {
self.installer.file_data()
}
}
fn source_offset(&self) -> usize {
if self.installer.compression_mode() == CompressionMode::Solid {
self.data_block_offset() as usize
} else {
self.installer.data_block_offset() + self.data_block_offset() as usize
}
}
fn length_prefix(&self) -> Option<(bool, u32)> {
let source = self.data_source();
let offset = self.source_offset();
if offset + 4 > source.len() {
return None;
}
decompress::read_length_prefix(&source[offset..]).ok()
}
}
pub struct FileIter<'a> {
installer: &'a NsisInstaller<'a>,
entries: EntryIter<'a>,
}
impl<'a> FileIter<'a> {
pub(crate) fn new(installer: &'a NsisInstaller<'a>, entries: EntryIter<'a>) -> Self {
Self { installer, entries }
}
}
impl<'a> Iterator for FileIter<'a> {
type Item = Result<ExtractedFile<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let entry_result = self.entries.next()?;
match entry_result {
Ok(entry) => {
if entry.which() == opcode::EW_EXTRACTFILE {
return Some(Ok(ExtractedFile {
installer: self.installer,
entry,
}));
}
}
Err(e) => return Some(Err(e)),
}
}
}
}