the better update

This commit is contained in:
endernon 2024-12-06 22:58:45 +00:00
parent 022dbec73e
commit 58dbe405d1
4 changed files with 163 additions and 42 deletions

9
Cargo.lock generated
View file

@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler2"
version = "2.0.0"
@ -965,9 +971,12 @@ dependencies = [
name = "respack-decrypter"
version = "0.1.0"
dependencies = [
"adler",
"clap",
"crc32fast",
"glob",
"image",
"miniz_oxide",
"oxipng",
"zune-png",
]

View file

@ -9,3 +9,6 @@ glob = "0.3.1"
image = "0.25.5"
oxipng = "9.1.3"
zune-png = "0.4.10"
adler = "1.0.2"
crc32fast = "1.4.2"
miniz_oxide = "0.8.0"

134
src/catzou/mod.rs Normal file
View file

@ -0,0 +1,134 @@
use std::{
fmt::Debug,
fs::File,
io::{BufReader, BufWriter, Read, Write},
usize,
};
use std::path::PathBuf;
use adler::Adler32;
use crc32fast::Hasher;
use miniz_oxide::inflate::core::{
decompress,
inflate_flags::{
TINFL_FLAG_IGNORE_ADLER32, TINFL_FLAG_PARSE_ZLIB_HEADER,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
},
DecompressorOxide,
};
struct Chunk {
kind: [u8; 4],
data: Vec<u8>,
crc: [u8; 4],
}
impl Debug for Chunk {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Chunk")
.field("kind", &self.kind)
.field("data len", &self.data.len())
.field("crc", &self.crc)
.finish()
}
}
impl Chunk {
fn kind_to_string(&self) -> String {
String::from_utf8_lossy(&self.kind).to_string()
}
}
pub fn encoder(inpath: PathBuf, outpath: PathBuf) {
let file = File::open(inpath).unwrap();
let mut bufread = BufReader::new(file);
// read the png header
let mut header = [0; 8];
bufread.read_exact(&mut header).unwrap();
let mut chunks = Vec::new();
let mut lenbuf = [0; 4];
while let Ok(_) = bufread.read_exact(&mut lenbuf) {
let len = u32::from_be_bytes(lenbuf);
let mut kind = [0; 4];
bufread.read_exact(&mut kind).unwrap();
let data = {
let mut data = vec![0; len as usize];
bufread.read_exact(&mut data).unwrap();
data
};
let mut crc = [0; 4];
bufread.read_exact(&mut crc).unwrap();
let mut chunk = Chunk { kind, data, crc };
println!("{:?}", chunk);
// recode the compressed image data
if chunk.kind == *b"IDAT" {
println!("Decompressing IDAT chunk");
let mut decompressor = DecompressorOxide::new();
decompressor.init();
let mut buf = vec![0; 1024 * 1024 * 1024]; // this could probably be smaller
let data = decompress(
&mut decompressor,
&chunk.data,
&mut buf,
0,
TINFL_FLAG_IGNORE_ADLER32
| TINFL_FLAG_PARSE_ZLIB_HEADER
| TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
);
println!(
"Decompressed IDAT chunk status {:?}, bytes read {}, bytes outputted {}",
data.0, data.1, data.2
);
let _ = buf.split_off(data.2);
let mut adler = Adler32::new();
adler.write_slice(&buf);
let csum = adler.checksum().to_be_bytes();
// replace the last 4 bytes of the data with the new checksum
let data_len = chunk.data.len();
chunk.data[data_len - 4..].copy_from_slice(&csum);
println!("Corrected Adler32 checksum");
}
let mut hasher = Hasher::new();
hasher.update(&chunk.kind);
hasher.update(&chunk.data);
let checksum = hasher.finalize();
if checksum != u32::from_be_bytes(chunk.crc) {
println!("CRC error in chunk {:?}", chunk.kind_to_string());
chunk.crc = checksum.to_be_bytes();
println!("Corrected CRC");
}
chunks.push(chunk);
}
let ofile = File::create(outpath).unwrap();
let mut writer = BufWriter::new(ofile);
// write a new header
writer
.write_all(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
.unwrap();
for chunk in chunks {
writer
.write_all(&u32::to_be_bytes(chunk.data.len() as u32))
.unwrap();
writer.write_all(&chunk.kind).unwrap();
writer.write_all(&chunk.data).unwrap();
writer.write_all(&chunk.crc).unwrap();
}
}

View file

@ -1,3 +1,4 @@
mod catzou;
use clap::Parser;
use glob::glob;
use image::ImageBuffer;
@ -14,6 +15,7 @@ struct Args {
/// Input path
///
/// Include a file path or a directory path. Do not use any asterisks or wildcards.
/// Do not use multiple paths in one command.
/// If the path includes spaces you may find it useful to use speechmarks (" ") around the path.
#[arg(short, long)]
path: PathBuf,
@ -21,9 +23,9 @@ struct Args {
// /// Output path
// #[arg(short, long,default_value="")]
// out: PathBuf,
/// Debug mode
/// Optional Debug mode
///
/// Either "true" or "false"
/// Either "true" or "false" in lowercase.
#[arg(short, long, default_value_t = false)]
debug: bool,
}
@ -31,7 +33,7 @@ struct Args {
fn main() {
// argument parsing yep
let Args: Args = Args::parse();
let pathfr = Args.path.clone();
let mut pathfr = Args.path.clone();
let debugmode = Args.debug;
let mut filelist: Vec<PathBuf> = Vec::new();
@ -39,9 +41,12 @@ fn main() {
let pathtype = metadata(&pathfr).unwrap();
if pathtype.is_file() {
filelist.push(PathBuf::from(format!("{:?}", pathtype)));
println!("input path FILE");
println!("input path type is FILE");
} else if pathtype.is_dir() {
println!("input path DIR");
println!("input path type is DIR");
if pathfr.ends_with("/") {
pathfr.pop();
}
// parse file list if it's a dir
for entry in glob(&format!("{}/**/*.png", pathfr.clone().display()))
.expect("Failed to read glob pattern (you should panic)")
@ -49,15 +54,17 @@ fn main() {
match entry {
Ok(path) => {
// println!("{:?}", path.display());
filelist.push(path.display().to_string().parse().unwrap());
filelist.push(
path.display().to_string().parse().unwrap()
);
}
Err(e) => {
println!("Shell globbing error");
eprintln!("Globbing error... uh oh...");
}
}
}
} else {
panic!("that input is neither file nor dir and thats scary...")
eprint!("that input is neither file nor dir and thats scary... aaa")
}
// if debugmode {
@ -67,38 +74,6 @@ fn main() {
// }
for thatpath in filelist {
encoder(thatpath, debugmode)
catzou::encoder(thatpath.to_owned(), thatpath)
}
}
fn encoder(filepath: PathBuf, modeDebug: bool) {
let file = File::open(&filepath).unwrap();
let mut data = BufReader::new(file);
let mut buf = Vec::new();
data.read_to_end(&mut buf).unwrap();
let mut decoder = PngDecoder::new_with_options(buf, DecoderOptions::new_cmd());
decoder.decode_headers().unwrap();
let dim = decoder.get_dimensions().unwrap();
//dbg!(dim);
let col = decoder.get_colorspace().unwrap();
//dbg!(col);
let data = decoder.decode_raw().unwrap();
//dbg!(&data);
let depth = decoder.get_depth().unwrap();
if modeDebug {
println!("Filepath: {:?}", filepath);
println!("Dimensions: {:?}", dim);
println!("Colorspace: {:?}", col);
println!("BitDepth: {:?}", depth);
}
// TODO: figure out propper color checks rn we assume data is RGBA
let out: ImageBuffer<image::Rgba<u8>, Vec<u8>> =
ImageBuffer::from_raw(dim.0 as u32, dim.1 as u32, data).unwrap();
out.save(filepath.clone()).unwrap();
println!("wrote file {:?}\n",filepath)
}