mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-08 16:07:04 -06:00
273 lines
6.5 KiB
C++
Executable File
273 lines
6.5 KiB
C++
Executable File
// 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;
|
|
}
|
|
|
|
}}
|