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#[derive(Parser, Debug, Clone)]
99#[clap(
100 group = ArgGroup::new("encodingg").multiple(false),
101 group = ArgGroup::new("output_encodingg").multiple(false),
102 group = ArgGroup::new("archive_encodingg").multiple(false),
103 group = ArgGroup::new("artemis_indentg").multiple(false),
104 group = ArgGroup::new("ex_hibit_rld_xor_keyg").multiple(false),
105 group = ArgGroup::new("ex_hibit_rld_def_xor_keyg").multiple(false),
106 group = ArgGroup::new("webp_qualityg").multiple(false),
107 group = ArgGroup::new("cat_system_int_encrypt_passwordg").multiple(false),
108 group = ArgGroup::new("kirikiri_chat_jsong").multiple(false),
109 group = ArgGroup::new("xp3-compression").multiple(false),
110)]
111#[command(
112 version,
113 about,
114 long_about = "Tools for export and import scripts\nhttps://github.com/lifegpc/msg-tool"
115)]
116pub struct Arg {
117 #[arg(short = 't', long, value_enum, global = true)]
118 pub script_type: Option<ScriptType>,
120 #[arg(short = 'T', long, value_enum, global = true)]
121 pub output_type: Option<OutputScriptType>,
123 #[arg(short = 'n', long, global = true)]
124 pub output_no_extra_ext: bool,
126 #[cfg(feature = "image")]
127 #[arg(short = 'i', long, value_enum, global = true)]
128 pub image_type: Option<ImageOutputType>,
130 #[arg(short = 'e', long, value_enum, global = true, group = "encodingg")]
131 pub encoding: Option<TextEncoding>,
133 #[cfg(windows)]
134 #[arg(short = 'c', long, value_enum, global = true, group = "encodingg")]
135 pub code_page: Option<u32>,
137 #[arg(
138 short = 'E',
139 long,
140 value_enum,
141 global = true,
142 group = "output_encodingg"
143 )]
144 pub output_encoding: Option<TextEncoding>,
146 #[cfg(windows)]
147 #[arg(
148 short = 'C',
149 long,
150 value_enum,
151 global = true,
152 group = "output_encodingg"
153 )]
154 pub output_code_page: Option<u32>,
156 #[arg(
157 short = 'a',
158 long,
159 value_enum,
160 global = true,
161 group = "archive_encodingg"
162 )]
163 pub archive_encoding: Option<TextEncoding>,
165 #[cfg(windows)]
166 #[arg(
167 short = 'A',
168 long,
169 value_enum,
170 global = true,
171 group = "archive_encodingg"
172 )]
173 pub archive_code_page: Option<u32>,
175 #[cfg(feature = "circus")]
176 #[arg(long, value_enum, global = true)]
177 pub circus_mes_type: Option<CircusMesType>,
179 #[arg(short, long, action = ArgAction::SetTrue, global = true)]
180 pub recursive: bool,
182 #[arg(global = true, action = ArgAction::SetTrue, short, long)]
183 pub backtrace: bool,
185 #[cfg(feature = "escude-arc")]
186 #[arg(long, action = ArgAction::SetTrue, global = true)]
187 pub escude_fake_compress: bool,
189 #[cfg(feature = "escude")]
190 #[arg(long, global = true)]
191 pub escude_enum_scr: Option<String>,
193 #[cfg(feature = "escude")]
194 #[arg(long, global = true)]
195 pub escude_op: Option<crate::scripts::escude::script::EscudeOp>,
197 #[cfg(feature = "bgi")]
198 #[arg(long, action = ArgAction::SetTrue, global = true)]
199 pub bgi_import_duplicate: bool,
202 #[cfg(feature = "bgi")]
203 #[arg(long, action = ArgAction::SetTrue, global = true, visible_alias = "bgi-no-append")]
204 pub bgi_disable_append: bool,
207 #[cfg(all(feature = "bgi-arc", feature = "bgi-img"))]
208 #[arg(long, global = true)]
209 pub bgi_is_sysgrp_arc: Option<bool>,
211 #[cfg(feature = "bgi-img")]
212 #[arg(long, global = true)]
213 pub bgi_img_scramble: Option<bool>,
216 #[cfg(feature = "bgi-img")]
217 #[arg(long, global = true, default_value_t = crate::types::get_default_threads())]
218 pub bgi_img_workers: usize,
221 #[cfg(feature = "cat-system-arc")]
222 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
223 pub cat_system_int_encrypt_password: Option<String>,
225 #[cfg(feature = "cat-system-arc")]
226 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
227 pub cat_system_int_exe: Option<String>,
229 #[cfg(feature = "cat-system-img")]
230 #[arg(long, global = true, action = ArgAction::SetTrue)]
231 pub cat_system_image_canvas: bool,
233 #[cfg(feature = "kirikiri")]
234 #[arg(long, global = true)]
235 pub kirikiri_language_index: Option<usize>,
237 #[cfg(feature = "kirikiri")]
238 #[arg(long, global = true)]
239 pub kirikiri_export_chat: bool,
242 #[cfg(feature = "kirikiri")]
243 #[arg(long, global = true, value_delimiter = ',')]
244 pub kirikiri_chat_key: Option<Vec<String>>,
247 #[cfg(feature = "kirikiri")]
248 #[arg(long, global = true, group = "kirikiri_chat_jsong")]
249 pub kirikiri_chat_json: Option<String>,
251 #[cfg(feature = "kirikiri")]
252 #[arg(long, global = true, group = "kirikiri_chat_jsong")]
253 pub kirikiri_chat_dir: Option<String>,
255 #[cfg(feature = "kirikiri")]
256 #[arg(long, global = true, value_delimiter = ',')]
257 pub kirikiri_languages: Option<Vec<String>>,
259 #[cfg(feature = "kirikiri")]
260 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-title")]
261 pub kirikiri_title: bool,
263 #[cfg(feature = "kirikiri")]
264 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-chat-no-multilang")]
265 pub kirikiri_chat_no_multilang: bool,
267 #[cfg(feature = "kirikiri")]
268 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-no-empty-lines", visible_alias = "kirikiri-no-empty-lines")]
269 pub kirikiri_remove_empty_lines: bool,
271 #[cfg(feature = "kirikiri")]
272 #[arg(
273 long,
274 global = true,
275 value_delimiter = ',',
276 default_value = "nm,set_title,speaker,Talk,talk,cn,name,名前"
277 )]
278 pub kirikiri_name_commands: Vec<String>,
280 #[cfg(feature = "kirikiri")]
281 #[arg(
282 long,
283 global = true,
284 value_delimiter = ',',
285 default_value = "sel01,sel02,sel03,sel04,AddSelect,ruby,exlink,e_xlink"
286 )]
287 pub kirikiri_message_commands: Vec<String>,
289 #[cfg(feature = "image")]
290 #[arg(short = 'f', long, global = true)]
291 pub image_output_flat: bool,
293 #[cfg(feature = "bgi-arc")]
294 #[arg(long, global = true, action = ArgAction::SetTrue)]
295 pub bgi_compress_file: bool,
297 #[cfg(feature = "bgi-arc")]
298 #[arg(long, global = true, default_value_t = 3, value_parser = crate::scripts::bgi::archive::dsc::parse_min_length)]
299 pub bgi_compress_min_len: usize,
301 #[cfg(feature = "emote-img")]
302 #[arg(long, global = true)]
303 pub emote_pimg_overlay: Option<bool>,
305 #[cfg(feature = "artemis-arc")]
306 #[arg(long, global = true)]
307 pub artemis_arc_disable_xor: bool,
309 #[cfg(feature = "artemis")]
310 #[arg(long, global = true, group = "artemis_indentg")]
311 pub artemis_indent: Option<usize>,
314 #[cfg(feature = "artemis")]
315 #[arg(long, global = true, action = ArgAction::SetTrue, group = "artemis_indentg")]
316 pub artemis_no_indent: bool,
318 #[cfg(feature = "artemis")]
319 #[arg(long, global = true, default_value_t = 100)]
320 pub artemis_max_line_width: usize,
322 #[cfg(feature = "artemis")]
323 #[arg(long, global = true)]
324 pub artemis_ast_lang: Option<String>,
327 #[cfg(feature = "artemis")]
328 #[arg(long, global = true, action = ArgAction::SetTrue)]
329 pub artemis_asb_no_format_lua: bool,
331 #[cfg(feature = "artemis-panmimisoft")]
333 #[arg(
334 long,
335 global = true,
336 value_delimiter = ',',
337 default_value = "背景,イベントCG,遅延背景,遅延背景予約,背景予約,遅延イベントCG,遅延イベントCG予約,イベントCG予約,遅延ポップアップ,遅延bgm_in,遅延bgm_out,遅延se_in,遅延se_out,遅延bgs_in,遅延bgs_out,立ち絵face非連動,セーブサムネイル置換終了,シネスコ,ポップアップ"
338 )]
339 pub artemis_panmimisoft_txt_blacklist_names: Vec<String>,
342 #[cfg(feature = "artemis-panmimisoft")]
343 #[arg(long, global = true)]
344 pub artemis_panmimisoft_txt_lang: Option<String>,
347 #[cfg(feature = "artemis-panmimisoft")]
348 #[arg(long, global = true, action = ArgAction::SetTrue, requires = "artemis_panmimisoft_txt_lang")]
349 pub artemis_panmimisoft_txt_multi_lang: bool,
352 #[cfg(feature = "artemis-panmimisoft")]
353 #[arg(long, global = true)]
354 pub artemis_panmimisoft_txt_tag_ini: Option<String>,
356 #[cfg(feature = "cat-system")]
357 #[arg(long, global = true)]
358 pub cat_system_cstl_lang: Option<String>,
361 #[cfg(feature = "flate2")]
362 #[arg(short = 'z', long, global = true, value_name = "LEVEL", value_parser = parse_compression_level, default_value_t = 6)]
363 pub zlib_compression_level: u32,
365 #[cfg(feature = "image")]
366 #[arg(short = 'g', long, global = true, value_enum, default_value_t = PngCompressionLevel::Fast)]
367 pub png_compression_level: PngCompressionLevel,
369 #[cfg(feature = "circus-img")]
370 #[arg(long, global = true, action = ArgAction::SetTrue)]
371 pub circus_crx_keep_original_bpp: bool,
373 #[cfg(feature = "circus-img")]
374 #[arg(long, global = true, action = ArgAction::SetTrue)]
375 pub circus_crx_zstd: bool,
377 #[cfg(feature = "zstd")]
378 #[arg(short = 'Z', long, global = true, value_name = "LEVEL", value_parser = parse_zstd_compression_level, default_value_t = 3)]
379 pub zstd_compression_level: i32,
381 #[cfg(feature = "circus-img")]
382 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::circus::image::crx::CircusCrxMode::Auto)]
383 pub circus_crx_mode: crate::scripts::circus::image::crx::CircusCrxMode,
385 #[cfg(feature = "circus-img")]
386 #[arg(long, global = true, action = ArgAction::SetTrue)]
387 pub circus_crx_canvas: bool,
389 #[arg(short = 'F', long, global = true, action = ArgAction::SetTrue)]
390 pub force_script: bool,
392 #[cfg(feature = "ex-hibit")]
393 #[arg(
394 long,
395 global = true,
396 value_name = "HEX",
397 group = "ex_hibit_rld_xor_keyg"
398 )]
399 pub ex_hibit_rld_xor_key: Option<String>,
402 #[cfg(feature = "ex-hibit")]
403 #[arg(
404 long,
405 global = true,
406 value_name = "PATH",
407 group = "ex_hibit_rld_xor_keyg"
408 )]
409 pub ex_hibit_rld_xor_key_file: Option<String>,
411 #[cfg(feature = "ex-hibit")]
412 #[arg(
413 long,
414 global = true,
415 value_name = "HEX",
416 group = "ex_hibit_rld_def_xor_keyg"
417 )]
418 pub ex_hibit_rld_def_xor_key: Option<String>,
420 #[cfg(feature = "ex-hibit")]
421 #[arg(
422 long,
423 global = true,
424 value_name = "PATH",
425 group = "ex_hibit_rld_def_xor_keyg"
426 )]
427 pub ex_hibit_rld_def_xor_key_file: Option<String>,
429 #[cfg(feature = "ex-hibit")]
430 #[arg(long, global = true, value_name = "PATH")]
431 pub ex_hibit_rld_keys: Option<String>,
434 #[cfg(feature = "ex-hibit")]
435 #[arg(long, global = true, value_name = "PATH")]
436 pub ex_hibit_rld_def_keys: Option<String>,
438 #[cfg(feature = "mozjpeg")]
439 #[arg(long, global = true, default_value_t = 80, value_parser = parse_jpeg_quality)]
440 pub jpeg_quality: u8,
442 #[cfg(feature = "webp")]
443 #[arg(short = 'w', long, global = true, group = "webp_qualityg")]
444 pub webp_lossless: bool,
446 #[cfg(feature = "webp")]
447 #[arg(short = 'W', long, global = true, value_name = "QUALITY", group = "webp_qualityg", value_parser = parse_webp_quality, default_value_t = 80)]
448 pub webp_quality: u8,
450 #[arg(long, global = true)]
451 pub custom_yaml: Option<bool>,
454 #[cfg(feature = "entis-gls")]
455 #[arg(long, global = true)]
456 pub entis_gls_srcxml_lang: Option<String>,
459 #[cfg(feature = "will-plus")]
460 #[arg(long, global = true)]
461 pub will_plus_ws2_no_disasm: bool,
465 #[cfg(feature = "lossless-audio")]
466 #[arg(short = 'l', long, global = true, value_enum, default_value_t = LosslessAudioFormat::Wav)]
467 pub lossless_audio_fmt: LosslessAudioFormat,
469 #[cfg(feature = "audio-flac")]
470 #[arg(short = 'L', long, global = true, default_value_t = 5, value_parser = parse_flac_compression_level)]
471 pub flac_compression_level: u32,
473 #[arg(long, global = true)]
474 pub llm_trans_mark: Option<String>,
477 #[cfg(feature = "favorite")]
478 #[arg(long, global = true, action = ArgAction::SetTrue)]
479 pub favorite_hcb_no_filter_ascii: bool,
481 #[cfg(feature = "image-jxl")]
482 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "jxl-no-lossless")]
483 pub jxl_lossy: bool,
485 #[cfg(feature = "image-jxl")]
486 #[arg(long, global = true, default_value_t = 1.0, value_parser = parse_jxl_distance)]
487 pub jxl_distance: f32,
490 #[cfg(feature = "image-jxl")]
491 #[arg(long, global = true, default_value_t = 1, visible_alias = "jxl-jobs")]
492 pub jxl_workers: usize,
495 #[cfg(feature = "image")]
496 #[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")]
497 pub image_workers: usize,
500 #[cfg(feature = "jieba")]
501 #[arg(long, global = true)]
502 pub jieba_dict: Option<String>,
504 #[cfg(feature = "emote-img")]
505 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "psb-no-tlg")]
506 pub psb_no_process_tlg: bool,
508 #[cfg(feature = "softpal-img")]
509 #[arg(long, global = true, visible_alias = "pgd-co")]
510 pub pgd_compress: bool,
513 #[arg(long, global = true)]
514 pub no_multi_message: bool,
516 #[cfg(feature = "softpal")]
517 #[arg(long, global = true, visible_alias = "softpal-idx")]
518 pub softpal_add_message_index: bool,
520 #[cfg(feature = "kirikiri-arc")]
521 #[arg(long, global = true)]
522 pub xp3_no_simple_crypt: bool,
524 #[cfg(feature = "kirikiri-arc")]
525 #[arg(long, global = true)]
526 pub xp3_no_mdf_decompress: bool,
528 #[cfg(feature = "kirikiri-arc")]
529 #[arg(long, global = true, default_value = "cdc:32KiB:256KiB:8MiB", value_parser = crate::scripts::kirikiri::archive::xp3::parse_segmenter_config)]
530 pub xp3_segmenter: crate::scripts::kirikiri::archive::xp3::SegmenterConfig,
535 #[cfg(feature = "kirikiri-arc")]
536 #[arg(long, global = true)]
537 pub xp3_no_compress_files: bool,
539 #[cfg(feature = "kirikiri-arc")]
540 #[arg(long, global = true)]
541 pub xp3_no_compress_index: bool,
543 #[cfg(feature = "kirikiri-arc")]
544 #[arg(long, global = true, default_value_t = num_cpus::get(), visible_alias = "xp3-compress-jobs")]
545 pub xp3_compress_workers: usize,
547 #[cfg(feature = "kirikiri-arc")]
548 #[arg(long, global = true, group = "xp3-compression")]
549 pub xp3_zstd: bool,
551 #[cfg(feature = "kirikiri-arc")]
552 #[arg(long, global = true, group = "xp3-compression")]
553 pub xp3_zopfli: bool,
555 #[cfg(feature = "kirikiri-arc")]
556 #[arg(
557 long,
558 global = true,
559 default_value_t = 1,
560 visible_alias = "xp3-pack-jobs"
561 )]
562 pub xp3_pack_workers: usize,
565 #[cfg(feature = "kirikiri")]
566 #[arg(long, global = true)]
567 pub kirikiri_language_insert: bool,
569 #[cfg(feature = "musica-arc")]
570 #[arg(long, global = true, value_parser = get_musica_game_title_value_parser())]
571 pub musica_game_title: Option<String>,
573 #[cfg(feature = "musica-arc")]
574 #[arg(long, global = true)]
575 pub musica_xor_key: Option<u8>,
577 #[cfg(feature = "musica-arc")]
578 #[arg(long, global = true)]
579 pub musica_compress: bool,
581 #[arg(short = 'x', long, default_value_t = 0, global = true)]
582 pub exit_code: i32,
584 #[arg(short = 'X', long, global = true)]
585 pub exit_code_all_failed: Option<i32>,
587 #[arg(long, global = true)]
588 pub m3t_no_quote: bool,
590 #[arg(long, global = true)]
591 pub m3t_use_original_text: bool,
593 #[cfg(feature = "kirikiri-arc")]
594 #[arg(long, global = true)]
595 pub xp3_no_adler: bool,
598 #[cfg(feature = "bgi")]
599 #[arg(long, global = true)]
600 pub bgi_add_space: bool,
603 #[cfg(feature = "zopfli")]
604 #[arg(long, global = true, default_value_t = std::num::NonZeroU64::new(15).unwrap(), visible_alias = "zp-ic")]
605 pub zopfli_iteration_count: std::num::NonZeroU64,
609 #[cfg(feature = "zopfli")]
610 #[arg(long, global = true, default_value_t = std::num::NonZeroU64::new(u64::MAX).unwrap(), visible_alias = "zp-iwi")]
611 pub zopfli_iterations_without_improvement: std::num::NonZeroU64,
614 #[cfg(feature = "zopfli")]
615 #[arg(long, global = true, default_value_t = 15, visible_alias = "zp-mbs")]
616 pub zopfli_maximum_block_splits: u16,
619 #[cfg(feature = "entis-gls")]
620 #[arg(long, global = true, action = ArgAction::SetTrue, alias = "entis-gls-csx-diasm")]
621 pub entis_gls_csx_disasm: bool,
623 #[cfg(feature = "entis-gls")]
624 #[arg(long, global = true, default_value = "/")]
625 pub entis_gls_csx_lf: String,
627 #[cfg(feature = "entis-gls")]
628 #[arg(long, global = true)]
629 pub entis_gls_csx_ver: Option<crate::scripts::entis_gls::csx::CSXScriptVersion>,
632 #[cfg(feature = "entis-gls")]
633 #[arg(long, global = true)]
634 pub entis_gls_csx_v2_ver: Option<crate::scripts::entis_gls::csx::CSXScriptV2FullVer>,
637 #[cfg(feature = "entis-gls")]
638 #[arg(long, global = true, action = ArgAction::SetTrue)]
639 pub entis_gls_csx_no_part_label: bool,
641 #[cfg(feature = "qlie-img")]
642 #[arg(long, global = true, action = ArgAction::SetTrue)]
643 pub qlie_abmp10_no_process_abmp10: bool,
645 #[cfg(feature = "qlie-arc")]
646 #[arg(long, global = true)]
647 pub qlie_pack_keyfile: Option<String>,
649 #[cfg(feature = "qlie-arc")]
650 #[arg(long, global = true, action = ArgAction::SetTrue)]
651 pub qlie_pack_compress_files: bool,
653 #[cfg(feature = "qlie-img")]
654 #[arg(long, global = true, action = ArgAction::SetTrue)]
655 pub qlie_dpng_use_raw_png: bool,
658 #[cfg(feature = "qlie-img")]
659 #[arg(long, global = true, action = ArgAction::SetTrue)]
660 pub qlie_dpng_psd: bool,
662 #[cfg(feature = "utils-psd")]
663 #[arg(long, global = true, action = ArgAction::SetTrue)]
664 pub psd_no_compress: bool,
666 #[cfg(feature = "emote-img")]
667 #[arg(long, global = true, action = ArgAction::SetTrue)]
668 pub emote_pimg_psd: bool,
670 #[cfg(feature = "kirikiri")]
671 #[arg(long, global = true)]
672 pub kirikiri_ks_hitret: Option<bool>,
674 #[cfg(feature = "kirikiri")]
675 #[arg(long, global = true)]
676 pub kirikiri_ks_lf: Option<String>,
678 #[cfg(feature = "kirikiri")]
679 #[arg(long, global = true, value_delimiter = ',', default_value = "macCmd")]
680 pub kirikiri_message_tags: Vec<String>,
682 #[cfg(feature = "kirikiri")]
683 #[arg(long, global = true)]
684 pub kirikiri_ks_bom: Option<BomType>,
686 #[cfg(feature = "emote-img")]
687 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::emote::psb::BC7Config::default())]
688 pub bc7: crate::scripts::emote::psb::BC7Config,
690 #[command(subcommand)]
691 pub command: Command,
693}
694
695#[derive(Parser, Debug, Clone)]
696#[clap(group = ArgGroup::new("patched_encodingg").multiple(false), group = ArgGroup::new("patched_archive_encodingg").multiple(false))]
697pub struct ImportArgs {
698 pub input: String,
700 pub output: String,
702 pub patched: String,
704 #[arg(short = 'p', long, group = "patched_encodingg")]
705 pub patched_encoding: Option<TextEncoding>,
707 #[cfg(windows)]
708 #[arg(short = 'P', long, group = "patched_encodingg")]
709 pub patched_code_page: Option<u32>,
711 #[arg(
712 long,
713 value_enum,
714 group = "patched_archive_encodingg",
715 visible_alias = "pa"
716 )]
717 pub patched_archive_encoding: Option<TextEncoding>,
719 #[cfg(windows)]
720 #[arg(
721 long,
722 value_enum,
723 group = "patched_archive_encodingg",
724 visible_alias = "PA"
725 )]
726 pub patched_archive_code_page: Option<u32>,
728 #[arg(long)]
729 pub patched_format: Option<FormatType>,
731 #[arg(long)]
732 pub patched_fixed_length: Option<usize>,
734 #[arg(long, action = ArgAction::SetTrue)]
735 pub patched_keep_original: bool,
737 #[arg(long, action = ArgAction::SetTrue)]
738 pub patched_break_words: bool,
740 #[arg(long, action = ArgAction::SetTrue)]
741 pub patched_insert_fullwidth_space_at_line_start: bool,
743 #[arg(long, action = ArgAction::SetTrue)]
744 pub patched_break_with_sentence: bool,
746 #[cfg(feature = "jieba")]
747 #[arg(long, action = ArgAction::SetTrue)]
748 pub patched_no_break_chinese_words: bool,
750 #[arg(long, action = ArgAction::SetTrue)]
751 pub patched_no_remove_space_at_line_start: bool,
753 #[arg(long)]
754 pub name_csv: Option<String>,
756 #[arg(long)]
757 pub replacement_json: Option<String>,
759 #[arg(long, action = ArgAction::SetTrue)]
760 pub warn_when_output_file_not_found: bool,
761 #[arg(long)]
762 pub dep_file: Option<String>,
764 #[arg(short = 'j', long, default_value_t = 1)]
765 pub jobs: usize,
767}
768
769#[derive(Subcommand, Debug, Clone)]
770pub enum Command {
772 Export {
774 input: String,
776 output: Option<String>,
778 },
779 Import(ImportArgs),
781 Pack {
783 input: String,
785 output: Option<String>,
787 #[arg(long)]
788 backslash: bool,
790 },
791 Unpack {
793 input: String,
795 output: Option<String>,
797 },
798 Create {
800 input: String,
802 output: Option<String>,
804 },
805 PackV2 {
807 #[arg(short = 'o', long)]
808 output: Option<String>,
810 input: Vec<String>,
812 #[arg(long)]
813 backslash: bool,
815 #[arg(long)]
816 no_dir: bool,
818 #[arg(long)]
819 dep_file: Option<String>,
821 },
822 Convert {
824 input_type: OutputScriptType,
826 output_type: OutputScriptType,
828 input: String,
830 output: Option<String>,
832 },
833}
834
835pub fn parse_args() -> Arg {
836 Arg::parse()
837}
838
839#[cfg(feature = "ex-hibit")]
840pub fn load_ex_hibit_rld_xor_key(arg: &Arg) -> anyhow::Result<Option<u32>> {
841 if let Some(key) = &arg.ex_hibit_rld_xor_key {
842 if key.starts_with("0x") {
843 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
844 } else {
845 return Ok(Some(u32::from_str_radix(key, 16)?));
846 }
847 }
848 if let Some(file) = &arg.ex_hibit_rld_xor_key_file {
849 let key = std::fs::read_to_string(file)?.trim().to_string();
850 if key.starts_with("0x") {
851 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
852 } else {
853 return Ok(Some(u32::from_str_radix(&key, 16)?));
854 }
855 }
856 Ok(None)
857}
858
859#[cfg(feature = "ex-hibit")]
860pub fn load_ex_hibit_rld_def_xor_key(arg: &crate::args::Arg) -> anyhow::Result<Option<u32>> {
861 if let Some(key) = &arg.ex_hibit_rld_def_xor_key {
862 if key.starts_with("0x") {
863 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
864 } else {
865 return Ok(Some(u32::from_str_radix(key, 16)?));
866 }
867 }
868 if let Some(file) = &arg.ex_hibit_rld_def_xor_key_file {
869 let key = std::fs::read_to_string(file)?.trim().to_string();
870 if key.starts_with("0x") {
871 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
872 } else {
873 return Ok(Some(u32::from_str_radix(&key, 16)?));
874 }
875 }
876 Ok(None)
877}
878
879#[cfg(feature = "cat-system-arc")]
880pub fn get_cat_system_int_encrypt_password(arg: &Arg) -> anyhow::Result<Option<String>> {
881 if let Some(exe) = &arg.cat_system_int_exe {
882 return Ok(Some(
883 crate::scripts::cat_system::archive::int::get_password_from_exe(exe)?,
884 ));
885 }
886 if let Some(password) = &arg.cat_system_int_encrypt_password {
887 return Ok(Some(password.clone()));
888 }
889 Ok(None)
890}
891
892#[cfg(feature = "artemis-panmimisoft")]
893pub fn get_artemis_panmimisoft_txt_blacklist_names(
894 arg: &Arg,
895) -> anyhow::Result<std::collections::HashSet<String>> {
896 match &arg.artemis_panmimisoft_txt_tag_ini {
897 Some(path) => {
898 let mut set = crate::scripts::artemis::panmimisoft::txt::read_tags_from_ini(path)?;
899 for name in &arg.artemis_panmimisoft_txt_blacklist_names {
900 set.insert(name.clone());
901 }
902 Ok(set)
903 }
904 None => Ok(arg
905 .artemis_panmimisoft_txt_blacklist_names
906 .iter()
907 .cloned()
908 .collect()),
909 }
910}
911
912#[cfg(feature = "kirikiri")]
913pub fn load_kirikiri_chat_json(
914 arg: &Arg,
915) -> anyhow::Result<
916 Option<
917 std::sync::Arc<
918 std::collections::HashMap<String, std::collections::HashMap<String, (String, usize)>>,
919 >,
920 >,
921> {
922 if let Some(path) = &arg.kirikiri_chat_json {
923 return Ok(Some(std::sync::Arc::new(
924 crate::scripts::kirikiri::read_kirikiri_comu_json(path)?
925 .into_iter()
926 .map(|(k, v)| {
927 let v: std::collections::HashMap<_, _> =
928 v.into_iter().map(|(k, v)| (k, (v, 1))).collect();
929 (k, v)
930 })
931 .collect(),
932 )));
933 }
934 if let Some(dir) = &arg.kirikiri_chat_dir {
935 let mut outt = arg.output_type.unwrap_or(OutputScriptType::M3t);
936 if !matches!(
937 outt,
938 OutputScriptType::M3t
939 | OutputScriptType::M3ta
940 | OutputScriptType::M3tTxt
941 | OutputScriptType::Po
942 | OutputScriptType::Pot
943 ) {
944 outt = OutputScriptType::M3t;
945 }
946 let files = crate::utils::files::find_ext_files(dir, arg.recursive, &[outt.as_ref()])?;
947 if !files.is_empty() {
948 let mut map = std::collections::HashMap::new();
949 let mut global: std::collections::HashMap<
950 String,
951 (String, std::collections::HashSet<String>),
952 > = std::collections::HashMap::new();
953 for file in files {
954 let f = crate::utils::files::read_file(&file)?;
955 let data = crate::utils::encoding::decode_to_string(
956 crate::get_output_encoding(arg),
957 &f,
958 true,
959 )?;
960 let m3t = if outt.is_m3t() {
961 crate::output_scripts::m3t::M3tParser::new(
962 &data,
963 arg.llm_trans_mark.as_ref().map(|s| s.as_str()),
964 arg.m3t_use_original_text,
965 )
966 .parse_as_vec()?
967 } else {
968 crate::output_scripts::po::PoParser::new(
969 &data,
970 arg.llm_trans_mark.as_ref().map(|s| s.as_str()),
971 )
972 .parse_as_vec()?
973 };
974 let current_key = std::path::Path::new(&file)
975 .file_stem()
976 .and_then(|s| s.to_str())
977 .unwrap_or("unknown")
978 .to_string();
979 let mut entry: std::collections::HashMap<
980 String,
981 (String, std::collections::HashSet<String>),
982 > = std::collections::HashMap::new();
983 for (k, v) in m3t {
984 if v.is_empty() {
985 continue;
986 }
987 let k = k.replace("\\[", "[");
988 let v = v.replace("\\[", "[");
989 if let Some((_, count)) = entry.get_mut(&k) {
990 count.insert(v.clone());
991 } else {
992 entry.insert(
993 k.clone(),
994 (v.clone(), std::collections::HashSet::from_iter([v.clone()])),
995 );
996 }
997 if let Some((_, count)) = global.get_mut(&k) {
998 count.insert(v.clone());
999 } else {
1000 global.insert(
1001 k,
1002 (v.clone(), std::collections::HashSet::from_iter([v.clone()])),
1003 );
1004 }
1005 }
1006 map.insert(current_key, entry);
1007 }
1008 map.insert("global".to_string(), global);
1009 return Ok(Some(std::sync::Arc::new(
1010 map.into_iter()
1011 .map(|(k, v)| {
1012 let v: std::collections::HashMap<_, _> =
1013 v.into_iter().map(|(k, (v, s))| (k, (v, s.len()))).collect();
1014 (k, v)
1015 })
1016 .collect(),
1017 )));
1018 }
1019 }
1020 Ok(None)
1021}