1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
//! Basic types
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
#[serde(untagged, rename_all = "camelCase")]
/// Text Encoding
pub enum Encoding {
/// Automatically detect encoding
Auto,
/// UTF-8 encoding
Utf8,
/// Shift-JIS encoding
Cp932,
/// GB2312 encoding
Gb2312,
/// UTF-16 Little Endian encoding
Utf16LE,
/// UTF-16 Big Endian encoding
Utf16BE,
/// Code page encoding (Windows only)
#[cfg(windows)]
CodePage(u32),
}
impl Default for Encoding {
fn default() -> Self {
Encoding::Utf8
}
}
impl Encoding {
/// Returns true if the encoding is Shift-JIS (CP932).
pub fn is_jis(&self) -> bool {
match self {
Self::Cp932 => true,
#[cfg(windows)]
Self::CodePage(code_page) => *code_page == 932,
_ => false,
}
}
/// Returns true if the encoding is UTF-16LE.
pub fn is_utf16le(&self) -> bool {
match self {
Self::Utf16LE => true,
#[cfg(windows)]
Self::CodePage(code_page) => *code_page == 1200,
_ => false,
}
}
/// Returns true if the encoding is UTF-16BE.
pub fn is_utf16be(&self) -> bool {
match self {
Self::Utf16BE => true,
#[cfg(windows)]
Self::CodePage(code_page) => *code_page == 1201,
_ => false,
}
}
/// Returns true if the encoding is UTF8.
pub fn is_utf8(&self) -> bool {
match self {
Self::Utf8 => true,
#[cfg(windows)]
Self::CodePage(code_page) => *code_page == 65001,
_ => false,
}
}
/// Returns the charset name
pub fn charset(&self) -> Option<&'static str> {
match self {
Self::Auto => None,
Self::Utf8 => Some("UTF-8"),
Self::Cp932 => Some("shift_jis"),
Self::Gb2312 => Some("gbk"),
Self::Utf16LE => Some("utf-16le"),
Self::Utf16BE => Some("utf-16be"),
#[cfg(windows)]
Self::CodePage(code_page) => match *code_page {
932 => Some("shift_jis"),
65001 => Some("utf-8"),
1200 => Some("utf-16le"),
936 => Some("gbk"),
_ => None,
},
}
}
}
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// Text Encoding (for CLI)
pub enum TextEncoding {
/// Use script's default encoding
Default,
/// Automatically detect encoding
Auto,
/// UTF-8 encoding
Utf8,
#[value(alias("jis"))]
/// Shift-JIS encoding
Cp932,
#[value(alias("gbk"))]
/// GB2312 encoding
Gb2312,
}
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// Script type
pub enum OutputScriptType {
/// Text script
M3t,
/// Same as M3t, buf different extension
M3ta,
/// Same as M3t, buf different extension
M3tTxt,
/// JSON which can be used for GalTransl
Json,
/// YAML (same as JSON, but with YAML syntax)
Yaml,
/// Gettext .pot file
Pot,
/// Gettext .po file
Po,
/// Custom output
Custom,
}
impl OutputScriptType {
/// Returns true if the script type is custom.
pub fn is_custom(&self) -> bool {
matches!(self, OutputScriptType::Custom)
}
/// Returns true if the script type is M3t/M3ta/M3tTxt.
pub fn is_m3t(&self) -> bool {
matches!(
self,
OutputScriptType::M3t | OutputScriptType::M3ta | OutputScriptType::M3tTxt
)
}
/// Returns true if the script type supports source messages.
pub fn is_src_supported(&self) -> bool {
matches!(
self,
OutputScriptType::M3t
| OutputScriptType::M3ta
| OutputScriptType::M3tTxt
| OutputScriptType::Po
| OutputScriptType::Pot
)
}
}
impl AsRef<str> for OutputScriptType {
/// Returns the extension for the script type.
fn as_ref(&self) -> &str {
match self {
OutputScriptType::M3t => "m3t",
OutputScriptType::M3ta => "m3ta",
OutputScriptType::M3tTxt => "txt",
OutputScriptType::Json => "json",
OutputScriptType::Yaml => "yaml",
OutputScriptType::Pot => "pot",
OutputScriptType::Po => "po",
OutputScriptType::Custom => "",
}
}
}
#[cfg(feature = "circus")]
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// Circus MES game
pub enum CircusMesType {
/// fortissimo//Akkord:Bsusvier
Ffexa,
/// fortissimo EXS//Akkord:nächsten Phase
Ffexs,
/// Eternal Fantasy
Ef,
/// D.C.〜ダ・カーポ〜 温泉編
Dcos,
/// ことり Love Ex P
Ktlep,
/// D.C.WhiteSeason
Dcws,
/// D.C. Summer Vacation
Dcsv,
/// D.C.P.C.(Vista)
Dcpc,
/// D.C.〜ダ・カーポ〜 MEMORIES DISC
Dcmems,
/// D.C. Dream X’mas
Dcdx,
/// D.C.A.S. 〜ダ・カーポ〜アフターシーズンズ
Dcas,
/// D.C.II 春風のアルティメットバトル!
Dcbs,
/// D.C.II Fall in Love
Dc2fl,
/// D.C.II 春風のアルティメットバトル!
Dc2bs,
/// D.C.II Dearest Marriage
Dc2dm,
/// D.C.II 〜featuring Yun2〜
Dc2fy,
/// D.C.II C.C. 月島小恋のらぶらぶバスルーム
Dc2cckko,
/// D.C.II C.C. 音姫先生のどきどき特別授業
Dc2ccotm,
/// D.C.II Spring Celebration
Dc2sc,
/// D.C.II To You
Dc2ty,
/// D.C.II P.C.
Dc2pc,
/// D.C.III RX-rated
Dc3rx,
/// D.C.III P.P.~ダ・カーポIII プラチナパートナー~
Dc3pp,
/// D.C.III WithYou
Dc3wy,
/// D.C.III DreamDays
Dc3dd,
/// D.C.4 ~ダ・カーポ4~
Dc4,
/// D.C.4 Plus Harmony 〜ダ・カーポ4〜 プラスハーモニー
Dc4ph,
/// D.S. -Dal Segno-
Ds,
/// D.S.i.F. -Dal Segno- in Future
Dsif,
/// てんぷれ!
Tmpl,
/// 百花百狼/Hyakka Hyakurou
Nightshade,
}
#[cfg(feature = "circus")]
impl AsRef<str> for CircusMesType {
/// Returns the name.
fn as_ref(&self) -> &str {
match self {
CircusMesType::Ffexa => "ffexa",
CircusMesType::Ffexs => "ffexs",
CircusMesType::Ef => "ef",
CircusMesType::Dcos => "dcos",
CircusMesType::Ktlep => "ktlep",
CircusMesType::Dcws => "dcws",
CircusMesType::Dcsv => "dcsv",
CircusMesType::Dcpc => "dcpc",
CircusMesType::Dcmems => "dcmems",
CircusMesType::Dcdx => "dcdx",
CircusMesType::Dcas => "dcas",
CircusMesType::Dcbs => "dcbs",
CircusMesType::Dc2fl => "dc2fl",
CircusMesType::Dc2bs => "dc2bs",
CircusMesType::Dc2dm => "dc2dm",
CircusMesType::Dc2fy => "dc2fy",
CircusMesType::Dc2cckko => "dc2cckko",
CircusMesType::Dc2ccotm => "dc2ccotm",
CircusMesType::Dc2sc => "dc2sc",
CircusMesType::Dc2ty => "dc2ty",
CircusMesType::Dc2pc => "dc2pc",
CircusMesType::Dc3rx => "dc3rx",
CircusMesType::Dc3pp => "dc3pp",
CircusMesType::Dc3wy => "dc3wy",
CircusMesType::Dc3dd => "dc3dd",
CircusMesType::Dc4 => "dc4",
CircusMesType::Dc4ph => "dc4ph",
CircusMesType::Ds => "ds",
CircusMesType::Dsif => "dsif",
CircusMesType::Tmpl => "tmpl",
CircusMesType::Nightshade => "nightshade",
}
}
}
/// Extra configuration options for the script.
#[derive(Debug, Clone, msg_tool_macro::Default)]
pub struct ExtraConfig {
#[cfg(feature = "circus")]
/// Circus Game for circus MES script.
pub circus_mes_type: Option<CircusMesType>,
#[cfg(feature = "escude-arc")]
/// Whether to use fake compression for Escude archive
pub escude_fake_compress: bool,
#[cfg(feature = "escude")]
/// The path to the Escude enum script file (enum_scr.bin)
pub escude_enum_scr: Option<String>,
#[cfg(feature = "bgi")]
/// Duplicate same strings when importing into BGI scripts.
/// Enable this will cause BGI scripts to become very large.
pub bgi_import_duplicate: bool,
#[cfg(feature = "bgi")]
/// Disable appending new strings to the end of BGI scripts.
/// Disable may cause BGI scripts broken.
pub bgi_disable_append: bool,
#[cfg(feature = "image")]
/// Output image type
pub image_type: Option<ImageOutputType>,
#[cfg(all(feature = "bgi-arc", feature = "bgi-img"))]
/// Detect all files in BGI archive as SysGrp Images. By default, only files which name is `sysgrp.arc` will enabled this.
pub bgi_is_sysgrp_arc: Option<bool>,
#[cfg(feature = "bgi-img")]
/// Whether to create scrambled SysGrp images. When in import mode, the default value depends on the original image.
/// When in creation mode, it is not enabled by default.
pub bgi_img_scramble: Option<bool>,
#[cfg(feature = "cat-system-arc")]
/// CatSystem2 engine int archive password
pub cat_system_int_encrypt_password: Option<String>,
#[cfg(feature = "cat-system-img")]
/// Draw CatSystem2 image on canvas (if canvas width and height are specified in file)
pub cat_system_image_canvas: bool,
#[cfg(feature = "kirikiri")]
/// Kirikiri language index in script. If not specified, the first language will be used.
pub kirikiri_language_index: Option<usize>,
#[cfg(feature = "kirikiri")]
/// Export chat message to extra json file. (for Kirikiri SCN script.)
/// For example, CIRCUS's comu message. Yuzusoft's phone chat message.
pub kirikiri_export_chat: bool,
#[cfg(feature = "kirikiri")]
/// Kirikiri chat message key. For example, CIRCUS's key is "comumode". Yuzusoft's key is "phonechat".
/// If not specified, "comumode" will be used.
pub kirikiri_chat_key: Option<Vec<String>>,
#[cfg(feature = "kirikiri")]
/// Kirikiri chat message translation. The outter object's key is filename(`global` is a special key).
/// The inner object: key is original text, value is (translated text, original text count).
pub kirikiri_chat_json:
Option<std::sync::Arc<HashMap<String, HashMap<String, (String, usize)>>>>,
#[cfg(feature = "kirikiri")]
/// Kirikiri language list. First language code is code for language index 1.
pub kirikiri_languages: Option<std::sync::Arc<Vec<String>>>,
#[cfg(feature = "kirikiri")]
/// Remove empty lines in Kirikiri KS script.
pub kirikiri_remove_empty_lines: bool,
#[cfg(feature = "kirikiri")]
/// Kirikiri name commands, used to extract names from ks script.
pub kirikiri_name_commands: std::sync::Arc<std::collections::HashSet<String>>,
#[cfg(feature = "kirikiri")]
/// Kirikiri message commands, used to extract more message from ks script.
pub kirikiri_message_commands: std::sync::Arc<std::collections::HashSet<String>>,
#[cfg(feature = "bgi-arc")]
/// Whether to compress files in BGI archive when packing BGI archive.
pub bgi_compress_file: bool,
#[cfg(feature = "bgi-arc")]
#[default(3)]
/// Minimum length of match size for DSC compression. Possible values are 2-256.
pub bgi_compress_min_len: usize,
#[cfg(feature = "emote-img")]
/// Whether to overlay PIMG images. (By default, true if all layers are not group layers.)
pub emote_pimg_overlay: Option<bool>,
#[cfg(feature = "artemis-arc")]
/// Disable Artemis archive (.pfs) XOR encryption when packing.
pub artemis_arc_disable_xor: bool,
#[cfg(feature = "artemis")]
/// Artemis script indent size, used to format Artemis script.
/// Default is 4 spaces.
pub artemis_indent: Option<usize>,
#[cfg(feature = "artemis")]
/// Disable Artemis script indent, used to format Artemis script.
pub artemis_no_indent: bool,
#[cfg(feature = "artemis")]
#[default(100)]
/// Max line width in Artemis script, used to format Artemis script.
pub artemis_max_line_width: usize,
#[cfg(feature = "artemis")]
/// Specify the language of Artemis AST script.
/// If not specified, the first language will be used.
pub artemis_ast_lang: Option<String>,
#[cfg(feature = "cat-system")]
/// CatSystem2 CSTL script language, used to extract messages from CSTL script.
/// If not specified, the first language will be used.
pub cat_system_cstl_lang: Option<String>,
#[cfg(feature = "flate2")]
#[default(6)]
/// Zlib compression level. 0 means no compression, 9 means best compression.
pub zlib_compression_level: u32,
#[cfg(feature = "image")]
/// PNG compression level.
pub png_compression_level: PngCompressionLevel,
#[cfg(feature = "circus-img")]
/// Keep original BPP when importing Circus CRX images.
pub circus_crx_keep_original_bpp: bool,
#[cfg(feature = "circus-img")]
/// Use zstd compression for Circus CRX images. (CIRCUS Engine don't support this. Hook is required.)
pub circus_crx_zstd: bool,
#[cfg(feature = "zstd")]
#[default(3)]
/// Zstd compression level. 0 means default compression level (3), 22 means best compression.
pub zstd_compression_level: i32,
#[cfg(feature = "circus-img")]
/// Circus CRX image row type mode
pub circus_crx_mode: crate::scripts::circus::image::crx::CircusCrxMode,
#[cfg(feature = "ex-hibit")]
/// ExHibit xor key for rld script.
/// Use [ReExHIBIT](https://github.com/ZQF-ReVN/RxExHIBIT) to find the key.
pub ex_hibit_rld_xor_key: Option<u32>,
#[cfg(feature = "ex-hibit")]
/// ExHibit def.rld xor key.
pub ex_hibit_rld_def_xor_key: Option<u32>,
#[cfg(feature = "ex-hibit")]
/// ExHibit rld xor keys.
pub ex_hibit_rld_keys: Option<Box<[u32; 0x100]>>,
#[cfg(feature = "ex-hibit")]
/// ExHibit def.rld xor keys.
pub ex_hibit_rld_def_keys: Option<Box<[u32; 0x100]>>,
#[cfg(feature = "mozjpeg")]
#[default(80)]
/// JPEG quality for output images, 0-100. 100 means best quality.
pub jpeg_quality: u8,
#[cfg(feature = "webp")]
/// Use WebP lossless compression for output images.
pub webp_lossless: bool,
#[cfg(feature = "webp")]
#[default(80)]
/// WebP quality for output images, 0-100. 100 means best quality.
pub webp_quality: u8,
#[cfg(feature = "circus-img")]
/// Draw Circus CRX images on canvas (if canvas width and height are specified in file)
pub circus_crx_canvas: bool,
/// Try use YAML format instead of JSON when custom exporting.
pub custom_yaml: bool,
#[cfg(feature = "entis-gls")]
/// Entis GLS srcxml script language, used to extract messages from srcxml script.
/// If not specified, the first language will be used.
pub entis_gls_srcxml_lang: Option<String>,
#[cfg(feature = "will-plus")]
/// Disable disassembly for WillPlus ws2 script.
/// Use another parser to parse the script.
/// Should only be used when the default parser not works well.
pub will_plus_ws2_no_disasm: bool,
#[cfg(feature = "artemis-panmimisoft")]
/// Artemis Engine blacklist tag names for TXT script.
/// This is used to ignore these tags when finding names in Artemis TXT (ぱんみみそふと) script.
pub artemis_panmimisoft_txt_blacklist_names: std::sync::Arc<std::collections::HashSet<String>>,
#[cfg(feature = "artemis-panmimisoft")]
/// Specify the language of Artemis TXT (ぱんみみそふと) script.
/// If not specified, the first language will be used.
pub artemis_panmimisoft_txt_lang: Option<String>,
#[cfg(feature = "artemis-panmimisoft")]
/// Enable multiple language support for single language Artemis TXT (ぱんみみそふと) script.
/// artemis_panmimisoft_txt_lang must be set when enabling this.
pub artemis_panmimisoft_txt_multi_lang: bool,
#[cfg(feature = "lossless-audio")]
/// Audio format for output lossless audio files.
pub lossless_audio_fmt: LosslessAudioFormat,
#[cfg(feature = "audio-flac")]
#[default(5)]
/// FLAC compression level for output FLAC audio files. 0 means fastest compression, 8 means best compression. Default level is 5.
pub flac_compression_level: u32,
#[cfg(feature = "artemis")]
#[default(true)]
/// Format lua code in Artemis ASB script(.asb/.iet) when exporting.
pub artemis_asb_format_lua: bool,
#[cfg(feature = "kirikiri")]
/// Whether to handle title in Kirikiri SCN script.
pub kirikiri_title: bool,
#[cfg(feature = "favorite")]
#[default(true)]
/// Whether to filter ascii strings in Favorite HCB script.
pub favorite_hcb_filter_ascii: bool,
#[cfg(feature = "bgi-img")]
#[default(get_default_threads())]
/// Workers count for decode BGI compressed images v2 in parallel. Default is half of CPU cores.
/// Set this to 1 to disable parallel decoding. 0 means same as 1.
pub bgi_img_workers: usize,
#[cfg(feature = "image-jxl")]
#[default(true)]
/// Use JXL lossless compression for output images. Enabled by default.
pub jxl_lossless: bool,
#[cfg(feature = "image-jxl")]
#[default(1.0)]
/// JXL distance for output images. 0 means mathematically lossless compression. 1.0 means visually lossless compression.
/// Allowed range is 0.0-25.0. Recommended range is 0.5-3.0. Default value is 1.0.
pub jxl_distance: f32,
#[cfg(feature = "image-jxl")]
#[default(1)]
/// Workers count for encode JXL images in parallel. Default is 1.
/// Set this to 1 to disable parallel encoding. 0 means same as 1
pub jxl_workers: usize,
#[cfg(feature = "emote-img")]
#[default(true)]
/// Process tlg images.
pub psb_process_tlg: bool,
#[cfg(feature = "softpal-img")]
#[default(true)]
/// Whether to use fake compression for Softpal Pgd images. Enabled by default.
/// WARN: Compress may cause image broken.
pub pgd_fake_compress: bool,
#[cfg(feature = "softpal")]
/// Whether to add message index to Softpal src script when exporting.
pub softpal_add_message_index: bool,
#[cfg(feature = "kirikiri")]
#[default(true)]
/// Enable multi-language support for Kirikiri chat messages. Default is true.
/// Note: This requires [Self::kirikiri_language_index] and [Self::kirikiri_languages] to be set correctly.
pub kirikiri_chat_multilang: bool,
#[cfg(feature = "kirikiri-arc")]
#[default(true)]
/// Decrypt SimpleCrypt files in Kirikiri XP3 archive when extracting. Default is true.
pub xp3_simple_crypt: bool,
#[cfg(feature = "kirikiri-arc")]
#[default(true)]
/// Decompress mdf files in Kirikiri XP3 archive when extracting. Default is true.
pub xp3_mdf_decompress: bool,
#[cfg(feature = "kirikiri-arc")]
/// Configuration for Kirikiri XP3 segmenter when creating XP3 archive.
pub xp3_segmenter: crate::scripts::kirikiri::archive::xp3::SegmenterConfig,
#[cfg(feature = "kirikiri-arc")]
#[default(true)]
/// Compress files in Kirikiri XP3 archive when creating. Default is true.
pub xp3_compress_files: bool,
#[cfg(feature = "kirikiri-arc")]
#[default(true)]
/// Compress index in Kirikiri XP3 archive when creating. Default is true.
pub xp3_compress_index: bool,
#[cfg(feature = "kirikiri-arc")]
#[default(num_cpus::get())]
/// Workers count for compress files in Kirikiri XP3 archive when creating in parallel. Default is CPU cores count.
pub xp3_compress_workers: usize,
#[cfg(feature = "kirikiri-arc")]
/// Use zstd compression for files in Kirikiri XP3 archive when creating. (Warning: Kirikiri engine don't support this. Hook is required.)
pub xp3_zstd: bool,
#[cfg(feature = "kirikiri-arc")]
/// Use zopfli compression for files in Kirikiri XP3 archive when creating. This is very slow.
pub xp3_zopfli: bool,
#[cfg(feature = "kirikiri-arc")]
#[default(1)]
/// Workers count for packing file in Kirikiri XP3 archive in parallel. Default is 1.
/// This not works when segment is disabled.
pub xp3_pack_workers: usize,
#[cfg(feature = "kirikiri-arc")]
/// Disable adler32 checksum for Kirikiri XP3 archive when creating.
/// This will keep compatibility with https://github.com/arcusmaximus/KirikiriTools tool.
pub xp3_no_adler: bool,
#[cfg(feature = "kirikiri")]
/// Insert new language at the specified index in Kirikiri SCN script. If index is out of bounds, this flags will be ignored.
pub kirikiri_language_insert: bool,
#[cfg(feature = "musica-arc")]
/// Musica game title for paz archive.
pub musica_game_title: Option<String>,
#[cfg(feature = "musica-arc")]
/// Musica xor key for paz archive.
pub musica_xor_key: Option<u8>,
#[cfg(feature = "musica-arc")]
/// Whether to compress files in Musica paz archive when packing paz archive.
pub musica_compress: bool,
#[cfg(feature = "bgi")]
/// Add an additional space at the end of message in BGI scripts when importing.
/// This may help BGI engine to display the message correctly in save/load screen for some games.
pub bgi_add_space: bool,
#[cfg(feature = "escude")]
/// Escude game title
pub escude_op: Option<crate::scripts::escude::script::EscudeOp>,
#[cfg(feature = "zopfli")]
#[default(std::num::NonZeroU64::new(15).unwrap())]
/// Maximum amount of times to rerun forward and backward pass to optimize LZ77 compression cost.
/// Good values: 10, 15 for small files, 5 for files over several MB in size or it will be too slow.
/// Default is 15.
pub zopfli_iteration_count: std::num::NonZeroU64,
#[cfg(feature = "zopfli")]
#[default(std::num::NonZeroU64::new(u64::MAX).unwrap())]
/// Stop after rerunning forward and backward pass this many times without finding a smaller representation of the block.
/// Default value: practically infinite (maximum u64 value)
pub zopfli_iterations_without_improvement: std::num::NonZeroU64,
#[cfg(feature = "zopfli")]
#[default(15)]
/// Maximum amount of blocks to split into (0 for unlimited, but this can give extreme results that hurt compression on some files).
/// Default value: 15.
pub zopfli_maximum_block_splits: u16,
#[cfg(feature = "entis-gls")]
/// Whether to disassemble Entis GLS csx script when exporting in custom mode.
pub entis_gls_csx_disasm: bool,
#[cfg(feature = "entis-gls")]
#[default(String::from("/"))]
/// The line feed character used in Entis GLS csx script.
pub entis_gls_csx_lf: String,
#[cfg(feature = "entis-gls")]
/// Entis GLS csx script version.
/// If not specified. Will try use lower version first.
pub entis_gls_csx_ver: Option<crate::scripts::entis_gls::csx::CSXScriptVersion>,
#[cfg(feature = "entis-gls")]
/// Entis GLS csx script version2 full version.
/// If not specified. Will try use higher version first.
pub entis_gls_csx_v2_ver: Option<crate::scripts::entis_gls::csx::CSXScriptV2FullVer>,
#[cfg(feature = "entis-gls")]
/// Disable part labels in Entis GLS csx script when exporting.
pub entis_gls_csx_no_part_label: bool,
#[cfg(feature = "qlie-img")]
#[default(true)]
/// Whether to process ABMP10 images in ABMP10 images.
pub qlie_abmp10_process_abmp10: bool,
#[cfg(feature = "qlie-arc")]
/// Path to qlie pack archive key file (pack_keyfile_kfueheish15538fa9or.key)
pub qlie_pack_keyfile: Option<String>,
#[cfg(feature = "qlie-arc")]
/// Whether to compress files in Qlie pack archive.
pub qlie_pack_compress_files: bool,
#[cfg(feature = "qlie-img")]
/// Whether to use PNG file directly for Qlie DPNG images when importing.
/// Enable this will disable reencoding PNG files. Useful when the PNG files are already optimized by other tools.
pub qlie_dpng_use_raw_png: bool,
#[cfg(feature = "qlie-img")]
/// Export Qlie DPNG images as PSD files.
pub qlie_dpng_psd: bool,
#[cfg(feature = "utils-psd")]
#[default(true)]
/// Whether to use compression for image data in PSD files.
pub psd_compress: bool,
#[cfg(feature = "emote-img")]
/// Export Emote PIMG images as PSD files.
pub emote_pimg_psd: bool,
#[cfg(feature = "kirikiri")]
/// Whether to only extract message between Talk and Hitret command in Kirikiri KS script. Auto detect if not specified.
pub kirikiri_ks_hitret: Option<bool>,
#[cfg(feature = "kirikiri")]
/// The line feed character used in Kirikiri KS script.
pub kirikiri_ks_lf: Option<String>,
#[cfg(feature = "kirikiri")]
/// Kirikiri message tags, used to extract more message from ks script.
pub kirikiri_message_tags: std::sync::Arc<std::collections::HashSet<String>>,
#[cfg(feature = "kirikiri")]
/// Specifiy BOM type when creating new Kirikiri ks script. If not specified, detect from original script.
pub kirikiri_ks_bom: Option<BomType>,
#[cfg(feature = "emote-img")]
/// BC7 compress configuration
pub bc7: crate::scripts::emote::psb::BC7Config,
#[cfg(feature = "artemis")]
#[default(default_artemis_asb_end_tags())]
/// A list of Artemis ASB script end tags, used to determine a dialogue block in script.
pub artemis_asb_end_tags: std::sync::Arc<std::collections::HashSet<String>>,
#[cfg(feature = "kirikiri-arc")]
/// Game title for Kirikiri XP3 archive. This is used to decrypt file in archives.
pub xp3_game_title: Option<String>,
#[cfg(feature = "kirikiri-arc")]
/// Print debug information for Kirikiri XP3 archive when extracting archive to stdout.
/// This is used to find correct configuration for unknown XP3 archives.
pub xp3_debug_archive: bool,
#[cfg(feature = "kirikiri-arc")]
/// Force extract encrypted files in Kirikiri XP3 archive without decryption.
pub xp3_force_extract: bool,
#[cfg(feature = "kirikiri-arc")]
/// Force decrypt files in Kirikiri xp3 archive even when flags are not set.
/// Some encrypted files in Kirikiri XP3 archive may not set encryption flag, but still encrypted. Enable this to force decrypt these files.
pub xp3_force_decrypt: bool,
#[cfg(feature = "emote-img")]
/// Disable diff handle when exporting Emote PIMG images to PSD files.
/// If enabled, no group layers will be crated if both layer don't have diff_id and group_layer_id attribute.
pub emote_pimg_psd_no_diff: bool,
#[cfg(feature = "kirikiri-arc")]
/// The path to the file list for Kirikiri XP3 archive. This is used to recover file names from hashed values.
/// Only works with some encyrption methods.
pub xp3_file_list_path: Option<String>,
}
#[cfg(feature = "artemis")]
fn default_artemis_asb_end_tags() -> std::sync::Arc<std::collections::HashSet<String>> {
std::sync::Arc::new(
["click", "hcls", "rpx"]
.into_iter()
.map(String::from)
.collect(),
)
}
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
/// Script type
pub enum ScriptType {
#[cfg(feature = "artemis")]
/// Artemis Engine AST script
Artemis,
#[cfg(feature = "artemis")]
/// Artemis Engine ASB script
ArtemisAsb,
#[cfg(feature = "artemis")]
/// Artemis Engine TXT (General) script
ArtemisTxt,
#[cfg(feature = "artemis-panmimisoft")]
/// Artemis Engine TXT (ぱんみみそふと) script
ArtemisPanmimisoftTxt,
#[cfg(feature = "artemis-arc")]
#[value(alias("pfs"))]
/// Artemis archive (pfs)
ArtemisArc,
#[cfg(feature = "artemis-arc")]
#[value(alias("pf2"))]
/// Artemis archive (pf2) (.pfs)
ArtemisPf2,
#[cfg(feature = "bgi")]
#[value(alias("ethornell"))]
/// Buriko General Interpreter/Ethornell Script
BGI,
#[cfg(feature = "bgi")]
#[value(alias("ethornell-bsi"))]
/// Buriko General Interpreter/Ethornell bsi script (._bsi)
BGIBsi,
#[cfg(feature = "bgi")]
#[value(alias("ethornell-bp"))]
/// Buriko General Interpreter/Ethornell bp script (._bp)
BGIBp,
#[cfg(feature = "bgi-arc")]
#[value(alias = "ethornell-arc-v1")]
/// Buriko General Interpreter/Ethornell archive v1
BGIArcV1,
#[cfg(feature = "bgi-arc")]
#[value(alias = "ethornell-arc-v2", alias = "bgi-arc", alias = "ethornell-arc")]
/// Buriko General Interpreter/Ethornell archive v2
BGIArcV2,
#[cfg(feature = "bgi-arc")]
#[value(alias("ethornell-dsc"))]
/// Buriko General Interpreter/Ethornell compressed file (DSC)
BGIDsc,
#[cfg(feature = "bgi-audio")]
#[value(alias("ethornell-audio"))]
/// Buriko General Interpreter/Ethornell audio file (Ogg/Vorbis)
BGIAudio,
#[cfg(feature = "bgi-img")]
#[value(alias("ethornell-img"))]
/// Buriko General Interpreter/Ethornell image (Image files in sysgrp.arc)
BGIImg,
#[cfg(feature = "bgi-img")]
#[value(alias("ethornell-cbg"))]
/// Buriko General Interpreter/Ethornell Compressed Background image (CBG)
BGICbg,
#[cfg(feature = "cat-system")]
/// CatSystem2 engine scene script
CatSystem,
#[cfg(feature = "cat-system")]
/// CatSystem2 engine CSTL script
CatSystemCstl,
#[cfg(feature = "cat-system-arc")]
/// CatSystem2 engine archive
CatSystemInt,
#[cfg(feature = "cat-system-img")]
/// CatSystem2 engine image
CatSystemHg3,
#[cfg(feature = "circus")]
/// Circus MES script
Circus,
#[cfg(feature = "circus-arc")]
/// Circus Image archive
CircusCrm,
#[cfg(feature = "circus-arc")]
/// Circus DAT archive
CircusDat,
#[cfg(feature = "circus-arc")]
/// Circus PCK archive
CircusPck,
#[cfg(feature = "circus-audio")]
/// Circus PCM audio
CircusPcm,
#[cfg(feature = "circus-img")]
/// Circus CRX Image
CircusCrx,
#[cfg(feature = "circus-img")]
/// Circus Differential Image
CircusCrxd,
#[cfg(feature = "emote-img")]
#[value(alias("psb"))]
/// Emote PSB (basic handle)
EmotePsb,
#[cfg(feature = "emote-img")]
#[value(alias("pimg"))]
/// Emote PIMG image
EmotePimg,
#[cfg(feature = "emote-img")]
#[value(alias("dref"))]
/// Emote DREF(DPAK-referenced) image
EmoteDref,
#[cfg(feature = "entis-gls")]
/// Entis GLS srcxml Script
EntisGls,
#[cfg(feature = "entis-gls")]
/// Entis GLS csx script
EntisGlsCsx,
#[cfg(feature = "escude-arc")]
/// Escude bin archive
EscudeArc,
#[cfg(feature = "escude")]
/// Escude bin script
Escude,
#[cfg(feature = "escude")]
/// Escude list script
EscudeList,
#[cfg(feature = "ex-hibit")]
/// ExHibit rld script
ExHibit,
#[cfg(feature = "ex-hibit-arc")]
/// ExHibit GRP archive
ExHibitGrp,
#[cfg(feature = "favorite")]
/// Favorite hcb script
Favorite,
#[cfg(feature = "hexen-haus")]
/// HexenHaus bin script
HexenHaus,
#[cfg(feature = "hexen-haus-arc")]
/// HexenHaus Arcc archive
HexenHausArcc,
#[cfg(feature = "hexen-haus-arc")]
/// HexenHaus Audio archive
HexenHausOdio,
#[cfg(feature = "hexen-haus-arc")]
/// HexenHaus WAG archive
HexenHausWag,
#[cfg(feature = "hexen-haus-img")]
/// HexenHaus PNG image
HexenHausPng,
#[cfg(feature = "kirikiri")]
#[value(alias("kr-scn"))]
/// Kirikiri SCN script
KirikiriScn,
#[cfg(feature = "kirikiri")]
#[value(alias("kr-simple-crypt"))]
/// Kirikiri SimpleCrypt's text file
KirikiriSimpleCrypt,
#[cfg(feature = "kirikiri")]
#[value(alias = "kr", alias = "kr-ks", alias = "kirikiri-ks")]
/// Kirikiri script
Kirikiri,
#[cfg(feature = "kirikiri-arc")]
#[value(alias = "kr-xp3", alias = "xp3")]
/// Kirikiri XP3 archive
KirikiriXp3,
#[cfg(feature = "kirikiri-img")]
#[value(alias("kr-tlg"))]
/// Kirikiri TLG image
KirikiriTlg,
#[cfg(feature = "kirikiri")]
#[value(alias("kr-mdf"))]
/// Kirikiri MDF (zlib compressed) file
KirikiriMdf,
#[cfg(feature = "kirikiri")]
#[value(alias("kr-tjs2"))]
/// Kirikiri compiled TJS2 script
KirikiriTjs2,
#[cfg(feature = "kirikiri")]
#[value(alias("kr-tjs-ns0"))]
/// Kirikiri TJS NS0 binary encoded script
KirikiriTjsNs0,
#[cfg(feature = "musica")]
/// Musica Script (.sc)
Musica,
#[cfg(feature = "musica-arc")]
/// Musica Engine Resource Archive (.paz)
MusicaPaz,
#[cfg(feature = "qlie")]
/// Qlie Engine Scenario script (.s)
Qlie,
#[cfg(feature = "qlie-arc")]
/// Qlie Pack Archive (.pack)
QliePack,
#[cfg(feature = "qlie-img")]
/// Qlie tiled PNG image (.png)
QlieDpng,
#[cfg(feature = "qlie-img")]
#[value(alias = "qlie-abmp11", alias = "qlie-abmp12")]
/// Qlie Abmp10/11/12 image (.b)
QlieAbmp10,
#[cfg(feature = "silky")]
/// Silky Engine Mes script
Silky,
#[cfg(feature = "silky")]
/// Silky Engine Map script
SilkyMap,
#[cfg(feature = "softpal")]
/// Softpal src script
Softpal,
#[cfg(feature = "softpal-arc")]
/// Softpal Pac archive
SoftpalPac,
#[cfg(feature = "softpal-arc")]
/// Softpal Pac/AMUSE archive
SoftpalPacAmuse,
#[cfg(feature = "softpal-img")]
#[value(alias = "pgd-ge", alias = "pgd")]
/// Softpal Pgd Ge image
SoftpalPgdGe,
#[cfg(feature = "softpal-img")]
#[value(alias = "softpal-pgd2", alias = "pgd3", alias = "pgd2")]
/// Softpal Pgd Differential image
SoftpalPgd3,
#[cfg(feature = "will-plus")]
#[value(alias("adv-hd-ws2"))]
/// WillPlus ws2 script
WillPlusWs2,
#[cfg(feature = "will-plus-img")]
#[value(alias("adv-hd-wip"))]
/// WillPlus WIP Image
WillPlusWip,
#[cfg(feature = "yaneurao-itufuru")]
#[value(alias("itufuru"))]
/// Yaneurao Itufuru script
YaneuraoItufuru,
#[cfg(feature = "yaneurao-itufuru")]
#[value(alias("itufuru-arc"))]
/// Yaneurao Itufuru script archive
YaneuraoItufuruArc,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
/// Message structure for scripts
pub struct Message {
#[serde(skip_serializing_if = "Option::is_none")]
/// Optional name for the message, used in some scripts.
pub name: Option<String>,
/// The actual message content.
pub message: String,
}
impl Message {
/// Creates a new `Message` instance.
pub fn new(message: String, name: Option<String>) -> Self {
Message { message, name }
}
}
#[derive(Clone, Debug)]
/// Extended message structure for scripts
pub struct ExtendedMessage {
/// Optional name for the message, used in some scripts.
pub name: Option<String>,
/// Original source text.
pub source: String,
/// Translated text.
pub translated: String,
/// Optional LLM translated text.
pub llm: Option<String>,
}
/// Result of script operation.
pub enum ScriptResult {
/// Operation completed successfully.
Ok,
/// Operation completed without any changes.
/// For example, no messages found in the script.
Ignored,
/// Operation not completed.
/// This will not count in statistics.
Uncount,
}
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// Format type (for CLI)
pub enum FormatType {
/// Wrap line with fixed length
Fixed,
/// Do not wrap line
None,
}
#[derive(Clone)]
/// Format options
pub enum FormatOptions {
/// Wrap line with fixed length
Fixed {
/// Fixed length
length: usize,
/// Whether to keep original line breaks
keep_original: bool,
/// Whether to break words(ASCII only) at the end of the line
break_words: bool,
/// Whether to insert a full-width space after a line break when a sentence starts with a full-width quotation mark.
insert_fullwidth_space_at_line_start: bool,
/// If a line break occurs in the middle of some symbols, bring the sentence to next line
break_with_sentence: bool,
#[cfg(feature = "jieba")]
/// Whether to break Chinese words at the end of the line.
break_chinese_words: bool,
#[cfg(feature = "jieba")]
/// Path to custom jieba dictionary
jieba_dict: Option<String>,
/// Do not remove space at the start of the line
no_remove_space_at_line_start: bool,
},
/// Do not wrap line
None,
}
#[derive(Debug, Serialize, Deserialize)]
/// Name table cell
pub struct NameTableCell {
#[serde(rename = "JP_Name")]
/// Original name
pub jp_name: String,
#[serde(rename = "CN_Name")]
/// Translated name
pub cn_name: String,
#[serde(rename = "Count")]
/// Number of times this name appears in the script
pub count: usize,
}
#[derive(Debug, Serialize, Deserialize)]
/// Replacement table for string replacements
pub struct ReplacementTable {
#[serde(flatten)]
/// Map of original strings to their replacements
pub map: HashMap<String, String>,
}
#[cfg(feature = "image")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
/// Image color type
pub enum ImageColorType {
/// Grayscale image
Grayscale,
/// RGB image
Rgb,
/// RGBA image
Rgba,
/// BGR image
Bgr,
/// BGRA image
Bgra,
}
#[cfg(feature = "image")]
impl ImageColorType {
/// Returns the number of bytes per pixel for the color type and depth.
pub fn bpp(&self, depth: u8) -> u16 {
match self {
ImageColorType::Grayscale => depth as u16,
ImageColorType::Rgb => depth as u16 * 3,
ImageColorType::Rgba => depth as u16 * 4,
ImageColorType::Bgr => depth as u16 * 3,
ImageColorType::Bgra => depth as u16 * 4,
}
}
}
#[cfg(feature = "image")]
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// Image output type
pub enum ImageOutputType {
/// PNG image
Png,
#[cfg(feature = "image-jpg")]
/// JPEG image
Jpg,
#[cfg(feature = "image-webp")]
/// WebP image
Webp,
#[cfg(feature = "image-jxl")]
/// JPEG XL image
Jxl,
}
#[cfg(feature = "image")]
impl TryFrom<&str> for ImageOutputType {
type Error = anyhow::Error;
/// Try to convert a extension string to an `ImageOutputType`.
/// Extensions are case-insensitive.
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value.to_ascii_lowercase().as_str() {
"png" => Ok(ImageOutputType::Png),
#[cfg(feature = "image-jpg")]
"jpg" => Ok(ImageOutputType::Jpg),
#[cfg(feature = "image-jpg")]
"jpeg" => Ok(ImageOutputType::Jpg),
#[cfg(feature = "image-webp")]
"webp" => Ok(ImageOutputType::Webp),
#[cfg(feature = "image-jxl")]
"jxl" => Ok(ImageOutputType::Jxl),
_ => Err(anyhow::anyhow!("Unsupported image output type: {}", value)),
}
}
}
#[cfg(feature = "image")]
impl TryFrom<&std::path::Path> for ImageOutputType {
type Error = anyhow::Error;
fn try_from(value: &std::path::Path) -> Result<Self, Self::Error> {
if let Some(ext) = value.extension() {
Self::try_from(ext.to_string_lossy().as_ref())
} else {
Err(anyhow::anyhow!("No extension found in path"))
}
}
}
#[cfg(feature = "image")]
impl AsRef<str> for ImageOutputType {
/// Returns the extension for the image output type.
fn as_ref(&self) -> &str {
match self {
ImageOutputType::Png => "png",
#[cfg(feature = "image-jpg")]
ImageOutputType::Jpg => "jpg",
#[cfg(feature = "image-webp")]
ImageOutputType::Webp => "webp",
#[cfg(feature = "image-jxl")]
ImageOutputType::Jxl => "jxl",
}
}
}
#[cfg(feature = "image")]
#[derive(Clone, Debug)]
/// Image data
pub struct ImageData {
/// Image width in pixels
pub width: u32,
/// Image height in pixels
pub height: u32,
/// Image color type
pub color_type: ImageColorType,
/// Image depth in bits per channel
pub depth: u8,
/// Image data
pub data: Vec<u8>,
}
#[cfg(feature = "image")]
#[derive(Clone, Debug)]
/// Image data with name
pub struct ImageDataWithName {
/// Image name
pub name: String,
/// Image data
pub data: ImageData,
}
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// BOM type
pub enum BomType {
/// No BOM
None,
/// UTF-8 BOM
Utf8,
/// UTF-16 Little Endian BOM
Utf16LE,
/// UTF-16 Big Endian BOM
Utf16BE,
}
impl BomType {
/// Returns the byte sequence for the BOM type.
pub fn as_bytes(&self) -> &'static [u8] {
match self {
BomType::None => &[],
BomType::Utf8 => b"\xEF\xBB\xBF",
BomType::Utf16LE => b"\xFF\xFE",
BomType::Utf16BE => b"\xFE\xFF",
}
}
}
#[cfg(feature = "image")]
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// PNG compression level
pub enum PngCompressionLevel {
#[value(alias = "n")]
/// No compression whatsoever. Fastest, but results in large files.
NoCompression,
#[value(alias = "d")]
/// Default level (usually balanced)
Default,
/// Extremely fast but light compression.
///
/// Note: When used in streaming mode, this compression level can actually result in files
/// *larger* than would be produced by `NoCompression` on incompressible data because
/// it doesn't do any buffering of the output stream to detect whether the data is being compressed or not.
Fastest,
#[value(alias = "f")]
/// Fast minimal compression
Fast,
#[value(alias = "b")]
/// Higher compression level. Same as high
Best,
#[value(alias = "h")]
/// Spend much more time to produce a slightly smaller file than with `Balanced`.
High,
}
#[cfg(feature = "image")]
impl Default for PngCompressionLevel {
fn default() -> Self {
PngCompressionLevel::Default
}
}
#[cfg(feature = "image")]
impl PngCompressionLevel {
/// Converts the [PngCompressionLevel] to a [png::Compression] enum.
pub fn to_compression(&self) -> png::Compression {
match self {
PngCompressionLevel::NoCompression => png::Compression::NoCompression,
PngCompressionLevel::Fastest => png::Compression::Fastest,
PngCompressionLevel::Default => png::Compression::Balanced,
PngCompressionLevel::Fast => png::Compression::Fast,
PngCompressionLevel::Best => png::Compression::High,
PngCompressionLevel::High => png::Compression::High,
}
}
}
#[cfg(feature = "lossless-audio")]
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
/// Lossless audio format
pub enum LosslessAudioFormat {
/// Wav
Wav,
#[cfg(feature = "audio-flac")]
/// FLAC Format
Flac,
}
#[cfg(feature = "lossless-audio")]
impl Default for LosslessAudioFormat {
fn default() -> Self {
LosslessAudioFormat::Wav
}
}
#[cfg(feature = "lossless-audio")]
impl TryFrom<&str> for LosslessAudioFormat {
type Error = anyhow::Error;
/// Try to convert a extension string to an `LosslessAudioFormat`.
/// Extensions are case-insensitive.
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value.to_ascii_lowercase().as_str() {
"wav" => Ok(LosslessAudioFormat::Wav),
#[cfg(feature = "audio-flac")]
"flac" => Ok(LosslessAudioFormat::Flac),
_ => Err(anyhow::anyhow!(
"Unsupported lossless audio format: {}",
value
)),
}
}
}
#[cfg(feature = "lossless-audio")]
impl TryFrom<&std::path::Path> for LosslessAudioFormat {
type Error = anyhow::Error;
fn try_from(value: &std::path::Path) -> Result<Self, Self::Error> {
if let Some(ext) = value.extension() {
Self::try_from(ext.to_string_lossy().as_ref())
} else {
Err(anyhow::anyhow!("No extension found in path"))
}
}
}
#[cfg(feature = "lossless-audio")]
impl AsRef<str> for LosslessAudioFormat {
/// Returns the extension for the lossless audio format.
fn as_ref(&self) -> &str {
match self {
LosslessAudioFormat::Wav => "wav",
#[cfg(feature = "audio-flac")]
LosslessAudioFormat::Flac => "flac",
}
}
}
#[allow(unused)]
pub(crate) fn get_default_threads() -> usize {
num_cpus::get().max(2) / 2
}