aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIgor Tolmachov <me@igorek.dev>2023-09-08 17:33:59 +0900
committerIgor Tolmachev <me@igorek.dev>2024-06-23 15:34:33 +0900
commit9003b81813ff171edfc6101868c226c5c7d1957c (patch)
tree63db162b56b282bc2ec71f86f26dee8dbc2550c8 /src
parentb8c83ab5c133dc1330aa425a012d45f3c62e7ef1 (diff)
downloadarchivator-9003b81813ff171edfc6101868c226c5c7d1957c.tar.gz
archivator-9003b81813ff171edfc6101868c226c5c7d1957c.zip
Add basic zip reader
Diffstat (limited to 'src')
-rw-r--r--src/archive.rs6
-rw-r--r--src/datatypes.rs116
-rw-r--r--src/file.rs2
-rw-r--r--src/io.rs14
-rw-r--r--src/lib.rs1
-rw-r--r--src/result.rs29
-rw-r--r--src/utils.rs60
-rw-r--r--src/zip/datatypes.rs278
-rw-r--r--src/zip/file.rs100
-rw-r--r--src/zip/io.rs127
10 files changed, 400 insertions, 333 deletions
diff --git a/src/archive.rs b/src/archive.rs
index 3c2106a..ef74de9 100644
--- a/src/archive.rs
+++ b/src/archive.rs
@@ -4,7 +4,7 @@ use std::fs::File;
4use std::path::Path; 4use std::path::Path;
5 5
6pub struct Archive<IO> { 6pub struct Archive<IO> {
7 io: IO, 7 pub io: IO,
8} 8}
9 9
10impl<IO: ArchiveRead> Archive<IO> { 10impl<IO: ArchiveRead> Archive<IO> {
@@ -22,6 +22,8 @@ impl<IO: ArchiveRead> Archive<IO> {
22 } 22 }
23} 23}
24 24
25impl<IO: ArchiveRead> Archive<IO> {}
26
25impl<IO: ArchiveWrite> Archive<IO> { 27impl<IO: ArchiveWrite> Archive<IO> {
26 pub fn file_writer(path: impl AsRef<Path>) -> ArchiveResult<Self> 28 pub fn file_writer(path: impl AsRef<Path>) -> ArchiveResult<Self>
27 where 29 where
@@ -36,3 +38,5 @@ impl<IO: ArchiveWrite> Archive<IO> {
36 }) 38 })
37 } 39 }
38} 40}
41
42impl<IO: ArchiveWrite> Archive<IO> {}
diff --git a/src/datatypes.rs b/src/datatypes.rs
index 7712064..59c7552 100644
--- a/src/datatypes.rs
+++ b/src/datatypes.rs
@@ -1,116 +1,20 @@
1use crate::result::ArchiveResult; 1use crate::result::ArchiveResult;
2use crate::utils::ReadHelper;
2use std::io::{Read, Write}; 3use std::io::{Read, Write};
3 4
4pub trait ArchiveDatatype 5pub trait ArchiveDatatype<const SIZE: usize>: Sized {
5where 6 const SIZE: usize = SIZE;
6 Self: Sized,
7{
8 const SIZE: usize;
9 7
10 fn read(reader: impl Read) -> ArchiveResult<Self>; 8 fn parse(buf: [u8; SIZE]) -> Self;
11 9
12 fn write(&self, writer: impl Write) -> ArchiveResult<()>; 10 fn dump(&self) -> [u8; SIZE];
13}
14
15pub mod utils {
16 pub fn read_string(buf: &[u8]) -> String {
17 String::from_utf8(buf.into()).unwrap()
18 }
19 11
20 pub fn write_string(string: &str) -> &[u8] { 12 fn read(mut reader: impl Read) -> ArchiveResult<Self> {
21 string.as_bytes() 13 Ok(Self::parse(reader.read2buf::<SIZE>()?))
22 } 14 }
23 15
24 pub fn vec(vec: &[u8]) -> &[u8] { 16 fn write(&self, mut writer: impl Write) -> ArchiveResult<()> {
25 vec 17 writer.write(&self.dump())?;
18 Ok(())
26 } 19 }
27
28 #[macro_export]
29 macro_rules! create_archive_datatype {
30 {
31 $vis:vis struct $struct_name:ident {
32 $($field:ident: $field_type:ty,)*
33 }
34
35 $(
36 let $custom_field:ident: $custom_field_type:ty {
37 size: $custom_field_size: expr,
38 read: $custom_field_read: path,
39 write: $custom_field_write: path,
40 }
41 )*
42
43 $(
44 const {
45 $($const_name:ident: $const_type:ty = $const_value:expr;)+
46 }
47 )?
48
49 $(
50 read $read_code:block
51 )?
52
53 $(
54 write $write_code:block
55 )?
56 }
57 => {
58 #[derive(Debug)]
59 $vis struct $struct_name {
60 $($vis $field: $field_type),*,
61 $($vis $custom_field: $custom_field_type),*
62 }
63
64 $(
65 impl $struct_name {
66 $(const $const_name: $const_type = $const_value;)*
67 }
68 )?
69
70 impl ArchiveDatatype for $struct_name {
71 const SIZE: usize = ($((<$field_type>::BITS / 8)+)*0) as usize;
72
73 fn read(mut reader: impl Read) -> ArchiveResult<Self> {
74 let mut buf = [0; Self::SIZE];
75 reader.read(&mut buf)?;
76
77 $(
78 let (bytes, buf) = buf.split_at((<$field_type>::BITS / 8) as usize);
79 let $field = <$field_type>::from_le_bytes(bytes.try_into().unwrap());
80 )*
81
82 $(
83 let $custom_field = {
84 let mut buf = vec![0; $custom_field_size as usize];
85 reader.read(&mut buf)?;
86 $custom_field_read(&buf).into()
87 };
88 )*
89
90 #[allow(dropping_references)]
91 drop(buf);
92
93 $($read_code;)?
94
95 Ok(
96 Self {
97 $($field),*,
98 $($custom_field),*
99 }
100 )
101 }
102
103 fn write(&self, mut writer: impl Write) -> ArchiveResult<()> {
104 $($write_code;)?
105 writer.write(&[
106 $(&self.$field.to_le_bytes()[..]),*,
107 $(&$custom_field_write(&self.$custom_field)),*
108 ].concat())?;
109 Ok(())
110 }
111 }
112 };
113 }
114
115 pub use create_archive_datatype;
116} 20}
diff --git a/src/file.rs b/src/file.rs
index 72a9eac..9fa2add 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -3,7 +3,7 @@ use std::io::{Read, Write};
3pub trait ArchiveFile { 3pub trait ArchiveFile {
4 type Info; 4 type Info;
5 5
6 fn info() -> Self::Info; 6 fn new(info: Self::Info) -> Self;
7} 7}
8 8
9pub trait ArchiveFileRead: Read + ArchiveFile {} 9pub trait ArchiveFileRead: Read + ArchiveFile {}
diff --git a/src/io.rs b/src/io.rs
index df81ce0..43cf1a4 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -2,10 +2,7 @@ use crate::file::{ArchiveFileRead, ArchiveFileWrite};
2use crate::result::ArchiveResult; 2use crate::result::ArchiveResult;
3use std::io::{Read, Write}; 3use std::io::{Read, Write};
4 4
5pub trait ArchiveRead 5pub trait ArchiveRead: Sized {
6where
7 Self: Sized,
8{
9 type Reader: Read; 6 type Reader: Read;
10 type FileInfo; 7 type FileInfo;
11 type FileReader: ArchiveFileRead<Info = Self::FileInfo>; 8 type FileReader: ArchiveFileRead<Info = Self::FileInfo>;
@@ -14,18 +11,15 @@ where
14 11
15 fn files(&self) -> ArchiveResult<Vec<Self::FileInfo>>; 12 fn files(&self) -> ArchiveResult<Vec<Self::FileInfo>>;
16 13
17 fn file_reader(&self, name: &str) -> ArchiveResult<Self::FileReader>; 14 fn open_file(&self, name: &str) -> ArchiveResult<Self::FileReader>;
18} 15}
19 16
20pub trait ArchiveWrite 17pub trait ArchiveWrite: Sized {
21where
22 Self: Sized,
23{
24 type Writer: Write; 18 type Writer: Write;
25 type FileInfo; 19 type FileInfo;
26 type FileWriter: ArchiveFileWrite<Info = Self::FileInfo>; 20 type FileWriter: ArchiveFileWrite<Info = Self::FileInfo>;
27 21
28 fn new(write: Self::Writer) -> ArchiveResult<Self>; 22 fn new(write: Self::Writer) -> ArchiveResult<Self>;
29 23
30 fn file_writer(&self, name: &str) -> ArchiveResult<Self::FileWriter>; 24 fn create_file(&self, name: &str) -> ArchiveResult<Self::FileWriter>;
31} 25}
diff --git a/src/lib.rs b/src/lib.rs
index abfd599..3907e21 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,7 @@
1pub mod zip; 1pub mod zip;
2 2
3mod result; 3mod result;
4mod utils;
4 5
5mod archive; 6mod archive;
6mod datatypes; 7mod datatypes;
diff --git a/src/result.rs b/src/result.rs
index 715f10c..9f35920 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -7,7 +7,12 @@ pub type ArchiveResult<R> = Result<R, ArchiveError>;
7#[derive(Debug)] 7#[derive(Debug)]
8pub enum ArchiveError { 8pub enum ArchiveError {
9 IO(io::Error), 9 IO(io::Error),
10 WrongSignature { expected: u32, received: u32 }, 10 BadArchive { reason: &'static str },
11 IncorrectSignature { expected: u32, received: u32 },
12 IncorrectString { location: &'static str },
13 IncorrectDate { year: u16, month: u16, day: u16 },
14 IncorrectTime { hour: u16, min: u16, sec: u16 },
15 UnsupportedCompressionMethod { method: u16 },
11} 16}
12 17
13impl From<io::Error> for ArchiveError { 18impl From<io::Error> for ArchiveError {
@@ -20,12 +25,30 @@ impl Display for ArchiveError {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 match self { 26 match self {
22 Self::IO(err) => write!(f, "{err}"), 27 Self::IO(err) => write!(f, "{err}"),
23 Self::WrongSignature { expected, received } => { 28 Self::BadArchive { reason } => {
29 write!(f, "Bad archive because {reason}")
30 }
31 Self::IncorrectSignature { expected, received } => {
32 write!(
33 f,
34 "Wrong signature (expected: {expected}, received: {received})"
35 )
36 }
37 Self::IncorrectString { location } => {
38 write!(f, "Incorrect string in {location}")
39 }
40 Self::IncorrectDate { year, month, day } => {
24 write!( 41 write!(
25 f, 42 f,
26 "Wrong signature. Expected: {expected}, received: {received}" 43 "Incorrect date (year: {year}, month: {month}, day: {day})"
27 ) 44 )
28 } 45 }
46 Self::IncorrectTime { hour, min, sec } => {
47 write!(f, "Incorrect time (hour: {hour}, min: {min}, sec: {sec})")
48 }
49 Self::UnsupportedCompressionMethod { method } => {
50 write!(f, "Unsupported compression method (method: {method})")
51 }
29 } 52 }
30 } 53 }
31} 54}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..57ad1c9
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,60 @@
1use std::io::{Read, Result as IOResult};
2
3macro_rules! archive_datatype {
4 {
5 $vis:vis struct $struct_name:ident {
6 $(
7 $($fix_field_name:ident: $fix_field_type:ty)?
8 $([const] $const_field_name:ident: $const_field_type:ty = $const_field_value:expr)?
9 ,
10 )*
11 }
12 }
13 => {
14 #[derive(Debug)]
15 $vis struct $struct_name { $($(pub $fix_field_name: $fix_field_type,)?)* }
16
17 #[allow(unused)]
18 impl $struct_name {
19 $($(pub const $const_field_name: $const_field_type = $const_field_value;)?)*
20 }
21
22
23 impl ArchiveDatatype<{$($((<$fix_field_type>::BITS as usize / 8)+)?)* 0}> for $struct_name {
24 fn parse(buf: [u8; Self::SIZE]) -> Self {
25 let mut byte = 0;
26
27 $($(
28 byte += (<$fix_field_type>::BITS / 8) as usize;
29 let $fix_field_name = <$fix_field_type>::from_le_bytes(buf[byte - (<$fix_field_type>::BITS as usize / 8)..byte].try_into().unwrap());
30 )?)*
31
32 Self { $($($fix_field_name,)?)* }
33 }
34
35 fn dump(&self) -> [u8; Self::SIZE] {
36 [$($(&self.$fix_field_name.to_le_bytes()[..],)?)*]
37 .concat()
38 .try_into()
39 .unwrap()
40 }
41 }
42 }
43}
44pub(crate) use archive_datatype;
45
46pub trait ReadHelper: Read {
47 fn read2buf<const SIZE: usize>(&mut self) -> IOResult<[u8; SIZE]> {
48 let mut buf = [0; SIZE];
49 self.read(&mut buf)?;
50 Ok(buf)
51 }
52
53 fn read2vec(&mut self, size: usize) -> IOResult<Vec<u8>> {
54 let mut buf = vec![0; size];
55 self.read(&mut buf)?;
56 Ok(buf)
57 }
58}
59
60impl<R: Read> ReadHelper for R {}
diff --git a/src/zip/datatypes.rs b/src/zip/datatypes.rs
index b280e5d..5ce1045 100644
--- a/src/zip/datatypes.rs
+++ b/src/zip/datatypes.rs
@@ -1,9 +1,11 @@
1use crate::datatypes::{utils, ArchiveDatatype}; 1pub use crate::datatypes::ArchiveDatatype;
2use crate::result::{ArchiveError, ArchiveResult}; 2use crate::result::{ArchiveError, ArchiveResult};
3use std::io::{Read, Write}; 3use crate::utils::{archive_datatype, ReadHelper};
4use std::io::{Read, Seek, SeekFrom};
4 5
5utils::create_archive_datatype! { 6archive_datatype! {
6 pub struct LocalFileHeader { 7 pub struct LocalFileHeader {
8 [const] SIGNATURE: u32 = 0x04034b50,
7 signature: u32, 9 signature: u32,
8 version_needed: u16, 10 version_needed: u16,
9 general_purpose_bit_flag: u16, 11 general_purpose_bit_flag: u16,
@@ -16,109 +18,28 @@ utils::create_archive_datatype! {
16 file_name_length: u16, 18 file_name_length: u16,
17 extra_field_length: u16, 19 extra_field_length: u16,
18 } 20 }
19
20 let file_name: String {
21 size: file_name_length,
22 read: utils::read_string,
23 write: utils::write_string,
24 }
25
26 let extra_field: Vec<u8> {
27 size: file_name_length,
28 read: utils::vec,
29 write: utils::vec,
30 }
31
32 const {
33 SIGNATURE: u32 = 0x04034b50;
34 }
35
36 read {
37 if signature != Self::SIGNATURE {
38 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature })
39 }
40 }
41} 21}
42 22
43pub struct DataDescriptor { 23archive_datatype! {
44 pub signature: Option<u32>, 24 pub struct DataDescriptor {
45 pub crc32: u32, 25 [const] SIGNATURE: u32 = 0x08074b50,
46 pub compressed_size: u32, 26 crc32: u32,
47 pub uncompressed_size: u32, 27 compressed_size: u32,
48} 28 uncompressed_size: u32,
49
50impl DataDescriptor {
51 const SIGNATURE: u32 = 0x04034b50;
52}
53
54impl ArchiveDatatype for DataDescriptor {
55 const SIZE: usize = 12;
56
57 fn read(mut reader: impl Read) -> ArchiveResult<Self> {
58 let mut buf = [0; Self::SIZE];
59 reader.read(&mut buf)?;
60
61 let signature = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
62 if signature == Self::SIGNATURE {
63 return Ok(Self {
64 signature: Some(signature),
65 crc32: u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]),
66 compressed_size: u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]),
67 uncompressed_size: {
68 let mut buf = [0; 4];
69 reader.read(&mut buf)?;
70 u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]])
71 },
72 });
73 } else {
74 return Ok(Self {
75 signature: None,
76 crc32: signature,
77 compressed_size: u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]),
78 uncompressed_size: u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]),
79 });
80 }
81 }
82
83 fn write(&self, mut writer: impl Write) -> ArchiveResult<()> {
84 writer.write(
85 &[
86 Self::SIGNATURE.to_le_bytes(),
87 self.crc32.to_le_bytes(),
88 self.compressed_size.to_le_bytes(),
89 self.uncompressed_size.to_le_bytes(),
90 ]
91 .concat(),
92 )?;
93 Ok(())
94 } 29 }
95} 30}
96 31
97utils::create_archive_datatype! { 32archive_datatype! {
98 pub struct ArchiveExtraDataRecord { 33 pub struct ArchiveExtraDataRecord {
34 [const] SIGNATURE: u32 = 0x08064b50,
99 signature: u32, 35 signature: u32,
100 extra_field_length: u32, 36 extra_field_length: u32,
101 } 37 }
102
103 let extra_field: Vec<u8> {
104 size: extra_field_length,
105 read: utils::vec,
106 write: utils::vec,
107 }
108
109 const {
110 SIGNATURE: u32 = 0x08064b50;
111 }
112
113 read {
114 if signature != Self::SIGNATURE {
115 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature })
116 }
117 }
118} 38}
119 39
120utils::create_archive_datatype! { 40archive_datatype! {
121 pub struct CentralDirectoryRecord { 41 pub struct CentralDirectoryRecord {
42 [const] SIGNATURE: u32 = 0x02014b50,
122 signature: u32, 43 signature: u32,
123 version_made_by: u16, 44 version_made_by: u16,
124 version_needed: u16, 45 version_needed: u16,
@@ -135,136 +56,111 @@ utils::create_archive_datatype! {
135 disk_number: u16, 56 disk_number: u16,
136 internal_file_attributes: u16, 57 internal_file_attributes: u16,
137 external_file_attributes: u32, 58 external_file_attributes: u32,
138 relative_header_offset: u32, 59 header_offset: u32,
139 }
140
141 let file_name: String {
142 size: file_name_length,
143 read: utils::read_string,
144 write: utils::write_string,
145 }
146
147 let extra_field: Vec<u8> {
148 size: external_file_attributes,
149 read: utils::vec,
150 write: utils::vec,
151 }
152
153 let file_comment: String {
154 size: file_comment_length,
155 read: utils::read_string,
156 write: utils::write_string,
157 }
158
159 const {
160 SIGNATURE: u32 = 0x02014b50;
161 }
162
163 read {
164 if signature != Self::SIGNATURE {
165 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature })
166 }
167 } 60 }
168} 61}
169 62
170utils::create_archive_datatype! { 63archive_datatype! {
171 pub struct DigitalSignature{ 64 pub struct DigitalSignature{
65 [const] SIGNATURE: u32 = 0x05054b50,
172 signature: u32, 66 signature: u32,
173 data_size: u16, 67 data_size: u16,
174 } 68 }
175
176 let data: Vec<u8> {
177 size: data_size,
178 read: utils::vec,
179 write: utils::vec,
180 }
181
182 const {
183 SIGNATURE: u32 = 0x05054b50;
184 }
185
186 read {
187 if signature != Self::SIGNATURE {
188 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature })
189 }
190 }
191} 69}
192 70
193utils::create_archive_datatype! { 71archive_datatype! {
194 pub struct Zip64EndOfCentralDirectoryRecord { 72 pub struct Zip64EndOfCentralDirectoryRecord {
73 [const] SIGNATURE: u32 = 0x06064b50,
195 signature: u32, 74 signature: u32,
196 size_of_struct: u64, 75 size_of_zip64_eocd: u64,
197 version_made_by: u16, 76 version_made_by: u16,
198 version_needed: u16, 77 version_needed: u16,
199 disk_number: u32, 78 disk: u32,
200 disk_number_where_starts_central_directory: u32, 79 disk_where_starts_cd: u32,
201 entries_in_central_directory_on_disk: u64, 80 entries_in_cd_on_disk: u64,
202 entries_in_central_directory: u64, 81 entries_in_cd: u64,
203 size_of_central_directory_records: u64, 82 size_of_cd_records: u64,
204 offset_of_central_directory_entries: u64, 83 offset_of_cd_entries: u64,
205 }
206
207 let extensible_data: Vec<u8> {
208 size: size_of_struct - Self::SIZE as u64 + 12,
209 read: utils::vec,
210 write: utils::vec,
211 } 84 }
85}
212 86
213 const { 87impl Zip64EndOfCentralDirectoryRecord {
214 SIGNATURE: u32 = 0x08064b50; 88 pub fn find(
215 } 89 mut reader: impl Read + Seek,
90 eocd_offset: u64,
91 ) -> ArchiveResult<Option<(u64, Self, Vec<u8>)>> {
92 let locator_offset = eocd_offset - Zip64EndOfCentralDirectoryLocator::SIZE as u64;
93 reader.seek(SeekFrom::Start(locator_offset))?;
94
95 let locator = Zip64EndOfCentralDirectoryLocator::read(&mut reader)?;
96 if locator.signature != Zip64EndOfCentralDirectoryLocator::SIGNATURE {
97 return Ok(None);
98 }
216 99
217 read { 100 reader.seek(SeekFrom::Start(locator.offset_of_zip64_eocd))?;
218 if signature != Self::SIGNATURE { 101 let eocd64 = Self::read(&mut reader)?;
219 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature }) 102 if eocd64.signature != Self::SIGNATURE {
103 return Err(ArchiveError::IncorrectSignature {
104 expected: Self::SIGNATURE,
105 received: eocd64.signature,
106 });
220 } 107 }
108
109 let ext_data = reader.read2vec(eocd64.size_of_zip64_eocd as usize + 12 - Self::SIZE)?;
110 Ok(Some((locator.offset_of_zip64_eocd, eocd64, ext_data)))
221 } 111 }
222} 112}
223 113
224utils::create_archive_datatype! { 114archive_datatype! {
225 pub struct Zip64EndOfCentralDirectoryLocator { 115 pub struct Zip64EndOfCentralDirectoryLocator {
116 [const] SIGNATURE: u32 = 0x07064b50,
226 signature: u32, 117 signature: u32,
227 disk_number_where_starts_zip64_eocd: u32, 118 disk_where_starts_zip64_eocd: u32,
228 relative_offset_of_zip64_eocd: u64, 119 offset_of_zip64_eocd: u64,
229 total_disks_number: u32, 120 total_disks_number: u32,
230 } 121 }
231
232 const {
233 SIGNATURE: u32 = 0x07064b50;
234 }
235
236 read {
237 if signature != Self::SIGNATURE {
238 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature })
239 }
240 }
241} 122}
242 123
243utils::create_archive_datatype! { 124archive_datatype! {
244 pub struct EndOfCentralDirectoryRecord { 125 pub struct EndOfCentralDirectoryRecord {
126 [const] SIGNATURE: u32 = 0x06054b50,
245 signature: u32, 127 signature: u32,
246 disk_number: u16, 128 disk: u16,
247 disk_number_where_starts_central_directory: u16, 129 disk_where_starts_cd: u16,
248 entries_in_central_directory_on_disk: u16, 130 entries_in_cd_on_disk: u16,
249 entries_in_central_directory: u16, 131 entries_in_cd: u16,
250 size_of_central_directory_records: u32, 132 size_of_cd_records: u32,
251 offset_of_central_directory_entries: u32, 133 offset_of_cd_entries: u32,
252 comment_length: u16, 134 comment_length: u16,
253 } 135 }
136}
254 137
255 let comment: String { 138impl EndOfCentralDirectoryRecord {
256 size: comment_length, 139 pub fn find(mut reader: impl Read + Seek) -> ArchiveResult<(u64, Self, String)> {
257 read: utils::read_string, 140 let file_size = reader.seek(SeekFrom::End(0))? as usize;
258 write: utils::write_string, 141 let limit: usize = (u16::MAX as usize + Self::SIZE).min(file_size);
259 } 142 let mut buf = vec![0; limit];
260 143
261 const { 144 reader.seek(SeekFrom::End(-(limit as i64)))?;
262 SIGNATURE: u32 = 0x06054b50; 145 reader.read(&mut buf)?;
263 } 146
147 for byte in 0..limit - 4 {
148 if u32::from_le_bytes(buf[byte..byte + 4].try_into().unwrap()) == Self::SIGNATURE {
149 let eocd = Self::parse(buf[byte..byte + Self::SIZE].try_into().unwrap());
264 150
265 read { 151 let comment = String::from_utf8(
266 if signature != Self::SIGNATURE { 152 buf[byte + Self::SIZE..byte + Self::SIZE + eocd.comment_length as usize].into(),
267 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature }) 153 )
154 .map_err(|_| ArchiveError::IncorrectString {
155 location: "archive_comment",
156 })?;
157
158 return Ok(((file_size - limit + byte) as u64, eocd, comment));
159 }
268 } 160 }
161
162 Err(ArchiveError::BadArchive {
163 reason: "end of central directory record not found",
164 })
269 } 165 }
270} 166}
diff --git a/src/zip/file.rs b/src/zip/file.rs
index dba8d06..261390f 100644
--- a/src/zip/file.rs
+++ b/src/zip/file.rs
@@ -1,44 +1,124 @@
1use crate::file::{ArchiveFile, ArchiveFileRead, ArchiveFileWrite}; 1use crate::file::{ArchiveFile, ArchiveFileRead, ArchiveFileWrite};
2use crate::result::{ArchiveError, ArchiveResult};
3use chrono::NaiveDateTime;
2use std::io::{Read, Write}; 4use std::io::{Read, Write};
3 5
4pub struct FileInfo {} 6pub struct GeneralPurposeBitFlag {}
5 7
6pub struct FileReader {} 8pub enum CompressionMethod {
9 Store,
10 Deflate,
11 Bzip2,
12 LZMA,
13 Zstandard,
14}
15
16impl TryFrom<u16> for CompressionMethod {
17 type Error = ArchiveError;
18
19 fn try_from(value: u16) -> ArchiveResult<Self> {
20 Ok(match value {
21 0 => Self::Store,
22 8 => Self::Deflate,
23 12 => Self::Bzip2,
24 14 => Self::LZMA,
25 93 | 20 => Self::Zstandard,
26 _ => return Err(ArchiveError::UnsupportedCompressionMethod { method: value }),
27 })
28 }
29}
7 30
8pub struct FileWriter {} 31pub struct FileInfo {
32 number: u64,
33 version_made_by: u16,
34 version_needed: u16,
35 general_purpose_bit_flag: GeneralPurposeBitFlag,
36 compression_method: CompressionMethod,
37 file_modified_at: NaiveDateTime,
38 crc32: u32,
39 compressed_size: u64,
40 uncompressed_size: u64,
41 name: String,
42 comment: String,
43 header_offset: u64,
44}
45
46impl FileInfo {
47 pub fn new(
48 number: u64,
49 version_made_by: u16,
50 version_needed: u16,
51 general_purpose_bit_flag: GeneralPurposeBitFlag,
52 compression_method: CompressionMethod,
53 file_modified_at: NaiveDateTime,
54 crc32: u32,
55 compressed_size: u64,
56 uncompressed_size: u64,
57 name: String,
58 comment: String,
59 header_offset: u64,
60 ) -> Self {
61 Self {
62 number,
63 version_made_by,
64 version_needed,
65 general_purpose_bit_flag,
66 compression_method,
67 file_modified_at,
68 crc32,
69 compressed_size,
70 uncompressed_size,
71 name,
72 comment,
73 header_offset,
74 }
75 }
76}
77
78pub struct FileReader {
79 info: FileInfo,
80}
9 81
10impl ArchiveFile for FileReader { 82impl ArchiveFile for FileReader {
11 type Info = FileInfo; 83 type Info = FileInfo;
12 84
13 fn info() -> Self::Info { 85 fn new(info: Self::Info) -> Self {
14 Self::Info {} 86 Self { info }
15 } 87 }
16} 88}
17 89
18impl Read for FileReader { 90impl Read for FileReader {
19 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { 91 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
20 return Ok(0); 92 todo!()
21 } 93 }
22} 94}
23 95
24impl ArchiveFileRead for FileReader {} 96impl ArchiveFileRead for FileReader {}
25 97
98impl FileReader {}
99
100pub struct FileWriter {
101 info: FileInfo,
102}
103
26impl ArchiveFile for FileWriter { 104impl ArchiveFile for FileWriter {
27 type Info = FileInfo; 105 type Info = FileInfo;
28 106
29 fn info() -> Self::Info { 107 fn new(info: Self::Info) -> Self {
30 Self::Info {} 108 Self { info }
31 } 109 }
32} 110}
33 111
34impl Write for FileWriter { 112impl Write for FileWriter {
35 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { 113 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
36 return Ok(0); 114 todo!()
37 } 115 }
38 116
39 fn flush(&mut self) -> std::io::Result<()> { 117 fn flush(&mut self) -> std::io::Result<()> {
40 Ok(()) 118 todo!()
41 } 119 }
42} 120}
43 121
44impl ArchiveFileWrite for FileWriter {} 122impl ArchiveFileWrite for FileWriter {}
123
124impl FileWriter {}
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 @@
1use super::datatypes::*;
1use super::file::{FileInfo, FileReader, FileWriter}; 2use super::file::{FileInfo, FileReader, FileWriter};
2use crate::io::{ArchiveRead, ArchiveWrite}; 3use crate::io::{ArchiveRead, ArchiveWrite};
3use crate::result::ArchiveResult; 4use crate::result::{ArchiveError, ArchiveResult};
5use crate::utils::ReadHelper;
6use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
7use std::collections::HashMap;
4use std::fs::File; 8use std::fs::File;
5use std::io::Read; 9use std::io::Read;
6use std::io::{Seek, Write}; 10use std::io::{Seek, SeekFrom, Write};
7 11
8pub struct Reader<R: Read + Seek = File> { 12pub struct Reader<R: Read + Seek = File> {
9 reader: R, 13 reader: R,
14 files: HashMap<String, FileInfo>,
15 comment: String,
10} 16}
11 17
12impl<R: Read + Seek> ArchiveRead for Reader<R> { 18impl<R: Read + Seek> ArchiveRead for Reader<R> {
@@ -14,16 +20,115 @@ impl<R: Read + Seek> ArchiveRead for Reader<R> {
14 type FileInfo = FileInfo; 20 type FileInfo = FileInfo;
15 type FileReader = FileReader; 21 type FileReader = FileReader;
16 22
17 fn new(reader: Self::Reader) -> ArchiveResult<Self> { 23 fn new(mut reader: Self::Reader) -> ArchiveResult<Self> {
18 Ok(Self { reader }) 24 let cd_size: u64;
25 let cd_offset: u64;
26 let cd_entries: u64;
27
28 let (eocd_offset, eocd, comment) = EndOfCentralDirectoryRecord::find(&mut reader)?;
29 if let Some((eocd64_offset, eocd64, eocd64_extensible)) =
30 Zip64EndOfCentralDirectoryRecord::find(&mut reader, eocd_offset)?
31 {
32 cd_size = eocd64.size_of_cd_records;
33 cd_offset = eocd64.offset_of_cd_entries;
34 cd_entries = eocd64.entries_in_cd;
35 } else {
36 cd_size = eocd.size_of_cd_records as u64;
37 cd_offset = eocd.offset_of_cd_entries as u64;
38 cd_entries = eocd.entries_in_cd as u64;
39 }
40
41 let mut buf = vec![0; cd_size as usize];
42 reader.seek(SeekFrom::Start(cd_offset))?;
43 reader.read(&mut buf)?;
44
45 let mut buf: &[u8] = &buf;
46 let mut files = HashMap::with_capacity(cd_entries as usize);
47
48 for entry_number in 0..cd_entries {
49 let cd = CentralDirectoryRecord::read(&mut buf)?;
50
51 let file_name = String::from_utf8(buf.read2vec(cd.file_name_length as usize)?).or(
52 Err(ArchiveError::IncorrectString {
53 location: "file_name",
54 }),
55 )?;
56 let mut extra_field: &[u8] = &buf.read2vec(cd.extra_field_length as usize)?;
57 let file_comment = String::from_utf8(buf.read2vec(cd.file_comment_length as usize)?)
58 .or(Err(ArchiveError::IncorrectString {
59 location: "file_comment",
60 }))?;
61
62 let mut uncompressed_size: u64 = cd.uncompressed_size as u64;
63 let mut compressed_size: u64 = cd.compressed_size as u64;
64 let mut header_offset: u64 = cd.header_offset as u64;
65
66 while extra_field.len() > 0 {
67 let header = u16::from_le_bytes(extra_field.read2buf()?);
68 let size = u16::from_le_bytes(extra_field.read2buf()?);
69 let mut data: &[u8] = &extra_field.read2vec(size as usize)?;
70
71 match header {
72 0x0001 => {
73 if uncompressed_size == 0xFFFFFFFF {
74 uncompressed_size = u64::from_le_bytes(data.read2buf()?);
75 }
76 if compressed_size == 0xFFFFFFFF {
77 compressed_size = u64::from_le_bytes(data.read2buf()?);
78 }
79 if header_offset == 0xFFFFFFFF {
80 header_offset = u64::from_le_bytes(data.read2buf()?);
81 }
82 }
83 _ => {}
84 };
85 }
86
87 let year = ((cd.last_mod_file_date >> 9) & 0x7F) + 1980;
88 let month = (cd.last_mod_file_date >> 5) & 0xF;
89 let day = cd.last_mod_file_date & 0x1F;
90
91 let hour = (cd.last_mod_file_time >> 11) & 0x1F;
92 let min = (cd.last_mod_file_time >> 5) & 0x3F;
93 let sec = (cd.last_mod_file_time & 0x1F) * 2;
94
95 files.insert(
96 file_name.clone(),
97 FileInfo::new(
98 entry_number,
99 cd.version_made_by,
100 cd.version_needed,
101 super::file::GeneralPurposeBitFlag {},
102 cd.compression_method.try_into()?,
103 NaiveDateTime::new(
104 NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32)
105 .ok_or(ArchiveError::IncorrectDate { year, month, day })?,
106 NaiveTime::from_hms_opt(hour as u32, min as u32, sec as u32)
107 .ok_or(ArchiveError::IncorrectTime { hour, min, sec })?,
108 ),
109 cd.crc32,
110 compressed_size,
111 uncompressed_size,
112 file_name,
113 file_comment,
114 header_offset,
115 ),
116 );
117 }
118
119 Ok(Self {
120 reader,
121 files,
122 comment,
123 })
19 } 124 }
20 125
21 fn files(&self) -> ArchiveResult<Vec<Self::FileInfo>> { 126 fn files(&self) -> ArchiveResult<Vec<Self::FileInfo>> {
22 Ok(Vec::new()) 127 todo!()
23 } 128 }
24 129
25 fn file_reader(&self, name: &str) -> ArchiveResult<Self::FileReader> { 130 fn open_file(&self, name: &str) -> ArchiveResult<Self::FileReader> {
26 Ok(Self::FileReader {}) 131 todo!()
27 } 132 }
28} 133}
29 134
@@ -33,17 +138,17 @@ pub struct Writer<W: Write = File> {
33 writer: W, 138 writer: W,
34} 139}
35 140
36impl<W: Write> ArchiveWrite for Writer<W> { 141impl<W: Write + Seek> ArchiveWrite for Writer<W> {
37 type Writer = W; 142 type Writer = W;
38 type FileInfo = FileInfo; 143 type FileInfo = FileInfo;
39 type FileWriter = FileWriter; 144 type FileWriter = FileWriter;
40 145
41 fn new(writer: Self::Writer) -> ArchiveResult<Self> { 146 fn new(writer: Self::Writer) -> ArchiveResult<Self> {
42 Ok(Self { writer }) 147 todo!()
43 } 148 }
44 149
45 fn file_writer(&self, name: &str) -> ArchiveResult<Self::FileWriter> { 150 fn create_file(&self, name: &str) -> ArchiveResult<Self::FileWriter> {
46 Ok(Self::FileWriter {}) 151 todo!()
47 } 152 }
48} 153}
49 154