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
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))),
|
|
}
|
|
}
|