msg_tool\scripts\circus\image/
crx.rs

1//! Circus Image File (.crx)
2use 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)]
15/// Circus CRX Row Encoding Mode
16pub enum CircusCrxMode {
17    /// Encoding all rows with a fixed type.
18    Fixed(u8),
19    /// When importing, use origin mode; when creating, use best mode.
20    Auto,
21    /// Use origin mode for importing; when creating, fallback to best mode.
22    Origin,
23    /// Try to use the best mode for encoding.
24    Best,
25}
26
27impl Default for CircusCrxMode {
28    fn default() -> Self {
29        CircusCrxMode::Auto
30    }
31}
32
33impl CircusCrxMode {
34    /// Returns mode for importing.
35    pub fn for_importing(&self) -> Self {
36        match self {
37            CircusCrxMode::Auto => CircusCrxMode::Origin,
38            _ => *self,
39        }
40    }
41
42    /// Returns mode for creating.
43    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    /// Checks if the mode is best.
52    pub fn is_best(&self) -> bool {
53        matches!(self, CircusCrxMode::Best)
54    }
55
56    /// Checks if the mode is origin.
57    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)]
94/// Circus CRX Image Builder
95pub struct CrxImageBuilder {}
96
97impl CrxImageBuilder {
98    /// Creates a new instance of `CrxImageBuilder`.
99    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
200/// Circus CRX Image
201pub 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    /// Creates a new `CrxImage` from the given data and configuration.
232    ///
233    /// * `data` - The reader to read the CRX image from.
234    /// * `config` - Extra configuration options.
235    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()?; // stored compressed size, ignored for v1
296            }
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    /// Whether to draw image on canvas if canvas's width and height are specified in image.
379    pub fn with_canvas(mut self, canvas: bool) -> Self {
380        self.canvas = canvas;
381        self
382    }
383
384    /// Draws another image on this image.
385    ///
386    /// Returns a new `ImageData` with the combined image.
387    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    /// Creates a CRX image file from the given image data and writes it to the specified writer.
940    ///
941    /// * `data` - The input image data.
942    /// * `writer` - The writer to write the CRX image to.
943    /// * `config` - Extra configuration options.
944    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, // Force add compressed data length
956            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; // Upgrade to version 2
1217        }
1218        new_header.flags |= 0x10; // Force add compressed data length
1219        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; // Skip if the base image is not tall enough
1322        }
1323
1324        for x in 0..diff.width {
1325            let base_x = left + x;
1326            if base_x >= base.width {
1327                continue; // Skip if the base image is not wide enough
1328            }
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}