WIP: Entis GLS CSX v2

This commit is contained in:
2026-01-20 11:40:09 +08:00
parent 8c9f2d7d52
commit ff886e66d3
33 changed files with 955 additions and 214 deletions

View File

@@ -4,6 +4,7 @@
//! Original license: GPL-3.0
mod base;
mod v1;
mod v2;
use crate::ext::io::*;
use crate::scripts::base::*;
@@ -12,6 +13,7 @@ use crate::utils::encoding::*;
use anyhow::Result;
use base::ECSImage;
use v1::ECSExecutionImageV1;
use v2::ECSExecutionImageV2;
#[derive(Debug)]
pub struct CSXScriptBuilder {}
@@ -66,9 +68,15 @@ pub struct CSXScript {
impl CSXScript {
pub fn new(buf: Vec<u8>, config: &ExtraConfig) -> Result<Self> {
let reader = MemReader::new(buf);
let img = ECSExecutionImageV1::new(reader.to_ref(), config)?;
let img = {
match ECSExecutionImageV1::new(reader.to_ref(), config) {
Ok(img) => Box::new(img),
Err(_) => Box::new(ECSExecutionImageV2::new(reader.to_ref(), config)?)
as Box<dyn ECSImage>,
}
};
Ok(Self {
img: Box::new(img),
img,
disasm: config.entis_gls_csx_disasm,
custom_yaml: config.custom_yaml,
})

View File

@@ -128,7 +128,7 @@ impl<'a> ECSExecutionImageDisassembler<'a> {
let length = self.stream.read_u32()?;
if length != 0x80000000 {
self.stream.seek_relative(-4)?;
let s = WideString::unpack(&mut self.stream, false, Encoding::Utf16LE)?.0;
let s = WideString::unpack(&mut self.stream, false, Encoding::Utf16LE, &None)?.0;
Ok((None, s))
} else if let Some(conststr) = &self.conststr {
let index = self.stream.read_u32()? as usize;

View File

@@ -36,10 +36,10 @@ pub struct ECSExecutionImage {
impl ECSExecutionImage {
pub fn new(mut reader: MemReaderRef<'_>, config: &ExtraConfig) -> Result<Self> {
let file_header = EMCFileHeader::unpack(&mut reader, false, Encoding::Utf8)?;
// if file_header.signagure != *b"Entis\x1a\0\0" {
// return Err(anyhow::anyhow!("Invalid EMC file signature"));
// }
let file_header = EMCFileHeader::unpack(&mut reader, false, Encoding::Utf8, &None)?;
if file_header.signagure != *b"Entis\x1a\0\0" {
return Err(anyhow::anyhow!("Invalid EMC file signature"));
}
let len = reader.data.len();
let mut exi_header = None;
let mut header = None;
@@ -70,7 +70,7 @@ impl ECSExecutionImage {
let buf = reader.read_exact_vec(size as usize)?;
{
let mut sread = MemReaderRef::new(&buf);
header = Some(EXIHeader::unpack(&mut sread, false, Encoding::Utf8)?);
header = Some(EXIHeader::unpack(&mut sread, false, Encoding::Utf8, &None)?);
}
exi_header = Some(buf);
if let Some(hdr) = &header {
@@ -85,12 +85,23 @@ impl ECSExecutionImage {
}
// function
0x6E6F6974636E7566 => {
pif_prologue = Some(DWordArray::unpack(&mut reader, false, Encoding::Utf8)?);
pif_epilogue = Some(DWordArray::unpack(&mut reader, false, Encoding::Utf8)?);
pif_prologue = Some(DWordArray::unpack(
&mut reader,
false,
Encoding::Utf8,
&None,
)?);
pif_epilogue = Some(DWordArray::unpack(
&mut reader,
false,
Encoding::Utf8,
&None,
)?);
function_list = Some(FunctionNameList::unpack(
&mut reader,
false,
Encoding::Utf8,
&None,
)?);
}
// global
@@ -98,7 +109,8 @@ impl ECSExecutionImage {
let count = reader.read_u32()?;
let mut items = Vec::with_capacity(count as usize);
for _ in 0..count {
let name = WideString::unpack(&mut reader, false, Encoding::Utf16LE)?.0;
let name =
WideString::unpack(&mut reader, false, Encoding::Utf16LE, &None)?.0;
let obj = ECSObject::read_from(&mut reader, int64)?;
items.push(ECSObjectItem { name, obj });
}
@@ -109,13 +121,19 @@ impl ECSExecutionImage {
let count = reader.read_u32()?;
let mut items = Vec::with_capacity(count as usize);
for _ in 0..count {
let name = WideString::unpack(&mut reader, false, Encoding::Utf16LE)?.0;
let name =
WideString::unpack(&mut reader, false, Encoding::Utf16LE, &None)?.0;
let length = reader.read_i32()?;
let obj = if length >= 0 {
let mut datas = Vec::with_capacity(length as usize);
for _ in 0..length {
let name =
WideString::unpack(&mut reader, false, Encoding::Utf16LE)?.0;
let name = WideString::unpack(
&mut reader,
false,
Encoding::Utf16LE,
&None,
)?
.0;
let obj = ECSObject::read_from(&mut reader, int64)?;
datas.push(ECSObjectItem { name, obj });
}
@@ -133,16 +151,17 @@ impl ECSExecutionImage {
&mut reader,
false,
Encoding::Utf8,
&None,
)?);
}
// linkinf
0x20666E696B6E696C => {
ext_global_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8)?;
ext_data_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8)?;
ext_global_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8, &None)?;
ext_data_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8, &None)?;
imp_global_ref =
TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8)?;
TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8, &None)?;
imp_data_ref =
TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8)?;
TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8, &None)?;
if !ext_global_ref.is_empty()
|| !ext_data_ref.is_empty()
|| !imp_global_ref.is_empty()
@@ -286,7 +305,8 @@ impl ECSExecutionImage {
}
fn save<'a>(&self, mut writer: Box<dyn Write + 'a>) -> Result<()> {
self.file_header.pack(&mut writer, false, Encoding::Utf8)?;
self.file_header
.pack(&mut writer, false, Encoding::Utf8, &None)?;
if let Some(exi_header) = &self.exi_header {
writer.write_u64(0x2020726564616568)?; // header
writer.write_u64(exi_header.len() as u64)?;
@@ -297,9 +317,12 @@ impl ECSExecutionImage {
writer.write_all(&self.image.data)?;
writer.write_u64(0x6E6F6974636E7566)?; // function
let mut mem = MemWriter::new();
self.pif_prologue.pack(&mut mem, false, Encoding::Utf8)?;
self.pif_epilogue.pack(&mut mem, false, Encoding::Utf8)?;
self.function_list.pack(&mut mem, false, Encoding::Utf8)?;
self.pif_prologue
.pack(&mut mem, false, Encoding::Utf8, &None)?;
self.pif_epilogue
.pack(&mut mem, false, Encoding::Utf8, &None)?;
self.function_list
.pack(&mut mem, false, Encoding::Utf8, &None)?;
let data = mem.into_inner();
writer.write_u64(data.len() as u64)?;
writer.write_all(&data)?;
@@ -312,7 +335,7 @@ impl ECSExecutionImage {
};
mem.write_u32(self.csg_global.len() as u32)?;
for item in self.csg_global.iter() {
WideString(item.name.clone()).pack(&mut mem, false, Encoding::Utf16LE)?;
WideString(item.name.clone()).pack(&mut mem, false, Encoding::Utf16LE, &None)?;
item.obj.write_to(&mut mem, int64)?;
}
let data = mem.into_inner();
@@ -322,7 +345,7 @@ impl ECSExecutionImage {
let mut mem = MemWriter::new();
mem.write_u32(self.csg_data.len() as u32)?;
for item in self.csg_data.iter() {
WideString(item.name.clone()).pack(&mut mem, false, Encoding::Utf16LE)?;
WideString(item.name.clone()).pack(&mut mem, false, Encoding::Utf16LE, &None)?;
match &item.obj {
ECSObject::Global(g) => {
mem.write_i32(g.len() as i32)?;
@@ -331,6 +354,7 @@ impl ECSExecutionImage {
&mut mem,
false,
Encoding::Utf16LE,
&None,
)?;
data_item.obj.write_to(&mut mem, int64)?;
}
@@ -347,17 +371,21 @@ impl ECSExecutionImage {
if let Some(ext_const_str) = &self.ext_const_str {
writer.write_u64(0x72747374736E6F63)?; // conststr
let mut mem = MemWriter::new();
ext_const_str.pack(&mut mem, false, Encoding::Utf8)?;
ext_const_str.pack(&mut mem, false, Encoding::Utf8, &None)?;
let data = mem.into_inner();
writer.write_u64(data.len() as u64)?;
writer.write_all(&data)?;
}
writer.write_u64(0x20666E696B6E696C)?; // linkinf
let mut mem = MemWriter::new();
self.ext_global_ref.pack(&mut mem, false, Encoding::Utf8)?;
self.ext_data_ref.pack(&mut mem, false, Encoding::Utf8)?;
self.imp_global_ref.pack(&mut mem, false, Encoding::Utf8)?;
self.imp_data_ref.pack(&mut mem, false, Encoding::Utf8)?;
self.ext_global_ref
.pack(&mut mem, false, Encoding::Utf8, &None)?;
self.ext_data_ref
.pack(&mut mem, false, Encoding::Utf8, &None)?;
self.imp_global_ref
.pack(&mut mem, false, Encoding::Utf8, &None)?;
self.imp_data_ref
.pack(&mut mem, false, Encoding::Utf8, &None)?;
let data = mem.into_inner();
writer.write_u64(data.len() as u64)?;
writer.write_all(&data)?;
@@ -402,7 +430,8 @@ impl ECSImage for ECSExecutionImage {
disasm.stream.pos = cmd.addr as usize + 1;
let _csom = disasm.read_csom()?;
let num_args = disasm.stream.read_i32()?;
let func_name = WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE)?.0;
let func_name =
WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
let mut is_mess = false;
if num_args == 1 {
if func_name == "SceneTitle" {
@@ -492,7 +521,8 @@ impl ECSImage for ECSExecutionImage {
disasm.stream.pos = cmd.addr as usize + 1;
let csom = disasm.read_csom()?;
let num_args = disasm.stream.read_i32()?;
let func_name = WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE)?.0;
let func_name =
WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
let mut is_mess = false;
if num_args == 1 {
if func_name == "SceneTitle" {
@@ -568,7 +598,8 @@ impl ECSImage for ECSExecutionImage {
string_stack.clear();
} else if is_enter {
disasm.stream.pos = cmd.addr as usize + 1;
let name = WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE)?.0;
let name =
WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
let num_args = disasm.stream.read_i32()?;
if num_args == 0 {
pre_enter_name = name.clone();
@@ -630,7 +661,8 @@ impl ECSImage for ECSExecutionImage {
disasm.stream.pos = cmd.addr as usize + 1;
let csom = disasm.read_csom()?;
let num_args = disasm.stream.read_i32()?;
let func_name = WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE)?.0;
let func_name =
WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
let mut is_mess = false;
if csom == CsomAuto && num_args == 1 && func_name == "Mess" {
is_mess = true;
@@ -718,7 +750,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(CsomImmediate.into())?;
new_image.write_u8(CsvtString.into())?;
WideString(mes).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(mes).pack(&mut new_image, false, Encoding::Utf8, &None)?;
new_assembly.push(tcmd);
let mut tcmd = if tmp_index <= post_index {
let data = assembly[tmp_index].clone();
@@ -746,6 +778,7 @@ impl ECSImage for ECSExecutionImage {
&mut new_image,
false,
Encoding::Utf16LE,
&None,
)?;
new_assembly.push(tcmd);
let mut tcmd = if tmp_index <= post_index {
@@ -825,7 +858,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(lcsom.into())?;
new_image.write_u8(lcsvt.into())?;
WideString(name).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(name).pack(&mut new_image, false, Encoding::Utf8, &None)?;
dumped_index += 1;
while dumped_index <= index {
let tcmd = &mut assembly[dumped_index];
@@ -886,7 +919,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(lcsom.into())?;
new_image.write_u8(lcsvt.into())?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
dumped_index += 1;
while dumped_index <= index {
let tcmd = &mut assembly[dumped_index];
@@ -947,7 +980,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(lcsom.into())?;
new_image.write_u8(lcsvt.into())?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
dumped_index += 1;
while dumped_index <= index {
let tcmd = &mut assembly[dumped_index];
@@ -1014,7 +1047,8 @@ impl ECSImage for ECSExecutionImage {
disasm.stream.pos = cmd.addr as usize + 1;
let csom = disasm.read_csom()?;
let num_args = disasm.stream.read_i32()?;
let func_name = WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE)?.0;
let func_name =
WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
let mut is_mess = false;
if csom == CsomAuto && num_args == 1 && func_name == "Mess" {
is_mess = true;
@@ -1103,7 +1137,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(CsomImmediate.into())?;
new_image.write_u8(CsvtString.into())?;
WideString(mes).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(mes).pack(&mut new_image, false, Encoding::Utf8, &None)?;
new_assembly.push(tcmd);
let mut tcmd = if tmp_index <= post_index {
let data = assembly[tmp_index].clone();
@@ -1131,6 +1165,7 @@ impl ECSImage for ECSExecutionImage {
&mut new_image,
false,
Encoding::Utf16LE,
&None,
)?;
new_assembly.push(tcmd);
let mut tcmd = if tmp_index <= post_index {
@@ -1215,7 +1250,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(lcsom.into())?;
new_image.write_u8(lcsvt.into())?;
WideString(name).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(name).pack(&mut new_image, false, Encoding::Utf8, &None)?;
dumped_index += 1;
while dumped_index <= index {
let tcmd = &mut assembly[dumped_index];
@@ -1274,7 +1309,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(lcsom.into())?;
new_image.write_u8(lcsvt.into())?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
dumped_index += 1;
while dumped_index <= index {
let tcmd = &mut assembly[dumped_index];
@@ -1339,7 +1374,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(CsicLoad.into())?;
new_image.write_u8(lcsom.into())?;
new_image.write_u8(lcsvt.into())?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8)?;
WideString(message).pack(&mut new_image, false, Encoding::Utf8, &None)?;
dumped_index += 1;
while dumped_index <= index {
let tcmd = &mut assembly[dumped_index];
@@ -1357,7 +1392,7 @@ impl ECSImage for ECSExecutionImage {
} else if is_enter {
disasm.stream.pos = cmd.addr as usize + 1;
let original_name =
WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE)?.0;
WideString::unpack(&mut disasm.stream, false, Encoding::Utf16LE, &None)?.0;
let num_args = disasm.stream.read_i32()?;
if num_args == 0 {
pre_enter_name = original_name.clone();
@@ -1426,7 +1461,7 @@ impl ECSImage for ECSExecutionImage {
new_image.write_u8(code)?;
new_image.write_u8(csom)?;
new_image.write_u8(csvt)?;
s.pack(&mut new_image, false, Encoding::Utf8)?;
s.pack(&mut new_image, false, Encoding::Utf8, &None)?;
continue;
}
}

View File

@@ -1,4 +1,4 @@
//! Ported from CSXTools C# project
//! Ported from CSXTool C# project
//! See parent module documentation for more details.
mod disasm;
mod img;

View File

@@ -129,8 +129,13 @@ impl DerefMut for DWordArray {
pub struct WideString(pub String);
impl StructUnpack for WideString {
fn unpack<R: Read + Seek>(reader: &mut R, big: bool, encoding: Encoding) -> Result<Self> {
let length = u32::unpack(reader, big, encoding)? as usize;
fn unpack<R: Read + Seek>(
reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<Self> {
let length = u32::unpack(reader, big, encoding, info)? as usize;
let to_read = length * 2;
let buf = reader.read_exact_vec(to_read)?;
let enc = if big {
@@ -144,7 +149,13 @@ impl StructUnpack for WideString {
}
impl StructPack for WideString {
fn pack<W: Write>(&self, writer: &mut W, big: bool, encoding: Encoding) -> Result<()> {
fn pack<W: Write>(
&self,
writer: &mut W,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<()> {
let enc = if big {
Encoding::Utf16BE
} else {
@@ -152,7 +163,7 @@ impl StructPack for WideString {
};
let encoded = encode_string(enc, &self.0, false)?;
let length = (encoded.len() / 2) as u32;
length.pack(writer, big, encoding)?;
length.pack(writer, big, encoding, info)?;
writer.write_all(&encoded)?;
Ok(())
}
@@ -204,7 +215,7 @@ impl ECSObject {
.map_err(|typ| anyhow::anyhow!("Invalid CSVariableType: {}", typ))?;
match obj_typ {
CSVariableType::CsvtObject => {
let class_name = WideString::unpack(reader, false, Encoding::Utf8)?.0;
let class_name = WideString::unpack(reader, false, Encoding::Utf8, &None)?.0;
return Ok(ECSObject::ClassInfoObject(class_name));
}
CSVariableType::CsvtReference => {
@@ -236,7 +247,7 @@ impl ECSObject {
return Ok(ECSObject::Real(val));
}
CSVariableType::CsvtString => {
let s = WideString::unpack(reader, false, Encoding::Utf8)?.0;
let s = WideString::unpack(reader, false, Encoding::Utf8, &None)?.0;
return Ok(ECSObject::String(s));
}
_ => {
@@ -250,7 +261,7 @@ impl ECSObject {
ECSObject::ClassInfoObject(name) => {
let obj_type: u8 = CSVariableType::CsvtObject.into();
writer.write_i32(obj_type as i32)?;
WideString(name.clone()).pack(writer, false, Encoding::Utf8)?;
WideString(name.clone()).pack(writer, false, Encoding::Utf8, &None)?;
}
ECSObject::Reference => {
let obj_type: u8 = CSVariableType::CsvtReference.into();
@@ -285,7 +296,7 @@ impl ECSObject {
ECSObject::String(s) => {
let obj_type: u8 = CSVariableType::CsvtString.into();
writer.write_i32(obj_type as i32)?;
WideString(s.clone()).pack(writer, false, Encoding::Utf8)?;
WideString(s.clone()).pack(writer, false, Encoding::Utf8, &None)?;
}
_ => {
return Err(anyhow::anyhow!(
@@ -380,7 +391,7 @@ impl DerefMut for ECSExecutionImageAssembly {
fn test_exi_header_unpack() {
let data = b"\x01\x00\x00\x00";
let mut cursor = MemReaderRef::new(data);
let header = EXIHeader::unpack(&mut cursor, false, Encoding::Utf8).unwrap();
let header = EXIHeader::unpack(&mut cursor, false, Encoding::Utf8, &None).unwrap();
assert_eq!(header.version, 1);
assert_eq!(header.int_base, 0);
}

View File

@@ -0,0 +1,144 @@
use super::super::base::*;
use super::types::*;
use crate::ext::io::*;
use crate::scripts::base::*;
use crate::types::*;
use crate::utils::struct_pack::*;
use anyhow::Result;
use std::collections::HashMap;
const ID_HEADER: u64 = 0x2020726564616568; // header
const ID_IMAGE: u64 = 0x2020206567616D69;
const ID_IMAGE_GLOBAL: u64 = 0x6C626F6C67676D69;
const ID_IMAGE_CONST: u64 = 0x74736E6F63676D69;
const ID_IMAGE_SHARED: u64 = 0x6572616873676D69;
const ID_CLASS_INFO: u64 = 0x666E697373616C63;
const ID_FUNCTION: u64 = 0x6E6F6974636E7566;
const ID_INIT_NAKED_FUNC: u64 = 0x636E666E74696E69;
const ID_FUNC_INFO: u64 = 0x6F666E69636E7566;
const ID_SYMBOL_INFO: u64 = 0x666E696C626D7973;
const ID_GLOBAL: u64 = 0x20206C61626F6C67;
const ID_DATA: u64 = 0x2020202061746164;
const ID_CONST_STRING: u64 = 0x72747374736E6F63;
const ID_LINK_INFO: u64 = 0x20666E696B6E696C;
const ID_LINK_INFO_EX: u64 = 0x343678656B6E696C;
const ID_REF_FUNC: u64 = 0x20636E7566666572;
const ID_REF_CODE: u64 = 0x2065646F63666572;
const ID_REF_CLASS: u64 = 0x7373616C63666572;
const ID_IMPORT_NATIVE_FUNC: u64 = 0x766974616E706D69;
#[derive(Clone, Debug)]
#[allow(unused)]
pub struct ECSExecutionImage {
file_header: FileHeader,
section_header: SectionHeader,
image: MemReader,
image_global: Option<MemReader>,
image_const: Option<MemReader>,
image_shared: Option<MemReader>,
}
impl ECSExecutionImage {
pub fn new(mut reader: MemReaderRef<'_>, _config: &ExtraConfig) -> Result<Self> {
let file_header = FileHeader::unpack(&mut reader, false, Encoding::Utf8, &None)?;
if file_header.signagure != *b"Entis\x1a\0\0" {
return Err(anyhow::anyhow!("Invalid EMC file signature"));
}
if !file_header.format_desc.starts_with(b"Cotopha Image file") {
return Err(anyhow::anyhow!("Invalid EMC file format description"));
}
let mut section_header = SectionHeader::default();
let len = reader.data.len();
let mut image = None;
let mut image_global = None;
let mut image_const = None;
let mut image_shared = None;
while reader.pos < len {
if len - reader.pos < 16 {
break;
}
let id = reader.read_u64()?;
if id == 0 {
break;
}
let size = reader.read_u64()?;
let pos = reader.pos;
match id {
ID_HEADER => {
let mut mem = StreamRegion::with_size(&mut reader, size)?;
section_header = SectionHeader::unpack(&mut mem, false, Encoding::Utf8, &None)?;
}
ID_IMAGE => {
image = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
}
ID_IMAGE_GLOBAL => {
image_global = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
}
ID_IMAGE_CONST => {
image_const = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
}
ID_IMAGE_SHARED => {
image_shared = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
}
0 => {
break;
}
_ => {
return Err(anyhow::anyhow!(
"Unknown ECSExecutionImage section ID: 0x{:016X}",
id
));
}
}
reader.pos = pos + size as usize;
}
Ok(Self {
file_header,
section_header,
image: image.ok_or_else(|| anyhow::anyhow!("Missing image data"))?,
image_global,
image_const,
image_shared,
})
}
}
impl ECSImage for ECSExecutionImage {
fn disasm<'a>(&self, _writer: Box<dyn std::io::Write + 'a>) -> Result<()> {
Err(anyhow::anyhow!("Disassembly not implemented for CSX v2"))
}
fn export(&self) -> Result<Vec<Message>> {
Err(anyhow::anyhow!("Export not implemented for CSX v2"))
}
fn export_multi(&self) -> Result<HashMap<String, Vec<Message>>> {
Err(anyhow::anyhow!("Export multi not implemented for CSX v2"))
}
fn export_all(&self) -> Result<Vec<String>> {
Err(anyhow::anyhow!("Export all not implemented for CSX v2"))
}
fn import<'a>(
&self,
_messages: Vec<Message>,
_file: Box<dyn WriteSeek + 'a>,
_replacement: Option<&'a ReplacementTable>,
) -> Result<()> {
Err(anyhow::anyhow!("Import not implemented for CSX v2"))
}
fn import_multi<'a>(
&self,
_messages: HashMap<String, Vec<Message>>,
_file: Box<dyn WriteSeek + 'a>,
_replacement: Option<&'a ReplacementTable>,
) -> Result<()> {
Err(anyhow::anyhow!("Import multi not implemented for CSX v2"))
}
fn import_all<'a>(&self, _messages: Vec<String>, _file: Box<dyn WriteSeek + 'a>) -> Result<()> {
Err(anyhow::anyhow!("Import all not implemented for CSX v2"))
}
}

View File

@@ -0,0 +1,6 @@
//! Ported from CSXToolPlus C# project
//! See parent module documentation for more details.
mod img;
mod types;
pub use img::ECSExecutionImage as ECSExecutionImageV2;

View File

@@ -0,0 +1,448 @@
use crate::ext::io::*;
use crate::types::*;
use crate::utils::encoding::*;
use crate::utils::struct_pack::*;
use anyhow::Result;
use int_enum::IntEnum;
use msg_tool_macro::{StructPack, StructUnpack};
use std::io::{Read, Seek, Write};
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSCompareType {
CsctNotEqual,
CsctEqual,
CsctLessThan,
CsctLessEqual,
CsctGreaterThan,
CsctGreaterEqual,
CsctNotEqualPointer,
CsctEqualPointer,
}
pub use CSCompareType::*;
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSExtraOperatorType {
CsxotArrayDim,
CsxotHashContainer,
CsxotMoveReference,
}
pub use CSExtraOperatorType::*;
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSExtraUniOperatorType {
CsxuotDeselect,
CsxuotBoolean,
CsxuotSizeOf,
CsxuotTypeOf,
CsxuotStaticCast,
CsxuotDynamicCast,
CsxuotDuplicate,
CsxuotDelete,
CsxuotDeleteArray,
CsxuotLoadAddress,
CsxuotRefAddress,
}
pub use CSExtraUniOperatorType::*;
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSInstructionCode {
CsicNew = 0,
CsicFree = 1,
CsicLoad = 2,
CsicStore = 3,
CsicEnter = 4,
CsicLeave = 5,
CsicJump = 6,
CsicCJump = 7,
CsicCall = 8,
CsicReturn = 9,
CsicElement = 10,
CsicElementIndirect = 11,
CsicOperate = 12,
CsicUniOperate = 13,
CsicCompare = 14,
CsicExOperate = 15,
CsicExUniOperate = 16,
CsicExCall = 17,
CsicExReturn = 18,
CsicCallMember = 19,
CsicCallNativeMember = 20,
CsicSwap = 21,
CsicCreateBufferVSize = 23,
CsicPointerToObject = 24,
CsicReferenceForPointer = 26,
CsicCallNativeFunction = 29,
// Shell
CodeLoadMem = 0x80,
CodeLoadMemBaseImm32,
CodeLoadMemBaseIndex,
CodeLoadMemBaseIndexImm32,
CodeStoreMem = 0x84,
CodeStoreMemBaseImm32,
CodeStoreMemBaseIndex,
CodeStoreMemBaseIndexImm32,
CodeLoadLocal = 0x88,
CodeLoadLocalIndexImm32,
CodeStoreLocal = 0x8A,
CodeStoreLocalIndexImm32,
CodeMoveReg = 0x90,
CodeCvtFloat2Int = 0x92,
CodeCvtInt2Float = 0x93,
CodeSrlImm8 = 0x94,
CodeSraImm8,
CodeSllImm8,
CodeMaskMove,
CodeAddImm32 = 0x98,
CodeMulImm32,
CodeAddSPImm32 = 0x9A,
CodeLoadImm64 = 0x9B,
CodeNegInt = 0x9C,
CodeNotInt,
CodeNegFloat,
CodeAddReg = 0xA0,
CodeSubReg,
CodeMulReg,
CodeDivReg,
CodeModReg,
CodeAndReg,
CodeOrReg,
CodeXorReg,
CodeSrlReg,
CodeSraReg,
CodeSllReg,
CodeMoveSx32Reg = 0xAB,
CodeMoveSx16Reg,
CodeMoveSx8Reg,
CodeFAddReg = 0xB0,
CodeFSubReg,
CodeFMulReg,
CodeFDivReg,
CodeMul32Reg = 0xB8,
CodeIMul32Reg,
CodeDiv32Reg,
CodeIDiv32Reg,
CodeMod32Reg,
CodeIMod32Reg,
CodeCmpNeReg = 0xC0,
CodeCmpEqReg,
CodeCmpLtReg,
CodeCmpLeReg,
CodeCmpGtReg,
CodeCmpGeReg,
CodeCmpCReg,
CodeCmpCZReg,
CodeFCmpNeReg = 0xC8,
CodeFCmpEqReg,
CodeFCmpLtReg,
CodeFCmpLeReg,
CodeFCmpGtReg,
CodeFCmpGeReg,
CodeJumpOffset32 = 0xD0,
CodeJumpReg = 0xD1,
CodeCNJumpOffset32 = 0xD2,
CodeCJumpOffset32,
CodeCallImm32 = 0xD4,
CodeCallReg = 0xD5,
CodeSysCallImm32 = 0xD6,
CodeSysCallReg = 0xD7,
CodeReturn = 0xD8,
CodePushReg = 0xDC,
CodePopReg,
CodePushRegs,
CodePopRegs,
CodeMemoryHint = 0xE0,
CodeFloatExtension,
CodeSIMD64Extension2Op,
CodeSIMD64Extension3Op,
CodeSIMD128Extension2Op,
CodeSIMD128Extension3Op,
CodeEscape = 0xFD,
CodeNoOperation = 0xFE,
CodeSystemReserved = 0xFF,
}
pub use CSInstructionCode::*;
pub use CodeLoadLocal as CodeLoadLocalImm32;
pub use CodeLoadMem as CodeLoadMemBase;
pub use CodeStoreLocal as CodeStoreLocalImm32;
pub use CodeStoreMem as CodeStoreMemBase;
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSObjectMode {
CsomImmediate,
CsomStack,
CsomThis,
CsomGlobal,
CsomData,
CsomAuto,
}
pub use CSObjectMode::*;
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSOperatorType {
CsotNop = 0xFF,
CsotAdd = 0,
CsotSub,
CsotMul,
CsotDiv,
CsotMod,
CsotAnd,
CsotOr,
CsotXor,
CsotLogicalAnd,
CsotLogicalOr,
CsotShiftRight,
CsotShiftLeft,
}
pub use CSOperatorType::*;
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSUnaryOperatorType {
CsuotPlus,
CsuotNegate,
CsuotBitnot,
CsuotLogicalNot,
}
pub use CSUnaryOperatorType::*;
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSVariableType {
CsvtObject,
CsvtReference,
CsvtArray,
CsvtHash,
CsvtInteger,
CsvtReal,
CsvtString,
CsvtInteger64,
CsvtPointer,
CsvtClassObject,
CsvtBoolean,
CsvtInt8,
CsvtUint8,
CsvtInt16,
CsvtUint16,
CsvtInt32,
CsvtUint32,
CsvtArrayDimension,
CsvtHashContainer,
CsvtReal32,
CsvtReal64,
CsvtPointerReference,
CsvtBuffer,
CsvtFunction,
}
pub use CSVariableType::*;
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct FileHeader {
pub signagure: [u8; 8],
pub file_id: u32,
pub _reserved: u32,
pub format_desc: [u8; 48],
}
#[derive(Clone, Debug, msg_tool_macro::Default)]
pub struct SectionHeader {
#[default(3)]
pub full_ver: u32,
pub header_size: u32,
pub version: u32,
pub int_base: u32,
pub container_flags: u32,
pub _reserved: u32,
pub stack_size: u32,
pub heap_size: u32,
pub entry_point: u32,
pub static_initialize: u32,
pub resume_prepare: u32,
}
impl StructUnpack for SectionHeader {
fn unpack<R: Read + Seek>(
reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<Self> {
let full_ver = 3;
let header_size = reader.stream_length()? as u32;
let version = if header_size >= 4 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let int_base = if header_size >= 8 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let container_flags = if header_size >= 12 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let _reserved = if header_size >= 16 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let stack_size = if header_size >= 20 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let heap_size = if header_size >= 24 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let entry_point = if header_size >= 28 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let static_initialize = if header_size >= 32 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
let resume_prepare = if header_size >= 36 {
u32::unpack(reader, big, encoding, info)?
} else {
0
};
Ok(Self {
full_ver,
header_size,
version,
int_base,
container_flags,
_reserved,
stack_size,
heap_size,
entry_point,
static_initialize,
resume_prepare,
})
}
}
impl StructPack for SectionHeader {
fn pack<W: Write>(
&self,
writer: &mut W,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<()> {
if self.header_size >= 4 {
self.version.pack(writer, big, encoding, info)?;
}
if self.header_size >= 8 {
self.int_base.pack(writer, big, encoding, info)?;
}
if self.header_size >= 12 {
self.container_flags.pack(writer, big, encoding, info)?;
}
if self.header_size >= 16 {
self._reserved.pack(writer, big, encoding, info)?;
}
if self.header_size >= 20 {
self.stack_size.pack(writer, big, encoding, info)?;
}
if self.header_size >= 24 {
self.heap_size.pack(writer, big, encoding, info)?;
}
if self.header_size >= 28 {
self.entry_point.pack(writer, big, encoding, info)?;
}
if self.header_size >= 32 {
self.static_initialize.pack(writer, big, encoding, info)?;
}
if self.header_size >= 36 {
self.resume_prepare.pack(writer, big, encoding, info)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct WideString(pub String);
impl StructUnpack for WideString {
fn unpack<R: Read + Seek>(
reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<Self> {
let length = u32::unpack(reader, big, encoding, info)? as usize;
let to_read = length * 2;
let buf = reader.read_exact_vec(to_read)?;
let enc = if big {
Encoding::Utf16BE
} else {
Encoding::Utf16LE
};
let s = decode_to_string(enc, &buf, true)?;
Ok(Self(s))
}
}
impl StructPack for WideString {
fn pack<W: Write>(
&self,
writer: &mut W,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn std::any::Any>>,
) -> Result<()> {
let enc = if big {
Encoding::Utf16BE
} else {
Encoding::Utf16LE
};
let encoded = encode_string(enc, &self.0, false)?;
let length = (encoded.len() / 2) as u32;
length.pack(writer, big, encoding, info)?;
writer.write_all(&encoded)?;
Ok(())
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct BaseClassInfoEntry {
pub flags: u32,
pub name: WideString,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct ECSCastInterface {
pub cast_object: i32,
pub var_offset: i32,
pub var_bounds: i32,
pub func_offset: i32,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct BaseClassCastInfoEntry {
pub name: WideString,
pub pci: ECSCastInterface,
pub flags: u32,
}