#![allow(dead_code)]
use std::io::{Cursor, Write};
use std::path::{Path, PathBuf};
pub fn brush_archive(name: &str) -> Vec<u8> {
use plist::{Dictionary, Uid, Value};
let mut settings = Dictionary::new();
settings.insert("name".into(), Value::Uid(Uid::new(2)));
let mut top = Dictionary::new();
top.insert("root".into(), Value::Uid(Uid::new(1)));
let mut root = Dictionary::new();
root.insert("$version".into(), Value::Integer(100_000.into()));
root.insert("$archiver".into(), Value::String("NSKeyedArchiver".into()));
root.insert(
"$objects".into(),
Value::Array(vec![
Value::String("$null".into()),
Value::Dictionary(settings),
Value::String(name.into()),
]),
);
root.insert("$top".into(), Value::Dictionary(top));
let mut out = Vec::new();
Value::Dictionary(root)
.to_writer_binary(&mut out)
.expect("binary plist");
out
}
pub fn brushset_plist(name: &str, uuids: &[&str]) -> Vec<u8> {
use plist::{Dictionary, Value};
let mut dict = Dictionary::new();
dict.insert("name".into(), Value::String(name.into()));
dict.insert(
"brushes".into(),
Value::Array(uuids.iter().map(|u| Value::String((*u).into())).collect()),
);
let mut out = Vec::new();
Value::Dictionary(dict)
.to_writer_binary(&mut out)
.expect("binary plist");
out
}
pub fn crc32(data: &[u8]) -> u32 {
let mut crc: u32 = 0xFFFF_FFFF;
for &b in data {
crc ^= b as u32;
for _ in 0..8 {
let mask = (crc & 1).wrapping_neg();
crc = (crc >> 1) ^ (0xEDB8_8320 & mask);
}
}
!crc
}
pub fn gray_png(width: u32, height: u32, fill: u8) -> Vec<u8> {
let mut out = Vec::new();
let mut enc = png::Encoder::new(&mut out, width, height);
enc.set_color(png::ColorType::Grayscale);
enc.set_depth(png::BitDepth::Eight);
let mut w = enc.write_header().expect("png header");
w.write_image_data(&vec![fill; (width * height) as usize])
.expect("png image data");
w.finish().expect("png finish");
out
}
pub fn real_4x4_png() -> Vec<u8> {
gray_png(4, 4, 128)
}
pub fn dimension_bomb_png(w: u32, h: u32) -> Vec<u8> {
let mut png = real_4x4_png();
assert_eq!(
&png[12..16],
b"IHDR",
"the png crate must emit IHDR first for these offsets to be right"
);
png[16..20].copy_from_slice(&w.to_be_bytes());
png[20..24].copy_from_slice(&h.to_be_bytes());
let crc = crc32(&png[12..29]); png[29..33].copy_from_slice(&crc.to_be_bytes());
png
}
pub fn depth_bomb_plist_xml() -> Vec<u8> {
let mut s = String::from(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \
\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\
<plist version=\"1.0\">\n",
);
s.push_str(&"<array>".repeat(10_000));
s.into_bytes()
}
pub fn zip_with(entries: &[(&str, &[u8])]) -> Vec<u8> {
let mut zw = zip::write::ZipWriter::new(Cursor::new(Vec::new()));
let opts = zip::write::SimpleFileOptions::default();
for (path, bytes) in entries {
zw.start_file(*path, opts).expect("start_file");
zw.write_all(bytes).expect("write entry");
}
zw.finish().expect("finish zip").into_inner()
}
pub struct SampTip {
pub width: u32,
pub height: u32,
pub fill: u8,
pub corrupt: bool,
}
pub fn samp_abr(tips: &[SampTip]) -> Vec<u8> {
let mut payload = Vec::new();
for tip in tips {
let mut entry = Vec::new();
entry.extend_from_slice(&0u32.to_be_bytes());
for bound in [0, 0, tip.height as i32, tip.width as i32] {
entry.extend_from_slice(&bound.to_be_bytes());
}
entry.extend_from_slice(&8u16.to_be_bytes());
entry.push(u8::from(tip.corrupt));
entry.extend(std::iter::repeat_n(
tip.fill,
(tip.width * tip.height) as usize,
));
payload.extend_from_slice(&(entry.len() as u32).to_be_bytes());
payload.extend_from_slice(&entry);
payload.resize(payload.len().next_multiple_of(4), 0);
}
let mut file = Vec::new();
file.extend_from_slice(&6u16.to_be_bytes());
file.extend_from_slice(&2u16.to_be_bytes());
file.extend_from_slice(b"8BIMsamp");
file.extend_from_slice(&(payload.len() as u32).to_be_bytes());
file.extend_from_slice(&payload);
file
}
pub fn corpus_files(directory: &Path, files: &mut Vec<PathBuf>) {
for entry in std::fs::read_dir(directory).expect("read corpus directory") {
let path = entry.unwrap().path();
if path.is_dir() {
corpus_files(&path, files);
} else if path
.extension()
.and_then(|ext| ext.to_str())
.is_some_and(|ext| matches!(ext.to_lowercase().as_str(), "abr" | "brush" | "brushset"))
{
files.push(path);
}
}
}