fix(tool): Исправлена ошибка доступа при сборке MPQ
This commit is contained in:
+90
-83
@@ -1,4 +1,5 @@
|
||||
use std::env;
|
||||
use clap::Parser;
|
||||
use log::{error, info};
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -7,105 +8,111 @@ use walkdir::WalkDir;
|
||||
use wow_mpq::compression::flags;
|
||||
use wow_mpq::{ArchiveBuilder, FormatVersion, ListfileOption};
|
||||
|
||||
/// Checks if a directory name looks like a WoW locale folder (e.g. ruRU, enUS, deDE).
|
||||
fn is_locale_dir(name: &str) -> bool {
|
||||
if name.len() != 4 {
|
||||
return false;
|
||||
}
|
||||
let b = name.as_bytes();
|
||||
b[0].is_ascii_lowercase()
|
||||
&& b[1].is_ascii_lowercase()
|
||||
&& b[2].is_ascii_uppercase()
|
||||
&& b[3].is_ascii_uppercase()
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
input_dir: PathBuf,
|
||||
|
||||
destination_dir: PathBuf,
|
||||
}
|
||||
|
||||
fn build_mpq(input_dir: &Path, output_path: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let mut output = output_path.to_path_buf();
|
||||
output.set_extension("MPQ");
|
||||
fn main() {
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.format_target(false)
|
||||
.format_timestamp(None)
|
||||
.init();
|
||||
|
||||
if let Some(parent) = output.parent() {
|
||||
let args = Args::parse();
|
||||
|
||||
if let Err(err) = run(args) {
|
||||
error!("{}", err);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
||||
info!("Starting tool...");
|
||||
|
||||
let data_dir = args.input_dir.join("Data");
|
||||
if !data_dir.exists() {
|
||||
return Err(format!("Data directory not found at {}", data_dir.display()).into());
|
||||
}
|
||||
|
||||
info!("Found Data directory at {}", data_dir.display());
|
||||
|
||||
for entry in fs::read_dir(&data_dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
if !path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let dir_name = entry.file_name().to_string_lossy().to_string();
|
||||
|
||||
if dir_name.starts_with("ruRU") {
|
||||
for sub_entry in fs::read_dir(&path)? {
|
||||
let sub_entry = sub_entry?;
|
||||
let sub_path = sub_entry.path();
|
||||
|
||||
let sub_name = sub_entry.file_name().to_string_lossy().to_string();
|
||||
let destination = args
|
||||
.destination_dir
|
||||
.join("Data")
|
||||
.join(&dir_name)
|
||||
.join(&sub_name);
|
||||
|
||||
let _ = build_mpq(&sub_path, &destination);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let destination = args.destination_dir.join("Data").join(&dir_name);
|
||||
|
||||
let _ = build_mpq(&path, &destination)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_mpq(input_dir: &Path, destination: &Path) -> Result<(), Box<dyn Error>> {
|
||||
info!(
|
||||
"Packing MPQ from {} to {}",
|
||||
input_dir.display(),
|
||||
destination.display()
|
||||
);
|
||||
|
||||
let mut destination = destination.to_path_buf();
|
||||
destination.set_extension("MPQ");
|
||||
|
||||
if let Some(parent) = destination.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
let base = input_dir.canonicalize()?;
|
||||
println!("Packing: {} -> {}", base.display(), output.display());
|
||||
|
||||
let mut builder = ArchiveBuilder::new()
|
||||
.version(FormatVersion::V2)
|
||||
.block_size(7) // 64 KB sectors
|
||||
.block_size(7)
|
||||
.default_compression(flags::ZLIB)
|
||||
.listfile_option(ListfileOption::Generate);
|
||||
|
||||
let base = input_dir.canonicalize()?;
|
||||
|
||||
for entry in WalkDir::new(&base)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.file_type().is_file())
|
||||
{
|
||||
let full_path = entry.path().canonicalize()?;
|
||||
let rel_path = match full_path.strip_prefix(&base) {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
eprintln!("Skipping (prefix mismatch): {}", full_path.display());
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let archive_path = rel_path.to_string_lossy().replace('\\', "/");
|
||||
println!(" Adding: {}", archive_path);
|
||||
builder = builder.add_file(&full_path, &archive_path);
|
||||
let absolute_path = entry.path().canonicalize()?;
|
||||
let relative_path = absolute_path.strip_prefix(&base)?;
|
||||
|
||||
builder = builder.add_file(&absolute_path, &relative_path.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
builder.build(&output)?;
|
||||
println!("Created: {}", output.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() != 3 {
|
||||
eprintln!("Usage: tool <src_dir> <output_dir>");
|
||||
eprintln!();
|
||||
eprintln!(" src_dir — root of source tree (must contain Data/)");
|
||||
eprintln!(" output_dir — destination root; MPQs are placed mirroring the src structure");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let src_dir = PathBuf::from(&args[1]);
|
||||
let output_dir = PathBuf::from(&args[2]);
|
||||
|
||||
let data_dir = src_dir.join("Data");
|
||||
if !data_dir.exists() {
|
||||
return Err(format!("Data/ not found inside src_dir: {}", src_dir.display()).into());
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(&data_dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if !path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
|
||||
if is_locale_dir(&name) {
|
||||
// e.g. ruRU/ — iterate its immediate subdirs, each is a patch source
|
||||
for sub in fs::read_dir(&path)? {
|
||||
let sub = sub?;
|
||||
let sub_path = sub.path();
|
||||
if !sub_path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
let sub_name = sub.file_name().to_string_lossy().to_string();
|
||||
let output_mpq = output_dir.join("Data").join(&name).join(&sub_name);
|
||||
build_mpq(&sub_path, &output_mpq)?;
|
||||
}
|
||||
} else {
|
||||
// Regular patch dir, e.g. patch-4/ or patch-Z/
|
||||
let output_mpq = output_dir.join("Data").join(&name);
|
||||
build_mpq(&path, &output_mpq)?;
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nAll MPQ archives built successfully.");
|
||||
let _ = builder.build(&destination);
|
||||
|
||||
info!("Successfully packed {}", destination.display());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user