diff --git a/src/args.rs b/src/args.rs index c580ae0..90656e7 100644 --- a/src/args.rs +++ b/src/args.rs @@ -93,6 +93,10 @@ pub struct Arg { /// Disable appending new strings to the end of BGI scripts. /// Disable may cause BGI scripts broken. pub bgi_disable_append: bool, + #[cfg(all(feature = "bgi-arc", feature = "bgi-img"))] + #[arg(long, global = true)] + /// Detect all files in BGI archive as SysGrp Images. By default, only files which name is `sysgrp.arc` will enabled this. + pub bgi_is_sysgrp_arc: Option, #[command(subcommand)] /// Command pub command: Command, diff --git a/src/main.rs b/src/main.rs index 7accde9..e1cad41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -364,6 +364,45 @@ pub fn export_script( let mut f = f?; if f.is_script() { let (script_file, _) = parse_script_from_archive(&mut f, arg, config)?; + #[cfg(feature = "image")] + if script_file.is_image() { + let img_data = match script_file.export_image() { + Ok(data) => data, + Err(e) => { + eprintln!("Error exporting image: {}", e); + COUNTER.inc_error(); + if arg.backtrace { + eprintln!("Backtrace: {}", e.backtrace()); + } + continue; + } + }; + let out_type = arg.image_type.unwrap_or(types::ImageOutputType::Png); + let mut out_path = std::path::PathBuf::from(&odir).join(f.name()); + out_path.set_extension(out_type.as_ref()); + match utils::files::make_sure_dir_exists(&out_path) { + Ok(_) => {} + Err(e) => { + eprintln!( + "Error creating parent directory for {}: {}", + out_path.display(), + e + ); + COUNTER.inc_error(); + continue; + } + } + match utils::img::encode_img(img_data, out_type, &out_path.to_string_lossy()) { + Ok(_) => {} + Err(e) => { + eprintln!("Error encoding image: {}", e); + COUNTER.inc_error(); + continue; + } + } + COUNTER.inc(types::ScriptResult::Ok); + continue; + } let mut of = match &arg.output_type { Some(t) => t.clone(), None => script_file.default_output_script_type(), @@ -1155,6 +1194,8 @@ fn main() { bgi_disable_append: arg.bgi_disable_append, #[cfg(feature = "image")] image_type: arg.image_type.clone(), + #[cfg(all(feature = "bgi-arc", feature = "bgi-img"))] + bgi_is_sysgrp_arc: arg.bgi_is_sysgrp_arc.clone(), }; match &arg.command { args::Command::Export { input, output } => { diff --git a/src/scripts/bgi/archive/v1.rs b/src/scripts/bgi/archive/v1.rs index 1f28392..4a475ff 100644 --- a/src/scripts/bgi/archive/v1.rs +++ b/src/scripts/bgi/archive/v1.rs @@ -31,7 +31,7 @@ impl ScriptBuilder for BgiArchiveBuilder { fn build_script( &self, data: Vec, - _filename: &str, + filename: &str, _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, @@ -40,39 +40,51 @@ impl ScriptBuilder for BgiArchiveBuilder { MemReader::new(data), archive_encoding, config, + filename, )?)) } fn build_script_from_file( &self, - _filename: &str, + filename: &str, _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, ) -> Result> { - if _filename == "-" { - let data = crate::utils::files::read_file(_filename)?; + if filename == "-" { + let data = crate::utils::files::read_file(filename)?; Ok(Box::new(BgiArchive::new( MemReader::new(data), archive_encoding, config, + filename, )?)) } else { - let f = std::fs::File::open(_filename)?; + let f = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(f); - Ok(Box::new(BgiArchive::new(reader, archive_encoding, config)?)) + Ok(Box::new(BgiArchive::new( + reader, + archive_encoding, + config, + filename, + )?)) } } fn build_script_from_reader( &self, reader: Box, - _filename: &str, + filename: &str, _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, ) -> Result> { - Ok(Box::new(BgiArchive::new(reader, archive_encoding, config)?)) + Ok(Box::new(BgiArchive::new( + reader, + archive_encoding, + config, + filename, + )?)) } fn extensions(&self) -> &'static [&'static str] { @@ -189,10 +201,17 @@ pub struct BgiArchive { reader: Arc>, file_count: u32, entries: Vec, + #[cfg(feature = "bgi-img")] + is_sysgrp_arc: bool, } impl BgiArchive { - pub fn new(mut reader: T, archive_encoding: Encoding, _config: &ExtraConfig) -> Result { + pub fn new( + mut reader: T, + archive_encoding: Encoding, + _config: &ExtraConfig, + _filename: &str, + ) -> Result { let mut header = [0u8; 12]; reader.read_exact(&mut header)?; if !header.starts_with(b"PackFile ") { @@ -206,10 +225,20 @@ impl BgiArchive { entries.push(entry); } + #[cfg(feature = "bgi-img")] + let is_sysgrp_arc = _config.bgi_is_sysgrp_arc.unwrap_or_else(|| { + std::path::Path::new(&_filename.to_lowercase()) + .file_name() + .map(|f| f == "sysgrp.arc") + .unwrap_or(false) + }); + Ok(BgiArchive { reader: Arc::new(Mutex::new(reader)), file_count, entries, + #[cfg(feature = "bgi-img")] + is_sysgrp_arc, }) } } @@ -240,28 +269,31 @@ impl Script for BgiArchive { entries: self.entries.iter(), reader: self.reader.clone(), base_offset: 16 + (self.file_count as u64 * 32), + #[cfg(feature = "bgi-img")] + is_sysgrp_arc: self.is_sysgrp_arc, })) } } -struct MemEntry { +struct MemEntry Option<&'static ScriptType>> { name: String, data: MemReader, + detect: F, } -impl Read for MemEntry { +impl Option<&'static ScriptType>> Read for MemEntry { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.data.read(buf) } } -impl ArchiveContent for MemEntry { +impl Option<&'static ScriptType>> ArchiveContent for MemEntry { fn name(&self) -> &str { &self.name } fn script_type(&self) -> Option<&ScriptType> { - detect_script_type(&self.data.data, self.data.data.len(), &self.name) + (self.detect)(&self.data.data, self.data.data.len(), &self.name) } fn data(&mut self) -> Result> { @@ -286,10 +318,21 @@ fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option<&'st None } +#[cfg(feature = "bgi-img")] +fn detect_script_type_sysgrp( + _buf: &[u8], + _buf_len: usize, + _filename: &str, +) -> Option<&'static ScriptType> { + 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 @@ -371,11 +414,27 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iterat 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(); - match BseReader::new(entry, detect_script_type, &filename) { + #[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(bse_reader) => { return Some(Ok(Box::new(bse_reader))); } @@ -388,7 +447,18 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iterat } }; } - entry.script_type = detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); + #[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))) } } diff --git a/src/scripts/bgi/archive/v2.rs b/src/scripts/bgi/archive/v2.rs index 5c61fe2..50fcef0 100644 --- a/src/scripts/bgi/archive/v2.rs +++ b/src/scripts/bgi/archive/v2.rs @@ -31,7 +31,7 @@ impl ScriptBuilder for BgiArchiveBuilder { fn build_script( &self, data: Vec, - _filename: &str, + filename: &str, _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, @@ -40,39 +40,51 @@ impl ScriptBuilder for BgiArchiveBuilder { MemReader::new(data), archive_encoding, config, + filename, )?)) } fn build_script_from_file( &self, - _filename: &str, + filename: &str, _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, ) -> Result> { - if _filename == "-" { - let data = crate::utils::files::read_file(_filename)?; + if filename == "-" { + let data = crate::utils::files::read_file(filename)?; Ok(Box::new(BgiArchive::new( MemReader::new(data), archive_encoding, config, + filename, )?)) } else { - let f = std::fs::File::open(_filename)?; + let f = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(f); - Ok(Box::new(BgiArchive::new(reader, archive_encoding, config)?)) + Ok(Box::new(BgiArchive::new( + reader, + archive_encoding, + config, + filename, + )?)) } } fn build_script_from_reader( &self, reader: Box, - _filename: &str, + filename: &str, _encoding: Encoding, archive_encoding: Encoding, config: &ExtraConfig, ) -> Result> { - Ok(Box::new(BgiArchive::new(reader, archive_encoding, config)?)) + Ok(Box::new(BgiArchive::new( + reader, + archive_encoding, + config, + filename, + )?)) } fn extensions(&self) -> &'static [&'static str] { @@ -186,24 +198,25 @@ impl Seek for Entry { } } -struct MemEntry { +struct MemEntry Option<&'static ScriptType>> { name: String, data: MemReader, + detect: F, } -impl Read for MemEntry { +impl Option<&'static ScriptType>> Read for MemEntry { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.data.read(buf) } } -impl ArchiveContent for MemEntry { +impl Option<&'static ScriptType>> ArchiveContent for MemEntry { fn name(&self) -> &str { &self.name } fn script_type(&self) -> Option<&ScriptType> { - detect_script_type(&self.data.data, self.data.data.len(), &self.name) + (self.detect)(&self.data.data, self.data.data.len(), &self.name) } fn data(&mut self) -> Result> { @@ -220,10 +233,17 @@ pub struct BgiArchive { reader: Arc>, file_count: u32, entries: Vec, + #[cfg(feature = "bgi-img")] + is_sysgrp_arc: bool, } impl BgiArchive { - pub fn new(mut reader: T, archive_encoding: Encoding, _config: &ExtraConfig) -> Result { + pub fn new( + mut reader: T, + archive_encoding: Encoding, + _config: &ExtraConfig, + _filename: &str, + ) -> Result { let mut header = [0u8; 12]; reader.read_exact(&mut header)?; if !header.starts_with(b"BURIKO ARC20") { @@ -237,10 +257,20 @@ impl BgiArchive { entries.push(entry); } + #[cfg(feature = "bgi-img")] + let is_sysgrp_arc = _config.bgi_is_sysgrp_arc.unwrap_or_else(|| { + std::path::Path::new(&_filename.to_lowercase()) + .file_name() + .map(|f| f == "sysgrp.arc") + .unwrap_or(false) + }); + Ok(BgiArchive { reader: Arc::new(Mutex::new(reader)), file_count, entries, + #[cfg(feature = "bgi-img")] + is_sysgrp_arc, }) } } @@ -271,6 +301,8 @@ impl Script for BgiArchive { entries: self.entries.iter(), reader: self.reader.clone(), base_offset: 16 + (self.file_count as u64 * 0x80), + #[cfg(feature = "bgi-img")] + is_sysgrp_arc: self.is_sysgrp_arc, })) } } @@ -288,10 +320,21 @@ fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option<&'st None } +#[cfg(feature = "bgi-img")] +fn detect_script_type_sysgrp( + _buf: &[u8], + _buf_len: usize, + _filename: &str, +) -> Option<&'static ScriptType> { + 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 @@ -373,11 +416,27 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iterat 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(); - match BseReader::new(entry, detect_script_type, &filename) { + #[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(bse_reader) => { return Some(Ok(Box::new(bse_reader))); } @@ -390,7 +449,18 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iterat } }; } - entry.script_type = detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); + #[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))) } } diff --git a/src/types.rs b/src/types.rs index 5c7d655..b0ddb61 100644 --- a/src/types.rs +++ b/src/types.rs @@ -199,6 +199,8 @@ pub struct ExtraConfig { pub bgi_disable_append: bool, #[cfg(feature = "image")] pub image_type: Option, + #[cfg(all(feature = "bgi-arc", feature = "bgi-img"))] + pub bgi_is_sysgrp_arc: Option, } #[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]