From 42be12392a0747fbd1b03662308a6a297b72eda6 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Tue, 2 Jul 2024 12:01:47 -0500 Subject: [PATCH] Improved modularity of tool --- cz/src/color.rs | 4 - cz/src/common.rs | 9 +- utils/src/main.rs | 229 +++++++++++++++++++--------------------------- 3 files changed, 98 insertions(+), 144 deletions(-) diff --git a/cz/src/color.rs b/cz/src/color.rs index a1543a6..d8c6795 100644 --- a/cz/src/color.rs +++ b/cz/src/color.rs @@ -107,13 +107,9 @@ pub fn indexed_gen_palette( .map(|c| Rgba([c.r, c.g, c.b, c.a])) .collect(); - dbg!(gen_palette.len()); - let mut output_palette = vec![Rgba([0, 0, 0, 0]); 256]; output_palette[0..gen_palette.len()].copy_from_slice(&gen_palette); - dbg!(output_palette.len()); - Ok((indicies, output_palette)) } diff --git a/cz/src/common.rs b/cz/src/common.rs index fca5c98..53366dd 100644 --- a/cz/src/common.rs +++ b/cz/src/common.rs @@ -16,6 +16,9 @@ pub enum CzError { #[error("Bitmap size does not match image size")] BitmapFormat, + #[error("CZ version is invalid: {}", 0)] + InvalidVersion(u32), + #[error("File data is incorrect, it might be corrupt: {0}")] Corrupt(String), @@ -156,10 +159,10 @@ impl CommonHeader { self.version } - pub fn set_version>(&mut self, version: I) -> Result<(), ()> { - self.version = match version.try_into() { + pub fn set_version + Into + Clone>(&mut self, version: I) -> Result<(), CzError> { + self.version = match version.clone().try_into() { Ok(val) => val, - Err(_) => return Err(()), + Err(_) => return Err(CzError::InvalidVersion(version.into())), }; Ok(()) diff --git a/utils/src/main.rs b/utils/src/main.rs index 4929a8a..4006811 100644 --- a/utils/src/main.rs +++ b/utils/src/main.rs @@ -1,7 +1,5 @@ use cz::DynamicCz; - -use std::path::PathBuf; - +use std::path::{Path, PathBuf}; use clap::{error::ErrorKind, Error, Parser, Subcommand}; #[derive(Parser)] @@ -54,7 +52,7 @@ enum Commands { /// Output CZ file bit depth #[arg(short, long, value_name = "BIT DEPTH")] - depth: Option, + depth: Option, } } @@ -65,25 +63,16 @@ fn main() { match &cli.command { Commands::Decode { input, output, batch } => { if !input.exists() { - Error::raw( - ErrorKind::ValueValidation, - format!("The input file/folder provided does not exist\n") - ).exit() + Error::raw(ErrorKind::ValueValidation, "The input file/folder provided does not exist\n").exit() } if *batch { if input.is_file() { - Error::raw( - ErrorKind::ValueValidation, - format!("Batch input must be a directory\n") - ).exit() + Error::raw(ErrorKind::ValueValidation, "Batch input must be a directory\n").exit() } if output.is_none() || output.as_ref().unwrap().is_file() { - Error::raw( - ErrorKind::ValueValidation, - format!("Batch output must be a directory\n") - ).exit() + Error::raw(ErrorKind::ValueValidation, "Batch output must be a directory\n").exit() } for entry in walkdir::WalkDir::new(input).max_depth(1) { @@ -124,154 +113,120 @@ fn main() { } Commands::Replace { batch, input, replacement, output, version, depth } => { if !input.exists() { - Error::raw( - ErrorKind::ValueValidation, - format!("The original file provided does not exist\n") - ).exit() + Error::raw(ErrorKind::ValueValidation, "The original file provided does not exist\n").exit() } if !replacement.exists() { - Error::raw( - ErrorKind::ValueValidation, - format!("The replacement file provided does not exist\n") - ).exit() + Error::raw(ErrorKind::ValueValidation, "The replacement file provided does not exist\n").exit() } + // If it's a batch replacement, we want directories to search if *batch { - if input.is_file() { - Error::raw( - ErrorKind::ValueValidation, - format!("Batch input must be a directory\n") - ).exit() + if !input.is_dir() { + Error::raw(ErrorKind::ValueValidation, "Batch input location must be a directory\n").exit() } - if replacement.is_file() { - Error::raw( - ErrorKind::ValueValidation, - format!("Batch replacement location must be a directory\n") - ).exit() + if !replacement.is_dir() { + Error::raw(ErrorKind::ValueValidation, "Batch replacement location must be a directory\n").exit() } - if output.is_file() { - Error::raw( - ErrorKind::ValueValidation, - format!("Batch output must be a directory\n") - ).exit() + if !output.is_dir() { + Error::raw(ErrorKind::ValueValidation, "Batch output location must be a directory\n").exit() } - for entry in walkdir::WalkDir::new(input).max_depth(1) { + // Replace all the files within the directory and print errors for them + for entry in walkdir::WalkDir::new(input) + .max_depth(1) + .same_file_system(true) + { let path = entry.unwrap().into_path(); if !path.is_file() { - continue; + continue } - let filename = PathBuf::from(path.file_name().unwrap()); + // Set the replacement image to the same name as the original file + let mut final_replacement = replacement.to_path_buf(); + final_replacement.push(PathBuf::from(path.file_name().unwrap()).with_extension("png")); - let mut final_path = output.clone(); - final_path.push(&filename); + // Set the replacement image to the same name as the original file + let mut final_output = output.to_path_buf(); + final_output.push(path.file_name().unwrap()); - let mut final_replacement = replacement.clone(); - final_replacement.push(filename.with_extension("png")); - - let repl_img = match image::open(&final_replacement) { - Ok(img) => img, - Err(_) => { - Error::raw( - ErrorKind::ValueValidation, - format!("Could not open replacement file as an image: {}\n", final_replacement.into_os_string().to_str().unwrap()) - ).print().unwrap(); - continue; - }, - }; - let repl_img = repl_img.to_rgba8(); - - let mut cz = match DynamicCz::open(&path) { - Ok(cz) => cz, - Err(_) => { - Error::raw( - ErrorKind::ValueValidation, - format!("Could not open input as a CZ file: {}\n", path.into_os_string().to_str().unwrap()) - ).print().unwrap(); - continue; - }, - }; - - cz.header_mut().set_width(repl_img.width() as u16); - cz.header_mut().set_height(repl_img.height() as u16); - cz.set_bitmap(repl_img.into_raw()); - cz.remove_palette(); - - if let Some(depth) = depth { - cz.header_mut().set_depth(*depth as u16) + if let Err(error) = replace_cz( + &path, + &final_output, + &final_replacement, + version, + depth + ) { + Error::raw(ErrorKind::ValueValidation, format!("{:?} - {}\n", path, error)).print().unwrap(); } - - if let Some(ver) = version { - match cz.header_mut().set_version(*ver) { - Ok(_) => (), - Err(_) => { - Error::raw( - ErrorKind::ValueValidation, - format!("Invalid CZ Version {}; expected 0, 1, 2, 3, or 4\n", ver) - ).exit() - }, - }; - } - - cz.save_as_cz(&final_path).unwrap(); } } else { - let mut cz = match DynamicCz::open(input) { - Ok(cz) => cz, - Err(err) => { - Error::raw( - ErrorKind::ValueValidation, - format!("Could not open input as a CZ file: {}\n", err) - ).exit() - }, - }; - - let repl_img = match image::open(replacement) { - Ok(img) => img, - Err(err) => { - Error::raw( - ErrorKind::ValueValidation, - format!("Could not open replacement file as an image: {}\n", err) - ).exit() - }, - }; - let repl_img = repl_img.to_rgba8(); - - cz.header_mut().set_width(repl_img.width() as u16); - cz.header_mut().set_height(repl_img.height() as u16); - cz.set_bitmap(repl_img.into_raw()); - cz.remove_palette(); - - if let Some(depth) = depth { - cz.header_mut().set_depth(*depth as u16) + if !input.is_file() { + Error::raw(ErrorKind::ValueValidation, "Input must be a file\n").exit() } - if let Some(ver) = version { - match cz.header_mut().set_version(*ver) { - Ok(_) => (), - Err(_) => { - Error::raw( - ErrorKind::ValueValidation, - format!("Invalid CZ Version {}; expected 0, 1, 2, 3, or 4\n", ver) - ).exit() - }, - }; + if !replacement.is_file() { + Error::raw(ErrorKind::ValueValidation, "Replacement must be a file\n").exit() } - match cz.save_as_cz(output) { - Ok(_) => (), - Err(err) => { - Error::raw( - ErrorKind::ValueValidation, - format!("Failed to save CZ file: {}\n", err) - ).exit() - }, + if !output.is_file() { + Error::raw(ErrorKind::ValueValidation, "Replacement output must be a file\n").exit() } + + // Replace the input file with the new image + replace_cz( + &input, + &output, + &replacement, + version, + depth + ).unwrap(); } }, } } + +/// Replace a CZ file with the bitmap of a PNG file +fn replace_cz>( + input_path: &P, + output_path: &P, + replacement_path: &P, + version: &Option, + depth: &Option, +) -> Result<(), Box> { + let path = input_path.as_ref(); + if !path.is_file() { + return Err("Input path is not a file".into()) + } + + if !replacement_path.as_ref().exists() || !replacement_path.as_ref().is_file() { + return Err("Replacement path does not exist or is not a file".into()) + } + + // Open the replacement image and convert it to RGBA8 + let repl_img = image::open(&replacement_path)?.to_rgba8(); + + // Open the original CZ file + let mut cz = DynamicCz::open(&path)?; + + // Set CZ header parameters and the new bitmap + cz.header_mut().set_width(repl_img.width() as u16); + cz.header_mut().set_height(repl_img.height() as u16); + cz.set_bitmap(repl_img.into_raw()); + cz.remove_palette(); + + if let Some(depth) = depth { + cz.header_mut().set_depth(*depth) + } + + if let Some(ver) = version { + cz.header_mut().set_version(*ver)?; + } + + // Save the file to the proper output location + cz.save_as_cz(&output_path.as_ref()).unwrap(); + + Ok(()) +}