diff --git a/src/ext/io.rs b/src/ext/io.rs new file mode 100644 index 0000000..ecaf7da --- /dev/null +++ b/src/ext/io.rs @@ -0,0 +1,631 @@ +use std::{ffi::CString, io::*}; + +pub trait Peek { + fn peek(&mut self, buf: &mut [u8]) -> Result; + fn peek_extract(&mut self, buf: &mut [u8]) -> Result<()>; + fn peek_at(&mut self, offset: usize, buf: &mut [u8]) -> Result; + fn peek_extract_at(&mut self, offset: usize, buf: &mut [u8]) -> Result<()>; + fn peek_at_vec(&mut self, offset: usize, len: usize) -> Result> { + 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> { + let mut buf = vec![0u8; len]; + self.peek_extract_at(offset, &mut buf)?; + Ok(buf) + } + + fn peek_u8(&mut self) -> Result { + let mut buf = [0u8; 1]; + self.peek_extract(&mut buf)?; + Ok(buf[0]) + } + fn peek_u16(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.peek_extract(&mut buf)?; + Ok(u16::from_le_bytes(buf)) + } + fn peek_u16_be(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.peek_extract(&mut buf)?; + Ok(u16::from_be_bytes(buf)) + } + fn peek_u32(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.peek_extract(&mut buf)?; + Ok(u32::from_le_bytes(buf)) + } + fn peek_u32_be(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.peek_extract(&mut buf)?; + Ok(u32::from_be_bytes(buf)) + } + fn peek_u64(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.peek_extract(&mut buf)?; + Ok(u64::from_le_bytes(buf)) + } + fn peek_u64_be(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.peek_extract(&mut buf)?; + Ok(u64::from_be_bytes(buf)) + } + fn peek_u128(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.peek_extract(&mut buf)?; + Ok(u128::from_le_bytes(buf)) + } + fn peek_u128_be(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.peek_extract(&mut buf)?; + Ok(u128::from_be_bytes(buf)) + } + fn peek_i8(&mut self) -> Result { + let mut buf = [0u8; 1]; + self.peek_extract(&mut buf)?; + Ok(i8::from_le_bytes(buf)) + } + fn peek_i16(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.peek_extract(&mut buf)?; + Ok(i16::from_le_bytes(buf)) + } + fn peek_i16_be(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.peek_extract(&mut buf)?; + Ok(i16::from_be_bytes(buf)) + } + fn peek_i32(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.peek_extract(&mut buf)?; + Ok(i32::from_le_bytes(buf)) + } + fn peek_i32_be(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.peek_extract(&mut buf)?; + Ok(i32::from_be_bytes(buf)) + } + fn peek_i64(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.peek_extract(&mut buf)?; + Ok(i64::from_le_bytes(buf)) + } + fn peek_i64_be(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.peek_extract(&mut buf)?; + Ok(i64::from_be_bytes(buf)) + } + fn peek_i128(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.peek_extract(&mut buf)?; + Ok(i128::from_le_bytes(buf)) + } + fn peek_i128_be(&mut self) -> Result { + 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 { + let mut buf = [0u8; 1]; + self.peek_extract_at(offset, &mut buf)?; + Ok(buf[0]) + } + fn peek_u16_at(&mut self, offset: usize) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + let mut buf = [0u8; 16]; + self.peek_extract_at(offset, &mut buf)?; + Ok(i128::from_be_bytes(buf)) + } + + fn peek_cstring(&mut self) -> Result; + fn peek_cstring_at(&mut self, offset: usize) -> Result; +} + +impl Peek for T { + fn peek(&mut self, buf: &mut [u8]) -> Result { + 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 { + 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 { + 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 { + 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; + fn read_u16(&mut self) -> Result; + fn read_u16_be(&mut self) -> Result; + fn read_u32(&mut self) -> Result; + fn read_u32_be(&mut self) -> Result; + fn read_u64(&mut self) -> Result; + fn read_u64_be(&mut self) -> Result; + fn read_u128(&mut self) -> Result; + fn read_u128_be(&mut self) -> Result; + fn read_i8(&mut self) -> Result; + fn read_i16(&mut self) -> Result; + fn read_i16_be(&mut self) -> Result; + fn read_i32(&mut self) -> Result; + fn read_i32_be(&mut self) -> Result; + fn read_i64(&mut self) -> Result; + fn read_i64_be(&mut self) -> Result; + fn read_i128(&mut self) -> Result; + fn read_i128_be(&mut self) -> Result; + + fn read_cstring(&mut self) -> Result; +} + +impl ReadExt for T { + fn read_u8(&mut self) -> Result { + let mut buf = [0u8; 1]; + self.read_exact(&mut buf)?; + Ok(buf[0]) + } + fn read_u16(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.read_exact(&mut buf)?; + Ok(u16::from_le_bytes(buf)) + } + fn read_u16_be(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.read_exact(&mut buf)?; + Ok(u16::from_be_bytes(buf)) + } + fn read_u32(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(u32::from_le_bytes(buf)) + } + fn read_u32_be(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(u32::from_be_bytes(buf)) + } + fn read_u64(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf)?; + Ok(u64::from_le_bytes(buf)) + } + fn read_u64_be(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf)?; + Ok(u64::from_be_bytes(buf)) + } + fn read_u128(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.read_exact(&mut buf)?; + Ok(u128::from_le_bytes(buf)) + } + fn read_u128_be(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.read_exact(&mut buf)?; + Ok(u128::from_be_bytes(buf)) + } + fn read_i8(&mut self) -> Result { + let mut buf = [0u8; 1]; + self.read_exact(&mut buf)?; + Ok(i8::from_le_bytes(buf)) + } + fn read_i16(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.read_exact(&mut buf)?; + Ok(i16::from_le_bytes(buf)) + } + fn read_i16_be(&mut self) -> Result { + let mut buf = [0u8; 2]; + self.read_exact(&mut buf)?; + Ok(i16::from_be_bytes(buf)) + } + fn read_i32(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(i32::from_le_bytes(buf)) + } + fn read_i32_be(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(i32::from_be_bytes(buf)) + } + fn read_i64(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf)?; + Ok(i64::from_le_bytes(buf)) + } + fn read_i64_be(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf)?; + Ok(i64::from_be_bytes(buf)) + } + fn read_i128(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.read_exact(&mut buf)?; + Ok(i128::from_le_bytes(buf)) + } + fn read_i128_be(&mut self) -> Result { + let mut buf = [0u8; 16]; + self.read_exact(&mut buf)?; + Ok(i128::from_be_bytes(buf)) + } + + fn read_cstring(&mut self) -> Result { + 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 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, + 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) -> 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + Ok(self.pos as u64) + } + + fn rewind(&mut self) -> Result<()> { + self.pos = 0; + Ok(()) + } +} diff --git a/src/ext/mod.rs b/src/ext/mod.rs new file mode 100644 index 0000000..af514a1 --- /dev/null +++ b/src/ext/mod.rs @@ -0,0 +1 @@ +pub mod io; diff --git a/src/main.rs b/src/main.rs index b4fb8fd..c65af12 100644 --- a/src/main.rs +++ b/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, @@ -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, + arg: &args::Arg, + config: &types::ExtraConfig, +) -> anyhow::Result<( + Box, + &'static Box, +)> { + 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::>() + } 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 { 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() { diff --git a/src/scripts/base.rs b/src/scripts/base.rs index 2182081..2db7e3b 100644 --- a/src/scripts/base.rs +++ b/src/scripts/base.rs @@ -10,7 +10,7 @@ pub trait ScriptBuilder: std::fmt::Debug { fn build_script( &self, - filename: &str, + buf: Vec, encoding: Encoding, config: &ExtraConfig, ) -> Result>; @@ -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>; + fn extract_messages(&self) -> Result> { + if !self.is_archive() { + return Err(anyhow::anyhow!( + "This script type does not support extracting messages." + )); + } + Ok(vec![]) + } fn import_messages( &self, - messages: Vec, - filename: &str, - encoding: Encoding, - replacement: Option<&ReplacementTable>, - ) -> Result<()>; + _messages: Vec, + _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>> + 'a>> { + Ok(Box::new(std::iter::empty())) + } } diff --git a/src/scripts/bgi/script.rs b/src/scripts/bgi/script.rs index 10ae4cb..0bfcb17 100644 --- a/src/scripts/bgi/script.rs +++ b/src/scripts/bgi/script.rs @@ -20,15 +20,11 @@ impl ScriptBuilder for BGIScriptBuilder { fn build_script( &self, - filename: &str, + buf: Vec, encoding: Encoding, config: &ExtraConfig, ) -> Result> { - 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 { - let data = crate::utils::files::read_file(filename)?; + pub fn new(data: Vec, encoding: Encoding, _config: &ExtraConfig) -> Result { if data.starts_with(b"BurikoCompiledScriptVer1.00\0") { let mut parser = V1Parser::new(&data, encoding)?; parser.disassemble()?; diff --git a/src/scripts/circus/script.rs b/src/scripts/circus/script.rs index 6b21062..9ecee95 100644 --- a/src/scripts/circus/script.rs +++ b/src/scripts/circus/script.rs @@ -20,15 +20,11 @@ impl ScriptBuilder for CircusMesScriptBuilder { fn build_script( &self, - filename: &str, + buf: Vec, encoding: Encoding, config: &ExtraConfig, ) -> Result> { - 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 { - let data = crate::utils::files::read_file(filename)?; + pub fn new(data: Vec, encoding: Encoding, config: &ExtraConfig) -> Result { 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; diff --git a/src/scripts/escude/archive.rs b/src/scripts/escude/archive.rs new file mode 100644 index 0000000..666f5e8 --- /dev/null +++ b/src/scripts/escude/archive.rs @@ -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, + encoding: Encoding, + config: &ExtraConfig, + ) -> Result> { + 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 { + 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, +} + +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, + encoding: Encoding, +} + +impl EscudeBinArchive { + pub fn new(data: Vec, encoding: Encoding, _config: &ExtraConfig) -> Result { + 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>> + '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> { + entries: T, + reader: MemReaderRef<'a>, + encoding: Encoding, + file_count: u32, +} + +impl<'a, T: Iterator> Iterator for EscudeBinArchiveIterator<'a, T> { + type Item = Result>; + + fn next(&mut self) -> Option { + 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 }))) + } +} diff --git a/src/scripts/escude/crypto.rs b/src/scripts/escude/crypto.rs new file mode 100644 index 0000000..d275467 --- /dev/null +++ b/src/scripts/escude/crypto.rs @@ -0,0 +1,54 @@ +use crate::ext::io::*; +use anyhow::Result; +use std::io::{Read, Seek}; + +pub struct CryptoReader { + reader: T, + _key: u32, + max_pos: u32, +} + +impl CryptoReader { + pub fn new(mut reader: T) -> Result { + 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 Read for CryptoReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + 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) + } +} diff --git a/src/scripts/escude/lzw.rs b/src/scripts/escude/lzw.rs new file mode 100644 index 0000000..2c96d8f --- /dev/null +++ b/src/scripts/escude/lzw.rs @@ -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 { + 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 { + 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> { + 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) + } +} diff --git a/src/scripts/escude/mod.rs b/src/scripts/escude/mod.rs index 8b13789..7820101 100644 --- a/src/scripts/escude/mod.rs +++ b/src/scripts/escude/mod.rs @@ -1 +1,4 @@ - +pub mod archive; +mod crypto; +mod lzw; +pub mod script; diff --git a/src/scripts/escude/script.rs b/src/scripts/escude/script.rs new file mode 100644 index 0000000..e984fd9 --- /dev/null +++ b/src/scripts/escude/script.rs @@ -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, + encoding: Encoding, + config: &ExtraConfig, + ) -> Result> { + 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 { + if buf_len > 8 && buf.starts_with(b"ESCR1_00") { + return Some(255); + } + None + } +} + +#[derive(Debug)] +pub struct EscudeBinScript { + offsets: Vec, + vms: Vec, + unk1: u32, + strings: Vec, +} + +impl EscudeBinScript { + pub fn new(data: Vec, encoding: Encoding, _config: &ExtraConfig) -> Result { + 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> { + Ok(self + .strings + .iter() + .map(|s| Message { + message: s.to_string(), + name: None, + }) + .collect()) + } + + fn import_messages( + &self, + _messages: Vec, + _filename: &str, + _encoding: Encoding, + _replacement: Option<&ReplacementTable>, + ) -> Result<()> { + Ok(()) + } + + fn is_archive(&self) -> bool { + false + } +} + +struct StrReplacer { + pub replacements: HashMap, Vec>, +} + +enum JisStr { + Single(u8), + Double(u8, u8), +} + +impl StrReplacer { + pub fn new() -> Result { + 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> { + 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) + } +} diff --git a/src/scripts/mod.rs b/src/scripts/mod.rs index eaf5f8e..2933970 100644 --- a/src/scripts/mod.rs +++ b/src/scripts/mod.rs @@ -9,6 +9,8 @@ lazy_static::lazy_static! { pub static ref BUILDER: Vec> = 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 = BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect(); diff --git a/src/types.rs b/src/types.rs index 62bc732..3d8339b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -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)] diff --git a/src/utils/encoding_win.rs b/src/utils/encoding_win.rs index 75b8094..b1a8f10 100644 --- a/src/utils/encoding_win.rs +++ b/src/utils/encoding_win.rs @@ -48,6 +48,9 @@ impl std::fmt::Display for WinError { } pub fn decode_to_string(cp: u32, data: &[u8]) -> Result { + if data.is_empty() { + return Ok(String::new()); + } let needed_len = unsafe { MultiByteToWideChar( cp,