Made code more maintainable, fixed bugs

This commit is contained in:
G2-Games 2024-07-07 03:54:45 -05:00
parent 1a9a05b004
commit 9e49b22b23
2 changed files with 75 additions and 52 deletions

View file

@ -257,7 +257,7 @@ impl Pak {
debug!("remainder {}", remainder); debug!("remainder {}", remainder);
debug!("block_offset {} - expected offset {}", block_offset, entry.offset); debug!("block_offset {} - expected offset {}", block_offset, entry.offset);
output.write_all(&entry.data)?; output.write_all(&entry.data)?;
output.write_all(&vec![0u8; remainder as usize])?; output.write_all(&vec![0u8; remainder])?;
block_offset += block_size as u32; block_offset += block_size as u32;
} }
@ -331,6 +331,23 @@ impl Pak {
Ok(()) Ok(())
} }
pub fn replace_by_id(
&mut self,
id: u32,
replacement_bytes: &[u8],
) -> Result<(), PakError> {
let entry = self.get_entry_by_id(id);
let index = if let Some(entry) = entry {
entry.index
} else {
return Err(PakError::IndexError)
};
self.replace(index, replacement_bytes)?;
Ok(())
}
/// Get the header information from the PAK /// Get the header information from the PAK
pub fn header(&self) -> &Header { pub fn header(&self) -> &Header {
&self.header &self.header
@ -350,7 +367,7 @@ impl Pak {
self.entries self.entries
.iter_mut() .iter_mut()
.find(|e| .find(|e|
e.name.as_ref().is_some_and(|n| n == &name) e.name.as_ref().is_some_and(|n| n == name)
) )
} }
@ -364,6 +381,6 @@ impl Pak {
self.entries self.entries
.iter() .iter()
.any(|e| e.name.as_ref() .any(|e| e.name.as_ref()
.is_some_and(|n| n == &name)) .is_some_and(|n| n == name))
} }
} }

View file

