add entis gls csx v1 disasm (ported from Crsky/EntisGLS_Tools)

This commit is contained in:
2026-01-17 17:30:41 +08:00
parent 3444c3ac65
commit ed4179473f
11 changed files with 1185 additions and 1 deletions

View File

@@ -0,0 +1,3 @@
//! Ported from Crsky/EntisGLS_Tools C# project
//! Original license: GPL-3.0
pub mod v1;

View File

@@ -0,0 +1,560 @@
use super::types::*;
use crate::ext::io::*;
use crate::types::*;
use crate::utils::struct_pack::*;
use anyhow::Result;
use std::io::{Seek, Write};
use CSCompareType::*;
use CSInstructionCode::*;
use CSObjectMode::*;
use CSOperatorType::*;
use CSUnaryOperatorType::*;
use CSVariableType::*;
fn escape_string(s: &str) -> String {
s.replace("\r", "\\r")
.replace("\n", "\\n")
.replace("\t", "\\t")
}
pub struct ECSExecutionImageDisassembler<'a> {
stream: MemReaderRef<'a>,
conststr: Option<&'a TaggedRefAddressList>,
assembly: ECSExecutionImageAssembly,
writer: Option<Box<dyn Write + 'a>>,
addr: u32,
code: CSInstructionCode,
}
impl<'a> ECSExecutionImageDisassembler<'a> {
pub fn new(
stream: MemReaderRef<'a>,
conststr: Option<&'a TaggedRefAddressList>,
writer: Option<Box<dyn Write + 'a>>,
) -> Self {
Self {
stream,
conststr,
assembly: ECSExecutionImageAssembly {
command_list: Vec::new(),
},
writer,
addr: 0,
code: CsicNew,
}
}
pub fn execute(&mut self) -> Result<()> {
let len = self.stream.data.len();
while self.stream.pos < len {
self.addr = self.stream.pos as u32;
let code_value = self.stream.read_u8()?;
self.code = CSInstructionCode::try_from(code_value).map_err(|_| {
anyhow::anyhow!(
"Invalid CSInstructionCode value: {} at {:08x}",
code_value,
self.addr
)
})?;
match self.code {
CsicNew => {
self.command_new()?;
}
CsicFree => {
self.command_free()?;
}
CsicLoad => {
self.command_load()?;
}
CsicStore => {
self.command_store()?;
}
CsicEnter => {
self.command_enter()?;
}
CsicLeave => {
self.command_leave()?;
}
CsicJump => {
self.command_jump()?;
}
CsicCJump => {
self.command_cjump()?;
}
CsicCall => {
self.command_call()?;
}
CsicReturn => {
self.command_return()?;
}
CsicElement => {
self.command_element()?;
}
CsicElementIndirect => {
self.command_element_indirect()?;
}
CsicOperate => {
self.command_operate()?;
}
CsicUniOperate => {
self.command_uni_operate()?;
}
CsicCompare => {
self.command_compare()?;
}
}
let size = self.stream.pos as u32 - self.addr;
self.assembly
.command_list
.push(ECSExecutionImageCommandRecord {
code: self.code,
addr: self.addr,
size,
new_addr: self.addr,
});
}
Ok(())
}
fn line<S: AsRef<str> + ?Sized>(&mut self, line: &S) -> anyhow::Result<()> {
if let Some(writer) = &mut self.writer {
writeln!(writer, "{:08x} {}", self.addr, line.as_ref())?;
}
Ok(())
}
fn get_string_literal2(&mut self) -> Result<(Option<usize>, String)> {
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;
Ok((None, s))
} else if let Some(conststr) = &self.conststr {
let index = self.stream.read_u32()? as usize;
match conststr.get(index) {
Some(s) => Ok((Some(index), s.tag.0.clone())),
None => Err(anyhow::anyhow!(
"Invalid string literal index: {} (max {})",
index,
conststr.len()
)),
}
} else {
Err(anyhow::anyhow!(
"No constant string table for string literal index"
))
}
}
fn get_string_literal(&mut self) -> Result<String> {
let (_, s) = self.get_string_literal2()?;
Ok(s)
}
fn read_csct(&mut self) -> Result<CSCompareType> {
let value = self.stream.read_u8()?;
CSCompareType::try_from(value).map_err(|_| {
anyhow::anyhow!(
"Invalid CSCompareType value: {} at {:08x}",
value,
self.addr
)
})
}
fn read_csom(&mut self) -> Result<CSObjectMode> {
let value = self.stream.read_u8()?;
CSObjectMode::try_from(value).map_err(|_| {
anyhow::anyhow!("Invalid CSObjectMode value: {} at {:08x}", value, self.addr)
})
}
fn read_csot(&mut self) -> Result<CSOperatorType> {
let value = self.stream.read_u8()?;
CSOperatorType::try_from(value).map_err(|_| {
anyhow::anyhow!(
"Invalid CSOperatorType value: {} at {:08x}",
value,
self.addr
)
})
}
fn read_csuot(&mut self) -> Result<CSUnaryOperatorType> {
let value = self.stream.read_u8()?;
CSUnaryOperatorType::try_from(value).map_err(|_| {
anyhow::anyhow!(
"Invalid CSUnaryOperatorType value: {} at {:08x}",
value,
self.addr
)
})
}
fn read_csvt(&mut self) -> Result<CSVariableType> {
let value = self.stream.read_u8()?;
CSVariableType::try_from(value).map_err(|_| {
anyhow::anyhow!(
"Invalid CSVariableType value: {} at {:08x}",
value,
self.addr
)
})
}
fn command_new(&mut self) -> Result<()> {
let csom = self.read_csom()?;
let typ = self.read_csvt()?;
let class_name = if typ == CsvtObject {
Some(self.get_string_literal()?)
} else {
None
};
let name = self.get_string_literal()?;
let pobj = match csom {
CsomStack => "stack",
CsomThis => "this",
_ => {
return Err(anyhow::anyhow!(
"Invalid CSObjectMode for 'new' instruction: {:?} at {:08x}",
csom,
self.addr
));
}
};
match &class_name {
Some(class_name) => {
self.line(&format!("New {pobj} \"{class_name}\" \"{name}\""))?;
}
None => {
self.line(&format!("New {pobj} \"{name}\""))?;
}
}
Ok(())
}
fn command_free(&mut self) -> Result<()> {
self.line("Free")?;
Ok(())
}
fn command_load(&mut self) -> Result<()> {
let csom = self.read_csom()?;
let csvt = self.read_csvt()?;
if csom == CsomImmediate {
match csvt {
CsvtObject => {
let class_name = self.get_string_literal()?;
self.line(&format!("Load * {class_name}"))?;
}
CsvtReference => {
self.line("Load * ECSReference")?;
}
CsvtArray => {
self.line("Load * ECSArray")?;
}
CsvtHash => {
self.line("Load * ECSHash")?;
}
CsvtInteger => {
let val = self.stream.read_u32()?;
self.line(&format!("Load Integer {}", val))?;
}
CsvtReal => {
let val = self.stream.read_f64()?;
self.line(&format!("Load Real {}", val))?;
}
CsvtString => {
let t = self.get_string_literal2()?;
let escaped = escape_string(&t.1);
if let Some(index) = t.0 {
self.line(&format!("Load Const String {index} \"{escaped}\""))?;
} else {
self.line(&format!("Load String \"{escaped}\""))?;
}
}
CsvtInteger64 => {
let val = self.stream.read_u64()?;
self.line(&format!("Load Integer64 {}", val))?;
}
CsvtPointer => {
let value = self.stream.read_u32()?;
self.line(&format!("Load Pointer {}", value))?;
}
}
} else {
let pobj = match csom {
CsomStack => "stack",
CsomThis => "this",
CsomGlobal => "global",
CsomData => "data",
CsomAuto => "auto",
_ => {
return Err(anyhow::anyhow!(
"Invalid CSObjectMode for 'load' instruction: {:?} at {:08x}",
csom,
self.addr
));
}
};
match csvt {
CsvtReference => {
self.line(&format!("Load {pobj}"))?;
}
CsvtInteger => {
let index = self.stream.read_i32()?;
self.line(&format!("Load {pobj} [{index}]"))?;
}
CsvtString => {
let name = self.get_string_literal()?;
self.line(&format!("Load {pobj} [\"{name}\"]"))?;
}
_ => {
return Err(anyhow::anyhow!(
"Invalid CSVariableType for 'load' instruction: {:?} at {:08x}",
csvt,
self.addr
));
}
}
}
Ok(())
}
fn command_store(&mut self) -> Result<()> {
let csot = self.read_csot()?;
match csot {
CsotNop => {
self.line("Store")?;
}
CsotAdd => {
self.line("Store Add")?;
}
CsotSub => {
self.line("Store Sub")?;
}
CsotMul => {
self.line("Store Mul")?;
}
CsotDiv => {
self.line("Store Div")?;
}
CsotMod => {
self.line("Store Mod")?;
}
CsotAnd => {
self.line("Store And")?;
}
CsotOr => {
self.line("Store Or")?;
}
CsotXor => {
self.line("Store Xor")?;
}
CsotLogicalAnd => {
self.line("Store LAnd")?;
}
CsotLogicalOr => {
self.line("Store LOr")?;
}
}
Ok(())
}
fn command_enter(&mut self) -> Result<()> {
let name = self.get_string_literal()?;
let num_args = self.stream.read_i32()?;
if num_args != -1 {
let mut sb = String::new();
sb.push('(');
for i in 0..num_args {
let csvt = self.read_csvt()?;
let class_name = if csvt == CsvtObject {
Some(self.get_string_literal()?)
} else {
None
};
let var_name = self.get_string_literal()?;
if let Some(cname) = class_name {
sb.push_str(&format!("{{{}:{}}}", cname, var_name));
} else {
sb.push_str(&var_name);
}
if i < num_args - 1 {
sb.push_str(", ");
}
}
sb.push(')');
self.line(&format!("Enter \"{}\" {}", name, sb))?;
} else {
let flag = self.stream.read_u8()?;
if flag != 0 {
return Err(anyhow::anyhow!(
"Invalid flag for variable argument 'enter' instruction: {} at {:08x}",
flag,
self.addr
));
}
let catch_addr = self.stream.read_u32()? + self.stream.pos as u32;
self.line(&format!("Enter \"{}\" Try-Catch {:08x}", name, catch_addr))?;
}
Ok(())
}
fn command_leave(&mut self) -> Result<()> {
self.line("Leave")?;
Ok(())
}
fn command_jump(&mut self) -> Result<()> {
let target_addr = self.stream.read_u32()? as u64 + self.stream.pos as u64;
self.line(&format!("Jump {:08x}", target_addr))?;
Ok(())
}
fn command_cjump(&mut self) -> Result<()> {
let cond = self.stream.read_u8()?;
let target_addr = self.stream.read_u32()? as u64 + self.stream.pos as u64;
self.line(&format!("CJump {} {:08x}", cond, target_addr))?;
Ok(())
}
fn command_call(&mut self) -> Result<()> {
let csom = self.read_csom()?;
let num_args = self.stream.read_i32()?;
let func_name = self.get_string_literal()?;
let pobj = match csom {
CsomImmediate if func_name == "@CATCH" => "",
CsomThis => "this",
CsomGlobal => "global",
CsomAuto => "auto",
_ => {
return Err(anyhow::anyhow!(
"Invalid CSObjectMode for 'call' instruction: {:?} at {:08x}",
csom,
self.addr
));
}
};
self.line(&format!("Call {} \"{}\" <{}>", pobj, func_name, num_args))?;
Ok(())
}
fn command_return(&mut self) -> Result<()> {
let free_stack = self.stream.read_u8()?;
self.line(&format!("Return {}", free_stack))?;
Ok(())
}
fn command_element(&mut self) -> Result<()> {
let csvt = self.read_csvt()?;
match csvt {
CsvtInteger => {
let index = self.stream.read_i32()?;
self.line(&format!("Element {}", index))?;
}
CsvtString => {
let name = self.get_string_literal()?;
self.line(&format!("Element \"{}\"", name))?;
}
_ => {
return Err(anyhow::anyhow!(
"Invalid CSVariableType for 'element' instruction: {:?} at {:08x}",
csvt,
self.addr
));
}
}
Ok(())
}
fn command_element_indirect(&mut self) -> Result<()> {
self.line("ElementIndirect")?;
Ok(())
}
fn command_operate(&mut self) -> Result<()> {
let csot = self.read_csot()?;
match csot {
CsotNop => {
self.line("Operate Nop")?;
}
CsotAdd => {
self.line("Operate Add")?;
}
CsotSub => {
self.line("Operate Sub")?;
}
CsotMul => {
self.line("Operate Mul")?;
}
CsotDiv => {
self.line("Operate Div")?;
}
CsotMod => {
self.line("Operate Mod")?;
}
CsotAnd => {
self.line("Operate And")?;
}
CsotOr => {
self.line("Operate Or")?;
}
CsotXor => {
self.line("Operate Xor")?;
}
CsotLogicalAnd => {
self.line("Operate LAnd")?;
}
CsotLogicalOr => {
self.line("Operate LOr")?;
}
}
Ok(())
}
fn command_uni_operate(&mut self) -> Result<()> {
let csuot = self.read_csuot()?;
match csuot {
CsuotPlus => {
self.line("UnaryOperate Plus")?;
}
CsuotNegate => {
self.line("UnaryOperate Negate")?;
}
CsuotBitnot => {
self.line("UnaryOperate BitNot")?;
}
CsuotLogicalNot => {
self.line("UnaryOperate LNot")?;
}
}
Ok(())
}
fn command_compare(&mut self) -> Result<()> {
let csct = self.read_csct()?;
match csct {
CsctEqual => {
self.line("Compare Equal")?;
}
CsctNotEqual => {
self.line("Compare NotEqual")?;
}
CsctLessThan => {
self.line("Compare LessThan")?;
}
CsctLessEqual => {
self.line("Compare LessEqual")?;
}
CsctGreaterThan => {
self.line("Compare GreaterThan")?;
}
CsctGreaterEqual => {
self.line("Compare GreaterEqual")?;
}
}
Ok(())
}
}

