use std::{
io::{self, BufReader, Read, Seek, SeekFrom},
mem,
};
const EOCD_SIG: usize = 0x0605_4b50;
const EOCD_SIG_U8: [u8; 4] = (EOCD_SIG as u32).to_le_bytes();
const SIZE_OF_EOCD_SIG: usize = mem::size_of::<u32>();
#[derive(Debug)]
pub struct EndOfCentralDirectoryRecord {
pub file_offset: usize,
pub signature: [u8; 4],
pub disk_number: u16,
pub disk_with_cd: u16,
pub num_entries: u16,
pub total_entries: u16,
pub cd_size: u32,
pub cd_offset: u32,
pub comment_len: u16,
pub comment: Vec<u8>,
}
impl EndOfCentralDirectoryRecord {
pub fn to_u8(&self) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&self.signature);
data.extend_from_slice(&self.disk_number.to_le_bytes());
data.extend_from_slice(&self.disk_with_cd.to_le_bytes());
data.extend_from_slice(&self.num_entries.to_le_bytes());
data.extend_from_slice(&self.total_entries.to_le_bytes());
data.extend_from_slice(&self.cd_size.to_le_bytes());
data.extend_from_slice(&self.cd_offset.to_le_bytes());
data.extend_from_slice(&self.comment_len.to_le_bytes());
data.extend_from_slice(&self.comment);
data
}
}
pub fn find_eocd<R: Read + Seek>(
apk: &mut R,
file_len: usize,
) -> Result<EndOfCentralDirectoryRecord, io::Error> {
for i in SIZE_OF_EOCD_SIG..file_len {
let idx = -(i as i64);
apk.seek(SeekFrom::End(idx))?;
let mut reader =
BufReader::with_capacity(SIZE_OF_EOCD_SIG, apk.take(SIZE_OF_EOCD_SIG as u64));
let mut buf = [0; SIZE_OF_EOCD_SIG];
reader.read_exact(&mut buf)?;
if buf == EOCD_SIG_U8 {
if i < 22 {
continue;
}
apk.seek(SeekFrom::End(idx))?;
let mut buff_block: Vec<u8> = vec![0; i];
apk.read_exact(&mut buff_block)?;
let disk_number = match buff_block.get(4..6) {
Some(data) => u16::from_le_bytes(create_fixed_buffer_2(data)),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let disk_with_cd = match buff_block.get(6..8) {
Some(data) => u16::from_le_bytes(create_fixed_buffer_2(data)),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let num_entries = match buff_block.get(8..10) {
Some(data) => u16::from_le_bytes(create_fixed_buffer_2(data)),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let total_entries = match buff_block.get(10..12) {
Some(data) => u16::from_le_bytes(create_fixed_buffer_2(data)),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let cd_size = match buff_block.get(12..16) {
Some(data) => u32::from_le_bytes(create_fixed_buffer_4(data)),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let cd_offset = match buff_block.get(16..20) {
Some(data) => u32::from_le_bytes(create_fixed_buffer_4(data)),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let comment_len = match buff_block.get(20..22) {
Some(data) => u16::from_le_bytes(create_fixed_buffer_2(data)),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let comment = match buff_block.get(22..) {
Some(data) => data.to_vec(),
None => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid EOCD")),
};
let eocd = EndOfCentralDirectoryRecord {
file_offset: file_len - i,
signature: buf,
disk_number,
disk_with_cd,
num_entries,
total_entries,
cd_size,
cd_offset,
comment_len,
comment,
};
return Ok(eocd);
}
}
Err(io::Error::new(io::ErrorKind::NotFound, "EOCD not found"))
}
pub(crate) const fn create_fixed_buffer_4(buf: &[u8]) -> [u8; 4] {
let mut buffer = [0; 4];
buffer.copy_from_slice(buf);
buffer
}
pub(crate) const fn create_fixed_buffer_2(buf: &[u8]) -> [u8; 2] {
let mut buffer = [0; 2];
buffer.copy_from_slice(buf);
buffer
}
#[derive(Debug)]
pub struct FileOffsets {
pub start_content: usize,
pub stop_content: usize,
pub start_cd: usize,
pub stop_cd: usize,
pub start_eocd: usize,
pub stop_eocd: usize,
}
impl FileOffsets {
pub const fn new(
stop_content: usize,
start_cd: usize,
stop_cd: usize,
stop_eocd: usize,
) -> Self {
Self {
start_content: 0,
stop_content,
start_cd,
stop_cd,
start_eocd: stop_cd,
stop_eocd,
}
}
pub const fn without_signature(stop_content: usize, stop_cd: usize, stop_eocd: usize) -> Self {
Self::new(stop_content, stop_content, stop_cd, stop_eocd)
}
}