private void ReadEntries() {
// Search for the End Of Central Directory. When a zip comment is
// present the directory will start earlier
//
// The search is limited to 64K which is the maximum size of a trailing comment field to aid speed.
// This should be compatible with both SFX and ZIP files but has only been tested for Zip files
// If a SFX file has the Zip data attached as a resource and there are other resources occuring later then
// this could be invalid.
// Could also speed this up by reading memory in larger blocks.
if (baseStream_.CanSeek==false) {
throw new ZipException("ZipFile stream must be seekable");
}
long locatedEndOfCentralDir=LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature,
baseStream_.Length,
ZipConstants.EndOfCentralRecordBaseSize, 0xffff);
if (locatedEndOfCentralDir<0) {
throw new ZipException("Cannot find central directory");
}
// Read end of central directory record
ushort thisDiskNumber=ReadLEUshort();
ushort startCentralDirDisk=ReadLEUshort();
ulong entriesForThisDisk=ReadLEUshort();
ulong entriesForWholeCentralDir=ReadLEUshort();
ulong centralDirSize=ReadLEUint();
long offsetOfCentralDir=ReadLEUint();
uint commentSize=ReadLEUshort();
if (commentSize>0) {
var comment=new byte[commentSize];
StreamUtils.ReadFully(baseStream_, comment);
comment_=ZipConstants.ConvertToString(comment);
} else {
comment_=string.Empty;
}
bool isZip64=false;
// Check if zip64 header information is required.
if ((thisDiskNumber==0xffff)||
(startCentralDirDisk==0xffff)||
(entriesForThisDisk==0xffff)||
(entriesForWholeCentralDir==0xffff)||
(centralDirSize==0xffffffff)||
(offsetOfCentralDir==0xffffffff)) {
isZip64=true;
long offset=LocateBlockWithSignature(ZipConstants.Zip64CentralDirLocatorSignature,
locatedEndOfCentralDir, 0, 0x1000);
if (offset<0) {
throw new ZipException("Cannot find Zip64 locator");
}
// number of the disk with the start of the zip64 end of central directory 4 bytes
// relative offset of the zip64 end of central directory record 8 bytes
// total number of disks 4 bytes
ReadLEUint(); // startDisk64 is not currently used
ulong offset64=ReadLEUlong();
uint totalDisks=ReadLEUint();
baseStream_.Position=(long)offset64;
long sig64=ReadLEUint();
if (sig64!=ZipConstants.Zip64CentralFileHeaderSignature) {
throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64));
}
// NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12.
ulong recordSize=ReadLEUlong();
int versionMadeBy=ReadLEUshort();
int versionToExtract=ReadLEUshort();
uint thisDisk=ReadLEUint();
uint centralDirDisk=ReadLEUint();
entriesForThisDisk=ReadLEUlong();
entriesForWholeCentralDir=ReadLEUlong();
centralDirSize=ReadLEUlong();
offsetOfCentralDir=(long)ReadLEUlong();
// NOTE: zip64 extensible data sector (variable size) is ignored.
}
entries_=new ZipEntry[entriesForThisDisk];
// SFX/embedded support, find the offset of the first entry vis the start of the stream
// This applies to Zip files that are appended to the end of an SFX stub.
// Or are appended as a resource to an executable.
// Zip files created by some archivers have the offsets altered to reflect the true offsets
// and so dont require any adjustment here...
// TODO: Difficulty with Zip64 and SFX offset handling needs resolution - maths?
if (!isZip64&&(offsetOfCentralDir<locatedEndOfCentralDir-(4+(long)centralDirSize))) {
offsetOfFirstEntry=locatedEndOfCentralDir-(4+(long)centralDirSize+offsetOfCentralDir);
if (offsetOfFirstEntry<=0) {
throw new ZipException("Invalid embedded zip archive");
}
}
baseStream_.Seek(offsetOfFirstEntry+offsetOfCentralDir, SeekOrigin.Begin);
for (ulong i=0; i<entriesForThisDisk; i++) {
if (ReadLEUint()!=ZipConstants.CentralHeaderSignature) {
throw new ZipException("Wrong Central Directory signature");
}
int versionMadeBy=ReadLEUshort();
int versionToExtract=ReadLEUshort();
int bitFlags=ReadLEUshort();
int method=ReadLEUshort();
uint dostime=ReadLEUint();
uint crc=ReadLEUint();
long csize=ReadLEUint();
long size=ReadLEUint();
int nameLen=ReadLEUshort();
int extraLen=ReadLEUshort();
int commentLen=ReadLEUshort();
int diskStartNo=ReadLEUshort(); // Not currently used
int internalAttributes=ReadLEUshort(); // Not currently used
uint externalAttributes=ReadLEUint();
long offset=ReadLEUint();
var buffer=new byte[Math.Max(nameLen, commentLen)];
StreamUtils.ReadFully(baseStream_, buffer, 0, nameLen);
string name=ZipConstants.ConvertToStringExt(bitFlags, buffer, nameLen);
var entry=new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method);
entry.Crc=crc&0xffffffffL;
entry.Size=size&0xffffffffL;
entry.CompressedSize=csize&0xffffffffL;
entry.Flags=bitFlags;
entry.DosTime=dostime;
entry.ZipFileIndex=(long)i;
entry.Offset=offset;
entry.ExternalFileAttributes=(int)externalAttributes;
if ((bitFlags&8)==0) {
entry.CryptoCheckValue=(byte)(crc>>24);
} else {
entry.CryptoCheckValue=(byte)((dostime>>8)&0xff);
}
if (extraLen>0) {
var extra=new byte[extraLen];
StreamUtils.ReadFully(baseStream_, extra);
entry.ExtraData=extra;
}
entry.ProcessExtraData(false);
if (commentLen>0) {
StreamUtils.ReadFully(baseStream_, buffer, 0, commentLen);
entry.Comment=ZipConstants.ConvertToStringExt(bitFlags, buffer, commentLen);
}
entries_[i]=entry;
}
}