View File

@@ -0,0 +1,186 @@
use super::disasm::*;
use super::types::*;
use crate::ext::io::*;
use crate::types::*;
use crate::utils::struct_pack::*;
use anyhow::Result;
use std::io::Write;
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct ECSExecutionImage {
file_header: EMCFileHeader,
exi_header: Option<Vec<u8>>,
header: Option<EXIHeader>,
image: MemReader,
pif_prologue: DWordArray,
pif_epilogue: DWordArray,
function_list: FunctionNameList,
csg_global: ECSGlobal,
csg_data: ECSGlobal,
ext_const_str: Option<TaggedRefAddressList>,
ext_global_ref: DWordArray,
ext_data_ref: DWordArray,
imp_global_ref: TaggedRefAddressList,
imp_data_ref: TaggedRefAddressList,
}
impl ECSExecutionImage {
pub fn new(mut reader: MemReader) -> 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 len = reader.data.len();
let mut exi_header = None;
let mut header = None;
let mut image = None;
let mut pif_prologue = None;
let mut pif_epilogue = None;
let mut function_list = None;
let mut csg_global = None;
let mut int64 = false;
let mut csg_data = None;
let mut ext_const_str = None;
let mut ext_global_ref = DWordArray::default();
let mut ext_data_ref = DWordArray::default();
let mut imp_global_ref = TaggedRefAddressList::default();
let mut imp_data_ref = TaggedRefAddressList::default();
while reader.pos < len {
if len - reader.pos < 16 {
break;
}
let id = reader.read_u64()?;
if id == 0 {
break;
}
let size = reader.read_u64()?;
match id {
// header
0x2020726564616568 => {
let buf = reader.read_exact_vec(size as usize)?;
{
let mut sread = MemReaderRef::new(&buf);
header = Some(EXIHeader::unpack(&mut sread, false, Encoding::Utf8)?);
}
exi_header = Some(buf);
if let Some(hdr) = &header {
if hdr.int_base == 64 {
int64 = true;
}
}
}
// image
0x2020206567616D69 => {
image = Some(MemReader::new(reader.read_exact_vec(size as usize)?));
}
// function
0x6E6F6974636E7566 => {
pif_prologue = Some(DWordArray::unpack(&mut reader, false, Encoding::Utf8)?);
pif_epilogue = Some(DWordArray::unpack(&mut reader, false, Encoding::Utf8)?);
function_list = Some(FunctionNameList::unpack(
&mut reader,
false,
Encoding::Utf8,
)?);
}
// global
0x20206C61626F6C67 => {
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 obj = ECSObject::read_from(&mut reader, int64)?;
items.push(ECSObjectItem { name, obj });
}
csg_global = Some(ECSGlobal(items));
}
// data
0x2020202061746164 => {
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 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 obj = ECSObject::read_from(&mut reader, int64)?;
datas.push(ECSObjectItem { name, obj });
}
ECSObject::Global(ECSGlobal(datas))
} else {
ECSObject::read_from(&mut reader, int64)?
};
items.push(ECSObjectItem { name, obj });
}
csg_data = Some(ECSGlobal(items));
}
// conststr
0x72747374736E6F63 => {
ext_const_str = Some(TaggedRefAddressList::unpack(
&mut reader,
false,
Encoding::Utf8,
)?);
}
// linkinf
0x20666E696B6E696C => {
ext_global_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8)?;
ext_data_ref = DWordArray::unpack(&mut reader, false, Encoding::Utf8)?;
imp_global_ref =
TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8)?;
imp_data_ref =
TaggedRefAddressList::unpack(&mut reader, false, Encoding::Utf8)?;
if !ext_global_ref.is_empty()
|| !ext_data_ref.is_empty()
|| !imp_global_ref.is_empty()
|| !imp_data_ref.is_empty()
{
eprintln!(
"Warning: External/global references(linker data) are not supported and will be ignored. This may cause script rebuild errors."
);
crate::COUNTER.inc_warning();
}
}
0 => {
break;
}
_ => {
return Err(anyhow::anyhow!(
"Unknown ECSExecutionImage section ID: 0x{:016X}",
id
));
}
}
}
Ok(Self {
file_header,
exi_header,
header,
image: image.ok_or_else(|| anyhow::anyhow!("Missing image data"))?,
pif_prologue: pif_prologue.ok_or_else(|| anyhow::anyhow!("Missing PIF prologue"))?,
pif_epilogue: pif_epilogue.ok_or_else(|| anyhow::anyhow!("Missing PIF epilogue"))?,
function_list: function_list.ok_or_else(|| anyhow::anyhow!("Missing function list"))?,
csg_global: csg_global.ok_or_else(|| anyhow::anyhow!("Missing CSG global"))?,
csg_data: csg_data.ok_or_else(|| anyhow::anyhow!("Missing CSG data"))?,
ext_const_str,
ext_global_ref,
ext_data_ref,
imp_global_ref,
imp_data_ref,
})
}
pub fn disasm<'a>(&self, writer: Box<dyn Write + 'a>) -> Result<()> {
let mut disasm = ECSExecutionImageDisassembler::new(
self.image.to_ref(),
self.ext_const_str.as_ref(),
Some(writer),
);
disasm.execute()?;
Ok(())
}
}

