Last active
January 23, 2023 05:53
-
-
Save MikuAuahDark/680d203795ce4f2b9843651cb9dc3e10 to your computer and use it in GitHub Desktop.
Rust code to check if PNG is KoiKatuChara or KoiKatuClothes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Code to check whetever PNG image is normal PNG image or it also contain | |
// Koikatu data like character or clothes. | |
// Copyright (c) 2023 Miku AuahDark | |
// You can use portion of this code or as a whole without my permission. | |
// clang++ -std=c++17 -D_CRT_SECURE_NO_WARNINGS koikatu_check.cpp | |
#include <cerrno> | |
#include <cstdio> | |
#include <algorithm> | |
#include <array> | |
#include <filesystem> | |
#include <memory> | |
#include <system_error> | |
#ifdef _WIN32 | |
#define strcmp_i _stricmp | |
#else | |
#define strcmp_i strcasecmp | |
#endif | |
void readFile(void *buf, size_t size, FILE *f) | |
{ | |
if (fread(buf, 1, size, f) != size) | |
throw std::system_error(errno, std::generic_category()); | |
} | |
std::string detectType(const std::string &path) | |
{ | |
constexpr std::array<unsigned char, 8> PNG_SIGNATURE {0x89, 'P', 'N', 'G' , 0x0D, 0x0A, 0x1A, 0x0A}; | |
constexpr std::array<unsigned char, 4> IEND {'I', 'E', 'N', 'D'}; | |
std::string pngtype {"Normal PNG"}; | |
// Open file | |
std::unique_ptr<FILE, int(*)(FILE*)> f {fopen(path.c_str(), "rb"), &fclose}; | |
if (f == nullptr) | |
throw std::system_error(errno, std::generic_category()); | |
// Read signature | |
std::array<unsigned char, 8> buf; | |
readFile(buf.data(), 8, f.get()); | |
if (!std::equal(buf.begin(), buf.end(), PNG_SIGNATURE.begin(), PNG_SIGNATURE.end())) | |
throw std::runtime_error("Not PNG"); | |
// Loop until IEND | |
do | |
{ | |
constexpr std::array<unsigned char, 4> ACTL {'a', 'c', 'T', 'L'}; | |
readFile(buf.data(), 4, f.get()); | |
// Chunk size | |
size_t chunkSize = (size_t(buf[0]) << 24) | (size_t(buf[1]) << 16) | (size_t(buf[2]) << 8) | size_t(buf[3]); | |
// Read chunk name | |
readFile(buf.data(), 4, f.get()); | |
// Skip chunk and CRC | |
if (fseek(f.get(), long(chunkSize) + 4, SEEK_CUR) != 0) | |
throw std::system_error(errno, std::generic_category()); | |
// Check APNG | |
if (std::equal(ACTL.begin(), ACTL.end(), buf.begin(), buf.begin() + 4)) | |
pngtype = "Animated PNG"; | |
} while (!std::equal(IEND.begin(), IEND.end(), buf.begin(), buf.begin() + 4)); | |
// More guesswork | |
size_t readed = fread(buf.data(), 1, 8, f.get()); | |
if (feof(f.get())) | |
return pngtype; | |
else if (readed != 8) | |
return pngtype + ", unknown data"; | |
if (buf[0] == 0x64 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0) | |
{ | |
switch (buf[4]) | |
{ | |
case 0x12: | |
return std::string("Koikatu character inside ") + pngtype; | |
case 0x14: | |
return std::string("Koikatu clothes inside ") + pngtype; | |
default: | |
return pngtype + ", unknown Koikatu data"; | |
} | |
} | |
else if (buf[0] < 0x64) | |
return std::string("Koikatu scene inside ") + pngtype; | |
else | |
return pngtype + ", unknown data"; | |
} | |
void printFileStatus(const std::filesystem::path &path) | |
{ | |
std::string result; | |
try | |
{ | |
result = detectType(path.string()); | |
} | |
catch (std::runtime_error &e) | |
{ | |
result = std::string("Error: ") + e.what(); | |
} | |
printf("%s: %s\n", path.string().c_str(), result.c_str()); | |
} | |
inline int usage(char *argv0) | |
{ | |
printf("Usage: %s [-r|--recursive] <file1|dir1> <file2|dir2> ...\n", argv0); | |
return 1; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
if (argc < 2) | |
return usage(argv[0]); | |
bool recursive = strcmp_i(argv[1], "-r") == 0 || strcmp_i(argv[1], "--recursive") == 0; | |
int filesArg = 1 + recursive; | |
if (recursive && argc < 3) | |
return usage(argv[0]); | |
for (int i = filesArg; i < argc; i++) | |
{ | |
std::filesystem::path path {argv[i]}; | |
if (std::filesystem::is_directory(path)) | |
{ | |
if (recursive) | |
{ | |
for (const std::filesystem::directory_entry &p: std::filesystem::recursive_directory_iterator(path)) | |
{ | |
if (std::filesystem::is_regular_file(p)) | |
printFileStatus(p.path()); | |
} | |
} | |
else | |
{ | |
for (const std::filesystem::directory_entry &p: std::filesystem::directory_iterator(path)) | |
{ | |
if (std::filesystem::is_regular_file(p)) | |
printFileStatus(p.path()); | |
} | |
} | |
} | |
else if (std::filesystem::is_regular_file(path)) | |
printFileStatus(path); | |
} | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Code to check whetever PNG image is normal | |
// PNG image or it also contain Koikatu data | |
// like character or clothes. | |
// Copyright (c) 2020 Miku AuahDark | |
// You can use portion of this code or as a whole without my permission. | |
use std::fs; | |
use std::fs::DirEntry; | |
use std::fs::File; | |
use std::fs::Metadata; | |
use std::io::Read; | |
use std::io::Seek; | |
use std::io::SeekFrom; | |
use std::path::MAIN_SEPARATOR; | |
fn detect_type(path: &str) -> String | |
{ | |
// PNG magic byte | |
const PNG_SIGNATURE: [u8; 8] = [0x89u8, b'P', b'N', b'G' ,0x0Du8, 0x0Au8, 0x1Au8, 0x0Au8]; | |
// Open file | |
let mut f: File = match File::open(path) | |
{ | |
Err(why) => return format!("{}", why), | |
Ok(file) => file | |
}; | |
// Read PNG signature | |
let mut buf: [u8; 8] = [0u8; 8]; | |
match f.read(&mut buf) | |
{ | |
Err(why) => return format!("{}", why), | |
Ok(n) => { | |
if n != 8 | |
{ | |
return String::from("read error"); | |
} | |
} | |
}; | |
if &buf != &PNG_SIGNATURE | |
{ | |
return String::from("not PNG"); | |
} | |
// Skip all chunks until IEND | |
loop | |
{ | |
// Read chunk size | |
match f.read(&mut buf[0..4]) | |
{ | |
Err(why) => return format!("{}", why), | |
Ok(n) => { | |
if n != 4 | |
{ | |
return String::from("read error"); | |
} | |
} | |
}; | |
// Chunk size from big endian | |
let chunksize: u32 = | |
((buf[0] as u32) << 24) | | |
((buf[1] as u32) << 16) | | |
((buf[2] as u32) << 8) | | |
(buf[3] as u32); | |
// Read chunk name | |
match f.read(&mut buf[0..4]) | |
{ | |
Err(why) => return format!("{}", why), | |
Ok(n) => { | |
if n != 4 | |
{ | |
return String::from("read error"); | |
} | |
} | |
}; | |
let end: bool = &buf[0..4] == b"IEND"; | |
// Skip data and CRC | |
match f.seek(SeekFrom::Current(chunksize as i64 + 4i64)) | |
{ | |
Err(why) => return format!("{}", why), | |
Ok(_) => () | |
} | |
if end | |
{ | |
break; | |
} | |
} | |
// More guesswork | |
match f.read(&mut buf) | |
{ | |
Err(why) => return format!("{}", why), | |
Ok(n) => { | |
if n == 0 | |
{ | |
return String::from("normal PNG"); | |
} | |
else if n != 8 | |
{ | |
return String::from("normal PNG, unknown data"); | |
} | |
} | |
} | |
if buf[0] == 0x64u8 && &buf[1..4] == &[0u8; 3] | |
{ | |
return if buf[4] == 0x12u8 | |
{ | |
String::from("Koikatu character") | |
} | |
else if buf[4] == 0x14u8 | |
{ | |
String::from("Koikatu clothes") | |
} | |
else | |
{ | |
String::from("Koikatu data, unknown") | |
} | |
} | |
else if buf[0] < 0x64u8 | |
{ | |
return String::from("Koikatu scene"); | |
} | |
else | |
{ | |
return String::from("normal PNG, unknown data"); | |
} | |
} | |
fn print_file_status(path: &str) | |
{ | |
let status: String = detect_type(path); | |
println!("{0}: {1}", path, status); | |
} | |
fn scan_dir(path: &str, recursive: bool) | |
{ | |
match fs::read_dir(path) | |
{ | |
Err(why) => println!("{0}: {1}", path, why), | |
Ok(dir) => | |
{ | |
for entry in dir | |
{ | |
if entry.is_ok() | |
{ | |
let dir_entry: DirEntry = entry.unwrap(); | |
let subpath = dir_entry.path(); | |
if subpath.is_file() | |
{ | |
let filename: &str = subpath.to_str().unwrap(); | |
print_file_status(filename); | |
} | |
else if recursive && subpath.is_dir() && subpath.to_str().unwrap() != "." | |
{ | |
let filename: String = format!( | |
"{0}{1}", | |
subpath.to_str().unwrap(), | |
MAIN_SEPARATOR | |
); | |
scan_dir(&filename, recursive); | |
} | |
} | |
} | |
} | |
} | |
} | |
fn main() | |
{ | |
// Get argv | |
let argv: Vec<String> = std::env::args().collect(); | |
let argc: usize = argv.len(); | |
if argc < 2 | |
{ | |
println!("Usage: {0} [-r|--recursive] <file1|dir1> <file2|dir2> ...", argv[0]); | |
return; | |
} | |
// Check recursive flag | |
let dir_recursive: bool = argv[1] == "-r" || argv[1].to_lowercase() == "--recursive"; | |
if dir_recursive && argc < 3 | |
{ | |
println!("Usage: {0} [-r|--recursive] <file1|dir1> <file2|dir2> ...", argv[0]); | |
return; | |
} | |
// Enumerate | |
for item in argv.iter().skip(if dir_recursive {2} else {1}) | |
{ | |
let md = fs::metadata(item); | |
if md.is_ok() | |
{ | |
let metadata: Metadata = md.unwrap(); | |
if metadata.is_dir() | |
{ | |
// Scan directories, but ensure the last | |
// character is a string separator | |
if item.chars().last().unwrap() == MAIN_SEPARATOR | |
{ | |
scan_dir(item, dir_recursive); | |
} | |
else | |
{ | |
let folderpath: String = format!("{0}{1}", item, MAIN_SEPARATOR); | |
scan_dir(&folderpath, dir_recursive); | |
} | |
} | |
else | |
{ | |
// Pass file | |
print_file_status(item); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment