use std::path::{Path, PathBuf};
use crate::Result;
#[cfg(not(feature = "aes"))]
use crate::format::parser::read_archive_header;
use crate::format::streams::ResourceLimits;
use crate::volume::{MultiVolumeReader, VolumeReader};
use super::entries;
use super::{Archive, VolumeInfo};
pub(crate) fn detect_multivolume_base(path: &Path) -> Option<PathBuf> {
let path_str = path.to_string_lossy();
if let Some(pos) = path_str.rfind(".7z.") {
let suffix = &path_str[pos + 4..];
if !suffix.is_empty() && suffix.chars().all(|c| c.is_ascii_digit()) {
return Some(PathBuf::from(&path_str[..pos + 3]));
}
}
if path_str.ends_with(".7z") {
let first_volume = PathBuf::from(format!("{}.001", path_str));
if first_volume.exists() {
return Some(path.to_path_buf());
}
}
None
}
impl Archive<MultiVolumeReader> {
pub fn open_multivolume(path: impl AsRef<Path>) -> Result<Self> {
Self::open_multivolume_internal(
path.as_ref(),
#[cfg(feature = "aes")]
None,
)
}
#[cfg(feature = "aes")]
pub fn open_multivolume_with_password(
path: impl AsRef<Path>,
password: impl Into<crate::crypto::Password>,
) -> Result<Self> {
Self::open_multivolume_internal(path.as_ref(), Some(password.into()))
}
fn open_multivolume_internal(
path: &Path,
#[cfg(feature = "aes")] password: Option<crate::crypto::Password>,
) -> Result<Self> {
let reader = MultiVolumeReader::open(path)?;
let volume_count = reader.volume_count();
let volume_paths: Vec<PathBuf> = (1..=volume_count)
.map(|n| reader.get_volume_path(n))
.collect();
let limits = ResourceLimits::default();
#[cfg(feature = "aes")]
let (_start_header, header) = crate::format::parser::read_archive_header_with_password(
&mut { reader },
Some(limits),
password.clone(),
)?;
#[cfg(not(feature = "aes"))]
let (_start_header, header) = read_archive_header(&mut { reader }, Some(limits))?;
let entries = entries::build_entries(&header);
let info = entries::build_info(&header, &entries);
let reader = MultiVolumeReader::open(path)?;
Ok(Self {
reader,
header,
entries,
info,
#[cfg(feature = "aes")]
password,
volume_info: Some(VolumeInfo {
count: volume_count,
paths: volume_paths,
}),
sfx_offset: 0, })
}
}
pub(crate) fn open_multivolume_as_single(
base_path: &Path,
limits: ResourceLimits,
#[cfg(feature = "aes")] password: Option<crate::crypto::Password>,
) -> Result<Archive<super::ArchiveSource>> {
let reader = MultiVolumeReader::open(base_path)?;
let volume_count = reader.volume_count();
let volume_paths: Vec<PathBuf> = (1..=volume_count)
.map(|n| reader.get_volume_path(n))
.collect();
#[cfg(feature = "aes")]
let (_start_header, header) = crate::format::parser::read_archive_header_with_password(
&mut { reader },
Some(limits),
password.clone(),
)?;
#[cfg(not(feature = "aes"))]
let (_start_header, header) = read_archive_header(&mut { reader }, Some(limits))?;
let entries = entries::build_entries(&header);
let info = entries::build_info(&header, &entries);
let reader = MultiVolumeReader::open(base_path)?;
Ok(Archive {
reader: super::ArchiveSource::Volumes(Box::new(reader)),
header,
entries,
info,
#[cfg(feature = "aes")]
password,
volume_info: Some(VolumeInfo {
count: volume_count,
paths: volume_paths,
}),
sfx_offset: 0, })
}