From 31c31bdcfb977614df991c9880577eb39787b207 Mon Sep 17 00:00:00 2001 From: Dennis Ranke Date: Wed, 21 Sep 2022 22:45:06 +0200 Subject: [PATCH] clean up command line interface --- Cargo.lock | 14 ++--- Cargo.toml | 4 +- src/lib.rs | 28 +++++++-- src/lz.rs | 23 ++++--- src/main.rs | 173 ++++++++++++++++++++++++++++------------------------ 5 files changed, 141 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3dd33f..2d64cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lexopt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8" + [[package]] name = "libc" version = "0.2.108" @@ -89,12 +95,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "pico-args" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" - [[package]] name = "sacabase" version = "2.0.0" @@ -121,8 +121,8 @@ version = "0.1.0" dependencies = [ "anyhow", "cdivsufsort", + "lexopt", "pbr", - "pico-args", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ec6ef90..16c1c8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "upkr" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] cdivsufsort = "2" -pico-args = "0.4" +lexopt = "0.2.1" anyhow = "1" pbr = "1" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 65f2afe..b56d57c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,21 +9,39 @@ pub use lz::unpack; pub type ProgressCallback<'a> = &'a mut dyn FnMut(usize); +pub struct Config { + pub use_bitstream: bool, + pub parity_contexts: usize, +} + +impl Default for Config { + fn default() -> Config { + Config { + use_bitstream: false, + parity_contexts: 1, + } + } +} + pub fn pack( data: &[u8], level: u8, - use_bitstream: bool, - parity_contexts: usize, + config: Config, progress_callback: Option, ) -> Vec { if level == 0 { - greedy_packer::pack(data, use_bitstream, parity_contexts, progress_callback) + greedy_packer::pack( + data, + config.use_bitstream, + config.parity_contexts, + progress_callback, + ) } else { parsing_packer::pack( data, level, - use_bitstream, - parity_contexts, + config.use_bitstream, + config.parity_contexts, progress_callback, ) } diff --git a/src/lz.rs b/src/lz.rs index c22af22..dd43aaa 100644 --- a/src/lz.rs +++ b/src/lz.rs @@ -106,9 +106,9 @@ impl CoderState { } } -pub fn unpack(packed_data: &[u8], use_bitstream: bool, parity_contexts: usize) -> Vec { - let mut decoder = RansDecoder::new(packed_data, use_bitstream); - let mut contexts = ContextState::new((1 + 255) * parity_contexts + 1 + 64 + 64); +pub fn unpack(packed_data: &[u8], config: crate::Config) -> Vec { + let mut decoder = RansDecoder::new(packed_data, config.use_bitstream); + let mut contexts = ContextState::new((1 + 255) * config.parity_contexts + 1 + 64 + 64); let mut result = vec![]; let mut offset = 0; let mut prev_was_match = false; @@ -131,17 +131,26 @@ pub fn unpack(packed_data: &[u8], use_bitstream: bool, parity_contexts: usize) - } loop { - let literal_base = result.len() % parity_contexts * 256; + let literal_base = result.len() % config.parity_contexts * 256; if decoder.decode_with_context(&mut contexts.context_mut(literal_base)) { if prev_was_match - || decoder.decode_with_context(&mut contexts.context_mut(256 * parity_contexts)) + || decoder + .decode_with_context(&mut contexts.context_mut(256 * config.parity_contexts)) { - offset = decode_length(&mut decoder, &mut contexts, 256 * parity_contexts + 1) - 1; + offset = decode_length( + &mut decoder, + &mut contexts, + 256 * config.parity_contexts + 1, + ) - 1; if offset == 0 { break; } } - let length = decode_length(&mut decoder, &mut contexts, 256 * parity_contexts + 65); + let length = decode_length( + &mut decoder, + &mut contexts, + 256 * config.parity_contexts + 65, + ); for _ in 0..length { result.push(result[result.len() - offset]); } diff --git a/src/main.rs b/src/main.rs index 3f84e41..eb30b25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,102 +1,115 @@ -use anyhow::{bail, Result}; +use anyhow::Result; +use std::ffi::OsStr; use std::io::prelude::*; use std::process; use std::{fs::File, path::PathBuf}; fn main() -> Result<()> { - let mut args = pico_args::Arguments::from_env(); + let mut config = upkr::Config::default(); + let mut reverse = false; + let mut unpack = false; + let mut level = 2; + let mut infile: Option = None; + let mut outfile: Option = None; - match args.subcommand()?.as_ref().map(|s| s.as_str()) { - None => print_help(), - Some("pack") => { - let level = args.opt_value_from_str(["-l", "--level"])?.unwrap_or(2u8); - let use_bitstream = args.contains(["-b", "--bitstream"]); - let parity_contexts = args - .opt_value_from_str(["-p", "--parity"])? - .unwrap_or(1usize); - let reverse = args.contains(["-r", "--reverse"]); - - if parity_contexts != 1 && parity_contexts != 2 && parity_contexts != 4 { - eprintln!("--parity has to be 1, 2 or 4"); - process::exit(1); - } - - let infile = args.free_from_os_str::(|s| Ok(s.into()))?; - let outfile = args.free_from_os_str::(|s| Ok(s.into()))?; - - let mut data = vec![]; - File::open(infile)?.read_to_end(&mut data)?; - if reverse { - data.reverse(); - } - - let mut pb = pbr::ProgressBar::new(data.len() as u64); - pb.set_units(pbr::Units::Bytes); - let mut packed_data = upkr::pack( - &data, - level, - use_bitstream, - parity_contexts, - Some(&mut |pos| { - pb.set(pos as u64); - }), - ); - pb.finish(); - - if reverse { - packed_data.reverse(); - } - - println!( - "Compressed {} bytes to {} bytes ({}%)", - data.len(), - packed_data.len(), - packed_data.len() as f32 * 100. / data.len() as f32 - ); - File::create(outfile)?.write_all(&packed_data)?; + let mut parser = lexopt::Parser::from_env(); + while let Some(arg) = parser.next()? { + use lexopt::prelude::*; + match arg { + Short('b') | Long("bitstream") => config.use_bitstream = true, + Short('p') | Long("parity") => config.parity_contexts = parser.value()?.parse()?, + Short('r') | Long("reverse") => reverse = true, + Short('u') | Long("unpack") => unpack = true, + Short('l') | Long("level") => level = parser.value()?.parse()?, + Short('h') | Long("help") => print_help(0), + Value(val) if infile.is_none() => infile = Some(val.try_into()?), + Value(val) if outfile.is_none() => outfile = Some(val.try_into()?), + _ => return Err(arg.unexpected().into()), } - Some("unpack") => { - let use_bitstream = args.contains(["-b", "--bitstream"]); - let parity_contexts = args - .opt_value_from_str(["-p", "--parity"])? - .unwrap_or(1usize); - let reverse = args.contains(["-r", "--reverse"]); + } - if parity_contexts != 1 && parity_contexts != 2 && parity_contexts != 4 { - eprintln!("--parity has to be 1, 2 or 4"); - process::exit(1); + let infile = infile.unwrap_or_else(|| print_help(1)); + let outfile = outfile.unwrap_or_else(|| { + let mut name = infile.clone(); + if unpack { + if name.extension().filter(|&e| e == "upk").is_some() { + name.set_extension(""); + } else { + name.set_extension("bin"); } - - let infile = args.free_from_os_str::(|s| Ok(s.into()))?; - let outfile = args.free_from_os_str::(|s| Ok(s.into()))?; - - let mut data = vec![]; - File::open(infile)?.read_to_end(&mut data)?; - if reverse { - data.reverse(); - } - let mut unpacked_data = upkr::unpack(&data, use_bitstream, parity_contexts); - if reverse { - unpacked_data.reverse(); - } - File::create(outfile)?.write_all(&unpacked_data)?; + } else { + let mut filename = name + .file_name() + .unwrap_or_else(|| OsStr::new("")) + .to_os_string(); + filename.push(".upk"); + name.set_file_name(filename); } - Some(other) => { - bail!("Unknown subcommand '{}'", other); + name + }); + + if config.parity_contexts != 1 && config.parity_contexts != 2 && config.parity_contexts != 4 { + eprintln!("--parity has to be 1, 2, or 4"); + process::exit(1); + } + + if !unpack { + let mut data = vec![]; + File::open(infile)?.read_to_end(&mut data)?; + if reverse { + data.reverse(); } + + let mut pb = pbr::ProgressBar::new(data.len() as u64); + pb.set_units(pbr::Units::Bytes); + let mut packed_data = upkr::pack( + &data, + level, + config, + Some(&mut |pos| { + pb.set(pos as u64); + }), + ); + pb.finish(); + + if reverse { + packed_data.reverse(); + } + + println!( + "Compressed {} bytes to {} bytes ({}%)", + data.len(), + packed_data.len(), + packed_data.len() as f32 * 100. / data.len() as f32 + ); + File::create(outfile)?.write_all(&packed_data)?; + } else { + let mut data = vec![]; + File::open(infile)?.read_to_end(&mut data)?; + if reverse { + data.reverse(); + } + let mut unpacked_data = upkr::unpack(&data, config); + if reverse { + unpacked_data.reverse(); + } + File::create(outfile)?.write_all(&unpacked_data)?; } Ok(()) } -fn print_help() { +fn print_help(exit_code: i32) -> ! { eprintln!("Usage:"); - eprintln!(" upkr pack [-b] [-l level(0-9)] [-p N] "); - eprintln!(" upkr unpack [-b] [-p N] "); + eprintln!(" upkr [-l level(0-9)] [config options] []"); + eprintln!(" upkr -u [config options] []"); eprintln!(); - eprintln!(" -b, --bitstream bitstream mode"); eprintln!(" -l, --level N compression level 0-9"); + eprintln!(" -u, --unpack unpack infile"); + eprintln!(); + eprintln!("Config options (need to match when packing/unpacking):"); + eprintln!(" -b, --bitstream bitstream mode"); eprintln!(" -p, --parity N use N (2/4) parity contexts"); eprintln!(" -r, --reverse reverse input & output"); - process::exit(1); + process::exit(exit_code); }