WIP: update csx deser

This commit is contained in:
2026-01-20 17:36:27 +08:00
parent bb6c56e560
commit edfb17205e
3 changed files with 708 additions and 18 deletions

View File

@@ -6,6 +6,7 @@ use crate::types::*;
use crate::utils::struct_pack::*;
use anyhow::Result;
use std::collections::HashMap;
use std::io::Seek;
const ID_HEADER: u64 = 0x2020726564616568; // header
const ID_IMAGE: u64 = 0x2020206567616D69;
@@ -36,6 +37,20 @@ pub struct ECSExecutionImage {
image_global: Option<MemReader>,
image_const: Option<MemReader>,
image_shared: Option<MemReader>,
section_class_info: SectionClassInfo,
section_function: SectionFunction,
section_init_naked_func: SectionInitNakedFunc,
section_func_info: SectionFuncInfo,
section_symbol_info: Option<SectionSymbolInfo>,
section_global: Option<SectionGlobal>,
section_data: Option<SectionData>,
section_const_string: SectionConstString,
section_link_info: Option<SectionLinkInfo>,
section_link_info_ex: Option<SectionLinkInfoEx>,
section_ref_func: Option<SectionRefFunc>,
section_ref_code: Option<SectionRefCode>,
section_ref_class: Option<SectionRefClass>,
section_import_native_func: SectionImportNativeFunc,
}
impl ECSExecutionImage {
@@ -53,6 +68,20 @@ impl ECSExecutionImage {
let mut image_global = None;
let mut image_const = None;
let mut image_shared = None;
let mut section_class_info = None;
let mut section_function = None;
let mut section_init_naked_func = None;
let mut section_func_info = None;
let mut section_symbol_info = None;
let mut section_global = None;
let mut section_data = None;
let mut section_const_string = None;
let mut section_link_info = None;
let mut section_link_info_ex = None;
let mut section_ref_func = None;
let mut section_ref_code = None;
let mut section_ref_class = None;
let mut section_import_native_func = None;
while reader.pos < len {
if len - reader.pos < 16 {
break;
@@ -80,6 +109,212 @@ impl ECSExecutionImage {
ID_IMAGE_SHARED => {
image_shared = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
}
ID_CLASS_INFO => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_class_info = Some(SectionClassInfo::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::CLASS_INFO"
);
crate::COUNTER.inc_warning();
}
}
ID_FUNCTION => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_function = Some(SectionFunction::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::FUNCTION"
);
crate::COUNTER.inc_warning();
}
}
ID_INIT_NAKED_FUNC => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_init_naked_func = Some(SectionInitNakedFunc::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::INIT_NAKED_FUNC"
);
crate::COUNTER.inc_warning();
}
}
ID_FUNC_INFO => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_func_info = Some(SectionFuncInfo::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::FUNC_INFO"
);
crate::COUNTER.inc_warning();
}
}
ID_SYMBOL_INFO => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_symbol_info = Some(SectionSymbolInfo::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::SYMBOL_INFO"
);
crate::COUNTER.inc_warning();
}
}
ID_GLOBAL => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_global = Some(SectionGlobal::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!("WARNING: Some data is not parsed in ECSExecutionImage::GLOBAL");
crate::COUNTER.inc_warning();
}
}
ID_DATA => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_data = Some(SectionData::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!("WARNING: Some data is not parsed in ECSExecutionImage::DATA");
crate::COUNTER.inc_warning();
}
}
ID_CONST_STRING => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_const_string = Some(SectionConstString::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::CONST_STRING"
);
crate::COUNTER.inc_warning();
}
}
ID_LINK_INFO => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_link_info = Some(SectionLinkInfo::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::LINK_INFO"
);
crate::COUNTER.inc_warning();
}
}
ID_LINK_INFO_EX => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_link_info_ex = Some(SectionLinkInfoEx::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::LINK_INFO_EX"
);
crate::COUNTER.inc_warning();
}
}
ID_REF_FUNC => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_ref_func = Some(SectionRefFunc::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::REF_FUNC"
);
crate::COUNTER.inc_warning();
}
}
ID_REF_CODE => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_ref_code = Some(SectionRefCode::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::REF_CODE"
);
crate::COUNTER.inc_warning();
}
}
ID_REF_CLASS => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_ref_class = Some(SectionRefClass::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::REF_CLASS"
);
crate::COUNTER.inc_warning();
}
}
ID_IMPORT_NATIVE_FUNC => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_import_native_func = Some(SectionImportNativeFunc::unpack(
&mut mem,
false,
Encoding::Utf8,
&Some(Box::new(section_header.clone())),
)?);
if mem.stream_position()? != size {
eprintln!(
"WARNING: Some data is not parsed in ECSExecutionImage::IMPORT_NATIVE_FUNC"
);
crate::COUNTER.inc_warning();
}
}
0 => {
break;
}
@@ -99,6 +334,26 @@ impl ECSExecutionImage {
image_global,
image_const,
image_shared,
section_class_info: section_class_info
.ok_or_else(|| anyhow::anyhow!("Missing class info section"))?,
section_function: section_function
.ok_or_else(|| anyhow::anyhow!("Missing function section"))?,
section_init_naked_func: section_init_naked_func
.ok_or_else(|| anyhow::anyhow!("Missing init naked func section"))?,
section_func_info: section_func_info
.ok_or_else(|| anyhow::anyhow!("Missing func info section"))?,
section_symbol_info,
section_global,
section_data,
section_const_string: section_const_string
.ok_or_else(|| anyhow::anyhow!("Missing const string section"))?,
section_link_info,
section_link_info_ex,
section_ref_func,
section_ref_code,
section_ref_class,
section_import_native_func: section_import_native_func
.ok_or_else(|| anyhow::anyhow!("Missing import native func section"))?,
})
}
}

