mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
Add a new parser for WillPlus(AdvHD) ws2 script
This commit is contained in:
193
src/ext/io.rs
193
src/ext/io.rs
@@ -749,6 +749,14 @@ pub trait ReadExt {
|
||||
fn read_i128(&mut self) -> Result<i128>;
|
||||
/// Reads an [i128] from the reader in big-endian order.
|
||||
fn read_i128_be(&mut self) -> Result<i128>;
|
||||
/// Reads a [f32] from the reader in little-endian order.
|
||||
fn read_f32(&mut self) -> Result<f32>;
|
||||
/// Reads a [f32] from the reader in big-endian order.
|
||||
fn read_f32_be(&mut self) -> Result<f32>;
|
||||
/// Reads a [f64] from the reader in little-endian order.
|
||||
fn read_f64(&mut self) -> Result<f64>;
|
||||
/// Reads a [f64] from the reader in big-endian order.
|
||||
fn read_f64_be(&mut self) -> Result<f64>;
|
||||
|
||||
/// Reads a C-style string (null-terminated) from the reader.
|
||||
fn read_cstring(&mut self) -> Result<CString>;
|
||||
@@ -856,6 +864,26 @@ impl<T: Read> ReadExt for T {
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(i128::from_be_bytes(buf))
|
||||
}
|
||||
fn read_f32(&mut self) -> Result<f32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(f32::from_le_bytes(buf))
|
||||
}
|
||||
fn read_f32_be(&mut self) -> Result<f32> {
|
||||
let mut buf = [0u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(f32::from_be_bytes(buf))
|
||||
}
|
||||
fn read_f64(&mut self) -> Result<f64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(f64::from_le_bytes(buf))
|
||||
}
|
||||
fn read_f64_be(&mut self) -> Result<f64> {
|
||||
let mut buf = [0u8; 8];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(f64::from_be_bytes(buf))
|
||||
}
|
||||
|
||||
fn read_cstring(&mut self) -> Result<CString> {
|
||||
let mut buf = Vec::new();
|
||||
@@ -1558,3 +1586,168 @@ impl<T: Seek> Seek for StreamRegion<T> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct RangeMap {
|
||||
original: (u64, u64),
|
||||
new: (u64, u64),
|
||||
}
|
||||
|
||||
/// A binary patcher that can be used to apply patches to binary data.
|
||||
pub struct BinaryPatcher<
|
||||
R: Read + Seek,
|
||||
W: Write + Seek,
|
||||
A: Fn(u64) -> Result<u64>,
|
||||
O: Fn(u64) -> Result<u64>,
|
||||
> {
|
||||
pub input: R,
|
||||
pub output: W,
|
||||
input_len: u64,
|
||||
address_to_offset: A,
|
||||
offset_to_address: O,
|
||||
range_maps: Vec<RangeMap>,
|
||||
}
|
||||
|
||||
impl<R: Read + Seek, W: Write + Seek, A: Fn(u64) -> Result<u64>, O: Fn(u64) -> Result<u64>>
|
||||
BinaryPatcher<R, W, A, O>
|
||||
{
|
||||
/// Creates a new `BinaryPatcher` with the specified input and output streams.
|
||||
pub fn new(
|
||||
mut input: R,
|
||||
output: W,
|
||||
address_to_offset: A,
|
||||
offset_to_address: O,
|
||||
) -> Result<Self> {
|
||||
let input_len = input.stream_length()?;
|
||||
Ok(BinaryPatcher {
|
||||
input,
|
||||
output,
|
||||
input_len,
|
||||
address_to_offset,
|
||||
offset_to_address,
|
||||
range_maps: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Copies data from the input stream to the output stream up to the specified address of original stream.
|
||||
pub fn copy_up_to(&mut self, original_offset: u64) -> Result<()> {
|
||||
let cur_pos = self.input.stream_position()?;
|
||||
if original_offset < cur_pos || original_offset > self.input_len {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Original offset is out of bounds",
|
||||
));
|
||||
}
|
||||
let bytes_to_copy = original_offset - cur_pos;
|
||||
std::io::copy(&mut (&mut self.input).take(bytes_to_copy), &mut self.output)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Maps an original offset to a new offset in the output stream.
|
||||
pub fn map_offset(&mut self, original_offset: u64) -> Result<u64> {
|
||||
if original_offset > self.input_len {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Original offset is out of bounds",
|
||||
));
|
||||
}
|
||||
let cur_pos = self.input.stream_position()?;
|
||||
if original_offset > cur_pos {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Original offset is beyond current position",
|
||||
));
|
||||
}
|
||||
let mut start = 0;
|
||||
let mut end = self.range_maps.len();
|
||||
while start < end {
|
||||
let pivot = (start + end) / 2;
|
||||
let range = &self.range_maps[pivot];
|
||||
if original_offset < range.original.0 {
|
||||
end = pivot;
|
||||
} else if original_offset == range.original.0 {
|
||||
return Ok(range.new.0);
|
||||
} else if original_offset >= range.original.1 {
|
||||
start = pivot + 1;
|
||||
} else {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Can't map an offset inside a changed section",
|
||||
));
|
||||
}
|
||||
}
|
||||
if start == 0 {
|
||||
return Ok(original_offset);
|
||||
}
|
||||
let index = start - 1;
|
||||
let range = &self.range_maps[index];
|
||||
let new_offset = original_offset + range.new.1 - range.original.1;
|
||||
let out_len = self.output.stream_length()?;
|
||||
if new_offset > out_len {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Mapped offset is beyond the end of the output stream",
|
||||
));
|
||||
}
|
||||
Ok(new_offset)
|
||||
}
|
||||
|
||||
/// Replaces bytes in the output stream with new data, starting from the current position in the input stream.
|
||||
///
|
||||
/// * `original_length` - The length of the original data to be replaced.
|
||||
/// * `new_data` - The new data to write to the output stream.
|
||||
pub fn replace_bytes(&mut self, original_length: u64, new_data: &[u8]) -> Result<()> {
|
||||
self.replace_bytes_with_write(original_length, |writer| writer.write_all(new_data))
|
||||
}
|
||||
|
||||
/// Replaces bytes in the output stream with new data, starting from the current position in the input stream.
|
||||
///
|
||||
/// * `original_length` - The length of the original data to be replaced.
|
||||
/// * `write` - A function that writes the new data to the output stream.
|
||||
pub fn replace_bytes_with_write<F: Fn(&mut W) -> Result<()>>(
|
||||
&mut self,
|
||||
original_length: u64,
|
||||
write: F,
|
||||
) -> Result<()> {
|
||||
let cur_pos = self.input.stream_position()?;
|
||||
if cur_pos + original_length > self.input_len {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Original length exceeds input length",
|
||||
));
|
||||
}
|
||||
let new_data_offset = self.output.stream_position()?;
|
||||
write(&mut self.output)?;
|
||||
let new_data_length = self.output.stream_position()? - new_data_offset;
|
||||
if new_data_length != original_length {
|
||||
self.range_maps.push(RangeMap {
|
||||
original: (cur_pos, cur_pos + original_length),
|
||||
new: (new_data_offset, new_data_offset + new_data_length),
|
||||
});
|
||||
}
|
||||
self.input
|
||||
.seek(SeekFrom::Start(cur_pos + original_length))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Patches a u32 address in the output stream at the specified original offset.
|
||||
pub fn patch_u32_address(&mut self, original_offset: u64) -> Result<()> {
|
||||
let input_pos = self.input.stream_position()?;
|
||||
if input_pos < original_offset + 4 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Original offset is out of bounds for u32 address patching",
|
||||
));
|
||||
}
|
||||
self.input.seek(SeekFrom::Start(original_offset))?;
|
||||
let original_address = self.input.read_u32()?;
|
||||
self.input.seek(SeekFrom::Start(input_pos))?;
|
||||
let new_offset = self.map_offset(original_offset)?;
|
||||
let offset = (self.address_to_offset)(original_address as u64)?;
|
||||
let offset = self.map_offset(offset)?;
|
||||
let new_addr = (self.offset_to_address)(offset)?;
|
||||
self.output.seek(SeekFrom::Start(new_offset))?;
|
||||
self.output.write_u32(new_addr as u32)?;
|
||||
self.output.seek(SeekFrom::End(0))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
//! WillPlus Scripts
|
||||
pub mod ws2;
|
||||
mod ws2_disasm;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! WillPlus Script File (.ws2)
|
||||
use super::ws2_disasm::*;
|
||||
use crate::ext::io::*;
|
||||
use crate::scripts::base::*;
|
||||
use crate::types::*;
|
||||
@@ -32,6 +33,16 @@ impl ScriptBuilder for Ws2ScriptBuilder {
|
||||
config: &ExtraConfig,
|
||||
_archive: Option<&Box<dyn Script>>,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
match Ws2DisasmScript::new(&buf, encoding, config, false) {
|
||||
Ok(script) => return Ok(Box::new(script)),
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"WARNING: Failed to disassemble WS2 script: {}. An another parser is used.",
|
||||
e
|
||||
);
|
||||
crate::COUNTER.inc_warning();
|
||||
}
|
||||
}
|
||||
Ok(Box::new(Ws2Script::new(buf, encoding, config, false)?))
|
||||
}
|
||||
|
||||
@@ -317,3 +328,169 @@ impl Script for Ws2Script {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// WillPlus Disassembled Script
|
||||
pub struct Ws2DisasmScript {
|
||||
data: MemReader,
|
||||
texts: Vec<Ws2DString>,
|
||||
addresses: Vec<usize>,
|
||||
/// Need encrypt when outputting
|
||||
encrypted: bool,
|
||||
}
|
||||
|
||||
impl Ws2DisasmScript {
|
||||
/// Creates a new `Ws2DisasmScript`
|
||||
///
|
||||
/// * `buf` - The buffer containing the script data
|
||||
/// * `encoding` - The encoding used for the script
|
||||
/// * `config` - Extra configuration options
|
||||
/// * `decrypted` - Whether the script is decrypted or not
|
||||
pub fn new(
|
||||
buf: &[u8],
|
||||
encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
decrypted: bool,
|
||||
) -> Result<Self> {
|
||||
match disassmble(&buf, encoding) {
|
||||
Ok((addresses, texts)) => {
|
||||
return Ok(Self {
|
||||
data: MemReader::new(buf.to_vec()),
|
||||
texts,
|
||||
addresses,
|
||||
encrypted: decrypted,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
if decrypted {
|
||||
return Err(e);
|
||||
} else {
|
||||
let mut data = buf.to_vec();
|
||||
Ws2Script::decrypt(&mut data);
|
||||
return Self::new(&data, encoding, config, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Script for Ws2DisasmScript {
|
||||
fn default_output_script_type(&self) -> OutputScriptType {
|
||||
OutputScriptType::Json
|
||||
}
|
||||
|
||||
fn default_format_type(&self) -> FormatOptions {
|
||||
FormatOptions::None
|
||||
}
|
||||
|
||||
fn extract_messages(&self) -> Result<Vec<Message>> {
|
||||
let mut messages = Vec::new();
|
||||
let mut name = None;
|
||||
for text in &self.texts {
|
||||
match text.typ {
|
||||
StringType::Name => {
|
||||
let text = text
|
||||
.text
|
||||
.trim_start_matches("%LC")
|
||||
.trim_start_matches("%LF")
|
||||
.to_string();
|
||||
name = Some(text);
|
||||
}
|
||||
StringType::Message => {
|
||||
let message = text.text.trim_end_matches("%K%P").to_string();
|
||||
messages.push(Message {
|
||||
message,
|
||||
name: name.take(),
|
||||
});
|
||||
}
|
||||
StringType::Internal => {}
|
||||
}
|
||||
}
|
||||
Ok(messages)
|
||||
}
|
||||
|
||||
fn import_messages<'a>(
|
||||
&'a self,
|
||||
messages: Vec<Message>,
|
||||
file: Box<dyn WriteSeek + 'a>,
|
||||
encoding: Encoding,
|
||||
replacement: Option<&'a ReplacementTable>,
|
||||
) -> Result<()> {
|
||||
let mut output = if self.encrypted {
|
||||
Box::new(EncryptWriter::new(file))
|
||||
} else {
|
||||
file
|
||||
};
|
||||
let mut mes = messages.iter();
|
||||
let mut mess = mes.next();
|
||||
{
|
||||
let mut patcher = BinaryPatcher::new(
|
||||
MemReaderRef::new(&self.data.data),
|
||||
&mut output,
|
||||
|s| Ok(s),
|
||||
|s| Ok(s),
|
||||
)?;
|
||||
for s in &self.texts {
|
||||
let text = match s.typ {
|
||||
StringType::Name => {
|
||||
let prefix = if s.text.starts_with("%LC") {
|
||||
"%LC"
|
||||
} else if s.text.starts_with("%LF") {
|
||||
"%LF"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let m = match mess {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
return Err(anyhow::anyhow!("No enough messages."));
|
||||
}
|
||||
};
|
||||
let mut name = match m.name.as_ref() {
|
||||
Some(name) => name.to_owned(),
|
||||
None => return Err(anyhow::anyhow!("Message without name.")),
|
||||
};
|
||||
if let Some(replacement) = replacement {
|
||||
for (k, v) in &replacement.map {
|
||||
name = name.replace(k, v);
|
||||
}
|
||||
}
|
||||
name = prefix.to_owned() + &name;
|
||||
name
|
||||
}
|
||||
StringType::Message => {
|
||||
let suffix = if s.text.ends_with("%K%P") { "%K%P" } else { "" };
|
||||
let m = match mess {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
return Err(anyhow::anyhow!("No enough messages."));
|
||||
}
|
||||
};
|
||||
let mut message = m.message.clone();
|
||||
if let Some(replacement) = replacement {
|
||||
for (k, v) in &replacement.map {
|
||||
message = message.replace(k, v);
|
||||
}
|
||||
}
|
||||
mess = mes.next();
|
||||
message + suffix
|
||||
}
|
||||
StringType::Internal => s.text.clone(),
|
||||
};
|
||||
let mut encoded = encode_string(encoding, &text, false)?;
|
||||
encoded.push(0); // Null terminator
|
||||
patcher.copy_up_to(s.offset as u64)?;
|
||||
patcher.replace_bytes(s.len as u64, &encoded)?;
|
||||
}
|
||||
if mess.is_some() || mes.next().is_some() {
|
||||
return Err(anyhow::anyhow!("Too many messages provided."));
|
||||
}
|
||||
patcher.copy_up_to(self.data.data.len() as u64)?;
|
||||
for offset in &self.addresses {
|
||||
patcher.patch_u32_address(*offset as u64)?;
|
||||
}
|
||||
}
|
||||
output.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
657
src/scripts/will_plus/ws2_disasm.rs
Normal file
657
src/scripts/will_plus/ws2_disasm.rs
Normal file
@@ -0,0 +1,657 @@
|
||||
use crate::ext::io::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::*;
|
||||
use anyhow::Result;
|
||||
use std::any::Any;
|
||||
|
||||
pub trait Disasm: Sized {
|
||||
fn disassmble(self) -> Result<(Vec<usize>, Vec<Ws2DString>)>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Oper {
|
||||
/// Byte
|
||||
B,
|
||||
/// Word
|
||||
H,
|
||||
/// Int
|
||||
I,
|
||||
/// Address
|
||||
A,
|
||||
/// Float
|
||||
F,
|
||||
/// String
|
||||
S,
|
||||
/// Array of operands (*)
|
||||
ARR,
|
||||
}
|
||||
use Oper::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StringType {
|
||||
Name,
|
||||
Message,
|
||||
Internal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Ws2DString {
|
||||
pub text: String,
|
||||
pub offset: usize,
|
||||
pub len: usize,
|
||||
pub typ: StringType,
|
||||
}
|
||||
|
||||
struct DisasmBase<'a> {
|
||||
reader: MemReaderRef<'a>,
|
||||
opers: &'a [(u8, &'static [Oper])],
|
||||
addresses: Vec<usize>,
|
||||
texts: Vec<Ws2DString>,
|
||||
encoding: Encoding,
|
||||
}
|
||||
|
||||
impl<'a> DisasmBase<'a> {
|
||||
pub fn new(data: &'a [u8], opers: &'a [(u8, &'static [Oper])], encoding: Encoding) -> Self {
|
||||
DisasmBase {
|
||||
reader: MemReaderRef::new(data),
|
||||
opers,
|
||||
addresses: Vec::new(),
|
||||
texts: Vec::new(),
|
||||
encoding,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_instruction(&mut self) -> Result<(u8, Vec<Box<dyn Any>>)> {
|
||||
let opcode = self.reader.read_u8()?;
|
||||
let opers = self
|
||||
.opers
|
||||
.iter()
|
||||
.find(|&&(op, _)| op == opcode)
|
||||
.ok_or_else(|| anyhow::anyhow!("Unknown opcode: {opcode}"))?;
|
||||
let operands = self.read_operands(opers.1)?;
|
||||
Ok((opcode, operands))
|
||||
}
|
||||
|
||||
fn read_operands(&mut self, opers: &[Oper]) -> Result<Vec<Box<dyn Any>>> {
|
||||
let mut operands = Vec::new();
|
||||
let mut i = 0;
|
||||
let oper_len = opers.len();
|
||||
while i < oper_len {
|
||||
let oper = opers[i];
|
||||
if i < oper_len - 1 && opers[i + 1] == ARR {
|
||||
i += 1;
|
||||
let count = self.reader.read_u8()?;
|
||||
for _ in 0..count {
|
||||
operands.push(self.read_operand(oper)?);
|
||||
}
|
||||
} else {
|
||||
let operand = self.read_operand(oper)?;
|
||||
operands.push(operand);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
Ok(operands)
|
||||
}
|
||||
|
||||
fn read_operand(&mut self, oper: Oper) -> Result<Box<dyn Any>> {
|
||||
match oper {
|
||||
B => {
|
||||
let value = self.reader.read_u8()?;
|
||||
Ok(Box::new(value))
|
||||
}
|
||||
H => {
|
||||
let value = self.reader.read_i16()?;
|
||||
Ok(Box::new(value))
|
||||
}
|
||||
I => {
|
||||
let value = self.reader.read_i32()?;
|
||||
Ok(Box::new(value))
|
||||
}
|
||||
A => {
|
||||
let pos = self.reader.pos;
|
||||
let address = self.reader.read_i32()?;
|
||||
self.addresses.push(pos);
|
||||
Ok(Box::new(address))
|
||||
}
|
||||
F => {
|
||||
let value = self.reader.read_f32()?;
|
||||
Ok(Box::new(value))
|
||||
}
|
||||
S => {
|
||||
let offset = self.reader.pos;
|
||||
let s = self.reader.read_cstring()?;
|
||||
let decoded = decode_to_string(self.encoding, s.as_bytes(), false)?;
|
||||
let len = s.as_bytes_with_nul().len();
|
||||
let str = Ws2DString {
|
||||
text: decoded,
|
||||
offset,
|
||||
len,
|
||||
typ: StringType::Internal,
|
||||
};
|
||||
Ok(Box::new(str))
|
||||
}
|
||||
_ => {
|
||||
// Handle other operand types as needed
|
||||
Err(anyhow::anyhow!("Unsupported operand type: {:?}", oper))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_choice_screen(&mut self, operands: &mut Vec<Box<dyn Any>>) -> Result<()> {
|
||||
if operands.len() < 1 {
|
||||
return Err(anyhow::anyhow!("Invalid operands for choice screen"));
|
||||
}
|
||||
let first = operands.remove(0);
|
||||
let num_choices = first
|
||||
.downcast::<u8>()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid choice count"))?;
|
||||
for _ in 0..*num_choices {
|
||||
let mut opers = self.read_operands(&[H, S, B, H])?;
|
||||
let range = opers.remove(1);
|
||||
let mut range = range
|
||||
.downcast::<Ws2DString>()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid range operand"))?;
|
||||
if range.len > 1 {
|
||||
range.typ = StringType::Message;
|
||||
self.texts.push(*range);
|
||||
}
|
||||
self.read_instruction()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_message(&mut self, operands: &mut Vec<Box<dyn Any>>) -> Result<()> {
|
||||
if operands.len() < 3 {
|
||||
return Err(anyhow::anyhow!("Invalid operands for message"));
|
||||
}
|
||||
let range = operands.remove(2);
|
||||
let mut range = range
|
||||
.downcast::<Ws2DString>()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid range operand"))?;
|
||||
if range.len > 1 {
|
||||
range.typ = StringType::Message;
|
||||
self.texts.push(*range);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_name(&mut self, operands: &mut Vec<Box<dyn Any>>) -> Result<()> {
|
||||
if operands.len() < 1 {
|
||||
return Err(anyhow::anyhow!("Invalid operands for name"));
|
||||
}
|
||||
let name = operands.remove(0);
|
||||
let mut name = name
|
||||
.downcast::<Ws2DString>()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid name operand"))?;
|
||||
if name.len > 1 {
|
||||
name.typ = StringType::Name;
|
||||
self.texts.push(*name);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Disasm for DisasmBase<'a> {
|
||||
fn disassmble(mut self) -> Result<(Vec<usize>, Vec<Ws2DString>)> {
|
||||
let maxlen = self.reader.data.len() - 8;
|
||||
while self.reader.pos < maxlen {
|
||||
let (opcode, mut operands) = self.read_instruction()?;
|
||||
match opcode {
|
||||
0x0F => self.handle_choice_screen(&mut operands)?,
|
||||
0x14 => self.handle_message(&mut operands)?,
|
||||
0x15 => self.handle_name(&mut operands)?,
|
||||
_ => {}
|
||||
}
|
||||
for oper in operands {
|
||||
if let Ok(str) = oper.downcast::<Ws2DString>() {
|
||||
if str.len > 1 {
|
||||
self.texts.push(*str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.texts.sort_by_key(|s| s.offset);
|
||||
Ok((self.addresses, self.texts))
|
||||
}
|
||||
}
|
||||
|
||||
const V1_OPS: [(u8, &'static [Oper]); 103] = [
|
||||
(0x00, &[]),
|
||||
(0x01, &[B, H, F, A, A]),
|
||||
(0x02, &[A]),
|
||||
(0x04, &[S]),
|
||||
(0x05, &[]),
|
||||
(0x06, &[A]),
|
||||
(0x07, &[S]),
|
||||
(0x08, &[B]),
|
||||
(0x09, &[B, H, F]),
|
||||
(0x0A, &[H, F]),
|
||||
(0x0B, &[H, B]),
|
||||
(0x0C, &[H, B, H, ARR]),
|
||||
(0x0D, &[H, H, F]),
|
||||
(0x0E, &[H, H, B]),
|
||||
(0x0F, &[B]),
|
||||
(0x11, &[S, F]),
|
||||
(0x12, &[S, B, S]),
|
||||
(0x13, &[]),
|
||||
(0x14, &[I, S, S]),
|
||||
(0x15, &[S]),
|
||||
(0x16, &[B]),
|
||||
(0x17, &[]),
|
||||
(0x18, &[B, S]),
|
||||
(0x19, &[]),
|
||||
(0x1A, &[S]),
|
||||
(0x1B, &[B]),
|
||||
(0x1C, &[S, S, H]),
|
||||
(0x1D, &[H]),
|
||||
(0x1E, &[S, S, F, F, H, H, B]),
|
||||
(0x1F, &[S, F]),
|
||||
(0x20, &[S, F, H]),
|
||||
(0x21, &[S, H, H, H]),
|
||||
(0x22, &[S, B]),
|
||||
(0x28, &[S, S, F, F, H, H, B, H, H, B]),
|
||||
(0x29, &[S, F]),
|
||||
(0x2A, &[S, F, H]),
|
||||
(0x2B, &[S]),
|
||||
(0x2C, &[S]),
|
||||
(0x2D, &[S, B]),
|
||||
(0x2E, &[]),
|
||||
(0x2F, &[S, H, F]),
|
||||
(0x32, &[S]),
|
||||
(0x33, &[S, S, B, B]),
|
||||
(0x34, &[S, S, B, B]),
|
||||
(0x35, &[S, S, B, B, B]),
|
||||
(0x36, &[S, F, F, F, F, F, F, F, B, B]),
|
||||
(0x37, &[S]),
|
||||
(0x38, &[S, B]),
|
||||
(0x39, &[S, B, B, H, ARR]),
|
||||
(0x3A, &[S, B, B]),
|
||||
(0x3B, &[S, S, H, H, H, F, F, F, F, F, F, F, F]),
|
||||
(0x3C, &[S]),
|
||||
(0x3D, &[H]),
|
||||
(0x3E, &[]),
|
||||
(0x3F, &[S, ARR]),
|
||||
(0x40, &[S, S, B]),
|
||||
(0x41, &[S, B]),
|
||||
(0x42, &[S, H]),
|
||||
(0x43, &[S]),
|
||||
(0x44, &[S, S, B]),
|
||||
(0x45, &[S, H, F, F, F, F]),
|
||||
(0x46, &[S, H, B, F, F, F, F]),
|
||||
(0x47, &[S, S, H, B, B, F, F, F, F, F, H, F]),
|
||||
(0x48, &[S, S, H, B, B, S]),
|
||||
(0x49, &[S, S, S]),
|
||||
(0x4A, &[S, S]),
|
||||
(0x4B, &[S, H, H, F, F, F, F]),
|
||||
(0x4C, &[S, H, H, B, F, F, F, F]),
|
||||
(0x4D, &[S, S, H, H, B, B, F, F, F, F, F, H, F]),
|
||||
(0x4E, &[S, S, H, H, B, B, S]),
|
||||
(0x4F, &[S, S, H, S]),
|
||||
(0x50, &[S, S, H]),
|
||||
(0x51, &[S, S, H, F, B]),
|
||||
(0x52, &[S, S, F, H, F, B, S]),
|
||||
(0x53, &[S, S]),
|
||||
(0x54, &[S, S, S]),
|
||||
(0x55, &[S, S]),
|
||||
(
|
||||
0x56,
|
||||
&[
|
||||
S, B, H, F, F, F, F, F, F, F, F, F, F, F, B, F, F, F, F, B, H, S, H, S, S, F,
|
||||
],
|
||||
),
|
||||
(0x57, &[S, H]),
|
||||
(0x58, &[S, S]),
|
||||
(0x59, &[S, S, H]),
|
||||
(0x5A, &[S, H, ARR]),
|
||||
(0x5B, &[S, H, B]),
|
||||
(0x5C, &[S]),
|
||||
(0x5D, &[S, S, B]),
|
||||
(0x5E, &[S, F, F]),
|
||||
(0x64, &[B]),
|
||||
(0x65, &[H, B, F, F, B, S]),
|
||||
(0x66, &[S]),
|
||||
(0x67, &[B, B, H, F, F, F, F, F, B]),
|
||||
(0x68, &[B]),
|
||||
(0x6E, &[S, S]),
|
||||
(0x6F, &[S]),
|
||||
(0x70, &[S, H]),
|
||||
(0x71, &[]),
|
||||
(0x72, &[S, H, H, S]),
|
||||
(0x73, &[S, S, H]),
|
||||
(0xFA, &[]),
|
||||
(0xFB, &[B]),
|
||||
(0xFC, &[H]),
|
||||
(0xFD, &[]),
|
||||
(0xFE, &[S]),
|
||||
(0xFF, &[]),
|
||||
];
|
||||
|
||||
const V2_OPS: [(u8, &'static [Oper]); 134] = [
|
||||
(0x00, &[]),
|
||||
(0x01, &[B, H, F, A, A]),
|
||||
(0x02, &[A]),
|
||||
(0x04, &[S]),
|
||||
(0x05, &[]),
|
||||
(0x06, &[A]),
|
||||
(0x07, &[S]),
|
||||
(0x08, &[B]),
|
||||
(0x09, &[B, H, F]),
|
||||
(0x0A, &[H, F]),
|
||||
(0x0B, &[H, B]),
|
||||
(0x0C, &[H, B, H, ARR]),
|
||||
(0x0D, &[H, H, F]),
|
||||
(0x0E, &[H, H, B]),
|
||||
(0x0F, &[B]),
|
||||
(0x11, &[S, F]),
|
||||
(0x12, &[S, B, S]),
|
||||
(0x13, &[]),
|
||||
(0x14, &[I, S, S]),
|
||||
(0x15, &[S]),
|
||||
(0x16, &[B]),
|
||||
(0x17, &[]),
|
||||
(0x18, &[B, S]),
|
||||
(0x19, &[]),
|
||||
(0x1A, &[S]),
|
||||
(0x1B, &[B]),
|
||||
(0x1C, &[S, S, H, B]),
|
||||
(0x1D, &[H]),
|
||||
(0x1E, &[S, S, F, F, H, H, B]),
|
||||
(0x1F, &[S, F]),
|
||||
(0x20, &[S, F, H]),
|
||||
(0x21, &[S, H, H, H]),
|
||||
(0x22, &[S, B]),
|
||||
(0x28, &[S, S, F, F, H, H, B, H, H, B]),
|
||||
(0x29, &[S, F]),
|
||||
(0x2A, &[S, F, H]),
|
||||
(0x2B, &[S]),
|
||||
(0x2C, &[S]),
|
||||
(0x2D, &[S, B]),
|
||||
(0x2E, &[]),
|
||||
(0x2F, &[S, H, F]),
|
||||
(0x32, &[S]),
|
||||
(0x33, &[S, S, B, B]),
|
||||
(0x34, &[S, S, B, B]),
|
||||
(0x35, &[S, S, B, B, B]),
|
||||
(0x36, &[S, F, F, F, F, F, F, F, B, B]),
|
||||
(0x37, &[S]),
|
||||
(0x38, &[S, B]),
|
||||
(0x39, &[S, B, B, H, ARR]),
|
||||
(0x3A, &[S, B, B]),
|
||||
(0x3B, &[S, S, H, H, H, F, F, F, F, F, F, F, F]),
|
||||
(0x3C, &[S]),
|
||||
(0x3D, &[H]),
|
||||
(0x3E, &[]),
|
||||
(0x3F, &[S, ARR]),
|
||||
(0x40, &[S, S, B]),
|
||||
(0x41, &[S, B]),
|
||||
(0x42, &[S, H]),
|
||||
(0x43, &[S]),
|
||||
(0x44, &[S, S, B]),
|
||||
(0x45, &[S, H, F, F, F, F]),
|
||||
(0x46, &[S, H, B, F, F, F, F]),
|
||||
(0x47, &[S, S, H, B, B, F, F, F, F, F, H, F]),
|
||||
(0x48, &[S, S, H, B, B, S]),
|
||||
(0x49, &[S, S, S]),
|
||||
(0x4A, &[S, S]),
|
||||
(0x4B, &[S, H, H, F, F, F, F]),
|
||||
(0x4C, &[S, H, H, B, F, F, F, F]),
|
||||
(0x4D, &[S, S, H, H, B, B, F, F, F, F, F, H, F]),
|
||||
(0x4E, &[S, S, H, H, B, B, S]),
|
||||
(0x4F, &[S, S, H, S]),
|
||||
(0x50, &[S, S, H]),
|
||||
(0x51, &[S, S, H, F, B]),
|
||||
(0x52, &[S, S, F, H, F, B, S]),
|
||||
(0x53, &[S, S]),
|
||||
(0x54, &[S, S, S]),
|
||||
(0x55, &[S, S]),
|
||||
(
|
||||
0x56,
|
||||
&[
|
||||
S, B, H, F, F, F, F, F, F, F, F, F, F, F, B, F, F, F, F, B, H, S, H, S, S, F,
|
||||
],
|
||||
),
|
||||
(0x57, &[S, H]),
|
||||
(0x58, &[S, S]),
|
||||
(0x59, &[S, S, H]),
|
||||
(0x5A, &[S, H, ARR]),
|
||||
(0x5B, &[S, H, B]),
|
||||
(0x5C, &[S]),
|
||||
(0x5D, &[S, S, B]),
|
||||
(0x5E, &[S, F, F]),
|
||||
(0x5F, &[S]),
|
||||
(0x60, &[H, H, H, H]),
|
||||
(0x61, &[B, F, F, F, F]),
|
||||
(0x62, &[S]),
|
||||
(0x63, &[S, B]),
|
||||
(0x64, &[B]),
|
||||
(0x65, &[H, B, F, F, B, S]),
|
||||
(0x66, &[S]),
|
||||
(0x67, &[B, B, H, F, F, F, F, F, B]),
|
||||
(0x68, &[B]),
|
||||
(0x69, &[S, B, B, F, F, F, F, F, H, F]),
|
||||
(0x6A, &[S, H, B, B, S]),
|
||||
(0x6E, &[S, S]),
|
||||
(0x6F, &[S]),
|
||||
(0x70, &[S, H]),
|
||||
(0x71, &[]),
|
||||
(0x72, &[S, H, H, S]),
|
||||
(0x73, &[S, S, H]),
|
||||
(0x74, &[S, S]),
|
||||
(0x75, &[S, S]),
|
||||
(0x78, &[S, S, B, B]),
|
||||
(0x79, &[S, S, F]),
|
||||
(0x7A, &[S, S, F, B, B, S]),
|
||||
(0x7B, &[S, S]),
|
||||
(0x7C, &[S, S, F]),
|
||||
(0x7D, &[S, F]),
|
||||
(0x7E, &[S]),
|
||||
(0xC8, &[]),
|
||||
(0xC9, &[S, S, H, H, H]),
|
||||
(0xCA, &[S, S]),
|
||||
(0xCB, &[S, B, B]),
|
||||
(0xCC, &[]),
|
||||
(0xCD, &[S, S, S, S, S, F, B]),
|
||||
(0xCE, &[B]),
|
||||
(0xCF, &[S, S, F]),
|
||||
(0xD0, &[S, H]),
|
||||
(0xD1, &[S, H]),
|
||||
(0xD2, &[S]),
|
||||
(0xD3, &[S]),
|
||||
(0xD4, &[S, H, H]),
|
||||
(0xF8, &[]),
|
||||
(0xF9, &[B, S]),
|
||||
(0xFA, &[]),
|
||||
(0xFB, &[B]),
|
||||
(0xFC, &[H]),
|
||||
(0xFD, &[]),
|
||||
(0xFE, &[S]),
|
||||
(0xFF, &[]),
|
||||
];
|
||||
|
||||
const V3_OPS: [(u8, &'static [Oper]); 165] = [
|
||||
(0x00, &[]),
|
||||
(0x01, &[B, H, F, A, A]),
|
||||
(0x02, &[A]),
|
||||
(0x04, &[S]),
|
||||
(0x05, &[]),
|
||||
(0x06, &[A]),
|
||||
(0x07, &[S]),
|
||||
(0x08, &[B]),
|
||||
(0x09, &[B, H, F]),
|
||||
(0x0A, &[H, F]),
|
||||
(0x0B, &[H, B]),
|
||||
(0x0C, &[H, B, H, ARR]),
|
||||
(0x0D, &[H, H, F]),
|
||||
(0x0E, &[H, H, B]),
|
||||
(0x0F, &[B]),
|
||||
(0x11, &[S, B, F]),
|
||||
(0x12, &[S, B, S]),
|
||||
(0x13, &[]),
|
||||
(0x14, &[I, S, S, B]),
|
||||
(0x15, &[S, B]),
|
||||
(0x16, &[B, B]),
|
||||
(0x17, &[]),
|
||||
(0x18, &[B, S]),
|
||||
(0x19, &[]),
|
||||
(0x1A, &[S]),
|
||||
(0x1B, &[B]),
|
||||
(0x1C, &[S, S, H, B]),
|
||||
(0x1D, &[H]),
|
||||
(0x1E, &[S, S, F, F, H, H, B, F]),
|
||||
(0x1F, &[S, F]),
|
||||
(0x20, &[S, F, H]),
|
||||
(0x21, &[S, H, H, H]),
|
||||
(0x22, &[S, B]),
|
||||
(0x28, &[S, S, F, F, H, H, B, H, H, B, F]),
|
||||
(0x29, &[S, F]),
|
||||
(0x2A, &[S, F, H]),
|
||||
(0x2B, &[S]),
|
||||
(0x2C, &[S]),
|
||||
(0x2D, &[S, B]),
|
||||
(0x2E, &[]),
|
||||
(0x2F, &[S, H, F]),
|
||||
(0x32, &[S]),
|
||||
(0x33, &[S, S, B, B]),
|
||||
(0x34, &[S, S, B, B]),
|
||||
(0x35, &[S, S, B, B, B]),
|
||||
(0x36, &[S, F, F, F, F, F, F, F, B, B]),
|
||||
(0x37, &[S]),
|
||||
(0x38, &[S, B]),
|
||||
(0x39, &[S, B, B, H, ARR]),
|
||||
(0x3A, &[S, B, B]),
|
||||
(0x3B, &[S, S, H, H, H, F, F, F, F, F, F, F, F]),
|
||||
(0x3C, &[S]),
|
||||
(0x3D, &[H]),
|
||||
(0x3E, &[]),
|
||||
(0x3F, &[S, ARR]),
|
||||
(0x40, &[S, S, B]),
|
||||
(0x41, &[S, B]),
|
||||
(0x42, &[S, H]),
|
||||
(0x43, &[S]),
|
||||
(0x44, &[S, S, B]),
|
||||
(0x45, &[S, H, F, F, F, F]),
|
||||
(0x46, &[S, H, B, F, F, F, F]),
|
||||
(0x47, &[S, S, H, B, B, F, F, F, F, F, H, F]),
|
||||
(0x48, &[S, S, H, B, B, S]),
|
||||
(0x49, &[S, S, S]),
|
||||
(0x4A, &[S, S]),
|
||||
(0x4B, &[S, H, H, F, F, F, F]),
|
||||
(0x4C, &[S, H, H, B, F, F, F, F]),
|
||||
(0x4D, &[S, S, H, H, B, B, F, F, F, F, F, H, F]),
|
||||
(0x4E, &[S, S, H, H, B, B, S]),
|
||||
(0x4F, &[S, S, H, S]),
|
||||
(0x50, &[S, S, H]),
|
||||
(0x51, &[S, S, H, F, B]),
|
||||
(0x52, &[S, S, F, H, F, B, S]),
|
||||
(0x53, &[S, S]),
|
||||
(0x54, &[S, S, S]),
|
||||
(0x55, &[S, S]),
|
||||
(
|
||||
0x56,
|
||||
&[
|
||||
S, B, H, F, F, F, F, F, F, F, F, F, F, F, B, F, F, F, F, B, H, S, H, S, S, F,
|
||||
],
|
||||
),
|
||||
(0x57, &[S, H]),
|
||||
(0x58, &[S, S]),
|
||||
(0x59, &[S, S, H]),
|
||||
(0x5A, &[S, H, ARR]),
|
||||
(0x5B, &[S, H, B]),
|
||||
(0x5C, &[S]),
|
||||
(0x5D, &[S, S, B]),
|
||||
(0x5E, &[S, F, F]),
|
||||
(0x5F, &[S]),
|
||||
(0x60, &[H, H, H, H]),
|
||||
(0x61, &[B, F, F, F, F]),
|
||||
(0x62, &[S]),
|
||||
(0x63, &[S, B]),
|
||||
(0x64, &[B]),
|
||||
(0x65, &[H, B, F, F, B, S]),
|
||||
(0x66, &[S]),
|
||||
(0x67, &[B, B, H, F, F, F, F, F, B]),
|
||||
(0x68, &[B]),
|
||||
(0x69, &[S, B, B, F, F, F, F, F, H, F]),
|
||||
(0x6A, &[S, H, B, B, S]),
|
||||
(0x6B, &[S, S]),
|
||||
(0x6C, &[S, F, F]),
|
||||
(0x6E, &[S, S]),
|
||||
(0x6F, &[S]),
|
||||
(0x70, &[S, H]),
|
||||
(0x71, &[]),
|
||||
(0x72, &[S, H, H, S]),
|
||||
(0x73, &[S, S, H]),
|
||||
(0x74, &[S, S]),
|
||||
(0x75, &[S, S]),
|
||||
(0x78, &[S, S, B, B, B]),
|
||||
(0x79, &[S, S, F]),
|
||||
(0x7A, &[S, S, F, B, B, S]),
|
||||
(0x7B, &[S, S]),
|
||||
(0x7C, &[S, S, F]),
|
||||
(0x7D, &[S, F]),
|
||||
(0x7E, &[S]),
|
||||
(0x7F, &[S, F, F, F, F, F]),
|
||||
(0x80, &[S]),
|
||||
(0x81, &[S, B, S, F, F, B]),
|
||||
(0x82, &[S, S, F]),
|
||||
(0x83, &[S, S, F, F]),
|
||||
(0x84, &[S, S, S, F, H, F]),
|
||||
(0x85, &[S, S, B, F]),
|
||||
(0x86, &[S, F, F, F]),
|
||||
(0x87, &[S, F]),
|
||||
(0x88, &[S, S, S, F, H, F]),
|
||||
(0x8C, &[S, S, S, B, B]),
|
||||
(0x8D, &[I, S, S, B, B, S]),
|
||||
(0x8E, &[I, S, S, B, B, S]),
|
||||
(0x8F, &[S, S]),
|
||||
(0x90, &[S]),
|
||||
(0x96, &[H, F, F, F, F]),
|
||||
(0x97, &[H, B, F, F, F, F]),
|
||||
(0x98, &[S, H, B, B, F, F, F, F, F, H, F]),
|
||||
(0x99, &[S, H, B, B, S]),
|
||||
(0x9A, &[]),
|
||||
(0x9B, &[S]),
|
||||
(0x9C, &[S, S]),
|
||||
(0x9D, &[S]),
|
||||
(0x9E, &[S, B]),
|
||||
(0x9F, &[S, B]),
|
||||
(0xC8, &[]),
|
||||
(0xC9, &[S, S, H, H, H, H]),
|
||||
(0xCA, &[S, S]),
|
||||
(0xCB, &[S, B, B]),
|
||||
(0xCC, &[]),
|
||||
(0xCD, &[S, S, S, S, S, F, B]),
|
||||
(0xCE, &[B]),
|
||||
(0xCF, &[S, S, F]),
|
||||
(0xD0, &[S, H]),
|
||||
(0xD1, &[S, H]),
|
||||
(0xD2, &[S]),
|
||||
(0xD3, &[S]),
|
||||
(0xD4, &[S, H, H]),
|
||||
(0xE6, &[I, I]),
|
||||
(0xE7, &[]),
|
||||
(0xE8, &[]),
|
||||
(0xF0, &[B]),
|
||||
(0xF8, &[]),
|
||||
(0xF9, &[B, S]),
|
||||
(0xFA, &[]),
|
||||
(0xFB, &[B]),
|
||||
(0xFC, &[H]),
|
||||
(0xFD, &[]),
|
||||
(0xFE, &[S]),
|
||||
(0xFF, &[]),
|
||||
];
|
||||
|
||||
const OPS: [&[(u8, &'static [Oper])]; 3] = [&V1_OPS, &V2_OPS, &V3_OPS];
|
||||
|
||||
pub fn disassmble(data: &[u8], encoding: Encoding) -> Result<(Vec<usize>, Vec<Ws2DString>)> {
|
||||
for op in &OPS {
|
||||
let disasm = DisasmBase::new(data, op, encoding);
|
||||
match disasm.disassmble() {
|
||||
Ok(result) => return Ok(result),
|
||||
Err(_) => continue, // Try the next version if this one fails
|
||||
}
|
||||
}
|
||||
Err(anyhow::anyhow!(
|
||||
"Failed to disassemble the data with all known versions"
|
||||
))
|
||||
}
|
||||
Reference in New Issue
Block a user