From 613e38c6b560990c77e994f2359fc4c30ff112a4 Mon Sep 17 00:00:00 2001
From: G2-Games <ke0bhogsg@gmail.com>
Date: Tue, 7 May 2024 11:38:27 -0500
Subject: [PATCH] Applied clippy suggestions, fixed errors, fixed CZ4
 implementation

---
 cz/Cargo.toml         |  1 +
 cz/src/binio.rs       |  8 ++++----
 cz/src/common.rs      |  4 ++--
 cz/src/compression.rs | 40 ++++++++++++++++-----------------------
 cz/src/dynamic.rs     | 28 ++++++++++++++++-----------
 cz/src/formats/cz1.rs |  8 ++++++--
 cz/src/formats/cz2.rs |  2 +-
 cz/src/formats/cz4.rs | 19 +++++++++++++++----
 utils/src/main.rs     | 44 ++++++++++++++++++-------------------------
 9 files changed, 80 insertions(+), 74 deletions(-)

diff --git a/cz/Cargo.toml b/cz/Cargo.toml
index 94f69da..6eeab66 100644
--- a/cz/Cargo.toml
+++ b/cz/Cargo.toml
@@ -11,3 +11,4 @@ A encoder/decoder for CZ# image files used in
 byteorder = "1.5.0"
 thiserror = "1.0.59"
 png = "0.17.13"
+image = { version = "0.25.1", default-features = false }
diff --git a/cz/src/binio.rs b/cz/src/binio.rs
index 5bf99e6..9a21db9 100644
--- a/cz/src/binio.rs
+++ b/cz/src/binio.rs
@@ -18,10 +18,10 @@ impl BitIO {
     }
 
     pub fn bit_offset(&self) -> usize {
-        self.byte_offset
+        self.bit_offset
     }
 
