// 7zIn.cpp | |
#include "StdAfx.h" | |
#ifdef _WIN32 | |
#include <wchar.h> | |
#else | |
#include <ctype.h> | |
#endif | |
#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 | |
using namespace NWindows; | |
using namespace NCOM; | |
namespace NArchive { | |
namespace N7z { | |
static const UInt32 k_LZMA2 = 0x21; | |
static const UInt32 k_LZMA = 0x030101; | |
static void BoolVector_Fill_False(CBoolVector &v, unsigned size) | |
{ | |
v.ClearAndSetSize(size); | |
bool *p = &v[0]; | |
for (unsigned i = 0; i < size; i++) | |
p[i] = 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(unsigned numUnpackSizes) const | |
{ | |
const unsigned kNumCodersMax = sizeof(UInt32) * 8; // don't change it | |
const unsigned kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax | |
const unsigned kNumBindsMax = 32; | |
if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax) | |
return false; | |
{ | |
CBoolVector v; | |
BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size()); | |
unsigned 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, numUnpackSizes); | |
for (i = 0; i < BindPairs.Size(); i++) | |
if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex)) | |
return false; | |
} | |
UInt32 mask[kMaskSize]; | |
unsigned i; | |
for (i = 0; i < kMaskSize; i++) | |
mask[i] = 0; | |
{ | |
CUIntVector 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 (unsigned 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 {}; | |
class CUnsupportedFeatureException: public CInArchiveException {}; | |
static void ThrowException() { throw CInArchiveException(); } | |
static inline void ThrowEndOfData() { ThrowException(); } | |
static inline void ThrowUnsupported() { throw CUnsupportedFeatureException(); } | |
static inline void ThrowIncorrect() { ThrowException(); } | |
class CStreamSwitch | |
{ | |
CInArchive *_archive; | |
bool _needRemove; | |
bool _needUpdatePos; | |
public: | |
CStreamSwitch(): _needRemove(false), _needUpdatePos(false) {} | |
~CStreamSwitch() { Remove(); } | |
void Remove(); | |
void Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos); | |
void Set(CInArchive *archive, const CByteBuffer &byteBuffer); | |
void Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector); | |
}; | |
void CStreamSwitch::Remove() | |
{ | |
if (_needRemove) | |
{ | |
if (_archive->_inByteBack->GetRem() != 0) | |
_archive->ThereIsHeaderError = true; | |
_archive->DeleteByteStream(_needUpdatePos); | |
_needRemove = false; | |
} | |
} | |
void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos) | |
{ | |
Remove(); | |
_archive = archive; | |
_archive->AddByteStream(data, size); | |
_needRemove = true; | |
_needUpdatePos = needUpdatePos; | |
} | |
void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer) | |
{ | |
Set(archive, byteBuffer, byteBuffer.Size(), false); | |
} | |
void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector) | |
{ | |
Remove(); | |
Byte external = archive->ReadByte(); | |
if (external != 0) | |
{ | |
CNum dataIndex = archive->ReadNum(); | |
if (dataIndex >= dataVector->Size()) | |
ThrowIncorrect(); | |
Set(archive, (*dataVector)[dataIndex]); | |
} | |
} | |
void CInArchive::AddByteStream(const Byte *buf, size_t size) | |
{ | |
if (_numInByteBufs == kNumBufLevelsMax) | |
ThrowIncorrect(); | |
_inByteBack = &_inByteVector[_numInByteBufs++]; | |
_inByteBack->Init(buf, size); | |
} | |
Byte CInByte2::ReadByte() | |
{ | |
if (_pos >= _size) | |
ThrowEndOfData(); | |
return _buffer[_pos++]; | |
} | |
void CInByte2::ReadBytes(Byte *data, size_t size) | |
{ | |
if (size > _size - _pos) | |
ThrowEndOfData(); | |
memcpy(data, _buffer + _pos, size); | |
_pos += size; | |
} | |
void CInByte2::SkipData(UInt64 size) | |
{ | |
if (size > _size - _pos) | |
ThrowEndOfData(); | |
_pos += (size_t)size; | |
} | |
void CInByte2::SkipData() | |
{ | |
SkipData(ReadNumber()); | |
} | |
static UInt64 ReadNumberSpec(const Byte *p, size_t size, size_t &processed) | |
{ | |
if (size == 0) | |
{ | |
processed = 0; | |
return 0; | |
} | |
Byte firstByte = *p++; | |
size--; | |
if ((firstByte & 0x80) == 0) | |
{ | |
processed = 1; | |
return firstByte; | |
} | |
Byte mask = 0x40; | |
if (size == 0) | |
{ | |
processed = 0; | |
return 0; | |
} | |
UInt64 value = (UInt64)*p; | |
p++; | |
size--; | |
for (unsigned i = 1; i < 8; i++) | |
{ | |
if ((firstByte & mask) == 0) | |
{ | |
UInt64 highPart = firstByte & (mask - 1); | |
value += (highPart << (i * 8)); | |
processed = i + 1; | |
return value; | |
} | |
if (size == 0) | |
{ | |
processed = 0; | |
return 0; | |
} | |
value |= ((UInt64)*p << (i * 8)); | |
p++; | |
size--; | |
mask >>= 1; | |
} | |
processed = 9; | |
return value; | |
} | |
UInt64 CInByte2::ReadNumber() | |
{ | |
size_t processed; | |
UInt64 res = ReadNumberSpec(_buffer + _pos, _size - _pos, processed); | |
if (processed == 0) | |
ThrowEndOfData(); | |
_pos += processed; | |
return res; | |
} | |
CNum CInByte2::ReadNum() | |
{ | |
/* | |
if (_pos < _size) | |
{ | |
Byte val = _buffer[_pos]; | |
if ((unsigned)val < 0x80) | |
{ | |
_pos++; | |
return (unsigned)val; | |
} | |
} | |
*/ | |
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; | |
} | |
#define CHECK_SIGNATURE if (p[0] != '7' || p[1] != 'z' || p[2] != 0xBC || p[3] != 0xAF || p[4] != 0x27 || p[5] != 0x1C) return false; | |
static inline bool TestSignature(const Byte *p) | |
{ | |
CHECK_SIGNATURE | |
return CrcCalc(p + 12, 20) == Get32(p + 8); | |
} | |
#ifdef FORMAT_7Z_RECOVERY | |
static inline bool TestSignature2(const Byte *p) | |
{ | |
CHECK_SIGNATURE; | |
if (CrcCalc(p + 12, 20) == Get32(p + 8)) | |
return true; | |
for (unsigned 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; | |
if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0) | |
return S_FALSE; | |
const UInt32 kBufSize = 1 << 15; | |
CByteArr buf(kBufSize); | |
memcpy(buf, _header, kHeaderSize); | |
UInt64 offset = 0; | |
for (;;) | |
{ | |
UInt32 readSize = kBufSize - kHeaderSize; | |
{ | |
UInt64 rem = *searchHeaderSizeLimit - offset; | |
if (readSize > rem) | |
readSize = (UInt32)rem; | |
if (readSize == 0) | |
return S_FALSE; | |
} | |
UInt32 processed = 0; | |
RINOK(stream->Read(buf + kHeaderSize, readSize, &processed)); | |
if (processed == 0) | |
return S_FALSE; | |
for (UInt32 pos = 0;;) | |
{ | |
const Byte *p = buf + pos + 1; | |
const Byte *lim = buf + processed; | |
for (; p <= lim; p += 4) | |
{ | |
if (p[0] == '7') break; | |
if (p[1] == '7') { p += 1; break; } | |
if (p[2] == '7') { p += 2; break; } | |
if (p[3] == '7') { p += 3; break; } | |
}; | |
if (p > lim) | |
break; | |
pos = (UInt32)(p - buf); | |
if (TestSignature(p)) | |
{ | |
memcpy(_header, p, kHeaderSize); | |
_arhiveBeginStreamPosition += offset + pos; | |
return stream->Seek(_arhiveBeginStreamPosition + kHeaderSize, STREAM_SEEK_SET, NULL); | |
} | |
} | |
offset += processed; | |
memmove(buf, buf + processed, kHeaderSize); | |
} | |
} | |
// 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(stream->Seek(0, STREAM_SEEK_END, &_fileEndPosition)) | |
RINOK(stream->Seek(_arhiveBeginStreamPosition, STREAM_SEEK_SET, NULL)) | |
RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); | |
_stream = stream; | |
return S_OK; | |
} | |
void CInArchive::Close() | |
{ | |
_numInByteBufs = 0; | |
_stream.Release(); | |
ThereIsHeaderError = false; | |
} | |
void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) | |
{ | |
for (;;) | |
{ | |
if (ReadID() == NID::kEnd) | |
break; | |
SkipData(); | |
} | |
} | |
// CFolder &folder can be non empty. So we must set all fields | |
void CInByte2::ParseFolder(CFolder &folder) | |
{ | |
CNum numCoders = ReadNum(); | |
folder.Coders.SetSize(numCoders); | |
CNum numInStreams = 0; | |
CNum numOutStreams = 0; | |
CNum i; | |
for (i = 0; i < numCoders; i++) | |
{ | |
CCoderInfo &coder = folder.Coders[i]; | |
{ | |
Byte mainByte = ReadByte(); | |
if ((mainByte & 0xC0) != 0) | |
ThrowUnsupported(); | |
unsigned idSize = (mainByte & 0xF); | |
if (idSize > 8 || idSize > GetRem()) | |
ThrowUnsupported(); | |
const Byte *longID = GetPtr(); | |
UInt64 id = 0; | |
for (unsigned j = 0; j < idSize; j++) | |
id = ((id << 8) | longID[j]); | |
SkipDataNoCheck(idSize); | |
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.Alloc((size_t)propsSize); | |
ReadBytes((Byte *)coder.Props, (size_t)propsSize); | |
} | |
else | |
coder.Props.Free(); | |
} | |
numInStreams += coder.NumInStreams; | |
numOutStreams += coder.NumOutStreams; | |
} | |
CNum numBindPairs = numOutStreams - 1; | |
folder.BindPairs.SetSize(numBindPairs); | |
for (i = 0; i < numBindPairs; i++) | |
{ | |
CBindPair &bp = folder.BindPairs[i]; | |
bp.InIndex = ReadNum(); | |
bp.OutIndex = ReadNum(); | |
} | |
if (numInStreams < numBindPairs) | |
ThrowUnsupported(); | |
CNum numPackStreams = numInStreams - numBindPairs; | |
folder.PackStreams.SetSize(numPackStreams); | |
if (numPackStreams == 1) | |
{ | |
for (i = 0; i < numInStreams; i++) | |
if (folder.FindBindPairForInStream(i) < 0) | |
{ | |
folder.PackStreams[0] = i; | |
break; | |
} | |
if (i == numInStreams) | |
ThrowUnsupported(); | |
} | |
else | |
for (i = 0; i < numPackStreams; i++) | |
folder.PackStreams[i] = ReadNum(); | |
} | |
void CFolders::ParseFolderInfo(unsigned folderIndex, CFolder &folder) const | |
{ | |
size_t startPos = FoCodersDataOffset[folderIndex]; | |
CInByte2 inByte; | |
inByte.Init(CodersData + startPos, FoCodersDataOffset[folderIndex + 1] - startPos); | |
inByte.ParseFolder(folder); | |
if (inByte.GetRem() != 0) | |
throw 20120424; | |
} | |
void CDatabase::GetPath(unsigned index, UString &path) const | |
{ | |
path.Empty(); | |
if (!NameOffsets || !NamesBuf) | |
return; | |
size_t offset = NameOffsets[index]; | |
size_t size = NameOffsets[index + 1] - offset - 1; | |
if (size >= (1 << 20)) | |
return; | |
wchar_t *s = path.GetBuffer((unsigned)size); | |
const Byte *p = ((const Byte *)NamesBuf + offset * 2); | |
#if defined(_WIN32) && defined(MY_CPU_LE) | |
wmemcpy(s, (const wchar_t *)p, size); | |
#else | |
for (size_t i = 0; i < size; i++) | |
{ | |
*s = Get16(p); | |
p += 2; | |
s++; | |
} | |
#endif | |
path.ReleaseBuffer((unsigned)size); | |
} | |
HRESULT CDatabase::GetPath_Prop(unsigned index, PROPVARIANT *path) const throw() | |
{ | |
PropVariant_Clear(path); | |
if (!NameOffsets || !NamesBuf) | |
return S_OK; | |
size_t offset = NameOffsets[index]; | |
size_t size = NameOffsets[index + 1] - offset; | |
if (size >= (1 << 14)) | |
return S_OK; | |
RINOK(PropVarEm_Alloc_Bstr(path, (unsigned)size - 1)); | |
wchar_t *s = path->bstrVal; | |
const Byte *p = ((const Byte *)NamesBuf + offset * 2); | |
for (size_t i = 0; i < size; i++) | |
{ | |
wchar_t c = Get16(p); | |
p += 2; | |
#if WCHAR_PATH_SEPARATOR != L'/' | |
if (c == L'/') | |
c = WCHAR_PATH_SEPARATOR; | |
#endif | |
*s++ = c; | |
} | |
return S_OK; | |
/* | |
unsigned cur = index; | |
unsigned size = 0; | |
for (int i = 0;; i++) | |
{ | |
size_t len = NameOffsets[cur + 1] - NameOffsets[cur]; | |
size += (unsigned)len; | |
if (i > 256 || len > (1 << 14) || size > (1 << 14)) | |
return PropVarEm_Set_Str(path, "[TOO-LONG]"); | |
cur = Files[cur].Parent; | |
if (cur < 0) | |
break; | |
} | |
size--; | |
RINOK(PropVarEm_Alloc_Bstr(path, size)); | |
wchar_t *s = path->bstrVal; | |
s += size; | |
*s = 0; | |
cur = index; | |
for (;;) | |
{ | |
unsigned len = (unsigned)(NameOffsets[cur + 1] - NameOffsets[cur] - 1); | |
const Byte *p = (const Byte *)NamesBuf + (NameOffsets[cur + 1] * 2) - 2; | |
do | |
{ | |
p -= 2; | |
--s; | |
wchar_t c = Get16(p); | |
if (c == '/') | |
c = WCHAR_PATH_SEPARATOR; | |
*s = c; | |
} | |
while (--len); | |
const CFileItem &file = Files[cur]; | |
cur = file.Parent; | |
if (cur < 0) | |
return S_OK; | |
*(--s) = (file.IsAltStream ? ':' : WCHAR_PATH_SEPARATOR); | |
} | |
*/ | |
} | |
void CInArchive::WaitId(UInt64 id) | |
{ | |
for (;;) | |
{ | |
UInt64 type = ReadID(); | |
if (type == id) | |
return; | |
if (type == NID::kEnd) | |
ThrowIncorrect(); | |
SkipData(); | |
} | |
} | |
void CInArchive::ReadHashDigests(unsigned numItems, CUInt32DefVector &crcs) | |
{ | |
ReadBoolVector2(numItems, crcs.Defs); | |
crcs.Vals.ClearAndSetSize(numItems); | |
UInt32 *p = &crcs.Vals[0]; | |
const bool *defs = &crcs.Defs[0]; | |
for (unsigned i = 0; i < numItems; i++) | |
{ | |
UInt32 crc = 0; | |
if (defs[i]) | |
crc = ReadUInt32(); | |
p[i] = crc; | |
} | |
} | |
void CInArchive::ReadPackInfo(CFolders &f) | |
{ | |
CNum numPackStreams = ReadNum(); | |
WaitId(NID::kSize); | |
f.PackPositions.Alloc(numPackStreams + 1); | |
f.NumPackStreams = numPackStreams; | |
UInt64 sum = 0; | |
for (CNum i = 0; i < numPackStreams; i++) | |
{ | |
f.PackPositions[i] = sum; | |
UInt64 packSize = ReadNumber(); | |
sum += packSize; | |
if (sum < packSize) | |
ThrowIncorrect(); | |
} | |
f.PackPositions[numPackStreams] = sum; | |
UInt64 type; | |
for (;;) | |
{ | |
type = ReadID(); | |
if (type == NID::kEnd) | |
return; | |
if (type == NID::kCRC) | |
{ | |
CUInt32DefVector PackCRCs; | |
ReadHashDigests(numPackStreams, PackCRCs); | |
continue; | |
} | |
SkipData(); | |
} | |
} | |
void CInArchive::ReadUnpackInfo( | |
const CObjectVector<CByteBuffer> *dataVector, | |
CFolders &folders) | |
{ | |
WaitId(NID::kFolder); | |
CNum numFolders = ReadNum(); | |
CNum numCodersOutStreams = 0; | |
{ | |
CStreamSwitch streamSwitch; | |
streamSwitch.Set(this, dataVector); | |
const Byte *startBufPtr = _inByteBack->GetPtr(); | |
folders.NumFolders = numFolders; | |
folders.FoStartPackStreamIndex.Alloc(numFolders + 1); | |
folders.FoToMainUnpackSizeIndex.Alloc(numFolders); | |
folders.FoCodersDataOffset.Alloc(numFolders + 1); | |
folders.FoToCoderUnpackSizes.Alloc(numFolders + 1); | |
CRecordVector<bool> InStreamUsed; | |
CRecordVector<bool> OutStreamUsed; | |
CNum packStreamIndex = 0; | |
CNum fo; | |
CInByte2 *inByte = _inByteBack; | |
for (fo = 0; fo < numFolders; fo++) | |
{ | |
UInt32 numOutStreams = 0; | |
UInt32 indexOfMainStream = 0; | |
UInt32 numPackStreams = 0; | |
folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr; | |
numOutStreams = 0; | |
CNum numInStreams = 0; | |
CNum numCoders = inByte->ReadNum(); | |
for (CNum ci = 0; ci < numCoders; ci++) | |
{ | |
Byte mainByte = inByte->ReadByte(); | |
if ((mainByte & 0xC0) != 0) | |
ThrowUnsupported(); | |
unsigned idSize = (mainByte & 0xF); | |
if (idSize > 8) | |
ThrowUnsupported(); | |
if (idSize > inByte->GetRem()) | |
ThrowEndOfData(); | |
const Byte *longID = inByte->GetPtr(); | |
UInt64 id = 0; | |
for (unsigned j = 0; j < idSize; j++) | |
id = ((id << 8) | longID[j]); | |
inByte->SkipDataNoCheck(idSize); | |
if (folders.ParsedMethods.IDs.Size() < 128) | |
folders.ParsedMethods.IDs.AddToUniqueSorted(id); | |
CNum coderInStreams = 1; | |
CNum coderOutStreams = 1; | |
if ((mainByte & 0x10) != 0) | |
{ | |
coderInStreams = inByte->ReadNum(); | |
coderOutStreams = inByte->ReadNum(); | |
} | |
numInStreams += coderInStreams; | |
if (numInStreams < coderInStreams) | |
ThrowUnsupported(); | |
numOutStreams += coderOutStreams; | |
if (numOutStreams < coderOutStreams) | |
ThrowUnsupported(); | |
if ((mainByte & 0x20) != 0) | |
{ | |
CNum propsSize = inByte->ReadNum(); | |
if (propsSize > inByte->GetRem()) | |
ThrowEndOfData(); | |
if (id == k_LZMA2 && propsSize == 1) | |
{ | |
Byte v = *_inByteBack->GetPtr(); | |
if (folders.ParsedMethods.Lzma2Prop < v) | |
folders.ParsedMethods.Lzma2Prop = v; | |
} | |
else if (id == k_LZMA && propsSize == 5) | |
{ | |
UInt32 dicSize = GetUi32(_inByteBack->GetPtr() + 1); | |
if (folders.ParsedMethods.LzmaDic < dicSize) | |
folders.ParsedMethods.LzmaDic = dicSize; | |
} | |
inByte->SkipDataNoCheck((size_t)propsSize); | |
} | |
} | |
if (numOutStreams == 1 && numInStreams == 1) | |
{ | |
indexOfMainStream = 0; | |
numPackStreams = 1; | |
} | |
else | |
{ | |
UInt32 i; | |
if (numOutStreams == 0) | |
ThrowUnsupported(); | |
CNum numBindPairs = numOutStreams - 1; | |
if (numInStreams < numBindPairs) | |
ThrowUnsupported(); | |
if (numInStreams >= 256 || numOutStreams >= 256) | |
ThrowUnsupported(); | |
InStreamUsed.ClearAndSetSize(numInStreams); | |
for (i = 0; i < numInStreams; i++) | |
InStreamUsed[i] = false; | |
OutStreamUsed.ClearAndSetSize(numOutStreams); | |
for (i = 0; i < numOutStreams; i++) | |
OutStreamUsed[i] = false; | |
for (i = 0; i < numBindPairs; i++) | |
{ | |
CNum index = ReadNum(); | |
if (index >= numInStreams || InStreamUsed[index]) | |
ThrowUnsupported(); | |
InStreamUsed[index] = true; | |
index = ReadNum(); | |
if (index >= numOutStreams || OutStreamUsed[index]) | |
ThrowUnsupported(); | |
OutStreamUsed[index] = true; | |
} | |
numPackStreams = numInStreams - numBindPairs; | |
if (numPackStreams != 1) | |
for (i = 0; i < numPackStreams; i++) | |
inByte->ReadNum(); // PackStreams | |
for (i = 0; i < numOutStreams; i++) | |
if (!OutStreamUsed[i]) | |
{ | |
indexOfMainStream = i; | |
break; | |
} | |
if (i == numOutStreams) | |
ThrowUnsupported(); | |
} | |
folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams; | |
numCodersOutStreams += numOutStreams; | |
folders.FoStartPackStreamIndex[fo] = packStreamIndex; | |
packStreamIndex += numPackStreams; | |
folders.FoToMainUnpackSizeIndex[fo] = (Byte)indexOfMainStream; | |
} | |
size_t dataSize = _inByteBack->GetPtr() - startBufPtr; | |
folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams; | |
folders.FoStartPackStreamIndex[fo] = packStreamIndex; | |
folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr; | |
folders.CodersData.CopyFrom(startBufPtr, dataSize); | |
} | |
WaitId(NID::kCodersUnpackSize); | |
folders.CoderUnpackSizes.Alloc(numCodersOutStreams); | |
for (CNum i = 0; i < numCodersOutStreams; i++) | |
folders.CoderUnpackSizes[i] = ReadNumber(); | |
for (;;) | |
{ | |
UInt64 type = ReadID(); | |
if (type == NID::kEnd) | |
return; | |
if (type == NID::kCRC) | |
{ | |
ReadHashDigests(numFolders, folders.FolderCRCs); | |
continue; | |
} | |
SkipData(); | |
} | |
} | |
void CInArchive::ReadSubStreamsInfo( | |
CFolders &folders, | |
CRecordVector<UInt64> &unpackSizes, | |
CUInt32DefVector &digests) | |
{ | |
folders.NumUnpackStreamsVector.Alloc(folders.NumFolders); | |
CNum i; | |
for (i = 0; i < folders.NumFolders; i++) | |
folders.NumUnpackStreamsVector[i] = 1; | |
UInt64 type; | |
for (;;) | |
{ | |
type = ReadID(); | |
if (type == NID::kNumUnpackStream) | |
{ | |
for (i = 0; i < folders.NumFolders; i++) | |
folders.NumUnpackStreamsVector[i] = ReadNum(); | |
continue; | |
} | |
if (type == NID::kCRC || type == NID::kSize || type == NID::kEnd) | |
break; | |
SkipData(); | |
} | |
if (type == NID::kSize) | |
{ | |
for (i = 0; i < folders.NumFolders; i++) | |
{ | |
// v3.13 incorrectly worked with empty folders | |
// v4.07: we check that folder is empty | |
CNum numSubstreams = folders.NumUnpackStreamsVector[i]; | |
if (numSubstreams == 0) | |
continue; | |
UInt64 sum = 0; | |
for (CNum j = 1; j < numSubstreams; j++) | |
{ | |
UInt64 size = ReadNumber(); | |
unpackSizes.Add(size); | |
sum += size; | |
if (sum < size) | |
ThrowIncorrect(); | |
} | |
UInt64 folderUnpackSize = folders.GetFolderUnpackSize(i); | |
if (folderUnpackSize < sum) | |
ThrowIncorrect(); | |
unpackSizes.Add(folderUnpackSize - sum); | |
} | |
type = ReadID(); | |
} | |
else | |
{ | |
for (i = 0; i < folders.NumFolders; i++) | |
{ | |
/* v9.26 - v9.29 incorrectly worked: | |
if (folders.NumUnpackStreamsVector[i] == 0), it threw error */ | |
CNum val = folders.NumUnpackStreamsVector[i]; | |
if (val > 1) | |
ThrowIncorrect(); | |
if (val == 1) | |
unpackSizes.Add(folders.GetFolderUnpackSize(i)); | |
} | |
} | |
unsigned numDigests = 0; | |
for (i = 0; i < folders.NumFolders; i++) | |
{ | |
CNum numSubstreams = folders.NumUnpackStreamsVector[i]; | |
if (numSubstreams != 1 || !folders.FolderCRCs.ValidAndDefined(i)) | |
numDigests += numSubstreams; | |
} | |
for (;;) | |
{ | |
if (type == NID::kEnd) | |
break; | |
if (type == NID::kCRC) | |
{ | |
// CUInt32DefVector digests2; | |
// ReadHashDigests(numDigests, digests2); | |
CBoolVector digests2; | |
ReadBoolVector2(numDigests, digests2); | |
digests.ClearAndSetSize(unpackSizes.Size()); | |
unsigned k = 0; | |
unsigned k2 = 0; | |
for (i = 0; i < folders.NumFolders; i++) | |
{ | |
CNum numSubstreams = folders.NumUnpackStreamsVector[i]; | |
if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i)) | |
{ | |
digests.Defs[k] = true; | |
digests.Vals[k] = folders.FolderCRCs.Vals[i]; | |
k++; | |
} | |
else for (CNum j = 0; j < numSubstreams; j++) | |
{ | |
bool defined = digests2[k2++]; | |
digests.Defs[k] = defined; | |
UInt32 crc = 0; | |
if (defined) | |
crc = ReadUInt32(); | |
digests.Vals[k] = crc; | |
k++; | |
} | |
} | |
// if (k != unpackSizes.Size()) throw 1234567; | |
} | |
else | |
SkipData(); | |
type = ReadID(); | |
} | |
if (digests.Defs.Size() != unpackSizes.Size()) | |
{ | |
digests.ClearAndSetSize(unpackSizes.Size()); | |
unsigned k = 0; | |
for (i = 0; i < folders.NumFolders; i++) | |
{ | |
CNum numSubstreams = folders.NumUnpackStreamsVector[i]; | |
if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i)) | |
{ | |
digests.Defs[k] = true; | |
digests.Vals[k] = folders.FolderCRCs.Vals[i]; | |
k++; | |
} | |
else for (CNum j = 0; j < numSubstreams; j++) | |
{ | |
digests.Defs[k] = false; | |
digests.Vals[k] = 0; | |
k++; | |
} | |
} | |
} | |
} | |
void CInArchive::ReadStreamsInfo( | |
const CObjectVector<CByteBuffer> *dataVector, | |
UInt64 &dataOffset, | |
CFolders &folders, | |
CRecordVector<UInt64> &unpackSizes, | |
CUInt32DefVector &digests) | |
{ | |
UInt64 type = ReadID(); | |
if (type == NID::kPackInfo) | |
{ | |
dataOffset = ReadNumber(); | |
ReadPackInfo(folders); | |
type = ReadID(); | |
} | |
if (type == NID::kUnpackInfo) | |
{ | |
ReadUnpackInfo(dataVector, folders); | |
type = ReadID(); | |
} | |
if (folders.NumFolders != 0 && !folders.PackPositions) | |
{ | |
// if there are folders, we need PackPositions also | |
folders.PackPositions.Alloc(1); | |
folders.PackPositions[0] = 0; | |
} | |
if (type == NID::kSubStreamsInfo) | |
{ | |
ReadSubStreamsInfo(folders, unpackSizes, digests); | |
type = ReadID(); | |
} | |
else | |
{ | |
folders.NumUnpackStreamsVector.Alloc(folders.NumFolders); | |
/* If digests.Defs.Size() == 0, it means that there are no crcs. | |
So we don't need to fill digests with values. */ | |
// digests.Vals.ClearAndSetSize(folders.NumFolders); | |
// BoolVector_Fill_False(digests.Defs, folders.NumFolders); | |
for (CNum i = 0; i < folders.NumFolders; i++) | |
{ | |
folders.NumUnpackStreamsVector[i] = 1; | |
unpackSizes.Add(folders.GetFolderUnpackSize(i)); | |
// digests.Vals[i] = 0; | |
} | |
} | |
if (type != NID::kEnd) | |
ThrowIncorrect(); | |
} | |
void CInArchive::ReadBoolVector(unsigned numItems, CBoolVector &v) | |
{ | |
v.ClearAndSetSize(numItems); | |
Byte b = 0; | |
Byte mask = 0; | |
bool *p = &v[0]; | |
for (unsigned i = 0; i < numItems; i++) | |
{ | |
if (mask == 0) | |
{ | |
b = ReadByte(); | |
mask = 0x80; | |
} | |
p[i] = ((b & mask) != 0); | |
mask >>= 1; | |
} | |
} | |
void CInArchive::ReadBoolVector2(unsigned numItems, CBoolVector &v) | |
{ | |
Byte allAreDefined = ReadByte(); | |
if (allAreDefined == 0) | |
{ | |
ReadBoolVector(numItems, v); | |
return; | |
} | |
v.ClearAndSetSize(numItems); | |
bool *p = &v[0]; | |
for (unsigned i = 0; i < numItems; i++) | |
p[i] = true; | |
} | |
void CInArchive::ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector, | |
CUInt64DefVector &v, unsigned numItems) | |
{ | |
ReadBoolVector2(numItems, v.Defs); | |
CStreamSwitch streamSwitch; | |
streamSwitch.Set(this, &dataVector); | |
v.Vals.ClearAndSetSize(numItems); | |
UInt64 *p = &v.Vals[0]; | |
const bool *defs = &v.Defs[0]; | |
for (unsigned i = 0; i < numItems; i++) | |
{ | |
UInt64 t = 0; | |
if (defs[i]) | |
t = ReadUInt64(); | |
p[i] = t; | |
} | |
} | |
HRESULT CInArchive::ReadAndDecodePackedStreams( | |
DECL_EXTERNAL_CODECS_LOC_VARS | |
UInt64 baseOffset, | |
UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector | |
_7Z_DECODER_CRYPRO_VARS_DECL | |
) | |
{ | |
CFolders folders; | |
CRecordVector<UInt64> unpackSizes; | |
CUInt32DefVector digests; | |
ReadStreamsInfo(NULL, | |
dataOffset, | |
folders, | |
unpackSizes, | |
digests); | |
CDecoder decoder( | |
#ifdef _ST_MODE | |
false | |
#else | |
true | |
#endif | |
); | |
for (CNum i = 0; i < folders.NumFolders; i++) | |
{ | |
CByteBuffer &data = dataVector.AddNew(); | |
UInt64 unpackSize64 = folders.GetFolderUnpackSize(i); | |
size_t unpackSize = (size_t)unpackSize64; | |
if (unpackSize != unpackSize64) | |
ThrowUnsupported(); | |
data.Alloc(unpackSize); | |
CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; | |
CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; | |
outStreamSpec->Init(data, unpackSize); | |
HRESULT result = decoder.Decode( | |
EXTERNAL_CODECS_LOC_VARS | |
_stream, baseOffset + dataOffset, | |
folders, i, | |
outStream, NULL | |
_7Z_DECODER_CRYPRO_VARS | |
#if !defined(_7ZIP_ST) && !defined(_SFX) | |
, false, 1 | |
#endif | |
); | |
RINOK(result); | |
if (folders.FolderCRCs.ValidAndDefined(i)) | |
if (CrcCalc(data, unpackSize) != folders.FolderCRCs.Vals[i]) | |
ThrowIncorrect(); | |
} | |
HeadersSize += folders.PackPositions[folders.NumPackStreams]; | |
return S_OK; | |
} | |
HRESULT CInArchive::ReadHeader( | |
DECL_EXTERNAL_CODECS_LOC_VARS | |
CDbEx &db | |
_7Z_DECODER_CRYPRO_VARS_DECL | |
) | |
{ | |
UInt64 type = ReadID(); | |
if (type == NID::kArchiveProperties) | |
{ | |
ReadArchiveProperties(db.ArcInfo); | |
type = ReadID(); | |
} | |
CObjectVector<CByteBuffer> dataVector; | |
if (type == NID::kAdditionalStreamsInfo) | |
{ | |
HRESULT result = ReadAndDecodePackedStreams( | |
EXTERNAL_CODECS_LOC_VARS | |
db.ArcInfo.StartPositionAfterHeader, | |
db.ArcInfo.DataStartPosition2, | |
dataVector | |
_7Z_DECODER_CRYPRO_VARS | |
); | |
RINOK(result); | |
db.ArcInfo.DataStartPosition2 += db.ArcInfo.StartPositionAfterHeader; | |
type = ReadID(); | |
} | |
CRecordVector<UInt64> unpackSizes; | |
CUInt32DefVector digests; | |
if (type == NID::kMainStreamsInfo) | |
{ | |
ReadStreamsInfo(&dataVector, | |
db.ArcInfo.DataStartPosition, | |
(CFolders &)db, | |
unpackSizes, | |
digests); | |
db.ArcInfo.DataStartPosition += db.ArcInfo.StartPositionAfterHeader; | |
type = ReadID(); | |
} | |
db.Files.Clear(); | |
if (type == NID::kFilesInfo) | |
{ | |
CNum numFiles = ReadNum(); | |
db.Files.ClearAndSetSize(numFiles); | |
CNum i; | |
/* | |
db.Files.Reserve(numFiles); | |
CNum i; | |
for (i = 0; i < numFiles; i++) | |
db.Files.Add(CFileItem()); | |
*/ | |
db.ArcInfo.FileInfoPopIDs.Add(NID::kSize); | |
// if (!db.PackSizes.IsEmpty()) | |
db.ArcInfo.FileInfoPopIDs.Add(NID::kPackInfo); | |
if (numFiles > 0 && !digests.Defs.IsEmpty()) | |
db.ArcInfo.FileInfoPopIDs.Add(NID::kCRC); | |
CBoolVector emptyStreamVector; | |
BoolVector_Fill_False(emptyStreamVector, (unsigned)numFiles); | |
CBoolVector emptyFileVector; | |
CBoolVector antiFileVector; | |
CNum numEmptyStreams = 0; | |
for (;;) | |
{ | |
UInt64 type = ReadID(); | |
if (type == NID::kEnd) | |
break; | |
UInt64 size = ReadNumber(); | |
if (size > _inByteBack->GetRem()) | |
ThrowIncorrect(); | |
CStreamSwitch switchProp; | |
switchProp.Set(this, _inByteBack->GetPtr(), (size_t)size, true); | |
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); | |
size_t rem = _inByteBack->GetRem(); | |
db.NamesBuf.Alloc(rem); | |
ReadBytes(db.NamesBuf, rem); | |
db.NameOffsets.Alloc(db.Files.Size() + 1); | |
size_t pos = 0; | |
unsigned i; | |
for (i = 0; i < db.Files.Size(); i++) | |
{ | |
size_t curRem = (rem - pos) / 2; | |
const UInt16 *buf = (const UInt16 *)(db.NamesBuf + pos); | |
size_t j; | |
for (j = 0; j < curRem && buf[j] != 0; j++); | |
if (j == curRem) | |
ThrowEndOfData(); | |
db.NameOffsets[i] = pos / 2; | |
pos += j * 2 + 2; | |
} | |
db.NameOffsets[i] = pos / 2; | |
if (pos != rem) | |
ThereIsHeaderError = true; | |
break; | |
} | |
case NID::kWinAttrib: | |
{ | |
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::kIsAux: | |
{ | |
ReadBoolVector(db.Files.Size(), db.IsAux); | |
break; | |
} | |
case NID::kParent: | |
{ | |
db.IsTree = true; | |
// CBoolVector boolVector; | |
// ReadBoolVector2(db.Files.Size(), boolVector); | |
// CStreamSwitch streamSwitch; | |
// streamSwitch.Set(this, &dataVector); | |
CBoolVector boolVector; | |
ReadBoolVector2(db.Files.Size(), boolVector); | |
db.ThereAreAltStreams = false; | |
for (i = 0; i < numFiles; i++) | |
{ | |
CFileItem &file = db.Files[i]; | |
// file.Parent = -1; | |
// if (boolVector[i]) | |
file.Parent = (int)ReadUInt32(); | |
file.IsAltStream = !boolVector[i]; | |
if (file.IsAltStream) | |
db.ThereAreAltStreams = true; | |
} | |
break; | |
} | |
*/ | |
case NID::kEmptyStream: | |
{ | |
ReadBoolVector(numFiles, emptyStreamVector); | |
numEmptyStreams = 0; | |
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, (unsigned)numFiles); break; | |
case NID::kCTime: ReadUInt64DefVector(dataVector, db.CTime, (unsigned)numFiles); break; | |
case NID::kATime: ReadUInt64DefVector(dataVector, db.ATime, (unsigned)numFiles); break; | |
case NID::kMTime: ReadUInt64DefVector(dataVector, db.MTime, (unsigned)numFiles); break; | |
case NID::kDummy: | |
{ | |
for (UInt64 j = 0; j < size; j++) | |
if (ReadByte() != 0) | |
ThereIsHeaderError = true; | |
addPropIdToList = false; | |
break; | |
} | |
/* | |
case NID::kNtSecure: | |
{ | |
try | |
{ | |
{ | |
CStreamSwitch streamSwitch; | |
streamSwitch.Set(this, &dataVector); | |
UInt32 numDescriptors = ReadUInt32(); | |
size_t offset = 0; | |
db.SecureOffsets.Clear(); | |
for (i = 0; i < numDescriptors; i++) | |
{ | |
UInt32 size = ReadUInt32(); | |
db.SecureOffsets.Add(offset); | |
offset += size; | |
} | |
// ThrowIncorrect();; | |
db.SecureOffsets.Add(offset); | |
db.SecureBuf.SetCapacity(offset); | |
for (i = 0; i < numDescriptors; i++) | |
{ | |
offset = db.SecureOffsets[i]; | |
ReadBytes(db.SecureBuf + offset, db.SecureOffsets[i + 1] - offset); | |
} | |
db.SecureIDs.Clear(); | |
for (unsigned i = 0; i < db.Files.Size(); i++) | |
{ | |
db.SecureIDs.Add(ReadNum()); | |
// db.SecureIDs.Add(ReadUInt32()); | |
} | |
// ReadUInt32(); | |
if (_inByteBack->GetRem() != 0) | |
ThrowIncorrect();; | |
} | |
} | |
catch(CInArchiveException &) | |
{ | |
ThereIsHeaderError = true; | |
addPropIdToList = isKnownType = false; | |
db.ClearSecure(); | |
} | |
break; | |
} | |
*/ | |
default: | |
addPropIdToList = isKnownType = false; | |
} | |
if (isKnownType) | |
{ | |
if (addPropIdToList) | |
db.ArcInfo.FileInfoPopIDs.Add(type); | |
} | |
else | |
{ | |
db.UnsupportedFeatureWarning = true; | |
_inByteBack->SkipRem(); | |
} | |
// SkipData worked incorrectly in some versions before v4.59 (7zVer <= 00.02) | |
if (_inByteBack->GetRem() != 0) | |
ThrowIncorrect(); | |
} | |
type = ReadID(); // Read (NID::kEnd) end of headers | |
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]; | |
file.Crc = 0; | |
if (file.HasStream) | |
{ | |
file.IsDir = false; | |
isAnti = false; | |
file.Size = unpackSizes[sizeIndex]; | |
file.CrcDefined = digests.ValidAndDefined(sizeIndex); | |
if (file.CrcDefined) | |
file.Crc = digests.Vals[sizeIndex]; | |
sizeIndex++; | |
} | |
else | |
{ | |
file.IsDir = !emptyFileVector[emptyFileIndex]; | |
isAnti = antiFileVector[emptyFileIndex]; | |
emptyFileIndex++; | |
file.Size = 0; | |
file.CrcDefined = false; | |
} | |
if (numAntiItems != 0) | |
db.IsAnti.Add(isAnti); | |
} | |
} | |
db.FillLinks(); | |
/* | |
if (type != NID::kEnd) | |
ThrowIncorrect(); | |
if (_inByteBack->GetRem() != 0) | |
ThrowIncorrect(); | |
*/ | |
return S_OK; | |
} | |
void CDbEx::FillLinks() | |
{ | |
FolderStartFileIndex.ClearAndSetSize(NumFolders); | |
FileIndexToFolderIndexMap.ClearAndSetSize(Files.Size()); | |
CNum folderIndex = 0; | |
CNum indexInFolder = 0; | |
unsigned i; | |
for (i = 0; i < Files.Size(); i++) | |
{ | |
bool emptyStream = !Files[i].HasStream; | |
if (indexInFolder == 0) | |
{ | |
if (emptyStream) | |
{ | |
FileIndexToFolderIndexMap[i] = kNumNoIndex; | |
continue; | |
} | |
// v3.13 incorrectly worked with empty folders | |
// v4.07: we skip empty folders | |
for (;;) | |
{ | |
if (folderIndex >= NumFolders) | |
ThrowIncorrect(); | |
FolderStartFileIndex[folderIndex] = i; | |
if (NumUnpackStreamsVector[folderIndex] != 0) | |
break; | |
folderIndex++; | |
} | |
} | |
FileIndexToFolderIndexMap[i] = folderIndex; | |
if (emptyStream) | |
continue; | |
if (++indexInFolder >= NumUnpackStreamsVector[folderIndex]) | |
{ | |
folderIndex++; | |
indexInFolder = 0; | |
} | |
} | |
if (indexInFolder != 0) | |
folderIndex++; | |
/* | |
if (indexInFolder != 0) | |
ThrowIncorrect(); | |
*/ | |
for (;;) | |
{ | |
if (folderIndex >= NumFolders) | |
return; | |
FolderStartFileIndex[folderIndex] = i; | |
/* | |
if (NumUnpackStreamsVector[folderIndex] != 0) | |
ThrowIncorrect();; | |
*/ | |
folderIndex++; | |
} | |
} | |
HRESULT CInArchive::ReadDatabase2( | |
DECL_EXTERNAL_CODECS_LOC_VARS | |
CDbEx &db | |
_7Z_DECODER_CRYPRO_VARS_DECL | |
) | |
{ | |
db.Clear(); | |
db.ArcInfo.StartPosition = _arhiveBeginStreamPosition; | |
db.ArcInfo.Version.Major = _header[6]; | |
db.ArcInfo.Version.Minor = _header[7]; | |
if (db.ArcInfo.Version.Major != kMajorVersion) | |
{ | |
// db.UnsupportedVersion = true; | |
return S_FALSE; | |
} | |
UInt64 nextHeaderOffset = Get64(_header + 12); | |
UInt64 nextHeaderSize = Get64(_header + 20); | |
UInt32 nextHeaderCRC = Get32(_header + 28); | |
#ifdef FORMAT_7Z_RECOVERY | |
UInt32 crcFromArc = Get32(_header + 8); | |
if (crcFromArc == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0) | |
{ | |
UInt64 cur, fileSize; | |
RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur)); | |
const unsigned kCheckSize = 512; | |
Byte buf[kCheckSize]; | |
RINOK(_stream->Seek(0, STREAM_SEEK_END, &fileSize)); | |
UInt64 rem = fileSize - cur; | |
unsigned checkSize = kCheckSize; | |
if (rem < kCheckSize) | |
checkSize = (unsigned)(rem); | |
if (checkSize < 3) | |
return S_FALSE; | |
RINOK(_stream->Seek(fileSize - checkSize, STREAM_SEEK_SET, NULL)); | |
RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize)); | |
if (buf[checkSize - 1] != 0) | |
return S_FALSE; | |
unsigned i; | |
for (i = checkSize - 2;; i--) | |
{ | |
if (buf[i] == NID::kEncodedHeader && buf[i + 1] == NID::kPackInfo || | |
buf[i] == NID::kHeader && buf[i + 1] == NID::kMainStreamsInfo) | |
break; | |
if (i == 0) | |
return S_FALSE; | |
} | |
nextHeaderSize = checkSize - i; | |
nextHeaderOffset = rem - nextHeaderSize; | |
nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); | |
RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL)); | |
db.StartHeaderWasRecovered = true; | |
} | |
else | |
#endif | |
{ | |
// Crc was tested already at signature check | |
// if (CrcCalc(_header + 12, 20) != crcFromArchive) ThrowIncorrect(); | |
} | |
db.ArcInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize; | |
db.PhySize = kHeaderSize; | |
db.IsArc = false; | |
if ((Int64)nextHeaderOffset < 0 || | |
nextHeaderSize > ((UInt64)1 << 62)) | |
return S_FALSE; | |
if (nextHeaderSize == 0) | |
{ | |
if (nextHeaderOffset != 0) | |
return S_FALSE; | |
db.IsArc = true; | |
return S_OK; | |
} | |
if (!db.StartHeaderWasRecovered) | |
db.IsArc = true; | |
HeadersSize += kHeaderSize + nextHeaderSize; | |
db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize; | |
if (_fileEndPosition - db.ArcInfo.StartPositionAfterHeader < nextHeaderOffset + nextHeaderSize) | |
{ | |
db.UnexpectedEnd = true; | |
return S_FALSE; | |
} | |
RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL)); | |
size_t nextHeaderSize_t = (size_t)nextHeaderSize; | |
if (nextHeaderSize_t != nextHeaderSize) | |
return E_OUTOFMEMORY; | |
CByteBuffer buffer2(nextHeaderSize_t); | |
RINOK(ReadStream_FALSE(_stream, buffer2, nextHeaderSize_t)); | |
if (CrcCalc(buffer2, nextHeaderSize_t) != nextHeaderCRC) | |
ThrowIncorrect(); | |
if (!db.StartHeaderWasRecovered) | |
db.PhySizeWasConfirmed = true; | |
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.ArcInfo.StartPositionAfterHeader, | |
db.ArcInfo.DataStartPosition2, | |
dataVector | |
_7Z_DECODER_CRYPRO_VARS | |
); | |
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.IsArc = true; | |
db.HeadersSize = HeadersSize; | |
return ReadHeader( | |
EXTERNAL_CODECS_LOC_VARS | |
db | |
_7Z_DECODER_CRYPRO_VARS | |
); | |
} | |
HRESULT CInArchive::ReadDatabase( | |
DECL_EXTERNAL_CODECS_LOC_VARS | |
CDbEx &db | |
_7Z_DECODER_CRYPRO_VARS_DECL | |
) | |
{ | |
try | |
{ | |
HRESULT res = ReadDatabase2( | |
EXTERNAL_CODECS_LOC_VARS db | |
_7Z_DECODER_CRYPRO_VARS | |
); | |
if (ThereIsHeaderError) | |
db.ThereIsHeaderError = true; | |
if (res == E_NOTIMPL) | |
ThrowUnsupported(); | |
return res; | |
} | |
catch(CUnsupportedFeatureException &) | |
{ | |
db.UnsupportedFeatureError = true; | |
return S_FALSE; | |
} | |
catch(CInArchiveException &) | |
{ | |
db.ThereIsHeaderError = true; | |
return S_FALSE; | |
} | |
} | |
}} |