feat(artemis): add support for ぱんみみそふと TXT scripts

- Updated main.rs to include new configuration options for artemis-panmimisoft.
- Modified asb.rs to handle both .asb and .iet file extensions.
- Introduced new module for panmimisoft scripts and implemented TxtBuilder for parsing.
- Enhanced the parser to support new tag structures and attributes specific to ぱんみみそふと scripts.
- Updated types.rs to reflect changes in configuration options and script types.
- Adjusted mod.rs to register the new TxtBuilder for panmimisoft.
This commit is contained in:
2025-08-28 20:35:11 +08:00
parent bb27378450
commit 7b3b852483
11 changed files with 210 additions and 61 deletions

View File

@@ -240,21 +240,26 @@ pub struct Arg {
/// Specify the language of Artemis AST script.
/// If not specified, the first language will be used.
pub artemis_ast_lang: Option<String>,
#[cfg(feature = "artemis")]
// Default value is from tagFilters in macro.iet
#[cfg(feature = "artemis-panmimisoft")]
#[arg(
long,
global = true,
value_delimiter = ',',
default_value = "遅延イベントCG,遅延背景,bgv_in,イベントCG,遅延ポップアップ"
default_value = "背景,イベントCG,遅延背景,遅延背景予約,背景予約,遅延イベントCG,遅延イベントCG予約,イベントCG予約,遅延ポップアップ,遅延bgm_in,遅延bgm_out,遅延se_in,遅延se_out,遅延bgs_in,遅延bgs_out,立ち絵face非連動,セーブサムネイル置換終了,シネスコ"
)]
/// Artemis Engine blacklist tag names for TXT script.
/// This is used to ignore these tags when finding names in Artemis TXT script.
pub artemis_txt_blacklist_names: Vec<String>,
#[cfg(feature = "artemis")]
/// This is used to ignore these tags when finding names in Artemis TXT script (ぱんみみそふと).
pub artemis_panmimisoft_txt_blacklist_names: Vec<String>,
#[cfg(feature = "artemis-panmimisoft")]
#[arg(long, global = true)]
/// Specify the language of Artemis TXT script.
/// Specify the language of Artemis TXT (ぱんみみそふと) script.
/// If not specified, the first language will be used.
pub artemis_txt_lang: Option<String>,
pub artemis_panmimisoft_txt_lang: Option<String>,
#[cfg(feature = "artemis-panmimisoft")]
#[arg(long, global = true)]
/// The path to the tag.ini file, which contains the tags to be ignored when finding names in Artemis TXT script (ぱんみみそふと).
pub artemis_panmimisoft_txt_tag_ini: Option<String>,
#[cfg(feature = "cat-system")]
#[arg(long, global = true)]
/// CatSystem2 CSTL script language, used to extract messages from CSTL script.
@@ -505,3 +510,23 @@ pub fn get_cat_system_int_encrypt_password(arg: &Arg) -> anyhow::Result<Option<S
}
Ok(None)
}
#[cfg(feature = "artemis-panmimisoft")]
pub fn get_artemis_panmimisoft_txt_blacklist_names(
arg: &Arg,
) -> anyhow::Result<std::collections::HashSet<String>> {
match &arg.artemis_panmimisoft_txt_tag_ini {
Some(path) => {
let mut set = crate::scripts::artemis::panmimisoft::txt::read_tags_from_ini(path)?;
for name in &arg.artemis_panmimisoft_txt_blacklist_names {
set.insert(name.clone());
}
Ok(set)
}
None => Ok(arg
.artemis_panmimisoft_txt_blacklist_names
.iter()
.cloned()
.collect()),
}
}

View File

