msg_tool\scripts\circus\image/
crxd.rs

1//! Circus Differential Image File (.crx)
2use super::crx::CrxImage;
3use crate::ext::io::*;
4use crate::scripts::base::*;
5use crate::types::*;
6use anyhow::Result;
7use std::io::{Read, Seek};
8
9#[derive(Debug)]
10/// Circus CRXD Image Builder
11pub struct CrxdImageBuilder {}
12
13impl CrxdImageBuilder {
14    /// Creates a new instance of `CrxdImageBuilder`.
15    pub fn new() -> Self {
16        Self {}
17    }
18}
19
20impl ScriptBuilder for CrxdImageBuilder {
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>> {
34        Ok(Box::new(CrxdImage::new(
35            MemReader::new(data),
36            filename,
37            encoding,
38            config,
39            archive,
40        )?))
41    }
42
43    fn extensions(&self) -> &'static [&'static str] {
44        &["crx"]
45    }
46
47    fn script_type(&self) -> &'static ScriptType {
48        &ScriptType::CircusCrxd
49    }
50
51    fn is_image(&self) -> bool {
52        true
53    }
54
55    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
56        if buf_len >= 4 && buf.starts_with(b"CRXD") {
57            return Some(255);
58        }
59        None
60    }
61}
62
63#[derive(Debug)]
64/// Circus CRXD Image
65pub struct CrxdImage {
66    base: CrxImage,
67    diff: CrxImage,
68}
69
70impl CrxdImage {
71    /// Creates a new `CrxdImage` from the given data and configuration.
72    ///
73    /// * `data` - The reader to read the CRXD image from.
74    /// * `filename` - The name of the file to read.
75    /// * `encoding` - The encoding to use for string fields.
76    /// * `config` - Extra configuration options.
77    /// * `archive` - Optional archive to read the image from.
78    pub fn new<T: Read + Seek>(
79        data: T,
80        filename: &str,
81        encoding: Encoding,
82        config: &ExtraConfig,
83        archive: Option<&Box<dyn Script>>,
84    ) -> Result<Self> {
85        let mut reader = data;
86        let mut magic = [0; 4];
87        reader.read_exact(&mut magic)?;
88        if magic != *b"CRXD" {
89            return Err(anyhow::anyhow!("Invalid CRXD magic"));
90        }
91        reader.seek_relative(4)?;
92        let offset = reader.read_u32()?;
93        let name = reader.read_fstring(0x14, encoding, true)?;
94        let base = if let Some(archive) = archive {
95            CrxImage::new(
96                archive.open_file_by_offset(offset as u64)?.to_data()?,
97                config,
98            )?
99        } else {
100            let mut nf = std::path::PathBuf::from(filename);
101            nf.set_file_name(name);
102            nf = crate::utils::files::get_ignorecase_path(&nf)?;
103            let f = std::fs::File::open(nf)?;
104            CrxImage::new(std::io::BufReader::new(f), config)?
105        }
106        .with_canvas(false);
107        let mut typ = [0; 4];
108        reader.read_exact(&mut typ)?;
109        if typ == *b"CRXJ" {
110            reader.seek_relative(4)?;
111            let offset = reader.read_u32()?;
112            let diff = Self::read_diff(
113                archive
114                    .ok_or(anyhow::anyhow!("No archive provided"))?
115                    .open_file_by_offset(offset as u64)?
116                    .to_data()?,
117                archive.clone(),
118                config,
119            )?;
120            return Ok(Self { base, diff });
121        } else if typ == *b"CRXG" {
122            let reader = StreamRegion::with_start_pos(reader, 0x20)?;
123            let diff = CrxImage::new(reader, config)?.with_canvas(false);
124            return Ok(Self { base, diff });
125        }
126        Err(anyhow::anyhow!("Unsupported diff CRXD type: {:?}", typ))
127    }
128
129    fn read_diff<T: Read + Seek>(
130        mut reader: T,
131        archive: Option<&Box<dyn Script>>,
132        config: &ExtraConfig,
133    ) -> Result<CrxImage> {
134        let mut magic = [0; 4];
135        reader.read_exact(&mut magic)?;
136        if magic != *b"CRXD" {
137            return Err(anyhow::anyhow!("Invalid CRXD magic"));
138        }
139        reader.seek_relative(0x1C)?;
140        let mut typ = [0; 4];
141        reader.read_exact(&mut typ)?;
142        if typ == *b"CRXJ" {
143            reader.seek_relative(4)?;
144            let offset = reader.read_u32()?;
145            return Self::read_diff(
146                archive
147                    .ok_or(anyhow::anyhow!("No archive provided"))?
148                    .open_file_by_offset(offset as u64)?
149                    .to_data()?,
150                archive,
151                config,
152            );
153        } else if typ == *b"CRXG" {
154            let reader = StreamRegion::with_start_pos(reader, 0x20)?;
155            return Ok(CrxImage::new(reader, config)?.with_canvas(false));
156        }
157        Err(anyhow::anyhow!("Unsupported diff CRXD type: {:?}", typ))
158    }
159}
160
161impl Script for CrxdImage {
162    fn default_output_script_type(&self) -> OutputScriptType {
163        OutputScriptType::Json
164    }
165
166    fn default_format_type(&self) -> FormatOptions {
167        FormatOptions::None
168    }
169
170    fn is_image(&self) -> bool {
171        true
172    }
173
174    fn export_image(&self) -> Result<ImageData> {
175        self.base.draw_diff(&self.diff)
176    }
177}