diff --git a/Cargo.toml b/Cargo.toml index 31eb562..235240f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,7 @@ authors = ["G2"] [workspace.lints.rust] unsafe_code = "forbid" + +[profile.production] +inherits = "release" +lto = true diff --git a/cz/src/color.rs b/cz/src/color.rs index 9633513..0de0d69 100644 --- a/cz/src/color.rs +++ b/cz/src/color.rs @@ -93,7 +93,7 @@ pub fn indexed_gen_palette( Ok((indicies, gen_palette)) } -pub fn default_palette() -> Vec> { +pub fn _default_palette() -> Vec> { let mut colormap = Vec::new(); for i in 0..=0xFF { diff --git a/cz/src/common.rs b/cz/src/common.rs index 3cae834..fca5c98 100644 --- a/cz/src/common.rs +++ b/cz/src/common.rs @@ -156,8 +156,13 @@ impl CommonHeader { self.version } - pub fn set_version(&mut self, version: CzVersion) { - self.version = version + pub fn set_version>(&mut self, version: I) -> Result<(), ()> { + self.version = match version.try_into() { + Ok(val) => val, + Err(_) => return Err(()), + }; + + Ok(()) } pub fn length(&self) -> usize { diff --git a/utils/Cargo.toml b/utils/Cargo.toml index f2db93e..085ad93 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] byteorder = "1.5.0" +clap = { version = "4.5.4", features = ["derive", "unicode"] } cz = { path = "../cz" } fontdue = { version = "0.8.0", features = ["parallel"] } image = "0.25.1" diff --git a/utils/src/main.rs b/utils/src/main.rs index 6cfa823..08db885 100644 --- a/utils/src/main.rs +++ b/utils/src/main.rs @@ -1,30 +1,141 @@ -use cz::{common::CzVersion, dynamic::DynamicCz}; +use cz::DynamicCz; + +use std::path::PathBuf; + +use clap::{error::ErrorKind, Error, Parser, Subcommand}; + +#[derive(Parser)] +#[command(name = "CZ Utils")] +#[command(version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Converts a CZ file to a PNG + Decode { + /// Input CZ file of any type + #[arg(value_name = "CZ FILE")] + input: PathBuf, + + /// Output PNG file location + #[arg(value_name = "PATH")] + output: Option, + }, + + /// Replace a CZ file's image data + Replace { + /// Original input CZ file of any type + #[arg(value_name = "CZ FILE")] + input: PathBuf, + + /// Image to use as the replacement + #[arg(value_name = "IMAGE")] + replacement: PathBuf, + + /// Output CZ file location + #[arg(value_name = "PATH")] + output: PathBuf, + + /// Output CZ file version + #[arg(short, long, value_name = "CZ VERSION")] + version: Option, + } +} fn main() { - // Open the desired PNG - //let new_bitmap = image::open("mio.png").unwrap().to_rgba8(); + let cli = Cli::parse(); - let gallery_cz = DynamicCz::open("test.cz4").unwrap(); - gallery_cz.save_as_png("test.png").unwrap(); + // Check what subcommand was run + match &cli.command { + Commands::Decode { input, output } => { + if !input.exists() { + Error::raw( + ErrorKind::ValueValidation, + format!("The input file provided does not exist\n") + ).exit() + } - gallery_cz.save_as_cz("test-reencode.cz4").unwrap(); + let 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 cz_image_test = DynamicCz::open("test-reencode.cz4").unwrap(); + if let Some(output) = output { + cz.save_as_png(output).unwrap(); + } else { + let file_stem = PathBuf::from(input.file_stem().unwrap()); + cz.save_as_png(&file_stem.with_extension("png")).unwrap(); + } + } + Commands::Replace { input, replacement, output, version } => { + if !input.exists() { + Error::raw( + ErrorKind::ValueValidation, + format!("The original file provided does not exist\n") + ).exit() + } - // Save the newly decoded CZ3 as another PNG as a test - cz_image_test.save_as_png("test-reencode.png").unwrap(); + if !replacement.exists() { + Error::raw( + ErrorKind::ValueValidation, + format!("The replacement file provided does not exist\n") + ).exit() + } - /* - gallery_cz.set_bitmap(new_bitmap.into_vec()); - gallery_cz.header_mut().set_depth(8); - gallery_cz.remove_palette(); - gallery_cz.header_mut().set_version(CzVersion::CZ2); - gallery_cz.save_as_cz("24-modified.cz2").unwrap(); + 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() + }, + }; - // Open that same CZ3 again to test decoding - let cz_image_test = DynamicCz::open("24-modified.cz2").unwrap(); + 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(); - // Save the newly decoded CZ3 as another PNG as a test - cz_image_test.save_as_png("24-modified.png").unwrap(); - */ + 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()); + + 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() + }, + }; + } + + match cz.save_as_cz(output) { + Ok(_) => (), + Err(err) => { + Error::raw( + ErrorKind::ValueValidation, + format!("Failed to save CZ file: {}\n", err) + ).exit() + }, + } + }, + } }