1use super::super::base::*;
2use super::disasm::*;
3use super::types::*;
4use crate::ext::io::*;
5use crate::ext::vec::*;
6use crate::scripts::base::*;
7use crate::types::*;
8use crate::utils::struct_pack::*;
9use anyhow::Result;
10use std::collections::HashMap;
11use std::io::Write;
12
13use CSInstructionCode::*;
14use CSObjectMode::*;
15use CSVariableType::*;
16
17#[derive(Clone, Debug)]
18#[allow(dead_code)]
19pub struct ECSExecutionImage {
20 file_header: EMCFileHeader,
21 exi_header: Option<Vec<u8>>,
22 header: Option<EXIHeader>,
23 image: MemReader,
24 pif_prologue: DWordArray,
25 pif_epilogue: DWordArray,
26 function_list: FunctionNameList,
27 csg_global: ECSGlobal,
28 csg_data: ECSGlobal,
29 ext_const_str: Option<TaggedRefAddressList>,
30 ext_global_ref: DWordArray,
31 ext_data_ref: DWordArray,
32 imp_global_ref: TaggedRefAddressList,
33 imp_data_ref: TaggedRefAddressList,
34 lf: String,
35}
36
37impl ECSExecutionImage {
38 pub fn new(mut reader: MemReaderRef<'_>, config: &ExtraConfig) -> Result<Self> {
39 let file_header = EMCFileHeader::unpack(&mut reader, false, Encoding::Utf8, &None)?;
40 if file_header.signagure != *b"Entis\x1a\0\0" {
41 return Err(anyhow::anyhow!("Invalid EMC file signature"));
42 }
43 let len = reader.data.len();
44 let mut exi_header = None;
45 let mut header = None;
46 let mut image = None;
47 let mut pif_prologue = None;
48 let mut pif_epilogue = None;
49 let mut function_list = None;
50 let mut csg_global = None;
51 let mut int64 = false;
52 let mut csg_data = None;
53 let mut ext_const_str = None;
54 let mut ext_global_ref = DWordArray::default();
55 let mut ext_data_ref = DWordArray::default();
56 let mut imp_global_ref = TaggedRefAddressList::default();
57 let mut imp_data_ref = TaggedRefAddressList::default();
58 while reader.pos < len {
59 if len - reader.pos < 16 {
60 break;
61 }
62 let id = reader.read_u64()?;
63 if id == 0 {
64 break;
65 }
66 let size = reader.read_u64()?;
67 match id {
68 0x2020726564616568 => {
70 let buf = reader.read_exact_vec(size as usize)?;
71 {
72 let mut sread = MemReaderRef::new(&buf);
73 header = Some(EXIHeader::unpack(&mut sread, false, Encoding::Utf8, &None)?);
74 }
75 exi_header = Some(buf);
76 if let Some(hdr) = &header {
77 if hdr.int_base == 64 {
78 int64 = true;
79 }
80 }
81 }
82 0x2020206567616D69 => {
84 image = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
85 }
86 0x6E6F6974636E7566 => {
88 pif_prologue = Some(DWordArray::unpack(
89 &mut reader,
90 false,
91 Encoding::Utf8,
92 &None,
93 )?);
94 pif_epilogue = Some(DWordArray::unpack(
95 &mut reader,
96 false,
97 Encoding::Utf8,
98 &None,
99 )?);
100 function_list = Some(FunctionNameList::unpack(
101 &mut reader,
102 false,
103 Encoding::Utf8,
104 &None,
105 )?);
106 }
107 0x20206C61626F6C67 => {
109 let count = reader.read_u32()?;
110 let mut items = Vec::with_capacity(count as usize);
111 for _ in 0..count {
112 let name =
113 WideString::unpack(&mut reader, false, Encoding::Utf16LE, &None)?.0;
114 let obj = ECSObject::read_from(&mut reader, int64)?;
115 items.push(ECSObjectItem { name, obj });
116 }
117 csg_global = Some(ECSGlobal(items));
118 }
119 0x2020202061746164 => {
121 let count = reader.read_u32()?;
122 let mut items = Vec::with_capacity(count as usize);
123 for _ in 0..count {
124 let name =
125 WideString::unpack(&mut reader, false, Encoding::Utf16LE, &None)?.0;
126 let length = reader.read_i32()?;
127 let obj = if length >= 0 {
128 let mut datas = Vec::with_capacity(length as usize);
129 for _ in 0..length {
130 let name = WideString::unpack(
131 &mut reader,
132 false,
133 Encoding::Utf16LE,
134 &None,
135 )?
136 .0;
137 let obj = ECSObject::read_from(&mut reader, int64)?;
138 datas.push(ECSObjectItem { name, obj });
139 }
140 ECSObject::Global(ECSGlobal(datas))
141 } else {
142 ECSObject::read_from(&mut reader, int64)?
143 };
144 items.push(ECSObjectItem { name, obj });
145 }
146 csg_data = Some(ECSGlobal(items));
147 }
148 0x72747374736E6F63 => {
150 ext_const_str = Some(TaggedRefAddressList::unpack(
151 &mut reader,
152 false,
153 Encoding::Utf8,
154 &None,
155 )?);
156 }
157 0x20666E696B6E696C => {
159 ext_global_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8, &None)?;
160 ext_data_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8, &None)?;
161 imp_global_ref =
162 TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8, &None)?;
163 imp_data_ref =
164 TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8, &None)?;
165 if !ext_global_ref.is_empty()
166 || !ext_data_ref.is_empty()
167 || !imp_global_ref.is_empty()
168 || !imp_data_ref.is_empty()
169 {
170 eprintln!(
171 "Warning: External/global references(linker data) are not supported and will be ignored. This may cause script rebuild errors."
172 );
173 crate::COUNTER.inc_warning();
174 }
175 }
176 0 => {
177 break;
178 }
179 _ => {
180 return Err(anyhow::anyhow!(
181 "Unknown ECSExecutionImage section ID: 0x{:016X}",
182 id
183 ));
184 }
185 }
186 }
187 Ok(Self {
188 file_header,
189 exi_header,
190 header,
191 image: image.ok_or_else(|| anyhow::anyhow!("Missing image data"))?,
192 pif_prologue: pif_prologue.ok_or_else(|| anyhow::anyhow!("Missing PIF prologue"))?,
193 pif_epilogue: pif_epilogue.ok_or_else(|| anyhow::anyhow!("Missing PIF epilogue"))?,
194 function_list: function_list.ok_or_else(|| anyhow::anyhow!("Missing function list"))?,
195 csg_global: csg_global.ok_or_else(|| anyhow::anyhow!("Missing CSG global"))?,
196 csg_data: csg_data.ok_or_else(|| anyhow::anyhow!("Missing CSG data"))?,
197 ext_const_str,
198 ext_global_ref,
199 ext_data_ref,
200 imp_global_ref,
201 imp_data_ref,
202 lf: config.entis_gls_csx_lf.clone(),
203 })
204 }
205
206 fn fix_image<'a, 'b>(
207 assembly: &ECSExecutionImageAssembly,
208 mut reader: MemReaderRef<'a>,
209 writer: &mut MemWriter,
210 commands: &HashMap<u32, &'b ECSExecutionImageCommandRecord>,
211 ) -> Result<()> {
212 for cmd in assembly.iter() {
213 if cmd.code == CsicEnter {
215 reader.pos = cmd.addr as usize + 1;
216 let name_length = reader.read_u32()?;
217 if name_length != 0x80000000 {
218 reader.pos += name_length as usize * 2;
219 } else {
220 reader.pos += 4;
221 }
222 let num_args = reader.read_i32()?;
223 if num_args == -1 {
224 let _flag = reader.read_u8()?;
225 let offset = reader.pos as i64 - cmd.addr as i64;
226 let original_addr = reader.read_i32()? as i64 + reader.pos as i64;
227 let target_cmd = commands.get(&(original_addr as u32)).ok_or_else(|| anyhow::anyhow!(
228 "Cannot find target command at address {:08X} for Enter instruction fixup at {:08X}",
229 original_addr as u32,
230 cmd.addr
231 ))?;
232 let new_addr = target_cmd.new_addr as i64 - cmd.new_addr as i64 - offset - 4;
233 writer.write_i32_at(cmd.new_addr as u64 + offset as u64, new_addr as i32)?;
234 }
235 } else if cmd.code == CsicJump {
236 reader.pos = cmd.addr as usize + 1;
238 let offset = reader.pos as i64 - cmd.addr as i64;
239 let original_addr = reader.read_i32()? as i64 + reader.pos as i64;
240 let target_cmd = commands.get(&(original_addr as u32)).ok_or_else(|| anyhow::anyhow!(
241 "Cannot find target command at address {:08X} for Jump instruction fixup at {:08X}",
242 original_addr as u32,
243 cmd.addr
244 ))?;
245 let new_addr = target_cmd.new_addr as i64 - cmd.new_addr as i64 - offset - 4;
246 writer.write_i32_at(cmd.new_addr as u64 + offset as u64, new_addr as i32)?;
247 } else if cmd.code == CsicCJump {
248 reader.pos = cmd.addr as usize + 2;
250 let offset = reader.pos as i64 - cmd.addr as i64;
251 let original_addr = reader.read_i32()? as i64 + reader.pos as i64;
252 let target_cmd = commands.get(&(original_addr as u32)).ok_or_else(|| anyhow::anyhow!(
253 "Cannot find target command at address {:08X} for CJump instruction fixup at {:08X}",
254 original_addr as u32,
255 cmd.addr
256 ))?;
257 let new_addr = target_cmd.new_addr as i64 - cmd.new_addr as i64 - offset - 4;
258 writer.write_i32_at(cmd.new_addr as u64 + offset as u64, new_addr as i32)?;
259 }
260 }
261 Ok(())
262 }
263
264 fn fix_references(
265 &mut self,
266 commands: &HashMap<u32, &ECSExecutionImageCommandRecord>,
267 ) -> Result<()> {
268 for cmd in self.pif_prologue.iter_mut() {
269 let ocmd = *cmd;
270 *cmd = commands
271 .get(&ocmd)
272 .ok_or_else(|| {
273 anyhow::anyhow!(
274 "Cannot find target command at address {:08X} for PIF prologue fixup",
275 ocmd
276 )
277 })?
278 .new_addr;
279 }
280 for cmd in self.pif_epilogue.iter_mut() {
281 let ocmd = *cmd;
282 *cmd = commands
283 .get(&ocmd)
284 .ok_or_else(|| {
285 anyhow::anyhow!(
286 "Cannot find target command at address {:08X} for PIF epilogue fixup",
287 ocmd
288 )
289 })?
290 .new_addr;
291 }
292 for func in self.function_list.iter_mut() {
293 let ocmd = func.addr;
294 func.addr = commands
295 .get(&ocmd)
296 .ok_or_else(|| {
297 anyhow::anyhow!(
298 "Cannot find target command at address {:08X} for function list fixup",
299 ocmd
300 )
301 })?
302 .new_addr;
303 }
304 Ok(())
305 }
306
307 fn save<'a>(&self, mut writer: Box<dyn Write + 'a>) -> Result<()> {
308 self.file_header
309 .pack(&mut writer, false, Encoding::Utf8, &None)?;
310 if let Some(exi_header) = &self.exi_header {
311 writer.write_u64(0x2020726564616568)?; writer.write_u64(exi_header.len() as u64)?;
313 writer.write_all(&exi_header)?;
314 }
315 writer.write_u64(0x2020206567616D69)?; writer.write_u64(self.image.data.len() as u64)?;
317 writer.write_all(&self.image.data)?;
318 writer.write_u64(0x6E6F6974636E7566)?; let mut mem = MemWriter::new();
320 self.pif_prologue
321 .pack(&mut mem, false, Encoding::Utf8, &None)?;
322 self.pif_epilogue
323 .pack(&mut mem, false, Encoding::Utf8, &None)?;
324 self.function_list
325 .pack(&mut mem, false, Encoding::Utf8, &None)?;
326 let data = mem.into_inner();
327 writer.write_u64(data.len() as u64)?;
328 writer.write_all(&data)?;
329 writer.write_u64(0x20206C61626F6C67)?; let mut mem = MemWriter::new();
331 let int64 = if let Some(hdr) = &self.header {
332 hdr.int_base == 64
333 } else {
334 false
335 };
336 mem.write_u32(self.csg_global.len() as u32)?;
337 for item in self.csg_global.iter() {
338 WideString(item.name.clone()).pack(&mut mem, false, Encoding::Utf16LE, &None)?;
339 item.obj.write_to(&mut mem, int64)?;
340 }
341 let data = mem.into_inner();
342 writer.write_u64(data.len() as u64)?;
343 writer.write_all(&data)?;
344 writer.write_u64(0x2020202061746164)?; let mut mem = MemWriter::new();
346 mem.write_u32(self.csg_data.len() as u32)?;
347 for item in self.csg_data.iter() {
348 WideString(item.name.clone()).pack(&mut mem, false, Encoding::Utf16LE, &None)?;
349 match &item.obj {
350 ECSObject::Global(g) => {
351 mem.write_i32(g.len() as i32)?;
352 for data_item in g.iter() {
353 WideString(data_item.name.clone()).pack(
354 &mut mem,
355 false,
356 Encoding::Utf16LE,
357 &None,
358 )?;
359 data_item.obj.write_to(&mut mem, int64)?;
360 }
361 }
362 _ => {
363 mem.write_u32(0x80000000)?;
364 item.obj.write_to(&mut mem, int64)?;
365 }
366 }
367 }
368 let data = mem.into_inner();
369 writer.write_u64(data.len() as u64)?;
370 writer.write_all(&data)?;
371 if let Some(ext_const_str) = &self.ext_const_str {
372 writer.write_u64(0x72747374736E6F63)?; let mut mem = MemWriter::new();
374 ext_const_str.pack(&mut mem, false, Encoding::Utf8, &None)?;
375 let data = mem.into_inner();
376 writer.write_u64(data.len() as u64)?;
377 writer.write_all(&data)?;
378 }
379 writer.write_u64(0x20666E696B6E696C)?; let mut mem = MemWriter::new();
381 self.ext_global_ref
382 .pack(&mut mem, false, Encoding::Utf8, &None)?;
383 self.ext_data_ref
384 .pack(&mut mem, false, Encoding::Utf8, &None)?;
385 self.imp_global_ref
386 .pack(&mut mem, false, Encoding::Utf8, &None)?;
387 self.imp_data_ref
388 .pack(&mut mem, false, Encoding::Utf8, &None)?;
389 let data = mem.into_inner();
390 writer.write_u64(data.len() as u64)?;
391 writer.write_all(&data)?;
392 Ok(())
393 }
394}
395
396impl ECSImage for ECSExecutionImage {
397 fn disasm<'a>(&self, writer: Box<dyn Write + 'a>) -> Result<()> {
398 let mut disasm = ECSExecutionImageDisassembler::new(
399 self.image.to_ref(),
400 self.ext_const_str.as_ref(),
401 Some(writer),
402 );
403 disasm.execute()?;
404 Ok(())
405 }
406
407 fn export(&self) -> Result<Vec<Message>> {
408 let mut disasm = ECSExecutionImageDisassembler::new(
409 self.image.to_ref(),
410 self.ext_const_str.as_ref(),
411 None,
412 );
413 disasm.execute()?;
414 let mut messages = Vec::new();
415 let assembly = disasm.assembly.clone();
416 let mut name = None;
417 let mut pre_is_mess = false;
418 let mut string_stack = Vec::new();
419 let mut message = String::new();
420 for cmd in assembly.iter() {
421 if cmd.code == CsicLoad {
422 disasm.stream.pos = cmd.addr as usize + 1;
423 let csom = disasm.read_csom()?;
424 let csvt = disasm.read_csvt()?;
425 if csom == CsomImmediate && csvt == CsvtString {
426 let text = disasm.get_string_literal()?;
427 string_stack.push(text);
428 }
429 } else if cmd.code == CsicCall {
430 disasm.stream.pos = cmd.addr as usize + 1;
431 let _csom = disasm.read_csom()?;
432 let num_args = disasm.stream.read_i32()?;
433 let func_name =
434 WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
435 let mut is_mess = false;
436 if num_args == 1 {
437 if func_name == "SceneTitle" {
438 if string_stack.is_empty() {
439 return Err(anyhow::anyhow!(
440 "SceneTitle call without string argument at {:08X}",
441 cmd.addr
442 ));
443 }
444 messages.push(Message::new(string_stack[0].clone(), None));
445 } else if func_name == "Mess" {
446 is_mess = true;
447 if string_stack.is_empty() {
448 return Err(anyhow::anyhow!(
449 "Mess call without string argument at {:08X}",
450 cmd.addr
451 ));
452 }
453 if string_stack[0].starts_with("@") {
454 string_stack.clear();
455 continue;
456 }
457 message.push_str(string_stack[0].as_str());
458 }
459 } else if num_args == 2 {
460 if func_name == "Talk" {
461 if string_stack.is_empty() {
462 return Err(anyhow::anyhow!(
463 "Talk call without string argument at {:08X}",
464 cmd.addr
465 ));
466 }
467 if string_stack[0] == "心の声" {
468 string_stack.clear();
469 continue;
471 }
472 name = Some(string_stack[0].clone());
473 } else if func_name == "AddSelect" {
474 if string_stack.is_empty() {
475 return Err(anyhow::anyhow!(
476 "AddSelect call without string argument at {:08X}",
477 cmd.addr
478 ));
479 }
480 messages.push(Message::new(string_stack[0].clone(), None));
481 }
482 }
483 if pre_is_mess && !is_mess {
484 messages.push(Message::new(message.replace(&self.lf, "\n"), name.take()));
485 message.clear();
486 }
487 string_stack.clear();
488 pre_is_mess = is_mess;
489 }
490 }
491 Ok(messages)
492 }
493
494 fn export_multi(&self) -> Result<HashMap<String, Vec<Message>>> {
495 let mut key = String::from("global");
496 let mut messages = HashMap::new();
497 let mut disasm = ECSExecutionImageDisassembler::new(
498 self.image.to_ref(),
499 self.ext_const_str.as_ref(),
500 None,
501 );
502 disasm.execute()?;
503 let assembly = disasm.assembly.clone();
504 let mut name = None;
505 let mut pre_is_mess = false;
506 let mut pre_is_enter = false;
507 let mut string_stack = Vec::new();
508 let mut message = String::new();
509 let mut pre_enter_name = String::new();
510 for cmd in assembly.iter() {
511 let is_enter = cmd.code == CsicEnter;
512 if cmd.code == CsicLoad {
513 disasm.stream.pos = cmd.addr as usize + 1;
514 let csom = disasm.read_csom()?;
515 let csvt = disasm.read_csvt()?;
516 if csom == CsomImmediate && csvt == CsvtString {
517 let text = disasm.get_string_literal()?;
518 string_stack.push(text);
519 }
520 } else if cmd.code == CsicCall {
521 disasm.stream.pos = cmd.addr as usize + 1;
522 let csom = disasm.read_csom()?;
523 let num_args = disasm.stream.read_i32()?;
524 let func_name =
525 WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
526 let mut is_mess = false;
527 if num_args == 1 {
528 if func_name == "SceneTitle" {
529 if string_stack.is_empty() {
530 return Err(anyhow::anyhow!(
531 "SceneTitle call without string argument at {:08X}",
532 cmd.addr
533 ));
534 }
535 messages
536 .entry(key.clone())
537 .or_insert_with(|| Vec::new())
538 .push(Message::new(string_stack[0].clone(), None));
539 } else if func_name == "Mess" {
540 is_mess = true;
541 if string_stack.is_empty() {
542 return Err(anyhow::anyhow!(
543 "Mess call without string argument at {:08X}",
544 cmd.addr
545 ));
546 }
547 if string_stack[0].starts_with("@") {
548 eprintln!(
549 "Skipping control string at 0x{:08x}: {}",
550 cmd.addr, string_stack[0]
551 );
552 string_stack.clear();
553 continue;
554 }
555 message.push_str(string_stack[0].as_str());
556 }
557 } else if num_args == 2 {
558 if func_name == "Talk" {
559 if string_stack.is_empty() {
560 return Err(anyhow::anyhow!(
561 "Talk call without string argument at {:08X}",
562 cmd.addr
563 ));
564 }
565 if string_stack[0] == "心の声" {
566 string_stack.clear();
568 continue;
569 }
570 name = Some(string_stack[0].clone());
571 } else if func_name == "AddSelect" {
572 if string_stack.is_empty() {
573 return Err(anyhow::anyhow!(
574 "AddSelect call without string argument at {:08X}",
575 cmd.addr
576 ));
577 }
578 messages
579 .entry(key.clone())
580 .or_insert_with(|| Vec::new())
581 .push(Message::new(string_stack[0].clone(), None));
582 }
583 } else if num_args == 0 && csom == CsomAuto && func_name == "ScenarioEnter" {
584 if pre_is_enter {
585 key = pre_enter_name.clone();
586 } else {
587 key = "global".to_string();
588 }
589 }
590 if pre_is_mess && !is_mess {
591 messages
592 .entry(key.clone())
593 .or_insert_with(|| Vec::new())
594 .push(Message::new(message.replace(&self.lf, "\n"), name.take()));
595 message.clear();
596 }
597 pre_is_mess = is_mess;
598 string_stack.clear();
599 } else if is_enter {
600 disasm.stream.pos = cmd.addr as usize + 1;
601 let name =
602 WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
603 let num_args = disasm.stream.read_i32()?;
604 if num_args == 0 {
605 pre_enter_name = name.clone();
606 }
607 }
608 pre_is_enter = is_enter;
609 }
610 Ok(messages)
611 }
612
613 fn export_all(&self) -> Result<Vec<String>> {
614 let mut disasm = ECSExecutionImageDisassembler::new(
615 self.image.to_ref(),
616 self.ext_const_str.as_ref(),
617 None,
618 );
619 disasm.execute()?;
620 let mut messages = Vec::new();
621 let assembly = disasm.assembly.clone();
622 for cmd in assembly.iter() {
623 if cmd.code == CsicLoad {
624 disasm.stream.pos = cmd.addr as usize + 1;
625 let csom = disasm.read_csom()?;
626 let csvt = disasm.read_csvt()?;
627 if csom == CsomImmediate && csvt == CsvtString {
628 let text = disasm.get_string_literal()?;
629 messages.push(text);
630 }
631 }
632 }
633 Ok(messages)
634 }
635
636 fn import<'a>(
637 &self,
638 messages: Vec<Message>,
639 file: Box<dyn WriteSeek + 'a>,
640 replacement: Option<&'a ReplacementTable>,
641 ) -> Result<()> {
642 let mut cloned = self.clone();
643 let mut disasm = ECSExecutionImageDisassembler::new(
644 self.image.to_ref(),
645 self.ext_const_str.as_ref(),
646 None,
647 );
648 disasm.execute()?;
649 let mut assembly = disasm.assembly.clone();
650 let mut index = 0;
651 let mut dumped_index = 0;
652 let mut new_image = MemWriter::new();
653 let mut pre_is_mess = false;
654 let mut first_mess_index = None;
655 let mut last_mess_index = None;
656 let mut message_iter = messages.into_iter();
657 let mut mess = message_iter.next();
658 while index < assembly.len() {
659 let cmd = assembly[index].clone();
660 if cmd.code == CsicCall {
661 disasm.stream.pos = cmd.addr as usize + 1;
662 let csom = disasm.read_csom()?;
663 let num_args = disasm.stream.read_i32()?;
664 let func_name =
665 WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
666 let mut is_mess = false;
667 if csom == CsomAuto && num_args == 1 && func_name == "Mess" {
668 is_mess = true;
669 if first_mess_index.is_none() {
670 first_mess_index = Some(index);
671 }
672 last_mess_index = Some(index);
673 }
674 if pre_is_mess && !is_mess {
675 let first_index = first_mess_index
676 .ok_or(anyhow::anyhow!("Internal error: first_mess_index is None"))?;
677 let last_index = last_mess_index
678 .ok_or(anyhow::anyhow!("Internal error: last_mess_index is None"))?;
679 let pre_index = first_index - 1;
681 while dumped_index < pre_index {
682 let tcmd = &mut assembly[dumped_index];
683 tcmd.new_addr = new_image.pos as u32;
684 new_image.write_from(
686 &mut disasm.stream,
687 tcmd.addr as u64,
688 tcmd.size as u64,
689 )?;
690 dumped_index += 1;
691 }
692 let post_index = last_index + 1;
694 let mut message = mess
695 .take()
696 .ok_or_else(|| {
697 anyhow::anyhow!("Not enough messages to import for Mess command.")
698 })?
699 .message;
700 mess = message_iter.next();
701 if let Some(repl) = replacement {
702 for (k, v) in repl.map.iter() {
703 message = message.replace(k, v);
704 }
705 }
706 for i in (first_index..=last_index).step_by(3) {
707 let tcmd = assembly[i - 1].clone();
708 disasm.stream.pos = tcmd.addr as usize + 1;
709 let lcsom = disasm.read_csom()?;
710 let lcsvt = disasm.read_csvt()?;
711 if lcsom != CsomImmediate || lcsvt != CsvtString {
712 return Err(anyhow::anyhow!(
713 "Invalid load command before Mess at {:08X}.",
714 tcmd.addr
715 ));
716 }
717 }
718 let mes_list: Vec<_> = message
719 .replace("\n", &self.lf)
720 .split(&self.lf)
721 .map(|s| s.to_string())
722 .collect();
723 let mut new_assembly = Vec::new();
724 let mes_count = mes_list.len();
725 let mut tmp_index = pre_index;
726 for i in 0..mes_count {
727 let mut mes = mes_list[i].clone();
728 if i < mes_count - 1 {
729 mes.push_str(&self.lf);
730 }
731 let mut tcmd = if tmp_index <= post_index {
732 let data = assembly[tmp_index].clone();
733 tmp_index += 1;
734 if data.code != CsicLoad {
735 return Err(anyhow::anyhow!(
736 "Internal error: expected Load command at {:08X}.",
737 data.addr
738 ));
739 }
740 data
741 } else {
742 ECSExecutionImageCommandRecord {
743 code: CsicLoad,
744 addr: u32::MAX,
745 size: 0,
746 new_addr: 0,
747 }
748 };
749 tcmd.new_addr = new_image.pos as u32;
750 new_image.write_u8(CsicLoad.into())?;
751 new_image.write_u8(CsomImmediate.into())?;
752 new_image.write_u8(CsvtString.into())?;
753 WideString(mes).pack(&mut new_image, false, Encoding::Utf8, &None)?;
754 new_assembly.push(tcmd);
755 let mut tcmd = if tmp_index <= post_index {
756 let data = assembly[tmp_index].clone();
757 tmp_index += 1;
758 if data.code != CsicCall {
759 return Err(anyhow::anyhow!(
760 "Expected Call command at {:08X}.",
761 data.addr
762 ));
763 }
764 data
765 } else {
766 ECSExecutionImageCommandRecord {
767 code: CsicCall,
768 addr: u32::MAX,
769 size: 0,
770 new_addr: 0,
771 }
772 };
773 tcmd.new_addr = new_image.pos as u32;
774 new_image.write_u8(CsicCall.into())?;
775 new_image.write_u8(CsomAuto.into())?;
776 new_image.write_i32(1)?; WideString("Mess".to_string()).pack(
778 &mut new_image,
779 false,
780 Encoding::Utf16LE,
781 &None,
782 )?;
783 new_assembly.push(tcmd);
784 let mut tcmd = if tmp_index <= post_index {
785 let data = assembly[tmp_index].clone();
786 tmp_index += 1;
787 if data.code != CsicFree {
788 return Err(anyhow::anyhow!(
789 "Expected Free command at {:08X}.",
790 data.addr
791 ));
792 }
793 data
794 } else {
795 ECSExecutionImageCommandRecord {
796 code: CsicFree,
797 addr: u32::MAX,
798 size: 0,
799 new_addr: 0,
800 }
801 };
802 tcmd.new_addr = new_image.pos as u32;
803 new_image.write_u8(CsicFree.into())?;
804 new_assembly.push(tcmd);
805 }
806 let ori_count = post_index - pre_index + 1;
807 let new_count = new_assembly.len();
808 dumped_index += new_count;
809 index = (index as isize + (new_count as isize - ori_count as isize)) as usize;
810 last_mess_index = None;
811 first_mess_index = None;
812 assembly.splice(pre_index..post_index + 1, new_assembly);
813 }
814 if csom == CsomAuto && num_args == 2 && func_name == "Talk" {
815 if index < 2 {
816 return Err(anyhow::anyhow!(
817 "No enough load command at {:08x}.",
818 cmd.addr
819 ));
820 }
821 let pre_index = index - 2;
822 while dumped_index < pre_index {
823 let tcmd = &mut assembly[dumped_index];
824 tcmd.new_addr = new_image.pos as u32;
825 new_image.write_from(
827 &mut disasm.stream,
828 tcmd.addr as u64,
829 tcmd.size as u64,
830 )?;
831 dumped_index += 1;
832 }
833 let tcmd = &mut assembly[pre_index];
834 tcmd.new_addr = new_image.pos as u32;
835 disasm.stream.pos = tcmd.addr as usize + 1;
836 let lcsom = disasm.read_csom()?;
837 let lcsvt = disasm.read_csvt()?;
838 if lcsom != CsomImmediate || lcsvt != CsvtString {
839 return Err(anyhow::anyhow!(
840 "Invalid load command before Talk at {:08X}.",
841 tcmd.addr
842 ));
843 }
844 let original_name = disasm.get_string_literal()?;
845 let name = if original_name == "心の声" {
846 original_name
847 } else {
848 let mut name = mess.as_mut().map(|s| s.name.take()).flatten().ok_or(
849 anyhow::anyhow!("No available name for Talk at {:08X}.", cmd.addr),
850 )?;
851 if let Some(repl) = replacement {
852 for (k, v) in repl.map.iter() {
853 name = name.replace(k, v);
854 }
855 }
856 name
857 };
858 new_image.write_u8(CsicLoad.into())?;
859 new_image.write_u8(lcsom.into())?;
860 new_image.write_u8(lcsvt.into())?;
861 WideString(name).pack(&mut new_image, false, Encoding::Utf8, &None)?;
862 dumped_index += 1;
863 while dumped_index <= index {
864 let tcmd = &mut assembly[dumped_index];
865 tcmd.new_addr = new_image.pos as u32;
866 new_image.write_from(
868 &mut disasm.stream,
869 tcmd.addr as u64,
870 tcmd.size as u64,
871 )?;
872 dumped_index += 1;
873 }
874 } else if csom == CsomAuto && num_args == 2 && func_name == "AddSelect" {
875 if index < 2 {
876 return Err(anyhow::anyhow!(
877 "No enough load command at {:08x}.",
878 cmd.addr
879 ));
880 }
881 let pre_index = index - 2;
882 while dumped_index < pre_index {
883 let tcmd = &mut assembly[dumped_index];
884 tcmd.new_addr = new_image.pos as u32;
885 new_image.write_from(
887 &mut disasm.stream,
888 tcmd.addr as u64,
889 tcmd.size as u64,
890 )?;
891 dumped_index += 1;
892 }
893 let tcmd = &mut assembly[pre_index];
894 tcmd.new_addr = new_image.pos as u32;
895 disasm.stream.pos = tcmd.addr as usize + 1;
896 let lcsom = disasm.read_csom()?;
897 let lcsvt = disasm.read_csvt()?;
898 if lcsom != CsomImmediate || lcsvt != CsvtString {
899 return Err(anyhow::anyhow!(
900 "Invalid load command before AddSelect at {:08X}.",
901 tcmd.addr
902 ));
903 }
904 let mut message = mess
905 .take()
906 .ok_or_else(|| {
907 anyhow::anyhow!(
908 "No available message for AddSelect at {:08X}.",
909 cmd.addr
910 )
911 })?
912 .message;
913 mess = message_iter.next();
914 if let Some(repl) = replacement {
915 for (k, v) in repl.map.iter() {
916 message = message.replace(k, v);
917 }
918 }
919 new_image.write_u8(CsicLoad.into())?;
920 new_image.write_u8(lcsom.into())?;
921 new_image.write_u8(lcsvt.into())?;
922 WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
923 dumped_index += 1;
924 while dumped_index <= index {
925 let tcmd = &mut assembly[dumped_index];
926 tcmd.new_addr = new_image.pos as u32;
927 new_image.write_from(
929 &mut disasm.stream,
930 tcmd.addr as u64,
931 tcmd.size as u64,
932 )?;
933 dumped_index += 1;
934 }
935 } else if csom == CsomAuto && num_args == 1 && func_name == "SceneTitle" {
936 if index < 1 {
937 return Err(anyhow::anyhow!(
938 "No enough load command at {:08x}.",
939 cmd.addr
940 ));
941 }
942 let pre_index = index - 1;
943 while dumped_index < pre_index {
944 let tcmd = &mut assembly[dumped_index];
945 tcmd.new_addr = new_image.pos as u32;
946 new_image.write_from(
948 &mut disasm.stream,
949 tcmd.addr as u64,
950 tcmd.size as u64,
951 )?;
952 dumped_index += 1;
953 }
954 let tcmd = &mut assembly[pre_index];
955 tcmd.new_addr = new_image.pos as u32;
956 disasm.stream.pos = tcmd.addr as usize + 1;
957 let lcsom = disasm.read_csom()?;
958 let lcsvt = disasm.read_csvt()?;
959 if lcsom != CsomImmediate || lcsvt != CsvtString {
960 return Err(anyhow::anyhow!(
961 "Invalid load command before SceneTitle at {:08X}.",
962 tcmd.addr
963 ));
964 }
965 let mut message = mess
966 .take()
967 .ok_or_else(|| {
968 anyhow::anyhow!(
969 "No available message for SceneTitle at {:08X}.",
970 cmd.addr
971 )
972 })?
973 .message;
974 mess = message_iter.next();
975 if let Some(repl) = replacement {
976 for (k, v) in repl.map.iter() {
977 message = message.replace(k, v);
978 }
979 }
980 new_image.write_u8(CsicLoad.into())?;
981 new_image.write_u8(lcsom.into())?;
982 new_image.write_u8(lcsvt.into())?;
983 WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
984 dumped_index += 1;
985 while dumped_index <= index {
986 let tcmd = &mut assembly[dumped_index];
987 tcmd.new_addr = new_image.pos as u32;
988 new_image.write_from(
990 &mut disasm.stream,
991 tcmd.addr as u64,
992 tcmd.size as u64,
993 )?;
994 dumped_index += 1;
995 }
996 }
997 pre_is_mess = is_mess;
998 }
999 index += 1;
1000 }
1001 while dumped_index < assembly.len() {
1002 let tcmd = &mut assembly[dumped_index];
1003 tcmd.new_addr = new_image.pos as u32;
1004 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1006 dumped_index += 1;
1007 }
1008 if mess.is_some() || message_iter.next().is_some() {
1009 return Err(anyhow::anyhow!("Too many messages to import."));
1010 }
1011 let commands: HashMap<u32, &ECSExecutionImageCommandRecord> =
1012 assembly.iter().map(|c| (c.addr, c)).collect();
1013 Self::fix_image(&assembly, disasm.stream.clone(), &mut new_image, &commands)?;
1014 cloned.image = MemReader::new(new_image.into_inner());
1015 cloned.fix_references(&commands)?;
1016 cloned.save(file)?;
1017 Ok(())
1018 }
1019
1020 fn import_multi<'a>(
1021 &self,
1022 mut messages: HashMap<String, Vec<Message>>,
1023 file: Box<dyn WriteSeek + 'a>,
1024 replacement: Option<&'a ReplacementTable>,
1025 ) -> Result<()> {
1026 let mut cloned = self.clone();
1027 let mut key = String::from("global");
1028 let mut disasm = ECSExecutionImageDisassembler::new(
1029 self.image.to_ref(),
1030 self.ext_const_str.as_ref(),
1031 None,
1032 );
1033 disasm.execute()?;
1034 let mut assembly = disasm.assembly.clone();
1035 let mut index = 0;
1036 let mut dumped_index = 0;
1037 let mut new_image = MemWriter::new();
1038 let mut pre_is_enter = false;
1039 let mut pre_enter_name = String::new();
1040 let mut pre_is_mess = false;
1041 let mut first_mess_index = None;
1042 let mut last_mess_index = None;
1043 while index < assembly.len() {
1044 let cmd = assembly[index].clone();
1045 let is_enter = cmd.code == CsicEnter;
1046 if cmd.code == CsicCall {
1047 disasm.stream.pos = cmd.addr as usize + 1;
1048 let csom = disasm.read_csom()?;
1049 let num_args = disasm.stream.read_i32()?;
1050 let func_name =
1051 WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
1052 let mut is_mess = false;
1053 if csom == CsomAuto && num_args == 1 && func_name == "Mess" {
1054 is_mess = true;
1055 if first_mess_index.is_none() {
1056 first_mess_index = Some(index);
1057 }
1058 last_mess_index = Some(index);
1059 }
1060 if pre_is_mess && !is_mess {
1061 let first_index = first_mess_index
1062 .ok_or(anyhow::anyhow!("Internal error: first_mess_index is None"))?;
1063 let last_index = last_mess_index
1064 .ok_or(anyhow::anyhow!("Internal error: last_mess_index is None"))?;
1065 let pre_index = first_index - 1;
1067 while dumped_index < pre_index {
1068 let tcmd = &mut assembly[dumped_index];
1069 tcmd.new_addr = new_image.pos as u32;
1070 new_image.write_from(
1072 &mut disasm.stream,
1073 tcmd.addr as u64,
1074 tcmd.size as u64,
1075 )?;
1076 dumped_index += 1;
1077 }
1078 let post_index = last_index + 1;
1080 let mut message = messages
1081 .get_mut(&key)
1082 .and_then(|messages| messages.pop_first())
1083 .ok_or(anyhow::anyhow!(
1084 "No available message for Mess at {:08X}.",
1085 cmd.addr
1086 ))?
1087 .message;
1088 if let Some(repl) = replacement {
1089 for (k, v) in repl.map.iter() {
1090 message = message.replace(k, v);
1091 }
1092 }
1093 for i in (first_index..=last_index).step_by(3) {
1094 let tcmd = assembly[i - 1].clone();
1095 disasm.stream.pos = tcmd.addr as usize + 1;
1096 let lcsom = disasm.read_csom()?;
1097 let lcsvt = disasm.read_csvt()?;
1098 if lcsom != CsomImmediate || lcsvt != CsvtString {
1099 return Err(anyhow::anyhow!(
1100 "Invalid load command before Mess at {:08X}.",
1101 tcmd.addr
1102 ));
1103 }
1104 }
1105 let mes_list: Vec<_> = message
1106 .replace("\n", &self.lf)
1107 .split(&self.lf)
1108 .map(|s| s.to_string())
1109 .collect();
1110 let mut new_assembly = Vec::new();
1111 let mes_count = mes_list.len();
1112 let mut tmp_index = pre_index;
1113 for i in 0..mes_count {
1114 let mut mes = mes_list[i].clone();
1115 if i < mes_count - 1 {
1116 mes.push_str(&self.lf);
1117 }
1118 let mut tcmd = if tmp_index <= post_index {
1119 let data = assembly[tmp_index].clone();
1120 tmp_index += 1;
1121 if data.code != CsicLoad {
1122 return Err(anyhow::anyhow!(
1123 "Internal error: expected Load command at {:08X}.",
1124 data.addr
1125 ));
1126 }
1127 data
1128 } else {
1129 ECSExecutionImageCommandRecord {
1130 code: CsicLoad,
1131 addr: u32::MAX,
1132 size: 0,
1133 new_addr: 0,
1134 }
1135 };
1136 tcmd.new_addr = new_image.pos as u32;
1137 new_image.write_u8(CsicLoad.into())?;
1138 new_image.write_u8(CsomImmediate.into())?;
1139 new_image.write_u8(CsvtString.into())?;
1140 WideString(mes).pack(&mut new_image, false, Encoding::Utf8, &None)?;
1141 new_assembly.push(tcmd);
1142 let mut tcmd = if tmp_index <= post_index {
1143 let data = assembly[tmp_index].clone();
1144 tmp_index += 1;
1145 if data.code != CsicCall {
1146 return Err(anyhow::anyhow!(
1147 "Expected Call command at {:08X}.",
1148 data.addr
1149 ));
1150 }
1151 data
1152 } else {
1153 ECSExecutionImageCommandRecord {
1154 code: CsicCall,
1155 addr: u32::MAX,
1156 size: 0,
1157 new_addr: 0,
1158 }
1159 };
1160 tcmd.new_addr = new_image.pos as u32;
1161 new_image.write_u8(CsicCall.into())?;
1162 new_image.write_u8(CsomAuto.into())?;
1163 new_image.write_i32(1)?; WideString("Mess".to_string()).pack(
1165 &mut new_image,
1166 false,
1167 Encoding::Utf16LE,
1168 &None,
1169 )?;
1170 new_assembly.push(tcmd);
1171 let mut tcmd = if tmp_index <= post_index {
1172 let data = assembly[tmp_index].clone();
1173 tmp_index += 1;
1174 if data.code != CsicFree {
1175 return Err(anyhow::anyhow!(
1176 "Expected Free command at {:08X}.",
1177 data.addr
1178 ));
1179 }
1180 data
1181 } else {
1182 ECSExecutionImageCommandRecord {
1183 code: CsicFree,
1184 addr: u32::MAX,
1185 size: 0,
1186 new_addr: 0,
1187 }
1188 };
1189 tcmd.new_addr = new_image.pos as u32;
1190 new_image.write_u8(CsicFree.into())?;
1191 new_assembly.push(tcmd);
1192 }
1193 let ori_count = post_index - pre_index + 1;
1194 let new_count = new_assembly.len();
1195 dumped_index += new_count;
1196 index = (index as isize + (new_count as isize - ori_count as isize)) as usize;
1197 last_mess_index = None;
1198 first_mess_index = None;
1199 assembly.splice(pre_index..post_index + 1, new_assembly);
1200 }
1201 if csom == CsomAuto && num_args == 2 && func_name == "Talk" {
1202 if index < 2 {
1203 return Err(anyhow::anyhow!(
1204 "No enough load command at {:08x}.",
1205 cmd.addr
1206 ));
1207 }
1208 let pre_index = index - 2;
1209 while dumped_index < pre_index {
1210 let tcmd = &mut assembly[dumped_index];
1211 tcmd.new_addr = new_image.pos as u32;
1212 new_image.write_from(
1214 &mut disasm.stream,
1215 tcmd.addr as u64,
1216 tcmd.size as u64,
1217 )?;
1218 dumped_index += 1;
1219 }
1220 let tcmd = &mut assembly[pre_index];
1221 tcmd.new_addr = new_image.pos as u32;
1222 disasm.stream.pos = tcmd.addr as usize + 1;
1223 let lcsom = disasm.read_csom()?;
1224 let lcsvt = disasm.read_csvt()?;
1225 if lcsom != CsomImmediate || lcsvt != CsvtString {
1226 return Err(anyhow::anyhow!(
1227 "Invalid load command before Talk at {:08X}.",
1228 tcmd.addr
1229 ));
1230 }
1231 let original_name = disasm.get_string_literal()?;
1232 let name = if original_name == "心の声" {
1233 original_name
1234 } else {
1235 let mut name = messages
1236 .get_mut(&key)
1237 .and_then(|messages| messages.first_mut().map(|m| m.name.take()))
1238 .flatten()
1239 .ok_or(anyhow::anyhow!(
1240 "No available name message for Talk at {:08X}.",
1241 cmd.addr
1242 ))?;
1243 if let Some(repl) = replacement {
1244 for (k, v) in repl.map.iter() {
1245 name = name.replace(k, v);
1246 }
1247 }
1248 name
1249 };
1250 new_image.write_u8(CsicLoad.into())?;
1251 new_image.write_u8(lcsom.into())?;
1252 new_image.write_u8(lcsvt.into())?;
1253 WideString(name).pack(&mut new_image, false, Encoding::Utf8, &None)?;
1254 dumped_index += 1;
1255 while dumped_index <= index {
1256 let tcmd = &mut assembly[dumped_index];
1257 tcmd.new_addr = new_image.pos as u32;
1258 new_image.write_from(
1260 &mut disasm.stream,
1261 tcmd.addr as u64,
1262 tcmd.size as u64,
1263 )?;
1264 dumped_index += 1;
1265 }
1266 } else if csom == CsomAuto && num_args == 2 && func_name == "AddSelect" {
1267 if index < 2 {
1268 return Err(anyhow::anyhow!(
1269 "No enough load command at {:08x}.",
1270 cmd.addr
1271 ));
1272 }
1273 let pre_index = index - 2;
1274 while dumped_index < pre_index {
1275 let tcmd = &mut assembly[dumped_index];
1276 tcmd.new_addr = new_image.pos as u32;
1277 new_image.write_from(
1279 &mut disasm.stream,
1280 tcmd.addr as u64,
1281 tcmd.size as u64,
1282 )?;
1283 dumped_index += 1;
1284 }
1285 let tcmd = &mut assembly[pre_index];
1286 tcmd.new_addr = new_image.pos as u32;
1287 disasm.stream.pos = tcmd.addr as usize + 1;
1288 let lcsom = disasm.read_csom()?;
1289 let lcsvt = disasm.read_csvt()?;
1290 if lcsom != CsomImmediate || lcsvt != CsvtString {
1291 return Err(anyhow::anyhow!(
1292 "Invalid load command before AddSelect at {:08X}.",
1293 tcmd.addr
1294 ));
1295 }
1296 let mut message = messages
1297 .get_mut(&key)
1298 .and_then(|messages| messages.pop_first())
1299 .ok_or(anyhow::anyhow!(
1300 "No available message for AddSelect at {:08X}.",
1301 cmd.addr
1302 ))?
1303 .message;
1304 if let Some(repl) = replacement {
1305 for (k, v) in repl.map.iter() {
1306 message = message.replace(k, v);
1307 }
1308 }
1309 new_image.write_u8(CsicLoad.into())?;
1310 new_image.write_u8(lcsom.into())?;
1311 new_image.write_u8(lcsvt.into())?;
1312 WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
1313 dumped_index += 1;
1314 while dumped_index <= index {
1315 let tcmd = &mut assembly[dumped_index];
1316 tcmd.new_addr = new_image.pos as u32;
1317 new_image.write_from(
1319 &mut disasm.stream,
1320 tcmd.addr as u64,
1321 tcmd.size as u64,
1322 )?;
1323 dumped_index += 1;
1324 }
1325 } else if csom == CsomAuto && num_args == 0 && func_name == "ScenarioEnter" {
1326 if pre_is_enter {
1327 key = pre_enter_name.clone();
1328 } else {
1329 key = "global".to_string();
1330 }
1331 } else if csom == CsomAuto && num_args == 1 && func_name == "SceneTitle" {
1332 if index < 1 {
1333 return Err(anyhow::anyhow!(
1334 "No enough load command at {:08x}.",
1335 cmd.addr
1336 ));
1337 }
1338 let pre_index = index - 1;
1339 while dumped_index < pre_index {
1340 let tcmd = &mut assembly[dumped_index];
1341 tcmd.new_addr = new_image.pos as u32;
1342 new_image.write_from(
1344 &mut disasm.stream,
1345 tcmd.addr as u64,
1346 tcmd.size as u64,
1347 )?;
1348 dumped_index += 1;
1349 }
1350 let tcmd = &mut assembly[pre_index];
1351 tcmd.new_addr = new_image.pos as u32;
1352 disasm.stream.pos = tcmd.addr as usize + 1;
1353 let lcsom = disasm.read_csom()?;
1354 let lcsvt = disasm.read_csvt()?;
1355 if lcsom != CsomImmediate || lcsvt != CsvtString {
1356 return Err(anyhow::anyhow!(
1357 "Invalid load command before SceneTitle at {:08X}.",
1358 tcmd.addr
1359 ));
1360 }
1361 let mut message = messages
1362 .get_mut(&key)
1363 .and_then(|messages| messages.pop_first())
1364 .ok_or(anyhow::anyhow!(
1365 "No available message for SceneTitle at {:08X}.",
1366 cmd.addr
1367 ))?
1368 .message;
1369 if let Some(repl) = replacement {
1370 for (k, v) in repl.map.iter() {
1371 message = message.replace(k, v);
1372 }
1373 }
1374 new_image.write_u8(CsicLoad.into())?;
1375 new_image.write_u8(lcsom.into())?;
1376 new_image.write_u8(lcsvt.into())?;
1377 WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
1378 dumped_index += 1;
1379 while dumped_index <= index {
1380 let tcmd = &mut assembly[dumped_index];
1381 tcmd.new_addr = new_image.pos as u32;
1382 new_image.write_from(
1384 &mut disasm.stream,
1385 tcmd.addr as u64,
1386 tcmd.size as u64,
1387 )?;
1388 dumped_index += 1;
1389 }
1390 }
1391 pre_is_mess = is_mess;
1392 } else if is_enter {
1393 disasm.stream.pos = cmd.addr as usize + 1;
1394 let original_name =
1395 WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
1396 let num_args = disasm.stream.read_i32()?;
1397 if num_args == 0 {
1398 pre_enter_name = original_name.clone();
1399 }
1400 }
1401 pre_is_enter = is_enter;
1402 index += 1;
1403 }
1404 while dumped_index < assembly.len() {
1405 let tcmd = &mut assembly[dumped_index];
1406 tcmd.new_addr = new_image.pos as u32;
1407 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1409 dumped_index += 1;
1410 }
1411 for (s, mes) in messages {
1412 if !mes.is_empty() {
1413 return Err(anyhow::anyhow!(
1414 "Not all messages were used for key '{}', {} remaining.",
1415 s,
1416 mes.len()
1417 ));
1418 }
1419 }
1420 let commands: HashMap<u32, &ECSExecutionImageCommandRecord> =
1421 assembly.iter().map(|c| (c.addr, c)).collect();
1422 Self::fix_image(&assembly, disasm.stream.clone(), &mut new_image, &commands)?;
1423 cloned.image = MemReader::new(new_image.into_inner());
1424 cloned.fix_references(&commands)?;
1425 cloned.save(file)?;
1426 Ok(())
1427 }
1428
1429 fn import_all<'a>(&self, messages: Vec<String>, file: Box<dyn WriteSeek + 'a>) -> Result<()> {
1430 let mut cloned = self.clone();
1431 let mut mess = messages.into_iter();
1432 let mut mes = mess.next();
1433 let mut disasm = ECSExecutionImageDisassembler::new(
1434 self.image.to_ref(),
1435 self.ext_const_str.as_ref(),
1436 None,
1437 );
1438 disasm.execute()?;
1439 let mut assembly = disasm.assembly.clone();
1440 let mut new_image = MemWriter::new();
1441 for cmd in assembly.iter_mut() {
1442 cmd.new_addr = new_image.pos as u32;
1443 if cmd.code == CsicLoad {
1444 disasm.stream.pos = cmd.addr as usize + 1;
1445 let csom = disasm.read_csom()?;
1446 let csvt = disasm.read_csvt()?;
1447 if csom == CsomImmediate && csvt == CsvtString {
1448 let code: u8 = CsicLoad.into();
1449 let csom: u8 = csom.into();
1450 let csvt: u8 = csvt.into();
1451 let s = match mes.take() {
1452 Some(v) => WideString(v),
1453 None => {
1454 return Err(anyhow::anyhow!(
1455 "Not enough messages to import, ran out at instruction address {:08X}",
1456 cmd.addr
1457 ));
1458 }
1459 };
1460 mes = mess.next();
1461 new_image.write_u8(code)?;
1462 new_image.write_u8(csom)?;
1463 new_image.write_u8(csvt)?;
1464 s.pack(&mut new_image, false, Encoding::Utf8, &None)?;
1465 continue;
1466 }
1467 }
1468 new_image.write_from(&mut disasm.stream, cmd.addr as u64, cmd.size as u64)?;
1470 }
1471 let commands: HashMap<u32, &ECSExecutionImageCommandRecord> =
1472 assembly.iter().map(|c| (c.addr, c)).collect();
1473 Self::fix_image(&assembly, disasm.stream.clone(), &mut new_image, &commands)?;
1474 cloned.image = MemReader::new(new_image.into_inner());
1475 cloned.fix_references(&commands)?;
1476 cloned.save(file)?;
1477 Ok(())
1478 }
1479}