diff --git a/src/main.rs b/src/main.rs index 83b58fd..15672e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -161,6 +161,7 @@ pub fn parse_script( encoding, archive_encoding, config, + None, )?, builder, )); @@ -189,7 +190,7 @@ pub fn parse_script( let encoding = get_encoding(arg, builder); let archive_encoding = get_archived_encoding(arg, builder, encoding); return Ok(( - builder.build_script_from_file(filename, encoding, archive_encoding, config)?, + builder.build_script_from_file(filename, encoding, archive_encoding, config, None)?, builder, )); } @@ -220,7 +221,7 @@ pub fn parse_script( let encoding = get_encoding(arg, builder); let archive_encoding = get_archived_encoding(arg, builder, encoding); return Ok(( - builder.build_script_from_file(filename, encoding, archive_encoding, config)?, + builder.build_script_from_file(filename, encoding, archive_encoding, config, None)?, builder, )); } @@ -234,10 +235,11 @@ pub fn parse_script( Err(anyhow::anyhow!("Unsupported script type")) } -pub fn parse_script_from_archive( - file: &mut Box, +pub fn parse_script_from_archive<'a>( + file: &mut Box, arg: &args::Arg, config: &types::ExtraConfig, + archive: &Box, ) -> anyhow::Result<( Box, &'static Box, @@ -255,6 +257,7 @@ pub fn parse_script_from_archive( encoding, archive_encoding, config, + Some(archive), )?, builder, )); @@ -289,6 +292,7 @@ pub fn parse_script_from_archive( encoding, archive_encoding, config, + Some(archive), )?, builder, )); @@ -315,7 +319,14 @@ pub fn parse_script_from_archive( let encoding = get_encoding(arg, builder); let archive_encoding = get_archived_encoding(arg, builder, encoding); return Ok(( - builder.build_script(buf, file.name(), encoding, archive_encoding, config)?, + builder.build_script( + buf, + file.name(), + encoding, + archive_encoding, + config, + Some(archive), + )?, builder, )); } @@ -338,7 +349,7 @@ pub fn export_script( is_dir: bool, ) -> anyhow::Result { eprintln!("Exporting {}", filename); - let mut script = parse_script(filename, arg, config)?.0; + let script = parse_script(filename, arg, config)?.0; if script.is_archive() { let odir = match output.as_ref() { Some(output) => { @@ -367,10 +378,31 @@ pub fn export_script( if !std::fs::exists(&odir)? { std::fs::create_dir_all(&odir)?; } - for f in script.iter_archive_mut()? { - let mut f = f?; + for (i, filename) in script.iter_archive_filename()?.enumerate() { + let filename = match filename { + Ok(f) => f, + Err(e) => { + eprintln!("Error reading archive filename: {}", e); + COUNTER.inc_error(); + if arg.backtrace { + eprintln!("Backtrace: {}", e.backtrace()); + } + continue; + } + }; + let mut f = match script.open_file(i) { + Ok(f) => f, + Err(e) => { + eprintln!("Error opening file {}: {}", filename, e); + COUNTER.inc_error(); + if arg.backtrace { + eprintln!("Backtrace: {}", e.backtrace()); + } + continue; + } + }; if f.is_script() { - let (script_file, _) = parse_script_from_archive(&mut f, arg, config)?; + let (script_file, _) = parse_script_from_archive(&mut f, arg, config, &script)?; #[cfg(feature = "image")] if script_file.is_image() { if script_file.is_multi_image() { @@ -807,7 +839,7 @@ pub fn import_script( repl: Option<&types::ReplacementTable>, ) -> anyhow::Result { eprintln!("Importing {}", filename); - let (mut script, builder) = parse_script(filename, arg, config)?; + let (script, builder) = parse_script(filename, arg, config)?; if script.is_archive() { let odir = { let mut pb = std::path::PathBuf::from(&imp_cfg.output); @@ -823,7 +855,7 @@ pub fn import_script( } pb.to_string_lossy().into_owned() }; - let files: Vec<_> = script.iter_archive()?.collect(); + let files: Vec<_> = script.iter_archive_filename()?.collect(); let files = files.into_iter().filter_map(|f| f.ok()).collect::>(); let patched_f = if is_dir { let f = std::path::PathBuf::from(filename); @@ -840,11 +872,32 @@ pub fn import_script( let pencoding = get_patched_encoding(imp_cfg, builder); let enc = get_patched_archive_encoding(imp_cfg, builder, pencoding); let mut arch = builder.create_archive(&patched_f, &files, enc, config)?; - for f in script.iter_archive_mut()? { - let mut f = f?; + for (index, filename) in script.iter_archive_filename()?.enumerate() { + let filename = match filename { + Ok(f) => f, + Err(e) => { + eprintln!("Error reading archive filename: {}", e); + COUNTER.inc_error(); + if arg.backtrace { + eprintln!("Backtrace: {}", e.backtrace()); + } + continue; + } + }; + let mut f = match script.open_file(index) { + Ok(f) => f, + Err(e) => { + eprintln!("Error opening file {}: {}", filename, e); + COUNTER.inc_error(); + if arg.backtrace { + eprintln!("Backtrace: {}", e.backtrace()); + } + continue; + } + }; let mut writer = arch.new_file(f.name())?; if f.is_script() { - let (script_file, _) = parse_script_from_archive(&mut f, arg, config)?; + let (script_file, _) = parse_script_from_archive(&mut f, arg, config, &script)?; let mut of = match &arg.output_type { Some(t) => t.clone(), None => script_file.default_output_script_type(), @@ -1252,7 +1305,7 @@ pub fn unpack_archive( is_dir: bool, ) -> anyhow::Result { eprintln!("Unpacking {}", filename); - let mut script = parse_script(filename, arg, config)?.0; + let script = parse_script(filename, arg, config)?.0; if !script.is_archive() { return Ok(types::ScriptResult::Ignored); } @@ -1283,8 +1336,29 @@ pub fn unpack_archive( if !std::fs::exists(&odir)? { std::fs::create_dir_all(&odir)?; } - for f in script.iter_archive_mut()? { - let mut f = f?; + for (index, filename) in script.iter_archive_filename()?.enumerate() { + let filename = match filename { + Ok(f) => f, + Err(e) => { + eprintln!("Error reading archive filename: {}", e); + COUNTER.inc_error(); + if arg.backtrace { + eprintln!("Backtrace: {}", e.backtrace()); + } + continue; + } + }; + let mut f = match script.open_file(index) { + Ok(f) => f, + Err(e) => { + eprintln!("Error opening file {}: {}", filename, e); + COUNTER.inc_error(); + if arg.backtrace { + eprintln!("Backtrace: {}", e.backtrace()); + } + continue; + } + }; let out_path = std::path::PathBuf::from(&odir).join(f.name()); match utils::files::make_sure_dir_exists(&out_path) { Ok(_) => {} diff --git a/src/scripts/artemis/archive/pfs.rs b/src/scripts/artemis/archive/pfs.rs index 4479ba7..cc2d656 100644 --- a/src/scripts/artemis/archive/pfs.rs +++ b/src/scripts/artemis/archive/pfs.rs @@ -1,6 +1,5 @@ use crate::ext::io::*; use crate::scripts::base::*; -use crate::try_option; use crate::types::*; use crate::utils::struct_pack::*; use anyhow::Result; @@ -35,6 +34,7 @@ impl ScriptBuilder for ArtemisArcBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(ArtemisArc::new( MemReader::new(buf), @@ -50,6 +50,7 @@ impl ScriptBuilder for ArtemisArcBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { let f = std::fs::File::open(filename)?; let f = std::io::BufReader::new(f); @@ -68,6 +69,7 @@ impl ScriptBuilder for ArtemisArcBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(ArtemisArc::new( reader, @@ -199,20 +201,41 @@ impl Script for ArtemisArc { true } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Ok(Box::new( self.entries.iter().map(|header| Ok(header.name.clone())), )) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(ArtemisArcIter { - entries: self.entries.iter(), + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Ok(Box::new( + self.entries.iter().map(|header| Ok(header.offset as u64)), + )) + } + + fn open_file<'a>(&'a self, index: usize) -> Result> { + if index >= self.entries.len() { + return Err(anyhow::anyhow!( + "Index out of bounds: {} (max: {})", + index, + self.entries.len() + )); + } + let header = &self.entries[index]; + let mut entry = Entry { + header: header.clone(), reader: self.reader.clone(), + pos: 0, + script_type: None, xor_key: self.xor_key.clone(), - })) + }; + let mut header = [0; 0x20]; + let readed = entry.read(&mut header)?; + entry.pos = 0; + entry.script_type = detect_script_type(&header, readed, &entry.header.name); + Ok(Box::new(entry)) } fn archive_output_ext<'a>(&'a self) -> Option<&'a str> { @@ -313,37 +336,6 @@ fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option, R: Read + Seek + 'static> { - entries: T, - reader: Arc>, - xor_key: Option<[u8; 20]>, -} - -impl<'a, T: Iterator, R: Read + Seek + 'static> Iterator - for ArtemisArcIter<'a, T, R> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - if let Some(header) = self.entries.next() { - let mut entry = Entry { - header: header.clone(), - reader: self.reader.clone(), - pos: 0, - script_type: None, - xor_key: self.xor_key.clone(), - }; - let mut header = [0; 0x20]; - let readed = try_option!(entry.read(&mut header)); - entry.pos = 0; - entry.script_type = detect_script_type(&header, readed, &entry.header.name); - Some(Ok(Box::new(entry))) - } else { - None - } - } -} - pub struct ArtemisArcWriter { writer: T, headers: HashMap, diff --git a/src/scripts/artemis/asb.rs b/src/scripts/artemis/asb.rs index ffebe73..24910f2 100644 --- a/src/scripts/artemis/asb.rs +++ b/src/scripts/artemis/asb.rs @@ -30,6 +30,7 @@ impl ScriptBuilder for ArtemisAsbBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(Asb::new(buf, encoding, config)?)) } diff --git a/src/scripts/artemis/ast/mod.rs b/src/scripts/artemis/ast/mod.rs index eaae9ae..db89695 100644 --- a/src/scripts/artemis/ast/mod.rs +++ b/src/scripts/artemis/ast/mod.rs @@ -31,6 +31,7 @@ impl ScriptBuilder for AstScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(AstScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/base.rs b/src/scripts/base.rs index 35b83f3..83a0803 100644 --- a/src/scripts/base.rs +++ b/src/scripts/base.rs @@ -29,6 +29,7 @@ pub trait ScriptBuilder: std::fmt::Debug { encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + archive: Option<&Box>, ) -> Result>; fn build_script_from_file( @@ -37,9 +38,10 @@ pub trait ScriptBuilder: std::fmt::Debug { encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + archive: Option<&Box>, ) -> Result> { let data = crate::utils::files::read_file(filename)?; - self.build_script(data, filename, encoding, archive_encoding, config) + self.build_script(data, filename, encoding, archive_encoding, config, archive) } fn build_script_from_reader( @@ -49,12 +51,13 @@ pub trait ScriptBuilder: std::fmt::Debug { encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + archive: Option<&Box>, ) -> Result> { let mut data = Vec::new(); reader .read_to_end(&mut data) .map_err(|e| anyhow::anyhow!("Failed to read from reader: {}", e))?; - self.build_script(data, filename, encoding, archive_encoding, config) + self.build_script(data, filename, encoding, archive_encoding, config, archive) } fn extensions(&self) -> &'static [&'static str]; @@ -162,7 +165,7 @@ pub trait ArchiveContent: Read { } } -pub trait Script: std::fmt::Debug { +pub trait Script: std::fmt::Debug + std::any::Any { fn default_output_script_type(&self) -> OutputScriptType; fn is_output_supported(&self, output: OutputScriptType) -> bool { @@ -245,16 +248,56 @@ pub trait Script: std::fmt::Debug { false } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Err(anyhow::anyhow!( - "This script type does not support iterating over archive contents." + "This script type does not support iterating over archive filenames." )) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(std::iter::empty())) + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Err(anyhow::anyhow!( + "This script type does not support iterating over archive offsets." + )) + } + + fn open_file<'a>(&'a self, _index: usize) -> Result> { + Err(anyhow::anyhow!( + "This script type does not support opening files." + )) + } + + fn open_file_by_name<'a>( + &'a self, + name: &str, + ignore_case: bool, + ) -> Result> { + for (i, fname) in self.iter_archive_filename()?.enumerate() { + if let Ok(fname) = fname { + if fname == name || (ignore_case && fname.eq_ignore_ascii_case(name)) { + return self.open_file(i); + } + } + } + Err(anyhow::anyhow!( + "File with name '{}' not found in archive.", + name + )) + } + + fn open_file_by_offset<'a>(&'a self, offset: u64) -> Result> { + for (i, off) in self.iter_archive_offset()?.enumerate() { + if let Ok(off) = off { + if off == offset { + return self.open_file(i); + } + } + } + Err(anyhow::anyhow!( + "File with offset '{}' not found in archive.", + offset + )) } /// Returns output extension for archive output folder. diff --git a/src/scripts/bgi/archive/dsc.rs b/src/scripts/bgi/archive/dsc.rs index a910896..bdf09fb 100644 --- a/src/scripts/bgi/archive/dsc.rs +++ b/src/scripts/bgi/archive/dsc.rs @@ -507,6 +507,7 @@ impl ScriptBuilder for DscBuilder { _encoding: Encoding, _archive_encoding: Encoding, _config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(Dsc::new(buf)?)) } diff --git a/src/scripts/bgi/archive/v1.rs b/src/scripts/bgi/archive/v1.rs index 0d9f298..f8d393c 100644 --- a/src/scripts/bgi/archive/v1.rs +++ b/src/scripts/bgi/archive/v1.rs @@ -36,6 +36,7 @@ impl ScriptBuilder for BgiArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BgiArchive::new( MemReader::new(data), @@ -51,6 +52,7 @@ impl ScriptBuilder for BgiArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -79,6 +81,7 @@ impl ScriptBuilder for BgiArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BgiArchive::new( reader, @@ -214,8 +217,8 @@ impl Seek for Entry { #[derive(Debug)] pub struct BgiArchive { reader: Arc>, - file_count: u32, entries: Vec, + base_offset: u64, #[cfg(feature = "bgi-img")] is_sysgrp_arc: bool, } @@ -250,8 +253,8 @@ impl BgiArchive { Ok(BgiArchive { reader: Arc::new(Mutex::new(reader)), - file_count, entries, + base_offset: 16 + (file_count as u64 * 32), #[cfg(feature = "bgi-img")] is_sysgrp_arc, }) @@ -271,22 +274,180 @@ impl Script for BgiArchive { true } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Ok(Box::new( self.entries.iter().map(|e| Ok(e.filename.clone())), )) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(BgiArchiveIter { - entries: self.entries.iter(), + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Ok(Box::new(self.entries.iter().map(|e| Ok(e.offset as u64)))) + } + + fn open_file<'a>(&'a self, index: usize) -> Result> { + if index >= self.entries.len() { + return Err(anyhow::anyhow!( + "Index out of bounds: {} (max: {})", + index, + self.entries.len() + )); + } + let entry = &self.entries[index]; + let mut entry = Entry { + header: entry.clone(), reader: self.reader.clone(), - base_offset: 16 + (self.file_count as u64 * 32), + pos: 0, + base_offset: self.base_offset, + script_type: None, + }; + let mut buf = [0u8; 32]; + match entry.read(&mut buf) { + Ok(_) => {} + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to read entry '{}': {}", + entry.header.filename, + e + )); + } + } + entry.pos = 0; + if buf.starts_with(b"DSC FORMAT 1.00") { + let data = match entry.data() { + Ok(data) => data, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to read DSC data for '{}': {}", + entry.header.filename, + e + )); + } + }; + entry.pos = 0; + let dsc = match DscDecoder::new(&data) { + Ok(dsc) => dsc, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create DSC decoder for '{}': {}", + entry.header.filename, + e + )); + } + }; + let decoded = match dsc.unpack() { + Ok(decoded) => decoded, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to unpack DSC data for '{}': {}", + entry.header.filename, + e + )); + } + }; + let reader = MemReader::new(decoded); + if reader.data.starts_with(b"BSE 1.") { + match BseReader::new(reader, detect_script_type, &entry.header.filename) { + Ok(bse_reader) => { + return Ok(Box::new(bse_reader)); + } + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create BSE reader for '{}': {}", + entry.header.filename, + e + )); + } + }; + } + return Ok(Box::new(MemEntry { + name: entry.header.filename.clone(), + data: reader, + #[cfg(feature = "bgi-img")] + detect: if self.is_sysgrp_arc { + detect_script_type_sysgrp + } else { + detect_script_type + }, + #[cfg(not(feature = "bgi-img"))] + detect: detect_script_type, + })); + } + if buf.starts_with(b"BSE 1.") { + let filename = entry.header.filename.clone(); #[cfg(feature = "bgi-img")] - is_sysgrp_arc: self.is_sysgrp_arc, - })) + let detect = if self.is_sysgrp_arc { + detect_script_type_sysgrp + } else { + detect_script_type + }; + #[cfg(not(feature = "bgi-img"))] + let detect = detect_script_type; + match BseReader::new(entry, detect, &filename) { + Ok(mut bse_reader) => { + if bse_reader.is_dsc() { + let data = match bse_reader.data() { + Ok(data) => data, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to read BSE data for '{}': {}", + &filename, + e + )); + } + }; + let dsc = match DscDecoder::new(&data) { + Ok(dsc) => dsc, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create DSC decoder for '{}': {}", + &filename, + e + )); + } + }; + let decoded = match dsc.unpack() { + Ok(decoded) => decoded, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to unpack DSC data for '{}': {}", + &filename, + e + )); + } + }; + let reader = MemReader::new(decoded); + return Ok(Box::new(MemEntry { + name: filename, + data: reader, + detect, + })); + } + return Ok(Box::new(bse_reader)); + } + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create BSE reader for '{}': {}", + &filename, + e + )); + } + }; + } + #[cfg(feature = "bgi-img")] + if self.is_sysgrp_arc { + entry.script_type = Some(ScriptType::BGIImg); + } else { + entry.script_type = + detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); + } + #[cfg(not(feature = "bgi-img"))] + { + entry.script_type = + detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); + } + Ok(Box::new(entry)) } } @@ -346,180 +507,6 @@ fn detect_script_type_sysgrp( Some(&ScriptType::BGIImg) } -struct BgiArchiveIter<'a, T: Iterator, R: Read + Seek> { - entries: T, - reader: Arc>, - base_offset: u64, - #[cfg(feature = "bgi-img")] - is_sysgrp_arc: bool, -} - -impl<'a, T: Iterator, R: Read + Seek + 'static> Iterator - for BgiArchiveIter<'a, T, R> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - let entry = match self.entries.next() { - Some(e) => e, - None => return None, - }; - let mut entry = Entry { - header: entry.clone(), - reader: self.reader.clone(), - pos: 0, - base_offset: self.base_offset, - script_type: None, - }; - let mut buf = [0u8; 32]; - match entry.read(&mut buf) { - Ok(_) => {} - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to read entry '{}': {}", - entry.header.filename, - e - ))); - } - } - entry.pos = 0; - if buf.starts_with(b"DSC FORMAT 1.00") { - let data = match entry.data() { - Ok(data) => data, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to read DSC data for '{}': {}", - entry.header.filename, - e - ))); - } - }; - entry.pos = 0; - let dsc = match DscDecoder::new(&data) { - Ok(dsc) => dsc, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create DSC decoder for '{}': {}", - entry.header.filename, - e - ))); - } - }; - let decoded = match dsc.unpack() { - Ok(decoded) => decoded, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to unpack DSC data for '{}': {}", - entry.header.filename, - e - ))); - } - }; - let reader = MemReader::new(decoded); - if reader.data.starts_with(b"BSE 1.") { - match BseReader::new(reader, detect_script_type, &entry.header.filename) { - Ok(bse_reader) => { - return Some(Ok(Box::new(bse_reader))); - } - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create BSE reader for '{}': {}", - entry.header.filename, - e - ))); - } - }; - } - return Some(Ok(Box::new(MemEntry { - name: entry.header.filename.clone(), - data: reader, - #[cfg(feature = "bgi-img")] - detect: if self.is_sysgrp_arc { - detect_script_type_sysgrp - } else { - detect_script_type - }, - #[cfg(not(feature = "bgi-img"))] - detect: detect_script_type, - }))); - } - if buf.starts_with(b"BSE 1.") { - let filename = entry.header.filename.clone(); - #[cfg(feature = "bgi-img")] - let detect = if self.is_sysgrp_arc { - detect_script_type_sysgrp - } else { - detect_script_type - }; - #[cfg(not(feature = "bgi-img"))] - let detect = detect_script_type; - match BseReader::new(entry, detect, &filename) { - Ok(mut bse_reader) => { - if bse_reader.is_dsc() { - let data = match bse_reader.data() { - Ok(data) => data, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to read BSE data for '{}': {}", - &filename, - e - ))); - } - }; - let dsc = match DscDecoder::new(&data) { - Ok(dsc) => dsc, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create DSC decoder for '{}': {}", - &filename, - e - ))); - } - }; - let decoded = match dsc.unpack() { - Ok(decoded) => decoded, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to unpack DSC data for '{}': {}", - &filename, - e - ))); - } - }; - let reader = MemReader::new(decoded); - return Some(Ok(Box::new(MemEntry { - name: filename, - data: reader, - detect, - }))); - } - return Some(Ok(Box::new(bse_reader))); - } - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create BSE reader for '{}': {}", - &filename, - e - ))); - } - }; - } - #[cfg(feature = "bgi-img")] - if self.is_sysgrp_arc { - entry.script_type = Some(ScriptType::BGIImg); - } else { - entry.script_type = - detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); - } - #[cfg(not(feature = "bgi-img"))] - { - entry.script_type = - detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); - } - Some(Ok(Box::new(entry))) - } -} - pub struct BgiArchiveWriter { writer: T, headers: HashMap, diff --git a/src/scripts/bgi/archive/v2.rs b/src/scripts/bgi/archive/v2.rs index 0bf3fbc..4b8c12a 100644 --- a/src/scripts/bgi/archive/v2.rs +++ b/src/scripts/bgi/archive/v2.rs @@ -36,6 +36,7 @@ impl ScriptBuilder for BgiArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BgiArchive::new( MemReader::new(data), @@ -51,6 +52,7 @@ impl ScriptBuilder for BgiArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -79,6 +81,7 @@ impl ScriptBuilder for BgiArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BgiArchive::new( reader, @@ -246,8 +249,8 @@ impl Option<&'static ScriptType>> ArchiveContent fo #[derive(Debug)] pub struct BgiArchive { reader: Arc>, - file_count: u32, entries: Vec, + base_offset: u64, #[cfg(feature = "bgi-img")] is_sysgrp_arc: bool, } @@ -282,8 +285,8 @@ impl BgiArchive { Ok(BgiArchive { reader: Arc::new(Mutex::new(reader)), - file_count, entries, + base_offset: 16 + (file_count as u64 * 0x80), #[cfg(feature = "bgi-img")] is_sysgrp_arc, }) @@ -303,22 +306,180 @@ impl Script for BgiArchive { true } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Ok(Box::new( self.entries.iter().map(|e| Ok(e.filename.clone())), )) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(BgiArchiveIter { - entries: self.entries.iter(), + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Ok(Box::new(self.entries.iter().map(|e| Ok(e.offset as u64)))) + } + + fn open_file<'a>(&'a self, index: usize) -> Result> { + if index >= self.entries.len() { + return Err(anyhow::anyhow!( + "Index out of bounds: {} (max: {})", + index, + self.entries.len() + )); + } + let entry = &self.entries[index]; + let mut entry = Entry { + header: entry.clone(), reader: self.reader.clone(), - base_offset: 16 + (self.file_count as u64 * 0x80), + pos: 0, + base_offset: self.base_offset, + script_type: None, + }; + let mut buf = [0u8; 32]; + match entry.read(&mut buf) { + Ok(_) => {} + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to read entry '{}': {}", + entry.header.filename, + e + )); + } + } + entry.pos = 0; + if buf.starts_with(b"DSC FORMAT 1.00") { + let data = match entry.data() { + Ok(data) => data, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to read DSC data for '{}': {}", + entry.header.filename, + e + )); + } + }; + entry.pos = 0; + let dsc = match DscDecoder::new(&data) { + Ok(dsc) => dsc, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create DSC decoder for '{}': {}", + entry.header.filename, + e + )); + } + }; + let decoded = match dsc.unpack() { + Ok(decoded) => decoded, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to unpack DSC data for '{}': {}", + entry.header.filename, + e + )); + } + }; + let reader = MemReader::new(decoded); + if reader.data.starts_with(b"BSE 1.") { + match BseReader::new(reader, detect_script_type, &entry.header.filename) { + Ok(bse_reader) => { + return Ok(Box::new(bse_reader)); + } + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create BSE reader for '{}': {}", + entry.header.filename, + e + )); + } + }; + } + return Ok(Box::new(MemEntry { + name: entry.header.filename.clone(), + data: reader, + #[cfg(feature = "bgi-img")] + detect: if self.is_sysgrp_arc { + detect_script_type_sysgrp + } else { + detect_script_type + }, + #[cfg(not(feature = "bgi-img"))] + detect: detect_script_type, + })); + } + if buf.starts_with(b"BSE 1.") { + let filename = entry.header.filename.clone(); #[cfg(feature = "bgi-img")] - is_sysgrp_arc: self.is_sysgrp_arc, - })) + let detect = if self.is_sysgrp_arc { + detect_script_type_sysgrp + } else { + detect_script_type + }; + #[cfg(not(feature = "bgi-img"))] + let detect = detect_script_type; + match BseReader::new(entry, detect, &filename) { + Ok(mut bse_reader) => { + if bse_reader.is_dsc() { + let data = match bse_reader.data() { + Ok(data) => data, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to read BSE data for '{}': {}", + &filename, + e + )); + } + }; + let dsc = match DscDecoder::new(&data) { + Ok(dsc) => dsc, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create DSC decoder for '{}': {}", + &filename, + e + )); + } + }; + let decoded = match dsc.unpack() { + Ok(decoded) => decoded, + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to unpack DSC data for '{}': {}", + &filename, + e + )); + } + }; + let reader = MemReader::new(decoded); + return Ok(Box::new(MemEntry { + name: filename, + data: reader, + detect, + })); + } + return Ok(Box::new(bse_reader)); + } + Err(e) => { + return Err(anyhow::anyhow!( + "Failed to create BSE reader for '{}': {}", + &filename, + e + )); + } + }; + } + #[cfg(feature = "bgi-img")] + if self.is_sysgrp_arc { + entry.script_type = Some(ScriptType::BGIImg); + } else { + entry.script_type = + detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); + } + #[cfg(not(feature = "bgi-img"))] + { + entry.script_type = + detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); + } + Ok(Box::new(entry)) } } @@ -348,180 +509,6 @@ fn detect_script_type_sysgrp( Some(&ScriptType::BGIImg) } -struct BgiArchiveIter<'a, T: Iterator, R: Read + Seek> { - entries: T, - reader: Arc>, - base_offset: u64, - #[cfg(feature = "bgi-img")] - is_sysgrp_arc: bool, -} - -impl<'a, T: Iterator, R: Read + Seek + 'static> Iterator - for BgiArchiveIter<'a, T, R> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - let entry = match self.entries.next() { - Some(e) => e, - None => return None, - }; - let mut entry = Entry { - header: entry.clone(), - reader: self.reader.clone(), - pos: 0, - base_offset: self.base_offset, - script_type: None, - }; - let mut buf = [0u8; 32]; - match entry.read(&mut buf) { - Ok(_) => {} - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to read entry '{}': {}", - entry.header.filename, - e - ))); - } - } - entry.pos = 0; - if buf.starts_with(b"DSC FORMAT 1.00") { - let data = match entry.data() { - Ok(data) => data, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to read DSC data for '{}': {}", - entry.header.filename, - e - ))); - } - }; - entry.pos = 0; - let dsc = match DscDecoder::new(&data) { - Ok(dsc) => dsc, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create DSC decoder for '{}': {}", - entry.header.filename, - e - ))); - } - }; - let decoded = match dsc.unpack() { - Ok(decoded) => decoded, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to unpack DSC data for '{}': {}", - entry.header.filename, - e - ))); - } - }; - let reader = MemReader::new(decoded); - if reader.data.starts_with(b"BSE 1.") { - match BseReader::new(reader, detect_script_type, &entry.header.filename) { - Ok(bse_reader) => { - return Some(Ok(Box::new(bse_reader))); - } - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create BSE reader for '{}': {}", - entry.header.filename, - e - ))); - } - }; - } - return Some(Ok(Box::new(MemEntry { - name: entry.header.filename.clone(), - data: reader, - #[cfg(feature = "bgi-img")] - detect: if self.is_sysgrp_arc { - detect_script_type_sysgrp - } else { - detect_script_type - }, - #[cfg(not(feature = "bgi-img"))] - detect: detect_script_type, - }))); - } - if buf.starts_with(b"BSE 1.") { - let filename = entry.header.filename.clone(); - #[cfg(feature = "bgi-img")] - let detect = if self.is_sysgrp_arc { - detect_script_type_sysgrp - } else { - detect_script_type - }; - #[cfg(not(feature = "bgi-img"))] - let detect = detect_script_type; - match BseReader::new(entry, detect, &filename) { - Ok(mut bse_reader) => { - if bse_reader.is_dsc() { - let data = match bse_reader.data() { - Ok(data) => data, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to read BSE data for '{}': {}", - &filename, - e - ))); - } - }; - let dsc = match DscDecoder::new(&data) { - Ok(dsc) => dsc, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create DSC decoder for '{}': {}", - &filename, - e - ))); - } - }; - let decoded = match dsc.unpack() { - Ok(decoded) => decoded, - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to unpack DSC data for '{}': {}", - &filename, - e - ))); - } - }; - let reader = MemReader::new(decoded); - return Some(Ok(Box::new(MemEntry { - name: filename, - data: reader, - detect, - }))); - } - return Some(Ok(Box::new(bse_reader))); - } - Err(e) => { - return Some(Err(anyhow::anyhow!( - "Failed to create BSE reader for '{}': {}", - &filename, - e - ))); - } - }; - } - #[cfg(feature = "bgi-img")] - if self.is_sysgrp_arc { - entry.script_type = Some(ScriptType::BGIImg); - } else { - entry.script_type = - detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); - } - #[cfg(not(feature = "bgi-img"))] - { - entry.script_type = - detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); - } - Some(Ok(Box::new(entry))) - } -} - pub struct BgiArchiveWriter { writer: T, headers: HashMap, diff --git a/src/scripts/bgi/bp.rs b/src/scripts/bgi/bp.rs index d34ba6e..6bc902b 100644 --- a/src/scripts/bgi/bp.rs +++ b/src/scripts/bgi/bp.rs @@ -26,6 +26,7 @@ impl ScriptBuilder for BGIBpScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BGIBpScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/bgi/bsi.rs b/src/scripts/bgi/bsi.rs index 5cb0a9a..aec9be5 100644 --- a/src/scripts/bgi/bsi.rs +++ b/src/scripts/bgi/bsi.rs @@ -27,6 +27,7 @@ impl ScriptBuilder for BGIBsiScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BGIBsiScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/bgi/image/cbg.rs b/src/scripts/bgi/image/cbg.rs index f033483..7e949c5 100644 --- a/src/scripts/bgi/image/cbg.rs +++ b/src/scripts/bgi/image/cbg.rs @@ -32,6 +32,7 @@ impl ScriptBuilder for BgiCBGBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BgiCBG::new(data, config)?)) } diff --git a/src/scripts/bgi/image/img.rs b/src/scripts/bgi/image/img.rs index 6c3e700..07bf8f5 100644 --- a/src/scripts/bgi/image/img.rs +++ b/src/scripts/bgi/image/img.rs @@ -51,6 +51,7 @@ impl ScriptBuilder for BgiImageBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BgiImage::new(data, config)?)) } diff --git a/src/scripts/bgi/script.rs b/src/scripts/bgi/script.rs index bbcbdce..7d9f82f 100644 --- a/src/scripts/bgi/script.rs +++ b/src/scripts/bgi/script.rs @@ -31,6 +31,7 @@ impl ScriptBuilder for BGIScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BGIScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/cat_system/archive/int.rs b/src/scripts/cat_system/archive/int.rs index dc9d257..511c648 100644 --- a/src/scripts/cat_system/archive/int.rs +++ b/src/scripts/cat_system/archive/int.rs @@ -35,6 +35,7 @@ impl ScriptBuilder for CSIntArcBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(CSIntArc::new( MemReader::new(data), @@ -50,6 +51,7 @@ impl ScriptBuilder for CSIntArcBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -78,6 +80,7 @@ impl ScriptBuilder for CSIntArcBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(CSIntArc::new( reader, @@ -390,48 +393,33 @@ impl Script for CSIntArc { true } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Ok(Box::new(self.entries.iter().map(|e| Ok(e.name.clone())))) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(CSIntArcIter { - entries: self.entries.iter(), - reader: self.reader.clone(), - encrypt: self.encrypt.as_ref(), - })) + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Ok(Box::new(self.entries.iter().map(|e| Ok(e.offset as u64)))) } -} -struct CSIntArcIter<'a, T: Iterator, R: Read + Seek> { - entries: T, - reader: Arc>, - encrypt: Option<&'a Blowfish>, -} - -impl<'a, T: Iterator, R: Read + Seek + 'static> Iterator - for CSIntArcIter<'a, T, R> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - let entry = match self.entries.next() { - Some(e) => e, - None => return None, - }; + fn open_file<'a>(&'a self, index: usize) -> Result> { + if index >= self.entries.len() { + return Err(anyhow::anyhow!( + "Index out of bounds: {} (max: {})", + index, + self.entries.len() + )); + } + let entry = &self.entries[index]; let mut entry = Entry { header: entry.clone(), reader: self.reader.clone(), pos: 0, script_type: None, }; - if let Some(encrypt) = self.encrypt { - let mut data = match entry.data() { - Ok(data) => data, - Err(e) => return Some(Err(e)), - }; + if let Some(encrypt) = &self.encrypt { + let mut data = entry.data()?; entry.pos = 0; for i in 0..data.len() / 8 { let j = i * 8; @@ -447,24 +435,24 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iter data[j..j + 4].copy_from_slice(&result[0].to_le_bytes()); data[j + 4..j + 8].copy_from_slice(&result[1].to_le_bytes()); } - return Some(Ok(Box::new(MemEntry { + return Ok(Box::new(MemEntry { name: entry.header.name.clone(), data: MemReader::new(data), - }))); + })); } let mut buf = [0u8; 32]; let buf_len = match entry.read(&mut buf) { Ok(len) => len, Err(e) => { - return Some(Err(anyhow::anyhow!( + return Err(anyhow::anyhow!( "Failed to read entry '{}': {}", entry.header.name, e - ))); + )); } }; entry.pos = 0; entry.script_type = detect_script_type(&buf, buf_len, &entry.header.name).copied(); - Some(Ok(Box::new(entry))) + Ok(Box::new(entry)) } } diff --git a/src/scripts/cat_system/cst.rs b/src/scripts/cat_system/cst.rs index cbc67e5..e7216ea 100644 --- a/src/scripts/cat_system/cst.rs +++ b/src/scripts/cat_system/cst.rs @@ -28,6 +28,7 @@ impl ScriptBuilder for CstScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(CstScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/cat_system/cstl.rs b/src/scripts/cat_system/cstl.rs index c6ebeb4..7c48e13 100644 --- a/src/scripts/cat_system/cstl.rs +++ b/src/scripts/cat_system/cstl.rs @@ -26,6 +26,7 @@ impl ScriptBuilder for CstlScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(CstlScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/cat_system/image/hg3.rs b/src/scripts/cat_system/image/hg3.rs index a57b542..ec37da9 100644 --- a/src/scripts/cat_system/image/hg3.rs +++ b/src/scripts/cat_system/image/hg3.rs @@ -31,6 +31,7 @@ impl ScriptBuilder for Hg3ImageBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(Hg3Image::new(data, config)?)) } diff --git a/src/scripts/circus/archive/pck.rs b/src/scripts/circus/archive/pck.rs index 30877fd..7c86dd4 100644 --- a/src/scripts/circus/archive/pck.rs +++ b/src/scripts/circus/archive/pck.rs @@ -34,6 +34,7 @@ impl ScriptBuilder for PckArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(PckArchive::new( MemReader::new(data), @@ -48,6 +49,7 @@ impl ScriptBuilder for PckArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -70,6 +72,7 @@ impl ScriptBuilder for PckArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(PckArchive::new(reader, archive_encoding, config)?)) } @@ -254,43 +257,25 @@ impl Script for PckArchive { true } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Ok(Box::new(self.entries.iter().map(|e| Ok(e.name.clone())))) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(PckArchiveIter { - entries: self.entries.iter(), - reader: self.reader.clone(), - })) + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Ok(Box::new(self.entries.iter().map(|e| Ok(e.offset as u64)))) } -} -fn detect_script_type(_buf: &[u8], _buf_len: usize, _filename: &str) -> Option { - #[cfg(feature = "circus-img")] - if _buf_len >= 4 && _buf.starts_with(b"CRXG") { - return Some(ScriptType::CircusCrx); - } - None -} - -struct PckArchiveIter<'a, T: Iterator, R: Read + Seek> { - entries: T, - reader: Arc>, -} - -impl<'a, T: Iterator, R: Read + Seek + 'static> Iterator - for PckArchiveIter<'a, T, R> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - let entry = match self.entries.next() { - Some(e) => e, - None => return None, - }; + fn open_file<'a>(&'a self, index: usize) -> Result> { + if index >= self.entries.len() { + return Err(anyhow::anyhow!( + "Index out of bounds: {} (max: {})", + index, + self.entries.len() + )); + } + let entry = &self.entries[index]; let mut entry = Entry { header: entry.clone(), reader: self.reader.clone(), @@ -301,19 +286,27 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iterat let readed = match entry.read(&mut buf) { Ok(readed) => readed, Err(e) => { - return Some(Err(anyhow::anyhow!( + return Err(anyhow::anyhow!( "Failed to read entry '{}': {}", entry.header.name, e - ))); + )); } }; entry.pos = 0; entry.script_type = detect_script_type(&buf, readed, &entry.header.name); - Some(Ok(Box::new(entry))) + Ok(Box::new(entry)) } } +fn detect_script_type(_buf: &[u8], _buf_len: usize, _filename: &str) -> Option { + #[cfg(feature = "circus-img")] + if _buf_len >= 4 && _buf.starts_with(b"CRXG") { + return Some(ScriptType::CircusCrx); + } + None +} + pub struct PckArchiveWriter { writer: T, headers: HashMap, diff --git a/src/scripts/circus/image/crx.rs b/src/scripts/circus/image/crx.rs index 9076af6..2315973 100644 --- a/src/scripts/circus/image/crx.rs +++ b/src/scripts/circus/image/crx.rs @@ -94,6 +94,7 @@ impl ScriptBuilder for CrxImageBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(CrxImage::new(MemReader::new(data), config)?)) } diff --git a/src/scripts/circus/script.rs b/src/scripts/circus/script.rs index 6775efc..39bccf6 100644 --- a/src/scripts/circus/script.rs +++ b/src/scripts/circus/script.rs @@ -25,6 +25,7 @@ impl ScriptBuilder for CircusMesScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(CircusMesScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/escude/archive.rs b/src/scripts/escude/archive.rs index f01e0e8..d0df97d 100644 --- a/src/scripts/escude/archive.rs +++ b/src/scripts/escude/archive.rs @@ -7,6 +7,7 @@ use anyhow::Result; use std::collections::HashMap; use std::ffi::CString; use std::io::{Read, Seek, SeekFrom, Write}; +use std::sync::{Arc, Mutex}; #[derive(Debug)] pub struct EscudeBinArchiveBuilder {} @@ -33,6 +34,7 @@ impl ScriptBuilder for EscudeBinArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(EscudeBinArchive::new( MemReader::new(data), @@ -47,6 +49,7 @@ impl ScriptBuilder for EscudeBinArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -73,6 +76,7 @@ impl ScriptBuilder for EscudeBinArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(EscudeBinArchive::new( reader, @@ -158,7 +162,7 @@ impl ArchiveContent for Entry { #[derive(Debug)] pub struct EscudeBinArchive { - reader: T, + reader: Arc>, file_count: u32, entries: Vec, archive_encoding: Encoding, @@ -187,7 +191,7 @@ impl EscudeBinArchive { }); } Ok(EscudeBinArchive { - reader, + reader: Arc::new(Mutex::new(reader)), file_count, entries, archive_encoding, @@ -195,7 +199,7 @@ impl EscudeBinArchive { } } -impl Script for EscudeBinArchive { +impl Script for EscudeBinArchive { fn default_output_script_type(&self) -> OutputScriptType { OutputScriptType::Json } @@ -208,30 +212,56 @@ impl Script for EscudeBinArchive { true } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Ok(Box::new(EscudeBinArchiveIter { entries: self.entries.iter(), - reader: &mut self.reader, + reader: self.reader.clone(), file_count: self.file_count, archive_encoding: self.archive_encoding, })) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(EscudeBinArchiveIterator { - entries: self.entries.iter(), - reader: &mut self.reader, - file_count: self.file_count, - archive_encoding: self.archive_encoding, + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Ok(Box::new( + self.entries.iter().map(|e| Ok(e.data_offset as u64)), + )) + } + + fn open_file<'a>(&'a self, index: usize) -> Result> { + if index >= self.entries.len() { + return Err(anyhow::anyhow!( + "Index out of bounds: {} (max: {})", + index, + self.entries.len() + )); + } + let entry = &self.entries[index]; + let name = self + .reader + .cpeek_cstring_at(entry.name_offset as usize + self.file_count as usize * 12 + 0x14)?; + let name = decode_to_string(self.archive_encoding, name.as_bytes(), true)?; + let mut data = self + .reader + .cpeek_at_vec(entry.data_offset as usize, entry.length as usize)?; + if data.starts_with(b"acp") { + let mut decoder = match super::lzw::LZWDecoder::new(&data) { + Ok(decoder) => decoder, + Err(e) => return Err(anyhow::anyhow!("Failed to create LZW decoder: {}", e)), + }; + data = decoder.unpack()?; + } + Ok(Box::new(Entry { + name, + data: MemReader::new(data), })) } } struct EscudeBinArchiveIter<'a, T: Iterator, R: Read + Seek> { entries: T, - reader: &'a mut R, + reader: Arc>, file_count: u32, archive_encoding: Encoding, } @@ -247,7 +277,7 @@ impl<'a, T: Iterator, R: Read + Seek> Iterator None => return None, }; let name_offset = entry.name_offset as usize + self.file_count as usize * 12 + 0x14; - let name = match self.reader.peek_cstring_at(name_offset) { + let name = match self.reader.cpeek_cstring_at(name_offset) { Ok(name) => name, Err(e) => return Some(Err(e.into())), }; @@ -259,58 +289,6 @@ impl<'a, T: Iterator, R: Read + Seek> Iterator } } -struct EscudeBinArchiveIterator<'a, T: Iterator, R: Read + Seek> { - entries: T, - reader: &'a mut R, - file_count: u32, - archive_encoding: Encoding, -} - -impl<'a, T: Iterator, R: Read + Seek> Iterator - for EscudeBinArchiveIterator<'a, T, R> -{ - 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.archive_encoding, name.as_bytes(), true) { - 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(e)), - }; - } - Some(Ok(Box::new(Entry { - name, - data: MemReader::new(data), - }))) - } -} - pub struct EscudeBinArchiveWriter { writer: T, headers: HashMap, diff --git a/src/scripts/escude/list.rs b/src/scripts/escude/list.rs index 171a50f..6d22d9e 100644 --- a/src/scripts/escude/list.rs +++ b/src/scripts/escude/list.rs @@ -29,6 +29,7 @@ impl ScriptBuilder for EscudeBinListBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(EscudeBinList::new( data, filename, encoding, config, diff --git a/src/scripts/escude/script.rs b/src/scripts/escude/script.rs index a141f26..759c9cf 100644 --- a/src/scripts/escude/script.rs +++ b/src/scripts/escude/script.rs @@ -33,6 +33,7 @@ impl ScriptBuilder for EscudeBinScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(EscudeBinScript::new(data, encoding, config)?)) } diff --git a/src/scripts/hexen_haus/bin.rs b/src/scripts/hexen_haus/bin.rs index 9b9d534..9e12bf1 100644 --- a/src/scripts/hexen_haus/bin.rs +++ b/src/scripts/hexen_haus/bin.rs @@ -27,6 +27,7 @@ impl ScriptBuilder for BinScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(BinScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/kirikiri/image/dref.rs b/src/scripts/kirikiri/image/dref.rs index 5f20cdd..2c18371 100644 --- a/src/scripts/kirikiri/image/dref.rs +++ b/src/scripts/kirikiri/image/dref.rs @@ -32,8 +32,11 @@ impl ScriptBuilder for DrefBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + archive: Option<&Box>, ) -> Result> { - Ok(Box::new(Dref::new(buf, encoding, filename, config)?)) + Ok(Box::new(Dref::new( + buf, encoding, filename, config, archive, + )?)) } fn extensions(&self) -> &'static [&'static str] { @@ -71,6 +74,16 @@ impl Dpak { Ok(Self { psb }) } + pub fn load_from_data(data: &[u8]) -> Result { + let mut psb = PsbReader::open_psb(MemReaderRef::new(data)) + .map_err(|e| anyhow::anyhow!("Failed to read PSB from DPAK data: {:?}", e))?; + let psb = psb + .load() + .map_err(|e| anyhow::anyhow!("Failed to load PSB from DPAK data: {:?}", e))?; + let psb = psb.to_psb_fixed(); + Ok(Self { psb }) + } + pub fn load_image(&self, name: &str) -> Result<(ImageData, Option)> { let root = self.psb.root(); let rid = root[name] @@ -149,12 +162,31 @@ impl DpakLoader { }; dpak.load_image(filename) } + + pub fn load_archives(&mut self, in_archives: &HashMap>) -> Result<()> { + for (name, data) in in_archives.iter() { + if !self.map.contains_key(name) { + let dpak = Dpak::load_from_data(data)?; + self.map.insert(name.clone(), dpak); + } + } + Ok(()) + } } -#[derive(Debug)] pub struct Dref { urls: Vec, dir: PathBuf, + in_archives: HashMap>, +} + +impl std::fmt::Debug for Dref { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Dref") + .field("urls", &self.urls) + .field("dir", &self.dir) + .finish() + } } impl Dref { @@ -163,6 +195,7 @@ impl Dref { encoding: Encoding, filename: &str, _config: &ExtraConfig, + archive: Option<&Box>, ) -> Result { let text = decode_with_bom_detect(encoding, &buf, true)?.0; let mut urls = Vec::new(); @@ -190,7 +223,25 @@ impl Dref { )); } } - Ok(Self { urls, dir }) + let mut in_archives = HashMap::new(); + if let Some(archive) = archive { + if archive.is_archive() { + for url in urls.iter() { + let filename = url.domain().ok_or(anyhow::anyhow!( + "Invalid URL in DREF file: {} (missing domain)", + url + ))?; + if let Ok(mut content) = archive.open_file_by_name(filename, true) { + in_archives.insert(filename.to_string(), content.data()?); + } + } + } + } + Ok(Self { + urls, + dir, + in_archives, + }) } } @@ -209,6 +260,7 @@ impl Script for Dref { fn export_image(&self) -> Result { let mut loader = DpakLoader::default(); + loader.load_archives(&self.in_archives)?; let base_url = &self.urls[0]; let dpak = base_url.domain().ok_or(anyhow::anyhow!( "Invalid URL in DREF file: {} (missing domain)", diff --git a/src/scripts/kirikiri/image/pimg.rs b/src/scripts/kirikiri/image/pimg.rs index 975967a..5449acb 100644 --- a/src/scripts/kirikiri/image/pimg.rs +++ b/src/scripts/kirikiri/image/pimg.rs @@ -32,6 +32,7 @@ impl ScriptBuilder for PImgBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(PImg::new(MemReader::new(buf), filename, config)?)) } @@ -42,6 +43,7 @@ impl ScriptBuilder for PImgBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -60,6 +62,7 @@ impl ScriptBuilder for PImgBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(PImg::new(reader, filename, config)?)) } diff --git a/src/scripts/kirikiri/image/tlg.rs b/src/scripts/kirikiri/image/tlg.rs index 430812e..67ddf55 100644 --- a/src/scripts/kirikiri/image/tlg.rs +++ b/src/scripts/kirikiri/image/tlg.rs @@ -26,6 +26,7 @@ impl ScriptBuilder for TlgImageBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(TlgImage::new(MemReader::new(data), config)?)) } diff --git a/src/scripts/kirikiri/ks.rs b/src/scripts/kirikiri/ks.rs index 4c1a1dc..364d7bf 100644 --- a/src/scripts/kirikiri/ks.rs +++ b/src/scripts/kirikiri/ks.rs @@ -31,6 +31,7 @@ impl ScriptBuilder for KsBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(KsScript::new(buf, encoding, config)?)) } diff --git a/src/scripts/kirikiri/mdf.rs b/src/scripts/kirikiri/mdf.rs index 295785a..e4ebfd1 100644 --- a/src/scripts/kirikiri/mdf.rs +++ b/src/scripts/kirikiri/mdf.rs @@ -25,6 +25,7 @@ impl ScriptBuilder for MdfBuilder { _encoding: Encoding, _archive_encoding: Encoding, _config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(Mdf::new(buf, filename)?)) } diff --git a/src/scripts/kirikiri/scn.rs b/src/scripts/kirikiri/scn.rs index ff22937..7041cd5 100644 --- a/src/scripts/kirikiri/scn.rs +++ b/src/scripts/kirikiri/scn.rs @@ -32,6 +32,7 @@ impl ScriptBuilder for ScnScriptBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(ScnScript::new( MemReader::new(buf), @@ -46,6 +47,7 @@ impl ScriptBuilder for ScnScriptBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -68,6 +70,7 @@ impl ScriptBuilder for ScnScriptBuilder { _encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(ScnScript::new(reader, filename, config)?)) } diff --git a/src/scripts/kirikiri/simple_crypt.rs b/src/scripts/kirikiri/simple_crypt.rs index 39364a9..5a7d234 100644 --- a/src/scripts/kirikiri/simple_crypt.rs +++ b/src/scripts/kirikiri/simple_crypt.rs @@ -26,6 +26,7 @@ impl ScriptBuilder for SimpleCryptBuilder { _encoding: Encoding, _archive_encoding: Encoding, _config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(SimpleCrypt::new(buf, filename)?)) } diff --git a/src/scripts/will_plus/ws2.rs b/src/scripts/will_plus/ws2.rs index e80ed63..55818c8 100644 --- a/src/scripts/will_plus/ws2.rs +++ b/src/scripts/will_plus/ws2.rs @@ -27,6 +27,7 @@ impl ScriptBuilder for Ws2ScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(Ws2Script::new(buf, encoding, config, false)?)) } diff --git a/src/scripts/yaneurao/itufuru/archive.rs b/src/scripts/yaneurao/itufuru/archive.rs index 1752cdf..86103ba 100644 --- a/src/scripts/yaneurao/itufuru/archive.rs +++ b/src/scripts/yaneurao/itufuru/archive.rs @@ -8,6 +8,7 @@ use anyhow::Result; use msg_tool_macro::*; use std::collections::HashMap; use std::io::{Read, Seek, SeekFrom, Write}; +use std::sync::{Arc, Mutex}; #[derive(Debug)] pub struct ItufuruArchiveBuilder {} @@ -34,6 +35,7 @@ impl ScriptBuilder for ItufuruArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(ItufuruArchive::new( MemReader::new(data), @@ -48,6 +50,7 @@ impl ScriptBuilder for ItufuruArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { if filename == "-" { let data = crate::utils::files::read_file(filename)?; @@ -74,6 +77,7 @@ impl ScriptBuilder for ItufuruArchiveBuilder { _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(ItufuruArchive::new( reader, @@ -163,7 +167,7 @@ impl ArchiveContent for Entry { #[derive(Debug)] pub struct ItufuruArchive { - reader: Crypto, + reader: Arc>>, first_file_offset: u32, files: Vec, } @@ -203,14 +207,14 @@ impl ItufuruArchive { files.push(file); } Ok(ItufuruArchive { - reader, + reader: Arc::new(Mutex::new(reader)), first_file_offset, files, }) } } -impl Script for ItufuruArchive { +impl Script for ItufuruArchive { fn default_output_script_type(&self) -> OutputScriptType { OutputScriptType::Json } @@ -223,56 +227,44 @@ impl Script for ItufuruArchive { true } - fn iter_archive<'a>(&'a mut self) -> Result> + 'a>> { + fn iter_archive_filename<'a>( + &'a self, + ) -> Result> + 'a>> { Ok(Box::new( self.files.iter().map(|s| Ok(s.file_name.to_owned())), )) } - fn iter_archive_mut<'a>( - &'a mut self, - ) -> Result>> + 'a>> { - Ok(Box::new(ItufuruArchiveIter { - entries: self.files.iter(), - reader: &mut self.reader, - first_file_offset: self.first_file_offset, - })) + fn iter_archive_offset<'a>(&'a self) -> Result> + 'a>> { + Ok(Box::new(self.files.iter().map(|s| Ok(s.offset as u64)))) } -} -struct ItufuruArchiveIter<'a, T: Iterator, R: Read + Seek> { - entries: T, - reader: &'a mut R, - first_file_offset: u32, -} - -impl<'a, T: Iterator, R: Read + Seek> Iterator - for ItufuruArchiveIter<'a, T, R> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - if let Some(entry) = self.entries.next() { - let file_offset = entry.offset as usize; - match self.reader.peek_exact_at_vec( - file_offset + self.first_file_offset as usize, - entry.size as usize, - ) { - Ok(data) => { - let name = entry.file_name.clone(); - Some(Ok(Box::new(Entry { - name, - data: MemReader::new(data), - }))) - } - Err(e) => Some(Err(anyhow::anyhow!( - "Failed to read file {}: {}", - entry.file_name, - e - ))), + fn open_file<'a>(&'a self, index: usize) -> Result> { + if index >= self.files.len() { + return Err(anyhow::anyhow!( + "Index out of bounds: {} (max: {})", + index, + self.files.len() + )); + } + let entry = &self.files[index]; + let file_offset = entry.offset as usize; + match self.reader.cpeek_exact_at_vec( + file_offset + self.first_file_offset as usize, + entry.size as usize, + ) { + Ok(data) => { + let name = entry.file_name.clone(); + Ok(Box::new(Entry { + name, + data: MemReader::new(data), + })) } - } else { - None + Err(e) => Err(anyhow::anyhow!( + "Failed to read file {}: {}", + entry.file_name, + e + )), } } } diff --git a/src/scripts/yaneurao/itufuru/script.rs b/src/scripts/yaneurao/itufuru/script.rs index 9a1c2dd..cda92dc 100644 --- a/src/scripts/yaneurao/itufuru/script.rs +++ b/src/scripts/yaneurao/itufuru/script.rs @@ -25,6 +25,7 @@ impl ScriptBuilder for ItufuruScriptBuilder { encoding: Encoding, _archive_encoding: Encoding, config: &ExtraConfig, + _archive: Option<&Box>, ) -> Result> { Ok(Box::new(ItufuruScript::new(data, encoding, config)?)) }