HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) { if (offset >= ((UInt64)1 << 63)) return S_FALSE; RINOK(Seek(offset)); Byte buf[kEcd64_FullSize]; RINOK(ReadStream_FALSE(Stream, buf, kEcd64_FullSize)); if (Get32(buf) != NSignature::kEcd64) return S_FALSE; UInt64 mainSize = Get64(buf + 4); if (mainSize < kEcd64_MainSize || mainSize > ((UInt64)1 << 32)) return S_FALSE; cdInfo.ParseEcd64(buf); return S_OK; }
HRESULT CInArchive::FindCd(CCdInfo &cdInfo) { UInt64 endPosition; RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPosition)); const UInt32 kBufSizeMax = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize; UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax; if (bufSize < kEcdSize) return S_FALSE; CByteArr byteBuffer(bufSize); UInt64 startPosition = endPosition - bufSize; RINOK(Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position)); if (m_Position != startPosition) return S_FALSE; RINOK(ReadStream_FALSE(Stream, byteBuffer, bufSize)); const Byte *buf = byteBuffer; for (UInt32 i = bufSize - kEcdSize;; i--) { if (buf[i] != 0x50) { if (i == 0) return S_FALSE; i--; if (buf[i] != 0x50) { if (i == 0) return S_FALSE; continue; } } if (Get32(buf + i) == NSignature::kEcd) { if (i >= kEcd64_FullSize + kEcd64Locator_Size) { const Byte *locator = buf + i - kEcd64Locator_Size; if (Get32(locator) == NSignature::kEcd64Locator && Get32(locator + 4) == 0) // number of the disk with the start of the zip64 ECD { // Most of the zip64 use fixed size Zip64 ECD UInt64 ecd64Offset = Get64(locator + 8); UInt64 absEcd64 = endPosition - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); { const Byte *ecd64 = locator - kEcd64_FullSize; if (Get32(ecd64) == NSignature::kEcd64 && Get64(ecd64 + 4) == kEcd64_MainSize) { cdInfo.ParseEcd64(ecd64); ArcInfo.Base = absEcd64 - ecd64Offset; return S_OK; } } // some zip64 use variable size Zip64 ECD. // we try to find it if (absEcd64 != ecd64Offset) { if (TryEcd64(ecd64Offset, cdInfo) == S_OK) { ArcInfo.Base = 0; return S_OK; } } if (ArcInfo.MarkerPos != 0 && ArcInfo.MarkerPos + ecd64Offset != absEcd64) { if (TryEcd64(ArcInfo.MarkerPos + ecd64Offset, cdInfo) == S_OK) { ArcInfo.Base = ArcInfo.MarkerPos; return S_OK; } } } } if (Get32(buf + i + 4) == 0) // ThisDiskNumber, StartCentralDirectoryDiskNumber; { cdInfo.ParseEcd(buf + i); UInt64 absEcdPos = endPosition - bufSize + i; UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; ArcInfo.Base = 0; if (absEcdPos != cdEnd) { /* if (cdInfo.Offset <= 16 && cdInfo.Size != 0) { // here we support some rare ZIP files with Central directory at the start ArcInfo.Base = 0; } else */ ArcInfo.Base = absEcdPos - cdEnd; } return S_OK; } } if (i == 0) return S_FALSE; } }