HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream, UInt32 crc, UInt64 unpackSize) { Byte temp[4]; RINOK(ReadStream_FALSE(inStream, temp, 2)); _ivSize = GetUi16(temp); if (_ivSize == 0) { memset(_iv, 0, 16); SetUi32(_iv + 0, crc); SetUi64(_iv + 4, unpackSize); _ivSize = 12; } else if (_ivSize == 16) { RINOK(ReadStream_FALSE(inStream, _iv, _ivSize)); } else return E_NOTIMPL; RINOK(ReadStream_FALSE(inStream, temp, 4)); _remSize = GetUi32(temp); // const UInt32 kAlign = 16; if (_remSize < 16 || _remSize > (1 << 18)) return E_NOTIMPL; if (_remSize > _bufAligned.Size()) { _bufAligned.AllocAtLeast(_remSize); if (!(Byte *)_bufAligned) return E_OUTOFMEMORY; } return ReadStream_FALSE(inStream, _bufAligned, _remSize); }
HRESULT CCabBlockInStream::PreRead(ISequentialInStream *stream, UInt32 &packSize, UInt32 &unpackSize) { const UInt32 kHeaderSize = 8; const UInt32 kReservedMax = 256; Byte header[kHeaderSize + kReservedMax]; RINOK(ReadStream_FALSE(stream, header, kHeaderSize + ReservedSize)) packSize = GetUi16(header + 4); unpackSize = GetUi16(header + 6); if (packSize > kBlockSize - _size) return S_FALSE; RINOK(ReadStream_FALSE(stream, _buf + _size, packSize)); if (MsZip) { if (_size == 0) { if (packSize < 2 || _buf[0] != 0x43 || _buf[1] != 0x4B) return S_FALSE; _pos = 2; } if (_size + packSize > ((UInt32)1 << 15) + 12) /* v9.31 fix. MSZIP specification */ return S_FALSE; } if (GetUi32(header) != 0) // checkSum if (CheckSum(header, kHeaderSize + ReservedSize) != CheckSum(_buf + _size, packSize)) return S_FALSE; _size += packSize; return S_OK; }
HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream, UInt32 crc, UInt64 unpackSize) { Byte temp[4]; RINOK(ReadStream_FALSE(inStream, temp, 2)); _ivSize = GetUi16(temp); if (_ivSize == 0) { memset(_iv, 0, 16); SetUi32(_iv + 0, crc); SetUi64(_iv + 4, unpackSize); _ivSize = 12; } else if (_ivSize == 16) { RINOK(ReadStream_FALSE(inStream, _iv, _ivSize)); } else return E_NOTIMPL; RINOK(ReadStream_FALSE(inStream, temp, 4)); _remSize = GetUi32(temp); const UInt32 kAlign = 16; if (_remSize < 16 || _remSize > (1 << 18)) return E_NOTIMPL; if (_remSize + kAlign > _buf.Size()) { _buf.Alloc(_remSize + kAlign); _bufAligned = (Byte *)((ptrdiff_t)((Byte *)_buf + kAlign - 1) & ~(ptrdiff_t)(kAlign - 1)); } return ReadStream_FALSE(inStream, _bufAligned, _remSize); }
HRESULT CCabBlockInStream::PreRead(UInt32 &packSize, UInt32 &unpackSize) { CTempCabInBuffer2 inBuffer; inBuffer.Pos = 0; RINOK(ReadStream_FALSE(_stream, inBuffer.Buffer, kDataBlockHeaderSize)) UInt32 checkSum = inBuffer.ReadUInt32(); packSize = inBuffer.ReadUInt16(); unpackSize = inBuffer.ReadUInt16(); if (ReservedSize != 0) { RINOK(ReadStream_FALSE(_stream, _buffer, ReservedSize)); } _pos = 0; CCheckSum2 checkSumCalc; checkSumCalc.Init(); UInt32 packSize2 = packSize; if (MsZip && _size == 0) { if (packSize < 2) return S_FALSE; // bad block; Byte sig[2]; RINOK(ReadStream_FALSE(_stream, sig, 2)); if (sig[0] != 0x43 || sig[1] != 0x4B) return S_FALSE; packSize2 -= 2; checkSumCalc.Update(sig, 2); } if (kBlockSize - _size < packSize2) return S_FALSE; UInt32 curSize = packSize2; if (curSize != 0) { size_t processedSizeLoc = curSize; RINOK(ReadStream(_stream, _buffer + _size, &processedSizeLoc)); checkSumCalc.Update(_buffer + _size, (UInt32)processedSizeLoc); _size += (UInt32)processedSizeLoc; if (processedSizeLoc != curSize) return S_FALSE; } TotalPackSize = _size; checkSumCalc.FinishDataUpdate(); bool dataError; if (checkSum == 0) dataError = false; else { checkSumCalc.UpdateUInt32(packSize | (((UInt32)unpackSize) << 16)); dataError = (checkSumCalc.GetResult() != checkSum); } DataError |= dataError; return dataError ? S_FALSE : S_OK; }
HRESULT CHandler::Open2(IInStream *stream) { UInt64 archiveStartPos; RINOK(stream->Seek(0, STREAM_SEEK_SET, &archiveStartPos)); const UInt32 kHeaderSize = 0x1C; Byte buf[kHeaderSize]; RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)); UInt32 size = Get16(buf + 4); // UInt32 ver = Get16(buf + 6); // == 0 if (Get32(buf) != 0x78617221 || size != kHeaderSize) return S_FALSE; UInt64 packSize = Get64(buf + 8); UInt64 unpackSize = Get64(buf + 0x10); // UInt32 checkSumAlogo = Get32(buf + 0x18); if (unpackSize >= kXmlSizeMax) return S_FALSE; _dataStartPos = archiveStartPos + kHeaderSize + packSize; char *ss = _xml.GetBuffer((int)unpackSize + 1); NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder(); CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec; CLimitedSequentialInStream *inStreamLimSpec = new CLimitedSequentialInStream; CMyComPtr<ISequentialInStream> inStreamLim(inStreamLimSpec); inStreamLimSpec->SetStream(stream); inStreamLimSpec->Init(packSize); CBufPtrSeqOutStream *outStreamLimSpec = new CBufPtrSeqOutStream; CMyComPtr<ISequentialOutStream> outStreamLim(outStreamLimSpec); outStreamLimSpec->Init((Byte *)ss, (size_t)unpackSize); RINOK(zlibCoder->Code(inStreamLim, outStreamLim, NULL, NULL, NULL)); if (outStreamLimSpec->GetPos() != (size_t)unpackSize) return S_FALSE; ss[(size_t)unpackSize] = 0; _xml.ReleaseBuffer(); CXml xml; if (!xml.Parse(_xml)) return S_FALSE; if (!xml.Root.IsTagged("xar") || xml.Root.SubItems.Size() != 1) return S_FALSE; const CXmlItem &toc = xml.Root.SubItems[0]; if (!toc.IsTagged("toc")) return S_FALSE; if (!AddItem(toc, _files, -1)) return S_FALSE; return S_OK; }
HRESULT CInArchive::Open2(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { m_CryptoMode = false; RINOK(stream->Seek(0, STREAM_SEEK_SET, &m_StreamStartPosition)); m_Position = m_StreamStartPosition; UInt64 arcStartPos; RINOK(FindSignatureInStream(stream, NHeader::kMarker, NHeader::kMarkerSize, searchHeaderSizeLimit, arcStartPos)); m_Position = arcStartPos + NHeader::kMarkerSize; RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); Byte buf[NHeader::NArchive::kArchiveHeaderSize + 1]; RINOK(ReadStream_FALSE(stream, buf, NHeader::NArchive::kArchiveHeaderSize)); AddToSeekValue(NHeader::NArchive::kArchiveHeaderSize); UInt32 blockSize = Get16(buf + 5); _header.EncryptVersion = 0; _header.Flags = Get16(buf + 3); UInt32 headerSize = NHeader::NArchive::kArchiveHeaderSize; if (_header.IsThereEncryptVer()) { if (blockSize <= headerSize) return S_FALSE; RINOK(ReadStream_FALSE(stream, buf + NHeader::NArchive::kArchiveHeaderSize, 1)); AddToSeekValue(1); _header.EncryptVersion = buf[NHeader::NArchive::kArchiveHeaderSize]; headerSize += 1; } if (blockSize < headerSize || buf[2] != NHeader::NBlockType::kArchiveHeader || (UInt32)Get16(buf) != (CrcCalc(buf + 2, headerSize - 2) & 0xFFFF)) return S_FALSE; size_t commentSize = blockSize - headerSize; _comment.SetCapacity(commentSize); RINOK(ReadStream_FALSE(stream, _comment, commentSize)); AddToSeekValue(commentSize); m_Stream = stream; _header.StartPosition = arcStartPos; return S_OK; }
HRESULT ReadHeader(IInStream *inStream, CHeader &h) { const UInt32 kHeaderSizeMax = 0xD0; Byte p[kHeaderSizeMax]; RINOK(ReadStream_FALSE(inStream, p, kHeaderSizeMax)); if (memcmp(p, kSignature, kSignatureSize) != 0) return S_FALSE; return h.Parse(p); }
HRESULT FindSignatureInStream(ISequentialInStream *stream, const Byte *signature, unsigned signatureSize, const UInt64 *limit, UInt64 &resPos) { resPos = 0; CByteBuffer byteBuffer2(signatureSize); RINOK(ReadStream_FALSE(stream, byteBuffer2, signatureSize)); if (memcmp(byteBuffer2, signature, signatureSize) == 0) return S_OK; const UInt32 kBufferSize = (1 << 16); CByteBuffer byteBuffer(kBufferSize); Byte *buffer = byteBuffer; UInt32 numPrevBytes = signatureSize - 1; memcpy(buffer, (const Byte *)byteBuffer2 + 1, numPrevBytes); resPos = 1; for (;;) { if (limit != NULL) if (resPos > *limit) return S_FALSE; do { UInt32 numReadBytes = kBufferSize - numPrevBytes; UInt32 processedSize; RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize)); numPrevBytes += processedSize; if (processedSize == 0) return S_FALSE; } while (numPrevBytes < signatureSize); UInt32 numTests = numPrevBytes - signatureSize + 1; for (UInt32 pos = 0; pos < numTests; pos++) { Byte b = signature[0]; for (; buffer[pos] != b && pos < numTests; pos++); if (pos == numTests) break; if (memcmp(buffer + pos, signature, signatureSize) == 0) { resPos += pos; return S_OK; } } resPos += numTests; numPrevBytes -= numTests; memmove(buffer, buffer + numTests, numPrevBytes); } }
HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { RINOK(ReadStream_FALSE(stream, _header, kHeaderSize)); if (TestSignature2(_header)) return S_OK; CByteBuffer byteBuffer; const UInt32 kBufferSize = (1 << 16); byteBuffer.SetCapacity(kBufferSize); Byte *buffer = byteBuffer; UInt32 numPrevBytes = kHeaderSize; memcpy(buffer, _header, kHeaderSize); UInt64 curTestPos = _arhiveBeginStreamPosition; for (;;) { if (searchHeaderSizeLimit != NULL) if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit) break; do { UInt32 numReadBytes = kBufferSize - numPrevBytes; UInt32 processedSize; RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize)); numPrevBytes += processedSize; if (processedSize == 0) return S_FALSE; } while (numPrevBytes <= kHeaderSize); UInt32 numTests = numPrevBytes - kHeaderSize; for (UInt32 pos = 0; pos < numTests; pos++) { for (; buffer[pos] != '7' && pos < numTests; pos++); if (pos == numTests) break; if (TestSignature(buffer + pos)) { memcpy(_header, buffer + pos, kHeaderSize); curTestPos += pos; _arhiveBeginStreamPosition = curTestPos; return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL); } } curTestPos += numTests; numPrevBytes -= numTests; memmove(buffer, buffer + numTests, numPrevBytes); } return S_FALSE; }
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 CDecoder::Init(ISequentialInStream *inStream, bool &useFilter) { useFilter = false; if (_decoderInStream) if (Method != _curMethod) Release(); _curMethod = Method; if (!_codecInStream) { switch (Method) { // case NMethodType::kCopy: return E_NOTIMPL; case NMethodType::kDeflate: _codecInStream = new NCompress::NDeflate::NDecoder::CNsisCOMCoder(); break; case NMethodType::kBZip2: _codecInStream = new NCompress::NBZip2::CNsisDecoder(); break; case NMethodType::kLZMA: _lzmaDecoder = new NCompress::NLzma::CDecoder(); _codecInStream = _lzmaDecoder; break; default: return E_NOTIMPL; } } if (FilterFlag) { Byte flag; RINOK(ReadStream_FALSE(inStream, &flag, 1)); if (flag > 1) return E_NOTIMPL; useFilter = (flag != 0); } if (!useFilter) _decoderInStream = _codecInStream; else { if (!_filterInStream) { _filter = new CFilterCoder(false); _filterInStream = _filter; _filter->Filter = new NCompress::NBcj::CCoder(false); } RINOK(_filter->SetInStream(_codecInStream)); _decoderInStream = _filterInStream; } if (Method == NMethodType::kLZMA) { const unsigned kPropsSize = LZMA_PROPS_SIZE; Byte props[kPropsSize]; RINOK(ReadStream_FALSE(inStream, props, kPropsSize)); RINOK(_lzmaDecoder->SetDecoderProperties2((const Byte *)props, kPropsSize)); } { CMyComPtr<ICompressSetInStream> setInStream; _codecInStream.QueryInterface(IID_ICompressSetInStream, &setInStream); if (!setInStream) return E_NOTIMPL; RINOK(setInStream->SetInStream(inStream)); } { CMyComPtr<ICompressSetOutStreamSize> setOutStreamSize; _codecInStream.QueryInterface(IID_ICompressSetOutStreamSize, &setOutStreamSize); if (!setOutStreamSize) return E_NOTIMPL; RINOK(setOutStreamSize->SetOutStreamSize(NULL)); } if (useFilter) { RINOK(_filter->SetOutStreamSize(NULL)); } return S_OK; }
HRESULT CDecoder::Decode(CByteBuffer *outBuf, bool unpackSizeDefined, UInt32 unpackSize, ISequentialOutStream *realOutStream, ICompressProgressInfo *progress, UInt32 &packSizeRes, UInt32 &unpackSizeRes) { CLimitedSequentialInStream *limitedStreamSpec = NULL; CMyComPtr<ISequentialInStream> limitedStream; packSizeRes = 0; unpackSizeRes = 0; if (Solid) { Byte temp[4]; size_t processedSize = 4; RINOK(Read(temp, &processedSize)); if (processedSize != 4) return S_FALSE; StreamPos += processedSize; UInt32 size = Get32(temp); if (unpackSizeDefined && size != unpackSize) return S_FALSE; unpackSize = size; unpackSizeDefined = true; } else { Byte temp[4]; RINOK(ReadStream_FALSE(InputStream, temp, 4)); StreamPos += 4; UInt32 size = Get32(temp); if ((size & kMask_IsCompressed) == 0) { if (unpackSizeDefined && size != unpackSize) return S_FALSE; packSizeRes = size; if (outBuf) outBuf->Alloc(size); UInt64 offset = 0; while (size > 0) { UInt32 curSize = (UInt32)MyMin((size_t)size, Buffer.Size()); UInt32 processedSize; RINOK(InputStream->Read(Buffer, curSize, &processedSize)); if (processedSize == 0) return S_FALSE; if (outBuf) memcpy((Byte *)*outBuf + (size_t)offset, Buffer, processedSize); offset += processedSize; size -= processedSize; StreamPos += processedSize; unpackSizeRes += processedSize; if (realOutStream) RINOK(WriteStream(realOutStream, Buffer, processedSize)); RINOK(progress->SetRatioInfo(&offset, &offset)); } return S_OK; } size &= ~kMask_IsCompressed; packSizeRes = size; limitedStreamSpec = new CLimitedSequentialInStream; limitedStream = limitedStreamSpec; limitedStreamSpec->SetStream(InputStream); limitedStreamSpec->Init(size); { bool useFilter; RINOK(Init(limitedStream, useFilter)); } } if (outBuf) { if (!unpackSizeDefined) return S_FALSE; outBuf->Alloc(unpackSize); } UInt64 inSizeStart = 0; if (_lzmaDecoder) inSizeStart = _lzmaDecoder->GetInputProcessedSize(); // we don't allow files larger than 4 GB; if (!unpackSizeDefined) unpackSize = 0xFFFFFFFF; UInt32 offset = 0; for (;;) { size_t rem = unpackSize - offset; if (rem == 0) break; size_t size = Buffer.Size(); if (size > rem) size = rem; RINOK(Read(Buffer, &size)); if (size == 0) { if (unpackSizeDefined) return S_FALSE; break; } if (outBuf) memcpy((Byte *)*outBuf + (size_t)offset, Buffer, size); StreamPos += size; offset += (UInt32)size; UInt64 inSize = 0; // it can be improved: we need inSize for Deflate and BZip2 too. if (_lzmaDecoder) inSize = _lzmaDecoder->GetInputProcessedSize() - inSizeStart; if (Solid) packSizeRes = (UInt32)inSize; unpackSizeRes += (UInt32)size; UInt64 outSize = offset; RINOK(progress->SetRatioInfo(&inSize, &outSize)); if (realOutStream) RINOK(WriteStream(realOutStream, Buffer, size)); } return S_OK; }
HRESULT CInArchive::ReadDatabase2( DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx &db #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined #endif ) { db.Clear(); db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition; db.ArchiveInfo.Version.Major = _header[6]; db.ArchiveInfo.Version.Minor = _header[7]; if (db.ArchiveInfo.Version.Major != kMajorVersion) ThrowUnsupportedVersion(); UInt32 crcFromArchive = Get32(_header + 8); UInt64 nextHeaderOffset = Get64(_header + 0xC); UInt64 nextHeaderSize = Get64(_header + 0x14); UInt32 nextHeaderCRC = Get32(_header + 0x1C); UInt32 crc = CrcCalc(_header + 0xC, 20); #ifdef FORMAT_7Z_RECOVERY if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0) { UInt64 cur, cur2; RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur)); const int kCheckSize = 500; Byte buf[kCheckSize]; RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2)); int checkSize = kCheckSize; if (cur2 - cur < kCheckSize) checkSize = (int)(cur2 - cur); RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2)); RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize)); int i; for (i = (int)checkSize - 2; i >= 0; i--) if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04) break; if (i < 0) return S_FALSE; nextHeaderSize = checkSize - i; nextHeaderOffset = cur2 - cur + i; nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL)); } else #endif { if (crc != crcFromArchive) ThrowIncorrect(); } db.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize; if (nextHeaderSize == 0) return S_OK; if (nextHeaderSize > (UInt64)0xFFFFFFFF) return S_FALSE; if ((Int64)nextHeaderOffset < 0) return S_FALSE; RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL)); CByteBuffer buffer2; buffer2.SetCapacity((size_t)nextHeaderSize); RINOK(ReadStream_FALSE(_stream, buffer2, (size_t)nextHeaderSize)); HeadersSize += kHeaderSize + nextHeaderSize; db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize; if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC) ThrowIncorrect(); CStreamSwitch streamSwitch; streamSwitch.Set(this, buffer2); CObjectVector<CByteBuffer> dataVector; UInt64 type = ReadID(); if (type != NID::kHeader) { if (type != NID::kEncodedHeader) ThrowIncorrect(); HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS db.ArchiveInfo.StartPositionAfterHeader, db.ArchiveInfo.DataStartPosition2, dataVector #ifndef _NO_CRYPTO , getTextPassword, passwordIsDefined #endif ); RINOK(result); if (dataVector.Size() == 0) return S_OK; if (dataVector.Size() > 1) ThrowIncorrect(); streamSwitch.Remove(); streamSwitch.Set(this, dataVector.Front()); if (ReadID() != NID::kHeader) ThrowIncorrect(); } db.HeadersSize = HeadersSize; return ReadHeader( EXTERNAL_CODECS_LOC_VARS db #ifndef _NO_CRYPTO , getTextPassword, passwordIsDefined #endif ); }
HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { RINOK(inStream->Seek(resource.Offset, STREAM_SEEK_SET, NULL)); CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream(); CMyComPtr<ISequentialInStream> limitedStream = limitedStreamSpec; limitedStreamSpec->SetStream(inStream); if (!copyCoder) { copyCoderSpec = new NCompress::CCopyCoder; copyCoder = copyCoderSpec; } if (!resource.IsCompressed()) { if (resource.PackSize != resource.UnpackSize) return S_FALSE; limitedStreamSpec->Init(resource.PackSize); return copyCoder->Code(limitedStreamSpec, outStream, NULL, NULL, progress); } if (resource.UnpackSize == 0) return S_OK; UInt64 numChunks = (resource.UnpackSize + kChunkSize - 1) >> kChunkSizeBits; unsigned entrySize = ((resource.UnpackSize > (UInt64)1 << 32) ? 8 : 4); UInt64 sizesBufSize64 = entrySize * (numChunks - 1); size_t sizesBufSize = (size_t)sizesBufSize64; if (sizesBufSize != sizesBufSize64) return E_OUTOFMEMORY; if (sizesBufSize > sizesBuf.GetCapacity()) { sizesBuf.Free(); sizesBuf.SetCapacity(sizesBufSize); } RINOK(ReadStream_FALSE(inStream, (Byte *)sizesBuf, sizesBufSize)); const Byte *p = (const Byte *)sizesBuf; if (lzxMode && !lzxDecoder) { lzxDecoderSpec = new NCompress::NLzx::CDecoder(true); lzxDecoder = lzxDecoderSpec; RINOK(lzxDecoderSpec->SetParams(kChunkSizeBits)); } UInt64 baseOffset = resource.Offset + sizesBufSize64; UInt64 outProcessed = 0; for (UInt32 i = 0; i < (UInt32)numChunks; i++) { UInt64 offset = 0; if (i > 0) { offset = (entrySize == 4) ? Get32(p): Get64(p); p += entrySize; } UInt64 nextOffset = resource.PackSize - sizesBufSize64; if (i + 1 < (UInt32)numChunks) nextOffset = (entrySize == 4) ? Get32(p): Get64(p); if (nextOffset < offset) return S_FALSE; RINOK(inStream->Seek(baseOffset + offset, STREAM_SEEK_SET, NULL)); UInt64 inSize = nextOffset - offset; limitedStreamSpec->Init(inSize); if (progress) { RINOK(progress->SetRatioInfo(&offset, &outProcessed)); } UInt32 outSize = kChunkSize; if (outProcessed + outSize > resource.UnpackSize) outSize = (UInt32)(resource.UnpackSize - outProcessed); UInt64 outSize64 = outSize; if (inSize == outSize) { RINOK(copyCoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL)); } else { if (lzxMode) { lzxDecoderSpec->SetKeepHistory(false); RINOK(lzxDecoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL)); } else { RINOK(xpressDecoder.Code(limitedStreamSpec, outStream, outSize)); } } outProcessed += outSize; } 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; } }