@ -1,5 +1,5 @@
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
use clap::{error::ErrorKind, Error, Parser, Subcommand}; use clap::{error::{Error, ErrorKind}, Parser, Subcommand};
use luca_pak::Pak; use luca_pak::Pak;
#[derive(Parser)] #[derive(Parser)]
@ -31,10 +31,16 @@ enum Commands {
/// The name of the file within the PAK you wish to replace. /// The name of the file within the PAK you wish to replace.
/// If not provided, the filename will be used. /// If not provided, the filename will be used.
/// Icompatible with batch mode. /// Incompatible with batch mode, and ID.
#[arg(short, long)] #[arg(short, long)]
name: Option<String>, name: Option<String>,
/// The ID of the file within the PAK you wish to replace.
/// If not provided, the filename will be used.
/// Incompatible with batch mode, and name.
#[arg(short, long)]
id: Option<u32>,
/// File or folder to use as a replacement /// File or folder to use as a replacement
#[arg(value_name = "REPLACEMENT")] #[arg(value_name = "REPLACEMENT")]
replacement: PathBuf, replacement: PathBuf,
@ -48,44 +54,34 @@ enum Commands {
fn main() { fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
if !cli.input.is_file() { let mut pak = match Pak::open(&cli.input) {
Error::raw(ErrorKind::ValueValidation, Ok(pak) => pak,
"The input file/folder provided is not a file\n", Err(err) => fmt_error(&format!("Could not open PAK file: {}", err)).exit()
).exit() };
}
let mut pak = Pak::open(&cli.input).unwrap();
match cli.command { match cli.command {
Commands::Extract { output } => { Commands::Extract { output } => {
if !output.is_dir() { if output.exists() && !output.is_dir() {
Error::raw(ErrorKind::ValueValidation, fmt_error("The output given was not a directory").exit()
"The output given was not a directory\n", } else if !output.exists() {
).exit() fs::create_dir(&output).unwrap();
} }
for entry in pak.entries() { for entry in pak.entries() {
entry.save(&output).unwrap(); entry.save(&output).unwrap();
} }
}, },
Commands::Replace { batch, name, replacement, output } => { Commands::Replace { batch, name, id, replacement, output } => {
if !output.is_file() { if id.is_some() && name.is_some() {
Error::raw(ErrorKind::ValueValidation, fmt_error("Cannot use ID and name together").exit()
"Replacement output must be a file\n",
).exit()
} }
if batch { if batch {
if name.is_some() { if name.is_some() || id.is_some() {
Error::raw(ErrorKind::ValueValidation, fmt_error("Cannot use name or ID with batch").exit()
"Cannot use name with batch\n",
).exit()
} }
if !replacement.is_dir() { if !replacement.is_dir() {
Error::raw(ErrorKind::ValueValidation, fmt_error("Batch replacement must be a directory").exit()
"Batch replacement must be a directory\n",
).exit()
} }
for entry in fs::read_dir(replacement).unwrap() { for entry in fs::read_dir(replacement).unwrap() {
@ -97,23 +93,21 @@ fn main() {
.to_string_lossy() .to_string_lossy()
.into(); .into();
dbg!(&search_name); let parsed_id: Option<u32> = search_name.parse().ok();
// Read in the replacement file to a vec // Read in the replacement file to a vec
let rep_data: Vec<u8> = std::fs::read(entry.path()).unwrap(); let rep_data: Vec<u8> = std::fs::read(entry.path()).unwrap();
if let Err(err) = pak.replace_by_name(search_name, &rep_data) {
Error::raw(ErrorKind::ValueValidation,
format!("Failed to replace file in PAK: {}\n", err),
).exit()
}
}
pak.save(&output).unwrap(); // Try replacing by name, if that fails, replace by parsed ID
if pak.replace_by_name(search_name, &rep_data).is_err() {
fmt_error("Could not replace entry in PAK: Could not find name").print().unwrap()
} else if parsed_id.is_some() && pak.replace_by_id(parsed_id.unwrap(), &rep_data).is_err() {
fmt_error("Could not replace entry in PAK: ID is invalid").print().unwrap()
}
}
} else { } else {
if !replacement.is_file() { if !replacement.is_file() {
Error::raw(ErrorKind::ValueValidation, fmt_error("Replacement input must be a file").exit()
"Replacement input must be a file\n",
).exit()
} }
let search_name = if let Some(name) = name { let search_name = if let Some(name) = name {
@ -126,24 +120,36 @@ fn main() {
.into() .into()
}; };
let search_id = if id.is_some() {
id
} else if let Ok(id) = search_name.parse::<u32>() {
Some(id)
} else {
None
};
// Read in the replacement file to a vec // Read in the replacement file to a vec
let rep_data: Vec<u8> = std::fs::read(replacement).unwrap(); let rep_data: Vec<u8> = std::fs::read(replacement).unwrap();
if let Err(err) = pak.replace_by_name(search_name, &rep_data) { if id.is_some() {
Error::raw(ErrorKind::ValueValidation, if pak.replace_by_id(search_id.unwrap(), &rep_data).is_err() {
format!("Failed to replace file in PAK: {}\n", err), fmt_error("Could not replace entry in PAK: ID is invalid").exit()
).exit() }
} else if pak.replace_by_name(search_name, &rep_data).is_err() {
fmt_error("Could not replace entry in PAK: Could not find name").exit()
}
pak.save(&output).unwrap();
} }
pak.save(&output).unwrap(); pak.save(&output).unwrap();
} }
},
} }
}
/*
let rep_cz_data: Vec<u8> = std::fs::read("en_manual01_Linkto_2_6.cz1").unwrap(); #[inline(always)]
pak.replace(4, &rep_cz_data).unwrap(); fn fmt_error(message: &str) -> Error {
Error::raw(
let mut output = BufWriter::new(File::create("MANUAL-modified.PAK").unwrap()); ErrorKind::ValueValidation,
pak.encode(&mut output).unwrap(); format!("{}\n", message),
*/ )
} }