1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use serde::ser::SerializeStruct;
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12use std::io::{Read, Seek, Write};
13
14#[derive(Debug)]
15pub struct RldScriptBuilder {}
17
18impl RldScriptBuilder {
19 pub fn new() -> Self {
21 Self {}
22 }
23}
24
25impl ScriptBuilder for RldScriptBuilder {
26 fn default_encoding(&self) -> Encoding {
27 Encoding::Cp932
28 }
29
30 fn build_script(
31 &self,
32 buf: Vec<u8>,
33 filename: &str,
34 encoding: Encoding,
35 _archive_encoding: Encoding,
36 config: &ExtraConfig,
37 _archive: Option<&Box<dyn Script>>,
38 ) -> Result<Box<dyn Script>> {
39 Ok(Box::new(RldScript::new(buf, filename, encoding, config)?))
40 }
41
42 fn extensions(&self) -> &'static [&'static str] {
43 &["rld"]
44 }
45
46 fn script_type(&self) -> &'static ScriptType {
47 &ScriptType::ExHibit
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"\0DLR") {
52 return Some(10);
53 }
54 None
55 }
56}
57
58#[derive(Debug)]
59struct XorKey {
60 xor_key: u32,
61 keys: [u32; 0x100],
62}
63
64#[derive(Debug, StructPack, StructUnpack)]
65struct Header {
66 ver: u32,
67 offset: u32,
68 count: u32,
69}
70
71#[derive(Clone, Debug, StructPack, StructUnpack)]
72struct Op {
73 op: u16,
74 init_count: u8,
75 unk: u8,
76}
77
78impl PartialEq<u16> for Op {
79 fn eq(&self, other: &u16) -> bool {
80 self.op == *other
81 }
82}
83
84impl Op {
85 pub fn str_count(&self) -> u8 {
86 self.unk & 0xF
87 }
88}
89
90#[derive(Clone, Debug)]
91struct OpExt {
92 op: Op,
93 strs: Vec<String>,
94 ints: Vec<u32>,
95}
96
97impl<'de> Deserialize<'de> for OpExt {
98 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
99 where
100 D: serde::Deserializer<'de>,
101 {
102 #[derive(Deserialize)]
103 struct OpExtHelper {
104 op: u16,
105 unk: u8,
106 strs: Vec<String>,
107 ints: Vec<u32>,
108 }
109
110 let helper = OpExtHelper::deserialize(deserializer)?;
111 let init_count = helper.ints.len() as u8;
112 let str_count = helper.strs.len() as u8;
113 let unk = (helper.unk << 4) | (str_count & 0xF);
114
115 Ok(OpExt {
116 op: Op {
117 op: helper.op,
118 init_count,
119 unk,
120 },
121 strs: helper.strs,
122 ints: helper.ints,
123 })
124 }
125}
126
127impl Serialize for OpExt {
128 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
129 let mut state = serializer.serialize_struct("OpExt", 4)?;
130 state.serialize_field("op", &self.op.op)?;
131 state.serialize_field("unk", &((self.op.unk & 0xF0) >> 4))?;
132 state.serialize_field("strs", &self.strs)?;
133 state.serialize_field("ints", &self.ints)?;
134 state.end()
135 }
136}
137
138impl StructPack for OpExt {
139 fn pack<W: Write>(
140 &self,
141 writer: &mut W,
142 big: bool,
143 encoding: Encoding,
144 info: &Option<Box<dyn std::any::Any>>,
145 ) -> Result<()> {
146 self.op.op.pack(writer, big, encoding, info)?;
147 let init_count = self.ints.len() as u8;
148 init_count.pack(writer, big, encoding, info)?;
149 let unk = (self.op.unk & 0xF0) | (self.strs.len() as u8 & 0xF);
150 unk.pack(writer, big, encoding, info)?;
151 for i in &self.ints {
152 i.pack(writer, big, encoding, info)?;
153 }
154 for s in &self.strs {
155 let encoded = encode_string(encoding, s, true)?;
156 writer.write_all(&encoded)?;
157 writer.write_u8(0)?; }
159 Ok(())
160 }
161}
162
163impl StructUnpack for OpExt {
164 fn unpack<R: Read + Seek>(
165 reader: &mut R,
166 big: bool,
167 encoding: Encoding,
168 info: &Option<Box<dyn std::any::Any>>,
169 ) -> Result<Self> {
170 let op = Op::unpack(reader, big, encoding, info)?;
171 let mut ints = Vec::with_capacity(op.init_count as usize);
172 for _ in 0..op.init_count {
173 let i = u32::unpack(reader, big, encoding, info)?;
174 ints.push(i);
175 }
176 let mut strs = Vec::with_capacity(op.str_count() as usize);
177 for _ in 0..op.str_count() {
178 let s = reader.read_cstring()?;
179 let s = decode_to_string(encoding, s.as_bytes(), true)?;
180 strs.push(s);
181 }
182 Ok(Self { op, strs, ints })
183 }
184}
185
186#[derive(Debug)]
187pub struct RldScript {
189 data: MemReader,
190 decrypted: bool,
191 xor_key: Option<XorKey>,
192 header: Header,
193 _flag: u32,
194 _tag: Option<String>,
195 ops: Vec<OpExt>,
196 is_def_chara: bool,
197 name_table: Option<BTreeMap<u32, String>>,
198 custom_yaml: bool,
199}
200
201impl RldScript {
202 pub fn new(
209 buf: Vec<u8>,
210 filename: &str,
211 encoding: Encoding,
212 config: &ExtraConfig,
213 ) -> Result<Self> {
214 let mut reader = MemReader::new(buf);
215 let mut magic = [0u8; 4];
216 reader.read_exact(&mut magic)?;
217 if &magic != b"\0DLR" {
218 return Err(anyhow::anyhow!("Invalid RLD script magic: {:?}", magic));
219 }
220 let is_def = std::path::Path::new(filename)
221 .file_stem()
222 .map(|s| s.to_ascii_lowercase() == "def")
223 .unwrap_or(false);
224 let is_def_chara = std::path::Path::new(filename)
225 .file_stem()
226 .map(|s| s.to_ascii_lowercase() == "defchara")
227 .unwrap_or(false);
228 let xor_key = if is_def {
229 if let Some(xor_key) = config.ex_hibit_rld_def_xor_key {
230 let keys = config
231 .ex_hibit_rld_def_keys
232 .as_deref()
233 .cloned()
234 .ok_or(anyhow::anyhow!("No keys provided for def RLD script"))?;
235 Some(XorKey {
236 xor_key,
237 keys: keys,
238 })
239 } else {
240 None
241 }
242 } else {
243 if let Some(xor_key) = config.ex_hibit_rld_xor_key {
244 let keys = config
245 .ex_hibit_rld_keys
246 .as_deref()
247 .cloned()
248 .ok_or(anyhow::anyhow!("No keys provided for RLD script"))?;
249 Some(XorKey {
250 xor_key,
251 keys: keys,
252 })
253 } else {
254 None
255 }
256 };
257 let header = Header::unpack(&mut reader, false, encoding, &None)?;
258 let mut decrypted = false;
259 if let Some(key) = &xor_key {
260 Self::xor(&mut reader.data, key);
261 decrypted = true;
262 }
263 let flag = reader.read_u32()?;
264 let tag = if flag == 1 {
265 let s = reader.read_cstring()?;
266 Some(decode_to_string(encoding, s.as_bytes(), true)?)
267 } else {
268 None
269 };
270 reader.pos = header.offset as usize;
271 let mut ops = Vec::with_capacity(header.count as usize);
272 for _ in 0..header.count {
273 let op = OpExt::unpack(&mut reader, false, encoding, &None)?;
274 ops.push(op);
275 }
276 let name_table = if is_def_chara {
277 None
278 } else {
279 match Self::try_load_name_table(filename, encoding, config) {
280 Ok(table) => Some(table),
281 Err(e) => {
282 eprintln!("WARN: Failed to load name table: {}", e);
283 crate::COUNTER.inc_warning();
284 None
285 }
286 }
287 };
288 Ok(Self {
289 data: reader,
290 decrypted,
291 xor_key,
292 header,
293 _flag: flag,
294 _tag: tag,
295 ops,
296 is_def_chara,
297 name_table,
298 custom_yaml: config.custom_yaml,
299 })
300 }
301
302 fn try_load_name_table(
303 filename: &str,
304 encoding: Encoding,
305 config: &ExtraConfig,
306 ) -> Result<BTreeMap<u32, String>> {
307 let mut pb = std::path::Path::new(filename).to_path_buf();
308 pb.set_file_name("defChara.rld");
309 let f = crate::utils::files::read_file(&pb)?;
310 let f = Self::new(f, &pb.to_string_lossy(), encoding, config)?;
311 Ok(f.name_table()?)
312 }
313
314 fn xor(data: &mut Vec<u8>, key: &XorKey) {
315 let mut end = data.len().min(0xFFCF);
316 end -= end % 4;
317 let mut ri = 0;
318 for i in (0x10..end).step_by(4) {
319 let en_temp = u32::from_le_bytes([data[i], data[i + 1], data[i + 2], data[i + 3]]);
320 let temp_key = key.keys[ri & 0xFF] ^ key.xor_key;
321 let de_temp = (en_temp ^ temp_key).to_le_bytes();
322 data[i] = de_temp[0];
323 data[i + 1] = de_temp[1];
324 data[i + 2] = de_temp[2];
325 data[i + 3] = de_temp[3];
326 ri += 1;
327 }
328 }
329
330 fn name_table(&self) -> Result<BTreeMap<u32, String>> {
331 let mut names = BTreeMap::new();
332 for op in &self.ops {
333 if op.op == 48 {
334 if op.strs.is_empty() {
335 return Err(anyhow::anyhow!("Op 48 has no strings"));
336 }
337 let name = op.strs[0].clone();
338 let data: Vec<_> = name.split(",").collect();
339 if data.len() < 4 {
340 return Err(anyhow::anyhow!("Op 48 has invalid data: {}", name));
341 }
342 let id = data[0].parse::<u32>()?;
343 let name = data[3].to_string();
344 names.insert(id, name);
345 }
346 }
347 Ok(names)
348 }
349
350 fn write_script<W: Write + Seek>(
351 &self,
352 mut writer: W,
353 encoding: Encoding,
354 ops: &[OpExt],
355 ) -> Result<()> {
356 writer.write_all(&self.data.data[..self.header.offset as usize])?;
357 let op_count = ops.len() as u32;
358 if op_count != self.header.count {
359 writer.write_u32_at(12, op_count)?;
360 }
361 for op in ops {
362 op.pack(&mut writer, false, encoding, &None)?;
363 }
364 if self.data.data.len() > self.data.pos {
365 writer.write_all(&self.data.data[self.data.pos..])?;
366 }
367 Ok(())
368 }
369}
370
371impl Script for RldScript {
372 fn default_output_script_type(&self) -> OutputScriptType {
373 if self.is_def_chara {
374 return OutputScriptType::Custom;
375 }
376 OutputScriptType::Json
377 }
378
379 fn is_output_supported(&self, output: OutputScriptType) -> bool {
380 if self.is_def_chara {
381 return matches!(output, OutputScriptType::Custom);
382 }
383 true
384 }
385
386 fn default_format_type(&self) -> FormatOptions {
387 FormatOptions::None
388 }
389
390 fn custom_output_extension<'a>(&'a self) -> &'a str {
391 if self.custom_yaml { "yaml" } else { "json" }
392 }
393
394 fn extract_messages(&self) -> Result<Vec<Message>> {
395 let mut messages = Vec::new();
396 for op in &self.ops {
397 if op.op == 28 {
398 if op.strs.len() < 2 {
399 return Err(anyhow::anyhow!("Op 28 has less than 2 strings"));
400 }
401 let name = if op.strs[0] == "*" {
402 if op.ints.is_empty() {
403 return Err(anyhow::anyhow!("Op 28 has no integers"));
404 }
405 let id = op.ints[0];
406 self.name_table
407 .as_ref()
408 .and_then(|table| table.get(&id).cloned())
409 } else if op.strs[0] == "$noname$" {
410 None
411 } else {
412 Some(op.strs[0].clone())
413 };
414 let text = op.strs[1].clone();
415 messages.push(Message {
416 name,
417 message: text,
418 });
419 } else if op.op == 21 || op.op == 191 {
420 eprintln!("{op:?}");
421 }
422 }
423 Ok(messages)
424 }
425
426 fn import_messages<'a>(
427 &'a self,
428 messages: Vec<Message>,
429 mut file: Box<dyn WriteSeek + 'a>,
430 _filename: &str,
431 encoding: Encoding,
432 replacement: Option<&'a ReplacementTable>,
433 ) -> Result<()> {
434 let mut ops = self.ops.clone();
435 let mut mes = messages.iter();
436 let mut mess = mes.next();
437 for op in ops.iter_mut() {
438 if op.op == 28 {
439 let m = match mess {
440 Some(m) => m,
441 None => return Err(anyhow::anyhow!("Not enough messages.")),
442 };
443 if op.strs.len() < 2 {
444 return Err(anyhow::anyhow!("Op 28 has less than 2 strings"));
445 }
446 if op.strs[0] != "*" && op.strs[0] != "$noname$" {
447 let mut name = match &m.name {
448 Some(name) => name.clone(),
449 None => {
450 return Err(anyhow::anyhow!("Message has no name"));
451 }
452 };
453 if let Some(replacement) = replacement {
454 for (k, v) in &replacement.map {
455 name = name.replace(k, v);
456 }
457 }
458 op.strs[0] = name;
459 }
460 let mut message = m.message.clone();
461 if let Some(replacement) = replacement {
462 for (k, v) in &replacement.map {
463 message = message.replace(k, v);
464 }
465 }
466 op.strs[1] = message;
467 mess = mes.next();
468 }
469 }
470 if mess.is_some() || mes.next().is_some() {
471 return Err(anyhow::anyhow!("Too many messages provided."));
472 }
473 if self.decrypted {
474 let mut writer = MemWriter::new();
475 self.write_script(&mut writer, encoding, &ops)?;
476 if let Some(key) = &self.xor_key {
477 Self::xor(&mut writer.data, key);
478 }
479 file.write_all(&writer.data)?;
480 } else {
481 self.write_script(&mut file, encoding, &ops)?;
482 }
483 Ok(())
484 }
485
486 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
487 let s = if self.is_def_chara {
488 let names = self.name_table()?;
489 if self.custom_yaml {
490 serde_yaml_ng::to_string(&names)
491 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
492 } else {
493 serde_json::to_string_pretty(&names)
494 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
495 }
496 } else {
497 if self.custom_yaml {
498 serde_yaml_ng::to_string(&self.ops)
499 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
500 } else {
501 serde_json::to_string_pretty(&self.ops)
502 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
503 }
504 };
505 let s = encode_string(encoding, &s, false)?;
506 let mut file = std::fs::File::create(filename)?;
507 file.write_all(&s)?;
508 Ok(())
509 }
510
511 fn custom_import<'a>(
512 &'a self,
513 custom_filename: &'a str,
514 mut file: Box<dyn WriteSeek + 'a>,
515 encoding: Encoding,
516 output_encoding: Encoding,
517 ) -> Result<()> {
518 let f = crate::utils::files::read_file(custom_filename)?;
519 let s = decode_to_string(output_encoding, &f, true)?;
520 let ops: Vec<OpExt> = if self.is_def_chara {
521 let mut ops = self.ops.clone();
522 let names: BTreeMap<u32, String> = if self.custom_yaml {
523 serde_yaml_ng::from_str(&s)
524 .map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
525 } else {
526 serde_json::from_str(&s)
527 .map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
528 };
529 for op in ops.iter_mut() {
530 if op.op == 48 {
531 if op.strs.is_empty() {
532 return Err(anyhow::anyhow!("Op 48 has no strings"));
533 }
534 let name = op.strs[0].clone();
535 let data: Vec<_> = name.split(",").collect();
536 if data.len() < 4 {
537 return Err(anyhow::anyhow!("Op 48 has invalid data: {}", name));
538 }
539 let id = data[0].parse::<u32>()?;
540 let name = names
541 .get(&id)
542 .cloned()
543 .unwrap_or_else(|| data[3].to_string());
544 let mut data = data.iter().map(|s| s.to_string()).collect::<Vec<_>>();
545 data[3] = name;
546 op.strs[0] = data.join(",");
547 }
548 }
549 ops
550 } else {
551 if self.custom_yaml {
552 serde_yaml_ng::from_str(&s)
553 .map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
554 } else {
555 serde_json::from_str(&s)
556 .map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
557 }
558 };
559 if self.decrypted {
560 let mut writer = MemWriter::new();
561 self.write_script(&mut writer, encoding, &ops)?;
562 if let Some(key) = &self.xor_key {
563 Self::xor(&mut writer.data, key);
564 }
565 file.write_all(&writer.data)?;
566 } else {
567 self.write_script(&mut file, encoding, &ops)?;
568 }
569 Ok(())
570 }
571}
572
573pub fn load_keys(path: Option<&String>) -> Result<Option<Box<[u32; 0x100]>>> {
575 if let Some(path) = path {
576 let f = crate::utils::files::read_file(path)?;
577 let mut reader = MemReader::new(f);
578 let mut keys = [0u32; 0x100];
579 for i in 0..0x100 {
580 keys[i] = reader.read_u32()?;
581 }
582 Ok(Some(Box::new(keys)))
583 } else {
584 Ok(None)
585 }
586}
587
588#[test]
589fn test_ser() {
590 let op = OpExt {
591 op: Op {
592 op: 28,
593 init_count: 1,
594 unk: 0x10 | 2,
595 },
596 strs: vec!["name".to_string(), "message".to_string()],
597 ints: vec![123],
598 };
599 let json = serde_json::to_string(&op).unwrap();
600 assert_eq!(
601 json,
602 r#"{"op":28,"unk":1,"strs":["name","message"],"ints":[123]}"#
603 );
604}
605
606#[test]
607fn test_de_ser() {
608 let json = r#"{"op":28,"unk":1,"strs":["name","message"],"ints":[123]}"#;
609 let op: OpExt = serde_json::from_str(json).unwrap();
610 assert_eq!(op.op.op, 28);
611 assert_eq!(op.op.init_count, 1);
612 assert_eq!(op.op.unk, 0x10 | 2);
613 assert_eq!(op.strs[0], "name");
614 assert_eq!(op.strs[1], "message");
615 assert_eq!(op.ints[0], 123);
616}