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)]
110#[command(
111 version,
112 about,
113 long_about = "Tools for export and import scripts\nhttps://github.com/lifegpc/msg-tool"
114)]
115pub struct Arg {
116 #[arg(short = 't', long, value_enum, global = true)]
117 pub script_type: Option<ScriptType>,
119 #[arg(short = 'T', long, value_enum, global = true)]
120 pub output_type: Option<OutputScriptType>,
122 #[arg(short = 'n', long, global = true)]
123 pub output_no_extra_ext: bool,
125 #[cfg(feature = "image")]
126 #[arg(short = 'i', long, value_enum, global = true)]
127 pub image_type: Option<ImageOutputType>,
129 #[arg(short = 'e', long, value_enum, global = true, group = "encodingg")]
130 pub encoding: Option<TextEncoding>,
132 #[cfg(windows)]
133 #[arg(short = 'c', long, value_enum, global = true, group = "encodingg")]
134 pub code_page: Option<u32>,
136 #[arg(
137 short = 'E',
138 long,
139 value_enum,
140 global = true,
141 group = "output_encodingg"
142 )]
143 pub output_encoding: Option<TextEncoding>,
145 #[cfg(windows)]
146 #[arg(
147 short = 'C',
148 long,
149 value_enum,
150 global = true,
151 group = "output_encodingg"
152 )]
153 pub output_code_page: Option<u32>,
155 #[arg(
156 short = 'a',
157 long,
158 value_enum,
159 global = true,
160 group = "archive_encodingg"
161 )]
162 pub archive_encoding: Option<TextEncoding>,
164 #[cfg(windows)]
165 #[arg(
166 short = 'A',
167 long,
168 value_enum,
169 global = true,
170 group = "archive_encodingg"
171 )]
172 pub archive_code_page: Option<u32>,
174 #[cfg(feature = "circus")]
175 #[arg(long, value_enum, global = true)]
176 pub circus_mes_type: Option<CircusMesType>,
178 #[arg(short, long, action = ArgAction::SetTrue, global = true)]
179 pub recursive: bool,
181 #[arg(global = true, action = ArgAction::SetTrue, short, long)]
182 pub backtrace: bool,
184 #[cfg(feature = "escude-arc")]
185 #[arg(long, action = ArgAction::SetTrue, global = true)]
186 pub escude_fake_compress: bool,
188 #[cfg(feature = "escude")]
189 #[arg(long, global = true)]
190 pub escude_enum_scr: Option<String>,
192 #[cfg(feature = "bgi")]
193 #[arg(long, action = ArgAction::SetTrue, global = true)]
194 pub bgi_import_duplicate: bool,
197 #[cfg(feature = "bgi")]
198 #[arg(long, action = ArgAction::SetTrue, global = true, visible_alias = "bgi-no-append")]
199 pub bgi_disable_append: bool,
202 #[cfg(all(feature = "bgi-arc", feature = "bgi-img"))]
203 #[arg(long, global = true)]
204 pub bgi_is_sysgrp_arc: Option<bool>,
206 #[cfg(feature = "bgi-img")]
207 #[arg(long, global = true)]
208 pub bgi_img_scramble: Option<bool>,
211 #[cfg(feature = "bgi-img")]
212 #[arg(long, global = true, default_value_t = crate::types::get_default_threads())]
213 pub bgi_img_workers: usize,
216 #[cfg(feature = "cat-system-arc")]
217 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
218 pub cat_system_int_encrypt_password: Option<String>,
220 #[cfg(feature = "cat-system-arc")]
221 #[arg(long, global = true, group = "cat_system_int_encrypt_passwordg")]
222 pub cat_system_int_exe: Option<String>,
224 #[cfg(feature = "cat-system-img")]
225 #[arg(long, global = true, action = ArgAction::SetTrue)]
226 pub cat_system_image_canvas: bool,
228 #[cfg(feature = "kirikiri")]
229 #[arg(long, global = true)]
230 pub kirikiri_language_index: Option<usize>,
232 #[cfg(feature = "kirikiri")]
233 #[arg(long, global = true)]
234 pub kirikiri_export_chat: bool,
237 #[cfg(feature = "kirikiri")]
238 #[arg(long, global = true, value_delimiter = ',')]
239 pub kirikiri_chat_key: Option<Vec<String>>,
242 #[cfg(feature = "kirikiri")]
243 #[arg(long, global = true, group = "kirikiri_chat_jsong")]
244 pub kirikiri_chat_json: Option<String>,
246 #[cfg(feature = "kirikiri")]
247 #[arg(long, global = true, group = "kirikiri_chat_jsong")]
248 pub kirikiri_chat_dir: Option<String>,
250 #[cfg(feature = "kirikiri")]
251 #[arg(long, global = true, value_delimiter = ',')]
252 pub kirikiri_languages: Option<Vec<String>>,
254 #[cfg(feature = "kirikiri")]
255 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-title")]
256 pub kirikiri_title: bool,
258 #[cfg(feature = "kirikiri")]
259 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-chat-no-multilang")]
260 pub kirikiri_chat_no_multilang: bool,
262 #[cfg(feature = "kirikiri")]
263 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "kr-no-empty-lines", visible_alias = "kirikiri-no-empty-lines")]
264 pub kirikiri_remove_empty_lines: bool,
266 #[cfg(feature = "kirikiri")]
267 #[arg(
268 long,
269 global = true,
270 value_delimiter = ',',
271 default_value = "nm,set_title,speaker,Talk,talk,cn,name,名前"
272 )]
273 pub kirikiri_name_commands: Vec<String>,
275 #[cfg(feature = "kirikiri")]
276 #[arg(
277 long,
278 global = true,
279 value_delimiter = ',',
280 default_value = "sel01,sel02,sel03,sel04,AddSelect,ruby,exlink,e_xlink"
281 )]
282 pub kirikiri_message_commands: Vec<String>,
284 #[cfg(feature = "image")]
285 #[arg(short = 'f', long, global = true)]
286 pub image_output_flat: bool,
288 #[cfg(feature = "bgi-arc")]
289 #[arg(long, global = true, action = ArgAction::SetTrue)]
290 pub bgi_compress_file: bool,
292 #[cfg(feature = "bgi-arc")]
293 #[arg(long, global = true, default_value_t = 3, value_parser = crate::scripts::bgi::archive::dsc::parse_min_length)]
294 pub bgi_compress_min_len: usize,
296 #[cfg(feature = "emote-img")]
297 #[arg(long, global = true)]
298 pub emote_pimg_overlay: Option<bool>,
300 #[cfg(feature = "artemis-arc")]
301 #[arg(long, global = true)]
302 pub artemis_arc_disable_xor: bool,
304 #[cfg(feature = "artemis")]
305 #[arg(long, global = true, group = "artemis_indentg")]
306 pub artemis_indent: Option<usize>,
309 #[cfg(feature = "artemis")]
310 #[arg(long, global = true, action = ArgAction::SetTrue, group = "artemis_indentg")]
311 pub artemis_no_indent: bool,
313 #[cfg(feature = "artemis")]
314 #[arg(long, global = true, default_value_t = 100)]
315 pub artemis_max_line_width: usize,
317 #[cfg(feature = "artemis")]
318 #[arg(long, global = true)]
319 pub artemis_ast_lang: Option<String>,
322 #[cfg(feature = "artemis")]
323 #[arg(long, global = true, action = ArgAction::SetTrue)]
324 pub artemis_asb_no_format_lua: bool,
326 #[cfg(feature = "artemis-panmimisoft")]
328 #[arg(
329 long,
330 global = true,
331 value_delimiter = ',',
332 default_value = "背景,イベントCG,遅延背景,遅延背景予約,背景予約,遅延イベントCG,遅延イベントCG予約,イベントCG予約,遅延ポップアップ,遅延bgm_in,遅延bgm_out,遅延se_in,遅延se_out,遅延bgs_in,遅延bgs_out,立ち絵face非連動,セーブサムネイル置換終了,シネスコ,ポップアップ"
333 )]
334 pub artemis_panmimisoft_txt_blacklist_names: Vec<String>,
337 #[cfg(feature = "artemis-panmimisoft")]
338 #[arg(long, global = true)]
339 pub artemis_panmimisoft_txt_lang: Option<String>,
342 #[cfg(feature = "artemis-panmimisoft")]
343 #[arg(long, global = true)]
344 pub artemis_panmimisoft_txt_tag_ini: Option<String>,
346 #[cfg(feature = "cat-system")]
347 #[arg(long, global = true)]
348 pub cat_system_cstl_lang: Option<String>,
351 #[cfg(feature = "flate2")]
352 #[arg(short = 'z', long, global = true, value_name = "LEVEL", value_parser = parse_compression_level, default_value_t = 6)]
353 pub zlib_compression_level: u32,
355 #[cfg(feature = "image")]
356 #[arg(short = 'g', long, global = true, value_enum, default_value_t = PngCompressionLevel::Fast)]
357 pub png_compression_level: PngCompressionLevel,
359 #[cfg(feature = "circus-img")]
360 #[arg(long, global = true, action = ArgAction::SetTrue)]
361 pub circus_crx_keep_original_bpp: bool,
363 #[cfg(feature = "circus-img")]
364 #[arg(long, global = true, action = ArgAction::SetTrue)]
365 pub circus_crx_zstd: bool,
367 #[cfg(feature = "zstd")]
368 #[arg(short = 'Z', long, global = true, value_name = "LEVEL", value_parser = parse_zstd_compression_level, default_value_t = 3)]
369 pub zstd_compression_level: i32,
371 #[cfg(feature = "circus-img")]
372 #[arg(long, global = true, value_enum, default_value_t = crate::scripts::circus::image::crx::CircusCrxMode::Auto)]
373 pub circus_crx_mode: crate::scripts::circus::image::crx::CircusCrxMode,
375 #[cfg(feature = "circus-img")]
376 #[arg(long, global = true, action = ArgAction::SetTrue)]
377 pub circus_crx_canvas: bool,
379 #[arg(short = 'F', long, global = true, action = ArgAction::SetTrue)]
380 pub force_script: bool,
382 #[cfg(feature = "ex-hibit")]
383 #[arg(
384 long,
385 global = true,
386 value_name = "HEX",
387 group = "ex_hibit_rld_xor_keyg"
388 )]
389 pub ex_hibit_rld_xor_key: Option<String>,
392 #[cfg(feature = "ex-hibit")]
393 #[arg(
394 long,
395 global = true,
396 value_name = "PATH",
397 group = "ex_hibit_rld_xor_keyg"
398 )]
399 pub ex_hibit_rld_xor_key_file: Option<String>,
401 #[cfg(feature = "ex-hibit")]
402 #[arg(
403 long,
404 global = true,
405 value_name = "HEX",
406 group = "ex_hibit_rld_def_xor_keyg"
407 )]
408 pub ex_hibit_rld_def_xor_key: Option<String>,
410 #[cfg(feature = "ex-hibit")]
411 #[arg(
412 long,
413 global = true,
414 value_name = "PATH",
415 group = "ex_hibit_rld_def_xor_keyg"
416 )]
417 pub ex_hibit_rld_def_xor_key_file: Option<String>,
419 #[cfg(feature = "ex-hibit")]
420 #[arg(long, global = true, value_name = "PATH")]
421 pub ex_hibit_rld_keys: Option<String>,
424 #[cfg(feature = "ex-hibit")]
425 #[arg(long, global = true, value_name = "PATH")]
426 pub ex_hibit_rld_def_keys: Option<String>,
428 #[cfg(feature = "mozjpeg")]
429 #[arg(long, global = true, default_value_t = 80, value_parser = parse_jpeg_quality)]
430 pub jpeg_quality: u8,
432 #[cfg(feature = "webp")]
433 #[arg(short = 'w', long, global = true, group = "webp_qualityg")]
434 pub webp_lossless: bool,
436 #[cfg(feature = "webp")]
437 #[arg(short = 'W', long, global = true, value_name = "QUALITY", group = "webp_qualityg", value_parser = parse_webp_quality, default_value_t = 80)]
438 pub webp_quality: u8,
440 #[arg(long, global = true)]
441 pub custom_yaml: Option<bool>,
444 #[cfg(feature = "entis-gls")]
445 #[arg(long, global = true)]
446 pub entis_gls_srcxml_lang: Option<String>,
449 #[cfg(feature = "will-plus")]
450 #[arg(long, global = true)]
451 pub will_plus_ws2_no_disasm: bool,
455 #[cfg(feature = "lossless-audio")]
456 #[arg(short = 'l', long, global = true, value_enum, default_value_t = LosslessAudioFormat::Wav)]
457 pub lossless_audio_fmt: LosslessAudioFormat,
459 #[cfg(feature = "audio-flac")]
460 #[arg(short = 'L', long, global = true, default_value_t = 5, value_parser = parse_flac_compression_level)]
461 pub flac_compression_level: u32,
463 #[arg(long, global = true)]
464 pub llm_trans_mark: Option<String>,
467 #[cfg(feature = "favorite")]
468 #[arg(long, global = true, action = ArgAction::SetTrue)]
469 pub favorite_hcb_no_filter_ascii: bool,
471 #[cfg(feature = "image-jxl")]
472 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "jxl-no-lossless")]
473 pub jxl_lossy: bool,
475 #[cfg(feature = "image-jxl")]
476 #[arg(long, global = true, default_value_t = 1.0, value_parser = parse_jxl_distance)]
477 pub jxl_distance: f32,
480 #[cfg(feature = "image-jxl")]
481 #[arg(long, global = true, default_value_t = 1, visible_alias = "jxl-jobs")]
482 pub jxl_workers: usize,
485 #[cfg(feature = "image")]
486 #[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")]
487 pub image_workers: usize,
490 #[cfg(feature = "jieba")]
491 #[arg(long, global = true)]
492 pub jieba_dict: Option<String>,
494 #[cfg(feature = "emote-img")]
495 #[arg(long, global = true, action = ArgAction::SetTrue, visible_alias = "psb-no-tlg")]
496 pub psb_no_process_tlg: bool,
498 #[cfg(feature = "softpal-img")]
499 #[arg(long, global = true, visible_alias = "pgd-co")]
500 pub pgd_compress: bool,
503 #[arg(long, global = true)]
504 pub no_multi_message: bool,
506 #[cfg(feature = "softpal")]
507 #[arg(long, global = true, visible_alias = "softpal-idx")]
508 pub softpal_add_message_index: bool,
510 #[cfg(feature = "kirikiri-arc")]
511 #[arg(long, global = true)]
512 pub xp3_no_simple_crypt: bool,
514 #[cfg(feature = "kirikiri-arc")]
515 #[arg(long, global = true)]
516 pub xp3_no_mdf_decompress: bool,
518 #[cfg(feature = "kirikiri-arc")]
519 #[arg(long, global = true, default_value = "cdc:32KiB:256KiB:8MiB", value_parser = crate::scripts::kirikiri::archive::xp3::parse_segmenter_config)]
520 pub xp3_segmenter: crate::scripts::kirikiri::archive::xp3::SegmenterConfig,
525 #[cfg(feature = "kirikiri-arc")]
526 #[arg(long, global = true)]
527 pub xp3_no_compress_files: bool,
529 #[cfg(feature = "kirikiri-arc")]
530 #[arg(long, global = true)]
531 pub xp3_no_compress_index: bool,
533 #[cfg(feature = "kirikiri-arc")]
534 #[arg(long, global = true, default_value_t = num_cpus::get(), visible_alias = "xp3-compress-jobs")]
535 pub xp3_compress_workers: usize,
537 #[cfg(feature = "kirikiri-arc")]
538 #[arg(long, global = true)]
539 pub xp3_zstd: bool,
541 #[cfg(feature = "kirikiri-arc")]
542 #[arg(
543 long,
544 global = true,
545 default_value_t = 1,
546 visible_alias = "xp3-pack-jobs"
547 )]
548 pub xp3_pack_workers: usize,
551 #[cfg(feature = "kirikiri")]
552 #[arg(long, global = true)]
553 pub kirikiri_language_insert: bool,
555 #[cfg(feature = "musica-arc")]
556 #[arg(long, global = true, value_parser = get_musica_game_title_value_parser())]
557 pub musica_game_title: Option<String>,
559 #[cfg(feature = "musica-arc")]
560 #[arg(long, global = true)]
561 pub musica_xor_key: Option<u8>,
563 #[cfg(feature = "musica-arc")]
564 #[arg(long, global = true)]
565 pub musica_compress: bool,
567 #[arg(short = 'x', long, default_value_t = 0, global = true)]
568 pub exit_code: i32,
570 #[arg(short = 'X', long, global = true)]
571 pub exit_code_all_failed: Option<i32>,
573 #[arg(long, global = true)]
574 pub m3t_no_quote: bool,
576 #[cfg(feature = "kirikiri-arc")]
577 #[arg(long, global = true)]
578 pub xp3_no_adler: bool,
581 #[cfg(feature = "bgi")]
582 #[arg(long, global = true)]
583 pub bgi_add_space: bool,
586 #[command(subcommand)]
587 pub command: Command,
589}
590
591#[derive(Parser, Debug, Clone)]
592#[clap(group = ArgGroup::new("patched_encodingg").multiple(false), group = ArgGroup::new("patched_archive_encodingg").multiple(false))]
593pub struct ImportArgs {
594 pub input: String,
596 pub output: String,
598 pub patched: String,
600 #[arg(short = 'p', long, group = "patched_encodingg")]
601 pub patched_encoding: Option<TextEncoding>,
603 #[cfg(windows)]
604 #[arg(short = 'P', long, group = "patched_encodingg")]
605 pub patched_code_page: Option<u32>,
607 #[arg(
608 long,
609 value_enum,
610 group = "patched_archive_encodingg",
611 visible_alias = "pa"
612 )]
613 pub patched_archive_encoding: Option<TextEncoding>,
615 #[cfg(windows)]
616 #[arg(
617 long,
618 value_enum,
619 group = "patched_archive_encodingg",
620 visible_alias = "PA"
621 )]
622 pub patched_archive_code_page: Option<u32>,
624 #[arg(long)]
625 pub patched_format: Option<FormatType>,
627 #[arg(long)]
628 pub patched_fixed_length: Option<usize>,
630 #[arg(long, action = ArgAction::SetTrue)]
631 pub patched_keep_original: bool,
633 #[arg(long, action = ArgAction::SetTrue)]
634 pub patched_break_words: bool,
636 #[arg(long, action = ArgAction::SetTrue)]
637 pub patched_insert_fullwidth_space_at_line_start: bool,
639 #[arg(long, action = ArgAction::SetTrue)]
640 pub patched_break_with_sentence: bool,
642 #[cfg(feature = "jieba")]
643 #[arg(long, action = ArgAction::SetTrue)]
644 pub patched_no_break_chinese_words: bool,
646 #[arg(long)]
647 pub name_csv: Option<String>,
649 #[arg(long)]
650 pub replacement_json: Option<String>,
652 #[arg(long, action = ArgAction::SetTrue)]
653 pub warn_when_output_file_not_found: bool,
654 #[arg(long)]
655 pub dep_file: Option<String>,
657 #[arg(short = 'j', long, default_value_t = 1)]
658 pub jobs: usize,
660}
661
662#[derive(Subcommand, Debug, Clone)]
663pub enum Command {
665 Export {
667 input: String,
669 output: Option<String>,
671 },
672 Import(ImportArgs),
674 Pack {
676 input: String,
678 output: Option<String>,
680 #[arg(long)]
681 backslash: bool,
683 },
684 Unpack {
686 input: String,
688 output: Option<String>,
690 },
691 Create {
693 input: String,
695 output: Option<String>,
697 },
698 PackV2 {
700 #[arg(short = 'o', long)]
701 output: Option<String>,
703 input: Vec<String>,
705 #[arg(long)]
706 backslash: bool,
708 #[arg(long)]
709 no_dir: bool,
711 #[arg(long)]
712 dep_file: Option<String>,
714 },
715}
716
717pub fn parse_args() -> Arg {
718 Arg::parse()
719}
720
721#[cfg(feature = "ex-hibit")]
722pub fn load_ex_hibit_rld_xor_key(arg: &Arg) -> anyhow::Result<Option<u32>> {
723 if let Some(key) = &arg.ex_hibit_rld_xor_key {
724 if key.starts_with("0x") {
725 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
726 } else {
727 return Ok(Some(u32::from_str_radix(key, 16)?));
728 }
729 }
730 if let Some(file) = &arg.ex_hibit_rld_xor_key_file {
731 let key = std::fs::read_to_string(file)?.trim().to_string();
732 if key.starts_with("0x") {
733 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
734 } else {
735 return Ok(Some(u32::from_str_radix(&key, 16)?));
736 }
737 }
738 Ok(None)
739}
740
741#[cfg(feature = "ex-hibit")]
742pub fn load_ex_hibit_rld_def_xor_key(arg: &crate::args::Arg) -> anyhow::Result<Option<u32>> {
743 if let Some(key) = &arg.ex_hibit_rld_def_xor_key {
744 if key.starts_with("0x") {
745 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
746 } else {
747 return Ok(Some(u32::from_str_radix(key, 16)?));
748 }
749 }
750 if let Some(file) = &arg.ex_hibit_rld_def_xor_key_file {
751 let key = std::fs::read_to_string(file)?.trim().to_string();
752 if key.starts_with("0x") {
753 return Ok(Some(u32::from_str_radix(&key[2..], 16)?));
754 } else {
755 return Ok(Some(u32::from_str_radix(&key, 16)?));
756 }
757 }
758 Ok(None)
759}
760
761#[cfg(feature = "cat-system-arc")]
762pub fn get_cat_system_int_encrypt_password(arg: &Arg) -> anyhow::Result<Option<String>> {
763 if let Some(exe) = &arg.cat_system_int_exe {
764 return Ok(Some(
765 crate::scripts::cat_system::archive::int::get_password_from_exe(exe)?,
766 ));
767 }
768 if let Some(password) = &arg.cat_system_int_encrypt_password {
769 return Ok(Some(password.clone()));
770 }
771 Ok(None)
772}
773
774#[cfg(feature = "artemis-panmimisoft")]
775pub fn get_artemis_panmimisoft_txt_blacklist_names(
776 arg: &Arg,
777) -> anyhow::Result<std::collections::HashSet<String>> {
778 match &arg.artemis_panmimisoft_txt_tag_ini {
779 Some(path) => {
780 let mut set = crate::scripts::artemis::panmimisoft::txt::read_tags_from_ini(path)?;
781 for name in &arg.artemis_panmimisoft_txt_blacklist_names {
782 set.insert(name.clone());
783 }
784 Ok(set)
785 }
786 None => Ok(arg
787 .artemis_panmimisoft_txt_blacklist_names
788 .iter()
789 .cloned()
790 .collect()),
791 }
792}
793
794#[cfg(feature = "kirikiri")]
795pub fn load_kirikiri_chat_json(
796 arg: &Arg,
797) -> anyhow::Result<
798 Option<
799 std::sync::Arc<
800 std::collections::HashMap<String, std::collections::HashMap<String, (String, usize)>>,
801 >,
802 >,
803> {
804 if let Some(path) = &arg.kirikiri_chat_json {
805 return Ok(Some(std::sync::Arc::new(
806 crate::scripts::kirikiri::read_kirikiri_comu_json(path)?
807 .into_iter()
808 .map(|(k, v)| {
809 let v: std::collections::HashMap<_, _> =
810 v.into_iter().map(|(k, v)| (k, (v, 1))).collect();
811 (k, v)
812 })
813 .collect(),
814 )));
815 }
816 if let Some(dir) = &arg.kirikiri_chat_dir {
817 let mut outt = arg.output_type.unwrap_or(OutputScriptType::M3t);
818 if !matches!(
819 outt,
820 OutputScriptType::M3t
821 | OutputScriptType::M3ta
822 | OutputScriptType::M3tTxt
823 | OutputScriptType::Po
824 | OutputScriptType::Pot
825 ) {
826 outt = OutputScriptType::M3t;
827 }
828 let files = crate::utils::files::find_ext_files(dir, arg.recursive, &[outt.as_ref()])?;
829 if !files.is_empty() {
830 let mut map = std::collections::HashMap::new();
831 let mut global: std::collections::HashMap<
832 String,
833 (String, std::collections::HashSet<String>),
834 > = std::collections::HashMap::new();
835 for file in files {
836 let f = crate::utils::files::read_file(&file)?;
837 let data = crate::utils::encoding::decode_to_string(
838 crate::get_output_encoding(arg),
839 &f,
840 true,
841 )?;
842 let m3t = if outt.is_m3t() {
843 crate::output_scripts::m3t::M3tParser::new(
844 &data,
845 arg.llm_trans_mark.as_ref().map(|s| s.as_str()),
846 )
847 .parse_as_vec()?
848 } else {
849 crate::output_scripts::po::PoParser::new(
850 &data,
851 arg.llm_trans_mark.as_ref().map(|s| s.as_str()),
852 )
853 .parse_as_vec()?
854 };
855 let current_key = std::path::Path::new(&file)
856 .file_stem()
857 .and_then(|s| s.to_str())
858 .unwrap_or("unknown")
859 .to_string();
860 let mut entry: std::collections::HashMap<
861 String,
862 (String, std::collections::HashSet<String>),
863 > = std::collections::HashMap::new();
864 for (k, v) in m3t {
865 if v.is_empty() {
866 continue;
867 }
868 let k = k.replace("\\[", "[");
869 let v = v.replace("\\[", "[");
870 if let Some((_, count)) = entry.get_mut(&k) {
871 count.insert(v.clone());
872 } else {
873 entry.insert(
874 k.clone(),
875 (v.clone(), std::collections::HashSet::from_iter([v.clone()])),
876 );
877 }
878 if let Some((_, count)) = global.get_mut(&k) {
879 count.insert(v.clone());
880 } else {
881 global.insert(
882 k,
883 (v.clone(), std::collections::HashSet::from_iter([v.clone()])),
884 );
885 }
886 }
887 map.insert(current_key, entry);
888 }
889 map.insert("global".to_string(), global);
890 return Ok(Some(std::sync::Arc::new(
891 map.into_iter()
892 .map(|(k, v)| {
893 let v: std::collections::HashMap<_, _> =
894 v.into_iter().map(|(k, (v, s))| (k, (v, s.len()))).collect();
895 (k, v)
896 })
897 .collect(),
898 )));
899 }
900 }
901 Ok(None)
902}