1use crate::types::*;
2#[allow(unused)]
3use crate::utils::num_range::*;
4use clap::{ArgAction, ArgGroup, Parser, Subcommand};
5
6#[cfg(feature = "flate2")]
7fn parse_compression_level(level: &str) -> Result<u32, String> {
8 let lower = level.to_ascii_lowercase();
9 if lower == "none" {
10 return Ok(0);
11 } else if lower == "best" {
12 return Ok(9);
13 } else if lower == "default" {
14 return Ok(6);
15 } else if lower == "fast" {
16 return Ok(1);
17 }
18 number_range(level, 0, 9)
19}
20
21#[cfg(feature = "mozjpeg")]
22fn parse_jpeg_quality(quality: &str) -> Result<u8, String> {
23 let lower = quality.to_ascii_lowercase();
24 if lower == "best" {
25 return Ok(100);
26 }
27 number_range(quality, 0, 100)
28}
29
30#[cfg(feature = "zstd")]
31fn parse_zstd_compression_level(level: &str) -> Result<i32, String> {
32 let lower = level.to_ascii_lowercase();
33 if lower == "default" {
34 return Ok(3);
35 } else if lower == "best" {
36 return Ok(22);
37 }
38 number_range(level, 0, 22)
39}
40
41#[cfg(feature = "webp")]
42fn parse_webp_quality(quality: &str) -> Result<u8, String> {
43 let lower = quality.to_ascii_lowercase();
44 if lower == "best" {
45 return Ok(100);
46 }
47 number_range(quality, 0, 100)
48}
49
50#[cfg(feature = "audio-flac")]
51fn parse_flac_compression_level(level: &str) -> Result<u32, String> {
52 let lower = level.to_ascii_lowercase();
53 if lower == "fast" {
54 return Ok(0);
55 } else if lower == "best" {
56 return Ok(8);
57 } else if lower == "default" {
58 return Ok(5);
59 }
60 number_range(level, 0, 8)
61}
62
63#[cfg(feature = "image-jxl")]
64fn parse_jxl_distance(s: &str) -> Result<f32, String> {
65 let lower = s.to_ascii_lowercase();
66 if lower == "lossless" {
67 return Ok(0.0);
68 } else if lower == "visually-lossless" {
69 return Ok(1.0);
70 }
71 number_range(s, 0.0, 25.0)
72}
73
74#[cfg(feature = "musica-arc")]
75pub fn get_musica_game_title_value_parser() -> Vec<clap::builder::PossibleValue> {
76 crate::scripts::musica::archive::paz::get_supported_games_with_title()
77 .iter()
78 .map(|(name, title)| {
79 let mut pv = clap::builder::PossibleValue::new(*name);
80 if let Some(t) = title {
81 pv = pv.help(t);
82 let mut alias_count = 0usize;
83 for i in t.split("|") {
84 pv = pv.alias(i.trim());
85 alias_count += 1;
86 }
87 if alias_count > 1 {
89 pv = pv.alias(t);
90 }
91 }
92 pv
93 })
94 .collect()
95}
96
97#[cfg(feature = "kirikiri-arc")]
98pub fn get_xp3_game_title_value_parser() -> Vec<clap::builder::PossibleValue> {
99 crate::scripts::kirikiri::archive::xp3::get_supported_games_with_title()
100 .iter()
101 .map(|(name, title)| {
102 let mut pv = clap::builder::PossibleValue::new(*name);
103 if let Some(t) = title {
104 pv = pv.help(t);
105 let mut alias_count = 0usize;
106 for i in t.split("|") {
107 pv = pv.alias(i.trim());
108 alias_count += 1;
109 }
110 if alias_count > 1 {
112 pv = pv.alias(t);
113 }
114 }
115 pv
116 })
117 .collect()
118}
119
120#[derive(Parser, Debug, Clone)]
122#[clap(
123 group = ArgGroup::new("encodingg").multiple(false),
124 group = ArgGroup::new("output_encodingg").multiple(false),
125 group = ArgGroup::new("archive_encodingg").multiple(false),
126 group = ArgGroup::new("artemis_indentg").multiple(false),
127 group = ArgGroup::new("ex_hibit_rld_xor_keyg").multiple(false),
128 group = ArgGroup::new("ex_hibit_rld_def_xor_keyg").multiple(false),
129 group = ArgGroup::new("webp_qualityg").multiple(false),
130 group = ArgGroup::new("cat_system_int_encrypt_passwordg").multiple(false),
131 group = ArgGroup::new("kirikiri_chat_jsong").multiple(false),
132 group = ArgGroup::new("xp3-compression").multiple(false),
133)]
134#[command(
135 version,
136 about,
137 long_about = "Tools for export and import scripts\nhttps://github.com/lifegpc/msg-tool"
138)]
139pub struct Arg {
140 #[arg(short = 't', long, value_enum, global = true)]
141 pub script_type: Option<ScriptType>,
143 #[arg(short = 'T', long, value_enum, global = true)]
144 pub output_type: Option<OutputScriptType>,
146 #[arg(short = 'n', long, global = true)]
147 pub output_no_extra_ext: bool,
149 #[cfg(feature = "image")]
150 #[arg(short = 'i', long, value_enum, global = true)]
151 pub image_type: Option<ImageOutputType>,
153 #[arg(short = 'e', long, value_enum, global = true, group = "encodingg")]
154 pub encoding: Option<TextEncoding>,
156 #[cfg(windows)]
157 #[arg(short = 'c', long, value_enum, global = true, group = "encodingg")]
158 pub code_page: Option<u32>,
160 #[arg(
161 short = 'E',
162 long,
163 value_enum,
164 global = true,
165 group = "output_encodingg"
166 )]
167 pub output_encoding: Option<TextEncoding>,
169 #[cfg(windows)]
170 #[arg(
171 short = 'C',
172 long,
173 value_enum,
174 global = true,
175 group = "output_encodingg"
176 )]
177 pub output_code_page: Option<u32>,
179 #[arg(
180 short = 'a',
181 long,
182 value_enum,
183 global = true,
184 group = "archive_encodingg"
185 )]
186 pub archive_encoding: Option<TextEncoding>,
188 #[cfg(windows)]
189 #[arg(
190 short = 'A',
191 long,
192 value_enum,
193 global = true,
194 group = "archive_encodingg"
195 )]
196 pub archive_code_page: Option<u32>,
198 #[cfg(feature = "circus")]
199 #[arg(long, value_enum, global = true)]
200 pub circus_mes_type: Option<CircusMesType>,
202 #[arg(short, long, action = ArgAction::SetTrue, global = true)]
203 pub recursive: bool,
205 #[arg(global = true, action = ArgAction::SetTrue, short, long)]
206 pub backtrace: bool,
208 #[cfg(feature = "escude-arc")]
209 #[arg(long, action = ArgAction::SetTrue, global = true)]
210 pub escude_fake_compress: bool,
212 #[cfg(feature = "escude")]
213 #[arg(long, global = true)]
214 pub escude_enum_scr: Option<String>,
216 #[cfg(feature = "escude")]
217 #[arg(long, global = true)]
218 pub escude_op: Option<crate::scripts::escude::script::EscudeOp>,
220 #[cfg(feature = "bgi")]
221 #[arg(long, action = ArgAction::SetTrue, global = true)]
222 pub bgi_import_duplicate: bool,
225 #[cfg(feature = "bgi")]
226 #[arg(long, action = ArgAction::SetTrue, global = true, visible_alias = "bgi-no-append")]
227 pub bgi_disable_append: bool,
230 #[cfg(all(feature = "bgi-arc", feature = "bgi-img"))]
231 #[arg(long, global = true)]
232 pub bgi_is_sysgrp_arc: Option<bool>,
234 #[cfg(feature = "bgi-img")]
235 #[arg(long, global = true)]
236 pub bgi_img_scramble: Option<bool>,
239 #[cfg(feature = "bgi-img")]
240 #[arg(long, global = true, default_value_t = crate::types::get_default_threads())]
241 pub bgi_img_workers: usize,
244 #[cfg(feature = "cat-system-arc")]
245 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
246 pub cat_system_int_encrypt_password: Option<String>,
248 #[cfg(feature = "cat-system-arc")]
249 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
250 pub cat_system_int_exe: Option<String>,
252 #[cfg(feature = "cat-system-img")]
253 #[arg(long, global = true, action = ArgAction::SetTrue)]
254 pub cat_system_image_canvas: bool,
256 #[cfg(feature = "kirikiri")]
257 #[arg(long, global = true)]
258 pub kirikiri_language_index: Option<usize>,
260 #[cfg(feature = "kirikiri")]
261 #[arg(long, global = true)]
262 pub kirikiri_export_chat: bool,
265 #[cfg(feature = "kirikiri")]
266 #[arg(long, global = true, value_delimiter = ',')]
267 pub kirikiri_chat_key: Option<Vec<String>>,
270 #[cfg(feature = "kirikiri")]
271 #[arg(long, global = true, group = "kirikiri_chat_jsong")]
272 pub kirikiri_chat_json: Option<String>,
274 #[cfg(feature = "kirikiri")]
275 #[arg(long, global = true, group = "kirikiri_chat_jsong")]
276 pub kirikiri_chat_dir: Option<String>,
278 #[cfg(feature = "kirikiri")]
279 #[arg(long, global = true, value_delimiter = ',')]
280 pub kirikiri_languages: Option<Vec<String>>,
282 #[cfg(feature = "kirikiri")]
283 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-title")]
284 pub kirikiri_title: bool,
286 #[cfg(feature = "kirikiri")]
287 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-chat-no-multilang")]
288 pub kirikiri_chat_no_multilang: bool,
290 #[cfg(feature = "kirikiri")]
291 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-no-empty-lines", visible_alias = "kirikiri-no-empty-lines")]
292 pub kirikiri_remove_empty_lines: bool,
294 #[cfg(feature = "kirikiri")]
295 #[arg(
296 long,
297 global = true,
298 value_delimiter = ',',
299 default_value = "nm,set_title,speaker,Talk,talk,cn,name,名前"
300 )]
301 pub kirikiri_name_commands: Vec<String>,
303 #[cfg(feature = "kirikiri")]
304 #[arg(
305 long,
306 global = true,
307 value_delimiter = ',',
308 default_value = "sel01,sel02,sel03,sel04,AddSelect,ruby,exlink,e_xlink"
309 )]
310 pub kirikiri_message_commands: Vec<String>,
312 #[cfg(feature = "image")]
313 #[arg(short = 'f', long, global = true)]
314 pub image_output_flat: bool,
316 #[cfg(feature = "bgi-arc")]
317 #[arg(long, global = true, action = ArgAction::SetTrue)]
318 pub bgi_compress_file: bool,
320 #[cfg(feature = "bgi-arc")]
321 #[arg(long, global = true, default_value_t = 9, value_parser = crate::scripts::bgi::archive::dsc::parse_compress_level)]
322 pub bgi_compress_level: u8,
325 #[cfg(feature = "bgi-arc")]
326 #[arg(long, global = true, default_value_t = num_cpus::get())]
327 pub bgi_arc_workers: usize,
329 #[cfg(feature = "emote-img")]
330 #[arg(long, global = true)]
331 pub emote_pimg_overlay: Option<bool>,
333 #[cfg(feature = "artemis-arc")]
334 #[arg(long, global = true)]
335 pub artemis_arc_disable_xor: bool,
337 #[cfg(feature = "artemis")]
338 #[arg(long, global = true, group = "artemis_indentg")]
339 pub artemis_indent: Option<usize>,
342 #[cfg(feature = "artemis")]
343 #[arg(long, global = true, action = ArgAction::SetTrue, group = "artemis_indentg")]
344 pub artemis_no_indent: bool,
346 #[cfg(feature = "artemis")]
347 #[arg(long, global = true, default_value_t = 100)]
348 pub artemis_max_line_width: usize,
350 #[cfg(feature = "artemis")]
351 #[arg(long, global = true)]
352 pub artemis_ast_lang: Option<String>,
355 #[cfg(feature = "artemis")]
356 #[arg(long, global = true, action = ArgAction::SetTrue)]
357 pub artemis_asb_no_format_lua: bool,
359 #[cfg(feature = "artemis-panmimisoft")]
361 #[arg(
362 long,
363 global = true,
364 value_delimiter = ',',
365 default_value = "背景,イベントCG,遅延背景,遅延背景予約,背景予約,遅延イベントCG,遅延イベントCG予約,イベントCG予約,遅延ポップアップ,遅延bgm_in,遅延bgm_out,遅延se_in,遅延se_out,遅延bgs_in,遅延bgs_out,立ち絵face非連動,セーブサムネイル置換終了,シネスコ,ポップアップ"
366 )]
367 pub artemis_panmimisoft_txt_blacklist_names: Vec<String>,
370 #[cfg(feature = "artemis-panmimisoft")]
371 #[arg(long, global = true)]
372 pub artemis_panmimisoft_txt_lang: Option<String>,
375 #[cfg(feature = "artemis-panmimisoft")]
376 #[arg(long, global = true, action = ArgAction::SetTrue, requires = "artemis_panmimisoft_txt_lang")]
377 pub artemis_panmimisoft_txt_multi_lang: bool,
380 #[cfg(feature = "artemis-panmimisoft")]
381 #[arg(long, global = true)]
382 pub artemis_panmimisoft_txt_tag_ini: Option<String>,
384 #[cfg(feature = "cat-system")]
385 #[arg(long, global = true)]
386 pub cat_system_cstl_lang: Option<String>,
389 #[cfg(feature = "flate2")]
390 #[arg(short = 'z', long, global = true, value_name = "LEVEL", value_parser = parse_compression_level, default_value_t = 6)]
391 pub zlib_compression_level: u32,
393 #[cfg(feature = "image")]
394 #[arg(short = 'g', long, global = true, value_enum, default_value_t = PngCompressionLevel::Fast)]
395 pub png_compression_level: PngCompressionLevel,
397 #[cfg(feature = "circus-img")]
398 #[arg(long, global = true, action = ArgAction::SetTrue)]
399 pub circus_crx_keep_original_bpp: bool,
401 #[cfg(feature = "circus-img")]
402 #[arg(long, global = true, action = ArgAction::SetTrue)]
403 pub circus_crx_zstd: bool,
405 #[cfg(feature = "zstd")]
406 #[arg(short = 'Z', long, global = true, value_name = "LEVEL", value_parser = parse_zstd_compression_level, default_value_t = 3)]
407 pub zstd_compression_level: i32,
409 #[cfg(feature = "circus-img")]
410 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::circus::image::crx::CircusCrxMode::Auto)]
411 pub circus_crx_mode: crate::scripts::circus::image::crx::CircusCrxMode,
413 #[cfg(feature = "circus-img")]
414 #[arg(long, global = true, action = ArgAction::SetTrue)]
415 pub circus_crx_canvas: bool,
417 #[arg(short = 'F', long, global = true, action = ArgAction::SetTrue)]
418 pub force_script: bool,
420 #[cfg(feature = "ex-hibit")]
421 #[arg(
422 long,
423 global = true,
424 value_name = "HEX",
425 group = "ex_hibit_rld_xor_keyg"
426 )]
427 pub ex_hibit_rld_xor_key: Option<String>,
430 #[cfg(feature = "ex-hibit")]
431 #[arg(
432 long,
433 global = true,
434 value_name = "PATH",
435 group = "ex_hibit_rld_xor_keyg"
436 )]
437 pub ex_hibit_rld_xor_key_file: Option<String>,
439 #[cfg(feature = "ex-hibit")]
440 #[arg(
441 long,
442 global = true,
443 value_name = "HEX",
444 group = "ex_hibit_rld_def_xor_keyg"
445 )]
446 pub ex_hibit_rld_def_xor_key: Option<String>,
448 #[cfg(feature = "ex-hibit")]
449 #[arg(
450 long,
451 global = true,
452 value_name = "PATH",
453 group = "ex_hibit_rld_def_xor_keyg"
454 )]
455 pub ex_hibit_rld_def_xor_key_file: Option<String>,
457 #[cfg(feature = "ex-hibit")]
458 #[arg(long, global = true, value_name = "PATH")]
459 pub ex_hibit_rld_keys: Option<String>,
462 #[cfg(feature = "ex-hibit")]
463 #[arg(long, global = true, value_name = "PATH")]
464 pub ex_hibit_rld_def_keys: Option<String>,
466 #[cfg(feature = "mozjpeg")]
467 #[arg(long, global = true, default_value_t = 80, value_parser = parse_jpeg_quality)]
468 pub jpeg_quality: u8,
470 #[cfg(feature = "webp")]
471 #[arg(short = 'w', long, global = true, group = "webp_qualityg")]
472 pub webp_lossless: bool,
474 #[cfg(feature = "webp")]
475 #[arg(short = 'W', long, global = true, value_name = "QUALITY", group = "webp_qualityg", value_parser = parse_webp_quality, default_value_t = 80)]
476 pub webp_quality: u8,
478 #[arg(long, global = true)]
479 pub custom_yaml: Option<bool>,
482 #[cfg(feature = "entis-gls")]
483 #[arg(long, global = true)]
484 pub entis_gls_srcxml_lang: Option<String>,
487 #[cfg(feature = "will-plus")]
488 #[arg(long, global = true)]
489 pub will_plus_ws2_no_disasm: bool,
493 #[cfg(feature = "lossless-audio")]
494 #[arg(short = 'l', long, global = true, value_enum, default_value_t = LosslessAudioFormat::Wav)]
495 pub lossless_audio_fmt: LosslessAudioFormat,
497 #[cfg(feature = "audio-flac")]
498 #[arg(short = 'L', long, global = true, default_value_t = 5, value_parser = parse_flac_compression_level)]
499 pub flac_compression_level: u32,
501 #[arg(long, global = true)]
502 pub llm_trans_mark: Option<String>,
505 #[cfg(feature = "favorite")]
506 #[arg(long, global = true, action = ArgAction::SetTrue)]
507 pub favorite_hcb_no_filter_ascii: bool,
509 #[cfg(feature = "image-jxl")]
510 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "jxl-no-lossless")]
511 pub jxl_lossy: bool,
513 #[cfg(feature = "image-jxl")]
514 #[arg(long, global = true, default_value_t = 1.0, value_parser = parse_jxl_distance)]
515 pub jxl_distance: f32,
518 #[cfg(feature = "image-jxl")]
519 #[arg(long, global = true, default_value_t = 1, visible_alias = "jxl-jobs")]
520 pub jxl_workers: usize,
523 #[cfg(feature = "image")]
524 #[arg(short = 'J', long, global = true, default_value_t = crate::types::get_default_threads(), visible_alias = "img-jobs", visible_alias = "img-workers", visible_alias = "image-jobs")]
525 pub image_workers: usize,
528 #[cfg(feature = "jieba")]
529 #[arg(long, global = true)]
530 pub jieba_dict: Option<String>,
532 #[cfg(feature = "emote-img")]
533 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "psb-no-tlg")]
534 pub psb_no_process_tlg: bool,
536 #[cfg(feature = "softpal-img")]
537 #[arg(long, global = true, visible_alias = "pgd-co")]
538 pub pgd_compress: bool,
541 #[arg(long, global = true)]
542 pub no_multi_message: bool,
544 #[cfg(feature = "softpal")]
545 #[arg(long, global = true, visible_alias = "softpal-idx")]
546 pub softpal_add_message_index: bool,
548 #[cfg(feature = "kirikiri-arc")]
549 #[arg(long, global = true)]
550 pub xp3_no_simple_crypt: bool,
552 #[cfg(feature = "kirikiri-arc")]
553 #[arg(long, global = true)]
554 pub xp3_no_mdf_decompress: bool,
556 #[cfg(feature = "kirikiri-arc")]
557 #[arg(long, global = true, default_value = "cdc:32KiB:256KiB:8MiB", value_parser = crate::scripts::kirikiri::archive::xp3::parse_segmenter_config)]
558 pub xp3_segmenter: crate::scripts::kirikiri::archive::xp3::SegmenterConfig,
564 #[cfg(feature = "kirikiri-arc")]
565 #[arg(long, global = true)]
566 pub xp3_no_compress_files: bool,
568 #[cfg(feature = "kirikiri-arc")]
569 #[arg(long, global = true)]
570 pub xp3_no_compress_index: bool,
572 #[cfg(feature = "kirikiri-arc")]
573 #[arg(long, global = true, default_value_t = num_cpus::get(), visible_alias = "xp3-compress-jobs")]
574 pub xp3_compress_workers: usize,
576 #[cfg(feature = "kirikiri-arc")]
577 #[arg(long, global = true, group = "xp3-compression")]
578 pub xp3_zstd: bool,
580 #[cfg(feature = "kirikiri-arc")]
581 #[arg(long, global = true, group = "xp3-compression")]
582 pub xp3_zopfli: bool,
584 #[cfg(feature = "kirikiri-arc")]
585 #[arg(
586 long,
587 global = true,
588 default_value_t = 1,
589 visible_alias = "xp3-pack-jobs"
590 )]
591 pub xp3_pack_workers: usize,
594 #[cfg(feature = "kirikiri")]
595 #[arg(long, global = true)]
596 pub kirikiri_language_insert: bool,
598 #[cfg(feature = "musica-arc")]
599 #[arg(long, global = true, value_parser = get_musica_game_title_value_parser())]
600 pub musica_game_title: Option<String>,
602 #[cfg(feature = "musica-arc")]
603 #[arg(long, global = true)]
604 pub musica_xor_key: Option<u8>,
606 #[cfg(feature = "musica-arc")]
607 #[arg(long, global = true)]
608 pub musica_compress: bool,
610 #[arg(short = 'x', long, default_value_t = 0, global = true)]
611 pub exit_code: i32,
613 #[arg(short = 'X', long, global = true)]
614 pub exit_code_all_failed: Option<i32>,
616 #[arg(long, global = true)]
617 pub m3t_no_quote: bool,
619 #[arg(long, global = true)]
620 pub m3t_use_original_text: bool,
622 #[cfg(feature = "kirikiri-arc")]
623 #[arg(long, global = true)]
624 pub xp3_no_adler: bool,
627 #[cfg(feature = "bgi")]
628 #[arg(long, global = true)]
629 pub bgi_add_space: bool,
632 #[cfg(feature = "zopfli")]
633 #[arg(long, global = true, default_value_t = std::num::NonZeroU64::new(15).unwrap(), visible_alias = "zp-ic")]
634 pub zopfli_iteration_count: std::num::NonZeroU64,
638 #[cfg(feature = "zopfli")]
639 #[arg(long, global = true, default_value_t = std::num::NonZeroU64::new(u64::MAX).unwrap(), visible_alias = "zp-iwi")]
640 pub zopfli_iterations_without_improvement: std::num::NonZeroU64,
643 #[cfg(feature = "zopfli")]
644 #[arg(long, global = true, default_value_t = 15, visible_alias = "zp-mbs")]
645 pub zopfli_maximum_block_splits: u16,
648 #[cfg(feature = "entis-gls")]
649 #[arg(long, global = true, action = ArgAction::SetTrue, alias = "entis-gls-csx-diasm")]
650 pub entis_gls_csx_disasm: bool,
652 #[cfg(feature = "entis-gls")]
653 #[arg(long, global = true, default_value = "/")]
654 pub entis_gls_csx_lf: String,
656 #[cfg(feature = "entis-gls")]
657 #[arg(long, global = true)]
658 pub entis_gls_csx_ver: Option<crate::scripts::entis_gls::csx::CSXScriptVersion>,
661 #[cfg(feature = "entis-gls")]
662 #[arg(long, global = true)]
663 pub entis_gls_csx_v2_ver: Option<crate::scripts::entis_gls::csx::CSXScriptV2FullVer>,
666 #[cfg(feature = "entis-gls")]
667 #[arg(long, global = true, action = ArgAction::SetTrue)]
668 pub entis_gls_csx_no_part_label: bool,
670 #[cfg(feature = "qlie-img")]
671 #[arg(long, global = true, action = ArgAction::SetTrue)]
672 pub qlie_abmp10_no_process_abmp10: bool,
674 #[cfg(feature = "qlie-arc")]
675 #[arg(long, global = true)]
676 pub qlie_pack_keyfile: Option<String>,
678 #[cfg(feature = "qlie-arc")]
679 #[arg(long, global = true, action = ArgAction::SetTrue)]
680 pub qlie_pack_compress_files: bool,
682 #[cfg(feature = "qlie-img")]
683 #[arg(long, global = true, action = ArgAction::SetTrue)]
684 pub qlie_dpng_use_raw_png: bool,
687 #[cfg(feature = "qlie-img")]
688 #[arg(long, global = true, action = ArgAction::SetTrue)]
689 pub qlie_dpng_psd: bool,
691 #[cfg(feature = "utils-psd")]
692 #[arg(long, global = true, action = ArgAction::SetTrue)]
693 pub psd_no_compress: bool,
695 #[cfg(feature = "emote-img")]
696 #[arg(long, global = true, action = ArgAction::SetTrue)]
697 pub emote_pimg_psd: bool,
699 #[cfg(feature = "kirikiri")]
700 #[arg(long, global = true)]
701 pub kirikiri_ks_hitret: Option<bool>,
703 #[cfg(feature = "kirikiri")]
704 #[arg(long, global = true)]
705 pub kirikiri_ks_lf: Option<String>,
707 #[cfg(feature = "kirikiri")]
708 #[arg(long, global = true, value_delimiter = ',', default_value = "macCmd")]
709 pub kirikiri_message_tags: Vec<String>,
711 #[cfg(feature = "kirikiri")]
712 #[arg(long, global = true)]
713 pub kirikiri_ks_bom: Option<BomType>,
715 #[cfg(feature = "emote-img")]
716 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::emote::psb::BC7Config::default())]
717 pub bc7: crate::scripts::emote::psb::BC7Config,
719 #[cfg(feature = "artemis")]
720 #[arg(
721 long,
722 global = true,
723 value_delimiter = ',',
724 default_value = "click,hcls,rpx"
725 )]
726 pub artemis_asb_end_tags: Vec<String>,
728 #[cfg(feature = "kirikiri-arc")]
729 #[arg(long, global = true, value_parser = get_xp3_game_title_value_parser())]
730 pub xp3_game_title: Option<String>,
732 #[cfg(feature = "kirikiri-arc")]
733 #[arg(long, global = true)]
734 pub xp3_debug_archive: bool,
737 #[cfg(feature = "kirikiri-arc")]
738 #[arg(long, global = true)]
739 pub xp3_force_extract: bool,
741 #[cfg(feature = "kirikiri-arc")]
742 #[arg(long, global = true, visible_alias = "xp3-fd")]
743 pub xp3_force_decrypt: bool,
746 #[cfg(feature = "emote-img")]
747 #[arg(long, global = true)]
748 pub emote_pimg_psd_no_diff: bool,
751 #[cfg(feature = "kirikiri-arc")]
752 #[arg(long, global = true)]
753 pub xp3_file_list_path: Option<String>,
756 #[cfg(feature = "kirikiri-arc")]
757 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::kirikiri::archive::xp3::FileHashOption::Both)]
758 pub xp3_cxdec_file_hash: crate::scripts::kirikiri::archive::xp3::FileHashOption,
760 #[cfg(feature = "kirikiri-arc")]
761 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::kirikiri::archive::xp3::PathHashOption::Both)]
762 pub xp3_cxdec_path_hash: crate::scripts::kirikiri::archive::xp3::PathHashOption,
764 #[cfg(feature = "kirikiri-arc")]
765 #[arg(long, global = true)]
766 pub xp3_dump_file_hash_list: Option<String>,
768 #[cfg(feature = "yuris")]
769 #[arg(long, global = true)]
770 pub yuris_ysc_path: Option<String>,
772 #[cfg(feature = "yuris")]
773 #[arg(long, global = true)]
774 pub yuris_ysl_path: Option<String>,
776 #[cfg(feature = "yuris")]
777 #[arg(long, global = true)]
778 pub yuris_ystb_disasm: bool,
780 #[cfg(feature = "yuris")]
781 #[arg(long, global = true)]
782 pub yuris_tips_map: Option<String>,
784 #[cfg(feature = "yuris-arc")]
785 #[arg(long, global = true, value_enum, default_value_t = Default::default())]
786 pub yuris_name_hash_type: crate::scripts::yuris::arc::ypf::NameHashType,
788 #[cfg(feature = "yuris-arc")]
789 #[arg(long, global = true, value_enum, default_value_t = Default::default())]
790 pub yuris_data_hash_type: crate::scripts::yuris::arc::ypf::DataHashType,
792 #[cfg(feature = "yuris-arc")]
793 #[arg(long, global = true)]
794 pub yuris_check_hash: bool,
796 #[cfg(feature = "yuris-arc")]
797 #[arg(long, global = true)]
798 pub yuris_debug_archive: bool,
801 #[cfg(feature = "yuris-arc")]
802 #[arg(long, global = true)]
803 pub yuris_ypf_version: Option<u32>,
805 #[cfg(feature = "yuris-arc")]
806 #[arg(long, global = true)]
807 pub yuris_ypf_no_compress_file: bool,
809 #[cfg(feature = "yuris-arc")]
810 #[arg(long, global = true)]
811 pub yuris_ypf_zopfli: bool,
813 #[cfg(feature = "yuris-arc")]
814 #[arg(long, global = true, default_value_t = num_cpus::get())]
815 pub yuris_ypf_workers: usize,
817 #[cfg(feature = "yuris-arc")]
818 #[arg(long, global = true)]
819 pub yuris_use_new_file_type: bool,
822 #[command(subcommand)]
823 pub command: Command,
825}
826
827#[derive(Parser, Debug, Clone)]
828#[clap(group = ArgGroup::new("patched_encodingg").multiple(false), group = ArgGroup::new("patched_archive_encodingg").multiple(false))]
829pub struct ImportArgs {
830 pub input: String,
832 pub output: String,
834 pub patched: String,
836 #[arg(short = 'p', long, group = "patched_encodingg")]
837 pub patched_encoding: Option<TextEncoding>,
839 #[cfg(windows)]
840 #[arg(short = 'P', long, group = "patched_encodingg")]
841 pub patched_code_page: Option<u32>,
843 #[arg(
844 long,
845 value_enum,
846 group = "patched_archive_encodingg",
847 visible_alias = "pa"
848 )]
849 pub patched_archive_encoding: Option<TextEncoding>,
851 #[cfg(windows)]
852 #[arg(
853 long,
854 value_enum,
855 group = "patched_archive_encodingg",
856 visible_alias = "PA"
857 )]
858 pub patched_archive_code_page: Option<u32>,
860 #[arg(long)]
861 pub patched_format: Option<FormatType>,
863 #[arg(long)]
864 pub patched_fixed_length: Option<usize>,
866 #[arg(long, action = ArgAction::SetTrue)]
867 pub patched_keep_original: bool,
869 #[arg(long, action = ArgAction::SetTrue)]
870 pub patched_break_words: bool,
872 #[arg(long, action = ArgAction::SetTrue)]
873 pub patched_insert_fullwidth_space_at_line_start: bool,
875 #[arg(long, action = ArgAction::SetTrue)]
876 pub patched_break_with_sentence: bool,
878 #[cfg(feature = "jieba")]
879 #[arg(long, action = ArgAction::SetTrue)]
880 pub patched_no_break_chinese_words: bool,
882 #[arg(long, action = ArgAction::SetTrue)]
883 pub patched_no_remove_space_at_line_start: bool,
885 #[arg(long)]
886 pub name_csv: Option<String>,
888 #[arg(long)]
889 pub replacement_json: Option<String>,
891 #[arg(long, action = ArgAction::SetTrue)]
892 pub warn_when_output_file_not_found: bool,
893 #[arg(long)]
894 pub dep_file: Option<String>,
896 #[arg(short = 'j', long, default_value_t = 1)]
897 pub jobs: usize,
899}
900
901#[derive(Subcommand, Debug, Clone)]
902pub enum Command {
904 Export {
906 input: String,
908 output: Option<String>,
910 },
911 Import(ImportArgs),
913 Pack {
915 input: String,
917 output: Option<String>,
919 #[arg(long)]
920 backslash: bool,
922 },
923 Unpack {
925 input: String,
927 output: Option<String>,
929 #[arg(short = 's', long)]
930 skip_existed: bool,
932 },
933 Create {
935 input: String,
937 output: Option<String>,
939 },
940 PackV2 {
942 #[arg(short = 'o', long)]
943 output: Option<String>,
945 input: Vec<String>,
947 #[arg(long)]
948 backslash: bool,
950 #[arg(long)]
951 no_dir: bool,
953 #[arg(long)]
954 dep_file: Option<String>,
956 },
957 Convert {
959 input_type: OutputScriptType,
961 output_type: OutputScriptType,
963 input: String,
965 output: Option<String>,
967 },
968}
969
970pub fn parse_args() -> Arg {
971 Arg::parse()
972}
973
974#[cfg(feature = "ex-hibit")]
975pub fn load_ex_hibit_rld_xor_key(arg: &Arg) -> anyhow::Result<Option<u32>> {
976 if let Some(key) = &arg.ex_hibit_rld_xor_key {
977 if key.starts_with("0x") {
978 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
979 } else {
980 return Ok(Some(u32::from_str_radix(key, 16)?));
981 }
982 }
983 if let Some(file) = &arg.ex_hibit_rld_xor_key_file {
984 let key = std::fs::read_to_string(file)?.trim().to_string();
985 if key.starts_with("0x") {
986 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
987 } else {
988 return Ok(Some(u32::from_str_radix(&key, 16)?));
989 }
990 }
991 Ok(None)
992}
993
994#[cfg(feature = "ex-hibit")]
995pub fn load_ex_hibit_rld_def_xor_key(arg: &crate::args::Arg) -> anyhow::Result<Option<u32>> {
996 if let Some(key) = &arg.ex_hibit_rld_def_xor_key {
997 if key.starts_with("0x") {
998 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
999 } else {
1000 return Ok(Some(u32::from_str_radix(key, 16)?));
1001 }
1002 }
1003 if let Some(file) = &arg.ex_hibit_rld_def_xor_key_file {
1004 let key = std::fs::read_to_string(file)?.trim().to_string();
1005 if key.starts_with("0x") {
1006 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
1007 } else {
1008 return Ok(Some(u32::from_str_radix(&key, 16)?));
1009 }
1010 }
1011 Ok(None)
1012}
1013
1014#[cfg(feature = "cat-system-arc")]
1015pub fn get_cat_system_int_encrypt_password(arg: &Arg) -> anyhow::Result<Option<String>> {
1016 if let Some(exe) = &arg.cat_system_int_exe {
1017 return Ok(Some(
1018 crate::scripts::cat_system::archive::int::get_password_from_exe(exe)?,
1019 ));
1020 }
1021 if let Some(password) = &arg.cat_system_int_encrypt_password {
1022 return Ok(Some(password.clone()));
1023 }
1024 Ok(None)
1025}
1026
1027#[cfg(feature = "artemis-panmimisoft")]
1028pub fn get_artemis_panmimisoft_txt_blacklist_names(
1029 arg: &Arg,
1030) -> anyhow::Result<std::collections::HashSet<String>> {
1031 match &arg.artemis_panmimisoft_txt_tag_ini {
1032 Some(path) => {
1033 let mut set = crate::scripts::artemis::panmimisoft::txt::read_tags_from_ini(path)?;
1034 for name in &arg.artemis_panmimisoft_txt_blacklist_names {
1035 set.insert(name.clone());
1036 }
1037 Ok(set)
1038 }
1039 None => Ok(arg
1040 .artemis_panmimisoft_txt_blacklist_names
1041 .iter()
1042 .cloned()
1043 .collect()),
1044 }
1045}
1046
1047#[cfg(feature = "kirikiri")]
1048pub fn load_kirikiri_chat_json(
1049 arg: &Arg,
1050) -> anyhow::Result<
1051 Option<
1052 std::sync::Arc<
1053 std::collections::HashMap<String, std::collections::HashMap<String, (String, usize)>>,
1054 >,
1055 >,
1056> {
1057 if let Some(path) = &arg.kirikiri_chat_json {
1058 return Ok(Some(std::sync::Arc::new(
1059 crate::scripts::kirikiri::read_kirikiri_comu_json(path)?
1060 .into_iter()
1061 .map(|(k, v)| {
1062 let v: std::collections::HashMap<_, _> =
1063 v.into_iter().map(|(k, v)| (k, (v, 1))).collect();
1064 (k, v)
1065 })
1066 .collect(),
1067 )));
1068 }
1069 if let Some(dir) = &arg.kirikiri_chat_dir {
1070 let mut outt = arg.output_type.unwrap_or(OutputScriptType::M3t);
1071 if !matches!(
1072 outt,
1073 OutputScriptType::M3t
1074 | OutputScriptType::M3ta
1075 | OutputScriptType::M3tTxt
1076 | OutputScriptType::Po
1077 | OutputScriptType::Pot
1078 ) {
1079 outt = OutputScriptType::M3t;
1080 }
1081 let files = crate::utils::files::find_ext_files(dir, arg.recursive, &[outt.as_ref()])?;
1082 if !files.is_empty() {
1083 let mut map = std::collections::HashMap::new();
1084 let mut global: std::collections::HashMap<
1085 String,
1086 (String, std::collections::HashSet<String>),
1087 > = std::collections::HashMap::new();
1088 for file in files {
1089 let f = crate::utils::files::read_file(&file)?;
1090 let data = crate::utils::encoding::decode_to_string(
1091 crate::get_output_encoding(arg),
1092 &f,
1093 true,
1094 )?;
1095 let m3t = if outt.is_m3t() {
1096 crate::output_scripts::m3t::M3tParser::new(
1097 &data,
1098 arg.llm_trans_mark.as_ref().map(|s| s.as_str()),
1099 arg.m3t_use_original_text,
1100 )
1101 .parse_as_vec()?
1102 } else {
1103 crate::output_scripts::po::PoParser::new(
1104 &data,
1105 arg.llm_trans_mark.as_ref().map(|s| s.as_str()),
1106 )
1107 .parse_as_vec()?
1108 };
1109 let current_key = std::path::Path::new(&file)
1110 .file_stem()
1111 .and_then(|s| s.to_str())
1112 .unwrap_or("unknown")
1113 .to_string();
1114 let mut entry: std::collections::HashMap<
1115 String,
1116 (String, std::collections::HashSet<String>),
1117 > = std::collections::HashMap::new();
1118 for (k, v) in m3t {
1119 if v.is_empty() {
1120 continue;
1121 }
1122 let k = k.replace("\\[", "[");
1123 let v = v.replace("\\[", "[");
1124 if let Some((_, count)) = entry.get_mut(&k) {
1125 count.insert(v.clone());
1126 } else {
1127 entry.insert(
1128 k.clone(),
1129 (v.clone(), std::collections::HashSet::from_iter([v.clone()])),
1130 );
1131 }
1132 if let Some((_, count)) = global.get_mut(&k) {
1133 count.insert(v.clone());
1134 } else {
1135 global.insert(
1136 k,
1137 (v.clone(), std::collections::HashSet::from_iter([v.clone()])),
1138 );
1139 }
1140 }
1141 map.insert(current_key, entry);
1142 }
1143 map.insert("global".to_string(), global);
1144 return Ok(Some(std::sync::Arc::new(
1145 map.into_iter()
1146 .map(|(k, v)| {
1147 let v: std::collections::HashMap<_, _> =
1148 v.into_iter().map(|(k, (v, s))| (k, (v, s.len()))).collect();
1149 (k, v)
1150 })
1151 .collect(),
1152 )));
1153 }
1154 }
1155 Ok(None)
1156}
1157
1158#[cfg(feature = "yuris")]
1159pub fn load_yuris_tips_map(
1160 arg: &Arg,
1161) -> anyhow::Result<Option<std::sync::Arc<std::collections::HashMap<String, String>>>> {
1162 if let Some(path) = &arg.yuris_tips_map {
1163 let data = crate::utils::files::read_file(path)?;
1164 let result = serde_json::from_slice(&data)?;
1165 return Ok(Some(std::sync::Arc::new(result)));
1166 }
1167 Ok(None)
1168}