Files
APK/zip.rs
T
2026-07-12 19:37:19 +00:00

131 lines
4.1 KiB
Rust

use crate::deflate::deflate;
pub struct Entry {
pub name: String,
stored: Vec<u8>,
uncompressed_size: u32,
method: u16,
offset: u32,
crc: u32,
}
pub struct Zip {
entries: Vec<Entry>,
}
impl Zip {
pub fn new() -> Self {
Zip { entries: Vec::new() }
}
pub fn add(&mut self, name: &str, data: Vec<u8>) {
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<u8>) {
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<u8> {
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<u8>, 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<u8>, 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<u8>, 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
}