From 20e51a681340f3f2b1b415b7d178c50602bf83ea Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 3 Sep 2025 14:51:14 +0800 Subject: [PATCH] Fix patched scn script may broken --- README.md | 2 +- src/ext/psb.rs | 30 ++- src/scripts/kirikiri/scn.rs | 410 ++++++++++++++++++------------------ 3 files changed, 230 insertions(+), 212 deletions(-) diff --git a/README.md b/README.md index c048a40..a93282d 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ msg-tool create -t | Script Type | Feature Name | Name | Export | Import | Custom Export | Custom Import | Create | Remarks | |---|---|---|---|---|---|---|---|---| | `kirikiri`/`kr`/`kr-ks`/`kirikiri-ks` | `kirikiri` | Kirikiri Script File (.ks) | ✔️ | ✔️ | ❌ | ❌ | ❌ | | -| `kirikiri-scn`/`kr-scn` | `kirikiri` | Kirikiri Scene File (.scn) | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | Patched script may be broken | +| `kirikiri-scn`/`kr-scn` | `kirikiri` | Kirikiri Scene File (.scn) | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | | | `kirikiri-simple-crypt`/`kr-simple-crypt` | `kirikiri` | Kirikiri Simple Crypt Text File | ❌ | ❌ | ✔️ | ❌ | ❌ | | | `kirikiri-mdf`/`kr-mdf` | `kirikiri` | Kirikiri Zlib-Compressed File | ❌ | ❌ | ✔️ | ❌ | ❌ | | | `kirikiri-tjs-ns0`/`kr-tjs-ns0` | `kirikiri` | Kirikiri TJS NS0 binary encoded script | ❌ | ❌ | ✔️ | ❌ | ❌ | | diff --git a/src/ext/psb.rs b/src/ext/psb.rs index a0581a5..5343500 100644 --- a/src/ext/psb.rs +++ b/src/ext/psb.rs @@ -57,16 +57,22 @@ pub enum PsbValueFixed { impl PsbValueFixed { /// Converts this value to original PSB value type. - pub fn to_psb(self) -> PsbValue { + pub fn to_psb(self, warn_on_none: bool) -> PsbValue { match self { - PsbValueFixed::None => PsbValue::None, + PsbValueFixed::None => { + if warn_on_none { + eprintln!("Warning: PSB value is None, output script may broken."); + crate::COUNTER.inc_warning(); + } + PsbValue::None + } PsbValueFixed::Null => PsbValue::Null, PsbValueFixed::Bool(b) => PsbValue::Bool(b), PsbValueFixed::Number(n) => PsbValue::Number(n), PsbValueFixed::IntArray(arr) => PsbValue::IntArray(arr), PsbValueFixed::String(s) => PsbValue::String(s), - PsbValueFixed::List(l) => PsbValue::List(l.to_psb()), - PsbValueFixed::Object(o) => PsbValue::Object(o.to_psb()), + PsbValueFixed::List(l) => PsbValue::List(l.to_psb(warn_on_none)), + PsbValueFixed::Object(o) => PsbValue::Object(o.to_psb(warn_on_none)), PsbValueFixed::Resource(r) => PsbValue::Resource(r), PsbValueFixed::ExtraResource(er) => PsbValue::ExtraResource(er), PsbValueFixed::CompilerNumber => PsbValue::CompilerNumber, @@ -460,8 +466,12 @@ pub struct PsbListFixed { impl PsbListFixed { /// Converts this PSB list to a original PSB list. - pub fn to_psb(self) -> PsbList { - let v: Vec<_> = self.values.into_iter().map(|v| v.to_psb()).collect(); + pub fn to_psb(self, warn_on_none: bool) -> PsbList { + let v: Vec<_> = self + .values + .into_iter() + .map(|v| v.to_psb(warn_on_none)) + .collect(); PsbList::from(v) } @@ -615,10 +625,10 @@ pub struct PsbObjectFixed { impl PsbObjectFixed { /// Creates a new empty PSB object. - pub fn to_psb(self) -> PsbObject { + pub fn to_psb(self, warn_on_none: bool) -> PsbObject { let mut hash_map = HashMap::new(); for (key, value) in self.values { - hash_map.insert(key, value.to_psb()); + hash_map.insert(key, value.to_psb(warn_on_none)); } PsbObject::from(hash_map) } @@ -871,9 +881,9 @@ impl VirtualPsbFixed { } /// Converts this fixed PSB to a virtual PSB. - pub fn to_psb(self) -> VirtualPsb { + pub fn to_psb(self, warn_on_none: bool) -> VirtualPsb { let (header, resources, extra, root) = self.unwrap(); - VirtualPsb::new(header, resources, extra, root.to_psb()) + VirtualPsb::new(header, resources, extra, root.to_psb(warn_on_none)) } /// Converts json object to a fixed PSB. diff --git a/src/scripts/kirikiri/scn.rs b/src/scripts/kirikiri/scn.rs index d578e92..9e2ff2a 100644 --- a/src/scripts/kirikiri/scn.rs +++ b/src/scripts/kirikiri/scn.rs @@ -369,208 +369,232 @@ impl Script for ScnScript { if !scene.is_object() { return Err(anyhow::anyhow!("scene at {} is not an object", i)); } - for (j, text) in scene["texts"].members_mut().enumerate() { - if text.is_list() { - if text.len() <= 1 { - continue; // Skip if there are not enough values - } - if cur_mes.is_none() { - cur_mes = mes.next(); - } - if !text[0].is_string_or_null() { - return Err(anyhow::anyhow!("name is not a string or null")); - } - let has_name = text[0].is_string(); - let mut has_display_name; - if text[1].is_list() { - if text[1].is_string() { - let m = match cur_mes.take() { - Some(m) => m, - None => { - return Err(anyhow::anyhow!( - "No enough messages. (text {j} at scene {i})" - )); - } - }; - if has_name { - if let Some(name) = &m.name { - let mut name = name.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - name = name.replace(key, value); - } + if scene["texts"].is_list() { + for (j, text) in scene["texts"].members_mut().enumerate() { + if text.is_list() { + if text.len() <= 1 { + continue; // Skip if there are not enough values + } + if cur_mes.is_none() { + cur_mes = mes.next(); + } + if !text[0].is_string_or_null() { + return Err(anyhow::anyhow!("name is not a string or null")); + } + let has_name = text[0].is_string(); + let mut has_display_name; + if text[1].is_list() { + if text[1].is_string() { + let m = match cur_mes.take() { + Some(m) => m, + None => { + return Err(anyhow::anyhow!( + "No enough messages. (text {j} at scene {i})" + )); } - text[0].set_string(name); - } else { - return Err(anyhow::anyhow!( - "Name is missing for message. (text {j} at scene {i})" - )); - } - } - let mut message = m.message.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - message = message.replace(key, value); - } - } - text[1].set_string(message.replace("\n", "\\n")); - } else if text[1].is_list() { - if text[1].len() > self.language_index - && text[1][self.language_index].is_list() - && text[1][self.language_index].len() >= 2 - { - if !text[1][self.language_index][0].is_string_or_null() { - return Err(anyhow::anyhow!( - "display name is not a string or null" - )); - } - has_display_name = text[1][self.language_index][0].is_string(); - if text[1][self.language_index][1].is_string() { - let m = match cur_mes.take() { - Some(m) => m, - None => { - return Err(anyhow::anyhow!( - "No enough messages. (text {j} at scene {i})" - )); - } - }; - if has_name { - if let Some(name) = &m.name { - let mut name = name.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - name = name.replace(key, value); - } + }; + if has_name { + if let Some(name) = &m.name { + let mut name = name.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + name = name.replace(key, value); } - if has_display_name { - text[1][self.language_index][0].set_string(name); - } else { - text[0].set_string(name); - } - } else { - return Err(anyhow::anyhow!( - "Name is missing for message. (text {j} at scene {i})" - )); } - } - let mut message = m.message.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - message = message.replace(key, value); - } - } - text[1][self.language_index][1] - .set_string(message.replace("\n", "\\n")); - } - } - } - } else { - if text.len() <= 2 { - continue; // Skip if there is no message - } - if !text[1].is_string_or_null() { - return Err(anyhow::anyhow!("display name is not a string or null")); - } - has_display_name = text[1].is_string(); - if text[2].is_string() { - let m = match cur_mes.take() { - Some(m) => m, - None => { - return Err(anyhow::anyhow!( - "No enough messages.(text {j} at scene {i})" - )); - } - }; - if has_name { - if let Some(name) = &m.name { - let mut name = name.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - name = name.replace(key, value); - } - } - if has_display_name { - text[1].set_string(name); - } else { text[0].set_string(name); + } else { + return Err(anyhow::anyhow!( + "Name is missing for message. (text {j} at scene {i})" + )); } - } else { - return Err(anyhow::anyhow!( - "Name is missing for message.(text {j} at scene {i})" - )); } - } - let mut message = m.message.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - message = message.replace(key, value); + let mut message = m.message.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + message = message.replace(key, value); + } } - } - text[2].set_string(message.replace("\n", "\\n")); - } else if text[2].is_list() { - if text[2].len() > self.language_index - && text[2][self.language_index].is_list() - && text[2][self.language_index].len() >= 2 - { - if !text[2][self.language_index][0].is_string_or_null() { - return Err(anyhow::anyhow!( - "display name is not a string or null" - )); - } - has_display_name = text[2][self.language_index][0].is_string(); - if text[2][self.language_index][1].is_string() { - let m = match cur_mes.take() { - Some(m) => m, - None => { - return Err(anyhow::anyhow!( - "No enough messages.(text {j} at scene {i})" - )); - } - }; - if has_name { - if let Some(name) = &m.name { - let mut name = name.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - name = name.replace(key, value); + text[1].set_string(message.replace("\n", "\\n")); + } else if text[1].is_list() { + if text[1].len() > self.language_index + && text[1][self.language_index].is_list() + && text[1][self.language_index].len() >= 2 + { + if !text[1][self.language_index][0].is_string_or_null() { + return Err(anyhow::anyhow!( + "display name is not a string or null" + )); + } + has_display_name = text[1][self.language_index][0].is_string(); + if text[1][self.language_index][1].is_string() { + let m = match cur_mes.take() { + Some(m) => m, + None => { + return Err(anyhow::anyhow!( + "No enough messages. (text {j} at scene {i})" + )); + } + }; + if has_name { + if let Some(name) = &m.name { + let mut name = name.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + name = name.replace(key, value); + } + } + if has_display_name { + text[1][self.language_index][0] + .set_string(name); + } else { + text[0].set_string(name); } - } - if has_display_name { - text[2][self.language_index][0].set_string(name); } else { - text[0].set_string(name); + return Err(anyhow::anyhow!( + "Name is missing for message. (text {j} at scene {i})" + )); } + } + let mut message = m.message.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + message = message.replace(key, value); + } + } + text[1][self.language_index][1] + .set_string(message.replace("\n", "\\n")); + } + } + } + } else { + if text.len() <= 2 { + continue; // Skip if there is no message + } + if !text[1].is_string_or_null() { + return Err(anyhow::anyhow!( + "display name is not a string or null" + )); + } + has_display_name = text[1].is_string(); + if text[2].is_string() { + let m = match cur_mes.take() { + Some(m) => m, + None => { + return Err(anyhow::anyhow!( + "No enough messages.(text {j} at scene {i})" + )); + } + }; + if has_name { + if let Some(name) = &m.name { + let mut name = name.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + name = name.replace(key, value); + } + } + if has_display_name { + text[1].set_string(name); } else { - return Err(anyhow::anyhow!( - "Name is missing for message.(text {j} at scene {i})" - )); + text[0].set_string(name); } + } else { + return Err(anyhow::anyhow!( + "Name is missing for message.(text {j} at scene {i})" + )); } - let mut message = m.message.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - message = message.replace(key, value); + } + let mut message = m.message.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + message = message.replace(key, value); + } + } + text[2].set_string(message.replace("\n", "\\n")); + } else if text[2].is_list() { + if text[2].len() > self.language_index + && text[2][self.language_index].is_list() + && text[2][self.language_index].len() >= 2 + { + if !text[2][self.language_index][0].is_string_or_null() { + return Err(anyhow::anyhow!( + "display name is not a string or null" + )); + } + has_display_name = text[2][self.language_index][0].is_string(); + if text[2][self.language_index][1].is_string() { + let m = match cur_mes.take() { + Some(m) => m, + None => { + return Err(anyhow::anyhow!( + "No enough messages.(text {j} at scene {i})" + )); + } + }; + if has_name { + if let Some(name) = &m.name { + let mut name = name.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + name = name.replace(key, value); + } + } + if has_display_name { + text[2][self.language_index][0] + .set_string(name); + } else { + text[0].set_string(name); + } + } else { + return Err(anyhow::anyhow!( + "Name is missing for message.(text {j} at scene {i})" + )); + } } + let mut message = m.message.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + message = message.replace(key, value); + } + } + text[2][self.language_index][1] + .set_string(message.replace("\n", "\\n")); } - text[2][self.language_index][1] - .set_string(message.replace("\n", "\\n")); } } } } } } - for select in scene["selects"].members_mut() { - if select.is_object() { - if cur_mes.is_none() { - cur_mes = mes.next(); - } - if select["language"].is_list() - && select["language"].len() > self.language_index - && select["language"][self.language_index].is_object() - { - let lang_obj = &mut select["language"][self.language_index]; - if lang_obj["text"].is_string() { + if scene["selects"].is_list() { + for select in scene["selects"].members_mut() { + if select.is_object() { + if cur_mes.is_none() { + cur_mes = mes.next(); + } + if select["language"].is_list() + && select["language"].len() > self.language_index + && select["language"][self.language_index].is_object() + { + let lang_obj = &mut select["language"][self.language_index]; + if lang_obj["text"].is_string() { + let m = match cur_mes.take() { + Some(m) => m, + None => { + return Err(anyhow::anyhow!("No enough messages.")); + } + }; + let mut text = m.message.clone(); + if let Some(replacement) = replacement { + for (key, value) in replacement.map.iter() { + text = text.replace(key, value); + } + } + lang_obj["text"].set_string(text.replace("\n", "\\n")); + continue; + } + } + if select["text"].is_string() { let m = match cur_mes.take() { Some(m) => m, None => { @@ -583,25 +607,9 @@ impl Script for ScnScript { text = text.replace(key, value); } } - lang_obj["text"].set_string(text.replace("\n", "\\n")); - continue; + select["text"].set_string(text.replace("\n", "\\n")); } } - if select["text"].is_string() { - let m = match cur_mes.take() { - Some(m) => m, - None => { - return Err(anyhow::anyhow!("No enough messages.")); - } - }; - let mut text = m.message.clone(); - if let Some(replacement) = replacement { - for (key, value) in replacement.map.iter() { - text = text.replace(key, value); - } - } - select["text"].set_string(text.replace("\n", "\\n")); - } } } comu.as_ref().map(|c| c.import(scene)); @@ -609,7 +617,7 @@ impl Script for ScnScript { if cur_mes.is_some() || mes.next().is_some() { return Err(anyhow::anyhow!("Some messages were not processed.")); } - let psb = psb.to_psb(); + let psb = psb.to_psb(true); let writer = PsbWriter::new(psb, file); writer.finish().map_err(|e| { anyhow::anyhow!("Failed to write PSB to file {}: {:?}", self.filename, e) @@ -644,12 +652,12 @@ impl Script for ScnScript { .map_err(|e| anyhow::anyhow!("Failed to deserialize YAML: {}", e))?; let mut psb = self.psb.clone(); psb.set_data(data); - psb.to_psb() + psb.to_psb(true) } else { let json = json::parse(&s)?; let mut psb = self.psb.clone(); psb.from_json(&json)?; - psb.to_psb() + psb.to_psb(true) }; let writer = PsbWriter::new(psb, file); writer.finish().map_err(|e| {