492 lines
12 KiB
C++
492 lines
12 KiB
C++
// Archive/CabIn.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
// #include <stdio.h>
|
|
|
|
#include "../../../../C/CpuArch.h"
|
|
|
|
#include "../../Common/LimitedStreams.h"
|
|
#include "../../Common/StreamUtils.h"
|
|
|
|
#include "CabIn.h"
|
|
|
|
#define Get16(p) GetUi16(p)
|
|
#define Get32(p) GetUi32(p)
|
|
|
|
namespace NArchive {
|
|
namespace NCab {
|
|
|
|
struct CUnexpectedEndException {};
|
|
|
|
void CInArchive::Skip(unsigned size)
|
|
{
|
|
if (_inBuffer.Skip(size) != size)
|
|
throw CUnexpectedEndException();
|
|
}
|
|
|
|
void CInArchive::Read(Byte *data, unsigned size)
|
|
{
|
|
if (_inBuffer.ReadBytes(data, size) != size)
|
|
throw CUnexpectedEndException();
|
|
}
|
|
|
|
void CInArchive::ReadName(AString &s)
|
|
{
|
|
for (size_t i = 0; i < ((size_t)1 << 13); i++)
|
|
{
|
|
Byte b;
|
|
if (!_inBuffer.ReadByte(b))
|
|
throw CUnexpectedEndException();
|
|
if (b == 0)
|
|
{
|
|
s.SetFrom((const char *)(const Byte *)_tempBuf, (unsigned)i);
|
|
return;
|
|
}
|
|
if (_tempBuf.Size() == i)
|
|
_tempBuf.ChangeSize_KeepData(i * 2, i);
|
|
_tempBuf[i] = b;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
Byte b;
|
|
if (!_inBuffer.ReadByte(b))
|
|
throw CUnexpectedEndException();
|
|
if (b == 0)
|
|
break;
|
|
}
|
|
|
|
ErrorInNames = true;
|
|
s = "[ERROR-LONG-PATH]";
|
|
}
|
|
|
|
void CInArchive::ReadOtherArc(COtherArc &oa)
|
|
{
|
|
ReadName(oa.FileName);
|
|
ReadName(oa.DiskName);
|
|
}
|
|
|
|
|
|
struct CSignatureFinder
|
|
{
|
|
Byte *Buf;
|
|
UInt32 Pos;
|
|
UInt32 End;
|
|
const Byte *Signature;
|
|
UInt32 SignatureSize;
|
|
|
|
UInt32 _HeaderSize;
|
|
UInt32 _AlignSize;
|
|
UInt32 _BufUseCapacity;
|
|
|
|
ISequentialInStream *Stream;
|
|
UInt64 Processed; // Global offset of start of Buf
|
|
|
|
const UInt64 *SearchLimit;
|
|
|
|
UInt32 GetTotalCapacity(UInt32 basicSize, UInt32 headerSize)
|
|
{
|
|
_HeaderSize = headerSize;
|
|
for (_AlignSize = (1 << 5); _AlignSize < _HeaderSize; _AlignSize <<= 1);
|
|
_BufUseCapacity = basicSize + _AlignSize;
|
|
return _BufUseCapacity + 16;
|
|
}
|
|
|
|
/*
|
|
returns:
|
|
S_OK - signature found (at Pos)
|
|
S_FALSE - signature not found
|
|
*/
|
|
HRESULT Find();
|
|
};
|
|
|
|
|
|
HRESULT CSignatureFinder::Find()
|
|
{
|
|
for (;;)
|
|
{
|
|
Buf[End] = Signature[0]; // it's for fast search;
|
|
|
|
while (End - Pos >= _HeaderSize)
|
|
{
|
|
const Byte *p = Buf + Pos;
|
|
Byte b = Signature[0];
|
|
for (;;)
|
|
{
|
|
if (*p == b) break; p++;
|
|
if (*p == b) break; p++;
|
|
}
|
|
Pos = (UInt32)(p - Buf);
|
|
if (End - Pos < _HeaderSize)
|
|
{
|
|
Pos = End - _HeaderSize + 1;
|
|
break;
|
|
}
|
|
UInt32 i;
|
|
for (i = 1; i < SignatureSize && p[i] == Signature[i]; i++);
|
|
if (i == SignatureSize)
|
|
return S_OK;
|
|
Pos++;
|
|
}
|
|
|
|
if (Pos >= _AlignSize)
|
|
{
|
|
UInt32 num = (Pos & ~(_AlignSize - 1));
|
|
Processed += num;
|
|
Pos -= num;
|
|
End -= num;
|
|
memmove(Buf, Buf + num, End);
|
|
}
|
|
UInt32 rem = _BufUseCapacity - End;
|
|
if (SearchLimit)
|
|
{
|
|
if (Processed + Pos > *SearchLimit)
|
|
return S_FALSE;
|
|
UInt64 rem2 = *SearchLimit - (Processed + End) + _HeaderSize;
|
|
if (rem > rem2)
|
|
rem = (UInt32)rem2;
|
|
}
|
|
|
|
UInt32 processedSize;
|
|
if (Processed == 0 && rem == _BufUseCapacity - _HeaderSize)
|
|
rem -= _AlignSize; // to make reads more aligned.
|
|
RINOK(Stream->Read(Buf + End, rem, &processedSize));
|
|
if (processedSize == 0)
|
|
return S_FALSE;
|
|
End += processedSize;
|
|
}
|
|
}
|
|
|
|
|
|
bool CInArcInfo::Parse(const Byte *p)
|
|
{
|
|
if (Get32(p + 0x0C) != 0 ||
|
|
Get32(p + 0x14) != 0)
|
|
return false;
|
|
Size = Get32(p + 8);
|
|
if (Size < 36)
|
|
return false;
|
|
Flags = Get16(p + 0x1E);
|
|
if (Flags > 7)
|
|
return false;
|
|
FileHeadersOffset = Get32(p + 0x10);
|
|
if (FileHeadersOffset != 0 && FileHeadersOffset > Size)
|
|
return false;
|
|
VersionMinor = p[0x18];
|
|
VersionMajor = p[0x19];
|
|
NumFolders = Get16(p + 0x1A);
|
|
NumFiles = Get16(p + 0x1C);
|
|
return true;
|
|
}
|
|
|
|
|
|
HRESULT CInArchive::Open2(CDatabaseEx &db, const UInt64 *searchHeaderSizeLimit)
|
|
{
|
|
IsArc = false;
|
|
ErrorInNames = false;
|
|
UnexpectedEnd = false;
|
|
HeaderError = false;
|
|
|
|
db.Clear();
|
|
RINOK(db.Stream->Seek(0, STREAM_SEEK_CUR, &db.StartPosition));
|
|
// UInt64 temp = db.StartPosition;
|
|
|
|
CByteBuffer buffer;
|
|
CInArcInfo &ai = db.ArcInfo;
|
|
UInt64 startInBuf = 0;
|
|
|
|
CLimitedSequentialInStream *limitedStreamSpec = NULL;
|
|
CMyComPtr<ISequentialInStream> limitedStream;
|
|
|
|
// for (int iii = 0; iii < 10000; iii++)
|
|
{
|
|
// db.StartPosition = temp; RINOK(db.Stream->Seek(db.StartPosition, STREAM_SEEK_SET, NULL));
|
|
|
|
const UInt32 kMainHeaderSize = 32;
|
|
Byte header[kMainHeaderSize];
|
|
const UInt32 kBufSize = 1 << 15;
|
|
RINOK(ReadStream_FALSE(db.Stream, header, kMainHeaderSize));
|
|
if (memcmp(header, NHeader::kMarker, NHeader::kMarkerSize) == 0 && ai.Parse(header))
|
|
{
|
|
limitedStreamSpec = new CLimitedSequentialInStream;
|
|
limitedStream = limitedStreamSpec;
|
|
limitedStreamSpec->SetStream(db.Stream);
|
|
limitedStreamSpec->Init(ai.Size - NHeader::kMarkerSize);
|
|
buffer.Alloc(kBufSize);
|
|
memcpy(buffer, header, kMainHeaderSize);
|
|
UInt32 numProcessedBytes;
|
|
RINOK(limitedStream->Read(buffer + kMainHeaderSize, kBufSize - kMainHeaderSize, &numProcessedBytes));
|
|
_inBuffer.SetBuf(buffer, (UInt32)kBufSize, kMainHeaderSize + numProcessedBytes, kMainHeaderSize);
|
|
}
|
|
else
|
|
{
|
|
if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0)
|
|
return S_FALSE;
|
|
|
|
CSignatureFinder finder;
|
|
|
|
finder.Stream = db.Stream;
|
|
finder.Signature = NHeader::kMarker;
|
|
finder.SignatureSize = NHeader::kMarkerSize;
|
|
finder.SearchLimit = searchHeaderSizeLimit;
|
|
|
|
buffer.Alloc(finder.GetTotalCapacity(kBufSize, kMainHeaderSize));
|
|
finder.Buf = buffer;
|
|
|
|
memcpy(buffer, header, kMainHeaderSize);
|
|
finder.Processed = db.StartPosition;
|
|
finder.End = kMainHeaderSize;
|
|
finder.Pos = 1;
|
|
|
|
for (;;)
|
|
{
|
|
RINOK(finder.Find());
|
|
if (ai.Parse(finder.Buf + finder.Pos))
|
|
{
|
|
db.StartPosition = finder.Processed + finder.Pos;
|
|
limitedStreamSpec = new CLimitedSequentialInStream;
|
|
limitedStreamSpec->SetStream(db.Stream);
|
|
limitedStream = limitedStreamSpec;
|
|
UInt32 remInFinder = finder.End - finder.Pos;
|
|
if (ai.Size <= remInFinder)
|
|
{
|
|
limitedStreamSpec->Init(0);
|
|
finder.End = finder.Pos + ai.Size;
|
|
}
|
|
else
|
|
limitedStreamSpec->Init(ai.Size - remInFinder);
|
|
|
|
startInBuf = finder.Pos;
|
|
_inBuffer.SetBuf(buffer, (UInt32)kBufSize, finder.End, finder.Pos + kMainHeaderSize);
|
|
break;
|
|
}
|
|
finder.Pos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
IsArc = true;
|
|
|
|
_inBuffer.SetStream(limitedStream);
|
|
if (_tempBuf.Size() == 0)
|
|
_tempBuf.Alloc(1 << 12);
|
|
|
|
Byte p[16];
|
|
unsigned nextSize = 4 + (ai.ReserveBlockPresent() ? 4 : 0);
|
|
Read(p, nextSize);
|
|
ai.SetID = Get16(p);
|
|
ai.CabinetNumber = Get16(p + 2);
|
|
|
|
if (ai.ReserveBlockPresent())
|
|
{
|
|
ai.PerCabinet_AreaSize = Get16(p + 4);
|
|
ai.PerFolder_AreaSize = p[6];
|
|
ai.PerDataBlock_AreaSize = p[7];
|
|
Skip(ai.PerCabinet_AreaSize);
|
|
}
|
|
|
|
if (ai.IsTherePrev()) ReadOtherArc(ai.PrevArc);
|
|
if (ai.IsThereNext()) ReadOtherArc(ai.NextArc);
|
|
|
|
UInt32 i;
|
|
|
|
db.Folders.ClearAndReserve(ai.NumFolders);
|
|
|
|
for (i = 0; i < ai.NumFolders; i++)
|
|
{
|
|
Read(p, 8);
|
|
CFolder folder;
|
|
folder.DataStart = Get32(p);
|
|
folder.NumDataBlocks = Get16(p + 4);
|
|
folder.MethodMajor = p[6];
|
|
folder.MethodMinor = p[7];
|
|
Skip(ai.PerFolder_AreaSize);
|
|
db.Folders.AddInReserved(folder);
|
|
}
|
|
|
|
// for (int iii = 0; iii < 10000; iii++) {
|
|
|
|
if (_inBuffer.GetProcessedSize() - startInBuf != ai.FileHeadersOffset)
|
|
{
|
|
// printf("\n!!! Seek Error !!!!\n");
|
|
// fflush(stdout);
|
|
RINOK(db.Stream->Seek(db.StartPosition + ai.FileHeadersOffset, STREAM_SEEK_SET, NULL));
|
|
limitedStreamSpec->Init(ai.Size - ai.FileHeadersOffset);
|
|
_inBuffer.Init();
|
|
}
|
|
|
|
db.Items.ClearAndReserve(ai.NumFiles);
|
|
|
|
for (i = 0; i < ai.NumFiles; i++)
|
|
{
|
|
Read(p, 16);
|
|
CItem &item = db.Items.AddNewInReserved();
|
|
item.Size = Get32(p);
|
|
item.Offset = Get32(p + 4);
|
|
item.FolderIndex = Get16(p + 8);
|
|
UInt16 pureDate = Get16(p + 10);
|
|
UInt16 pureTime = Get16(p + 12);
|
|
item.Time = (((UInt32)pureDate << 16)) | pureTime;
|
|
item.Attributes = Get16(p + 14);
|
|
|
|
ReadName(item.Name);
|
|
|
|
if (item.GetFolderIndex(db.Folders.Size()) >= (int)db.Folders.Size())
|
|
{
|
|
HeaderError = true;
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
// }
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CInArchive::Open(CDatabaseEx &db, const UInt64 *searchHeaderSizeLimit)
|
|
{
|
|
try
|
|
{
|
|
return Open2(db, searchHeaderSizeLimit);
|
|
}
|
|
catch(const CInBufferException &e) { return e.ErrorCode; }
|
|
catch(CUnexpectedEndException &) { UnexpectedEnd = true; return S_FALSE; }
|
|
}
|
|
|
|
|
|
|
|
#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(unsigned i1, unsigned 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_VECTOR (v, Volumes)
|
|
{
|
|
const CDatabaseEx &db = Volumes[v];
|
|
int curOffset = offset;
|
|
if (db.IsTherePrevFolder())
|
|
curOffset--;
|
|
StartFolderOfVol.Add(curOffset);
|
|
offset += db.GetNumberOfNewFolders();
|
|
|
|
CMvItem mvItem;
|
|
mvItem.VolumeIndex = v;
|
|
FOR_VECTOR (i, db.Items)
|
|
{
|
|
mvItem.ItemIndex = i;
|
|
Items.Add(mvItem);
|
|
}
|
|
}
|
|
|
|
if (Items.Size() > 1)
|
|
{
|
|
Items.Sort(CompareMvItems, (void *)this);
|
|
unsigned j = 1;
|
|
unsigned i = 1;
|
|
for (; i < Items.Size(); i++)
|
|
if (!AreItemsEqual(i, i - 1))
|
|
Items[j++] = Items[i];
|
|
Items.DeleteFrom(j);
|
|
}
|
|
|
|
FOR_VECTOR (i, Items)
|
|
{
|
|
int folderIndex = GetFolderIndex(&Items[i]);
|
|
while (folderIndex >= (int)FolderStartFileIndex.Size())
|
|
FolderStartFileIndex.Add(i);
|
|
}
|
|
}
|
|
|
|
|
|
bool CMvDatabaseEx::Check()
|
|
{
|
|
for (unsigned 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.MethodMajor != f1.MethodMajor ||
|
|
f0.MethodMinor != f1.MethodMinor)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UInt32 beginPos = 0;
|
|
UInt64 endPos = 0;
|
|
int prevFolder = -2;
|
|
|
|
FOR_VECTOR (i, Items)
|
|
{
|
|
const CMvItem &mvItem = Items[i];
|
|
int fIndex = GetFolderIndex(&mvItem);
|
|
if (fIndex >= (int)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;
|
|
}
|
|
|
|
}}
|