mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
1286 lines
45 KiB
Rust
1286 lines
45 KiB
Rust
//! 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(9)]
|
|
/// Compress level for BGI Dsc file. 0 means store, 9 mean best compression.
|
|
pub bgi_compress_level: u8,
|
|
#[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 = "kirikiri-arc")]
|
|
/// Control the behavior to how to extract files from Cxdec3/4(Hxv4) protected archives.
|
|
pub xp3_cxdec_file_hash: crate::scripts::kirikiri::archive::xp3::FileHashOption,
|
|
#[cfg(feature = "kirikiri-arc")]
|
|
/// Control the behavior to how to append path name to files from Cxdec3/4(Hxv4) protected archives.
|
|
pub xp3_cxdec_path_hash: crate::scripts::kirikiri::archive::xp3::PathHashOption,
|
|
}
|
|
|
|
#[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,
|
|
#[cfg(feature = "yuris")]
|
|
/// Yu-Ris YSCM(opcodes metadata) file (.ybn)
|
|
YurisYSCM,
|
|
#[cfg(feature = "yuris")]
|
|
/// Yu-Ris YSER(error message) file (.ybn)
|
|
YurisYSER,
|
|
#[cfg(feature = "yuris")]
|
|
/// Yu-Ris YSCFG(config) file (.ybn)
|
|
YurisYSCFG,
|
|
}
|
|
|
|
#[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
|
|
}
|