978 lines
22 KiB
C++
978 lines
22 KiB
C++
|
// ArjHandler.cpp
|
||
|
|
||
|
#include "StdAfx.h"
|
||
|
|
||
|
#include "../../../C/CpuArch.h"
|
||
|
|
||
|
#include "../../Common/ComTry.h"
|
||
|
#include "../../Common/IntToString.h"
|
||
|
#include "../../Common/StringConvert.h"
|
||
|
|
||
|
#include "../../Windows/PropVariant.h"
|
||
|
#include "../../Windows/TimeUtils.h"
|
||
|
|
||
|
#include "../Common/LimitedStreams.h"
|
||
|
#include "../Common/ProgressUtils.h"
|
||
|
#include "../Common/RegisterArc.h"
|
||
|
#include "../Common/StreamObjects.h"
|
||
|
#include "../Common/StreamUtils.h"
|
||
|
|
||
|
#include "../Compress/CopyCoder.h"
|
||
|
#include "../Compress/LzhDecoder.h"
|
||
|
|
||
|
#include "Common/ItemNameUtils.h"
|
||
|
#include "Common/OutStreamWithCRC.h"
|
||
|
|
||
|
namespace NCompress {
|
||
|
namespace NArj {
|
||
|
namespace NDecoder {
|
||
|
|
||
|
static const unsigned kMatchMinLen = 3;
|
||
|
|
||
|
static const UInt32 kWindowSize = 1 << 15; // must be >= (1 << 14)
|
||
|
|
||
|
class CCoder:
|
||
|
public ICompressCoder,
|
||
|
public CMyUnknownImp
|
||
|
{
|
||
|
CLzOutWindow _outWindow;
|
||
|
NBitm::CDecoder<CInBuffer> _inBitStream;
|
||
|
|
||
|
class CCoderReleaser
|
||
|
{
|
||
|
CCoder *_coder;
|
||
|
public:
|
||
|
CCoderReleaser(CCoder *coder): _coder(coder) {}
|
||
|
void Disable() { _coder = NULL; }
|
||
|
~CCoderReleaser() { if (_coder) _coder->_outWindow.Flush(); }
|
||
|
};
|
||
|
friend class CCoderReleaser;
|
||
|
|
||
|
HRESULT CodeReal(UInt64 outSize, ICompressProgressInfo *progress);
|
||
|
public:
|
||
|
MY_UNKNOWN_IMP
|
||
|
|
||
|
bool FinishMode;
|
||
|
CCoder(): FinishMode(false) {}
|
||
|
|
||
|
STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
||
|
const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
|
||
|
UInt64 GetInputProcessedSize() const { return _inBitStream.GetProcessedSize(); }
|
||
|
};
|
||
|
|
||
|
HRESULT CCoder::CodeReal(UInt64 rem, ICompressProgressInfo *progress)
|
||
|
{
|
||
|
const UInt32 kStep = 1 << 20;
|
||
|
UInt64 next = 0;
|
||
|
if (rem > kStep && progress)
|
||
|
next = rem - kStep;
|
||
|
|
||
|
while (rem != 0)
|
||
|
{
|
||
|
if (rem <= next)
|
||
|
{
|
||
|
if (_inBitStream.ExtraBitsWereRead())
|
||
|
return S_FALSE;
|
||
|
|
||
|
UInt64 packSize = _inBitStream.GetProcessedSize();
|
||
|
UInt64 pos = _outWindow.GetProcessedSize();
|
||
|
RINOK(progress->SetRatioInfo(&packSize, &pos));
|
||
|
next = 0;
|
||
|
if (rem > kStep)
|
||
|
next = rem - kStep;
|
||
|
}
|
||
|
|
||
|
UInt32 len;
|
||
|
|
||
|
{
|
||
|
const unsigned kNumBits = 7 + 7;
|
||
|
UInt32 val = _inBitStream.GetValue(kNumBits);
|
||
|
|
||
|
if ((val & (1 << (kNumBits - 1))) == 0)
|
||
|
{
|
||
|
_outWindow.PutByte((Byte)(val >> 5));
|
||
|
_inBitStream.MovePos(1 + 8);
|
||
|
rem--;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
UInt32 mask = 1 << (kNumBits - 2);
|
||
|
unsigned w;
|
||
|
|
||
|
for (w = 1; w < 7; w++, mask >>= 1)
|
||
|
if ((val & mask) == 0)
|
||
|
break;
|
||
|
|
||
|
unsigned readBits = (w != 7 ? 1 : 0);
|
||
|
readBits += w + w;
|
||
|
len = (1 << w) - 1 + kMatchMinLen - 1 +
|
||
|
(((val >> (kNumBits - readBits)) & ((1 << w) - 1)));
|
||
|
_inBitStream.MovePos(readBits);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
const unsigned kNumBits = 4 + 13;
|
||
|
UInt32 val = _inBitStream.GetValue(kNumBits);
|
||
|
|
||
|
unsigned readBits = 1;
|
||
|
unsigned w;
|
||
|
|
||
|
if ((val & ((UInt32)1 << 16)) == 0) w = 9;
|
||
|
else if ((val & ((UInt32)1 << 15)) == 0) w = 10;
|
||
|
else if ((val & ((UInt32)1 << 14)) == 0) w = 11;
|
||
|
else if ((val & ((UInt32)1 << 13)) == 0) w = 12;
|
||
|
else { w = 13; readBits = 0; }
|
||
|
|
||
|
readBits += w + w - 9;
|
||
|
|
||
|
UInt32 dist = ((UInt32)1 << w) - (1 << 9) +
|
||
|
(((val >> (kNumBits - readBits)) & ((1 << w) - 1)));
|
||
|
_inBitStream.MovePos(readBits);
|
||
|
|
||
|
if (len > rem)
|
||
|
len = (UInt32)rem;
|
||
|
|
||
|
if (!_outWindow.CopyBlock(dist, len))
|
||
|
return S_FALSE;
|
||
|
rem -= len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FinishMode)
|
||
|
{
|
||
|
if (_inBitStream.ReadAlignBits() != 0)
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
if (_inBitStream.ExtraBitsWereRead())
|
||
|
return S_FALSE;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
STDMETHODIMP CCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
||
|
const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (!outSize)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (!_outWindow.Create(kWindowSize))
|
||
|
return E_OUTOFMEMORY;
|
||
|
if (!_inBitStream.Create(1 << 17))
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
_outWindow.SetStream(outStream);
|
||
|
_outWindow.Init(false);
|
||
|
_inBitStream.SetStream(inStream);
|
||
|
_inBitStream.Init();
|
||
|
|
||
|
CCoderReleaser coderReleaser(this);
|
||
|
HRESULT res;
|
||
|
{
|
||
|
res = CodeReal(*outSize, progress);
|
||
|
if (res != S_OK)
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
coderReleaser.Disable();
|
||
|
return _outWindow.Flush();
|
||
|
}
|
||
|
catch(const CInBufferException &e) { return e.ErrorCode; }
|
||
|
catch(const CLzOutWindowException &e) { return e.ErrorCode; }
|
||
|
catch(...) { return S_FALSE; }
|
||
|
}
|
||
|
|
||
|
}}}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
using namespace NWindows;
|
||
|
|
||
|
#define Get16(p) GetUi16(p)
|
||
|
#define Get32(p) GetUi32(p)
|
||
|
|
||
|
namespace NArchive {
|
||
|
namespace NArj {
|
||
|
|
||
|
static const unsigned kBlockSizeMin = 30;
|
||
|
static const unsigned kBlockSizeMax = 2600;
|
||
|
|
||
|
static const Byte kSig0 = 0x60;
|
||
|
static const Byte kSig1 = 0xEA;
|
||
|
|
||
|
namespace NCompressionMethod
|
||
|
{
|
||
|
enum
|
||
|
{
|
||
|
kStored = 0,
|
||
|
kCompressed1a = 1,
|
||
|
kCompressed1b = 2,
|
||
|
kCompressed1c = 3,
|
||
|
kCompressed2 = 4,
|
||
|
kNoDataNoCRC = 8,
|
||
|
kNoData = 9
|
||
|
};
|
||
|
}
|
||
|
|
||
|
namespace NFileType
|
||
|
{
|
||
|
enum
|
||
|
{
|
||
|
kBinary = 0,
|
||
|
k7BitText,
|
||
|
kArchiveHeader,
|
||
|
kDirectory,
|
||
|
kVolumeLablel,
|
||
|
kChapterLabel
|
||
|
};
|
||
|
}
|
||
|
|
||
|
namespace NFlags
|
||
|
{
|
||
|
const Byte kGarbled = 1 << 0;
|
||
|
const Byte kAnsiPage = 1 << 1; // or (OLD_SECURED_FLAG) obsolete
|
||
|
const Byte kVolume = 1 << 2;
|
||
|
const Byte kExtFile = 1 << 3;
|
||
|
const Byte kPathSym = 1 << 4;
|
||
|
const Byte kBackup = 1 << 5; // obsolete
|
||
|
const Byte kSecured = 1 << 6;
|
||
|
const Byte kDualName = 1 << 7;
|
||
|
}
|
||
|
|
||
|
namespace NHostOS
|
||
|
{
|
||
|
enum EEnum
|
||
|
{
|
||
|
kMSDOS = 0, // MS-DOS, OS/2, Win32, pkarj 2.50 (FAT / VFAT / FAT32)
|
||
|
kPRIMOS,
|
||
|
kUnix,
|
||
|
kAMIGA,
|
||
|
kMac,
|
||
|
kOS_2,
|
||
|
kAPPLE_GS,
|
||
|
kAtari_ST,
|
||
|
kNext,
|
||
|
kVAX_VMS,
|
||
|
kWIN95
|
||
|
};
|
||
|
}
|
||
|
|
||
|
static const char * const kHostOS[] =
|
||
|
{
|
||
|
"MSDOS"
|
||
|
, "PRIMOS"
|
||
|
, "UNIX"
|
||
|
, "AMIGA"
|
||
|
, "MAC"
|
||
|
, "OS/2"
|
||
|
, "APPLE GS"
|
||
|
, "ATARI ST"
|
||
|
, "NEXT"
|
||
|
, "VAX VMS"
|
||
|
, "WIN95"
|
||
|
};
|
||
|
|
||
|
struct CArcHeader
|
||
|
{
|
||
|
// Byte ArchiverVersion;
|
||
|
// Byte ExtractVersion;
|
||
|
Byte HostOS;
|
||
|
// Byte Flags;
|
||
|
// Byte SecuryVersion;
|
||
|
// Byte FileType;
|
||
|
// Byte Reserved;
|
||
|
UInt32 CTime;
|
||
|
UInt32 MTime;
|
||
|
UInt32 ArchiveSize;
|
||
|
// UInt32 SecurPos;
|
||
|
// UInt16 FilespecPosInFilename;
|
||
|
UInt16 SecurSize;
|
||
|
// Byte EncryptionVersion;
|
||
|
// Byte LastChapter;
|
||
|
AString Name;
|
||
|
AString Comment;
|
||
|
|
||
|
HRESULT Parse(const Byte *p, unsigned size);
|
||
|
};
|
||
|
|
||
|
API_FUNC_static_IsArc IsArc_Arj(const Byte *p, size_t size)
|
||
|
{
|
||
|
if (size < kBlockSizeMin + 4)
|
||
|
return k_IsArc_Res_NEED_MORE;
|
||
|
if (p[0] != kSig0 || p[1] != kSig1)
|
||
|
return k_IsArc_Res_NO;
|
||
|
UInt32 blockSize = Get16(p + 2);
|
||
|
if (blockSize < kBlockSizeMin ||
|
||
|
blockSize > kBlockSizeMax)
|
||
|
return k_IsArc_Res_NO;
|
||
|
|
||
|
p += 4;
|
||
|
size -= 4;
|
||
|
|
||
|
Byte headerSize = p[0];
|
||
|
if (headerSize < kBlockSizeMin ||
|
||
|
headerSize > blockSize ||
|
||
|
p[6] != NFileType::kArchiveHeader ||
|
||
|
p[28] > 8) // EncryptionVersion
|
||
|
return k_IsArc_Res_NO;
|
||
|
|
||
|
if (blockSize + 4 <= size)
|
||
|
if (Get32(p + blockSize) != CrcCalc(p, blockSize))
|
||
|
return k_IsArc_Res_NO;
|
||
|
|
||
|
return k_IsArc_Res_YES;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static HRESULT ReadString(const Byte *p, unsigned &size, AString &res)
|
||
|
{
|
||
|
unsigned num = size;
|
||
|
for (unsigned i = 0; i < num;)
|
||
|
{
|
||
|
if (p[i++] == 0)
|
||
|
{
|
||
|
size = i;
|
||
|
res = (const char *)p;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
HRESULT CArcHeader::Parse(const Byte *p, unsigned size)
|
||
|
{
|
||
|
Byte headerSize = p[0];
|
||
|
if (headerSize < kBlockSizeMin || headerSize > size)
|
||
|
return S_FALSE;
|
||
|
// ArchiverVersion = p[1];
|
||
|
// ExtractVersion = p[2];
|
||
|
HostOS = p[3];
|
||
|
// Flags = p[4];
|
||
|
// SecuryVersion = p[5];
|
||
|
if (p[6] != NFileType::kArchiveHeader)
|
||
|
return S_FALSE;
|
||
|
// Reserved = p[7];
|
||
|
CTime = Get32(p + 8);
|
||
|
MTime = Get32(p + 12);
|
||
|
ArchiveSize = Get32(p + 16); // it can be zero. (currently used only for secured archives)
|
||
|
// SecurPos = Get32(p + 20);
|
||
|
// UInt16 filespecPositionInFilename = Get16(p + 24);
|
||
|
SecurSize = Get16(p + 26);
|
||
|
// EncryptionVersion = p[28];
|
||
|
// LastChapter = p[29];
|
||
|
unsigned pos = headerSize;
|
||
|
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 FilespecPosInFilename;
|
||
|
UInt16 FileAccessMode;
|
||
|
// Byte FirstChapter;
|
||
|
// Byte LastChapter;
|
||
|
|
||
|
UInt64 DataPosition;
|
||
|
|
||
|
bool IsEncrypted() const { return (Flags & NFlags::kGarbled) != 0; }
|
||
|
bool IsDir() const { return (FileType == NFileType::kDirectory); }
|
||
|
bool IsSplitAfter() const { return (Flags & NFlags::kVolume) != 0; }
|
||
|
bool IsSplitBefore() const { return (Flags & NFlags::kExtFile) != 0; }
|
||
|
UInt32 GetWinAttrib() const
|
||
|
{
|
||
|
UInt32 atrrib = 0;
|
||
|
switch (HostOS)
|
||
|
{
|
||
|
case NHostOS::kMSDOS:
|
||
|
case NHostOS::kWIN95:
|
||
|
atrrib = FileAccessMode;
|
||
|
break;
|
||
|
}
|
||
|
if (IsDir())
|
||
|
atrrib |= FILE_ATTRIBUTE_DIRECTORY;
|
||
|
return atrrib;
|
||
|
}
|
||
|
|
||
|
HRESULT Parse(const Byte *p, unsigned size);
|
||
|
};
|
||
|
|
||
|
HRESULT CItem::Parse(const Byte *p, unsigned size)
|
||
|
{
|
||
|
Byte headerSize = p[0];
|
||
|
if (headerSize < kBlockSizeMin || headerSize > size)
|
||
|
return S_FALSE;
|
||
|
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);
|
||
|
// FilespecPosInFilename = Get16(p + 24);
|
||
|
FileAccessMode = Get16(p + 26);
|
||
|
// FirstChapter = p[28];
|
||
|
// FirstChapter = p[29];
|
||
|
|
||
|
SplitPos = 0;
|
||
|
if (IsSplitBefore() && headerSize >= 34)
|
||
|
SplitPos = Get32(p + 30);
|
||
|
|
||
|
unsigned pos = headerSize;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
enum EErrorType
|
||
|
{
|
||
|
k_ErrorType_OK,
|
||
|
k_ErrorType_Corrupted,
|
||
|
k_ErrorType_UnexpectedEnd,
|
||
|
};
|
||
|
|
||
|
class CArc
|
||
|
{
|
||
|
public:
|
||
|
UInt64 Processed;
|
||
|
EErrorType Error;
|
||
|
bool IsArc;
|
||
|
IInStream *Stream;
|
||
|
IArchiveOpenCallback *Callback;
|
||
|
UInt64 NumFiles;
|
||
|
CArcHeader Header;
|
||
|
|
||
|
HRESULT Open();
|
||
|
HRESULT GetNextItem(CItem &item, bool &filled);
|
||
|
void Close()
|
||
|
{
|
||
|
IsArc = false;
|
||
|
Error = k_ErrorType_OK;
|
||
|
}
|
||
|
private:
|
||
|
UInt32 _blockSize;
|
||
|
Byte _block[kBlockSizeMax + 4];
|
||
|
|
||
|
HRESULT ReadBlock(bool &filled, bool readSignature);
|
||
|
HRESULT SkipExtendedHeaders();
|
||
|
HRESULT Read(void *data, size_t *size);
|
||
|
};
|
||
|
|
||
|
HRESULT CArc::Read(void *data, size_t *size)
|
||
|
{
|
||
|
HRESULT res = ReadStream(Stream, data, size);
|
||
|
Processed += *size;
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
#define READ_STREAM(_dest_, _size_) \
|
||
|
{ size_t _processed_ = (_size_); RINOK(Read(_dest_, &_processed_)); \
|
||
|
if (_processed_ != (_size_)) { Error = k_ErrorType_UnexpectedEnd; return S_OK; } }
|
||
|
|
||
|
HRESULT CArc::ReadBlock(bool &filled, bool readSignature)
|
||
|
{
|
||
|
Error = k_ErrorType_OK;
|
||
|
filled = false;
|
||
|
Byte buf[4];
|
||
|
unsigned signSize = readSignature ? 2 : 0;
|
||
|
READ_STREAM(buf, signSize + 2)
|
||
|
if (readSignature)
|
||
|
if (buf[0] != kSig0 || buf[1] != kSig1)
|
||
|
{
|
||
|
Error = k_ErrorType_Corrupted;
|
||
|
return S_OK;
|
||
|
}
|
||
|
_blockSize = Get16(buf + signSize);
|
||
|
if (_blockSize == 0) // end of archive
|
||
|
return S_OK;
|
||
|
if (_blockSize < kBlockSizeMin ||
|
||
|
_blockSize > kBlockSizeMax)
|
||
|
{
|
||
|
Error = k_ErrorType_Corrupted;
|
||
|
return S_OK;
|
||
|
}
|
||
|
READ_STREAM(_block, _blockSize + 4);
|
||
|
if (Get32(_block + _blockSize) != CrcCalc(_block, _blockSize))
|
||
|
{
|
||
|
Error = k_ErrorType_Corrupted;
|
||
|
return S_OK;
|
||
|
}
|
||
|
filled = true;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CArc::SkipExtendedHeaders()
|
||
|
{
|
||
|
for (UInt32 i = 0;; i++)
|
||
|
{
|
||
|
bool filled;
|
||
|
RINOK(ReadBlock(filled, false));
|
||
|
if (!filled)
|
||
|
return S_OK;
|
||
|
if (Callback && (i & 0xFF) == 0)
|
||
|
RINOK(Callback->SetCompleted(&NumFiles, &Processed));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT CArc::Open()
|
||
|
{
|
||
|
bool filled;
|
||
|
RINOK(ReadBlock(filled, true));
|
||
|
if (!filled)
|
||
|
return S_FALSE;
|
||
|
RINOK(Header.Parse(_block, _blockSize));
|
||
|
IsArc = true;
|
||
|
return SkipExtendedHeaders();
|
||
|
}
|
||
|
|
||
|
HRESULT CArc::GetNextItem(CItem &item, bool &filled)
|
||
|
{
|
||
|
RINOK(ReadBlock(filled, true));
|
||
|
if (!filled)
|
||
|
return S_OK;
|
||
|
filled = false;
|
||
|
if (item.Parse(_block, _blockSize) != S_OK)
|
||
|
{
|
||
|
Error = k_ErrorType_Corrupted;
|
||
|
return S_OK;
|
||
|
}
|
||
|
/*
|
||
|
UInt32 extraData;
|
||
|
if ((header.Flags & NFlags::kExtFile) != 0)
|
||
|
extraData = GetUi32(_block + pos);
|
||
|
*/
|
||
|
|
||
|
RINOK(SkipExtendedHeaders());
|
||
|
filled = true;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
class CHandler:
|
||
|
public IInArchive,
|
||
|
public CMyUnknownImp
|
||
|
{
|
||
|
CObjectVector<CItem> _items;
|
||
|
CMyComPtr<IInStream> _stream;
|
||
|
UInt64 _phySize;
|
||
|
CArc _arc;
|
||
|
public:
|
||
|
MY_UNKNOWN_IMP1(IInArchive)
|
||
|
|
||
|
INTERFACE_IInArchive(;)
|
||
|
|
||
|
HRESULT Open2(IInStream *inStream, IArchiveOpenCallback *callback);
|
||
|
};
|
||
|
|
||
|
static const Byte kArcProps[] =
|
||
|
{
|
||
|
kpidName,
|
||
|
kpidCTime,
|
||
|
kpidMTime,
|
||
|
kpidHostOS,
|
||
|
kpidComment
|
||
|
};
|
||
|
|
||
|
static const Byte kProps[] =
|
||
|
{
|
||
|
kpidPath,
|
||
|
kpidIsDir,
|
||
|
kpidSize,
|
||
|
kpidPosition,
|
||
|
kpidPackSize,
|
||
|
kpidMTime,
|
||
|
kpidAttrib,
|
||
|
kpidEncrypted,
|
||
|
kpidCRC,
|
||
|
kpidMethod,
|
||
|
kpidHostOS,
|
||
|
kpidComment
|
||
|
};
|
||
|
|
||
|
IMP_IInArchive_Props
|
||
|
IMP_IInArchive_ArcProps
|
||
|
|
||
|
static void SetTime(UInt32 dosTime, 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, NCOM::CPropVariant &prop)
|
||
|
{
|
||
|
char temp[16];
|
||
|
const char *s = NULL;
|
||
|
if (hostOS < ARRAY_SIZE(kHostOS))
|
||
|
s = kHostOS[hostOS];
|
||
|
else
|
||
|
{
|
||
|
ConvertUInt32ToString(hostOS, temp);
|
||
|
s = temp;
|
||
|
}
|
||
|
prop = s;
|
||
|
}
|
||
|
|
||
|
static void SetUnicodeString(const AString &s, NCOM::CPropVariant &prop)
|
||
|
{
|
||
|
if (!s.IsEmpty())
|
||
|
prop = MultiByteToUnicodeString(s, CP_OEMCP);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
NCOM::CPropVariant prop;
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidPhySize: prop = _phySize; break;
|
||
|
case kpidName: SetUnicodeString(_arc.Header.Name, prop); break;
|
||
|
case kpidCTime: SetTime(_arc.Header.CTime, prop); break;
|
||
|
case kpidMTime: SetTime(_arc.Header.MTime, prop); break;
|
||
|
case kpidHostOS: SetHostOS(_arc.Header.HostOS, prop); break;
|
||
|
case kpidComment: SetUnicodeString(_arc.Header.Comment, prop); break;
|
||
|
case kpidErrorFlags:
|
||
|
{
|
||
|
UInt32 v = 0;
|
||
|
if (!_arc.IsArc) v |= kpv_ErrorFlags_IsNotArc;
|
||
|
switch (_arc.Error)
|
||
|
{
|
||
|
case k_ErrorType_UnexpectedEnd: v |= kpv_ErrorFlags_UnexpectedEnd; break;
|
||
|
case k_ErrorType_Corrupted: v |= kpv_ErrorFlags_HeadersError; break;
|
||
|
}
|
||
|
prop = v;
|
||
|
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
|
||
|
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.GetWinAttrib(); 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, IArchiveOpenCallback *callback)
|
||
|
{
|
||
|
Close();
|
||
|
|
||
|
UInt64 endPos = 0;
|
||
|
RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos));
|
||
|
RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
|
||
|
|
||
|
_arc.Stream = inStream;
|
||
|
_arc.Callback = callback;
|
||
|
_arc.NumFiles = 0;
|
||
|
_arc.Processed = 0;
|
||
|
|
||
|
RINOK(_arc.Open());
|
||
|
|
||
|
_phySize = _arc.Processed;
|
||
|
if (_arc.Header.ArchiveSize != 0)
|
||
|
_phySize = (UInt64)_arc.Header.ArchiveSize + _arc.Header.SecurSize;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
CItem item;
|
||
|
bool filled;
|
||
|
|
||
|
_arc.Error = k_ErrorType_OK;
|
||
|
RINOK(_arc.GetNextItem(item, filled));
|
||
|
|
||
|
if (_arc.Error != k_ErrorType_OK)
|
||
|
break;
|
||
|
|
||
|
if (!filled)
|
||
|
{
|
||
|
if (_arc.Error == k_ErrorType_OK)
|
||
|
if (_arc.Header.ArchiveSize == 0)
|
||
|
_phySize = _arc.Processed;
|
||
|
break;
|
||
|
}
|
||
|
item.DataPosition = _arc.Processed;
|
||
|
_items.Add(item);
|
||
|
|
||
|
UInt64 pos = item.DataPosition + item.PackSize;
|
||
|
if (_arc.Header.ArchiveSize == 0)
|
||
|
_phySize = pos;
|
||
|
if (pos > endPos)
|
||
|
{
|
||
|
_arc.Error = k_ErrorType_UnexpectedEnd;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
RINOK(inStream->Seek(pos, STREAM_SEEK_SET, NULL));
|
||
|
_arc.NumFiles = _items.Size();
|
||
|
_arc.Processed = pos;
|
||
|
|
||
|
if (callback && (_items.Size() & 0xFF) == 0)
|
||
|
{
|
||
|
RINOK(callback->SetCompleted(&_arc.NumFiles, &_arc.Processed));
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Open(IInStream *inStream,
|
||
|
const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
HRESULT res;
|
||
|
{
|
||
|
res = Open2(inStream, callback);
|
||
|
if (res == S_OK)
|
||
|
{
|
||
|
_stream = inStream;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
return res;
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Close()
|
||
|
{
|
||
|
_arc.Close();
|
||
|
_phySize = 0;
|
||
|
_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)(Int32)-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;
|
||
|
|
||
|
NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = NULL;
|
||
|
CMyComPtr<ICompressCoder> lzhDecoder;
|
||
|
|
||
|
NCompress::NArj::NDecoder::CCoder *arjDecoderSpec = NULL;
|
||
|
CMyComPtr<ICompressCoder> arjDecoder;
|
||
|
|
||
|
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 NCompressionMethod::kStored:
|
||
|
{
|
||
|
result = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
|
||
|
if (result == S_OK && copyCoderSpec->TotalSize != item.PackSize)
|
||
|
result = S_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
case NCompressionMethod::kCompressed1a:
|
||
|
case NCompressionMethod::kCompressed1b:
|
||
|
case NCompressionMethod::kCompressed1c:
|
||
|
{
|
||
|
if (!lzhDecoder)
|
||
|
{
|
||
|
lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder;
|
||
|
lzhDecoder = lzhDecoderSpec;
|
||
|
}
|
||
|
lzhDecoderSpec->FinishMode = true;
|
||
|
const UInt32 kHistorySize = 26624;
|
||
|
lzhDecoderSpec->SetDictSize(kHistorySize);
|
||
|
result = lzhDecoder->Code(inStream, outStream, NULL, &curUnpacked, progress);
|
||
|
if (result == S_OK && lzhDecoderSpec->GetInputProcessedSize() != item.PackSize)
|
||
|
result = S_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
case NCompressionMethod::kCompressed2:
|
||
|
{
|
||
|
if (!arjDecoder)
|
||
|
{
|
||
|
arjDecoderSpec = new NCompress::NArj::NDecoder::CCoder;
|
||
|
arjDecoder = arjDecoderSpec;
|
||
|
}
|
||
|
arjDecoderSpec->FinishMode = true;
|
||
|
result = arjDecoder->Code(inStream, outStream, NULL, &curUnpacked, progress);
|
||
|
if (result == S_OK && arjDecoderSpec->GetInputProcessedSize() != item.PackSize)
|
||
|
result = S_FALSE;
|
||
|
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 const Byte k_Signature[] = { kSig0, kSig1 };
|
||
|
|
||
|
REGISTER_ARC_I(
|
||
|
"Arj", "arj", 0, 4,
|
||
|
k_Signature,
|
||
|
0,
|
||
|
0,
|
||
|
IsArc_Arj)
|
||
|
|
||
|
}}
|