From 263c156d03bcb351fd82533af6ee7690e18193d0 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 1 Jul 2025 16:54:16 +0800 Subject: [PATCH] Add support to import comu message to scn scripts --- src/args.rs | 4 ++ src/ext/psb.rs | 43 ++++++++++++++++- src/main.rs | 5 ++ src/scripts/kirikiri/mod.rs | 8 ++++ src/scripts/kirikiri/scn.rs | 93 ++++++++++++++++++++++++++++++++++++- src/types.rs | 2 + 6 files changed, 152 insertions(+), 3 deletions(-) diff --git a/src/args.rs b/src/args.rs index 43eafd2..52e8822 100644 --- a/src/args.rs +++ b/src/args.rs @@ -119,6 +119,10 @@ pub struct Arg { /// Export COMU message to extra json file. (for Kirikiri SCN script.) /// Only CIRCUS's game have COMU message. pub kirikiri_export_comumode: bool, + #[cfg(feature = "kirikiri")] + #[arg(long, global = true)] + /// Kirikiri COMU message translation file. (Map, key is original text, value is translated text.) + pub kirikiri_comumode_json: Option, #[command(subcommand)] /// Command pub command: Command, diff --git a/src/ext/psb.rs b/src/ext/psb.rs index 277d89c..0b0bf10 100644 --- a/src/ext/psb.rs +++ b/src/ext/psb.rs @@ -5,6 +5,7 @@ use emote_psb::types::number::*; use emote_psb::types::reference::*; use emote_psb::types::string::*; use emote_psb::types::*; +use std::cmp::PartialEq; use std::collections::HashMap; use std::ops::{Index, IndexMut}; @@ -158,7 +159,7 @@ impl IndexMut for PsbValueFixed { &mut l.values[index] } else { l.values.push(NONE); - &mut l.values[index] + l.values.last_mut().unwrap() } } _ => { @@ -246,6 +247,27 @@ impl Clone for PsbValueFixed { } } +impl PartialEq for PsbValueFixed { + fn eq(&self, other: &String) -> bool { + self == other.as_str() + } +} + +impl PartialEq for PsbValueFixed { + fn eq(&self, other: &str) -> bool { + match self { + PsbValueFixed::String(s) => s.string() == other, + _ => false, + } + } +} + +impl<'a> PartialEq<&'a str> for PsbValueFixed { + fn eq(&self, other: &&'a str) -> bool { + self == *other + } +} + pub trait PsbValueExt { fn to_psb_fixed(self) -> PsbValueFixed; } @@ -306,6 +328,25 @@ impl PsbListFixed { } } +impl Index for PsbListFixed { + type Output = PsbValueFixed; + + fn index(&self, index: usize) -> &Self::Output { + self.values.get(index).unwrap_or(&NONE) + } +} + +impl IndexMut for PsbListFixed { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + if index < self.values.len() { + &mut self.values[index] + } else { + self.values.push(NONE); + self.values.last_mut().unwrap() + } + } +} + pub struct ListIter<'a> { inner: std::slice::Iter<'a, PsbValueFixed>, } diff --git a/src/main.rs b/src/main.rs index 15ec1ca..5ec4815 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1360,6 +1360,11 @@ fn main() { kirikiri_language_index: arg.kirikiri_language_index.clone(), #[cfg(feature = "kirikiri")] kirikiri_export_comumode: arg.kirikiri_export_comumode, + #[cfg(feature = "kirikiri")] + kirikiri_comumode_json: arg + .kirikiri_comumode_json + .as_ref() + .map(|s| scripts::kirikiri::read_kirikiri_comu_json(s).unwrap()), }; match &arg.command { args::Command::Export { input, output } => { diff --git a/src/scripts/kirikiri/mod.rs b/src/scripts/kirikiri/mod.rs index 6c45797..8a33933 100644 --- a/src/scripts/kirikiri/mod.rs +++ b/src/scripts/kirikiri/mod.rs @@ -1 +1,9 @@ pub mod scn; +use std::collections::HashMap; +use std::sync::Arc; + +pub fn read_kirikiri_comu_json(path: &str) -> anyhow::Result>> { + let mut reader = std::fs::File::open(path)?; + let data = serde_json::from_reader(&mut reader)?; + Ok(Arc::new(data)) +} diff --git a/src/scripts/kirikiri/scn.rs b/src/scripts/kirikiri/scn.rs index 6de9ad5..07f4cfe 100644 --- a/src/scripts/kirikiri/scn.rs +++ b/src/scripts/kirikiri/scn.rs @@ -4,9 +4,10 @@ use crate::scripts::base::*; use crate::types::*; use anyhow::Result; use emote_psb::{PsbReader, PsbWriter}; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::io::{Read, Seek}; use std::path::Path; +use std::sync::Arc; #[derive(Debug)] pub struct ScnScriptBuilder {} @@ -101,6 +102,7 @@ pub struct ScnScript { language_index: usize, export_comumode: bool, filename: String, + comumode_json: Option>>, } impl ScnScript { @@ -115,6 +117,7 @@ impl ScnScript { language_index: config.kirikiri_language_index.unwrap_or(0), export_comumode: config.kirikiri_export_comumode, filename: filename.to_string(), + comumode_json: config.kirikiri_comumode_json.clone(), }) } } @@ -320,6 +323,10 @@ impl Script for ScnScript { if !scenes.is_list() { return Err(anyhow::anyhow!("scenes is not an array")); } + let comu = self + .comumode_json + .as_ref() + .map(|json| ImportComuMes::new(json, replacement)); for (i, scene) in scenes.members_mut().enumerate() { if !scene.is_object() { return Err(anyhow::anyhow!("scene at {} is not an object", i)); @@ -547,7 +554,7 @@ impl Script for ScnScript { } } } - // #TODO: comumode + comu.as_ref().map(|c| c.import(scene)); } if cur_mes.is_some() || mes.next().is_some() { return Err(anyhow::anyhow!("Some messages were not processed.")); @@ -619,3 +626,85 @@ impl ExportComuMes { } } } + +#[derive(Debug)] +struct ImportComuMes<'a> { + messages: &'a Arc>, + replacement: Option<&'a ReplacementTable>, +} + +impl<'a> ImportComuMes<'a> { + pub fn new( + messages: &'a Arc>, + replacement: Option<&'a ReplacementTable>, + ) -> Self { + Self { + messages, + replacement, + } + } + + pub fn import(&self, value: &mut PsbValueFixed) { + match value { + PsbValueFixed::Object(obj) => { + for (k, v) in obj.iter_mut() { + if k == "comumode" { + for obj in v.members_mut() { + if let Some(text) = obj["text"].as_str() { + if let Some(replace_text) = self.messages.get(text) { + let mut text = replace_text.clone(); + if let Some(replacement) = self.replacement { + for (key, value) in replacement.map.iter() { + text = text.replace(key, value); + } + } + obj["text"].set_string(text.replace("\n", "\\n")); + } else { + eprintln!( + "Warning: COMU message '{}' not found in translation table.", + text + ); + crate::COUNTER.inc_warning(); + } + } + } + } else { + self.import(v); + } + } + } + PsbValueFixed::List(list) => { + if list.len() > 1 { + if list[0] == "comumode" { + for i in 1..list.len() { + if list[i - 1] == "text" { + if let Some(text) = list[i].as_str() { + if let Some(replace_text) = self.messages.get(text) { + let mut text = replace_text.clone(); + if let Some(replacement) = self.replacement { + for (key, value) in replacement.map.iter() { + text = text.replace(key, value); + } + } + list[i].set_string(text.replace("\n", "\\n")); + } else { + eprintln!( + "Warning: COMU message '{}' not found in translation table.", + text + ); + crate::COUNTER.inc_warning(); + } + } + } + } + return; + } + } + for item in list.iter_mut() { + self.import(item); + } + } + _ => {} + } + } +} diff --git a/src/types.rs b/src/types.rs index 1a359bf..6683452 100644 --- a/src/types.rs +++ b/src/types.rs @@ -211,6 +211,8 @@ pub struct ExtraConfig { pub kirikiri_language_index: Option, #[cfg(feature = "kirikiri")] pub kirikiri_export_comumode: bool, + #[cfg(feature = "kirikiri")] + pub kirikiri_comumode_json: Option>>, } #[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]