diff --git a/src/ext/io.rs b/src/ext/io.rs index d5d5faa..3aafd31 100644 --- a/src/ext/io.rs +++ b/src/ext/io.rs @@ -749,6 +749,14 @@ pub trait ReadExt { fn read_i128(&mut self) -> Result; /// Reads an [i128] from the reader in big-endian order. fn read_i128_be(&mut self) -> Result; + /// Reads a [f32] from the reader in little-endian order. + fn read_f32(&mut self) -> Result; + /// Reads a [f32] from the reader in big-endian order. + fn read_f32_be(&mut self) -> Result; + /// Reads a [f64] from the reader in little-endian order. + fn read_f64(&mut self) -> Result; + /// Reads a [f64] from the reader in big-endian order. + fn read_f64_be(&mut self) -> Result; /// Reads a C-style string (null-terminated) from the reader. fn read_cstring(&mut self) -> Result; @@ -856,6 +864,26 @@ impl ReadExt for T { self.read_exact(&mut buf)?; Ok(i128::from_be_bytes(buf)) } + fn read_f32(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(f32::from_le_bytes(buf)) + } + fn read_f32_be(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(f32::from_be_bytes(buf)) + } + fn read_f64(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf)?; + Ok(f64::from_le_bytes(buf)) + } + fn read_f64_be(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf)?; + Ok(f64::from_be_bytes(buf)) + } fn read_cstring(&mut self) -> Result { let mut buf = Vec::new(); @@ -1558,3 +1586,168 @@ impl Seek for StreamRegion { 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, + O: Fn(u64) -> Result, +> { + pub input: R, + pub output: W, + input_len: u64, + address_to_offset: A, + offset_to_address: O, + range_maps: Vec, +} + +impl Result, O: Fn(u64) -> Result> + BinaryPatcher +{ + /// 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 { + 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 { + 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 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(()) + } +} diff --git a/src/scripts/will_plus/mod.rs b/src/scripts/will_plus/mod.rs index f8bc38a..f6dbea1 100644 --- a/src/scripts/will_plus/mod.rs +++ b/src/scripts/will_plus/mod.rs @@ -1,2 +1,3 @@ //! WillPlus Scripts pub mod ws2; +mod ws2_disasm; diff --git a/src/scripts/will_plus/ws2.rs b/src/scripts/will_plus/ws2.rs index 49b0127..af4a85f 100644 --- a/src/scripts/will_plus/ws2.rs +++ b/src/scripts/will_plus/ws2.rs @@ -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>, ) -> Result> { + 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, + addresses: Vec, + /// 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 { + 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> { + 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, + file: Box, + 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(()) + } +} diff --git a/src/scripts/will_plus/ws2_disasm.rs b/src/scripts/will_plus/ws2_disasm.rs new file mode 100644 index 0000000..b17fb82 --- /dev/null +++ b/src/scripts/will_plus/ws2_disasm.rs @@ -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, Vec)>; +} + +#[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, + texts: Vec, + 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>)> { + 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>> { + 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> { + 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>) -> Result<()> { + if operands.len() < 1 { + return Err(anyhow::anyhow!("Invalid operands for choice screen")); + } + let first = operands.remove(0); + let num_choices = first + .downcast::() + .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::() + .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>) -> Result<()> { + if operands.len() < 3 { + return Err(anyhow::anyhow!("Invalid operands for message")); + } + let range = operands.remove(2); + let mut range = range + .downcast::() + .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>) -> Result<()> { + if operands.len() < 1 { + return Err(anyhow::anyhow!("Invalid operands for name")); + } + let name = operands.remove(0); + let mut name = name + .downcast::() + .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, Vec)> { + 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::() { + 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, Vec)> { + 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" + )) +}