use crate::Settings;
use anyhow::Result;
use std::{
fs::OpenOptions,
io::{BufReader, Read, Write},
path::Path,
};
use zstd::stream::copy_encode;
macro_rules! u64_from_usize {
($value:expr) => {{
let value: usize = $value;
u64::try_from(value).unwrap_or_else(|_| {
static_assertions_next::assert_cfg!(any(
target_pointer_width = "32",
target_pointer_width = "64"
));
unreachable!();
})
}};
}
impl Settings {
pub(crate) fn export_helper(&self, destination: &Path) -> Result<()> {
let mut destination_file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(destination)?;
let settings_file = self.open_mod_settings_file()?;
export_file(settings_file, &mut destination_file)?;
for entry in self.read_mod_files_dir()? {
let entry = entry?;
let file_name = entry.file_name();
let extension = Path::new(&file_name).extension().unwrap_or_default();
if !&extension.eq_ignore_ascii_case("pak") {
continue;
}
let mod_file = self.open_mod_file(&file_name)?;
export_file(mod_file, &mut destination_file)?;
let file_name_bytes = file_name.as_encoded_bytes();
destination_file.write_all(file_name_bytes)?;
destination_file.write_all(
u64_from_usize!(file_name_bytes.len())
.to_le_bytes()
.as_slice(),
)?;
}
Ok(())
}
}
struct CountWrite<W> {
num_bytes_written: usize,
inner: W,
}
impl<W> CountWrite<W> {
const fn new(inner: W) -> Self {
Self {
num_bytes_written: 0,
inner,
}
}
}
impl<W: Write> Write for CountWrite<W> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let num_bytes_written = self.inner.write(buf)?;
self.num_bytes_written += num_bytes_written;
Ok(num_bytes_written)
}
fn flush(&mut self) -> std::io::Result<()> {
self.inner.flush()
}
}
fn export_file(file: impl Read, destination: impl Write) -> Result<()> {
let mut file = BufReader::new(file);
let mut destination = CountWrite::new(destination);
copy_encode(&mut file, &mut destination, 0)?;
destination.write_all(
u64_from_usize!(destination.num_bytes_written)
.to_le_bytes()
.as_slice(),
)?;
Ok(())
}