From 04fef8f2a3ea3b09cfe13c6de4c5b74cd3aa16b5 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 13 May 2026 21:25:21 +0800 Subject: [PATCH] Add support for Yu-Ris YSCFG(config) file (.ybn) --- README.md | 3 +- src/scripts/mod.rs | 2 + src/scripts/yuris/mod.rs | 1 + src/scripts/yuris/yscfg.rs | 144 +++++++++++++++++++++++++++++++++++++ src/types.rs | 3 + 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/scripts/yuris/yscfg.rs diff --git a/README.md b/README.md index a741bcd..6811ae7 100644 --- a/README.md +++ b/README.md @@ -267,4 +267,5 @@ msg-tool create -t | Script Type | Feature Name | Name | Export | Import | Export Multiple | Import Multiple | Custom Export | Custom Import | Create | Remarks | |---|---|---|---|---|---|---|---|---|---|---| | `yuris-yscm` | `yuris` | Yu-Ris YSCM(opcodes metadata) file (.ybn) | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ | | -| `yuris-yser` | `yuris` | Yu-Ris YSER(error message) file (.ybn) | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ | | +| `yuris-yser` | `yuris` | Yu-Ris YSER(error message) file (.ybn) | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ | | +| `yuris-yscfg` | `yuris` | Yu-Ris YSCFG(config) file (.ybn) | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ | | diff --git a/src/scripts/mod.rs b/src/scripts/mod.rs index 4803c4d..8c76a30 100644 --- a/src/scripts/mod.rs +++ b/src/scripts/mod.rs @@ -180,6 +180,8 @@ lazy_static::lazy_static! { Box::new(yuris::yscm::YSCMBuilder::new()), #[cfg(feature = "yuris")] Box::new(yuris::yser::YSERBuilder::new()), + #[cfg(feature = "yuris")] + Box::new(yuris::yscfg::YSCFGBuilder::new()), ]; /// A list of all script extensions. pub static ref ALL_EXTS: Vec = diff --git a/src/scripts/yuris/mod.rs b/src/scripts/yuris/mod.rs index c582208..0edb732 100644 --- a/src/scripts/yuris/mod.rs +++ b/src/scripts/yuris/mod.rs @@ -1,4 +1,5 @@ //! Yu-Ris Engine Scripts mod types; +pub mod yscfg; pub mod yscm; pub mod yser; diff --git a/src/scripts/yuris/yscfg.rs b/src/scripts/yuris/yscfg.rs new file mode 100644 index 0000000..ecb3eda --- /dev/null +++ b/src/scripts/yuris/yscfg.rs @@ -0,0 +1,144 @@ +//! Yu-Ris YSCFG files +use crate::ext::io::*; +use crate::scripts::base::*; +use crate::types::*; +use crate::utils::encoding::*; +use crate::utils::struct_pack::*; +use anyhow::Result; +use msg_tool_macro::*; +use serde::{Deserialize, Serialize}; +use std::io::{Read, Seek, Write}; + +#[derive(Debug)] +pub struct YSCFGBuilder {} + +impl YSCFGBuilder { + /// Creates a new instance of `YSERBuilder` + pub const fn new() -> Self { + YSCFGBuilder {} + } +} + +impl ScriptBuilder for YSCFGBuilder { + fn default_encoding(&self) -> Encoding { + Encoding::Cp932 + } + + fn build_script( + &self, + buf: Vec, + _filename: &str, + encoding: Encoding, + _archive_encoding: Encoding, + config: &ExtraConfig, + _archive: Option<&Box>, + ) -> Result> { + Ok(Box::new(YSCFG::new(MemReader::new(buf), encoding, config)?)) + } + + fn extensions(&self) -> &'static [&'static str] { + &["ybn"] + } + + fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option { + if buf_len >= 4 && buf.starts_with(b"YSCF") { + return Some(20); + } + None + } + + fn script_type(&self) -> &'static ScriptType { + &ScriptType::YurisYSCFG + } +} + +#[derive(Debug, StructPack, StructUnpack, Deserialize, Serialize)] +struct YSCFGData { + engine: u32, + unk0: u32, + compile: u32, + screen_width: u32, + screen_height: u32, + enable: u32, + image_type_slots: [u8; 8], + sound_type_slots: [u8; 4], + thread: u32, + debug_mode: u32, + sound: u32, + window_resize: u32, + window_frame: u32, + file_priority_dev: u32, + file_priority_debug: u32, + file_priority_release: u32, + unk1: u32, + // #TODO: Better version handle + #[skip_pack_if(self.engine < 500)] + #[skip_unpack_if(engine < 500)] + unk2: u32, + #[skip_pack_if(self.engine < 500)] + #[skip_unpack_if(engine < 500)] + unk3: u32, + #[skip_pack_if(self.engine < 500)] + #[skip_unpack_if(engine < 500)] + unk4: u32, + #[pstring(u16)] + caption: String, +} + +#[derive(Debug)] +pub struct YSCFG { + data: YSCFGData, + custom_yaml: bool, +} + +impl YSCFG { + pub fn new( + mut reader: T, + encoding: Encoding, + config: &ExtraConfig, + ) -> Result { + let mut sig = [0; 4]; + reader.read_exact(&mut sig)?; + if &sig != b"YSCF" { + anyhow::bail!("Unsupported YSCFG file."); + } + let data = YSCFGData::unpack(&mut reader, false, encoding, &None)?; + Ok(Self { + data, + custom_yaml: config.custom_yaml, + }) + } +} + +impl Script for YSCFG { + fn default_output_script_type(&self) -> OutputScriptType { + OutputScriptType::Custom + } + + fn is_output_supported(&self, output: OutputScriptType) -> bool { + matches!(output, OutputScriptType::Custom) + } + + fn default_format_type(&self) -> FormatOptions { + FormatOptions::None + } + + fn custom_output_extension(&self) -> &'static str { + if self.custom_yaml { "yaml" } else { "json" } + } + + fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> { + let s = if self.custom_yaml { + serde_yaml_ng::to_string(&self.data) + .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))? + } else { + serde_json::to_string_pretty(&self.data) + .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))? + }; + let mut writer = crate::utils::files::write_file(filename)?; + let s = encode_string(encoding, &s, false)?; + writer.write_all(&s)?; + writer.flush()?; + Ok(()) + } +} diff --git a/src/types.rs b/src/types.rs index 0093f90..4429e90 100644 --- a/src/types.rs +++ b/src/types.rs @@ -914,6 +914,9 @@ pub enum ScriptType { #[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)]