aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Tolmachov <me@igorek.dev>2023-08-30 23:33:11 +0900
committerIgor Tolmachev <me@igorek.dev>2024-06-23 15:34:33 +0900
commitb8c83ab5c133dc1330aa425a012d45f3c62e7ef1 (patch)
treee25f07a28b09ba5ae3d6c435a9be63fc59105800
parentf02a44964a2fdb91d62dee9c55f6d03648f985cb (diff)
downloadarchivator-b8c83ab5c133dc1330aa425a012d45f3c62e7ef1.tar.gz
archivator-b8c83ab5c133dc1330aa425a012d45f3c62e7ef1.zip
Add zip archive datatypes
-rw-r--r--.vscode/settings.json2
-rw-r--r--src/datatypes.rs116
-rw-r--r--src/lib.rs1
-rw-r--r--src/result.rs8
-rw-r--r--src/zip/datatypes.rs270
-rw-r--r--src/zip/mod.rs1
6 files changed, 397 insertions, 1 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 94bf061..6c82d51 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,4 @@
1{ 1{
2 "cSpell.words": ["archivator"], 2 "cSpell.words": ["archivator", "datatypes", "eocd"],
3 "rust-analyzer.linkedProjects": ["./Cargo.toml", "./Cargo.toml"] 3 "rust-analyzer.linkedProjects": ["./Cargo.toml", "./Cargo.toml"]
4} 4}
diff --git a/src/datatypes.rs b/src/datatypes.rs
new file mode 100644
index 0000000..7712064
--- /dev/null
+++ b/src/datatypes.rs
@@ -0,0 +1,116 @@
1use crate::result::ArchiveResult;
2use std::io::{Read, Write};
3
4pub trait ArchiveDatatype
5where
6 Self: Sized,
7{
8 const SIZE: usize;
9
10 fn read(reader: impl Read) -> ArchiveResult<Self>;
11
12 fn write(&self, writer: impl Write) -> ArchiveResult<()>;
13}
14
15pub mod utils {
16 pub fn read_string(buf: &[u8]) -> String {
17 String::from_utf8(buf.into()).unwrap()
18 }
19
20 pub fn write_string(string: &str) -> &[u8] {
21 string.as_bytes()
22 }
23
24 pub fn vec(vec: &[u8]) -> &[u8] {
25 vec
26 }
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}
diff --git a/src/lib.rs b/src/lib.rs
index 163f760..abfd599 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,6 +3,7 @@ pub mod zip;
3mod result; 3mod result;
4 4
5mod archive; 5mod archive;
6mod datatypes;
6mod file; 7mod file;
7mod io; 8mod io;
8 9
diff --git a/src/result.rs b/src/result.rs
index 47302ae..715f10c 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -7,6 +7,7 @@ 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} 11}
11 12
12impl From<io::Error> for ArchiveError { 13impl From<io::Error> for ArchiveError {
@@ -19,6 +20,12 @@ impl Display for ArchiveError {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self { 21 match self {
21 Self::IO(err) => write!(f, "{err}"), 22 Self::IO(err) => write!(f, "{err}"),
23 Self::WrongSignature { expected, received } => {
24 write!(
25 f,
26 "Wrong signature. Expected: {expected}, received: {received}"
27 )
28 }
22 } 29 }
23 } 30 }
24} 31}
@@ -27,6 +34,7 @@ impl Error for ArchiveError {
27 fn source(&self) -> Option<&(dyn Error + 'static)> { 34 fn source(&self) -> Option<&(dyn Error + 'static)> {
28 match self { 35 match self {
29 Self::IO(source) => Some(source), 36 Self::IO(source) => Some(source),
37 _ => None,
30 } 38 }
31 } 39 }
32} 40}
diff --git a/src/zip/datatypes.rs b/src/zip/datatypes.rs
new file mode 100644
index 0000000..b280e5d
--- /dev/null
+++ b/src/zip/datatypes.rs
@@ -0,0 +1,270 @@
1use crate::datatypes::{utils, ArchiveDatatype};
2use crate::result::{ArchiveError, ArchiveResult};
3use std::io::{Read, Write};
4
5utils::create_archive_datatype! {
6 pub struct LocalFileHeader {
7 signature: u32,
8 version_needed: u16,
9 general_purpose_bit_flag: u16,
10 compression_method: u16,
11 last_mod_file_time: u16,
12 last_mod_file_date: u16,
13 crc32: u32,
14 compressed_size: u32,
15 uncompressed_size: u32,
16 file_name_length: u16,
17 extra_field_length: u16,
18 }
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}
42
43pub struct DataDescriptor {
44 pub signature: Option<u32>,
45 pub crc32: u32,
46 pub compressed_size: u32,
47 pub uncompressed_size: u32,
48}
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 }
95}
96
97utils::create_archive_datatype! {
98 pub struct ArchiveExtraDataRecord {
99 signature: u32,
100 extra_field_length: u32,
101 }
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}
119
120utils::create_archive_datatype! {
121 pub struct CentralDirectoryRecord {
122 signature: u32,
123 version_made_by: u16,
124 version_needed: u16,
125 general_purpose_bit_flag: u16,
126 compression_method: u16,
127 last_mod_file_time: u16,
128 last_mod_file_date: u16,
129 crc32: u32,
130 compressed_size: u32,
131 uncompressed_size: u32,
132 file_name_length: u16,
133 extra_field_length: u16,
134 file_comment_length: u16,
135 disk_number: u16,
136 internal_file_attributes: u16,
137 external_file_attributes: u32,
138 relative_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 }
168}
169
170utils::create_archive_datatype! {
171 pub struct DigitalSignature{
172 signature: u32,
173 data_size: u16,
174 }
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}
192
193utils::create_archive_datatype! {
194 pub struct Zip64EndOfCentralDirectoryRecord {
195 signature: u32,
196 size_of_struct: u64,
197 version_made_by: u16,
198 version_needed: u16,
199 disk_number: u32,
200 disk_number_where_starts_central_directory: u32,
201 entries_in_central_directory_on_disk: u64,
202 entries_in_central_directory: u64,
203 size_of_central_directory_records: u64,
204 offset_of_central_directory_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 }
212
213 const {
214 SIGNATURE: u32 = 0x08064b50;
215 }
216
217 read {
218 if signature != Self::SIGNATURE {
219 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature })
220 }
221 }
222}
223
224utils::create_archive_datatype! {
225 pub struct Zip64EndOfCentralDirectoryLocator {
226 signature: u32,
227 disk_number_where_starts_zip64_eocd: u32,
228 relative_offset_of_zip64_eocd: u64,
229 total_disks_number: u32,
230 }
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}
242
243utils::create_archive_datatype! {
244 pub struct EndOfCentralDirectoryRecord {
245 signature: u32,
246 disk_number: u16,
247 disk_number_where_starts_central_directory: u16,
248 entries_in_central_directory_on_disk: u16,
249 entries_in_central_directory: u16,
250 size_of_central_directory_records: u32,
251 offset_of_central_directory_entries: u32,
252 comment_length: u16,
253 }
254
255 let comment: String {
256 size: comment_length,
257 read: utils::read_string,
258 write: utils::write_string,
259 }
260
261 const {
262 SIGNATURE: u32 = 0x06054b50;
263 }
264
265 read {
266 if signature != Self::SIGNATURE {
267 return Err(ArchiveError::WrongSignature { expected: Self::SIGNATURE, received: signature })
268 }
269 }
270}
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index 5241c87..b668b6b 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -1,4 +1,5 @@
1mod archive; 1mod archive;
2mod datatypes;
2mod file; 3mod file;
3mod io; 4mod io;
4 5