msg_tool\scripts\escude/
script.rs

1//! Escu:de Script File (.bin)
2use super::list::{EnumScr, EscudeBinList, ListData, NameT, VarT};
3use super::ops::base::CustomOps;
4use crate::ext::io::*;
5use crate::scripts::base::*;
6use crate::types::*;
7use crate::utils::encoding::{decode_to_string, encode_string};
8use crate::utils::struct_pack::StructPack;
9use anyhow::Result;
10use clap::ValueEnum;
11use int_enum::IntEnum;
12use std::collections::{BTreeSet, HashMap};
13use std::ffi::CString;
14use std::io::{Read, Seek, SeekFrom};
15use unicode_segmentation::UnicodeSegmentation;
16
17#[derive(Debug, ValueEnum, Clone, Copy)]
18/// Game title
19pub enum EscudeOp {
20    /// パニカルコンフュージョン
21    Panicon,
22    /// 花嫁と魔王 ~王室のハーレムは下克上~
23    Hanaou,
24}
25
26#[derive(Debug)]
27/// Builder for Escu:de binary script files
28pub struct EscudeBinScriptBuilder {}
29
30impl EscudeBinScriptBuilder {
31    /// Creates a new instance of `EscudeBinScriptBuilder`
32    pub const fn new() -> Self {
33        EscudeBinScriptBuilder {}
34    }
35}
36
37impl ScriptBuilder for EscudeBinScriptBuilder {
38    fn default_encoding(&self) -> Encoding {
39        Encoding::Cp932
40    }
41
42    fn build_script(
43        &self,
44        data: Vec<u8>,
45        _filename: &str,
46        encoding: Encoding,
47        _archive_encoding: Encoding,
48        config: &ExtraConfig,
49        _archive: Option<&Box<dyn Script>>,
50    ) -> Result<Box<dyn Script>> {
51        Ok(Box::new(EscudeBinScript::new(data, encoding, config)?))
52    }
53
54    fn extensions(&self) -> &'static [&'static str] {
55        &["bin"]
56    }
57
58    fn script_type(&self) -> &'static ScriptType {
59        &ScriptType::Escude
60    }
61
62    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
63        if buf_len > 8 && buf.starts_with(b"ESCR1_00") {
64            return Some(255);
65        }
66        None
67    }
68}
69
70#[derive(Debug)]
71/// Escu:de binary script file
72pub struct EscudeBinScript {
73    vms: Vec<u8>,
74    unk1: u32,
75    strings: Vec<String>,
76    names: Option<HashMap<usize, String>>,
77}
78
79fn load_enum_script(
80    filename: &str,
81    encoding: Encoding,
82    config: &ExtraConfig,
83) -> Result<(Vec<NameT>, Vec<VarT>)> {
84    let buf = crate::utils::files::read_file(filename)?;
85    let scr = EscudeBinList::new(buf, filename, encoding, config)?;
86    let mut names = None;
87    let mut vars = None;
88    for scr in scr.entries {
89        match scr.data {
90            ListData::Scr(scr) => match scr {
91                EnumScr::Names(name) => {
92                    names = Some(name);
93                }
94                EnumScr::Vars(var) => {
95                    vars = Some(var);
96                }
97                _ => {}
98            },
99            _ => {}
100        }
101    }
102    Ok((
103        names.ok_or_else(|| anyhow::anyhow!("No names data in enum script"))?,
104        vars.ok_or_else(|| anyhow::anyhow!("No vars data in enum script"))?,
105    ))
106}
107
108// fn check_messages_in_vms<'a, T: TryInto<usize> + Copy + 'a, I>(messages: &[String], mes: I)
109// where
110//     I: Iterator<Item = &'a T>,
111// {
112//     let mess = mes.filter_map(|m| (*m).try_into().ok()).collect::<HashSet<usize>>();
113//     for (i, str) in messages.iter().enumerate() {
114//         if str.is_empty() {
115//             continue;
116//         }
117//         if !mess.contains(&i) {
118//             eprintln!("WARN: Message at index {i} not referenced in VMs: {}", str);
119//             crate::COUNTER.inc_warning();
120//         }
121//     }
122// }
123
124impl EscudeBinScript {
125    /// Creates a new `EscudeBinScript`
126    ///
127    /// * `data` - The reader to read the data from
128    /// * `encoding` - The encoding of the script
129    /// * `config` - Extra configuration options
130    pub fn new(data: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
131        let mut reader = MemReader::new(data);
132        let mut magic = [0u8; 8];
133        reader.read_exact(&mut magic)?;
134        if &magic != b"ESCR1_00" {
135            return Err(anyhow::anyhow!(
136                "Invalid Escude binary script magic: {:?}",
137                magic
138            ));
139        }
140        let string_count = reader.read_u32()?;
141        let mut offsets = Vec::with_capacity(string_count as usize);
142        for _ in 0..string_count {
143            offsets.push(reader.read_u32()?);
144        }
145        let vm_count = reader.read_u32()?;
146        let mut vms = Vec::with_capacity(vm_count as usize);
147        vms.resize(vm_count as usize, 0);
148        reader.read_exact(&mut vms)?;
149        let unk1 = reader.read_u32()?;
150        let mut strings = Vec::with_capacity(string_count as usize);
151        if encoding.is_jis() {
152            let replaces = StrReplacer::new()?;
153            for _ in 0..string_count {
154                let s = reader.read_cstring()?;
155                let s = replaces.replace(s.as_bytes())?;
156                strings.push(decode_to_string(encoding, &s, true)?);
157            }
158        } else {
159            for _ in 0..string_count {
160                let s = reader.read_cstring()?;
161                strings.push(decode_to_string(encoding, s.as_bytes(), true)?);
162            }
163        }
164        let names = match &config.escude_enum_scr {
165            Some(loc) => match load_enum_script(loc, encoding, config) {
166                Ok((list, vars)) => match config.escude_op {
167                    Some(EscudeOp::Panicon) => {
168                        let mut names = HashMap::new();
169                        let mut vm = VM::new(&vms);
170                        for var in vars {
171                            vm.vars.insert(var.value as i32, var.flag as i32);
172                        }
173                        let _ = vm.run(Some(Box::new(super::ops::panicon::PaniconOps::new())));
174                        for (index, name) in vm.names.iter() {
175                            if let Some(name) = list.get(*name as usize) {
176                                names.insert(*index as usize, name.text.clone());
177                            }
178                        }
179                        // check_messages_in_vms(&strings, vm.mess.iter());
180                        Some(names)
181                    }
182                    Some(EscudeOp::Hanaou) => {
183                        let mut names = HashMap::new();
184                        let mut vm = VM::new(&vms);
185                        for var in vars {
186                            vm.vars.insert(var.value as i32, var.flag as i32);
187                        }
188                        let _ = vm.run(Some(Box::new(super::ops::hanaou::HanaouOps::new())));
189                        for (index, name) in vm.names.iter() {
190                            if let Some(name) = list.get(*name as usize) {
191                                names.insert(*index as usize, name.text.clone());
192                            }
193                        }
194                        // check_messages_in_vms(&strings, vm.mess.iter());
195                        Some(names)
196                    }
197                    None => {
198                        let mut names = HashMap::new();
199                        let mut vm = VM::new(&vms);
200                        for var in vars {
201                            vm.vars.insert(var.value as i32, var.flag as i32);
202                        }
203                        let _ = vm.run(None);
204                        for (index, name) in vm.names.iter() {
205                            if let Some(name) = list.get(*name as usize) {
206                                names.insert(*index as usize, name.text.clone());
207                            }
208                        }
209                        // check_messages_in_vms(&strings, vm.mess.iter());
210                        Some(names)
211                    }
212                },
213                Err(e) => {
214                    eprintln!(
215                        "WARN: Failed to load Escude enum script from {}: {}",
216                        loc, e
217                    );
218                    crate::COUNTER.inc_warning();
219                    None
220                }
221            },
222            None => None,
223        };
224        Ok(EscudeBinScript {
225            vms,
226            unk1,
227            strings,
228            names,
229        })
230    }
231}
232
233impl Script for EscudeBinScript {
234    fn default_output_script_type(&self) -> OutputScriptType {
235        OutputScriptType::Json
236    }
237
238    fn default_format_type(&self) -> FormatOptions {
239        FormatOptions::None
240    }
241
242    fn extract_messages(&self) -> Result<Vec<Message>> {
243        Ok(self
244            .strings
245            .iter()
246            .enumerate()
247            .map(|(i, s)| Message {
248                message: s.replace("<r>", "\n"),
249                name: self.names.as_ref().map(|n| n.get(&i).cloned()).flatten(),
250            })
251            .collect())
252    }
253
254    fn import_messages<'a>(
255        &'a self,
256        messages: Vec<Message>,
257        mut writer: Box<dyn WriteSeek + 'a>,
258        _filename: &str,
259        encoding: Encoding,
260        replacement: Option<&'a ReplacementTable>,
261    ) -> Result<()> {
262        writer.write_all(b"ESCR1_00")?;
263        let mut offsets = Vec::with_capacity(messages.len());
264        let mut strs = Vec::with_capacity(messages.len());
265        let mut len = 0;
266        for message in messages {
267            offsets.push(len);
268            let mut s = message.message.replace("\n", "<r>");
269            if let Some(repl) = replacement {
270                for (from, to) in &repl.map {
271                    s = s.replace(from, to);
272                }
273            }
274            let encoded = encode_string(encoding, &s, false)?;
275            len += encoded.len() as u32 + 1;
276            strs.push(CString::new(encoded)?);
277        }
278        writer.write_u32(offsets.len() as u32)?;
279        offsets.pack(&mut writer, false, encoding, &None)?;
280        writer.write_u32(self.vms.len() as u32)?;
281        writer.write_all(&self.vms)?;
282        writer.write_u32(self.unk1)?;
283        for s in strs {
284            writer.write_all(s.as_bytes_with_nul())?;
285        }
286        Ok(())
287    }
288
289    fn is_archive(&self) -> bool {
290        false
291    }
292}
293
294struct StrReplacer {
295    pub replacements: HashMap<Vec<u8>, Vec<u8>>,
296}
297
298enum JisStr {
299    Single(u8),
300    Double(u8, u8),
301}
302
303impl StrReplacer {
304    pub fn new() -> Result<Self> {
305        let mut s = StrReplacer {
306            replacements: HashMap::new(),
307        };
308        // 0xa0 to 0xde: Half-width katakana in CP932
309        let half_width_katakana = "!? 。「」、…をぁぃぅぇぉゃゅょっーあいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわん゛゜";
310        let mut bytes: Vec<u8> = (0xa0..=0xde).collect();
311        bytes.insert(0, 0x21);
312        bytes.insert(1, 0x3f);
313        s.add(&bytes, half_width_katakana)?;
314        Ok(s)
315    }
316
317    fn add(&mut self, from: &[u8], to: &str) -> Result<()> {
318        let encoding = Encoding::Cp932; // Default encoding, can be changed as needed
319        let tos = UnicodeSegmentation::graphemes(to, true);
320        for (from, to) in from.into_iter().zip(tos) {
321            let from_bytes = vec![from.clone()];
322            let to_bytes = encode_string(encoding, to, true)?;
323            self.replacements.insert(from_bytes, to_bytes);
324        }
325        Ok(())
326    }
327
328    pub fn replace(&self, input: &[u8]) -> Result<Vec<u8>> {
329        let mut result = Vec::new();
330        let mut reader = MemReaderRef::new(input);
331        while let Ok(byte) = reader.read_u8() {
332            if byte < 0x80 || (byte >= 0xa0 && byte <= 0xdf) {
333                result.push(JisStr::Single(byte));
334            } else if (byte >= 0x81 && byte <= 0x9f) || (byte >= 0xe0 && byte <= 0xef) {
335                let next_byte = reader.read_u8()?;
336                if next_byte < 0x40 || next_byte > 0xfc {
337                    return Err(anyhow::anyhow!("Invalid JIS encoding sequence"));
338                }
339                result.push(JisStr::Double(byte, next_byte));
340            } else {
341                return Err(anyhow::anyhow!("Invalid byte in JIS encoding: {}", byte));
342            }
343        }
344        let mut output = Vec::new();
345        for item in result {
346            match item {
347                JisStr::Single(byte) => {
348                    let vec = vec![byte];
349                    if let Some(replacement) = self.replacements.get(&vec) {
350                        output.extend_from_slice(replacement);
351                    } else {
352                        output.push(byte);
353                    }
354                }
355                JisStr::Double(byte1, byte2) => {
356                    let key = vec![byte1, byte2];
357                    if let Some(replacement) = self.replacements.get(&key) {
358                        output.extend_from_slice(replacement);
359                    } else {
360                        output.push(byte1);
361                        output.push(byte2);
362                    }
363                }
364            }
365        }
366        Ok(output)
367    }
368}
369
370#[repr(u8)]
371#[derive(Debug, IntEnum)]
372enum BaseOp {
373    End,
374    Jump,
375    JumpZ,
376    Call,
377    Ret,
378    Push,
379    Pop,
380    Str,
381    SetVar,
382    GetVar,
383    SetFlag,
384    GetFlag,
385    Neg,
386    Add,
387    Sub,
388    Mul,
389    Div,
390    Mod,
391    Not,
392    And,
393    Or,
394    Xor,
395    Shr,
396    Shl,
397    Eq,
398    Ne,
399    Gt,
400    Ge,
401    Lt,
402    Le,
403    LNot,
404    LAnd,
405    LOr,
406    FileLine,
407}
408
409pub(crate) trait ReadParam<T> {
410    fn read_param(&mut self) -> Result<T>;
411}
412
413#[derive(Debug)]
414pub(crate) struct VM<'a, T: std::fmt::Debug> {
415    pub reader: MemReaderRef<'a>,
416    pub data: Vec<T>,
417    pub stack: Vec<u64>,
418    pub strs: Vec<T>,
419    pub vars: HashMap<T, T>,
420    pub flags: HashMap<T, bool>,
421    pub mess: BTreeSet<T>,
422    pub names: HashMap<T, T>,
423}
424
425impl ReadParam<i32> for MemReaderRef<'_> {
426    fn read_param(&mut self) -> Result<i32> {
427        Ok(self.read_i32()?)
428    }
429}
430
431impl<'a, T> VM<'a, T>
432where
433    MemReaderRef<'a>: ReadParam<T>,
434    T: TryInto<u64>
435        + Default
436        + Eq
437        + Ord
438        + Copy
439        + std::fmt::Debug
440        + std::fmt::Display
441        + std::hash::Hash
442        + From<u8>
443        + std::ops::Neg<Output = T>
444        + std::ops::Add<Output = T>
445        + std::ops::Sub<Output = T>
446        + std::ops::Mul<Output = T>
447        + std::ops::Div<Output = T>
448        + std::ops::Rem<Output = T>
449        + std::ops::Not<Output = T>
450        + std::ops::BitAnd<Output = T>
451        + std::ops::BitOr<Output = T>
452        + std::ops::BitXor<Output = T>
453        + std::ops::Shr<Output = T>
454        + std::ops::Shl<Output = T>,
455    anyhow::Error: From<<T as TryInto<u64>>::Error>,
456{
457    pub fn new(data: &'a [u8]) -> Self {
458        VM {
459            reader: MemReaderRef::new(data),
460            data: Vec::new(),
461            stack: Vec::new(),
462            strs: Vec::new(),
463            vars: HashMap::new(),
464            flags: HashMap::new(),
465            mess: BTreeSet::new(),
466            names: HashMap::new(),
467        }
468    }
469
470    pub fn pop_data(&mut self) -> Result<T> {
471        self.data
472            .pop()
473            .ok_or_else(|| anyhow::anyhow!("No data to pop"))
474    }
475
476    fn pop_stack(&mut self) -> Result<u64> {
477        self.stack
478            .pop()
479            .ok_or_else(|| anyhow::anyhow!("No stack to pop"))
480    }
481
482    pub fn run(&mut self, mut custom_ops: Option<Box<dyn CustomOps<T>>>) -> Result<()> {
483        loop {
484            if self.reader.is_eof() {
485                break;
486            }
487            let op = self.reader.read_u8()?;
488            if let Ok(op) = BaseOp::try_from(op) {
489                // println!("Op code: {op:?}");
490                match op {
491                    BaseOp::End => break,
492                    BaseOp::Jump => {
493                        let offset: T = self.reader.read_param()?;
494                        let offset: u64 = offset.try_into()?;
495                        self.reader.seek(SeekFrom::Start(offset))?;
496                    }
497                    BaseOp::JumpZ => {
498                        let offset: T = self.reader.read_param()?;
499                        let offset: u64 = offset.try_into()?;
500                        if self.pop_data()? == Default::default() {
501                            self.reader.seek(SeekFrom::Start(offset))?;
502                        }
503                    }
504                    BaseOp::Call => {
505                        let offset: T = self.reader.read_param()?;
506                        let offset: u64 = offset.try_into()?;
507                        let pos = self.reader.stream_position()?;
508                        self.stack.push(pos);
509                        self.reader.seek(SeekFrom::Start(offset))?;
510                    }
511                    BaseOp::Ret => {
512                        if self.stack.is_empty() {
513                            let code = self.reader.read_u8()?;
514                            if code == 0 && self.reader.is_eof() {
515                                break;
516                            }
517                        }
518                        let stack = self.pop_stack()?;
519                        self.reader.seek(SeekFrom::Start(stack))?;
520                    }
521                    BaseOp::Push => {
522                        let d = self.reader.read_param()?;
523                        self.data.push(d);
524                    }
525                    BaseOp::Pop => {
526                        self.pop_data()?;
527                    }
528                    BaseOp::Str => {
529                        let param = self.reader.read_param()?;
530                        self.strs.push(param);
531                        self.data.push(param);
532                    }
533                    BaseOp::SetVar => {
534                        let value = self.pop_data()?;
535                        let index = self.pop_data()?;
536                        self.vars.insert(index, value);
537                        self.data.push(value);
538                    }
539                    BaseOp::GetVar => {
540                        let index = self.pop_data()?;
541                        let value = self
542                            .vars
543                            .get(&index)
544                            .ok_or_else(|| anyhow::anyhow!("Variable not found: {}", index))?;
545                        self.data.push(*value);
546                    }
547                    BaseOp::SetFlag => {
548                        let value = self.pop_data()?;
549                        let index = self.pop_data()?;
550                        let flag = value != Default::default();
551                        self.flags.insert(index, flag);
552                        self.data.push(value);
553                    }
554                    BaseOp::GetFlag => {
555                        let index = self.pop_data()?;
556                        let flag = self.flags.get(&index).cloned().unwrap_or(false);
557                        self.data
558                            .push(if flag { T::from(1u8) } else { T::from(0u8) });
559                    }
560                    BaseOp::Neg => {
561                        let value = -self.pop_data()?;
562                        self.data.push(value);
563                    }
564                    BaseOp::Add => {
565                        let b = self.pop_data()?;
566                        let a = self.pop_data()?;
567                        self.data.push(a + b);
568                    }
569                    BaseOp::Sub => {
570                        let b = self.pop_data()?;
571                        let a = self.pop_data()?;
572                        self.data.push(a - b);
573                    }
574                    BaseOp::Mul => {
575                        let b = self.pop_data()?;
576                        let a = self.pop_data()?;
577                        self.data.push(a * b);
578                    }
579                    BaseOp::Div => {
580                        let b = self.pop_data()?;
581                        if b == Default::default() {
582                            return Err(anyhow::anyhow!("Division by zero"));
583                        }
584                        let a = self.pop_data()?;
585                        self.data.push(a / b);
586                    }
587                    BaseOp::Mod => {
588                        let b = self.pop_data()?;
589                        if b == Default::default() {
590                            return Err(anyhow::anyhow!("Division by zero"));
591                        }
592                        let a = self.pop_data()?;
593                        self.data.push(a % b);
594                    }
595                    BaseOp::Not => {
596                        let value = self.pop_data()?;
597                        self.data.push(!value);
598                    }
599                    BaseOp::And => {
600                        let b = self.pop_data()?;
601                        let a = self.pop_data()?;
602                        self.data.push(a & b);
603                    }
604                    BaseOp::Or => {
605                        let b = self.pop_data()?;
606                        let a = self.pop_data()?;
607                        self.data.push(a | b);
608                    }
609                    BaseOp::Xor => {
610                        let b = self.pop_data()?;
611                        let a = self.pop_data()?;
612                        self.data.push(a ^ b);
613                    }
614                    BaseOp::Shr => {
615                        let b = self.pop_data()?;
616                        let a = self.pop_data()?;
617                        self.data.push(a >> b);
618                    }
619                    BaseOp::Shl => {
620                        let b = self.pop_data()?;
621                        let a = self.pop_data()?;
622                        self.data.push(a << b);
623                    }
624                    BaseOp::Eq => {
625                        let b = self.pop_data()?;
626                        let a = self.pop_data()?;
627                        self.data
628                            .push(if a == b { T::from(1u8) } else { T::from(0u8) });
629                    }
630                    BaseOp::Ne => {
631                        let b = self.pop_data()?;
632                        let a = self.pop_data()?;
633                        self.data
634                            .push(if a != b { T::from(1u8) } else { T::from(0u8) });
635                    }
636                    // Original code may contains undefined behavior for these operations
637                    BaseOp::Gt => {
638                        let a = self.pop_data()?;
639                        let b = self.pop_data()?;
640                        self.data
641                            .push(if a > b { T::from(1u8) } else { T::from(0u8) });
642                    }
643                    BaseOp::Ge => {
644                        let a = self.pop_data()?;
645                        let b = self.pop_data()?;
646                        self.data
647                            .push(if a >= b { T::from(1u8) } else { T::from(0u8) });
648                    }
649                    BaseOp::Lt => {
650                        let a = self.pop_data()?;
651                        let b = self.pop_data()?;
652                        self.data
653                            .push(if a < b { T::from(1u8) } else { T::from(0u8) });
654                    }
655                    BaseOp::Le => {
656                        let a = self.pop_data()?;
657                        let b = self.pop_data()?;
658                        self.data
659                            .push(if a <= b { T::from(1u8) } else { T::from(0u8) });
660                    }
661                    BaseOp::LNot => {
662                        let value = self.pop_data()?;
663                        self.data.push(if value == Default::default() {
664                            T::from(1u8)
665                        } else {
666                            T::from(0u8)
667                        });
668                    }
669                    BaseOp::LAnd => {
670                        let b = self.pop_data()? != Default::default();
671                        let a = self.pop_data()? != Default::default();
672                        self.data
673                            .push(if a && b { T::from(1u8) } else { T::from(0u8) });
674                    }
675                    BaseOp::LOr => {
676                        let b = self.pop_data()? != Default::default();
677                        let a = self.pop_data()? != Default::default();
678                        self.data
679                            .push(if a || b { T::from(1u8) } else { T::from(0u8) });
680                    }
681                    BaseOp::FileLine => {
682                        let _: T = self.reader.read_param()?;
683                    }
684                }
685                continue;
686            }
687            if let Some(ops) = &mut custom_ops {
688                let nbreak = ops.run(self, op)?;
689                if nbreak {
690                    break;
691                }
692            } else {
693                return Err(anyhow::anyhow!("Unknown operation: {}", op));
694            }
695        }
696        Ok(())
697    }
698
699    pub fn skip_n_params(&mut self, n: u64, nbreak: bool) -> Result<bool> {
700        if (self.data.len() as u64) < n {
701            println!("{:?}", self.data);
702        }
703        for _ in 0..n {
704            self.pop_data()?;
705        }
706        Ok(nbreak)
707    }
708
709    pub fn skip_params(&mut self, nbreak: bool) -> Result<bool> {
710        let count: T = self.reader.read_param()?;
711        let count: u64 = count.try_into()?;
712        self.skip_n_params(count, nbreak)
713    }
714
715    pub fn read_params(&mut self, ncount: Option<u64>) -> Result<Vec<T>> {
716        let count = match ncount {
717            Some(count) => count,
718            None => {
719                let count: T = self.reader.read_param()?;
720                count.try_into()?
721            }
722        };
723        let data_len = self.data.len();
724        if (data_len as u64) < count {
725            return Err(anyhow::anyhow!(
726                "Not enough data to read {} parameters, only {} parameters available",
727                count,
728                data_len
729            ));
730        }
731        let mut params = Vec::with_capacity(count as usize);
732        params.resize(count as usize, Default::default());
733        params.copy_from_slice(&self.data[data_len - count as usize..]);
734        self.data.truncate(data_len - count as usize);
735        Ok(params)
736    }
737}