diff options
Diffstat (limited to 'CPP/7zip/Archive/Cab/CabIn.cpp')
-rwxr-xr-x | CPP/7zip/Archive/Cab/CabIn.cpp | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Cab/CabIn.cpp b/CPP/7zip/Archive/Cab/CabIn.cpp new file mode 100755 index 00000000..ae774f19 --- /dev/null +++ b/CPP/7zip/Archive/Cab/CabIn.cpp @@ -0,0 +1,343 @@ +// Archive/CabIn.cpp + +#include "StdAfx.h" + +#include "Common/StringConvert.h" +#include "Common/MyCom.h" +#include "CabIn.h" +#include "Windows/Defs.h" + +#include "../../Common/StreamUtils.h" + +namespace NArchive{ +namespace NCab{ + +/* +static HRESULT ReadBytes(IInStream *inStream, void *data, UInt32 size) +{ + UInt32 realProcessedSize; + RINOK(ReadStream(inStream, data, size, &realProcessedSize)); + if(realProcessedSize != size) + return S_FALSE; + return S_OK; +} + +static HRESULT SafeRead(IInStream *inStream, void *data, UInt32 size) +{ + UInt32 realProcessedSize; + RINOK(ReadStream(inStream, data, size, &realProcessedSize)); + if(realProcessedSize != size) + throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); + return S_OK; +} + +static void SafeInByteRead(::CInBuffer &inBuffer, void *data, UInt32 size) +{ + UInt32 realProcessedSize; + inBuffer.ReadBytes(data, size, realProcessedSize); + if(realProcessedSize != size) + throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); +} +*/ + +Byte CInArchive::ReadByte() +{ + Byte b; + if (!inBuffer.ReadByte(b)) + throw CInArchiveException(CInArchiveException::kUnsupported); + return b; +} + +UInt16 CInArchive::ReadUInt16() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + { + Byte b = ReadByte(); + value |= (UInt16(b) << (8 * i)); + } + return value; +} + +UInt32 CInArchive::ReadUInt32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + { + Byte b = ReadByte(); + value |= (UInt32(b) << (8 * i)); + } + return value; +} + +AString CInArchive::SafeReadName() +{ + AString name; + for (;;) + { + Byte b = ReadByte(); + if (b == 0) + return name; + name += (char)b; + } +} + +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)); + + { + if (!inBuffer.Create(1 << 17)) + return E_OUTOFMEMORY; + inBuffer.SetStream(inStream); + inBuffer.Init(); + UInt64 value = 0; + const int kSignatureSize = 8; + UInt64 kSignature64 = NHeader::NArchive::kSignature; + for (;;) + { + Byte b; + if (!inBuffer.ReadByte(b)) + return S_FALSE; + value >>= 8; + value |= ((UInt64)b) << ((kSignatureSize - 1) * 8); + if (inBuffer.GetProcessedSize() >= kSignatureSize) + { + if (value == kSignature64) + break; + if (searchHeaderSizeLimit != NULL) + if (inBuffer.GetProcessedSize() > (*searchHeaderSizeLimit)) + return S_FALSE; + } + } + database.StartPosition += inBuffer.GetProcessedSize() - kSignatureSize; + } + + 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()) + { + 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); + } + + { + if (archiveInfo.IsTherePrev()) + ReadOtherArchive(archiveInfo.PreviousArchive); + if (archiveInfo.IsThereNext()) + ReadOtherArchive(archiveInfo.NextArchive); + } + + int i; + for(i = 0; i < archiveInfo.NumFolders; i++) + { + CFolder folder; + + folder.DataStart = ReadUInt32(); + folder.NumDataBlocks = ReadUInt16(); + folder.CompressionTypeMajor = ReadByte(); + folder.CompressionTypeMinor = ReadByte(); + + Skeep(archiveInfo.PerFolderAreaSize); + database.Folders.Add(folder); + } + + RINOK(inStream->Seek(database.StartPosition + archiveInfo.FileHeadersOffset, STREAM_SEEK_SET, NULL)); + + inBuffer.SetStream(inStream); + inBuffer.Init(); + for(i = 0; i < archiveInfo.NumFiles; i++) + { + CItem item; + item.Size = ReadUInt32(); + item.Offset = ReadUInt32(); + item.FolderIndex = ReadUInt16(); + UInt16 pureDate = ReadUInt16(); + UInt16 pureTime = ReadUInt16(); + item.Time = ((UInt32(pureDate) << 16)) | pureTime; + item.Attributes = ReadUInt16(); + 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++) + { + mvItem.ItemIndex = i; + Items.Add(mvItem); + } + } + + 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; +} + +}} |