// 7zIn.cpp | |
#include "StdAfx.h" | |
#include "../../../../C/7zCrc.h" | |
#include "../../../../C/CpuArch.h" | |
#include "../../Common/StreamObjects.h" | |
#include "../../Common/StreamUtils.h" | |
#include "7zDecode.h" | |
#include "7zIn.h" | |
#define Get16(p) GetUi16(p) | |
#define Get32(p) GetUi32(p) | |
#define Get64(p) GetUi64(p) | |
// define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader | |
#ifndef _SFX | |
#define FORMAT_7Z_RECOVERY | |
#endif | |
namespace NArchive { | |
namespace N7z { | |
static void BoolVector_Fill_False(CBoolVector &v, int size) | |
{ | |
v.Clear(); | |
v.Reserve(size); | |
for (int i = 0; i < size; i++) | |
v.Add(false); | |
} | |
static bool BoolVector_GetAndSet(CBoolVector &v, UInt32 index) | |
{ | |
if (index >= (UInt32)v.Size()) | |
return true; | |
bool res = v[index]; | |
v[index] = true; | |
return res; | |
} | |
bool CFolder::CheckStructure() const | |
{ | |
const int kNumCodersMax = sizeof(UInt32) * 8; // don't change it | |
const int kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax | |
const int kNumBindsMax = 32; | |
if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax) | |
return false; | |
{ | |
CBoolVector v; | |
BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size()); | |
int i; | |
for (i = 0; i < BindPairs.Size(); i++) | |
if (BoolVector_GetAndSet(v, BindPairs[i].InIndex)) | |
return false; | |
for (i = 0; i < PackStreams.Size(); i++) | |
if (BoolVector_GetAndSet(v, PackStreams[i])) | |
return false; | |
BoolVector_Fill_False(v, UnpackSizes.Size()); | |
for (i = 0; i < BindPairs.Size(); i++) | |
if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex)) | |
return false; | |
} | |
UInt32 mask[kMaskSize]; | |
int i; | |
for (i = 0; i < kMaskSize; i++) | |
mask[i] = 0; | |
{ | |
CIntVector inStreamToCoder, outStreamToCoder; | |
for (i = 0; i < Coders.Size(); i++) | |
{ | |
CNum j; | |
const CCoderInfo &coder = Coders[i]; | |
for (j = 0; j < coder.NumInStreams; j++) | |
inStreamToCoder.Add(i); | |
for (j = 0; j < coder.NumOutStreams; j++) | |
outStreamToCoder.Add(i); | |
} | |
for (i = 0; i < BindPairs.Size(); i++) | |
{ | |
const CBindPair &bp = BindPairs[i]; | |
mask[inStreamToCoder[bp.InIndex]] |= (1 << outStreamToCoder[bp.OutIndex]); | |
} | |
} | |
for (i = 0; i < kMaskSize; i++) | |
for (int j = 0; j < kMaskSize; j++) | |
if (((1 << j) & mask[i]) != 0) | |
mask[i] |= mask[j]; | |
for (i = 0; i < kMaskSize; i++) | |
if (((1 << i) & mask[i]) != 0) | |
return false; | |
return true; | |
} | |
class CInArchiveException {}; | |
static void ThrowException() { throw CInArchiveException(); } | |
static inline void ThrowEndOfData() { ThrowException(); } | |
static inline void ThrowUnsupported() { ThrowException(); } | |
static inline void ThrowIncorrect() { ThrowException(); } | |
static inline void ThrowUnsupportedVersion() { ThrowException(); } | |
/* | |
class CInArchiveException | |
{ | |
public: | |
enum CCauseType | |
{ | |
kUnsupportedVersion = 0, | |
kUnsupported, | |
kIncorrect, | |
kEndOfData | |
} Cause; | |
CInArchiveException(CCauseType cause): Cause(cause) {}; | |
}; | |
static void ThrowException(CInArchiveException::CCauseType c) { throw CInArchiveException(c); } | |
static void ThrowEndOfData() { ThrowException(CInArchiveException::kEndOfData); } | |
static void ThrowUnsupported() { ThrowException(CInArchiveException::kUnsupported); } | |
static void ThrowIncorrect() { ThrowException(CInArchiveException::kIncorrect); } | |
static void ThrowUnsupportedVersion() { ThrowException(CInArchiveException::kUnsupportedVersion); } | |
*/ | |
class CStreamSwitch | |
{ | |
CInArchive *_archive; | |
bool _needRemove; | |
public: | |
CStreamSwitch(): _needRemove(false) {} | |
~CStreamSwitch() { Remove(); } | |
void Remove(); | |
void Set(CInArchive *archive, const Byte *data, size_t size); | |
void Set(CInArchive *archive, const CByteBuffer &byteBuffer); | |
void Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector); | |
}; | |
void CStreamSwitch::Remove() | |
{ | |
if (_needRemove) | |
{ | |
_archive->DeleteByteStream(); | |
_needRemove = false; | |
} | |
} | |
void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size) | |
{ | |
Remove(); | |
_archive = archive; | |
_archive->AddByteStream(data, size); | |
_needRemove = true; | |
} | |
void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer) | |
{ | |
Set(archive, byteBuffer, byteBuffer.GetCapacity()); | |
} | |
void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector) | |
{ | |
Remove(); | |
Byte external = archive->ReadByte(); | |
if (external != 0) | |
{ | |
int dataIndex = (int)archive->ReadNum(); | |
if (dataIndex < 0 || dataIndex >= dataVector->Size()) | |
ThrowIncorrect(); | |
Set(archive, (*dataVector)[dataIndex]); | |
} | |
} | |
Byte CInByte2::ReadByte() | |
{ | |
if (_pos >= _size) | |
ThrowEndOfData(); | |
return _buffer[_pos++]; | |
} | |
void CInByte2::ReadBytes(Byte *data, size_t size) | |
{ | |
if (size > _size - _pos) | |
ThrowEndOfData(); | |
for (size_t i = 0; i < size; i++) | |
data[i] = _buffer[_pos++]; | |
} | |
void CInByte2::SkipData(UInt64 size) | |
{ | |
if (size > _size - _pos) | |
ThrowEndOfData(); | |
_pos += (size_t)size; | |
} | |
void CInByte2::SkipData() | |
{ | |
SkipData(ReadNumber()); | |
} | |
UInt64 CInByte2::ReadNumber() | |
{ | |
if (_pos >= _size) | |
ThrowEndOfData(); | |
Byte firstByte = _buffer[_pos++]; | |
Byte mask = 0x80; | |
UInt64 value = 0; | |
for (int i = 0; i < 8; i++) | |
{ | |
if ((firstByte & mask) == 0) | |
{ | |
UInt64 highPart = firstByte & (mask - 1); | |
value += (highPart << (i * 8)); | |
return value; | |
} | |
if (_pos >= _size) | |
ThrowEndOfData(); | |
value |= ((UInt64)_buffer[_pos++] << (8 * i)); | |
mask >>= 1; | |
} | |
return value; | |
} | |
CNum CInByte2::ReadNum() | |
{ | |
UInt64 value = ReadNumber(); | |
if (value > kNumMax) | |
ThrowUnsupported(); | |
return (CNum)value; | |
} | |
UInt32 CInByte2::ReadUInt32() | |
{ | |
if (_pos + 4 > _size) | |
ThrowEndOfData(); | |
UInt32 res = Get32(_buffer + _pos); | |
_pos += 4; | |
return res; | |
} | |
UInt64 CInByte2::ReadUInt64() | |
{ | |
if (_pos + 8 > _size) | |
ThrowEndOfData(); | |
UInt64 res = Get64(_buffer + _pos); | |
_pos += 8; | |
return res; | |
} | |
void CInByte2::ReadString(UString &s) | |
{ | |
const Byte *buf = _buffer + _pos; | |
size_t rem = (_size - _pos) / 2 * 2; | |
{ | |
size_t i; | |
for (i = 0; i < rem; i += 2) | |
if (buf[i] == 0 && buf[i + 1] == 0) | |
break; | |
if (i == rem) | |
ThrowEndOfData(); | |
rem = i; | |
} | |
int len = (int)(rem / 2); | |
if (len < 0 || (size_t)len * 2 != rem) | |
ThrowUnsupported(); | |
wchar_t *p = s.GetBuffer(len); | |
int i; | |
for (i = 0; i < len; i++, buf += 2) | |
p[i] = (wchar_t)Get16(buf); | |
s.ReleaseBuffer(len); | |
_pos += rem + 2; | |
} | |
static inline bool TestSignature(const Byte *p) | |
{ | |
for (int i = 0; i < kSignatureSize; i++) | |
if (p[i] != kSignature[i]) | |
return false; | |
return CrcCalc(p + 12, 20) == GetUi32(p + 8); | |
} | |
#ifdef FORMAT_7Z_RECOVERY | |
static inline bool TestSignature2(const Byte *p) | |
{ | |
int i; | |
for (i = 0; i < kSignatureSize; i++) | |
if (p[i] != kSignature[i]) | |
return false; | |
if (CrcCalc(p + 12, 20) == GetUi32(p + 8)) | |
return true; | |
for (i = 8; i < kHeaderSize; i++) | |
if (p[i] != 0) | |
return false; | |
return (p[6] != 0 || p[7] != 0); | |
} | |
#else | |
#define TestSignature2(p) TestSignature(p) | |
#endif | |
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; | |
} | |
// S_FALSE means that file is not archive | |
HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) | |
{ | |
HeadersSize = 0; | |
Close(); | |
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition)) | |
RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); | |
_stream = stream; | |
return S_OK; | |
} | |
void CInArchive::Close() | |
{ | |
_stream.Release(); | |
} | |
void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) | |
{ | |
for (;;) | |
{ | |
if (ReadID() == NID::kEnd) | |
break; | |
SkipData(); | |
} | |
} | |
void CInArchive::GetNextFolderItem(CFolder &folder) | |
{ | |
CNum numCoders = ReadNum(); | |
folder.Coders.Clear(); | |
folder.Coders.Reserve((int)numCoders); | |
CNum numInStreams = 0; | |
CNum numOutStreams = 0; | |
CNum i; | |
for (i = 0; i < numCoders; i++) | |
{ | |
folder.Coders.Add(CCoderInfo()); | |
CCoderInfo &coder = folder.Coders.Back(); | |
{ | |
Byte mainByte = ReadByte(); | |
int idSize = (mainByte & 0xF); | |
Byte longID[15]; | |
ReadBytes(longID, idSize); | |
if (idSize > 8) | |
ThrowUnsupported(); | |
UInt64 id = 0; | |
for (int j = 0; j < idSize; j++) | |
id |= (UInt64)longID[idSize - 1 - j] << (8 * j); | |
coder.MethodID = id; | |
if ((mainByte & 0x10) != 0) | |
{ | |
coder.NumInStreams = ReadNum(); | |
coder.NumOutStreams = ReadNum(); | |
} | |
else | |
{ | |
coder.NumInStreams = 1; | |
coder.NumOutStreams = 1; | |
} | |
if ((mainByte & 0x20) != 0) | |
{ | |
CNum propsSize = ReadNum(); | |
coder.Props.SetCapacity((size_t)propsSize); | |
ReadBytes((Byte *)coder.Props, (size_t)propsSize); | |
} | |
if ((mainByte & 0x80) != 0) | |
ThrowUnsupported(); | |
} | |
numInStreams += coder.NumInStreams; | |
numOutStreams += coder.NumOutStreams; | |
} | |
CNum numBindPairs = numOutStreams - 1; | |
folder.BindPairs.Clear(); | |
folder.BindPairs.Reserve(numBindPairs); | |
for (i = 0; i < numBindPairs; i++) | |
{ | |
CBindPair bp; | |
bp.InIndex = ReadNum(); | |
bp.OutIndex = ReadNum(); | |
folder.BindPairs.Add(bp); | |
} | |
if (numInStreams < numBindPairs) | |
ThrowUnsupported(); | |
CNum numPackStreams = numInStreams - numBindPairs; | |
folder.PackStreams.Reserve(numPackStreams); | |
if (numPackStreams == 1) | |
{ | |
for (i = 0; i < numInStreams; i++) | |
if (folder.FindBindPairForInStream(i) < 0) | |
{ | |
folder.PackStreams.Add(i); | |
break; | |
} | |
if (folder.PackStreams.Size() != 1) | |
ThrowUnsupported(); | |
} | |
else | |
for (i = 0; i < numPackStreams; i++) | |
folder.PackStreams.Add(ReadNum()); | |
} | |
void CInArchive::WaitAttribute(UInt64 attribute) | |
{ | |
for (;;) | |
{ | |
UInt64 type = ReadID(); | |
if (type == attribute) | |
return; | |
if (type == NID::kEnd) | |
ThrowIncorrect(); | |
SkipData(); | |
} | |
} | |
void CInArchive::ReadHashDigests(int numItems, | |
CBoolVector &digestsDefined, | |
CRecordVector<UInt32> &digests) | |
{ | |
ReadBoolVector2(numItems, digestsDefined); | |
digests.Clear(); | |
digests.Reserve(numItems); | |
for (int i = 0; i < numItems; i++) | |
{ | |
UInt32 crc = 0; | |
if (digestsDefined[i]) | |
crc = ReadUInt32(); | |
digests.Add(crc); | |
} | |
} | |
void CInArchive::ReadPackInfo( | |
UInt64 &dataOffset, | |
CRecordVector<UInt64> &packSizes, | |
CBoolVector &packCRCsDefined, | |
CRecordVector<UInt32> &packCRCs) | |
{ | |
dataOffset = ReadNumber(); | |
CNum numPackStreams = ReadNum(); | |
WaitAttribute(NID::kSize); | |
packSizes.Clear(); | |
packSizes.Reserve(numPackStreams); | |
for (CNum i = 0; i < numPackStreams; i++) | |
packSizes.Add(ReadNumber()); | |
UInt64 type; | |
for (;;) | |
{ | |
type = ReadID(); | |
if (type == NID::kEnd) | |
break; | |
if (type == NID::kCRC) | |
{ | |
ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs); | |
continue; | |
} | |
SkipData(); | |
} | |
if (packCRCsDefined.IsEmpty()) | |
{ | |
BoolVector_Fill_False(packCRCsDefined, numPackStreams); | |
packCRCs.Reserve(numPackStreams); | |
packCRCs.Clear(); | |
for (CNum i = 0; i < numPackStreams; i++) | |
packCRCs.Add(0); | |
} | |
} | |
void CInArchive::ReadUnpackInfo( | |
const CObjectVector<CByteBuffer> *dataVector, | |
CObjectVector<CFolder> &folders) | |
{ | |
WaitAttribute(NID::kFolder); | |
CNum numFolders = ReadNum(); | |
{ | |
CStreamSwitch streamSwitch; | |
streamSwitch.Set(this, dataVector); | |
folders.Clear(); | |
folders.Reserve(numFolders); | |
for (CNum i = 0; i < numFolders; i++) | |
{ | |
folders.Add(CFolder()); | |
GetNextFolderItem(folders.Back()); | |
} | |
} | |
WaitAttribute(NID::kCodersUnpackSize); | |
CNum i; | |
for (i = 0; i < numFolders; i++) | |
{ | |
CFolder &folder = folders[i]; | |
CNum numOutStreams = folder.GetNumOutStreams(); | |
folder.UnpackSizes.Reserve(numOutStreams); | |
for (CNum j = 0; j < numOutStreams; j++) | |
folder.UnpackSizes.Add(ReadNumber()); | |
} | |
for (;;) | |
{ | |
UInt64 type = ReadID(); | |
if (type == NID::kEnd) | |
return; | |
if (type == NID::kCRC) | |
{ | |
CBoolVector crcsDefined; | |
CRecordVector<UInt32> crcs; | |
ReadHashDigests(numFolders, crcsDefined, crcs); | |
for (i = 0; i < numFolders; i++) | |
{ | |
CFolder &folder = folders[i]; | |
folder.UnpackCRCDefined = crcsDefined[i]; | |
folder.UnpackCRC = crcs[i]; | |
} | |
continue; | |
} | |
SkipData(); | |
} | |
} | |
void CInArchive::ReadSubStreamsInfo( | |
const CObjectVector<CFolder> &folders, | |
CRecordVector<CNum> &numUnpackStreamsInFolders, | |
CRecordVector<UInt64> &unpackSizes, | |
CBoolVector &digestsDefined, | |
CRecordVector<UInt32> &digests) | |
{ | |
numUnpackStreamsInFolders.Clear(); | |
numUnpackStreamsInFolders.Reserve(folders.Size()); | |
UInt64 type; | |
for (;;) | |
{ | |
type = ReadID(); | |
if (type == NID::kNumUnpackStream) | |
{ | |
for (int i = 0; i < folders.Size(); i++) | |
numUnpackStreamsInFolders.Add(ReadNum()); | |
continue; | |
} | |
if (type == NID::kCRC || type == NID::kSize) | |
break; | |
if (type == NID::kEnd) | |
break; | |
SkipData(); | |
} | |
if (numUnpackStreamsInFolders.IsEmpty()) | |
for (int i = 0; i < folders.Size(); i++) | |
numUnpackStreamsInFolders.Add(1); | |
int i; | |
for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) | |
{ | |
// v3.13 incorrectly worked with empty folders | |
// v4.07: we check that folder is empty | |
CNum numSubstreams = numUnpackStreamsInFolders[i]; | |
if (numSubstreams == 0) | |
continue; | |
UInt64 sum = 0; | |
for (CNum j = 1; j < numSubstreams; j++) | |
if (type == NID::kSize) | |
{ | |
UInt64 size = ReadNumber(); | |
unpackSizes.Add(size); | |
sum += size; | |
} | |
unpackSizes.Add(folders[i].GetUnpackSize() - sum); | |
} | |
if (type == NID::kSize) | |
type = ReadID(); | |
int numDigests = 0; | |
int numDigestsTotal = 0; | |
for (i = 0; i < folders.Size(); i++) | |
{ | |
CNum numSubstreams = numUnpackStreamsInFolders[i]; | |
if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) | |
numDigests += numSubstreams; | |
numDigestsTotal += numSubstreams; | |
} | |
for (;;) | |
{ | |
if (type == NID::kCRC) | |
{ | |
CBoolVector digestsDefined2; | |
CRecordVector<UInt32> digests2; | |
ReadHashDigests(numDigests, digestsDefined2, digests2); | |
int digestIndex = 0; | |
for (i = 0; i < folders.Size(); i++) | |
{ | |
CNum numSubstreams = numUnpackStreamsInFolders[i]; | |
const CFolder &folder = folders[i]; | |
if (numSubstreams == 1 && folder.UnpackCRCDefined) | |
{ | |
digestsDefined.Add(true); | |
digests.Add(folder.UnpackCRC); | |
} | |
else | |
for (CNum j = 0; j < numSubstreams; j++, digestIndex++) | |
{ | |
digestsDefined.Add(digestsDefined2[digestIndex]); | |
digests.Add(digests2[digestIndex]); | |
} | |
} | |
} | |
else if (type == NID::kEnd) | |
{ | |
if (digestsDefined.IsEmpty()) | |
{ | |
BoolVector_Fill_False(digestsDefined, numDigestsTotal); | |
digests.Clear(); | |
for (int i = 0; i < numDigestsTotal; i++) | |
digests.Add(0); | |
} | |
return; | |
} | |
else | |
SkipData(); | |
type = ReadID(); | |
} | |
} | |
void CInArchive::ReadStreamsInfo( | |
const CObjectVector<CByteBuffer> *dataVector, | |
UInt64 &dataOffset, | |
CRecordVector<UInt64> &packSizes, | |
CBoolVector &packCRCsDefined, | |
CRecordVector<UInt32> &packCRCs, | |
CObjectVector<CFolder> &folders, | |
CRecordVector<CNum> &numUnpackStreamsInFolders, | |
CRecordVector<UInt64> &unpackSizes, | |
CBoolVector &digestsDefined, | |
CRecordVector<UInt32> &digests) | |
{ | |
for (;;) | |
{ | |
UInt64 type = ReadID(); | |
if (type > ((UInt32)1 << 30)) | |
ThrowIncorrect(); | |
switch((UInt32)type) | |
{ | |
case NID::kEnd: | |
return; | |
case NID::kPackInfo: | |
{ | |
ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs); | |
break; | |
} | |
case NID::kUnpackInfo: | |
{ | |
ReadUnpackInfo(dataVector, folders); | |
break; | |
} | |
case NID::kSubStreamsInfo: | |
{ | |
ReadSubStreamsInfo(folders, numUnpackStreamsInFolders, | |
unpackSizes, digestsDefined, digests); | |
break; | |
} | |
default: | |
ThrowIncorrect(); | |
} | |
} | |
} | |
void CInArchive::ReadBoolVector(int numItems, CBoolVector &v) | |
{ | |
v.Clear(); | |
v.Reserve(numItems); | |
Byte b = 0; | |
Byte mask = 0; | |
for (int i = 0; i < numItems; i++) | |
{ | |
if (mask == 0) | |
{ | |
b = ReadByte(); | |
mask = 0x80; | |
} | |
v.Add((b & mask) != 0); | |
mask >>= 1; | |
} | |
} | |
void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v) | |
{ | |
Byte allAreDefined = ReadByte(); | |
if (allAreDefined == 0) | |
{ | |
ReadBoolVector(numItems, v); | |
return; | |
} | |
v.Clear(); | |
v.Reserve(numItems); | |
for (int i = 0; i < numItems; i++) | |
v.Add(true); | |
} | |
void CInArchive::ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector, | |
CUInt64DefVector &v, int numFiles) | |
{ | |
ReadBoolVector2(numFiles, v.Defined); | |
CStreamSwitch streamSwitch; | |
streamSwitch.Set(this, &dataVector); | |
v.Values.Reserve(numFiles); | |
for (int i = 0; i < numFiles; i++) | |
{ | |
UInt64 t = 0; | |
if (v.Defined[i]) | |
t = ReadUInt64(); | |
v.Values.Add(t); | |
} | |
} | |
HRESULT CInArchive::ReadAndDecodePackedStreams( | |
DECL_EXTERNAL_CODECS_LOC_VARS | |
UInt64 baseOffset, | |
UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector | |
#ifndef _NO_CRYPTO | |
, ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined | |
#endif | |
) | |
{ | |
CRecordVector<UInt64> packSizes; | |
CBoolVector packCRCsDefined; | |
CRecordVector<UInt32> packCRCs; | |
CObjectVector<CFolder> folders; | |
CRecordVector<CNum> numUnpackStreamsInFolders; | |
CRecordVector<UInt64> unpackSizes; | |
CBoolVector digestsDefined; | |
CRecordVector<UInt32> digests; | |
ReadStreamsInfo(NULL, | |
dataOffset, | |
packSizes, | |
packCRCsDefined, | |
packCRCs, | |
folders, | |
numUnpackStreamsInFolders, | |
unpackSizes, | |
digestsDefined, | |
digests); | |
// db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader; | |
CNum packIndex = 0; | |
CDecoder decoder( | |
#ifdef _ST_MODE | |
false | |
#else | |
true | |
#endif | |
); | |
UInt64 dataStartPos = baseOffset + dataOffset; | |
for (int i = 0; i < folders.Size(); i++) | |
{ | |
const CFolder &folder = folders[i]; | |
dataVector.Add(CByteBuffer()); | |
CByteBuffer &data = dataVector.Back(); | |
UInt64 unpackSize64 = folder.GetUnpackSize(); | |
size_t unpackSize = (size_t)unpackSize64; | |
if (unpackSize != unpackSize64) | |
ThrowUnsupported(); | |
data.SetCapacity(unpackSize); | |
CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; | |
CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; | |
outStreamSpec->Init(data, unpackSize); | |
HRESULT result = decoder.Decode( | |
EXTERNAL_CODECS_LOC_VARS | |
_stream, dataStartPos, | |
&packSizes[packIndex], folder, outStream, NULL | |
#ifndef _NO_CRYPTO | |
, getTextPassword, passwordIsDefined | |
#endif | |
#if !defined(_7ZIP_ST) && !defined(_SFX) | |
, false, 1 | |
#endif | |
); | |
RINOK(result); | |
if (folder.UnpackCRCDefined) | |
if (CrcCalc(data, unpackSize) != folder.UnpackCRC) | |
ThrowIncorrect(); | |
for (int j = 0; j < folder.PackStreams.Size(); j++) | |
{ | |
UInt64 packSize = packSizes[packIndex++]; | |
dataStartPos += packSize; | |
HeadersSize += packSize; | |
} | |
} | |
return S_OK; | |
} | |
HRESULT CInArchive::ReadHeader( | |
DECL_EXTERNAL_CODECS_LOC_VARS | |
CArchiveDatabaseEx &db | |
#ifndef _NO_CRYPTO | |
, ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined | |
#endif | |
) | |
{ | |
UInt64 type = ReadID(); | |
if (type == NID::kArchiveProperties) | |
{ | |
ReadArchiveProperties(db.ArchiveInfo); | |
type = ReadID(); | |
} | |
CObjectVector<CByteBuffer> dataVector; | |
if (type == NID::kAdditionalStreamsInfo) | |
{ | |
HRESULT result = ReadAndDecodePackedStreams( | |
EXTERNAL_CODECS_LOC_VARS | |
db.ArchiveInfo.StartPositionAfterHeader, | |
db.ArchiveInfo.DataStartPosition2, | |
dataVector | |
#ifndef _NO_CRYPTO | |
, getTextPassword, passwordIsDefined | |
#endif | |
); | |
RINOK(result); | |
db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader; | |
type = ReadID(); | |
} | |
CRecordVector<UInt64> unpackSizes; | |
CBoolVector digestsDefined; | |
CRecordVector<UInt32> digests; | |
if (type == NID::kMainStreamsInfo) | |
{ | |
ReadStreamsInfo(&dataVector, | |
db.ArchiveInfo.DataStartPosition, | |
db.PackSizes, | |
db.PackCRCsDefined, | |
db.PackCRCs, | |
db.Folders, | |
db.NumUnpackStreamsVector, | |
unpackSizes, | |
digestsDefined, | |
digests); | |
db.ArchiveInfo.DataStartPosition += db.ArchiveInfo.StartPositionAfterHeader; | |
type = ReadID(); | |
} | |
else | |
{ | |
for (int i = 0; i < db.Folders.Size(); i++) | |
{ | |
db.NumUnpackStreamsVector.Add(1); | |
CFolder &folder = db.Folders[i]; | |
unpackSizes.Add(folder.GetUnpackSize()); | |
digestsDefined.Add(folder.UnpackCRCDefined); | |
digests.Add(folder.UnpackCRC); | |
} | |
} | |
db.Files.Clear(); | |
if (type == NID::kEnd) | |
return S_OK; | |
if (type != NID::kFilesInfo) | |
ThrowIncorrect(); | |
CNum numFiles = ReadNum(); | |
db.Files.Reserve(numFiles); | |
CNum i; | |
for (i = 0; i < numFiles; i++) | |
db.Files.Add(CFileItem()); | |
db.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize); | |
if (!db.PackSizes.IsEmpty()) | |
db.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo); | |
if (numFiles > 0 && !digests.IsEmpty()) | |
db.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC); | |
CBoolVector emptyStreamVector; | |
BoolVector_Fill_False(emptyStreamVector, (int)numFiles); | |
CBoolVector emptyFileVector; | |
CBoolVector antiFileVector; | |
CNum numEmptyStreams = 0; | |
for (;;) | |
{ | |
UInt64 type = ReadID(); | |
if (type == NID::kEnd) | |
break; | |
UInt64 size = ReadNumber(); | |
size_t ppp = _inByteBack->_pos; | |
bool addPropIdToList = true; | |
bool isKnownType = true; | |
if (type > ((UInt32)1 << 30)) | |
isKnownType = false; | |
else switch((UInt32)type) | |
{ | |
case NID::kName: | |
{ | |
CStreamSwitch streamSwitch; | |
streamSwitch.Set(this, &dataVector); | |
for (int i = 0; i < db.Files.Size(); i++) | |
_inByteBack->ReadString(db.Files[i].Name); | |
break; | |
} | |
case NID::kWinAttributes: | |
{ | |
CBoolVector boolVector; | |
ReadBoolVector2(db.Files.Size(), boolVector); | |
CStreamSwitch streamSwitch; | |
streamSwitch.Set(this, &dataVector); | |
for (i = 0; i < numFiles; i++) | |
{ | |
CFileItem &file = db.Files[i]; | |
file.AttribDefined = boolVector[i]; | |
if (file.AttribDefined) | |
file.Attrib = ReadUInt32(); | |
} | |
break; | |
} | |
case NID::kEmptyStream: | |
{ | |
ReadBoolVector(numFiles, emptyStreamVector); | |
for (i = 0; i < (CNum)emptyStreamVector.Size(); i++) | |
if (emptyStreamVector[i]) | |
numEmptyStreams++; | |
BoolVector_Fill_False(emptyFileVector, numEmptyStreams); | |
BoolVector_Fill_False(antiFileVector, numEmptyStreams); | |
break; | |
} | |
case NID::kEmptyFile: ReadBoolVector(numEmptyStreams, emptyFileVector); break; | |
case NID::kAnti: ReadBoolVector(numEmptyStreams, antiFileVector); break; | |
case NID::kStartPos: ReadUInt64DefVector(dataVector, db.StartPos, (int)numFiles); break; | |
case NID::kCTime: ReadUInt64DefVector(dataVector, db.CTime, (int)numFiles); break; | |
case NID::kATime: ReadUInt64DefVector(dataVector, db.ATime, (int)numFiles); break; | |
case NID::kMTime: ReadUInt64DefVector(dataVector, db.MTime, (int)numFiles); break; | |
case NID::kDummy: | |
{ | |
for (UInt64 j = 0; j < size; j++) | |
if (ReadByte() != 0) | |
ThrowIncorrect(); | |
addPropIdToList = false; | |
break; | |
} | |
default: | |
addPropIdToList = isKnownType = false; | |
} | |
if (isKnownType) | |
{ | |
if(addPropIdToList) | |
db.ArchiveInfo.FileInfoPopIDs.Add(type); | |
} | |
else | |
SkipData(size); | |
bool checkRecordsSize = (db.ArchiveInfo.Version.Major > 0 || | |
db.ArchiveInfo.Version.Minor > 2); | |
if (checkRecordsSize && _inByteBack->_pos - ppp != size) | |
ThrowIncorrect(); | |
} | |
CNum emptyFileIndex = 0; | |
CNum sizeIndex = 0; | |
CNum numAntiItems = 0; | |
for (i = 0; i < numEmptyStreams; i++) | |
if (antiFileVector[i]) | |
numAntiItems++; | |
for (i = 0; i < numFiles; i++) | |
{ | |
CFileItem &file = db.Files[i]; | |
bool isAnti; | |
file.HasStream = !emptyStreamVector[i]; | |
if (file.HasStream) | |
{ | |
file.IsDir = false; | |
isAnti = false; | |
file.Size = unpackSizes[sizeIndex]; | |
file.Crc = digests[sizeIndex]; | |
file.CrcDefined = digestsDefined[sizeIndex]; | |
sizeIndex++; | |
} | |
else | |
{ | |
file.IsDir = !emptyFileVector[emptyFileIndex]; | |
isAnti = antiFileVector[emptyFileIndex]; | |
emptyFileIndex++; | |
file.Size = 0; | |
file.CrcDefined = false; | |
} | |
if (numAntiItems != 0) | |
db.IsAnti.Add(isAnti); | |
} | |
return S_OK; | |
} | |
void CArchiveDatabaseEx::FillFolderStartPackStream() | |
{ | |
FolderStartPackStreamIndex.Clear(); | |
FolderStartPackStreamIndex.Reserve(Folders.Size()); | |
CNum startPos = 0; | |
for (int i = 0; i < Folders.Size(); i++) | |
{ | |
FolderStartPackStreamIndex.Add(startPos); | |
startPos += (CNum)Folders[i].PackStreams.Size(); | |
} | |
} | |
void CArchiveDatabaseEx::FillStartPos() | |
{ | |
PackStreamStartPositions.Clear(); | |
PackStreamStartPositions.Reserve(PackSizes.Size()); | |
UInt64 startPos = 0; | |
for (int i = 0; i < PackSizes.Size(); i++) | |
{ | |
PackStreamStartPositions.Add(startPos); | |
startPos += PackSizes[i]; | |
} | |
} | |
void CArchiveDatabaseEx::FillFolderStartFileIndex() | |
{ | |
FolderStartFileIndex.Clear(); | |
FolderStartFileIndex.Reserve(Folders.Size()); | |
FileIndexToFolderIndexMap.Clear(); | |
FileIndexToFolderIndexMap.Reserve(Files.Size()); | |
int folderIndex = 0; | |
CNum indexInFolder = 0; | |
for (int i = 0; i < Files.Size(); i++) | |
{ | |
const CFileItem &file = Files[i]; | |
bool emptyStream = !file.HasStream; | |
if (emptyStream && indexInFolder == 0) | |
{ | |
FileIndexToFolderIndexMap.Add(kNumNoIndex); | |
continue; | |
} | |
if (indexInFolder == 0) | |
{ | |
// v3.13 incorrectly worked with empty folders | |
// v4.07: Loop for skipping empty folders | |
for (;;) | |
{ | |
if (folderIndex >= Folders.Size()) | |
ThrowIncorrect(); | |
FolderStartFileIndex.Add(i); // check it | |
if (NumUnpackStreamsVector[folderIndex] != 0) | |
break; | |
folderIndex++; | |
} | |
} | |
FileIndexToFolderIndexMap.Add(folderIndex); | |
if (emptyStream) | |
continue; | |
indexInFolder++; | |
if (indexInFolder >= NumUnpackStreamsVector[folderIndex]) | |
{ | |
folderIndex++; | |
indexInFolder = 0; | |
} | |
} | |
} | |
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 CInArchive::ReadDatabase( | |
DECL_EXTERNAL_CODECS_LOC_VARS | |
CArchiveDatabaseEx &db | |
#ifndef _NO_CRYPTO | |
, ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined | |
#endif | |
) | |
{ | |
try | |
{ | |
return ReadDatabase2( | |
EXTERNAL_CODECS_LOC_VARS db | |
#ifndef _NO_CRYPTO | |
, getTextPassword, passwordIsDefined | |
#endif | |
); | |
} | |
catch(CInArchiveException &) { return S_FALSE; } | |
} | |
}} |