mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-07-08 01:31:53 +08:00
Add support to export multiple images
This commit is contained in:
94
src/main.rs
94
src/main.rs
@@ -366,6 +366,46 @@ pub fn export_script(
|
|||||||
let (script_file, _) = parse_script_from_archive(&mut f, arg, config)?;
|
let (script_file, _) = parse_script_from_archive(&mut f, arg, config)?;
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
if script_file.is_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() {
|
let img_data = match script_file.export_image() {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -557,6 +597,60 @@ pub fn export_script(
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
if script.is_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 img_data = script.export_image()?;
|
||||||
let out_type = arg.image_type.unwrap_or(types::ImageOutputType::Png);
|
let out_type = arg.image_type.unwrap_or(types::ImageOutputType::Png);
|
||||||
let f = if filename == "-" {
|
let f = if filename == "-" {
|
||||||
|
|||||||
@@ -282,6 +282,42 @@ pub trait Script: std::fmt::Debug {
|
|||||||
let f = std::io::BufWriter::new(f);
|
let f = std::io::BufWriter::new(f);
|
||||||
self.import_image(data, Box::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<Box<dyn Iterator<Item = Result<ImageDataWithName>> + '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<ImageDataWithName>,
|
||||||
|
_file: Box<dyn WriteSeek + 'a>,
|
||||||
|
) -> 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<ImageDataWithName>,
|
||||||
|
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 {
|
pub trait Archive {
|
||||||
|
|||||||
@@ -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")]
|
#[cfg(feature = "cat-system-img")]
|
||||||
if _buf_len >= 4 && _buf.starts_with(b"HG-3") {
|
if _buf_len >= 4 && _buf.starts_with(b"HG-3") {
|
||||||
return Some(&ScriptType::CatSystemHg3);
|
return Some(&ScriptType::CatSystemHg3);
|
||||||
|
|||||||
@@ -149,6 +149,69 @@ impl Script for Hg3Image {
|
|||||||
}
|
}
|
||||||
Ok(img)
|
Ok(img)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_multi_image(&self) -> bool {
|
||||||
|
self.entries.len() > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export_multi_image<'a>(
|
||||||
|
&'a self,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = Result<ImageDataWithName>> + '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<Item = &'a (Hg3Entry, usize, usize)> + 'a> {
|
||||||
|
iter: T,
|
||||||
|
index: usize,
|
||||||
|
data: MemReaderRef<'a>,
|
||||||
|
draw_canvas: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Iterator<Item = &'a (Hg3Entry, usize, usize)> + 'a> Iterator for Hg3ImageIter<'a, T> {
|
||||||
|
type Item = Result<ImageDataWithName>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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> {
|
pub struct Hg3Reader<'a> {
|
||||||
|
|||||||
@@ -375,3 +375,10 @@ pub struct ImageData {
|
|||||||
pub depth: u8,
|
pub depth: u8,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ImageDataWithName {
|
||||||
|
pub name: String,
|
||||||
|
pub data: ImageData,
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user