mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-07 13:28:47 +08:00
Add CRXD support
This commit is contained in:
@@ -282,5 +282,9 @@ fn detect_script_type(_buf: &[u8], _buf_len: usize, _filename: &str) -> Option<S
|
||||
if _buf_len >= 4 && _buf.starts_with(b"CRXG") {
|
||||
return Some(ScriptType::CircusCrx);
|
||||
}
|
||||
#[cfg(feature = "circus-img")]
|
||||
if _buf_len >= 4 && _buf.starts_with(b"CRXD") {
|
||||
return Some(ScriptType::CircusCrxd);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use anyhow::Result;
|
||||
use clap::ValueEnum;
|
||||
use clap::builder::PossibleValue;
|
||||
use msg_tool_macro::*;
|
||||
use overf::wrapping;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -135,12 +136,12 @@ impl ScriptBuilder for CrxImageBuilder {
|
||||
#[derive(Clone, Debug, StructPack, StructUnpack)]
|
||||
struct Clip {
|
||||
field_0: u32,
|
||||
img_width: u16,
|
||||
img_height: u16,
|
||||
clip_offset_x: u16,
|
||||
clip_offset_y: u16,
|
||||
clip_width: u16,
|
||||
clip_height: u16,
|
||||
field_8: u16,
|
||||
field_a: u16,
|
||||
width: u16,
|
||||
height: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, StructPack, StructUnpack)]
|
||||
@@ -232,6 +233,43 @@ impl CrxImage {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn draw_diff(&self, diff: &Self) -> Result<ImageData> {
|
||||
let base_header = &self.header;
|
||||
let diff_header = &diff.header;
|
||||
let (img_width, img_height) =
|
||||
if base_header.clips.is_empty() && diff_header.clips.is_empty() {
|
||||
(
|
||||
(base_header.width + base_header.inner_x)
|
||||
.max(diff_header.width + diff_header.inner_x),
|
||||
(base_header.height + base_header.inner_y)
|
||||
.max(diff_header.height + diff_header.inner_y),
|
||||
)
|
||||
} else {
|
||||
if base_header.clips.is_empty() {
|
||||
let clip = &diff_header.clips[0];
|
||||
(clip.img_width, clip.img_height)
|
||||
} else {
|
||||
let clip = &base_header.clips[0];
|
||||
(clip.img_width, clip.img_height)
|
||||
}
|
||||
};
|
||||
let base = self.export_image()?;
|
||||
let mut nw = draw_on_canvas(
|
||||
base,
|
||||
img_width as u32,
|
||||
img_height as u32,
|
||||
base_header.inner_x as u32,
|
||||
base_header.inner_y as u32,
|
||||
)?;
|
||||
draw_on_img(
|
||||
&mut nw,
|
||||
&diff.export_image()?,
|
||||
diff_header.inner_x as u32,
|
||||
diff_header.inner_y as u32,
|
||||
)?;
|
||||
Ok(nw)
|
||||
}
|
||||
|
||||
fn decode_row0(
|
||||
dst: &mut Vec<u8>,
|
||||
mut dst_p: usize,
|
||||
@@ -907,3 +945,55 @@ impl Script for CrxImage {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
|
||||
if base.color_type != diff.color_type {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Color types do not match: {:?} vs {:?}",
|
||||
base.color_type,
|
||||
diff.color_type
|
||||
));
|
||||
}
|
||||
let bpp = base.color_type.bpp(1) as usize;
|
||||
let base_stride = base.width as usize * bpp;
|
||||
let diff_stride = diff.width as usize * bpp;
|
||||
|
||||
for y in 0..diff.height {
|
||||
let base_y = top + y;
|
||||
if base_y >= base.height {
|
||||
continue; // Skip if the base image is not tall enough
|
||||
}
|
||||
|
||||
for x in 0..diff.width {
|
||||
let base_x = left + x;
|
||||
if base_x >= base.width {
|
||||
continue; // Skip if the base image is not wide enough
|
||||
}
|
||||
|
||||
let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
|
||||
let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
|
||||
|
||||
let diff_pixel = &diff.data[diff_index..diff_index + bpp];
|
||||
let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
|
||||
let mut b = base_pixel_orig[0];
|
||||
let mut g = base_pixel_orig[1];
|
||||
let mut r = base_pixel_orig[2];
|
||||
wrapping! {
|
||||
b += diff_pixel[0];
|
||||
g += diff_pixel[1];
|
||||
r += diff_pixel[2];
|
||||
}
|
||||
base.data[base_index] = b;
|
||||
base.data[base_index + 1] = g;
|
||||
base.data[base_index + 2] = r;
|
||||
if bpp == 4 {
|
||||
let mut a = base_pixel_orig[3];
|
||||
wrapping! {
|
||||
a -= diff_pixel[3];
|
||||
}
|
||||
base.data[base_index + 3] = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
163
src/scripts/circus/image/crxd.rs
Normal file
163
src/scripts/circus/image/crxd.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
use super::crx::CrxImage;
|
||||
use crate::ext::io::*;
|
||||
use crate::scripts::base::*;
|
||||
use crate::types::*;
|
||||
use anyhow::Result;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CrxdImageBuilder {}
|
||||
|
||||
impl CrxdImageBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptBuilder for CrxdImageBuilder {
|
||||
fn default_encoding(&self) -> Encoding {
|
||||
Encoding::Cp932
|
||||
}
|
||||
|
||||
fn build_script(
|
||||
&self,
|
||||
data: Vec<u8>,
|
||||
filename: &str,
|
||||
encoding: Encoding,
|
||||
_archive_encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
archive: Option<&Box<dyn Script>>,
|
||||
) -> Result<Box<dyn Script>> {
|
||||
Ok(Box::new(CrxdImage::new(
|
||||
MemReader::new(data),
|
||||
filename,
|
||||
encoding,
|
||||
config,
|
||||
archive,
|
||||
)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
&["crx"]
|
||||
}
|
||||
|
||||
fn script_type(&self) -> &'static ScriptType {
|
||||
&ScriptType::CircusCrxd
|
||||
}
|
||||
|
||||
fn is_image(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
||||
if buf_len >= 4 && buf.starts_with(b"CRXD") {
|
||||
return Some(255);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CrxdImage {
|
||||
base: CrxImage,
|
||||
diff: CrxImage,
|
||||
}
|
||||
|
||||
impl CrxdImage {
|
||||
pub fn new<T: Read + Seek>(
|
||||
data: T,
|
||||
filename: &str,
|
||||
encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
archive: Option<&Box<dyn Script>>,
|
||||
) -> Result<Self> {
|
||||
let mut reader = data;
|
||||
let mut magic = [0; 4];
|
||||
reader.read_exact(&mut magic)?;
|
||||
if magic != *b"CRXD" {
|
||||
return Err(anyhow::anyhow!("Invalid CRXD magic"));
|
||||
}
|
||||
reader.seek_relative(4)?;
|
||||
let offset = reader.read_u32()?;
|
||||
let name = reader.read_fstring(0x14, encoding, true)?;
|
||||
let base = if let Some(archive) = archive {
|
||||
CrxImage::new(
|
||||
archive.open_file_by_offset(offset as u64)?.to_data()?,
|
||||
config,
|
||||
)?
|
||||
} else {
|
||||
let mut nf = std::path::PathBuf::from(filename);
|
||||
nf.set_file_name(name);
|
||||
let f = std::fs::File::open(nf)?;
|
||||
CrxImage::new(std::io::BufReader::new(f), config)?
|
||||
};
|
||||
let mut typ = [0; 4];
|
||||
reader.read_exact(&mut typ)?;
|
||||
if typ == *b"CRXJ" {
|
||||
reader.seek_relative(4)?;
|
||||
let offset = reader.read_u32()?;
|
||||
let diff = Self::read_diff(
|
||||
archive
|
||||
.ok_or(anyhow::anyhow!("No archive provided"))?
|
||||
.open_file_by_offset(offset as u64)?
|
||||
.to_data()?,
|
||||
archive.clone(),
|
||||
config,
|
||||
)?;
|
||||
return Ok(Self { base, diff });
|
||||
} else if typ == *b"CRXG" {
|
||||
let reader = StreamRegion::with_start_pos(reader, 0x20)?;
|
||||
let diff = CrxImage::new(reader, config)?;
|
||||
return Ok(Self { base, diff });
|
||||
}
|
||||
Err(anyhow::anyhow!("Unsupported diff CRXD type: {:?}", typ))
|
||||
}
|
||||
|
||||
fn read_diff<T: Read + Seek>(
|
||||
mut reader: T,
|
||||
archive: Option<&Box<dyn Script>>,
|
||||
config: &ExtraConfig,
|
||||
) -> Result<CrxImage> {
|
||||
let mut magic = [0; 4];
|
||||
reader.read_exact(&mut magic)?;
|
||||
if magic != *b"CRXD" {
|
||||
return Err(anyhow::anyhow!("Invalid CRXD magic"));
|
||||
}
|
||||
reader.seek_relative(0x1C)?;
|
||||
let mut typ = [0; 4];
|
||||
reader.read_exact(&mut typ)?;
|
||||
if typ == *b"CRXJ" {
|
||||
reader.seek_relative(4)?;
|
||||
let offset = reader.read_u32()?;
|
||||
return Ok(CrxImage::new(
|
||||
archive
|
||||
.ok_or(anyhow::anyhow!("No archive provided"))?
|
||||
.open_file_by_offset(offset as u64)?
|
||||
.to_data()?,
|
||||
config,
|
||||
)?);
|
||||
} else if typ == *b"CRXG" {
|
||||
let reader = StreamRegion::with_start_pos(reader, 0x20)?;
|
||||
return Ok(CrxImage::new(reader, config)?);
|
||||
}
|
||||
Err(anyhow::anyhow!("Unsupported diff CRXD type: {:?}", typ))
|
||||
}
|
||||
}
|
||||
|
||||
impl Script for CrxdImage {
|
||||
fn default_output_script_type(&self) -> OutputScriptType {
|
||||
OutputScriptType::Json
|
||||
}
|
||||
|
||||
fn default_format_type(&self) -> FormatOptions {
|
||||
FormatOptions::None
|
||||
}
|
||||
|
||||
fn is_image(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn export_image(&self) -> Result<ImageData> {
|
||||
self.base.draw_diff(&self.diff)
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
pub mod crx;
|
||||
pub mod crxd;
|
||||
|
||||
@@ -96,6 +96,8 @@ lazy_static::lazy_static! {
|
||||
Box::new(circus::archive::dat::DatArchiveBuilder::new()),
|
||||
#[cfg(feature = "circus-arc")]
|
||||
Box::new(circus::archive::crm::CrmArchiveBuilder::new()),
|
||||
#[cfg(feature = "circus-img")]
|
||||
Box::new(circus::image::crxd::CrxdImageBuilder::new()),
|
||||
];
|
||||
pub static ref ALL_EXTS: Vec<String> =
|
||||
BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect();
|
||||
|
||||
Reference in New Issue
Block a user