View File

@@ -0,0 +1,91 @@
//! Ported from CSXTools C# project
//! See parent module documentation for more details.
mod disasm;
mod img;
mod types;
use crate::ext::io::*;
use crate::scripts::base::*;
use crate::types::*;
use anyhow::Result;
use img::ECSExecutionImage;
#[derive(Debug)]
pub struct CSXScriptBuilder {}
impl CSXScriptBuilder {
pub fn new() -> Self {
Self {}
}
}
impl ScriptBuilder for CSXScriptBuilder {
fn default_encoding(&self) -> Encoding {
Encoding::Utf16LE
}
fn build_script(
&self,
buf: Vec<u8>,
_filename: &str,
_encoding: Encoding,
_archive_encoding: Encoding,
config: &ExtraConfig,
_archive: Option<&Box<dyn Script>>,
) -> Result<Box<dyn Script>> {
Ok(Box::new(CSXScript::new(buf, config)?))
}
fn extensions(&self) -> &'static [&'static str] {
&["csx"]
}
fn script_type(&self) -> &'static ScriptType {
&ScriptType::EntisGlsCsx1
}
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
if buf_len >= 8 && &buf[0..8] == b"Entis\x1a\0\0" {
Some(30)
} else {
None
}
}
}
#[derive(Debug)]
pub struct CSXScript {
img: ECSExecutionImage,
}
impl CSXScript {
pub fn new(buf: Vec<u8>, _config: &ExtraConfig) -> Result<Self> {
let reader = MemReader::new(buf);
let img = ECSExecutionImage::new(reader)?;
Ok(Self { img })
}
}
impl Script for CSXScript {
fn default_output_script_type(&self) -> OutputScriptType {
OutputScriptType::Custom
}
fn is_output_supported(&self, output: OutputScriptType) -> bool {
matches!(output, OutputScriptType::Custom)
}
fn default_format_type(&self) -> FormatOptions {
FormatOptions::None
}
fn custom_output_extension<'a>(&'a self) -> &'a str {
"s"
}
fn custom_export(&self, filename: &std::path::Path, _encoding: Encoding) -> Result<()> {
let file = crate::utils::files::write_file(filename)?;
self.img.disasm(Box::new(file))?;
Ok(())
}
}

