forked from kangjianwei/LearningJDK
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathZipEntry.java
785 lines (693 loc) · 28.7 KB
/
ZipEntry.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
/*
* Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.zip;
import java.nio.file.attribute.FileTime;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static java.util.zip.ZipConstants64.EXTID_EXTT;
import static java.util.zip.ZipConstants64.EXTID_NTFS;
import static java.util.zip.ZipConstants64.EXTID_ZIP64;
import static java.util.zip.ZipUtils.WINDOWS_TIME_NOT_AVAILABLE;
import static java.util.zip.ZipUtils.extendedDosToJavaTime;
import static java.util.zip.ZipUtils.get16;
import static java.util.zip.ZipUtils.get32S;
import static java.util.zip.ZipUtils.get64;
import static java.util.zip.ZipUtils.javaToExtendedDosTime;
import static java.util.zip.ZipUtils.unixTimeToFileTime;
import static java.util.zip.ZipUtils.winTimeToFileTime;
/**
* This class is used to represent a ZIP file entry.
*
* @author David Connelly
* @since 1.1
*/
/*
* 压缩包中的实体条目(并不代表文件内容),每一个未压缩的文件/文件夹都会对应一个压缩后的条目。
* ZipEntry内部的属性可能解析自zip文件第一部分的第1小节,也可能解析自zip文件的第二部分。
*/
public class ZipEntry implements ZipConstants, Cloneable {
/**
* Approximately 128 years, in milliseconds (ignoring leap years etc).
*
* This establish an approximate high-bound value for DOS times in
* milliseconds since epoch, used to enable an efficient but
* sufficient bounds check to avoid generating extended last modified
* time entries.
*
* Calculating the exact number is locale dependent, would require loading
* TimeZone data eagerly, and would make little practical sense. Since DOS
* times theoretically go to 2107 - with compatibility not guaranteed
* after 2099 - setting this to a time that is before but near 2099
* should be sufficient.
*/
private static final long UPPER_DOSTIME_BOUND = 128L * 365 * 24 * 60 * 60 * 1000;
/**
* DOS time constant for representing timestamps before 1980.
*/
static final long DOSTIME_BEFORE_1980 = (1 << 21) | (1 << 16);
/**
* Compression method for uncompressed entries.
*/
public static final int STORED = 0; // 原始数据未压缩
/**
* Compression method for compressed (deflated) entries.
*/
public static final int DEFLATED = 8; // 原始数据使用了默认压缩方式
int flag = 0; // general purpose flag | 通用位标记
int method = -1; // compression method | 压缩方法
// 文件最后修改时间(日期)
long xdostime = -1; // last modification time (in extended DOS time, where milliseconds lost in conversion might be encoded into the upper half)
// 以下三个参数在STORED模式下必须主动设置
long crc = -1; // crc-32 of entry data | crc-32校验码
long size = -1; // uncompressed size of entry data | 压缩前的大小
long csize = -1; // compressed size of entry data | 压缩后的大小
String name; // entry name | 待压缩实体的名称(文件相对路径)
// 文件最后修改时间(扩展区)
FileTime mtime; // last modification time, from extra field data
// 文件最后访问时间(扩展区)
FileTime atime; // last access time, from extra field data
// 文件创建时间(扩展区)
FileTime ctime; // creation time, from extra field data
byte[] extra; // optional extra field data for entry | 其他扩展区信息,使用小端法存储
String comment; // optional comment string for entry | 可选的注释信息
/*▼ 构造器 ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Creates a new un-initialized zip entry
*/
ZipEntry() {
}
/**
* Creates a new zip entry with the specified name.
*
* @param name The entry name
*
* @throws NullPointerException if the entry name is null
* @throws IllegalArgumentException if the entry name is longer than
* 0xFFFF bytes
*/
public ZipEntry(String name) {
Objects.requireNonNull(name, "name");
if(name.length()>0xFFFF) {
throw new IllegalArgumentException("entry name too long");
}
this.name = name;
}
/**
* Creates a new zip entry with fields taken from the specified
* zip entry.
*
* @param e A zip Entry object
*
* @throws NullPointerException if the entry object is null
*/
public ZipEntry(ZipEntry e) {
Objects.requireNonNull(e, "entry");
name = e.name;
xdostime = e.xdostime;
mtime = e.mtime;
atime = e.atime;
ctime = e.ctime;
crc = e.crc;
size = e.size;
csize = e.csize;
method = e.method;
flag = e.flag;
extra = e.extra;
comment = e.comment;
}
/*▲ 构造器 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ set ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Sets the compression method for the entry.
*
* @param method the compression method, either STORED or DEFLATED
*
* @throws IllegalArgumentException if the specified compression
* method is invalid
* @see #getMethod()
*/
// 设置对压缩实体的压缩方法
public void setMethod(int method) {
if(method != STORED && method != DEFLATED) {
throw new IllegalArgumentException("invalid compression method");
}
this.method = method;
}
/**
* Sets the last modification time of the entry.
*
* <p> If the entry is output to a ZIP file or ZIP file formatted
* output stream the last modification time set by this method will
* be stored into the {@code date and time fields} of the zip file
* entry and encoded in standard {@code MS-DOS date and time format}.
* The {@link java.util.TimeZone#getDefault() default TimeZone} is
* used to convert the epoch time to the MS-DOS data and time.
*
* @param time The last modification time of the entry in milliseconds
* since the epoch
*
* @see #getTime()
* @see #getLastModifiedTime()
*/
// 设置压缩实体的最后修改时间
public void setTime(long time) {
this.xdostime = javaToExtendedDosTime(time);
// Avoid setting the mtime field if time is in the valid range for a DOS time
if(xdostime != DOSTIME_BEFORE_1980 && time<=UPPER_DOSTIME_BOUND) {
this.mtime = null;
} else {
this.mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
}
}
/**
* Sets the last modification time of the entry in local date-time.
*
* <p> If the entry is output to a ZIP file or ZIP file formatted
* output stream the last modification time set by this method will
* be stored into the {@code date and time fields} of the zip file
* entry and encoded in standard {@code MS-DOS date and time format}.
* If the date-time set is out of the range of the standard {@code
* MS-DOS date and time format}, the time will also be stored into
* zip file entry's extended timestamp fields in {@code optional
* extra data} in UTC time. The {@link java.time.ZoneId#systemDefault()
* system default TimeZone} is used to convert the local date-time
* to UTC time.
*
* <p> {@code LocalDateTime} uses a precision of nanoseconds, whereas
* this class uses a precision of milliseconds. The conversion will
* truncate any excess precision information as though the amount in
* nanoseconds was subject to integer division by one million.
*
* @param time The last modification time of the entry in local date-time
*
* @see #getTimeLocal()
* @since 9
*/
// 设置压缩实体的最后修改时间
public void setTimeLocal(LocalDateTime time) {
int year = time.getYear() - 1980;
if(year<0) {
this.xdostime = DOSTIME_BEFORE_1980;
} else {
this.xdostime = ((year << 25 | time.getMonthValue() << 21 | time.getDayOfMonth() << 16 | time.getHour() << 11 | time.getMinute() << 5 | time.getSecond() >> 1) & 0xffffffffL)
+ ((long) (((time.getSecond() & 0x1) * 1000) + time.getNano() / 1000_000) << 32);
}
if(xdostime != DOSTIME_BEFORE_1980 && year<=0x7f) {
this.mtime = null;
} else {
this.mtime = FileTime.from(ZonedDateTime.of(time, ZoneId.systemDefault()).toInstant());
}
}
/**
* Sets the last modification time of the entry.
*
* <p> When output to a ZIP file or ZIP file formatted output stream
* the last modification time set by this method will be stored into
* zip file entry's {@code date and time fields} in {@code standard
* MS-DOS date and time format}), and the extended timestamp fields
* in {@code optional extra data} in UTC time.
*
* @param time The last modification time of the entry
*
* @return This zip entry
*
* @throws NullPointerException if the {@code time} is null
* @see #getLastModifiedTime()
* @since 1.8
*/
// 设置压缩实体的最后修改时间
public ZipEntry setLastModifiedTime(FileTime time) {
this.mtime = Objects.requireNonNull(time, "lastModifiedTime");
this.xdostime = javaToExtendedDosTime(time.to(TimeUnit.MILLISECONDS));
return this;
}
/**
* Sets the last access time of the entry.
*
* <p> If set, the last access time will be stored into the extended
* timestamp fields of entry's {@code optional extra data}, when output
* to a ZIP file or ZIP file formatted stream.
*
* @param time The last access time of the entry
*
* @return This zip entry
*
* @throws NullPointerException if the {@code time} is null
* @see #getLastAccessTime()
* @since 1.8
*/
// 设置压缩实体的最后访问时间
public ZipEntry setLastAccessTime(FileTime time) {
this.atime = Objects.requireNonNull(time, "lastAccessTime");
return this;
}
/**
* Sets the creation time of the entry.
*
* <p> If set, the creation time will be stored into the extended
* timestamp fields of entry's {@code optional extra data}, when
* output to a ZIP file or ZIP file formatted stream.
*
* @param time The creation time of the entry
*
* @return This zip entry
*
* @throws NullPointerException if the {@code time} is null
* @see #getCreationTime()
* @since 1.8
*/
// 设置压缩实体的创建时间
public ZipEntry setCreationTime(FileTime time) {
this.ctime = Objects.requireNonNull(time, "creationTime");
return this;
}
/**
* Sets the CRC-32 checksum of the uncompressed entry data.
*
* @param crc the CRC-32 value
*
* @throws IllegalArgumentException if the specified CRC-32 value is
* less than 0 or greater than 0xFFFFFFFF
* @see #getCrc()
*/
// 设置crc-32校验码
public void setCrc(long crc) {
if(crc<0 || crc>0xFFFFFFFFL) {
throw new IllegalArgumentException("invalid entry crc-32");
}
this.crc = crc;
}
/**
* Sets the uncompressed size of the entry data.
*
* @param size the uncompressed size in bytes
*
* @throws IllegalArgumentException if the specified size is less
* than 0, is greater than 0xFFFFFFFF when
* <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
* or is less than 0 when ZIP64 is supported
* @see #getSize()
*/
// 设置压缩实体压缩前的大小
public void setSize(long size) {
if(size<0) {
throw new IllegalArgumentException("invalid entry size");
}
this.size = size;
}
/**
* Sets the size of the compressed entry data.
*
* @param csize the compressed size to set
*
* @see #getCompressedSize()
*/
// 设置压缩实体压缩后的大小
public void setCompressedSize(long csize) {
this.csize = csize;
}
/**
* Sets the optional extra field data for the entry.
*
* <p> Invoking this method may change this entry's last modification
* time, last access time and creation time, if the {@code extra} field
* data includes the extensible timestamp fields, such as {@code NTFS tag
* 0x0001} or {@code Info-ZIP Extended Timestamp}, as specified in
* <a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">Info-ZIP
* Application Note 970311</a>.
*
* @param extra The extra field data bytes
*
* @throws IllegalArgumentException if the length of the specified
* extra field data is greater than 0xFFFF bytes
* @see #getExtra()
*/
// 为当前实体设置附加信息
public void setExtra(byte[] extra) {
setExtra0(extra, false);
}
/**
* Sets the optional comment string for the entry.
*
* <p>ZIP entry comments have maximum length of 0xffff. If the length of the
* specified comment string is greater than 0xFFFF bytes after encoding, only
* the first 0xFFFF bytes are output to the ZIP file entry.
*
* @param comment the comment string
*
* @see #getComment()
*/
// 为当前实体设置注释信息
public void setComment(String comment) {
this.comment = comment;
}
/*▲ set ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ get ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Returns the compression method of the entry.
*
* @return the compression method of the entry, or -1 if not specified
*
* @see #setMethod(int)
*/
// 返回对压缩实体的压缩方法
public int getMethod() {
return method;
}
/**
* Returns the last modification time of the entry.
*
* <p> If the entry is read from a ZIP file or ZIP file formatted
* input stream, this is the last modification time from the {@code
* date and time fields} of the zip file entry. The
* {@link java.util.TimeZone#getDefault() default TimeZone} is used
* to convert the standard MS-DOS formatted date and time to the
* epoch time.
*
* @return The last modification time of the entry in milliseconds
* since the epoch, or -1 if not specified
*
* @see #setTime(long)
* @see #setLastModifiedTime(FileTime)
*/
// 返回压缩实体的最后修改时间
public long getTime() {
if(mtime != null) {
return mtime.toMillis();
}
return (xdostime != -1) ? extendedDosToJavaTime(xdostime) : -1;
}
/**
* Returns the last modification time of the entry in local date-time.
*
* <p> If the entry is read from a ZIP file or ZIP file formatted
* input stream, this is the last modification time from the zip
* file entry's {@code optional extra data} if the extended timestamp
* fields are present. Otherwise, the last modification time is read
* from entry's standard MS-DOS formatted {@code date and time fields}.
*
* <p> The {@link java.time.ZoneId#systemDefault() system default TimeZone}
* is used to convert the UTC time to local date-time.
*
* @return The last modification time of the entry in local date-time
*
* @see #setTimeLocal(LocalDateTime)
* @since 9
*/
// 返回压缩实体的最后修改时间
public LocalDateTime getTimeLocal() {
if(mtime != null) {
return LocalDateTime.ofInstant(mtime.toInstant(), ZoneId.systemDefault());
}
int ms = (int) (xdostime >> 32);
return LocalDateTime.of(
(int) (((xdostime >> 25) & 0x7f) + 1980),
(int) ((xdostime >> 21) & 0x0f),
(int) ((xdostime >> 16) & 0x1f),
(int) ((xdostime >> 11) & 0x1f),
(int) ((xdostime >> 5) & 0x3f),
(int) ((xdostime << 1) & 0x3e) + ms / 1000,
(ms % 1000) * 1000_000);
}
/**
* Returns the last modification time of the entry.
*
* <p> If the entry is read from a ZIP file or ZIP file formatted
* input stream, this is the last modification time from the zip
* file entry's {@code optional extra data} if the extended timestamp
* fields are present. Otherwise the last modification time is read
* from the entry's {@code date and time fields}, the {@link
* java.util.TimeZone#getDefault() default TimeZone} is used to convert
* the standard MS-DOS formatted date and time to the epoch time.
*
* @return The last modification time of the entry, null if not specified
*
* @see #setLastModifiedTime(FileTime)
* @since 1.8
*/
// 返回压缩实体的最后修改时间
public FileTime getLastModifiedTime() {
if(mtime != null) {
return mtime;
}
if(xdostime == -1) {
return null;
}
return FileTime.from(getTime(), TimeUnit.MILLISECONDS);
}
/**
* Returns the last access time of the entry.
*
* <p> The last access time is from the extended timestamp fields
* of entry's {@code optional extra data} when read from a ZIP file
* or ZIP file formatted stream.
*
* @return The last access time of the entry, null if not specified
*
* @see #setLastAccessTime(FileTime)
* @since 1.8
*/
// 返回压缩实体的最后访问时间
public FileTime getLastAccessTime() {
return atime;
}
/**
* Returns the creation time of the entry.
*
* <p> The creation time is from the extended timestamp fields of
* entry's {@code optional extra data} when read from a ZIP file
* or ZIP file formatted stream.
*
* @return the creation time of the entry, null if not specified
*
* @see #setCreationTime(FileTime)
* @since 1.8
*/
// 返回压缩实体的创建时间
public FileTime getCreationTime() {
return ctime;
}
/**
* Returns the CRC-32 checksum of the uncompressed entry data.
*
* @return the CRC-32 checksum of the uncompressed entry data, or -1 if
* not known
*
* @see #setCrc(long)
*/
// 返回crc-32校验码
public long getCrc() {
return crc;
}
/**
* Returns the uncompressed size of the entry data.
*
* @return the uncompressed size of the entry data, or -1 if not known
*
* @see #setSize(long)
*/
// 返回压缩实体压缩前的大小
public long getSize() {
return size;
}
/**
* Returns the size of the compressed entry data.
*
* <p> In the case of a stored entry, the compressed size will be the same
* as the uncompressed size of the entry.
*
* @return the size of the compressed entry data, or -1 if not known
*
* @see #setCompressedSize(long)
*/
// 返回压缩实体压缩后的大小
public long getCompressedSize() {
return csize;
}
/**
* Returns the extra field data for the entry.
*
* @return the extra field data for the entry, or null if none
*
* @see #setExtra(byte[])
*/
// 返回当前实体的附加信息
public byte[] getExtra() {
return extra;
}
/**
* Returns the comment string for the entry.
*
* @return the comment string for the entry, or null if none
*
* @see #setComment(String)
*/
// 返回当前实体的注释信息
public String getComment() {
return comment;
}
/**
* Returns the name of the entry.
*
* @return the name of the entry
*/
// 返回当前实体的名称
public String getName() {
return name;
}
/*▲ get ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Returns true if this is a directory entry.
* A directory entry is defined to be one whose name ends with a '/'.
*
* @return true if this is a directory entry
*/
// 判断当前实体是否为目录(很重要的一点:需要以/结尾)
public boolean isDirectory() {
return name.endsWith("/");
}
/*▲ ████████████████████████████████████████████████████████████████████████████████┛ */
/**
* Returns a string representation of the ZIP entry.
*/
public String toString() {
return getName();
}
/**
* Returns the hash code value for this entry.
*/
public int hashCode() {
return name.hashCode();
}
/**
* Returns a copy of this entry.
*/
public Object clone() {
try {
ZipEntry e = (ZipEntry) super.clone();
e.extra = (extra == null) ? null : extra.clone();
return e;
} catch(CloneNotSupportedException e) {
// This should never happen, since we are Cloneable
throw new InternalError(e);
}
}
/**
* Sets the optional extra field data for the entry.
*
* @param extra the extra field data bytes
* @param doZIP64 if true, set size and csize from ZIP64 fields if present
*/
// 为当前zip实体填充扩展信息
void setExtra0(byte[] extra, boolean doZIP64) {
if(extra != null) {
// 附加数据不宜过长
if(extra.length>0xFFFF) {
throw new IllegalArgumentException("invalid extra field length");
}
// extra fields are in "HeaderID(2)DataSize(2)Data... format
int off = 0;
int len = extra.length;
while(off + 4<len) {
// 从extra的off处获取2个字节
int tag = get16(extra, off);
// 从extra的off + 2处获取2个字节
int sz = get16(extra, off + 2);
off += 4;
if(off + sz>len) {
break; // 无效数据
}
switch(tag) {
case EXTID_ZIP64:
if(doZIP64) {
/*
* LOC extra zip64 entry MUST include BOTH original and compressed file size fields.
* If invalid zip64 extra fields, simply skip.
* Even it's rare, it's possible the entry size happens to be the magic value
* and it "accidently" has some bytes in extra match the id.
*/
if(sz >= 16) {
// 从extra的off处获取8个字节
size = get64(extra, off);
// 从extra的off+ 8处获取8个字节
csize = get64(extra, off + 8);
}
}
break;
case EXTID_NTFS:
// reserved 4 bytes + tag 2 bytes + size 2 bytes
if(sz<32) {
break;// m[a|c]time 24 bytes
}
int pos = off + 4; // reserved 4 bytes
if(get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24) {
break;
}
long wtime = get64(extra, pos + 4);
if(wtime != WINDOWS_TIME_NOT_AVAILABLE) {
mtime = winTimeToFileTime(wtime);
}
wtime = get64(extra, pos + 12);
if(wtime != WINDOWS_TIME_NOT_AVAILABLE) {
atime = winTimeToFileTime(wtime);
}
wtime = get64(extra, pos + 20);
if(wtime != WINDOWS_TIME_NOT_AVAILABLE) {
ctime = winTimeToFileTime(wtime);
}
break;
case EXTID_EXTT:
int flag = Byte.toUnsignedInt(extra[off]);
int sz0 = 1;
/*
* The CEN-header extra field contains the modification time only, or no timestamp at all.
* 'sz' is used to flag its presence or absence.
* But if mtime is present in LOC it must be present in CEN as well.
*/
if((flag & 0x1) != 0 && (sz0 + 4)<=sz) {
mtime = unixTimeToFileTime(get32S(extra, off + sz0));
sz0 += 4;
}
if((flag & 0x2) != 0 && (sz0 + 4)<=sz) {
atime = unixTimeToFileTime(get32S(extra, off + sz0));
sz0 += 4;
}
if((flag & 0x4) != 0 && (sz0 + 4)<=sz) {
ctime = unixTimeToFileTime(get32S(extra, off + sz0));
sz0 += 4;
}
break;
default:
}
off += sz;
}
}
this.extra = extra;
}
}