mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 03:14:59 -06:00
325 lines
8.5 KiB
C++
Executable File
325 lines
8.5 KiB
C++
Executable File
// Archive/CabIn.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "CabIn.h"
|
|
|
|
#include "../Common/FindSignature.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 *stream,
|
|
const UInt64 *searchHeaderSizeLimit,
|
|
CDatabase &database)
|
|
{
|
|
database.Clear();
|
|
RINOK(stream->Seek(0, STREAM_SEEK_SET, &database.StartPosition));
|
|
|
|
RINOK(FindSignatureInStream(stream, NHeader::kMarker, NHeader::kMarkerSize,
|
|
searchHeaderSizeLimit, database.StartPosition));
|
|
|
|
RINOK(stream->Seek(database.StartPosition + NHeader::kMarkerSize, STREAM_SEEK_SET, NULL));
|
|
if (!inBuffer.Create(1 << 17))
|
|
return E_OUTOFMEMORY;
|
|
inBuffer.SetStream(stream);
|
|
inBuffer.Init();
|
|
|
|
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();
|
|
if (archiveInfo.Flags > 7)
|
|
return S_FALSE;
|
|
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(stream->Seek(database.StartPosition + archiveInfo.FileHeadersOffset, STREAM_SEEK_SET, NULL));
|
|
|
|
inBuffer.SetStream(stream);
|
|
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.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));
|
|
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.IsDir())
|
|
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;
|
|
}
|
|
|
|
}}
|