mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
Add escude script export support
This commit is contained in:
631
src/ext/io.rs
Normal file
631
src/ext/io.rs
Normal file
@@ -0,0 +1,631 @@
|
||||
use std::{ffi::CString, io::*};
|
||||
|
||||
pub trait Peek {
|
||||
fn peek(&mut self, buf: &mut [u8]) -> Result<usize>;
|
||||
fn peek_extract(&mut self, buf: &mut [u8]) -> Result<()>;
|
||||
fn peek_at(&mut self, offset: usize, buf: &mut [u8]) -> Result<usize>;
|
||||
fn peek_extract_at(&mut self, offset: usize, buf: &mut [u8]) -> Result<()>;
|
||||
fn peek_at_vec(&mut self, offset: usize, len: usize) -> Result<Vec<u8>> {
|
||||
let mut buf = vec![0u8; len];
|
||||
let bytes_read = self.peek_at(offset, &mut buf)?;
|
||||
if bytes_read < len {
|
||||
buf.truncate(bytes_read);
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
fn peek_extract_at_vec(&mut self, offset: usize, len: usize) -> Result<Vec<u8>> {
|
||||
let mut buf = vec![0u8; len];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn peek_u8(&mut self) -> Result<u8> {
|
||||
let mut buf = [0u8; 1];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
fn peek_u16(&mut self) -> Result<u16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u16_be(&mut self) -> Result<u16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u16::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_u32(&mut self) -> Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u32_be(&mut self) -> Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u32::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_u64(&mut self) -> Result<u64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u64::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u64_be(&mut self) -> Result<u64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u64::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_u128(&mut self) -> Result<u128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u128::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u128_be(&mut self) -> Result<u128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(u128::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i8(&mut self) -> Result<i8> {
|
||||
let mut buf = [0u8; 1];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i8::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i16(&mut self) -> Result<i16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i16::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i16_be(&mut self) -> Result<i16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i16::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i32(&mut self) -> Result<i32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i32::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i32_be(&mut self) -> Result<i32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i32::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i64(&mut self) -> Result<i64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i64::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i64_be(&mut self) -> Result<i64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i64::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i128(&mut self) -> Result<i128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i128::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i128_be(&mut self) -> Result<i128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract(&mut buf)?;
|
||||
Ok(i128::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_u8_at(&mut self, offset: usize) -> Result<u8> {
|
||||
let mut buf = [0u8; 1];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
fn peek_u16_at(&mut self, offset: usize) -> Result<u16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u16_be_at(&mut self, offset: usize) -> Result<u16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u16::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_u32_at(&mut self, offset: usize) -> Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u32_be_at(&mut self, offset: usize) -> Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u32::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_u64_at(&mut self, offset: usize) -> Result<u64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u64::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u64_be_at(&mut self, offset: usize) -> Result<u64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u64::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_u128_at(&mut self, offset: usize) -> Result<u128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u128::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_u128_be_at(&mut self, offset: usize) -> Result<u128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(u128::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i8_at(&mut self, offset: usize) -> Result<i8> {
|
||||
let mut buf = [0u8; 1];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i8::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i16_at(&mut self, offset: usize) -> Result<i16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i16::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i16_be_at(&mut self, offset: usize) -> Result<i16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i16::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i32_at(&mut self, offset: usize) -> Result<i32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i32::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i32_be_at(&mut self, offset: usize) -> Result<i32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i32::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i64_at(&mut self, offset: usize) -> Result<i64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i64::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i64_be_at(&mut self, offset: usize) -> Result<i64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i64::from_be_bytes(buf))
|
||||
}
|
||||
fn peek_i128_at(&mut self, offset: usize) -> Result<i128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i128::from_le_bytes(buf))
|
||||
}
|
||||
fn peek_i128_be_at(&mut self, offset: usize) -> Result<i128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.peek_extract_at(offset, &mut buf)?;
|
||||
Ok(i128::from_be_bytes(buf))
|
||||
}
|
||||
|
||||
fn peek_cstring(&mut self) -> Result<CString>;
|
||||
fn peek_cstring_at(&mut self, offset: usize) -> Result<CString>;
|
||||
}
|
||||
|
||||
impl<T: Read + Seek> Peek for T {
|
||||
fn peek(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let current_pos = self.stream_position()?;
|
||||
let bytes_read = self.read(buf)?;
|
||||
self.seek(SeekFrom::Start(current_pos))?;
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
fn peek_extract(&mut self, buf: &mut [u8]) -> Result<()> {
|
||||
let current_pos = self.stream_position()?;
|
||||
self.read_exact(buf)?;
|
||||
self.seek(SeekFrom::Start(current_pos))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn peek_at(&mut self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let current_pos = self.stream_position()?;
|
||||
self.seek(SeekFrom::Start(offset as u64))?;
|
||||
let bytes_read = self.read(buf)?;
|
||||
self.seek(SeekFrom::Start(current_pos))?;
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
fn peek_extract_at(&mut self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||
let current_pos = self.stream_position()?;
|
||||
self.seek(SeekFrom::Start(offset as u64))?;
|
||||
self.read_exact(buf)?;
|
||||
self.seek(SeekFrom::Start(current_pos))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn peek_cstring(&mut self) -> Result<CString> {
|
||||
let current_pos = self.stream_position()?;
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
let mut byte = [0u8; 1];
|
||||
self.read_exact(&mut byte)?;
|
||||
if byte[0] == 0 {
|
||||
break;
|
||||
}
|
||||
buf.push(byte[0]);
|
||||
}
|
||||
self.seek(SeekFrom::Start(current_pos))?;
|
||||
CString::new(buf).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
|
||||
fn peek_cstring_at(&mut self, offset: usize) -> Result<CString> {
|
||||
let current_pos = self.stream_position()?;
|
||||
let mut buf = Vec::new();
|
||||
self.seek(SeekFrom::Start(offset as u64))?;
|
||||
loop {
|
||||
let mut byte = [0u8; 1];
|
||||
self.read_exact(&mut byte)?;
|
||||
if byte[0] == 0 {
|
||||
break;
|
||||
}
|
||||
buf.push(byte[0]);
|
||||
}
|
||||
self.seek(SeekFrom::Start(current_pos))?;
|
||||
CString::new(buf).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReadExt {
|
||||
fn read_u8(&mut self) -> Result<u8>;
|
||||
fn read_u16(&mut self) -> Result<u16>;
|
||||
fn read_u16_be(&mut self) -> Result<u16>;
|
||||
fn read_u32(&mut self) -> Result<u32>;
|
||||
fn read_u32_be(&mut self) -> Result<u32>;
|
||||
fn read_u64(&mut self) -> Result<u64>;
|
||||
fn read_u64_be(&mut self) -> Result<u64>;
|
||||
fn read_u128(&mut self) -> Result<u128>;
|
||||
fn read_u128_be(&mut self) -> Result<u128>;
|
||||
fn read_i8(&mut self) -> Result<i8>;
|
||||
fn read_i16(&mut self) -> Result<i16>;
|
||||
fn read_i16_be(&mut self) -> Result<i16>;
|
||||
fn read_i32(&mut self) -> Result<i32>;
|
||||
fn read_i32_be(&mut self) -> Result<i32>;
|
||||
fn read_i64(&mut self) -> Result<i64>;
|
||||
fn read_i64_be(&mut self) -> Result<i64>;
|
||||
fn read_i128(&mut self) -> Result<i128>;
|
||||
fn read_i128_be(&mut self) -> Result<i128>;
|
||||
|
||||
fn read_cstring(&mut self) -> Result<CString>;
|
||||
}
|
||||
|
||||
impl<T: Read> ReadExt for T {
|
||||
fn read_u8(&mut self) -> Result<u8> {
|
||||
let mut buf = [0u8; 1];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
fn read_u16(&mut self) -> Result<u16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
fn read_u16_be(&mut self) -> Result<u16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u16::from_be_bytes(buf))
|
||||
}
|
||||
fn read_u32(&mut self) -> Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
fn read_u32_be(&mut self) -> Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u32::from_be_bytes(buf))
|
||||
}
|
||||
fn read_u64(&mut self) -> Result<u64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u64::from_le_bytes(buf))
|
||||
}
|
||||
fn read_u64_be(&mut self) -> Result<u64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u64::from_be_bytes(buf))
|
||||
}
|
||||
fn read_u128(&mut self) -> Result<u128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u128::from_le_bytes(buf))
|
||||
}
|
||||
fn read_u128_be(&mut self) -> Result<u128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u128::from_be_bytes(buf))
|
||||
}
|
||||
fn read_i8(&mut self) -> Result<i8> {
|
||||
let mut buf = [0u8; 1];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i8::from_le_bytes(buf))
|
||||
}
|
||||
fn read_i16(&mut self) -> Result<i16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i16::from_le_bytes(buf))
|
||||
}
|
||||
fn read_i16_be(&mut self) -> Result<i16> {
|
||||
let mut buf = [0u8; 2];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i16::from_be_bytes(buf))
|
||||
}
|
||||
fn read_i32(&mut self) -> Result<i32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i32::from_le_bytes(buf))
|
||||
}
|
||||
fn read_i32_be(&mut self) -> Result<i32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i32::from_be_bytes(buf))
|
||||
}
|
||||
fn read_i64(&mut self) -> Result<i64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i64::from_le_bytes(buf))
|
||||
}
|
||||
fn read_i64_be(&mut self) -> Result<i64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i64::from_be_bytes(buf))
|
||||
}
|
||||
fn read_i128(&mut self) -> Result<i128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i128::from_le_bytes(buf))
|
||||
}
|
||||
fn read_i128_be(&mut self) -> Result<i128> {
|
||||
let mut buf = [0u8; 16];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i128::from_be_bytes(buf))
|
||||
}
|
||||
|
||||
fn read_cstring(&mut self) -> Result<CString> {
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
let mut byte = [0u8; 1];
|
||||
self.read_exact(&mut byte)?;
|
||||
if byte[0] == 0 {
|
||||
break;
|
||||
}
|
||||
buf.push(byte[0]);
|
||||
}
|
||||
CString::new(buf).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WriteExt {
|
||||
fn write_u8(&mut self, value: u8) -> Result<()>;
|
||||
fn write_u16(&mut self, value: u16) -> Result<()>;
|
||||
fn write_u16_be(&mut self, value: u16) -> Result<()>;
|
||||
fn write_u32(&mut self, value: u32) -> Result<()>;
|
||||
fn write_u32_be(&mut self, value: u32) -> Result<()>;
|
||||
fn write_u64(&mut self, value: u64) -> Result<()>;
|
||||
fn write_u64_be(&mut self, value: u64) -> Result<()>;
|
||||
fn write_u128(&mut self, value: u128) -> Result<()>;
|
||||
fn write_u128_be(&mut self, value: u128) -> Result<()>;
|
||||
fn write_i8(&mut self, value: i8) -> Result<()>;
|
||||
fn write_i16(&mut self, value: i16) -> Result<()>;
|
||||
fn write_i16_be(&mut self, value: i16) -> Result<()>;
|
||||
fn write_i32(&mut self, value: i32) -> Result<()>;
|
||||
fn write_i32_be(&mut self, value: i32) -> Result<()>;
|
||||
fn write_i64(&mut self, value: i64) -> Result<()>;
|
||||
fn write_i64_be(&mut self, value: i64) -> Result<()>;
|
||||
fn write_i128(&mut self, value: i128) -> Result<()>;
|
||||
fn write_i128_be(&mut self, value: i128) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Write> WriteExt for T {
|
||||
fn write_u8(&mut self, value: u8) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_u16(&mut self, value: u16) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_u16_be(&mut self, value: u16) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
fn write_u32(&mut self, value: u32) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_u32_be(&mut self, value: u32) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
fn write_u64(&mut self, value: u64) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_u64_be(&mut self, value: u64) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
fn write_u128(&mut self, value: u128) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_u128_be(&mut self, value: u128) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
fn write_i8(&mut self, value: i8) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_i16(&mut self, value: i16) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_i16_be(&mut self, value: i16) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
fn write_i32(&mut self, value: i32) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_i32_be(&mut self, value: i32) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
fn write_i64(&mut self, value: i64) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_i64_be(&mut self, value: i64) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
fn write_i128(&mut self, value: i128) -> Result<()> {
|
||||
self.write_all(&value.to_le_bytes())
|
||||
}
|
||||
fn write_i128_be(&mut self, value: i128) -> Result<()> {
|
||||
self.write_all(&value.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemReader {
|
||||
data: Vec<u8>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
pub struct MemReaderRef<'a> {
|
||||
data: &'a [u8],
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for MemReader {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MemReader")
|
||||
.field("pos", &self.pos)
|
||||
.field("data_length", &self.data.len())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl MemReader {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
MemReader { data, pos: 0 }
|
||||
}
|
||||
|
||||
pub fn to_ref(&self) -> MemReaderRef {
|
||||
MemReaderRef {
|
||||
data: &self.data,
|
||||
pos: self.pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MemReaderRef<'a> {
|
||||
pub fn new(data: &'a [u8]) -> Self {
|
||||
MemReaderRef { data, pos: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for MemReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
if self.pos >= self.data.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
let bytes_to_read = buf.len().min(self.data.len() - self.pos);
|
||||
let mut bu = &self.data[self.pos..self.pos + bytes_to_read];
|
||||
bu.read(buf)?;
|
||||
self.pos += bytes_to_read;
|
||||
Ok(bytes_to_read)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for MemReader {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
|
||||
match pos {
|
||||
SeekFrom::Start(offset) => {
|
||||
if offset > self.data.len() as u64 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Seek position is beyond the end of the data",
|
||||
));
|
||||
}
|
||||
self.pos = offset as usize;
|
||||
}
|
||||
SeekFrom::End(offset) => {
|
||||
let end_pos = self.data.len() as i64 + offset;
|
||||
if end_pos < 0 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Seek from end resulted in negative position",
|
||||
));
|
||||
}
|
||||
self.pos = end_pos as usize;
|
||||
}
|
||||
SeekFrom::Current(offset) => {
|
||||
let new_pos = (self.pos as i64 + offset) as usize;
|
||||
if new_pos > self.data.len() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Seek position is beyond the end of the data",
|
||||
));
|
||||
}
|
||||
self.pos = new_pos;
|
||||
}
|
||||
}
|
||||
Ok(self.pos as u64)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> Result<u64> {
|
||||
Ok(self.pos as u64)
|
||||
}
|
||||
|
||||
fn rewind(&mut self) -> Result<()> {
|
||||
self.pos = 0;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Read for MemReaderRef<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
if self.pos >= self.data.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
let bytes_to_read = buf.len().min(self.data.len() - self.pos);
|
||||
let mut bu = &self.data[self.pos..self.pos + bytes_to_read];
|
||||
bu.read(buf)?;
|
||||
self.pos += bytes_to_read;
|
||||
Ok(bytes_to_read)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Seek for MemReaderRef<'a> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
|
||||
match pos {
|
||||
SeekFrom::Start(offset) => {
|
||||
if offset > self.data.len() as u64 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Seek position is beyond the end of the data",
|
||||
));
|
||||
}
|
||||
self.pos = offset as usize;
|
||||
}
|
||||
SeekFrom::End(offset) => {
|
||||
let end_pos = self.data.len() as i64 + offset;
|
||||
if end_pos < 0 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Seek from end resulted in negative position",
|
||||
));
|
||||
}
|
||||
self.pos = end_pos as usize;
|
||||
}
|
||||
SeekFrom::Current(offset) => {
|
||||
let new_pos = (self.pos as i64 + offset) as usize;
|
||||
if new_pos > self.data.len() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Seek position is beyond the end of the data",
|
||||
));
|
||||
}
|
||||
self.pos = new_pos;
|
||||
}
|
||||
}
|
||||
Ok(self.pos as u64)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> Result<u64> {
|
||||
Ok(self.pos as u64)
|
||||
}
|
||||
|
||||
fn rewind(&mut self) -> Result<()> {
|
||||
self.pos = 0;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1
src/ext/mod.rs
Normal file
1
src/ext/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod io;
|
||||
217
src/main.rs
217
src/main.rs
@@ -1,10 +1,13 @@
|
||||
pub mod args;
|
||||
pub mod ext;
|
||||
pub mod format;
|
||||
pub mod output_scripts;
|
||||
pub mod scripts;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
|
||||
use scripts::base::ArchiveContent;
|
||||
|
||||
fn get_encoding(
|
||||
arg: &args::Arg,
|
||||
builder: &Box<dyn scripts::ScriptBuilder + Send + Sync>,
|
||||
@@ -93,7 +96,8 @@ pub fn parse_script(
|
||||
for builder in scripts::BUILDER.iter() {
|
||||
if typ == builder.script_type() {
|
||||
let encoding = get_encoding(arg, builder);
|
||||
return Ok((builder.build_script(filename, encoding, config)?, builder));
|
||||
let data = utils::files::read_file(filename)?;
|
||||
return Ok((builder.build_script(data, encoding, config)?, builder));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,6 +109,7 @@ pub fn parse_script(
|
||||
for ext in exts {
|
||||
if filename.to_lowercase().ends_with(ext) {
|
||||
exts_builder.push(builder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +121,8 @@ pub fn parse_script(
|
||||
if exts_builder.len() == 1 {
|
||||
let builder = exts_builder.first().unwrap();
|
||||
let encoding = get_encoding(arg, builder);
|
||||
return Ok((builder.build_script(filename, encoding, config)?, builder));
|
||||
let data = utils::files::read_file(filename)?;
|
||||
return Ok((builder.build_script(data, encoding, config)?, builder));
|
||||
}
|
||||
let mut buf = [0u8; 1024];
|
||||
let mut size = 0;
|
||||
@@ -143,7 +149,8 @@ pub fn parse_script(
|
||||
if best_builders.len() == 1 {
|
||||
let builder = best_builders.first().unwrap();
|
||||
let encoding = get_encoding(arg, builder);
|
||||
return Ok((builder.build_script(filename, encoding, config)?, builder));
|
||||
let data = utils::files::read_file(filename)?;
|
||||
return Ok((builder.build_script(data, encoding, config)?, builder));
|
||||
}
|
||||
if best_builders.len() > 1 {
|
||||
eprintln!(
|
||||
@@ -155,6 +162,72 @@ pub fn parse_script(
|
||||
Err(anyhow::anyhow!("Unsupported script type"))
|
||||
}
|
||||
|
||||
pub fn parse_script_from_archive(
|
||||
file: &Box<dyn ArchiveContent>,
|
||||
arg: &args::Arg,
|
||||
config: &types::ExtraConfig,
|
||||
) -> anyhow::Result<(
|
||||
Box<dyn scripts::Script>,
|
||||
&'static Box<dyn scripts::ScriptBuilder + Send + Sync>,
|
||||
)> {
|
||||
let mut exts_builder = Vec::new();
|
||||
for builder in scripts::BUILDER.iter() {
|
||||
let exts = builder.extensions();
|
||||
for ext in exts {
|
||||
if file.name().to_lowercase().ends_with(ext) {
|
||||
exts_builder.push(builder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let exts_builder = if exts_builder.is_empty() {
|
||||
scripts::BUILDER.iter().collect::<Vec<_>>()
|
||||
} else {
|
||||
exts_builder
|
||||
};
|
||||
if exts_builder.len() == 1 {
|
||||
let builder = exts_builder.first().unwrap();
|
||||
let encoding = get_encoding(arg, builder);
|
||||
return Ok((
|
||||
builder.build_script(file.data().to_vec(), encoding, config)?,
|
||||
builder,
|
||||
));
|
||||
}
|
||||
let mut scores = Vec::new();
|
||||
for builder in exts_builder.iter() {
|
||||
if let Some(score) = builder.is_this_format(file.name(), file.data(), file.data().len()) {
|
||||
scores.push((score, builder));
|
||||
}
|
||||
}
|
||||
if scores.is_empty() {
|
||||
return Err(anyhow::anyhow!("Unsupported script type"));
|
||||
}
|
||||
let max_score = scores.iter().map(|s| s.0).max().unwrap();
|
||||
let mut best_builders = Vec::new();
|
||||
for (score, builder) in scores.iter() {
|
||||
if *score == max_score {
|
||||
best_builders.push(builder);
|
||||
}
|
||||
}
|
||||
if best_builders.len() == 1 {
|
||||
let builder = best_builders.first().unwrap();
|
||||
let encoding = get_encoding(arg, builder);
|
||||
return Ok((
|
||||
builder.build_script(file.data().to_vec(), encoding, config)?,
|
||||
builder,
|
||||
));
|
||||
}
|
||||
if best_builders.len() > 1 {
|
||||
eprintln!(
|
||||
"Multiple script types found for {}: {:?}",
|
||||
file.name(),
|
||||
best_builders
|
||||
);
|
||||
return Err(anyhow::anyhow!("Multiple script types found"));
|
||||
}
|
||||
Err(anyhow::anyhow!("Unsupported script type"))
|
||||
}
|
||||
|
||||
pub fn export_script(
|
||||
filename: &str,
|
||||
arg: &args::Arg,
|
||||
@@ -164,6 +237,144 @@ pub fn export_script(
|
||||
) -> anyhow::Result<types::ScriptResult> {
|
||||
eprintln!("Exporting {}", filename);
|
||||
let script = parse_script(filename, arg, config)?.0;
|
||||
if script.is_archive() {
|
||||
let odir = match output.as_ref() {
|
||||
Some(output) => {
|
||||
if is_dir {
|
||||
let mut pb = std::path::PathBuf::from(output);
|
||||
let filename = std::path::PathBuf::from(filename);
|
||||
if let Some(fname) = filename.file_name() {
|
||||
pb.push(fname);
|
||||
}
|
||||
pb.to_string_lossy().into_owned()
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"A directory is required for archive export"
|
||||
));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let mut pb = std::path::PathBuf::from(filename);
|
||||
pb.set_extension("");
|
||||
pb.to_string_lossy().into_owned()
|
||||
}
|
||||
};
|
||||
if !std::fs::exists(&odir)? {
|
||||
std::fs::create_dir_all(&odir)?;
|
||||
}
|
||||
for f in script.iter_archive()? {
|
||||
let f = f?;
|
||||
if f.is_script() {
|
||||
let (script_file, _) = parse_script_from_archive(&f, arg, config)?;
|
||||
let mes = match script_file.extract_messages() {
|
||||
Ok(mes) => mes,
|
||||
Err(e) => {
|
||||
eprintln!("Error extracting messages from {}: {}", f.name(), e);
|
||||
COUNTER.inc_error();
|
||||
if arg.backtrace {
|
||||
eprintln!("Backtrace: {}", e.backtrace());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if mes.is_empty() {
|
||||
eprintln!("No messages found in {}", f.name());
|
||||
COUNTER.inc(types::ScriptResult::Ignored);
|
||||
continue;
|
||||
}
|
||||
let of = match &arg.output_type {
|
||||
Some(t) => t.clone(),
|
||||
None => script_file.default_output_script_type(),
|
||||
};
|
||||
let mut out_path = std::path::PathBuf::from(&odir).join(f.name());
|
||||
out_path.set_extension(of.as_ref());
|
||||
match of {
|
||||
types::OutputScriptType::Json => {
|
||||
let enc = get_output_encoding(arg);
|
||||
let s = match serde_json::to_string_pretty(&mes) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("Error serializing messages to JSON: {}", e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let b = match utils::encoding::encode_string(enc, &s, false) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
eprintln!("Error encoding string: {}", e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut f = match utils::files::write_file(&out_path) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
eprintln!("Error writing file {}: {}", out_path.display(), e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
match f.write_all(&b) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
eprintln!("Error writing to file {}: {}", out_path.display(), e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
types::OutputScriptType::M3t => {
|
||||
let enc = get_output_encoding(arg);
|
||||
let s = output_scripts::m3t::M3tDumper::dump(&mes);
|
||||
let b = match utils::encoding::encode_string(enc, &s, false) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
eprintln!("Error encoding string: {}", e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut f = match utils::files::write_file(&out_path) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
eprintln!("Error writing file {}: {}", out_path.display(), e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
match f.write_all(&b) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
eprintln!("Error writing to file {}: {}", out_path.display(), e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let out_path = std::path::PathBuf::from(&odir).join(f.name());
|
||||
match utils::files::write_file(&out_path) {
|
||||
Ok(mut fi) => match fi.write_all(f.data()) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
eprintln!("Error writing to file {}: {}", out_path.display(), e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Error writing file {}: {}", out_path.display(), e);
|
||||
COUNTER.inc_error();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
COUNTER.inc(types::ScriptResult::Ok);
|
||||
}
|
||||
return Ok(types::ScriptResult::Ok);
|
||||
}
|
||||
// println!("{:?}", script);
|
||||
let mes = script.extract_messages()?;
|
||||
// for m in mes.iter() {
|
||||
|
||||
@@ -10,7 +10,7 @@ pub trait ScriptBuilder: std::fmt::Debug {
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
filename: &str,
|
||||
buf: Vec<u8>,
|
||||
encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<Box<dyn Script>>;
|
||||
@@ -24,18 +24,48 @@ pub trait ScriptBuilder: std::fmt::Debug {
|
||||
fn script_type(&self) -> &'static ScriptType;
|
||||
}
|
||||
|
||||
pub trait ArchiveContent {
|
||||
fn name(&self) -> &str;
|
||||
fn data(&self) -> &[u8];
|
||||
fn is_script(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait Script: std::fmt::Debug {
|
||||
fn default_output_script_type(&self) -> OutputScriptType;
|
||||
|
||||
fn default_format_type(&self) -> FormatOptions;
|
||||
|
||||
fn extract_messages(&self) -> Result<Vec<Message>>;
|
||||
fn extract_messages(&self) -> Result<Vec<Message>> {
|
||||
if !self.is_archive() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"This script type does not support extracting messages."
|
||||
));
|
||||
}
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn import_messages(
|
||||
&self,
|
||||
messages: Vec<Message>,
|
||||
filename: &str,
|
||||
encoding: Encoding,
|
||||
replacement: Option<&ReplacementTable>,
|
||||
) -> Result<()>;
|
||||
_messages: Vec<Message>,
|
||||
_filename: &str,
|
||||
_encoding: Encoding,
|
||||
_replacement: Option<&ReplacementTable>,
|
||||
) -> Result<()> {
|
||||
if !self.is_archive() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"This script type does not support importing messages."
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_archive(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn iter_archive<'a>(
|
||||
&'a self,
|
||||
) -> Result<Box<dyn Iterator<Item = Result<Box<dyn ArchiveContent>>> + 'a>> {
|
||||
Ok(Box::new(std::iter::empty()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,11 @@ impl ScriptBuilder for BGIScriptBuilder {
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
filename: &str,
|
||||
buf: Vec<u8>,
|
||||
encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
Ok(Box::new(BGIScript::new(
|
||||
filename.as_ref(),
|
||||
encoding,
|
||||
config,
|
||||
)?))
|
||||
Ok(Box::new(BGIScript::new(buf, encoding, config)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
@@ -64,8 +60,7 @@ impl std::fmt::Debug for BGIScript {
|
||||
}
|
||||
|
||||
impl BGIScript {
|
||||
pub fn new(filename: &str, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
|
||||
let data = crate::utils::files::read_file(filename)?;
|
||||
pub fn new(data: Vec<u8>, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
|
||||
if data.starts_with(b"BurikoCompiledScriptVer1.00\0") {
|
||||
let mut parser = V1Parser::new(&data, encoding)?;
|
||||
parser.disassemble()?;
|
||||
|
||||
@@ -20,15 +20,11 @@ impl ScriptBuilder for CircusMesScriptBuilder {
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
filename: &str,
|
||||
buf: Vec<u8>,
|
||||
encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
Ok(Box::new(CircusMesScript::new(
|
||||
filename.as_ref(),
|
||||
encoding,
|
||||
config,
|
||||
)?))
|
||||
Ok(Box::new(CircusMesScript::new(buf, encoding, config)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
@@ -59,8 +55,7 @@ pub struct CircusMesScript {
|
||||
}
|
||||
|
||||
impl CircusMesScript {
|
||||
pub fn new(filename: &str, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
|
||||
let data = crate::utils::files::read_file(filename)?;
|
||||
pub fn new(data: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
|
||||
let head0 = i32::from_le_bytes(data[0..4].try_into()?);
|
||||
let head1 = i32::from_le_bytes(data[4..8].try_into()?);
|
||||
let mut is_new_ver = false;
|
||||
|
||||
188
src/scripts/escude/archive.rs
Normal file
188
src/scripts/escude/archive.rs
Normal file
@@ -0,0 +1,188 @@
|
||||
use super::crypto::*;
|
||||
use crate::ext::io::*;
|
||||
use crate::scripts::base::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::decode_to_string;
|
||||
use anyhow::Result;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EscudeBinArchiveBuilder {}
|
||||
|
||||
impl EscudeBinArchiveBuilder {
|
||||
pub const fn new() -> Self {
|
||||
EscudeBinArchiveBuilder {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptBuilder for EscudeBinArchiveBuilder {
|
||||
fn default_encoding(&self) -> Encoding {
|
||||
Encoding::Cp932
|
||||
}
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
data: Vec<u8>,
|
||||
encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
Ok(Box::new(EscudeBinArchive::new(data, encoding, config)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
&["bin"]
|
||||
}
|
||||
|
||||
fn script_type(&self) -> &'static ScriptType {
|
||||
&ScriptType::EscudeArc
|
||||
}
|
||||
|
||||
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
||||
if buf_len > 8 && buf.starts_with(b"ESC-ARC2") {
|
||||
return Some(255);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BinEntry {
|
||||
name_offset: u32,
|
||||
data_offset: u32,
|
||||
length: u32,
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
name: String,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ArchiveContent for Entry {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn data(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn is_script(&self) -> bool {
|
||||
self.data.starts_with(b"ESCR1_00")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EscudeBinArchive {
|
||||
reader: MemReader,
|
||||
file_count: u32,
|
||||
name_tbl_len: u32,
|
||||
entries: Vec<BinEntry>,
|
||||
encoding: Encoding,
|
||||
}
|
||||
|
||||
impl EscudeBinArchive {
|
||||
pub fn new(data: Vec<u8>, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
|
||||
let mut reader = MemReader::new(data);
|
||||
let mut header = [0u8; 8];
|
||||
reader.read_exact(&mut header)?;
|
||||
if &header != b"ESC-ARC2" {
|
||||
return Err(anyhow::anyhow!("Invalid Escude binary script header"));
|
||||
}
|
||||
reader.seek(SeekFrom::Start(0xC))?;
|
||||
let mut crypto_reader = CryptoReader::new(&mut reader)?;
|
||||
let file_count = crypto_reader.read_u32()?;
|
||||
let name_tbl_len = crypto_reader.read_u32()?;
|
||||
let mut entries = Vec::with_capacity(file_count as usize);
|
||||
for _ in 0..file_count {
|
||||
let name_offset = crypto_reader.read_u32()?;
|
||||
let data_offset = crypto_reader.read_u32()?;
|
||||
let length = crypto_reader.read_u32()?;
|
||||
entries.push(BinEntry {
|
||||
name_offset,
|
||||
data_offset,
|
||||
length,
|
||||
});
|
||||
}
|
||||
Ok(EscudeBinArchive {
|
||||
reader,
|
||||
file_count,
|
||||
name_tbl_len,
|
||||
entries,
|
||||
encoding,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Script for EscudeBinArchive {
|
||||
fn default_output_script_type(&self) -> OutputScriptType {
|
||||
OutputScriptType::Json
|
||||
}
|
||||
|
||||
fn default_format_type(&self) -> FormatOptions {
|
||||
FormatOptions::None
|
||||
}
|
||||
|
||||
fn is_archive(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn iter_archive<'a>(
|
||||
&'a self,
|
||||
) -> Result<Box<dyn Iterator<Item = Result<Box<dyn ArchiveContent>>> + 'a>> {
|
||||
let reader = self.reader.to_ref();
|
||||
let encoding = self.encoding;
|
||||
Ok(Box::new(EscudeBinArchiveIterator {
|
||||
entries: self.entries.iter(),
|
||||
reader,
|
||||
encoding,
|
||||
file_count: self.file_count,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
struct EscudeBinArchiveIterator<'a, T: Iterator<Item = &'a BinEntry>> {
|
||||
entries: T,
|
||||
reader: MemReaderRef<'a>,
|
||||
encoding: Encoding,
|
||||
file_count: u32,
|
||||
}
|
||||
|
||||
impl<'a, T: Iterator<Item = &'a BinEntry>> Iterator for EscudeBinArchiveIterator<'a, T> {
|
||||
type Item = Result<Box<dyn ArchiveContent>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let entry = match self.entries.next() {
|
||||
Some(entry) => entry,
|
||||
None => return None,
|
||||
};
|
||||
let name = match self
|
||||
.reader
|
||||
.peek_cstring_at(entry.name_offset as usize + self.file_count as usize * 12 + 0x14)
|
||||
{
|
||||
Ok(name) => name,
|
||||
Err(e) => return Some(Err(e.into())),
|
||||
};
|
||||
let name = match decode_to_string(self.encoding, name.as_bytes()) {
|
||||
Ok(name) => name,
|
||||
Err(e) => return Some(Err(e.into())),
|
||||
};
|
||||
let mut data = match self
|
||||
.reader
|
||||
.peek_at_vec(entry.data_offset as usize, entry.length as usize)
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => return Some(Err(e.into())),
|
||||
};
|
||||
if data.starts_with(b"acp") {
|
||||
let mut decoder = match super::lzw::LZWDecoder::new(&data) {
|
||||
Ok(decoder) => decoder,
|
||||
Err(e) => return Some(Err(anyhow::anyhow!("Failed to create LZW decoder: {}", e))),
|
||||
};
|
||||
data = match decoder.unpack() {
|
||||
Ok(unpacked_data) => unpacked_data,
|
||||
Err(e) => return Some(Err(anyhow::anyhow!("Failed to unpack LZW data: {}", e))),
|
||||
};
|
||||
}
|
||||
Some(Ok(Box::new(Entry { name, data })))
|
||||
}
|
||||
}
|
||||
54
src/scripts/escude/crypto.rs
Normal file
54
src/scripts/escude/crypto.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use crate::ext::io::*;
|
||||
use anyhow::Result;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub struct CryptoReader<T: Read + Seek> {
|
||||
reader: T,
|
||||
_key: u32,
|
||||
max_pos: u32,
|
||||
}
|
||||
|
||||
impl<T: Read + Seek> CryptoReader<T> {
|
||||
pub fn new(mut reader: T) -> Result<Self> {
|
||||
let _key = reader.peek_u32_at(0x8)?;
|
||||
let mut s = CryptoReader {
|
||||
reader,
|
||||
_key,
|
||||
max_pos: 0,
|
||||
};
|
||||
s.init()?;
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn key(&mut self) -> u32 {
|
||||
self._key ^= 0x65AC9365;
|
||||
self._key ^= (((self._key >> 1) ^ self._key) >> 3) ^ (((self._key << 1) ^ self._key) << 3);
|
||||
return self._key;
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<()> {
|
||||
let _key = self._key;
|
||||
self.max_pos = (self.reader.peek_u32_at(0xC)? ^ self.key()) * 12 + 0xC;
|
||||
self._key = _key;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read + Seek> Read for CryptoReader<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let remaing = self.max_pos as usize + 0x8 - self.reader.stream_position()? as usize;
|
||||
let count = buf.len().min(remaing);
|
||||
let readed = self.reader.read(&mut buf[..count])?;
|
||||
for i in 0..readed / 4 {
|
||||
let val = u32::from_le_bytes(buf[i * 4..i * 4 + 4].try_into().map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Failed to convert slice to u32",
|
||||
)
|
||||
})?);
|
||||
let decrypted = val ^ self.key();
|
||||
buf[i * 4..i * 4 + 4].copy_from_slice(&decrypted.to_le_bytes());
|
||||
}
|
||||
Ok(readed)
|
||||
}
|
||||
}
|
||||
98
src/scripts/escude/lzw.rs
Normal file
98
src/scripts/escude/lzw.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use crate::ext::io::*;
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct BitStream<'a> {
|
||||
m_input: MemReaderRef<'a>,
|
||||
m_bits: u32,
|
||||
m_cached_bits: u32,
|
||||
}
|
||||
|
||||
impl<'a> BitStream<'a> {
|
||||
pub fn new(input: MemReaderRef<'a>) -> Self {
|
||||
BitStream {
|
||||
m_input: input,
|
||||
m_bits: 0,
|
||||
m_cached_bits: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_bits(&mut self, count: u32) -> Result<u32> {
|
||||
while self.m_cached_bits < count {
|
||||
let byte = self.m_input.read_u8()?;
|
||||
self.m_bits = (self.m_bits << 8) | byte as u32;
|
||||
self.m_cached_bits += 8;
|
||||
}
|
||||
let mask = (1 << count) - 1;
|
||||
self.m_cached_bits -= count;
|
||||
let result = (self.m_bits >> self.m_cached_bits) & mask;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LZWDecoder<'a> {
|
||||
m_input: BitStream<'a>,
|
||||
m_output_size: u32,
|
||||
}
|
||||
|
||||
impl<'a> LZWDecoder<'a> {
|
||||
pub fn new(input: &'a [u8]) -> Result<Self> {
|
||||
let mut input_reader = MemReaderRef::new(input);
|
||||
let size = input_reader.peek_u32_be_at(0x4)?;
|
||||
let m_input = BitStream::new(MemReaderRef::new(&input[0x8..]));
|
||||
Ok(LZWDecoder {
|
||||
m_input,
|
||||
m_output_size: size,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn unpack(&mut self) -> Result<Vec<u8>> {
|
||||
let size = self.m_output_size as usize;
|
||||
let mut output = Vec::with_capacity(size);
|
||||
output.resize(size, 0);
|
||||
let mut dict = Vec::with_capacity(0x8900);
|
||||
dict.resize(0x8900, 0u32);
|
||||
let mut token_width = 9;
|
||||
let mut dict_pos = 0;
|
||||
let mut dst = 0;
|
||||
while dst < size {
|
||||
let mut token = self.m_input.get_bits(token_width)?;
|
||||
if token == 0x100 {
|
||||
// End of stream
|
||||
break;
|
||||
} else if token == 0x101 {
|
||||
token_width += 1;
|
||||
if token_width > 24 {
|
||||
return Err(anyhow::anyhow!("Token width exceeded maximum of 12 bits"));
|
||||
}
|
||||
} else if token == 0x102 {
|
||||
token_width = 9;
|
||||
dict_pos = 0;
|
||||
} else {
|
||||
if dict_pos > 0x8900 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Dictionary position exceeded maximum of 0x8900"
|
||||
));
|
||||
}
|
||||
dict[dict_pos] = dst as u32;
|
||||
dict_pos += 1;
|
||||
if token < 0x100 {
|
||||
output[dst] = token as u8;
|
||||
dst += 1;
|
||||
} else {
|
||||
token -= 0x103;
|
||||
if token >= dict_pos as u32 {
|
||||
return Err(anyhow::anyhow!("Token out of bounds: {}", token));
|
||||
}
|
||||
let src = dict[token as usize];
|
||||
let count =
|
||||
(self.m_output_size - dst as u32).min(dict[token as usize + 1] - src + 1);
|
||||
for i in 0..count {
|
||||
output[dst + i as usize] = output[src as usize + i as usize];
|
||||
}
|
||||
dst += count as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
@@ -1 +1,4 @@
|
||||
|
||||
pub mod archive;
|
||||
mod crypto;
|
||||
mod lzw;
|
||||
pub mod script;
|
||||
|
||||
210
src/scripts/escude/script.rs
Normal file
210
src/scripts/escude/script.rs
Normal file
@@ -0,0 +1,210 @@
|
||||
use crate::ext::io::*;
|
||||
use crate::scripts::base::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::{decode_to_string, encode_string};
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EscudeBinScriptBuilder {}
|
||||
|
||||
impl EscudeBinScriptBuilder {
|
||||
pub const fn new() -> Self {
|
||||
EscudeBinScriptBuilder {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptBuilder for EscudeBinScriptBuilder {
|
||||
fn default_encoding(&self) -> Encoding {
|
||||
Encoding::Cp932
|
||||
}
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
data: Vec<u8>,
|
||||
encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
Ok(Box::new(EscudeBinScript::new(data, encoding, config)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
&["bin"]
|
||||
}
|
||||
|
||||
fn script_type(&self) -> &'static ScriptType {
|
||||
&ScriptType::Escude
|
||||
}
|
||||
|
||||
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
||||
if buf_len > 8 && buf.starts_with(b"ESCR1_00") {
|
||||
return Some(255);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EscudeBinScript {
|
||||
offsets: Vec<u32>,
|
||||
vms: Vec<u8>,
|
||||
unk1: u32,
|
||||
strings: Vec<String>,
|
||||
}
|
||||
|
||||
impl EscudeBinScript {
|
||||
pub fn new(data: Vec<u8>, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
|
||||
let mut reader = MemReader::new(data);
|
||||
let mut magic = [0u8; 8];
|
||||
reader.read_exact(&mut magic)?;
|
||||
if &magic != b"ESCR1_00" {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Invalid Escude binary script magic: {:?}",
|
||||
magic
|
||||
));
|
||||
}
|
||||
let string_count = reader.read_u32()?;
|
||||
let mut offsets = Vec::with_capacity(string_count as usize);
|
||||
for _ in 0..string_count {
|
||||
offsets.push(reader.read_u32()?);
|
||||
}
|
||||
let vm_count = reader.read_u32()?;
|
||||
let mut vms = Vec::with_capacity(vm_count as usize);
|
||||
vms.resize(vm_count as usize, 0);
|
||||
reader.read_exact(&mut vms)?;
|
||||
let unk1 = reader.read_u32()?;
|
||||
let mut strings = Vec::with_capacity(string_count as usize);
|
||||
if encoding.is_jis() {
|
||||
let replaces = StrReplacer::new()?;
|
||||
for _ in 0..string_count {
|
||||
let s = reader.read_cstring()?;
|
||||
let s = replaces.replace(s.as_bytes())?;
|
||||
strings.push(decode_to_string(encoding, &s)?);
|
||||
}
|
||||
} else {
|
||||
for _ in 0..string_count {
|
||||
let s = reader.read_cstring()?;
|
||||
strings.push(decode_to_string(encoding, s.as_bytes())?);
|
||||
}
|
||||
}
|
||||
Ok(EscudeBinScript {
|
||||
offsets,
|
||||
vms,
|
||||
unk1,
|
||||
strings,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Script for EscudeBinScript {
|
||||
fn default_output_script_type(&self) -> OutputScriptType {
|
||||
OutputScriptType::Json
|
||||
}
|
||||
|
||||
fn default_format_type(&self) -> FormatOptions {
|
||||
FormatOptions::None
|
||||
}
|
||||
|
||||
fn extract_messages(&self) -> Result<Vec<Message>> {
|
||||
Ok(self
|
||||
.strings
|
||||
.iter()
|
||||
.map(|s| Message {
|
||||
message: s.to_string(),
|
||||
name: None,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn import_messages(
|
||||
&self,
|
||||
_messages: Vec<Message>,
|
||||
_filename: &str,
|
||||
_encoding: Encoding,
|
||||
_replacement: Option<&ReplacementTable>,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_archive(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct StrReplacer {
|
||||
pub replacements: HashMap<Vec<u8>, Vec<u8>>,
|
||||
}
|
||||
|
||||
enum JisStr {
|
||||
Single(u8),
|
||||
Double(u8, u8),
|
||||
}
|
||||
|
||||
impl StrReplacer {
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut s = StrReplacer {
|
||||
replacements: HashMap::new(),
|
||||
};
|
||||
s.add("!?。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚", "!? 。「」、…をぁぃぅぇぉゃゅょっーあいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわん゛゜")?;
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn add(&mut self, from: &str, to: &str) -> Result<()> {
|
||||
let encoding = Encoding::Cp932; // Default encoding, can be changed as needed
|
||||
let froms = UnicodeSegmentation::graphemes(from, true);
|
||||
let tos = UnicodeSegmentation::graphemes(to, true);
|
||||
for (from, to) in froms.zip(tos) {
|
||||
let from_bytes = if from == "" {
|
||||
vec![0xa0]
|
||||
} else {
|
||||
encode_string(encoding, from, true)?
|
||||
};
|
||||
let to_bytes = encode_string(encoding, to, true)?;
|
||||
self.replacements.insert(from_bytes, to_bytes);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn replace(&self, input: &[u8]) -> Result<Vec<u8>> {
|
||||
let mut result = Vec::new();
|
||||
let mut reader = MemReaderRef::new(input);
|
||||
while let Ok(byte) = reader.read_u8() {
|
||||
if byte < 0x80 || (byte >= 0xa0 && byte <= 0xdf) {
|
||||
result.push(JisStr::Single(byte));
|
||||
} else if (byte >= 0x81 && byte <= 0x9f) || (byte >= 0xe0 && byte <= 0xef) {
|
||||
let next_byte = reader.read_u8()?;
|
||||
if next_byte < 0x40 || next_byte > 0xfc {
|
||||
return Err(anyhow::anyhow!("Invalid JIS encoding sequence"));
|
||||
}
|
||||
result.push(JisStr::Double(byte, next_byte));
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Invalid byte in JIS encoding: {}", byte));
|
||||
}
|
||||
}
|
||||
let mut output = Vec::new();
|
||||
for item in result {
|
||||
match item {
|
||||
JisStr::Single(byte) => {
|
||||
let vec = vec![byte];
|
||||
if let Some(replacement) = self.replacements.get(&vec) {
|
||||
output.extend_from_slice(replacement);
|
||||
} else {
|
||||
output.push(byte);
|
||||
}
|
||||
}
|
||||
JisStr::Double(byte1, byte2) => {
|
||||
let key = vec![byte1, byte2];
|
||||
if let Some(replacement) = self.replacements.get(&key) {
|
||||
output.extend_from_slice(replacement);
|
||||
} else {
|
||||
output.push(byte1);
|
||||
output.push(byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ lazy_static::lazy_static! {
|
||||
pub static ref BUILDER: Vec<Box<dyn ScriptBuilder + Sync + Send>> = vec![
|
||||
Box::new(circus::script::CircusMesScriptBuilder::new()),
|
||||
Box::new(bgi::script::BGIScriptBuilder::new()),
|
||||
Box::new(escude::archive::EscudeBinArchiveBuilder::new()),
|
||||
Box::new(escude::script::EscudeBinScriptBuilder::new()),
|
||||
];
|
||||
pub static ref ALL_EXTS: Vec<String> =
|
||||
BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect();
|
||||
|
||||
@@ -45,8 +45,10 @@ pub enum TextEncoding {
|
||||
Auto,
|
||||
/// UTF-8 encoding
|
||||
Utf8,
|
||||
#[value(alias("jis"))]
|
||||
/// Shift-JIS encoding
|
||||
Cp932,
|
||||
#[value(alias("gbk"))]
|
||||
/// GB2312 encoding
|
||||
Gb2312,
|
||||
}
|
||||
@@ -185,6 +187,10 @@ pub enum ScriptType {
|
||||
#[value(alias("ethornell"))]
|
||||
/// Buriko General Interpreter/Ethornell Script
|
||||
BGI,
|
||||
/// Escude bin archive
|
||||
EscudeArc,
|
||||
/// Escude bin script
|
||||
Escude,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -48,6 +48,9 @@ impl std::fmt::Display for WinError {
|
||||
}
|
||||
|
||||
pub fn decode_to_string(cp: u32, data: &[u8]) -> Result<String, WinError> {
|
||||
if data.is_empty() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
let needed_len = unsafe {
|
||||
MultiByteToWideChar(
|
||||
cp,
|
||||
|
||||
Reference in New Issue
Block a user