mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 09:14:58 -06:00
799 lines
20 KiB
C++
Executable File
799 lines
20 KiB
C++
Executable File
// ArjHandler.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "../../../C/CpuArch.h"
|
|
|
|
#include "Common/ComTry.h"
|
|
#include "Common/StringConvert.h"
|
|
|
|
#include "Windows/PropVariant.h"
|
|
#include "Windows/Time.h"
|
|
|
|
#include "../Common/LimitedStreams.h"
|
|
#include "../Common/ProgressUtils.h"
|
|
#include "../Common/RegisterArc.h"
|
|
#include "../Common/StreamObjects.h"
|
|
#include "../Common/StreamUtils.h"
|
|
|
|
#include "../Compress/ArjDecoder1.h"
|
|
#include "../Compress/ArjDecoder2.h"
|
|
#include "../Compress/CopyCoder.h"
|
|
|
|
#include "Common/ItemNameUtils.h"
|
|
#include "Common/OutStreamWithCRC.h"
|
|
|
|
using namespace NWindows;
|
|
|
|
#define Get16(p) GetUi16(p)
|
|
#define Get32(p) GetUi32(p)
|
|
|
|
namespace NArchive {
|
|
namespace NArj {
|
|
|
|
const int kBlockSizeMin = 30;
|
|
const int kBlockSizeMax = 2600;
|
|
|
|
namespace NSignature
|
|
{
|
|
const Byte kSig0 = 0x60;
|
|
const Byte kSig1 = 0xEA;
|
|
}
|
|
|
|
namespace NFileHeader
|
|
{
|
|
namespace NCompressionMethod
|
|
{
|
|
enum
|
|
{
|
|
kStored = 0,
|
|
kCompressed1a = 1,
|
|
kCompressed1b = 2,
|
|
kCompressed1c = 3,
|
|
kCompressed2 = 4,
|
|
kNoDataNoCRC = 8,
|
|
kNoData = 9
|
|
};
|
|
}
|
|
|
|
namespace NFileType
|
|
{
|
|
enum
|
|
{
|
|
kBinary = 0,
|
|
k7BitText = 1,
|
|
kArchiveHeader = 2,
|
|
kDirectory = 3,
|
|
kVolumeLablel = 4,
|
|
kChapterLabel = 5
|
|
};
|
|
}
|
|
|
|
namespace NFlags
|
|
{
|
|
const Byte kGarbled = 1;
|
|
const Byte kVolume = 4;
|
|
const Byte kExtFile = 8;
|
|
const Byte kPathSym = 0x10;
|
|
const Byte kBackup = 0x20;
|
|
}
|
|
|
|
namespace NHostOS
|
|
{
|
|
enum EEnum
|
|
{
|
|
kMSDOS = 0, // filesystem used by MS-DOS, OS/2, Win32
|
|
// pkarj 2.50 (FAT / VFAT / FAT32 file systems)
|
|
kPRIMOS,
|
|
kUnix,
|
|
kAMIGA,
|
|
kMac,
|
|
kOS_2,
|
|
kAPPLE_GS,
|
|
kAtari_ST,
|
|
kNext,
|
|
kVAX_VMS,
|
|
kWIN95
|
|
};
|
|
}
|
|
}
|
|
|
|
struct CArchiveHeader
|
|
{
|
|
// Byte ArchiverVersion;
|
|
// Byte ExtractVersion;
|
|
Byte HostOS;
|
|
// Byte Flags;
|
|
// Byte SecuryVersion;
|
|
// Byte FileType;
|
|
// Byte Reserved;
|
|
UInt32 CTime;
|
|
UInt32 MTime;
|
|
UInt32 ArchiveSize;
|
|
// UInt32 SecurityEnvelopeFilePosition;
|
|
// UInt16 FilespecPositionInFilename;
|
|
// UInt16 LengthOfSecurityEnvelopeSata;
|
|
// Byte EncryptionVersion;
|
|
// Byte LastChapter;
|
|
AString Name;
|
|
AString Comment;
|
|
|
|
HRESULT Parse(const Byte *p, unsigned size);
|
|
};
|
|
|
|
static HRESULT ReadString(const Byte *p, unsigned &size, AString &res)
|
|
{
|
|
AString s;
|
|
for (unsigned i = 0; i < size;)
|
|
{
|
|
char c = (char)p[i++];
|
|
if (c == 0)
|
|
{
|
|
size = i;
|
|
res = s;
|
|
return S_OK;
|
|
}
|
|
s += c;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT CArchiveHeader::Parse(const Byte *p, unsigned size)
|
|
{
|
|
if (size < kBlockSizeMin)
|
|
return S_FALSE;
|
|
Byte firstHeaderSize = p[0];
|
|
if (firstHeaderSize > size)
|
|
return S_FALSE;
|
|
// ArchiverVersion = p[1];
|
|
// ExtractVersion = p[2];
|
|
HostOS = p[3];
|
|
// Flags = p[4];
|
|
// SecuryVersion = p[5];
|
|
if (p[6] != NFileHeader::NFileType::kArchiveHeader)
|
|
return S_FALSE;
|
|
// Reserved = p[7];
|
|
CTime = Get32(p + 8);
|
|
MTime = Get32(p + 12);
|
|
ArchiveSize = Get32(p + 16);
|
|
// SecurityEnvelopeFilePosition = Get32(p + 20);
|
|
// UInt16 filespecPositionInFilename = Get16(p + 24);
|
|
// LengthOfSecurityEnvelopeSata = Get16(p + 26);
|
|
// EncryptionVersion = p[28];
|
|
// LastChapter = p[29];
|
|
unsigned pos = firstHeaderSize;
|
|
unsigned size1 = size - pos;
|
|
RINOK(ReadString(p + pos, size1, Name));
|
|
pos += size1;
|
|
size1 = size - pos;
|
|
RINOK(ReadString(p + pos, size1, Comment));
|
|
pos += size1;
|
|
return S_OK;
|
|
}
|
|
|
|
struct CItem
|
|
{
|
|
AString Name;
|
|
AString Comment;
|
|
|
|
UInt32 MTime;
|
|
UInt32 PackSize;
|
|
UInt32 Size;
|
|
UInt32 FileCRC;
|
|
UInt32 SplitPos;
|
|
|
|
Byte Version;
|
|
Byte ExtractVersion;
|
|
Byte HostOS;
|
|
Byte Flags;
|
|
Byte Method;
|
|
Byte FileType;
|
|
|
|
// UInt16 FilespecPositionInFilename;
|
|
UInt16 FileAccessMode;
|
|
// Byte FirstChapter;
|
|
// Byte LastChapter;
|
|
|
|
UInt64 DataPosition;
|
|
|
|
bool IsEncrypted() const { return (Flags & NFileHeader::NFlags::kGarbled) != 0; }
|
|
bool IsDir() const { return (FileType == NFileHeader::NFileType::kDirectory); }
|
|
bool IsSplitAfter() const { return (Flags & NFileHeader::NFlags::kVolume) != 0; }
|
|
bool IsSplitBefore() const { return (Flags & NFileHeader::NFlags::kExtFile) != 0; }
|
|
UInt32 GetWinAttributes() const
|
|
{
|
|
UInt32 winAtrributes;
|
|
switch(HostOS)
|
|
{
|
|
case NFileHeader::NHostOS::kMSDOS:
|
|
case NFileHeader::NHostOS::kWIN95:
|
|
winAtrributes = FileAccessMode;
|
|
break;
|
|
default:
|
|
winAtrributes = 0;
|
|
}
|
|
if (IsDir())
|
|
winAtrributes |= FILE_ATTRIBUTE_DIRECTORY;
|
|
return winAtrributes;
|
|
}
|
|
|
|
HRESULT Parse(const Byte *p, unsigned size);
|
|
};
|
|
|
|
HRESULT CItem::Parse(const Byte *p, unsigned size)
|
|
{
|
|
if (size < kBlockSizeMin)
|
|
return S_FALSE;
|
|
|
|
Byte firstHeaderSize = p[0];
|
|
|
|
Version = p[1];
|
|
ExtractVersion = p[2];
|
|
HostOS = p[3];
|
|
Flags = p[4];
|
|
Method = p[5];
|
|
FileType = p[6];
|
|
// Reserved = p[7];
|
|
MTime = Get32(p + 8);
|
|
PackSize = Get32(p + 12);
|
|
Size = Get32(p + 16);
|
|
FileCRC = Get32(p + 20);
|
|
// FilespecPositionInFilename = Get16(p + 24);
|
|
FileAccessMode = Get16(p + 26);
|
|
// FirstChapter = p[28];
|
|
// FirstChapter = p[29];
|
|
|
|
SplitPos = 0;
|
|
if (IsSplitBefore() && firstHeaderSize >= 34)
|
|
SplitPos = Get32(p + 30);
|
|
|
|
unsigned pos = firstHeaderSize;
|
|
unsigned size1 = size - pos;
|
|
RINOK(ReadString(p + pos, size1, Name));
|
|
pos += size1;
|
|
size1 = size - pos;
|
|
RINOK(ReadString(p + pos, size1, Comment));
|
|
pos += size1;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
struct CInArchiveException
|
|
{
|
|
enum CCauseType
|
|
{
|
|
kUnexpectedEndOfArchive = 0,
|
|
kCRCError,
|
|
kIncorrectArchive
|
|
}
|
|
Cause;
|
|
CInArchiveException(CCauseType cause): Cause(cause) {};
|
|
};
|
|
|
|
class CInArchive
|
|
{
|
|
UInt32 _blockSize;
|
|
Byte _block[kBlockSizeMax + 4];
|
|
|
|
HRESULT ReadBlock(bool &filled);
|
|
HRESULT ReadSignatureAndBlock(bool &filled);
|
|
HRESULT SkipExtendedHeaders();
|
|
|
|
HRESULT SafeReadBytes(void *data, UInt32 size);
|
|
|
|
public:
|
|
CArchiveHeader Header;
|
|
|
|
IInStream *Stream;
|
|
IArchiveOpenCallback *Callback;
|
|
UInt64 NumFiles;
|
|
UInt64 NumBytes;
|
|
|
|
HRESULT Open(const UInt64 *searchHeaderSizeLimit);
|
|
HRESULT GetNextItem(bool &filled, CItem &item);
|
|
};
|
|
|
|
static inline bool TestMarkerCandidate(const Byte *p, unsigned maxSize)
|
|
{
|
|
if (p[0] != NSignature::kSig0 || p[1] != NSignature::kSig1)
|
|
return false;
|
|
UInt32 blockSize = Get16(p + 2);
|
|
p += 4;
|
|
if (p[6] != NFileHeader::NFileType::kArchiveHeader ||
|
|
p[0] > blockSize ||
|
|
maxSize < 2 + 2 + blockSize + 4 ||
|
|
blockSize < kBlockSizeMin || blockSize > kBlockSizeMax ||
|
|
p[28] > 8) // EncryptionVersion
|
|
return false;
|
|
// return (Get32(p + blockSize) == CrcCalc(p, blockSize));
|
|
return true;
|
|
}
|
|
|
|
static HRESULT FindAndReadMarker(ISequentialInStream *stream, const UInt64 *searchHeaderSizeLimit, UInt64 &position)
|
|
{
|
|
position = 0;
|
|
|
|
const int kMarkerSizeMin = 2 + 2 + kBlockSizeMin + 4;
|
|
const int kMarkerSizeMax = 2 + 2 + kBlockSizeMax + 4;
|
|
|
|
CByteBuffer byteBuffer;
|
|
const UInt32 kBufSize = 1 << 16;
|
|
byteBuffer.SetCapacity(kBufSize);
|
|
Byte *buf = byteBuffer;
|
|
|
|
size_t processedSize = kMarkerSizeMax;
|
|
RINOK(ReadStream(stream, buf, &processedSize));
|
|
if (processedSize < kMarkerSizeMin)
|
|
return S_FALSE;
|
|
if (TestMarkerCandidate(buf, (unsigned)processedSize))
|
|
return S_OK;
|
|
|
|
UInt32 numBytesPrev = (UInt32)processedSize - 1;
|
|
memmove(buf, buf + 1, numBytesPrev);
|
|
UInt64 curTestPos = 1;
|
|
for (;;)
|
|
{
|
|
if (searchHeaderSizeLimit != NULL)
|
|
if (curTestPos > *searchHeaderSizeLimit)
|
|
return S_FALSE;
|
|
processedSize = kBufSize - numBytesPrev;
|
|
RINOK(ReadStream(stream, buf + numBytesPrev, &processedSize));
|
|
UInt32 numBytesInBuffer = numBytesPrev + (UInt32)processedSize;
|
|
if (numBytesInBuffer < kMarkerSizeMin)
|
|
return S_FALSE;
|
|
UInt32 numTests = numBytesInBuffer - kMarkerSizeMin + 1;
|
|
UInt32 pos;
|
|
for (pos = 0; pos < numTests; pos++)
|
|
{
|
|
for (; buf[pos] != NSignature::kSig0 && pos < numTests; pos++);
|
|
if (pos == numTests)
|
|
break;
|
|
if (TestMarkerCandidate(buf + pos, numBytesInBuffer - pos))
|
|
{
|
|
position = curTestPos + pos;
|
|
return S_OK;
|
|
}
|
|
}
|
|
curTestPos += pos;
|
|
numBytesPrev = numBytesInBuffer - numTests;
|
|
memmove(buf, buf + numTests, numBytesPrev);
|
|
}
|
|
}
|
|
|
|
HRESULT CInArchive::SafeReadBytes(void *data, UInt32 size)
|
|
{
|
|
size_t processed = size;
|
|
RINOK(ReadStream(Stream, data, &processed));
|
|
if (processed != size)
|
|
throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CInArchive::ReadBlock(bool &filled)
|
|
{
|
|
filled = false;
|
|
Byte buf[2];
|
|
RINOK(SafeReadBytes(buf, 2));
|
|
_blockSize = Get16(buf);
|
|
if (_blockSize == 0)
|
|
return S_OK;
|
|
if (_blockSize > kBlockSizeMax)
|
|
throw CInArchiveException(CInArchiveException::kIncorrectArchive);
|
|
RINOK(SafeReadBytes(_block, _blockSize + 4));
|
|
NumBytes += _blockSize + 6;
|
|
if (Get32(_block + _blockSize) != CrcCalc(_block, _blockSize))
|
|
throw CInArchiveException(CInArchiveException::kCRCError);
|
|
filled = true;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CInArchive::ReadSignatureAndBlock(bool &filled)
|
|
{
|
|
Byte id[2];
|
|
RINOK(SafeReadBytes(id, 2));
|
|
if (id[0] != NSignature::kSig0 || id[1] != NSignature::kSig1)
|
|
throw CInArchiveException(CInArchiveException::kIncorrectArchive);
|
|
return ReadBlock(filled);
|
|
}
|
|
|
|
HRESULT CInArchive::SkipExtendedHeaders()
|
|
{
|
|
for (UInt32 i = 0;; i++)
|
|
{
|
|
bool filled;
|
|
RINOK(ReadBlock(filled));
|
|
if (!filled)
|
|
return S_OK;
|
|
if (Callback && (i & 0xFF) == 0)
|
|
RINOK(Callback->SetCompleted(&NumFiles, &NumBytes));
|
|
}
|
|
}
|
|
|
|
HRESULT CInArchive::Open(const UInt64 *searchHeaderSizeLimit)
|
|
{
|
|
UInt64 position = 0;
|
|
RINOK(FindAndReadMarker(Stream, searchHeaderSizeLimit, position));
|
|
RINOK(Stream->Seek(position, STREAM_SEEK_SET, NULL));
|
|
bool filled;
|
|
RINOK(ReadSignatureAndBlock(filled));
|
|
if (!filled)
|
|
return S_FALSE;
|
|
RINOK(Header.Parse(_block, _blockSize));
|
|
return SkipExtendedHeaders();
|
|
}
|
|
|
|
HRESULT CInArchive::GetNextItem(bool &filled, CItem &item)
|
|
{
|
|
RINOK(ReadSignatureAndBlock(filled));
|
|
if (!filled)
|
|
return S_OK;
|
|
filled = false;
|
|
RINOK(item.Parse(_block, _blockSize));
|
|
/*
|
|
UInt32 extraData;
|
|
if ((header.Flags & NFileHeader::NFlags::kExtFile) != 0)
|
|
extraData = GetUi32(_block + pos);
|
|
*/
|
|
|
|
RINOK(SkipExtendedHeaders());
|
|
filled = true;
|
|
return S_OK;
|
|
}
|
|
|
|
class CHandler:
|
|
public IInArchive,
|
|
public CMyUnknownImp
|
|
{
|
|
public:
|
|
MY_UNKNOWN_IMP1(IInArchive)
|
|
|
|
INTERFACE_IInArchive(;)
|
|
|
|
HRESULT Open2(IInStream *inStream, const UInt64 *maxCheckStartPosition,
|
|
IArchiveOpenCallback *callback);
|
|
private:
|
|
CInArchive _archive;
|
|
CObjectVector<CItem> _items;
|
|
CMyComPtr<IInStream> _stream;
|
|
};
|
|
|
|
const wchar_t *kHostOS[] =
|
|
{
|
|
L"MSDOS",
|
|
L"PRIMOS",
|
|
L"UNIX",
|
|
L"AMIGA",
|
|
L"MAC",
|
|
L"OS/2",
|
|
L"APPLE GS",
|
|
L"ATARI ST",
|
|
L"NEXT",
|
|
L"VAX VMS",
|
|
L"WIN95"
|
|
};
|
|
|
|
const wchar_t *kUnknownOS = L"Unknown";
|
|
|
|
const int kNumHostOSes = sizeof(kHostOS) / sizeof(kHostOS[0]);
|
|
|
|
STATPROPSTG kArcProps[] =
|
|
{
|
|
{ NULL, kpidName, VT_BSTR},
|
|
{ NULL, kpidCTime, VT_BSTR},
|
|
{ NULL, kpidMTime, VT_BSTR},
|
|
{ NULL, kpidHostOS, VT_BSTR},
|
|
{ NULL, kpidComment, VT_BSTR}
|
|
};
|
|
|
|
STATPROPSTG kProps[] =
|
|
{
|
|
{ NULL, kpidPath, VT_BSTR},
|
|
{ NULL, kpidIsDir, VT_BOOL},
|
|
{ NULL, kpidSize, VT_UI4},
|
|
{ NULL, kpidPosition, VT_UI8},
|
|
{ NULL, kpidPackSize, VT_UI4},
|
|
{ NULL, kpidMTime, VT_FILETIME},
|
|
{ NULL, kpidAttrib, VT_UI4},
|
|
{ NULL, kpidEncrypted, VT_BOOL},
|
|
{ NULL, kpidCRC, VT_UI4},
|
|
{ NULL, kpidMethod, VT_UI1},
|
|
{ NULL, kpidHostOS, VT_BSTR},
|
|
{ NULL, kpidComment, VT_BSTR}
|
|
};
|
|
|
|
IMP_IInArchive_Props
|
|
IMP_IInArchive_ArcProps
|
|
|
|
static void SetTime(UInt32 dosTime, NWindows::NCOM::CPropVariant &prop)
|
|
{
|
|
if (dosTime == 0)
|
|
return;
|
|
FILETIME localFileTime, utc;
|
|
if (NTime::DosTimeToFileTime(dosTime, localFileTime))
|
|
{
|
|
if (!LocalFileTimeToFileTime(&localFileTime, &utc))
|
|
utc.dwHighDateTime = utc.dwLowDateTime = 0;
|
|
}
|
|
else
|
|
utc.dwHighDateTime = utc.dwLowDateTime = 0;
|
|
prop = utc;
|
|
}
|
|
|
|
static void SetHostOS(Byte hostOS, NWindows::NCOM::CPropVariant &prop)
|
|
{
|
|
prop = hostOS < kNumHostOSes ? kHostOS[hostOS] : kUnknownOS;
|
|
}
|
|
|
|
static void SetUnicodeString(const AString &s, NWindows::NCOM::CPropVariant &prop)
|
|
{
|
|
if (!s.IsEmpty())
|
|
prop = MultiByteToUnicodeString(s, CP_OEMCP);
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NWindows::NCOM::CPropVariant prop;
|
|
switch(propID)
|
|
{
|
|
case kpidName: SetUnicodeString(_archive.Header.Name, prop); break;
|
|
case kpidCTime: SetTime(_archive.Header.CTime, prop); break;
|
|
case kpidMTime: SetTime(_archive.Header.MTime, prop); break;
|
|
case kpidHostOS: SetHostOS(_archive.Header.HostOS, prop); break;
|
|
case kpidComment: SetUnicodeString(_archive.Header.Comment, prop); break;
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
|
{
|
|
*numItems = _items.Size();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NWindows::NCOM::CPropVariant prop;
|
|
const CItem &item = _items[index];
|
|
switch(propID)
|
|
{
|
|
case kpidPath: prop = NItemName::GetOSName(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break;
|
|
case kpidIsDir: prop = item.IsDir(); break;
|
|
case kpidSize: prop = item.Size; break;
|
|
case kpidPackSize: prop = item.PackSize; break;
|
|
case kpidPosition: if (item.IsSplitBefore() || item.IsSplitAfter()) prop = (UInt64)item.SplitPos; break;
|
|
case kpidAttrib: prop = item.GetWinAttributes(); break;
|
|
case kpidEncrypted: prop = item.IsEncrypted(); break;
|
|
case kpidCRC: prop = item.FileCRC; break;
|
|
case kpidMethod: prop = item.Method; break;
|
|
case kpidHostOS: SetHostOS(item.HostOS, prop); break;
|
|
case kpidMTime: SetTime(item.MTime, prop); break;
|
|
case kpidComment: SetUnicodeString(item.Comment, prop); break;
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
HRESULT CHandler::Open2(IInStream *inStream, const UInt64 *maxCheckStartPosition,
|
|
IArchiveOpenCallback *callback)
|
|
{
|
|
Close();
|
|
|
|
UInt64 endPos = 0;
|
|
if (callback != NULL)
|
|
{
|
|
RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos));
|
|
RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
|
|
}
|
|
|
|
_archive.Stream = inStream;
|
|
_archive.Callback = callback;
|
|
_archive.NumFiles = _archive.NumBytes = 0;
|
|
|
|
RINOK(_archive.Open(maxCheckStartPosition));
|
|
if (callback != NULL)
|
|
RINOK(callback->SetTotal(NULL, &endPos));
|
|
for (;;)
|
|
{
|
|
CItem item;
|
|
bool filled;
|
|
|
|
|
|
RINOK(_archive.GetNextItem(filled, item));
|
|
|
|
RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &item.DataPosition));
|
|
|
|
if (!filled)
|
|
break;
|
|
_items.Add(item);
|
|
|
|
if (inStream->Seek(item.PackSize, STREAM_SEEK_CUR, NULL) != S_OK)
|
|
throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive);
|
|
|
|
_archive.NumFiles = _items.Size();
|
|
_archive.NumBytes = item.DataPosition;
|
|
|
|
if (callback != NULL && _items.Size() % 100 == 0)
|
|
{
|
|
RINOK(callback->SetCompleted(&_archive.NumFiles, &_archive.NumBytes));
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Open(IInStream *inStream,
|
|
const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback)
|
|
{
|
|
COM_TRY_BEGIN
|
|
HRESULT res;
|
|
try
|
|
{
|
|
res = Open2(inStream, maxCheckStartPosition, callback);
|
|
if (res == S_OK)
|
|
{
|
|
_stream = inStream;
|
|
return S_OK;
|
|
}
|
|
}
|
|
catch(const CInArchiveException &) { res = S_FALSE; }
|
|
Close();
|
|
return res;
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Close()
|
|
{
|
|
_items.Clear();
|
|
_stream.Release();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
|
Int32 testMode, IArchiveExtractCallback *extractCallback)
|
|
{
|
|
COM_TRY_BEGIN
|
|
UInt64 totalUnpacked = 0, totalPacked = 0;
|
|
bool allFilesMode = (numItems == (UInt32)-1);
|
|
if (allFilesMode)
|
|
numItems = _items.Size();
|
|
if (numItems == 0)
|
|
return S_OK;
|
|
UInt32 i;
|
|
for (i = 0; i < numItems; i++)
|
|
{
|
|
const CItem &item = _items[allFilesMode ? i : indices[i]];
|
|
totalUnpacked += item.Size;
|
|
totalPacked += item.PackSize;
|
|
}
|
|
extractCallback->SetTotal(totalUnpacked);
|
|
|
|
totalUnpacked = totalPacked = 0;
|
|
UInt64 curUnpacked, curPacked;
|
|
|
|
CMyComPtr<ICompressCoder> arj1Decoder;
|
|
CMyComPtr<ICompressCoder> arj2Decoder;
|
|
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
|
|
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
|
|
|
|
CLocalProgress *lps = new CLocalProgress;
|
|
CMyComPtr<ICompressProgressInfo> progress = lps;
|
|
lps->Init(extractCallback, false);
|
|
|
|
CLimitedSequentialInStream *inStreamSpec = new CLimitedSequentialInStream;
|
|
CMyComPtr<ISequentialInStream> inStream(inStreamSpec);
|
|
inStreamSpec->SetStream(_stream);
|
|
|
|
for (i = 0; i < numItems; i++, totalUnpacked += curUnpacked, totalPacked += curPacked)
|
|
{
|
|
lps->InSize = totalPacked;
|
|
lps->OutSize = totalUnpacked;
|
|
RINOK(lps->SetCur());
|
|
|
|
curUnpacked = curPacked = 0;
|
|
|
|
CMyComPtr<ISequentialOutStream> realOutStream;
|
|
Int32 askMode = testMode ?
|
|
NExtract::NAskMode::kTest :
|
|
NExtract::NAskMode::kExtract;
|
|
Int32 index = allFilesMode ? i : indices[i];
|
|
const CItem &item = _items[index];
|
|
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
|
|
|
if (item.IsDir())
|
|
{
|
|
// if (!testMode)
|
|
{
|
|
RINOK(extractCallback->PrepareOperation(askMode));
|
|
RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!testMode && !realOutStream)
|
|
continue;
|
|
|
|
RINOK(extractCallback->PrepareOperation(askMode));
|
|
curUnpacked = item.Size;
|
|
curPacked = item.PackSize;
|
|
|
|
{
|
|
COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
|
|
CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
|
|
outStreamSpec->SetStream(realOutStream);
|
|
realOutStream.Release();
|
|
outStreamSpec->Init();
|
|
|
|
inStreamSpec->Init(item.PackSize);
|
|
|
|
UInt64 pos;
|
|
_stream->Seek(item.DataPosition, STREAM_SEEK_SET, &pos);
|
|
|
|
HRESULT result = S_OK;
|
|
Int32 opRes = NExtract::NOperationResult::kOK;
|
|
|
|
if (item.IsEncrypted())
|
|
opRes = NExtract::NOperationResult::kUnSupportedMethod;
|
|
else
|
|
{
|
|
switch(item.Method)
|
|
{
|
|
case NFileHeader::NCompressionMethod::kStored:
|
|
{
|
|
result = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
|
|
if (result == S_OK && copyCoderSpec->TotalSize != item.PackSize)
|
|
result = S_FALSE;
|
|
break;
|
|
}
|
|
case NFileHeader::NCompressionMethod::kCompressed1a:
|
|
case NFileHeader::NCompressionMethod::kCompressed1b:
|
|
case NFileHeader::NCompressionMethod::kCompressed1c:
|
|
{
|
|
if (!arj1Decoder)
|
|
arj1Decoder = new NCompress::NArj::NDecoder1::CCoder;
|
|
result = arj1Decoder->Code(inStream, outStream, NULL, &curUnpacked, progress);
|
|
break;
|
|
}
|
|
case NFileHeader::NCompressionMethod::kCompressed2:
|
|
{
|
|
if (!arj2Decoder)
|
|
arj2Decoder = new NCompress::NArj::NDecoder2::CCoder;
|
|
result = arj2Decoder->Code(inStream, outStream, NULL, &curUnpacked, progress);
|
|
break;
|
|
}
|
|
default:
|
|
opRes = NExtract::NOperationResult::kUnSupportedMethod;
|
|
}
|
|
}
|
|
if (opRes == NExtract::NOperationResult::kOK)
|
|
{
|
|
if (result == S_FALSE)
|
|
opRes = NExtract::NOperationResult::kDataError;
|
|
else
|
|
{
|
|
RINOK(result);
|
|
opRes = (outStreamSpec->GetCRC() == item.FileCRC) ?
|
|
NExtract::NOperationResult::kOK:
|
|
NExtract::NOperationResult::kCRCError;
|
|
}
|
|
}
|
|
outStream.Release();
|
|
RINOK(extractCallback->SetOperationResult(opRes));
|
|
}
|
|
}
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
static IInArchive *CreateArc() { return new CHandler; }
|
|
|
|
static CArcInfo g_ArcInfo =
|
|
{ L"Arj", L"arj", 0, 4, { 0x60, 0xEA }, 2, false, CreateArc, 0 };
|
|
|
|
REGISTER_ARC(Arj)
|
|
|
|
}}
|