ZipIn.cpp (p7zip_15.14.1_src_all) | : | ZipIn.cpp (p7zip_16.02_src_all) | ||
---|---|---|---|---|
// Archive/ZipIn.cpp | // Archive/ZipIn.cpp | |||
#include "StdAfx.h" | #include "StdAfx.h" | |||
// #include <stdio.h> | // #include <stdio.h> | |||
#include "../../../Common/DynamicBuffer.h" | #include "../../../Common/DynamicBuffer.h" | |||
#include "../../../Common/IntToString.h" | ||||
#include "../../../Common/StringToInt.h" | ||||
#include "../../../Windows/PropVariant.h" | ||||
#include "../../Common/LimitedStreams.h" | ||||
#include "../../Common/StreamUtils.h" | #include "../../Common/StreamUtils.h" | |||
#include "../IArchive.h" | #include "../IArchive.h" | |||
#include "ZipIn.h" | #include "ZipIn.h" | |||
#define Get16(p) GetUi16(p) | #define Get16(p) GetUi16(p) | |||
#define Get32(p) GetUi32(p) | #define Get32(p) GetUi32(p) | |||
#define Get64(p) GetUi64(p) | #define Get64(p) GetUi64(p) | |||
#define G16(offs, v) v = Get16(p + (offs)) | ||||
#define G32(offs, v) v = Get32(p + (offs)) | ||||
#define G64(offs, v) v = Get64(p + (offs)) | ||||
namespace NArchive { | namespace NArchive { | |||
namespace NZip { | namespace NZip { | |||
struct CEcd | struct CEcd | |||
{ | { | |||
UInt16 thisDiskNumber; | UInt16 ThisDisk; | |||
UInt16 startCDDiskNumber; | UInt16 CdDisk; | |||
UInt16 numEntriesInCDOnThisDisk; | UInt16 NumEntries_in_ThisDisk; | |||
UInt16 numEntriesInCD; | UInt16 NumEntries; | |||
UInt32 cdSize; | UInt32 Size; | |||
UInt32 cdStartOffset; | UInt32 Offset; | |||
UInt16 commentSize; | UInt16 CommentSize; | |||
void Parse(const Byte *p); | bool IsEmptyArc() const | |||
{ | ||||
bool IsEmptyArc() | return ThisDisk == 0 | |||
{ | && CdDisk == 0 | |||
return thisDiskNumber == 0 && startCDDiskNumber == 0 && | && NumEntries_in_ThisDisk == 0 | |||
numEntriesInCDOnThisDisk == 0 && numEntriesInCD == 0 && cdSize == 0 | && NumEntries == 0 | |||
&& cdStartOffset == 0 // test it | && Size == 0 | |||
&& Offset == 0 // test it | ||||
; | ; | |||
} | } | |||
void Parse(const Byte *p); // (p) doesn't include signature | ||||
}; | }; | |||
void CEcd::Parse(const Byte *p) | void CEcd::Parse(const Byte *p) | |||
{ | { | |||
thisDiskNumber = Get16(p); | // (p) doesn't include signature | |||
startCDDiskNumber = Get16(p + 2); | G16(0, ThisDisk); | |||
numEntriesInCDOnThisDisk = Get16(p + 4); | G16(2, CdDisk); | |||
numEntriesInCD = Get16(p + 6); | G16(4, NumEntries_in_ThisDisk); | |||
cdSize = Get32(p + 8); | G16(6, NumEntries); | |||
cdStartOffset = Get32(p + 12); | G32(8, Size); | |||
commentSize = Get16(p + 16); | G32(12, Offset); | |||
} | G16(16, CommentSize); | |||
} | ||||
struct CEcd64 | ||||
{ | ||||
UInt16 versionMade; | ||||
UInt16 versionNeedExtract; | ||||
UInt32 thisDiskNumber; | ||||
UInt32 startCDDiskNumber; | ||||
UInt64 numEntriesInCDOnThisDisk; | ||||
UInt64 numEntriesInCD; | ||||
UInt64 cdSize; | ||||
UInt64 cdStartOffset; | ||||
void Parse(const Byte *p); | void CCdInfo::ParseEcd32(const Byte *p) | |||
CEcd64() { memset(this, 0, sizeof(*this)); } | { | |||
}; | // (p) includes signature | |||
p += 4; | ||||
G16(0, ThisDisk); | ||||
G16(2, CdDisk); | ||||
G16(4, NumEntries_in_ThisDisk); | ||||
G16(6, NumEntries); | ||||
G32(8, Size); | ||||
G32(12, Offset); | ||||
G16(16, CommentSize); | ||||
} | ||||
void CEcd64::Parse(const Byte *p) | void CCdInfo::ParseEcd64e(const Byte *p) | |||
{ | { | |||
versionMade = Get16(p); | // (p) exclude signature | |||
versionNeedExtract = Get16(p + 2); | G16(0, VersionMade); | |||
thisDiskNumber = Get32(p + 4); | G16(2, VersionNeedExtract); | |||
startCDDiskNumber = Get32(p + 8); | G32(4, ThisDisk); | |||
numEntriesInCDOnThisDisk = Get64(p + 12); | G32(8, CdDisk); | |||
numEntriesInCD = Get64(p + 20); | ||||
cdSize = Get64(p + 28); | G64(12, NumEntries_in_ThisDisk); | |||
cdStartOffset = Get64(p + 36); | G64(20, NumEntries); | |||
G64(28, Size); | ||||
G64(36, Offset); | ||||
} | } | |||
HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) | struct CLocator | |||
{ | { | |||
_inBufMode = false; | UInt32 Ecd64Disk; | |||
Close(); | UInt32 NumDisks; | |||
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_Position)); | UInt64 Ecd64Offset; | |||
RINOK(stream->Seek(0, STREAM_SEEK_END, &ArcInfo.FileEndPos)); | ||||
RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); | ||||
// printf("\nOpen offset = %d", (int)m_Position); | CLocator(): Ecd64Disk(0), NumDisks(0), Ecd64Offset(0) {} | |||
RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit)); | ||||
RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); | void Parse(const Byte *p) | |||
Stream = stream; | { | |||
return S_OK; | G32(0, Ecd64Disk); | |||
G64(4, Ecd64Offset); | ||||
G32(12, NumDisks); | ||||
} | ||||
}; | ||||
void CInArchive::ClearRefs() | ||||
{ | ||||
StreamRef.Release(); | ||||
Stream = NULL; | ||||
StartStream = NULL; | ||||
Callback = NULL; | ||||
Vols.Clear(); | ||||
} | } | |||
void CInArchive::Close() | void CInArchive::Close() | |||
{ | { | |||
_processedCnt = 0; | ||||
IsArc = false; | IsArc = false; | |||
IsArcOpen = false; | ||||
IsMultiVol = false; | ||||
UseDisk_in_SingleVol = false; | ||||
EcdVolIndex = 0; | ||||
HeadersError = false; | HeadersError = false; | |||
HeadersWarning = false; | HeadersWarning = false; | |||
ExtraMinorError = false; | ExtraMinorError = false; | |||
UnexpectedEnd = false; | UnexpectedEnd = false; | |||
NoCentralDir = false; | NoCentralDir = false; | |||
IsZip64 = false; | IsZip64 = false; | |||
Stream.Release(); | MarkerIsFound = false; | |||
ClearRefs(); | ||||
} | } | |||
HRESULT CInArchive::Seek(UInt64 offset) | HRESULT CInArchive::Seek(UInt64 offset) | |||
{ | { | |||
return Stream->Seek(offset, STREAM_SEEK_SET, NULL); | return Stream->Seek(offset, STREAM_SEEK_SET, NULL); | |||
} | } | |||
static bool CheckDosTime(UInt32 dosTime) | static bool CheckDosTime(UInt32 dosTime) | |||
{ | { | |||
if (dosTime == 0) | if (dosTime == 0) | |||
skipping to change at line 137 | skipping to change at line 166 | |||
API_FUNC_IsArc IsArc_Zip(const Byte *p, size_t size) | API_FUNC_IsArc IsArc_Zip(const Byte *p, size_t size) | |||
{ | { | |||
if (size < 8) | if (size < 8) | |||
return k_IsArc_Res_NEED_MORE; | return k_IsArc_Res_NEED_MORE; | |||
if (p[0] != 'P') | if (p[0] != 'P') | |||
return k_IsArc_Res_NO; | return k_IsArc_Res_NO; | |||
UInt32 value = Get32(p); | UInt32 value = Get32(p); | |||
if (value == NSignature::kNoSpan) | if (value == NSignature::kNoSpan | |||
|| value == NSignature::kSpan) | ||||
{ | { | |||
p += 4; | p += 4; | |||
size -= 4; | size -= 4; | |||
} | } | |||
value = Get32(p); | value = Get32(p); | |||
if (value == NSignature::kEcd) | if (value == NSignature::kEcd) | |||
{ | { | |||
if (size < kEcdSize) | if (size < kEcdSize) | |||
skipping to change at line 253 | skipping to change at line 283 | |||
} | } | |||
static UInt32 IsArc_Zip_2(const Byte *p, size_t size, bool isFinal) | static UInt32 IsArc_Zip_2(const Byte *p, size_t size, bool isFinal) | |||
{ | { | |||
UInt32 res = IsArc_Zip(p, size); | UInt32 res = IsArc_Zip(p, size); | |||
if (res == k_IsArc_Res_NEED_MORE && isFinal) | if (res == k_IsArc_Res_NEED_MORE && isFinal) | |||
return k_IsArc_Res_NO; | return k_IsArc_Res_NO; | |||
return res; | return res; | |||
} | } | |||
HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim it) | HRESULT CInArchive::FindMarker(IInStream *stream, const UInt64 *searchLimit) | |||
{ | { | |||
ArcInfo.Clear(); | ||||
ArcInfo.MarkerPos = m_Position; | ArcInfo.MarkerPos = m_Position; | |||
ArcInfo.MarkerPos2 = m_Position; | ArcInfo.MarkerPos2 = m_Position; | |||
if (searchLimit && *searchLimit == 0) | if (searchLimit && *searchLimit == 0) | |||
{ | { | |||
const unsigned kStartBufSize = kMarkerSize; | Byte startBuf[kMarkerSize]; | |||
Byte startBuf[kStartBufSize]; | { | |||
size_t processed = kStartBufSize; | size_t processed = kMarkerSize; | |||
RINOK(ReadStream(stream, startBuf, &processed)); | RINOK(ReadStream(stream, startBuf, &processed)); | |||
m_Position += processed; | m_Position += processed; | |||
if (processed < kMarkerSize) | if (processed != kMarkerSize) | |||
return S_FALSE; | return S_FALSE; | |||
} | ||||
m_Signature = Get32(startBuf); | m_Signature = Get32(startBuf); | |||
if (m_Signature != NSignature::kEcd && | if (m_Signature != NSignature::kEcd && | |||
m_Signature != NSignature::kLocalFileHeader) | m_Signature != NSignature::kLocalFileHeader) | |||
{ | { | |||
if (m_Signature != NSignature::kNoSpan) | if (m_Signature != NSignature::kNoSpan) | |||
return S_FALSE; | { | |||
size_t processed = kStartBufSize; | if (m_Signature != NSignature::kSpan) | |||
return S_FALSE; | ||||
if (m_Position != 4) // we don't support multivol archives with sfx stub | ||||
return S_FALSE; | ||||
ArcInfo.IsSpanMode = true; | ||||
} | ||||
size_t processed = kMarkerSize; | ||||
RINOK(ReadStream(stream, startBuf, &processed)); | RINOK(ReadStream(stream, startBuf, &processed)); | |||
m_Position += processed; | m_Position += processed; | |||
if (processed < kMarkerSize) | if (processed != kMarkerSize) | |||
return S_FALSE; | return S_FALSE; | |||
m_Signature = Get32(startBuf); | m_Signature = Get32(startBuf); | |||
if (m_Signature != NSignature::kEcd && | if (m_Signature != NSignature::kEcd && | |||
m_Signature != NSignature::kLocalFileHeader) | m_Signature != NSignature::kLocalFileHeader) | |||
return S_FALSE; | return S_FALSE; | |||
ArcInfo.MarkerPos2 += 4; | ArcInfo.MarkerPos2 += 4; | |||
} | } | |||
// we use weak test in case of *searchLimit == 0) | // we use weak test in case of (*searchLimit == 0) | |||
// since error will be detected later in Open function | // since error will be detected later in Open function | |||
// m_Position = ArcInfo.MarkerPos2 + 4; | ||||
return S_OK; // maybe we need to search backward. | return S_OK; // maybe we need to search backward. | |||
} | } | |||
const size_t kBufSize = (size_t)1 << 18; // must be larger than kCheckSize | const size_t kBufSize = (size_t)1 << 18; // must be larger than kCheckSize | |||
const size_t kCheckSize = (size_t)1 << 16; // must be smaller than kBufSize | const size_t kCheckSize = (size_t)1 << 16; // must be smaller than kBufSize | |||
CByteArr buffer(kBufSize); | CByteArr buffer(kBufSize); | |||
size_t numBytesInBuffer = 0; | size_t numBytesInBuffer = 0; | |||
UInt64 curScanPos = 0; | UInt64 curScanPos = 0; | |||
for (;;) | for (;;) | |||
{ | { | |||
size_t numReadBytes = kBufSize - numBytesInBuffer; | size_t numReadBytes = kBufSize - numBytesInBuffer; | |||
RINOK(ReadStream(stream, buffer + numBytesInBuffer, &numReadBytes)); | RINOK(ReadStream(stream, buffer + numBytesInBuffer, &numReadBytes)); | |||
m_Position += numReadBytes; | m_Position += numReadBytes; | |||
numBytesInBuffer += numReadBytes; | numBytesInBuffer += numReadBytes; | |||
bool isFinished = (numBytesInBuffer != kBufSize); | const bool isFinished = (numBytesInBuffer != kBufSize); | |||
size_t limit = (isFinished ? numBytesInBuffer : numBytesInBuffer - kCheckSiz | size_t limit = numBytesInBuffer;; | |||
e); | if (isFinished) | |||
{ | ||||
if (limit == 0) | ||||
break; | ||||
limit--; | ||||
} | ||||
else | ||||
limit -= kCheckSize; | ||||
if (searchLimit && curScanPos + limit > *searchLimit) | if (searchLimit && curScanPos + limit > *searchLimit) | |||
limit = (size_t)(*searchLimit - curScanPos + 1); | limit = (size_t)(*searchLimit - curScanPos + 1); | |||
if (limit < 1) | if (limit < 1) | |||
break; | break; | |||
const Byte *buf = buffer; | const Byte *buf = buffer; | |||
for (size_t pos = 0; pos < limit; pos++) | for (size_t pos = 0; pos < limit; pos++) | |||
{ | { | |||
skipping to change at line 331 | skipping to change at line 376 | |||
continue; | continue; | |||
size_t rem = numBytesInBuffer - pos; | size_t rem = numBytesInBuffer - pos; | |||
UInt32 res = IsArc_Zip_2(buf + pos, rem, isFinished); | UInt32 res = IsArc_Zip_2(buf + pos, rem, isFinished); | |||
if (res != k_IsArc_Res_NO) | if (res != k_IsArc_Res_NO) | |||
{ | { | |||
if (rem < kMarkerSize) | if (rem < kMarkerSize) | |||
return S_FALSE; | return S_FALSE; | |||
m_Signature = Get32(buf + pos); | m_Signature = Get32(buf + pos); | |||
ArcInfo.MarkerPos += curScanPos + pos; | ArcInfo.MarkerPos += curScanPos + pos; | |||
ArcInfo.MarkerPos2 = ArcInfo.MarkerPos; | ArcInfo.MarkerPos2 = ArcInfo.MarkerPos; | |||
if (m_Signature == NSignature::kNoSpan) | if (m_Signature == NSignature::kNoSpan | |||
|| m_Signature == NSignature::kSpan) | ||||
{ | { | |||
m_Signature = Get32(buf + pos + 4); | m_Signature = Get32(buf + pos + 4); | |||
ArcInfo.MarkerPos2 += 4; | ArcInfo.MarkerPos2 += 4; | |||
} | } | |||
m_Position = ArcInfo.MarkerPos2 + kMarkerSize; | m_Position = ArcInfo.MarkerPos2 + kMarkerSize; | |||
return S_OK; | return S_OK; | |||
} | } | |||
} | } | |||
if (isFinished) | if (isFinished) | |||
break; | break; | |||
curScanPos += limit; | curScanPos += limit; | |||
numBytesInBuffer -= limit; | numBytesInBuffer -= limit; | |||
memmove(buffer, buffer + limit, numBytesInBuffer); | memmove(buffer, buffer + limit, numBytesInBuffer); | |||
} | } | |||
return S_FALSE; | return S_FALSE; | |||
} | } | |||
HRESULT CInArchive::IncreaseRealPosition(Int64 addValue) | HRESULT CInArchive::IncreaseRealPosition(Int64 addValue, bool &isFinished) | |||
{ | { | |||
return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); | isFinished = false; | |||
if (!IsMultiVol) | ||||
return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); | ||||
for (;;) | ||||
{ | ||||
if (addValue == 0) | ||||
return S_OK; | ||||
if (addValue > 0) | ||||
{ | ||||
if (Vols.StreamIndex < 0) | ||||
return S_FALSE; | ||||
if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size()) | ||||
{ | ||||
isFinished = true; | ||||
return S_OK; | ||||
} | ||||
{ | ||||
const CVols::CSubStreamInfo &s = Vols.Streams[Vols.StreamIndex]; | ||||
if (!s.Stream) | ||||
{ | ||||
isFinished = true; | ||||
return S_OK; | ||||
} | ||||
if (m_Position > s.Size) | ||||
return S_FALSE; | ||||
UInt64 rem = s.Size - m_Position; | ||||
if ((UInt64)addValue <= rem) | ||||
return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); | ||||
RINOK(Stream->Seek(s.Size, STREAM_SEEK_SET, &m_Position)); | ||||
addValue -= rem; | ||||
Stream = NULL; | ||||
Vols.StreamIndex++; | ||||
if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size()) | ||||
{ | ||||
isFinished = true; | ||||
return S_OK; | ||||
} | ||||
} | ||||
const CVols::CSubStreamInfo &s2 = Vols.Streams[Vols.StreamIndex]; | ||||
if (!s2.Stream) | ||||
{ | ||||
isFinished = true; | ||||
return S_OK; | ||||
} | ||||
Stream = s2.Stream; | ||||
m_Position = 0; | ||||
RINOK(Stream->Seek(0, STREAM_SEEK_SET, &m_Position)); | ||||
} | ||||
else | ||||
{ | ||||
if (!Stream) | ||||
return S_FALSE; | ||||
{ | ||||
if (m_Position >= (UInt64)(-addValue)) | ||||
return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); | ||||
addValue += m_Position; | ||||
RINOK(Stream->Seek(0, STREAM_SEEK_SET, &m_Position)); | ||||
m_Position = 0; | ||||
Stream = NULL; | ||||
if (--Vols.StreamIndex < 0) | ||||
return S_FALSE; | ||||
} | ||||
const CVols::CSubStreamInfo &s2 = Vols.Streams[Vols.StreamIndex]; | ||||
if (!s2.Stream) | ||||
return S_FALSE; | ||||
Stream = s2.Stream; | ||||
m_Position = s2.Size; | ||||
RINOK(Stream->Seek(s2.Size, STREAM_SEEK_SET, &m_Position)); | ||||
} | ||||
} | ||||
} | } | |||
class CUnexpectEnd {}; | class CUnexpectEnd {}; | |||
HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) | HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) | |||
{ | { | |||
size_t realProcessedSize = size; | size_t realProcessedSize = size; | |||
HRESULT result = S_OK; | HRESULT result = S_OK; | |||
if (_inBufMode) | if (_inBufMode) | |||
{ | { | |||
skipping to change at line 379 | skipping to change at line 495 | |||
result = ReadStream(Stream, data, &realProcessedSize); | result = ReadStream(Stream, data, &realProcessedSize); | |||
if (processedSize) | if (processedSize) | |||
*processedSize = (UInt32)realProcessedSize; | *processedSize = (UInt32)realProcessedSize; | |||
m_Position += realProcessedSize; | m_Position += realProcessedSize; | |||
return result; | return result; | |||
} | } | |||
void CInArchive::SafeReadBytes(void *data, unsigned size) | void CInArchive::SafeReadBytes(void *data, unsigned size) | |||
{ | { | |||
size_t processed = size; | size_t processed = size; | |||
if (_inBufMode) | ||||
{ | HRESULT result = S_OK; | |||
processed = _inBuffer.ReadBytes((Byte *)data, size); | ||||
m_Position += processed; | if (!_inBufMode) | |||
} | result = ReadStream(Stream, data, &processed); | |||
else | else | |||
{ | { | |||
HRESULT result = ReadStream(Stream, data, &processed); | for (;;) | |||
m_Position += processed; | { | |||
if (result != S_OK) | processed = _inBuffer.ReadBytes((Byte *)data, size); | |||
throw CSystemException(result); | if (processed != 0 | |||
|| IsMultiVol | ||||
|| !CanStartNewVol | ||||
|| Vols.StreamIndex < 0 | ||||
|| (unsigned)Vols.StreamIndex >= Vols.Streams.Size()) | ||||
break; | ||||
Vols.StreamIndex++; | ||||
const CVols::CSubStreamInfo &s = Vols.Streams[Vols.StreamIndex]; | ||||
if (!s.Stream) | ||||
break; | ||||
// if (Vols.NeedSeek) | ||||
{ | ||||
result = s.Stream->Seek(0, STREAM_SEEK_SET, NULL); | ||||
m_Position = 0; | ||||
if (result != S_OK) | ||||
break; | ||||
Vols.NeedSeek = false; | ||||
} | ||||
_inBuffer.SetStream(s.Stream); | ||||
_inBuffer.Init(); | ||||
} | ||||
CanStartNewVol = false; | ||||
} | } | |||
m_Position += processed; | ||||
_processedCnt += processed; | ||||
if (result != S_OK) | ||||
throw CSystemException(result); | ||||
if (processed != size) | if (processed != size) | |||
throw CUnexpectEnd(); | throw CUnexpectEnd(); | |||
} | } | |||
void CInArchive::ReadBuffer(CByteBuffer &buffer, unsigned size) | void CInArchive::ReadBuffer(CByteBuffer &buffer, unsigned size) | |||
{ | { | |||
buffer.Alloc(size); | buffer.Alloc(size); | |||
if (size > 0) | if (size > 0) | |||
SafeReadBytes(buffer, size); | SafeReadBytes(buffer, size); | |||
} | } | |||
skipping to change at line 413 | skipping to change at line 557 | |||
{ | { | |||
Byte b; | Byte b; | |||
SafeReadBytes(&b, 1); | SafeReadBytes(&b, 1); | |||
return b; | return b; | |||
} | } | |||
UInt16 CInArchive::ReadUInt16() { Byte buf[2]; SafeReadBytes(buf, 2); return Get 16(buf); } | UInt16 CInArchive::ReadUInt16() { Byte buf[2]; SafeReadBytes(buf, 2); return Get 16(buf); } | |||
UInt32 CInArchive::ReadUInt32() { Byte buf[4]; SafeReadBytes(buf, 4); return Get 32(buf); } | UInt32 CInArchive::ReadUInt32() { Byte buf[4]; SafeReadBytes(buf, 4); return Get 32(buf); } | |||
UInt64 CInArchive::ReadUInt64() { Byte buf[8]; SafeReadBytes(buf, 8); return Get 64(buf); } | UInt64 CInArchive::ReadUInt64() { Byte buf[8]; SafeReadBytes(buf, 8); return Get 64(buf); } | |||
// we use Skip() inside headers only, so no need for stream change in multivol. | ||||
void CInArchive::Skip(unsigned num) | void CInArchive::Skip(unsigned num) | |||
{ | { | |||
if (_inBufMode) | if (_inBufMode) | |||
{ | { | |||
size_t skip = _inBuffer.Skip(num); | size_t skip = _inBuffer.Skip(num); | |||
m_Position += skip; | m_Position += skip; | |||
_processedCnt += skip; | ||||
if (skip != num) | if (skip != num) | |||
throw CUnexpectEnd(); | throw CUnexpectEnd(); | |||
} | } | |||
else | else | |||
{ | { | |||
for (unsigned i = 0; i < num; i++) | for (unsigned i = 0; i < num; i++) | |||
ReadByte(); | ReadByte(); | |||
} | } | |||
} | } | |||
skipping to change at line 442 | skipping to change at line 589 | |||
ReadByte(); | ReadByte(); | |||
} | } | |||
void CInArchive::ReadFileName(unsigned size, AString &s) | void CInArchive::ReadFileName(unsigned size, AString &s) | |||
{ | { | |||
if (size == 0) | if (size == 0) | |||
{ | { | |||
s.Empty(); | s.Empty(); | |||
return; | return; | |||
} | } | |||
char *p = s.GetBuf(size); | SafeReadBytes(s.GetBuf(size), size); | |||
SafeReadBytes(p, size); | ||||
s.ReleaseBuf_CalcLen(size); | s.ReleaseBuf_CalcLen(size); | |||
} | } | |||
bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, | bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, | |||
UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &dis kStartNumber) | UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &dis kStartNumber) | |||
{ | { | |||
extraBlock.Clear(); | extraBlock.Clear(); | |||
UInt32 remain = extraSize; | UInt32 remain = extraSize; | |||
while (remain >= 4) | while (remain >= 4) | |||
{ | { | |||
CExtraSubBlock subBlock; | CExtraSubBlock subBlock; | |||
subBlock.ID = ReadUInt16(); | subBlock.ID = ReadUInt16(); | |||
unsigned dataSize = ReadUInt16(); | unsigned dataSize = ReadUInt16(); | |||
remain -= 4; | remain -= 4; | |||
if (dataSize > remain) // it's bug | if (dataSize > remain) // it's bug | |||
{ | { | |||
HeadersWarning = true; | HeadersWarning = true; | |||
Skip(remain); | Skip(remain); | |||
skipping to change at line 511 | skipping to change at line 659 | |||
} | } | |||
Skip(dataSize); | Skip(dataSize); | |||
} | } | |||
else | else | |||
{ | { | |||
ReadBuffer(subBlock.Data, dataSize); | ReadBuffer(subBlock.Data, dataSize); | |||
extraBlock.SubBlocks.Add(subBlock); | extraBlock.SubBlocks.Add(subBlock); | |||
} | } | |||
remain -= dataSize; | remain -= dataSize; | |||
} | } | |||
if (remain != 0) | if (remain != 0) | |||
{ | { | |||
ExtraMinorError = true; | ExtraMinorError = true; | |||
// 7-Zip before 9.31 created incorrect WsAES Extra in folder's local headers . | // 7-Zip before 9.31 created incorrect WsAES Extra in folder's local headers . | |||
// so we don't return false, but just set warning flag | // so we don't return false, but just set warning flag | |||
// return false; | // return false; | |||
} | } | |||
Skip(remain); | Skip(remain); | |||
return true; | return true; | |||
} | } | |||
bool CInArchive::ReadLocalItem(CItemEx &item) | bool CInArchive::ReadLocalItem(CItemEx &item) | |||
{ | { | |||
item.Disk = 0; | ||||
if (IsMultiVol && Vols.StreamIndex >= 0) | ||||
item.Disk = Vols.StreamIndex; | ||||
const unsigned kPureHeaderSize = kLocalHeaderSize - 4; | const unsigned kPureHeaderSize = kLocalHeaderSize - 4; | |||
Byte p[kPureHeaderSize]; | Byte p[kPureHeaderSize]; | |||
SafeReadBytes(p, kPureHeaderSize); | SafeReadBytes(p, kPureHeaderSize); | |||
{ | { | |||
unsigned i; | unsigned i; | |||
for (i = 0; i < kPureHeaderSize && p[i] == 0; i++); | for (i = 0; i < kPureHeaderSize && p[i] == 0; i++); | |||
if (i == kPureHeaderSize) | if (i == kPureHeaderSize) | |||
return false; | return false; | |||
} | } | |||
item.ExtractVersion.Version = p[0]; | item.ExtractVersion.Version = p[0]; | |||
item.ExtractVersion.HostOS = p[1]; | item.ExtractVersion.HostOS = p[1]; | |||
item.Flags = Get16(p + 2); | G16(2, item.Flags); | |||
item.Method = Get16(p + 4); | G16(4, item.Method); | |||
item.Time = Get32(p + 6); | G32(6, item.Time); | |||
item.Crc = Get32(p + 10); | G32(10, item.Crc); | |||
item.PackSize = Get32(p + 14); | G32(14, item.PackSize); | |||
item.Size = Get32(p + 18); | G32(18, item.Size); | |||
unsigned nameSize = Get16(p + 22); | const unsigned nameSize = Get16(p + 22); | |||
unsigned extraSize = Get16(p + 24); | const unsigned extraSize = Get16(p + 24); | |||
ReadFileName(nameSize, item.Name); | ReadFileName(nameSize, item.Name); | |||
item.LocalFullHeaderSize = kLocalHeaderSize + (UInt32)nameSize + extraSize; | item.LocalFullHeaderSize = kLocalHeaderSize + (UInt32)nameSize + extraSize; | |||
/* | /* | |||
if (item.IsDir()) | if (item.IsDir()) | |||
item.Size = 0; // check It | item.Size = 0; // check It | |||
*/ | */ | |||
if (extraSize > 0) | if (extraSize > 0) | |||
{ | { | |||
skipping to change at line 599 | skipping to change at line 752 | |||
UInt32 mask = 0xFFFF; | UInt32 mask = 0xFFFF; | |||
switch (i1.Method) | switch (i1.Method) | |||
{ | { | |||
case NFileHeader::NCompressionMethod::kDeflated: | case NFileHeader::NCompressionMethod::kDeflated: | |||
mask = 0x7FF9; | mask = 0x7FF9; | |||
break; | break; | |||
default: | default: | |||
if (i1.Method <= NFileHeader::NCompressionMethod::kImploded) | if (i1.Method <= NFileHeader::NCompressionMethod::kImploded) | |||
mask = 0x7FFF; | mask = 0x7FFF; | |||
} | } | |||
// we can ignore utf8 flag, if name is ascii | ||||
if ((i1.Flags ^ i2.Flags) & NFileHeader::NFlags::kUtf8) | ||||
if (i1.Name.IsAscii() && i2.Name.IsAscii()) | ||||
mask &= ~NFileHeader::NFlags::kUtf8; | ||||
return ((i1.Flags & mask) == (i2.Flags & mask)); | return ((i1.Flags & mask) == (i2.Flags & mask)); | |||
} | } | |||
// #ifdef _WIN32 | // #ifdef _WIN32 | |||
static bool AreEqualPaths_IgnoreSlashes(const char *s1, const char *s2) | static bool AreEqualPaths_IgnoreSlashes(const char *s1, const char *s2) | |||
{ | { | |||
for (;;) | for (;;) | |||
{ | { | |||
char c1 = *s1++; | char c1 = *s1++; | |||
char c2 = *s2++; | char c2 = *s2++; | |||
skipping to change at line 672 | skipping to change at line 831 | |||
} | } | |||
/* | /* | |||
else | else | |||
#endif | #endif | |||
return false; | return false; | |||
*/ | */ | |||
} | } | |||
return true; | return true; | |||
} | } | |||
HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item) | HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item, bool &isAvail) | |||
{ | { | |||
isAvail = true; | ||||
if (item.FromLocal) | if (item.FromLocal) | |||
return S_OK; | return S_OK; | |||
try | try | |||
{ | { | |||
UInt64 offset = ArcInfo.Base + item.LocalHeaderPos; | UInt64 offset = item.LocalHeaderPos; | |||
if (ArcInfo.Base < 0 && (Int64)offset < 0) | ||||
return S_FALSE; | if (IsMultiVol) | |||
RINOK(Seek(offset)); | { | |||
if (item.Disk >= Vols.Streams.Size()) | ||||
{ | ||||
isAvail = false; | ||||
return S_FALSE; | ||||
} | ||||
IInStream *str2 = Vols.Streams[item.Disk].Stream; | ||||
if (!str2) | ||||
{ | ||||
isAvail = false; | ||||
return S_FALSE; | ||||
} | ||||
RINOK(str2->Seek(offset, STREAM_SEEK_SET, NULL)); | ||||
Stream = str2; | ||||
Vols.StreamIndex = item.Disk; | ||||
} | ||||
else | ||||
{ | ||||
if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex) | ||||
{ | ||||
isAvail = false; | ||||
return S_FALSE; | ||||
} | ||||
Stream = StreamRef; | ||||
offset += ArcInfo.Base; | ||||
if (ArcInfo.Base < 0 && (Int64)offset < 0) | ||||
{ | ||||
isAvail = false; | ||||
return S_FALSE; | ||||
} | ||||
RINOK(Seek(offset)); | ||||
} | ||||
CItemEx localItem; | CItemEx localItem; | |||
if (ReadUInt32() != NSignature::kLocalFileHeader) | if (ReadUInt32() != NSignature::kLocalFileHeader) | |||
return S_FALSE; | return S_FALSE; | |||
ReadLocalItem(localItem); | ReadLocalItem(localItem); | |||
if (!AreItemsEqual(localItem, item)) | if (!AreItemsEqual(localItem, item)) | |||
return S_FALSE; | return S_FALSE; | |||
item.LocalFullHeaderSize = localItem.LocalFullHeaderSize; | item.LocalFullHeaderSize = localItem.LocalFullHeaderSize; | |||
item.LocalExtra = localItem.LocalExtra; | item.LocalExtra = localItem.LocalExtra; | |||
item.FromLocal = true; | item.FromLocal = true; | |||
} | } | |||
skipping to change at line 728 | skipping to change at line 921 | |||
continue; | continue; | |||
// !!!! It must be fixed for Zip64 archives | // !!!! It must be fixed for Zip64 archives | |||
if (Get32(buf + i) == NSignature::kDataDescriptor) | if (Get32(buf + i) == NSignature::kDataDescriptor) | |||
{ | { | |||
UInt32 descriptorPackSize = Get32(buf + i + 8); | UInt32 descriptorPackSize = Get32(buf + i + 8); | |||
if (descriptorPackSize == packedSize + i) | if (descriptorPackSize == packedSize + i) | |||
{ | { | |||
item.Crc = Get32(buf + i + 4); | item.Crc = Get32(buf + i + 4); | |||
item.PackSize = descriptorPackSize; | item.PackSize = descriptorPackSize; | |||
item.Size = Get32(buf + i + 12); | item.Size = Get32(buf + i + 12); | |||
return IncreaseRealPosition((Int64)(Int32)(0 - (numBytesInBuffer - i - | bool isFinished; | |||
kDataDescriptorSize))); | return IncreaseRealPosition((Int64)(Int32)(0 - (numBytesInBuffer - i - | |||
kDataDescriptorSize)), isFinished); | ||||
} | } | |||
} | } | |||
} | } | |||
packedSize += i; | packedSize += i; | |||
unsigned j; | unsigned j; | |||
for (j = 0; i < numBytesInBuffer; i++, j++) | for (j = 0; i < numBytesInBuffer; i++, j++) | |||
buf[j] = buf[i]; | buf[j] = buf[i]; | |||
numBytesInBuffer = j; | numBytesInBuffer = j; | |||
} | } | |||
} | } | |||
HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) | HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) | |||
{ | { | |||
if (item.FromLocal) | if (item.FromLocal) | |||
return S_OK; | return S_OK; | |||
try | try | |||
{ | { | |||
RINOK(ReadLocalItemAfterCdItem(item)); | bool isAvail = true; | |||
RINOK(ReadLocalItemAfterCdItem(item, isAvail)); | ||||
if (item.HasDescriptor()) | if (item.HasDescriptor()) | |||
{ | { | |||
// pkzip's version without descriptor is not supported | ||||
RINOK(Seek(ArcInfo.Base + item.GetDataPosition() + item.PackSize)); | RINOK(Seek(ArcInfo.Base + item.GetDataPosition() + item.PackSize)); | |||
if (ReadUInt32() != NSignature::kDataDescriptor) | if (ReadUInt32() != NSignature::kDataDescriptor) | |||
return S_FALSE; | return S_FALSE; | |||
UInt32 crc = ReadUInt32(); | UInt32 crc = ReadUInt32(); | |||
UInt64 packSize, unpackSize; | UInt64 packSize, unpackSize; | |||
/* | /* | |||
if (IsZip64) | if (IsZip64) | |||
{ | { | |||
packSize = ReadUInt64(); | packSize = ReadUInt64(); | |||
skipping to change at line 787 | skipping to change at line 983 | |||
HRESULT CInArchive::ReadCdItem(CItemEx &item) | HRESULT CInArchive::ReadCdItem(CItemEx &item) | |||
{ | { | |||
item.FromCentral = true; | item.FromCentral = true; | |||
Byte p[kCentralHeaderSize - 4]; | Byte p[kCentralHeaderSize - 4]; | |||
SafeReadBytes(p, kCentralHeaderSize - 4); | SafeReadBytes(p, kCentralHeaderSize - 4); | |||
item.MadeByVersion.Version = p[0]; | item.MadeByVersion.Version = p[0]; | |||
item.MadeByVersion.HostOS = p[1]; | item.MadeByVersion.HostOS = p[1]; | |||
item.ExtractVersion.Version = p[2]; | item.ExtractVersion.Version = p[2]; | |||
item.ExtractVersion.HostOS = p[3]; | item.ExtractVersion.HostOS = p[3]; | |||
item.Flags = Get16(p + 4); | G16(4, item.Flags); | |||
item.Method = Get16(p + 6); | G16(6, item.Method); | |||
item.Time = Get32(p + 8); | G32(8, item.Time); | |||
item.Crc = Get32(p + 12); | G32(12, item.Crc); | |||
item.PackSize = Get32(p + 16); | G32(16, item.PackSize); | |||
item.Size = Get32(p + 20); | G32(20, item.Size); | |||
const unsigned nameSize = Get16(p + 24); | const unsigned nameSize = Get16(p + 24); | |||
const unsigned extraSize = Get16(p + 26); | const unsigned extraSize = Get16(p + 26); | |||
const unsigned commentSize = Get16(p + 28); | const unsigned commentSize = Get16(p + 28); | |||
UInt32 diskNumberStart = Get16(p + 30); | G16(30, item.Disk); | |||
item.InternalAttrib = Get16(p + 32); | G16(32, item.InternalAttrib); | |||
item.ExternalAttrib = Get32(p + 34); | G32(34, item.ExternalAttrib); | |||
item.LocalHeaderPos = Get32(p + 38); | G32(38, item.LocalHeaderPos); | |||
ReadFileName(nameSize, item.Name); | ReadFileName(nameSize, item.Name); | |||
if (extraSize > 0) | if (extraSize > 0) | |||
{ | ReadExtra(extraSize, item.CentralExtra, item.Size, item.PackSize, item.Local | |||
ReadExtra(extraSize, item.CentralExtra, item.Size, item.PackSize, | HeaderPos, item.Disk); | |||
item.LocalHeaderPos, diskNumberStart); | ||||
} | ||||
if (diskNumberStart != 0) | ||||
return E_NOTIMPL; | ||||
// May be these strings must be deleted | // May be these strings must be deleted | |||
/* | /* | |||
if (item.IsDir()) | if (item.IsDir()) | |||
item.Size = 0; | item.Size = 0; | |||
*/ | */ | |||
ReadBuffer(item.Comment, commentSize); | ReadBuffer(item.Comment, commentSize); | |||
return S_OK; | return S_OK; | |||
} | } | |||
void CCdInfo::ParseEcd(const Byte *p) | ||||
{ | ||||
NumEntries = Get16(p + 10); | ||||
Size = Get32(p + 12); | ||||
Offset = Get32(p + 16); | ||||
} | ||||
void CCdInfo::ParseEcd64(const Byte *p) | ||||
{ | ||||
NumEntries = Get64(p + 24); | ||||
Size = Get64(p + 40); | ||||
Offset = Get64(p + 48); | ||||
} | ||||
HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) | HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) | |||
{ | { | |||
if (offset >= ((UInt64)1 << 63)) | if (offset >= ((UInt64)1 << 63)) | |||
return S_FALSE; | return S_FALSE; | |||
RINOK(Seek(offset)); | RINOK(Seek(offset)); | |||
Byte buf[kEcd64_FullSize]; | Byte buf[kEcd64_FullSize]; | |||
RINOK(ReadStream_FALSE(Stream, buf, kEcd64_FullSize)); | RINOK(ReadStream_FALSE(Stream, buf, kEcd64_FullSize)); | |||
if (Get32(buf) != NSignature::kEcd64) | if (Get32(buf) != NSignature::kEcd64) | |||
return S_FALSE; | return S_FALSE; | |||
UInt64 mainSize = Get64(buf + 4); | UInt64 mainSize = Get64(buf + 4); | |||
if (mainSize < kEcd64_MainSize || mainSize > ((UInt64)1 << 32)) | if (mainSize < kEcd64_MainSize || mainSize > ((UInt64)1 << 32)) | |||
return S_FALSE; | return S_FALSE; | |||
cdInfo.ParseEcd64(buf); | cdInfo.ParseEcd64e(buf + 12); | |||
return S_OK; | return S_OK; | |||
} | } | |||
HRESULT CInArchive::FindCd(CCdInfo &cdInfo) | HRESULT CInArchive::FindCd(bool checkOffsetMode) | |||
{ | { | |||
UInt64 endPosition; | CCdInfo &cdInfo = Vols.ecd; | |||
RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPosition)); | ||||
UInt64 endPos; | ||||
RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPos)); | ||||
const UInt32 kBufSizeMax = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize; | const UInt32 kBufSizeMax = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize; | |||
UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeM ax; | const UInt32 bufSize = (endPos < kBufSizeMax) ? (UInt32)endPos : kBufSizeMax; | |||
if (bufSize < kEcdSize) | if (bufSize < kEcdSize) | |||
return S_FALSE; | return S_FALSE; | |||
CByteArr byteBuffer(bufSize); | CByteArr byteBuffer(bufSize); | |||
UInt64 startPosition = endPosition - bufSize; | const UInt64 startPos = endPos - bufSize; | |||
RINOK(Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position)); | RINOK(Stream->Seek(startPos, STREAM_SEEK_SET, &m_Position)); | |||
if (m_Position != startPosition) | if (m_Position != startPos) | |||
return S_FALSE; | return S_FALSE; | |||
RINOK(ReadStream_FALSE(Stream, byteBuffer, bufSize)); | RINOK(ReadStream_FALSE(Stream, byteBuffer, bufSize)); | |||
const Byte *buf = byteBuffer; | for (UInt32 i = bufSize - kEcdSize + 1;;) | |||
for (UInt32 i = bufSize - kEcdSize;; i--) | ||||
{ | { | |||
if (buf[i] != 0x50) | if (i == 0) | |||
return S_FALSE; | ||||
const Byte *buf = byteBuffer; | ||||
for (;;) | ||||
{ | { | |||
if (i == 0) return S_FALSE; | ||||
i--; | i--; | |||
if (buf[i] != 0x50) | if (buf[i] == 0x50) | |||
{ | break; | |||
if (i == 0) return S_FALSE; | if (i == 0) | |||
continue; | return S_FALSE; | |||
} | ||||
} | } | |||
if (Get32(buf + i) == NSignature::kEcd) | ||||
if (Get32(buf + i) != NSignature::kEcd) | ||||
continue; | ||||
cdInfo.ParseEcd32(buf + i); | ||||
if (i >= kEcd64Locator_Size) | ||||
{ | { | |||
if (i >= kEcd64_FullSize + kEcd64Locator_Size) | const Byte *locatorPtr = buf + i - kEcd64Locator_Size; | |||
if (Get32(locatorPtr) == NSignature::kEcd64Locator) | ||||
{ | { | |||
const Byte *locator = buf + i - kEcd64Locator_Size; | CLocator locator; | |||
if (Get32(locator) == NSignature::kEcd64Locator && | locator.Parse(locatorPtr + 4); | |||
Get32(locator + 4) == 0) // number of the disk with the start of the | if ((cdInfo.ThisDisk == locator.NumDisks - 1 || cdInfo.ThisDisk == 0xFFF | |||
zip64 ECD | F) | |||
&& locator.Ecd64Disk < locator.NumDisks) | ||||
{ | { | |||
if (locator.Ecd64Disk != cdInfo.ThisDisk && cdInfo.ThisDisk != 0xFFFF) | ||||
return E_NOTIMPL; | ||||
// Most of the zip64 use fixed size Zip64 ECD | // Most of the zip64 use fixed size Zip64 ECD | |||
// we try relative backward reading. | ||||
UInt64 ecd64Offset = Get64(locator + 8); | UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_ | |||
UInt64 absEcd64 = endPosition - bufSize + i - (kEcd64Locator_Size + kE | FullSize); | |||
cd64_FullSize); | if (checkOffsetMode || absEcd64 == locator.Ecd64Offset) | |||
{ | { | |||
const Byte *ecd64 = locator - kEcd64_FullSize; | const Byte *ecd64 = locatorPtr - kEcd64_FullSize; | |||
if (Get32(ecd64) == NSignature::kEcd64 && | if (Get32(ecd64) == NSignature::kEcd64) | |||
Get64(ecd64 + 4) == kEcd64_MainSize) | ||||
{ | { | |||
cdInfo.ParseEcd64(ecd64); | UInt64 mainEcd64Size = Get64(ecd64 + 4); | |||
ArcInfo.Base = absEcd64 - ecd64Offset; | if (mainEcd64Size == kEcd64_MainSize) | |||
return S_OK; | { | |||
cdInfo.ParseEcd64e(ecd64 + 12); | ||||
ArcInfo.Base = absEcd64 - locator.Ecd64Offset; | ||||
// ArcInfo.BaseVolIndex = cdInfo.ThisDisk; | ||||
return S_OK; | ||||
} | ||||
} | } | |||
} | } | |||
// some zip64 use variable size Zip64 ECD. | // some zip64 use variable size Zip64 ECD. | |||
// we try to find it | // we try to use absolute offset from locator. | |||
if (absEcd64 != ecd64Offset) | ||||
if (absEcd64 != locator.Ecd64Offset) | ||||
{ | { | |||
if (TryEcd64(ecd64Offset, cdInfo) == S_OK) | if (TryEcd64(locator.Ecd64Offset, cdInfo) == S_OK) | |||
{ | { | |||
ArcInfo.Base = 0; | ArcInfo.Base = 0; | |||
// ArcInfo.BaseVolIndex = cdInfo.ThisDisk; | ||||
return S_OK; | return S_OK; | |||
} | } | |||
} | } | |||
if (ArcInfo.MarkerPos != 0 && | ||||
ArcInfo.MarkerPos + ecd64Offset != absEcd64) | // for variable Zip64 ECD with for archives with offset != 0. | |||
if (checkOffsetMode | ||||
&& ArcInfo.MarkerPos != 0 | ||||
&& ArcInfo.MarkerPos + locator.Ecd64Offset != absEcd64) | ||||
{ | { | |||
if (TryEcd64(ArcInfo.MarkerPos + ecd64Offset, cdInfo) == S_OK) | if (TryEcd64(ArcInfo.MarkerPos + locator.Ecd64Offset, cdInfo) == S_O K) | |||
{ | { | |||
ArcInfo.Base = ArcInfo.MarkerPos; | ArcInfo.Base = ArcInfo.MarkerPos; | |||
// ArcInfo.BaseVolIndex = cdInfo.ThisDisk; | ||||
return S_OK; | return S_OK; | |||
} | } | |||
} | } | |||
} | } | |||
} | } | |||
if (Get32(buf + i + 4) == 0) // ThisDiskNumber, StartCentralDirectoryDiskN | } | |||
umber; | ||||
// bool isVolMode = (Vols.EndVolIndex != -1); | ||||
// UInt32 searchDisk = (isVolMode ? Vols.EndVolIndex : 0); | ||||
if (/* searchDisk == thisDisk && */ cdInfo.CdDisk <= cdInfo.ThisDisk) | ||||
{ | ||||
// if (isVolMode) | ||||
{ | ||||
if (cdInfo.CdDisk != cdInfo.ThisDisk) | ||||
return S_OK; | ||||
} | ||||
UInt64 absEcdPos = endPos - bufSize + i; | ||||
UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; | ||||
ArcInfo.Base = 0; | ||||
// ArcInfo.BaseVolIndex = cdInfo.ThisDisk; | ||||
if (absEcdPos != cdEnd) | ||||
{ | { | |||
cdInfo.ParseEcd(buf + i); | /* | |||
UInt64 absEcdPos = endPosition - bufSize + i; | if (cdInfo.Offset <= 16 && cdInfo.Size != 0) | |||
UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; | ||||
ArcInfo.Base = 0; | ||||
if (absEcdPos != cdEnd) | ||||
{ | { | |||
/* | // here we support some rare ZIP files with Central directory at the s | |||
if (cdInfo.Offset <= 16 && cdInfo.Size != 0) | tart | |||
{ | ArcInfo.Base = 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; | else | |||
*/ | ||||
ArcInfo.Base = absEcdPos - cdEnd; | ||||
} | } | |||
return S_OK; | ||||
} | } | |||
if (i == 0) | ||||
return S_FALSE; | ||||
} | } | |||
} | } | |||
HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UI nt64 cdSize, CProgressVirt *progress) | HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, const CCdInfo &cdIn fo, UInt64 cdOffset, UInt64 cdSize) | |||
{ | { | |||
items.Clear(); | items.Clear(); | |||
RINOK(Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); | ||||
if (m_Position != cdOffset) | ISequentialInStream *stream; | |||
return S_FALSE; | ||||
if (!IsMultiVol) | ||||
{ | ||||
stream = this->StartStream; | ||||
Vols.StreamIndex = -1; | ||||
RINOK(this->StartStream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); | ||||
if (m_Position != cdOffset) | ||||
return S_FALSE; | ||||
} | ||||
else | ||||
{ | ||||
if (cdInfo.CdDisk >= Vols.Streams.Size()) | ||||
return S_FALSE; | ||||
IInStream *str2 = Vols.Streams[cdInfo.CdDisk].Stream; | ||||
if (!str2) | ||||
return S_FALSE; | ||||
RINOK(str2->Seek(cdOffset, STREAM_SEEK_SET, NULL)); | ||||
stream = str2; | ||||
Vols.NeedSeek = false; | ||||
Vols.StreamIndex = cdInfo.CdDisk; | ||||
m_Position = cdOffset; | ||||
} | ||||
_inBuffer.SetStream(stream); | ||||
_inBuffer.Init(); | _inBuffer.Init(); | |||
_inBufMode = true; | _inBufMode = true; | |||
while (m_Position - cdOffset < cdSize) | _processedCnt = 0; | |||
while (_processedCnt < cdSize) | ||||
{ | { | |||
CanStartNewVol = true; | ||||
if (ReadUInt32() != NSignature::kCentralFileHeader) | if (ReadUInt32() != NSignature::kCentralFileHeader) | |||
return S_FALSE; | return S_FALSE; | |||
CItemEx cdItem; | { | |||
RINOK(ReadCdItem(cdItem)); | CItemEx cdItem; | |||
items.Add(cdItem); | RINOK(ReadCdItem(cdItem)); | |||
if (progress && (items.Size() & 0xFFF) == 0) | items.Add(cdItem); | |||
RINOK(progress->SetCompletedCD(items.Size())); | } | |||
if (Callback && (items.Size() & 0xFFF) == 0) | ||||
{ | ||||
const UInt64 numFiles = items.Size(); | ||||
RINOK(Callback->SetCompleted(&numFiles, NULL)); | ||||
} | ||||
} | } | |||
return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE; | ||||
CanStartNewVol = true; | ||||
return (_processedCnt == cdSize) ? S_OK : S_FALSE; | ||||
} | } | |||
HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt 64 &cdSize, CProgressVirt *progress) | HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt32 &cdDisk, UInt64 &cdOffset, UInt64 &cdSize) | |||
{ | { | |||
CCdInfo cdInfo; | bool checkOffsetMode = true; | |||
RINOK(FindCd(cdInfo)); | ||||
if (IsMultiVol) | ||||
{ | ||||
if (Vols.EndVolIndex == -1) | ||||
return S_FALSE; | ||||
Stream = Vols.Streams[Vols.EndVolIndex].Stream; | ||||
if (!Vols.StartIsZip) | ||||
checkOffsetMode = false; | ||||
} | ||||
else | ||||
Stream = StartStream; | ||||
if (!Vols.ecd_wasRead) | ||||
{ | ||||
RINOK(FindCd(checkOffsetMode)); | ||||
} | ||||
CCdInfo &cdInfo = Vols.ecd; | ||||
HRESULT res = S_FALSE; | HRESULT res = S_FALSE; | |||
cdSize = cdInfo.Size; | cdSize = cdInfo.Size; | |||
cdOffset = cdInfo.Offset; | cdOffset = cdInfo.Offset; | |||
if (progress) | cdDisk = cdInfo.CdDisk; | |||
progress->SetTotalCD(cdInfo.NumEntries); | ||||
res = TryReadCd(items, ArcInfo.Base + cdOffset, cdSize, progress); | if (Callback) | |||
if (res == S_FALSE && ArcInfo.Base == 0) | ||||
{ | { | |||
res = TryReadCd(items, ArcInfo.MarkerPos + cdOffset, cdSize, progress); | RINOK(Callback->SetTotal(&cdInfo.NumEntries, NULL)); | |||
} | ||||
const UInt64 base = (IsMultiVol ? 0 : ArcInfo.Base); | ||||
res = TryReadCd(items, cdInfo, base + cdOffset, cdSize); | ||||
if (res == S_FALSE && !IsMultiVol && base != ArcInfo.MarkerPos) | ||||
{ | ||||
// do we need that additional attempt to read cd? | ||||
res = TryReadCd(items, cdInfo, ArcInfo.MarkerPos + cdOffset, cdSize); | ||||
if (res == S_OK) | if (res == S_OK) | |||
ArcInfo.Base = ArcInfo.MarkerPos; | ArcInfo.Base = ArcInfo.MarkerPos; | |||
} | } | |||
return res; | return res; | |||
} | } | |||
static HRESULT FindItem(const CObjectVector<CItemEx> &items, UInt64 offset) | static int FindItem(const CObjectVector<CItemEx> &items, const CItemEx &item) | |||
{ | { | |||
unsigned left = 0, right = items.Size(); | unsigned left = 0, right = items.Size(); | |||
for (;;) | for (;;) | |||
{ | { | |||
if (left >= right) | if (left >= right) | |||
return -1; | return -1; | |||
unsigned index = (left + right) / 2; | unsigned index = (left + right) / 2; | |||
UInt64 position = items[index].LocalHeaderPos; | const CItemEx &item2 = items[index]; | |||
if (offset == position) | if (item.Disk < item2.Disk) | |||
right = index; | ||||
else if (item.Disk > item2.Disk) | ||||
left = index + 1; | ||||
else if (item.LocalHeaderPos == item2.LocalHeaderPos) | ||||
return index; | return index; | |||
if (offset < position) | else if (item.LocalHeaderPos < item2.LocalHeaderPos) | |||
right = index; | right = index; | |||
else | else | |||
left = index + 1; | left = index + 1; | |||
} | } | |||
} | } | |||
bool IsStrangeItem(const CItem &item) | static bool IsStrangeItem(const CItem &item) | |||
{ | { | |||
return item.Name.Len() > (1 << 14) || item.Method > (1 << 8); | return item.Name.Len() > (1 << 14) || item.Method > (1 << 8); | |||
} | } | |||
HRESULT CInArchive::ReadLocals( | HRESULT CInArchive::ReadLocals(CObjectVector<CItemEx> &items) | |||
CObjectVector<CItemEx> &items, CProgressVirt *progress) | ||||
{ | { | |||
items.Clear(); | items.Clear(); | |||
while (m_Signature == NSignature::kLocalFileHeader) | while (m_Signature == NSignature::kLocalFileHeader) | |||
{ | { | |||
CItemEx item; | CItemEx item; | |||
item.LocalHeaderPos = m_Position - 4 - ArcInfo.MarkerPos; | item.LocalHeaderPos = m_Position - 4; | |||
if (!IsMultiVol) | ||||
item.LocalHeaderPos -= ArcInfo.MarkerPos; | ||||
// we write ralative LocalHeaderPos here. Later we can correct it to real Ba se. | // we write ralative LocalHeaderPos here. Later we can correct it to real Ba se. | |||
try | try | |||
{ | { | |||
ReadLocalItem(item); | ReadLocalItem(item); | |||
item.FromLocal = true; | item.FromLocal = true; | |||
bool isFinished = false; | ||||
if (item.HasDescriptor()) | if (item.HasDescriptor()) | |||
ReadLocalItemDescriptor(item); | ReadLocalItemDescriptor(item); | |||
else | else | |||
{ | { | |||
RINOK(IncreaseRealPosition(item.PackSize)); | /* | |||
if (IsMultiVol) | ||||
{ | ||||
const int kStep = 10000; | ||||
RINOK(IncreaseRealPosition(-kStep, isFinished)); | ||||
RINOK(IncreaseRealPosition(item.PackSize + kStep, isFinished)); | ||||
} | ||||
else | ||||
*/ | ||||
RINOK(IncreaseRealPosition(item.PackSize, isFinished)); | ||||
} | } | |||
items.Add(item); | items.Add(item); | |||
if (isFinished) | ||||
throw CUnexpectEnd(); | ||||
m_Signature = ReadUInt32(); | m_Signature = ReadUInt32(); | |||
} | } | |||
catch (CUnexpectEnd &) | catch (CUnexpectEnd &) | |||
{ | { | |||
if (items.IsEmpty() || items.Size() == 1 && IsStrangeItem(items[0])) | if (items.IsEmpty() || items.Size() == 1 && IsStrangeItem(items[0])) | |||
return S_FALSE; | return S_FALSE; | |||
throw; | throw; | |||
} | } | |||
if (progress && (items.Size() & 0xFF) == 0) | if (Callback && (items.Size() & 0xFF) == 0) | |||
RINOK(progress->SetCompletedLocal(items.Size(), item.LocalHeaderPos)); | { | |||
const UInt64 numFiles = items.Size(); | ||||
UInt64 numBytes = 0; | ||||
// if (!sMultiVol) | ||||
numBytes = item.LocalHeaderPos; | ||||
RINOK(Callback->SetCompleted(&numFiles, &numBytes)); | ||||
} | ||||
} | } | |||
if (items.Size() == 1 && m_Signature != NSignature::kCentralFileHeader) | if (items.Size() == 1 && m_Signature != NSignature::kCentralFileHeader) | |||
if (IsStrangeItem(items[0])) | if (IsStrangeItem(items[0])) | |||
return S_FALSE; | return S_FALSE; | |||
return S_OK; | return S_OK; | |||
} | } | |||
#define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF) ecd64. n = ecd | HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback) | |||
. n; | ||||
#define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd | ||||
. n; | ||||
HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p | ||||
rogress) | ||||
{ | { | |||
items.Clear(); | UString name; | |||
// m_Signature must be kLocalFileHeader or kEcd | ||||
// m_Position points to next byte after signature | ||||
RINOK(Stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); | ||||
if (!_inBuffer.Create(1 << 15)) | ||||
return E_OUTOFMEMORY; | ||||
_inBuffer.SetStream(Stream); | ||||
bool needReadCd = true; | ||||
bool localsWereRead = false; | ||||
if (m_Signature == NSignature::kEcd) | ||||
{ | { | |||
// It must be empty archive or backware archive | NWindows::NCOM::CPropVariant prop; | |||
// we don't support backware archive still | RINOK(volCallback->GetProperty(kpidName, &prop)); | |||
if (prop.vt != VT_BSTR) | ||||
return S_OK; | ||||
name = prop.bstrVal; | ||||
} | ||||
const unsigned kBufSize = kEcdSize - 4; | UString base = name; | |||
Byte buf[kBufSize]; | int dotPos = name.ReverseFind_Dot(); | |||
SafeReadBytes(buf, kBufSize); | ||||
CEcd ecd; | ||||
ecd.Parse(buf); | ||||
// if (ecd.cdSize != 0) | ||||
// Do we need also to support the case where empty zip archive with PK00 use | ||||
s cdOffset = 4 ?? | ||||
if (!ecd.IsEmptyArc()) | ||||
return S_FALSE; | ||||
ArcInfo.Base = ArcInfo.MarkerPos; | if (dotPos < 0) | |||
needReadCd = false; | return S_OK; | |||
IsArc = true; // check it: we need more tests? | ||||
RINOK(Stream->Seek(ArcInfo.MarkerPos2 + 4, STREAM_SEEK_SET, &m_Position)); | ||||
} | ||||
UInt64 cdSize = 0, cdRelatOffset = 0, cdAbsOffset = 0; | base.DeleteFrom(dotPos + 1); | |||
HRESULT res = S_OK; | ||||
if (needReadCd) | const UString ext = name.Ptr(dotPos + 1); | |||
StartVolIndex = (Int32)(-1); | ||||
if (ext.IsEmpty()) | ||||
return S_OK; | ||||
else | ||||
{ | { | |||
CItemEx firstItem; | wchar_t c = ext[0]; | |||
// try | IsUpperCase = (c >= 'A' && c <= 'Z'); | |||
if (ext.IsEqualTo_Ascii_NoCase("zip")) | ||||
{ | ||||
BaseName = base; | ||||
StartIsZ = true; | ||||
StartIsZip = true; | ||||
return S_OK; | ||||
} | ||||
else if (ext.IsEqualTo_Ascii_NoCase("exe")) | ||||
{ | ||||
StartIsExe = true; | ||||
BaseName = base; | ||||
StartVolIndex = 0; | ||||
} | ||||
else if (ext[0] == 'z' || ext[0] == 'Z') | ||||
{ | ||||
if (ext.Len() < 3) | ||||
return S_OK; | ||||
const wchar_t *end = NULL; | ||||
UInt32 volNum = ConvertStringToUInt32(ext.Ptr(1), &end); | ||||
if (*end != 0 || volNum < 1 || volNum > ((UInt32)1 << 30)) | ||||
return S_OK; | ||||
StartVolIndex = volNum - 1; | ||||
BaseName = base; | ||||
StartIsZ = true; | ||||
} | ||||
else | ||||
return S_OK; | ||||
} | ||||
UString volName = BaseName; | ||||
volName.AddAscii(IsUpperCase ? "ZIP" : "zip"); | ||||
HRESULT result = volCallback->GetStream(volName, &ZipStream); | ||||
if (result == S_FALSE || !ZipStream) | ||||
{ | ||||
if (MissingName.IsEmpty()) | ||||
MissingName = volName; | ||||
return S_OK; | ||||
} | ||||
return result; | ||||
} | ||||
HRESULT CInArchive::ReadVols2(IArchiveOpenVolumeCallback *volCallback, | ||||
unsigned start, int lastDisk, int zipDisk, unsigned numMissingVolsMax, unsig | ||||
ned &numMissingVols) | ||||
{ | ||||
numMissingVols = 0; | ||||
for (unsigned i = start;; i++) | ||||
{ | ||||
if (lastDisk >= 0 && i >= (unsigned)lastDisk) | ||||
break; | ||||
if (i < Vols.Streams.Size()) | ||||
if (Vols.Streams[i].Stream) | ||||
continue; | ||||
CMyComPtr<IInStream> stream; | ||||
if ((int)i == zipDisk) | ||||
{ | ||||
stream = Vols.ZipStream; | ||||
} | ||||
else if ((int)i == Vols.StartVolIndex) | ||||
{ | ||||
stream = StartStream; | ||||
} | ||||
else | ||||
{ | ||||
UString volName = Vols.BaseName; | ||||
{ | ||||
volName += (wchar_t)(Vols.IsUpperCase ? 'Z' : 'z'); | ||||
{ | ||||
char s[32]; | ||||
ConvertUInt32ToString(i + 1, s); | ||||
unsigned len = (unsigned)strlen(s); | ||||
while (len < 2) | ||||
{ | ||||
volName += (wchar_t)'0'; | ||||
len++; | ||||
} | ||||
volName.AddAscii(s); | ||||
} | ||||
} | ||||
HRESULT result = volCallback->GetStream(volName, &stream); | ||||
if (result != S_OK && result != S_FALSE) | ||||
return result; | ||||
if (result == S_FALSE || !stream) | ||||
{ | ||||
if (Vols.MissingName.IsEmpty()) | ||||
Vols.MissingName = volName; | ||||
numMissingVols++; | ||||
if (numMissingVols > numMissingVolsMax) | ||||
return S_OK; | ||||
if (lastDisk == -1 && numMissingVols != 0) | ||||
return S_OK; | ||||
continue; | ||||
} | ||||
} | ||||
UInt64 size; | ||||
UInt64 pos; | ||||
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &pos)); | ||||
RINOK(stream->Seek(0, STREAM_SEEK_END, &size)); | ||||
RINOK(stream->Seek(pos, STREAM_SEEK_SET, NULL)); | ||||
while (i >= Vols.Streams.Size()) | ||||
Vols.Streams.AddNew(); | ||||
CVols::CSubStreamInfo &ss = Vols.Streams[i]; | ||||
Vols.NumVols++; | ||||
ss.Stream = stream; | ||||
ss.Size = size; | ||||
if ((int)i == zipDisk) | ||||
{ | ||||
Vols.EndVolIndex = Vols.Streams.Size() - 1; | ||||
break; | ||||
} | ||||
} | ||||
return S_OK; | ||||
} | ||||
HRESULT CInArchive::ReadVols() | ||||
{ | ||||
CMyComPtr<IArchiveOpenVolumeCallback> volCallback; | ||||
Callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volCallback | ||||
); | ||||
if (!volCallback) | ||||
return S_OK; | ||||
RINOK(Vols.ParseArcName(volCallback)); | ||||
int startZIndex = Vols.StartVolIndex; | ||||
if (!Vols.StartIsZ) | ||||
{ | ||||
// if (!Vols.StartIsExe) | ||||
return S_OK; | ||||
} | ||||
int zipDisk = -1; | ||||
int cdDisk = -1; | ||||
if (Vols.StartIsZip) | ||||
Vols.ZipStream = StartStream; | ||||
// bool cdOK = false; | ||||
if (Vols.ZipStream) | ||||
{ | ||||
Stream = Vols.ZipStream; | ||||
HRESULT res = FindCd(true); | ||||
CCdInfo &ecd = Vols.ecd; | ||||
if (res == S_OK) | ||||
{ | ||||
zipDisk = ecd.ThisDisk; | ||||
Vols.ecd_wasRead = true; | ||||
if (ecd.ThisDisk == 0 | ||||
|| ecd.ThisDisk >= ((UInt32)1 << 30) | ||||
|| ecd.ThisDisk < ecd.CdDisk) | ||||
return S_OK; | ||||
cdDisk = ecd.CdDisk; | ||||
if (Vols.StartVolIndex < 0) | ||||
Vols.StartVolIndex = ecd.ThisDisk; | ||||
// Vols.StartVolIndex = ecd.ThisDisk; | ||||
// Vols.EndVolIndex = ecd.ThisDisk; | ||||
unsigned numMissingVols; | ||||
if (cdDisk == zipDisk) | ||||
{ | ||||
// cdOK = true; | ||||
} | ||||
else | ||||
{ | ||||
RINOK(ReadVols2(volCallback, cdDisk, zipDisk, zipDisk, 0, numMissingVols | ||||
)); | ||||
if (numMissingVols == 0) | ||||
{ | ||||
// cdOK = false; | ||||
} | ||||
} | ||||
} | ||||
else if (res != S_FALSE) | ||||
return res; | ||||
} | ||||
if (Vols.Streams.Size() > 0) | ||||
IsMultiVol = true; | ||||
if (Vols.StartVolIndex < 0) | ||||
return S_OK; | ||||
unsigned numMissingVols; | ||||
if (cdDisk != 0) | ||||
{ | ||||
RINOK(ReadVols2(volCallback, 0, cdDisk < 0 ? -1 : cdDisk, zipDisk, 1 << 10, | ||||
numMissingVols)); | ||||
} | ||||
if (Vols.ZipStream) | ||||
{ | ||||
if (Vols.Streams.IsEmpty()) | ||||
if (zipDisk > (1 << 10)) | ||||
return S_OK; | ||||
RINOK(ReadVols2(volCallback, zipDisk, zipDisk + 1, zipDisk, 0, numMissingVol | ||||
s)); | ||||
} | ||||
if (!Vols.Streams.IsEmpty()) | ||||
{ | ||||
IsMultiVol = true; | ||||
/* | ||||
if (cdDisk) | ||||
IsMultiVol = true; | ||||
*/ | ||||
if (startZIndex >= 0) | ||||
{ | ||||
if (Vols.Streams.Size() >= (unsigned)startZIndex) | ||||
{ | ||||
for (unsigned i = 0; i < (unsigned)startZIndex; i++) | ||||
if (!Vols.Streams[i].Stream) | ||||
{ | ||||
Vols.StartParsingVol = startZIndex; | ||||
break; | ||||
} | ||||
} | ||||
} | ||||
} | ||||
return S_OK; | ||||
} | ||||
HRESULT CVols::Read(void *data, UInt32 size, UInt32 *processedSize) | ||||
{ | ||||
if (processedSize) | ||||
*processedSize = 0; | ||||
if (size == 0) | ||||
return S_OK; | ||||
for (;;) | ||||
{ | ||||
if (StreamIndex < 0) | ||||
return S_OK; | ||||
if ((unsigned)StreamIndex >= Streams.Size()) | ||||
return S_OK; | ||||
const CVols::CSubStreamInfo &s = Streams[StreamIndex]; | ||||
if (!s.Stream) | ||||
return S_FALSE; | ||||
if (NeedSeek) | ||||
{ | ||||
RINOK(s.Stream->Seek(0, STREAM_SEEK_SET, NULL)); | ||||
NeedSeek = false; | ||||
} | ||||
UInt32 realProcessedSize = 0; | ||||
HRESULT res = s.Stream->Read(data, size, &realProcessedSize); | ||||
if (processedSize) | ||||
*processedSize = realProcessedSize; | ||||
if (res != S_OK) | ||||
return res; | ||||
if (realProcessedSize != 0) | ||||
return res; | ||||
StreamIndex++; | ||||
NeedSeek = true; | ||||
} | ||||
} | ||||
STDMETHODIMP CVolStream::Read(void *data, UInt32 size, UInt32 *processedSize) | ||||
{ | ||||
return Vols->Read(data, size, processedSize); | ||||
} | ||||
#define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF) ecd64. n = ecd | ||||
. n; | ||||
#define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd | ||||
. n; | ||||
HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items) | ||||
{ | ||||
HRESULT res = S_OK; | ||||
bool localsWereRead = false; | ||||
UInt64 cdSize = 0, cdRelatOffset = 0, cdAbsOffset = 0; | ||||
UInt32 cdDisk = 0; | ||||
if (!_inBuffer.Create(1 << 15)) | ||||
return E_OUTOFMEMORY; | ||||
if (!MarkerIsFound) | ||||
{ | ||||
IsArc = true; | ||||
res = ReadCd(items, cdDisk, cdRelatOffset, cdSize); | ||||
if (res == S_OK) | ||||
m_Signature = ReadUInt32(); | ||||
} | ||||
else | ||||
{ | ||||
// m_Signature must be kLocalFileHeader or kEcd | ||||
// m_Position points to next byte after signature | ||||
RINOK(Stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); | ||||
_inBuffer.SetStream(Stream); | ||||
bool needReadCd = true; | ||||
if (m_Signature == NSignature::kEcd) | ||||
{ | ||||
// It must be empty archive or backware archive | ||||
// we don't support backware archive still | ||||
const unsigned kBufSize = kEcdSize - 4; | ||||
Byte buf[kBufSize]; | ||||
SafeReadBytes(buf, kBufSize); | ||||
CEcd ecd; | ||||
ecd.Parse(buf); | ||||
// if (ecd.cdSize != 0) | ||||
// Do we need also to support the case where empty zip archive with PK00 use | ||||
s cdOffset = 4 ?? | ||||
if (!ecd.IsEmptyArc()) | ||||
return S_FALSE; | ||||
ArcInfo.Base = ArcInfo.MarkerPos; | ||||
needReadCd = false; | ||||
IsArc = true; // check it: we need more tests? | ||||
RINOK(Stream->Seek(ArcInfo.MarkerPos2 + 4, STREAM_SEEK_SET, &m_Position)); | ||||
} | ||||
if (needReadCd) | ||||
{ | ||||
CItemEx firstItem; | ||||
// try | ||||
{ | { | |||
try | try | |||
{ | { | |||
if (!ReadLocalItem(firstItem)) | if (!ReadLocalItem(firstItem)) | |||
return S_FALSE; | return S_FALSE; | |||
} | } | |||
catch(CUnexpectEnd &) | catch(CUnexpectEnd &) | |||
{ | { | |||
return S_FALSE; | return S_FALSE; | |||
} | } | |||
IsArc = true; | IsArc = true; | |||
res = ReadCd(items, cdRelatOffset, cdSize, progress); | res = ReadCd(items, cdDisk, cdRelatOffset, cdSize); | |||
if (res == S_OK) | if (res == S_OK) | |||
m_Signature = ReadUInt32(); | m_Signature = ReadUInt32(); | |||
} | } | |||
// catch() { res = S_FALSE; } | // catch() { res = S_FALSE; } | |||
if (res != S_FALSE && res != S_OK) | if (res != S_FALSE && res != S_OK) | |||
return res; | return res; | |||
if (res == S_OK && items.Size() == 0) | if (res == S_OK && items.Size() == 0) | |||
res = S_FALSE; | res = S_FALSE; | |||
if (res == S_OK) | if (res == S_OK) | |||
{ | { | |||
// we can't read local items here to keep _inBufMode state | // we can't read local items here to keep _inBufMode state | |||
firstItem.LocalHeaderPos = ArcInfo.MarkerPos2 - ArcInfo.Base; | if ((Int64)ArcInfo.MarkerPos2 < ArcInfo.Base) | |||
int index = FindItem(items, firstItem.LocalHeaderPos); | ||||
if (index == -1) | ||||
res = S_FALSE; | ||||
else if (!AreItemsEqual(firstItem, items[index])) | ||||
res = S_FALSE; | res = S_FALSE; | |||
ArcInfo.CdWasRead = true; | else | |||
ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos; | { | |||
firstItem.LocalHeaderPos = ArcInfo.MarkerPos2 - ArcInfo.Base; | ||||
int index = FindItem(items, firstItem); | ||||
if (index == -1) | ||||
res = S_FALSE; | ||||
else if (!AreItemsEqual(firstItem, items[index])) | ||||
res = S_FALSE; | ||||
else | ||||
{ | ||||
ArcInfo.CdWasRead = true; | ||||
ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos; | ||||
} | ||||
} | ||||
} | } | |||
} | } | |||
} | ||||
CObjectVector<CItemEx> cdItems; | CObjectVector<CItemEx> cdItems; | |||
bool needSetBase = false; | bool needSetBase = false; | |||
unsigned numCdItems = items.Size(); | unsigned numCdItems = items.Size(); | |||
if (res == S_FALSE) | if (res == S_FALSE) | |||
{ | { | |||
// CD doesn't match firstItem so we clear items and read Locals. | // CD doesn't match firstItem, | |||
// so we clear items and read Locals. | ||||
items.Clear(); | items.Clear(); | |||
localsWereRead = true; | localsWereRead = true; | |||
_inBufMode = false; | _inBufMode = false; | |||
ArcInfo.Base = ArcInfo.MarkerPos; | ArcInfo.Base = ArcInfo.MarkerPos; | |||
if (IsMultiVol) | ||||
{ | ||||
Vols.StreamIndex = Vols.StartParsingVol; | ||||
if (Vols.StartParsingVol >= (int)Vols.Streams.Size()) | ||||
return S_FALSE; | ||||
Stream = Vols.Streams[Vols.StartParsingVol].Stream; | ||||
if (!Stream) | ||||
return S_FALSE; | ||||
} | ||||
RINOK(Stream->Seek(ArcInfo.MarkerPos2, STREAM_SEEK_SET, &m_Position)); | RINOK(Stream->Seek(ArcInfo.MarkerPos2, STREAM_SEEK_SET, &m_Position)); | |||
m_Signature = ReadUInt32(); | m_Signature = ReadUInt32(); | |||
RINOK(ReadLocals(items, progress)); | RINOK(ReadLocals(items)); | |||
if (m_Signature != NSignature::kCentralFileHeader) | if (m_Signature != NSignature::kCentralFileHeader) | |||
{ | { | |||
m_Position -= 4; | // if (!UnexpectedEnd) | |||
m_Position -= 4; | ||||
NoCentralDir = true; | NoCentralDir = true; | |||
HeadersError = true; | HeadersError = true; | |||
return S_OK; | return S_OK; | |||
} | } | |||
_inBufMode = true; | _inBufMode = true; | |||
_inBuffer.Init(); | _inBuffer.Init(); | |||
cdAbsOffset = m_Position - 4; | cdAbsOffset = m_Position - 4; | |||
cdDisk = Vols.StreamIndex; | ||||
for (;;) | for (;;) | |||
{ | { | |||
CItemEx cdItem; | CItemEx cdItem; | |||
CanStartNewVol = true; | ||||
RINOK(ReadCdItem(cdItem)); | RINOK(ReadCdItem(cdItem)); | |||
cdItems.Add(cdItem); | cdItems.Add(cdItem); | |||
if (progress && (cdItems.Size() & 0xFFF) == 0) | if (Callback && (cdItems.Size() & 0xFFF) == 0) | |||
RINOK(progress->SetCompletedCD(items.Size())); | { | |||
const UInt64 numFiles = items.Size(); | ||||
RINOK(Callback->SetCompleted(&numFiles, NULL)); | ||||
} | ||||
CanStartNewVol = true; | ||||
m_Signature = ReadUInt32(); | m_Signature = ReadUInt32(); | |||
if (m_Signature != NSignature::kCentralFileHeader) | if (m_Signature != NSignature::kCentralFileHeader) | |||
break; | break; | |||
} | } | |||
cdSize = (m_Position - 4) - cdAbsOffset; | cdSize = (m_Position - 4) - cdAbsOffset; | |||
needSetBase = true; | needSetBase = true; | |||
numCdItems = cdItems.Size(); | numCdItems = cdItems.Size(); | |||
if (!cdItems.IsEmpty()) | if (!cdItems.IsEmpty()) | |||
{ | { | |||
ArcInfo.CdWasRead = true; | ArcInfo.CdWasRead = true; | |||
ArcInfo.FirstItemRelatOffset = cdItems[0].LocalHeaderPos; | ArcInfo.FirstItemRelatOffset = cdItems[0].LocalHeaderPos; | |||
} | } | |||
} | } | |||
CEcd64 ecd64; | CCdInfo ecd64; | |||
CLocator locator; | ||||
bool isZip64 = false; | bool isZip64 = false; | |||
UInt64 ecd64AbsOffset = m_Position - 4; | const UInt64 ecd64AbsOffset = m_Position - 4; | |||
int ecd64Disk = -1; | ||||
if (m_Signature == NSignature::kEcd64) | if (m_Signature == NSignature::kEcd64) | |||
{ | { | |||
ecd64Disk = Vols.StreamIndex; | ||||
IsZip64 = isZip64 = true; | IsZip64 = isZip64 = true; | |||
UInt64 recordSize = ReadUInt64(); | ||||
const unsigned kBufSize = kEcd64_MainSize; | { | |||
Byte buf[kBufSize]; | const UInt64 recordSize = ReadUInt64(); | |||
SafeReadBytes(buf, kBufSize); | if (recordSize < kEcd64_MainSize) | |||
ecd64.Parse(buf); | { | |||
HeadersError = true; | ||||
return S_OK; | ||||
} | ||||
{ | ||||
const unsigned kBufSize = kEcd64_MainSize; | ||||
Byte buf[kBufSize]; | ||||
SafeReadBytes(buf, kBufSize); | ||||
ecd64.ParseEcd64e(buf); | ||||
} | ||||
Skip64(recordSize - kEcd64_MainSize); | ||||
} | ||||
Skip64(recordSize - kEcd64_MainSize); | ||||
m_Signature = ReadUInt32(); | m_Signature = ReadUInt32(); | |||
if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) | if (m_Signature != NSignature::kEcd64Locator) | |||
return E_NOTIMPL; | { | |||
HeadersError = true; | ||||
return S_OK; | ||||
} | ||||
if (needSetBase) | ||||
{ | { | |||
ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset; | const unsigned kBufSize = 16; | |||
cdRelatOffset = ecd64.cdStartOffset; | Byte buf[kBufSize]; | |||
needSetBase = false; | SafeReadBytes(buf, kBufSize); | |||
locator.Parse(buf); | ||||
} | } | |||
if (ecd64.numEntriesInCDOnThisDisk != numCdItems || | m_Signature = ReadUInt32(); | |||
ecd64.numEntriesInCD != numCdItems || | ||||
ecd64.cdSize != cdSize || | ||||
(ecd64.cdStartOffset != cdRelatOffset && | ||||
(!items.IsEmpty()))) | ||||
return S_FALSE; | ||||
} | } | |||
if (m_Signature == NSignature::kEcd64Locator) | if (m_Signature != NSignature::kEcd) | |||
{ | { | |||
if (!isZip64) | HeadersError = true; | |||
return S_FALSE; | return S_OK; | |||
/* UInt32 startEndCDDiskNumber = */ ReadUInt32(); | ||||
UInt64 ecd64RelatOffset = ReadUInt64(); | ||||
/* UInt32 numberOfDisks = */ ReadUInt32(); | ||||
if (ecd64AbsOffset != ArcInfo.Base + ecd64RelatOffset) | ||||
return S_FALSE; | ||||
m_Signature = ReadUInt32(); | ||||
} | } | |||
if (m_Signature != NSignature::kEcd) | // ---------- ECD ---------- | |||
return S_FALSE; | ||||
const unsigned kBufSize = kEcdSize - 4; | ||||
Byte buf[kBufSize]; | ||||
SafeReadBytes(buf, kBufSize); | ||||
CEcd ecd; | CEcd ecd; | |||
ecd.Parse(buf); | { | |||
const unsigned kBufSize = kEcdSize - 4; | ||||
Byte buf[kBufSize]; | ||||
SafeReadBytes(buf, kBufSize); | ||||
ecd.Parse(buf); | ||||
} | ||||
COPY_ECD_ITEM_16(thisDiskNumber); | COPY_ECD_ITEM_16(ThisDisk); | |||
COPY_ECD_ITEM_16(startCDDiskNumber); | COPY_ECD_ITEM_16(CdDisk); | |||
COPY_ECD_ITEM_16(numEntriesInCDOnThisDisk); | COPY_ECD_ITEM_16(NumEntries_in_ThisDisk); | |||
COPY_ECD_ITEM_16(numEntriesInCD); | COPY_ECD_ITEM_16(NumEntries); | |||
COPY_ECD_ITEM_32(cdSize); | COPY_ECD_ITEM_32(Size); | |||
COPY_ECD_ITEM_32(cdStartOffset); | COPY_ECD_ITEM_32(Offset); | |||
if (needSetBase) | if (IsMultiVol) | |||
{ | { | |||
ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset; | if (cdDisk != (int)ecd64.CdDisk) | |||
cdRelatOffset = ecd64.cdStartOffset; | HeadersError = true; | |||
needSetBase = false; | ||||
} | } | |||
else if (needSetBase) | ||||
{ | ||||
if (isZip64) | ||||
{ | ||||
if (ecd64Disk == Vols.StartVolIndex) | ||||
{ | ||||
ArcInfo.Base = ecd64AbsOffset - locator.Ecd64Offset; | ||||
// cdRelatOffset = ecd64.Offset; | ||||
needSetBase = false; | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
if ((int)cdDisk == Vols.StartVolIndex) | ||||
{ | ||||
ArcInfo.Base = cdAbsOffset - ecd64.Offset; | ||||
cdRelatOffset = ecd64.Offset; | ||||
needSetBase = false; | ||||
} | ||||
} | ||||
} | ||||
EcdVolIndex = ecd64.ThisDisk; | ||||
if (localsWereRead && (UInt64)ArcInfo.Base != ArcInfo.MarkerPos) | if (!IsMultiVol) | |||
{ | { | |||
UInt64 delta = ArcInfo.MarkerPos - ArcInfo.Base; | UseDisk_in_SingleVol = true; | |||
for (unsigned i = 0; i < items.Size(); i++) | ||||
items[i].LocalHeaderPos += delta; | if (localsWereRead) | |||
{ | ||||
if ((UInt64)ArcInfo.Base != ArcInfo.MarkerPos) | ||||
{ | ||||
const UInt64 delta = ArcInfo.MarkerPos - ArcInfo.Base; | ||||
FOR_VECTOR (i, items) | ||||
items[i].LocalHeaderPos += delta; | ||||
} | ||||
if (EcdVolIndex != 0) | ||||
{ | ||||
FOR_VECTOR (i, items) | ||||
items[i].Disk = EcdVolIndex; | ||||
} | ||||
} | ||||
} | ||||
if (isZip64) | ||||
{ | ||||
if (ecd64.ThisDisk == 0 && ecd64AbsOffset != ArcInfo.Base + locator.Ecd64Off | ||||
set | ||||
// || ecd64.NumEntries_in_ThisDisk != numCdItems | ||||
|| ecd64.NumEntries != numCdItems | ||||
|| ecd64.Size != cdSize | ||||
|| (ecd64.Offset != cdRelatOffset && !items.IsEmpty())) | ||||
{ | ||||
HeadersError = true; | ||||
return S_OK; | ||||
} | ||||
} | } | |||
// ---------- merge Central Directory Items ---------- | // ---------- merge Central Directory Items ---------- | |||
if (!cdItems.IsEmpty()) | if (!cdItems.IsEmpty()) | |||
{ | { | |||
for (unsigned i = 0; i < cdItems.Size(); i++) | CObjectVector<CItemEx> items2; | |||
FOR_VECTOR (i, cdItems) | ||||
{ | { | |||
const CItemEx &cdItem = cdItems[i]; | const CItemEx &cdItem = cdItems[i]; | |||
int index = FindItem(items, cdItem.LocalHeaderPos); | int index = FindItem(items, cdItem); | |||
if (index == -1) | if (index == -1) | |||
{ | { | |||
items.Add(cdItem); | items2.Add(cdItem); | |||
HeadersError = true; | ||||
continue; | continue; | |||
} | } | |||
CItemEx &item = items[index]; | CItemEx &item = items[index]; | |||
if (item.Name != cdItem.Name | if (item.Name != cdItem.Name | |||
// || item.Name.Len() != cdItem.Name.Len() | // || item.Name.Len() != cdItem.Name.Len() | |||
|| item.PackSize != cdItem.PackSize | || item.PackSize != cdItem.PackSize | |||
|| item.Size != cdItem.Size | || item.Size != cdItem.Size | |||
// item.ExtractVersion != cdItem.ExtractVersion | // item.ExtractVersion != cdItem.ExtractVersion | |||
|| !FlagsAreSame(item, cdItem) | || !FlagsAreSame(item, cdItem) | |||
|| item.Crc != cdItem.Crc) | || item.Crc != cdItem.Crc) | |||
{ | ||||
HeadersError = true; | ||||
continue; | continue; | |||
} | ||||
// item.LocalHeaderPos = cdItem.LocalHeaderPos; | ||||
// item.Name = cdItem.Name; | // item.Name = cdItem.Name; | |||
item.MadeByVersion = cdItem.MadeByVersion; | item.MadeByVersion = cdItem.MadeByVersion; | |||
item.CentralExtra = cdItem.CentralExtra; | item.CentralExtra = cdItem.CentralExtra; | |||
item.InternalAttrib = cdItem.InternalAttrib; | item.InternalAttrib = cdItem.InternalAttrib; | |||
item.ExternalAttrib = cdItem.ExternalAttrib; | item.ExternalAttrib = cdItem.ExternalAttrib; | |||
item.Comment = cdItem.Comment; | item.Comment = cdItem.Comment; | |||
item.FromCentral = cdItem.FromCentral; | item.FromCentral = cdItem.FromCentral; | |||
} | } | |||
items += items2; | ||||
} | ||||
if (ecd.NumEntries < ecd.NumEntries_in_ThisDisk) | ||||
HeadersError = true; | ||||
if (ecd.ThisDisk == 0) | ||||
{ | ||||
// if (isZip64) | ||||
{ | ||||
if (ecd.NumEntries != ecd.NumEntries_in_ThisDisk) | ||||
HeadersError = true; | ||||
} | ||||
} | } | |||
if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) | if (ecd.NumEntries > items.Size()) | |||
return E_NOTIMPL; | HeadersError = true; | |||
if (isZip64) | if (isZip64) | |||
{ | { | |||
if (ecd64.numEntriesInCDOnThisDisk != items.Size()) | if (ecd64.NumEntries != items.Size()) | |||
HeadersError = true; | HeadersError = true; | |||
} | } | |||
else | else | |||
{ | { | |||
// old 7-zip could store 32-bit number of CD items to 16-bit field. | // old 7-zip could store 32-bit number of CD items to 16-bit field. | |||
if ((UInt16)ecd64.numEntriesInCDOnThisDisk != (UInt16)numCdItems || | /* | |||
(UInt16)ecd64.numEntriesInCDOnThisDisk != (UInt16)items.Size()) | if ((UInt16)ecd64.NumEntries == (UInt16)items.Size()) | |||
HeadersError = true; | HeadersError = true; | |||
*/ | ||||
} | } | |||
ReadBuffer(ArcInfo.Comment, ecd.commentSize); | ReadBuffer(ArcInfo.Comment, ecd.CommentSize); | |||
_inBufMode = false; | _inBufMode = false; | |||
_inBuffer.Free(); | _inBuffer.Free(); | |||
if ( | if ((UInt16)ecd64.NumEntries != (UInt16)numCdItems | |||
(UInt16)ecd64.numEntriesInCD != ((UInt16)numCdItems) || | || (UInt32)ecd64.Size != (UInt32)cdSize | |||
(UInt32)ecd64.cdSize != (UInt32)cdSize || | || ((UInt32)ecd64.Offset != (UInt32)cdRelatOffset && !items.IsEmpty())) | |||
((UInt32)(ecd64.cdStartOffset) != (UInt32)cdRelatOffset && | ||||
(!items.IsEmpty()))) | ||||
{ | { | |||
// return S_FALSE; | // return S_FALSE; | |||
HeadersError = true; | HeadersError = true; | |||
} | } | |||
// printf("\nOpen OK"); | // printf("\nOpen OK"); | |||
return S_OK; | return S_OK; | |||
} | } | |||
HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *pr | HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchLimit, | |||
ogress) | IArchiveOpenCallback *callback, CObjectVector<CItemEx> &items) | |||
{ | { | |||
HRESULT res; | _inBufMode = false; | |||
try | items.Clear(); | |||
Close(); | ||||
ArcInfo.Clear(); | ||||
UInt64 startPos; | ||||
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &startPos)); | ||||
RINOK(stream->Seek(0, STREAM_SEEK_END, &ArcInfo.FileEndPos)); | ||||
m_Position = ArcInfo.FileEndPos; | ||||
StartStream = stream; | ||||
Callback = callback; | ||||
bool volWasRequested = false; | ||||
if (callback | ||||
&& (startPos == 0 || !searchLimit || *searchLimit != 0)) | ||||
{ | { | |||
res = ReadHeaders2(items, progress); | volWasRequested = true; | |||
RINOK(ReadVols()); | ||||
} | } | |||
catch (const CInBufferException &e) { res = e.ErrorCode; } | ||||
catch (const CUnexpectEnd &) | if (IsMultiVol && Vols.StartVolIndex != 0) | |||
{ | { | |||
if (items.IsEmpty()) | Stream = Vols.Streams[0].Stream; | |||
return S_FALSE; | if (Stream) | |||
UnexpectedEnd = true; | { | |||
res = S_OK; | m_Position = 0; | |||
RINOK(Stream->Seek(0, STREAM_SEEK_SET, NULL)); | ||||
UInt64 limit = 0; | ||||
HRESULT res = FindMarker(Stream, &limit); | ||||
if (res == S_OK) | ||||
MarkerIsFound = true; | ||||
else if (res != S_FALSE) | ||||
return res; | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
// printf("\nOpen offset = %u\n", (unsigned)startPos); | ||||
RINOK(stream->Seek(startPos, STREAM_SEEK_SET, NULL)); | ||||
m_Position = startPos; | ||||
HRESULT res = FindMarker(stream, searchLimit); | ||||
UInt64 curPos = m_Position; | ||||
if (res == S_OK) | ||||
MarkerIsFound = true; | ||||
else | ||||
{ | ||||
// if (res != S_FALSE) | ||||
return res; | ||||
} | ||||
MarkerIsFound = true; | ||||
if (ArcInfo.IsSpanMode && !volWasRequested) | ||||
{ | ||||
RINOK(ReadVols()); | ||||
} | ||||
if (IsMultiVol && (unsigned)Vols.StartVolIndex < Vols.Streams.Size()) | ||||
{ | ||||
Stream = Vols.Streams[Vols.StartVolIndex].Stream; | ||||
if (!Stream) | ||||
IsMultiVol = false; | ||||
else | ||||
{ | ||||
RINOK(Stream->Seek(curPos, STREAM_SEEK_SET, NULL)); | ||||
m_Position = curPos; | ||||
} | ||||
} | ||||
else | ||||
IsMultiVol = false; | ||||
if (!IsMultiVol) | ||||
{ | ||||
RINOK(stream->Seek(curPos, STREAM_SEEK_SET, NULL)); | ||||
m_Position = curPos; | ||||
StreamRef = stream; | ||||
Stream = stream; | ||||
} | ||||
} | } | |||
catch (...) | ||||
{ | { | |||
HRESULT res; | ||||
try | ||||
{ | ||||
res = ReadHeaders2(items); | ||||
} | ||||
catch (const CInBufferException &e) { res = e.ErrorCode; } | ||||
catch (const CUnexpectEnd &) | ||||
{ | ||||
if (items.IsEmpty()) | ||||
return S_FALSE; | ||||
UnexpectedEnd = true; | ||||
res = S_OK; | ||||
} | ||||
catch (...) | ||||
{ | ||||
_inBufMode = false; | ||||
throw; | ||||
} | ||||
if (IsMultiVol) | ||||
{ | ||||
ArcInfo.FinishPos = ArcInfo.FileEndPos; | ||||
if ((unsigned)Vols.StreamIndex < Vols.Streams.Size()) | ||||
if (m_Position < Vols.Streams[Vols.StreamIndex].Size) | ||||
ArcInfo.ThereIsTail = true; | ||||
} | ||||
else | ||||
{ | ||||
ArcInfo.FinishPos = m_Position; | ||||
ArcInfo.ThereIsTail = (ArcInfo.FileEndPos > m_Position); | ||||
} | ||||
_inBufMode = false; | _inBufMode = false; | |||
throw; | IsArcOpen = true; | |||
if (!IsMultiVol) | ||||
Vols.Streams.Clear(); | ||||
return res; | ||||
} | } | |||
ArcInfo.FinishPos = m_Position; | ||||
_inBufMode = false; | ||||
return res; | ||||
} | } | |||
ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 siz e) | HRESULT CInArchive::GetItemStream(const CItemEx &item, bool seekPackData, CMyCom Ptr<ISequentialInStream> &stream) | |||
{ | { | |||
CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; | stream.Release(); | |||
CMyComPtr<ISequentialInStream> stream(streamSpec); | ||||
Stream->Seek(ArcInfo.Base + position, STREAM_SEEK_SET, NULL); | UInt64 pos = item.LocalHeaderPos; | |||
streamSpec->SetStream(Stream); | if (seekPackData) | |||
streamSpec->Init(size); | pos += item.LocalFullHeaderSize; | |||
return stream.Detach(); | ||||
if (!IsMultiVol) | ||||
{ | ||||
if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex) | ||||
return S_OK; | ||||
pos += ArcInfo.Base; | ||||
RINOK(StreamRef->Seek(pos, STREAM_SEEK_SET, NULL)); | ||||
stream = StreamRef; | ||||
return S_OK; | ||||
} | ||||
if (item.Disk >= Vols.Streams.Size()) | ||||
return S_OK; | ||||
IInStream *str2 = Vols.Streams[item.Disk].Stream; | ||||
if (!str2) | ||||
return S_OK; | ||||
RINOK(str2->Seek(pos, STREAM_SEEK_SET, NULL)); | ||||
Vols.NeedSeek = false; | ||||
Vols.StreamIndex = item.Disk; | ||||
CVolStream *volsStreamSpec = new CVolStream; | ||||
volsStreamSpec->Vols = &Vols; | ||||
stream = volsStreamSpec; | ||||
return S_OK; | ||||
} | } | |||
}} | }} | |||
End of changes. 173 change blocks. | ||||
368 lines changed or deleted | 1221 lines changed or added |