use std::io; use result::{ZipResult, ZipError}; use std::iter::range_step_inclusive; pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50; pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50; static CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50; pub struct CentralDirectoryEnd { pub disk_number: u16, pub disk_with_central_directory: u16, pub number_of_files_on_this_disk: u16, pub number_of_files: u16, pub central_directory_size: u32, pub central_directory_offset: u32, pub zip_file_comment: Vec, } impl CentralDirectoryEnd { pub fn parse(reader: &mut T) -> ZipResult { let magic = try!(reader.read_le_u32()); if magic != CENTRAL_DIRECTORY_END_SIGNATURE { return Err(ZipError::UnsupportedZipFile("Invalid digital signature header")) } let disk_number = try!(reader.read_le_u16()); let disk_with_central_directory = try!(reader.read_le_u16()); let number_of_files_on_this_disk = try!(reader.read_le_u16()); let number_of_files = try!(reader.read_le_u16()); let central_directory_size = try!(reader.read_le_u32()); let central_directory_offset = try!(reader.read_le_u32()); let zip_file_comment_length = try!(reader.read_le_u16()) as uint; let zip_file_comment = try!(reader.read_exact(zip_file_comment_length)); Ok(CentralDirectoryEnd { disk_number: disk_number, disk_with_central_directory: disk_with_central_directory, number_of_files_on_this_disk: number_of_files_on_this_disk, number_of_files: number_of_files, central_directory_size: central_directory_size, central_directory_offset: central_directory_offset, zip_file_comment: zip_file_comment, }) } pub fn find_and_parse(reader: &mut T) -> ZipResult { let header_size = 22; let bytes_between_magic_and_comment_size = header_size - 6; try!(reader.seek(0, io::SeekEnd)); let file_length = try!(reader.tell()) as i64; let search_upper_bound = ::std::cmp::max(0, file_length - header_size - ::std::u16::MAX as i64); for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1) { try!(reader.seek(pos, io::SeekSet)); if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE { try!(reader.seek(bytes_between_magic_and_comment_size, io::SeekCur)); let comment_length = try!(reader.read_le_u16()) as i64; if file_length - pos - header_size == comment_length { try!(reader.seek(pos, io::SeekSet)); return CentralDirectoryEnd::parse(reader); } } } Err(ZipError::UnsupportedZipFile("Could not find central directory end")) } pub fn write(&self, writer: &mut T) -> ZipResult<()> { try!(writer.write_le_u32(CENTRAL_DIRECTORY_END_SIGNATURE)); try!(writer.write_le_u16(self.disk_number)); try!(writer.write_le_u16(self.disk_with_central_directory)); try!(writer.write_le_u16(self.number_of_files_on_this_disk)); try!(writer.write_le_u16(self.number_of_files)); try!(writer.write_le_u32(self.central_directory_size)); try!(writer.write_le_u32(self.central_directory_offset)); try!(writer.write_le_u16(self.zip_file_comment.len() as u16)); try!(writer.write(self.zip_file_comment.as_slice())); Ok(()) } }