diff --git a/src/args.rs b/src/args.rs index cc6a39b..43eafd2 100644 --- a/src/args.rs +++ b/src/args.rs @@ -114,6 +114,11 @@ pub struct Arg { #[arg(long, global = true)] /// Kirikiri language index in script. If not specified, the first language will be used. pub kirikiri_language_index: Option, + #[cfg(feature = "kirikiri")] + #[arg(long, global = true, action = ArgAction::SetTrue)] + /// Export COMU message to extra json file. (for Kirikiri SCN script.) + /// Only CIRCUS's game have COMU message. + pub kirikiri_export_comumode: bool, #[command(subcommand)] /// Command pub command: Command, diff --git a/src/main.rs b/src/main.rs index c47b2b1..15ec1ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1358,6 +1358,8 @@ fn main() { cat_system_image_canvas: arg.cat_system_image_canvas, #[cfg(feature = "kirikiri")] kirikiri_language_index: arg.kirikiri_language_index.clone(), + #[cfg(feature = "kirikiri")] + kirikiri_export_comumode: arg.kirikiri_export_comumode, }; match &arg.command { args::Command::Export { input, output } => { diff --git a/src/scripts/kirikiri/scn.rs b/src/scripts/kirikiri/scn.rs index 8b91003..e82a8a5 100644 --- a/src/scripts/kirikiri/scn.rs +++ b/src/scripts/kirikiri/scn.rs @@ -6,6 +6,7 @@ use anyhow::Result; use emote_psb::types::PsbValue; use emote_psb::{PsbReader, PsbWriter, VirtualPsb}; use serde::{Deserialize, Serialize}; +use std::collections::HashSet; use std::io::{Read, Seek, Write}; use std::path::Path; @@ -100,6 +101,8 @@ impl ScriptBuilder for ScnScriptBuilder { pub struct ScnScript { psb: VirtualPsb, language_index: usize, + export_comumode: bool, + filename: String, } impl ScnScript { @@ -112,6 +115,8 @@ impl ScnScript { Ok(Self { psb, language_index: config.kirikiri_language_index.unwrap_or(0), + export_comumode: config.kirikiri_export_comumode, + filename: filename.to_string(), }) } } @@ -173,8 +178,13 @@ impl Script for ScnScript { PsbValue::List(list) => list, _ => return Err(anyhow::anyhow!("scenes is not a list")), }; - for (i, scene) in scenes.iter().enumerate() { - let scene = match scene { + let mut comu = if self.export_comumode { + Some(ExportComuMes::new()) + } else { + None + }; + for (i, oscene) in scenes.iter().enumerate() { + let scene = match oscene { PsbValue::Object(obj) => obj, _ => return Err(anyhow::anyhow!("scene at index {} is not an object", i)), }; @@ -304,7 +314,34 @@ impl Script for ScnScript { } } } - // #TODO: comudata(circus) + comu.as_mut().map(|c| c.export(&oscene)); + } + if let Some(comu) = comu { + if !comu.messages.is_empty() { + let mut pb = std::path::PathBuf::from(&self.filename); + let filename = pb + .file_stem() + .map(|s| s.to_string_lossy()) + .unwrap_or(std::borrow::Cow::from("comumode")); + pb.set_file_name(format!("{}_comumode.json", filename)); + match std::fs::File::create(&pb) { + Ok(mut f) => { + let messages: Vec = comu.messages.into_iter().collect(); + if let Err(e) = serde_json::to_writer_pretty(&mut f, &messages) { + eprintln!("Failed to write COMU messages to {}: {:?}", pb.display(), e); + crate::COUNTER.inc_warning(); + } + } + Err(e) => { + eprintln!( + "Failed to create COMU messages file {}: {:?}", + pb.display(), + e + ); + crate::COUNTER.inc_warning(); + } + } + } } Ok(messages) } @@ -352,3 +389,62 @@ impl Script for ScnScript { Ok(()) } } + +#[derive(Debug)] +struct ExportComuMes { + pub messages: HashSet, +} + +impl ExportComuMes { + pub fn new() -> Self { + Self { + messages: HashSet::new(), + } + } + + pub fn export(&mut self, value: &PsbValue) { + match value { + PsbValue::Object(obj) => { + for (k, v) in obj.iter() { + if k == "comumode" { + if let PsbValue::List(list) = v { + for item in list.iter() { + if let PsbValue::Object(obj) = item { + if let Some(PsbValue::String(s)) = obj.get_value("text".into()) + { + self.messages.insert(s.string().to_owned()); + } + } + } + } + } else { + self.export(v); + } + } + } + PsbValue::List(list) => { + let list = list.values(); + if list.len() > 1 { + if let PsbValue::String(s) = &list[0] { + if s.string() == "comumode" { + for i in 1..list.len() { + if let PsbValue::String(s) = &list[i - 1] { + if s.string() == "text" { + if let PsbValue::String(text) = &list[i] { + self.messages.insert(text.string().to_owned()); + } + } + } + } + return; + } + } + } + for item in list { + self.export(item); + } + } + _ => {} + } + } +} diff --git a/src/types.rs b/src/types.rs index e98ef69..1a359bf 100644 --- a/src/types.rs +++ b/src/types.rs @@ -209,6 +209,8 @@ pub struct ExtraConfig { pub cat_system_image_canvas: bool, #[cfg(feature = "kirikiri")] pub kirikiri_language_index: Option, + #[cfg(feature = "kirikiri")] + pub kirikiri_export_comumode: bool, } #[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]