msg_tool\scripts\circus/
script.rs

1//! Circus Script File (.mes)
2use super::info::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::{decode_to_string, encode_string};
6use anyhow::Result;
7
8#[derive(Debug)]
9/// Circus MES Script Builder
10pub struct CircusMesScriptBuilder {}
11
12impl CircusMesScriptBuilder {
13    /// Creates a new instance of `CircusMesScriptBuilder`.
14    pub const fn new() -> Self {
15        CircusMesScriptBuilder {}
16    }
17}
18
19impl ScriptBuilder for CircusMesScriptBuilder {
20    fn default_encoding(&self) -> Encoding {
21        Encoding::Cp932
22    }
23
24    fn build_script(
25        &self,
26        buf: Vec<u8>,
27        _filename: &str,
28        encoding: Encoding,
29        _archive_encoding: Encoding,
30        config: &ExtraConfig,
31        _archive: Option<&Box<dyn Script>>,
32    ) -> Result<Box<dyn Script>> {
33        Ok(Box::new(CircusMesScript::new(buf, encoding, config)?))
34    }
35
36    fn extensions(&self) -> &'static [&'static str] {
37        &["mes"]
38    }
39
40    fn script_type(&self) -> &'static ScriptType {
41        &ScriptType::Circus
42    }
43}
44
45#[derive(Debug)]
46struct Token {
47    offset: usize,
48    length: usize,
49    value: u8,
50}
51
52/// Circus MES Script
53pub struct CircusMesScript {
54    data: Vec<u8>,
55    encoding: Encoding,
56    is_new_ver: bool,
57    version: u16,
58    info: &'static ScriptInfo,
59    asm_bin_offset: usize,
60    blocks_offset: usize,
61    tokens: Vec<Token>,
62}
63
64impl CircusMesScript {
65    /// Creates a new `CircusMesScript` from the given data and configuration.
66    ///
67    /// * `data` - The data to read the MES script from.
68    /// * `encoding` - The encoding to use for string fields.
69    /// * `config` - Extra configuration options.
70    pub fn new(data: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
71        let head0 = i32::from_le_bytes(data[0..4].try_into()?);
72        let head1 = i32::from_le_bytes(data[4..8].try_into()?);
73        let mut is_new_ver = false;
74        let mut version = 0;
75        let mut info = config
76            .circus_mes_type
77            .as_ref()
78            .and_then(|name| ScriptInfo::query(name.as_ref()));
79        let mut asm_bin_offset = 0;
80        let mut blocks_offset = 0;
81        if head1 == 0x3 {
82            let offset = head0 * 0x6 + 0x4;
83            if data.len() > offset as usize {
84                if data.len() > offset as usize + 3 {
85                    version =
86                        u16::from_le_bytes(data[offset as usize..offset as usize + 2].try_into()?);
87                    if info.is_none() {
88                        info = ScriptInfo::query_by_version(version);
89                    }
90                    asm_bin_offset = offset as usize + 3;
91                }
92                blocks_offset = 8;
93            }
94            is_new_ver = true;
95        } else {
96            let offset = head0 * 0x4 + 0x4;
97            if data.len() > offset as usize {
98                if data.len() > offset as usize + 2 {
99                    version =
100                        u16::from_le_bytes(data[offset as usize..offset as usize + 2].try_into()?);
101                    if info.is_none() {
102                        info = ScriptInfo::query_by_version(version);
103                    }
104                    asm_bin_offset = offset as usize + 2;
105                }
106                blocks_offset = 4;
107            }
108        }
109        let info = info.ok_or(anyhow::anyhow!("Failed to detect version."))?;
110        let mut tokens = Vec::new();
111        let mut offset = 0;
112        let asm_bin_size = if asm_bin_offset == 0 {
113            0
114        } else {
115            data.len() - asm_bin_offset
116        };
117        while offset < asm_bin_size {
118            let value = data[asm_bin_offset + offset];
119            let length = if info.uint8x2.its(value) {
120                0x03
121            } else if info.uint8str.its(value) {
122                let mut len = 0x3;
123                let mut temp = data[asm_bin_offset + offset + len - 1];
124                while temp != 0x00 {
125                    len += 0x1;
126                    if asm_bin_offset + offset + len >= data.len() {
127                        break;
128                    }
129                    temp = data[asm_bin_offset + offset + len - 1];
130                }
131                len
132            } else if info.string.its(value) || info.encstr.its(value) {
133                let mut len = 1;
134                let mut temp = data[asm_bin_offset + offset + len - 1];
135                while temp != 0x00 {
136                    len += 0x1;
137                    if asm_bin_offset + offset + len >= data.len() {
138                        break;
139                    }
140                    temp = data[asm_bin_offset + offset + len - 1];
141                }
142                len
143            } else if info.uint16x4.its(value) {
144                0x09
145            } else {
146                return Err(anyhow::anyhow!(format!(
147                    "Unknown token type: 0x{:02X} at offset {}",
148                    value,
149                    asm_bin_offset + offset
150                )));
151            };
152            let token = Token {
153                offset,
154                length,
155                value,
156            };
157            offset += length;
158            tokens.push(token);
159        }
160        Ok(CircusMesScript {
161            data,
162            encoding,
163            is_new_ver,
164            version,
165            info,
166            asm_bin_offset,
167            blocks_offset,
168            tokens,
169        })
170    }
171}
172
173impl std::fmt::Debug for CircusMesScript {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        f.debug_struct("CircusMesScript")
176            .field("encoding", &self.encoding)
177            .field("is_new_ver", &self.is_new_ver)
178            .field("version", &self.version)
179            .field("info", &self.info)
180            .field("asm_bin_offset", &self.asm_bin_offset)
181            .field("blocks_offset", &self.blocks_offset)
182            .field("tokens", &self.tokens)
183            .finish_non_exhaustive()
184    }
185}
186
187impl Script for CircusMesScript {
188    fn default_output_script_type(&self) -> OutputScriptType {
189        OutputScriptType::Json
190    }
191
192    fn default_format_type(&self) -> FormatOptions {
193        FormatOptions::Fixed {
194            length: 32,
195            keep_original: false,
196        }
197    }
198
199    fn extract_messages(&self) -> Result<Vec<Message>> {
200        let mut mes = vec![];
201        let mut name = None;
202        for token in self.tokens.iter() {
203            let mut t = None;
204            if self.info.encstr.its(token.value) {
205                let mut text = self.data[self.asm_bin_offset + token.offset + 1
206                    ..self.asm_bin_offset + token.offset + token.length - 1]
207                    .to_vec();
208                for t in text.iter_mut() {
209                    *t = (*t).overflowing_add(self.info.deckey).0;
210                }
211                t = Some(decode_to_string(self.encoding, &text, true)?);
212                // println!("Token(enc): {:?}, {}", token, t.as_ref().unwrap());
213            } else if token.value == self.info.optunenc {
214                let text = &self.data[self.asm_bin_offset + token.offset + 1
215                    ..self.asm_bin_offset + token.offset + token.length - 1];
216                t = Some(decode_to_string(self.encoding, text, true)?);
217                // println!("Token: {:?}, {}", token, t.as_ref().unwrap());
218            }
219            match t {
220                Some(t) => {
221                    if token.value == self.info.nameopcode {
222                        name = Some(t);
223                    } else {
224                        let message = Message::new(t, name.take());
225                        mes.push(message);
226                    }
227                }
228                None => {}
229            }
230        }
231        Ok(mes)
232    }
233
234    fn import_messages<'a>(
235        &'a self,
236        messages: Vec<Message>,
237        mut writer: Box<dyn WriteSeek + 'a>,
238        _filename: &str,
239        encoding: Encoding,
240        replacement: Option<&'a ReplacementTable>,
241    ) -> Result<()> {
242        let mut repls = Vec::new();
243        if !encoding.is_jis() {
244            fn insert_repl(
245                repls: &mut Vec<(String, String)>,
246                s: &'static str,
247                encoding: Encoding,
248            ) -> Result<()> {
249                let jis = encode_string(Encoding::Cp932, s, true)?;
250                let out = decode_to_string(encoding, &jis, true)?;
251                repls.push((s.to_string(), out));
252                Ok(())
253            }
254            let _ = insert_repl(&mut repls, "{", encoding);
255            let _ = insert_repl(&mut repls, "/", encoding);
256            let _ = insert_repl(&mut repls, "}", encoding);
257            if repls.len() < 3 {
258                eprintln!(
259                    "Warning: Some replacements cannot used in current encoding. Ruby text may be broken."
260                );
261                crate::COUNTER.inc_warning();
262            }
263        }
264        match replacement {
265            Some(repl) => {
266                for (k, v) in repl.map.iter() {
267                    repls.push((k.to_string(), v.to_string()));
268                }
269            }
270            None => {}
271        }
272        let mut buffer = Vec::with_capacity(self.data.len());
273        buffer.extend_from_slice(&self.data[..self.asm_bin_offset]);
274        let mut nmes = Vec::with_capacity(messages.len());
275        for m in messages {
276            nmes.insert(0, m);
277        }
278        let mut mes = nmes.pop();
279        let mut block_count = 0;
280        for token in self.tokens.iter() {
281            if !self.is_new_ver {
282                let count = buffer.len() as u32;
283                let offset = count - self.asm_bin_offset as u32 + 2;
284                buffer[self.blocks_offset + block_count * 4
285                    ..self.blocks_offset + block_count * 4 + 4]
286                    .copy_from_slice(&offset.to_le_bytes());
287                block_count += 1;
288            }
289            if self.info.encstr.its(token.value) {
290                if mes.is_none() {
291                    mes = nmes.pop();
292                    if mes.is_none() {
293                        return Err(anyhow::anyhow!("No more messages to import"));
294                    }
295                }
296                let mut s = if token.value == self.info.nameopcode {
297                    match mes.as_mut().unwrap().name.take() {
298                        Some(s) => s,
299                        None => {
300                            let t = mes.as_ref().unwrap().message.clone();
301                            mes = None;
302                            t
303                        }
304                    }
305                } else {
306                    let t = mes.as_ref().unwrap().message.clone();
307                    mes = None;
308                    t
309                };
310                for i in repls.iter() {
311                    s = s.replace(i.0.as_str(), i.1.as_str());
312                }
313                let mut text = encode_string(encoding, &s, false)?;
314                if text.contains(&self.info.deckey) {
315                    eprintln!(
316                        "Warning: text contains deckey 0x{:02X}, text may be truncated: {}",
317                        self.info.deckey, s,
318                    );
319                    crate::COUNTER.inc_warning();
320                }
321                buffer.push(token.value);
322                for t in text.iter_mut() {
323                    *t = (*t).overflowing_sub(self.info.deckey).0;
324                }
325                buffer.extend_from_slice(&text);
326                buffer.push(0x00);
327                continue;
328            }
329            if token.value == self.info.optunenc {
330                if mes.is_none() {
331                    mes = nmes.pop();
332                    if mes.is_none() {
333                        return Err(anyhow::anyhow!("No more messages to import"));
334                    }
335                }
336                let mut s = if token.value == self.info.nameopcode {
337                    match mes.as_mut().unwrap().name.take() {
338                        Some(s) => s,
339                        None => {
340                            let t = mes.as_ref().unwrap().message.clone();
341                            mes = None;
342                            t
343                        }
344                    }
345                } else {
346                    let t = mes.as_ref().unwrap().message.clone();
347                    mes = None;
348                    t
349                };
350                for i in repls.iter() {
351                    s = s.replace(i.0.as_str(), i.1.as_str());
352                }
353                buffer.push(token.value);
354                let text = encode_string(encoding, &s, false)?;
355                buffer.extend_from_slice(&text);
356                buffer.push(0x00);
357                continue;
358            }
359            if self.is_new_ver && (token.value == 0x3 || token.value == 0x4) {
360                let count = buffer.len() as u32;
361                let offset = count - self.asm_bin_offset as u32 + token.length as u32;
362                let block = u32::from_le_bytes(
363                    buffer[self.blocks_offset + block_count * 4
364                        ..self.blocks_offset + block_count * 4 + 4]
365                        .try_into()?,
366                );
367                let block = (block & (0xFF << 0x18)) | offset;
368                buffer[self.blocks_offset + block_count * 4
369                    ..self.blocks_offset + block_count * 4 + 4]
370                    .copy_from_slice(&block.to_le_bytes());
371                block_count += 1;
372            }
373            let len = std::cmp::min(
374                self.asm_bin_offset + token.offset + token.length,
375                self.data.len(),
376            );
377            buffer.extend_from_slice(&self.data[self.asm_bin_offset + token.offset..len]);
378        }
379        writer.write_all(&buffer)?;
380        Ok(())
381    }
382}