diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2005-09-21 04:00:00 +0400 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:15:44 +0300 |
commit | d66cf2fcf3cc3667f76f1ea50e8360f1c2813753 (patch) | |
tree | 22ffe1a5473b0e988ab1626a0294e1e1b80ca3ee /7zip/Archive/Cab/CabIn.cpp | |
parent | 31e7b924e826b529aa3ad412730a02ee06959985 (diff) |
4.27 beta
Diffstat (limited to '7zip/Archive/Cab/CabIn.cpp')
-rwxr-xr-x | 7zip/Archive/Cab/CabIn.cpp | 352 |
1 files changed, 218 insertions, 134 deletions
diff --git a/7zip/Archive/Cab/CabIn.cpp b/7zip/Archive/Cab/CabIn.cpp index a9d40726..1d47502c 100755 --- a/7zip/Archive/Cab/CabIn.cpp +++ b/7zip/Archive/Cab/CabIn.cpp @@ -6,7 +6,8 @@ #include "Common/MyCom.h" #include "CabIn.h" #include "Windows/Defs.h" -#include "../../Common/InBuffer.h" + +#include "../../Common/StreamUtils.h" namespace NArchive{ namespace NCab{ @@ -14,7 +15,7 @@ namespace NCab{ static HRESULT ReadBytes(IInStream *inStream, void *data, UInt32 size) { UInt32 realProcessedSize; - RINOK(inStream->Read(data, size, &realProcessedSize)); + RINOK(ReadStream(inStream, data, size, &realProcessedSize)); if(realProcessedSize != size) return S_FALSE; return S_OK; @@ -23,7 +24,7 @@ static HRESULT ReadBytes(IInStream *inStream, void *data, UInt32 size) static HRESULT SafeRead(IInStream *inStream, void *data, UInt32 size) { UInt32 realProcessedSize; - RINOK(inStream->Read(data, size, &realProcessedSize)); + RINOK(ReadStream(inStream, data, size, &realProcessedSize)); if(realProcessedSize != size) throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); return S_OK; @@ -37,25 +38,12 @@ static void SafeInByteRead(::CInBuffer &inBuffer, void *data, UInt32 size) throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); } -static void SafeReadName(::CInBuffer &inBuffer, AString &name) -{ - name.Empty(); - while(true) - { - Byte b; - if (!inBuffer.ReadByte(b)) - throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); - if (b == 0) - return; - name += char(b); - } -} - Byte CInArchive::ReadByte() { - if (_blockPos >= _blockSize) - throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); - return _block[_blockPos++]; + Byte b; + if (!inBuffer.ReadByte(b)) + throw CInArchiveException(CInArchiveException::kUnsupported); + return b; } UInt16 CInArchive::ReadUInt16() @@ -80,20 +68,38 @@ UInt32 CInArchive::ReadUInt32() return value; } -HRESULT CInArchive::Open(IInStream *inStream, - const UInt64 *searchHeaderSizeLimit, - CInArchiveInfo &inArchiveInfo, - CObjectVector<NHeader::CFolder> &folders, - CObjectVector<CItem> &files, - CProgressVirt *progressVirt) +AString CInArchive::SafeReadName() { - UInt64 startPosition; - RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &startPosition)); + AString name; + while(true) + { + Byte b = ReadByte(); + if (b == 0) + return name; + name += (char)b; + } +} - // NHeader::NArchive::CBlock archiveHeader; +void CInArchive::ReadOtherArchive(COtherArchive &oa) +{ + oa.FileName = SafeReadName(); + oa.DiskName = SafeReadName(); +} + +void CInArchive::Skeep(size_t size) +{ + while (size-- != 0) + ReadByte(); +} + +HRESULT CInArchive::Open2(IInStream *inStream, + const UInt64 *searchHeaderSizeLimit, + CDatabase &database) +{ + database.Clear(); + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &database.StartPosition)); { - ::CInBuffer inBuffer; if (!inBuffer.Create(1 << 17)) return E_OUTOFMEMORY; inBuffer.SetStream(inStream); @@ -117,141 +123,219 @@ HRESULT CInArchive::Open(IInStream *inStream, return S_FALSE; } } - startPosition += inBuffer.GetProcessedSize() - kSignatureSize; + database.StartPosition += inBuffer.GetProcessedSize() - kSignatureSize; } - RINOK(inStream->Seek(startPosition, STREAM_SEEK_SET, NULL)); - - RINOK(ReadBytes(inStream, _block, NHeader::NArchive::kArchiveHeaderSize)); - _blockSize = NHeader::NArchive::kArchiveHeaderSize; - _blockPos = 0; - - ReadUInt32(); // Signature; // cabinet file signature - // if (archiveHeader.Signature != NHeader::NArchive::kSignature) - // return S_FALSE; - - UInt32 reserved1 = ReadUInt32(); - UInt32 size = ReadUInt32(); // size of this cabinet file in bytes - UInt32 reserved2 = ReadUInt32(); - UInt32 fileOffset = ReadUInt32(); // offset of the first CFFILE entry - UInt32 reserved3 = ReadUInt32(); - - inArchiveInfo.VersionMinor = ReadByte(); // cabinet file format version, minor - inArchiveInfo.VersionMajor = ReadByte(); // cabinet file format version, major - inArchiveInfo.NumFolders = ReadUInt16(); // number of CFFOLDER entries in this cabinet - inArchiveInfo.NumFiles = ReadUInt16(); // number of CFFILE entries in this cabinet - inArchiveInfo.Flags = ReadUInt16(); // number of CFFILE entries in this cabinet - UInt16 setID = ReadUInt16(); // must be the same for all cabinets in a set - UInt16 cabinetNumber = ReadUInt16(); // number of this cabinet file in a set - - if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0) - throw CInArchiveException(CInArchiveException::kUnsupported); - if (inArchiveInfo.ReserveBlockPresent()) + CInArchiveInfo &archiveInfo = database.ArchiveInfo; + + archiveInfo.Size = ReadUInt32(); // size of this cabinet file in bytes + if (ReadUInt32() != 0) + return S_FALSE; + archiveInfo.FileHeadersOffset = ReadUInt32(); // offset of the first CFFILE entry + if (ReadUInt32() != 0) + return S_FALSE; + + archiveInfo.VersionMinor = ReadByte(); // cabinet file format version, minor + archiveInfo.VersionMajor = ReadByte(); // cabinet file format version, major + archiveInfo.NumFolders = ReadUInt16(); // number of CFFOLDER entries in this cabinet + archiveInfo.NumFiles = ReadUInt16(); // number of CFFILE entries in this cabinet + archiveInfo.Flags = ReadUInt16(); // number of CFFILE entries in this cabinet + archiveInfo.SetID = ReadUInt16(); // must be the same for all cabinets in a set + archiveInfo.CabinetNumber = ReadUInt16(); // number of this cabinet file in a set + + if (archiveInfo.ReserveBlockPresent()) { - RINOK(SafeRead(inStream, _block, NHeader::NArchive::kPerDataSizesHeaderSize)); - _blockSize = NHeader::NArchive::kPerDataSizesHeaderSize; - _blockPos = 0; - - inArchiveInfo.PerDataSizes.PerCabinetAreaSize = ReadUInt16(); // (optional) size of per-cabinet reserved area - inArchiveInfo.PerDataSizes.PerFolderAreaSize = ReadByte(); // (optional) size of per-folder reserved area - inArchiveInfo.PerDataSizes.PerDatablockAreaSize = ReadByte(); // (optional) size of per-datablock reserved area - RINOK(inStream->Seek(inArchiveInfo.PerDataSizes.PerCabinetAreaSize, - STREAM_SEEK_CUR, NULL)); + archiveInfo.PerCabinetAreaSize = ReadUInt16(); // (optional) size of per-cabinet reserved area + archiveInfo.PerFolderAreaSize = ReadByte(); // (optional) size of per-folder reserved area + archiveInfo.PerDataBlockAreaSize = ReadByte(); // (optional) size of per-datablock reserved area + + Skeep(archiveInfo.PerCabinetAreaSize); } { - UInt64 foldersStartPosition; - RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &foldersStartPosition)); - ::CInBuffer inBuffer; - if (!inBuffer.Create(1 << 17)) - return E_OUTOFMEMORY; - inBuffer.SetStream(inStream); - inBuffer.Init(); - if ((inArchiveInfo.Flags & NHeader::NArchive::NFlags::kPrevCabinet) != 0) - { - SafeReadName(inBuffer, inArchiveInfo.PreviousCabinetName); - SafeReadName(inBuffer, inArchiveInfo.PreviousDiskName); - } - if ((inArchiveInfo.Flags & NHeader::NArchive::NFlags::kNextCabinet) != 0) - { - SafeReadName(inBuffer, inArchiveInfo.NextCabinetName); - SafeReadName(inBuffer, inArchiveInfo.NextDiskName); - } - foldersStartPosition += inBuffer.GetProcessedSize(); - RINOK(inStream->Seek(foldersStartPosition, STREAM_SEEK_SET, NULL)); + if (archiveInfo.IsTherePrev()) + ReadOtherArchive(archiveInfo.PreviousArchive); + if (archiveInfo.IsThereNext()) + ReadOtherArchive(archiveInfo.NextArchive); } - if (progressVirt != NULL) - { - UInt64 numFiles = inArchiveInfo.NumFiles; - RINOK(progressVirt->SetTotal(&numFiles)); - } - folders.Clear(); int i; - for(i = 0; i < inArchiveInfo.NumFolders; i++) + for(i = 0; i < archiveInfo.NumFolders; i++) { - if (progressVirt != NULL) - { - UInt64 numFiles = 0; - RINOK(progressVirt->SetCompleted(&numFiles)); - } - NHeader::CFolder folder; - RINOK(SafeRead(inStream, _block, NHeader::kFolderHeaderSize)); - _blockSize = NHeader::kFolderHeaderSize; - _blockPos = 0; + CFolder folder; folder.DataStart = ReadUInt32(); folder.NumDataBlocks = ReadUInt16(); folder.CompressionTypeMajor = ReadByte(); folder.CompressionTypeMinor = ReadByte(); - if (inArchiveInfo.ReserveBlockPresent()) - { - RINOK(inStream->Seek( - inArchiveInfo.PerDataSizes.PerFolderAreaSize, STREAM_SEEK_CUR, NULL)); - } - folder.DataStart += (UInt32)startPosition; - folders.Add(folder); + Skeep(archiveInfo.PerFolderAreaSize); + database.Folders.Add(folder); } - RINOK(inStream->Seek(startPosition + fileOffset, - STREAM_SEEK_SET, NULL)); + RINOK(inStream->Seek(database.StartPosition + archiveInfo.FileHeadersOffset, STREAM_SEEK_SET, NULL)); - ::CInBuffer inBuffer; - if (!inBuffer.Create(1 << 17)) - return E_OUTOFMEMORY; inBuffer.SetStream(inStream); inBuffer.Init(); - files.Clear(); - if (progressVirt != NULL) - { - UInt64 numFiles = files.Size(); - RINOK(progressVirt->SetCompleted(&numFiles)); - } - for(i = 0; i < inArchiveInfo.NumFiles; i++) + for(i = 0; i < archiveInfo.NumFiles; i++) { - SafeInByteRead(inBuffer, _block, NHeader::kFileHeaderSize); - _blockSize = NHeader::kFileHeaderSize; - _blockPos = 0; CItem item; - item.UnPackSize = ReadUInt32(); - item.UnPackOffset = ReadUInt32(); + item.Size = ReadUInt32(); + item.Offset = ReadUInt32(); item.FolderIndex = ReadUInt16(); - if (item.FolderIndex > inArchiveInfo.NumFolders) - return S_FALSE; UInt16 pureDate = ReadUInt16(); UInt16 pureTime = ReadUInt16(); item.Time = ((UInt32(pureDate) << 16)) | pureTime; item.Attributes = ReadUInt16(); - SafeReadName(inBuffer, item.Name); - files.Add(item); - if (progressVirt != NULL) + item.Name = SafeReadName(); + int folderIndex = item.GetFolderIndex(database.Folders.Size()); + if (folderIndex >= database.Folders.Size()) + return S_FALSE; + database.Items.Add(item); + } + return S_OK; +} + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +HRESULT CInArchive::Open( + const UInt64 *searchHeaderSizeLimit, + CDatabaseEx &database) +{ + return Open2(database.Stream, searchHeaderSizeLimit, database); +} + + +static int CompareMvItems2(const CMvItem *p1, const CMvItem *p2) +{ + RINOZ(MyCompare(p1->VolumeIndex, p2->VolumeIndex)); + return MyCompare(p1->ItemIndex, p2->ItemIndex); +} + +static int CompareMvItems(const CMvItem *p1, const CMvItem *p2, void *param) +{ + const CMvDatabaseEx &mvDb = *(const CMvDatabaseEx *)param; + const CDatabaseEx &db1 = mvDb.Volumes[p1->VolumeIndex]; + const CDatabaseEx &db2 = mvDb.Volumes[p2->VolumeIndex]; + const CItem &item1 = db1.Items[p1->ItemIndex]; + const CItem &item2 = db2.Items[p2->ItemIndex];; + bool isDir1 = item1.IsDirectory(); + bool isDir2 = item2.IsDirectory(); + if (isDir1 && !isDir2) + return -1; + if (isDir2 && !isDir1) + return 1; + int f1 = mvDb.GetFolderIndex(p1); + int f2 = mvDb.GetFolderIndex(p2); + RINOZ(MyCompare(f1, f2)); + RINOZ(MyCompare(item1.Offset, item2.Offset)); + RINOZ(MyCompare(item1.Size, item2.Size)); + return CompareMvItems2(p1, p2); +} + +bool CMvDatabaseEx::AreItemsEqual(int i1, int i2) +{ + const CMvItem *p1 = &Items[i1]; + const CMvItem *p2 = &Items[i2]; + const CDatabaseEx &db1 = Volumes[p1->VolumeIndex]; + const CDatabaseEx &db2 = Volumes[p2->VolumeIndex]; + const CItem &item1 = db1.Items[p1->ItemIndex]; + const CItem &item2 = db2.Items[p2->ItemIndex];; + int f1 = GetFolderIndex(p1); + int f2 = GetFolderIndex(p2); + if (f1 != f2) + return false; + if (item1.Offset != item2.Offset) + return false; + if (item1.Size != item2.Size) + return false; + + return true; +} + +void CMvDatabaseEx::FillSortAndShrink() +{ + Items.Clear(); + StartFolderOfVol.Clear(); + FolderStartFileIndex.Clear(); + int offset = 0; + for (int v = 0; v < Volumes.Size(); v++) + { + const CDatabaseEx &db = Volumes[v]; + int curOffset = offset; + if (db.IsTherePrevFolder()) + curOffset--; + StartFolderOfVol.Add(curOffset); + offset += db.GetNumberOfNewFolders(); + + CMvItem mvItem; + mvItem.VolumeIndex = v; + for (int i = 0 ; i < db.Items.Size(); i++) { - UInt64 numFiles = files.Size(); - RINOK(progressVirt->SetCompleted(&numFiles)); + mvItem.ItemIndex = i; + Items.Add(mvItem); } } - return S_OK; + + Items.Sort(CompareMvItems, (void *)this); + int j = 1; + int i; + for (i = 1; i < Items.Size(); i++) + if (!AreItemsEqual(i, i -1)) + Items[j++] = Items[i]; + Items.DeleteFrom(j); + + for (i = 0; i < Items.Size(); i++) + { + const CMvItem &mvItem = Items[i]; + int folderIndex = GetFolderIndex(&mvItem); + if (folderIndex >= FolderStartFileIndex.Size()) + FolderStartFileIndex.Add(i); + } +} + +bool CMvDatabaseEx::Check() +{ + for (int v = 1; v < Volumes.Size(); v++) + { + const CDatabaseEx &db1 = Volumes[v]; + if (db1.IsTherePrevFolder()) + { + const CDatabaseEx &db0 = Volumes[v - 1]; + if (db0.Folders.IsEmpty() || db1.Folders.IsEmpty()) + return false; + const CFolder &f0 = db0.Folders.Back(); + const CFolder &f1 = db1.Folders.Front(); + if (f0.CompressionTypeMajor != f1.CompressionTypeMajor || + f0.CompressionTypeMinor != f1.CompressionTypeMinor) + return false; + } + } + UInt64 maxPos = 0; + int prevFolder = -2; + for(int i = 0; i < Items.Size(); i++) + { + const CMvItem &mvItem = Items[i]; + int fIndex = GetFolderIndex(&mvItem); + if (fIndex >= FolderStartFileIndex.Size()) + return false; + const CItem &item = Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; + if (item.IsDirectory()) + continue; + int folderIndex = GetFolderIndex(&mvItem); + if (folderIndex != prevFolder) + { + prevFolder = folderIndex; + maxPos = 0; + continue; + } + if (item.Offset < maxPos) + return false; + maxPos = item.GetEndOffset(); + if (maxPos < item.Offset) + return false; + } + return true; } }} |