msg_tool\scripts\yuris/
ystb.rs

1//! Yu-Ris YSTB files
2use super::yscm::YSCMData;
3use super::yslb::{Label, YSLBData};
4use crate::ext::io::*;
5use crate::scripts::base::*;
6use crate::types::*;
7use crate::utils::encoding::*;
8use crate::utils::serde_base64bytes::*;
9use crate::utils::struct_pack::*;
10use crate::utils::xored_stream::*;
11use anyhow::Result;
12use msg_tool_macro::*;
13use serde::{Deserialize, Serialize};
14use std::any::Any;
15use std::collections::{BTreeMap, BTreeSet};
16use std::io::{Read, Seek, SeekFrom, Write};
17use std::ops::{Deref, DerefMut};
18
19#[derive(Clone, Debug, StructUnpack, StructPack, Deserialize, Serialize)]
20struct YSTBHeader {
21    version: u32,
22    #[serde(skip)]
23    inst_entry_count: u32,
24    #[serde(skip)]
25    inst_index_size: u32,
26    #[serde(skip)]
27    args_index_size: u32,
28    #[serde(skip)]
29    args_data_size: u32,
30    #[serde(skip)]
31    line_numbers_size: u32,
32    reserve0: u32,
33}
34
35#[derive(Clone, Debug, StructUnpack, StructPack)]
36struct YSTBHeaderV2 {
37    version: u32,
38    code_seg_size: u32,
39    args_seg_size: u32,
40    args_seg_offset: u32,
41    reserved0: u32,
42    reserved1: u32,
43    reserved2: u32,
44}
45
46#[derive(Deserialize, Serialize)]
47struct YSTBData {
48    #[serde(flatten)]
49    header: YSTBHeader,
50    insts: Vec<YSTBInst>,
51    line_numbers: Base64Bytes,
52}
53
54impl std::fmt::Debug for YSTBData {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        f.debug_struct("YSTBData")
57            .field("header", &self.header)
58            .field("insts", &self.insts)
59            .field("line_numbers", &hex::encode(&self.line_numbers.bytes))
60            .finish()
61    }
62}
63
64impl StructUnpack for YSTBData {
65    fn unpack<R: Read + Seek>(
66        reader: &mut R,
67        big: bool,
68        encoding: Encoding,
69        info: &Option<Box<dyn Any>>,
70    ) -> Result<Self> {
71        let header = YSTBHeader::unpack(reader, big, encoding, info)?;
72        let insts = reader.read_struct_vec::<YSTBInstBase>(
73            header.inst_entry_count as usize,
74            big,
75            encoding,
76            info,
77        )?;
78        let info = Box::new(header.clone()) as Box<dyn Any>;
79        let args = reader.read_struct_vec::<YSTBArg>(
80            (header.args_index_size / 0xC) as usize,
81            big,
82            encoding,
83            &Some(info),
84        )?;
85        let mut args = args.into_iter();
86        let insts = insts
87            .into_iter()
88            .map(|base| {
89                let args = args.by_ref().take(base.arg_count as usize).collect();
90                YSTBInst { base, args }
91            })
92            .collect();
93        let line_numbers = reader.peek_exact_at_vec(
94            0x20 + header.inst_index_size as u64
95                + header.args_index_size as u64
96                + header.args_data_size as u64,
97            header.line_numbers_size as usize,
98        )?;
99        Ok(Self {
100            header,
101            insts,
102            line_numbers: line_numbers.into(),
103        })
104    }
105}
106
107#[derive(Clone, Debug, StructUnpack, StructPack, Deserialize, Serialize)]
108struct YSTBInstBase {
109    opcode: u8,
110    #[serde(skip)]
111    arg_count: u8,
112    unk: u16,
113}
114
115#[derive(Deserialize, Serialize)]
116struct YSTBInst {
117    #[serde(flatten)]
118    base: YSTBInstBase,
119    args: Vec<YSTBArg>,
120}
121
122impl Deref for YSTBInst {
123    type Target = YSTBInstBase;
124    fn deref(&self) -> &Self::Target {
125        &self.base
126    }
127}
128
129impl DerefMut for YSTBInst {
130    fn deref_mut(&mut self) -> &mut Self::Target {
131        &mut self.base
132    }
133}
134
135impl std::fmt::Debug for YSTBInst {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        f.debug_struct("YSTBInst")
138            .field("opcode", &self.opcode)
139            .field("arg_count", &self.arg_count)
140            .field("unk", &self.unk)
141            .field("args", &self.args)
142            .finish()
143    }
144}
145
146#[derive(Clone, Debug, StructUnpack, StructPack, Deserialize, Serialize)]
147struct YSTBArgBase {
148    id: u16,
149    typ: u16,
150    #[serde(skip)]
151    size: u32,
152}
153
154struct YSTBArg {
155    base: YSTBArgBase,
156    data: Vec<u8>,
157    encoding: Encoding,
158}
159
160#[derive(Deserialize, Serialize)]
161#[serde(tag = "t")]
162enum YSTBArgDat {
163    Raw { data: Base64Bytes },
164    NotEqual,
165    Mod,
166    LogAnd,
167    PerformVarIndexAtion,
168    Mul,
169    Add,
170    Nop,
171    Sub,
172    Div,
173    Equal,
174    Less,
175    Greater,
176    BinAnd,
177    PushInt8 { value: i8 },
178    PushDouble { value: f64 },
179    PushScalarVarVar { index: u16 },
180    PushScalarVarStr { index: u16 },
181    PushInt32 { value: i32 },
182    PushInt64 { value: i64 },
183    MString { s: String },
184    BinOr,
185    ChangeSign,
186    Le,
187    PrepareVarIndexationVar { index: u16 },
188    PrepareVarIndexationStr { index: u16 },
189    PushInt16 { value: i16 },
190    Ge,
191    BinXor,
192    ToNumber,
193    ToString,
194    PushArrayVarVar { index: u16 },
195    PushArrayVarStr { index: u16 },
196    LogOr,
197    Array { data: Vec<YSTBArgDat> },
198    String { s: String },
199}
200
201impl YSTBArgDat {
202    fn to_data(self, encoding: Encoding) -> Result<Vec<u8>> {
203        Ok(match self {
204            YSTBArgDat::Raw { data } => data.bytes,
205            YSTBArgDat::NotEqual => NOTEQUAL_TYPE.into(),
206            YSTBArgDat::Mod => MOD_TYPE.into(),
207            YSTBArgDat::LogAnd => LOGAND_TYPE.into(),
208            YSTBArgDat::PerformVarIndexAtion => PERFORMVARINDEXATION_TYPE.into(),
209            YSTBArgDat::Mul => MUL_TYPE.into(),
210            YSTBArgDat::Add => ADD_TYPE.into(),
211            YSTBArgDat::Nop => NOP_TYPE.into(),
212            YSTBArgDat::Sub => SUB_TYPE.into(),
213            YSTBArgDat::Div => DIV_TYPE.into(),
214            YSTBArgDat::Equal => EQUAL_TYPE.into(),
215            YSTBArgDat::Less => LESS_TYPE.into(),
216            YSTBArgDat::Greater => GREATER_TYPE.into(),
217            YSTBArgDat::BinAnd => BINAND_TYPE.into(),
218            YSTBArgDat::PushInt8 { value } => {
219                let mut m = MemWriter::new();
220                m.write_u8(b'B')?;
221                m.write_u16(1)?;
222                m.write_i8(value)?;
223                m.into_inner()
224            }
225            YSTBArgDat::PushDouble { value } => {
226                let mut m = MemWriter::new();
227                m.write_u8(b'F')?;
228                m.write_u16(8)?;
229                m.write_f64(value)?;
230                m.into_inner()
231            }
232            YSTBArgDat::PushScalarVarVar { index } => {
233                let mut m = MemWriter::new();
234                m.write_u8(b'H')?;
235                m.write_u16(3)?;
236                m.write_u8(b'$')?;
237                m.write_u16(index)?;
238                m.into_inner()
239            }
240            YSTBArgDat::PushScalarVarStr { index } => {
241                let mut m = MemWriter::new();
242                m.write_u8(b'H')?;
243                m.write_u16(3)?;
244                m.write_u8(b'@')?;
245                m.write_u16(index)?;
246                m.into_inner()
247            }
248            YSTBArgDat::PushInt32 { value } => {
249                let mut m = MemWriter::new();
250                m.write_u8(b'I')?;
251                m.write_u16(4)?;
252                m.write_i32(value)?;
253                m.into_inner()
254            }
255            YSTBArgDat::PushInt64 { value } => {
256                let mut m = MemWriter::new();
257                m.write_u8(b'L')?;
258                m.write_u16(8)?;
259                m.write_i64(value)?;
260                m.into_inner()
261            }
262            YSTBArgDat::MString { s } => {
263                let mut m = MemWriter::new();
264                m.write_u8(b'M')?;
265                let d = encode_string(encoding, &s, true)?;
266                m.write_u16(d.len() as u16)?;
267                m.write_all(&d)?;
268                m.into_inner()
269            }
270            YSTBArgDat::BinOr => BINOR_TYPE.into(),
271            YSTBArgDat::ChangeSign => CHANGESIGN_TYPE.into(),
272            YSTBArgDat::Le => LE_TYPE.into(),
273            YSTBArgDat::PrepareVarIndexationVar { index } => {
274                let mut m = MemWriter::new();
275                m.write_u8(b'V')?;
276                m.write_u16(3)?;
277                m.write_u8(b'$')?;
278                m.write_u16(index)?;
279                m.into_inner()
280            }
281            YSTBArgDat::PrepareVarIndexationStr { index } => {
282                let mut m = MemWriter::new();
283                m.write_u8(b'V')?;
284                m.write_u16(3)?;
285                m.write_u8(b'@')?;
286                m.write_u16(index)?;
287                m.into_inner()
288            }
289            YSTBArgDat::PushInt16 { value } => {
290                let mut m = MemWriter::new();
291                m.write_u8(b'W')?;
292                m.write_u16(2)?;
293                m.write_i16(value)?;
294                m.into_inner()
295            }
296            YSTBArgDat::Ge => GE_TYPE.into(),
297            YSTBArgDat::BinXor => BINXOR_TYPE.into(),
298            YSTBArgDat::ToNumber => TONUMBER_TYPE.into(),
299            YSTBArgDat::ToString => TOSTRING_TYPE.into(),
300            YSTBArgDat::PushArrayVarVar { index } => {
301                let mut m = MemWriter::new();
302                m.write_u8(b'v')?;
303                m.write_u16(3)?;
304                m.write_u8(b'$')?;
305                m.write_u16(index)?;
306                m.into_inner()
307            }
308            YSTBArgDat::PushArrayVarStr { index } => {
309                let mut m = MemWriter::new();
310                m.write_u8(b'v')?;
311                m.write_u16(3)?;
312                m.write_u8(b'@')?;
313                m.write_u16(index)?;
314                m.into_inner()
315            }
316            YSTBArgDat::LogOr => LOGOR_TYPE.into(),
317            YSTBArgDat::Array { data } => {
318                let mut m = MemWriter::new();
319                for d in data {
320                    m.write_all(&d.to_data(encoding)?)?;
321                }
322                m.into_inner()
323            }
324            YSTBArgDat::String { s } => encode_string(encoding, &s, true)?,
325        })
326    }
327}
328
329impl TryFrom<YSTBArgTmp> for YSTBArg {
330    type Error = anyhow::Error;
331    fn try_from(value: YSTBArgTmp) -> Result<Self> {
332        let data = value.data.to_data(value.encoding)?;
333        Ok(Self {
334            base: value.base,
335            data,
336            encoding: value.encoding,
337        })
338    }
339}
340
341impl<'a> TryFrom<&'a YSTBArg> for YSTBArgTmp {
342    type Error = anyhow::Error;
343    fn try_from(value: &'a YSTBArg) -> Result<Self> {
344        let mut list = Vec::new();
345        let mut data = value.data.as_slice();
346        loop {
347            if data.is_empty() {
348                break;
349            }
350            if data.starts_with(NOTEQUAL_TYPE) {
351                list.push(YSTBArgDat::NotEqual);
352                data = &data[3..];
353            } else if data.starts_with(MOD_TYPE) {
354                list.push(YSTBArgDat::Mod);
355                data = &data[3..];
356            } else if data.starts_with(LOGAND_TYPE) {
357                list.push(YSTBArgDat::LogAnd);
358                data = &data[3..];
359            } else if data.starts_with(PERFORMVARINDEXATION_TYPE) {
360                list.push(YSTBArgDat::PerformVarIndexAtion);
361                data = &data[4..];
362            } else if data.starts_with(MUL_TYPE) {
363                list.push(YSTBArgDat::Mul);
364                data = &data[3..];
365            } else if data.starts_with(ADD_TYPE) {
366                list.push(YSTBArgDat::Add);
367                data = &data[3..];
368            } else if data.starts_with(NOP_TYPE) {
369                list.push(YSTBArgDat::Nop);
370                data = &data[3..];
371            } else if data.starts_with(SUB_TYPE) {
372                list.push(YSTBArgDat::Sub);
373                data = &data[3..];
374            } else if data.starts_with(DIV_TYPE) {
375                list.push(YSTBArgDat::Div);
376                data = &data[3..];
377            } else if data.starts_with(EQUAL_TYPE) {
378                list.push(YSTBArgDat::Equal);
379                data = &data[3..];
380            } else if data.starts_with(LESS_TYPE) {
381                list.push(YSTBArgDat::Less);
382                data = &data[3..];
383            } else if data.starts_with(GREATER_TYPE) {
384                list.push(YSTBArgDat::Greater);
385                data = &data[3..];
386            } else if data.starts_with(BINAND_TYPE) {
387                list.push(YSTBArgDat::BinAnd);
388                data = &data[3..];
389            } else if data.starts_with(PUSHINT8_TYPE) {
390                if data.len() < 4 {
391                    list.push(YSTBArgDat::Raw {
392                        data: data.to_vec().into(),
393                    });
394                    break;
395                }
396                list.push(YSTBArgDat::PushInt8 {
397                    value: i8::from_le_bytes([data[3]]),
398                });
399                data = &data[4..];
400            } else if data.starts_with(PUSHDOUBLE_TYPE) {
401                if data.len() < 11 {
402                    list.push(YSTBArgDat::Raw {
403                        data: data.to_vec().into(),
404                    });
405                    break;
406                }
407                list.push(YSTBArgDat::PushDouble {
408                    value: f64::from_le_bytes([
409                        data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10],
410                    ]),
411                });
412                data = &data[11..];
413            } else if data.starts_with(PUSHSCALARVAR_VAR_TYPE) {
414                if data.len() < 6 {
415                    list.push(YSTBArgDat::Raw {
416                        data: data.to_vec().into(),
417                    });
418                    break;
419                }
420                list.push(YSTBArgDat::PushScalarVarVar {
421                    index: u16::from_le_bytes([data[4], data[5]]),
422                });
423                data = &data[6..];
424            } else if data.starts_with(PUSHSCALARVAR_STR_TYPE) {
425                if data.len() < 6 {
426                    list.push(YSTBArgDat::Raw {
427                        data: data.to_vec().into(),
428                    });
429                    break;
430                }
431                list.push(YSTBArgDat::PushScalarVarStr {
432                    index: u16::from_le_bytes([data[4], data[5]]),
433                });
434                data = &data[6..];
435            } else if data.starts_with(PUSHINT32_TYPE) {
436                if data.len() < 7 {
437                    list.push(YSTBArgDat::Raw {
438                        data: data.to_vec().into(),
439                    });
440                    break;
441                }
442                list.push(YSTBArgDat::PushInt32 {
443                    value: i32::from_le_bytes([data[3], data[4], data[5], data[6]]),
444                });
445                data = &data[7..];
446            } else if data.starts_with(PUSHINT64_TYPE) {
447                if data.len() < 11 {
448                    list.push(YSTBArgDat::Raw {
449                        data: data.to_vec().into(),
450                    });
451                    break;
452                }
453                list.push(YSTBArgDat::PushInt64 {
454                    value: i64::from_le_bytes([
455                        data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10],
456                    ]),
457                });
458                data = &data[11..];
459            } else if data.starts_with(PUSHSTRING_TYPE) {
460                if data.len() < 3 {
461                    list.push(YSTBArgDat::Raw {
462                        data: data.to_vec().into(),
463                    });
464                    break;
465                }
466                let len = u16::from_le_bytes([data[1], data[2]]) as usize;
467                if data.len() < 3 + len {
468                    list.push(YSTBArgDat::Raw {
469                        data: data.to_vec().into(),
470                    });
471                    break;
472                }
473                if let Ok(s) = decode_to_string(value.encoding, &data[3..3 + len], true) {
474                    list.push(YSTBArgDat::MString { s });
475                } else {
476                    list.push(YSTBArgDat::Raw {
477                        data: data[..3 + len].to_vec().into(),
478                    });
479                }
480                data = &data[3 + len..];
481            } else if data.starts_with(BINOR_TYPE) {
482                list.push(YSTBArgDat::BinOr);
483                data = &data[3..];
484            } else if data.starts_with(CHANGESIGN_TYPE) {
485                list.push(YSTBArgDat::ChangeSign);
486                data = &data[3..];
487            } else if data.starts_with(LE_TYPE) {
488                list.push(YSTBArgDat::Le);
489                data = &data[3..];
490            } else if data.starts_with(PREPAREVARINDEXATION_VAR_TYPE) {
491                if data.len() < 6 {
492                    list.push(YSTBArgDat::Raw {
493                        data: data.to_vec().into(),
494                    });
495                    break;
496                }
497                list.push(YSTBArgDat::PrepareVarIndexationVar {
498                    index: u16::from_le_bytes([data[4], data[5]]),
499                });
500                data = &data[6..];
501            } else if data.starts_with(PREPAREVARINDEXATION_STR_TYPE) {
502                if data.len() < 6 {
503                    list.push(YSTBArgDat::Raw {
504                        data: data.to_vec().into(),
505                    });
506                    break;
507                }
508                list.push(YSTBArgDat::PrepareVarIndexationStr {
509                    index: u16::from_le_bytes([data[4], data[5]]),
510                });
511                data = &data[6..];
512            } else if data.starts_with(PUSHINT16_TYPE) {
513                if data.len() < 5 {
514                    list.push(YSTBArgDat::Raw {
515                        data: data.to_vec().into(),
516                    });
517                    break;
518                }
519                list.push(YSTBArgDat::PushInt16 {
520                    value: i16::from_le_bytes([data[3], data[4]]),
521                });
522                data = &data[5..];
523            } else if data.starts_with(GE_TYPE) {
524                list.push(YSTBArgDat::Ge);
525                data = &data[3..];
526            } else if data.starts_with(BINXOR_TYPE) {
527                list.push(YSTBArgDat::BinXor);
528                data = &data[3..];
529            } else if data.starts_with(TONUMBER_TYPE) {
530                list.push(YSTBArgDat::ToNumber);
531                data = &data[3..];
532            } else if data.starts_with(TOSTRING_TYPE) {
533                list.push(YSTBArgDat::ToString);
534                data = &data[3..];
535            } else if data.starts_with(PUSHARRAYVAR_VAR_TYPE) {
536                if data.len() < 6 {
537                    list.push(YSTBArgDat::Raw {
538                        data: data.to_vec().into(),
539                    });
540                    break;
541                }
542                list.push(YSTBArgDat::PushArrayVarVar {
543                    index: u16::from_le_bytes([data[4], data[5]]),
544                });
545                data = &data[6..];
546            } else if data.starts_with(PUSHARRAYVAR_STR_TYPE) {
547                if data.len() < 6 {
548                    list.push(YSTBArgDat::Raw {
549                        data: data.to_vec().into(),
550                    });
551                    break;
552                }
553                list.push(YSTBArgDat::PushArrayVarStr {
554                    index: u16::from_le_bytes([data[4], data[5]]),
555                });
556                data = &data[6..];
557            } else if data.starts_with(LOGOR_TYPE) {
558                list.push(YSTBArgDat::LogOr);
559                data = &data[3..];
560            } else {
561                if list.is_empty() {
562                    if !data.contains(&0)
563                        && let Ok(s) = decode_to_string(value.encoding, data, true)
564                    {
565                        list.push(YSTBArgDat::String { s });
566                        break;
567                    }
568                }
569                list.push(YSTBArgDat::Raw {
570                    data: data.to_vec().into(),
571                });
572                break;
573            }
574        }
575        if list.len() > 1 {
576            return Ok(Self {
577                base: value.base.clone(),
578                data: YSTBArgDat::Array { data: list },
579                encoding: value.encoding,
580            });
581        }
582        if let Some(data) = list.pop() {
583            return Ok(Self {
584                base: value.base.clone(),
585                data,
586                encoding: value.encoding,
587            });
588        }
589        Ok(Self {
590            base: value.base.clone(),
591            data: YSTBArgDat::Raw {
592                data: value.data.clone().into(),
593            },
594            encoding: value.encoding,
595        })
596    }
597}
598
599#[derive(Deserialize, Serialize)]
600struct YSTBArgTmp {
601    #[serde(flatten)]
602    base: YSTBArgBase,
603    data: YSTBArgDat,
604    encoding: Encoding,
605}
606
607impl<'de> Deserialize<'de> for YSTBArg {
608    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
609    where
610        D: serde::Deserializer<'de>,
611    {
612        let tmp = YSTBArgTmp::deserialize(deserializer)?;
613        tmp.try_into().map_err(serde::de::Error::custom)
614    }
615}
616
617impl Serialize for YSTBArg {
618    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
619    where
620        S: serde::Serializer,
621    {
622        let tmp: YSTBArgTmp = self.try_into().map_err(serde::ser::Error::custom)?;
623        tmp.serialize(serializer)
624    }
625}
626
627struct YSTBArgData<'a>(&'a [u8], Encoding);
628
629// [opcode] (1 byte) [data size] (2 byte little endian) [data]
630const NOTEQUAL_TYPE: &[u8] = b"!\0\0";
631const MOD_TYPE: &[u8] = b"%\0\0";
632const LOGAND_TYPE: &[u8] = b"&\0\0";
633const PERFORMVARINDEXATION_TYPE: &[u8] = b")\x01\0\0";
634const MUL_TYPE: &[u8] = b"*\0\0";
635const ADD_TYPE: &[u8] = b"+\0\0";
636const NOP_TYPE: &[u8] = b",\0\0";
637const SUB_TYPE: &[u8] = b"-\0\0";
638const DIV_TYPE: &[u8] = b"/\0\0";
639const EQUAL_TYPE: &[u8] = b"=\0\0";
640const LESS_TYPE: &[u8] = b"<\0\0";
641const GREATER_TYPE: &[u8] = b">\0\0";
642const BINAND_TYPE: &[u8] = b"A\0\0";
643/// then one byte data
644const PUSHINT8_TYPE: &[u8] = b"B\x01\0";
645/// then eight byte data
646const PUSHDOUBLE_TYPE: &[u8] = b"F\x08\0";
647const PUSHSCALARVAR_VAR_TYPE: &[u8] = b"H\x03\0$";
648const PUSHSCALARVAR_STR_TYPE: &[u8] = b"H\x03\0@";
649const PUSHINT32_TYPE: &[u8] = b"I\x04\0";
650const PUSHINT64_TYPE: &[u8] = b"L\x08\0";
651const PUSHSTRING_TYPE: &[u8] = b"M";
652const BINOR_TYPE: &[u8] = b"O\0\0";
653const CHANGESIGN_TYPE: &[u8] = b"R\0\0";
654const LE_TYPE: &[u8] = b"S\0\0";
655const PREPAREVARINDEXATION_VAR_TYPE: &[u8] = b"V\x03\0$";
656const PREPAREVARINDEXATION_STR_TYPE: &[u8] = b"V\x03\0@";
657const PUSHINT16_TYPE: &[u8] = b"W\x02\0";
658const GE_TYPE: &[u8] = b"Z\0\0";
659const BINXOR_TYPE: &[u8] = b"^\0\0";
660const TONUMBER_TYPE: &[u8] = b"i\0\0";
661const TOSTRING_TYPE: &[u8] = b"s\0\0";
662const PUSHARRAYVAR_VAR_TYPE: &[u8] = b"v\x03\0$";
663const PUSHARRAYVAR_STR_TYPE: &[u8] = b"v\x03\0@";
664const LOGOR_TYPE: &[u8] = b"|\0\0";
665
666impl<'a> std::fmt::Debug for YSTBArgData<'a> {
667    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
668        let mut data = self.0;
669        let mut first = true;
670        loop {
671            if data.is_empty() {
672                break;
673            }
674            let is_first = first;
675            if first {
676                first = false;
677            } else {
678                f.write_str(" ")?;
679            }
680            if data.starts_with(NOTEQUAL_TYPE) {
681                f.write_str("notequal")?;
682                data = &data[3..];
683            } else if data.starts_with(MOD_TYPE) {
684                f.write_str("mod")?;
685                data = &data[3..];
686            } else if data.starts_with(LOGAND_TYPE) {
687                f.write_str("logand")?;
688                data = &data[3..];
689            } else if data.starts_with(PERFORMVARINDEXATION_TYPE) {
690                f.write_str("performvarindexation")?;
691                data = &data[4..];
692            } else if data.starts_with(MUL_TYPE) {
693                f.write_str("mul")?;
694                data = &data[3..];
695            } else if data.starts_with(ADD_TYPE) {
696                f.write_str("add")?;
697                data = &data[3..];
698            } else if data.starts_with(NOP_TYPE) {
699                f.write_str("nop")?;
700                data = &data[3..];
701            } else if data.starts_with(SUB_TYPE) {
702                f.write_str("sub")?;
703                data = &data[3..];
704            } else if data.starts_with(DIV_TYPE) {
705                f.write_str("div")?;
706                data = &data[3..];
707            } else if data.starts_with(EQUAL_TYPE) {
708                f.write_str("equal")?;
709                data = &data[3..];
710            } else if data.starts_with(LESS_TYPE) {
711                f.write_str("less")?;
712                data = &data[3..];
713            } else if data.starts_with(GREATER_TYPE) {
714                f.write_str("greater")?;
715                data = &data[3..];
716            } else if data.starts_with(BINAND_TYPE) {
717                f.write_str("binand")?;
718                data = &data[3..];
719            } else if data.starts_with(PUSHINT8_TYPE) {
720                if data.len() < 4 {
721                    f.write_str(&hex::encode(data))?;
722                    break;
723                }
724                f.write_fmt(format_args!("pushint8({})", i8::from_le_bytes([data[3]])))?;
725                data = &data[4..];
726            } else if data.starts_with(PUSHDOUBLE_TYPE) {
727                if data.len() < 11 {
728                    f.write_str(&hex::encode(data))?;
729                    break;
730                }
731                f.write_fmt(format_args!(
732                    "pushdouble({})",
733                    f64::from_le_bytes([
734                        data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]
735                    ])
736                ))?;
737                data = &data[11..];
738            } else if data.starts_with(PUSHSCALARVAR_VAR_TYPE) {
739                if data.len() < 6 {
740                    f.write_str(&hex::encode(data))?;
741                    break;
742                }
743                f.write_fmt(format_args!(
744                    "pushscalarvar(var[{}])",
745                    u16::from_le_bytes([data[4], data[5]])
746                ))?;
747                data = &data[6..];
748            } else if data.starts_with(PUSHSCALARVAR_STR_TYPE) {
749                if data.len() < 6 {
750                    f.write_str(&hex::encode(data))?;
751                    break;
752                }
753                f.write_fmt(format_args!(
754                    "pushscalarvar(str[{}])",
755                    u16::from_le_bytes([data[4], data[5]])
756                ))?;
757                data = &data[6..];
758            } else if data.starts_with(PUSHINT32_TYPE) {
759                if data.len() < 7 {
760                    f.write_str(&hex::encode(data))?;
761                    break;
762                }
763                f.write_fmt(format_args!(
764                    "pushint32({})",
765                    i32::from_le_bytes([data[3], data[4], data[5], data[6]])
766                ))?;
767                data = &data[7..];
768            } else if data.starts_with(PUSHINT64_TYPE) {
769                if data.len() < 11 {
770                    f.write_str(&hex::encode(data))?;
771                    break;
772                }
773                f.write_fmt(format_args!(
774                    "pushint64({})",
775                    i64::from_le_bytes([
776                        data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]
777                    ])
778                ))?;
779                data = &data[11..];
780            } else if data.starts_with(PUSHSTRING_TYPE) {
781                if data.len() < 3 {
782                    f.write_str(&hex::encode(data))?;
783                    break;
784                }
785                let len = u16::from_le_bytes([data[1], data[2]]) as usize;
786                if data.len() < 3 + len {
787                    f.write_str(&hex::encode(data))?;
788                    break;
789                }
790                if let Ok(s) = decode_to_string(self.1, &data[3..3 + len], true) {
791                    f.write_str(&s)?;
792                } else {
793                    f.write_str(&hex::encode(&data[..3 + len]))?;
794                }
795                data = &data[3 + len..];
796            } else if data.starts_with(BINOR_TYPE) {
797                f.write_str("binor")?;
798                data = &data[3..];
799            } else if data.starts_with(CHANGESIGN_TYPE) {
800                f.write_str("changesign")?;
801                data = &data[3..];
802            } else if data.starts_with(LE_TYPE) {
803                f.write_str("le")?;
804                data = &data[3..];
805            } else if data.starts_with(PREPAREVARINDEXATION_VAR_TYPE) {
806                if data.len() < 6 {
807                    f.write_str(&hex::encode(data))?;
808                    break;
809                }
810                f.write_fmt(format_args!(
811                    "preparevarindexation(var[{}])",
812                    u16::from_le_bytes([data[4], data[5]])
813                ))?;
814                data = &data[6..];
815            } else if data.starts_with(PREPAREVARINDEXATION_STR_TYPE) {
816                if data.len() < 6 {
817                    f.write_str(&hex::encode(data))?;
818                    break;
819                }
820                f.write_fmt(format_args!(
821                    "preparevarindexation(str[{}])",
822                    u16::from_le_bytes([data[4], data[5]])
823                ))?;
824                data = &data[6..];
825            } else if data.starts_with(PUSHINT16_TYPE) {
826                if data.len() < 5 {
827                    f.write_str(&hex::encode(data))?;
828                    break;
829                }
830                f.write_fmt(format_args!(
831                    "pushint16({})",
832                    i16::from_le_bytes([data[3], data[4]])
833                ))?;
834                data = &data[5..];
835            } else if data.starts_with(GE_TYPE) {
836                f.write_str("ge")?;
837                data = &data[3..];
838            } else if data.starts_with(BINXOR_TYPE) {
839                f.write_str("binxor")?;
840                data = &data[3..];
841            } else if data.starts_with(TONUMBER_TYPE) {
842                f.write_str("tonumber")?;
843                data = &data[3..];
844            } else if data.starts_with(TOSTRING_TYPE) {
845                f.write_str("tostring")?;
846                data = &data[3..];
847            } else if data.starts_with(PUSHARRAYVAR_VAR_TYPE) {
848                if data.len() < 6 {
849                    f.write_str(&hex::encode(data))?;
850                    break;
851                }
852                f.write_fmt(format_args!(
853                    "pusharrayvar(var[{}])",
854                    u16::from_le_bytes([data[4], data[5]])
855                ))?;
856                data = &data[6..];
857            } else if data.starts_with(PUSHARRAYVAR_STR_TYPE) {
858                if data.len() < 6 {
859                    f.write_str(&hex::encode(data))?;
860                    break;
861                }
862                f.write_fmt(format_args!(
863                    "pusharrayvar(str[{}])",
864                    u16::from_le_bytes([data[4], data[5]])
865                ))?;
866                data = &data[6..];
867            } else if data.starts_with(LOGOR_TYPE) {
868                f.write_str("logor")?;
869                data = &data[3..];
870            } else {
871                if is_first {
872                    if !data.contains(&0)
873                        && let Ok(s) = decode_to_string(self.1, &data, true)
874                    {
875                        f.write_str(&s)?;
876                        break;
877                    }
878                }
879                f.write_str(&hex::encode(data))?;
880                break;
881            }
882        }
883        Ok(())
884    }
885}
886
887impl std::fmt::Debug for YSTBArg {
888    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
889        f.debug_struct("YSTBArg")
890            .field("id", &self.id)
891            .field("type", &self.typ)
892            .field("size", &self.size)
893            .field("data", &YSTBArgData(&self.data, self.encoding))
894            .finish()
895    }
896}
897
898impl Deref for YSTBArg {
899    type Target = YSTBArgBase;
900    fn deref(&self) -> &Self::Target {
901        &self.base
902    }
903}
904
905impl DerefMut for YSTBArg {
906    fn deref_mut(&mut self) -> &mut Self::Target {
907        &mut self.base
908    }
909}
910
911fn get_info_as_header(info: &Option<Box<dyn Any>>) -> Result<&YSTBHeader> {
912    Ok(info
913        .as_ref()
914        .ok_or_else(|| anyhow::anyhow!("info not found"))?
915        .downcast_ref()
916        .ok_or_else(|| anyhow::anyhow!("not YSTBHeader"))?)
917}
918
919impl StructUnpack for YSTBArg {
920    fn unpack<R: Read + Seek>(
921        reader: &mut R,
922        big: bool,
923        encoding: Encoding,
924        info: &Option<Box<dyn Any>>,
925    ) -> Result<Self> {
926        let base = YSTBArgBase::unpack(reader, big, encoding, info)?;
927        let offset = u32::unpack(reader, big, encoding, info)?;
928        let header = get_info_as_header(info)?;
929        let target =
930            0x20 + header.inst_index_size as u64 + header.args_index_size as u64 + offset as u64;
931        let data = reader.peek_exact_at_vec(target, base.size as usize)?;
932        Ok(Self {
933            base,
934            data,
935            encoding,
936        })
937    }
938}
939
940#[derive(Debug)]
941pub struct YSTBBuilder {}
942
943impl YSTBBuilder {
944    /// Creates a new instance of `YSTBBuilder`
945    pub const fn new() -> Self {
946        YSTBBuilder {}
947    }
948}
949
950impl ScriptBuilder for YSTBBuilder {
951    fn default_encoding(&self) -> Encoding {
952        Encoding::Cp932
953    }
954
955    fn build_script(
956        &self,
957        buf: Vec<u8>,
958        filename: &str,
959        encoding: Encoding,
960        _archive_encoding: Encoding,
961        config: &ExtraConfig,
962        archive: Option<&Box<dyn Script>>,
963    ) -> Result<Box<dyn Script + Send + Sync>> {
964        Ok(Box::new(YSTB::new(
965            MemReader::new(buf),
966            filename,
967            encoding,
968            config,
969            archive,
970        )?))
971    }
972
973    fn extensions(&self) -> &'static [&'static str] {
974        &["ybn"]
975    }
976
977    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
978        if buf_len >= 4 && buf.starts_with(b"YSTB") {
979            return Some(20);
980        }
981        None
982    }
983
984    fn script_type(&self) -> &'static ScriptType {
985        &ScriptType::YurisYSTB
986    }
987}
988
989#[derive(Debug)]
990pub struct YSTB {
991    data: YSTBData,
992    com: YSCMData,
993    xor_key: Option<u32>,
994    disasm: bool,
995    custom_yaml: bool,
996    labels: BTreeMap<u32, Label>,
997}
998
999impl YSTB {
1000    pub fn new<T: Read + Seek>(
1001        mut reader: T,
1002        filename: &str,
1003        encoding: Encoding,
1004        config: &ExtraConfig,
1005        archive: Option<&Box<dyn Script>>,
1006    ) -> Result<Self> {
1007        let mut sig = [0; 4];
1008        reader.read_exact(&mut sig)?;
1009        if &sig != b"YSTB" {
1010            anyhow::bail!("Unsupported YSTB file.");
1011        }
1012        let mut xor_key = None;
1013        let data = match YSTBData::unpack(&mut reader, false, encoding, &None) {
1014            Ok(data) => data,
1015            Err(err) => {
1016                let key = Self::get_xor_key(&mut reader)?;
1017                if key == 0 {
1018                    return Err(err);
1019                }
1020                xor_key = Some(key);
1021                let mut writer = MemWriter::with_capacity(reader.stream_length()? as usize);
1022                Self::xor(&mut reader, &mut writer, key)?;
1023                let mut reader = writer.to_ref();
1024                reader.pos = 4;
1025                YSTBData::unpack(&mut reader, false, encoding, &None)?
1026            }
1027        };
1028        // println!("xor_key: {:?}, {:#?}", xor_key, data);
1029        let yscm = if let Some(path) = config.yuris_ysc_path.as_ref() {
1030            crate::utils::files::read_file(path)?
1031        } else {
1032            let path = std::path::Path::new(filename);
1033            let pdir = path.parent().unwrap_or_else(|| std::path::Path::new(""));
1034            let fp = pdir.join("ysc.ybn");
1035            if let Some(archive) = &archive {
1036                let mut file = archive.open_file_by_name(&fp.to_string_lossy(), true)?;
1037                file.data()?
1038            } else {
1039                let p = crate::utils::files::get_ignorecase_path(&fp)?;
1040                crate::utils::files::read_file(&p)?
1041            }
1042        };
1043        if !yscm.starts_with(b"YSCM") {
1044            anyhow::bail!("Unsupported YSCM file. (ysc.ybn)");
1045        }
1046        let mut reader = MemReader::new(yscm);
1047        reader.pos = 4;
1048        let com = YSCMData::unpack(&mut reader, false, encoding, &None)?;
1049        let labels = match Self::try_load_yslb(filename, &archive, config, encoding) {
1050            Ok(labels) => labels,
1051            Err(e) => {
1052                eprintln!("WARNING: Failed to load ysl.bin file: {}", e);
1053                crate::COUNTER.inc_warning();
1054                BTreeMap::new()
1055            }
1056        };
1057        Ok(Self {
1058            data,
1059            com,
1060            xor_key,
1061            disasm: config.yuris_ystb_disasm,
1062            custom_yaml: config.custom_yaml,
1063            labels,
1064        })
1065    }
1066
1067    fn try_load_yslb(
1068        filename: &str,
1069        archive: &Option<&Box<dyn Script>>,
1070        config: &ExtraConfig,
1071        encoding: Encoding,
1072    ) -> Result<BTreeMap<u32, Label>> {
1073        let yslb = if let Some(path) = config.yuris_ysl_path.as_ref() {
1074            crate::utils::files::read_file(path)?
1075        } else {
1076            let path = std::path::Path::new(filename);
1077            let pdir = path.parent().unwrap_or_else(|| std::path::Path::new(""));
1078            let fp = pdir.join("ysl.ybn");
1079            if let Some(archive) = &archive {
1080                let mut file = archive.open_file_by_name(&fp.to_string_lossy(), true)?;
1081                file.data()?
1082            } else {
1083                let p = crate::utils::files::get_ignorecase_path(&fp)?;
1084                crate::utils::files::read_file(&p)?
1085            }
1086        };
1087        if !yslb.starts_with(b"YSLB") {
1088            anyhow::bail!("Unsupported YSLB file. (ysl.ybn)");
1089        }
1090        let mut reader = MemReader::new(yslb);
1091        reader.pos = 4;
1092        let labels = YSLBData::unpack(&mut reader, false, encoding, &None)?;
1093        let path = std::path::Path::new(filename);
1094        let filename = path
1095            .file_stem()
1096            .ok_or_else(|| anyhow::anyhow!("No filename"))?
1097            .to_string_lossy()
1098            .into_owned();
1099        let script_idx_name = String::from_iter(
1100            filename
1101                .chars()
1102                .rev()
1103                .take(5)
1104                .collect::<Vec<_>>()
1105                .iter()
1106                .rev(),
1107        );
1108        let script_idx: u16 = script_idx_name.parse()?;
1109        let mut map = BTreeMap::new();
1110        for label in labels.labels {
1111            if label.script_index == script_idx {
1112                map.insert(label.offset, label);
1113            }
1114        }
1115        Ok(map)
1116    }
1117
1118    fn get_xor_key<T: Read + Seek>(reader: &mut T) -> Result<u32> {
1119        let version = reader.peek_u32_at(4)?;
1120        reader.seek(SeekFrom::Start(4))?;
1121        Ok(if matches!(version, 201..300) {
1122            let header: YSTBHeaderV2 = reader.read_struct(false, Encoding::Cp932, &None)?;
1123            if (header.code_seg_size as u64) + (header.args_seg_size as u64) < 0x10 {
1124                0
1125            } else {
1126                reader.peek_u32_at(0x2C)?
1127            }
1128        } else {
1129            let header: YSTBHeader = reader.read_struct(false, Encoding::Cp932, &None)?;
1130            if header.args_data_size == 0 {
1131                0
1132            } else {
1133                reader.peek_u32_at(header.inst_index_size as u64 + 0x28)?
1134            }
1135        })
1136    }
1137
1138    fn xor<R: Read + Seek, W: Write>(
1139        mut reader: &mut R,
1140        writer: &mut W,
1141        xor_key: u32,
1142    ) -> Result<()> {
1143        let key = xor_key.to_le_bytes();
1144        reader.seek(SeekFrom::Start(4))?;
1145        writer.write_all(b"YSTB")?;
1146        let version = reader.peek_u32()?;
1147        if matches!(version, 201..300) {
1148            let header: YSTBHeaderV2 = reader.read_struct(false, Encoding::Cp932, &None)?;
1149            writer.write_struct(&header, false, Encoding::Cp932, &None)?;
1150            let mut stream = XoredKeyStream::new(
1151                StreamRegion::with_size(&mut reader, header.code_seg_size as u64)?,
1152                key.to_vec(),
1153                0,
1154            );
1155            std::io::copy(&mut stream, writer)?;
1156            stream = XoredKeyStream::new(
1157                StreamRegion::with_size(&mut reader, header.args_seg_size as u64)?,
1158                key.to_vec(),
1159                0,
1160            );
1161            std::io::copy(&mut stream, writer)?;
1162            std::io::copy(reader, writer)?;
1163        } else {
1164            let header: YSTBHeader = reader.read_struct(false, Encoding::Cp932, &None)?;
1165            writer.write_struct(&header, false, Encoding::Cp932, &None)?;
1166            let mut stream = XoredKeyStream::new(
1167                StreamRegion::with_size(&mut reader, header.inst_index_size as u64)?,
1168                key.to_vec(),
1169                0,
1170            );
1171            std::io::copy(&mut stream, writer)?;
1172            stream = XoredKeyStream::new(
1173                StreamRegion::with_size(&mut reader, header.args_index_size as u64)?,
1174                key.to_vec(),
1175                0,
1176            );
1177            std::io::copy(&mut stream, writer)?;
1178            stream = XoredKeyStream::new(
1179                StreamRegion::with_size(&mut reader, header.args_data_size as u64)?,
1180                key.to_vec(),
1181                0,
1182            );
1183            std::io::copy(&mut stream, writer)?;
1184            stream = XoredKeyStream::new(
1185                StreamRegion::with_size(&mut reader, header.line_numbers_size as u64)?,
1186                key.to_vec(),
1187                0,
1188            );
1189            std::io::copy(&mut stream, writer)?;
1190            std::io::copy(reader, writer)?;
1191        }
1192        Ok(())
1193    }
1194}
1195
1196struct YSTBDataSer<'a> {
1197    data: &'a YSTBData,
1198    com: &'a YSCMData,
1199    labels: &'a BTreeMap<u32, Label>,
1200}
1201
1202impl<'a> serde::Serialize for YSTBDataSer<'a> {
1203    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1204    where
1205        S: serde::Serializer,
1206    {
1207        use serde::ser::SerializeStruct;
1208        let h = &self.data.header;
1209        let mut s = serializer.serialize_struct("YSTBData", 4)?;
1210        s.serialize_field("version", &h.version)?;
1211        s.serialize_field("reserve0", &h.reserve0)?;
1212        s.serialize_field(
1213            "insts",
1214            &YSTBInstSliceSer {
1215                insts: &self.data.insts,
1216                com: self.com,
1217                labels: self.labels,
1218            },
1219        )?;
1220        s.serialize_field("line_numbers", &self.data.line_numbers)?;
1221        s.end()
1222    }
1223}
1224
1225struct YSTBInstSliceSer<'a> {
1226    insts: &'a [YSTBInst],
1227    com: &'a YSCMData,
1228    labels: &'a BTreeMap<u32, Label>,
1229}
1230
1231impl<'a> serde::Serialize for YSTBInstSliceSer<'a> {
1232    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1233    where
1234        S: serde::Serializer,
1235    {
1236        use serde::ser::SerializeSeq;
1237        let mut seq = serializer.serialize_seq(Some(self.insts.len()))?;
1238        for (i, inst) in self.insts.iter().enumerate() {
1239            let offset = i as u32;
1240            let opcode_name = self
1241                .com
1242                .opcodes
1243                .get(inst.opcode as usize)
1244                .map(|m| m.name.as_str());
1245            let label = self.labels.get(&offset);
1246            seq.serialize_element(&YSTBInstSer {
1247                inst,
1248                opcode_name,
1249                label,
1250            })?;
1251        }
1252        seq.end()
1253    }
1254}
1255
1256struct YSTBInstSer<'a> {
1257    inst: &'a YSTBInst,
1258    opcode_name: Option<&'a str>,
1259    label: Option<&'a Label>,
1260}
1261
1262impl<'a> serde::Serialize for YSTBInstSer<'a> {
1263    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1264    where
1265        S: serde::Serializer,
1266    {
1267        use serde::ser::SerializeStruct;
1268        let nfields = 3 + self.opcode_name.is_some() as usize + self.label.is_some() as usize;
1269        let mut s = serializer.serialize_struct("YSTBInst", nfields)?;
1270        s.serialize_field("opcode", &self.inst.opcode)?;
1271        if let Some(name) = self.opcode_name {
1272            s.serialize_field("opcode_name", name)?;
1273        }
1274        s.serialize_field("unk", &self.inst.unk)?;
1275        s.serialize_field("args", &self.inst.args)?;
1276        if let Some(label) = self.label {
1277            s.serialize_field("label", &label.name)?;
1278        }
1279        s.end()
1280    }
1281}
1282
1283/// Build PUSHSTRING data: `M` + (content_len + 2) + `"` + content + `"`
1284fn make_pushstring_data(content: &[u8]) -> Vec<u8> {
1285    let mut new_data = Vec::with_capacity(5 + content.len());
1286    new_data.push(b'M');
1287    new_data.extend_from_slice(&((content.len() + 2) as u16).to_le_bytes());
1288    new_data.push(b'"');
1289    new_data.extend_from_slice(content);
1290    new_data.push(b'"');
1291    new_data
1292}
1293
1294impl Script for YSTB {
1295    fn default_output_script_type(&self) -> OutputScriptType {
1296        OutputScriptType::Json
1297    }
1298
1299    fn is_output_supported(&self, _output: OutputScriptType) -> bool {
1300        true
1301    }
1302
1303    fn default_format_type(&self) -> FormatOptions {
1304        FormatOptions::None
1305    }
1306
1307    fn custom_output_extension(&self) -> &'static str {
1308        if self.disasm {
1309            "txt"
1310        } else if self.custom_yaml {
1311            "yaml"
1312        } else {
1313            "json"
1314        }
1315    }
1316
1317    fn extract_messages(&self) -> Result<Vec<Message>> {
1318        let mut mes = Vec::new();
1319        for code in self.data.insts.iter() {
1320            let meta =
1321                self.com.opcodes.get(code.opcode as usize).ok_or_else(|| {
1322                    anyhow::anyhow!("Failed to find op {:x}'s metadata", code.opcode)
1323                })?;
1324            if meta.name == "WORD" {
1325                if code.arg_count != 1 {
1326                    anyhow::bail!("Bad argument count for WORD.");
1327                }
1328                let arg = &code.args[0];
1329                if arg.typ == 0 && arg.size > 0 {
1330                    let mut data = decode_to_string(arg.encoding, &arg.data, true)?;
1331                    let name = if data.starts_with("【")
1332                        && let Some(end_pos) = data.find("】")
1333                    {
1334                        let n = data[3..end_pos].to_owned();
1335                        data = data[end_pos + 3..].to_owned();
1336                        Some(n)
1337                    } else {
1338                        None
1339                    };
1340                    mes.push(Message::new(data, name));
1341                }
1342            } else if meta.name == "_"
1343                && meta.arguments.len() > 0
1344                && code.arg_count == 1
1345                && meta.arguments[0].data == 3
1346            {
1347                let arg = &code.args[0];
1348                if arg.data.starts_with(PUSHSTRING_TYPE) {
1349                    let len = u16::from_le_bytes([arg.data[1], arg.data[2]]);
1350                    if len as u32 + 3 == arg.size {
1351                        let data = decode_to_string(
1352                            arg.encoding,
1353                            &arg.data[4..arg.size as usize - 1],
1354                            true,
1355                        )?;
1356                        mes.push(Message::new(data, None));
1357                    }
1358                }
1359            } else if meta.name == "GOSUB" && code.arg_count >= 2 {
1360                let arg0 = &code.args[0];
1361                let name = format!("{:?}", &YSTBArgData(&arg0.data, arg0.encoding))
1362                    .trim_matches('"')
1363                    .to_lowercase();
1364                if name == "es.sel.set" {
1365                    for arg in &code.args[1..] {
1366                        if arg.data.starts_with(PUSHSTRING_TYPE) {
1367                            let len = u16::from_le_bytes([arg.data[1], arg.data[2]]);
1368                            if len as u32 + 3 == arg.size {
1369                                let data = decode_to_string(
1370                                    arg.encoding,
1371                                    &arg.data[4..arg.size as usize - 1],
1372                                    true,
1373                                )?;
1374                                if !data.is_empty() {
1375                                    mes.push(Message::new(data, None));
1376                                }
1377                            }
1378                        }
1379                    }
1380                } else if name == "es.char.name" && code.arg_count >= 3 {
1381                    let arg = &code.args[2];
1382                    if arg.data.starts_with(PUSHSTRING_TYPE) {
1383                        let len = u16::from_le_bytes([arg.data[1], arg.data[2]]);
1384                        if len as u32 + 3 == arg.size {
1385                            let data = decode_to_string(
1386                                arg.encoding,
1387                                &arg.data[4..arg.size as usize - 1],
1388                                true,
1389                            )?;
1390                            mes.push(Message::new(data, None));
1391                        }
1392                    }
1393                }
1394            }
1395        }
1396        Ok(mes)
1397    }
1398
1399    fn import_messages<'a>(
1400        &'a self,
1401        messages: Vec<Message>,
1402        mut file: Box<dyn WriteSeek + 'a>,
1403        _filename: &str,
1404        encoding: Encoding,
1405        replacement: Option<&'a ReplacementTable>,
1406    ) -> Result<()> {
1407        let mut messages_iter = messages.into_iter();
1408
1409        // Build modified instruction data: (YSTBInstBase, Vec<(YSTBArgBase, Vec<u8>)>)
1410        let mut inst_data: Vec<(YSTBInstBase, Vec<(YSTBArgBase, Vec<u8>)>)> = Vec::new();
1411
1412        for code in self.data.insts.iter() {
1413            let meta =
1414                self.com.opcodes.get(code.opcode as usize).ok_or_else(|| {
1415                    anyhow::anyhow!("Failed to find op {:x}'s metadata", code.opcode)
1416                })?;
1417
1418            // Default: copy all args as-is
1419            let mut new_args: Vec<(YSTBArgBase, Vec<u8>)> = code
1420                .args
1421                .iter()
1422                .map(|arg| (arg.base.clone(), arg.data.clone()))
1423                .collect();
1424
1425            if meta.name == "WORD" {
1426                if code.arg_count == 1 {
1427                    let arg = &code.args[0];
1428                    if arg.typ == 0 && arg.size > 0 {
1429                        let mut msg = messages_iter
1430                            .next()
1431                            .ok_or_else(|| anyhow::anyhow!("No more messages to import"))?;
1432                        if let Some(table) = replacement {
1433                            for (from, to) in &table.map {
1434                                msg.message = msg.message.replace(from, to);
1435                            }
1436                            if let Some(ref name) = msg.name {
1437                                let mut new_name = name.clone();
1438                                for (from, to) in &table.map {
1439                                    new_name = new_name.replace(from, to);
1440                                }
1441                                msg.name = Some(new_name);
1442                            }
1443                        }
1444                        let mut text = msg.message;
1445                        if let Some(name) = msg.name {
1446                            text = format!("【{}】{}", name, text);
1447                        }
1448                        let encoded = encode_string(encoding, &text, true)?;
1449                        new_args[0].1 = encoded;
1450                        new_args[0].0.size = new_args[0].1.len() as u32;
1451                    }
1452                }
1453            } else if meta.name == "_"
1454                && !meta.arguments.is_empty()
1455                && code.arg_count == 1
1456                && meta.arguments[0].data == 3
1457            {
1458                let arg = &code.args[0];
1459                if arg.data.starts_with(PUSHSTRING_TYPE) {
1460                    let len = u16::from_le_bytes([arg.data[1], arg.data[2]]);
1461                    if len as u32 + 3 == arg.size {
1462                        let mut msg = messages_iter
1463                            .next()
1464                            .ok_or_else(|| anyhow::anyhow!("No more messages to import"))?;
1465                        if let Some(table) = replacement {
1466                            for (from, to) in &table.map {
1467                                msg.message = msg.message.replace(from, to);
1468                            }
1469                        }
1470                        let d = encode_string(encoding, &msg.message, true)?;
1471                        new_args[0].1 = make_pushstring_data(&d);
1472                        new_args[0].0.size = new_args[0].1.len() as u32;
1473                    }
1474                }
1475            } else if meta.name == "GOSUB" && code.arg_count >= 2 {
1476                let arg0 = &code.args[0];
1477                let name = format!("{:?}", &YSTBArgData(&arg0.data, arg0.encoding))
1478                    .trim_matches('"')
1479                    .to_lowercase();
1480                if name == "es.sel.set" {
1481                    for arg_pair in new_args.iter_mut().skip(1) {
1482                        let data = &arg_pair.1;
1483                        if data.starts_with(PUSHSTRING_TYPE) {
1484                            let slen = u16::from_le_bytes([data[1], data[2]]);
1485                            if slen as u32 + 3 == arg_pair.0.size {
1486                                let mut msg = messages_iter
1487                                    .next()
1488                                    .ok_or_else(|| anyhow::anyhow!("No more messages to import"))?;
1489                                if let Some(table) = replacement {
1490                                    for (from, to) in &table.map {
1491                                        msg.message = msg.message.replace(from, to);
1492                                    }
1493                                }
1494                                if !msg.message.is_empty() {
1495                                    let d = encode_string(encoding, &msg.message, true)?;
1496                                    arg_pair.1 = make_pushstring_data(&d);
1497                                    arg_pair.0.size = arg_pair.1.len() as u32;
1498                                }
1499                            }
1500                        }
1501                    }
1502                } else if name == "es.char.name" && code.arg_count >= 3 {
1503                    // Re-encode arg[1] from original encoding to target encoding
1504                    let arg1 = &code.args[1];
1505                    if arg1.data.starts_with(PUSHSTRING_TYPE) {
1506                        let slen = u16::from_le_bytes([arg1.data[1], arg1.data[2]]);
1507                        if slen as u32 + 3 == arg1.size {
1508                            let mut decoded = decode_to_string(
1509                                arg1.encoding,
1510                                &arg1.data[4..arg1.size as usize - 1],
1511                                true,
1512                            )?;
1513                            if let Some(table) = replacement {
1514                                for (from, to) in &table.map {
1515                                    decoded = decoded.replace(from, to);
1516                                }
1517                            }
1518                            let d = encode_string(encoding, &decoded, true)?;
1519                            new_args[1].1 = make_pushstring_data(&d);
1520                            new_args[1].0.size = new_args[1].1.len() as u32;
1521                        }
1522                    }
1523                    // Patch arg[2] with message
1524                    let arg2 = &code.args[2];
1525                    if arg2.data.starts_with(PUSHSTRING_TYPE) {
1526                        let slen = u16::from_le_bytes([arg2.data[1], arg2.data[2]]);
1527                        if slen as u32 + 3 == arg2.size {
1528                            let mut msg = messages_iter
1529                                .next()
1530                                .ok_or_else(|| anyhow::anyhow!("No more messages to import"))?;
1531                            if let Some(table) = replacement {
1532                                for (from, to) in &table.map {
1533                                    msg.message = msg.message.replace(from, to);
1534                                }
1535                            }
1536                            let d = encode_string(encoding, &msg.message, true)?;
1537                            new_args[2].1 = make_pushstring_data(&d);
1538                            new_args[2].0.size = new_args[2].1.len() as u32;
1539                        }
1540                    }
1541                } else if name == "es.char.name.mark.set" && code.arg_count >= 2 {
1542                    // Re-encode arg[1] from original encoding to target encoding only
1543                    let arg1 = &code.args[1];
1544                    if arg1.data.starts_with(PUSHSTRING_TYPE) {
1545                        let slen = u16::from_le_bytes([arg1.data[1], arg1.data[2]]);
1546                        if slen as u32 + 3 == arg1.size {
1547                            let mut decoded = decode_to_string(
1548                                arg1.encoding,
1549                                &arg1.data[4..arg1.size as usize - 1],
1550                                true,
1551                            )?;
1552                            if let Some(table) = replacement {
1553                                for (from, to) in &table.map {
1554                                    decoded = decoded.replace(from, to);
1555                                }
1556                            }
1557                            let d = encode_string(encoding, &decoded, true)?;
1558                            new_args[1].1 = make_pushstring_data(&d);
1559                            new_args[1].0.size = new_args[1].1.len() as u32;
1560                        }
1561                    }
1562                }
1563            }
1564
1565            inst_data.push((code.base.clone(), new_args));
1566        }
1567
1568        // Write binary output (same structure as custom_import)
1569        let mut f = MemWriter::new();
1570        f.write_all(b"YSTB")?;
1571
1572        let inst_entry_count = inst_data.len() as u32;
1573        let inst_index_size = inst_entry_count * 4;
1574        let arg_count: usize = inst_data.iter().map(|(_, args)| args.len()).sum();
1575        let args_index_size = arg_count as u32 * 0xC;
1576
1577        let mut header = self.data.header.clone();
1578        header.inst_entry_count = inst_entry_count;
1579        header.inst_index_size = inst_index_size;
1580        header.args_index_size = args_index_size;
1581        header.line_numbers_size = self.data.line_numbers.len() as u32;
1582
1583        // Pack header (will update args_data_size later)
1584        header.pack(&mut f, false, encoding, &None)?;
1585
1586        // Pack instruction bases with correct arg_counts
1587        for (base, args) in inst_data.iter() {
1588            let mut b = base.clone();
1589            b.arg_count = args.len() as u8;
1590            b.pack(&mut f, false, encoding, &None)?;
1591        }
1592
1593        // Pack arg index table and arg data
1594        let mut cpos = f.pos as u64;
1595        f.pos += args_index_size as usize;
1596        let bpos = f.pos as u32;
1597
1598        for (base, args) in inst_data.iter_mut() {
1599            let meta =
1600                self.com.opcodes.get(base.opcode as usize).ok_or_else(|| {
1601                    anyhow::anyhow!("Failed to find op {:x}'s metadata", base.opcode)
1602                })?;
1603
1604            for arg in args.iter_mut() {
1605                arg.0.size = arg.1.len() as u32;
1606                f.write_struct_at(cpos, &arg.0, false, encoding, &None)?;
1607                cpos += 8;
1608
1609                if arg.0.size == 0
1610                    || (meta.name == "RETURNCODE" && arg.0.size == 1 && arg.1[0] == b'M')
1611                {
1612                    f.write_u32_at(cpos, 0)?;
1613                    cpos += 4;
1614                    continue;
1615                }
1616
1617                let offset = f.pos as u32 - bpos;
1618                f.write_u32_at(cpos, offset)?;
1619                cpos += 4;
1620                f.write_all(&arg.1)?;
1621            }
1622        }
1623
1624        // Update args_data_size and write line_numbers
1625        header.args_data_size = f.pos as u32 - bpos;
1626        f.write_all(&self.data.line_numbers)?;
1627
1628        // Rewrite header with correct args_data_size
1629        f.pos = 4;
1630        header.pack(&mut f, false, encoding, &None)?;
1631
1632        // Apply XOR if needed
1633        if let Some(xor) = self.xor_key {
1634            let mut r = MemReader::new(f.into_inner());
1635            f = MemWriter::new();
1636            Self::xor(&mut r, &mut f, xor)?;
1637        }
1638
1639        file.write_all(&f.data)?;
1640        Ok(())
1641    }
1642
1643    fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
1644        if !self.disasm {
1645            let wrapper = YSTBDataSer {
1646                data: &self.data,
1647                com: &self.com,
1648                labels: &self.labels,
1649            };
1650            let s = if self.custom_yaml {
1651                serde_yaml_ng::to_string(&wrapper)?
1652            } else {
1653                serde_json::to_string_pretty(&wrapper)?
1654            };
1655            let mut f = std::fs::File::create(filename)?;
1656            let encoded = encode_string(encoding, &s, true)?;
1657            f.write_all(&encoded)?;
1658            return Ok(());
1659        }
1660        let mut file = MemWriter::new();
1661        let mut indent = String::new();
1662        let mut unused_labels = BTreeSet::from_iter(self.labels.keys().cloned());
1663        for (i, code) in self.data.insts.iter().enumerate() {
1664            let offset = i as u32;
1665            let meta =
1666                self.com.opcodes.get(code.opcode as usize).ok_or_else(|| {
1667                    anyhow::anyhow!("Failed to find op {:x}'s metadata", code.opcode)
1668                })?;
1669            if let Some(lab) = self.labels.get(&offset) {
1670                writeln!(file, "#{}", lab.name)?;
1671                unused_labels.remove(&offset);
1672            }
1673            if meta.name == "IFEND" || meta.name == "IFBLEND" || meta.name == "LOOPEND" {
1674                indent.pop();
1675                indent.pop();
1676            }
1677            write!(file, "{}", indent)?;
1678            if meta.name == "GOSUB" {
1679                if code.arg_count < 1 {
1680                    anyhow::bail!("GOSUB at least need one argument.");
1681                }
1682                let arg0 = &code.args[0];
1683                let name = format!("{:?}", &YSTBArgData(&arg0.data, arg0.encoding));
1684                write!(file, "\\{}(", name.trim_matches('"'))?;
1685                let mut first = true;
1686                for arg in &code.args[1..] {
1687                    write!(
1688                        file,
1689                        "{}{:?}",
1690                        if first {
1691                            first = false;
1692                            ""
1693                        } else {
1694                            ", "
1695                        },
1696                        &YSTBArgData(&arg.data, arg.encoding)
1697                    )?;
1698                }
1699                writeln!(file, ")")?;
1700            } else {
1701                write!(file, "{}[", meta.name)?;
1702                let mut first = true;
1703                for arg in &code.args {
1704                    if first {
1705                        first = false;
1706                    } else {
1707                        write!(file, ", ")?;
1708                    }
1709                    if meta.arguments.len() > arg.id as usize {
1710                        write!(file, "{}=", meta.arguments[arg.id as usize].name)?;
1711                    }
1712                    write!(file, "{:?}", &YSTBArgData(&arg.data, arg.encoding))?;
1713                }
1714                writeln!(file, "]")?;
1715            }
1716            if meta.name == "IF" || meta.name == "ELSE" || meta.name == "LOOP" {
1717                indent += "  ";
1718            }
1719        }
1720        let mut f = std::fs::File::create(filename)?;
1721        if encoding.is_utf8() {
1722            f.write_all(&file.data)?;
1723        } else {
1724            let s = decode_to_string(Encoding::Utf8, &file.data, true)?;
1725            let encoded = encode_string(encoding, &s, true)?;
1726            f.write_all(&encoded)?;
1727        }
1728        if !unused_labels.is_empty() {
1729            eprintln!("WARNING: Some labels not used: {:?}", unused_labels);
1730            crate::COUNTER.inc_warning();
1731        }
1732        Ok(())
1733    }
1734
1735    fn custom_import<'a>(
1736        &'a self,
1737        custom_filename: &'a str,
1738        mut file: Box<dyn WriteSeek + 'a>,
1739        encoding: Encoding,
1740        output_encoding: Encoding,
1741    ) -> Result<()> {
1742        if self.disasm {
1743            anyhow::bail!("Import is not supported for disasm mode.");
1744        }
1745        let mut f = MemWriter::new();
1746        let data = crate::utils::files::read_file(custom_filename)?;
1747        let data = decode_to_string(output_encoding, &data, true)?;
1748        let mut data: YSTBData = if self.custom_yaml {
1749            serde_yaml_ng::from_str(&data)?
1750        } else {
1751            serde_json::from_str(&data)?
1752        };
1753        f.write_all(b"YSTB")?;
1754        data.header.line_numbers_size = data.line_numbers.len() as u32;
1755        data.header.inst_entry_count = data.insts.len() as u32;
1756        data.header.inst_index_size = data.header.inst_entry_count * 4;
1757        data.header.pack(&mut f, false, encoding, &None)?;
1758        for i in data.insts.iter_mut() {
1759            i.base.arg_count = i.args.len() as u8;
1760            i.base.pack(&mut f, false, encoding, &None)?;
1761        }
1762        let arg_count = data.insts.iter().fold(0, |c, i| c + i.args.len());
1763        data.header.args_index_size = arg_count as u32 * 0xC;
1764        let mut cpos = f.pos as u64;
1765        f.pos += data.header.args_index_size as usize;
1766        let bpos = f.pos as u32;
1767        for i in data.insts.iter_mut() {
1768            let meta =
1769                self.com.opcodes.get(i.opcode as usize).ok_or_else(|| {
1770                    anyhow::anyhow!("Failed to find op {:x}'s metadata", i.opcode)
1771                })?;
1772            for arg in i.args.iter_mut() {
1773                arg.base.size = arg.data.len() as u32;
1774                f.write_struct_at(cpos, &arg.base, false, encoding, &None)?;
1775                cpos += 8;
1776                if arg.base.size == 0
1777                    || (meta.name == "RETURNCODE" && arg.base.size == 1 && arg.data[0] == b'M')
1778                {
1779                    f.write_u32_at(cpos, 0)?;
1780                    cpos += 4;
1781                    continue;
1782                }
1783                let offset = f.pos as u32 - bpos;
1784                f.write_u32_at(cpos, offset)?;
1785                cpos += 4;
1786                f.write_all(&arg.data)?;
1787            }
1788        }
1789        data.header.args_data_size = f.pos as u32 - bpos;
1790        f.write_all(&data.line_numbers)?;
1791        f.pos = 4;
1792        data.header.pack(&mut f, false, encoding, &None)?;
1793        if let Some(xor) = self.xor_key {
1794            let mut r = MemReader::new(f.into_inner());
1795            f = MemWriter::new();
1796            Self::xor(&mut r, &mut f, xor)?;
1797        }
1798        file.write_all(&f.data)?;
1799        Ok(())
1800    }
1801}