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            let f = std::fs::File::open(nf)?;
103            CrxImage::new(std::io::BufReader::new(f), config)?
104        }
105        .with_canvas(false);
106        let mut typ = [0; 4];
107        reader.read_exact(&mut typ)?;
108        if typ == *b"CRXJ" {
109            reader.seek_relative(4)?;
110            let offset = reader.read_u32()?;
111            let diff = Self::read_diff(
112                archive
113                    .ok_or(anyhow::anyhow!("No archive provided"))?
114                    .open_file_by_offset(offset as u64)?
115                    .to_data()?,
116                archive.clone(),
117                config,
118            )?;
119            return Ok(Self { base, diff });
120        } else if typ == *b"CRXG" {
121            let reader = StreamRegion::with_start_pos(reader, 0x20)?;
122            let diff = CrxImage::new(reader, config)?.with_canvas(false);
123            return Ok(Self { base, diff });
124        }
125        Err(anyhow::anyhow!("Unsupported diff CRXD type: {:?}", typ))
126    }
127
128    fn read_diff<T: Read + Seek>(
129        mut reader: T,
130        archive: Option<&Box<dyn Script>>,
131        config: &ExtraConfig,
132    ) -> Result<CrxImage> {
133        let mut magic = [0; 4];
134        reader.read_exact(&mut magic)?;
135        if magic != *b"CRXD" {
136            return Err(anyhow::anyhow!("Invalid CRXD magic"));
137        }
138        reader.seek_relative(0x1C)?;
139        let mut typ = [0; 4];
140        reader.read_exact(&mut typ)?;
141        if typ == *b"CRXJ" {
142            reader.seek_relative(4)?;
143            let offset = reader.read_u32()?;
144            return Self::read_diff(
145                archive
146                    .ok_or(anyhow::anyhow!("No archive provided"))?
147                    .open_file_by_offset(offset as u64)?
148                    .to_data()?,
149                archive,
150                config,
151            );
152        } else if typ == *b"CRXG" {
153            let reader = StreamRegion::with_start_pos(reader, 0x20)?;
154            return Ok(CrxImage::new(reader, config)?.with_canvas(false));
155        }
156        Err(anyhow::anyhow!("Unsupported diff CRXD type: {:?}", typ))
157    }
158}
159
160impl Script for CrxdImage {
161    fn default_output_script_type(&self) -> OutputScriptType {
162        OutputScriptType::Json
163    }
164
165    fn default_format_type(&self) -> FormatOptions {
166        FormatOptions::None
167    }
168
169    fn is_image(&self) -> bool {
170        true
171    }
172
173    fn export_image(&self) -> Result<ImageData> {
174        self.base.draw_diff(&self.diff)
175    }
176}