Skip to main content

classpad_image/
lib.rs

1//! Image converter for the Casio fx-CP400 calculator.
2//!
3//! Provides functions to convert an image with a common format (PNG, JPG, WEBP, BMP) into a C2P file, or multiple image files into a C2B animation file.
4//! Also provides functions to convert C2P and C2B files back into the mentioned common formats.
5
6use image::{GenericImageView, Pixel};
7use std::{error::Error, path::Path};
8
9/// The largest possible image width on the fx-CP400.
10/// MAX_IMAGE_WIDTH is equal to 310 pixels (0x136).
11pub const MAX_IMAGE_WIDTH: u32 = 0x136;
12
13/// The largest possible image height on the fx-CP400.
14/// MAX_IMAGE_HEIGHT is equal to 401 pixels (0x191).
15pub const MAX_IMAGE_HEIGHT: u32 = 0x191;
16
17/// Function to convert an image to C2B.
18///
19/// Takes two arguments: a vector of image paths, which must point to valid image files with PNG, JPG, WEBP or BMP format.
20/// At least two images must be supplied.
21/// The second argument is the destination of the C2B file.
22///
23/// Example:
24/// ```
25/// let paths: Vec<&str> = vec!["path/to/file1.png", "path/to/file2.png"];
26/// convert_imgs_to_c2b(paths, "path/to/destination.c2b").unwrap();
27/// ```
28pub fn convert_imgs_to_c2b<T, S>(image_paths: Vec<T>, destination: S) -> Result<(), Box<dyn Error>>
29where
30    T: AsRef<Path> + Clone,
31    S: AsRef<Path> + Clone,
32{
33    if image_paths.len() < 2 {
34        eprintln!("At least two images have to be supplied!");
35        return Ok(());
36    }
37
38    let image = image::ImageReader::open(&image_paths[0])
39        .expect("Failed to open image!")
40        .decode()
41        .expect("Failed to decode image!");
42    let (mut width, mut height): (u32, u32) = (image.width(), image.height());
43    (width, height) = fit_image_dimensions(width, height);
44    assert!(width > 0 && height > 0);
45
46    let mut images_data: Vec<u8> = Vec::new();
47    for path in &image_paths {
48        let image = image::ImageReader::open(path)
49            .expect("Failed to open image!")
50            .decode()
51            .expect("Failed to decode image!");
52        let (i_width, i_height): (u32, u32) = (image.width(), image.height());
53        assert!(i_width > 0 && i_height > 0);
54
55        let image = if i_width > MAX_IMAGE_WIDTH || i_height > MAX_IMAGE_HEIGHT {
56            eprintln!("Image too big! scaling down...");
57            image.resize(width, height, image::imageops::FilterType::Lanczos3)
58        } else {
59            image
60        };
61
62        let img_data = bitmap_to_rgb565(&image);
63        let compressed_img_data = compress(img_data);
64
65        // Segment length
66        let f = compressed_img_data.len();
67        let f4 = ((f >> 24) & 0xFF) as u8;
68        let f3 = ((f >> 16) & 0xFF) as u8;
69        let f2 = ((f >> 8) & 0xFF) as u8;
70        let f1 = (f & 0xFF) as u8;
71
72        images_data.extend_from_slice(&[f4, f3, f2, f1]);
73        images_data.extend_from_slice(&compressed_img_data);
74    }
75    let header = &get_c2b_header(
76        images_data.len(),
77        image_paths.len(),
78        width as usize,
79        height as usize,
80    );
81    let footer = &get_c2b_footer();
82    let mut new_file: Vec<u8> = Vec::new();
83
84    new_file.extend_from_slice(header);
85    new_file.extend_from_slice(&images_data);
86    new_file.extend_from_slice(footer);
87    std::fs::write(destination, new_file)?;
88
89    Ok(())
90}
91
92/// Function to convert an image to C2P.
93///
94/// Takes two arguments: an image path, which must point to a valid image file with PNG, JPG, WEBP or BMP format.
95/// The second argument is the destination of the C2P file.
96///
97/// Example:
98/// ```
99/// let path: &str = "path/to/file.png";
100/// convert_img_to_c2p(path, "path/to/destination.c2p").unwrap();
101/// ```
102pub fn convert_img_to_c2p<T, S>(image_path: T, destination: S) -> Result<(), Box<dyn Error>>
103where
104    T: AsRef<Path> + Clone,
105    S: AsRef<Path> + Clone,
106{
107    let image = image::ImageReader::open(image_path)
108        .expect("Failed to open image!")
109        .decode()
110        .expect("Failed to decode image!");
111    let (width, height): (u32, u32) = fit_image_dimensions(image.width(), image.height());
112    assert!(width > 0 && height > 0);
113
114    let image = if width > MAX_IMAGE_WIDTH || height > MAX_IMAGE_HEIGHT {
115        eprintln!("Image too big! scaling down...");
116        let (new_width, new_height) = (
117            (width / (width / MAX_IMAGE_WIDTH)).clamp(1, MAX_IMAGE_WIDTH),
118            (height / (height / MAX_IMAGE_HEIGHT)).clamp(1, MAX_IMAGE_HEIGHT),
119        );
120        image.resize(new_width, new_height, image::imageops::FilterType::Lanczos3)
121    } else {
122        image
123    };
124
125    let img_data = bitmap_to_rgb565(&image);
126    let compressed_img_data = compress(img_data);
127
128    let header = &get_c2p_header(
129        compressed_img_data.len(),
130        image.width() as usize,
131        image.height() as usize,
132    );
133
134    let footer = &get_c2p_footer();
135
136    let mut new_file = Vec::new();
137    new_file.extend_from_slice(header);
138    new_file.extend_from_slice(&compressed_img_data);
139    new_file.extend_from_slice(footer);
140
141    std::fs::write(destination, new_file)?;
142
143    Ok(())
144}
145
146/// Function to convert a C2P image into a normal image file.
147///
148/// Takes two arguments: an image path, which must point to a valid C2P image.
149/// The second argument is the destination of the converted image file.
150///
151/// Example:
152/// ```
153/// let path: &str = "path/to/file.c2p";
154/// convert_c2p_to_img(path, "path/to/destination.png").unwrap();
155/// ```
156pub fn convert_c2p_to_img<T, S>(c2p_path: T, destination: S) -> Result<(), Box<dyn Error>>
157where
158    T: AsRef<Path> + Clone,
159    S: AsRef<Path> + Clone,
160{
161    let c2p_data: Vec<u8> = std::fs::read(c2p_path)?;
162    let (header, slice) = c2p_data.split_at(0xDC);
163    let (compressed_data, _footer) = slice.split_at(slice.len() - 0x17C);
164
165    let (width, height): (u32, u32) = (
166        ((header[0xC2] as u32) << 8) + header[0xC3] as u32,
167        ((header[0xC4] as u32) << 8) + header[0xC5] as u32,
168    );
169
170    let bytes = miniz_oxide::inflate::decompress_to_vec_zlib(compressed_data)
171        .expect("Failed to decompress compressed c2p image data!");
172
173    let mut img_buffer = image::RgbImage::new(width, height);
174
175    let (mut x, mut y) = (0, 0);
176    for i in (0..bytes.len()).step_by(2) {
177        let pixel = img_buffer.get_pixel_mut(x, y);
178
179        let rgb565: u32 = ((bytes[i] as u32) << 8) + bytes[i + 1] as u32;
180
181        let r5 = rgb565 >> 11;
182        let g6 = (rgb565 >> 5) & 0b111111;
183        let b5 = rgb565 & 0b11111;
184
185        let r = (r5 as f32 / 31.0 * 255.0) as u8;
186        let g = (g6 as f32 / 63.0 * 255.0) as u8;
187        let b = (b5 as f32 / 31.0 * 255.0) as u8;
188
189        *pixel = image::Rgb::from([r, g, b]);
190
191        x += 1;
192        if x == img_buffer.width() {
193            x = 0;
194            y += 1
195        }
196    }
197
198    img_buffer.save(destination)?;
199    Ok(())
200}
201
202/// Function to convert a C2B image into a group of normal image file.
203///
204/// Takes two arguments: an image path, which must point to a valid C2B image.
205/// The second argument is the destination of the converted image files.
206/// All images following the first will be named after the first with their respective index.
207///
208/// Example:
209/// ```
210/// let path: &str = "path/to/file.c2b";
211/// convert_c2p_to_img(path, "path/to/destination.png").unwrap();
212/// ```
213pub fn convert_c2b_to_imgs<T, S>(c2b_path: T, destination: S) -> Result<(), Box<dyn Error>>
214where
215    T: AsRef<Path> + Clone,
216    S: AsRef<Path> + Clone,
217{
218    let c2b_data: Vec<u8> = std::fs::read(c2b_path)?;
219    let (header, data) = c2b_data.split_at(0xD8);
220
221    let (width, height): (u32, u32) = (
222        ((header[0xC2] as u32) << 8) + header[0xC3] as u32,
223        ((header[0xC4] as u32) << 8) + header[0xC5] as u32,
224    );
225
226    let mut image_block_len = (((data[0x00] as u32) << 24)
227        + ((data[0x01] as u32) << 16)
228        + ((data[0x02] as u32) << 8)
229        + (data[0x03] as u32)) as usize;
230    let mut offset: usize = 0x04;
231    let mut index: usize = 0;
232
233    while data[offset] == 0x78 && data[offset + 1] == 0x9C {
234        let (mut x, mut y) = (0, 0);
235        let mut img_buffer = image::RgbImage::new(width, height);
236        let data_buffer = &data[offset..offset + image_block_len];
237        let bytes = miniz_oxide::inflate::decompress_to_vec_zlib(data_buffer)
238            .expect("Failed to decompress compressed c2p image data!");
239
240        for i in (0..bytes.len()).step_by(2) {
241            let pixel = img_buffer.get_pixel_mut(x, y);
242
243            let rgb565: u32 = ((bytes[i] as u32) << 8) + bytes[i + 1] as u32;
244
245            let r5 = rgb565 >> 11;
246            let g6 = (rgb565 >> 5) & 0b111111;
247            let b5 = rgb565 & 0b11111;
248
249            let r = (r5 as f32 / 31.0 * 255.0) as u8;
250            let g = (g6 as f32 / 63.0 * 255.0) as u8;
251            let b = (b5 as f32 / 31.0 * 255.0) as u8;
252
253            *pixel = image::Rgb::from([r, g, b]);
254
255            x += 1;
256            if x == img_buffer.width() {
257                x = 0;
258                y += 1
259            }
260        }
261
262        let tmp = image_block_len;
263        image_block_len = (((data[offset + image_block_len] as u32) << 24)
264            + ((data[offset + image_block_len + 0x01] as u32) << 16)
265            + ((data[offset + image_block_len + 0x02] as u32) << 8)
266            + (data[offset + image_block_len + 0x03] as u32)) as usize;
267        offset += tmp + 4;
268
269        let path: &Path = destination.as_ref();
270        let file_dest: &str = path
271            .file_stem()
272            .unwrap_or_default()
273            .to_str()
274            .unwrap_or_default();
275        let extension: &str = path
276            .extension()
277            .unwrap_or_default()
278            .to_str()
279            .unwrap_or_default();
280        let destination = format!("{file_dest}{index}.{extension}");
281        index += 1;
282
283        img_buffer.save(&destination)?;
284    }
285
286    Ok(())
287}
288
289/// Function to scale the initial image dimensions to fit into the maximum image scale.
290const fn fit_image_dimensions(width: u32, height: u32) -> (u32, u32) {
291    let (mut new_width, mut new_height) = (width, height);
292
293    if width > MAX_IMAGE_WIDTH || height > MAX_IMAGE_HEIGHT {
294        if width < height {
295            let aspect_ratio: f32 = width as f32 / height as f32;
296            new_height = MAX_IMAGE_HEIGHT;
297            new_width = (aspect_ratio * new_height as f32) as u32;
298        } else {
299            let aspect_ratio: f32 = height as f32 / width as f32;
300            new_width = MAX_IMAGE_WIDTH;
301            new_height = (aspect_ratio * new_width as f32) as u32;
302        }
303    }
304
305    (new_width, new_height)
306}
307
308/// Function to convert the color data of the image into the RGB565 color format.
309fn bitmap_to_rgb565(image: &image::DynamicImage) -> Vec<u8> {
310    let w = image.width();
311    let h = image.height();
312    let mut image_data = Vec::with_capacity((w * h) as usize * 2);
313
314    for y in 0..h {
315        for x in 0..w {
316            let c = image.get_pixel(x, y).to_rgb().0;
317
318            let (r, g, b) = (
319                convert_range(0, 255, 0, 0x1F, c[0] as usize),
320                convert_range(0, 255, 0, 0x3F, c[1] as usize),
321                convert_range(0, 255, 0, 0x1F, c[2] as usize),
322            );
323
324            let rgb565: usize = (r << 11) + (g << 5) + b;
325            let rgb565_2: u8 = (rgb565 >> 8 & 0xFF) as u8;
326            let rgb565_1: u8 = (rgb565 & 0xFF) as u8;
327
328            image_data.push(rgb565_2);
329            image_data.push(rgb565_1);
330        }
331    }
332    image_data
333}
334
335/// Function that converts the given range to correctly display the bytes.
336const fn convert_range(
337    original_start: usize,
338    original_end: usize,
339    new_start: usize,
340    new_end: usize,
341    value: usize,
342) -> usize {
343    let scale: f64 = (new_end - new_start) as f64 / (original_end - original_start) as f64;
344    (new_start as f64 + ((value - original_start) as f64 * scale)) as usize
345}
346
347/// Compresses the given image data with the default zlib compression.
348fn compress(input: Vec<u8>) -> Vec<u8> {
349    use miniz_oxide::deflate;
350    deflate::compress_to_vec_zlib(&input, deflate::CompressionLevel::DefaultLevel as u8)
351}
352
353/// Function that returns the C2P file header, with certain bytes changed depending on the file size.
354fn get_c2p_header(image_data_size: usize, width: usize, height: usize) -> Vec<u8> {
355    let file_size: usize = image_data_size + 0xDC + 0x17C; // image size + header size + footer size
356    let a = !(file_size & 0xFFFFFF) & 0xFFFFFF;
357    let a3 = (a >> 16 & 0xFF) as u8;
358    let a2 = (a >> 8 & 0xFF) as u8;
359    let a1 = (a & 0xFF) as u8;
360
361    let b1 = ((0x1D1 - (file_size & 0xFF)) & 0xFF) as u8;
362
363    let c = file_size - 0x20;
364    let c4 = (c >> 24 & 0xFF) as u8;
365    let c3 = (c >> 16 & 0xFF) as u8;
366    let c2 = (c >> 8 & 0xFF) as u8;
367    let c1 = (c & 0xFF) as u8;
368
369    let d = file_size - 0x234;
370    let d4 = (d >> 24 & 0xFF) as u8;
371    let d3 = (d >> 16 & 0xFF) as u8;
372    let d2 = (d >> 8 & 0xFF) as u8;
373    let d1 = (d & 0xFF) as u8;
374
375    let e = file_size - 0x254;
376    let e4 = (e >> 24 & 0xFF) as u8;
377    let e3 = (e >> 16 & 0xFF) as u8;
378    let e2 = (e >> 8 & 0xFF) as u8;
379    let e1 = (e & 0xFF) as u8;
380
381    let w = width & 0xFFFF;
382    let h = height & 0xFFFF;
383    let w2 = (w >> 8 & 0xFF) as u8;
384    let w1 = (w & 0xFF) as u8;
385    let h2 = (h >> 8 & 0xFF) as u8;
386    let h1 = (h & 0xFF) as u8;
387
388    let f = file_size - 0x258;
389    let f4 = (f >> 24 & 0xFF) as u8;
390    let f3 = (f >> 16 & 0xFF) as u8;
391    let f2 = (f >> 8 & 0xFF) as u8;
392    let f1 = (f & 0xFF) as u8;
393
394    vec![
395        0xBC, 0xBE, 0xAC, 0xB6, 0xB0, 0xFF, 0xFF, 0xFF, 0x9C, 0xCD, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF,
396        0xFF, 0xFF, 0xFE, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, a3, a2, a1, b1, 0x00, 0x00, 0x00, 0x00,
397        0x00, 0x43, 0x43, 0x30, 0x31, 0x30, 0x30, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x43, 0x50, 0x00,
398        0x00, 0x00, c4, c3, c2, c1, 0x00, 0x00, 0x00, 0x09, d4, d3, d2, d1, 0x00, 0x00, 0x00, 0x00,
399        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
400        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
401        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
402        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
403        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
404        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
405        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
406        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
407        0x30, 0x31, 0x30, 0x30, e4, e3, e2, e1, 0x00, 0x00, w2, w1, h2, h1, 0x00, 0x10, 0x00, 0xFF,
408        0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, f4, f3,
409        f2, f1,
410    ]
411}
412
413/// Function that returns the C2P footer.
414/// This is the same for all C2P files.
415fn get_c2p_footer() -> Vec<u8> {
416    vec![
417        0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x8C, 0x07, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
418        0x00, 0x00, 0x00, 0x60, 0x00, 0x07, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
419        0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x05,
420        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x98, 0x04, 0x60, 0x00, 0x00,
421        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
422        0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
423        0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x98, 0x00,
424        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x28, 0x31, 0x85,
425        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x28, 0x32, 0x00, 0x00, 0x00, 0x00,
426        0x00, 0x00, 0x00, 0x09, 0x98, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x31,
427        0x30, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428        0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
429        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x50, 0x00,
430        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
431        0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432        0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
433        0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x00, 0x00,
434        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
435        0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
436        0x00, 0x10, 0x00, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01,
437        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x07, 0x00, 0x00,
438        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x14, 0x15, 0x93, 0x00, 0x00,
439        0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x03, 0x14, 0x15, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00,
440        0x00, 0x10, 0x00, 0x03, 0x14, 0x15, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
441        0x03, 0x14, 0x15, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x01, 0x01,
442        0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
443    ]
444}
445
446/// Function that returns the C2B file header, with certain bytes changed depending on the file size.
447fn get_c2b_header(
448    image_data_size: usize,
449    total_image_count: usize,
450    width: usize,
451    height: usize,
452) -> Vec<u8> {
453    let file_size: usize = image_data_size + 0xD8 + 0x1A8; // image size + header size + footer size
454    let a = !(file_size & 0xFFFFFF) & 0xFFFFFF;
455    let a3 = (a >> 16 & 0xFF) as u8;
456    let a2 = (a >> 8 & 0xFF) as u8;
457    let a1 = (a & 0xFF) as u8;
458
459    let b1 = ((0x1D0 - (file_size & 0xFF)) & 0xFF) as u8;
460
461    let c = file_size - 0x20;
462    let c4 = (c >> 24 & 0xFF) as u8;
463    let c3 = (c >> 16 & 0xFF) as u8;
464    let c2 = (c >> 8 & 0xFF) as u8;
465    let c1 = (c & 0xFF) as u8;
466
467    let d = file_size - 0x260;
468    let d4 = (d >> 24 & 0xFF) as u8;
469    let d3 = (d >> 16 & 0xFF) as u8;
470    let d2 = (d >> 8 & 0xFF) as u8;
471    let d1 = (d & 0xFF) as u8;
472
473    let e = file_size - 0x280;
474    let e4 = (e >> 24 & 0xFF) as u8;
475    let e3 = (e >> 16 & 0xFF) as u8;
476    let e2 = (e >> 8 & 0xFF) as u8;
477    let e1 = (e & 0xFF) as u8;
478
479    let w = width & 0xFFFF;
480    let h = height & 0xFFFF;
481    let w2 = (w >> 8 & 0xFF) as u8;
482    let w1 = (w & 0xFF) as u8;
483    let h2 = (h >> 8 & 0xFF) as u8;
484    let h1 = (h & 0xFF) as u8;
485
486    let i = total_image_count as u8;
487
488    vec![
489        0xBC, 0xBE, 0xAC, 0xB6, 0xB0, 0xFF, 0xFF, 0xFF, 0x9C, 0xCD, 0x9D, 0xFF, 0xFF, 0xFF, 0xFF,
490        0xFF, 0xFF, 0xFE, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, a3, a2, a1, b1, 0x00, 0x00, 0x00, 0x00,
491        0x00, 0x43, 0x43, 0x30, 0x31, 0x30, 0x30, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x43, 0x50, 0x00,
492        0x00, 0x00, c4, c3, c2, c1, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, d4, d3, d2, d1,
493        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7C, 0x00, 0x00, 0x00,
494        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501        0x30, 0x31, 0x30, 0x30, e4, e3, e2, e1, 0x00, 0x00, w2, w1, h2, h1, 0x00, 0x10, 0x00, 0xFF,
502        0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x01, 0x00, i, 0xFF, 0xFF, 0xFF, 0xFF,
503    ]
504}
505
506/// Function that returns the C2B footer.
507/// Note: this is the same for all files in this program. There are certain bytes that change the size of the window in the 'Picture Plot' program, which are ignored here.
508fn get_c2b_footer() -> Vec<u8> {
509    vec![
510        0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
511        0x00, 0x00, 0x00, 0x10, 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
512        0x10, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03,
513        0x24, 0x67, 0x53, 0x24, 0x67, 0x53, 0x25, 0x00, 0x00, 0x09, 0x97, 0x01, 0x02, 0x54, 0x71,
514        0x69, 0x81, 0x13, 0x21, 0x00, 0x00, 0x10, 0x01, 0x01, 0x07, 0x45, 0x28, 0x30, 0x18, 0x86,
515        0x79, 0x00, 0x00, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
516        0x10, 0x00, 0x02, 0x35, 0x84, 0x90, 0x56, 0x60, 0x37, 0x74, 0x00, 0x00, 0x09, 0x97, 0x00,
517        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x28, 0x31, 0x85,
518        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x28, 0x32, 0x00, 0x00, 0x00, 0x00,
519        0x00, 0x00, 0x00, 0x09, 0x98, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x31,
520        0x30, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
521        0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
522        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x50, 0x00,
523        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
524        0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
525        0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
526        0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x00, 0x00,
527        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
528        0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
529        0x00, 0x10, 0x00, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01,
530        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x07, 0x00, 0x00,
531        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x14, 0x15, 0x93, 0x00, 0x00,
532        0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x03, 0x14, 0x15, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00,
533        0x00, 0x10, 0x00, 0x03, 0x14, 0x15, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
534        0x03, 0x14, 0x15, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x01, 0x01,
535        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
536        0x50, 0x47, 0x54, 0x69, 0x6D, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
537        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
538        0x00, 0x00, 0x09, 0x99,
539    ]
540}