View File

@@ -0,0 +1,320 @@
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};
use std::ops::{Deref, DerefMut};
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSCompareType {
CsctNotEqual,
CsctEqual,
CsctLessThan,
CsctLessEqual,
CsctGreaterThan,
CsctGreaterEqual,
}
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSInstructionCode {
CsicNew,
CsicFree,
CsicLoad,
CsicStore,
CsicEnter,
CsicLeave,
CsicJump,
CsicCJump,
CsicCall,
CsicReturn,
CsicElement,
CsicElementIndirect,
CsicOperate,
CsicUniOperate,
CsicCompare,
}
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSObjectMode {
CsomImmediate,
CsomStack,
CsomThis,
CsomGlobal,
CsomData,
CsomAuto,
}
#[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,
}
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSUnaryOperatorType {
CsuotPlus,
CsuotNegate,
CsuotBitnot,
CsuotLogicalNot,
}
#[repr(u8)]
#[derive(Debug, IntEnum, PartialEq, Eq, Clone, Copy)]
pub enum CSVariableType {
CsvtObject,
CsvtReference,
CsvtArray,
CsvtHash,
CsvtInteger,
CsvtReal,
CsvtString,
CsvtInteger64,
CsvtPointer,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct EMCFileHeader {
pub signagure: [u8; 8],
pub file_id: u32,
pub _reserved: u32,
pub format_desc: [u8; 48],
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct EXIHeader {
#[skip_unpack_if(reader.stream_length()? < 4)]
pub version: u32,
#[skip_unpack_if(reader.stream_length()? < 8)]
pub int_base: u32,
}
#[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)]
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;
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) -> 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)?;
writer.write_all(&encoded)?;
Ok(())
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct FunctionNameItem {
pub addr: u32,
pub name: WideString,
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct FunctionNameList {
#[pvec(u32)]
pub items: Vec<FunctionNameItem>,
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub enum ECSObject {
Integer(i64),
Real(f64),
String(String),
Array(Vec<ECSObject>),
ClassInfoObject(String),
Hash,
Reference,
Global(ECSGlobal),
}
impl ECSObject {
pub fn read_from<R: Read + Seek>(reader: &mut R, int64: bool) -> Result<Self> {
let typ = reader.read_u32()?;
let obj_typ = CSVariableType::try_from(typ as u8)
.map_err(|typ| anyhow::anyhow!("Invalid CSVariableType: {}", typ))?;
match obj_typ {
CSVariableType::CsvtObject => {
let class_name = WideString::unpack(reader, false, Encoding::Utf8)?.0;
return Ok(ECSObject::ClassInfoObject(class_name));
}
CSVariableType::CsvtReference => {
return Ok(ECSObject::Reference);
}
CSVariableType::CsvtArray => {
let count = reader.read_u32()? as usize;
let mut items = Vec::with_capacity(count);
for _ in 0..count {
let item = ECSObject::read_from(reader, int64)?;
items.push(item);
}
return Ok(ECSObject::Array(items));
}
CSVariableType::CsvtHash => {
return Ok(ECSObject::Hash);
}
CSVariableType::CsvtInteger => {
if int64 {
let val = reader.read_i64()?;
return Ok(ECSObject::Integer(val));
} else {
let val = reader.read_i32()? as i64;
return Ok(ECSObject::Integer(val));
}
}
CSVariableType::CsvtReal => {
let val = reader.read_f64()?;
return Ok(ECSObject::Real(val));
}
CSVariableType::CsvtString => {
let s = WideString::unpack(reader, false, Encoding::Utf8)?.0;
return Ok(ECSObject::String(s));
}
_ => {
return Err(anyhow::anyhow!("Unsupported CSVariableType: {:?}", obj_typ));
}
}
}
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct ECSObjectItem {
pub name: String,
pub obj: ECSObject,
}
#[derive(Clone, Debug)]
pub struct ECSGlobal(pub Vec<ECSObjectItem>);
impl Deref for ECSGlobal {
type Target = Vec<ECSObjectItem>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ECSGlobal {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Clone, Debug, StructPack, StructUnpack)]
pub struct TaggedRefAddress {
pub tag: WideString,
pub refs: DWordArray,
}
#[derive(Clone, Debug, Default, StructPack, StructUnpack)]
pub struct TaggedRefAddressList {
#[pvec(u32)]
pub items: Vec<TaggedRefAddress>,
}
impl Deref for TaggedRefAddressList {
type Target = Vec<TaggedRefAddress>;
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl DerefMut for TaggedRefAddressList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct ECSExecutionImageCommandRecord {
pub code: CSInstructionCode,
pub addr: u32,
pub size: u32,
pub new_addr: u32,
}
#[derive(Clone, Debug)]
pub struct ECSExecutionImageAssembly {
pub command_list: Vec<ECSExecutionImageCommandRecord>,
}
impl Deref for ECSExecutionImageAssembly {
type Target = Vec<ECSExecutionImageCommandRecord>;
fn deref(&self) -> &Self::Target {
&self.command_list
}
}
impl DerefMut for ECSExecutionImageAssembly {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.command_list
}
}
#[test]
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();
assert_eq!(header.version, 1);
assert_eq!(header.int_base, 0);
}

View File

@@ -1,2 +1,3 @@
//! Entis GLS engine Script
pub mod csx;
pub mod srcxml;

View File

@@ -162,6 +162,8 @@ lazy_static::lazy_static! {
Box::new(musica::sc::MusicaBuilder::new()),
#[cfg(feature = "musica-arc")]
Box::new(musica::archive::paz::PazArcBuilder::new()),
#[cfg(feature = "entis-gls")]
Box::new(entis_gls::csx::v1::CSXScriptBuilder::new()),
];
/// A list of all script extensions.
pub static ref ALL_EXTS: Vec<String> =