|
1 |
| -use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; |
| 1 | +use clap::{value_parser, ArgAction, Parser}; |
2 | 2 | use rpgmad_lib::Decrypter;
|
3 | 3 | use std::{
|
4 |
| - fs::read, |
5 |
| - path::{Path, PathBuf}, |
| 4 | + fs::{read, read_dir}, |
| 5 | + path::PathBuf, |
6 | 6 | time::Instant,
|
7 | 7 | };
|
8 | 8 |
|
9 |
| -struct Localization<'a> { |
10 |
| - // Arg descriptions |
11 |
| - input_path_arg_desc: &'a str, |
12 |
| - output_path_arg_desc: &'a str, |
13 |
| - force_arg_desc: &'a str, |
14 |
| - help_arg_desc: &'a str, |
15 |
| - about: &'a str, |
16 |
| - |
17 |
| - // Messages |
18 |
| - input_path_missing_msg: &'a str, |
19 |
| - output_path_missing_msg: &'a str, |
| 9 | +#[derive(Parser, Debug)] |
| 10 | +#[command( |
| 11 | + about = "Extract encrypted .rgss RPG Maker archives.", |
| 12 | + term_width = 120 |
| 13 | +)] |
| 14 | +struct Cli { |
| 15 | + /// Path to the .rgss file or directory containing it. |
| 16 | + #[arg(short, long, value_parser = value_parser!(PathBuf), default_value = "./", hide_default_value = true)] |
| 17 | + input_path: PathBuf, |
| 18 | + |
| 19 | + /// Output directory. |
| 20 | + #[arg(short, long, value_parser = value_parser!(PathBuf), default_value = "./", hide_default_value = true)] |
| 21 | + output_path: PathBuf, |
| 22 | + |
| 23 | + /// Overwrite existing files. |
| 24 | + #[arg(short, long, action = ArgAction::SetTrue)] |
| 25 | + force: bool, |
20 | 26 | }
|
21 | 27 |
|
22 | 28 | fn main() {
|
23 | 29 | let start_time: Instant = Instant::now();
|
| 30 | + let mut cli: Cli = Cli::parse(); |
24 | 31 |
|
25 |
| - const LOCALIZATION: Localization = Localization { |
26 |
| - input_path_arg_desc: "Path to the .rgss file.", |
27 |
| - output_path_arg_desc: "Path to put output files.", |
28 |
| - force_arg_desc: "Forcefully overwrite existing Data, Graphics and other files.", |
29 |
| - help_arg_desc: "Prints the help message.", |
30 |
| - about: "A tool to extract encrypted .rgss RPG Maker archives.", |
31 |
| - |
32 |
| - input_path_missing_msg: "Input file does not exist.", |
33 |
| - output_path_missing_msg: "Output path does not exist.", |
34 |
| - }; |
35 |
| - |
36 |
| - let input_path_arg: Arg = Arg::new("input-path") |
37 |
| - .short('i') |
38 |
| - .long("input-file") |
39 |
| - .help(LOCALIZATION.input_path_arg_desc) |
40 |
| - .value_parser(value_parser!(PathBuf)) |
41 |
| - .default_value("./") |
42 |
| - .hide_default_value(true); |
43 |
| - |
44 |
| - let output_path_arg: Arg = Arg::new("output-path") |
45 |
| - .short('o') |
46 |
| - .long("output-dir") |
47 |
| - .help(LOCALIZATION.output_path_arg_desc) |
48 |
| - .value_parser(value_parser!(PathBuf)) |
49 |
| - .default_value("./") |
50 |
| - .hide_default_value(true); |
51 |
| - |
52 |
| - let force: Arg = Arg::new("force") |
53 |
| - .short('f') |
54 |
| - .long("force") |
55 |
| - .help(LOCALIZATION.force_arg_desc) |
56 |
| - .action(ArgAction::SetTrue); |
57 |
| - |
58 |
| - let help: Arg = Arg::new("help") |
59 |
| - .short('h') |
60 |
| - .long("help") |
61 |
| - .help(LOCALIZATION.help_arg_desc) |
62 |
| - .action(ArgAction::Help); |
63 |
| - |
64 |
| - let cli: Command = Command::new("") |
65 |
| - .about(LOCALIZATION.about) |
66 |
| - .disable_version_flag(true) |
67 |
| - .disable_help_subcommand(true) |
68 |
| - .disable_help_flag(true) |
69 |
| - .next_line_help(true) |
70 |
| - .term_width(120) |
71 |
| - .args([input_path_arg, output_path_arg, force, help]); |
72 |
| - |
73 |
| - let matches: ArgMatches = cli.get_matches(); |
74 |
| - |
75 |
| - let input_path: &Path = matches.get_one::<PathBuf>("input-path").unwrap(); |
76 |
| - |
77 |
| - if !input_path.exists() { |
78 |
| - panic!("{}", LOCALIZATION.input_path_missing_msg) |
| 32 | + if !cli.input_path.exists() { |
| 33 | + panic!("Input path does not exist."); |
79 | 34 | }
|
80 | 35 |
|
81 |
| - let mut output_path: &Path = matches.get_one::<PathBuf>("output-path").unwrap(); |
82 |
| - if *output_path.as_os_str() == *"./" { |
83 |
| - output_path = unsafe { input_path.parent().unwrap_unchecked() } |
| 36 | + if !cli.output_path.exists() { |
| 37 | + panic!("Output path does not exist."); |
84 | 38 | }
|
85 | 39 |
|
86 |
| - if !output_path.exists() { |
87 |
| - panic!("{}", LOCALIZATION.output_path_missing_msg); |
| 40 | + if cli |
| 41 | + .input_path |
| 42 | + .extension() |
| 43 | + .and_then(|e| e.to_str()) |
| 44 | + .is_none_or(|e| !e.starts_with("rgss")) |
| 45 | + { |
| 46 | + cli.input_path = read_dir(&cli.input_path) |
| 47 | + .unwrap() |
| 48 | + .flatten() |
| 49 | + .find(|e| { |
| 50 | + e.path() |
| 51 | + .extension() |
| 52 | + .and_then(|ext| ext.to_str()) |
| 53 | + .is_some_and(|ext| ext.starts_with("rgss")) |
| 54 | + }) |
| 55 | + .map(|e| e.path()) |
| 56 | + .expect("No .rgss archive found in the directory."); |
88 | 57 | }
|
89 | 58 |
|
90 |
| - let force_flag: bool = matches.get_flag("force"); |
91 |
| - |
92 |
| - let bytes: Vec<u8> = read(input_path).unwrap(); |
| 59 | + let bytes: Vec<u8> = read(&cli.input_path).unwrap(); |
93 | 60 |
|
94 | 61 | Decrypter::new(bytes)
|
95 |
| - .extract(output_path, force_flag) |
| 62 | + .extract(&cli.output_path, cli.force) |
96 | 63 | .unwrap();
|
97 | 64 |
|
98 |
| - println!("Elapsed: {}", start_time.elapsed().as_secs_f64()) |
| 65 | + println!("Elapsed: {:.2}s", start_time.elapsed().as_secs_f32()); |
99 | 66 | }
|
0 commit comments