9.04 beta

This commit is contained in:
Igor Pavlov
2009-06-02 00:00:00 +00:00
committed by Kornel Lesiński
parent 8874e4fbc9
commit 829409452d
440 changed files with 19803 additions and 9941 deletions

820
CPP/7zip/Archive/GzHandler.cpp Executable file
View File

@@ -0,0 +1,820 @@
// GzHandler.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/ProgressUtils.h"
#include "../Common/RegisterArc.h"
#include "../Common/StreamUtils.h"
#include "../Compress/CopyCoder.h"
#include "../Compress/DeflateDecoder.h"
#include "../Compress/DeflateEncoder.h"
#include "Common/InStreamWithCRC.h"
#include "Common/OutStreamWithCRC.h"
#include "Common/ParseProperties.h"
#define Get32(p) GetUi32(p)
using namespace NWindows;
namespace NArchive {
namespace NGz {
static const UInt16 kSignature = 0x8B1F;
namespace NHeader
{
namespace NFlags
{
const Byte kIsText = 1 << 0;
const Byte kCrc = 1 << 1;
const Byte kExtra = 1 << 2;
const Byte kName = 1 << 3;
const Byte kComment = 1 << 4;
}
namespace NExtraFlags
{
const Byte kMaximum = 2;
const Byte kFastest = 4;
}
namespace NCompressionMethod
{
const Byte kDeflate = 8;
}
namespace NHostOS
{
enum EEnum
{
kFAT = 0,
kAMIGA,
kVMS,
kUnix,
kVM_CMS,
kAtari,
kHPFS,
kMac,
kZ_System,
kCPM,
kTOPS20,
kNTFS,
kQDOS,
kAcorn,
kVFAT,
kMVS,
kBeOS,
kTandem,
kUnknown = 255
};
}
}
static const char *kHostOSes[] =
{
"FAT",
"AMIGA",
"VMS",
"Unix",
"VM/CMS",
"Atari",
"HPFS",
"Macintosh",
"Z-System",
"CP/M",
"TOPS-20",
"NTFS",
"SMS/QDOS",
"Acorn",
"VFAT",
"MVS",
"BeOS",
"Tandem",
"OS/400",
"OS/X"
};
static const char *kUnknownOS = "Unknown";
class CItem
{
bool TestFlag(Byte flag) const { return (Flags & flag) != 0; }
public:
Byte Method;
Byte Flags;
Byte ExtraFlags;
Byte HostOS;
UInt32 Time;
UInt32 Crc;
UInt32 Size32;
AString Name;
AString Comment;
// CByteBuffer Extra;
// bool IsText() const { return TestFlag(NHeader::NFlags::kIsText); }
bool HeaderCrcIsPresent() const { return TestFlag(NHeader::NFlags::kCrc); }
bool ExtraFieldIsPresent() const { return TestFlag(NHeader::NFlags::kExtra); }
bool NameIsPresent() const { return TestFlag(NHeader::NFlags::kName); }
bool CommentIsPresent() const { return TestFlag(NHeader::NFlags::kComment); }
void Clear()
{
Name.Empty();
Comment.Empty();
// Extra.SetCapacity(0);
}
HRESULT ReadHeader(NCompress::NDeflate::NDecoder::CCOMCoder *stream);
HRESULT ReadFooter1(NCompress::NDeflate::NDecoder::CCOMCoder *stream);
HRESULT ReadFooter2(ISequentialInStream *stream);
HRESULT WriteHeader(ISequentialOutStream *stream);
HRESULT WriteFooter(ISequentialOutStream *stream);
};
static HRESULT ReadBytes(NCompress::NDeflate::NDecoder::CCOMCoder *stream, Byte *data, UInt32 size)
{
for (UInt32 i = 0; i < size; i++)
data[i] = stream->ReadByte();
return stream->InputEofError() ? S_FALSE : S_OK;
}
static HRESULT SkipBytes(NCompress::NDeflate::NDecoder::CCOMCoder *stream, UInt32 size)
{
for (UInt32 i = 0; i < size; i++)
stream->ReadByte();
return stream->InputEofError() ? S_FALSE : S_OK;
}
static HRESULT ReadUInt16(NCompress::NDeflate::NDecoder::CCOMCoder *stream, UInt16 &value /* , UInt32 &crc */)
{
value = 0;
for (int i = 0; i < 2; i++)
{
Byte b = stream->ReadByte();
if (stream->InputEofError())
return S_FALSE;
// crc = CRC_UPDATE_BYTE(crc, b);
value |= (UInt16(b) << (8 * i));
}
return S_OK;
}
static HRESULT ReadString(NCompress::NDeflate::NDecoder::CCOMCoder *stream, AString &s, UInt32 limit /* , UInt32 &crc */)
{
s.Empty();
for (UInt32 i = 0; i < limit; i++)
{
Byte b = stream->ReadByte();
if (stream->InputEofError())
return S_FALSE;
// crc = CRC_UPDATE_BYTE(crc, b);
if (b == 0)
return S_OK;
s += (char)b;
}
return S_FALSE;
}
HRESULT CItem::ReadHeader(NCompress::NDeflate::NDecoder::CCOMCoder *stream)
{
Clear();
// Header-CRC field had another meaning in old version of gzip!
// UInt32 crc = CRC_INIT_VAL;
Byte buf[10];
RINOK(ReadBytes(stream, buf, 10));
if (GetUi16(buf) != kSignature)
return S_FALSE;
Method = buf[2];
if (Method != NHeader::NCompressionMethod::kDeflate)
return S_FALSE;
Flags = buf[3];
Time = Get32(buf + 4);
ExtraFlags = buf[8];
HostOS = buf[9];
// crc = CrcUpdate(crc, buf, 10);
if (ExtraFieldIsPresent())
{
UInt16 extraSize;
RINOK(ReadUInt16(stream, extraSize /* , crc */));
RINOK(SkipBytes(stream, extraSize));
// Extra.SetCapacity(extraSize);
// RINOK(ReadStream_FALSE(stream, Extra, extraSize));
// crc = CrcUpdate(crc, Extra, extraSize);
}
if (NameIsPresent())
RINOK(ReadString(stream, Name, (1 << 10) /* , crc */));
if (CommentIsPresent())
RINOK(ReadString(stream, Comment, (1 << 16) /* , crc */));
if (HeaderCrcIsPresent())
{
UInt16 headerCRC;
// UInt32 dummy = 0;
RINOK(ReadUInt16(stream, headerCRC /* , dummy */));
/*
if ((UInt16)CRC_GET_DIGEST(crc) != headerCRC)
return S_FALSE;
*/
}
return stream->InputEofError() ? S_FALSE : S_OK;
}
HRESULT CItem::ReadFooter1(NCompress::NDeflate::NDecoder::CCOMCoder *stream)
{
Byte buf[8];
RINOK(ReadBytes(stream, buf, 8));
Crc = Get32(buf);
Size32 = Get32(buf + 4);
return stream->InputEofError() ? S_FALSE : S_OK;
}
HRESULT CItem::ReadFooter2(ISequentialInStream *stream)
{
Byte buf[8];
RINOK(ReadStream_FALSE(stream, buf, 8));
Crc = Get32(buf);
Size32 = Get32(buf + 4);
return S_OK;
}
HRESULT CItem::WriteHeader(ISequentialOutStream *stream)
{
Byte buf[10];
SetUi16(buf, kSignature);
buf[2] = Method;
buf[3] = Flags & NHeader::NFlags::kName;
// buf[3] |= NHeader::NFlags::kCrc;
SetUi32(buf + 4, Time);
buf[8] = ExtraFlags;
buf[9] = HostOS;
RINOK(WriteStream(stream, buf, 10));
// crc = CrcUpdate(CRC_INIT_VAL, buf, 10);
if (NameIsPresent())
{
// crc = CrcUpdate(crc, (const char *)Name, Name.Length() + 1);
RINOK(WriteStream(stream, (const char *)Name, Name.Length() + 1));
}
// SetUi16(buf, (UInt16)CRC_GET_DIGEST(crc));
// RINOK(WriteStream(stream, buf, 2));
return S_OK;
}
HRESULT CItem::WriteFooter(ISequentialOutStream *stream)
{
Byte buf[8];
SetUi32(buf, Crc);
SetUi32(buf + 4, Size32);
return WriteStream(stream, buf, 8);
}
static const UInt32 kAlgoX1 = 0;
static const UInt32 kAlgoX5 = 1;
static const UInt32 kNumPassesX1 = 1;
static const UInt32 kNumPassesX7 = 3;
static const UInt32 kNumPassesX9 = 10;
static const UInt32 kNumFastBytesX1 = 32;
static const UInt32 kNumFastBytesX7 = 64;
static const UInt32 kNumFastBytesX9 = 128;
struct CCompressMode
{
UInt32 NumPasses;
UInt32 NumFastBytes;
UInt32 Algo;
UInt32 Mc;
bool McDefined;
bool IsMaximum() const { return Algo > 0; }
void Init()
{
NumPasses = NumFastBytes = Mc = Algo = 0xFFFFFFFF;
McDefined = false;
}
void Normalize(UInt32 level)
{
if (level == 0xFFFFFFFF)
level = 5;
if (NumPasses == 0xFFFFFFFF)
NumPasses =
(level >= 9 ? kNumPassesX9 :
(level >= 7 ? kNumPassesX7 :
kNumPassesX1));
if (NumFastBytes == 0xFFFFFFFF)
NumFastBytes =
(level >= 9 ? kNumFastBytesX9 :
(level >= 7 ? kNumFastBytesX7 :
kNumFastBytesX1));
if (Algo == 0xFFFFFFFF)
Algo = (level >= 5 ?
kAlgoX5 :
kAlgoX1);
}
};
class CHandler:
public IInArchive,
public IArchiveOpenSeq,
public IOutArchive,
public ISetProperties,
public CMyUnknownImp
{
CItem _item;
UInt64 _startPosition;
UInt64 _headerSize;
UInt64 _packSize;
bool _packSizeDefined;
CMyComPtr<IInStream> _stream;
CMyComPtr<ICompressCoder> _decoder;
NCompress::NDeflate::NDecoder::CCOMCoder *_decoderSpec;
CCompressMode _method;
UInt32 _level;
void InitMethodProperties()
{
_level = 0xFFFFFFFF;
_method.Init();
}
public:
MY_UNKNOWN_IMP4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties)
INTERFACE_IInArchive(;)
INTERFACE_IOutArchive(;)
STDMETHOD(OpenSeq)(ISequentialInStream *stream);
STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps);
CHandler()
{
InitMethodProperties();
_decoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder;
_decoder = _decoderSpec;
}
};
STATPROPSTG kProps[] =
{
{ NULL, kpidPath, VT_BSTR},
{ NULL, kpidSize, VT_UI8},
{ NULL, kpidPackSize, VT_UI8},
{ NULL, kpidMTime, VT_FILETIME},
{ NULL, kpidHostOS, VT_BSTR},
{ NULL, kpidCRC, VT_UI4}
// { NULL, kpidComment, VT_BSTR}
}
;
IMP_IInArchive_Props
IMP_IInArchive_ArcProps_NO_Table
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
NCOM::CPropVariant prop;
switch(propID)
{
case kpidPhySize: if (_packSizeDefined) prop = _packSize; break;
}
prop.Detach(value);
return S_OK;
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
{
*numItems = 1;
return S_OK;
}
STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
switch(propID)
{
case kpidPath:
if (_item.NameIsPresent())
prop = MultiByteToUnicodeString(_item.Name, CP_ACP);
break;
// case kpidComment: if (_item.CommentIsPresent()) prop = MultiByteToUnicodeString(_item.Comment, CP_ACP); break;
case kpidMTime:
{
if (_item.Time != 0)
{
FILETIME utc;
NTime::UnixTimeToFileTime(_item.Time, utc);
prop = utc;
}
break;
}
case kpidSize: if (_stream) prop = (UInt64)_item.Size32; break;
case kpidPackSize: if (_packSizeDefined) prop = _packSize; break;
case kpidHostOS: prop = (_item.HostOS < sizeof(kHostOSes) / sizeof(kHostOSes[0])) ?
kHostOSes[_item.HostOS] : kUnknownOS; break;
case kpidCRC: if (_stream) prop = _item.Crc; break;
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *)
{
COM_TRY_BEGIN
HRESULT res;
try
{
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPosition));
res = OpenSeq(stream);
if (res == S_OK)
{
UInt64 endPos;
res = stream->Seek(-8, STREAM_SEEK_END, &endPos);
_packSize = endPos + 8 - _startPosition;
_packSizeDefined = true;
if (res == S_OK)
{
res = _item.ReadFooter2(stream);
_stream = stream;
}
}
}
catch(...) { res = S_FALSE; }
if (res != S_OK)
Close();
return res;
COM_TRY_END
}
STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
{
COM_TRY_BEGIN
HRESULT res;
try
{
Close();
_decoderSpec->SetInStream(stream);
_decoderSpec->InitInStream(true);
res = _item.ReadHeader(_decoderSpec);
_headerSize = _decoderSpec->GetInputProcessedSize();
}
catch(...) { res = S_FALSE; }
if (res != S_OK)
Close();
return res;
COM_TRY_END
}
STDMETHODIMP CHandler::Close()
{
_packSizeDefined = false;
_stream.Release();
_decoderSpec->ReleaseInStream();
return S_OK;
}
STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
{
COM_TRY_BEGIN
bool allFilesMode = (numItems == (UInt32)-1);
if (!allFilesMode)
{
if (numItems == 0)
return S_OK;
if (numItems != 1 || indices[0] != 0)
return E_INVALIDARG;
}
bool testMode = (_aTestMode != 0);
if (_stream)
extractCallback->SetTotal(_packSize);
UInt64 currentTotalPacked = 0;
RINOK(extractCallback->SetCompleted(&currentTotalPacked));
CMyComPtr<ISequentialOutStream> realOutStream;
Int32 askMode = testMode ?
NArchive::NExtract::NAskMode::kTest :
NArchive::NExtract::NAskMode::kExtract;
RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
if (!testMode && !realOutStream)
return S_OK;
extractCallback->PrepareOperation(askMode);
COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
outStreamSpec->SetStream(realOutStream);
outStreamSpec->Init();
realOutStream.Release();
CLocalProgress *lps = new CLocalProgress;
CMyComPtr<ICompressProgressInfo> progress = lps;
lps->Init(extractCallback, true);
if (_stream)
{
RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
_decoderSpec->InitInStream(true);
}
bool firstItem = true;
Int32 opRes;
for (;;)
{
lps->InSize = _packSize = _decoderSpec->GetInputProcessedSize();
_packSizeDefined = true;
lps->OutSize = outStreamSpec->GetSize();
RINOK(lps->SetCur());
CItem item;
if (!firstItem || _stream)
{
HRESULT result = item.ReadHeader(_decoderSpec);
if (result != S_OK)
{
if (result != S_FALSE)
return result;
opRes = firstItem ?
NArchive::NExtract::NOperationResult::kDataError :
NArchive::NExtract::NOperationResult::kOK;
break;
}
}
firstItem = false;
UInt64 startOffset = outStreamSpec->GetSize();
outStreamSpec->InitCRC();
HRESULT result = _decoderSpec->CodeResume(outStream, NULL, progress);
if (result != S_OK)
{
if (result != S_FALSE)
return result;
opRes = NArchive::NExtract::NOperationResult::kDataError;
break;
}
_decoderSpec->AlignToByte();
if (item.ReadFooter1(_decoderSpec) != S_OK)
{
opRes = NArchive::NExtract::NOperationResult::kDataError;
break;
}
if (item.Crc != outStreamSpec->GetCRC() ||
item.Size32 != (UInt32)(outStreamSpec->GetSize() - startOffset))
{
opRes = NArchive::NExtract::NOperationResult::kCRCError;
break;
}
}
outStream.Release();
return extractCallback->SetOperationResult(opRes);
COM_TRY_END
}
static const Byte kHostOS =
#ifdef _WIN32
NHeader::NHostOS::kFAT;
#else
NHeader::NHostOS::kUnix;
#endif
static HRESULT UpdateArchive(
ISequentialOutStream *outStream,
UInt64 unpackSize,
const CItem &newItem,
const CCompressMode &compressionMode,
IArchiveUpdateCallback *updateCallback)
{
UInt64 complexity = 0;
RINOK(updateCallback->SetTotal(unpackSize));
RINOK(updateCallback->SetCompleted(&complexity));
CMyComPtr<ISequentialInStream> fileInStream;
RINOK(updateCallback->GetStream(0, &fileInStream));
CSequentialInStreamWithCRC *inStreamSpec = new CSequentialInStreamWithCRC;
CMyComPtr<ISequentialInStream> crcStream(inStreamSpec);
inStreamSpec->SetStream(fileInStream);
inStreamSpec->Init();
CLocalProgress *lps = new CLocalProgress;
CMyComPtr<ICompressProgressInfo> progress = lps;
lps->Init(updateCallback, true);
CItem item = newItem;
item.Method = NHeader::NCompressionMethod::kDeflate;
item.ExtraFlags = compressionMode.IsMaximum() ?
NHeader::NExtraFlags::kMaximum :
NHeader::NExtraFlags::kFastest;
item.HostOS = kHostOS;
RINOK(item.WriteHeader(outStream));
NCompress::NDeflate::NEncoder::CCOMCoder *deflateEncoderSpec = new NCompress::NDeflate::NEncoder::CCOMCoder;
CMyComPtr<ICompressCoder> deflateEncoder = deflateEncoderSpec;
{
NWindows::NCOM::CPropVariant props[] =
{
compressionMode.Algo,
compressionMode.NumPasses,
compressionMode.NumFastBytes,
compressionMode.Mc
};
PROPID propIDs[] =
{
NCoderPropID::kAlgorithm,
NCoderPropID::kNumPasses,
NCoderPropID::kNumFastBytes,
NCoderPropID::kMatchFinderCycles
};
int numProps = sizeof(propIDs) / sizeof(propIDs[0]);
if (!compressionMode.McDefined)
numProps--;
RINOK(deflateEncoderSpec->SetCoderProperties(propIDs, props, numProps));
}
RINOK(deflateEncoder->Code(crcStream, outStream, NULL, NULL, progress));
item.Crc = inStreamSpec->GetCRC();
item.Size32 = (UInt32)inStreamSpec->GetSize();
RINOK(item.WriteFooter(outStream));
return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
}
STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
{
*timeType = NFileTimeType::kUnix;
return S_OK;
}
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
IArchiveUpdateCallback *updateCallback)
{
if (numItems != 1)
return E_INVALIDARG;
Int32 newData, newProps;
UInt32 indexInArchive;
if (!updateCallback)
return E_FAIL;
RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
CItem newItem = _item;
newItem.ExtraFlags = 0;
newItem.Flags = 0;
if (IntToBool(newProps))
{
{
FILETIME utcTime;
NCOM::CPropVariant prop;
RINOK(updateCallback->GetProperty(0, kpidMTime, &prop));
if (prop.vt != VT_FILETIME)
return E_INVALIDARG;
utcTime = prop.filetime;
if (!NTime::FileTimeToUnixTime(utcTime, newItem.Time))
return E_INVALIDARG;
}
{
NCOM::CPropVariant prop;
RINOK(updateCallback->GetProperty(0, kpidPath, &prop));
if (prop.vt == VT_BSTR)
{
UString name = prop.bstrVal;
int dirDelimiterPos = name.ReverseFind(CHAR_PATH_SEPARATOR);
if (dirDelimiterPos >= 0)
name = name.Mid(dirDelimiterPos + 1);
newItem.Name = UnicodeStringToMultiByte(name, CP_ACP);
if (!newItem.Name.IsEmpty())
newItem.Flags |= NHeader::NFlags::kName;
}
else if (prop.vt != VT_EMPTY)
return E_INVALIDARG;
}
{
NCOM::CPropVariant prop;
RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
if (prop.vt == VT_BOOL)
{
if (prop.boolVal != VARIANT_FALSE)
return E_INVALIDARG;
}
else if (prop.vt != VT_EMPTY)
return E_INVALIDARG;
}
}
if (IntToBool(newData))
{
UInt64 size;
{
NCOM::CPropVariant prop;
RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
if (prop.vt != VT_UI8)
return E_INVALIDARG;
size = prop.uhVal.QuadPart;
}
_method.Normalize(_level);
return UpdateArchive(outStream, size, newItem, _method, updateCallback);
}
if (indexInArchive != 0)
return E_INVALIDARG;
if (!_stream)
return E_NOTIMPL;
UInt64 offset = _startPosition;
if (IntToBool(newProps))
{
newItem.WriteHeader(outStream);
offset += _headerSize;
}
RINOK(_stream->Seek(offset, STREAM_SEEK_SET, NULL));
return NCompress::CopyStream(_stream, outStream, NULL);
}
STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps)
{
InitMethodProperties();
for (int i = 0; i < numProps; i++)
{
UString name = names[i];
name.MakeUpper();
if (name.IsEmpty())
return E_INVALIDARG;
const PROPVARIANT &prop = values[i];
if (name[0] == L'X')
{
UInt32 level = 9;
RINOK(ParsePropValue(name.Mid(1), prop, level));
_level = level;
}
else if (name.Left(4) == L"PASS")
{
UInt32 num = kNumPassesX9;
RINOK(ParsePropValue(name.Mid(4), prop, num));
_method.NumPasses = num;
}
else if (name.Left(2) == L"FB")
{
UInt32 num = kNumFastBytesX9;
RINOK(ParsePropValue(name.Mid(2), prop, num));
_method.NumFastBytes = num;
}
else if (name.Left(2) == L"MC")
{
UInt32 num = 0xFFFFFFFF;
RINOK(ParsePropValue(name.Mid(2), prop, num));
_method.Mc = num;
_method.McDefined = true;
}
else if (name.Left(1) == L"A")
{
UInt32 num = kAlgoX5;
RINOK(ParsePropValue(name.Mid(1), prop, num));
_method.Algo = num;
}
else
return E_INVALIDARG;
}
return S_OK;
}
static IInArchive *CreateArc() { return new CHandler; }
#ifndef EXTRACT_ONLY
static IOutArchive *CreateArcOut() { return new CHandler; }
#else
#define CreateArcOut 0
#endif
static CArcInfo g_ArcInfo =
{ L"GZip", L"gz gzip tgz tpz", L"* * .tar .tar", 0xEF, { 0x1F, 0x8B, 8 }, 3, true, CreateArc, CreateArcOut };
REGISTER_ARC(GZip)
}}