msg_tool\scripts\circus\image/
crxd.rs1use 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)]
10pub struct CrxdImageBuilder {}
12
13impl CrxdImageBuilder {
14 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)]
64pub struct CrxdImage {
66 base: CrxImage,
67 diff: CrxImage,
68}
69
70impl CrxdImage {
71 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}