You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

76 lines
2.6 KiB

use std::fs;
use std::io::Write;
use std::path;
use nom::Err::{Error as NomError, Failure as NomFailure};
use crate::formats::dat;
use crate::unpackers::errors::UnpackError;
use crate::utils::fs as utils_fs;
/// Extract DAT file contents to `output_path`.
///
/// Wraps around `extract_dat` to load the DAT from disk.
pub fn extract_dat_file(dat_path: &str, output_path: &str) -> Result<(), UnpackError> {
let (dat, dat_data) = load_dat_file(dat_path)?;
extract_dat(&dat, dat_data, output_path)
}
/// Extract DAT contents to `output_path`.
pub fn extract_dat(
dat: &dat::Dat,
dat_data: Vec<u8>,
output_path: &str
) -> Result<(), UnpackError> {
let output_dir = path::Path::new(output_path);
utils_fs::ensure_dir_exists(output_dir)?;
for file_entry in &dat.files {
match extract_file(file_entry, &dat_data, output_dir) {
Err(UnpackError::Io(e)) => eprintln!("Can't extract {}: {}", file_entry.name, e),
_ => {}
}
}
Ok(())
}
/// Extract one `DatFileEntry`, preserving internal dir structure.
fn extract_file(
file_entry: &dat::DatFileEntry,
data: &Vec<u8>,
output_dir: &path::Path
) -> Result<(), UnpackError> {
let ofs_start = file_entry.ofs_data as usize;
let ofs_end = ofs_start + file_entry.size as usize;
let data = &data[ofs_start..ofs_end];
let internal_path = &file_entry.name;
// If the path contains dirs, they have to be created.
if internal_path.contains('/') {
let internal_pb = path::PathBuf::from(internal_path);
let internal_parent_dir = internal_pb.parent()
.ok_or(UnpackError::Naming(format!("Bad path: {:?}", internal_pb)))?;
let mut parent_dir = output_dir.to_path_buf();
parent_dir.push(internal_parent_dir);
utils_fs::ensure_dir_exists(&parent_dir)?;
}
let mut file_path = output_dir.to_path_buf();
file_path.push(internal_path);
let mut output_file = fs::File::create(file_path)?;
output_file.write_all(&data)?;
Ok(())
}
/// Load a DAT file from disk.
pub fn load_dat_file(dat_path: &str) -> Result<(dat::Dat, Vec<u8>), UnpackError> {
let dat_data = utils_fs::open_file_to_vec(path::Path::new(dat_path))?;
Ok((load_dat(&dat_data)?, dat_data))
}
/// Load a DAT file from a bytes slice.
pub fn load_dat(dat_data: &[u8]) -> Result<dat::Dat, UnpackError> {
match dat::parse(&dat_data) {
Ok((_, dat)) => Ok(dat),
Err(NomError(e)) | Err(NomFailure(e)) => Err(UnpackError::parsing_err("DAT", e.1)),
e => Err(UnpackError::Unknown(format!("Unknown error: {:?}", e))),
}
}