1use crate::types::*;
2use clap::{ArgAction, ArgGroup, Parser, Subcommand};
3
4#[cfg(feature = "flate2")]
5fn parse_compression_level(level: &str) -> Result<u32, String> {
6 let lower = level.to_ascii_lowercase();
7 if lower == "none" {
8 return Ok(0);
9 } else if lower == "best" {
10 return Ok(9);
11 } else if lower == "default" {
12 return Ok(6);
13 } else if lower == "fast" {
14 return Ok(1);
15 }
16 clap_num::number_range(level, 0, 9)
17}
18
19#[cfg(feature = "mozjpeg")]
20fn parse_jpeg_quality(quality: &str) -> Result<u8, String> {
21 let lower = quality.to_ascii_lowercase();
22 if lower == "best" {
23 return Ok(100);
24 }
25 clap_num::number_range(quality, 0, 100)
26}
27
28#[cfg(feature = "zstd")]
29fn parse_zstd_compression_level(level: &str) -> Result<i32, String> {
30 let lower = level.to_ascii_lowercase();
31 if lower == "default" {
32 return Ok(3);
33 } else if lower == "best" {
34 return Ok(22);
35 }
36 clap_num::number_range(level, 0, 22)
37}
38
39#[cfg(feature = "webp")]
40fn parse_webp_quality(quality: &str) -> Result<u8, String> {
41 let lower = quality.to_ascii_lowercase();
42 if lower == "best" {
43 return Ok(100);
44 }
45 clap_num::number_range(quality, 0, 100)
46}
47
48#[derive(Parser, Debug)]
50#[clap(
51 group = ArgGroup::new("encodingg").multiple(false),
52 group = ArgGroup::new("output_encodingg").multiple(false),
53 group = ArgGroup::new("archive_encodingg").multiple(false),
54 group = ArgGroup::new("artemis_indentg").multiple(false),
55 group = ArgGroup::new("ex_hibit_rld_xor_keyg").multiple(false),
56 group = ArgGroup::new("ex_hibit_rld_def_xor_keyg").multiple(false),
57 group = ArgGroup::new("webp_qualityg").multiple(false),
58 group = ArgGroup::new("cat_system_int_encrypt_passwordg").multiple(false),
59)]
60#[command(
61 version,
62 about,
63 long_about = "Tools for export and import scripts\nhttps://github.com/lifegpc/msg-tool"
64)]
65pub struct Arg {
66 #[arg(short = 't', long, value_enum, global = true)]
67 pub script_type: Option<ScriptType>,
69 #[arg(short = 'T', long, value_enum, global = true)]
70 pub output_type: Option<OutputScriptType>,
72 #[cfg(feature = "image")]
73 #[arg(short = 'i', long, value_enum, global = true)]
74 pub image_type: Option<ImageOutputType>,
76 #[arg(short = 'e', long, value_enum, global = true, group = "encodingg")]
77 pub encoding: Option<TextEncoding>,
79 #[cfg(windows)]
80 #[arg(short = 'c', long, value_enum, global = true, group = "encodingg")]
81 pub code_page: Option<u32>,
83 #[arg(
84 short = 'E',
85 long,
86 value_enum,
87 global = true,
88 group = "output_encodingg"
89 )]
90 pub output_encoding: Option<TextEncoding>,
92 #[cfg(windows)]
93 #[arg(
94 short = 'C',
95 long,
96 value_enum,
97 global = true,
98 group = "output_encodingg"
99 )]
100 pub output_code_page: Option<u32>,
102 #[arg(
103 short = 'a',
104 long,
105 value_enum,
106 global = true,
107 group = "archive_encodingg"
108 )]
109 pub archive_encoding: Option<TextEncoding>,
111 #[cfg(windows)]
112 #[arg(
113 short = 'A',
114 long,
115 value_enum,
116 global = true,
117 group = "archive_encodingg"
118 )]
119 pub archive_code_page: Option<u32>,
121 #[cfg(feature = "circus")]
122 #[arg(long, value_enum, global = true)]
123 pub circus_mes_type: Option<CircusMesType>,
125 #[arg(short, long, action = ArgAction::SetTrue, global = true)]
126 pub recursive: bool,
128 #[arg(global = true, action = ArgAction::SetTrue, short, long)]
129 pub backtrace: bool,
131 #[cfg(feature = "escude-arc")]
132 #[arg(long, action = ArgAction::SetTrue, global = true)]
133 pub escude_fake_compress: bool,
135 #[cfg(feature = "escude")]
136 #[arg(long, global = true)]
137 pub escude_enum_scr: Option<String>,
139 #[cfg(feature = "bgi")]
140 #[arg(long, action = ArgAction::SetTrue, global = true)]
141 pub bgi_import_duplicate: bool,
144 #[cfg(feature = "bgi")]
145 #[arg(long, action = ArgAction::SetTrue, global = true, alias = "bgi-no-append")]
146 pub bgi_disable_append: bool,
149 #[cfg(all(feature = "bgi-arc", feature = "bgi-img"))]
150 #[arg(long, global = true)]
151 pub bgi_is_sysgrp_arc: Option<bool>,
153 #[cfg(feature = "bgi-img")]
154 #[arg(long, global = true)]
155 pub bgi_img_scramble: Option<bool>,
158 #[cfg(feature = "cat-system-arc")]
159 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
160 pub cat_system_int_encrypt_password: Option<String>,
162 #[cfg(feature = "cat-system-arc")]
163 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
164 pub cat_system_int_exe: Option<String>,
166 #[cfg(feature = "cat-system-img")]
167 #[arg(long, global = true, action = ArgAction::SetTrue)]
168 pub cat_system_image_canvas: bool,
170 #[cfg(feature = "kirikiri")]
171 #[arg(long, global = true)]
172 pub kirikiri_language_index: Option<usize>,
174 #[cfg(feature = "kirikiri")]
175 #[arg(long, global = true, action = ArgAction::SetTrue)]
176 pub kirikiri_export_comumode: bool,
179 #[cfg(feature = "kirikiri")]
180 #[arg(long, global = true)]
181 pub kirikiri_comumode_json: Option<String>,
183 #[cfg(feature = "kirikiri")]
184 #[arg(long, global = true, action = ArgAction::SetTrue, alias = "kr-no-empty-lines", alias = "kirikiri-no-empty-lines")]
185 pub kirikiri_remove_empty_lines: bool,
187 #[cfg(feature = "kirikiri")]
188 #[arg(
189 long,
190 global = true,
191 value_delimiter = ',',
192 default_value = "nm,set_title,speaker,Talk,talk,cn,name,名前"
193 )]
194 pub kirikiri_name_commands: Vec<String>,
196 #[cfg(feature = "kirikiri")]
197 #[arg(
198 long,
199 global = true,
200 value_delimiter = ',',
201 default_value = "sel01,sel02,sel03,sel04,AddSelect,ruby,exlink,e_xlink"
202 )]
203 pub kirikiri_message_commands: Vec<String>,
205 #[cfg(feature = "image")]
206 #[arg(short = 'f', long, global = true)]
207 pub image_output_flat: bool,
209 #[cfg(feature = "bgi-arc")]
210 #[arg(long, global = true, action = ArgAction::SetTrue)]
211 pub bgi_compress_file: bool,
213 #[cfg(feature = "bgi-arc")]
214 #[arg(long, global = true, default_value_t = 3, value_parser = crate::scripts::bgi::archive::dsc::parse_min_length)]
215 pub bgi_compress_min_len: usize,
217 #[cfg(feature = "emote-img")]
218 #[arg(long, global = true)]
219 pub emote_pimg_overlay: Option<bool>,
221 #[cfg(feature = "artemis-arc")]
222 #[arg(long, global = true)]
223 pub artemis_arc_disable_xor: bool,
225 #[cfg(feature = "artemis")]
226 #[arg(long, global = true, group = "artemis_indentg")]
227 pub artemis_indent: Option<usize>,
230 #[cfg(feature = "artemis")]
231 #[arg(long, global = true, action = ArgAction::SetTrue, group = "artemis_indentg")]
232 pub artemis_no_indent: bool,
234 #[cfg(feature = "artemis")]
235 #[arg(long, global = true, default_value_t = 100)]
236 pub artemis_max_line_width: usize,
238 #[cfg(feature = "artemis")]
239 #[arg(long, global = true)]
240 pub artemis_ast_lang: Option<String>,
243 #[cfg(feature = "artemis")]
244 #[arg(
245 long,
246 global = true,
247 value_delimiter = ',',
248 default_value = "遅延イベントCG,遅延背景,bgv_in,イベントCG,遅延ポップアップ"
249 )]
250 pub artemis_txt_blacklist_names: Vec<String>,
253 #[cfg(feature = "artemis")]
254 #[arg(long, global = true)]
255 pub artemis_txt_lang: Option<String>,
258 #[cfg(feature = "cat-system")]
259 #[arg(long, global = true)]
260 pub cat_system_cstl_lang: Option<String>,
263 #[cfg(feature = "flate2")]
264 #[arg(short = 'z', long, global = true, value_name = "LEVEL", value_parser = parse_compression_level, default_value_t = 6)]
265 pub zlib_compression_level: u32,
267 #[cfg(feature = "image")]
268 #[arg(short = 'g', long, global = true, value_enum, default_value_t = PngCompressionLevel::Fast)]
269 pub png_compression_level: PngCompressionLevel,
271 #[cfg(feature = "circus-img")]
272 #[arg(long, global = true, action = ArgAction::SetTrue)]
273 pub circus_crx_keep_original_bpp: bool,
275 #[cfg(feature = "circus-img")]
276 #[arg(long, global = true, action = ArgAction::SetTrue)]
277 pub circus_crx_zstd: bool,
279 #[cfg(feature = "zstd")]
280 #[arg(short = 'Z', long, global = true, value_name = "LEVEL", value_parser = parse_zstd_compression_level, default_value_t = 3)]
281 pub zstd_compression_level: i32,
283 #[cfg(feature = "circus-img")]
284 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::circus::image::crx::CircusCrxMode::Auto)]
285 pub circus_crx_mode: crate::scripts::circus::image::crx::CircusCrxMode,
287 #[cfg(feature = "circus-img")]
288 #[arg(long, global = true, action = ArgAction::SetTrue)]
289 pub circus_crx_canvas: bool,
291 #[arg(short = 'F', long, global = true, action = ArgAction::SetTrue)]
292 pub force_script: bool,
294 #[cfg(feature = "ex-hibit")]
295 #[arg(
296 long,
297 global = true,
298 value_name = "HEX",
299 group = "ex_hibit_rld_xor_keyg"
300 )]
301 pub ex_hibit_rld_xor_key: Option<String>,
304 #[cfg(feature = "ex-hibit")]
305 #[arg(
306 long,
307 global = true,
308 value_name = "PATH",
309 group = "ex_hibit_rld_xor_keyg"
310 )]
311 pub ex_hibit_rld_xor_key_file: Option<String>,
313 #[cfg(feature = "ex-hibit")]
314 #[arg(
315 long,
316 global = true,
317 value_name = "HEX",
318 group = "ex_hibit_rld_def_xor_keyg"
319 )]
320 pub ex_hibit_rld_def_xor_key: Option<String>,
322 #[cfg(feature = "ex-hibit")]
323 #[arg(
324 long,
325 global = true,
326 value_name = "PATH",
327 group = "ex_hibit_rld_def_xor_keyg"
328 )]
329 pub ex_hibit_rld_def_xor_key_file: Option<String>,
331 #[cfg(feature = "ex-hibit")]
332 #[arg(long, global = true, value_name = "PATH")]
333 pub ex_hibit_rld_keys: Option<String>,
336 #[cfg(feature = "ex-hibit")]
337 #[arg(long, global = true, value_name = "PATH")]
338 pub ex_hibit_rld_def_keys: Option<String>,
340 #[cfg(feature = "mozjpeg")]
341 #[arg(short = 'j', long, global = true, default_value_t = 80, value_parser = parse_jpeg_quality)]
342 pub jpeg_quality: u8,
344 #[cfg(feature = "webp")]
345 #[arg(short = 'w', long, global = true, group = "webp_qualityg")]
346 pub webp_lossless: bool,
348 #[cfg(feature = "webp")]
349 #[arg(short = 'W', long, global = true, value_name = "QUALITY", group = "webp_qualityg", value_parser = parse_webp_quality, default_value_t = 80)]
350 pub webp_quality: u8,
352 #[arg(long, global = true)]
353 pub custom_yaml: Option<bool>,
356 #[cfg(feature = "entis-gls")]
357 #[arg(long, global = true)]
358 pub entis_gls_srcxml_lang: Option<String>,
361 #[cfg(feature = "will-plus")]
362 #[arg(long, global = true)]
363 pub will_plus_ws2_no_disasm: bool,
367 #[command(subcommand)]
368 pub command: Command,
370}
371
372#[derive(Parser, Debug)]
373#[clap(group = ArgGroup::new("patched_encodingg").multiple(false), group = ArgGroup::new("patched_archive_encodingg").multiple(false))]
374pub struct ImportArgs {
375 pub input: String,
377 pub output: String,
379 pub patched: String,
381 #[arg(short = 'p', long, group = "patched_encodingg")]
382 pub patched_encoding: Option<TextEncoding>,
384 #[cfg(windows)]
385 #[arg(short = 'P', long, group = "patched_encodingg")]
386 pub patched_code_page: Option<u32>,
388 #[arg(long, value_enum, group = "patched_archive_encodingg", alias = "pa")]
389 pub patched_archive_encoding: Option<TextEncoding>,
391 #[cfg(windows)]
392 #[arg(long, value_enum, group = "patched_archive_encodingg", alias = "PA")]
393 pub patched_archive_code_page: Option<u32>,
395 #[arg(long)]
396 pub patched_format: Option<FormatType>,
398 #[arg(long)]
399 pub patched_fixed_length: Option<usize>,
401 #[arg(long, action = ArgAction::SetTrue)]
402 pub patched_keep_original: bool,
404 #[arg(long)]
405 pub name_csv: Option<String>,
407 #[arg(long)]
408 pub replacement_json: Option<String>,
410 #[arg(long, action = ArgAction::SetTrue)]
411 pub warn_when_output_file_not_found: bool,
412}
413
414#[derive(Subcommand, Debug)]
415pub enum Command {
417 Export {
419 input: String,
421 output: Option<String>,
423 },
424 Import(ImportArgs),
426 Pack {
428 input: String,
430 output: Option<String>,
432 },
433 Unpack {
435 input: String,
437 output: Option<String>,
439 },
440 Create {
442 input: String,
444 output: Option<String>,
446 },
447}
448
449pub fn parse_args() -> Arg {
450 Arg::parse()
451}
452
453#[cfg(feature = "ex-hibit")]
454pub fn load_ex_hibit_rld_xor_key(arg: &Arg) -> anyhow::Result<Option<u32>> {
455 if let Some(key) = &arg.ex_hibit_rld_xor_key {
456 if key.starts_with("0x") {
457 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
458 } else {
459 return Ok(Some(u32::from_str_radix(key, 16)?));
460 }
461 }
462 if let Some(file) = &arg.ex_hibit_rld_xor_key_file {
463 let key = std::fs::read_to_string(file)?.trim().to_string();
464 if key.starts_with("0x") {
465 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
466 } else {
467 return Ok(Some(u32::from_str_radix(&key, 16)?));
468 }
469 }
470 Ok(None)
471}
472
473#[cfg(feature = "ex-hibit")]
474pub fn load_ex_hibit_rld_def_xor_key(arg: &crate::args::Arg) -> anyhow::Result<Option<u32>> {
475 if let Some(key) = &arg.ex_hibit_rld_def_xor_key {
476 if key.starts_with("0x") {
477 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
478 } else {
479 return Ok(Some(u32::from_str_radix(key, 16)?));
480 }
481 }
482 if let Some(file) = &arg.ex_hibit_rld_def_xor_key_file {
483 let key = std::fs::read_to_string(file)?.trim().to_string();
484 if key.starts_with("0x") {
485 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
486 } else {
487 return Ok(Some(u32::from_str_radix(&key, 16)?));
488 }
489 }
490 Ok(None)
491}
492
493#[cfg(feature = "cat-system-arc")]
494pub fn get_cat_system_int_encrypt_password(arg: &Arg) -> anyhow::Result<Option<String>> {
495 if let Some(exe) = &arg.cat_system_int_exe {
496 return Ok(Some(
497 crate::scripts::cat_system::archive::int::get_password_from_exe(exe)?,
498 ));
499 }
500 if let Some(password) = &arg.cat_system_int_encrypt_password {
501 return Ok(Some(password.clone()));
502 }
503 Ok(None)
504}