From ebcce0b741c26fe15988b49ff0ac3b2c37231aa7 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sun, 29 Jun 2025 19:25:55 +0800 Subject: [PATCH] Add support to export multiple images --- src/main.rs | 94 +++++++++++++++++++++++++++ src/scripts/base.rs | 36 ++++++++++ src/scripts/cat_system/archive/int.rs | 6 +- src/scripts/cat_system/image/hg3.rs | 63 ++++++++++++++++++ src/types.rs | 7 ++ 5 files changed, 205 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index b01c622..efe1638 100644 --- a/src/main.rs +++ b/src/main.rs @@ -366,6 +366,46 @@ pub fn export_script( let (script_file, _) = parse_script_from_archive(&mut f, arg, config)?; #[cfg(feature = "image")] if script_file.is_image() { + if script_file.is_multi_image() { + for i in script_file.export_multi_image()? { + let img_data = match i { + 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_path.push(img_data.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; + } + } + utils::img::encode_img( + img_data.data, + out_type, + &out_path.to_string_lossy(), + )?; + COUNTER.inc(types::ScriptResult::Ok); + } + COUNTER.inc(types::ScriptResult::Ok); + continue; + } let img_data = match script_file.export_image() { Ok(data) => data, Err(e) => { @@ -557,6 +597,60 @@ pub fn export_script( } #[cfg(feature = "image")] if script.is_image() { + if script.is_multi_image() { + for i in script.export_multi_image()? { + let img_data = match i { + 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 f = match output.as_ref() { + Some(output) => { + if is_dir { + let f = std::path::PathBuf::from(filename); + let mut pb = std::path::PathBuf::from(output); + if let Some(fname) = f.file_name() { + pb.push(fname); + pb.set_extension(""); + } + pb.push(img_data.name); + pb.set_extension(out_type.as_ref()); + pb.to_string_lossy().into_owned() + } else { + let mut pb = std::path::PathBuf::from(output); + pb.push(img_data.name); + pb.set_extension(out_type.as_ref()); + pb.to_string_lossy().into_owned() + } + } + None => { + let mut pb = std::path::PathBuf::from(filename); + pb.set_extension(""); + pb.push(img_data.name); + pb.set_extension(out_type.as_ref()); + pb.to_string_lossy().into_owned() + } + }; + match utils::files::make_sure_dir_exists(&f) { + Ok(_) => {} + Err(e) => { + eprintln!("Error creating parent directory for {}: {}", f, e); + COUNTER.inc_error(); + continue; + } + } + utils::img::encode_img(img_data.data, out_type, &f)?; + COUNTER.inc(types::ScriptResult::Ok); + } + return Ok(types::ScriptResult::Ok); + } let img_data = script.export_image()?; let out_type = arg.image_type.unwrap_or(types::ImageOutputType::Png); let f = if filename == "-" { diff --git a/src/scripts/base.rs b/src/scripts/base.rs index 80a2524..29c870c 100644 --- a/src/scripts/base.rs +++ b/src/scripts/base.rs @@ -282,6 +282,42 @@ pub trait Script: std::fmt::Debug { let f = std::io::BufWriter::new(f); self.import_image(data, Box::new(f)) } + + #[cfg(feature = "image")] + fn is_multi_image(&self) -> bool { + false + } + + #[cfg(feature = "image")] + fn export_multi_image<'a>( + &'a self, + ) -> Result> + 'a>> { + Err(anyhow::anyhow!( + "This script type does not support to export multi image." + )) + } + + #[cfg(feature = "image")] + fn import_multi_image<'a>( + &'a self, + _data: Vec, + _file: Box, + ) -> Result<()> { + Err(anyhow::anyhow!( + "This script type does not support to import multi image." + )) + } + + #[cfg(feature = "image")] + fn import_multi_image_filename( + &self, + data: Vec, + filename: &str, + ) -> Result<()> { + let f = std::fs::File::create(filename)?; + let f = std::io::BufWriter::new(f); + self.import_multi_image(data, Box::new(f)) + } } pub trait Archive { diff --git a/src/scripts/cat_system/archive/int.rs b/src/scripts/cat_system/archive/int.rs index 8210389..ad3047c 100644 --- a/src/scripts/cat_system/archive/int.rs +++ b/src/scripts/cat_system/archive/int.rs @@ -107,7 +107,11 @@ impl ScriptBuilder for CSIntArcBuilder { } } -fn detect_script_type(_buf: &[u8], _buf_len: usize, _filename: &str) -> Option<&'static ScriptType> { +fn detect_script_type( + _buf: &[u8], + _buf_len: usize, + _filename: &str, +) -> Option<&'static ScriptType> { #[cfg(feature = "cat-system-img")] if _buf_len >= 4 && _buf.starts_with(b"HG-3") { return Some(&ScriptType::CatSystemHg3); diff --git a/src/scripts/cat_system/image/hg3.rs b/src/scripts/cat_system/image/hg3.rs index 00767ff..12a65b2 100644 --- a/src/scripts/cat_system/image/hg3.rs +++ b/src/scripts/cat_system/image/hg3.rs @@ -149,6 +149,69 @@ impl Script for Hg3Image { } Ok(img) } + + fn is_multi_image(&self) -> bool { + self.entries.len() > 1 + } + + fn export_multi_image<'a>( + &'a self, + ) -> Result> + 'a>> { + Ok(Box::new(Hg3ImageIter { + iter: self.entries.iter(), + index: 0, + data: self.data.to_ref(), + draw_canvas: self.draw_canvas, + })) + } +} + +struct Hg3ImageIter<'a, T: Iterator + 'a> { + iter: T, + index: usize, + data: MemReaderRef<'a>, + draw_canvas: bool, +} + +impl<'a, T: Iterator + 'a> Iterator for Hg3ImageIter<'a, T> { + type Item = Result; + + fn next(&mut self) -> Option { + if let Some((entry, offset, size)) = self.iter.next() { + let data = &self.data.data[*offset..*offset + *size]; + let reader = Hg3Reader { + m_input: MemReaderRef::new(data), + m_info: entry.clone(), + m_pixel_size: entry.bpp / 8, + }; + self.index += 1; + match reader.unpack() { + Ok(mut img) => { + if self.draw_canvas { + if entry.canvas_width > 0 && entry.canvas_height > 0 { + img = match draw_on_canvas( + img, + entry.canvas_width, + entry.canvas_height, + entry.offset_x, + entry.offset_y, + ) { + Ok(canvas_img) => canvas_img, + Err(e) => return Some(Err(e)), + }; + } + } + Some(Ok(ImageDataWithName { + name: format!("{:04}", self.index - 1), + data: img, + })) + }, + Err(e) => Some(Err(e)), + } + } else { + None + } + } } pub struct Hg3Reader<'a> { diff --git a/src/types.rs b/src/types.rs index d518b00..e2f56c6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -375,3 +375,10 @@ pub struct ImageData { pub depth: u8, pub data: Vec, } + +#[cfg(feature = "image")] +#[derive(Clone, Debug)] +pub struct ImageDataWithName { + pub name: String, + pub data: ImageData, +}