| // Archive/CabIn.cpp |
| |
| #include "StdAfx.h" |
| |
| #include "../Common/FindSignature.h" |
| |
| #include "CabIn.h" |
| |
| namespace NArchive { |
| namespace NCab { |
| |
| Byte CInArchive::Read8() |
| { |
| Byte b; |
| if (!inBuffer.ReadByte(b)) |
| throw CInArchiveException(CInArchiveException::kUnsupported); |
| return b; |
| } |
| |
| UInt16 CInArchive::Read16() |
| { |
| UInt16 value = 0; |
| for (int i = 0; i < 2; i++) |
| { |
| Byte b = Read8(); |
| value |= (UInt16(b) << (8 * i)); |
| } |
| return value; |
| } |
| |
| UInt32 CInArchive::Read32() |
| { |
| UInt32 value = 0; |
| for (int i = 0; i < 4; i++) |
| { |
| Byte b = Read8(); |
| value |= (UInt32(b) << (8 * i)); |
| } |
| return value; |
| } |
| |
| AString CInArchive::SafeReadName() |
| { |
| AString name; |
| for (;;) |
| { |
| Byte b = Read8(); |
| if (b == 0) |
| return name; |
| name += (char)b; |
| } |
| } |
| |
| void CInArchive::ReadOtherArchive(COtherArchive &oa) |
| { |
| oa.FileName = SafeReadName(); |
| oa.DiskName = SafeReadName(); |
| } |
| |
| void CInArchive::Skip(UInt32 size) |
| { |
| while (size-- != 0) |
| Read8(); |
| } |
| |
| HRESULT CInArchive::Open(const UInt64 *searchHeaderSizeLimit, CDatabaseEx &db) |
| { |
| IInStream *stream = db.Stream; |
| db.Clear(); |
| RINOK(stream->Seek(0, STREAM_SEEK_SET, &db.StartPosition)); |
| |
| RINOK(FindSignatureInStream(stream, NHeader::kMarker, NHeader::kMarkerSize, |
| searchHeaderSizeLimit, db.StartPosition)); |
| |
| RINOK(stream->Seek(db.StartPosition + NHeader::kMarkerSize, STREAM_SEEK_SET, NULL)); |
| if (!inBuffer.Create(1 << 17)) |
| return E_OUTOFMEMORY; |
| inBuffer.SetStream(stream); |
| inBuffer.Init(); |
| |
| CInArchiveInfo &ai = db.ArchiveInfo; |
| |
| ai.Size = Read32(); |
| if (Read32() != 0) |
| return S_FALSE; |
| ai.FileHeadersOffset = Read32(); |
| if (Read32() != 0) |
| return S_FALSE; |
| |
| ai.VersionMinor = Read8(); |
| ai.VersionMajor = Read8(); |
| ai.NumFolders = Read16(); |
| ai.NumFiles = Read16(); |
| ai.Flags = Read16(); |
| if (ai.Flags > 7) |
| return S_FALSE; |
| ai.SetID = Read16(); |
| ai.CabinetNumber = Read16(); |
| |
| if (ai.ReserveBlockPresent()) |
| { |
| ai.PerCabinetAreaSize = Read16(); |
| ai.PerFolderAreaSize = Read8(); |
| ai.PerDataBlockAreaSize = Read8(); |
| |
| Skip(ai.PerCabinetAreaSize); |
| } |
| |
| { |
| if (ai.IsTherePrev()) |
| ReadOtherArchive(ai.PrevArc); |
| if (ai.IsThereNext()) |
| ReadOtherArchive(ai.NextArc); |
| } |
| |
| int i; |
| for (i = 0; i < ai.NumFolders; i++) |
| { |
| CFolder folder; |
| |
| folder.DataStart = Read32(); |
| folder.NumDataBlocks = Read16(); |
| folder.CompressionTypeMajor = Read8(); |
| folder.CompressionTypeMinor = Read8(); |
| |
| Skip(ai.PerFolderAreaSize); |
| db.Folders.Add(folder); |
| } |
| |
| RINOK(stream->Seek(db.StartPosition + ai.FileHeadersOffset, STREAM_SEEK_SET, NULL)); |
| |
| inBuffer.SetStream(stream); |
| inBuffer.Init(); |
| for (i = 0; i < ai.NumFiles; i++) |
| { |
| CItem item; |
| item.Size = Read32(); |
| item.Offset = Read32(); |
| item.FolderIndex = Read16(); |
| UInt16 pureDate = Read16(); |
| UInt16 pureTime = Read16(); |
| item.Time = ((UInt32(pureDate) << 16)) | pureTime; |
| item.Attributes = Read16(); |
| item.Name = SafeReadName(); |
| int folderIndex = item.GetFolderIndex(db.Folders.Size()); |
| if (folderIndex >= db.Folders.Size()) |
| return S_FALSE; |
| db.Items.Add(item); |
| } |
| return S_OK; |
| } |
| |
| #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } |
| |
| 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.IsDir(); |
| bool isDir2 = item2.IsDir(); |
| 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)); |
| RINOZ(MyCompare(p1->VolumeIndex, p2->VolumeIndex)); |
| return MyCompare(p1->ItemIndex, p2->ItemIndex); |
| } |
| |
| 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];; |
| return GetFolderIndex(p1) == GetFolderIndex(p2) && |
| item1.Offset == item2.Offset && |
| item1.Size == item2.Size && |
| item1.Name == item2.Name; |
| } |
| |
| 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++) |
| { |
| int folderIndex = GetFolderIndex(&Items[i]); |
| 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; |
| } |
| } |
| UInt32 beginPos = 0; |
| UInt64 endPos = 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.IsDir()) |
| continue; |
| int folderIndex = GetFolderIndex(&mvItem); |
| if (folderIndex != prevFolder) |
| prevFolder = folderIndex; |
| else if (item.Offset < endPos && |
| (item.Offset != beginPos || item.GetEndOffset() != endPos)) |
| return false; |
| beginPos = item.Offset; |
| endPos = item.GetEndOffset(); |
| } |
| return true; |
| } |
| |
| }} |