@@ -1765,12 +1765,12 @@ fn main() {
entis_gls_srcxml_lang: arg.entis_gls_srcxml_lang.clone(),
#[cfg(feature = "will-plus")]
will_plus_ws2_no_disasm: arg.will_plus_ws2_no_disasm,
#[cfg(feature = "artemis")]
artemis_txt_blacklist_names: std::sync::Arc::new(std::collections::HashSet::from_iter(
arg.artemis_txt_blacklist_names.iter().cloned(),
)),
#[cfg(feature = "artemis")]
artemis_txt_lang: arg.artemis_txt_lang.clone(),
#[cfg(feature = "artemis-panmimisoft")]
artemis_panmimisoft_txt_blacklist_names: std::sync::Arc::new(
args::get_artemis_panmimisoft_txt_blacklist_names(&arg).unwrap(),
),
#[cfg(feature = "artemis-panmimisoft")]
artemis_panmimisoft_txt_lang: arg.artemis_panmimisoft_txt_lang.clone(),
};
match &arg.command {
args::Command::Export { input, output } => {

View File

@@ -1,4 +1,4 @@
//! Artemis Engine ASB file (.asb)
//! Artemis Engine ASB file (.asb/.iet)
use crate::ext::io::*;
use crate::scripts::base::*;
use crate::types::*;
@@ -30,17 +30,17 @@ impl ScriptBuilder for ArtemisAsbBuilder {
fn build_script(
&self,
buf: Vec<u8>,
_filename: &str,
filename: &str,
encoding: Encoding,
_archive_encoding: Encoding,
config: &ExtraConfig,
_archive: Option<&Box<dyn Script>>,
) -> Result<Box<dyn Script>> {
Ok(Box::new(Asb::new(buf, encoding, config)?))
Ok(Box::new(Asb::new(buf, encoding, config, filename)?))
}
fn extensions(&self) -> &'static [&'static str] {
&["asb"]
&["asb", "iet"]
}
fn script_type(&self) -> &'static ScriptType {
@@ -421,6 +421,7 @@ impl<'a> TextParser<'a> {
pub struct Asb {
items: Vec<Item>,
custom_yaml: bool,
is_iet: bool,
}
impl Asb {
@@ -429,7 +430,12 @@ impl Asb {
/// * `buf` - The buffer containing the ASB data.
/// * `encoding` - The encoding used for the ASB data.
/// * `config` - Extra configuration options.
pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
pub fn new(
buf: Vec<u8>,
encoding: Encoding,
config: &ExtraConfig,
filename: &str,
) -> Result<Self> {
let mut data = MemReader::new(buf);
let mut magic = [0; 5];
data.read_exact(&mut magic)?;
@@ -444,17 +450,28 @@ impl Asb {
Ok(Asb {
items,
custom_yaml: config.custom_yaml,
is_iet: std::path::Path::new(filename)
.extension()
.map_or(false, |ext| ext.eq_ignore_ascii_case("iet")),
})
}
}
impl Script for Asb {
fn default_output_script_type(&self) -> OutputScriptType {
OutputScriptType::Json
if self.is_iet {
OutputScriptType::Custom
} else {
OutputScriptType::Json
}
}
fn is_output_supported(&self, _: OutputScriptType) -> bool {
true
fn is_output_supported(&self, out: OutputScriptType) -> bool {
if self.is_iet {
matches!(out, OutputScriptType::Custom)
} else {
true
}
}
fn default_format_type(&self) -> FormatOptions {

View File

@@ -3,4 +3,5 @@
pub mod archive;
pub mod asb;
pub mod ast;
pub mod txt;
#[cfg(feature = "artemis-panmimisoft")]
pub mod panmimisoft;

View File

@@ -0,0 +1,2 @@
//! Special parsers for [ぱんみみそふと](https://pannomimi.net/panmimisoft) games using the Artemis Engine.
pub mod txt;

View File

@@ -41,7 +41,7 @@ impl ScriptBuilder for TxtBuilder {
}
fn script_type(&self) -> &'static ScriptType {
&ScriptType::ArtemisTxt
&ScriptType::ArtemisPanmimisoftTxt
}
}
@@ -138,7 +138,7 @@ impl TagNode {
/// Returns true if the tag is not suitable for name.
pub fn is_blocked_name(&self, set: &HashSet<String>) -> bool {
set.contains(&self.name)
self.name.is_ascii() || set.contains(&self.name)
}
/// Checks if the tag has a specific attribute.
@@ -814,8 +814,8 @@ impl TxtScript {
let tree = parser.parse(true)?;
Ok(Self {
tree,
blacklist_names: config.artemis_txt_blacklist_names.clone(),
lang: config.artemis_txt_lang.clone(),
blacklist_names: config.artemis_panmimisoft_txt_blacklist_names.clone(),
lang: config.artemis_panmimisoft_txt_lang.clone(),
})
}
}
@@ -873,23 +873,13 @@ impl Script for TxtScript {
let mes = message.to_xml();
message.clear();
if !mes.is_empty() {
let name = if mes.starts_with("") {
match &last_tag_block {
Some(block) => {
Some(if let Some(name) = block.get_attr("name") {
name.to_string()
} else {
block.name.clone()
})
}
_ => {
eprintln!("Warn: Name block not found.");
crate::COUNTER.inc_warning();
None
}
}
} else {
None
let name = match &last_tag_block {
Some(block) => Some(if let Some(name) = block.get_attr("name") {
name.to_string()
} else {
block.name.clone()
}),
_ => None,
};
messages.push(Message { name, message: mes });
}
@@ -1177,6 +1167,17 @@ impl Script for TxtScript {
}
}
/// Reads tags list from tag.ini file.
pub fn read_tags_from_ini<P: AsRef<std::path::Path>>(path: P) -> Result<HashSet<String>> {
let conf = ini::Ini::load_from_file(path)?;
let set = HashSet::from_iter(conf.sections().flat_map(|s| s.map(|s| s.to_string())));
eprintln!(
"Read tags from ini: {}",
set.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(",")
);
Ok(set)
}
#[test]
fn test_xml_parser() {
let data = "测试文本\nok<r a=\"b\">测试<b o=\"文本\n换行\">";

View File

@@ -112,8 +112,8 @@ lazy_static::lazy_static! {
Box::new(entis_gls::srcxml::SrcXmlScriptBuilder::new()),
#[cfg(feature = "softpal")]
Box::new(softpal::scr::SoftpalScriptBuilder::new()),
#[cfg(feature = "artemis")]
Box::new(artemis::txt::TxtBuilder::new()),
#[cfg(feature = "artemis-panmimisoft")]
Box::new(artemis::panmimisoft::txt::TxtBuilder::new()),
];
/// A list of all script extensions.
pub static ref ALL_EXTS: Vec<String> =

View File

@@ -344,14 +344,14 @@ pub struct ExtraConfig {
/// 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")]
#[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_txt_blacklist_names: std::sync::Arc<std::collections::HashSet<String>>,
#[cfg(feature = "artemis")]
/// Specify the language of Artemis 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_txt_lang: Option<String>,
pub artemis_panmimisoft_txt_lang: Option<String>,
}
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
@@ -363,9 +363,9 @@ pub enum ScriptType {
#[cfg(feature = "artemis")]
/// Artemis Engine ASB script
ArtemisAsb,
#[cfg(feature = "artemis")]
/// Artemis Engine TXT script
ArtemisTxt,
#[cfg(feature = "artemis-panmimisoft")]
/// Artemis Engine TXT (ぱんみみそふと) script
ArtemisPanmimisoftTxt,
#[cfg(feature = "artemis-arc")]
#[value(alias("pfs"))]
/// Artemis archive (pfs)