msg_tool\scripts\hexen_haus\img/
png.rs

1//! HexenHaus PNG Image
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use anyhow::Result;
7use std::io::{Read, Seek, SeekFrom};
8
9#[derive(Debug)]
10/// HexenHaus PNG Image Builder
11pub struct PngImageBuilder {}
12
13impl PngImageBuilder {
14    /// Creates a new instance of `PngImageBuilder`
15    pub fn new() -> Self {
16        PngImageBuilder {}
17    }
18}
19
20impl ScriptBuilder for PngImageBuilder {
21    fn default_encoding(&self) -> Encoding {
22        Encoding::Cp932
23    }
24
25    fn build_script(
26        &self,
27        data: Vec<u8>,
28        _filename: &str,
29        _encoding: Encoding,
30        _archive_encoding: Encoding,
31        config: &ExtraConfig,
32        _archive: Option<&Box<dyn Script>>,
33    ) -> Result<Box<dyn Script + Send + Sync>> {
34        Ok(Box::new(PngImage::new(MemReader::new(data), config)?))
35    }
36
37    fn extensions(&self) -> &'static [&'static str] {
38        &["png"]
39    }
40
41    fn script_type(&self) -> &'static ScriptType {
42        &ScriptType::HexenHausPng
43    }
44
45    fn is_image(&self) -> bool {
46        true
47    }
48
49    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
50        if buf_len >= 4 && buf.starts_with(b"IMGD") {
51            return Some(10);
52        }
53        None
54    }
55}
56
57#[derive(Clone, Debug)]
58/// Extra information for PNG image
59pub struct ExtraInfo {
60    /// x offset
61    pub offset_x: u32,
62    /// y offset
63    pub offset_y: u32,
64}
65
66impl AnyDebug for ExtraInfo {
67    fn as_any(&self) -> &dyn std::any::Any {
68        self
69    }
70}
71
72#[derive(Debug)]
73pub struct PngImage {
74    reader: MemReader,
75    extra: Option<ExtraInfo>,
76}
77
78impl PngImage {
79    /// Creates a new instance of `PngImage`
80    pub fn new(mut reader: MemReader, _config: &ExtraConfig) -> Result<Self> {
81        let mut header = [0; 4];
82        reader.read_exact(&mut header)?;
83        if &header != b"IMGD" {
84            return Err(anyhow::anyhow!("Not a valid HexenHaus PNG image"));
85        }
86        reader.seek(SeekFrom::End(-14))?;
87        let cnt = reader.read_exact_vec(12)?;
88        let extra = if cnt.starts_with(b"CNTR") {
89            let mut cnt_reader = MemReaderRef::new(&cnt[4..]);
90            let offset_x = cnt_reader.read_u32()?;
91            let offset_y = cnt_reader.read_u32()?;
92            Some(ExtraInfo { offset_x, offset_y })
93        } else {
94            None
95        };
96        Ok(PngImage { reader, extra })
97    }
98}
99
100impl Script for PngImage {
101    fn default_output_script_type(&self) -> OutputScriptType {
102        OutputScriptType::Json
103    }
104
105    fn default_format_type(&self) -> FormatOptions {
106        FormatOptions::None
107    }
108
109    fn is_image(&self) -> bool {
110        true
111    }
112
113    fn export_image(&self) -> Result<ImageData> {
114        let mut reader = self.reader.to_ref();
115        reader.pos = 0;
116        let reader = StreamRegion::with_start_pos(reader, 0x10)?;
117        let img = load_png(reader)?;
118        Ok(img)
119    }
120
121    fn extra_info<'a>(&'a self) -> Option<Box<dyn AnyDebug + 'a>> {
122        self.extra
123            .as_ref()
124            .map(|e| Box::new(e.clone()) as Box<dyn AnyDebug>)
125    }
126}