aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Tolmachev <me@igorek.dev>2024-06-23 15:19:40 +0900
committerIgor Tolmachev <me@igorek.dev>2024-06-23 15:34:35 +0900
commita4e92ed9bec1f5879eb1c20dfe281c4d25ed5f89 (patch)
tree6acef99bfaf57c573b543f29836701a92c215a83
parent62aaae347d87c5c9411f1e9f8db525b7c2c603d2 (diff)
downloadarchivator-a4e92ed9bec1f5879eb1c20dfe281c4d25ed5f89.tar.gz
archivator-a4e92ed9bec1f5879eb1c20dfe281c4d25ed5f89.zip
Improve ZipFile
-rw-r--r--.vscode/settings.json1
-rw-r--r--src/structs/error.rs6
-rw-r--r--src/structs/settings.rs20
-rw-r--r--src/zip/driver.rs113
-rw-r--r--src/zip/error.rs36
-rw-r--r--src/zip/file.rs69
-rw-r--r--src/zip/mod.rs2
-rw-r--r--src/zip/structs.rs8
8 files changed, 163 insertions, 92 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index c19e694..de3e119 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,6 +6,7 @@
6 "datatypes", 6 "datatypes",
7 "datetime", 7 "datetime",
8 "eocdr", 8 "eocdr",
9 "LZMA",
9 "newtype", 10 "newtype",
10 "repr", 11 "repr",
11 "rposition" 12 "rposition"
diff --git a/src/structs/error.rs b/src/structs/error.rs
index f08c9a3..3e74e45 100644
--- a/src/structs/error.rs
+++ b/src/structs/error.rs
@@ -23,13 +23,13 @@ impl From<StructError> for ArchiveError<StructError> {
23impl Display for StructError { 23impl Display for StructError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self { 25 match self {
26 StructError::SerializationNotSupported { type_name } => { 26 Self::SerializationNotSupported { type_name } => {
27 writeln!(f, "Serialization for type '{type_name}' not supported") 27 writeln!(f, "Serialization for type '{type_name}' not supported")
28 } 28 }
29 StructError::DeserializationNotSupported { type_name } => { 29 Self::DeserializationNotSupported { type_name } => {
30 writeln!(f, "Deserialization for type '{type_name}' not supported") 30 writeln!(f, "Deserialization for type '{type_name}' not supported")
31 } 31 }
32 StructError::UnexpectedEOF => writeln!(f, "Unexpected EOF"), 32 Self::UnexpectedEOF => writeln!(f, "Unexpected EOF"),
33 } 33 }
34 } 34 }
35} 35}
diff --git a/src/structs/settings.rs b/src/structs/settings.rs
index fce6d9e..cf71453 100644
--- a/src/structs/settings.rs
+++ b/src/structs/settings.rs
@@ -20,17 +20,17 @@ macro_rules! impl_byte_order {
20 impl ByteOrder {$( 20 impl ByteOrder {$(
21 pub fn $fr(&self, num: $n) -> Vec<u8> { 21 pub fn $fr(&self, num: $n) -> Vec<u8> {
22 match self { 22 match self {
23 ByteOrder::Le => num.to_le_bytes().to_vec(), 23 Self::Le => num.to_le_bytes().to_vec(),
24 ByteOrder::Be => num.to_be_bytes().to_vec(), 24 Self::Be => num.to_be_bytes().to_vec(),
25 ByteOrder::Ne => num.to_ne_bytes().to_vec(), 25 Self::Ne => num.to_ne_bytes().to_vec(),
26 } 26 }
27 } 27 }
28 28
29 pub fn $to(&self, bytes: [u8; size_of::<$n>()]) -> $n { 29 pub fn $to(&self, bytes: [u8; size_of::<$n>()]) -> $n {
30 match self { 30 match self {
31 ByteOrder::Le => $n::from_le_bytes(bytes), 31 Self::Le => $n::from_le_bytes(bytes),
32 ByteOrder::Be => $n::from_be_bytes(bytes), 32 Self::Be => $n::from_be_bytes(bytes),
33 ByteOrder::Ne => $n::from_ne_bytes(bytes), 33 Self::Ne => $n::from_ne_bytes(bytes),
34 } 34 }
35 } 35 }
36 )+} 36 )+}
@@ -55,10 +55,10 @@ impl_byte_order!(
55impl VariantIndexType { 55impl VariantIndexType {
56 pub fn cast(&self, num: u32, byte_order: &ByteOrder) -> Vec<u8> { 56 pub fn cast(&self, num: u32, byte_order: &ByteOrder) -> Vec<u8> {
57 match self { 57 match self {
58 VariantIndexType::U8 => byte_order.from_u8(num as u8), 58 Self::U8 => byte_order.from_u8(num as u8),
59 VariantIndexType::U16 => byte_order.form_u16(num as u16), 59 Self::U16 => byte_order.form_u16(num as u16),
60 VariantIndexType::U32 => byte_order.form_u32(num as u32), 60 Self::U32 => byte_order.form_u32(num),
61 VariantIndexType::U64 => byte_order.form_u64(num as u64), 61 Self::U64 => byte_order.form_u64(num as u64),
62 } 62 }
63 } 63 }
64} 64}
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
index e5aa58d..8e8c27c 100644
--- a/src/zip/driver.rs
+++ b/src/zip/driver.rs
@@ -1,7 +1,8 @@
1use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; 1use crate::driver::{ArchiveRead, ArchiveWrite, Driver};
2use crate::zip::error::{ZipError, ZipResult}; 2use crate::zip::file::CompressionMethod;
3use crate::zip::structs::{deserialize, EOCDR64Locator, CDR, EOCDR, EOCDR64}; 3use crate::zip::structs::{deserialize, EOCDR64Locator, ExtraHeader, CDR, EOCDR, EOCDR64};
4use crate::zip::ZipFile; 4use crate::zip::{ZipError, ZipFile, ZipResult};
5use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime};
5use std::collections::HashMap as Map; 6use std::collections::HashMap as Map;
6use std::fs::File; 7use std::fs::File;
7use std::io::{Read, Seek, SeekFrom, Write}; 8use std::io::{Read, Seek, SeekFrom, Write};
@@ -41,7 +42,7 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> {
41 io.read(&mut buf)?; 42 io.read(&mut buf)?;
42 buf 43 buf
43 }; 44 };
44 let eocdr: EOCDR = deserialize(&buf).map_err(|_| ZipError::InvalidEOCDR)?; 45 let eocdr: EOCDR = deserialize(&buf).unwrap();
45 let comment = { 46 let comment = {
46 let mut buf: Vec<u8> = vec![0; eocdr.comment_len as usize]; 47 let mut buf: Vec<u8> = vec![0; eocdr.comment_len as usize];
47 io.read(&mut buf)?; 48 io.read(&mut buf)?;
@@ -55,31 +56,29 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> {
55 io.read(&mut buf)?; 56 io.read(&mut buf)?;
56 buf 57 buf
57 }; 58 };
58 let (cd_pointer, cd_size, cd_records) = if u32::from_le_bytes(buf[0..4].try_into().unwrap()) 59 let (cd_pointer, cd_size, cd_records) =
59 == 0x07064b50 60 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) == 0x07064b50 {
60 { 61 let eocdr64locator: EOCDR64Locator = deserialize(&buf[4..]).unwrap();
61 let eocdr64locator: EOCDR64Locator = 62
62 deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64Locator)?; 63 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?;
63 64 let buf = {
64 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; 65 let mut buf = [0; 56];
65 let buf = { 66 io.read(&mut buf)?;
66 let mut buf = [0; 56]; 67 buf
67 io.read(&mut buf)?; 68 };
68 buf 69 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 {
70 return Err(ZipError::InvalidEOCDR64Signature.into());
71 }
72 let eocdr64: EOCDR64 = deserialize(&buf[4..]).unwrap();
73
74 (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records)
75 } else {
76 (
77 eocdr.cd_pointer as u64,
78 eocdr.cd_size as u64,
79 eocdr.cd_records as u64,
80 )
69 }; 81 };
70 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 {
71 return Err(ZipError::InvalidEOCDR64Signature.into());
72 }
73 let eocdr64: EOCDR64 = deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64)?;
74
75 (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records)
76 } else {
77 (
78 eocdr.cd_pointer as u64,
79 eocdr.cd_size as u64,
80 eocdr.cd_records as u64,
81 )
82 };
83 82
84 // Read cd records 83 // Read cd records
85 let mut files = Map::with_capacity(cd_records as usize); 84 let mut files = Map::with_capacity(cd_records as usize);
@@ -96,7 +95,7 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> {
96 return Err(ZipError::InvalidCDRSignature.into()); 95 return Err(ZipError::InvalidCDRSignature.into());
97 } 96 }
98 p += 4; 97 p += 4;
99 let cdr: CDR = deserialize(&buf[p..p + 42]).map_err(|_| ZipError::InvalidCDR)?; 98 let cdr: CDR = deserialize(&buf[p..p + 42]).unwrap();
100 p += 42; 99 p += 42;
101 let name = String::from_utf8(buf[p..p + cdr.name_len as usize].into()).unwrap(); 100 let name = String::from_utf8(buf[p..p + cdr.name_len as usize].into()).unwrap();
102 p += cdr.name_len as usize; 101 p += cdr.name_len as usize;
@@ -105,15 +104,61 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> {
105 let comment = String::from_utf8(buf[p..p + cdr.comment_len as usize].into()).unwrap(); 104 let comment = String::from_utf8(buf[p..p + cdr.comment_len as usize].into()).unwrap();
106 p += cdr.comment_len as usize; 105 p += cdr.comment_len as usize;
107 106
107 let mut compressed_size = cdr.compressed_size as u64;
108 let mut size = cdr.size as u64;
109 let mut header_pointer = cdr.header_pointer as u64;
110
111 let mut ep: usize = 0;
112 while ep < cdr.extra_field_len as usize {
113 let header: ExtraHeader = deserialize(&extra_fields[ep..ep + 4]).unwrap();
114 ep += 4;
115 match header.id {
116 0x0001 => {
117 if size == 0xFFFFFFFF {
118 compressed_size = deserialize(&extra_fields[ep..ep + 8]).unwrap();
119 ep += 8;
120 }
121 if compressed_size == 0xFFFFFFFF {
122 size = deserialize(&extra_fields[ep..ep + 8]).unwrap();
123 ep += 8;
124 }
125 if header_pointer == 0xFFFFFFFF {
126 header_pointer = deserialize(&extra_fields[ep..ep + 8]).unwrap();
127 ep += 8;
128 }
129 if cdr.disk == 0xFFFF {
130 ep += 4
131 }
132 }
133 _ => ep += header.size as usize,
134 }
135 }
136
108 files.insert( 137 files.insert(
109 name.clone(), 138 name.clone(),
110 ZipFile::new( 139 ZipFile::new(
140 CompressionMethod::from_struct_id(cdr.compression_method)?,
141 NaiveDateTime::new(
142 NaiveDate::from_ymd_opt(
143 (cdr.dos_date as i32 >> 9 & 0x7F) + 1980,
144 cdr.dos_date as u32 >> 5 & 0xF,
145 cdr.dos_date as u32 & 0x1F,
146 )
147 .ok_or(ZipError::InvalidDate)?,
148 NaiveTime::from_hms_opt(
149 (cdr.dos_time as u32 >> 11) & 0x1F,
150 (cdr.dos_time as u32 >> 5) & 0x3F,
151 (cdr.dos_time as u32 & 0x1F) * 2,
152 )
153 .ok_or(ZipError::InvalidTime)?,
154 )
155 .and_local_timezone(Local)
156 .unwrap(),
157 cdr.crc,
158 compressed_size,
159 size,
160 header_pointer,
111 name, 161 name,
112 cdr.dos_date,
113 cdr.dos_time,
114 cdr.compression_method,
115 cdr.compressed_size as u64,
116 cdr.size as u64,
117 comment, 162 comment,
118 ), 163 ),
119 ); 164 );
diff --git a/src/zip/error.rs b/src/zip/error.rs
index 18bbb22..1c5527c 100644
--- a/src/zip/error.rs
+++ b/src/zip/error.rs
@@ -7,15 +7,14 @@ pub type ZipResult<T> = ArchiveResult<T, ZipError>;
7#[derive(Debug)] 7#[derive(Debug)]
8pub enum ZipError { 8pub enum ZipError {
9 EOCDRNotFound, 9 EOCDRNotFound,
10 InvalidEOCDR,
11 InvalidArchiveComment,
12
13 InvalidEOCDR64Locator,
14 InvalidEOCDR64Signature, 10 InvalidEOCDR64Signature,
15 InvalidEOCDR64,
16
17 InvalidCDRSignature, 11 InvalidCDRSignature,
18 InvalidCDR, 12
13 InvalidArchiveComment,
14 InvalidCompressionMethod,
15 UnsupportedCompressionMethod,
16 InvalidDate,
17 InvalidTime,
19 InvalidFileName, 18 InvalidFileName,
20 InvalidFileComment, 19 InvalidFileComment,
21} 20}
@@ -32,25 +31,24 @@ impl From<ZipError> for ArchiveError<ZipError> {
32impl Display for ZipError { 31impl Display for ZipError {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self { 33 match self {
35 ZipError::EOCDRNotFound => write!(f, "End of central directory record not found"), 34 Self::EOCDRNotFound => write!(f, "End of central directory record not found"),
36 ZipError::InvalidEOCDR => write!(f, "Invalid end of central directory record"), 35 Self::InvalidEOCDR64Signature => {
37 ZipError::InvalidArchiveComment => write!(f, "Invalid archive comment"),
38 ZipError::InvalidEOCDR64Locator => {
39 write!(f, "Invalid zip64 end of central directory locator")
40 }
41 ZipError::InvalidEOCDR64Signature => {
42 write!( 36 write!(
43 f, 37 f,
44 "Invalid signature of zip64 end of central directory record" 38 "Invalid signature of zip64 end of central directory record"
45 ) 39 )
46 } 40 }
47 ZipError::InvalidEOCDR64 => write!(f, "Invalid zip64 end of central directory record"), 41 Self::InvalidCDRSignature => {
48 ZipError::InvalidCDRSignature => {
49 write!(f, "Invalid signature of central directory record") 42 write!(f, "Invalid signature of central directory record")
50 } 43 }
51 ZipError::InvalidCDR => write!(f, "Invalid central directory record"), 44
52 ZipError::InvalidFileName => write!(f, "Invalid file name"), 45 Self::InvalidArchiveComment => write!(f, "Invalid archive comment"),
53 ZipError::InvalidFileComment => write!(f, "Invalid file comment"), 46 Self::InvalidCompressionMethod => writeln!(f, "Invalid compression method"),
47 Self::UnsupportedCompressionMethod => writeln!(f, "Unsupported compression method"),
48 Self::InvalidDate => write!(f, "Invalid date"),
49 Self::InvalidTime => write!(f, "Invalid time"),
50 Self::InvalidFileName => write!(f, "Invalid file name"),
51 Self::InvalidFileComment => write!(f, "Invalid file comment"),
54 } 52 }
55 } 53 }
56} 54}
diff --git a/src/zip/file.rs b/src/zip/file.rs
index d5b3327..f735d65 100644
--- a/src/zip/file.rs
+++ b/src/zip/file.rs
@@ -1,45 +1,66 @@
1use crate::driver::ArchiveFile; 1use crate::driver::ArchiveFile;
2use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; 2use crate::zip::{ZipError, ZipResult};
3use chrono::{DateTime, Local};
4
5pub enum CompressionMethod {
6 Store,
7 Deflate,
8 BZIP2,
9 LZMA,
10 ZStd,
11 XZ,
12}
13
14impl CompressionMethod {
15 pub(crate) fn from_struct_id(id: u16) -> ZipResult<Self> {
16 match id {
17 0 => Ok(Self::Store),
18 8 => Ok(Self::Deflate),
19 12 => Ok(Self::BZIP2),
20 14 => Ok(Self::LZMA),
21 93 => Ok(Self::ZStd),
22 95 => Ok(Self::XZ),
23 1..=7 | 9..=11 | 13 | 15..=20 | 94 | 96..=99 => {
24 Err(ZipError::UnsupportedCompressionMethod.into())
25 }
26 21..=92 | 100.. => Err(ZipError::InvalidCompressionMethod.into()),
27 }
28 }
29}
3 30
4pub struct ZipFile { 31pub struct ZipFile {
5 pub name: String, 32 pub compression_method: CompressionMethod,
6 pub datetime: NaiveDateTime, 33 pub datetime: DateTime<Local>,
7 pub compression_method: u16, 34 pub crc: u32,
8 pub compressed_size: u64, 35 pub compressed_size: u64,
9 pub size: u64, 36 pub size: u64,
37 pub header_pointer: u64,
38 pub name: String,
10 pub comment: String, 39 pub comment: String,
11} 40}
12 41
13impl ArchiveFile for ZipFile {}
14
15impl ZipFile { 42impl ZipFile {
16 pub(crate) fn new( 43 pub fn new(
17 name: String, 44 compression_method: CompressionMethod,
18 dos_date: u16, 45 datetime: DateTime<Local>,
19 dos_time: u16, 46 crc: u32,
20 compression_method: u16,
21 compressed_size: u64, 47 compressed_size: u64,
22 size: u64, 48 size: u64,
49 header_pointer: u64,
50 name: String,
23 comment: String, 51 comment: String,
24 ) -> Self { 52 ) -> Self {
25 let year = (dos_date >> 9 & 0x7F) + 1980;
26 let month = dos_date >> 5 & 0xF;
27 let day = dos_date & 0x1F;
28
29 let hour = (dos_time >> 11) & 0x1F;
30 let minute = (dos_time >> 5) & 0x3F;
31 let seconds = (dos_time & 0x1F) * 2;
32
33 Self { 53 Self {
34 name,
35 datetime: NaiveDateTime::new(
36 NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).unwrap(),
37 NaiveTime::from_hms_opt(hour as u32, minute as u32, seconds as u32).unwrap(),
38 ),
39 compression_method, 54 compression_method,
55 datetime,
56 crc,
40 compressed_size, 57 compressed_size,
41 size, 58 size,
59 header_pointer,
60 name,
42 comment, 61 comment,
43 } 62 }
44 } 63 }
45} 64}
65
66impl ArchiveFile for ZipFile {}
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index 5aeca97..8601526 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -4,5 +4,5 @@ mod file;
4mod structs; 4mod structs;
5 5
6pub use driver::Zip; 6pub use driver::Zip;
7pub use error::ZipError; 7pub use error::{ZipError, ZipResult};
8pub use file::ZipFile; 8pub use file::ZipFile;
diff --git a/src/zip/structs.rs b/src/zip/structs.rs
index 0f9579e..3a93c1b 100644
--- a/src/zip/structs.rs
+++ b/src/zip/structs.rs
@@ -40,7 +40,7 @@ pub struct CDR {
40 pub compression_method: u16, 40 pub compression_method: u16,
41 pub dos_time: u16, 41 pub dos_time: u16,
42 pub dos_date: u16, 42 pub dos_date: u16,
43 pub crc32: u32, 43 pub crc: u32,
44 pub compressed_size: u32, 44 pub compressed_size: u32,
45 pub size: u32, 45 pub size: u32,
46 pub name_len: u16, 46 pub name_len: u16,
@@ -52,6 +52,12 @@ pub struct CDR {
52 pub header_pointer: u32, 52 pub header_pointer: u32,
53} 53}
54 54
55#[derive(Serialize, Deserialize)]
56pub struct ExtraHeader {
57 pub id: u16,
58 pub size: u16,
59}
60
55pub fn serialize<T: Serialize>(object: &mut T) -> StructResult<Vec<u8>> { 61pub fn serialize<T: Serialize>(object: &mut T) -> StructResult<Vec<u8>> {
56 Settings::default().serialize(object) 62 Settings::default().serialize(object)
57} 63}