1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use clap::ValueEnum;
9use clap::builder::PossibleValue;
10use msg_tool_macro::*;
11use overf::wrapping;
12use std::io::{Read, Seek, Write};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum CircusCrxMode {
17 Fixed(u8),
19 Auto,
21 Origin,
23 Best,
25}
26
27impl Default for CircusCrxMode {
28 fn default() -> Self {
29 CircusCrxMode::Auto
30 }
31}
32
33impl CircusCrxMode {
34 pub fn for_importing(&self) -> Self {
36 match self {
37 CircusCrxMode::Auto => CircusCrxMode::Origin,
38 _ => *self,
39 }
40 }
41
42 pub fn for_creating(&self) -> Self {
44 match self {
45 CircusCrxMode::Auto => CircusCrxMode::Best,
46 CircusCrxMode::Origin => CircusCrxMode::Best,
47 _ => *self,
48 }
49 }
50
51 pub fn is_best(&self) -> bool {
53 matches!(self, CircusCrxMode::Best)
54 }
55
56 pub fn is_origin(&self) -> bool {
58 matches!(self, CircusCrxMode::Origin)
59 }
60}
61
62impl ValueEnum for CircusCrxMode {
63 fn value_variants<'a>() -> &'a [Self] {
64 &[
65 CircusCrxMode::Fixed(0),
66 CircusCrxMode::Fixed(1),
67 CircusCrxMode::Fixed(2),
68 CircusCrxMode::Fixed(3),
69 CircusCrxMode::Fixed(4),
70 CircusCrxMode::Auto,
71 CircusCrxMode::Origin,
72 CircusCrxMode::Best,
73 ]
74 }
75
76 fn to_possible_value(&self) -> Option<PossibleValue> {
77 Some(match self {
78 CircusCrxMode::Fixed(0) => PossibleValue::new("0").help("Row type 0"),
79 CircusCrxMode::Fixed(1) => PossibleValue::new("1").help("Row type 1"),
80 CircusCrxMode::Fixed(2) => PossibleValue::new("2").help("Row type 2"),
81 CircusCrxMode::Fixed(3) => PossibleValue::new("3").help("Row type 3"),
82 CircusCrxMode::Fixed(4) => PossibleValue::new("4").help("Row type 4"),
83 CircusCrxMode::Auto => PossibleValue::new("auto")
84 .help("When importing, use origin mode, otherwise use best mode."),
85 CircusCrxMode::Origin => PossibleValue::new("origin")
86 .help("Use origin mode for importing. When creating, fallback to best mode."),
87 CircusCrxMode::Best => PossibleValue::new("best").help("Try to use the best mode."),
88 _ => return None,
89 })
90 }
91}
92
93#[derive(Debug)]
94pub struct CrxImageBuilder {}
96
97impl CrxImageBuilder {
98 pub const fn new() -> Self {
100 CrxImageBuilder {}
101 }
102}
103
104impl ScriptBuilder for CrxImageBuilder {
105 fn default_encoding(&self) -> Encoding {
106 Encoding::Cp932
107 }
108
109 fn build_script(
110 &self,
111 data: Vec<u8>,
112 _filename: &str,
113 _encoding: Encoding,
114 _archive_encoding: Encoding,
115 config: &ExtraConfig,
116 _archive: Option<&Box<dyn Script>>,
117 ) -> Result<Box<dyn Script>> {
118 Ok(Box::new(CrxImage::new(MemReader::new(data), config)?))
119 }
120
121 fn extensions(&self) -> &'static [&'static str] {
122 &["crx"]
123 }
124
125 fn script_type(&self) -> &'static ScriptType {
126 &ScriptType::CircusCrx
127 }
128
129 fn is_image(&self) -> bool {
130 true
131 }
132
133 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
134 if buf_len >= 4 && buf.starts_with(b"CRXG") {
135 return Some(255);
136 }
137 None
138 }
139
140 fn can_create_image_file(&self) -> bool {
141 true
142 }
143
144 fn create_image_file<'a>(
145 &'a self,
146 data: ImageData,
147 writer: Box<dyn WriteSeek + 'a>,
148 options: &ExtraConfig,
149 ) -> Result<()> {
150 CrxImage::create_image(data, writer, options)
151 }
152}
153
154#[derive(Clone, Debug, StructPack, StructUnpack)]
155struct Clip {
156 field_0: u32,
157 img_width: u16,
158 img_height: u16,
159 clip_offset_x: u16,
160 clip_offset_y: u16,
161 clip_width: u16,
162 clip_height: u16,
163}
164
165#[derive(Clone, Debug, StructPack, StructUnpack)]
166struct Header {
167 inner_x: u16,
168 inner_y: u16,
169 width: u16,
170 height: u16,
171 version: u16,
172 flags: u16,
173 bpp: u16,
174 mode: u16,
175 #[skip_pack_if(self.version != 3)]
176 #[skip_unpack_if(version != 3)]
177 #[pvec(u32)]
178 clips: Vec<Clip>,
179}
180
181#[derive(Clone, Debug)]
182enum CrxImageData {
183 RowEncoded(Vec<u8>),
184 IndexedV1 {
185 pixels: Vec<u8>,
186 stride: usize,
187 palette: Vec<u8>,
188 palette_format: PaletteFormat,
189 pixel_depth_bits: usize,
190 },
191 Direct(Vec<u8>),
192}
193
194impl CrxImageData {
195 fn is_row_encoded(&self) -> bool {
196 matches!(self, CrxImageData::RowEncoded(_))
197 }
198}
199
200pub struct CrxImage {
202 header: Header,
203 color_type: ImageColorType,
204 data: CrxImageData,
205 compress_level: u32,
206 keep_original_bpp: bool,
207 zstd: bool,
208 zstd_compression_level: i32,
209 row_type: CircusCrxMode,
210 canvas: bool,
211}
212
213impl std::fmt::Debug for CrxImage {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 let data_info = match &self.data {
216 CrxImageData::RowEncoded(buf) => format!("row-encoded({})", buf.len()),
217 CrxImageData::IndexedV1 { pixels, .. } => {
218 format!("indexed-v1({})", pixels.len())
219 }
220 CrxImageData::Direct(buf) => format!("direct({})", buf.len()),
221 };
222 f.debug_struct("CrxImage")
223 .field("header", &self.header)
224 .field("color_type", &self.color_type)
225 .field("data", &data_info)
226 .finish()
227 }
228}
229
230impl CrxImage {
231 pub fn new<T: Read + Seek>(data: T, config: &ExtraConfig) -> Result<Self> {
236 let mut reader = data;
237 let mut magic = [0; 4];
238 reader.read_exact(&mut magic)?;
239 if magic != *b"CRXG" {
240 return Err(anyhow::anyhow!("Invalid CRX image magic"));
241 }
242 let header: Header = reader.read_struct(false, Encoding::Utf8)?;
243 if header.version == 0 || header.version > 3 {
244 return Err(anyhow::anyhow!(
245 "Unsupported CRX version: {}",
246 header.version
247 ));
248 }
249
250 let (color_type, data) = if header.version == 1 {
251 let width = usize::from(header.width);
252 let height = usize::from(header.height);
253 if width == 0 || height == 0 {
254 return Err(anyhow::anyhow!("CRX v1 image has zero dimensions"));
255 }
256
257 let bits_per_pixel = match header.bpp {
258 0 => 24usize,
259 1 => 32usize,
260 _ => 8usize,
261 };
262 if bits_per_pixel % 8 != 0 {
263 return Err(anyhow::anyhow!(
264 "Unsupported bits per pixel {} for CRX v1",
265 bits_per_pixel
266 ));
267 }
268 let pixel_size = bits_per_pixel / 8;
269 if pixel_size == 0 {
270 return Err(anyhow::anyhow!("Invalid pixel size for CRX v1 image"));
271 }
272
273 let row_bytes = width
274 .checked_mul(pixel_size)
275 .ok_or_else(|| anyhow::anyhow!("CRX v1 row size overflow"))?;
276 let stride = (row_bytes
277 .checked_add(3)
278 .ok_or_else(|| anyhow::anyhow!("CRX v1 stride overflow"))?)
279 & !3usize;
280 let output_len = stride
281 .checked_mul(height)
282 .ok_or_else(|| anyhow::anyhow!("CRX v1 buffer size overflow"))?;
283
284 let palette = if bits_per_pixel == 8 {
285 let raw_colors = usize::from(header.bpp);
286 Some((
287 Self::read_v1_palette(&mut reader, raw_colors)?,
288 PaletteFormat::Rgb,
289 ))
290 } else {
291 None
292 };
293
294 if (header.flags & 0x10) != 0 {
295 reader.read_u32()?; }
297
298 let pixels = Self::unpack_v1(&mut reader, output_len)?;
299
300 if let Some((palette, palette_format)) = palette {
301 let data = CrxImageData::IndexedV1 {
302 pixels,
303 stride,
304 palette,
305 palette_format,
306 pixel_depth_bits: bits_per_pixel,
307 };
308 (ImageColorType::Bgr, data)
309 } else {
310 let mut trimmed = Vec::with_capacity(
311 row_bytes
312 .checked_mul(height)
313 .ok_or_else(|| anyhow::anyhow!("CRX v1 buffer size overflow"))?,
314 );
315 for row in 0..height {
316 let start = row
317 .checked_mul(stride)
318 .ok_or_else(|| anyhow::anyhow!("CRX v1 row offset overflow"))?;
319 let end = start
320 .checked_add(row_bytes)
321 .ok_or_else(|| anyhow::anyhow!("CRX v1 row slice overflow"))?;
322 if end > pixels.len() {
323 return Err(anyhow::anyhow!(
324 "CRX v1 image data is shorter than expected"
325 ));
326 }
327 trimmed.extend_from_slice(&pixels[start..end]);
328 }
329 let color_type = match bits_per_pixel {
330 24 => ImageColorType::Bgr,
331 32 => ImageColorType::Bgra,
332 _ => ImageColorType::Bgr,
333 };
334 (color_type, CrxImageData::Direct(trimmed))
335 }
336 } else {
337 let color_type = if header.bpp == 0 {
338 ImageColorType::Bgr
339 } else if header.bpp == 1 {
340 ImageColorType::Bgra
341 } else {
342 return Err(anyhow::anyhow!("Unsupported CRX bpp: {}", header.bpp));
343 };
344 let compressed_size = if (header.flags & 0x10) == 0 {
345 let len = reader.stream_length()?;
346 (len - reader.stream_position()?) as u32
347 } else {
348 reader.read_u32()?
349 };
350 let compressed_data = reader.read_exact_vec(compressed_size as usize)?;
351 let uncompressed = if compressed_data.starts_with(&[0x28, 0xb5, 0x2f, 0xfd]) {
352 let mut decoder = zstd::Decoder::new(MemReaderRef::new(&compressed_data))?;
353 let mut decompressed_data = Vec::new();
354 decoder.read_to_end(&mut decompressed_data)?;
355 decompressed_data
356 } else {
357 let mut decompressed_data = Vec::new();
358 flate2::read::ZlibDecoder::new(MemReaderRef::new(&compressed_data))
359 .read_to_end(&mut decompressed_data)?;
360 decompressed_data
361 };
362 (color_type, CrxImageData::RowEncoded(uncompressed))
363 };
364
365 Ok(CrxImage {
366 header,
367 color_type,
368 data,
369 compress_level: config.zlib_compression_level,
370 keep_original_bpp: config.circus_crx_keep_original_bpp,
371 zstd: config.circus_crx_zstd,
372 zstd_compression_level: config.zstd_compression_level,
373 row_type: config.circus_crx_mode.for_importing(),
374 canvas: config.circus_crx_canvas,
375 })
376 }
377
378 pub fn with_canvas(mut self, canvas: bool) -> Self {
380 self.canvas = canvas;
381 self
382 }
383
384 pub fn draw_diff(&self, diff: &Self) -> Result<ImageData> {
388 let base_header = &self.header;
389 let diff_header = &diff.header;
390 let (img_width, img_height) =
391 if base_header.clips.is_empty() && diff_header.clips.is_empty() {
392 (
393 (base_header.width + base_header.inner_x)
394 .max(diff_header.width + diff_header.inner_x),
395 (base_header.height + base_header.inner_y)
396 .max(diff_header.height + diff_header.inner_y),
397 )
398 } else {
399 if base_header.clips.is_empty() {
400 let clip = &diff_header.clips[0];
401 (clip.img_width, clip.img_height)
402 } else {
403 let clip = &base_header.clips[0];
404 (clip.img_width, clip.img_height)
405 }
406 };
407 let base = self.export_image()?;
408 let mut nw = draw_on_canvas(
409 base,
410 img_width as u32,
411 img_height as u32,
412 base_header.inner_x as u32,
413 base_header.inner_y as u32,
414 )?;
415 draw_on_img(
416 &mut nw,
417 &diff.export_image()?,
418 diff_header.inner_x as u32,
419 diff_header.inner_y as u32,
420 )?;
421 Ok(nw)
422 }
423
424 fn decode_row0(
425 dst: &mut Vec<u8>,
426 mut dst_p: usize,
427 src: &[u8],
428 mut src_p: usize,
429 width: u16,
430 pixel_size: u8,
431 ) -> Result<usize> {
432 let mut prev_p = dst_p;
433 for _ in 0..pixel_size {
434 dst[dst_p] = src[src_p];
435 dst_p += 1;
436 src_p += 1;
437 }
438 let remaining = width - 1;
439 for _ in 0..remaining {
440 for _ in 0..pixel_size {
441 dst[dst_p] = src[src_p].overflowing_add(dst[prev_p]).0;
442 dst_p += 1;
443 src_p += 1;
444 prev_p += 1;
445 }
446 }
447 Ok(src_p)
448 }
449
450 fn decode_row1(
451 dst: &mut Vec<u8>,
452 mut dst_p: usize,
453 src: &[u8],
454 mut src_p: usize,
455 width: u16,
456 pixel_size: u8,
457 mut prev_row_p: usize,
458 ) -> Result<usize> {
459 for _ in 0..width {
460 for _ in 0..pixel_size {
461 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
462 dst_p += 1;
463 src_p += 1;
464 prev_row_p += 1;
465 }
466 }
467 Ok(src_p)
468 }
469
470 fn decode_row2(
471 dst: &mut Vec<u8>,
472 mut dst_p: usize,
473 src: &[u8],
474 mut src_p: usize,
475 width: u16,
476 pixel_size: u8,
477 mut prev_row_p: usize,
478 ) -> Result<usize> {
479 for _ in 0..pixel_size {
480 dst[dst_p] = src[src_p];
481 dst_p += 1;
482 src_p += 1;
483 }
484 let remaining = width - 1;
485 for _ in 0..remaining {
486 for _ in 0..pixel_size {
487 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
488 dst_p += 1;
489 src_p += 1;
490 prev_row_p += 1;
491 }
492 }
493 Ok(src_p)
494 }
495
496 fn decode_row3(
497 dst: &mut Vec<u8>,
498 mut dst_p: usize,
499 src: &[u8],
500 mut src_p: usize,
501 width: u16,
502 pixel_size: u8,
503 mut prev_row_p: usize,
504 ) -> Result<usize> {
505 let count = width - 1;
506 prev_row_p += pixel_size as usize;
507 for _ in 0..count {
508 for _ in 0..pixel_size {
509 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
510 dst_p += 1;
511 src_p += 1;
512 prev_row_p += 1;
513 }
514 }
515 for _ in 0..pixel_size {
516 dst[dst_p] = src[src_p];
517 dst_p += 1;
518 src_p += 1;
519 }
520 Ok(src_p)
521 }
522
523 fn decode_row4(
524 dst: &mut Vec<u8>,
525 dst_p: usize,
526 src: &[u8],
527 mut src_p: usize,
528 width: u16,
529 pixel_size: u8,
530 ) -> Result<usize> {
531 for offset in 0..pixel_size {
532 let mut dst_c = dst_p + offset as usize;
533 let mut remaining = width;
534 let value = src[src_p];
535 src_p += 1;
536 dst[dst_c] = value;
537 dst_c += pixel_size as usize;
538 remaining -= 1;
539 if remaining == 0 {
540 continue;
541 }
542 if value == src[src_p] {
543 src_p += 1;
544 let count = src[src_p] as u16;
545 src_p += 1;
546 remaining -= count;
547 for _ in 0..count {
548 dst[dst_c] = value;
549 dst_c += pixel_size as usize;
550 }
551 }
552 while remaining > 0 {
553 let value = src[src_p];
554 src_p += 1;
555 dst[dst_c] = value;
556 dst_c += pixel_size as usize;
557 remaining -= 1;
558 if remaining == 0 {
559 break;
560 }
561 if value == src[src_p] {
562 src_p += 1;
563 let count = src[src_p] as u16;
564 src_p += 1;
565 remaining -= count;
566 for _ in 0..count {
567 dst[dst_c] = value;
568 dst_c += pixel_size as usize;
569 }
570 }
571 }
572 }
573 Ok(src_p)
574 }
575
576 fn read_v1_palette<T: Read>(reader: &mut T, raw_colors: usize) -> Result<Vec<u8>> {
577 if raw_colors == 0 {
578 return Err(anyhow::anyhow!("CRX v1 palette has zero colors"));
579 }
580 let color_size = if raw_colors == 0x0102 { 4usize } else { 3usize };
581 let mut colors = raw_colors;
582 if colors > 0x0100 {
583 colors = 0x0100;
584 }
585 let palette_size = colors
586 .checked_mul(color_size)
587 .ok_or_else(|| anyhow::anyhow!("CRX v1 palette size overflow"))?;
588 if palette_size == 0 {
589 return Err(anyhow::anyhow!("CRX v1 palette size is zero"));
590 }
591 let mut palette_raw = vec![0u8; palette_size];
592 reader.read_exact(&mut palette_raw)?;
593 let mut palette = Vec::with_capacity(colors * 3);
594 let mut pos = 0usize;
595 while pos < palette_raw.len() {
596 let r = palette_raw[pos];
597 let mut g = palette_raw[pos + 1];
598 let b = palette_raw[pos + 2];
599 if b == 0xFF && g == 0x00 && r == 0xFF {
600 g = 0xFF;
601 }
602 palette.push(r);
603 palette.push(g);
604 palette.push(b);
605 pos += color_size;
606 }
607 Ok(palette)
608 }
609
610 fn unpack_v1<T: Read>(reader: &mut T, output_len: usize) -> Result<Vec<u8>> {
611 const WINDOW_SIZE: usize = 0x10000;
612 const WINDOW_MASK: usize = WINDOW_SIZE - 1;
613 let mut window = vec![0u8; WINDOW_SIZE];
614 let mut win_pos: usize = 0;
615 let mut dst = vec![0u8; output_len];
616 let mut dst_pos = 0usize;
617 let mut flag: u16 = 0;
618 while dst_pos < output_len {
619 flag >>= 1;
620 if (flag & 0x100) == 0 {
621 let next = reader.read_u8()? as u16;
622 flag = next | 0xFF00;
623 }
624 if (flag & 1) != 0 {
625 let byte = reader.read_u8()?;
626 window[win_pos] = byte;
627 win_pos = (win_pos + 1) & WINDOW_MASK;
628 dst[dst_pos] = byte;
629 dst_pos += 1;
630 } else {
631 let control = reader.read_u8()?;
632 let (count, offset_value) = if control >= 0xC0 {
633 let next = reader.read_u8()? as usize;
634 let offset = (((control as usize) & 0x03) << 8) | next;
635 let count = 4 + (((control as usize) >> 2) & 0x0F);
636 (count, offset)
637 } else if (control & 0x80) != 0 {
638 let mut offset = (control & 0x1F) as usize;
639 let count = 2 + (((control as usize) >> 5) & 0x03);
640 if offset == 0 {
641 offset = reader.read_u8()? as usize;
642 }
643 (count, offset)
644 } else if control == 0x7F {
645 let count = 2 + reader.read_u16()? as usize;
646 let offset = reader.read_u16()? as usize;
647 (count, offset)
648 } else {
649 let offset = reader.read_u16()? as usize;
650 let count = control as usize + 4;
651 (count, offset)
652 };
653
654 let mut offset_pos = (win_pos.wrapping_sub(offset_value)) & WINDOW_MASK;
655 for _ in 0..count {
656 if dst_pos >= output_len {
657 break;
658 }
659 let value = window[offset_pos];
660 offset_pos = (offset_pos + 1) & WINDOW_MASK;
661 window[win_pos] = value;
662 win_pos = (win_pos + 1) & WINDOW_MASK;
663 dst[dst_pos] = value;
664 dst_pos += 1;
665 }
666 }
667 }
668 Ok(dst)
669 }
670
671 fn decode_image(
672 dst: &mut Vec<u8>,
673 src: &[u8],
674 width: u16,
675 height: u16,
676 pixel_size: u8,
677 encode_type: &mut Vec<u8>,
678 ) -> Result<()> {
679 let mut src_p = 0;
680 let mut dst_p = 0;
681 let mut prev_row_p = 0;
682 for _ in 0..height {
683 let data = src[src_p];
684 encode_type.push(data);
685 src_p += 1;
686 match data {
687 0 => {
688 src_p = Self::decode_row0(dst, dst_p, src, src_p, width, pixel_size)?;
689 }
690 1 => {
691 src_p =
692 Self::decode_row1(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
693 }
694 2 => {
695 src_p =
696 Self::decode_row2(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
697 }
698 3 => {
699 src_p =
700 Self::decode_row3(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
701 }
702 4 => {
703 src_p = Self::decode_row4(dst, dst_p, src, src_p, width, pixel_size)?;
704 }
705 _ => {
706 return Err(anyhow::anyhow!("Invalid row type: {}", data));
707 }
708 }
709 prev_row_p = dst_p;
710 dst_p += pixel_size as usize * width as usize;
711 }
712 Ok(())
713 }
714
715 fn encode_row0(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
716 let pixel_size = pixel_size as usize;
717 let mut src_p = y as usize * width as usize * pixel_size;
718 for _ in 0..pixel_size {
719 dst.push(src[src_p]);
720 src_p += 1;
721 }
722 for _ in 1..width {
723 for _ in 0..pixel_size {
724 dst.push(src[src_p].wrapping_sub(src[src_p - pixel_size]));
725 src_p += 1;
726 }
727 }
728 }
729
730 fn encode_row1(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
731 let pixel_size = pixel_size as usize;
732 let mut src_p = y as usize * width as usize * pixel_size;
733 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
734 for _ in 0..width {
735 for _ in 0..pixel_size {
736 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
737 src_p += 1;
738 prev_row_p += 1;
739 }
740 }
741 }
742
743 fn encode_row2(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
744 let pixel_size = pixel_size as usize;
745 let mut src_p = y as usize * width as usize * pixel_size;
746 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
747 for _ in 0..pixel_size {
748 dst.push(src[src_p]);
749 src_p += 1;
750 }
751 for _ in 1..width {
752 for _ in 0..pixel_size {
753 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
754 src_p += 1;
755 prev_row_p += 1;
756 }
757 }
758 }
759
760 fn encode_row3(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
761 let pixel_size = pixel_size as usize;
762 let mut src_p = y as usize * width as usize * pixel_size;
763 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size + pixel_size;
764 for _ in 0..width - 1 {
765 for _ in 0..pixel_size {
766 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
767 src_p += 1;
768 prev_row_p += 1;
769 }
770 }
771 for _ in 0..pixel_size {
772 dst.push(src[src_p]);
773 src_p += 1;
774 }
775 }
776
777 fn encode_row4(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
778 let pixel_size = pixel_size as usize;
779 let src_p = y as usize * width as usize * pixel_size;
780 for offset in 0..pixel_size {
781 let mut src_c = src_p + offset;
782 let mut remaining = width;
783 let value = src[src_c];
784 src_c += pixel_size;
785 dst.push(value);
786 remaining -= 1;
787 if remaining == 0 {
788 continue;
789 }
790 let mut count = 0;
791 loop {
792 if count as u16 >= remaining || count >= 255 || src[src_c] != value {
793 break;
794 }
795 src_c += pixel_size;
796 count += 1;
797 }
798 if count > 0 {
799 dst.push(value);
800 dst.push(count);
801 remaining -= count as u16;
802 }
803 while remaining > 0 {
804 let value = src[src_c];
805 src_c += pixel_size;
806 dst.push(value);
807 remaining -= 1;
808 if remaining == 0 {
809 break;
810 }
811 let mut count = 0;
812 loop {
813 if count as u16 >= remaining || count >= 255 || src[src_c] != value {
814 break;
815 }
816 src_c += pixel_size;
817 count += 1;
818 }
819 if count > 0 {
820 dst.push(value);
821 dst.push(count);
822 remaining -= count as u16;
823 }
824 }
825 }
826 }
827
828 fn encode_row_best(
829 dst: &mut Vec<u8>,
830 src: &[u8],
831 width: u16,
832 pixel_size: u8,
833 y: u16,
834 ) -> Result<()> {
835 let mut buf = Vec::with_capacity(width as usize * pixel_size as usize);
836 Self::encode_row0(&mut buf, src, width, pixel_size, y);
837 let mut compressed_len = {
838 let mut encoder =
839 flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
840 encoder.write_all(&buf)?;
841 let compressed_data = encoder.finish()?;
842 compressed_data.into_inner().len()
843 };
844 let mut buf_row_type = 0;
845 for row_type in 1..5u8 {
846 if y == 0 && row_type < 4 {
847 continue;
848 }
849 let mut newbuf = Vec::with_capacity(width as usize * pixel_size as usize);
850 match row_type {
851 1 => Self::encode_row1(&mut newbuf, src, width, pixel_size, y),
852 2 => Self::encode_row2(&mut newbuf, src, width, pixel_size, y),
853 3 => Self::encode_row3(&mut newbuf, src, width, pixel_size, y),
854 4 => Self::encode_row4(&mut newbuf, src, width, pixel_size, y),
855 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
856 };
857 let new_compressed_len = {
858 let mut encoder =
859 flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
860 encoder.write_all(&newbuf)?;
861 let compressed_data = encoder.finish()?;
862 compressed_data.into_inner().len()
863 };
864 if new_compressed_len < compressed_len {
865 compressed_len = new_compressed_len;
866 buf = newbuf;
867 buf_row_type = row_type;
868 }
869 }
870 dst.push(buf_row_type);
871 dst.extend_from_slice(&buf);
872 Ok(())
873 }
874
875 fn encode_image_best(src: &[u8], width: u16, height: u16, pixel_size: u8) -> Result<Vec<u8>> {
876 let size = width as usize * height as usize * pixel_size as usize + height as usize;
877 let mut dst = Vec::with_capacity(size);
878 for y in 0..height {
879 Self::encode_row_best(&mut dst, src, width, pixel_size, y)?;
880 }
881 Ok(dst)
882 }
883
884 fn encode_image_fixed(
885 src: &[u8],
886 width: u16,
887 height: u16,
888 pixel_size: u8,
889 row_type: u8,
890 ) -> Result<Vec<u8>> {
891 let size = width as usize * height as usize * pixel_size as usize + height as usize;
892 let mut dst = Vec::with_capacity(size);
893 for y in 0..height {
894 let row_type = if y == 0 && row_type != 0 && row_type != 4 {
895 0
896 } else {
897 row_type
898 };
899 dst.push(row_type);
900 match row_type {
901 0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
902 1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
903 2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
904 3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
905 4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
906 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
907 };
908 }
909 Ok(dst)
910 }
911
912 fn encode_image_origin(
913 src: &[u8],
914 width: u16,
915 height: u16,
916 pixel_size: u8,
917 row_type: &[u8],
918 ) -> Result<Vec<u8>> {
919 if row_type.len() != height as usize {
920 return Err(anyhow::anyhow!("Row type length does not match height"));
921 }
922 let size = width as usize * height as usize * pixel_size as usize + height as usize;
923 let mut dst = Vec::with_capacity(size);
924 for y in 0..height {
925 let row_type = row_type[y as usize];
926 dst.push(row_type);
927 match row_type {
928 0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
929 1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
930 2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
931 3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
932 4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
933 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
934 };
935 }
936 Ok(dst)
937 }
938
939 pub fn create_image<T: Write + Seek>(
945 mut data: ImageData,
946 mut writer: T,
947 config: &ExtraConfig,
948 ) -> Result<()> {
949 let header = Header {
950 inner_x: 0,
951 inner_y: 0,
952 width: data.width as u16,
953 height: data.height as u16,
954 version: 2,
955 flags: 0x10, bpp: match data.color_type {
957 ImageColorType::Bgr => 0,
958 ImageColorType::Bgra => 1,
959 ImageColorType::Rgb => {
960 convert_rgb_to_bgr(&mut data)?;
961 0
962 }
963 ImageColorType::Rgba => {
964 convert_rgba_to_bgra(&mut data)?;
965 1
966 }
967 _ => {
968 return Err(anyhow::anyhow!(
969 "Unsupported color type: {:?}",
970 data.color_type
971 ));
972 }
973 },
974 mode: 0,
975 clips: Vec::new(),
976 };
977 let pixel_size = data.color_type.bpp(1) as u8;
978 if data.color_type == ImageColorType::Bgra && header.mode != 1 {
979 let alpha_flip = if header.mode == 2 { 0 } else { 0xFF };
980 for i in (0..data.data.len()).step_by(4) {
981 let b = data.data[i];
982 let g = data.data[i + 1];
983 let r = data.data[i + 2];
984 let a = data.data[i + 3];
985 data.data[i] = a ^ alpha_flip;
986 data.data[i + 1] = b;
987 data.data[i + 2] = g;
988 data.data[i + 3] = r;
989 }
990 }
991 let mode = config.circus_crx_mode.for_creating();
992 let encoded = if mode.is_best() {
993 Self::encode_image_best(&data.data, header.width, header.height, pixel_size)?
994 } else if let CircusCrxMode::Fixed(mode) = mode {
995 Self::encode_image_fixed(&data.data, header.width, header.height, pixel_size, mode)?
996 } else {
997 return Err(anyhow::anyhow!(
998 "Unsupported row type for creating: {:?}",
999 mode
1000 ));
1001 };
1002 let compressed = if config.circus_crx_zstd {
1003 let mut encoder = zstd::Encoder::new(MemWriter::new(), config.zstd_compression_level)?;
1004 encoder.write_all(&encoded)?;
1005 let compressed_data = encoder.finish()?;
1006 compressed_data.into_inner()
1007 } else {
1008 let mut encoder = flate2::write::ZlibEncoder::new(
1009 MemWriter::new(),
1010 flate2::Compression::new(config.zlib_compression_level),
1011 );
1012 encoder.write_all(&encoded)?;
1013 let compressed_data = encoder.finish()?;
1014 compressed_data.into_inner()
1015 };
1016 writer.write_all(b"CRXG")?;
1017 header.pack(&mut writer, false, Encoding::Utf8)?;
1018 writer.write_u32(compressed.len() as u32)?;
1019 writer.write_all(&compressed)?;
1020 Ok(())
1021 }
1022}
1023
1024impl Script for CrxImage {
1025 fn default_output_script_type(&self) -> OutputScriptType {
1026 OutputScriptType::Json
1027 }
1028
1029 fn default_format_type(&self) -> FormatOptions {
1030 FormatOptions::None
1031 }
1032
1033 fn is_image(&self) -> bool {
1034 true
1035 }
1036
1037 fn export_image(&self) -> Result<ImageData> {
1038 let width = usize::from(self.header.width);
1039 let height = usize::from(self.header.height);
1040 let mut img = match &self.data {
1041 CrxImageData::RowEncoded(encoded) => {
1042 let pixel_size = self.color_type.bpp(1) as usize;
1043 let row_bytes = pixel_size
1044 .checked_mul(width)
1045 .ok_or_else(|| anyhow::anyhow!("Image row size overflow"))?;
1046 let data_size = row_bytes
1047 .checked_mul(height)
1048 .ok_or_else(|| anyhow::anyhow!("Image buffer size overflow"))?;
1049 let mut data = vec![0u8; data_size];
1050 let mut encode_type = Vec::with_capacity(height);
1051 Self::decode_image(
1052 &mut data,
1053 encoded,
1054 self.header.width,
1055 self.header.height,
1056 self.color_type.bpp(1) as u8,
1057 &mut encode_type,
1058 )?;
1059 if self.color_type.bpp(1) == 4 && self.header.mode != 1 {
1060 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1061 for chunk in data.chunks_mut(4) {
1062 let a = chunk[0];
1063 let b = chunk[1];
1064 let g = chunk[2];
1065 let r = chunk[3];
1066 chunk[0] = b;
1067 chunk[1] = g;
1068 chunk[2] = r;
1069 chunk[3] = a ^ alpha_flip;
1070 }
1071 }
1072 ImageData {
1073 width: self.header.width as u32,
1074 height: self.header.height as u32,
1075 depth: 8,
1076 color_type: self.color_type,
1077 data,
1078 }
1079 }
1080 CrxImageData::Direct(pixels) => {
1081 let mut data = pixels.clone();
1082 if self.color_type == ImageColorType::Bgra && self.header.mode != 1 {
1083 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1084 for chunk in data.chunks_mut(4) {
1085 let a = chunk[0];
1086 let b = chunk[1];
1087 let g = chunk[2];
1088 let r = chunk[3];
1089 chunk[0] = b;
1090 chunk[1] = g;
1091 chunk[2] = r;
1092 chunk[3] = a ^ alpha_flip;
1093 }
1094 }
1095 ImageData {
1096 width: self.header.width as u32,
1097 height: self.header.height as u32,
1098 depth: 8,
1099 color_type: self.color_type,
1100 data,
1101 }
1102 }
1103 CrxImageData::IndexedV1 {
1104 pixels,
1105 stride,
1106 palette,
1107 palette_format,
1108 pixel_depth_bits,
1109 } => {
1110 let total_pixels = width
1111 .checked_mul(height)
1112 .ok_or_else(|| anyhow::anyhow!("Image dimensions overflow"))?;
1113 let mut indexed = Vec::with_capacity(total_pixels);
1114 for row in 0..height {
1115 let start = row
1116 .checked_mul(*stride)
1117 .ok_or_else(|| anyhow::anyhow!("Row offset overflow"))?;
1118 let end = start
1119 .checked_add(width)
1120 .ok_or_else(|| anyhow::anyhow!("Row slice overflow"))?;
1121 if end > pixels.len() {
1122 return Err(anyhow::anyhow!("CRX v1 indexed data is truncated"));
1123 }
1124 indexed.extend_from_slice(&pixels[start..end]);
1125 }
1126 let image = convert_index_palette_to_normal_bitmap(
1127 &indexed,
1128 *pixel_depth_bits,
1129 palette,
1130 *palette_format,
1131 width,
1132 height,
1133 )?;
1134 image
1135 }
1136 };
1137
1138 if self.canvas {
1139 let (img_width, img_height) = if self.header.clips.is_empty() {
1140 (self.header.width as u32, self.header.height as u32)
1141 } else {
1142 let clip = &self.header.clips[0];
1143 (clip.img_width as u32, clip.img_height as u32)
1144 };
1145 img = draw_on_canvas(
1146 img,
1147 img_width,
1148 img_height,
1149 self.header.inner_x as u32,
1150 self.header.inner_y as u32,
1151 )?;
1152 }
1153 Ok(img)
1154 }
1155
1156 fn import_image<'a>(
1157 &'a self,
1158 mut data: ImageData,
1159 mut file: Box<dyn WriteSeek + 'a>,
1160 ) -> Result<()> {
1161 let mut color_type = match data.color_type {
1162 ImageColorType::Bgr => ImageColorType::Bgr,
1163 ImageColorType::Bgra => ImageColorType::Bgra,
1164 ImageColorType::Rgb => {
1165 convert_rgb_to_bgr(&mut data)?;
1166 ImageColorType::Bgr
1167 }
1168 ImageColorType::Rgba => {
1169 convert_rgba_to_bgra(&mut data)?;
1170 ImageColorType::Bgra
1171 }
1172 _ => {
1173 return Err(anyhow::anyhow!(
1174 "Unsupported color type: {:?}",
1175 data.color_type
1176 ));
1177 }
1178 };
1179 if data.width != self.header.width as u32 {
1180 return Err(anyhow::anyhow!(
1181 "Image width does not match: expected {}, got {}",
1182 self.header.width,
1183 data.width
1184 ));
1185 }
1186 if data.height != self.header.height as u32 {
1187 return Err(anyhow::anyhow!(
1188 "Image height does not match: expected {}, got {}",
1189 self.header.height,
1190 data.height
1191 ));
1192 }
1193 if data.depth != 8 {
1194 return Err(anyhow::anyhow!("Image depth must be 8, got {}", data.depth));
1195 }
1196 if data.color_type != self.color_type && self.keep_original_bpp {
1197 if self.color_type == ImageColorType::Bgr {
1198 convert_bgra_to_bgr(&mut data)?;
1199 } else if self.color_type == ImageColorType::Bgra {
1200 convert_bgr_to_bgra(&mut data)?;
1201 } else {
1202 return Err(anyhow::anyhow!(
1203 "Unsupported color type for import: {:?}",
1204 self.color_type
1205 ));
1206 }
1207 color_type = self.color_type;
1208 }
1209 let mut new_header = self.header.clone();
1210 new_header.bpp = match color_type {
1211 ImageColorType::Bgr => 0,
1212 ImageColorType::Bgra => 1,
1213 _ => return Err(anyhow::anyhow!("Unsupported color type: {:?}", color_type)),
1214 };
1215 if new_header.version == 1 {
1216 new_header.version = 2; }
1218 new_header.flags |= 0x10; let pixel_size = color_type.bpp(1) as u8;
1220 if color_type == ImageColorType::Bgra && self.header.mode != 1 {
1221 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1222 for i in (0..data.data.len()).step_by(4) {
1223 let b = data.data[i];
1224 let g = data.data[i + 1];
1225 let r = data.data[i + 2];
1226 let a = data.data[i + 3];
1227 data.data[i] = a ^ alpha_flip;
1228 data.data[i + 1] = b;
1229 data.data[i + 2] = g;
1230 data.data[i + 3] = r;
1231 }
1232 }
1233 let encoded = if self.row_type.is_origin() && self.data.is_row_encoded() {
1234 let mut row_type = Vec::with_capacity(self.header.height as usize);
1235 let pixel_size_bytes = self.color_type.bpp(1) as usize;
1236 let row_len = pixel_size_bytes
1237 .checked_mul(self.header.width as usize)
1238 .ok_or_else(|| anyhow::anyhow!("Row length overflow"))?
1239 .checked_add(1)
1240 .ok_or_else(|| anyhow::anyhow!("Row length overflow"))?;
1241 let buffer = match &self.data {
1242 CrxImageData::RowEncoded(buf) => buf,
1243 _ => {
1244 return Err(anyhow::anyhow!(
1245 "Original row type information is unavailable"
1246 ));
1247 }
1248 };
1249 let mut cur_pos = 0usize;
1250 for _ in 0..self.header.height {
1251 if cur_pos >= buffer.len() {
1252 return Err(anyhow::anyhow!("Row type offset exceeds buffer length"));
1253 }
1254 row_type.push(buffer[cur_pos]);
1255 cur_pos = cur_pos
1256 .checked_add(row_len)
1257 .ok_or_else(|| anyhow::anyhow!("Row type offset overflow"))?;
1258 }
1259 Self::encode_image_origin(
1260 &data.data,
1261 new_header.width,
1262 new_header.height,
1263 pixel_size,
1264 &row_type,
1265 )?
1266 } else if self.row_type.is_best()
1267 || (self.row_type.is_origin() && !self.data.is_row_encoded())
1268 {
1269 Self::encode_image_best(&data.data, new_header.width, new_header.height, pixel_size)?
1270 } else if let CircusCrxMode::Fixed(mode) = self.row_type {
1271 Self::encode_image_fixed(
1272 &data.data,
1273 new_header.width,
1274 new_header.height,
1275 pixel_size,
1276 mode,
1277 )?
1278 } else {
1279 return Err(anyhow::anyhow!(
1280 "Unsupported row type for import: {:?}",
1281 self.row_type
1282 ));
1283 };
1284 let compressed = if self.zstd {
1285 let mut encoder = zstd::Encoder::new(MemWriter::new(), self.zstd_compression_level)?;
1286 encoder.write_all(&encoded)?;
1287 let compressed_data = encoder.finish()?;
1288 compressed_data.into_inner()
1289 } else {
1290 let mut encoder = flate2::write::ZlibEncoder::new(
1291 MemWriter::new(),
1292 flate2::Compression::new(self.compress_level),
1293 );
1294 encoder.write_all(&encoded)?;
1295 let compressed_data = encoder.finish()?;
1296 compressed_data.into_inner()
1297 };
1298 file.write_all(b"CRXG")?;
1299 new_header.pack(&mut file, false, Encoding::Utf8)?;
1300 file.write_u32(compressed.len() as u32)?;
1301 file.write_all(&compressed)?;
1302 Ok(())
1303 }
1304}
1305
1306fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
1307 if base.color_type != diff.color_type {
1308 return Err(anyhow::anyhow!(
1309 "Color types do not match: {:?} vs {:?}",
1310 base.color_type,
1311 diff.color_type
1312 ));
1313 }
1314 let bpp = base.color_type.bpp(1) as usize;
1315 let base_stride = base.width as usize * bpp;
1316 let diff_stride = diff.width as usize * bpp;
1317
1318 for y in 0..diff.height {
1319 let base_y = top + y;
1320 if base_y >= base.height {
1321 continue; }
1323
1324 for x in 0..diff.width {
1325 let base_x = left + x;
1326 if base_x >= base.width {
1327 continue; }
1329
1330 let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
1331 let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
1332
1333 let diff_pixel = &diff.data[diff_index..diff_index + bpp];
1334 let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
1335 let mut b = base_pixel_orig[0];
1336 let mut g = base_pixel_orig[1];
1337 let mut r = base_pixel_orig[2];
1338 wrapping! {
1339 b += diff_pixel[0];
1340 g += diff_pixel[1];
1341 r += diff_pixel[2];
1342 }
1343 base.data[base_index] = b;
1344 base.data[base_index + 1] = g;
1345 base.data[base_index + 2] = r;
1346 if bpp == 4 {
1347 let mut a = base_pixel_orig[3];
1348 wrapping! {
1349 a -= diff_pixel[3];
1350 }
1351 base.data[base_index + 3] = a;
1352 }
1353 }
1354 }
1355 Ok(())
1356}