mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-10 22:07:08 -06:00
256 lines
7.5 KiB
C++
Executable File
256 lines
7.5 KiB
C++
Executable File
// Archive/CabIn.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "Common/StringConvert.h"
|
|
#include "Common/MyCom.h"
|
|
#include "CabIn.h"
|
|
#include "Windows/Defs.h"
|
|
#include "../../Common/InBuffer.h"
|
|
|
|
namespace NArchive{
|
|
namespace NCab{
|
|
|
|
static HRESULT ReadBytes(IInStream *inStream, void *data, UInt32 size)
|
|
{
|
|
UInt32 realProcessedSize;
|
|
RINOK(inStream->Read(data, size, &realProcessedSize));
|
|
if(realProcessedSize != size)
|
|
return S_FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT SafeRead(IInStream *inStream, void *data, UInt32 size)
|
|
{
|
|
UInt32 realProcessedSize;
|
|
RINOK(inStream->Read(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);
|
|
}
|
|
|
|
static void SafeReadName(::CInBuffer &inBuffer, AString &name)
|
|
{
|
|
name.Empty();
|
|
while(true)
|
|
{
|
|
Byte b;
|
|
if (!inBuffer.ReadByte(b))
|
|
throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive);
|
|
if (b == 0)
|
|
return;
|
|
name += char(b);
|
|
}
|
|
}
|
|
|
|
Byte CInArchive::ReadByte()
|
|
{
|
|
if (_blockPos >= _blockSize)
|
|
throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive);
|
|
return _block[_blockPos++];
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
HRESULT CInArchive::Open(IInStream *inStream,
|
|
const UInt64 *searchHeaderSizeLimit,
|
|
CInArchiveInfo &inArchiveInfo,
|
|
CObjectVector<NHeader::CFolder> &folders,
|
|
CObjectVector<CItem> &files,
|
|
CProgressVirt *progressVirt)
|
|
{
|
|
UInt64 startPosition;
|
|
RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &startPosition));
|
|
|
|
// NHeader::NArchive::CBlock archiveHeader;
|
|
|
|
{
|
|
::CInBuffer inBuffer;
|
|
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;
|
|
while(true)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
startPosition += inBuffer.GetProcessedSize() - kSignatureSize;
|
|
}
|
|
RINOK(inStream->Seek(startPosition, STREAM_SEEK_SET, NULL));
|
|
|
|
RINOK(ReadBytes(inStream, _block, NHeader::NArchive::kArchiveHeaderSize));
|
|
_blockSize = NHeader::NArchive::kArchiveHeaderSize;
|
|
_blockPos = 0;
|
|
|
|
ReadUInt32(); // Signature; // cabinet file signature
|
|
// if (archiveHeader.Signature != NHeader::NArchive::kSignature)
|
|
// return S_FALSE;
|
|
|
|
UInt32 reserved1 = ReadUInt32();
|
|
UInt32 size = ReadUInt32(); // size of this cabinet file in bytes
|
|
UInt32 reserved2 = ReadUInt32();
|
|
UInt32 fileOffset = ReadUInt32(); // offset of the first CFFILE entry
|
|
UInt32 reserved3 = ReadUInt32();
|
|
|
|
inArchiveInfo.VersionMinor = ReadByte(); // cabinet file format version, minor
|
|
inArchiveInfo.VersionMajor = ReadByte(); // cabinet file format version, major
|
|
inArchiveInfo.NumFolders = ReadUInt16(); // number of CFFOLDER entries in this cabinet
|
|
inArchiveInfo.NumFiles = ReadUInt16(); // number of CFFILE entries in this cabinet
|
|
inArchiveInfo.Flags = ReadUInt16(); // number of CFFILE entries in this cabinet
|
|
UInt16 setID = ReadUInt16(); // must be the same for all cabinets in a set
|
|
UInt16 cabinetNumber = ReadUInt16(); // number of this cabinet file in a set
|
|
|
|
if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0)
|
|
throw CInArchiveException(CInArchiveException::kUnsupported);
|
|
|
|
if (inArchiveInfo.ReserveBlockPresent())
|
|
{
|
|
RINOK(SafeRead(inStream, _block, NHeader::NArchive::kPerDataSizesHeaderSize));
|
|
_blockSize = NHeader::NArchive::kPerDataSizesHeaderSize;
|
|
_blockPos = 0;
|
|
|
|
inArchiveInfo.PerDataSizes.PerCabinetAreaSize = ReadUInt16(); // (optional) size of per-cabinet reserved area
|
|
inArchiveInfo.PerDataSizes.PerFolderAreaSize = ReadByte(); // (optional) size of per-folder reserved area
|
|
inArchiveInfo.PerDataSizes.PerDatablockAreaSize = ReadByte(); // (optional) size of per-datablock reserved area
|
|
RINOK(inStream->Seek(inArchiveInfo.PerDataSizes.PerCabinetAreaSize,
|
|
STREAM_SEEK_CUR, NULL));
|
|
}
|
|
|
|
{
|
|
UInt64 foldersStartPosition;
|
|
RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &foldersStartPosition));
|
|
::CInBuffer inBuffer;
|
|
if (!inBuffer.Create(1 << 17))
|
|
return E_OUTOFMEMORY;
|
|
inBuffer.SetStream(inStream);
|
|
inBuffer.Init();
|
|
if ((inArchiveInfo.Flags & NHeader::NArchive::NFlags::kPrevCabinet) != 0)
|
|
{
|
|
SafeReadName(inBuffer, inArchiveInfo.PreviousCabinetName);
|
|
SafeReadName(inBuffer, inArchiveInfo.PreviousDiskName);
|
|
}
|
|
if ((inArchiveInfo.Flags & NHeader::NArchive::NFlags::kNextCabinet) != 0)
|
|
{
|
|
SafeReadName(inBuffer, inArchiveInfo.NextCabinetName);
|
|
SafeReadName(inBuffer, inArchiveInfo.NextDiskName);
|
|
}
|
|
foldersStartPosition += inBuffer.GetProcessedSize();
|
|
RINOK(inStream->Seek(foldersStartPosition, STREAM_SEEK_SET, NULL));
|
|
}
|
|
|
|
if (progressVirt != NULL)
|
|
{
|
|
UInt64 numFiles = inArchiveInfo.NumFiles;
|
|
RINOK(progressVirt->SetTotal(&numFiles));
|
|
}
|
|
folders.Clear();
|
|
int i;
|
|
for(i = 0; i < inArchiveInfo.NumFolders; i++)
|
|
{
|
|
if (progressVirt != NULL)
|
|
{
|
|
UInt64 numFiles = 0;
|
|
RINOK(progressVirt->SetCompleted(&numFiles));
|
|
}
|
|
NHeader::CFolder folder;
|
|
RINOK(SafeRead(inStream, _block, NHeader::kFolderHeaderSize));
|
|
_blockSize = NHeader::kFolderHeaderSize;
|
|
_blockPos = 0;
|
|
|
|
folder.DataStart = ReadUInt32();
|
|
folder.NumDataBlocks = ReadUInt16();
|
|
folder.CompressionTypeMajor = ReadByte();
|
|
folder.CompressionTypeMinor = ReadByte();
|
|
|
|
if (inArchiveInfo.ReserveBlockPresent())
|
|
{
|
|
RINOK(inStream->Seek(
|
|
inArchiveInfo.PerDataSizes.PerFolderAreaSize, STREAM_SEEK_CUR, NULL));
|
|
}
|
|
folder.DataStart += (UInt32)startPosition;
|
|
folders.Add(folder);
|
|
}
|
|
|
|
RINOK(inStream->Seek(startPosition + fileOffset,
|
|
STREAM_SEEK_SET, NULL));
|
|
|
|
::CInBuffer inBuffer;
|
|
if (!inBuffer.Create(1 << 17))
|
|
return E_OUTOFMEMORY;
|
|
inBuffer.SetStream(inStream);
|
|
inBuffer.Init();
|
|
files.Clear();
|
|
if (progressVirt != NULL)
|
|
{
|
|
UInt64 numFiles = files.Size();
|
|
RINOK(progressVirt->SetCompleted(&numFiles));
|
|
}
|
|
for(i = 0; i < inArchiveInfo.NumFiles; i++)
|
|
{
|
|
SafeInByteRead(inBuffer, _block, NHeader::kFileHeaderSize);
|
|
_blockSize = NHeader::kFileHeaderSize;
|
|
_blockPos = 0;
|
|
CItem item;
|
|
item.UnPackSize = ReadUInt32();
|
|
item.UnPackOffset = ReadUInt32();
|
|
item.FolderIndex = ReadUInt16();
|
|
UInt16 pureDate = ReadUInt16();
|
|
UInt16 pureTime = ReadUInt16();
|
|
item.Time = ((UInt32(pureDate) << 16)) | pureTime;
|
|
item.Attributes = ReadUInt16();
|
|
SafeReadName(inBuffer, item.Name);
|
|
files.Add(item);
|
|
if (progressVirt != NULL)
|
|
{
|
|
UInt64 numFiles = files.Size();
|
|
RINOK(progressVirt->SetCompleted(&numFiles));
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
}}
|