View File

@@ -6,6 +6,7 @@ use anyhow::Result;
use int_enum::IntEnum;
use msg_tool_macro::{StructPack, StructUnpack};
use std::io::{Read, Seek, Write};
use std::ops::{Deref, DerefMut};
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
@@ -771,3 +772,437 @@ impl StructPack for TypedObject {
Ok(())
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct FieldInfoEntry {
pub name: WideString,
pub flags: u32,
pub type_object: TypedObject,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct TypeInfoEntry {
pub flags: u32,
pub type_object: TypedObject,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct PrototypeInfoEntry {
pub flags: u32,
pub name: WideString,
pub global_name: WideString,
pub return_type: TypeInfoEntry,
#[pvec(u32)]
pub arguments: Vec<TypeInfoEntry>,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct MethodInfoEntry {
pub prototype_info: PrototypeInfoEntry,
pub func_class: WideString,
pub pointer_data: [u8; 40],
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct ClassInfoEntry {
pub flags: u32,
pub name: WideString,
pub global_name: WideString,
#[pvec(u32)]
pub base_class_info: Vec<BaseClassInfoEntry>,
#[pvec(u32)]
pub base_class_cast_info: Vec<BaseClassCastInfoEntry>,
#[pvec(u32)]
pub field_info: Vec<FieldInfoEntry>,
#[pvec(u32)]
pub method_info: Vec<MethodInfoEntry>,
#[pvec(u32)]
pub extra_data: Vec<u8>,
}
#[derive(Clone, Debug)]
pub struct SectionClassInfo {
names: Vec<WideString>,
infos: Vec<ClassInfoEntry>,
}
impl StructUnpack for SectionClassInfo {
fn unpack<R: Read + Seek>(
reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<Self> {
let count = u32::unpack(reader, big, encoding, info)? as usize;
let mut names = Vec::with_capacity(count);
for _ in 0..count {
let name = WideString::unpack(reader, big, encoding, info)?;
names.push(name);
}
let mut infos = Vec::with_capacity(count);
for _ in 0..count {
let class_info = ClassInfoEntry::unpack(reader, big, encoding, info)?;
infos.push(class_info);
}
Ok(SectionClassInfo { names, infos })
}
}
impl StructPack for SectionClassInfo {
fn pack<W: Write>(
&self,
writer: &mut W,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<()> {
let count = self.names.len() as u32;
if count != self.infos.len() as u32 {
return Err(anyhow::anyhow!(
"SectionClassInfo pack error: names count {} does not match infos count {}",
count,
self.infos.len()
));
}
count.pack(writer, big, encoding, info)?;
for name in &self.names {
name.pack(writer, big, encoding, info)?;
}
for class_info in &self.infos {
class_info.pack(writer, big, encoding, info)?;
}
Ok(())
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct FuncNameEntry {
pub address: u32,
pub name: WideString,
}
#[derive(Clone, Debug, Default, StructPack, StructUnpack)]
pub struct DWordArray {
#[pvec(u32)]
pub data: Vec<u32>,
}
impl Deref for DWordArray {
type Target = Vec<u32>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl DerefMut for DWordArray {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionFunction {
pub prologue: DWordArray,
pub epilogue: DWordArray,
#[pvec(u32)]
pub func_names: Vec<FuncNameEntry>,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionInitNakedFunc {
pub naked_prologue: DWordArray,
pub naked_epilogue: DWordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct FuncEntryHeader {
pub flags: u32,
pub address: u32,
pub bytes: u32,
pub reserved: u32,
}
#[derive(Clone, Debug, StructPack)]
pub struct FuncInfoEntry {
pub header: FuncEntryHeader,
pub name: WideString,
pub reserved: Vec<u8>,
}
impl StructUnpack for FuncInfoEntry {
fn unpack<R: Read + Seek>(
reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<Self> {
let header = FuncEntryHeader::unpack(reader, big, encoding, info)?;
let name = WideString::unpack(reader, big, encoding, info)?;
let reserved_size = header.reserved as usize;
let reserved = reader.read_exact_vec(reserved_size)?;
Ok(Self {
header,
name,
reserved,
})
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionFuncInfo {
#[pvec(u32)]
pub functions: Vec<FuncInfoEntry>,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct NakedSymbolInfoEntry {
pub flags: u32,
pub reserved: u32,
pub address: u64,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SymbolInfoEntry {
pub info: NakedSymbolInfoEntry,
pub name: WideString,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionSymbolInfo {
#[pvec(u32)]
pub symbols: Vec<SymbolInfoEntry>,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct TagedObjectEntry {
pub tag: WideString,
pub object: TypedObject,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionGlobal {
#[pvec(u32)]
pub objects: Vec<TagedObjectEntry>,
}
#[derive(Clone, Debug)]
pub enum GlobalObjectEntryObj {
Object(TypedObject),
List(Vec<TagedObjectEntry>),
}
#[derive(Clone, Debug)]
pub struct GlobalObjectEntry {
pub name: WideString,
pub object: GlobalObjectEntryObj,
}
impl StructUnpack for GlobalObjectEntry {
fn unpack<R: Read + Seek>(
reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<Self> {
let name = WideString::unpack(reader, big, encoding, info)?;
let length = i32::unpack(reader, big, encoding, info)?;
if length >= 0 {
let obj = reader.read_struct_vec(length as usize, big, encoding, info)?;
Ok(Self {
name,
object: GlobalObjectEntryObj::List(obj),
})
} else {
let obj = TypedObject::unpack(reader, big, encoding, info)?;
Ok(Self {
name,
object: GlobalObjectEntryObj::Object(obj),
})
}
}
}
impl StructPack for GlobalObjectEntry {
fn pack<W: Write>(
&self,
writer: &mut W,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<()> {
self.name.pack(writer, big, encoding, info)?;
match &self.object {
GlobalObjectEntryObj::Object(obj) => {
(0x80000000u32).pack(writer, big, encoding, info)?;
obj.pack(writer, big, encoding, info)?;
}
GlobalObjectEntryObj::List(list) => {
let length = list.len() as i32;
length.pack(writer, big, encoding, info)?;
for item in list {
item.pack(writer, big, encoding, info)?;
}
}
}
Ok(())
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionData {
#[pvec(u32)]
pub objects: Vec<GlobalObjectEntry>,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct ConstStringEntry {
pub string: WideString,
pub refs: DWordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionConstString {
#[pvec(u32)]
pub strings: Vec<ConstStringEntry>,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct TagedDwordArrayEntry {
pub tag: WideString,
pub array: DWordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct TagedDwordArray {
#[pvec(u32)]
pub elements: Vec<TagedDwordArrayEntry>,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionLinkInfo {
pub ext_global_ref: DWordArray,
pub ext_data_ref: DWordArray,
pub imp_global_ref: TagedDwordArray,
pub imp_data_ref: TagedDwordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionLinkInfoEx {
pub flags: u32,
pub ext_naked_global_ref: DWordArray,
pub ext_naked_const_ref: DWordArray,
pub ext_naked_shared_ref: DWordArray,
pub ext_naked_func_ref: DWordArray,
pub imp_naked_global_ref: TagedDwordArray,
pub imp_naked_const_ref: TagedDwordArray,
pub imp_naked_shared_ref: TagedDwordArray,
pub imp_naked_func_ref: TagedDwordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionRefFunc {
pub refs: TagedDwordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionRefCode {
pub refs: DWordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionRefClass {
pub refs: DWordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionNativeFunc {
#[pvec(u32)]
pub names: Vec<WideString>,
pub addresses: DWordArray,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct SectionNakedFunc {
#[pvec(u32)]
pub names: Vec<WideString>,
pub addresses: DWordArray,
}
const ID_NATIVE_FUNC: u64 = 0x636E66766974616E;
const ID_NAKED_FUNC: u64 = 0x636E6664656B616E;
#[derive(Clone, Debug)]
pub struct SectionImportNativeFunc {
pub native_func: SectionNativeFunc,
pub naked_func: SectionNakedFunc,
}
impl StructUnpack for SectionImportNativeFunc {
fn unpack<R: Read + Seek>(
mut reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<Self> {
let len = reader.stream_length()?;
let mut native_func = None;
let mut naked_func = None;
while reader.stream_position()? < len {
let id = u64::unpack(reader, big, encoding, info)?;
let length = u64::unpack(reader, big, encoding, info)?;
match id {
ID_NATIVE_FUNC => {
let mut mem = StreamRegion::with_size(&mut reader, length)?;
let func = SectionNativeFunc::unpack(&mut mem, big, encoding, info)?;
native_func = Some(func);
}
ID_NAKED_FUNC => {
let mut mem = StreamRegion::with_size(&mut reader, length)?;
let func = SectionNakedFunc::unpack(&mut mem, big, encoding, info)?;
naked_func = Some(func);
}
_ => {
return Err(anyhow::anyhow!(
"Unknown SectionImportNativeFunc id: {:#X}",
id
));
}
}
}
Ok(Self {
native_func: native_func
.ok_or_else(|| anyhow::anyhow!("Missing native_func in SectionImportNativeFunc"))?,
naked_func: naked_func
.ok_or_else(|| anyhow::anyhow!("Missing naked_func in SectionImportNativeFunc"))?,
})
}
}
impl StructPack for SectionImportNativeFunc {
fn pack<W: Write>(
&self,
mut writer: &mut W,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<()> {
// Write native_func
ID_NATIVE_FUNC.pack(&mut writer, big, encoding, info)?;
let mut native_func_buf = Vec::new();
self.native_func
.pack(&mut native_func_buf, big, encoding, info)?;
(native_func_buf.len() as u64).pack(&mut writer, big, encoding, info)?;
writer.write_all(&native_func_buf)?;
// Write naked_func
ID_NAKED_FUNC.pack(&mut writer, big, encoding, info)?;
let mut naked_func_buf = Vec::new();
self.naked_func
.pack(&mut naked_func_buf, big, encoding, info)?;
(naked_func_buf.len() as u64).pack(&mut writer, big, encoding, info)?;
writer.write_all(&naked_func_buf)?;
Ok(())
}
}