use crate::deflate::deflate; pub struct Entry { pub name: String, stored: Vec, uncompressed_size: u32, method: u16, offset: u32, crc: u32, } pub struct Zip { entries: Vec, } impl Zip { pub fn new() -> Self { Zip { entries: Vec::new() } } pub fn add(&mut self, name: &str, data: Vec) { let crc = crc32(&data); let size = data.len() as u32; self.entries.push(Entry { name: name.to_string(), stored: data, uncompressed_size: size, method: 0, offset: 0, crc, }); } pub fn add_deflated(&mut self, name: &str, data: Vec) { let crc = crc32(&data); let uncompressed_size = data.len() as u32; let compressed = deflate(&data); if compressed.len() < data.len() { self.entries.push(Entry { name: name.to_string(), stored: compressed, uncompressed_size, method: 8, offset: 0, crc, }); } else { self.add(name, data); } } pub fn finish(mut self) -> Vec { let mut out = Vec::new(); for e in self.entries.iter_mut() { e.offset = out.len() as u32; write_local_header(&mut out, e); out.extend_from_slice(&e.stored); } let cd_start = out.len() as u32; for e in &self.entries { write_central_header(&mut out, e); } let cd_size = out.len() as u32 - cd_start; write_eocd(&mut out, self.entries.len() as u16, cd_size, cd_start); out } } fn write_local_header(out: &mut Vec, e: &Entry) { out.extend_from_slice(&0x0403_4b50u32.to_le_bytes()); out.extend_from_slice(&20u16.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&e.method.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0x21u16.to_le_bytes()); out.extend_from_slice(&e.crc.to_le_bytes()); out.extend_from_slice(&(e.stored.len() as u32).to_le_bytes()); out.extend_from_slice(&e.uncompressed_size.to_le_bytes()); out.extend_from_slice(&(e.name.len() as u16).to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(e.name.as_bytes()); } fn write_central_header(out: &mut Vec, e: &Entry) { out.extend_from_slice(&0x0201_4b50u32.to_le_bytes()); out.extend_from_slice(&20u16.to_le_bytes()); out.extend_from_slice(&20u16.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&e.method.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0x21u16.to_le_bytes()); out.extend_from_slice(&e.crc.to_le_bytes()); out.extend_from_slice(&(e.stored.len() as u32).to_le_bytes()); out.extend_from_slice(&e.uncompressed_size.to_le_bytes()); out.extend_from_slice(&(e.name.len() as u16).to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&e.offset.to_le_bytes()); out.extend_from_slice(e.name.as_bytes()); } fn write_eocd(out: &mut Vec, count: u16, cd_size: u32, cd_offset: u32) { 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(&cd_size.to_le_bytes()); out.extend_from_slice(&cd_offset.to_le_bytes()); out.extend_from_slice(&0u16.to_le_bytes()); } 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 }