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.
101 lines
3.5 KiB
101 lines
3.5 KiB
use std::fs;
|
|
use std::io::{self, Write};
|
|
use std::path;
|
|
|
|
use crate::formats::common::Pack;
|
|
use crate::formats::dat;
|
|
use crate::utils::bin as utils_bin;
|
|
use crate::utils::fs as utils_fs;
|
|
|
|
/// Pack a directory as a DAT archive.
|
|
///
|
|
/// Walks recursively in `files_path` to build all file entries.
|
|
/// For performance and laziness, the archive is built directly in RAM.
|
|
pub fn pack_dat(files_path: &str, output_path: &str) -> Result<(), io::Error> {
|
|
// Pack all files and entries description in memory.
|
|
let files_path = path::Path::new(files_path);
|
|
let mut entries = vec!();
|
|
let mut files_data = vec!();
|
|
pack_dat_dir(files_path, "", &mut entries, &mut files_data)?;
|
|
|
|
let mut output_file = fs::File::create(output_path)?;
|
|
let mut ofs = 0usize;
|
|
|
|
// Write header.
|
|
let header = dat::DatHeader { unk00: dat::MAGIC, num_files: entries.len() as u32 };
|
|
header.write(&mut output_file)?;
|
|
output_file.write_all(&vec![0u8; dat::HEADER_PAD])?;
|
|
ofs += dat::HEADER_SIZE;
|
|
|
|
// Write entries, but shift their data offset beforehand.
|
|
let entries_size = entries.len() * dat::FILE_ENTRY_SIZE;
|
|
let entries_pad = utils_bin::pad(ofs + entries_size, dat::DATA_ALIGN);
|
|
let ofs_data = ofs + entries_size + entries_pad;
|
|
for entry in &mut entries {
|
|
entry.ofs_data += ofs_data as u32;
|
|
entry.write(&mut output_file)?;
|
|
}
|
|
output_file.write_all(&vec![0u8; entries_pad])?;
|
|
|
|
// Finally, write files data.
|
|
output_file.write_all(&files_data)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Recursively walks in `dir` to create `DatFileEntry`s.
|
|
///
|
|
/// `prefix` is initially "" and will contain current relative dir with
|
|
/// separator suffixed during walks, e.g. "param/".
|
|
fn pack_dat_dir(
|
|
dir: &path::Path,
|
|
prefix: &str,
|
|
entries: &mut Vec<dat::DatFileEntry>,
|
|
files_data: &mut Vec<u8>,
|
|
) -> Result<(), io::Error> {
|
|
for entry in fs::read_dir(dir)? {
|
|
let entry = entry?.path();
|
|
if entry.is_dir() {
|
|
if let Some(dir_name) = entry.file_name().and_then(|n| n.to_str()) {
|
|
let mut prefix = String::from(prefix);
|
|
prefix.push_str(dir_name);
|
|
prefix.push(dat::INTERNAL_PATH_SEP);
|
|
pack_dat_dir(&entry, &prefix, entries, files_data)?;
|
|
}
|
|
} else if entry.is_file() /* No symlink support. */ {
|
|
if let Some(name) = entry.file_name().and_then(|n| n.to_str()) {
|
|
if let Ok(metadata) = entry.metadata() {
|
|
let mut entry_name = String::from(prefix);
|
|
entry_name.push_str(name);
|
|
pack_dat_entry(&entry, entry_name, &metadata, entries, files_data)?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Pack the file in `files_data` and update `entries` accordingly.
|
|
fn pack_dat_entry(
|
|
file_entry: &path::PathBuf,
|
|
internal_name: String,
|
|
metadata: &fs::Metadata,
|
|
entries: &mut Vec<dat::DatFileEntry>,
|
|
files_data: &mut Vec<u8>,
|
|
) -> Result<(), io::Error> {
|
|
let file_size = metadata.len() as u32;
|
|
let padding = utils_bin::pad(file_size as usize, dat::DATA_ALIGN);
|
|
entries.push(dat::DatFileEntry {
|
|
name: internal_name,
|
|
size: file_size,
|
|
padded_size: file_size + padding as u32,
|
|
ofs_data: files_data.len() as u32, // Data will be pushed at the current end of file.
|
|
});
|
|
|
|
let mut data = utils_fs::open_file_to_vec(file_entry)?;
|
|
files_data.append(&mut data);
|
|
let mut padding_data = vec![0u8; padding];
|
|
files_data.append(&mut padding_data);
|
|
Ok(())
|
|
}
|