1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::{decode_to_string, encode_string};
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use serde::{Deserialize, Serialize};
10use std::io::{Read, Seek, Write};
11
12#[derive(Debug)]
13pub struct EscudeBinListBuilder {}
15
16impl EscudeBinListBuilder {
17 pub const fn new() -> Self {
19 EscudeBinListBuilder {}
20 }
21}
22
23impl ScriptBuilder for EscudeBinListBuilder {
24 fn default_encoding(&self) -> Encoding {
25 Encoding::Cp932
26 }
27
28 fn build_script(
29 &self,
30 data: Vec<u8>,
31 filename: &str,
32 encoding: Encoding,
33 _archive_encoding: Encoding,
34 config: &ExtraConfig,
35 _archive: Option<&Box<dyn Script>>,
36 ) -> Result<Box<dyn Script>> {
37 Ok(Box::new(EscudeBinList::new(
38 data, filename, encoding, config,
39 )?))
40 }
41
42 fn extensions(&self) -> &'static [&'static str] {
43 &["bin"]
44 }
45
46 fn script_type(&self) -> &'static ScriptType {
47 &ScriptType::EscudeList
48 }
49
50 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
51 if buf_len > 4 && buf.starts_with(b"LIST") {
52 return Some(255);
53 }
54 None
55 }
56
57 fn can_create_file(&self) -> bool {
58 true
59 }
60
61 fn create_file<'a>(
62 &'a self,
63 filename: &'a str,
64 writer: Box<dyn WriteSeek + 'a>,
65 encoding: Encoding,
66 file_encoding: Encoding,
67 config: &ExtraConfig,
68 ) -> Result<()> {
69 create_file(
70 filename,
71 writer,
72 encoding,
73 file_encoding,
74 config.custom_yaml,
75 )
76 }
77}
78
79#[derive(Debug)]
80pub struct EscudeBinList {
82 pub entries: Vec<ListEntry>,
84 custom_yaml: bool,
85}
86
87impl EscudeBinList {
88 pub fn new(
95 data: Vec<u8>,
96 filename: &str,
97 encoding: Encoding,
98 config: &ExtraConfig,
99 ) -> Result<Self> {
100 let mut reader = MemReader::new(data);
101 let mut magic = [0; 4];
102 reader.read_exact(&mut magic)?;
103 if &magic != b"LIST" {
104 return Err(anyhow::anyhow!("Invalid Escude list file format"));
105 }
106 let wsize = reader.read_u32()?;
107 let mut entries = Vec::new();
108 loop {
109 let current = reader.stream_position()?;
110 if current as usize >= wsize as usize + 8 {
111 break;
112 }
113 let id = reader.read_u32()?;
114 let size = reader.read_u32()?;
115 let data = reader.read_exact_vec(size as usize)?;
116 entries.push(ListEntry {
117 id: id,
118 data: ListData::Unknown(data),
119 });
120 }
121 let mut s = EscudeBinList {
122 entries,
123 custom_yaml: config.custom_yaml,
124 };
125 match s.try_decode(filename, encoding) {
126 Ok(_) => {}
127 Err(e) => {
128 eprintln!("WARN: Failed to decode Escude list: {}", e);
129 crate::COUNTER.inc_warning();
130 }
131 }
132 Ok(s)
133 }
134
135 pub fn try_decode(&mut self, filename: &str, encoding: Encoding) -> Result<()> {
140 let filename = std::path::Path::new(filename);
141 if let Some(filename) = filename.file_name() {
142 let filename = filename.to_ascii_lowercase();
143 if filename == "enum_scr.bin" {
144 for ent in self.entries.iter_mut() {
145 let id = ent.id;
146 if let ListData::Unknown(unk) = &ent.data {
147 let mut reader = MemReader::new(unk.clone());
148 let mut element_size = if id == 0 {
149 132
150 } else if id == 1 {
151 100
152 } else if id == 2 {
153 36
154 } else if id == 3 {
155 104
156 } else if id == 9999 {
157 1
158 } else {
159 return Err(anyhow::anyhow!("Unknown enum source ID: {}", id));
160 };
161 let len = unk.len();
162 let mut script_old = false;
163 if id == 0 && len % element_size != 0 {
164 element_size = 128;
165 script_old = true;
166 }
167 if len % element_size != 0 {
168 return Err(anyhow::anyhow!(
169 "Invalid enum source length: {} for ID: {}",
170 len,
171 id
172 ));
173 }
174 let count = len / element_size;
175 let data_entry = match id {
176 0 => {
177 if script_old {
178 ListData::Scr(EnumScr::Scripts2(
179 reader.read_struct_vec::<ScriptT2>(
180 count, false, encoding, &None,
181 )?,
182 ))
183 } else {
184 ListData::Scr(EnumScr::Scripts(
185 reader.read_struct_vec::<ScriptT>(
186 count, false, encoding, &None,
187 )?,
188 ))
189 }
190 }
191 1 => ListData::Scr(EnumScr::Names(
192 reader.read_struct_vec::<NameT>(count, false, encoding, &None)?,
193 )),
194 2 => ListData::Scr(EnumScr::Vars(
195 reader.read_struct_vec::<VarT>(count, false, encoding, &None)?,
196 )),
197 3 => ListData::Scr(EnumScr::Scenes(
198 reader.read_struct_vec::<SceneT>(count, false, encoding, &None)?,
199 )),
200 9999 => {
201 ListData::Unknown(unk.clone())
203 }
204 _ => return Err(anyhow::anyhow!("Unknown enum source ID: {}", id)),
205 };
206 ent.data = data_entry;
207 }
208 }
209 } else if filename == "enum_gfx.bin" {
210 for ent in self.entries.iter_mut() {
211 let id = ent.id;
212 if let ListData::Unknown(unk) = &ent.data {
213 let mut reader = MemReader::new(unk.clone());
214 let element_size = if id == 0 {
215 248
216 } else if id == 1 {
217 248
218 } else if id == 2 {
219 248
220 } else if id == 3 {
221 112
222 } else if id == 4 {
223 32
224 } else if id == 9999 {
225 1
226 } else {
227 return Err(anyhow::anyhow!("Unknown enum gfx ID: {}", id));
228 };
229 let len = unk.len();
230 if len % element_size != 0 {
231 return Err(anyhow::anyhow!(
232 "Invalid enum gfx length: {} for ID: {}",
233 len,
234 id
235 ));
236 }
237 let count = len / element_size;
238 let data_entry = match id {
239 0 => ListData::Gfx(EnumGfx::Bgs(
240 reader.read_struct_vec::<BgT>(count, false, encoding, &None)?,
241 )),
242 1 => ListData::Gfx(EnumGfx::Evs(
243 reader.read_struct_vec::<EvT>(count, false, encoding, &None)?,
244 )),
245 2 => ListData::Gfx(EnumGfx::Sts(
246 reader.read_struct_vec::<StT>(count, false, encoding, &None)?,
247 )),
248 3 => ListData::Gfx(EnumGfx::Efxs(
249 reader.read_struct_vec::<EfxT>(count, false, encoding, &None)?,
250 )),
251 4 => ListData::Gfx(EnumGfx::Locs(
252 reader.read_struct_vec::<LocT>(count, false, encoding, &None)?,
253 )),
254 9999 => {
255 ListData::Unknown(unk.clone())
257 }
258 _ => return Err(anyhow::anyhow!("Unknown enum gfx ID: {}", id)),
259 };
260 ent.data = data_entry;
261 }
262 }
263 } else if filename == "enum_snd.bin" {
264 for ent in self.entries.iter_mut() {
265 let id = ent.id;
266 if let ListData::Unknown(unk) = &ent.data {
267 let mut reader = MemReader::new(unk.clone());
268 let element_size = if id == 0 {
269 196
270 } else if id == 1 {
271 128
272 } else if id == 2 {
273 128
274 } else if id == 3 {
275 128
276 } else if id == 9999 {
277 1
278 } else {
279 return Err(anyhow::anyhow!("Unknown enum sound ID: {}", id));
280 };
281 let len = unk.len();
282 if len % element_size != 0 {
283 return Err(anyhow::anyhow!(
284 "Invalid enum sound length: {} for ID: {}",
285 len,
286 id
287 ));
288 }
289 let count = len / element_size;
290 let data_entry = match id {
291 0 => ListData::Snd(EnumSnd::Bgm(
292 reader.read_struct_vec::<BgmT>(count, false, encoding, &None)?,
293 )),
294 1 => ListData::Snd(EnumSnd::Amb(
295 reader.read_struct_vec::<AmbT>(count, false, encoding, &None)?,
296 )),
297 2 => ListData::Snd(EnumSnd::Se(
298 reader.read_struct_vec::<SeT>(count, false, encoding, &None)?,
299 )),
300 3 => ListData::Snd(EnumSnd::Sfx(
301 reader.read_struct_vec::<SfxT>(count, false, encoding, &None)?,
302 )),
303 9999 => {
304 ListData::Unknown(unk.clone())
306 }
307 _ => return Err(anyhow::anyhow!("Unknown enum sound ID: {}", id)),
308 };
309 ent.data = data_entry;
310 }
311 }
312 }
313 }
314 Ok(())
315 }
316}
317
318fn create_file<'a>(
319 custom_filename: &'a str,
320 mut writer: Box<dyn WriteSeek + 'a>,
321 encoding: Encoding,
322 output_encoding: Encoding,
323 yaml: bool,
324) -> Result<()> {
325 let input = crate::utils::files::read_file(custom_filename)?;
326 let s = decode_to_string(output_encoding, &input, true)?;
327 let entries: Vec<ListEntry> = if yaml {
328 serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
329 } else {
330 serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
331 };
332 writer.write_all(b"LIST")?;
333 writer.write_u32(0)?; let mut total_size = 0;
335 for entry in entries {
336 let cur_pos = writer.stream_position()?;
337 writer.write_u32(entry.id)?;
338 writer.write_u32(0)?; entry.data.pack(&mut writer, false, encoding, &None)?;
340 let end_pos = writer.stream_position()?;
341 let size = (end_pos - cur_pos - 8) as u32; writer.seek(std::io::SeekFrom::Start(cur_pos + 4))?; writer.write_u32(size)?;
344 writer.seek(std::io::SeekFrom::Start(end_pos))?; total_size += size + 8;
346 }
347 writer.seek(std::io::SeekFrom::Start(4))?; writer.write_u32(total_size)?;
349 writer.flush()?;
350 Ok(())
351}
352
353impl Script for EscudeBinList {
354 fn default_output_script_type(&self) -> OutputScriptType {
355 OutputScriptType::Custom
356 }
357
358 fn is_output_supported(&self, output: OutputScriptType) -> bool {
359 matches!(output, OutputScriptType::Custom)
360 }
361
362 fn default_format_type(&self) -> FormatOptions {
363 FormatOptions::None
364 }
365
366 fn custom_output_extension(&self) -> &'static str {
367 if self.custom_yaml { "yaml" } else { "json" }
368 }
369
370 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
371 let s = if self.custom_yaml {
372 serde_yaml_ng::to_string(&self.entries)
373 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
374 } else {
375 serde_json::to_string_pretty(&self.entries)
376 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
377 };
378 let mut writer = crate::utils::files::write_file(filename)?;
379 let s = encode_string(encoding, &s, false)?;
380 writer.write_all(&s)?;
381 writer.flush()?;
382 Ok(())
383 }
384
385 fn custom_import<'a>(
386 &'a self,
387 custom_filename: &'a str,
388 writer: Box<dyn WriteSeek + 'a>,
389 encoding: Encoding,
390 output_encoding: Encoding,
391 ) -> Result<()> {
392 create_file(
393 custom_filename,
394 writer,
395 encoding,
396 output_encoding,
397 self.custom_yaml,
398 )
399 }
400}
401
402#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
403pub struct ScriptT {
405 #[fstring = 64]
406 #[fstring_pad = 0x20]
407 pub file: String,
409 pub source: u32,
411 #[fstring = 64]
412 #[fstring_pad = 0x20]
413 pub title: String,
415}
416
417#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
418pub struct ScriptT2 {
420 #[fstring = 64]
421 #[fstring_pad = 0x20]
422 pub file: String,
424 #[fstring = 64]
425 #[fstring_pad = 0x20]
426 pub title: String,
428}
429
430#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
431pub struct NameT {
433 #[fstring = 64]
434 #[fstring_pad = 0x20]
435 pub text: String,
437 pub color: u32,
439 #[fstring = 32]
440 #[fstring_pad = 0x20]
441 pub face: String,
443}
444
445#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
446pub struct VarT {
448 #[fstring = 32]
450 #[fstring_pad = 0x20]
451 pub name: String,
452 pub value: u16,
454 pub flag: u16,
456}
457
458#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
459pub struct SceneT {
461 pub script: u32,
463 #[fstring = 64]
465 #[fstring_pad = 0x20]
466 pub name: String,
467 #[fstring = 32]
469 #[fstring_pad = 0x20]
470 pub thumbnail: String,
471 pub order: i32,
473}
474
475#[derive(Debug, Serialize, Deserialize, StructPack)]
476#[serde(tag = "type", content = "data")]
477pub enum EnumScr {
479 Scripts(Vec<ScriptT>),
481 Scripts2(Vec<ScriptT2>),
483 Names(Vec<NameT>),
485 Vars(Vec<VarT>),
487 Scenes(Vec<SceneT>),
489}
490
491#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
492pub struct BgT {
494 #[fstring = 32]
496 #[fstring_pad = 0x20]
497 name: String,
498 #[fstring = 64]
500 #[fstring_pad = 0x20]
501 file: String,
502 #[fstring = 128]
503 #[fstring_pad = 0x20]
504 option: String,
506 coverd: u32,
508 color: u32,
510 id: u32,
512 loc: u32,
514 order: i32,
516 link: u32,
518}
519
520#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
521pub struct EvT {
523 #[fstring = 32]
525 #[fstring_pad = 0x20]
526 name: String,
527 #[fstring = 64]
529 #[fstring_pad = 0x20]
530 file: String,
531 #[fstring = 128]
532 #[fstring_pad = 0x20]
533 option: String,
535 coverd: u32,
537 color: u32,
539 id: u32,
541 loc: u32,
543 order: i32,
545 link: u32,
547}
548
549#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
550pub struct StT {
552 #[fstring = 32]
553 #[fstring_pad = 0x20]
554 name: String,
556 #[fstring = 64]
557 #[fstring_pad = 0x20]
558 file: String,
560 #[fstring = 128]
561 #[fstring_pad = 0x20]
562 option: String,
564 coverd: u32,
566 color: u32,
568 id: u32,
570 loc: u32,
572 order: i32,
574 link: u32,
576}
577
578#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
579pub struct EfxT {
581 #[fstring = 32]
583 #[fstring_pad = 0x20]
584 name: String,
585 #[fstring = 64]
587 #[fstring_pad = 0x20]
588 file: String,
589 spot: i32,
591 dx: i32,
593 dy: i32,
595 r#loop: bool,
597 #[fvec = 3]
598 #[serde(skip, default = "exft_padding")]
599 padding: Vec<u8>,
601}
602
603fn exft_padding() -> Vec<u8> {
604 vec![0; 3]
605}
606
607#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
608pub struct Point {
610 x: i16,
612 y: i16,
614}
615
616#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
617pub struct LocT {
619 #[fvec = 8]
621 pt: Vec<Point>,
622}
623
624#[derive(Debug, Serialize, Deserialize, StructPack)]
625#[serde(tag = "type", content = "data")]
626pub enum EnumGfx {
628 Bgs(Vec<BgT>),
630 Evs(Vec<EvT>),
632 Sts(Vec<StT>),
634 Efxs(Vec<EfxT>),
636 Locs(Vec<LocT>),
638}
639
640#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
641pub struct BgmT {
643 #[fstring = 64]
644 #[fstring_pad = 0x20]
645 pub name: String,
647 #[fstring = 64]
648 #[fstring_pad = 0x20]
649 pub file: String,
651 #[fstring = 64]
652 #[fstring_pad = 0x20]
653 pub title: String,
655 pub order: i32,
657}
658
659#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
660pub struct AmbT {
662 #[fstring = 64]
663 #[fstring_pad = 0x20]
664 pub name: String,
666 #[fstring = 64]
667 #[fstring_pad = 0x20]
668 pub file: String,
670}
671
672#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
673pub struct SeT {
675 #[fstring = 64]
676 #[fstring_pad = 0x20]
677 pub name: String,
679 #[fstring = 64]
680 #[fstring_pad = 0x20]
681 pub file: String,
683}
684
685#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
686pub struct SfxT {
688 #[fstring = 64]
689 #[fstring_pad = 0x20]
690 pub name: String,
692 #[fstring = 64]
693 #[fstring_pad = 0x20]
694 pub file: String,
696}
697
698#[derive(Debug, Serialize, Deserialize, StructPack)]
699#[serde(tag = "type", content = "data")]
700pub enum EnumSnd {
702 Bgm(Vec<BgmT>),
704 Amb(Vec<AmbT>),
706 Se(Vec<SeT>),
708 Sfx(Vec<SfxT>),
710}
711
712#[derive(Debug, Serialize, Deserialize, StructPack)]
713#[serde(tag = "type", content = "data")]
714pub enum ListData {
716 Scr(EnumScr),
718 Gfx(EnumGfx),
720 Snd(EnumSnd),
722 Unknown(Vec<u8>),
724}
725
726#[derive(Debug, Serialize, Deserialize)]
727pub struct ListEntry {
729 id: u32,
730 pub data: ListData,
732}