-    pub fn to_vec(self) -> Vec<u8> {
+    pub fn into_vec(self) -> Vec<u8> {
         self.data
     }
 
@@ -38,7 +38,7 @@ impl BitIO {
         let mut result = 0;
         for i in 0..bit_len {
             let bit_value =
-                ((self.data[self.byte_offset] as usize >> self.bit_offset as usize) & 1) as u64;
+                ((self.data[self.byte_offset] as usize >> self.bit_offset) & 1) as u64;
             self.bit_offset += 1;
 
             if self.bit_offset == 8 {
@@ -49,7 +49,7 @@ impl BitIO {
             result |= bit_value << i;
         }
 
-        return result;
+        result
     }
 
     pub fn read(&mut self, byte_len: usize) -> u64 {
diff --git a/cz/src/common.rs b/cz/src/common.rs
index 7b0b092..da2c33e 100644
--- a/cz/src/common.rs
+++ b/cz/src/common.rs
@@ -101,7 +101,7 @@ impl CzHeader for CommonHeader {
     }
 
     fn common(&self) -> &CommonHeader {
-        &self
+        self
     }
 
     fn version(&self) -> u8 {
@@ -184,7 +184,7 @@ pub fn parse_colormap<T: Seek + ReadBytesExt + Read>(
     Ok(colormap)
 }
 
-pub fn apply_palette(input: &mut Vec<u8>, palette: &[[u8; 4]]) -> Vec<u8> {
+pub fn apply_palette(input: &mut &[u8], palette: &[[u8; 4]]) -> Vec<u8> {
     let mut output_map = Vec::new();
 
     for byte in input.iter() {
diff --git a/cz/src/compression.rs b/cz/src/compression.rs
index a4c9889..aa8b93f 100644
--- a/cz/src/compression.rs
+++ b/cz/src/compression.rs
@@ -1,4 +1,5 @@
 use byteorder::{LittleEndian, ReadBytesExt};
+use image::RgbaImage;
 use std::{
     collections::BTreeMap,
     io::{Read, Seek, Write},
@@ -145,11 +146,11 @@ pub fn decompress_lzw2(input_data: &[u8], size: usize) -> Vec<u8> {
 
         //println!("{}", element);
 
-        result.write(&entry).unwrap();
+        result.write_all(&entry).unwrap();
         w.push(entry[0]);
         dictionary.insert(dictionary_count, w.clone());
         dictionary_count += 1;
-        w = entry.clone();
+        w.clone_from(&entry);
     }
     result
 }
@@ -219,7 +220,7 @@ pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
             }
         }
 
-        prev_line = curr_line.clone();
+        prev_line.clone_from(&curr_line);
         if pixel_byte_count == 4 {
             output_buf[i..i + line_byte_count].copy_from_slice(&curr_line);
         } else if pixel_byte_count == 3 {
@@ -227,7 +228,7 @@ pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
                 let loc = ((y * width) as usize + x) * 4;
 
                 output_buf[loc..loc + 4].copy_from_slice(&[
-                    curr_line[x + 0],
+                    curr_line[x],
                     curr_line[x + 1],
                     curr_line[x + 2],
                     0xFF
@@ -241,16 +242,11 @@ pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
     output_buf
 }
 
-pub fn line_diff_cz4<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
-    let width = header.width() as u32;
-    let height = header.height() as u32;
+pub fn line_diff_cz4(picture: &mut RgbaImage, pixel_byte_count: usize, data: &[u8]) {
+    let width = picture.width();
+    let height = picture.height();
     let block_height = (f32::ceil(height as f32 / 3.0) as u16) as u32;
 
-    //let pixel_byte_count = (header.depth() >> 3) as usize;
-    let pixel_byte_count = 3;
-
-    let mut output_buf = data.to_vec();
-
     let mut curr_line;
     let mut prev_line = vec![0u8; width as usize * pixel_byte_count];
 
@@ -265,30 +261,26 @@ pub fn line_diff_cz4<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
         }
 
         for x in 0..width as usize {
-            let loc = ((y * width) as usize + x) * 4;
-
             if pixel_byte_count == 1 {
-                output_buf[loc + 4] = curr_line[x];
+                picture.get_pixel_mut(x as u32, y).0[3] = curr_line[x];
             } else if pixel_byte_count == 4 {
-                output_buf[loc..loc + 4].copy_from_slice(&[
-                    curr_line[x * pixel_byte_count + 0],
+                picture.get_pixel_mut(x as u32, y).0 = [
+                    curr_line[x * pixel_byte_count],
                     curr_line[x * pixel_byte_count + 1],
                     curr_line[x * pixel_byte_count + 2],
                     curr_line[x * pixel_byte_count + 3],
-                ]);
+                ];
             } else if pixel_byte_count == 3 {
-                output_buf[loc..loc + 4].copy_from_slice(&[
-                    curr_line[x * pixel_byte_count + 0],
+                picture.get_pixel_mut(x as u32, y).0 = [
+                    curr_line[x * pixel_byte_count],
                     curr_line[x * pixel_byte_count + 1],
                     curr_line[x * pixel_byte_count + 2],
                     0xFF,
-                ]);
+                ];
             }
         }
 
-        prev_line = curr_line.clone();
+        prev_line.clone_from(&curr_line);
         i += width as usize * pixel_byte_count;
     }
-
-    output_buf
 }
diff --git a/cz/src/dynamic.rs b/cz/src/dynamic.rs
index 81e5deb..1aac7f4 100644
--- a/cz/src/dynamic.rs
+++ b/cz/src/dynamic.rs
@@ -1,4 +1,4 @@
-use std::io::{Read, Seek};
+use std::{io::{Read, Seek}, path::{Path, PathBuf}};
 use byteorder::ReadBytesExt;
 
 use crate::{
@@ -20,12 +20,18 @@ pub enum DynamicCz {
 }
 
 impl DynamicCz {
-    pub fn save_as_png(&self, name: &str) -> Result<(), png::EncodingError> {
-        let file = std::fs::File::create(name).unwrap();
-        let ref mut w = std::io::BufWriter::new(file);
+    pub fn open<P: ?Sized + AsRef<Path>>(path: &P) -> Result<Self, CzError> {
+        let mut img_file = std::fs::File::open(path)?;
+
+        Self::decode(&mut img_file)
+    }
+
+    pub fn save_as_png<P: ?Sized + AsRef<Path>>(&self, path: &P) -> Result<(), png::EncodingError> {
+        let file = std::fs::File::create(path).unwrap();
+        let writer = std::io::BufWriter::new(file);
 
         let mut encoder = png::Encoder::new(
-            w,
+            writer,
             self.header().width() as u32,
             self.header().height() as u32,
         );
@@ -33,7 +39,7 @@ impl DynamicCz {
         encoder.set_depth(png::BitDepth::Eight);
         let mut writer = encoder.write_header()?;
 
-        writer.write_image_data(&self.bitmap())?; // Save
+        writer.write_image_data(self.bitmap())?; // Save
 
         Ok(())
     }
@@ -64,11 +70,11 @@ impl CzImage for DynamicCz {
 
     fn header(&self) -> &Self::Header {
         match self {
-            DynamicCz::CZ0(img) => &img.header().common(),
-            DynamicCz::CZ1(img) => &img.header().common(),
-            DynamicCz::CZ2(img) => &img.header().common(),
-            DynamicCz::CZ3(img) => &img.header().common(),
-            DynamicCz::CZ4(img) => &img.header().common(),
+            DynamicCz::CZ0(img) => img.header().common(),
+            DynamicCz::CZ1(img) => img.header().common(),
+            DynamicCz::CZ2(img) => img.header().common(),
+            DynamicCz::CZ3(img) => img.header().common(),
+            DynamicCz::CZ4(img) => img.header().common(),
         }
     }
 
diff --git a/cz/src/formats/cz1.rs b/cz/src/formats/cz1.rs
index e8adb76..2c035b4 100644
--- a/cz/src/formats/cz1.rs
+++ b/cz/src/formats/cz1.rs
@@ -21,13 +21,17 @@ impl CzImage for Cz1Image {
 
     fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
         // Get the header from the input
-        let header = CommonHeader::new(bytes).unwrap();
+        let mut header = CommonHeader::new(bytes).unwrap();
         bytes.seek(SeekFrom::Start(header.length() as u64))?;
 
         if header.version() != 1 {
             return Err(CzError::VersionMismatch(1, header.version()));
         }
 
+        if header.depth() > 32 {
+            header.depth = 8
+        }
+
         // The color palette, gotten for 8 and 4 BPP images
         let mut palette = None;
         if header.depth() == 8 || header.depth() == 4 {
@@ -45,7 +49,7 @@ impl CzImage for Cz1Image {
                 bitmap.clone_into(raw);
             }
 
-            bitmap = apply_palette(&mut bitmap, pal);
+            bitmap = apply_palette(&mut bitmap.as_slice(), pal);
         }
 
         let image = Self {
diff --git a/cz/src/formats/cz2.rs b/cz/src/formats/cz2.rs
index d311c08..b701acd 100644
--- a/cz/src/formats/cz2.rs
+++ b/cz/src/formats/cz2.rs
@@ -102,7 +102,7 @@ impl CzImage for Cz2Image {
 
         // Apply the palette if it exists
         if let Some(pal) = &palette {
-            bitmap = apply_palette(&mut bitmap, pal);
+            bitmap = apply_palette(&mut bitmap.as_slice(), pal);
         }
 
         let image = Self {
diff --git a/cz/src/formats/cz4.rs b/cz/src/formats/cz4.rs
index fe0863d..cb28cb3 100644
--- a/cz/src/formats/cz4.rs
+++ b/cz/src/formats/cz4.rs
@@ -76,11 +76,22 @@ impl CzImage for Cz4Image {
         let block_info = parse_chunk_info(bytes)?;
         bytes.seek(SeekFrom::Start(block_info.length as u64))?;
 
-        let bitmap = decompress(bytes, &block_info)?;
+        let pcount = (header.width() as usize * header.height() as usize) * 3;
+        let data = decompress(bytes, &block_info)?;
+        let data2 = data[pcount..].to_vec();
 
-        let bitmap = line_diff_cz4(&header, &bitmap);
+        let mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32);
 
-        Ok(Self { header, bitmap })
+        let pixel_byte_count = 3;
+        line_diff_cz4(&mut picture, pixel_byte_count, &data);
+
+        let pixel_byte_count = 1;
+        line_diff_cz4(&mut picture, pixel_byte_count, &data2);
+
+        Ok(Self {
+            header,
+            bitmap: picture.into_raw()
+        })
     }
 
     fn header(&self) -> &Self::Header {
@@ -104,6 +115,6 @@ impl CzImage for Cz4Image {
     }
 
     fn set_bitmap(&mut self, bitmap: &[u8], header: &Self::Header) {
-        todo!()
+        self.bitmap = bitmap.to_vec();
     }
 }
diff --git a/utils/src/main.rs b/utils/src/main.rs
index 231ba6b..1d9c382 100644
--- a/utils/src/main.rs
+++ b/utils/src/main.rs
@@ -1,43 +1,35 @@
-use cz::{dynamic::DynamicCz, CzImage};
-use std::fs;
+use std::{fs::DirBuilder, time::{Duration, Instant}};
+
+use cz::dynamic::DynamicCz;
 use walkdir::WalkDir;
 
 fn main() {
-    if let Err(err) = fs::DirBuilder::new().create("test/") {
-        println!("{}", err);
-    }
+    let _ = DirBuilder::new().create("test");
 
-    let mut success = 0;
-    let mut failure = 0;
-    for entry in WalkDir::new("../../test_files") {
+    let mut total_time = Duration::default();
+    let mut num_images = 0;
+    for entry in WalkDir::new("../../test_files/loopers/") {
         let entry = entry.unwrap();
-
         if entry.path().is_dir() {
             continue;
         }
 
-        let mut input = match fs::File::open(entry.path()) {
-            Ok(file) => file,
-            Err(_) => continue,
-        };
-
-        let img_file = match DynamicCz::decode(&mut input) {
-            Ok(file) => file,
+        let timer = Instant::now();
+        let img = match DynamicCz::open(entry.path()) {
+            Ok(img) => img,
             Err(err) => {
-                println!(
-                    "{}: {}",
-                    entry.path().file_name().unwrap().to_string_lossy(),
-                    err,
-                );
-                failure += 1;
+                println!("{}: {}", entry.path().file_name().unwrap().to_string_lossy(), err);
                 continue;
             },
         };
+        let elapsed = timer.elapsed();
+        total_time += elapsed;
+        num_images += 1;
 
-        success += 1;
-
-        img_file.save_as_png(
-            &format!("test/z-{}.png", entry.path().file_stem().unwrap().to_string_lossy())
+        img.save_as_png(
+            &format!("test/{}.png", entry.path().file_name().unwrap().to_string_lossy())
         ).unwrap();
     }
+
+    dbg!(total_time / num_images);
 }