Add support for HexenHaus PNG Image File (.png)

This commit is contained in:
2025-09-28 10:09:28 +08:00
parent 91bd43826d
commit 3ae55b03c0
8 changed files with 139 additions and 2 deletions

View File

@@ -7,5 +7,9 @@ fn detect_script_type(_filename: &str, buf: &[u8]) -> Option<ScriptType> {
if buf.len() >= 4 && buf.starts_with(b"NORI") {
return Some(ScriptType::HexenHaus);
}
#[cfg(feature = "hexen-haus-img")]
if buf.len() >= 4 && buf.starts_with(b"IMGD") {
return Some(ScriptType::HexenHausPng);
}
None
}

View File

@@ -0,0 +1 @@
pub mod png;

View File

@@ -0,0 +1,120 @@
//! HexenHaus PNG Image
use crate::ext::io::*;
use crate::scripts::base::*;
use crate::types::*;
use crate::utils::img::*;
use anyhow::Result;
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug)]
/// HexenHaus PNG Image Builder
pub struct PngImageBuilder {}
impl PngImageBuilder {
/// Creates a new instance of `PngImageBuilder`
pub fn new() -> Self {
PngImageBuilder {}
}
}
impl ScriptBuilder for PngImageBuilder {
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(PngImage::new(MemReader::new(data), config)?))
}
fn extensions(&self) -> &'static [&'static str] {
&["png"]
}
fn script_type(&self) -> &'static ScriptType {
&ScriptType::HexenHausPng
}
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"IMGD") {
return Some(10);
}
None
}
}
#[derive(Debug)]
/// Extra information for PNG image
pub struct ExtraInfo {
/// x offset
pub offset_x: u32,
/// y offset
pub offset_y: u32,
}
#[derive(Debug)]
pub struct PngImage {
reader: MemReader,
extra: Option<ExtraInfo>,
}
impl PngImage {
/// Creates a new instance of `PngImage`
pub fn new(mut reader: MemReader, _config: &ExtraConfig) -> Result<Self> {
let mut header = [0; 4];
reader.read_exact(&mut header)?;
if &header != b"IMGD" {
return Err(anyhow::anyhow!("Not a valid HexenHaus PNG image"));
}
reader.seek(SeekFrom::End(-14))?;
let cnt = reader.read_exact_vec(12)?;
let extra = if cnt.starts_with(b"CNTR") {
let mut cnt_reader = MemReaderRef::new(&cnt[4..]);
let offset_x = cnt_reader.read_u32()?;
let offset_y = cnt_reader.read_u32()?;
Some(ExtraInfo { offset_x, offset_y })
} else {
None
};
Ok(PngImage { reader, extra })
}
}
impl Script for PngImage {
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> {
let mut reader = self.reader.to_ref();
reader.pos = 0;
let reader = StreamRegion::with_start_pos(reader, 0x10)?;
let img = load_png(reader)?;
Ok(img)
}
fn extra_info<'a>(&'a self) -> Option<Box<dyn AnyDebug + 'a>> {
self.extra
.as_ref()
.map(|e| Box::new(e) as Box<dyn AnyDebug>)
}
}

View File

@@ -2,3 +2,5 @@
#[cfg(feature = "hexen-haus-arc")]
pub mod archive;
pub mod bin;
#[cfg(feature = "hexen-haus-img")]
pub mod img;

View File

@@ -146,6 +146,8 @@ lazy_static::lazy_static! {
Box::new(artemis::archive::pf2::ArtemisPf2Builder::new()),
#[cfg(feature = "hexen-haus-arc")]
Box::new(hexen_haus::archive::wag::HexenHausWagArchiveBuilder::new()),
#[cfg(feature = "hexen-haus-img")]
Box::new(hexen_haus::img::png::PngImageBuilder::new()),
];
/// A list of all script extensions.
pub static ref ALL_EXTS: Vec<String> =