diff options
author | kh1 <karsten.heimrich@nokia.com> | 2012-03-15 14:53:47 +0100 |
---|---|---|
committer | Karsten Heimrich <karsten.heimrich@nokia.com> | 2012-03-19 16:14:04 +0100 |
commit | be3b47d0d504a3409ce66bd77bb8c0acff87c4f5 (patch) | |
tree | 09dfb02d484a4f395991972b828da71400fb761a /src/libs/7zip/win/CPP/7zip/Archive/Zip | |
parent | 9fd62353cf7f973d78cd2093328ac15b5c4980b6 (diff) |
Reorganize the tree, have better ifw.pri. Shadow build support.
Change-Id: I01fb12537f863ed0744979973c7e4153889cc5cb
Reviewed-by: Tim Jenssen <tim.jenssen@nokia.com>
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/Zip')
19 files changed, 5253 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/StdAfx.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/StdAfx.h new file mode 100644 index 000000000..e7fb6986d --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/StdAfx.h @@ -0,0 +1,8 @@ +// StdAfx.h + +#ifndef __STDAFX_H +#define __STDAFX_H + +#include "../../../Common/MyWindows.h" + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipAddCommon.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipAddCommon.cpp new file mode 100644 index 000000000..4c5fd38d1 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipAddCommon.cpp @@ -0,0 +1,379 @@ +// ZipAddCommon.cpp + +#include "StdAfx.h" + +#include "../../../../C/7zCrc.h" + +#include "Windows/PropVariant.h" + +#include "../../ICoder.h" +#include "../../IPassword.h" +#include "../../MyVersion.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" + +#include "../../Compress/LzmaEncoder.h" +#include "../../Compress/PpmdZip.h" + +#include "../Common/InStreamWithCRC.h" + +#include "ZipAddCommon.h" +#include "ZipHeader.h" + +namespace NArchive { +namespace NZip { + +static const CMethodId kMethodId_ZipBase = 0x040100; +static const CMethodId kMethodId_BZip2 = 0x040202; + +static const UInt32 kLzmaPropsSize = 5; +static const UInt32 kLzmaHeaderSize = 4 + kLzmaPropsSize; + +class CLzmaEncoder: + public ICompressCoder, + public CMyUnknownImp +{ + NCompress::NLzma::CEncoder *EncoderSpec; + CMyComPtr<ICompressCoder> Encoder; + Byte Header[kLzmaHeaderSize]; +public: + STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); + HRESULT SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); + + MY_UNKNOWN_IMP +}; + +HRESULT CLzmaEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps) +{ + if (!Encoder) + { + EncoderSpec = new NCompress::NLzma::CEncoder; + Encoder = EncoderSpec; + } + CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->Init(Header + 4, kLzmaPropsSize); + RINOK(EncoderSpec->SetCoderProperties(propIDs, props, numProps)); + RINOK(EncoderSpec->WriteCoderProperties(outStream)); + if (outStreamSpec->GetPos() != kLzmaPropsSize) + return E_FAIL; + Header[0] = MY_VER_MAJOR; + Header[1] = MY_VER_MINOR; + Header[2] = kLzmaPropsSize; + Header[3] = 0; + return S_OK; +} + +HRESULT CLzmaEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress) +{ + RINOK(WriteStream(outStream, Header, kLzmaHeaderSize)); + return Encoder->Code(inStream, outStream, inSize, outSize, progress); +} + + +CAddCommon::CAddCommon(const CCompressionMethodMode &options): + _options(options), + _copyCoderSpec(NULL), + _cryptoStreamSpec(0) + {} + +static HRESULT GetStreamCRC(ISequentialInStream *inStream, UInt32 &resultCRC) +{ + UInt32 crc = CRC_INIT_VAL; + const UInt32 kBufferSize = (1 << 14); + Byte buffer[kBufferSize]; + for (;;) + { + UInt32 realProcessedSize; + RINOK(inStream->Read(buffer, kBufferSize, &realProcessedSize)); + if (realProcessedSize == 0) + { + resultCRC = CRC_GET_DIGEST(crc); + return S_OK; + } + crc = CrcUpdate(crc, buffer, (size_t)realProcessedSize); + } +} + +HRESULT CAddCommon::Compress( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, IOutStream *outStream, + ICompressProgressInfo *progress, CCompressingResult &opRes) +{ + CSequentialInStreamWithCRC *inSecCrcStreamSpec = 0; + CInStreamWithCRC *inCrcStreamSpec = 0; + CMyComPtr<ISequentialInStream> inCrcStream; + { + CMyComPtr<IInStream> inStream2; + // we don't support stdin, since stream from stdin can require 64-bit size header + RINOK(inStream->QueryInterface(IID_IInStream, (void **)&inStream2)); + if (inStream2) + { + inCrcStreamSpec = new CInStreamWithCRC; + inCrcStream = inCrcStreamSpec; + inCrcStreamSpec->SetStream(inStream2); + inCrcStreamSpec->Init(); + } + else + { + inSecCrcStreamSpec = new CSequentialInStreamWithCRC; + inCrcStream = inSecCrcStreamSpec; + inSecCrcStreamSpec->SetStream(inStream); + inSecCrcStreamSpec->Init(); + } + } + + int numTestMethods = _options.MethodSequence.Size(); + if (numTestMethods > 1 || _options.PasswordIsDefined) + { + if (inCrcStreamSpec == 0) + { + if (_options.PasswordIsDefined) + return E_NOTIMPL; + numTestMethods = 1; + } + } + Byte method = 0; + COutStreamReleaser outStreamReleaser; + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_Default; + for (int i = 0; i < numTestMethods; i++) + { + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_Default; + if (inCrcStreamSpec != 0) + RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(outStream->SetSize(0)); + RINOK(outStream->Seek(0, STREAM_SEEK_SET, NULL)); + if (_options.PasswordIsDefined) + { + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_ZipCrypto; + + if (!_cryptoStream) + { + _cryptoStreamSpec = new CFilterCoder; + _cryptoStream = _cryptoStreamSpec; + } + if (_options.IsAesMode) + { + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_Aes; + if (!_cryptoStreamSpec->Filter) + { + _cryptoStreamSpec->Filter = _filterAesSpec = new NCrypto::NWzAes::CEncoder; + _filterAesSpec->SetKeyMode(_options.AesKeyMode); + RINOK(_filterAesSpec->CryptoSetPassword((const Byte *)(const char *)_options.Password, _options.Password.Length())); + } + RINOK(_filterAesSpec->WriteHeader(outStream)); + } + else + { + if (!_cryptoStreamSpec->Filter) + { + _cryptoStreamSpec->Filter = _filterSpec = new NCrypto::NZip::CEncoder; + _filterSpec->CryptoSetPassword((const Byte *)(const char *)_options.Password, _options.Password.Length()); + } + UInt32 crc = 0; + RINOK(GetStreamCRC(inStream, crc)); + RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(_filterSpec->WriteHeader(outStream, crc)); + } + RINOK(_cryptoStreamSpec->SetOutStream(outStream)); + outStreamReleaser.FilterCoder = _cryptoStreamSpec; + } + + method = _options.MethodSequence[i]; + switch(method) + { + case NFileHeader::NCompressionMethod::kStored: + { + if (_copyCoderSpec == NULL) + { + _copyCoderSpec = new NCompress::CCopyCoder; + _copyCoder = _copyCoderSpec; + } + CMyComPtr<ISequentialOutStream> outStreamNew; + if (_options.PasswordIsDefined) + outStreamNew = _cryptoStream; + else + outStreamNew = outStream; + RINOK(_copyCoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress)); + break; + } + default: + { + if (!_compressEncoder) + { + if (method == NFileHeader::NCompressionMethod::kLZMA) + { + _compressExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_LZMA; + CLzmaEncoder *_lzmaEncoder = new CLzmaEncoder(); + _compressEncoder = _lzmaEncoder; + NWindows::NCOM::CPropVariant props[] = + { + #ifndef _7ZIP_ST + _options.NumThreads, + #endif + _options.Algo, + _options.DicSize, + _options.NumFastBytes, + const_cast<BSTR>((const wchar_t *)_options.MatchFinder), + _options.NumMatchFinderCycles + }; + PROPID propIDs[] = + { + #ifndef _7ZIP_ST + NCoderPropID::kNumThreads, + #endif + NCoderPropID::kAlgorithm, + NCoderPropID::kDictionarySize, + NCoderPropID::kNumFastBytes, + NCoderPropID::kMatchFinder, + NCoderPropID::kMatchFinderCycles + }; + int numProps = sizeof(propIDs) / sizeof(propIDs[0]); + if (!_options.NumMatchFinderCyclesDefined) + numProps--; + RINOK(_lzmaEncoder->SetCoderProperties(propIDs, props, numProps)); + } + else if (method == NFileHeader::NCompressionMethod::kPPMd) + { + _compressExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_PPMd; + NCompress::NPpmdZip::CEncoder *encoder = new NCompress::NPpmdZip::CEncoder(); + _compressEncoder = encoder; + NWindows::NCOM::CPropVariant props[] = + { + _options.Algo, + _options.MemSize, + _options.Order + + }; + PROPID propIDs[] = + { + NCoderPropID::kAlgorithm, + NCoderPropID::kUsedMemorySize, + NCoderPropID::kOrder + }; + RINOK(encoder->SetCoderProperties(propIDs, props, sizeof(propIDs) / sizeof(propIDs[0]))); + } + else + { + CMethodId methodId; + switch(method) + { + case NFileHeader::NCompressionMethod::kBZip2: + methodId = kMethodId_BZip2; + _compressExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_BZip2; + break; + default: + _compressExtractVersion = ((method == NFileHeader::NCompressionMethod::kDeflated64) ? + NFileHeader::NCompressionMethod::kExtractVersion_Deflate64 : + NFileHeader::NCompressionMethod::kExtractVersion_Deflate); + methodId = kMethodId_ZipBase + method; + break; + } + RINOK(CreateCoder( + EXTERNAL_CODECS_LOC_VARS + methodId, _compressEncoder, true)); + if (!_compressEncoder) + return E_NOTIMPL; + + if (method == NFileHeader::NCompressionMethod::kDeflated || + method == NFileHeader::NCompressionMethod::kDeflated64) + { + NWindows::NCOM::CPropVariant props[] = + { + _options.Algo, + _options.NumPasses, + _options.NumFastBytes, + _options.NumMatchFinderCycles + }; + PROPID propIDs[] = + { + NCoderPropID::kAlgorithm, + NCoderPropID::kNumPasses, + NCoderPropID::kNumFastBytes, + NCoderPropID::kMatchFinderCycles + }; + int numProps = sizeof(propIDs) / sizeof(propIDs[0]); + if (!_options.NumMatchFinderCyclesDefined) + numProps--; + CMyComPtr<ICompressSetCoderProperties> setCoderProperties; + _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties); + if (setCoderProperties) + { + RINOK(setCoderProperties->SetCoderProperties(propIDs, props, numProps)); + } + } + else if (method == NFileHeader::NCompressionMethod::kBZip2) + { + NWindows::NCOM::CPropVariant props[] = + { + _options.DicSize, + _options.NumPasses + #ifndef _7ZIP_ST + , _options.NumThreads + #endif + }; + PROPID propIDs[] = + { + NCoderPropID::kDictionarySize, + NCoderPropID::kNumPasses + #ifndef _7ZIP_ST + , NCoderPropID::kNumThreads + #endif + }; + CMyComPtr<ICompressSetCoderProperties> setCoderProperties; + _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties); + if (setCoderProperties) + { + RINOK(setCoderProperties->SetCoderProperties(propIDs, props, sizeof(propIDs) / sizeof(propIDs[0]))); + } + } + } + } + CMyComPtr<ISequentialOutStream> outStreamNew; + if (_options.PasswordIsDefined) + outStreamNew = _cryptoStream; + else + outStreamNew = outStream; + if (_compressExtractVersion > opRes.ExtractVersion) + opRes.ExtractVersion = _compressExtractVersion; + RINOK(_compressEncoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress)); + break; + } + } + + RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &opRes.PackSize)); + + if (inCrcStreamSpec != 0) + { + opRes.CRC = inCrcStreamSpec->GetCRC(); + opRes.UnpackSize = inCrcStreamSpec->GetSize(); + } + else + { + opRes.CRC = inSecCrcStreamSpec->GetCRC(); + opRes.UnpackSize = inSecCrcStreamSpec->GetSize(); + } + + if (_options.PasswordIsDefined) + { + if (opRes.PackSize < opRes.UnpackSize + + (_options.IsAesMode ? _filterAesSpec->GetHeaderSize() : NCrypto::NZip::kHeaderSize)) + break; + } + else if (opRes.PackSize < opRes.UnpackSize) + break; + } + if (_options.IsAesMode) + { + RINOK(_filterAesSpec->WriteFooter(outStream)); + RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &opRes.PackSize)); + } + opRes.Method = method; + return S_OK; +} + +}} diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipAddCommon.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipAddCommon.h new file mode 100644 index 000000000..e4c02db3f --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipAddCommon.h @@ -0,0 +1,56 @@ +// ZipAddCommon.h + +#ifndef __ZIP_ADD_COMMON_H +#define __ZIP_ADD_COMMON_H + +#include "../../ICoder.h" +#include "../../IProgress.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/FilterCoder.h" + +#include "../../Compress/CopyCoder.h" + +#include "../../Crypto/ZipCrypto.h" +#include "../../Crypto/WzAes.h" + +#include "ZipCompressionMode.h" + +namespace NArchive { +namespace NZip { + +struct CCompressingResult +{ + UInt64 UnpackSize; + UInt64 PackSize; + UInt32 CRC; + UInt16 Method; + Byte ExtractVersion; +}; + +class CAddCommon +{ + CCompressionMethodMode _options; + NCompress::CCopyCoder *_copyCoderSpec; + CMyComPtr<ICompressCoder> _copyCoder; + + CMyComPtr<ICompressCoder> _compressEncoder; + Byte _compressExtractVersion; + + CFilterCoder *_cryptoStreamSpec; + CMyComPtr<ISequentialOutStream> _cryptoStream; + + NCrypto::NZip::CEncoder *_filterSpec; + NCrypto::NWzAes::CEncoder *_filterAesSpec; + +public: + CAddCommon(const CCompressionMethodMode &options); + HRESULT Compress( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, IOutStream *outStream, + ICompressProgressInfo *progress, CCompressingResult &operationResult); +}; + +}} + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipCompressionMode.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipCompressionMode.h new file mode 100644 index 000000000..7ef7cfb28 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipCompressionMode.h @@ -0,0 +1,42 @@ +// CompressionMode.h + +#ifndef __ZIP_COMPRESSION_MODE_H +#define __ZIP_COMPRESSION_MODE_H + +#include "Common/MyString.h" + +namespace NArchive { +namespace NZip { + +struct CCompressionMethodMode +{ + CRecordVector<Byte> MethodSequence; + UString MatchFinder; + UInt32 Algo; + UInt32 NumPasses; + UInt32 NumFastBytes; + bool NumMatchFinderCyclesDefined; + UInt32 NumMatchFinderCycles; + UInt32 DicSize; + UInt32 MemSize; + UInt32 Order; + + #ifndef _7ZIP_ST + UInt32 NumThreads; + #endif + bool PasswordIsDefined; + AString Password; + bool IsAesMode; + Byte AesKeyMode; + + CCompressionMethodMode(): + NumMatchFinderCyclesDefined(false), + PasswordIsDefined(false), + IsAesMode(false), + AesKeyMode(3) + {} +}; + +}} + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandler.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandler.cpp new file mode 100644 index 000000000..bd1563226 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandler.cpp @@ -0,0 +1,822 @@ +// ZipHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../IPassword.h" + +#include "../../Common/FilterCoder.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" + +#include "../../Compress/CopyCoder.h" +#include "../../Compress/LzmaDecoder.h" +#include "../../Compress/ImplodeDecoder.h" +#include "../../Compress/PpmdZip.h" +#include "../../Compress/ShrinkDecoder.h" + +#include "../../Crypto/WzAes.h" +#include "../../Crypto/ZipCrypto.h" +#include "../../Crypto/ZipStrong.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/OutStreamWithCRC.h" + +#include "ZipHandler.h" + +using namespace NWindows; + +namespace NArchive { +namespace NZip { + +static const CMethodId kMethodId_ZipBase = 0x040100; +static const CMethodId kMethodId_BZip2 = 0x040202; + +static const char *kHostOS[] = +{ + "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"; + +static const char *kMethods[] = +{ + "Store", + "Shrink", + "Reduced1", + "Reduced2", + "Reduced3", + "Reduced4", + "Implode", + "Tokenizing", + "Deflate", + "Deflate64", + "PKImploding" +}; + +static const char *kBZip2Method = "BZip2"; +static const char *kLZMAMethod = "LZMA"; +static const char *kJpegMethod = "Jpeg"; +static const char *kWavPackMethod = "WavPack"; +static const char *kPPMdMethod = "PPMd"; +static const char *kAESMethod = "AES"; +static const char *kZipCryptoMethod = "ZipCrypto"; +static const char *kStrongCryptoMethod = "StrongCrypto"; + +static struct CStrongCryptoPair +{ + UInt16 Id; + const char *Name; +} g_StrongCryptoPairs[] = +{ + { NStrongCryptoFlags::kDES, "DES" }, + { NStrongCryptoFlags::kRC2old, "RC2a" }, + { NStrongCryptoFlags::k3DES168, "3DES-168" }, + { NStrongCryptoFlags::k3DES112, "3DES-112" }, + { NStrongCryptoFlags::kAES128, "pkAES-128" }, + { NStrongCryptoFlags::kAES192, "pkAES-192" }, + { NStrongCryptoFlags::kAES256, "pkAES-256" }, + { NStrongCryptoFlags::kRC2, "RC2" }, + { NStrongCryptoFlags::kBlowfish, "Blowfish" }, + { NStrongCryptoFlags::kTwofish, "Twofish" }, + { NStrongCryptoFlags::kRC4, "RC4" } +}; + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidEncrypted, VT_BOOL}, + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidCRC, VT_UI4}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidUnpackVer, VT_UI4} +}; + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidBit64, VT_BOOL}, + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidOffset, VT_UI8} +}; + +CHandler::CHandler() +{ + InitMethodProperties(); +} + +static AString BytesToString(const CByteBuffer &data) +{ + AString s; + int size = (int)data.GetCapacity(); + if (size > 0) + { + char *p = s.GetBuffer(size + 1); + memcpy(p, (const Byte *)data, size); + p[size] = '\0'; + s.ReleaseBuffer(); + } + return s; +} + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidBit64: if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break; + case kpidComment: prop = MultiByteToUnicodeString(BytesToString(m_Archive.ArcInfo.Comment), CP_ACP); break; + case kpidPhySize: prop = m_Archive.ArcInfo.GetPhySize(); break; + case kpidOffset: if (m_Archive.ArcInfo.StartPosition != 0) prop = m_Archive.ArcInfo.StartPosition; break; + } + prop.Detach(value); + COM_TRY_END + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = m_Items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItemEx &item = m_Items[index]; + switch(propID) + { + case kpidPath: prop = NItemName::GetOSName2(item.GetUnicodeString(item.Name)); break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: prop = item.UnPackSize; break; + case kpidPackSize: prop = item.PackSize; break; + case kpidTimeType: + { + FILETIME ft; + UInt32 unixTime; + if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, ft)) + prop = (UInt32)NFileTimeType::kWindows; + else if (item.CentralExtra.GetUnixTime(NFileHeader::NUnixTime::kMTime, unixTime)) + prop = (UInt32)NFileTimeType::kUnix; + else + prop = (UInt32)NFileTimeType::kDOS; + break; + } + case kpidCTime: + { + FILETIME ft; + if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kCTime, ft)) + prop = ft; + break; + } + case kpidATime: + { + FILETIME ft; + if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kATime, ft)) + prop = ft; + break; + } + case kpidMTime: + { + FILETIME utc; + if (!item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, utc)) + { + UInt32 unixTime; + if (item.CentralExtra.GetUnixTime(NFileHeader::NUnixTime::kMTime, unixTime)) + NTime::UnixTimeToFileTime(unixTime, utc); + else + { + FILETIME localFileTime; + if (!NTime::DosTimeToFileTime(item.Time, localFileTime) || + !LocalFileTimeToFileTime(&localFileTime, &utc)) + utc.dwHighDateTime = utc.dwLowDateTime = 0; + } + } + prop = utc; + break; + } + case kpidAttrib: prop = item.GetWinAttributes(); break; + case kpidEncrypted: prop = item.IsEncrypted(); break; + case kpidComment: prop = item.GetUnicodeString(BytesToString(item.Comment)); break; + case kpidCRC: if (item.IsThereCrc()) prop = item.FileCRC; break; + case kpidMethod: + { + UInt16 methodId = item.CompressionMethod; + AString method; + if (item.IsEncrypted()) + { + if (methodId == NFileHeader::NCompressionMethod::kWzAES) + { + method = kAESMethod; + CWzAesExtraField aesField; + if (item.CentralExtra.GetWzAesField(aesField)) + { + method += '-'; + char s[32]; + ConvertUInt64ToString((aesField.Strength + 1) * 64 , s); + method += s; + method += ' '; + methodId = aesField.Method; + } + } + else + { + if (item.IsStrongEncrypted()) + { + CStrongCryptoField f; + bool finded = false; + if (item.CentralExtra.GetStrongCryptoField(f)) + { + for (int i = 0; i < sizeof(g_StrongCryptoPairs) / sizeof(g_StrongCryptoPairs[0]); i++) + { + const CStrongCryptoPair &pair = g_StrongCryptoPairs[i]; + if (f.AlgId == pair.Id) + { + method += pair.Name; + finded = true; + break; + } + } + } + if (!finded) + method += kStrongCryptoMethod; + } + else + method += kZipCryptoMethod; + method += ' '; + } + } + if (methodId < sizeof(kMethods) / sizeof(kMethods[0])) + method += kMethods[methodId]; + else switch (methodId) + { + case NFileHeader::NCompressionMethod::kLZMA: + method += kLZMAMethod; + if (item.IsLzmaEOS()) + method += ":EOS"; + break; + case NFileHeader::NCompressionMethod::kBZip2: method += kBZip2Method; break; + case NFileHeader::NCompressionMethod::kJpeg: method += kJpegMethod; break; + case NFileHeader::NCompressionMethod::kWavPack: method += kWavPackMethod; break; + case NFileHeader::NCompressionMethod::kPPMd: method += kPPMdMethod; break; + default: + { + char s[32]; + ConvertUInt64ToString(methodId, s); + method += s; + } + } + prop = method; + break; + } + case kpidHostOS: + prop = (item.MadeByVersion.HostOS < sizeof(kHostOS) / sizeof(kHostOS[0])) ? + (kHostOS[item.MadeByVersion.HostOS]) : kUnknownOS; + break; + case kpidUnpackVer: + prop = (UInt32)item.ExtractVersion.Version; + break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CProgressImp: public CProgressVirt +{ + CMyComPtr<IArchiveOpenCallback> _callback; +public: + STDMETHOD(SetTotal)(UInt64 numFiles); + STDMETHOD(SetCompleted)(UInt64 numFiles); + CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {} +}; + +STDMETHODIMP CProgressImp::SetTotal(UInt64 numFiles) +{ + if (_callback) + return _callback->SetTotal(&numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CProgressImp::SetCompleted(UInt64 numFiles) +{ + if (_callback) + return _callback->SetCompleted(&numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + try + { + Close(); + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(m_Archive.Open(inStream, maxCheckStartPosition)); + CProgressImp progressImp(callback); + return m_Archive.ReadHeaders(m_Items, &progressImp); + } + catch(const CInArchiveException &) { Close(); return S_FALSE; } + catch(...) { Close(); throw; } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + m_Items.Clear(); + m_Archive.Close(); + return S_OK; +} + +////////////////////////////////////// +// CHandler::DecompressItems + +class CLzmaDecoder: + public ICompressCoder, + public CMyUnknownImp +{ + NCompress::NLzma::CDecoder *DecoderSpec; + CMyComPtr<ICompressCoder> Decoder; +public: + CLzmaDecoder(); + STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); + + MY_UNKNOWN_IMP +}; + +CLzmaDecoder::CLzmaDecoder() +{ + DecoderSpec = new NCompress::NLzma::CDecoder; + Decoder = DecoderSpec; +} + +HRESULT CLzmaDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) +{ + Byte buf[9]; + RINOK(ReadStream_FALSE(inStream, buf, 9)); + if (buf[2] != 5 || buf[3] != 0) + return E_NOTIMPL; + RINOK(DecoderSpec->SetDecoderProperties2(buf + 4, 5)); + return Decoder->Code(inStream, outStream, NULL, outSize, progress); +} + +struct CMethodItem +{ + UInt16 ZipMethod; + CMyComPtr<ICompressCoder> Coder; +}; + +class CZipDecoder +{ + NCrypto::NZip::CDecoder *_zipCryptoDecoderSpec; + NCrypto::NZipStrong::CDecoder *_pkAesDecoderSpec; + NCrypto::NWzAes::CDecoder *_wzAesDecoderSpec; + + CMyComPtr<ICompressFilter> _zipCryptoDecoder; + CMyComPtr<ICompressFilter> _pkAesDecoder; + CMyComPtr<ICompressFilter> _wzAesDecoder; + + CFilterCoder *filterStreamSpec; + CMyComPtr<ISequentialInStream> filterStream; + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + CObjectVector<CMethodItem> methodItems; + +public: + CZipDecoder(): + _zipCryptoDecoderSpec(0), + _pkAesDecoderSpec(0), + _wzAesDecoderSpec(0), + filterStreamSpec(0) {} + + HRESULT Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + CInArchive &archive, const CItemEx &item, + ISequentialOutStream *realOutStream, + IArchiveExtractCallback *extractCallback, + ICompressProgressInfo *compressProgress, + UInt32 numThreads, Int32 &res); +}; + +HRESULT CZipDecoder::Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + CInArchive &archive, const CItemEx &item, + ISequentialOutStream *realOutStream, + IArchiveExtractCallback *extractCallback, + ICompressProgressInfo *compressProgress, + UInt32 numThreads, Int32 &res) +{ + res = NExtract::NOperationResult::kDataError; + CInStreamReleaser inStreamReleaser; + + bool needCRC = true; + bool wzAesMode = false; + bool pkAesMode = false; + UInt16 methodId = item.CompressionMethod; + if (item.IsEncrypted()) + { + if (item.IsStrongEncrypted()) + { + CStrongCryptoField f; + if (item.CentralExtra.GetStrongCryptoField(f)) + { + pkAesMode = true; + } + if (!pkAesMode) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + } + if (methodId == NFileHeader::NCompressionMethod::kWzAES) + { + CWzAesExtraField aesField; + if (item.CentralExtra.GetWzAesField(aesField)) + { + wzAesMode = true; + needCRC = aesField.NeedCrc(); + } + } + } + + COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; + CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(needCRC); + + UInt64 authenticationPos; + + CMyComPtr<ISequentialInStream> inStream; + { + UInt64 packSize = item.PackSize; + if (wzAesMode) + { + if (packSize < NCrypto::NWzAes::kMacSize) + return S_OK; + packSize -= NCrypto::NWzAes::kMacSize; + } + UInt64 dataPos = item.GetDataPosition(); + inStream.Attach(archive.CreateLimitedStream(dataPos, packSize)); + authenticationPos = dataPos + packSize; + } + + CMyComPtr<ICompressFilter> cryptoFilter; + if (item.IsEncrypted()) + { + if (wzAesMode) + { + CWzAesExtraField aesField; + if (!item.CentralExtra.GetWzAesField(aesField)) + return S_OK; + methodId = aesField.Method; + if (!_wzAesDecoder) + { + _wzAesDecoderSpec = new NCrypto::NWzAes::CDecoder; + _wzAesDecoder = _wzAesDecoderSpec; + } + cryptoFilter = _wzAesDecoder; + Byte properties = aesField.Strength; + RINOK(_wzAesDecoderSpec->SetDecoderProperties2(&properties, 1)); + } + else if (pkAesMode) + { + if (!_pkAesDecoder) + { + _pkAesDecoderSpec = new NCrypto::NZipStrong::CDecoder; + _pkAesDecoder = _pkAesDecoderSpec; + } + cryptoFilter = _pkAesDecoder; + } + else + { + if (!_zipCryptoDecoder) + { + _zipCryptoDecoderSpec = new NCrypto::NZip::CDecoder; + _zipCryptoDecoder = _zipCryptoDecoderSpec; + } + cryptoFilter = _zipCryptoDecoder; + } + CMyComPtr<ICryptoSetPassword> cryptoSetPassword; + RINOK(cryptoFilter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword)); + + if (!getTextPassword) + extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword); + + if (getTextPassword) + { + CMyComBSTR password; + RINOK(getTextPassword->CryptoGetTextPassword(&password)); + AString charPassword; + if (wzAesMode || pkAesMode) + { + charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_ACP); + /* + for (int i = 0;; i++) + { + wchar_t c = password[i]; + if (c == 0) + break; + if (c >= 0x80) + { + res = NExtract::NOperationResult::kDataError; + return S_OK; + } + charPassword += (char)c; + } + */ + } + else + { + // we use OEM. WinZip/Windows probably use ANSI for some files + charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP); + } + HRESULT result = cryptoSetPassword->CryptoSetPassword( + (const Byte *)(const char *)charPassword, charPassword.Length()); + if (result != S_OK) + return S_OK; + } + else + { + RINOK(cryptoSetPassword->CryptoSetPassword(0, 0)); + } + } + + int m; + for (m = 0; m < methodItems.Size(); m++) + if (methodItems[m].ZipMethod == methodId) + break; + + if (m == methodItems.Size()) + { + CMethodItem mi; + mi.ZipMethod = methodId; + if (methodId == NFileHeader::NCompressionMethod::kStored) + mi.Coder = new NCompress::CCopyCoder; + else if (methodId == NFileHeader::NCompressionMethod::kShrunk) + mi.Coder = new NCompress::NShrink::CDecoder; + else if (methodId == NFileHeader::NCompressionMethod::kImploded) + mi.Coder = new NCompress::NImplode::NDecoder::CCoder; + else if (methodId == NFileHeader::NCompressionMethod::kLZMA) + mi.Coder = new CLzmaDecoder; + else if (methodId == NFileHeader::NCompressionMethod::kPPMd) + mi.Coder = new NCompress::NPpmdZip::CDecoder(true); + else + { + CMethodId szMethodID; + if (methodId == NFileHeader::NCompressionMethod::kBZip2) + szMethodID = kMethodId_BZip2; + else + { + if (methodId > 0xFF) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + szMethodID = kMethodId_ZipBase + (Byte)methodId; + } + + RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS szMethodID, mi.Coder, false)); + + if (mi.Coder == 0) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + } + m = methodItems.Add(mi); + } + ICompressCoder *coder = methodItems[m].Coder; + + { + CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties; + coder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties); + if (setDecoderProperties) + { + Byte properties = (Byte)item.Flags; + RINOK(setDecoderProperties->SetDecoderProperties2(&properties, 1)); + } + } + + #ifndef _7ZIP_ST + { + CMyComPtr<ICompressSetCoderMt> setCoderMt; + coder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt); + if (setCoderMt) + { + RINOK(setCoderMt->SetNumberOfThreads(numThreads)); + } + } + #endif + + { + HRESULT result = S_OK; + CMyComPtr<ISequentialInStream> inStreamNew; + if (item.IsEncrypted()) + { + if (!filterStream) + { + filterStreamSpec = new CFilterCoder; + filterStream = filterStreamSpec; + } + filterStreamSpec->Filter = cryptoFilter; + if (wzAesMode) + { + result = _wzAesDecoderSpec->ReadHeader(inStream); + } + else if (pkAesMode) + { + result =_pkAesDecoderSpec->ReadHeader(inStream, item.FileCRC, item.UnPackSize); + if (result == S_OK) + { + bool passwOK; + result = _pkAesDecoderSpec->CheckPassword(passwOK); + if (result == S_OK && !passwOK) + result = S_FALSE; + } + } + else + { + result = _zipCryptoDecoderSpec->ReadHeader(inStream); + } + + if (result == S_OK) + { + RINOK(filterStreamSpec->SetInStream(inStream)); + inStreamReleaser.FilterCoder = filterStreamSpec; + inStreamNew = filterStream; + if (wzAesMode) + { + if (!_wzAesDecoderSpec->CheckPasswordVerifyCode()) + result = S_FALSE; + } + } + } + else + inStreamNew = inStream; + if (result == S_OK) + result = coder->Code(inStreamNew, outStream, NULL, &item.UnPackSize, compressProgress); + if (result == S_FALSE) + return S_OK; + if (result == E_NOTIMPL) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + + RINOK(result); + } + bool crcOK = true; + bool authOk = true; + if (needCRC) + crcOK = (outStreamSpec->GetCRC() == item.FileCRC); + if (wzAesMode) + { + inStream.Attach(archive.CreateLimitedStream(authenticationPos, NCrypto::NWzAes::kMacSize)); + if (_wzAesDecoderSpec->CheckMac(inStream, authOk) != S_OK) + authOk = false; + } + + res = ((crcOK && authOk) ? + NExtract::NOperationResult::kOK : + NExtract::NOperationResult::kCRCError); + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + CZipDecoder myDecoder; + UInt64 totalUnPacked = 0, totalPacked = 0; + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = m_Items.Size(); + if(numItems == 0) + return S_OK; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const CItemEx &item = m_Items[allFilesMode ? i : indices[i]]; + totalUnPacked += item.UnPackSize; + totalPacked += item.PackSize; + } + RINOK(extractCallback->SetTotal(totalUnPacked)); + + UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0; + UInt64 currentItemUnPacked, currentItemPacked; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked, + currentTotalPacked += currentItemPacked) + { + currentItemUnPacked = 0; + currentItemPacked = 0; + + lps->InSize = currentTotalPacked; + lps->OutSize = currentTotalUnPacked; + RINOK(lps->SetCur()); + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + CItemEx item = m_Items[index]; + if (!item.FromLocal) + { + HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item); + if (res == S_FALSE) + { + if (item.IsDir() || realOutStream || testMode) + { + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + } + continue; + } + RINOK(res); + } + + if (item.IsDir() || item.IgnoreItem()) + { + // if (!testMode) + { + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + continue; + } + + currentItemUnPacked = item.UnPackSize; + currentItemPacked = item.PackSize; + + if (!testMode && !realOutStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + + Int32 res; + RINOK(myDecoder.Decode( + EXTERNAL_CODECS_VARS + m_Archive, item, realOutStream, extractCallback, + progress, _numThreads, res)); + realOutStream.Release(); + + RINOK(extractCallback->SetOperationResult(res)) + } + return S_OK; + COM_TRY_END +} + +IMPL_ISetCompressCodecsInfo + +}} diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandler.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandler.h new file mode 100644 index 000000000..fdb60aafe --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandler.h @@ -0,0 +1,101 @@ +// Zip/Handler.h + +#ifndef __ZIP_HANDLER_H +#define __ZIP_HANDLER_H + +#include "Common/DynamicBuffer.h" +#include "../../ICoder.h" +#include "../IArchive.h" + +#include "../../Common/CreateCoder.h" + +#include "ZipIn.h" +#include "ZipCompressionMode.h" + +#ifndef _7ZIP_ST +#include "../../../Windows/System.h" +#endif + +namespace NArchive { +namespace NZip { + +class CHandler: + public IInArchive, + public IOutArchive, + public ISetProperties, + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + MY_QUERYINTERFACE_ENTRY(IOutArchive) + MY_QUERYINTERFACE_ENTRY(ISetProperties) + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + INTERFACE_IOutArchive(;) + + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties); + + DECL_ISetCompressCodecsInfo + + CHandler(); +private: + CObjectVector<CItemEx> m_Items; + CInArchive m_Archive; + + int m_Level; + int m_MainMethod; + UInt32 m_DicSize; + UInt32 m_Algo; + UInt32 m_NumPasses; + UInt32 m_NumFastBytes; + UInt32 m_NumMatchFinderCycles; + UInt32 m_MemSize; + UInt32 m_Order; + + bool m_NumMatchFinderCyclesDefined; + + bool m_ForceAesMode; + bool m_IsAesMode; + Byte m_AesKeyMode; + + bool m_WriteNtfsTimeExtra; + bool m_ForceLocal; + bool m_ForceUtf8; + + #ifndef _7ZIP_ST + UInt32 _numThreads; + #endif + + DECL_EXTERNAL_CODECS_VARS + + void InitMethodProperties() + { + m_Level = -1; + m_MainMethod = -1; + m_Algo = + m_DicSize = + m_NumPasses = + m_NumFastBytes = + m_Order = + m_MemSize = + m_NumMatchFinderCycles = 0xFFFFFFFF; + m_NumMatchFinderCyclesDefined = false; + m_ForceAesMode = false; + m_IsAesMode = false; + m_AesKeyMode = 3; // aes-256 + m_WriteNtfsTimeExtra = true; + m_ForceLocal = false; + m_ForceUtf8 = false; + #ifndef _7ZIP_ST + _numThreads = NWindows::NSystem::GetNumberOfProcessors();; + #endif + } +}; + +}} + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandlerOut.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandlerOut.cpp new file mode 100644 index 000000000..a5e0f59d7 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHandlerOut.cpp @@ -0,0 +1,531 @@ +// ZipHandlerOut.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" +#include "Common/StringToInt.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../IPassword.h" + +#include "../../Common/OutBuffer.h" + +#include "../../Crypto/WzAes.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/ParseProperties.h" + +#include "ZipHandler.h" +#include "ZipUpdate.h" + +using namespace NWindows; +using namespace NCOM; +using namespace NTime; + +namespace NArchive { +namespace NZip { + +static const UInt32 kLzAlgoX1 = 0; +static const UInt32 kLzAlgoX5 = 1; + +static const UInt32 kDeflateNumPassesX1 = 1; +static const UInt32 kDeflateNumPassesX7 = 3; +static const UInt32 kDeflateNumPassesX9 = 10; + +static const UInt32 kDeflateNumFastBytesX1 = 32; +static const UInt32 kDeflateNumFastBytesX7 = 64; +static const UInt32 kDeflateNumFastBytesX9 = 128; + +static const wchar_t *kLzmaMatchFinderX1 = L"HC4"; +static const wchar_t *kLzmaMatchFinderX5 = L"BT4"; + +static const UInt32 kLzmaNumFastBytesX1 = 32; +static const UInt32 kLzmaNumFastBytesX7 = 64; + +static const UInt32 kLzmaDicSizeX1 = 1 << 16; +static const UInt32 kLzmaDicSizeX3 = 1 << 20; +static const UInt32 kLzmaDicSizeX5 = 1 << 24; +static const UInt32 kLzmaDicSizeX7 = 1 << 25; +static const UInt32 kLzmaDicSizeX9 = 1 << 26; + +static const UInt32 kBZip2NumPassesX1 = 1; +static const UInt32 kBZip2NumPassesX7 = 2; +static const UInt32 kBZip2NumPassesX9 = 7; + +static const UInt32 kBZip2DicSizeX1 = 100000; +static const UInt32 kBZip2DicSizeX3 = 500000; +static const UInt32 kBZip2DicSizeX5 = 900000; + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) +{ + *timeType = NFileTimeType::kDOS; + return S_OK; +} + +static bool IsAsciiString(const UString &s) +{ + for (int i = 0; i < s.Length(); i++) + { + wchar_t c = s[i]; + if (c < 0x20 || c > 0x7F) + return false; + } + return true; +} + +#define COM_TRY_BEGIN2 try { +#define COM_TRY_END2 } \ +catch(const CSystemException &e) { return e.ErrorCode; } \ +catch(...) { return E_OUTOFMEMORY; } + +static HRESULT GetTime(IArchiveUpdateCallback *callback, int index, PROPID propID, FILETIME &filetime) +{ + filetime.dwHighDateTime = filetime.dwLowDateTime = 0; + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(index, propID, &prop)); + if (prop.vt == VT_FILETIME) + filetime = prop.filetime; + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *callback) +{ + COM_TRY_BEGIN2 + CObjectVector<CUpdateItem> updateItems; + bool thereAreAesUpdates = false; + for (UInt32 i = 0; i < numItems; i++) + { + CUpdateItem ui; + Int32 newData; + Int32 newProperties; + UInt32 indexInArchive; + if (!callback) + return E_FAIL; + RINOK(callback->GetUpdateItemInfo(i, &newData, &newProperties, &indexInArchive)); + ui.NewProperties = IntToBool(newProperties); + ui.NewData = IntToBool(newData); + ui.IndexInArchive = indexInArchive; + ui.IndexInClient = i; + bool existInArchive = (indexInArchive != (UInt32)-1); + if (existInArchive && newData) + if (m_Items[indexInArchive].IsAesEncrypted()) + thereAreAesUpdates = true; + + if (IntToBool(newProperties)) + { + UString name; + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidAttrib, &prop)); + if (prop.vt == VT_EMPTY) + ui.Attributes = 0; + else if (prop.vt != VT_UI4) + return E_INVALIDARG; + else + ui.Attributes = prop.ulVal; + } + + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidPath, &prop)); + if (prop.vt == VT_EMPTY) + name.Empty(); + else if (prop.vt != VT_BSTR) + return E_INVALIDARG; + else + name = prop.bstrVal; + } + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidIsDir, &prop)); + if (prop.vt == VT_EMPTY) + ui.IsDir = false; + else if (prop.vt != VT_BOOL) + return E_INVALIDARG; + else + ui.IsDir = (prop.boolVal != VARIANT_FALSE); + } + + { + CPropVariant prop; + RINOK(callback->GetProperty(i, kpidTimeType, &prop)); + if (prop.vt == VT_UI4) + ui.NtfsTimeIsDefined = (prop.ulVal == NFileTimeType::kWindows); + else + ui.NtfsTimeIsDefined = m_WriteNtfsTimeExtra; + } + RINOK(GetTime(callback, i, kpidMTime, ui.NtfsMTime)); + RINOK(GetTime(callback, i, kpidATime, ui.NtfsATime)); + RINOK(GetTime(callback, i, kpidCTime, ui.NtfsCTime)); + + { + FILETIME localFileTime = { 0, 0 }; + if (ui.NtfsMTime.dwHighDateTime != 0 || + ui.NtfsMTime.dwLowDateTime != 0) + if (!FileTimeToLocalFileTime(&ui.NtfsMTime, &localFileTime)) + return E_INVALIDARG; + FileTimeToDosTime(localFileTime, ui.Time); + } + + name = NItemName::MakeLegalName(name); + bool needSlash = ui.IsDir; + const wchar_t kSlash = L'/'; + if (!name.IsEmpty()) + { + if (name[name.Length() - 1] == kSlash) + { + if (!ui.IsDir) + return E_INVALIDARG; + needSlash = false; + } + } + if (needSlash) + name += kSlash; + + bool tryUtf8 = true; + if (m_ForceLocal || !m_ForceUtf8) + { + bool defaultCharWasUsed; + ui.Name = UnicodeStringToMultiByte(name, CP_OEMCP, '_', defaultCharWasUsed); + tryUtf8 = (!m_ForceLocal && (defaultCharWasUsed || + MultiByteToUnicodeString(ui.Name, CP_OEMCP) != name)); + } + + if (tryUtf8) + { + int i; + for (i = 0; i < name.Length() && (unsigned)name[i] < 0x80; i++); + ui.IsUtf8 = (i != name.Length()); + if (!ConvertUnicodeToUTF8(name, ui.Name)) + return E_INVALIDARG; + } + + if (ui.Name.Length() >= (1 << 16)) + return E_INVALIDARG; + + ui.IndexInClient = i; + /* + if (existInArchive) + { + const CItemEx &itemInfo = m_Items[indexInArchive]; + // ui.Commented = itemInfo.IsCommented(); + ui.Commented = false; + if (ui.Commented) + { + ui.CommentRange.Position = itemInfo.GetCommentPosition(); + ui.CommentRange.Size = itemInfo.CommentSize; + } + } + else + ui.Commented = false; + */ + } + if (IntToBool(newData)) + { + UInt64 size; + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + ui.Size = size; + } + updateItems.Add(ui); + } + + CMyComPtr<ICryptoGetTextPassword2> getTextPassword; + { + CMyComPtr<IArchiveUpdateCallback> udateCallBack2(callback); + udateCallBack2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword); + } + CCompressionMethodMode options; + + if (getTextPassword) + { + CMyComBSTR password; + Int32 passwordIsDefined; + RINOK(getTextPassword->CryptoGetTextPassword2(&passwordIsDefined, &password)); + options.PasswordIsDefined = IntToBool(passwordIsDefined); + if (options.PasswordIsDefined) + { + options.IsAesMode = (m_ForceAesMode ? m_IsAesMode : thereAreAesUpdates); + options.AesKeyMode = m_AesKeyMode; + + if (!IsAsciiString((const wchar_t *)password)) + return E_INVALIDARG; + if (options.IsAesMode) + { + if (options.Password.Length() > NCrypto::NWzAes::kPasswordSizeMax) + return E_INVALIDARG; + } + options.Password = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP); + } + } + else + options.PasswordIsDefined = false; + + int level = m_Level; + if (level < 0) + level = 5; + + Byte mainMethod; + if (m_MainMethod < 0) + mainMethod = (Byte)(((level == 0) ? + NFileHeader::NCompressionMethod::kStored : + NFileHeader::NCompressionMethod::kDeflated)); + else + mainMethod = (Byte)m_MainMethod; + options.MethodSequence.Add(mainMethod); + if (mainMethod != NFileHeader::NCompressionMethod::kStored) + options.MethodSequence.Add(NFileHeader::NCompressionMethod::kStored); + bool isDeflate = (mainMethod == NFileHeader::NCompressionMethod::kDeflated) || + (mainMethod == NFileHeader::NCompressionMethod::kDeflated64); + bool isLZMA = (mainMethod == NFileHeader::NCompressionMethod::kLZMA); + bool isLz = (isLZMA || isDeflate); + options.NumPasses = m_NumPasses; + options.DicSize = m_DicSize; + options.NumFastBytes = m_NumFastBytes; + options.NumMatchFinderCycles = m_NumMatchFinderCycles; + options.NumMatchFinderCyclesDefined = m_NumMatchFinderCyclesDefined; + options.Algo = m_Algo; + options.MemSize = m_MemSize; + options.Order = m_Order; + #ifndef _7ZIP_ST + options.NumThreads = _numThreads; + #endif + if (isLz) + { + if (isDeflate) + { + if (options.NumPasses == 0xFFFFFFFF) + options.NumPasses = (level >= 9 ? kDeflateNumPassesX9 : + (level >= 7 ? kDeflateNumPassesX7 : + kDeflateNumPassesX1)); + if (options.NumFastBytes == 0xFFFFFFFF) + options.NumFastBytes = (level >= 9 ? kDeflateNumFastBytesX9 : + (level >= 7 ? kDeflateNumFastBytesX7 : + kDeflateNumFastBytesX1)); + } + else if (isLZMA) + { + if (options.DicSize == 0xFFFFFFFF) + options.DicSize = + (level >= 9 ? kLzmaDicSizeX9 : + (level >= 7 ? kLzmaDicSizeX7 : + (level >= 5 ? kLzmaDicSizeX5 : + (level >= 3 ? kLzmaDicSizeX3 : + kLzmaDicSizeX1)))); + + if (options.NumFastBytes == 0xFFFFFFFF) + options.NumFastBytes = (level >= 7 ? kLzmaNumFastBytesX7 : + kLzmaNumFastBytesX1); + + options.MatchFinder = + (level >= 5 ? kLzmaMatchFinderX5 : + kLzmaMatchFinderX1); + } + + if (options.Algo == 0xFFFFFFFF) + options.Algo = (level >= 5 ? kLzAlgoX5 : + kLzAlgoX1); + } + if (mainMethod == NFileHeader::NCompressionMethod::kBZip2) + { + if (options.NumPasses == 0xFFFFFFFF) + options.NumPasses = (level >= 9 ? kBZip2NumPassesX9 : + (level >= 7 ? kBZip2NumPassesX7 : + kBZip2NumPassesX1)); + if (options.DicSize == 0xFFFFFFFF) + options.DicSize = (level >= 5 ? kBZip2DicSizeX5 : + (level >= 3 ? kBZip2DicSizeX3 : + kBZip2DicSizeX1)); + } + if (mainMethod == NFileHeader::NCompressionMethod::kPPMd) + { + int level2 = level; + if (level2 < 1) level2 = 1; + if (level2 > 9) level2 = 9; + + if (options.MemSize == 0xFFFFFFFF) + options.MemSize = (1 << (19 + (level2 > 8 ? 8 : level2))); + + if (options.Order == 0xFFFFFFFF) + options.Order = 3 + level2; + + if (options.Algo == 0xFFFFFFFF) + options.Algo = (level2 >= 7 ? 1 : 0); + } + + return Update( + EXTERNAL_CODECS_VARS + m_Items, updateItems, outStream, + m_Archive.IsOpen() ? &m_Archive : NULL, &options, callback); + COM_TRY_END2 +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) +{ + #ifndef _7ZIP_ST + const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); + _numThreads = numProcessors; + #endif + InitMethodProperties(); + for (int i = 0; i < numProperties; i++) + { + UString name = UString(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)); + m_Level = level; + continue; + } + else if (name == L"M") + { + if (prop.vt == VT_BSTR) + { + UString m = prop.bstrVal; + m.MakeUpper(); + if (m == L"COPY") m_MainMethod = NFileHeader::NCompressionMethod::kStored; + else if (m == L"DEFLATE") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated; + else if (m == L"DEFLATE64") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated64; + else if (m == L"BZIP2") m_MainMethod = NFileHeader::NCompressionMethod::kBZip2; + else if (m == L"LZMA") m_MainMethod = NFileHeader::NCompressionMethod::kLZMA; + else if (m == L"PPMD") m_MainMethod = NFileHeader::NCompressionMethod::kPPMd; + else return E_INVALIDARG; + } + else if (prop.vt == VT_UI4) + { + switch(prop.ulVal) + { + case NFileHeader::NCompressionMethod::kStored: + case NFileHeader::NCompressionMethod::kDeflated: + case NFileHeader::NCompressionMethod::kDeflated64: + case NFileHeader::NCompressionMethod::kBZip2: + case NFileHeader::NCompressionMethod::kLZMA: + m_MainMethod = (Byte)prop.ulVal; + break; + default: + return E_INVALIDARG; + } + } + else + return E_INVALIDARG; + } + else if (name.Left(2) == L"EM") + { + if (prop.vt == VT_BSTR) + { + UString valueString = prop.bstrVal; + valueString.MakeUpper(); + if (valueString.Left(3) == L"AES") + { + valueString = valueString.Mid(3); + if (valueString == L"128") + m_AesKeyMode = 1; + else if (valueString == L"192") + m_AesKeyMode = 2; + else if (valueString == L"256" || valueString.IsEmpty()) + m_AesKeyMode = 3; + else + return E_INVALIDARG; + m_IsAesMode = true; + m_ForceAesMode = true; + } + else if (valueString == L"ZIPCRYPTO") + { + m_IsAesMode = false; + m_ForceAesMode = true; + } + else + return E_INVALIDARG; + } + else + return E_INVALIDARG; + } + else if (name[0] == L'D') + { + UInt32 dicSize = kBZip2DicSizeX5; + RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize)); + m_DicSize = dicSize; + } + else if (name.Left(3) == L"MEM") + { + UInt32 memSize = 1 << 24; + RINOK(ParsePropDictionaryValue(name.Mid(3), prop, memSize)); + m_MemSize = memSize; + } + else if (name[0] == L'O') + { + UInt32 order = 8; + RINOK(ParsePropValue(name.Mid(1), prop, order)); + m_Order = order; + } + else if (name.Left(4) == L"PASS") + { + UInt32 num = kDeflateNumPassesX9; + RINOK(ParsePropValue(name.Mid(4), prop, num)); + m_NumPasses = num; + } + else if (name.Left(2) == L"FB") + { + UInt32 num = kDeflateNumFastBytesX9; + RINOK(ParsePropValue(name.Mid(2), prop, num)); + m_NumFastBytes = num; + } + else if (name.Left(2) == L"MC") + { + UInt32 num = 0xFFFFFFFF; + RINOK(ParsePropValue(name.Mid(2), prop, num)); + m_NumMatchFinderCycles = num; + m_NumMatchFinderCyclesDefined = true; + } + else if (name.Left(2) == L"MT") + { + #ifndef _7ZIP_ST + RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads)); + #endif + } + else if (name.Left(1) == L"A") + { + UInt32 num = kLzAlgoX5; + RINOK(ParsePropValue(name.Mid(1), prop, num)); + m_Algo = num; + } + else if (name.CompareNoCase(L"TC") == 0) + { + RINOK(SetBoolProperty(m_WriteNtfsTimeExtra, prop)); + } + else if (name.CompareNoCase(L"CL") == 0) + { + RINOK(SetBoolProperty(m_ForceLocal, prop)); + if (m_ForceLocal) + m_ForceUtf8 = false; + } + else if (name.CompareNoCase(L"CU") == 0) + { + RINOK(SetBoolProperty(m_ForceUtf8, prop)); + if (m_ForceUtf8) + m_ForceLocal = false; + } + else + return E_INVALIDARG; + } + return S_OK; +} + +}} diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHeader.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHeader.cpp new file mode 100644 index 000000000..582187b51 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHeader.cpp @@ -0,0 +1,36 @@ +// Archive/Zip/Header.h + +#include "StdAfx.h" + +#include "ZipHeader.h" + +namespace NArchive { +namespace NZip { + +namespace NSignature +{ + UInt32 kLocalFileHeader = 0x04034B50 + 1; + UInt32 kDataDescriptor = 0x08074B50 + 1; + UInt32 kCentralFileHeader = 0x02014B50 + 1; + UInt32 kEndOfCentralDir = 0x06054B50 + 1; + UInt32 kZip64EndOfCentralDir = 0x06064B50 + 1; + UInt32 kZip64EndOfCentralDirLocator = 0x07064B50 + 1; + + class CMarkersInitializer + { + public: + CMarkersInitializer() + { + kLocalFileHeader--; + kDataDescriptor--; + kCentralFileHeader--; + kEndOfCentralDir--; + kZip64EndOfCentralDir--; + kZip64EndOfCentralDirLocator--; + } + }; + static CMarkersInitializer g_MarkerInitializer; +} + +}} + diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHeader.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHeader.h new file mode 100644 index 000000000..ce8c1e4f7 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipHeader.h @@ -0,0 +1,284 @@ +// Archive/Zip/Header.h + +#ifndef __ARCHIVE_ZIP_HEADER_H +#define __ARCHIVE_ZIP_HEADER_H + +#include "Common/Types.h" + +namespace NArchive { +namespace NZip { + +namespace NSignature +{ + extern UInt32 kLocalFileHeader; + extern UInt32 kDataDescriptor; + extern UInt32 kCentralFileHeader; + extern UInt32 kEndOfCentralDir; + extern UInt32 kZip64EndOfCentralDir; + extern UInt32 kZip64EndOfCentralDirLocator; + + static const UInt32 kMarkerSize = 4; +} + +const UInt32 kEcdSize = 22; +const UInt32 kZip64EcdSize = 44; +const UInt32 kZip64EcdLocatorSize = 20; +/* +struct CEndOfCentralDirectoryRecord +{ + UInt16 ThisDiskNumber; + UInt16 StartCentralDirectoryDiskNumber; + UInt16 NumEntriesInCentaralDirectoryOnThisDisk; + UInt16 NumEntriesInCentaralDirectory; + UInt32 CentralDirectorySize; + UInt32 CentralDirectoryStartOffset; + UInt16 CommentSize; +}; + +struct CEndOfCentralDirectoryRecordFull +{ + UInt32 Signature; + CEndOfCentralDirectoryRecord Header; +}; +*/ + +namespace NFileHeader +{ + /* + struct CVersion + { + Byte Version; + Byte HostOS; + }; + */ + + namespace NCompressionMethod + { + enum EType + { + kStored = 0, + kShrunk = 1, + kReduced1 = 2, + kReduced2 = 3, + kReduced3 = 4, + kReduced4 = 5, + kImploded = 6, + kReservedTokenizing = 7, // reserved for tokenizing + kDeflated = 8, + kDeflated64 = 9, + kPKImploding = 10, + + kBZip2 = 12, + kLZMA = 14, + kTerse = 18, + kLz77 = 19, + kJpeg = 0x60, + kWavPack = 0x61, + kPPMd = 0x62, + kWzAES = 0x63 + }; + const int kNumCompressionMethods = 11; + const Byte kMadeByProgramVersion = 63; + + const Byte kExtractVersion_Default = 10; + const Byte kExtractVersion_Dir = 20; + const Byte kExtractVersion_ZipCrypto = 20; + const Byte kExtractVersion_Deflate = 20; + const Byte kExtractVersion_Deflate64 = 21; + const Byte kExtractVersion_Zip64 = 45; + const Byte kExtractVersion_BZip2 = 46; + const Byte kExtractVersion_Aes = 51; + const Byte kExtractVersion_LZMA = 63; + const Byte kExtractVersion_PPMd = 63; + + // const Byte kSupportedVersion = 20; + } + + namespace NExtraID + { + enum + { + kZip64 = 0x01, + kNTFS = 0x0A, + kStrongEncrypt = 0x17, + kUnixTime = 0x5455, + kWzAES = 0x9901 + }; + } + + namespace NNtfsExtra + { + const UInt16 kTagTime = 1; + enum + { + kMTime = 0, + kATime, + kCTime + }; + } + + namespace NUnixTime + { + enum + { + kMTime = 0, + kATime, + kCTime + }; + } + + const UInt32 kLocalBlockSize = 26; + /* + struct CLocalBlock + { + CVersion ExtractVersion; + + UInt16 Flags; + UInt16 CompressionMethod; + UInt32 Time; + UInt32 FileCRC; + UInt32 PackSize; + UInt32 UnPackSize; + UInt16 NameSize; + UInt16 ExtraSize; + }; + */ + + const UInt32 kDataDescriptorSize = 16; + // const UInt32 kDataDescriptor64Size = 16 + 8; + /* + struct CDataDescriptor + { + UInt32 Signature; + UInt32 FileCRC; + UInt32 PackSize; + UInt32 UnPackSize; + }; + + struct CLocalBlockFull + { + UInt32 Signature; + CLocalBlock Header; + }; + */ + + const UInt32 kCentralBlockSize = 42; + /* + struct CBlock + { + CVersion MadeByVersion; + CVersion ExtractVersion; + UInt16 Flags; + UInt16 CompressionMethod; + UInt32 Time; + UInt32 FileCRC; + UInt32 PackSize; + UInt32 UnPackSize; + UInt16 NameSize; + UInt16 ExtraSize; + UInt16 CommentSize; + UInt16 DiskNumberStart; + UInt16 InternalAttributes; + UInt32 ExternalAttributes; + UInt32 LocalHeaderOffset; + }; + + struct CBlockFull + { + UInt32 Signature; + CBlock Header; + }; + */ + + namespace NFlags + { + const int kEncrypted = 1 << 0; + const int kLzmaEOS = 1 << 1; + const int kDescriptorUsedMask = 1 << 3; + const int kStrongEncrypted = 1 << 6; + const int kUtf8 = 1 << 11; + + const int kImplodeDictionarySizeMask = 1 << 1; + const int kImplodeLiteralsOnMask = 1 << 2; + + const int kDeflateTypeBitStart = 1; + const int kNumDeflateTypeBits = 2; + const int kNumDeflateTypes = (1 << kNumDeflateTypeBits); + const int kDeflateTypeMask = (1 << kNumDeflateTypeBits) - 1; + } + + namespace NHostOS + { + enum EEnum + { + kFAT = 0, + kAMIGA = 1, + kVMS = 2, // VAX/VMS + kUnix = 3, + kVM_CMS = 4, + kAtari = 5, // what if it's a minix filesystem? [cjh] + kHPFS = 6, // filesystem used by OS/2 (and NT 3.x) + kMac = 7, + kZ_System = 8, + kCPM = 9, + kTOPS20 = 10, // pkzip 2.50 NTFS + kNTFS = 11, // filesystem used by Windows NT + kQDOS = 12, // SMS/QDOS + kAcorn = 13, // Archimedes Acorn RISC OS + kVFAT = 14, // filesystem used by Windows 95, NT + kMVS = 15, + kBeOS = 16, // hybrid POSIX/database filesystem + kTandem = 17, + kOS400 = 18, + kOSX = 19 + }; + } + namespace NUnixAttribute + { + const UInt32 kIFMT = 0170000; /* Unix file type mask */ + + const UInt32 kIFDIR = 0040000; /* Unix directory */ + const UInt32 kIFREG = 0100000; /* Unix regular file */ + const UInt32 kIFSOCK = 0140000; /* Unix socket (BSD, not SysV or Amiga) */ + const UInt32 kIFLNK = 0120000; /* Unix symbolic link (not SysV, Amiga) */ + const UInt32 kIFBLK = 0060000; /* Unix block special (not Amiga) */ + const UInt32 kIFCHR = 0020000; /* Unix character special (not Amiga) */ + const UInt32 kIFIFO = 0010000; /* Unix fifo (BCC, not MSC or Amiga) */ + + const UInt32 kISUID = 04000; /* Unix set user id on execution */ + const UInt32 kISGID = 02000; /* Unix set group id on execution */ + const UInt32 kISVTX = 01000; /* Unix directory permissions control */ + const UInt32 kENFMT = kISGID; /* Unix record locking enforcement flag */ + const UInt32 kIRWXU = 00700; /* Unix read, write, execute: owner */ + const UInt32 kIRUSR = 00400; /* Unix read permission: owner */ + const UInt32 kIWUSR = 00200; /* Unix write permission: owner */ + const UInt32 kIXUSR = 00100; /* Unix execute permission: owner */ + const UInt32 kIRWXG = 00070; /* Unix read, write, execute: group */ + const UInt32 kIRGRP = 00040; /* Unix read permission: group */ + const UInt32 kIWGRP = 00020; /* Unix write permission: group */ + const UInt32 kIXGRP = 00010; /* Unix execute permission: group */ + const UInt32 kIRWXO = 00007; /* Unix read, write, execute: other */ + const UInt32 kIROTH = 00004; /* Unix read permission: other */ + const UInt32 kIWOTH = 00002; /* Unix write permission: other */ + const UInt32 kIXOTH = 00001; /* Unix execute permission: other */ + } + + namespace NAmigaAttribute + { + const UInt32 kIFMT = 06000; /* Amiga file type mask */ + const UInt32 kIFDIR = 04000; /* Amiga directory */ + const UInt32 kIFREG = 02000; /* Amiga regular file */ + const UInt32 kIHIDDEN = 00200; /* to be supported in AmigaDOS 3.x */ + const UInt32 kISCRIPT = 00100; /* executable script (text command file) */ + const UInt32 kIPURE = 00040; /* allow loading into resident memory */ + const UInt32 kIARCHIVE = 00020; /* not modified since bit was last set */ + const UInt32 kIREAD = 00010; /* can be opened for reading */ + const UInt32 kIWRITE = 00004; /* can be opened for writing */ + const UInt32 kIEXECUTE = 00002; /* executable image, a loadable runfile */ + const UInt32 kIDELETE = 00001; /* can be deleted */ + } +} + +}} + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipIn.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipIn.cpp new file mode 100644 index 000000000..b36b61be7 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipIn.cpp @@ -0,0 +1,893 @@ +// Archive/ZipIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/DynamicBuffer.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/StreamUtils.h" + +#include "ZipIn.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +namespace NArchive { +namespace NZip { + +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + _inBufMode = false; + Close(); + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_StreamStartPosition)); + m_Position = m_StreamStartPosition; + RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit)); + RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); + m_Stream = stream; + return S_OK; +} + +void CInArchive::Close() +{ + _inBuffer.ReleaseStream(); + m_Stream.Release(); +} + +HRESULT CInArchive::Seek(UInt64 offset) +{ + return m_Stream->Seek(offset, STREAM_SEEK_SET, NULL); +} + +////////////////////////////////////// +// Markers + +static inline bool TestMarkerCandidate(const Byte *p, UInt32 &value) +{ + value = Get32(p); + return + (value == NSignature::kLocalFileHeader) || + (value == NSignature::kEndOfCentralDir); +} + +static const UInt32 kNumMarkerAddtionalBytes = 2; +static inline bool TestMarkerCandidate2(const Byte *p, UInt32 &value) +{ + value = Get32(p); + if (value == NSignature::kEndOfCentralDir) + return (Get16(p + 4) == 0); + return (value == NSignature::kLocalFileHeader && p[4] < 128); +} + +HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + ArcInfo.Clear(); + m_Position = m_StreamStartPosition; + + Byte marker[NSignature::kMarkerSize]; + RINOK(ReadStream_FALSE(stream, marker, NSignature::kMarkerSize)); + m_Position += NSignature::kMarkerSize; + if (TestMarkerCandidate(marker, m_Signature)) + return S_OK; + + CByteDynamicBuffer dynamicBuffer; + const UInt32 kSearchMarkerBufferSize = 0x10000; + dynamicBuffer.EnsureCapacity(kSearchMarkerBufferSize); + Byte *buffer = dynamicBuffer; + UInt32 numBytesPrev = NSignature::kMarkerSize - 1; + memcpy(buffer, marker + 1, numBytesPrev); + UInt64 curTestPos = m_StreamStartPosition + 1; + for (;;) + { + if (searchHeaderSizeLimit != NULL) + if (curTestPos - m_StreamStartPosition > *searchHeaderSizeLimit) + break; + size_t numReadBytes = kSearchMarkerBufferSize - numBytesPrev; + RINOK(ReadStream(stream, buffer + numBytesPrev, &numReadBytes)); + m_Position += numReadBytes; + UInt32 numBytesInBuffer = numBytesPrev + (UInt32)numReadBytes; + const UInt32 kMarker2Size = NSignature::kMarkerSize + kNumMarkerAddtionalBytes; + if (numBytesInBuffer < kMarker2Size) + break; + UInt32 numTests = numBytesInBuffer - kMarker2Size + 1; + for (UInt32 pos = 0; pos < numTests; pos++) + { + if (buffer[pos] != 0x50) + continue; + if (TestMarkerCandidate2(buffer + pos, m_Signature)) + { + curTestPos += pos; + ArcInfo.StartPosition = curTestPos; + m_Position = curTestPos + NSignature::kMarkerSize; + return S_OK; + } + } + curTestPos += numTests; + numBytesPrev = numBytesInBuffer - numTests; + memmove(buffer, buffer + numTests, numBytesPrev); + } + return S_FALSE; +} + +HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) +{ + size_t realProcessedSize = size; + HRESULT result = S_OK; + if (_inBufMode) + { + try { realProcessedSize = _inBuffer.ReadBytes((Byte *)data, size); } + catch (const CInBufferException &e) { return e.ErrorCode; } + } + else + result = ReadStream(m_Stream, data, &realProcessedSize); + if (processedSize != NULL) + *processedSize = (UInt32)realProcessedSize; + m_Position += realProcessedSize; + return result; +} + +void CInArchive::Skip(UInt64 num) +{ + for (UInt64 i = 0; i < num; i++) + ReadByte(); +} + +void CInArchive::IncreaseRealPosition(UInt64 addValue) +{ + if (m_Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position) != S_OK) + throw CInArchiveException(CInArchiveException::kSeekStreamError); +} + +bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size) +{ + UInt32 realProcessedSize; + if (ReadBytes(data, size, &realProcessedSize) != S_OK) + throw CInArchiveException(CInArchiveException::kReadStreamError); + return (realProcessedSize == size); +} + +void CInArchive::SafeReadBytes(void *data, UInt32 size) +{ + if (!ReadBytesAndTestSize(data, size)) + throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); +} + +void CInArchive::ReadBuffer(CByteBuffer &buffer, UInt32 size) +{ + buffer.SetCapacity(size); + if (size > 0) + SafeReadBytes(buffer, size); +} + +Byte CInArchive::ReadByte() +{ + Byte b; + SafeReadBytes(&b, 1); + return b; +} + +UInt16 CInArchive::ReadUInt16() +{ + Byte buf[2]; + SafeReadBytes(buf, 2); + return Get16(buf); +} + +UInt32 CInArchive::ReadUInt32() +{ + Byte buf[4]; + SafeReadBytes(buf, 4); + return Get32(buf); +} + +UInt64 CInArchive::ReadUInt64() +{ + Byte buf[8]; + SafeReadBytes(buf, 8); + return Get64(buf); +} + +bool CInArchive::ReadUInt32(UInt32 &value) +{ + Byte buf[4]; + if (!ReadBytesAndTestSize(buf, 4)) + return false; + value = Get32(buf); + return true; +} + +void CInArchive::ReadFileName(UInt32 nameSize, AString &dest) +{ + if (nameSize == 0) + dest.Empty(); + char *p = dest.GetBuffer((int)nameSize); + SafeReadBytes(p, nameSize); + p[nameSize] = 0; + dest.ReleaseBuffer(); +} + +void CInArchive::ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock, + UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber) +{ + extraBlock.Clear(); + UInt32 remain = extraSize; + while(remain >= 4) + { + CExtraSubBlock subBlock; + subBlock.ID = ReadUInt16(); + UInt32 dataSize = ReadUInt16(); + remain -= 4; + if (dataSize > remain) // it's bug + dataSize = remain; + if (subBlock.ID == NFileHeader::NExtraID::kZip64) + { + if (unpackSize == 0xFFFFFFFF) + { + if (dataSize < 8) + break; + unpackSize = ReadUInt64(); + remain -= 8; + dataSize -= 8; + } + if (packSize == 0xFFFFFFFF) + { + if (dataSize < 8) + break; + packSize = ReadUInt64(); + remain -= 8; + dataSize -= 8; + } + if (localHeaderOffset == 0xFFFFFFFF) + { + if (dataSize < 8) + break; + localHeaderOffset = ReadUInt64(); + remain -= 8; + dataSize -= 8; + } + if (diskStartNumber == 0xFFFF) + { + if (dataSize < 4) + break; + diskStartNumber = ReadUInt32(); + remain -= 4; + dataSize -= 4; + } + for (UInt32 i = 0; i < dataSize; i++) + ReadByte(); + } + else + { + ReadBuffer(subBlock.Data, dataSize); + extraBlock.SubBlocks.Add(subBlock); + } + remain -= dataSize; + } + Skip(remain); +} + +HRESULT CInArchive::ReadLocalItem(CItemEx &item) +{ + const int kBufSize = 26; + Byte p[kBufSize]; + SafeReadBytes(p, kBufSize); + + item.ExtractVersion.Version = p[0]; + item.ExtractVersion.HostOS = p[1]; + item.Flags = Get16(p + 2); + item.CompressionMethod = Get16(p + 4); + item.Time = Get32(p + 6); + item.FileCRC = Get32(p + 10); + item.PackSize = Get32(p + 14); + item.UnPackSize = Get32(p + 18); + UInt32 fileNameSize = Get16(p + 22); + item.LocalExtraSize = Get16(p + 24); + ReadFileName(fileNameSize, item.Name); + item.FileHeaderWithNameSize = 4 + NFileHeader::kLocalBlockSize + fileNameSize; + if (item.LocalExtraSize > 0) + { + UInt64 localHeaderOffset = 0; + UInt32 diskStartNumber = 0; + ReadExtra(item.LocalExtraSize, item.LocalExtra, item.UnPackSize, item.PackSize, + localHeaderOffset, diskStartNumber); + } + /* + if (item.IsDir()) + item.UnPackSize = 0; // check It + */ + return S_OK; +} + +static bool FlagsAreSame(CItem &i1, CItem &i2) +{ + if (i1.CompressionMethod != i2.CompressionMethod) + return false; + // i1.Time + + if (i1.Flags == i2.Flags) + return true; + UInt32 mask = 0xFFFF; + switch(i1.CompressionMethod) + { + case NFileHeader::NCompressionMethod::kDeflated: + mask = 0x7FF9; + break; + default: + if (i1.CompressionMethod <= NFileHeader::NCompressionMethod::kImploded) + mask = 0x7FFF; + } + return ((i1.Flags & mask) == (i2.Flags & mask)); +} + +HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item) +{ + if (item.FromLocal) + return S_OK; + try + { + RINOK(Seek(ArcInfo.Base + item.LocalHeaderPosition)); + CItemEx localItem; + if (ReadUInt32() != NSignature::kLocalFileHeader) + return S_FALSE; + RINOK(ReadLocalItem(localItem)); + if (!FlagsAreSame(item, localItem)) + return S_FALSE; + + if ((!localItem.HasDescriptor() && + ( + item.FileCRC != localItem.FileCRC || + item.PackSize != localItem.PackSize || + item.UnPackSize != localItem.UnPackSize + ) + ) || + item.Name.Length() != localItem.Name.Length() + ) + return S_FALSE; + item.FileHeaderWithNameSize = localItem.FileHeaderWithNameSize; + item.LocalExtraSize = localItem.LocalExtraSize; + item.LocalExtra = localItem.LocalExtra; + item.FromLocal = true; + } + catch(...) { return S_FALSE; } + return S_OK; +} + +HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) +{ + if (item.HasDescriptor()) + { + const int kBufferSize = (1 << 12); + Byte buffer[kBufferSize]; + + UInt32 numBytesInBuffer = 0; + UInt32 packedSize = 0; + + bool descriptorWasFound = false; + for (;;) + { + UInt32 processedSize; + RINOK(ReadBytes(buffer + numBytesInBuffer, kBufferSize - numBytesInBuffer, &processedSize)); + numBytesInBuffer += processedSize; + if (numBytesInBuffer < NFileHeader::kDataDescriptorSize) + return S_FALSE; + UInt32 i; + for (i = 0; i <= numBytesInBuffer - NFileHeader::kDataDescriptorSize; i++) + { + // descriptorSignature field is Info-ZIP's extension + // to Zip specification. + UInt32 descriptorSignature = Get32(buffer + i); + + // !!!! It must be fixed for Zip64 archives + UInt32 descriptorPackSize = Get32(buffer + i + 8); + if (descriptorSignature== NSignature::kDataDescriptor && descriptorPackSize == packedSize + i) + { + descriptorWasFound = true; + item.FileCRC = Get32(buffer + i + 4); + item.PackSize = descriptorPackSize; + item.UnPackSize = Get32(buffer + i + 12); + IncreaseRealPosition(Int64(Int32(0 - (numBytesInBuffer - i - NFileHeader::kDataDescriptorSize)))); + break; + } + } + if (descriptorWasFound) + break; + packedSize += i; + int j; + for (j = 0; i < numBytesInBuffer; i++, j++) + buffer[j] = buffer[i]; + numBytesInBuffer = j; + } + } + else + IncreaseRealPosition(item.PackSize); + return S_OK; +} + +HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) +{ + if (item.FromLocal) + return S_OK; + try + { + RINOK(ReadLocalItemAfterCdItem(item)); + if (item.HasDescriptor()) + { + RINOK(Seek(ArcInfo.Base + item.GetDataPosition() + item.PackSize)); + if (ReadUInt32() != NSignature::kDataDescriptor) + return S_FALSE; + UInt32 crc = ReadUInt32(); + UInt64 packSize, unpackSize; + + /* + if (IsZip64) + { + packSize = ReadUInt64(); + unpackSize = ReadUInt64(); + } + else + */ + { + packSize = ReadUInt32(); + unpackSize = ReadUInt32(); + } + + if (crc != item.FileCRC || item.PackSize != packSize || item.UnPackSize != unpackSize) + return S_FALSE; + } + } + catch(...) { return S_FALSE; } + return S_OK; +} + +HRESULT CInArchive::ReadCdItem(CItemEx &item) +{ + item.FromCentral = true; + const int kBufSize = 42; + Byte p[kBufSize]; + SafeReadBytes(p, kBufSize); + item.MadeByVersion.Version = p[0]; + item.MadeByVersion.HostOS = p[1]; + item.ExtractVersion.Version = p[2]; + item.ExtractVersion.HostOS = p[3]; + item.Flags = Get16(p + 4); + item.CompressionMethod = Get16(p + 6); + item.Time = Get32(p + 8); + item.FileCRC = Get32(p + 12); + item.PackSize = Get32(p + 16); + item.UnPackSize = Get32(p + 20); + UInt16 headerNameSize = Get16(p + 24); + UInt16 headerExtraSize = Get16(p + 26); + UInt16 headerCommentSize = Get16(p + 28); + UInt32 headerDiskNumberStart = Get16(p + 30); + item.InternalAttributes = Get16(p + 32); + item.ExternalAttributes = Get32(p + 34); + item.LocalHeaderPosition = Get32(p + 38); + ReadFileName(headerNameSize, item.Name); + + if (headerExtraSize > 0) + { + ReadExtra(headerExtraSize, item.CentralExtra, item.UnPackSize, item.PackSize, + item.LocalHeaderPosition, headerDiskNumberStart); + } + + if (headerDiskNumberStart != 0) + throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported); + + // May be these strings must be deleted + /* + if (item.IsDir()) + item.UnPackSize = 0; + */ + + ReadBuffer(item.Comment, headerCommentSize); + return S_OK; +} + +HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) +{ + RINOK(Seek(offset)); + const UInt32 kEcd64Size = 56; + Byte buf[kEcd64Size]; + if (!ReadBytesAndTestSize(buf, kEcd64Size)) + return S_FALSE; + if (Get32(buf) != NSignature::kZip64EndOfCentralDir) + return S_FALSE; + // cdInfo.NumEntries = Get64(buf + 24); + cdInfo.Size = Get64(buf + 40); + cdInfo.Offset = Get64(buf + 48); + return S_OK; +} + +HRESULT CInArchive::FindCd(CCdInfo &cdInfo) +{ + UInt64 endPosition; + RINOK(m_Stream->Seek(0, STREAM_SEEK_END, &endPosition)); + const UInt32 kBufSizeMax = (1 << 16) + kEcdSize + kZip64EcdLocatorSize; + CByteBuffer byteBuffer; + byteBuffer.SetCapacity(kBufSizeMax); + Byte *buf = byteBuffer; + UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax; + if (bufSize < kEcdSize) + return S_FALSE; + UInt64 startPosition = endPosition - bufSize; + RINOK(m_Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position)); + if (m_Position != startPosition) + return S_FALSE; + if (!ReadBytesAndTestSize(buf, bufSize)) + return S_FALSE; + for (int i = (int)(bufSize - kEcdSize); i >= 0; i--) + { + if (Get32(buf + i) == NSignature::kEndOfCentralDir) + { + if (i >= kZip64EcdLocatorSize) + { + const Byte *locator = buf + i - kZip64EcdLocatorSize; + if (Get32(locator) == NSignature::kZip64EndOfCentralDirLocator) + { + UInt64 ecd64Offset = Get64(locator + 8); + if (TryEcd64(ecd64Offset, cdInfo) == S_OK) + return S_OK; + if (TryEcd64(ArcInfo.StartPosition + ecd64Offset, cdInfo) == S_OK) + { + ArcInfo.Base = ArcInfo.StartPosition; + return S_OK; + } + } + } + if (Get32(buf + i + 4) == 0) + { + // cdInfo.NumEntries = GetUInt16(buf + i + 10); + cdInfo.Size = Get32(buf + i + 12); + cdInfo.Offset = Get32(buf + i + 16); + UInt64 curPos = endPosition - bufSize + i; + UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; + if (curPos != cdEnd) + { + /* + if (cdInfo.Offset <= 16 && cdInfo.Size != 0) + { + // here we support some rare ZIP files with Central directory at the start + ArcInfo.Base = 0; + } + else + */ + ArcInfo.Base = curPos - cdEnd; + } + return S_OK; + } + } + } + return S_FALSE; +} + +HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress) +{ + items.Clear(); + RINOK(m_Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); + if (m_Position != cdOffset) + return S_FALSE; + + if (!_inBuffer.Create(1 << 15)) + return E_OUTOFMEMORY; + _inBuffer.SetStream(m_Stream); + _inBuffer.Init(); + _inBufMode = true; + + while(m_Position - cdOffset < cdSize) + { + if (ReadUInt32() != NSignature::kCentralFileHeader) + return S_FALSE; + CItemEx cdItem; + RINOK(ReadCdItem(cdItem)); + items.Add(cdItem); + if (progress && items.Size() % 1000 == 0) + RINOK(progress->SetCompleted(items.Size())); + } + return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE; +} + +HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress) +{ + ArcInfo.Base = 0; + CCdInfo cdInfo; + RINOK(FindCd(cdInfo)); + HRESULT res = S_FALSE; + cdSize = cdInfo.Size; + cdOffset = cdInfo.Offset; + res = TryReadCd(items, ArcInfo.Base + cdOffset, cdSize, progress); + if (res == S_FALSE && ArcInfo.Base == 0) + { + res = TryReadCd(items, cdInfo.Offset + ArcInfo.StartPosition, cdSize, progress); + if (res == S_OK) + ArcInfo.Base = ArcInfo.StartPosition; + } + if (!ReadUInt32(m_Signature)) + return S_FALSE; + return res; +} + +HRESULT CInArchive::ReadLocalsAndCd(CObjectVector<CItemEx> &items, CProgressVirt *progress, UInt64 &cdOffset, int &numCdItems) +{ + items.Clear(); + numCdItems = 0; + while (m_Signature == NSignature::kLocalFileHeader) + { + // FSeek points to next byte after signature + // NFileHeader::CLocalBlock localHeader; + CItemEx item; + item.LocalHeaderPosition = m_Position - m_StreamStartPosition - 4; // points to signature; + RINOK(ReadLocalItem(item)); + item.FromLocal = true; + ReadLocalItemDescriptor(item); + items.Add(item); + if (progress && items.Size() % 100 == 0) + RINOK(progress->SetCompleted(items.Size())); + if (!ReadUInt32(m_Signature)) + break; + } + cdOffset = m_Position - 4; + int i; + for (i = 0; i < items.Size(); i++, numCdItems++) + { + if (progress && i % 1000 == 0) + RINOK(progress->SetCompleted(items.Size())); + if (m_Signature == NSignature::kEndOfCentralDir) + break; + + if (m_Signature != NSignature::kCentralFileHeader) + return S_FALSE; + + CItemEx cdItem; + RINOK(ReadCdItem(cdItem)); + + if (i == 0) + { + int j; + for (j = 0; j < items.Size(); j++) + { + CItemEx &item = items[j]; + if (item.Name == cdItem.Name) + { + ArcInfo.Base = item.LocalHeaderPosition - cdItem.LocalHeaderPosition; + break; + } + } + if (j == items.Size()) + return S_FALSE; + } + + int index; + int left = 0, right = items.Size(); + for (;;) + { + if (left >= right) + return S_FALSE; + index = (left + right) / 2; + UInt64 position = items[index].LocalHeaderPosition - ArcInfo.Base; + if (cdItem.LocalHeaderPosition == position) + break; + if (cdItem.LocalHeaderPosition < position) + right = index; + else + left = index + 1; + } + CItemEx &item = items[index]; + // item.LocalHeaderPosition = cdItem.LocalHeaderPosition; + item.MadeByVersion = cdItem.MadeByVersion; + item.CentralExtra = cdItem.CentralExtra; + + if ( + // item.ExtractVersion != cdItem.ExtractVersion || + !FlagsAreSame(item, cdItem) || + item.FileCRC != cdItem.FileCRC) + return S_FALSE; + + if (item.Name.Length() != cdItem.Name.Length() || + item.PackSize != cdItem.PackSize || + item.UnPackSize != cdItem.UnPackSize + ) + return S_FALSE; + item.Name = cdItem.Name; + item.InternalAttributes = cdItem.InternalAttributes; + item.ExternalAttributes = cdItem.ExternalAttributes; + item.Comment = cdItem.Comment; + item.FromCentral = cdItem.FromCentral; + if (!ReadUInt32(m_Signature)) + return S_FALSE; + } + for (i = 0; i < items.Size(); i++) + items[i].LocalHeaderPosition -= ArcInfo.Base; + return S_OK; +} + +struct CEcd +{ + UInt16 thisDiskNumber; + UInt16 startCDDiskNumber; + UInt16 numEntriesInCDOnThisDisk; + UInt16 numEntriesInCD; + UInt32 cdSize; + UInt32 cdStartOffset; + UInt16 commentSize; + void Parse(const Byte *p); +}; + +void CEcd::Parse(const Byte *p) +{ + thisDiskNumber = Get16(p); + startCDDiskNumber = Get16(p + 2); + numEntriesInCDOnThisDisk = Get16(p + 4); + numEntriesInCD = Get16(p + 6); + cdSize = Get32(p + 8); + cdStartOffset = Get32(p + 12); + commentSize = Get16(p + 16); +} + +struct CEcd64 +{ + UInt16 versionMade; + UInt16 versionNeedExtract; + UInt32 thisDiskNumber; + UInt32 startCDDiskNumber; + UInt64 numEntriesInCDOnThisDisk; + UInt64 numEntriesInCD; + UInt64 cdSize; + UInt64 cdStartOffset; + void Parse(const Byte *p); + CEcd64() { memset(this, 0, sizeof(*this)); } +}; + +void CEcd64::Parse(const Byte *p) +{ + versionMade = Get16(p); + versionNeedExtract = Get16(p + 2); + thisDiskNumber = Get32(p + 4); + startCDDiskNumber = Get32(p + 8); + numEntriesInCDOnThisDisk = Get64(p + 12); + numEntriesInCD = Get64(p + 20); + cdSize = Get64(p + 28); + cdStartOffset = Get64(p + 36); +} + +#define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF) ecd64. n = ecd. n; +#define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd. n; + +HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress) +{ + // m_Signature must be kLocalFileHeaderSignature or + // kEndOfCentralDirSignature + // m_Position points to next byte after signature + + IsZip64 = false; + items.Clear(); + + UInt64 cdSize, cdStartOffset; + HRESULT res; + try + { + res = ReadCd(items, cdStartOffset, cdSize, progress); + } + catch(CInArchiveException &) + { + res = S_FALSE; + } + if (res != S_FALSE && res != S_OK) + return res; + + /* + if (res != S_OK) + return res; + res = S_FALSE; + */ + + int numCdItems = items.Size(); + if (res == S_FALSE) + { + _inBufMode = false; + ArcInfo.Base = 0; + RINOK(m_Stream->Seek(ArcInfo.StartPosition, STREAM_SEEK_SET, &m_Position)); + if (m_Position != ArcInfo.StartPosition) + return S_FALSE; + if (!ReadUInt32(m_Signature)) + return S_FALSE; + RINOK(ReadLocalsAndCd(items, progress, cdStartOffset, numCdItems)); + cdSize = (m_Position - 4) - cdStartOffset; + cdStartOffset -= ArcInfo.Base; + } + + CEcd64 ecd64; + bool isZip64 = false; + UInt64 zip64EcdStartOffset = m_Position - 4 - ArcInfo.Base; + if (m_Signature == NSignature::kZip64EndOfCentralDir) + { + IsZip64 = isZip64 = true; + UInt64 recordSize = ReadUInt64(); + + const int kBufSize = kZip64EcdSize; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + ecd64.Parse(buf); + + Skip(recordSize - kZip64EcdSize); + if (!ReadUInt32(m_Signature)) + return S_FALSE; + if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) + throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported); + if (ecd64.numEntriesInCDOnThisDisk != numCdItems || + ecd64.numEntriesInCD != numCdItems || + ecd64.cdSize != cdSize || + (ecd64.cdStartOffset != cdStartOffset && + (!items.IsEmpty()))) + return S_FALSE; + } + if (m_Signature == NSignature::kZip64EndOfCentralDirLocator) + { + /* UInt32 startEndCDDiskNumber = */ ReadUInt32(); + UInt64 endCDStartOffset = ReadUInt64(); + /* UInt32 numberOfDisks = */ ReadUInt32(); + if (zip64EcdStartOffset != endCDStartOffset) + return S_FALSE; + if (!ReadUInt32(m_Signature)) + return S_FALSE; + } + if (m_Signature != NSignature::kEndOfCentralDir) + return S_FALSE; + + const int kBufSize = kEcdSize - 4; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + CEcd ecd; + ecd.Parse(buf); + + COPY_ECD_ITEM_16(thisDiskNumber); + COPY_ECD_ITEM_16(startCDDiskNumber); + COPY_ECD_ITEM_16(numEntriesInCDOnThisDisk); + COPY_ECD_ITEM_16(numEntriesInCD); + COPY_ECD_ITEM_32(cdSize); + COPY_ECD_ITEM_32(cdStartOffset); + + ReadBuffer(ArcInfo.Comment, ecd.commentSize); + + if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) + throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported); + if ((UInt16)ecd64.numEntriesInCDOnThisDisk != ((UInt16)numCdItems) || + (UInt16)ecd64.numEntriesInCD != ((UInt16)numCdItems) || + (UInt32)ecd64.cdSize != (UInt32)cdSize || + ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdStartOffset && + (!items.IsEmpty()))) + return S_FALSE; + + _inBufMode = false; + _inBuffer.Free(); + IsOkHeaders = (numCdItems == items.Size()); + ArcInfo.FinishPosition = m_Position; + return S_OK; +} + +ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size) +{ + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> stream(streamSpec); + SeekInArchive(ArcInfo.Base + position); + streamSpec->SetStream(m_Stream); + streamSpec->Init(size); + return stream.Detach(); +} + +IInStream* CInArchive::CreateStream() +{ + CMyComPtr<IInStream> stream = m_Stream; + return stream.Detach(); +} + +bool CInArchive::SeekInArchive(UInt64 position) +{ + UInt64 newPosition; + if (m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition) != S_OK) + return false; + return (newPosition == position); +} + +}} diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipIn.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipIn.h new file mode 100644 index 000000000..0565339a0 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipIn.h @@ -0,0 +1,125 @@ +// Archive/ZipIn.h + +#ifndef __ZIP_IN_H +#define __ZIP_IN_H + +#include "Common/MyCom.h" + +#include "../../IStream.h" + +#include "../../Common/InBuffer.h" + +#include "ZipHeader.h" +#include "ZipItemEx.h" + +namespace NArchive { +namespace NZip { + +class CInArchiveException +{ +public: + enum ECauseType + { + kUnexpectedEndOfArchive = 0, + kArchiceHeaderCRCError, + kFileHeaderCRCError, + kIncorrectArchive, + kDataDescroptorsAreNotSupported, + kMultiVolumeArchiveAreNotSupported, + kReadStreamError, + kSeekStreamError + } + Cause; + CInArchiveException(ECauseType cause): Cause(cause) {} +}; + +class CInArchiveInfo +{ +public: + UInt64 Base; + UInt64 StartPosition; + UInt64 FinishPosition; + CByteBuffer Comment; + + CInArchiveInfo(): Base(0), StartPosition(0) {} + UInt64 GetPhySize() const { return FinishPosition - StartPosition; } + void Clear() + { + Base = 0; + StartPosition = 0; + Comment.SetCapacity(0); + } +}; + +class CProgressVirt +{ +public: + STDMETHOD(SetTotal)(UInt64 numFiles) PURE; + STDMETHOD(SetCompleted)(UInt64 numFiles) PURE; +}; + +struct CCdInfo +{ + // UInt64 NumEntries; + UInt64 Size; + UInt64 Offset; +}; + +class CInArchive +{ + CMyComPtr<IInStream> m_Stream; + UInt32 m_Signature; + UInt64 m_StreamStartPosition; + UInt64 m_Position; + + bool _inBufMode; + CInBuffer _inBuffer; + + HRESULT Seek(UInt64 offset); + + HRESULT FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit); + void ReadFileName(UInt32 nameSize, AString &dest); + + HRESULT ReadBytes(void *data, UInt32 size, UInt32 *processedSize); + bool ReadBytesAndTestSize(void *data, UInt32 size); + void SafeReadBytes(void *data, UInt32 size); + void ReadBuffer(CByteBuffer &buffer, UInt32 size); + Byte ReadByte(); + UInt16 ReadUInt16(); + UInt32 ReadUInt32(); + UInt64 ReadUInt64(); + bool ReadUInt32(UInt32 &signature); + + void Skip(UInt64 num); + void IncreaseRealPosition(UInt64 addValue); + + void ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock, + UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber); + HRESULT ReadLocalItem(CItemEx &item); + HRESULT ReadLocalItemDescriptor(CItemEx &item); + HRESULT ReadCdItem(CItemEx &item); + HRESULT TryEcd64(UInt64 offset, CCdInfo &cdInfo); + HRESULT FindCd(CCdInfo &cdInfo); + HRESULT TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress); + HRESULT ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress); + HRESULT ReadLocalsAndCd(CObjectVector<CItemEx> &items, CProgressVirt *progress, UInt64 &cdOffset, int &numCdItems); +public: + CInArchiveInfo ArcInfo; + bool IsZip64; + bool IsOkHeaders; + + HRESULT ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress); + HRESULT ReadLocalItemAfterCdItem(CItemEx &item); + HRESULT ReadLocalItemAfterCdItemFull(CItemEx &item); + HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); + void Close(); + bool SeekInArchive(UInt64 position); + ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size); + IInStream* CreateStream(); + + bool IsOpen() const { return m_Stream != NULL; } +}; + +}} + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItem.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItem.cpp new file mode 100644 index 000000000..139b01291 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItem.cpp @@ -0,0 +1,172 @@ +// Archive/ZipItem.cpp + +#include "StdAfx.h" + +#include "ZipHeader.h" +#include "ZipItem.h" +#include "../Common/ItemNameUtils.h" +#include "../../../../C/CpuArch.h" + +namespace NArchive { +namespace NZip { + +bool operator==(const CVersion &v1, const CVersion &v2) +{ + return (v1.Version == v2.Version) && (v1.HostOS == v2.HostOS); +} + +bool operator!=(const CVersion &v1, const CVersion &v2) +{ + return !(v1 == v2); +} + +bool CExtraSubBlock::ExtractNtfsTime(int index, FILETIME &ft) const +{ + ft.dwHighDateTime = ft.dwLowDateTime = 0; + UInt32 size = (UInt32)Data.GetCapacity(); + if (ID != NFileHeader::NExtraID::kNTFS || size < 32) + return false; + const Byte *p = (const Byte *)Data; + p += 4; // for reserved + size -= 4; + while (size > 4) + { + UInt16 tag = GetUi16(p); + UInt32 attrSize = GetUi16(p + 2); + p += 4; + size -= 4; + if (attrSize > size) + attrSize = size; + + if (tag == NFileHeader::NNtfsExtra::kTagTime && attrSize >= 24) + { + p += 8 * index; + ft.dwLowDateTime = GetUi32(p); + ft.dwHighDateTime = GetUi32(p + 4); + return true; + } + p += attrSize; + size -= attrSize; + } + return false; +} + +bool CExtraSubBlock::ExtractUnixTime(int index, UInt32 &res) const +{ + res = 0; + UInt32 size = (UInt32)Data.GetCapacity(); + if (ID != NFileHeader::NExtraID::kUnixTime || size < 5) + return false; + const Byte *p = (const Byte *)Data; + Byte flags = *p++; + size--; + for (int i = 0; i < 3; i++) + if ((flags & (1 << i)) != 0) + { + if (size < 4) + return false; + if (index == i) + { + res = GetUi32(p); + return true; + } + p += 4; + size -= 4; + } + return false; +} + +bool CLocalItem::IsDir() const +{ + return NItemName::HasTailSlash(Name, GetCodePage()); +} + +bool CItem::IsDir() const +{ + if (NItemName::HasTailSlash(Name, GetCodePage())) + return true; + if (!FromCentral) + return false; + WORD highAttributes = WORD((ExternalAttributes >> 16 ) & 0xFFFF); + switch(MadeByVersion.HostOS) + { + case NFileHeader::NHostOS::kAMIGA: + switch (highAttributes & NFileHeader::NAmigaAttribute::kIFMT) + { + case NFileHeader::NAmigaAttribute::kIFDIR: return true; + case NFileHeader::NAmigaAttribute::kIFREG: return false; + default: return false; // change it throw kUnknownAttributes; + } + case NFileHeader::NHostOS::kFAT: + case NFileHeader::NHostOS::kNTFS: + case NFileHeader::NHostOS::kHPFS: + case NFileHeader::NHostOS::kVFAT: + return ((ExternalAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); + case NFileHeader::NHostOS::kAtari: + case NFileHeader::NHostOS::kMac: + case NFileHeader::NHostOS::kVMS: + case NFileHeader::NHostOS::kVM_CMS: + case NFileHeader::NHostOS::kAcorn: + case NFileHeader::NHostOS::kMVS: + return false; // change it throw kUnknownAttributes; + default: + /* + switch (highAttributes & NFileHeader::NUnixAttribute::kIFMT) + { + case NFileHeader::NUnixAttribute::kIFDIR: + return true; + default: + return false; + } + */ + return false; + } +} + +UInt32 CLocalItem::GetWinAttributes() const +{ + DWORD winAttributes = 0; + if (IsDir()) + winAttributes |= FILE_ATTRIBUTE_DIRECTORY; + return winAttributes; +} + +UInt32 CItem::GetWinAttributes() const +{ + DWORD winAttributes = 0; + switch(MadeByVersion.HostOS) + { + case NFileHeader::NHostOS::kFAT: + case NFileHeader::NHostOS::kNTFS: + if (FromCentral) + winAttributes = ExternalAttributes; + break; + default: + winAttributes = 0; // must be converted from unix value; + } + if (IsDir()) // test it; + winAttributes |= FILE_ATTRIBUTE_DIRECTORY; + return winAttributes; +} + +void CLocalItem::SetFlagBits(int startBitNumber, int numBits, int value) +{ + UInt16 mask = (UInt16)(((1 << numBits) - 1) << startBitNumber); + Flags &= ~mask; + Flags |= value << startBitNumber; +} + +void CLocalItem::SetBitMask(int bitMask, bool enable) +{ + if(enable) + Flags |= bitMask; + else + Flags &= ~bitMask; +} + +void CLocalItem::SetEncrypted(bool encrypted) + { SetBitMask(NFileHeader::NFlags::kEncrypted, encrypted); } +void CLocalItem::SetUtf8(bool isUtf8) + { SetBitMask(NFileHeader::NFlags::kUtf8, isUtf8); } + +}} diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItem.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItem.h new file mode 100644 index 000000000..31f2de732 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItem.h @@ -0,0 +1,281 @@ +// Archive/ZipItem.h + +#ifndef __ARCHIVE_ZIP_ITEM_H +#define __ARCHIVE_ZIP_ITEM_H + +#include "Common/Types.h" +#include "Common/MyString.h" +#include "Common/Buffer.h" +#include "Common/UTFConvert.h" +#include "Common/StringConvert.h" + +#include "ZipHeader.h" + +namespace NArchive { +namespace NZip { + +struct CVersion +{ + Byte Version; + Byte HostOS; +}; + +bool operator==(const CVersion &v1, const CVersion &v2); +bool operator!=(const CVersion &v1, const CVersion &v2); + +struct CExtraSubBlock +{ + UInt16 ID; + CByteBuffer Data; + bool ExtractNtfsTime(int index, FILETIME &ft) const; + bool ExtractUnixTime(int index, UInt32 &res) const; +}; + +struct CWzAesExtraField +{ + UInt16 VendorVersion; // 0x0001 - AE-1, 0x0002 - AE-2, + // UInt16 VendorId; // "AE" + Byte Strength; // 1 - 128-bit , 2 - 192-bit , 3 - 256-bit + UInt16 Method; + + CWzAesExtraField(): VendorVersion(2), Strength(3), Method(0) {} + + bool NeedCrc() const { return (VendorVersion == 1); } + + bool ParseFromSubBlock(const CExtraSubBlock &sb) + { + if (sb.ID != NFileHeader::NExtraID::kWzAES) + return false; + if (sb.Data.GetCapacity() < 7) + return false; + const Byte *p = (const Byte *)sb.Data; + VendorVersion = (((UInt16)p[1]) << 8) | p[0]; + if (p[2] != 'A' || p[3] != 'E') + return false; + Strength = p[4]; + Method = (((UInt16)p[6]) << 16) | p[5]; + return true; + } + void SetSubBlock(CExtraSubBlock &sb) const + { + sb.Data.SetCapacity(7); + sb.ID = NFileHeader::NExtraID::kWzAES; + Byte *p = (Byte *)sb.Data; + p[0] = (Byte)VendorVersion; + p[1] = (Byte)(VendorVersion >> 8); + p[2] = 'A'; + p[3] = 'E'; + p[4] = Strength; + p[5] = (Byte)Method; + p[6] = (Byte)(Method >> 8); + } +}; + +namespace NStrongCryptoFlags +{ + const UInt16 kDES = 0x6601; + const UInt16 kRC2old = 0x6602; + const UInt16 k3DES168 = 0x6603; + const UInt16 k3DES112 = 0x6609; + const UInt16 kAES128 = 0x660E; + const UInt16 kAES192 = 0x660F; + const UInt16 kAES256 = 0x6610; + const UInt16 kRC2 = 0x6702; + const UInt16 kBlowfish = 0x6720; + const UInt16 kTwofish = 0x6721; + const UInt16 kRC4 = 0x6801; +} + +struct CStrongCryptoField +{ + UInt16 Format; + UInt16 AlgId; + UInt16 BitLen; + UInt16 Flags; + + bool ParseFromSubBlock(const CExtraSubBlock &sb) + { + if (sb.ID != NFileHeader::NExtraID::kStrongEncrypt) + return false; + const Byte *p = (const Byte *)sb.Data; + if (sb.Data.GetCapacity() < 8) + return false; + Format = (((UInt16)p[1]) << 8) | p[0]; + AlgId = (((UInt16)p[3]) << 8) | p[2]; + BitLen = (((UInt16)p[5]) << 8) | p[4]; + Flags = (((UInt16)p[7]) << 8) | p[6]; + return (Format == 2); + } +}; + +struct CExtraBlock +{ + CObjectVector<CExtraSubBlock> SubBlocks; + void Clear() { SubBlocks.Clear(); } + size_t GetSize() const + { + size_t res = 0; + for (int i = 0; i < SubBlocks.Size(); i++) + res += SubBlocks[i].Data.GetCapacity() + 2 + 2; + return res; + } + bool GetWzAesField(CWzAesExtraField &aesField) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + if (aesField.ParseFromSubBlock(SubBlocks[i])) + return true; + return false; + } + + bool GetStrongCryptoField(CStrongCryptoField &f) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + if (f.ParseFromSubBlock(SubBlocks[i])) + return true; + return false; + } + + bool HasWzAesField() const + { + CWzAesExtraField aesField; + return GetWzAesField(aesField); + } + + bool GetNtfsTime(int index, FILETIME &ft) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + { + const CExtraSubBlock &sb = SubBlocks[i]; + if (sb.ID == NFileHeader::NExtraID::kNTFS) + return sb.ExtractNtfsTime(index, ft); + } + return false; + } + + bool GetUnixTime(int index, UInt32 &res) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + { + const CExtraSubBlock &sb = SubBlocks[i]; + if (sb.ID == NFileHeader::NExtraID::kUnixTime) + return sb.ExtractUnixTime(index, res); + } + return false; + } + + /* + bool HasStrongCryptoField() const + { + CStrongCryptoField f; + return GetStrongCryptoField(f); + } + */ + + void RemoveUnknownSubBlocks() + { + for (int i = SubBlocks.Size() - 1; i >= 0; i--) + if (SubBlocks[i].ID != NFileHeader::NExtraID::kWzAES) + SubBlocks.Delete(i); + } +}; + + +class CLocalItem +{ +public: + CVersion ExtractVersion; + UInt16 Flags; + UInt16 CompressionMethod; + UInt32 Time; + UInt32 FileCRC; + UInt64 PackSize; + UInt64 UnPackSize; + + AString Name; + + CExtraBlock LocalExtra; + + bool IsUtf8() const { return (Flags & NFileHeader::NFlags::kUtf8) != 0; } + + bool IsEncrypted() const { return (Flags & NFileHeader::NFlags::kEncrypted) != 0; } + bool IsStrongEncrypted() const { return IsEncrypted() && (Flags & NFileHeader::NFlags::kStrongEncrypted) != 0; }; + bool IsAesEncrypted() const { return IsEncrypted() && (IsStrongEncrypted() || CompressionMethod == NFileHeader::NCompressionMethod::kWzAES); }; + + bool IsLzmaEOS() const { return (Flags & NFileHeader::NFlags::kLzmaEOS) != 0; } + + bool IsDir() const; + bool IgnoreItem() const { return false; } + UInt32 GetWinAttributes() const; + + bool HasDescriptor() const { return (Flags & NFileHeader::NFlags::kDescriptorUsedMask) != 0; } + + UString GetUnicodeString(const AString &s) const + { + UString res; + if (IsUtf8()) + if (!ConvertUTF8ToUnicode(s, res)) + res.Empty(); + if (res.IsEmpty()) + res = MultiByteToUnicodeString(s, GetCodePage()); + return res; + } + +private: + void SetFlagBits(int startBitNumber, int numBits, int value); + void SetBitMask(int bitMask, bool enable); +public: + void ClearFlags() { Flags = 0; } + void SetEncrypted(bool encrypted); + void SetUtf8(bool isUtf8); + + WORD GetCodePage() const { return CP_OEMCP; } +}; + +class CItem: public CLocalItem +{ +public: + CVersion MadeByVersion; + UInt16 InternalAttributes; + UInt32 ExternalAttributes; + + UInt64 LocalHeaderPosition; + + FILETIME NtfsMTime; + FILETIME NtfsATime; + FILETIME NtfsCTime; + + CExtraBlock CentralExtra; + CByteBuffer Comment; + + bool FromLocal; + bool FromCentral; + bool NtfsTimeIsDefined; + + bool IsDir() const; + UInt32 GetWinAttributes() const; + + bool IsThereCrc() const + { + if (CompressionMethod == NFileHeader::NCompressionMethod::kWzAES) + { + CWzAesExtraField aesField; + if (CentralExtra.GetWzAesField(aesField)) + return aesField.NeedCrc(); + } + return (FileCRC != 0 || !IsDir()); + } + + WORD GetCodePage() const + { + return (WORD)((MadeByVersion.HostOS == NFileHeader::NHostOS::kFAT + || MadeByVersion.HostOS == NFileHeader::NHostOS::kNTFS + ) ? CP_OEMCP : CP_ACP); + } + CItem() : FromLocal(false), FromCentral(false), NtfsTimeIsDefined(false) {} +}; + +}} + +#endif + + diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItemEx.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItemEx.h new file mode 100644 index 000000000..ab62cdbb4 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipItemEx.h @@ -0,0 +1,34 @@ +// Archive/ZipItemEx.h + +#ifndef __ARCHIVE_ZIP_ITEMEX_H +#define __ARCHIVE_ZIP_ITEMEX_H + +#include "ZipHeader.h" +#include "ZipItem.h" + +namespace NArchive { +namespace NZip { + +class CItemEx: public CItem +{ +public: + UInt32 FileHeaderWithNameSize; + UInt16 LocalExtraSize; + + UInt64 GetLocalFullSize() const + { return FileHeaderWithNameSize + LocalExtraSize + PackSize + + (HasDescriptor() ? NFileHeader::kDataDescriptorSize : 0); }; + /* + UInt64 GetLocalFullSize(bool isZip64) const + { return FileHeaderWithNameSize + LocalExtraSize + PackSize + + (HasDescriptor() ? (isZip64 ? NFileHeader::kDataDescriptor64Size : NFileHeader::kDataDescriptorSize) : 0); }; + */ + UInt64 GetLocalExtraPosition() const + { return LocalHeaderPosition + FileHeaderWithNameSize; }; + UInt64 GetDataPosition() const + { return GetLocalExtraPosition() + LocalExtraSize; }; +}; + +}} + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipOut.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipOut.cpp new file mode 100644 index 000000000..aa82143e3 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipOut.cpp @@ -0,0 +1,289 @@ +// ZipOut.cpp + +#include "StdAfx.h" + +#include "../../Common/OffsetStream.h" + +#include "ZipOut.h" + +namespace NArchive { +namespace NZip { + +void COutArchive::Create(IOutStream *outStream) +{ + if (!m_OutBuffer.Create(1 << 16)) + throw CSystemException(E_OUTOFMEMORY); + m_Stream = outStream; + m_OutBuffer.SetStream(outStream); + m_OutBuffer.Init(); + m_BasePosition = 0; +} + +void COutArchive::MoveBasePosition(UInt64 distanceToMove) +{ + m_BasePosition += distanceToMove; // test overflow +} + +void COutArchive::PrepareWriteCompressedDataZip64(UInt16 fileNameLength, bool isZip64, bool aesEncryption) +{ + m_IsZip64 = isZip64; + m_ExtraSize = isZip64 ? (4 + 8 + 8) : 0; + if (aesEncryption) + m_ExtraSize += 4 + 7; + m_LocalFileHeaderSize = 4 + NFileHeader::kLocalBlockSize + fileNameLength + m_ExtraSize; +} + +void COutArchive::PrepareWriteCompressedData(UInt16 fileNameLength, UInt64 unPackSize, bool aesEncryption) +{ + // We test it to 0xF8000000 to support case when compressed size + // can be larger than uncompressed size. + PrepareWriteCompressedDataZip64(fileNameLength, unPackSize >= 0xF8000000, aesEncryption); +} + +void COutArchive::PrepareWriteCompressedData2(UInt16 fileNameLength, UInt64 unPackSize, UInt64 packSize, bool aesEncryption) +{ + bool isUnPack64 = unPackSize >= 0xFFFFFFFF; + bool isPack64 = packSize >= 0xFFFFFFFF; + bool isZip64 = isPack64 || isUnPack64; + PrepareWriteCompressedDataZip64(fileNameLength, isZip64, aesEncryption); +} + +void COutArchive::WriteBytes(const void *buffer, UInt32 size) +{ + m_OutBuffer.WriteBytes(buffer, size); + m_BasePosition += size; +} + +void COutArchive::WriteByte(Byte b) +{ + WriteBytes(&b, 1); +} + +void COutArchive::WriteUInt16(UInt16 value) +{ + for (int i = 0; i < 2; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteUInt32(UInt32 value) +{ + for (int i = 0; i < 4; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteUInt64(UInt64 value) +{ + for (int i = 0; i < 8; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteExtra(const CExtraBlock &extra) +{ + if (extra.SubBlocks.Size() != 0) + { + for (int i = 0; i < extra.SubBlocks.Size(); i++) + { + const CExtraSubBlock &subBlock = extra.SubBlocks[i]; + WriteUInt16(subBlock.ID); + WriteUInt16((UInt16)subBlock.Data.GetCapacity()); + WriteBytes(subBlock.Data, (UInt32)subBlock.Data.GetCapacity()); + } + } +} + +void COutArchive::SeekTo(UInt64 offset) +{ + HRESULT res = m_Stream->Seek(offset, STREAM_SEEK_SET, NULL); + if (res != S_OK) + throw CSystemException(res); +} + +void COutArchive::WriteLocalHeader(const CLocalItem &item) +{ + SeekTo(m_BasePosition); + + bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF; + + WriteUInt32(NSignature::kLocalFileHeader); + { + Byte ver = item.ExtractVersion.Version; + if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64) + ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64; + WriteByte(ver); + } + WriteByte(item.ExtractVersion.HostOS); + WriteUInt16(item.Flags); + WriteUInt16(item.CompressionMethod); + WriteUInt32(item.Time); + WriteUInt32(item.FileCRC); + WriteUInt32(isZip64 ? 0xFFFFFFFF: (UInt32)item.PackSize); + WriteUInt32(isZip64 ? 0xFFFFFFFF: (UInt32)item.UnPackSize); + WriteUInt16((UInt16)item.Name.Length()); + { + UInt16 localExtraSize = (UInt16)((isZip64 ? (4 + 16): 0) + item.LocalExtra.GetSize()); + if (localExtraSize > m_ExtraSize) + throw CSystemException(E_FAIL); + } + WriteUInt16((UInt16)m_ExtraSize); // test it; + WriteBytes((const char *)item.Name, item.Name.Length()); + + UInt32 extraPos = 0; + if (isZip64) + { + extraPos += 4 + 16; + WriteUInt16(NFileHeader::NExtraID::kZip64); + WriteUInt16(16); + WriteUInt64(item.UnPackSize); + WriteUInt64(item.PackSize); + } + + WriteExtra(item.LocalExtra); + extraPos += (UInt32)item.LocalExtra.GetSize(); + for (; extraPos < m_ExtraSize; extraPos++) + WriteByte(0); + + m_OutBuffer.FlushWithCheck(); + MoveBasePosition(item.PackSize); + SeekTo(m_BasePosition); +} + +void COutArchive::WriteCentralHeader(const CItem &item) +{ + bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF; + bool isPack64 = item.PackSize >= 0xFFFFFFFF; + bool isPosition64 = item.LocalHeaderPosition >= 0xFFFFFFFF; + bool isZip64 = isPack64 || isUnPack64 || isPosition64; + + WriteUInt32(NSignature::kCentralFileHeader); + WriteByte(item.MadeByVersion.Version); + WriteByte(item.MadeByVersion.HostOS); + { + Byte ver = item.ExtractVersion.Version; + if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64) + ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64; + WriteByte(ver); + } + WriteByte(item.ExtractVersion.HostOS); + WriteUInt16(item.Flags); + WriteUInt16(item.CompressionMethod); + WriteUInt32(item.Time); + WriteUInt32(item.FileCRC); + WriteUInt32(isPack64 ? 0xFFFFFFFF: (UInt32)item.PackSize); + WriteUInt32(isUnPack64 ? 0xFFFFFFFF: (UInt32)item.UnPackSize); + WriteUInt16((UInt16)item.Name.Length()); + UInt16 zip64ExtraSize = (UInt16)((isUnPack64 ? 8: 0) + (isPack64 ? 8: 0) + (isPosition64 ? 8: 0)); + const UInt16 kNtfsExtraSize = 4 + 2 + 2 + (3 * 8); + UInt16 centralExtraSize = (UInt16)(isZip64 ? (4 + zip64ExtraSize) : 0) + (item.NtfsTimeIsDefined ? (4 + kNtfsExtraSize) : 0); + centralExtraSize = (UInt16)(centralExtraSize + item.CentralExtra.GetSize()); + WriteUInt16(centralExtraSize); // test it; + WriteUInt16((UInt16)item.Comment.GetCapacity()); + WriteUInt16(0); // DiskNumberStart; + WriteUInt16(item.InternalAttributes); + WriteUInt32(item.ExternalAttributes); + WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition); + WriteBytes((const char *)item.Name, item.Name.Length()); + if (isZip64) + { + WriteUInt16(NFileHeader::NExtraID::kZip64); + WriteUInt16(zip64ExtraSize); + if(isUnPack64) + WriteUInt64(item.UnPackSize); + if(isPack64) + WriteUInt64(item.PackSize); + if(isPosition64) + WriteUInt64(item.LocalHeaderPosition); + } + if (item.NtfsTimeIsDefined) + { + WriteUInt16(NFileHeader::NExtraID::kNTFS); + WriteUInt16(kNtfsExtraSize); + WriteUInt32(0); // reserved + WriteUInt16(NFileHeader::NNtfsExtra::kTagTime); + WriteUInt16(8 * 3); + WriteUInt32(item.NtfsMTime.dwLowDateTime); + WriteUInt32(item.NtfsMTime.dwHighDateTime); + WriteUInt32(item.NtfsATime.dwLowDateTime); + WriteUInt32(item.NtfsATime.dwHighDateTime); + WriteUInt32(item.NtfsCTime.dwLowDateTime); + WriteUInt32(item.NtfsCTime.dwHighDateTime); + } + WriteExtra(item.CentralExtra); + if (item.Comment.GetCapacity() > 0) + WriteBytes(item.Comment, (UInt32)item.Comment.GetCapacity()); +} + +void COutArchive::WriteCentralDir(const CObjectVector<CItem> &items, const CByteBuffer *comment) +{ + SeekTo(m_BasePosition); + + UInt64 cdOffset = GetCurrentPosition(); + for(int i = 0; i < items.Size(); i++) + WriteCentralHeader(items[i]); + UInt64 cd64EndOffset = GetCurrentPosition(); + UInt64 cdSize = cd64EndOffset - cdOffset; + bool cdOffset64 = cdOffset >= 0xFFFFFFFF; + bool cdSize64 = cdSize >= 0xFFFFFFFF; + bool items64 = items.Size() >= 0xFFFF; + bool isZip64 = (cdOffset64 || cdSize64 || items64); + + if (isZip64) + { + WriteUInt32(NSignature::kZip64EndOfCentralDir); + WriteUInt64(kZip64EcdSize); // ThisDiskNumber = 0; + WriteUInt16(45); // version + WriteUInt16(45); // version + WriteUInt32(0); // ThisDiskNumber = 0; + WriteUInt32(0); // StartCentralDirectoryDiskNumber;; + WriteUInt64((UInt64)items.Size()); + WriteUInt64((UInt64)items.Size()); + WriteUInt64((UInt64)cdSize); + WriteUInt64((UInt64)cdOffset); + + WriteUInt32(NSignature::kZip64EndOfCentralDirLocator); + WriteUInt32(0); // number of the disk with the start of the zip64 end of central directory + WriteUInt64(cd64EndOffset); + WriteUInt32(1); // total number of disks + } + WriteUInt32(NSignature::kEndOfCentralDir); + WriteUInt16(0); // ThisDiskNumber = 0; + WriteUInt16(0); // StartCentralDirectoryDiskNumber; + WriteUInt16((UInt16)(items64 ? 0xFFFF: items.Size())); + WriteUInt16((UInt16)(items64 ? 0xFFFF: items.Size())); + WriteUInt32(cdSize64 ? 0xFFFFFFFF: (UInt32)cdSize); + WriteUInt32(cdOffset64 ? 0xFFFFFFFF: (UInt32)cdOffset); + UInt32 commentSize = (UInt32)(comment ? comment->GetCapacity() : 0); + WriteUInt16((UInt16)commentSize); + if (commentSize > 0) + WriteBytes((const Byte *)*comment, commentSize); + m_OutBuffer.FlushWithCheck(); +} + +void COutArchive::CreateStreamForCompressing(IOutStream **outStream) +{ + COffsetOutStream *streamSpec = new COffsetOutStream; + CMyComPtr<IOutStream> tempStream(streamSpec); + streamSpec->Init(m_Stream, m_BasePosition + m_LocalFileHeaderSize); + *outStream = tempStream.Detach(); +} + +void COutArchive::SeekToPackedDataPosition() +{ + SeekTo(m_BasePosition + m_LocalFileHeaderSize); +} + +void COutArchive::CreateStreamForCopying(ISequentialOutStream **outStream) +{ + CMyComPtr<ISequentialOutStream> tempStream(m_Stream); + *outStream = tempStream.Detach(); +} + +}} diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipOut.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipOut.h new file mode 100644 index 000000000..2f6349e5c --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipOut.h @@ -0,0 +1,56 @@ +// ZipOut.h + +#ifndef __ZIP_OUT_H +#define __ZIP_OUT_H + +#include "Common/MyCom.h" + +#include "../../IStream.h" +#include "../../Common/OutBuffer.h" + +#include "ZipItem.h" + +namespace NArchive { +namespace NZip { + +// can throw CSystemException and COutBufferException + +class COutArchive +{ + CMyComPtr<IOutStream> m_Stream; + COutBuffer m_OutBuffer; + + UInt64 m_BasePosition; + UInt32 m_LocalFileHeaderSize; + UInt32 m_ExtraSize; + bool m_IsZip64; + + void WriteBytes(const void *buffer, UInt32 size); + void WriteByte(Byte b); + void WriteUInt16(UInt16 value); + void WriteUInt32(UInt32 value); + void WriteUInt64(UInt64 value); + + void WriteExtraHeader(const CItem &item); + void WriteCentralHeader(const CItem &item); + void WriteExtra(const CExtraBlock &extra); + void SeekTo(UInt64 offset); +public: + void Create(IOutStream *outStream); + void MoveBasePosition(UInt64 distanceToMove); + UInt64 GetCurrentPosition() const { return m_BasePosition; }; + void PrepareWriteCompressedDataZip64(UInt16 fileNameLength, bool isZip64, bool aesEncryption); + void PrepareWriteCompressedData(UInt16 fileNameLength, UInt64 unPackSize, bool aesEncryption); + void PrepareWriteCompressedData2(UInt16 fileNameLength, UInt64 unPackSize, UInt64 packSize, bool aesEncryption); + void WriteLocalHeader(const CLocalItem &item); + + void WriteCentralDir(const CObjectVector<CItem> &items, const CByteBuffer *comment); + + void CreateStreamForCompressing(IOutStream **outStream); + void CreateStreamForCopying(ISequentialOutStream **outStream); + void SeekToPackedDataPosition(); +}; + +}} + +#endif diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipRegister.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipRegister.cpp new file mode 100644 index 000000000..3e7aade85 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipRegister.cpp @@ -0,0 +1,18 @@ +// ZipRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "ZipHandler.h" +static IInArchive *CreateArc() { return new NArchive::NZip::CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new NArchive::NZip::CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"zip", L"zip jar xpi odt ods docx xlsx", 0, 1, { 0x50, 0x4B, 0x03, 0x04 }, 4, false, CreateArc, CreateArcOut }; + +REGISTER_ARC(Zip) diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipUpdate.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipUpdate.cpp new file mode 100644 index 000000000..d4fdee3d7 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipUpdate.cpp @@ -0,0 +1,1068 @@ +// ZipUpdate.cpp + +#include "StdAfx.h" + +#include "../../../../C/Alloc.h" + +#include "Common/AutoPtr.h" +#include "Common/Defs.h" +#include "Common/StringConvert.h" + +#include "Windows/Defs.h" +#include "Windows/Thread.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/OutMemStream.h" +#include "../../Common/ProgressUtils.h" +#ifndef _7ZIP_ST +#include "../../Common/ProgressMt.h" +#endif +#include "../../Common/StreamUtils.h" + +#include "../../Compress/CopyCoder.h" + +#include "ZipAddCommon.h" +#include "ZipOut.h" +#include "ZipUpdate.h" + +using namespace NWindows; +using namespace NSynchronization; + +namespace NArchive { +namespace NZip { + +static const Byte kHostOS = + #ifdef _WIN32 + NFileHeader::NHostOS::kFAT; + #else + NFileHeader::NHostOS::kUnix; + #endif + +static const Byte kMadeByHostOS = kHostOS; +static const Byte kExtractHostOS = kHostOS; + +static const Byte kMethodForDirectory = NFileHeader::NCompressionMethod::kStored; + +static HRESULT CopyBlockToArchive(ISequentialInStream *inStream, + COutArchive &outArchive, ICompressProgressInfo *progress) +{ + CMyComPtr<ISequentialOutStream> outStream; + outArchive.CreateStreamForCopying(&outStream); + return NCompress::CopyStream(inStream, outStream, progress); +} + +static HRESULT WriteRange(IInStream *inStream, COutArchive &outArchive, + const CUpdateRange &range, ICompressProgressInfo *progress) +{ + UInt64 position; + RINOK(inStream->Seek(range.Position, STREAM_SEEK_SET, &position)); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec); + streamSpec->SetStream(inStream); + streamSpec->Init(range.Size); + + RINOK(CopyBlockToArchive(inStreamLimited, outArchive, progress)); + return progress->SetRatioInfo(&range.Size, &range.Size); +} + +static void SetFileHeader( + COutArchive &archive, + const CCompressionMethodMode &options, + const CUpdateItem &ui, + CItem &item) +{ + item.UnPackSize = ui.Size; + bool isDir; + + item.ClearFlags(); + + if (ui.NewProperties) + { + isDir = ui.IsDir; + item.Name = ui.Name; + item.SetUtf8(ui.IsUtf8); + item.ExternalAttributes = ui.Attributes; + item.Time = ui.Time; + item.NtfsMTime = ui.NtfsMTime; + item.NtfsATime = ui.NtfsATime; + item.NtfsCTime = ui.NtfsCTime; + item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined; + } + else + isDir = item.IsDir(); + + item.LocalHeaderPosition = archive.GetCurrentPosition(); + item.MadeByVersion.HostOS = kMadeByHostOS; + item.MadeByVersion.Version = NFileHeader::NCompressionMethod::kMadeByProgramVersion; + + item.ExtractVersion.HostOS = kExtractHostOS; + + item.InternalAttributes = 0; // test it + item.SetEncrypted(!isDir && options.PasswordIsDefined); + if (isDir) + { + item.ExtractVersion.Version = NFileHeader::NCompressionMethod::kExtractVersion_Dir; + item.CompressionMethod = kMethodForDirectory; + item.PackSize = 0; + item.FileCRC = 0; // test it + } +} + +static void SetItemInfoFromCompressingResult(const CCompressingResult &compressingResult, + bool isAesMode, Byte aesKeyMode, CItem &item) +{ + item.ExtractVersion.Version = compressingResult.ExtractVersion; + item.CompressionMethod = compressingResult.Method; + item.FileCRC = compressingResult.CRC; + item.UnPackSize = compressingResult.UnpackSize; + item.PackSize = compressingResult.PackSize; + + item.LocalExtra.Clear(); + item.CentralExtra.Clear(); + + if (isAesMode) + { + CWzAesExtraField wzAesField; + wzAesField.Strength = aesKeyMode; + wzAesField.Method = compressingResult.Method; + item.CompressionMethod = NFileHeader::NCompressionMethod::kWzAES; + item.FileCRC = 0; + CExtraSubBlock sb; + wzAesField.SetSubBlock(sb); + item.LocalExtra.SubBlocks.Add(sb); + item.CentralExtra.SubBlocks.Add(sb); + } +} + +#ifndef _7ZIP_ST + +static THREAD_FUNC_DECL CoderThread(void *threadCoderInfo); + +struct CThreadInfo +{ + #ifdef EXTERNAL_CODECS + CMyComPtr<ICompressCodecsInfo> _codecsInfo; + const CObjectVector<CCodecInfoEx> *_externalCodecs; + #endif + + NWindows::CThread Thread; + NWindows::NSynchronization::CAutoResetEvent CompressEvent; + NWindows::NSynchronization::CAutoResetEvent CompressionCompletedEvent; + bool ExitThread; + + CMtCompressProgress *ProgressSpec; + CMyComPtr<ICompressProgressInfo> Progress; + + COutMemStream *OutStreamSpec; + CMyComPtr<IOutStream> OutStream; + CMyComPtr<ISequentialInStream> InStream; + + CAddCommon Coder; + HRESULT Result; + CCompressingResult CompressingResult; + + bool IsFree; + UInt32 UpdateIndex; + + CThreadInfo(const CCompressionMethodMode &options): + ExitThread(false), + ProgressSpec(0), + OutStreamSpec(0), + Coder(options) + {} + + HRESULT CreateEvents() + { + RINOK(CompressEvent.CreateIfNotCreated()); + return CompressionCompletedEvent.CreateIfNotCreated(); + } + HRes CreateThread() { return Thread.Create(CoderThread, this); } + + void WaitAndCode(); + void StopWaitClose() + { + ExitThread = true; + if (OutStreamSpec != 0) + OutStreamSpec->StopWriting(E_ABORT); + if (CompressEvent.IsCreated()) + CompressEvent.Set(); + Thread.Wait(); + Thread.Close(); + } + +}; + +void CThreadInfo::WaitAndCode() +{ + for (;;) + { + CompressEvent.Lock(); + if (ExitThread) + return; + Result = Coder.Compress( + #ifdef EXTERNAL_CODECS + _codecsInfo, _externalCodecs, + #endif + InStream, OutStream, Progress, CompressingResult); + if (Result == S_OK && Progress) + Result = Progress->SetRatioInfo(&CompressingResult.UnpackSize, &CompressingResult.PackSize); + CompressionCompletedEvent.Set(); + } +} + +static THREAD_FUNC_DECL CoderThread(void *threadCoderInfo) +{ + ((CThreadInfo *)threadCoderInfo)->WaitAndCode(); + return 0; +} + +class CThreads +{ +public: + CObjectVector<CThreadInfo> Threads; + ~CThreads() + { + for (int i = 0; i < Threads.Size(); i++) + Threads[i].StopWaitClose(); + } +}; + +struct CMemBlocks2: public CMemLockBlocks +{ + CCompressingResult CompressingResult; + bool Defined; + bool Skip; + CMemBlocks2(): Defined(false), Skip(false) {} +}; + +class CMemRefs +{ +public: + CMemBlockManagerMt *Manager; + CObjectVector<CMemBlocks2> Refs; + CMemRefs(CMemBlockManagerMt *manager): Manager(manager) {} ; + ~CMemRefs() + { + for (int i = 0; i < Refs.Size(); i++) + Refs[i].FreeOpt(Manager); + } +}; + +class CMtProgressMixer2: + public ICompressProgressInfo, + public CMyUnknownImp +{ + UInt64 ProgressOffset; + UInt64 InSizes[2]; + UInt64 OutSizes[2]; + CMyComPtr<IProgress> Progress; + CMyComPtr<ICompressProgressInfo> RatioProgress; + bool _inSizeIsMain; +public: + NWindows::NSynchronization::CCriticalSection CriticalSection; + MY_UNKNOWN_IMP + void Create(IProgress *progress, bool inSizeIsMain); + void SetProgressOffset(UInt64 progressOffset); + HRESULT SetRatioInfo(int index, const UInt64 *inSize, const UInt64 *outSize); + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +void CMtProgressMixer2::Create(IProgress *progress, bool inSizeIsMain) +{ + Progress = progress; + Progress.QueryInterface(IID_ICompressProgressInfo, &RatioProgress); + _inSizeIsMain = inSizeIsMain; + ProgressOffset = InSizes[0] = InSizes[1] = OutSizes[0] = OutSizes[1] = 0; +} + +void CMtProgressMixer2::SetProgressOffset(UInt64 progressOffset) +{ + CriticalSection.Enter(); + InSizes[1] = OutSizes[1] = 0; + ProgressOffset = progressOffset; + CriticalSection.Leave(); +} + +HRESULT CMtProgressMixer2::SetRatioInfo(int index, const UInt64 *inSize, const UInt64 *outSize) +{ + NWindows::NSynchronization::CCriticalSectionLock lock(CriticalSection); + if (index == 0 && RatioProgress) + { + RINOK(RatioProgress->SetRatioInfo(inSize, outSize)); + } + if (inSize != 0) + InSizes[index] = *inSize; + if (outSize != 0) + OutSizes[index] = *outSize; + UInt64 v = ProgressOffset + (_inSizeIsMain ? + (InSizes[0] + InSizes[1]) : + (OutSizes[0] + OutSizes[1])); + return Progress->SetCompleted(&v); +} + +STDMETHODIMP CMtProgressMixer2::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + return SetRatioInfo(0, inSize, outSize); +} + +class CMtProgressMixer: + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + CMtProgressMixer2 *Mixer2; + CMyComPtr<ICompressProgressInfo> RatioProgress; + void Create(IProgress *progress, bool inSizeIsMain); + MY_UNKNOWN_IMP + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +void CMtProgressMixer::Create(IProgress *progress, bool inSizeIsMain) +{ + Mixer2 = new CMtProgressMixer2; + RatioProgress = Mixer2; + Mixer2->Create(progress, inSizeIsMain); +} + +STDMETHODIMP CMtProgressMixer::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + return Mixer2->SetRatioInfo(1, inSize, outSize); +} + + +#endif + + +static HRESULT UpdateItemOldData(COutArchive &archive, + IInStream *inStream, + const CUpdateItem &ui, CItemEx &item, + /* bool izZip64, */ + ICompressProgressInfo *progress, + UInt64 &complexity) +{ + if (ui.NewProperties) + { + if (item.HasDescriptor()) + return E_NOTIMPL; + + // use old name size. + // CUpdateRange range(item.GetLocalExtraPosition(), item.LocalExtraSize + item.PackSize); + CUpdateRange range(item.GetDataPosition(), item.PackSize); + + // item.ExternalAttributes = ui.Attributes; + // Test it + item.Name = ui.Name; + item.SetUtf8(ui.IsUtf8); + item.Time = ui.Time; + item.NtfsMTime = ui.NtfsMTime; + item.NtfsATime = ui.NtfsATime; + item.NtfsCTime = ui.NtfsCTime; + item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined; + + item.CentralExtra.RemoveUnknownSubBlocks(); + item.LocalExtra.RemoveUnknownSubBlocks(); + + archive.PrepareWriteCompressedData2((UInt16)item.Name.Length(), item.UnPackSize, item.PackSize, item.LocalExtra.HasWzAesField()); + item.LocalHeaderPosition = archive.GetCurrentPosition(); + archive.SeekToPackedDataPosition(); + RINOK(WriteRange(inStream, archive, range, progress)); + complexity += range.Size; + archive.WriteLocalHeader(item); + } + else + { + CUpdateRange range(item.LocalHeaderPosition, item.GetLocalFullSize()); + + // set new header position + item.LocalHeaderPosition = archive.GetCurrentPosition(); + + RINOK(WriteRange(inStream, archive, range, progress)); + complexity += range.Size; + archive.MoveBasePosition(range.Size); + } + return S_OK; +} + +static void WriteDirHeader(COutArchive &archive, const CCompressionMethodMode *options, + const CUpdateItem &ui, CItemEx &item) +{ + SetFileHeader(archive, *options, ui, item); + archive.PrepareWriteCompressedData((UInt16)item.Name.Length(), ui.Size, options->IsAesMode); + archive.WriteLocalHeader(item); +} + +static HRESULT Update2St( + DECL_EXTERNAL_CODECS_LOC_VARS + COutArchive &archive, + CInArchive *inArchive, + IInStream *inStream, + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + const CCompressionMethodMode *options, + const CByteBuffer *comment, + IArchiveUpdateCallback *updateCallback) +{ + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + CAddCommon compressor(*options); + + CObjectVector<CItem> items; + UInt64 unpackSizeTotal = 0, packSizeTotal = 0; + + for (int itemIndex = 0; itemIndex < updateItems.Size(); itemIndex++) + { + lps->InSize = unpackSizeTotal; + lps->OutSize = packSizeTotal; + RINOK(lps->SetCur()); + const CUpdateItem &ui = updateItems[itemIndex]; + CItemEx item; + if (!ui.NewProperties || !ui.NewData) + { + item = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(item) != S_OK) + return E_NOTIMPL; + } + + if (ui.NewData) + { + bool isDir = ((ui.NewProperties) ? ui.IsDir : item.IsDir()); + if (isDir) + { + WriteDirHeader(archive, options, ui, item); + } + else + { + CMyComPtr<ISequentialInStream> fileInStream; + HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream); + if (res == S_FALSE) + { + lps->ProgressOffset += ui.Size; + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + continue; + } + RINOK(res); + + // file Size can be 64-bit !!! + SetFileHeader(archive, *options, ui, item); + archive.PrepareWriteCompressedData((UInt16)item.Name.Length(), ui.Size, options->IsAesMode); + CCompressingResult compressingResult; + CMyComPtr<IOutStream> outStream; + archive.CreateStreamForCompressing(&outStream); + RINOK(compressor.Compress( + EXTERNAL_CODECS_LOC_VARS + fileInStream, outStream, progress, compressingResult)); + SetItemInfoFromCompressingResult(compressingResult, options->IsAesMode, options->AesKeyMode, item); + archive.WriteLocalHeader(item); + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + unpackSizeTotal += item.UnPackSize; + packSizeTotal += item.PackSize; + } + } + else + { + UInt64 complexity = 0; + lps->SendRatio = false; + RINOK(UpdateItemOldData(archive, inStream, ui, item, progress, complexity)); + lps->SendRatio = true; + lps->ProgressOffset += complexity; + } + items.Add(item); + lps->ProgressOffset += NFileHeader::kLocalBlockSize; + } + archive.WriteCentralDir(items, comment); + return S_OK; +} + +static HRESULT Update2( + DECL_EXTERNAL_CODECS_LOC_VARS + COutArchive &archive, + CInArchive *inArchive, + IInStream *inStream, + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + const CCompressionMethodMode *options, + const CByteBuffer *comment, + IArchiveUpdateCallback *updateCallback) +{ + UInt64 complexity = 0; + UInt64 numFilesToCompress = 0; + UInt64 numBytesToCompress = 0; + + int i; + for(i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &ui = updateItems[i]; + if (ui.NewData) + { + complexity += ui.Size; + numBytesToCompress += ui.Size; + numFilesToCompress++; + /* + if (ui.Commented) + complexity += ui.CommentRange.Size; + */ + } + else + { + CItemEx inputItem = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(inputItem) != S_OK) + return E_NOTIMPL; + complexity += inputItem.GetLocalFullSize(); + // complexity += inputItem.GetCentralExtraPlusCommentSize(); + } + complexity += NFileHeader::kLocalBlockSize; + complexity += NFileHeader::kCentralBlockSize; + } + + if (comment) + complexity += comment->GetCapacity(); + complexity++; // end of central + updateCallback->SetTotal(complexity); + + CAddCommon compressor(*options); + + complexity = 0; + + #ifndef _7ZIP_ST + + const size_t kNumMaxThreads = (1 << 10); + UInt32 numThreads = options->NumThreads; + if (numThreads > kNumMaxThreads) + numThreads = kNumMaxThreads; + + const size_t kMemPerThread = (1 << 25); + const size_t kBlockSize = 1 << 16; + + CCompressionMethodMode options2; + if (options != 0) + options2 = *options; + + bool mtMode = ((options != 0) && (numThreads > 1)); + + if (numFilesToCompress <= 1) + mtMode = false; + + if (mtMode) + { + Byte method = options->MethodSequence.Front(); + if (method == NFileHeader::NCompressionMethod::kStored && !options->PasswordIsDefined) + mtMode = false; + if (method == NFileHeader::NCompressionMethod::kBZip2) + { + UInt64 averageSize = numBytesToCompress / numFilesToCompress; + UInt32 blockSize = options->DicSize; + if (blockSize == 0) + blockSize = 1; + UInt64 averageNumberOfBlocks = averageSize / blockSize; + UInt32 numBZip2Threads = 32; + if (averageNumberOfBlocks < numBZip2Threads) + numBZip2Threads = (UInt32)averageNumberOfBlocks; + if (numBZip2Threads < 1) + numBZip2Threads = 1; + numThreads = numThreads / numBZip2Threads; + options2.NumThreads = numBZip2Threads; + if (numThreads <= 1) + mtMode = false; + } + if (method == NFileHeader::NCompressionMethod::kLZMA) + { + UInt32 numLZMAThreads = (options->Algo > 0 ? 2 : 1); + numThreads /= numLZMAThreads; + options2.NumThreads = numLZMAThreads; + if (numThreads <= 1) + mtMode = false; + } + } + + if (!mtMode) + #endif + return Update2St( + EXTERNAL_CODECS_LOC_VARS + archive, inArchive,inStream, + inputItems, updateItems, options, comment, updateCallback); + + + #ifndef _7ZIP_ST + + CObjectVector<CItem> items; + + CMtProgressMixer *mtProgressMixerSpec = new CMtProgressMixer; + CMyComPtr<ICompressProgressInfo> progress = mtProgressMixerSpec; + mtProgressMixerSpec->Create(updateCallback, true); + + CMtCompressProgressMixer mtCompressProgressMixer; + mtCompressProgressMixer.Init(numThreads, mtProgressMixerSpec->RatioProgress); + + CMemBlockManagerMt memManager(kBlockSize); + CMemRefs refs(&memManager); + + CThreads threads; + CRecordVector<HANDLE> compressingCompletedEvents; + CRecordVector<int> threadIndices; // list threads in order of updateItems + + { + RINOK(memManager.AllocateSpaceAlways((size_t)numThreads * (kMemPerThread / kBlockSize))); + for(i = 0; i < updateItems.Size(); i++) + refs.Refs.Add(CMemBlocks2()); + + UInt32 i; + for (i = 0; i < numThreads; i++) + threads.Threads.Add(CThreadInfo(options2)); + + for (i = 0; i < numThreads; i++) + { + CThreadInfo &threadInfo = threads.Threads[i]; + #ifdef EXTERNAL_CODECS + threadInfo._codecsInfo = codecsInfo; + threadInfo._externalCodecs = externalCodecs; + #endif + RINOK(threadInfo.CreateEvents()); + threadInfo.OutStreamSpec = new COutMemStream(&memManager); + RINOK(threadInfo.OutStreamSpec->CreateEvents()); + threadInfo.OutStream = threadInfo.OutStreamSpec; + threadInfo.IsFree = true; + threadInfo.ProgressSpec = new CMtCompressProgress(); + threadInfo.Progress = threadInfo.ProgressSpec; + threadInfo.ProgressSpec->Init(&mtCompressProgressMixer, (int)i); + RINOK(threadInfo.CreateThread()); + } + } + int mtItemIndex = 0; + + int itemIndex = 0; + int lastRealStreamItemIndex = -1; + + while (itemIndex < updateItems.Size()) + { + if ((UInt32)threadIndices.Size() < numThreads && mtItemIndex < updateItems.Size()) + { + const CUpdateItem &ui = updateItems[mtItemIndex++]; + if (!ui.NewData) + continue; + CItemEx item; + if (ui.NewProperties) + { + if (ui.IsDir) + continue; + } + else + { + item = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(item) != S_OK) + return E_NOTIMPL; + if (item.IsDir()) + continue; + } + CMyComPtr<ISequentialInStream> fileInStream; + { + NWindows::NSynchronization::CCriticalSectionLock lock(mtProgressMixerSpec->Mixer2->CriticalSection); + HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream); + if (res == S_FALSE) + { + complexity += ui.Size; + complexity += NFileHeader::kLocalBlockSize; + mtProgressMixerSpec->Mixer2->SetProgressOffset(complexity); + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + refs.Refs[mtItemIndex - 1].Skip = true; + continue; + } + RINOK(res); + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + } + + for (UInt32 i = 0; i < numThreads; i++) + { + CThreadInfo &threadInfo = threads.Threads[i]; + if (threadInfo.IsFree) + { + threadInfo.IsFree = false; + threadInfo.InStream = fileInStream; + + // !!!!! we must release ref before sending event + // BUG was here in v4.43 and v4.44. It could change ref counter in two threads in same time + fileInStream.Release(); + + threadInfo.OutStreamSpec->Init(); + threadInfo.ProgressSpec->Reinit(); + threadInfo.CompressEvent.Set(); + threadInfo.UpdateIndex = mtItemIndex - 1; + + compressingCompletedEvents.Add(threadInfo.CompressionCompletedEvent); + threadIndices.Add(i); + break; + } + } + continue; + } + + if (refs.Refs[itemIndex].Skip) + { + itemIndex++; + continue; + } + + const CUpdateItem &ui = updateItems[itemIndex]; + + CItemEx item; + if (!ui.NewProperties || !ui.NewData) + { + item = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(item) != S_OK) + return E_NOTIMPL; + } + + if (ui.NewData) + { + bool isDir = ((ui.NewProperties) ? ui.IsDir : item.IsDir()); + if (isDir) + { + WriteDirHeader(archive, options, ui, item); + } + else + { + if (lastRealStreamItemIndex < itemIndex) + { + lastRealStreamItemIndex = itemIndex; + SetFileHeader(archive, *options, ui, item); + // file Size can be 64-bit !!! + archive.PrepareWriteCompressedData((UInt16)item.Name.Length(), ui.Size, options->IsAesMode); + } + + CMemBlocks2 &memRef = refs.Refs[itemIndex]; + if (memRef.Defined) + { + CMyComPtr<IOutStream> outStream; + archive.CreateStreamForCompressing(&outStream); + memRef.WriteToStream(memManager.GetBlockSize(), outStream); + SetItemInfoFromCompressingResult(memRef.CompressingResult, + options->IsAesMode, options->AesKeyMode, item); + SetFileHeader(archive, *options, ui, item); + archive.WriteLocalHeader(item); + // RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + memRef.FreeOpt(&memManager); + } + else + { + { + CThreadInfo &thread = threads.Threads[threadIndices.Front()]; + if (!thread.OutStreamSpec->WasUnlockEventSent()) + { + CMyComPtr<IOutStream> outStream; + archive.CreateStreamForCompressing(&outStream); + thread.OutStreamSpec->SetOutStream(outStream); + thread.OutStreamSpec->SetRealStreamMode(); + } + } + + DWORD result = ::WaitForMultipleObjects(compressingCompletedEvents.Size(), + &compressingCompletedEvents.Front(), FALSE, INFINITE); + int t = (int)(result - WAIT_OBJECT_0); + CThreadInfo &threadInfo = threads.Threads[threadIndices[t]]; + threadInfo.InStream.Release(); + threadInfo.IsFree = true; + RINOK(threadInfo.Result); + threadIndices.Delete(t); + compressingCompletedEvents.Delete(t); + if (t == 0) + { + RINOK(threadInfo.OutStreamSpec->WriteToRealStream()); + threadInfo.OutStreamSpec->ReleaseOutStream(); + SetItemInfoFromCompressingResult(threadInfo.CompressingResult, + options->IsAesMode, options->AesKeyMode, item); + SetFileHeader(archive, *options, ui, item); + archive.WriteLocalHeader(item); + } + else + { + CMemBlocks2 &memRef = refs.Refs[threadInfo.UpdateIndex]; + threadInfo.OutStreamSpec->DetachData(memRef); + memRef.CompressingResult = threadInfo.CompressingResult; + memRef.Defined = true; + continue; + } + } + } + } + else + { + RINOK(UpdateItemOldData(archive, inStream, ui, item, progress, complexity)); + } + items.Add(item); + complexity += NFileHeader::kLocalBlockSize; + mtProgressMixerSpec->Mixer2->SetProgressOffset(complexity); + itemIndex++; + } + archive.WriteCentralDir(items, comment); + return S_OK; + #endif +} + +static const size_t kCacheBlockSize = (1 << 20); +static const size_t kCacheSize = (kCacheBlockSize << 2); +static const size_t kCacheMask = (kCacheSize - 1); + +class CCacheOutStream: + public IOutStream, + public CMyUnknownImp +{ + CMyComPtr<IOutStream> _stream; + Byte *_cache; + UInt64 _virtPos; + UInt64 _virtSize; + UInt64 _phyPos; + UInt64 _phySize; // <= _virtSize + UInt64 _cachedPos; // (_cachedPos + _cachedSize) <= _virtSize + size_t _cachedSize; + + HRESULT MyWrite(size_t size); + HRESULT MyWriteBlock() + { + return MyWrite(kCacheBlockSize - ((size_t)_cachedPos & (kCacheBlockSize - 1))); + } + HRESULT FlushCache(); +public: + CCacheOutStream(): _cache(0) {} + ~CCacheOutStream(); + bool Allocate(); + HRESULT Init(IOutStream *stream); + + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + STDMETHOD(SetSize)(UInt64 newSize); +}; + +bool CCacheOutStream::Allocate() +{ + if (!_cache) + _cache = (Byte *)::MidAlloc(kCacheSize); + return (_cache != NULL); +} + +HRESULT CCacheOutStream::Init(IOutStream *stream) +{ + _virtPos = _phyPos = 0; + _stream = stream; + RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &_virtPos)); + RINOK(_stream->Seek(0, STREAM_SEEK_END, &_virtSize)); + RINOK(_stream->Seek(_virtPos, STREAM_SEEK_SET, &_virtPos)); + _phyPos = _virtPos; + _phySize = _virtSize; + _cachedPos = 0; + _cachedSize = 0; + return S_OK; +} + +HRESULT CCacheOutStream::MyWrite(size_t size) +{ + while (size != 0 && _cachedSize != 0) + { + if (_phyPos != _cachedPos) + { + RINOK(_stream->Seek(_cachedPos, STREAM_SEEK_SET, &_phyPos)); + } + size_t pos = (size_t)_cachedPos & kCacheMask; + size_t curSize = MyMin(kCacheSize - pos, _cachedSize); + curSize = MyMin(curSize, size); + RINOK(WriteStream(_stream, _cache + pos, curSize)); + _phyPos += curSize; + if (_phySize < _phyPos) + _phySize = _phyPos; + _cachedPos += curSize; + _cachedSize -= curSize; + size -= curSize; + } + return S_OK; +} + +HRESULT CCacheOutStream::FlushCache() +{ + return MyWrite(_cachedSize); +} + +CCacheOutStream::~CCacheOutStream() +{ + FlushCache(); + if (_virtSize != _phySize) + _stream->SetSize(_virtSize); + if (_virtPos != _phyPos) + _stream->Seek(_virtPos, STREAM_SEEK_SET, NULL); + ::MidFree(_cache); +} + +STDMETHODIMP CCacheOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (size == 0) + return S_OK; + + UInt64 zerosStart = _virtPos; + if (_cachedSize != 0) + { + if (_virtPos < _cachedPos) + { + RINOK(FlushCache()); + } + else + { + UInt64 cachedEnd = _cachedPos + _cachedSize; + if (cachedEnd < _virtPos) + { + if (cachedEnd < _phySize) + { + RINOK(FlushCache()); + } + else + zerosStart = cachedEnd; + } + } + } + + if (_cachedSize == 0 && _phySize < _virtPos) + _cachedPos = zerosStart = _phySize; + + if (zerosStart != _virtPos) + { + // write zeros to [cachedEnd ... _virtPos) + + for (;;) + { + UInt64 cachedEnd = _cachedPos + _cachedSize; + size_t endPos = (size_t)cachedEnd & kCacheMask; + size_t curSize = kCacheSize - endPos; + if (curSize > _virtPos - cachedEnd) + curSize = (size_t)(_virtPos - cachedEnd); + if (curSize == 0) + break; + while (curSize > (kCacheSize - _cachedSize)) + { + RINOK(MyWriteBlock()); + } + memset(_cache + endPos, 0, curSize); + _cachedSize += curSize; + } + } + + if (_cachedSize == 0) + _cachedPos = _virtPos; + + size_t pos = (size_t)_virtPos & kCacheMask; + size = (UInt32)MyMin((size_t)size, kCacheSize - pos); + UInt64 cachedEnd = _cachedPos + _cachedSize; + if (_virtPos != cachedEnd) // _virtPos < cachedEnd + size = (UInt32)MyMin((size_t)size, (size_t)(cachedEnd - _virtPos)); + else + { + // _virtPos == cachedEnd + if (_cachedSize == kCacheSize) + { + RINOK(MyWriteBlock()); + } + size_t startPos = (size_t)_cachedPos & kCacheMask; + if (startPos > pos) + size = (UInt32)MyMin((size_t)size, (size_t)(startPos - pos)); + _cachedSize += size; + } + memcpy(_cache + pos, data, size); + if (processedSize) + *processedSize = size; + _virtPos += size; + if (_virtSize < _virtPos) + _virtSize = _virtPos; + return S_OK; +} + +STDMETHODIMP CCacheOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch(seekOrigin) + { + case STREAM_SEEK_SET: _virtPos = offset; break; + case STREAM_SEEK_CUR: _virtPos += offset; break; + case STREAM_SEEK_END: _virtPos = _virtSize + offset; break; + default: return STG_E_INVALIDFUNCTION; + } + if (newPosition) + *newPosition = _virtPos; + return S_OK; +} + +STDMETHODIMP CCacheOutStream::SetSize(UInt64 newSize) +{ + _virtSize = newSize; + if (newSize < _phySize) + { + RINOK(_stream->SetSize(newSize)); + _phySize = newSize; + } + if (newSize <= _cachedPos) + { + _cachedSize = 0; + _cachedPos = newSize; + } + if (newSize < _cachedPos + _cachedSize) + _cachedSize = (size_t)(newSize - _cachedPos); + return S_OK; +} + + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + ISequentialOutStream *seqOutStream, + CInArchive *inArchive, + CCompressionMethodMode *compressionMethodMode, + IArchiveUpdateCallback *updateCallback) +{ + CMyComPtr<IOutStream> outStream; + { + CMyComPtr<IOutStream> outStreamReal; + seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStreamReal); + if (!outStreamReal) + return E_NOTIMPL; + CCacheOutStream *cacheStream = new CCacheOutStream(); + outStream = cacheStream; + if (!cacheStream->Allocate()) + return E_OUTOFMEMORY; + RINOK(cacheStream->Init(outStreamReal)); + } + + if (inArchive) + { + if (inArchive->ArcInfo.Base != 0 || + inArchive->ArcInfo.StartPosition != 0 || + !inArchive->IsOkHeaders) + return E_NOTIMPL; + } + + COutArchive outArchive; + outArchive.Create(outStream); + /* + if (inArchive && inArchive->ArcInfo.StartPosition > 0) + { + CMyComPtr<ISequentialInStream> inStream; + inStream.Attach(inArchive->CreateLimitedStream(0, inArchive->ArcInfo.StartPosition)); + RINOK(CopyBlockToArchive(inStream, outArchive, NULL)); + outArchive.MoveBasePosition(inArchive->ArcInfo.StartPosition); + } + */ + CMyComPtr<IInStream> inStream; + if (inArchive) + inStream.Attach(inArchive->CreateStream()); + + return Update2( + EXTERNAL_CODECS_LOC_VARS + outArchive, inArchive, inStream, + inputItems, updateItems, + compressionMethodMode, + inArchive ? &inArchive->ArcInfo.Comment : NULL, + updateCallback); +} + +}} diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipUpdate.h b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipUpdate.h new file mode 100644 index 000000000..eee16738c --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/Zip/ZipUpdate.h @@ -0,0 +1,58 @@ +// Zip/Update.h + +#ifndef __ZIP_UPDATE_H +#define __ZIP_UPDATE_H + +#include "../../ICoder.h" +#include "../IArchive.h" + +#include "../../Common/CreateCoder.h" + +#include "ZipCompressionMode.h" +#include "ZipIn.h" + +namespace NArchive { +namespace NZip { + +struct CUpdateRange +{ + UInt64 Position; + UInt64 Size; + CUpdateRange() {}; + CUpdateRange(UInt64 position, UInt64 size): Position(position), Size(size) {}; +}; + +struct CUpdateItem +{ + bool NewData; + bool NewProperties; + bool IsDir; + bool NtfsTimeIsDefined; + bool IsUtf8; + int IndexInArchive; + int IndexInClient; + UInt32 Attributes; + UInt32 Time; + UInt64 Size; + AString Name; + // bool Commented; + // CUpdateRange CommentRange; + FILETIME NtfsMTime; + FILETIME NtfsATime; + FILETIME NtfsCTime; + + CUpdateItem(): NtfsTimeIsDefined(false), IsUtf8(false), Size(0) {} +}; + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + ISequentialOutStream *seqOutStream, + CInArchive *inArchive, + CCompressionMethodMode *compressionMethodMode, + IArchiveUpdateCallback *updateCallback); + +}} + +#endif |