msg_tool\scripts\entis_gls\csx\v1/
disasm.rs

1use super::types::*;
2use crate::ext::io::*;
3use crate::types::*;
4use crate::utils::struct_pack::*;
5use anyhow::Result;
6use std::io::{Seek, Write};
7
8use CSCompareType::*;
9use CSInstructionCode::*;
10use CSObjectMode::*;
11use CSOperatorType::*;
12use CSUnaryOperatorType::*;
13use CSVariableType::*;
14
15fn escape_string(s: &str) -> String {
16    s.replace("\r", "\\r")
17        .replace("\n", "\\n")
18        .replace("\t", "\\t")
19}
20
21pub struct ECSExecutionImageDisassembler<'a> {
22    pub stream: MemReaderRef<'a>,
23    conststr: Option<&'a TaggedRefAddressList>,
24    pub assembly: ECSExecutionImageAssembly,
25    writer: Option<Box<dyn Write + 'a>>,
26    addr: u32,
27    code: CSInstructionCode,
28}
29
30impl<'a> ECSExecutionImageDisassembler<'a> {
31    pub fn new(
32        stream: MemReaderRef<'a>,
33        conststr: Option<&'a TaggedRefAddressList>,
34        writer: Option<Box<dyn Write + 'a>>,
35    ) -> Self {
36        Self {
37            stream,
38            conststr,
39            assembly: ECSExecutionImageAssembly {
40                command_list: Vec::new(),
41            },
42            writer,
43            addr: 0,
44            code: CsicNew,
45        }
46    }
47
48    pub fn execute(&mut self) -> Result<()> {
49        let len = self.stream.data.len();
50        while self.stream.pos < len {
51            self.addr = self.stream.pos as u32;
52            let code_value = self.stream.read_u8()?;
53            self.code = CSInstructionCode::try_from(code_value).map_err(|_| {
54                anyhow::anyhow!(
55                    "Invalid CSInstructionCode value: {} at {:08x}",
56                    code_value,
57                    self.addr
58                )
59            })?;
60            match self.code {
61                CsicNew => {
62                    self.command_new()?;
63                }
64                CsicFree => {
65                    self.command_free()?;
66                }
67                CsicLoad => {
68                    self.command_load()?;
69                }
70                CsicStore => {
71                    self.command_store()?;
72                }
73                CsicEnter => {
74                    self.command_enter()?;
75                }
76                CsicLeave => {
77                    self.command_leave()?;
78                }
79                CsicJump => {
80                    self.command_jump()?;
81                }
82                CsicCJump => {
83                    self.command_cjump()?;
84                }
85                CsicCall => {
86                    self.command_call()?;
87                }
88                CsicReturn => {
89                    self.command_return()?;
90                }
91                CsicElement => {
92                    self.command_element()?;
93                }
94                CsicElementIndirect => {
95                    self.command_element_indirect()?;
96                }
97                CsicOperate => {
98                    self.command_operate()?;
99                }
100                CsicUniOperate => {
101                    self.command_uni_operate()?;
102                }
103                CsicCompare => {
104                    self.command_compare()?;
105                }
106            }
107            let size = self.stream.pos as u32 - self.addr;
108            self.assembly
109                .command_list
110                .push(ECSExecutionImageCommandRecord {
111                    code: self.code,
112                    addr: self.addr,
113                    size,
114                    new_addr: self.addr,
115                });
116        }
117        Ok(())
118    }
119
120    fn line<S: AsRef<str> + ?Sized>(&mut self, line: &S) -> anyhow::Result<()> {
121        if let Some(writer) = &mut self.writer {
122            writeln!(writer, "{:08x} {}", self.addr, line.as_ref())?;
123        }
124        Ok(())
125    }
126
127    fn get_string_literal2(&mut self) -> Result<(Option<usize>, String)> {
128        let length = self.stream.read_u32()?;
129        if length != 0x80000000 {
130            self.stream.seek_relative(-4)?;
131            let s = WideString::unpack(&mut self.stream, false, Encoding::Utf16LE, &None)?.0;
132            Ok((None, s))
133        } else if let Some(conststr) = &self.conststr {
134            let index = self.stream.read_u32()? as usize;
135            match conststr.get(index) {
136                Some(s) => Ok((Some(index), s.tag.0.clone())),
137                None => Err(anyhow::anyhow!(
138                    "Invalid string literal index: {} (max {})",
139                    index,
140                    conststr.len()
141                )),
142            }
143        } else {
144            Err(anyhow::anyhow!(
145                "No constant string table for string literal index"
146            ))
147        }
148    }
149
150    pub fn get_string_literal(&mut self) -> Result<String> {
151        let (_, s) = self.get_string_literal2()?;
152        Ok(s)
153    }
154
155    fn read_csct(&mut self) -> Result<CSCompareType> {
156        let value = self.stream.read_u8()?;
157        CSCompareType::try_from(value).map_err(|_| {
158            anyhow::anyhow!(
159                "Invalid CSCompareType value: {} at {:08x}",
160                value,
161                self.addr
162            )
163        })
164    }
165
166    pub fn read_csom(&mut self) -> Result<CSObjectMode> {
167        let value = self.stream.read_u8()?;
168        CSObjectMode::try_from(value).map_err(|_| {
169            anyhow::anyhow!("Invalid CSObjectMode value: {} at {:08x}", value, self.addr)
170        })
171    }
172
173    fn read_csot(&mut self) -> Result<CSOperatorType> {
174        let value = self.stream.read_u8()?;
175        CSOperatorType::try_from(value).map_err(|_| {
176            anyhow::anyhow!(
177                "Invalid CSOperatorType value: {} at {:08x}",
178                value,
179                self.addr
180            )
181        })
182    }
183
184    fn read_csuot(&mut self) -> Result<CSUnaryOperatorType> {
185        let value = self.stream.read_u8()?;
186        CSUnaryOperatorType::try_from(value).map_err(|_| {
187            anyhow::anyhow!(
188                "Invalid CSUnaryOperatorType value: {} at {:08x}",
189                value,
190                self.addr
191            )
192        })
193    }
194
195    pub fn read_csvt(&mut self) -> Result<CSVariableType> {
196        let value = self.stream.read_u8()?;
197        CSVariableType::try_from(value).map_err(|_| {
198            anyhow::anyhow!(
199                "Invalid CSVariableType value: {} at {:08x}",
200                value,
201                self.addr
202            )
203        })
204    }
205
206    fn command_new(&mut self) -> Result<()> {
207        let csom = self.read_csom()?;
208        let typ = self.read_csvt()?;
209        let class_name = if typ == CsvtObject {
210            Some(self.get_string_literal()?)
211        } else {
212            None
213        };
214        let name = self.get_string_literal()?;
215        let pobj = match csom {
216            CsomStack => "stack",
217            CsomThis => "this",
218            _ => {
219                return Err(anyhow::anyhow!(
220                    "Invalid CSObjectMode for 'new' instruction: {:?} at {:08x}",
221                    csom,
222                    self.addr
223                ));
224            }
225        };
226        match &class_name {
227            Some(class_name) => {
228                self.line(&format!("New {pobj} \"{class_name}\" \"{name}\""))?;
229            }
230            None => {
231                self.line(&format!("New {pobj} \"{name}\""))?;
232            }
233        }
234        Ok(())
235    }
236
237    fn command_free(&mut self) -> Result<()> {
238        self.line("Free")?;
239        Ok(())
240    }
241
242    fn command_load(&mut self) -> Result<()> {
243        let csom = self.read_csom()?;
244        let csvt = self.read_csvt()?;
245        if csom == CsomImmediate {
246            match csvt {
247                CsvtObject => {
248                    let class_name = self.get_string_literal()?;
249                    self.line(&format!("Load * {class_name}"))?;
250                }
251                CsvtReference => {
252                    self.line("Load * ECSReference")?;
253                }
254                CsvtArray => {
255                    self.line("Load * ECSArray")?;
256                }
257                CsvtHash => {
258                    self.line("Load * ECSHash")?;
259                }
260                CsvtInteger => {
261                    let val = self.stream.read_u32()?;
262                    self.line(&format!("Load Integer {}", val))?;
263                }
264                CsvtReal => {
265                    let val = self.stream.read_f64()?;
266                    self.line(&format!("Load Real {}", val))?;
267                }
268                CsvtString => {
269                    let t = self.get_string_literal2()?;
270                    let escaped = escape_string(&t.1);
271                    if let Some(index) = t.0 {
272                        self.line(&format!("Load Const String {index} \"{escaped}\""))?;
273                    } else {
274                        self.line(&format!("Load String \"{escaped}\""))?;
275                    }
276                }
277                CsvtInteger64 => {
278                    let val = self.stream.read_u64()?;
279                    self.line(&format!("Load Integer64 {}", val))?;
280                }
281                CsvtPointer => {
282                    let value = self.stream.read_u32()?;
283                    self.line(&format!("Load Pointer {}", value))?;
284                }
285            }
286        } else {
287            let pobj = match csom {
288                CsomStack => "stack",
289                CsomThis => "this",
290                CsomGlobal => "global",
291                CsomData => "data",
292                CsomAuto => "auto",
293                _ => {
294                    return Err(anyhow::anyhow!(
295                        "Invalid CSObjectMode for 'load' instruction: {:?} at {:08x}",
296                        csom,
297                        self.addr
298                    ));
299                }
300            };
301            match csvt {
302                CsvtReference => {
303                    self.line(&format!("Load {pobj}"))?;
304                }
305                CsvtInteger => {
306                    let index = self.stream.read_i32()?;
307                    self.line(&format!("Load {pobj} [{index}]"))?;
308                }
309                CsvtString => {
310                    let name = self.get_string_literal()?;
311                    self.line(&format!("Load {pobj} [\"{name}\"]"))?;
312                }
313                _ => {
314                    return Err(anyhow::anyhow!(
315                        "Invalid CSVariableType for 'load' instruction: {:?} at {:08x}",
316                        csvt,
317                        self.addr
318                    ));
319                }
320            }
321        }
322        Ok(())
323    }
324
325    fn command_store(&mut self) -> Result<()> {
326        let csot = self.read_csot()?;
327        match csot {
328            CsotNop => {
329                self.line("Store")?;
330            }
331            CsotAdd => {
332                self.line("Store Add")?;
333            }
334            CsotSub => {
335                self.line("Store Sub")?;
336            }
337            CsotMul => {
338                self.line("Store Mul")?;
339            }
340            CsotDiv => {
341                self.line("Store Div")?;
342            }
343            CsotMod => {
344                self.line("Store Mod")?;
345            }
346            CsotAnd => {
347                self.line("Store And")?;
348            }
349            CsotOr => {
350                self.line("Store Or")?;
351            }
352            CsotXor => {
353                self.line("Store Xor")?;
354            }
355            CsotLogicalAnd => {
356                self.line("Store LAnd")?;
357            }
358            CsotLogicalOr => {
359                self.line("Store LOr")?;
360            }
361        }
362        Ok(())
363    }
364
365    fn command_enter(&mut self) -> Result<()> {
366        let name = self.get_string_literal()?;
367        let num_args = self.stream.read_i32()?;
368        if num_args != -1 {
369            let mut sb = String::new();
370            sb.push('(');
371            for i in 0..num_args {
372                let csvt = self.read_csvt()?;
373                let class_name = if csvt == CsvtObject {
374                    Some(self.get_string_literal()?)
375                } else {
376                    None
377                };
378                let var_name = self.get_string_literal()?;
379                if let Some(cname) = class_name {
380                    sb.push_str(&format!("{{{}:{}}}", cname, var_name));
381                } else {
382                    sb.push_str(&var_name);
383                }
384                if i < num_args - 1 {
385                    sb.push_str(", ");
386                }
387            }
388            sb.push(')');
389            self.line(&format!("Enter \"{}\" {}", name, sb))?;
390        } else {
391            let flag = self.stream.read_u8()?;
392            if flag != 0 {
393                return Err(anyhow::anyhow!(
394                    "Invalid flag for variable argument 'enter' instruction: {} at {:08x}",
395                    flag,
396                    self.addr
397                ));
398            }
399            let catch_addr = self.stream.read_i32()? as i64 + self.stream.pos as i64;
400            self.line(&format!("Enter \"{}\" Try-Catch {:08x}", name, catch_addr))?;
401        }
402        Ok(())
403    }
404
405    fn command_leave(&mut self) -> Result<()> {
406        self.line("Leave")?;
407        Ok(())
408    }
409
410    fn command_jump(&mut self) -> Result<()> {
411        let target_addr = self.stream.read_i32()? as i64 + self.stream.pos as i64;
412        self.line(&format!("Jump {:08x}", target_addr))?;
413        Ok(())
414    }
415
416    fn command_cjump(&mut self) -> Result<()> {
417        let cond = self.stream.read_u8()?;
418        let target_addr = self.stream.read_i32()? as i64 + self.stream.pos as i64;
419        self.line(&format!("CJump {} {:08x}", cond, target_addr))?;
420        Ok(())
421    }
422
423    fn command_call(&mut self) -> Result<()> {
424        let csom = self.read_csom()?;
425        let num_args = self.stream.read_i32()?;
426        let func_name = self.get_string_literal()?;
427        let pobj = match csom {
428            CsomImmediate if func_name == "@CATCH" => "",
429            CsomThis => "this",
430            CsomGlobal => "global",
431            CsomAuto => "auto",
432            _ => {
433                return Err(anyhow::anyhow!(
434                    "Invalid CSObjectMode for 'call' instruction: {:?} at {:08x}",
435                    csom,
436                    self.addr
437                ));
438            }
439        };
440        self.line(&format!("Call {} \"{}\" <{}>", pobj, func_name, num_args))?;
441        Ok(())
442    }
443
444    fn command_return(&mut self) -> Result<()> {
445        let free_stack = self.stream.read_u8()?;
446        self.line(&format!("Return {}", free_stack))?;
447        Ok(())
448    }
449
450    fn command_element(&mut self) -> Result<()> {
451        let csvt = self.read_csvt()?;
452        match csvt {
453            CsvtInteger => {
454                let index = self.stream.read_i32()?;
455                self.line(&format!("Element {}", index))?;
456            }
457            CsvtString => {
458                let name = self.get_string_literal()?;
459                self.line(&format!("Element \"{}\"", name))?;
460            }
461            _ => {
462                return Err(anyhow::anyhow!(
463                    "Invalid CSVariableType for 'element' instruction: {:?} at {:08x}",
464                    csvt,
465                    self.addr
466                ));
467            }
468        }
469        Ok(())
470    }
471
472    fn command_element_indirect(&mut self) -> Result<()> {
473        self.line("ElementIndirect")?;
474        Ok(())
475    }
476
477    fn command_operate(&mut self) -> Result<()> {
478        let csot = self.read_csot()?;
479        match csot {
480            CsotNop => {
481                self.line("Operate Nop")?;
482            }
483            CsotAdd => {
484                self.line("Operate Add")?;
485            }
486            CsotSub => {
487                self.line("Operate Sub")?;
488            }
489            CsotMul => {
490                self.line("Operate Mul")?;
491            }
492            CsotDiv => {
493                self.line("Operate Div")?;
494            }
495            CsotMod => {
496                self.line("Operate Mod")?;
497            }
498            CsotAnd => {
499                self.line("Operate And")?;
500            }
501            CsotOr => {
502                self.line("Operate Or")?;
503            }
504            CsotXor => {
505                self.line("Operate Xor")?;
506            }
507            CsotLogicalAnd => {
508                self.line("Operate LAnd")?;
509            }
510            CsotLogicalOr => {
511                self.line("Operate LOr")?;
512            }
513        }
514        Ok(())
515    }
516
517    fn command_uni_operate(&mut self) -> Result<()> {
518        let csuot = self.read_csuot()?;
519        match csuot {
520            CsuotPlus => {
521                self.line("UnaryOperate Plus")?;
522            }
523            CsuotNegate => {
524                self.line("UnaryOperate Negate")?;
525            }
526            CsuotBitnot => {
527                self.line("UnaryOperate BitNot")?;
528            }
529            CsuotLogicalNot => {
530                self.line("UnaryOperate LNot")?;
531            }
532        }
533        Ok(())
534    }
535
536    fn command_compare(&mut self) -> Result<()> {
537        let csct = self.read_csct()?;
538        match csct {
539            CsctEqual => {
540                self.line("Compare Equal")?;
541            }
542            CsctNotEqual => {
543                self.line("Compare NotEqual")?;
544            }
545            CsctLessThan => {
546                self.line("Compare LessThan")?;
547            }
548            CsctLessEqual => {
549                self.line("Compare LessEqual")?;
550            }
551            CsctGreaterThan => {
552                self.line("Compare GreaterThan")?;
553            }
554            CsctGreaterEqual => {
555                self.line("Compare GreaterEqual")?;
556            }
557        }
558        Ok(())
559    }
560}