From 9003b81813ff171edfc6101868c226c5c7d1957c Mon Sep 17 00:00:00 2001 From: Igor Tolmachov Date: Fri, 8 Sep 2023 17:33:59 +0900 Subject: Add basic zip reader --- src/zip/io.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 116 insertions(+), 11 deletions(-) (limited to 'src/zip/io.rs') diff --git a/src/zip/io.rs b/src/zip/io.rs index b79ad0d..c41607f 100644 --- a/src/zip/io.rs +++ b/src/zip/io.rs @@ -1,12 +1,18 @@ +use super::datatypes::*; use super::file::{FileInfo, FileReader, FileWriter}; use crate::io::{ArchiveRead, ArchiveWrite}; -use crate::result::ArchiveResult; +use crate::result::{ArchiveError, ArchiveResult}; +use crate::utils::ReadHelper; +use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; +use std::collections::HashMap; use std::fs::File; use std::io::Read; -use std::io::{Seek, Write}; +use std::io::{Seek, SeekFrom, Write}; pub struct Reader { reader: R, + files: HashMap, + comment: String, } impl ArchiveRead for Reader { @@ -14,16 +20,115 @@ impl ArchiveRead for Reader { type FileInfo = FileInfo; type FileReader = FileReader; - fn new(reader: Self::Reader) -> ArchiveResult { - Ok(Self { reader }) + fn new(mut reader: Self::Reader) -> ArchiveResult { + let cd_size: u64; + let cd_offset: u64; + let cd_entries: u64; + + let (eocd_offset, eocd, comment) = EndOfCentralDirectoryRecord::find(&mut reader)?; + if let Some((eocd64_offset, eocd64, eocd64_extensible)) = + Zip64EndOfCentralDirectoryRecord::find(&mut reader, eocd_offset)? + { + cd_size = eocd64.size_of_cd_records; + cd_offset = eocd64.offset_of_cd_entries; + cd_entries = eocd64.entries_in_cd; + } else { + cd_size = eocd.size_of_cd_records as u64; + cd_offset = eocd.offset_of_cd_entries as u64; + cd_entries = eocd.entries_in_cd as u64; + } + + let mut buf = vec![0; cd_size as usize]; + reader.seek(SeekFrom::Start(cd_offset))?; + reader.read(&mut buf)?; + + let mut buf: &[u8] = &buf; + let mut files = HashMap::with_capacity(cd_entries as usize); + + for entry_number in 0..cd_entries { + let cd = CentralDirectoryRecord::read(&mut buf)?; + + let file_name = String::from_utf8(buf.read2vec(cd.file_name_length as usize)?).or( + Err(ArchiveError::IncorrectString { + location: "file_name", + }), + )?; + let mut extra_field: &[u8] = &buf.read2vec(cd.extra_field_length as usize)?; + let file_comment = String::from_utf8(buf.read2vec(cd.file_comment_length as usize)?) + .or(Err(ArchiveError::IncorrectString { + location: "file_comment", + }))?; + + let mut uncompressed_size: u64 = cd.uncompressed_size as u64; + let mut compressed_size: u64 = cd.compressed_size as u64; + let mut header_offset: u64 = cd.header_offset as u64; + + while extra_field.len() > 0 { + let header = u16::from_le_bytes(extra_field.read2buf()?); + let size = u16::from_le_bytes(extra_field.read2buf()?); + let mut data: &[u8] = &extra_field.read2vec(size as usize)?; + + match header { + 0x0001 => { + if uncompressed_size == 0xFFFFFFFF { + uncompressed_size = u64::from_le_bytes(data.read2buf()?); + } + if compressed_size == 0xFFFFFFFF { + compressed_size = u64::from_le_bytes(data.read2buf()?); + } + if header_offset == 0xFFFFFFFF { + header_offset = u64::from_le_bytes(data.read2buf()?); + } + } + _ => {} + }; + } + + let year = ((cd.last_mod_file_date >> 9) & 0x7F) + 1980; + let month = (cd.last_mod_file_date >> 5) & 0xF; + let day = cd.last_mod_file_date & 0x1F; + + let hour = (cd.last_mod_file_time >> 11) & 0x1F; + let min = (cd.last_mod_file_time >> 5) & 0x3F; + let sec = (cd.last_mod_file_time & 0x1F) * 2; + + files.insert( + file_name.clone(), + FileInfo::new( + entry_number, + cd.version_made_by, + cd.version_needed, + super::file::GeneralPurposeBitFlag {}, + cd.compression_method.try_into()?, + NaiveDateTime::new( + NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32) + .ok_or(ArchiveError::IncorrectDate { year, month, day })?, + NaiveTime::from_hms_opt(hour as u32, min as u32, sec as u32) + .ok_or(ArchiveError::IncorrectTime { hour, min, sec })?, + ), + cd.crc32, + compressed_size, + uncompressed_size, + file_name, + file_comment, + header_offset, + ), + ); + } + + Ok(Self { + reader, + files, + comment, + }) } fn files(&self) -> ArchiveResult> { - Ok(Vec::new()) + todo!() } - fn file_reader(&self, name: &str) -> ArchiveResult { - Ok(Self::FileReader {}) + fn open_file(&self, name: &str) -> ArchiveResult { + todo!() } } @@ -33,17 +138,17 @@ pub struct Writer { writer: W, } -impl ArchiveWrite for Writer { +impl ArchiveWrite for Writer { type Writer = W; type FileInfo = FileInfo; type FileWriter = FileWriter; fn new(writer: Self::Writer) -> ArchiveResult { - Ok(Self { writer }) + todo!() } - fn file_writer(&self, name: &str) -> ArchiveResult { - Ok(Self::FileWriter {}) + fn create_file(&self, name: &str) -> ArchiveResult { + todo!() } } -- cgit v1.2.3