the better update
This commit is contained in:
parent
022dbec73e
commit
58dbe405d1
4 changed files with 163 additions and 42 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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
134
src/catzou/mod.rs
Normal 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();
|
||||
}
|
||||
}
|
59
src/main.rs
59
src/main.rs
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue