1use 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)]
9pub struct CircusMesScriptBuilder {}
11
12impl CircusMesScriptBuilder {
13 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
52pub 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 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 } 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 }
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}