use std::path::Path;
use crate::streaming::run_compression;
use crate::{sys, Chd, ChdCompressor, ChdDataHandler, ChdError, CompressionProgress, Result};
const CHDMETAINDEX_APPEND: u32 = !0u32;
#[derive(Debug, Clone, Default)]
pub struct CopyOptions {
pub hunk_size: Option<u32>,
pub codecs: [u32; 4],
}
struct ChdReadSource {
chd: Chd,
}
impl ChdDataHandler for ChdReadSource {
fn read_data(&mut self, dest: &mut [u8], offset: u64) -> u32 {
match self.chd.read_bytes(offset, dest) {
Ok(()) => dest.len() as u32,
Err(_) => 0,
}
}
}
pub fn copy(
source: &Path,
dest: &Path,
opts: CopyOptions,
progress: &mut dyn FnMut(CompressionProgress),
cancel: &dyn Fn() -> bool,
) -> Result<()> {
let source_chd = Chd::open(source.to_str().ok_or(ChdError::InvalidFile)?, false, None)?;
let logical = source_chd.logical_bytes();
let unit = source_chd.unit_bytes();
let hunk = opts.hunk_size.unwrap_or(source_chd.hunk_bytes());
let metadata: Vec<crate::MetadataEntry> =
source_chd.metadata_iter().collect::<Result<Vec<_>>>()?;
let handler = ChdReadSource { chd: source_chd };
let mut compressor = ChdCompressor::new(handler);
let dest_str = dest.to_str().ok_or(ChdError::InvalidFile)?.to_string();
compressor.create_file(&dest_str, logical, hunk, unit, opts.codecs)?;
for entry in &metadata {
let err = unsafe {
sys::chd_shim_write_metadata(
compressor.as_chd_file_ptr(),
entry.tag,
CHDMETAINDEX_APPEND,
entry.data.as_ptr() as *const _,
entry.data.len() as u32,
entry.flags,
)
};
if err != ChdError::NoError {
return Err(err);
}
}
run_compression(compressor, dest, progress, cancel)
}