mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-09 14:28:53 +08:00
Add pack-v2 command which support multiple input files/folders
This commit is contained in:
14
src/args.rs
14
src/args.rs
@@ -638,6 +638,20 @@ pub enum Command {
|
||||
/// Output script file
|
||||
output: Option<String>,
|
||||
},
|
||||
/// Pack files to archive (version 2)
|
||||
PackV2 {
|
||||
#[arg(short = 'o', long)]
|
||||
/// Output archive file
|
||||
output: Option<String>,
|
||||
/// Path to input files/directories
|
||||
input: Vec<String>,
|
||||
#[arg(long)]
|
||||
/// Use \ as path separator instead of / in archive
|
||||
backslash: bool,
|
||||
#[arg(long)]
|
||||
/// Do not create directory entries in archive. This means all files are stored in a flat structure.
|
||||
no_dir: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn parse_args() -> Arg {
|
||||
|
||||
139
src/main.rs
139
src/main.rs
@@ -2380,6 +2380,121 @@ pub fn pack_archive(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pack_archive_v2(
|
||||
input: &[&str],
|
||||
output: Option<&str>,
|
||||
arg: &args::Arg,
|
||||
config: std::sync::Arc<types::ExtraConfig>,
|
||||
backslash: bool,
|
||||
no_dir: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let typ = match &arg.script_type {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
return Err(anyhow::anyhow!("No script type specified"));
|
||||
}
|
||||
};
|
||||
// File List in real path
|
||||
let mut files = Vec::new();
|
||||
// File list in archive path
|
||||
let mut re_files = Vec::new();
|
||||
for i in input {
|
||||
let (fs, is_dir) = utils::files::collect_files(i, arg.recursive, true)?;
|
||||
if is_dir {
|
||||
files.extend_from_slice(&fs);
|
||||
for n in fs.iter() {
|
||||
if no_dir {
|
||||
if let Some(p) = std::path::PathBuf::from(n).file_name() {
|
||||
re_files.push(p.to_string_lossy().into_owned());
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Failed to get filename from {}", n));
|
||||
}
|
||||
} else {
|
||||
if let Some(p) = {
|
||||
std::path::PathBuf::from(n)
|
||||
.strip_prefix(i)
|
||||
.ok()
|
||||
.and_then(|p| {
|
||||
p.to_str().map(|s| {
|
||||
if backslash {
|
||||
s.replace("/", "\\").trim_start_matches("\\").to_owned()
|
||||
} else {
|
||||
s.replace("\\", "/").trim_start_matches("/").to_owned()
|
||||
}
|
||||
})
|
||||
})
|
||||
} {
|
||||
re_files.push(p);
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Failed to get relative path from {}", n));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
files.push(i.to_string());
|
||||
let p = std::path::PathBuf::from(i);
|
||||
if let Some(fname) = p.file_name() {
|
||||
re_files.push(fname.to_string_lossy().into_owned());
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Failed to get filename from {}", i));
|
||||
}
|
||||
}
|
||||
}
|
||||
let reff = re_files.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
let builder = scripts::BUILDER
|
||||
.iter()
|
||||
.find(|b| b.script_type() == typ)
|
||||
.ok_or_else(|| anyhow::anyhow!("Unsupported script type"))?;
|
||||
let output = match output {
|
||||
Some(output) => output.to_string(),
|
||||
None => {
|
||||
let mut pb = std::path::PathBuf::from(input[0]);
|
||||
let ext = builder.extensions().first().unwrap_or(&"unk");
|
||||
pb.set_extension(ext);
|
||||
if pb.to_string_lossy() == input[0] {
|
||||
pb.set_extension(format!("{}.{}", ext, ext));
|
||||
}
|
||||
pb.to_string_lossy().into_owned()
|
||||
}
|
||||
};
|
||||
let mut archive = builder.create_archive(
|
||||
&output,
|
||||
&reff,
|
||||
get_archived_encoding(arg, builder, get_encoding(arg, builder)),
|
||||
&config,
|
||||
)?;
|
||||
for (file, name) in files.iter().zip(reff) {
|
||||
let mut f = match std::fs::File::open(file) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
eprintln!("Error opening file {}: {}", file, e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut wf = match archive.new_file_non_seek(name) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
eprintln!("Error creating file {} in archive: {}", name, e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
match std::io::copy(&mut f, &mut wf) {
|
||||
Ok(_) => {
|
||||
COUNTER.inc(types::ScriptResult::Ok);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error writing to file {} in archive: {}", name, e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
archive.write_header()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpack_archive(
|
||||
filename: &str,
|
||||
arg: &args::Arg,
|
||||
@@ -3046,6 +3161,30 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
args::Command::PackV2 {
|
||||
output,
|
||||
input,
|
||||
backslash,
|
||||
no_dir,
|
||||
} => {
|
||||
if !input.is_empty() {
|
||||
let input = input.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
let re = pack_archive_v2(
|
||||
&input,
|
||||
output.as_ref().map(|s| s.as_str()),
|
||||
&arg,
|
||||
cfg.clone(),
|
||||
*backslash,
|
||||
*no_dir,
|
||||
);
|
||||
if let Err(e) = re {
|
||||
COUNTER.inc_error();
|
||||
eprintln!("Error packing archive: {}", e);
|
||||
}
|
||||
} else {
|
||||
eprintln!("No input files specified for packing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
eprintln!("{}", std::ops::Deref::deref(&COUNTER));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user