1717 * @author Matt Coley
1818 */
1919public abstract class AbstractZipFileHeader implements ZipPart , ZipRead {
20+ // Extra field ID's can be found at: APPNOTE 4.5.2
21+ protected static final int EXTRA_FID_ZIP64 = 0x0001 ;
22+
2023 // Zip spec elements, common between central/local file headers
2124 protected int versionNeededToExtract ;
2225 protected int generalPurposeBitFlag ;
@@ -36,7 +39,8 @@ public abstract class AbstractZipFileHeader implements ZipPart, ZipRead {
3639
3740 // Data source that contents were read from.
3841 protected transient MemorySegment data ;
39-
42+ protected transient boolean zip64CompressedSize ;
43+ protected transient boolean zip64UncompressedSize ;
4044
4145 /**
4246 * @return The associated backing data that this file header was read from.
@@ -74,6 +78,8 @@ public void setVersionNeededToExtract(int versionNeededToExtract) {
7478
7579 /**
7680 * @return Used primarily to expand on details of file compression.
81+ *
82+ * @apiNote APPNOTE 4.4.4 for flag bit purposes.
7783 */
7884 public int getGeneralPurposeBitFlag () {
7985 return generalPurposeBitFlag ;
@@ -169,7 +175,7 @@ public long getCompressedSize() {
169175 * Compressed size of {@link LocalFileHeader#getFileData()}.
170176 */
171177 public void setCompressedSize (long compressedSize ) {
172- this .compressedSize = compressedSize & 0xFFFFFFFFL ;
178+ this .compressedSize = compressedSize ;
173179 }
174180
175181 /**
@@ -188,7 +194,7 @@ public long getUncompressedSize() {
188194 * Uncompressed size after {@link LocalFileHeader#decompress(Decompressor)} is used on {@link LocalFileHeader#getFileData()}.
189195 */
190196 public void setUncompressedSize (long uncompressedSize ) {
191- this .uncompressedSize = uncompressedSize & 0xFFFFFFFFL ;
197+ this .uncompressedSize = uncompressedSize ;
192198 }
193199
194200 /**
@@ -266,6 +272,117 @@ public String getExtraFieldAsString() {
266272 return MemorySegmentUtil .toString (extraField .get ());
267273 }
268274
275+ /**
276+ * @return {@code true} when the compressed-size value was hydrated from the ZIP64 extended information extra field.
277+ */
278+ protected boolean hasZip64CompressedSize () {
279+ return zip64CompressedSize ;
280+ }
281+
282+ /**
283+ * @return {@code true} when the uncompressed-size value was hydrated from the ZIP64 extended information extra field.
284+ */
285+ protected boolean hasZip64UncompressedSize () {
286+ return zip64UncompressedSize ;
287+ }
288+
289+ /**
290+ * Reads ZIP64 extended information from the extra field, if present.
291+ *
292+ * @param readRelativeOffset
293+ * {@code true} to read the relative header offset, {@code false} to skip it.
294+ * @param readDiskStart
295+ * {@code true} to read the disk start number, {@code false} to skip it.
296+ *
297+ * @return ZIP64 extended information, or an empty instance if not present or invalid.
298+ */
299+ @ Nonnull
300+ protected final Zip64ExtendedInfo readZip64ExtendedInfo (boolean readRelativeOffset , boolean readDiskStart ) {
301+ zip64CompressedSize = false ;
302+ zip64UncompressedSize = false ;
303+ if (extraFieldLength <= 0 )
304+ return Zip64ExtendedInfo .EMPTY ;
305+
306+ MemorySegment extra = extraField .get ();
307+ int off = 0 ;
308+ int len = (int ) extra .byteSize ();
309+ while (off + 4 <= len ) {
310+ // APPNOTE 4.5.3 defines the Zip64 extended information extra field as follows:
311+ // 2: Header ID (0x0001)
312+ // 2: Size of this block
313+ // 8: Size of uncompressed/original data
314+ // 8: Size of compressed data
315+ // 8: Relative header offset (from the start of the first disk)
316+ // 4: Disk start number
317+ int tag = MemorySegmentUtil .readWord (extra , off );
318+ int size = MemorySegmentUtil .readWord (extra , off + 2 );
319+ off += 4 ;
320+ if (off + size > len )
321+ return Zip64ExtendedInfo .EMPTY ;
322+ if (tag == EXTRA_FID_ZIP64 ) {
323+ long pos = off ;
324+ long limit = off + size ;
325+ long resolvedUncompressedSize = 0L ;
326+ long resolvedCompressedSize = 0L ;
327+ long resolvedRelativeOffset = 0L ;
328+ long resolvedDiskStart = 0L ;
329+ boolean hasResolvedUncompressedSize = false ;
330+ boolean hasResolvedCompressedSize = false ;
331+ boolean hasResolvedRelativeOffset = false ;
332+ boolean hasResolvedDiskStart = false ;
333+
334+ if (uncompressedSize == 0xFFFFFFFFL ) {
335+ if (pos + 8L > limit )
336+ return Zip64ExtendedInfo .EMPTY ;
337+ resolvedUncompressedSize = MemorySegmentUtil .readLong (extra , pos );
338+ pos += 8L ;
339+ hasResolvedUncompressedSize = true ;
340+ }
341+ if (compressedSize == 0xFFFFFFFFL ) {
342+ if (pos + 8L > limit )
343+ return Zip64ExtendedInfo .EMPTY ;
344+ resolvedCompressedSize = MemorySegmentUtil .readLong (extra , pos );
345+ pos += 8L ;
346+ hasResolvedCompressedSize = true ;
347+ }
348+ if (readRelativeOffset ) {
349+ if (pos + 8L > limit )
350+ return Zip64ExtendedInfo .EMPTY ;
351+ resolvedRelativeOffset = MemorySegmentUtil .readLong (extra , pos );
352+ pos += 8L ;
353+ hasResolvedRelativeOffset = true ;
354+ }
355+ if (readDiskStart ) {
356+ if (pos + 4L > limit )
357+ return Zip64ExtendedInfo .EMPTY ;
358+ resolvedDiskStart = MemorySegmentUtil .readMaskedLongQuad (extra , pos , 0 );
359+ hasResolvedDiskStart = true ;
360+ }
361+
362+ if (hasResolvedUncompressedSize )
363+ zip64UncompressedSize = true ;
364+ if (hasResolvedCompressedSize )
365+ zip64CompressedSize = true ;
366+
367+ return new Zip64ExtendedInfo (
368+ hasResolvedUncompressedSize , resolvedUncompressedSize ,
369+ hasResolvedCompressedSize , resolvedCompressedSize ,
370+ hasResolvedRelativeOffset , resolvedRelativeOffset ,
371+ hasResolvedDiskStart , resolvedDiskStart
372+ );
373+ }
374+ off += size ;
375+ }
376+ return Zip64ExtendedInfo .EMPTY ;
377+ }
378+
379+ protected record Zip64ExtendedInfo (boolean hasUncompressedSize , long uncompressedSize ,
380+ boolean hasCompressedSize , long compressedSize ,
381+ boolean hasRelativeOffset , long relativeOffset ,
382+ boolean hasDiskStart , long diskStart ) {
383+ protected static final Zip64ExtendedInfo EMPTY = new Zip64ExtendedInfo (false , 0L , false , 0L , false , 0L , false , 0L );
384+ }
385+
269386 @ Override
270387 public boolean equals (Object o ) {
271388 if (this == o ) return true ;
0 commit comments