use crate::{read_only_guard::ReadOnlyGuard, settings::Settings}; use anyhow::{bail, Result}; use std::{ ffi::OsString, fs::File, io::{BufReader, BufWriter, Read, Seek, SeekFrom}, os::unix::ffi::OsStringExt, path::Path, }; use zstd::stream::copy_decode; impl Settings { pub(crate) fn import_helper(&self, source: &Path) -> Result<()> { let mut source_file = File::open(source)?; source_file.seek(SeekFrom::End(0))?; loop { let length_of_entry = read_length(&source_file)?; let Ok(entry_offset) = i64::try_from(length_of_entry) else { bail!("found length of {length_of_entry} bytes, which is too large to be correct"); }; source_file.seek(SeekFrom::Current(-entry_offset))?; if source_file.stream_position()? == 0 { return import_file(&self.settings_path, source_file, length_of_entry); } let Ok(length_to_decode) = usize::try_from(length_of_entry) else { bail!("found length of {length_of_entry}, which is too long for this platform"); }; let file_name_bytes = read_bytes(&source_file, length_to_decode)?; let file_name = OsString::from_vec(file_name_bytes); let num_content_bytes = read_length(&source_file)?; let Ok(content_offset) = i64::try_from(num_content_bytes) else { bail!( "found length of {num_content_bytes} bytes, which is too large to be correct" ); }; source_file.seek(SeekFrom::Current(-content_offset))?; let destination_path = self.get_mod_file_path(file_name); import_file(destination_path, &source_file, num_content_bytes)?; } } } fn import_file( destination_path: impl AsRef, mut source: impl Read + Seek, num_bytes: u64, ) -> Result<()> { let reset_point = SeekFrom::Start(source.stream_position()?); let destination_file = ReadOnlyGuard::new(destination_path, false)?; copy_decode( BufReader::new(Read::take(&mut source, num_bytes)), BufWriter::new(destination_file), )?; source.seek(reset_point)?; Ok(()) } fn read_length(mut source: impl Read + Seek) -> Result { source.seek(std::io::SeekFrom::Current(-8))?; let reset_point = SeekFrom::Start(source.stream_position()?); let mut length_bytes = [0_u8; 8]; source.read_exact(&mut length_bytes)?; let num_bytes = u64::from_le_bytes(length_bytes); source.seek(reset_point)?; Ok(num_bytes) } fn read_bytes(mut source: impl Read + Seek, num_bytes: usize) -> Result> { let reset_point = SeekFrom::Start(source.stream_position()?); let mut bytes = vec![0_u8; num_bytes]; source.read_exact(&mut bytes)?; source.seek(reset_point)?; Ok(bytes) }