use super::common::DEFAULT_HD_CODECS;
use crate::error::DiscError;
use crate::types::DVD_METADATA_TAG;
use chdlady_core::{
ChdFile, ChdWriteConfig, ChdWriter, CreateResult, MetadataItem, ProgressStatus,
CHD_MDFLAGS_CHECKSUM,
};
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
#[derive(Debug, Clone, Default)]
pub struct CreateDvdOptions {
pub start_byte: Option<u64>,
pub start_hunk: Option<u64>,
pub length_bytes: Option<u64>,
pub length_hunks: Option<u64>,
pub hunk_size: Option<u32>,
pub codecs: Option<[u32; 4]>,
pub parent_path: Option<PathBuf>,
pub num_processors: Option<usize>,
pub cancel_token: Option<Arc<AtomicBool>>,
}
pub fn create_dvd<F>(
input_path: &Path,
output_path: &Path,
options: &CreateDvdOptions,
mut progress: F,
) -> Result<CreateResult, DiscError>
where
F: FnMut(ProgressStatus),
{
let mut input_file = File::open(input_path)?;
let total_size = input_file.metadata()?.len();
let unit_size = 2048u32;
let hunk_size = options.hunk_size.unwrap_or(4096);
let start = match (options.start_byte, options.start_hunk) {
(Some(b), _) => b,
(None, Some(h)) => h * hunk_size as u64,
(None, None) => 0,
};
let length = match (options.length_bytes, options.length_hunks) {
(Some(b), _) => b,
(None, Some(h)) => h * hunk_size as u64,
(None, None) => total_size.saturating_sub(start),
};
let end = start
.checked_add(length)
.ok_or_else(|| DiscError::InvalidMetadata("specified range overflows u64".into()))?;
if end > total_size {
return Err(DiscError::InvalidMetadata(
"specified range exceeds input file size".into(),
));
}
if !length.is_multiple_of(unit_size as u64) {
return Err(DiscError::InvalidMetadata(format!(
"data length {} is not divisible by DVD sector size 2048",
length
)));
}
let (parent_map, parent_sha1) = if let Some(ref p) = options.parent_path {
let pf = File::open(p)?;
let mut parent_chd = ChdFile::open(pf)?;
let p_sha1 = parent_chd.header().sha1;
let map = ChdWriter::build_parent_map(&mut parent_chd, hunk_size, unit_size)?;
(Some(map), p_sha1)
} else {
(None, [0u8; 20])
};
let compressors = options.codecs.unwrap_or(DEFAULT_HD_CODECS);
let config = ChdWriteConfig {
logical_bytes: length,
hunk_bytes: hunk_size,
unit_bytes: unit_size,
compressors,
parent_sha1,
num_processors: options.num_processors,
cancel_token: options.cancel_token.clone(),
};
input_file.seek(SeekFrom::Start(start))?;
let mut reader = BufReader::with_capacity(1024 * 1024, input_file.take(length));
let dvd_meta = MetadataItem {
tag: DVD_METADATA_TAG,
flags: CHD_MDFLAGS_CHECKSUM,
value: vec![0], };
let output_file = File::create(output_path)?;
let mut writer = BufWriter::new(output_file);
let result = ChdWriter::write_chd_with_parent(
&mut reader,
&mut writer,
&config,
&[dvd_meta],
parent_map.as_ref(),
&mut progress,
);
match result {
Ok(res) => Ok(res),
Err(e) => {
let _ = std::fs::remove_file(output_path);
Err(DiscError::Chd(e))
}
}
}