msg_tool\scripts\yuris/
yscm.rs1use super::types::*;
3use crate::ext::io::*;
4use crate::scripts::base::*;
5use crate::types::*;
6use crate::utils::encoding::*;
7use crate::utils::struct_pack::*;
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10use std::io::{Read, Seek};
11
12#[derive(Debug)]
13pub struct YSCMBuilder {}
14
15impl YSCMBuilder {
16 pub const fn new() -> Self {
18 YSCMBuilder {}
19 }
20}
21
22impl ScriptBuilder for YSCMBuilder {
23 fn default_encoding(&self) -> Encoding {
24 Encoding::Cp932
25 }
26
27 fn build_script(
28 &self,
29 buf: Vec<u8>,
30 _filename: &str,
31 encoding: Encoding,
32 _archive_encoding: Encoding,
33 config: &ExtraConfig,
34 _archive: Option<&Box<dyn Script>>,
35 ) -> Result<Box<dyn Script + Send + Sync>> {
36 Ok(Box::new(YSCM::new(MemReader::new(buf), encoding, config)?))
37 }
38
39 fn extensions(&self) -> &'static [&'static str] {
40 &["ybn"]
41 }
42
43 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
44 if buf_len >= 4 && buf.starts_with(b"YSCM") {
45 return Some(20);
46 }
47 None
48 }
49
50 fn script_type(&self) -> &'static ScriptType {
51 &ScriptType::YurisYSCM
52 }
53}
54
55#[derive(Debug, Deserialize, Serialize)]
56pub(crate) struct YSCMData {
57 pub engine: u32,
58 pub opcode_length: u32,
59 pub unk: u32,
60 pub opcodes: Vec<CodeMeta>,
61 pub errmsgs: Vec<String>,
62 pub unk_tbl: Vec<u8>,
63}
64
65impl StructUnpack for YSCMData {
66 fn unpack<R: Read + Seek>(
67 reader: &mut R,
68 big: bool,
69 encoding: Encoding,
70 info: &Option<Box<dyn std::any::Any>>,
71 ) -> Result<Self> {
72 let engine = u32::unpack(reader, big, encoding, info)?;
73 let opcode_length = u32::unpack(reader, big, encoding, info)?;
74 let unk = u32::unpack(reader, big, encoding, info)?;
75 let opcodes = reader.read_struct_vec(opcode_length as usize, big, encoding, info)?;
76 let target_len = reader.stream_length()? - 0x100;
77 let mut errmsgs = Vec::new();
78 while reader.stream_position()? < target_len {
79 let s = reader.read_cstring()?;
80 errmsgs.push(decode_to_string(encoding, s.as_bytes(), true)?);
81 }
82 let unk_tbl = reader.read_exact_vec(0x100)?;
83 Ok(Self {
84 engine,
85 opcode_length,
86 unk,
87 opcodes,
88 errmsgs,
89 unk_tbl,
90 })
91 }
92}
93
94#[derive(Debug)]
95pub struct YSCM {
96 pub(crate) data: YSCMData,
97 custom_yaml: bool,
98}
99
100impl YSCM {
101 pub fn new<T: Read + Seek>(
102 mut reader: T,
103 encoding: Encoding,
104 config: &ExtraConfig,
105 ) -> Result<Self> {
106 let mut sig = [0; 4];
107 reader.read_exact(&mut sig)?;
108 if &sig != b"YSCM" {
109 anyhow::bail!("Unsupported YSCM file.");
110 }
111 let data = YSCMData::unpack(&mut reader, false, encoding, &None)?;
112 Ok(Self {
113 data,
114 custom_yaml: config.custom_yaml,
115 })
116 }
117}
118
119impl Script for YSCM {
120 fn default_output_script_type(&self) -> OutputScriptType {
121 OutputScriptType::Custom
122 }
123
124 fn is_output_supported(&self, output: OutputScriptType) -> bool {
125 matches!(output, OutputScriptType::Custom)
126 }
127
128 fn default_format_type(&self) -> FormatOptions {
129 FormatOptions::None
130 }
131
132 fn custom_output_extension(&self) -> &'static str {
133 if self.custom_yaml { "yaml" } else { "json" }
134 }
135
136 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
137 let s = if self.custom_yaml {
138 serde_yaml_ng::to_string(&self.data)
139 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
140 } else {
141 serde_json::to_string_pretty(&self.data)
142 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
143 };
144 let mut writer = crate::utils::files::write_file(filename)?;
145 let s = encode_string(encoding, &s, false)?;
146 writer.write_all(&s)?;
147 writer.flush()?;
148 Ok(())
149 }
150}