#![allow(dead_code)]
const UNIX_FILE: (u16, u32) = (0x031E, 0o100_644 << 16);
pub struct Member {
pub name: Vec<u8>,
pub data: Vec<u8>,
pub flags: u16,
pub method: u16,
pub version_made_by: u16,
pub external_attributes: u32,
}
impl Member {
pub fn new(name: &str, data: &[u8]) -> Self {
Self {
name: name.as_bytes().to_vec(),
data: data.to_vec(),
flags: 0,
method: 0,
version_made_by: UNIX_FILE.0,
external_attributes: UNIX_FILE.1,
}
}
pub fn named_raw(name: &[u8], data: &[u8]) -> Self {
let mut m = Self::new("", data);
m.name = name.to_vec();
m
}
pub fn flagged_utf8(mut self) -> Self {
self.flags |= 1 << 11;
self
}
pub fn encrypted(mut self) -> Self {
self.flags |= 1;
self
}
pub fn claims_method(mut self, method: u16) -> Self {
self.method = method;
self
}
pub fn symlink(self) -> Self {
self.with_mode(0o120_777)
}
pub fn with_mode(mut self, mode: u32) -> Self {
self.external_attributes = mode << 16;
self
}
pub fn dos_made(mut self) -> Self {
self.version_made_by = 0x0014;
self.external_attributes = 0x20;
self
}
}
pub fn raw_zip(members: &[Member]) -> Vec<u8> {
let mut out = Vec::new();
let mut central = Vec::new();
for m in members {
let crc = crc32fast::hash(&m.data);
let offset = u32::try_from(out.len()).expect("fixture under 4 GiB");
let len = u32::try_from(m.data.len()).expect("fixture under 4 GiB");
let name_len = u16::try_from(m.name.len()).expect("name under 64 KiB");
out.extend_from_slice(&0x0403_4B50u32.to_le_bytes());
out.extend_from_slice(&20u16.to_le_bytes()); out.extend_from_slice(&m.flags.to_le_bytes());
out.extend_from_slice(&m.method.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0x21u16.to_le_bytes()); out.extend_from_slice(&crc.to_le_bytes());
out.extend_from_slice(&len.to_le_bytes()); out.extend_from_slice(&len.to_le_bytes()); out.extend_from_slice(&name_len.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&m.name);
out.extend_from_slice(&m.data);
central.extend_from_slice(&0x0201_4B50u32.to_le_bytes());
central.extend_from_slice(&m.version_made_by.to_le_bytes());
central.extend_from_slice(&20u16.to_le_bytes());
central.extend_from_slice(&m.flags.to_le_bytes());
central.extend_from_slice(&m.method.to_le_bytes());
central.extend_from_slice(&0u16.to_le_bytes());
central.extend_from_slice(&0x21u16.to_le_bytes());
central.extend_from_slice(&crc.to_le_bytes());
central.extend_from_slice(&len.to_le_bytes());
central.extend_from_slice(&len.to_le_bytes());
central.extend_from_slice(&name_len.to_le_bytes());
central.extend_from_slice(&0u16.to_le_bytes()); central.extend_from_slice(&0u16.to_le_bytes()); central.extend_from_slice(&0u16.to_le_bytes()); central.extend_from_slice(&0u16.to_le_bytes()); central.extend_from_slice(&m.external_attributes.to_le_bytes());
central.extend_from_slice(&offset.to_le_bytes());
central.extend_from_slice(&m.name);
}
let count = u16::try_from(members.len()).expect("under 64 Ki members");
let central_len = u32::try_from(central.len()).expect("directory under 4 GiB");
let central_at = u32::try_from(out.len()).expect("fixture under 4 GiB");
out.extend_from_slice(¢ral);
out.extend_from_slice(&0x0605_4B50u32.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&count.to_le_bytes());
out.extend_from_slice(&count.to_le_bytes());
out.extend_from_slice(¢ral_len.to_le_bytes());
out.extend_from_slice(¢ral_at.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes()); out
}
pub fn metadata(payload_file: &str) -> String {
let escaped: String = payload_file
.chars()
.map(|c| match c {
'\\' => "\\\\".to_string(),
'"' => "\\\"".to_string(),
c if c.is_ascii() && c.is_control() => format!("\\u{:04X}", c as u32),
c => c.to_string(),
})
.collect();
format!("slipcase_version = \"1.0\"\n\n[payload]\nfile = \"{escaped}\"\n")
}
pub fn container(payload_file: &str, payload: &[u8]) -> Vec<u8> {
raw_zip(&[
Member::new(slpc::METADATA_MEMBER, metadata(payload_file).as_bytes()),
Member::new(payload_file, payload),
])
}
pub fn open(bytes: &[u8]) -> slpc::Result<slpc::Container<std::io::Cursor<Vec<u8>>>> {
slpc::Container::read(std::io::Cursor::new(bytes.to_vec()))
}
pub fn payload_of(c: &mut slpc::Container<std::io::Cursor<Vec<u8>>>) -> Vec<u8> {
use std::io::Read;
let mut got = Vec::new();
c.payload().unwrap().read_to_end(&mut got).unwrap();
got
}