1use super::super::CSXScriptV2FullVer;
2use super::super::base::*;
3use super::disasm::*;
4use super::types::*;
5use crate::ext::io::*;
6use crate::ext::vec::*;
7use crate::scripts::base::*;
8use crate::types::*;
9use crate::utils::struct_pack::*;
10use anyhow::Result;
11use std::collections::HashMap;
12use std::io::{Seek, Write};
13
14const ID_HEADER: u64 = 0x2020726564616568; const ID_IMAGE: u64 = 0x2020206567616D69;
16const ID_IMAGE_GLOBAL: u64 = 0x6C626F6C67676D69;
17const ID_IMAGE_CONST: u64 = 0x74736E6F63676D69;
18const ID_IMAGE_SHARED: u64 = 0x6572616873676D69;
19const ID_CLASS_INFO: u64 = 0x666E697373616C63;
20const ID_FUNCTION: u64 = 0x6E6F6974636E7566;
21const ID_INIT_NAKED_FUNC: u64 = 0x636E666E74696E69;
22const ID_FUNC_INFO: u64 = 0x6F666E69636E7566;
23const ID_SYMBOL_INFO: u64 = 0x666E696C626D7973;
24const ID_GLOBAL: u64 = 0x20206C61626F6C67;
25const ID_DATA: u64 = 0x2020202061746164;
26const ID_CONST_STRING: u64 = 0x72747374736E6F63;
27const ID_LINK_INFO: u64 = 0x20666E696B6E696C;
28const ID_LINK_INFO_EX: u64 = 0x343678656B6E696C;
29const ID_REF_FUNC: u64 = 0x20636E7566666572;
30const ID_REF_CODE: u64 = 0x2065646F63666572;
31const ID_REF_CLASS: u64 = 0x7373616C63666572;
32const ID_IMPORT_NATIVE_FUNC: u64 = 0x766974616E706D69;
33
34#[derive(Clone, Debug)]
35#[allow(unused)]
36pub struct ECSExecutionImage {
37 file_header: FileHeader,
38 section_header: SectionHeader,
39 image: MemReader,
40 image_global: Option<MemReader>,
41 image_const: Option<MemReader>,
42 image_shared: Option<MemReader>,
43 section_class_info: SectionClassInfo,
44 section_function: SectionFunction,
45 section_init_naked_func: SectionInitNakedFunc,
46 section_func_info: SectionFuncInfo,
47 section_symbol_info: Option<SectionSymbolInfo>,
48 section_global: Option<SectionGlobal>,
49 section_data: Option<SectionData>,
50 section_const_string: SectionConstString,
51 section_link_info: Option<SectionLinkInfo>,
52 section_link_info_ex: Option<SectionLinkInfoEx>,
53 section_ref_func: Option<SectionRefFunc>,
54 section_ref_code: Option<SectionRefCode>,
55 section_ref_class: Option<SectionRefClass>,
56 section_import_native_func: SectionImportNativeFunc,
57 no_part_label: bool,
58}
59
60impl ECSExecutionImage {
61 pub fn new(reader: MemReaderRef<'_>, config: &ExtraConfig) -> Result<Self> {
62 if let Some(ver) = config.entis_gls_csx_v2_ver {
63 match ver {
64 CSXScriptV2FullVer::V3 => Self::inner_new(reader, config, 3),
65 CSXScriptV2FullVer::V2 => Self::inner_new(reader, config, 2),
66 }
67 } else {
68 match Self::inner_new(reader.clone(), config, 3) {
69 Ok(img) => Ok(img),
70 Err(_) => Self::inner_new(reader, config, 2),
71 }
72 }
73 }
74
75 fn inner_new(mut reader: MemReaderRef<'_>, config: &ExtraConfig, ver: u32) -> Result<Self> {
76 let file_header = FileHeader::unpack(&mut reader, false, Encoding::Utf8, &None)?;
77 if file_header.signagure != *b"Entis\x1a\0\0" {
78 return Err(anyhow::anyhow!("Invalid EMC file signature"));
79 }
80 if !file_header.format_desc.starts_with(b"Cotopha Image file") {
81 return Err(anyhow::anyhow!("Invalid EMC file format description"));
82 }
83 let mut section_header = SectionHeader::default();
84 section_header.full_ver = ver;
85 let len = reader.data.len();
86 let mut image = None;
87 let mut image_global = None;
88 let mut image_const = None;
89 let mut image_shared = None;
90 let mut section_class_info = None;
91 let mut section_function = None;
92 let mut section_init_naked_func = None;
93 let mut section_func_info = None;
94 let mut section_symbol_info = None;
95 let mut section_global = None;
96 let mut section_data = None;
97 let mut section_const_string = None;
98 let mut section_link_info = None;
99 let mut section_link_info_ex = None;
100 let mut section_ref_func = None;
101 let mut section_ref_code = None;
102 let mut section_ref_class = None;
103 let mut section_import_native_func = None;
104 while reader.pos < len {
105 if len - reader.pos < 16 {
106 break;
107 }
108 let id = reader.read_u64()?;
109 if id == 0 {
110 break;
111 }
112 let size = reader.read_u64()?;
113 let pos = reader.pos;
114 match id {
115 ID_HEADER => {
116 let mut mem = StreamRegion::with_size(&mut reader, size)?;
117 section_header = SectionHeader::unpack(&mut mem, false, Encoding::Utf8, &None)?;
118 section_header.full_ver = ver;
119 }
120 ID_IMAGE => {
121 image = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
122 }
123 ID_IMAGE_GLOBAL => {
124 image_global = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
125 }
126 ID_IMAGE_CONST => {
127 image_const = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
128 }
129 ID_IMAGE_SHARED => {
130 image_shared = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
131 }
132 ID_CLASS_INFO => {
133 let mut mem = StreamRegion::with_size(&mut reader, size)?;
134 section_class_info = Some(SectionClassInfo::unpack(
135 &mut mem,
136 false,
137 Encoding::Utf8,
138 &Some(Box::new(section_header.clone())),
139 )?);
140 if mem.stream_position()? != size {
141 eprintln!(
142 "WARNING: Some data is not parsed in ECSExecutionImage::CLASS_INFO"
143 );
144 crate::COUNTER.inc_warning();
145 }
146 }
147 ID_FUNCTION => {
148 let mut mem = StreamRegion::with_size(&mut reader, size)?;
149 section_function = Some(SectionFunction::unpack(
150 &mut mem,
151 false,
152 Encoding::Utf8,
153 &Some(Box::new(section_header.clone())),
154 )?);
155 if mem.stream_position()? != size {
156 eprintln!(
157 "WARNING: Some data is not parsed in ECSExecutionImage::FUNCTION"
158 );
159 crate::COUNTER.inc_warning();
160 }
161 }
162 ID_INIT_NAKED_FUNC => {
163 let mut mem = StreamRegion::with_size(&mut reader, size)?;
164 section_init_naked_func = Some(SectionInitNakedFunc::unpack(
165 &mut mem,
166 false,
167 Encoding::Utf8,
168 &Some(Box::new(section_header.clone())),
169 )?);
170 if mem.stream_position()? != size {
171 eprintln!(
172 "WARNING: Some data is not parsed in ECSExecutionImage::INIT_NAKED_FUNC"
173 );
174 crate::COUNTER.inc_warning();
175 }
176 }
177 ID_FUNC_INFO => {
178 let mut mem = StreamRegion::with_size(&mut reader, size)?;
179 section_func_info = Some(SectionFuncInfo::unpack(
180 &mut mem,
181 false,
182 Encoding::Utf8,
183 &Some(Box::new(section_header.clone())),
184 )?);
185 if mem.stream_position()? != size {
186 eprintln!(
187 "WARNING: Some data is not parsed in ECSExecutionImage::FUNC_INFO"
188 );
189 crate::COUNTER.inc_warning();
190 }
191 }
192 ID_SYMBOL_INFO => {
193 let mut mem = StreamRegion::with_size(&mut reader, size)?;
194 section_symbol_info = Some(SectionSymbolInfo::unpack(
195 &mut mem,
196 false,
197 Encoding::Utf8,
198 &Some(Box::new(section_header.clone())),
199 )?);
200 if mem.stream_position()? != size {
201 eprintln!(
202 "WARNING: Some data is not parsed in ECSExecutionImage::SYMBOL_INFO"
203 );
204 crate::COUNTER.inc_warning();
205 }
206 }
207 ID_GLOBAL => {
208 let mut mem = StreamRegion::with_size(&mut reader, size)?;
209 section_global = Some(SectionGlobal::unpack(
210 &mut mem,
211 false,
212 Encoding::Utf8,
213 &Some(Box::new(section_header.clone())),
214 )?);
215 if mem.stream_position()? != size {
216 eprintln!("WARNING: Some data is not parsed in ECSExecutionImage::GLOBAL");
217 crate::COUNTER.inc_warning();
218 }
219 }
220 ID_DATA => {
221 let mut mem = StreamRegion::with_size(&mut reader, size)?;
222 section_data = Some(SectionData::unpack(
223 &mut mem,
224 false,
225 Encoding::Utf8,
226 &Some(Box::new(section_header.clone())),
227 )?);
228 if mem.stream_position()? != size {
229 eprintln!("WARNING: Some data is not parsed in ECSExecutionImage::DATA");
230 crate::COUNTER.inc_warning();
231 }
232 }
233 ID_CONST_STRING => {
234 let mut mem = StreamRegion::with_size(&mut reader, size)?;
235 section_const_string = Some(SectionConstString::unpack(
236 &mut mem,
237 false,
238 Encoding::Utf8,
239 &Some(Box::new(section_header.clone())),
240 )?);
241 if mem.stream_position()? != size {
242 eprintln!(
243 "WARNING: Some data is not parsed in ECSExecutionImage::CONST_STRING"
244 );
245 crate::COUNTER.inc_warning();
246 }
247 }
248 ID_LINK_INFO => {
249 let mut mem = StreamRegion::with_size(&mut reader, size)?;
250 section_link_info = Some(SectionLinkInfo::unpack(
251 &mut mem,
252 false,
253 Encoding::Utf8,
254 &Some(Box::new(section_header.clone())),
255 )?);
256 if mem.stream_position()? != size {
257 eprintln!(
258 "WARNING: Some data is not parsed in ECSExecutionImage::LINK_INFO"
259 );
260 crate::COUNTER.inc_warning();
261 }
262 }
263 ID_LINK_INFO_EX => {
264 let mut mem = StreamRegion::with_size(&mut reader, size)?;
265 section_link_info_ex = Some(SectionLinkInfoEx::unpack(
266 &mut mem,
267 false,
268 Encoding::Utf8,
269 &Some(Box::new(section_header.clone())),
270 )?);
271 if mem.stream_position()? != size {
272 eprintln!(
273 "WARNING: Some data is not parsed in ECSExecutionImage::LINK_INFO_EX"
274 );
275 crate::COUNTER.inc_warning();
276 }
277 }
278 ID_REF_FUNC => {
279 let mut mem = StreamRegion::with_size(&mut reader, size)?;
280 section_ref_func = Some(SectionRefFunc::unpack(
281 &mut mem,
282 false,
283 Encoding::Utf8,
284 &Some(Box::new(section_header.clone())),
285 )?);
286 if mem.stream_position()? != size {
287 eprintln!(
288 "WARNING: Some data is not parsed in ECSExecutionImage::REF_FUNC"
289 );
290 crate::COUNTER.inc_warning();
291 }
292 }
293 ID_REF_CODE => {
294 let mut mem = StreamRegion::with_size(&mut reader, size)?;
295 section_ref_code = Some(SectionRefCode::unpack(
296 &mut mem,
297 false,
298 Encoding::Utf8,
299 &Some(Box::new(section_header.clone())),
300 )?);
301 if mem.stream_position()? != size {
302 eprintln!(
303 "WARNING: Some data is not parsed in ECSExecutionImage::REF_CODE"
304 );
305 crate::COUNTER.inc_warning();
306 }
307 }
308 ID_REF_CLASS => {
309 let mut mem = StreamRegion::with_size(&mut reader, size)?;
310 section_ref_class = Some(SectionRefClass::unpack(
311 &mut mem,
312 false,
313 Encoding::Utf8,
314 &Some(Box::new(section_header.clone())),
315 )?);
316 if mem.stream_position()? != size {
317 eprintln!(
318 "WARNING: Some data is not parsed in ECSExecutionImage::REF_CLASS"
319 );
320 crate::COUNTER.inc_warning();
321 }
322 }
323 ID_IMPORT_NATIVE_FUNC => {
324 let mut mem = StreamRegion::with_size(&mut reader, size)?;
325 section_import_native_func = Some(SectionImportNativeFunc::unpack(
326 &mut mem,
327 false,
328 Encoding::Utf8,
329 &Some(Box::new(section_header.clone())),
330 )?);
331 if mem.stream_position()? != size {
332 eprintln!(
333 "WARNING: Some data is not parsed in ECSExecutionImage::IMPORT_NATIVE_FUNC"
334 );
335 crate::COUNTER.inc_warning();
336 }
337 }
338 0 => {
339 break;
340 }
341 _ => {
342 return Err(anyhow::anyhow!(
343 "Unknown ECSExecutionImage section ID: 0x{:016X}",
344 id
345 ));
346 }
347 }
348 reader.pos = pos + size as usize;
349 }
350 Ok(Self {
351 file_header,
352 section_header,
353 image: image.ok_or_else(|| anyhow::anyhow!("Missing image data"))?,
354 image_global,
355 image_const,
356 image_shared,
357 section_class_info: section_class_info
358 .ok_or_else(|| anyhow::anyhow!("Missing class info section"))?,
359 section_function: section_function
360 .ok_or_else(|| anyhow::anyhow!("Missing function section"))?,
361 section_init_naked_func: section_init_naked_func
362 .ok_or_else(|| anyhow::anyhow!("Missing init naked func section"))?,
363 section_func_info: section_func_info
364 .ok_or_else(|| anyhow::anyhow!("Missing func info section"))?,
365 section_symbol_info,
366 section_global,
367 section_data,
368 section_const_string: section_const_string
369 .ok_or_else(|| anyhow::anyhow!("Missing const string section"))?,
370 section_link_info,
371 section_link_info_ex,
372 section_ref_func,
373 section_ref_code,
374 section_ref_class,
375 section_import_native_func: section_import_native_func
376 .ok_or_else(|| anyhow::anyhow!("Missing import native func section"))?,
377 no_part_label: config.entis_gls_csx_no_part_label,
378 })
379 }
380
381 fn fix_image<'a, 'b>(
382 assembly: &ECSExecutionImageAssembly,
383 disasm: &mut ECSExecutionImageDisassembler<'a>,
384 writer: &mut MemWriter,
385 commands: &HashMap<u32, &'b ECSExecutionImageCommandRecord>,
386 ) -> Result<()> {
387 for cmd in assembly.iter() {
388 if cmd.code == CsicEnter {
389 disasm.stream.pos = cmd.addr as usize + 1;
390 let name_length = disasm.stream.read_u32()?;
391 if name_length != 0x80000000 {
392 disasm.stream.pos += name_length as usize * 2;
393 } else {
394 disasm.stream.pos += 4;
395 }
396 let num_args = disasm.stream.read_i32()?;
397 if num_args == -1 {
398 let _flag = disasm.stream.read_u8()?;
399 let offset = disasm.stream.pos as i64 - cmd.addr as i64;
400 let original_addr = disasm.stream.read_i32()? as i64 + disasm.stream.pos as i64;
401 let target_cmd = commands.get(&(original_addr as u32)).ok_or_else(|| anyhow::anyhow!(
402 "Cannot find target command at address {:08X} for Enter instruction fixup at {:08X}",
403 original_addr as u32,
404 cmd.addr
405 ))?;
406 let new_addr = target_cmd.new_addr as i64 - cmd.new_addr as i64 - offset - 4;
407 writer.write_i32_at(cmd.new_addr as u64 + offset as u64, new_addr as i32)?;
408 }
409 } else if matches!(cmd.code, CsicJump | CodeJumpOffset32) {
410 disasm.stream.pos = cmd.addr as usize + 1;
411 let offset = disasm.stream.pos as i64 - cmd.addr as i64;
412 let original_addr = disasm.stream.read_i32()? as i64 + disasm.stream.pos as i64;
413 let target_cmd = commands.get(&(original_addr as u32)).ok_or_else(|| anyhow::anyhow!(
414 "Cannot find target command at address {:08X} for {:?} instruction fixup at {:08X}",
415 original_addr as u32,
416 cmd.code,
417 cmd.addr
418 ))?;
419 let new_addr = target_cmd.new_addr as i64 - cmd.new_addr as i64 - offset - 4;
420 writer.write_i32_at(cmd.new_addr as u64 + offset as u64, new_addr as i32)?;
421 } else if matches!(cmd.code, CsicCJump | CodeCJumpOffset32 | CodeCNJumpOffset32) {
422 disasm.stream.pos = cmd.addr as usize + 2;
423 let offset = disasm.stream.pos as i64 - cmd.addr as i64;
424 let original_addr = disasm.stream.read_i32()? as i64 + disasm.stream.pos as i64;
425 let target_cmd = commands.get(&(original_addr as u32)).ok_or_else(|| anyhow::anyhow!(
426 "Cannot find target command at address {:08X} for {:?} instruction fixup at {:08X}",
427 original_addr as u32,
428 cmd.code,
429 cmd.addr
430 ))?;
431 let new_addr = target_cmd.new_addr as i64 - cmd.new_addr as i64 - offset - 4;
432 writer.write_i32_at(cmd.new_addr as u64 + offset as u64, new_addr as i32)?;
433 } else if cmd.code == CsicExCall {
434 disasm.stream.pos = cmd.addr as usize + 1;
435 let _arg_count = disasm.stream.read_i32()?;
436 let csom = disasm.read_csom()?;
437 let csvt = disasm.read_csvt()?;
438 if csom == CsomImmediate && csvt == CsvtInteger {
439 let offset = disasm.stream.pos as i64 - cmd.addr as i64;
440 let addr = disasm.stream.read_u32()?;
441 let target_cmd = commands.get(&addr).ok_or_else(|| anyhow::anyhow!(
442 "Cannot find target command at address {:08X} for ExCall instruction fixup at {:08X}",
443 addr,
444 cmd.addr
445 ))?;
446 let new_addr = target_cmd.new_addr;
447 writer.write_u32_at(cmd.new_addr as u64 + offset as u64, new_addr)?;
448 }
449 } else if cmd.code == CodeCallImm32 {
450 disasm.stream.pos = cmd.addr as usize + 1;
451 let offset = disasm.stream.pos as i64 - cmd.addr as i64;
452 let addr = disasm.stream.read_u32()?;
453 let target_cmd = commands.get(&addr).ok_or_else(|| anyhow::anyhow!(
454 "Cannot find target command at address {:08X} for CallImm32 instruction fixup at {:08X}",
455 addr,
456 cmd.addr
457 ))?;
458 let new_addr = target_cmd.new_addr;
459 writer.write_u32_at(cmd.new_addr as u64 + offset as u64, new_addr)?;
460 }
461 }
462 Ok(())
463 }
464
465 fn fix_references(
466 &mut self,
467 commands: &HashMap<u32, &ECSExecutionImageCommandRecord>,
468 ) -> Result<()> {
469 let mut list: Vec<u32> = commands.iter().map(|(&k, _)| k).collect();
470 list.sort();
471 for cmd in self.section_function.prologue.iter_mut() {
472 let ocmd = *cmd;
473 if let Some(tcmd) = commands.get(&ocmd) {
474 *cmd = tcmd.new_addr;
475 } else {
476 let pre_one_idx = match list.binary_search(&ocmd) {
477 Ok(idx) => idx,
478 Err(idx) => {
479 if idx == 0 {
480 idx
481 } else {
482 idx - 1
483 }
484 }
485 };
486 let tcmd = &commands[&list[pre_one_idx]];
487 if !tcmd.internal || tcmd.size + tcmd.addr < ocmd {
488 return Err(anyhow::anyhow!(
489 "Cannot find target command at address {:08X} for PIF prologue fixup",
490 ocmd
491 ));
492 }
493 let offset = tcmd.new_addr as i64 - tcmd.addr as i64;
494 *cmd = (ocmd as i64 + offset) as u32;
495 }
496 }
497 for cmd in self.section_function.epilogue.iter_mut() {
498 let ocmd = *cmd;
499 *cmd = commands
500 .get(&ocmd)
501 .ok_or_else(|| {
502 anyhow::anyhow!(
503 "Cannot find target command at address {:08X} for PIF epilogue fixup",
504 ocmd
505 )
506 })?
507 .new_addr;
508 }
509 for func in self.section_function.func_names.iter_mut() {
510 let ocmd = func.address;
511 func.address = commands
512 .get(&ocmd)
513 .ok_or_else(|| {
514 anyhow::anyhow!(
515 "Cannot find target command at address {:08X} for function names list fixup",
516 ocmd
517 )
518 })?
519 .new_addr;
520 }
521 for cmd in self.section_init_naked_func.naked_prologue.iter_mut() {
522 let ocmd = *cmd;
523 *cmd = commands
524 .get(&ocmd)
525 .ok_or_else(|| {
526 anyhow::anyhow!(
527 "Cannot find target command at address {:08X} for NNF prologue fixup",
528 ocmd
529 )
530 })?
531 .new_addr;
532 }
533 for cmd in self.section_init_naked_func.naked_epilogue.iter_mut() {
534 let ocmd = *cmd;
535 *cmd = commands
536 .get(&ocmd)
537 .ok_or_else(|| {
538 anyhow::anyhow!(
539 "Cannot find target command at address {:08X} for NNF epilogue fixup",
540 ocmd
541 )
542 })?
543 .new_addr;
544 }
545 for func in self.section_func_info.functions.iter_mut() {
546 let ocmd = func.header.address;
547 func.header.address = commands
548 .get(&ocmd)
549 .ok_or_else(|| {
550 anyhow::anyhow!(
551 "Cannot find target command at address {:08X} for function info list fixup",
552 ocmd
553 )
554 })?
555 .new_addr;
556 if func.header.bytes != u32::MAX {
557 let end_ocmd = ocmd + func.header.bytes;
558 let end_tcmd = commands
559 .get(&end_ocmd)
560 .ok_or_else(|| {
561 anyhow::anyhow!(
562 "Cannot find target command at address {:08X} for function info list fixup",
563 end_ocmd
564 )
565 })?.new_addr;
566 func.header.bytes = end_tcmd - func.header.address;
567 }
568 }
569 Ok(())
570 }
571
572 fn save<'a>(&self, mut writer: Box<dyn Write + 'a>) -> Result<()> {
573 self.file_header
574 .pack(&mut writer, false, Encoding::Utf8, &None)?;
575 if self.section_header.header_size > 0 {
576 let mut mem = MemWriter::new();
577 self.section_header
578 .pack(&mut mem, false, Encoding::Utf8, &None)?;
579 writer.write_u64(ID_HEADER)?;
580 writer.write_u64(mem.data.len() as u64)?;
581 writer.write_all(&mem.into_inner())?;
582 }
583 writer.write_u64(ID_IMAGE)?;
584 writer.write_u64(self.image.data.len() as u64)?;
585 writer.write_all(&self.image.data)?;
586 if let Some(img_global) = &self.image_global {
587 writer.write_u64(ID_IMAGE_GLOBAL)?;
588 writer.write_u64(img_global.data.len() as u64)?;
589 writer.write_all(&img_global.data)?;
590 }
591 if let Some(img_const) = &self.image_const {
592 writer.write_u64(ID_IMAGE_CONST)?;
593 writer.write_u64(img_const.data.len() as u64)?;
594 writer.write_all(&img_const.data)?;
595 }
596 if let Some(img_shared) = &self.image_shared {
597 writer.write_u64(ID_IMAGE_SHARED)?;
598 writer.write_u64(img_shared.data.len() as u64)?;
599 writer.write_all(&img_shared.data)?;
600 }
601 writer.write_u64(ID_CLASS_INFO)?;
602 let mut mem = MemWriter::new();
603 self.section_class_info.pack(
604 &mut mem,
605 false,
606 Encoding::Utf8,
607 &Some(Box::new(self.section_header.clone())),
608 )?;
609 writer.write_u64(mem.data.len() as u64)?;
610 writer.write_all(&mem.into_inner())?;
611 writer.write_u64(ID_FUNCTION)?;
612 let mut mem = MemWriter::new();
613 self.section_function.pack(
614 &mut mem,
615 false,
616 Encoding::Utf8,
617 &Some(Box::new(self.section_header.clone())),
618 )?;
619 writer.write_u64(mem.data.len() as u64)?;
620 writer.write_all(&mem.into_inner())?;
621 writer.write_u64(ID_INIT_NAKED_FUNC)?;
622 let mut mem = MemWriter::new();
623 self.section_init_naked_func.pack(
624 &mut mem,
625 false,
626 Encoding::Utf8,
627 &Some(Box::new(self.section_header.clone())),
628 )?;
629 writer.write_u64(mem.data.len() as u64)?;
630 writer.write_all(&mem.into_inner())?;
631 writer.write_u64(ID_FUNC_INFO)?;
632 let mut mem = MemWriter::new();
633 self.section_func_info.pack(
634 &mut mem,
635 false,
636 Encoding::Utf8,
637 &Some(Box::new(self.section_header.clone())),
638 )?;
639 writer.write_u64(mem.data.len() as u64)?;
640 writer.write_all(&mem.into_inner())?;
641 if let Some(section_symbol_info) = &self.section_symbol_info {
642 writer.write_u64(ID_SYMBOL_INFO)?;
643 let mut mem = MemWriter::new();
644 section_symbol_info.pack(
645 &mut mem,
646 false,
647 Encoding::Utf8,
648 &Some(Box::new(self.section_header.clone())),
649 )?;
650 writer.write_u64(mem.data.len() as u64)?;
651 writer.write_all(&mem.into_inner())?;
652 }
653 if let Some(section_global) = &self.section_global {
654 writer.write_u64(ID_GLOBAL)?;
655 let mut mem = MemWriter::new();
656 section_global.pack(
657 &mut mem,
658 false,
659 Encoding::Utf8,
660 &Some(Box::new(self.section_header.clone())),
661 )?;
662 writer.write_u64(mem.data.len() as u64)?;
663 writer.write_all(&mem.into_inner())?;
664 }
665 if let Some(section_data) = &self.section_data {
666 writer.write_u64(ID_DATA)?;
667 let mut mem = MemWriter::new();
668 section_data.pack(
669 &mut mem,
670 false,
671 Encoding::Utf8,
672 &Some(Box::new(self.section_header.clone())),
673 )?;
674 writer.write_u64(mem.data.len() as u64)?;
675 writer.write_all(&mem.into_inner())?;
676 }
677 writer.write_u64(ID_CONST_STRING)?;
678 let mut mem = MemWriter::new();
679 self.section_const_string.pack(
680 &mut mem,
681 false,
682 Encoding::Utf8,
683 &Some(Box::new(self.section_header.clone())),
684 )?;
685 writer.write_u64(mem.data.len() as u64)?;
686 writer.write_all(&mem.into_inner())?;
687 if let Some(section_link_info) = &self.section_link_info {
688 writer.write_u64(ID_LINK_INFO)?;
689 let mut mem = MemWriter::new();
690 section_link_info.pack(
691 &mut mem,
692 false,
693 Encoding::Utf8,
694 &Some(Box::new(self.section_header.clone())),
695 )?;
696 writer.write_u64(mem.data.len() as u64)?;
697 writer.write_all(&mem.into_inner())?;
698 }
699 if let Some(section_link_info_ex) = &self.section_link_info_ex {
700 writer.write_u64(ID_LINK_INFO_EX)?;
701 let mut mem = MemWriter::new();
702 section_link_info_ex.pack(
703 &mut mem,
704 false,
705 Encoding::Utf8,
706 &Some(Box::new(self.section_header.clone())),
707 )?;
708 writer.write_u64(mem.data.len() as u64)?;
709 writer.write_all(&mem.into_inner())?;
710 }
711 if let Some(section_ref_func) = &self.section_ref_func {
712 writer.write_u64(ID_REF_FUNC)?;
713 let mut mem = MemWriter::new();
714 section_ref_func.pack(
715 &mut mem,
716 false,
717 Encoding::Utf8,
718 &Some(Box::new(self.section_header.clone())),
719 )?;
720 writer.write_u64(mem.data.len() as u64)?;
721 writer.write_all(&mem.into_inner())?;
722 }
723 if let Some(section_ref_code) = &self.section_ref_code {
724 writer.write_u64(ID_REF_CODE)?;
725 let mut mem = MemWriter::new();
726 section_ref_code.pack(
727 &mut mem,
728 false,
729 Encoding::Utf8,
730 &Some(Box::new(self.section_header.clone())),
731 )?;
732 writer.write_u64(mem.data.len() as u64)?;
733 writer.write_all(&mem.into_inner())?;
734 }
735 if let Some(section_ref_class) = &self.section_ref_class {
736 writer.write_u64(ID_REF_CLASS)?;
737 let mut mem = MemWriter::new();
738 section_ref_class.pack(
739 &mut mem,
740 false,
741 Encoding::Utf8,
742 &Some(Box::new(self.section_header.clone())),
743 )?;
744 writer.write_u64(mem.data.len() as u64)?;
745 writer.write_all(&mem.into_inner())?;
746 }
747 writer.write_u64(ID_IMPORT_NATIVE_FUNC)?;
748 let mut mem = MemWriter::new();
749 self.section_import_native_func.pack(
750 &mut mem,
751 false,
752 Encoding::Utf8,
753 &Some(Box::new(self.section_header.clone())),
754 )?;
755 writer.write_u64(mem.data.len() as u64)?;
756 writer.write_all(&mem.into_inner())?;
757 Ok(())
758 }
759}
760
761impl ECSImage for ECSExecutionImage {
762 fn disasm<'a>(&self, writer: Box<dyn std::io::Write + 'a>) -> Result<()> {
763 let mut disasm = ECSExecutionImageDisassembler::new(
764 self.image.to_ref(),
765 &self.section_function,
766 &self.section_func_info,
767 &self.section_import_native_func,
768 &self.section_class_info,
769 &self.section_const_string,
770 Some(writer),
771 );
772 disasm.execute()?;
773 Ok(())
774 }
775
776 fn export(&self) -> Result<Vec<Message>> {
777 let mut messages = Vec::new();
778 let mut disasm = ECSExecutionImageDisassembler::new(
779 self.image.to_ref(),
780 &self.section_function,
781 &self.section_func_info,
782 &self.section_import_native_func,
783 &self.section_class_info,
784 &self.section_const_string,
785 None,
786 );
787 disasm.execute()?;
788 let assembly = disasm.assembly.clone();
789 let mut string_stack = Vec::new();
790 let mut stacks = Vec::new();
791 let mut index = 0;
792 let len = assembly.len();
793 while index < len {
794 let cmd = &assembly[index];
795 if cmd.code == CsicLoad {
796 disasm.stream.pos = cmd.addr as usize + 1;
797 let csom = disasm.read_csom()?;
798 let csvt = disasm.read_csvt()?;
799 let is_string = csom == CsomImmediate && csvt == CsvtString;
800 if is_string {
801 let s = disasm.get_string_literal()?;
802 string_stack.push(s);
803 }
804 stacks.push(is_string);
805 } else if matches!(
806 cmd.code,
807 CsicCall
808 | CsicCallMember
809 | CsicCallNativeFunction
810 | CsicEnter
811 | CsicElementIndirect
812 ) {
813 string_stack.clear();
814 stacks.clear();
815 } else if cmd.code == CsicOperate {
816 disasm.stream.pos = cmd.addr as usize + 1;
817 let csot = disasm.read_csot()?;
818 if csot == CsotAdd {
819 if string_stack.len() >= 2
820 && index >= 2
821 && stacks.len() >= 2
822 && stacks[stacks.len() - 1]
823 && stacks[stacks.len() - 2]
824 {
825 let s2 = string_stack.pop().unwrap();
826 let s1 = string_stack.pop().unwrap();
827 let s = s1 + &s2;
828 string_stack.push(s);
829 stacks.pop();
830 index += 1;
832 continue;
833 }
834 }
835 if let Some(is_str) = stacks.pop() {
836 if is_str && string_stack.is_empty() {
837 return Err(anyhow::anyhow!(
838 "String stack is empty when processing Operate at {:08X}",
839 cmd.addr,
840 ));
841 }
842 if is_str {
843 string_stack.pop();
844 }
845 }
846 } else if cmd.code == CsicExCall {
847 disasm.stream.pos = cmd.addr as usize + 1;
848 let arg_count = disasm.stream.read_i32()?;
849 let csom = disasm.read_csom()?;
850 let csvt = disasm.read_csvt()?;
851 if csom == CsomImmediate {
852 let func_name = if csvt == CsvtString {
853 disasm.get_string_literal()?
854 } else if csvt == CsvtInteger {
855 let func_address = disasm.stream.read_u32()?;
856 let func = disasm.func_map.get(&func_address).ok_or_else(|| {
857 anyhow::anyhow!(
858 "Function address 0x{:08X} not found in ExCall",
859 func_address
860 )
861 })?;
862 func.name.0.clone()
863 } else {
864 return Err(anyhow::anyhow!(
865 "Unexpected CSVT for function name in ExCall"
866 ));
867 };
868 if func_name == "WitchWizard::OutMsg" && arg_count == 8 {
869 if string_stack.len() < 2 {
870 return Err(anyhow::anyhow!(
871 "String stack has less than 2 items when processing OutMsg at {:08X}",
872 cmd.addr,
873 ));
874 }
875 if string_stack.len() > 2 {
876 eprintln!(
877 "WARNING: String stack has more than 2 items when processing OutMsg at {:08X}",
878 cmd.addr,
879 );
880 crate::COUNTER.inc_warning();
881 }
882 let name = string_stack[0].clone();
883 let message = string_stack[1].clone();
884 messages.push(Message {
885 name: if name.is_empty() { None } else { Some(name) },
886 message,
887 });
888 }
889 }
890 string_stack.clear();
891 stacks.clear();
892 } else if cmd.code == CsicCallNativeMember {
893 disasm.stream.pos = cmd.addr as usize + 1;
894 let arg_count = disasm.stream.read_i32()?;
895 let class_index = disasm.stream.read_u32()?;
896 let class = self
897 .section_class_info
898 .infos
899 .get(class_index as usize)
900 .ok_or_else(|| {
901 anyhow::anyhow!(
902 "Invalid class info index: {} (max {}) at {:08x}",
903 class_index,
904 self.section_class_info.infos.len(),
905 cmd.addr
906 )
907 })?;
908 let func_index = disasm.stream.read_u32()?;
909 let func = class.method_info.get(func_index as usize).ok_or_else(|| {
910 anyhow::anyhow!(
911 "Invalid method info index: {} (max {}) at {:08x}",
912 func_index,
913 class.method_info.len(),
914 cmd.addr
915 )
916 })?;
917 let func_name = func.prototype_info.global_name.0.as_str();
918 if func_name == "Window::CreateDisplay@2" && arg_count == 5 {
919 if string_stack.is_empty() {
920 return Err(anyhow::anyhow!(
921 "String stack is empty when processing Window::CreateDisplay@2"
922 ));
923 }
924 if string_stack.len() > 1 {
925 eprintln!(
926 "WARNING: String stack has more than 1 item when processing Window::CreateDisplay@2 at {:08X}",
927 cmd.addr,
928 );
929 crate::COUNTER.inc_warning();
930 }
931 let message = string_stack[0].clone();
932 messages.push(Message {
933 name: None,
934 message,
935 });
936 }
937 string_stack.clear();
938 stacks.clear();
939 }
940 index += 1;
941 }
942 Ok(messages)
943 }
944
945 fn export_multi(&self) -> Result<HashMap<String, Vec<Message>>> {
946 let mut key = String::from("global");
947 let mut messages: HashMap<String, Vec<Message>> = HashMap::new();
948 let mut disasm = ECSExecutionImageDisassembler::new(
949 self.image.to_ref(),
950 &self.section_function,
951 &self.section_func_info,
952 &self.section_import_native_func,
953 &self.section_class_info,
954 &self.section_const_string,
955 None,
956 );
957 disasm.execute()?;
958 let assembly = disasm.assembly.clone();
959 let mut string_stack = Vec::new();
960 let mut stacks = Vec::new();
961 let mut index = 0;
962 let len = assembly.len();
963 while index < len {
964 let cmd = &assembly[index];
965 if cmd.code == CsicLoad {
966 disasm.stream.pos = cmd.addr as usize + 1;
967 let csom = disasm.read_csom()?;
968 let csvt = disasm.read_csvt()?;
969 let is_string = csom == CsomImmediate && csvt == CsvtString;
970 if is_string {
971 let s = disasm.get_string_literal()?;
972 string_stack.push(s);
973 }
974 stacks.push(is_string);
975 } else if matches!(
976 cmd.code,
977 CsicCall
978 | CsicCallMember
979 | CsicCallNativeFunction
980 | CsicEnter
981 | CsicElementIndirect
982 ) {
983 string_stack.clear();
984 stacks.clear();
985 } else if cmd.code == CsicOperate {
986 disasm.stream.pos = cmd.addr as usize + 1;
987 let csot = disasm.read_csot()?;
988 if csot == CsotAdd {
989 if string_stack.len() >= 2
990 && index >= 2
991 && stacks.len() >= 2
992 && stacks[stacks.len() - 1]
993 && stacks[stacks.len() - 2]
994 {
995 let s2 = string_stack.pop().unwrap();
996 let s1 = string_stack.pop().unwrap();
997 let s = s1 + &s2;
998 string_stack.push(s);
999 stacks.pop();
1000 index += 1;
1002 continue;
1003 }
1004 }
1005 if let Some(is_str) = stacks.pop() {
1006 if is_str && string_stack.is_empty() {
1007 return Err(anyhow::anyhow!(
1008 "String stack is empty when processing Operate at {:08X}",
1009 cmd.addr,
1010 ));
1011 }
1012 if is_str {
1013 string_stack.pop();
1014 }
1015 }
1016 } else if cmd.code == CsicExCall {
1017 disasm.stream.pos = cmd.addr as usize + 1;
1018 let arg_count = disasm.stream.read_i32()?;
1019 let csom = disasm.read_csom()?;
1020 let csvt = disasm.read_csvt()?;
1021 if csom == CsomImmediate {
1022 let func_name = if csvt == CsvtString {
1023 disasm.get_string_literal()?
1024 } else if csvt == CsvtInteger {
1025 let func_address = disasm.stream.read_u32()?;
1026 let func = disasm.func_map.get(&func_address).ok_or_else(|| {
1027 anyhow::anyhow!(
1028 "Function address 0x{:08X} not found in ExCall",
1029 func_address
1030 )
1031 })?;
1032 func.name.0.clone()
1033 } else {
1034 return Err(anyhow::anyhow!(
1035 "Unexpected CSVT for function name in ExCall"
1036 ));
1037 };
1038 if func_name == "WitchWizard::SetPastLabel"
1039 && arg_count == 2
1040 && !self.no_part_label
1041 {
1042 if string_stack.is_empty() {
1043 return Err(anyhow::anyhow!(
1044 "String stack is empty when processing SetPastLabel"
1045 ));
1046 }
1047 if string_stack.len() > 1 {
1048 eprintln!(
1049 "WARNING: String stack has more than 1 item when processing SetPastLabel at {:08X}",
1050 cmd.addr,
1051 );
1052 crate::COUNTER.inc_warning();
1053 }
1054 key = string_stack[0].clone();
1055 } else if func_name == "WitchWizard::OutMsg" && arg_count == 8 {
1056 if string_stack.len() < 2 {
1057 return Err(anyhow::anyhow!(
1058 "String stack has less than 2 items when processing OutMsg at {:08X}",
1059 cmd.addr,
1060 ));
1061 }
1062 if string_stack.len() > 2 {
1063 eprintln!(
1064 "WARNING: String stack has more than 2 items when processing OutMsg at {:08X}",
1065 cmd.addr,
1066 );
1067 crate::COUNTER.inc_warning();
1068 }
1069 let name = string_stack[0].clone();
1070 let message = string_stack[1].clone();
1071 messages
1072 .entry(key.clone())
1073 .or_insert_with(Vec::new)
1074 .push(Message {
1075 name: if name.is_empty() { None } else { Some(name) },
1076 message,
1077 });
1078 } else if func_name == "WitchWizard::SetCurrentScriptName" && arg_count == 2 {
1079 if string_stack.is_empty() {
1080 return Err(anyhow::anyhow!(
1081 "String stack is empty when processing SetCurrentScriptName"
1082 ));
1083 }
1084 if string_stack.len() > 1 {
1085 eprintln!(
1086 "WARNING: String stack has more than 1 item when processing SetCurrentScriptName at {:08X}",
1087 cmd.addr,
1088 );
1089 crate::COUNTER.inc_warning();
1090 }
1091 key = string_stack[0].clone();
1092 }
1093 }
1094 string_stack.clear();
1095 stacks.clear();
1096 } else if cmd.code == CsicCallNativeMember {
1097 disasm.stream.pos = cmd.addr as usize + 1;
1098 let arg_count = disasm.stream.read_i32()?;
1099 let class_index = disasm.stream.read_u32()?;
1100 let class = self
1101 .section_class_info
1102 .infos
1103 .get(class_index as usize)
1104 .ok_or_else(|| {
1105 anyhow::anyhow!(
1106 "Invalid class info index: {} (max {}) at {:08x}",
1107 class_index,
1108 self.section_class_info.infos.len(),
1109 cmd.addr
1110 )
1111 })?;
1112 let func_index = disasm.stream.read_u32()?;
1113 let func = class.method_info.get(func_index as usize).ok_or_else(|| {
1114 anyhow::anyhow!(
1115 "Invalid method info index: {} (max {}) at {:08x}",
1116 func_index,
1117 class.method_info.len(),
1118 cmd.addr
1119 )
1120 })?;
1121 let func_name = func.prototype_info.global_name.0.as_str();
1122 if func_name == "Window::CreateDisplay@2" && arg_count == 5 {
1123 if string_stack.is_empty() {
1124 return Err(anyhow::anyhow!(
1125 "String stack is empty when processing Window::CreateDisplay@2"
1126 ));
1127 }
1128 if string_stack.len() > 1 {
1129 eprintln!(
1130 "WARNING: String stack has more than 1 item when processing Window::CreateDisplay@2 at {:08X}",
1131 cmd.addr,
1132 );
1133 crate::COUNTER.inc_warning();
1134 }
1135 let message = string_stack[0].clone();
1136 messages
1137 .entry(key.clone())
1138 .or_insert_with(Vec::new)
1139 .push(Message {
1140 name: None,
1141 message,
1142 });
1143 }
1144 string_stack.clear();
1145 stacks.clear();
1146 }
1147 index += 1;
1148 }
1149 Ok(messages)
1150 }
1151
1152 fn export_all(&self) -> Result<Vec<String>> {
1153 let mut disasm = ECSExecutionImageDisassembler::new(
1154 self.image.to_ref(),
1155 &self.section_function,
1156 &self.section_func_info,
1157 &self.section_import_native_func,
1158 &self.section_class_info,
1159 &self.section_const_string,
1160 None,
1161 );
1162 disasm.execute()?;
1163 let mut messages = Vec::new();
1164 for cmd in disasm.assembly.clone().iter() {
1165 if cmd.code == CsicLoad {
1166 disasm.stream.pos = cmd.addr as usize + 1;
1167 let csom = disasm.read_csom()?;
1168 let csvt = disasm.read_csvt()?;
1169 if csom == CsomImmediate && csvt == CsvtString {
1170 let s = disasm.get_string_literal()?;
1171 messages.push(s);
1172 }
1173 }
1174 }
1175 Ok(messages)
1176 }
1177
1178 fn import<'a>(
1179 &self,
1180 messages: Vec<Message>,
1181 file: Box<dyn WriteSeek + 'a>,
1182 replacement: Option<&'a ReplacementTable>,
1183 ) -> Result<()> {
1184 let mut cloned = self.clone();
1185 let mut mess = messages.iter();
1186 let mut mes = mess.next();
1187 let mut disasm = ECSExecutionImageDisassembler::new(
1188 self.image.to_ref(),
1189 &self.section_function,
1190 &self.section_func_info,
1191 &self.section_import_native_func,
1192 &self.section_class_info,
1193 &self.section_const_string,
1194 None,
1195 );
1196 disasm.execute()?;
1197 let mut constr_map: HashMap<String, u32> = cloned
1198 .section_const_string
1199 .strings
1200 .iter()
1201 .enumerate()
1202 .map(|(i, s)| (s.string.0.clone(), i as u32))
1203 .collect();
1204 let mut assembly = disasm.assembly.clone();
1205 let mut new_image = MemWriter::new();
1206 let mut index = 0;
1207 let mut dumped_index = 0;
1208 let mut string_stack = Vec::new();
1209 let mut stacks = Vec::new();
1210 let len = assembly.len();
1211 while index < len {
1212 let cmd = assembly[index].clone();
1213 if cmd.code == CsicLoad {
1214 disasm.stream.pos = cmd.addr as usize + 1;
1215 let csom = disasm.read_csom()?;
1216 let csvt = disasm.read_csvt()?;
1217 let is_string = csom == CsomImmediate && csvt == CsvtString;
1218 if is_string {
1219 let s = disasm.get_string_literal()?;
1220 string_stack.push((Some(index), s));
1221 }
1222 stacks.push(is_string);
1223 } else if matches!(
1224 cmd.code,
1225 CsicCall
1226 | CsicCallMember
1227 | CsicCallNativeFunction
1228 | CsicEnter
1229 | CsicElementIndirect
1230 ) {
1231 string_stack.clear();
1232 stacks.clear();
1233 while dumped_index <= index {
1234 let tcmd = &mut assembly[dumped_index];
1235 tcmd.new_addr = new_image.pos as u32;
1236 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1238 dumped_index += 1;
1239 }
1240 } else if cmd.code == CsicOperate {
1241 disasm.stream.pos = cmd.addr as usize + 1;
1242 let csot = disasm.read_csot()?;
1243 if csot == CsotAdd {
1244 if string_stack.len() >= 2
1245 && index >= 2
1246 && stacks.len() >= 2
1247 && stacks[stacks.len() - 1]
1248 && stacks[stacks.len() - 2]
1249 {
1250 let s2 = string_stack.pop().unwrap().1;
1251 let s1 = string_stack.pop().unwrap().1;
1252 let s = s1 + &s2;
1253 string_stack.push((None, s));
1254 stacks.pop();
1255 index += 1;
1257 continue;
1258 }
1259 }
1260 if let Some(is_str) = stacks.pop() {
1261 if is_str && string_stack.is_empty() {
1262 return Err(anyhow::anyhow!(
1263 "String stack is empty when processing Operate at {:08X}",
1264 cmd.addr,
1265 ));
1266 }
1267 if is_str {
1268 string_stack.pop();
1269 }
1270 }
1271 } else if cmd.code == CsicExCall {
1272 disasm.stream.pos = cmd.addr as usize + 1;
1273 let arg_count = disasm.stream.read_i32()?;
1274 let csom = disasm.read_csom()?;
1275 let csvt = disasm.read_csvt()?;
1276 if csom == CsomImmediate {
1277 let func_name = if csvt == CsvtString {
1278 disasm.get_string_literal()?
1279 } else if csvt == CsvtInteger {
1280 let func_address = disasm.stream.read_u32()?;
1281 let func = disasm.func_map.get(&func_address).ok_or_else(|| {
1282 anyhow::anyhow!(
1283 "Function address 0x{:08X} not found in ExCall",
1284 func_address
1285 )
1286 })?;
1287 func.name.0.clone()
1288 } else {
1289 return Err(anyhow::anyhow!(
1290 "Unexpected CSVT for function name in ExCall"
1291 ));
1292 };
1293 if func_name == "WitchWizard::OutMsg" && arg_count == 8 {
1294 if string_stack.len() < 2 {
1295 return Err(anyhow::anyhow!(
1296 "String stack has less than 2 items when processing OutMsg at {:08X}",
1297 cmd.addr,
1298 ));
1299 }
1300 if string_stack.len() > 2 {
1301 eprintln!(
1302 "WARNING: String stack has more than 2 items when processing OutMsg at {:08X}",
1303 cmd.addr,
1304 );
1305 crate::COUNTER.inc_warning();
1306 }
1307 let name = string_stack[0].clone();
1308 let message = string_stack[1].clone();
1309 let message_idx = message.0.ok_or_else(|| {
1310 anyhow::anyhow!(
1311 "Cannot replace constructed string for message at {:08X}",
1312 cmd.addr,
1313 )
1314 })?;
1315 if !name.1.is_empty() {
1316 let name_idx = name.0.ok_or_else(|| {
1317 anyhow::anyhow!(
1318 "Cannot replace constructed string for message name at {:08X}",
1319 cmd.addr,
1320 )
1321 })?;
1322 let mut name = match mes {
1323 Some(m) => match &m.name {
1324 Some(n) => n.clone(),
1325 None => {
1326 return Err(anyhow::anyhow!(
1327 "No name available for OutMsg at {:08X}",
1328 cmd.addr,
1329 ));
1330 }
1331 },
1332 None => {
1333 return Err(anyhow::anyhow!(
1334 "No more messages available for OutMsg at {:08X}",
1335 cmd.addr,
1336 ));
1337 }
1338 };
1339 if let Some(repl) = replacement {
1340 for (k, v) in repl.map.iter() {
1341 name = name.replace(k, v);
1342 }
1343 }
1344 let constr_idx = if let Some(&idx) = constr_map.get(&name) {
1345 idx
1346 } else {
1347 let idx = cloned.section_const_string.strings.len() as u32;
1349 cloned.section_const_string.strings.push(ConstStringEntry {
1350 string: WideString(name.clone()),
1351 refs: DWordArray { data: Vec::new() },
1352 });
1353 constr_map.insert(name.clone(), idx);
1354 idx
1355 };
1356 while dumped_index < name_idx {
1357 let tcmd = &mut assembly[dumped_index];
1358 tcmd.new_addr = new_image.pos as u32;
1359 new_image.write_from(
1361 &mut disasm.stream,
1362 tcmd.addr as u64,
1363 tcmd.size as u64,
1364 )?;
1365 dumped_index += 1;
1366 }
1367 let name_cmd = &mut assembly[name_idx];
1368 name_cmd.new_addr = new_image.pos as u32;
1369 new_image.write_u8(CsicLoad as u8)?;
1371 new_image.write_u8(CsomImmediate as u8)?;
1372 new_image.write_u8(CsvtString as u8)?;
1373 new_image.write_u32(0x80000000)?;
1374 new_image.write_u32(constr_idx)?;
1375 dumped_index += 1;
1376 }
1377 let mut message = match mes {
1378 Some(m) => m.message.clone(),
1379 None => {
1380 return Err(anyhow::anyhow!(
1381 "No more messages available for OutMsg at {:08X}",
1382 cmd.addr,
1383 ));
1384 }
1385 };
1386 mes = mess.next();
1387 if let Some(repl) = replacement {
1388 for (k, v) in repl.map.iter() {
1389 message = message.replace(k, v);
1390 }
1391 }
1392 while dumped_index < message_idx {
1393 let tcmd = &mut assembly[dumped_index];
1394 tcmd.new_addr = new_image.pos as u32;
1395 new_image.write_from(
1397 &mut disasm.stream,
1398 tcmd.addr as u64,
1399 tcmd.size as u64,
1400 )?;
1401 dumped_index += 1;
1402 }
1403 let message_cmd = &mut assembly[message_idx];
1404 message_cmd.new_addr = new_image.pos as u32;
1405 new_image.write_u8(CsicLoad as u8)?;
1407 new_image.write_u8(CsomImmediate as u8)?;
1408 new_image.write_u8(CsvtString as u8)?;
1409 new_image.write_u32(0x80000000)?;
1410 let constr_idx = if let Some(&idx) = constr_map.get(&message) {
1411 idx
1412 } else {
1413 let idx = cloned.section_const_string.strings.len() as u32;
1415 cloned.section_const_string.strings.push(ConstStringEntry {
1416 string: WideString(message.clone()),
1417 refs: DWordArray { data: Vec::new() },
1418 });
1419 constr_map.insert(message.clone(), idx);
1420 idx
1421 };
1422 new_image.write_u32(constr_idx)?;
1423 dumped_index += 1;
1424 }
1425 }
1426 string_stack.clear();
1427 stacks.clear();
1428 while dumped_index <= index {
1429 let tcmd = &mut assembly[dumped_index];
1430 tcmd.new_addr = new_image.pos as u32;
1431 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1433 dumped_index += 1;
1434 }
1435 } else if cmd.code == CsicCallNativeMember {
1436 disasm.stream.pos = cmd.addr as usize + 1;
1437 let arg_count = disasm.stream.read_i32()?;
1438 let class_index = disasm.stream.read_u32()?;
1439 let class = self
1440 .section_class_info
1441 .infos
1442 .get(class_index as usize)
1443 .ok_or_else(|| {
1444 anyhow::anyhow!(
1445 "Invalid class info index: {} (max {}) at {:08x}",
1446 class_index,
1447 self.section_class_info.infos.len(),
1448 cmd.addr
1449 )
1450 })?;
1451 let func_index = disasm.stream.read_u32()?;
1452 let func = class.method_info.get(func_index as usize).ok_or_else(|| {
1453 anyhow::anyhow!(
1454 "Invalid method info index: {} (max {}) at {:08x}",
1455 func_index,
1456 class.method_info.len(),
1457 cmd.addr
1458 )
1459 })?;
1460 let func_name = func.prototype_info.global_name.0.as_str();
1461 if func_name == "Window::CreateDisplay@2" && arg_count == 5 {
1462 if string_stack.is_empty() {
1463 return Err(anyhow::anyhow!(
1464 "String stack is empty when processing Window::CreateDisplay@2"
1465 ));
1466 }
1467 if string_stack.len() > 1 {
1468 eprintln!(
1469 "WARNING: String stack has more than 1 item when processing Window::CreateDisplay@2 at {:08X}",
1470 cmd.addr,
1471 );
1472 crate::COUNTER.inc_warning();
1473 }
1474 let message = string_stack[0].clone();
1475 let message_idx = message.0.ok_or_else(|| {
1476 anyhow::anyhow!(
1477 "Cannot replace constructed string for message at {:08X}",
1478 cmd.addr,
1479 )
1480 })?;
1481 let mut message = match mes {
1482 Some(m) => m.message.clone(),
1483 None => {
1484 return Err(anyhow::anyhow!(
1485 "No more messages available for Window::CreateDisplay@2 at {:08X}",
1486 cmd.addr,
1487 ));
1488 }
1489 };
1490 mes = mess.next();
1491 if let Some(repl) = replacement {
1492 for (k, v) in repl.map.iter() {
1493 message = message.replace(k, v);
1494 }
1495 }
1496 while dumped_index < message_idx {
1497 let tcmd = &mut assembly[dumped_index];
1498 tcmd.new_addr = new_image.pos as u32;
1499 new_image.write_from(
1501 &mut disasm.stream,
1502 tcmd.addr as u64,
1503 tcmd.size as u64,
1504 )?;
1505 dumped_index += 1;
1506 }
1507 let message_cmd = &mut assembly[message_idx];
1508 message_cmd.new_addr = new_image.pos as u32;
1509 new_image.write_u8(CsicLoad as u8)?;
1511 new_image.write_u8(CsomImmediate as u8)?;
1512 new_image.write_u8(CsvtString as u8)?;
1513 new_image.write_u32(0x80000000)?;
1514 let constr_idx = if let Some(&idx) = constr_map.get(&message) {
1515 idx
1516 } else {
1517 let idx = cloned.section_const_string.strings.len() as u32;
1519 cloned.section_const_string.strings.push(ConstStringEntry {
1520 string: WideString(message.clone()),
1521 refs: DWordArray { data: Vec::new() },
1522 });
1523 constr_map.insert(message.clone(), idx);
1524 idx
1525 };
1526 new_image.write_u32(constr_idx)?;
1527 dumped_index += 1;
1528 }
1529 string_stack.clear();
1530 stacks.clear();
1531 while dumped_index <= index {
1532 let tcmd = &mut assembly[dumped_index];
1533 tcmd.new_addr = new_image.pos as u32;
1534 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1536 dumped_index += 1;
1537 }
1538 }
1539 index += 1;
1540 }
1541 while dumped_index < len {
1542 let tcmd = &mut assembly[dumped_index];
1543 tcmd.new_addr = new_image.pos as u32;
1544 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1546 dumped_index += 1;
1547 }
1548 if mes.is_some() || mess.next().is_some() {
1549 return Err(anyhow::anyhow!("Not all messages were used."));
1550 }
1551 let commands: HashMap<u32, &ECSExecutionImageCommandRecord> =
1552 assembly.iter().map(|c| (c.addr, c)).collect();
1553 Self::fix_image(&assembly, &mut disasm, &mut new_image, &commands)?;
1554 cloned.image = MemReader::new(new_image.into_inner());
1555 cloned.fix_references(&commands)?;
1556 cloned.save(file)?;
1557 Ok(())
1558 }
1559
1560 fn import_multi<'a>(
1561 &self,
1562 mut messages: HashMap<String, Vec<Message>>,
1563 file: Box<dyn WriteSeek + 'a>,
1564 replacement: Option<&'a ReplacementTable>,
1565 ) -> Result<()> {
1566 let mut cloned = self.clone();
1567 let mut key = String::from("global");
1568 let mut disasm = ECSExecutionImageDisassembler::new(
1569 self.image.to_ref(),
1570 &self.section_function,
1571 &self.section_func_info,
1572 &self.section_import_native_func,
1573 &self.section_class_info,
1574 &self.section_const_string,
1575 None,
1576 );
1577 disasm.execute()?;
1578 let mut constr_map: HashMap<String, u32> = cloned
1579 .section_const_string
1580 .strings
1581 .iter()
1582 .enumerate()
1583 .map(|(i, s)| (s.string.0.clone(), i as u32))
1584 .collect();
1585 let mut assembly = disasm.assembly.clone();
1586 let mut new_image = MemWriter::new();
1587 let mut index = 0;
1588 let mut dumped_index = 0;
1589 let mut string_stack = Vec::new();
1590 let mut stacks = Vec::new();
1591 let len = assembly.len();
1592 while index < len {
1593 let cmd = assembly[index].clone();
1594 if cmd.code == CsicLoad {
1595 disasm.stream.pos = cmd.addr as usize + 1;
1596 let csom = disasm.read_csom()?;
1597 let csvt = disasm.read_csvt()?;
1598 let is_string = csom == CsomImmediate && csvt == CsvtString;
1599 if is_string {
1600 let s = disasm.get_string_literal()?;
1601 string_stack.push((Some(index), s));
1602 }
1603 stacks.push(is_string);
1604 } else if matches!(
1605 cmd.code,
1606 CsicCall
1607 | CsicCallMember
1608 | CsicCallNativeFunction
1609 | CsicEnter
1610 | CsicElementIndirect
1611 ) {
1612 string_stack.clear();
1613 stacks.clear();
1614 while dumped_index <= index {
1615 let tcmd = &mut assembly[dumped_index];
1616 tcmd.new_addr = new_image.pos as u32;
1617 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1619 dumped_index += 1;
1620 }
1621 } else if cmd.code == CsicOperate {
1622 disasm.stream.pos = cmd.addr as usize + 1;
1623 let csot = disasm.read_csot()?;
1624 if csot == CsotAdd {
1625 if string_stack.len() >= 2
1626 && index >= 2
1627 && stacks.len() >= 2
1628 && stacks[stacks.len() - 1]
1629 && stacks[stacks.len() - 2]
1630 {
1631 let s2 = string_stack.pop().unwrap().1;
1632 let s1 = string_stack.pop().unwrap().1;
1633 let s = s1 + &s2;
1634 string_stack.push((None, s));
1635 stacks.pop();
1636 index += 1;
1638 continue;
1639 }
1640 }
1641 if let Some(is_str) = stacks.pop() {
1642 if is_str && string_stack.is_empty() {
1643 return Err(anyhow::anyhow!(
1644 "String stack is empty when processing Operate at {:08X}",
1645 cmd.addr,
1646 ));
1647 }
1648 if is_str {
1649 string_stack.pop();
1650 }
1651 }
1652 } else if cmd.code == CsicExCall {
1653 disasm.stream.pos = cmd.addr as usize + 1;
1654 let arg_count = disasm.stream.read_i32()?;
1655 let csom = disasm.read_csom()?;
1656 let csvt = disasm.read_csvt()?;
1657 if csom == CsomImmediate {
1658 let func_name = if csvt == CsvtString {
1659 disasm.get_string_literal()?
1660 } else if csvt == CsvtInteger {
1661 let func_address = disasm.stream.read_u32()?;
1662 let func = disasm.func_map.get(&func_address).ok_or_else(|| {
1663 anyhow::anyhow!(
1664 "Function address 0x{:08X} not found in ExCall",
1665 func_address
1666 )
1667 })?;
1668 func.name.0.clone()
1669 } else {
1670 return Err(anyhow::anyhow!(
1671 "Unexpected CSVT for function name in ExCall"
1672 ));
1673 };
1674 if func_name == "WitchWizard::SetPastLabel"
1675 && arg_count == 2
1676 && !self.no_part_label
1677 {
1678 if string_stack.is_empty() {
1679 return Err(anyhow::anyhow!(
1680 "String stack is empty when processing SetPastLabel"
1681 ));
1682 }
1683 if string_stack.len() > 1 {
1684 eprintln!(
1685 "WARNING: String stack has more than 1 item when processing SetPastLabel at {:08X}",
1686 cmd.addr,
1687 );
1688 crate::COUNTER.inc_warning();
1689 }
1690 key = string_stack[0].1.clone();
1691 } else if func_name == "WitchWizard::OutMsg" && arg_count == 8 {
1692 if string_stack.len() < 2 {
1693 return Err(anyhow::anyhow!(
1694 "String stack has less than 2 items when processing OutMsg at {:08X}",
1695 cmd.addr,
1696 ));
1697 }
1698 if string_stack.len() > 2 {
1699 eprintln!(
1700 "WARNING: String stack has more than 2 items when processing OutMsg at {:08X}",
1701 cmd.addr,
1702 );
1703 crate::COUNTER.inc_warning();
1704 }
1705 let name = string_stack[0].clone();
1706 let message = string_stack[1].clone();
1707 let message_idx = message.0.ok_or_else(|| {
1708 anyhow::anyhow!(
1709 "Cannot replace constructed string for message at {:08X}",
1710 cmd.addr,
1711 )
1712 })?;
1713 if !name.1.is_empty() {
1714 let name_idx = name.0.ok_or_else(|| {
1715 anyhow::anyhow!(
1716 "Cannot replace constructed string for message name at {:08X}",
1717 cmd.addr,
1718 )
1719 })?;
1720 let mut name = messages
1721 .get_mut(&key)
1722 .and_then(|messages| messages.first_mut().map(|m| m.name.take()))
1723 .flatten()
1724 .ok_or(anyhow::anyhow!(
1725 "No available name message at {:08X}.",
1726 cmd.addr
1727 ))?;
1728 if let Some(repl) = replacement {
1729 for (k, v) in repl.map.iter() {
1730 name = name.replace(k, v);
1731 }
1732 }
1733 let constr_idx = if let Some(&idx) = constr_map.get(&name) {
1734 idx
1735 } else {
1736 let idx = cloned.section_const_string.strings.len() as u32;
1738 cloned.section_const_string.strings.push(ConstStringEntry {
1739 string: WideString(name.clone()),
1740 refs: DWordArray { data: Vec::new() },
1741 });
1742 constr_map.insert(name.clone(), idx);
1743 idx
1744 };
1745 while dumped_index < name_idx {
1746 let tcmd = &mut assembly[dumped_index];
1747 tcmd.new_addr = new_image.pos as u32;
1748 new_image.write_from(
1750 &mut disasm.stream,
1751 tcmd.addr as u64,
1752 tcmd.size as u64,
1753 )?;
1754 dumped_index += 1;
1755 }
1756 let name_cmd = &mut assembly[name_idx];
1757 name_cmd.new_addr = new_image.pos as u32;
1758 new_image.write_u8(CsicLoad as u8)?;
1760 new_image.write_u8(CsomImmediate as u8)?;
1761 new_image.write_u8(CsvtString as u8)?;
1762 new_image.write_u32(0x80000000)?;
1763 new_image.write_u32(constr_idx)?;
1764 dumped_index += 1;
1765 }
1766 let mut message = messages
1767 .get_mut(&key)
1768 .and_then(|messages| messages.pop_first())
1769 .ok_or(anyhow::anyhow!(
1770 "No available message for AddSelect at {:08X}.",
1771 cmd.addr
1772 ))?
1773 .message;
1774 if let Some(repl) = replacement {
1775 for (k, v) in repl.map.iter() {
1776 message = message.replace(k, v);
1777 }
1778 }
1779 while dumped_index < message_idx {
1780 let tcmd = &mut assembly[dumped_index];
1781 tcmd.new_addr = new_image.pos as u32;
1782 new_image.write_from(
1784 &mut disasm.stream,
1785 tcmd.addr as u64,
1786 tcmd.size as u64,
1787 )?;
1788 dumped_index += 1;
1789 }
1790 let message_cmd = &mut assembly[message_idx];
1791 message_cmd.new_addr = new_image.pos as u32;
1792 new_image.write_u8(CsicLoad as u8)?;
1794 new_image.write_u8(CsomImmediate as u8)?;
1795 new_image.write_u8(CsvtString as u8)?;
1796 new_image.write_u32(0x80000000)?;
1797 let constr_idx = if let Some(&idx) = constr_map.get(&message) {
1798 idx
1799 } else {
1800 let idx = cloned.section_const_string.strings.len() as u32;
1802 cloned.section_const_string.strings.push(ConstStringEntry {
1803 string: WideString(message.clone()),
1804 refs: DWordArray { data: Vec::new() },
1805 });
1806 constr_map.insert(message.clone(), idx);
1807 idx
1808 };
1809 new_image.write_u32(constr_idx)?;
1810 dumped_index += 1;
1811 } else if func_name == "WitchWizard::SetCurrentScriptName" && arg_count == 2 {
1812 if string_stack.is_empty() {
1813 return Err(anyhow::anyhow!(
1814 "String stack is empty when processing SetCurrentScriptName"
1815 ));
1816 }
1817 if string_stack.len() > 1 {
1818 eprintln!(
1819 "WARNING: String stack has more than 1 item when processing SetCurrentScriptName at {:08X}",
1820 cmd.addr,
1821 );
1822 crate::COUNTER.inc_warning();
1823 }
1824 key = string_stack[0].1.clone();
1825 }
1826 }
1827 string_stack.clear();
1828 stacks.clear();
1829 while dumped_index <= index {
1830 let tcmd = &mut assembly[dumped_index];
1831 tcmd.new_addr = new_image.pos as u32;
1832 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1834 dumped_index += 1;
1835 }
1836 } else if cmd.code == CsicCallNativeMember {
1837 disasm.stream.pos = cmd.addr as usize + 1;
1838 let arg_count = disasm.stream.read_i32()?;
1839 let class_index = disasm.stream.read_u32()?;
1840 let class = self
1841 .section_class_info
1842 .infos
1843 .get(class_index as usize)
1844 .ok_or_else(|| {
1845 anyhow::anyhow!(
1846 "Invalid class info index: {} (max {}) at {:08x}",
1847 class_index,
1848 self.section_class_info.infos.len(),
1849 cmd.addr
1850 )
1851 })?;
1852 let func_index = disasm.stream.read_u32()?;
1853 let func = class.method_info.get(func_index as usize).ok_or_else(|| {
1854 anyhow::anyhow!(
1855 "Invalid method info index: {} (max {}) at {:08x}",
1856 func_index,
1857 class.method_info.len(),
1858 cmd.addr
1859 )
1860 })?;
1861 let func_name = func.prototype_info.global_name.0.as_str();
1862 if func_name == "Window::CreateDisplay@2" && arg_count == 5 {
1863 if string_stack.is_empty() {
1864 return Err(anyhow::anyhow!(
1865 "String stack is empty when processing Window::CreateDisplay@2"
1866 ));
1867 }
1868 if string_stack.len() > 1 {
1869 eprintln!(
1870 "WARNING: String stack has more than 1 item when processing Window::CreateDisplay@2 at {:08X}",
1871 cmd.addr,
1872 );
1873 crate::COUNTER.inc_warning();
1874 }
1875 let message = string_stack[0].clone();
1876 let message_idx = message.0.ok_or_else(|| {
1877 anyhow::anyhow!(
1878 "Cannot replace constructed string for message at {:08X}",
1879 cmd.addr,
1880 )
1881 })?;
1882 let mut message = messages
1883 .get_mut(&key)
1884 .and_then(|messages| messages.pop_first())
1885 .ok_or(anyhow::anyhow!(
1886 "No available message for CreateDisplay at {:08X}.",
1887 cmd.addr,
1888 ))?
1889 .message;
1890 if let Some(repl) = replacement {
1891 for (k, v) in repl.map.iter() {
1892 message = message.replace(k, v);
1893 }
1894 }
1895 while dumped_index < message_idx {
1896 let tcmd = &mut assembly[dumped_index];
1897 tcmd.new_addr = new_image.pos as u32;
1898 new_image.write_from(
1900 &mut disasm.stream,
1901 tcmd.addr as u64,
1902 tcmd.size as u64,
1903 )?;
1904 dumped_index += 1;
1905 }
1906 let message_cmd = &mut assembly[message_idx];
1907 message_cmd.new_addr = new_image.pos as u32;
1908 new_image.write_u8(CsicLoad as u8)?;
1910 new_image.write_u8(CsomImmediate as u8)?;
1911 new_image.write_u8(CsvtString as u8)?;
1912 new_image.write_u32(0x80000000)?;
1913 let constr_idx = if let Some(&idx) = constr_map.get(&message) {
1914 idx
1915 } else {
1916 let idx = cloned.section_const_string.strings.len() as u32;
1918 cloned.section_const_string.strings.push(ConstStringEntry {
1919 string: WideString(message.clone()),
1920 refs: DWordArray { data: Vec::new() },
1921 });
1922 constr_map.insert(message.clone(), idx);
1923 idx
1924 };
1925 new_image.write_u32(constr_idx)?;
1926 dumped_index += 1;
1927 }
1928 string_stack.clear();
1929 stacks.clear();
1930 while dumped_index <= index {
1931 let tcmd = &mut assembly[dumped_index];
1932 tcmd.new_addr = new_image.pos as u32;
1933 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1935 dumped_index += 1;
1936 }
1937 }
1938 index += 1;
1939 }
1940 while dumped_index < len {
1941 let tcmd = &mut assembly[dumped_index];
1942 tcmd.new_addr = new_image.pos as u32;
1943 new_image.write_from(&mut disasm.stream, tcmd.addr as u64, tcmd.size as u64)?;
1945 dumped_index += 1;
1946 }
1947 for (s, mes) in messages {
1948 if !mes.is_empty() {
1949 return Err(anyhow::anyhow!(
1950 "Not all messages were used for key '{}', {} remaining.",
1951 s,
1952 mes.len()
1953 ));
1954 }
1955 }
1956 let commands: HashMap<u32, &ECSExecutionImageCommandRecord> =
1957 assembly.iter().map(|c| (c.addr, c)).collect();
1958 Self::fix_image(&assembly, &mut disasm, &mut new_image, &commands)?;
1959 cloned.image = MemReader::new(new_image.into_inner());
1960 cloned.fix_references(&commands)?;
1961 cloned.save(file)?;
1962 Ok(())
1963 }
1964
1965 fn import_all<'a>(&self, messages: Vec<String>, file: Box<dyn WriteSeek + 'a>) -> Result<()> {
1966 let mut cloned = self.clone();
1967 let mut mess = messages.into_iter();
1968 let mut mes = mess.next();
1969 let mut disasm = ECSExecutionImageDisassembler::new(
1970 self.image.to_ref(),
1971 &self.section_function,
1972 &self.section_func_info,
1973 &self.section_import_native_func,
1974 &self.section_class_info,
1975 &self.section_const_string,
1976 None,
1977 );
1978 disasm.execute()?;
1979 let mut conststr_map: HashMap<String, u32> = cloned
1980 .section_const_string
1981 .strings
1982 .iter()
1983 .enumerate()
1984 .map(|(i, s)| (s.string.0.clone(), i as u32))
1985 .collect();
1986 let mut assembly = disasm.assembly.clone();
1987 let mut new_image = MemWriter::new();
1988 for cmd in assembly.iter_mut() {
1989 cmd.new_addr = new_image.pos as u32;
1990 if cmd.code == CsicLoad {
1991 disasm.stream.pos = cmd.addr as usize + 1;
1992 let csom = disasm.read_csom()?;
1993 let csvt = disasm.read_csvt()?;
1994 if csom == CsomImmediate && csvt == CsvtString {
1995 let s = match mes {
1996 Some(s) => s,
1997 None => {
1998 return Err(anyhow::anyhow!(
1999 "Not enough messages for import_all at {:08X}",
2000 cmd.addr,
2001 ));
2002 }
2003 };
2004 mes = mess.next();
2005 let constr_idx = if let Some(&idx) = conststr_map.get(&s) {
2006 idx
2007 } else {
2008 let idx = cloned.section_const_string.strings.len() as u32;
2010 cloned.section_const_string.strings.push(ConstStringEntry {
2011 string: WideString(s.clone()),
2012 refs: DWordArray { data: Vec::new() },
2013 });
2014 conststr_map.insert(s.clone(), idx);
2015 idx
2016 };
2017 new_image.write_u8(CsicLoad as u8)?;
2018 new_image.write_u8(CsomImmediate as u8)?;
2019 new_image.write_u8(CsvtString as u8)?;
2020 new_image.write_u32(0x80000000)?;
2021 new_image.write_u32(constr_idx)?;
2022 continue;
2023 }
2024 }
2025 new_image.write_from(&mut disasm.stream, cmd.addr as u64, cmd.size as u64)?;
2027 }
2028 if mes.is_some() || mess.next().is_some() {
2029 return Err(anyhow::anyhow!("Too many messages for import_all"));
2030 }
2031 let commands: HashMap<u32, &ECSExecutionImageCommandRecord> =
2032 assembly.iter().map(|c| (c.addr, c)).collect();
2033 Self::fix_image(&assembly, &mut disasm, &mut new_image, &commands)?;
2034 cloned.image = MemReader::new(new_image.into_inner());
2035 cloned.fix_references(&commands)?;
2036 cloned.save(file)?;
2037 Ok(())
2038 }
2039}