
software.coley.llzip.format.model.CentralDirectoryFileHeader Maven / Gradle / Ivy
package software.coley.llzip.format.model;
import software.coley.llzip.util.ByteData;
import software.coley.llzip.util.ByteDataUtil;
import software.coley.llzip.util.lazy.LazyByteData;
import software.coley.llzip.util.lazy.LazyInt;
import software.coley.llzip.util.lazy.LazyLong;
import java.util.Objects;
/**
* ZIP CentralDirectoryFileHeader structure.
*
* {@code
* SIGNATURE Signature ;
* VERSION_MADE_BY VersionMadeBy ;
* WORD VersionNeededToExtract ;
* WORD GeneralPurposeBitFlag ;
* COMPRESSION_METHOD CompressionMethod ;
* DOSTIME LastModFileTime ;
* DOSDATE LastModFileDate ;
* DWORD Crc32 ;
* DWORD CompressedSize ;
* DWORD UncompressedSize ;
* WORD FileNameLength ;
* WORD ExtraFieldLength ;
* WORD FileCommentLength ;
* WORD DiskNumberStart ;
* WORD InternalFileAttributes ;
* DWORD ExternalFileAttributes ;
* DWORD RelativeOffsetOfLocalHeader ;
* }
*
*
* @author Matt Coley
*/
public class CentralDirectoryFileHeader extends AbstractZipFileHeader {
protected static final long MIN_FIXED_SIZE = 46;
private transient LocalFileHeader linkedFileHeader;
// CentralDirectoryFileHeader spec (plus common elements between this and local file)
private LazyInt versionMadeBy;
private LazyInt fileCommentLength;
private LazyByteData fileComment;
private LazyInt diskNumberStart;
private LazyInt internalFileAttributes;
private LazyInt externalFileAttributes;
private LazyLong relativeOffsetOfLocalHeader;
// String cache values
private transient String fileCommentCache;
@Override
public void read(ByteData data, long offset) {
super.read(data, offset);
versionMadeBy = readWord(data, 4);
versionMadeBy = readWord(data, 4);
versionNeededToExtract = readWord(data, 6);
generalPurposeBitFlag = readWord(data, 8);
compressionMethod = readWord(data, 10);
lastModFileTime = readWord(data, 12);
lastModFileDate = readWord(data, 14);
crc32 = readQuad(data, 16);
compressedSize = readMaskedLongQuad(data, 20);
uncompressedSize = readMaskedLongQuad(data, 24);
fileNameLength = readWord(data, 28);
extraFieldLength = readWord(data, 30);
fileCommentLength = readWord(data, 32);
diskNumberStart = readWord(data, 34);
internalFileAttributes = readWord(data, 36);
externalFileAttributes = readQuad(data, 38);
relativeOffsetOfLocalHeader = readMaskedLongQuad(data, 42);
fileName = readSlice(data, new LazyInt(() -> 46), fileNameLength);
extraField = readSlice(data, fileNameLength.add(46), extraFieldLength);
fileComment = readSlice(data, fileNameLength.add(46).add(extraFieldLength), fileCommentLength);
}
@Override
public long length() {
return MIN_FIXED_SIZE +
fileNameLength.get() +
extraFieldLength.get() +
fileCommentLength.get();
}
@Override
public PartType type() {
return PartType.CENTRAL_DIRECTORY_FILE_HEADER;
}
/**
* @return The file header associated with {@link #getRelativeOffsetOfLocalHeader()}. May be {@code null}.
*/
public LocalFileHeader getLinkedFileHeader() {
return linkedFileHeader;
}
/**
* @param header
* The file header associated with {@link #getRelativeOffsetOfLocalHeader()}. May be {@code null}.
*/
public void link(LocalFileHeader header) {
this.linkedFileHeader = header;
}
/**
* @return Version of zip software used to make the archive.
*/
public int getVersionMadeBy() {
return versionMadeBy.get();
}
/**
* @param versionMadeBy
* Version of zip software used to make the archive.
*/
public void setVersionMadeBy(int versionMadeBy) {
this.versionMadeBy.set(versionMadeBy);
}
/**
* @return Disk number where the archive starts from, or {@code 0xFFFF} for ZIP64.
*/
public int getDiskNumberStart() {
return diskNumberStart.get();
}
/**
* @param diskNumberStart
* Disk number where the archive starts from, or {@code 0xFFFF} for ZIP64.
*/
public void setDiskNumberStart(int diskNumberStart) {
this.diskNumberStart.set(diskNumberStart);
}
/**
* The lowest bit of this field indicates, if set,
* that the file is apparently an ASCII or text file.
* If not set, that the file apparently contains binary data.
*
* The {@code 0x0002} bit of this field indicates, if set,
* that a 4 byte variable record length control field precedes each
* logical record indicating the length of the record.
*
* @return Internal attributes used for inferring content type.
*/
public int getInternalFileAttributes() {
return internalFileAttributes.get();
}
/**
* @param internalFileAttributes
* Internal attributes used for inferring content type.
*/
public void setInternalFileAttributes(int internalFileAttributes) {
this.internalFileAttributes.set(internalFileAttributes);
}
/**
* For MS-DOS, the low order byte is the MS-DOS directory attribute byte.
* If input came from standard input, this field is zero.
*
* @return Host system dependent attributes.
*/
public int getExternalFileAttributes() {
return externalFileAttributes.get();
}
/**
* @param externalFileAttributes
* Host system dependent attributes.
*/
public void setExternalFileAttributes(int externalFileAttributes) {
this.externalFileAttributes.set(externalFileAttributes);
}
/**
* @return Offset from the start of the {@link #getDiskNumberStart() first disk} where the file appears.
* This should also be where the {@link LocalFileHeader} is located. Or {@code 0xFFFFFFFF} for ZIP64.
*/
public long getRelativeOffsetOfLocalHeader() {
return relativeOffsetOfLocalHeader.get();
}
/**
* @param relativeOffsetOfLocalHeader
* Offset from the start of the {@link #getDiskNumberStart() first disk} where the file appears.
* This should also be where the {@link LocalFileHeader} is located. Or {@code 0xFFFFFFFF} for ZIP64.
*/
public void setRelativeOffsetOfLocalHeader(long relativeOffsetOfLocalHeader) {
this.relativeOffsetOfLocalHeader.set(relativeOffsetOfLocalHeader & 0xFFFFFFFFL);
}
/**
* @return Length of {@link #getFileComment()}.
*/
public int getFileCommentLength() {
return fileCommentLength.get();
}
/**
* @param fileCommentLength
* Length of {@link #getFileComment()}.
*/
public void setFileCommentLength(int fileCommentLength) {
this.fileCommentLength.set(fileCommentLength & 0xFFFF);
}
/**
* @return File comment.
*/
public ByteData getFileComment() {
return fileComment.get();
}
/**
* @param fileComment
* File comment.
*/
public void setFileComment(ByteData fileComment) {
this.fileComment.set(fileComment);
}
/**
* @return File comment.
*/
public String getFileCommentAsString() {
String fileCommentCache = this.fileCommentCache;
if (fileCommentCache == null) {
return this.fileCommentCache = ByteDataUtil.toString(fileComment.get());
}
return fileCommentCache;
}
@Override
public String toString() {
return "CentralDirectoryFileHeader{" +
" versionMadeBy=" + versionMadeBy +
", versionNeededToExtract=" + versionNeededToExtract +
", generalPurposeBitFlag=" + generalPurposeBitFlag +
", compressionMethod=" + compressionMethod +
", lastModFileTime=" + lastModFileTime +
", lastModFileDate=" + lastModFileDate +
", crc32=" + crc32 +
", compressedSize=" + compressedSize +
", uncompressedSize=" + uncompressedSize +
", fileNameLength=" + fileNameLength +
", extraFieldLength=" + extraFieldLength +
", fileCommentLength=" + fileCommentLength +
", diskNumberStart=" + diskNumberStart +
", internalFileAttributes=" + internalFileAttributes +
", externalFileAttributes=" + externalFileAttributes +
", relativeOffsetOfLocalHeader=" + relativeOffsetOfLocalHeader +
", fileName='" + getFileNameAsString() + '\'' +
", extraField='" + getExtraFieldAsString() + '\'' +
", fileComment='" + getFileCommentAsString() + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CentralDirectoryFileHeader that = (CentralDirectoryFileHeader) o;
if (!Objects.equals(linkedFileHeader, that.linkedFileHeader)) return false;
if (!Objects.equals(versionMadeBy, that.versionMadeBy)) return false;
if (!Objects.equals(versionNeededToExtract, that.versionNeededToExtract)) return false;
if (!Objects.equals(generalPurposeBitFlag, that.generalPurposeBitFlag)) return false;
if (!Objects.equals(compressionMethod, that.compressionMethod)) return false;
if (!Objects.equals(lastModFileTime, that.lastModFileTime)) return false;
if (!Objects.equals(lastModFileDate, that.lastModFileDate)) return false;
if (!Objects.equals(crc32, that.crc32)) return false;
if (!Objects.equals(compressedSize, that.compressedSize)) return false;
if (!Objects.equals(uncompressedSize, that.uncompressedSize)) return false;
if (!Objects.equals(fileNameLength, that.fileNameLength)) return false;
if (!Objects.equals(extraFieldLength, that.extraFieldLength)) return false;
if (!Objects.equals(fileCommentLength, that.fileCommentLength)) return false;
if (!Objects.equals(diskNumberStart, that.diskNumberStart)) return false;
if (!Objects.equals(internalFileAttributes, that.internalFileAttributes)) return false;
if (!Objects.equals(externalFileAttributes, that.externalFileAttributes)) return false;
if (!Objects.equals(relativeOffsetOfLocalHeader, that.relativeOffsetOfLocalHeader)) return false;
if (!Objects.equals(fileName, that.fileName)) return false;
if (!Objects.equals(extraField, that.extraField)) return false;
return Objects.equals(fileComment, that.fileComment);
}
@Override
public int hashCode() {
int result = linkedFileHeader != null ? linkedFileHeader.hashCode() : 0;
result = 31 * result + (versionMadeBy != null ? versionMadeBy.hashCode() : 0);
result = 31 * result + (versionNeededToExtract != null ? versionNeededToExtract.hashCode() : 0);
result = 31 * result + (generalPurposeBitFlag != null ? generalPurposeBitFlag.hashCode() : 0);
result = 31 * result + (compressionMethod != null ? compressionMethod.hashCode() : 0);
result = 31 * result + (lastModFileTime != null ? lastModFileTime.hashCode() : 0);
result = 31 * result + (lastModFileDate != null ? lastModFileDate.hashCode() : 0);
result = 31 * result + (crc32 != null ? crc32.hashCode() : 0);
result = 31 * result + (compressedSize != null ? compressedSize.hashCode() : 0);
result = 31 * result + (uncompressedSize != null ? uncompressedSize.hashCode() : 0);
result = 31 * result + (fileNameLength != null ? fileNameLength.hashCode() : 0);
result = 31 * result + (extraFieldLength != null ? extraFieldLength.hashCode() : 0);
result = 31 * result + (fileCommentLength != null ? fileCommentLength.hashCode() : 0);
result = 31 * result + (diskNumberStart != null ? diskNumberStart.hashCode() : 0);
result = 31 * result + (internalFileAttributes != null ? internalFileAttributes.hashCode() : 0);
result = 31 * result + (externalFileAttributes != null ? externalFileAttributes.hashCode() : 0);
result = 31 * result + (relativeOffsetOfLocalHeader != null ? relativeOffsetOfLocalHeader.hashCode() : 0);
result = 31 * result + (fileName != null ? fileName.hashCode() : 0);
result = 31 * result + (extraField != null ? extraField.hashCode() : 0);
result = 31 * result + (fileComment != null ? fileComment.hashCode() : 0);
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy