1 // ZipAddCommon.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../../C/7zCrc.h" 6 #include "../../../../C/Alloc.h" 7 8 #include "../../../Windows/PropVariant.h" 9 10 #include "../../ICoder.h" 11 #include "../../IPassword.h" 12 #include "../../MyVersion.h" 13 14 #include "../../Common/CreateCoder.h" 15 #include "../../Common/StreamObjects.h" 16 #include "../../Common/StreamUtils.h" 17 18 #include "../../Compress/LzmaEncoder.h" 19 #include "../../Compress/PpmdZip.h" 20 #include "../../Compress/XzEncoder.h" 21 22 #include "../Common/InStreamWithCRC.h" 23 24 #include "ZipAddCommon.h" 25 #include "ZipHeader.h" 26 27 namespace NArchive { 28 namespace NZip { 29 30 using namespace NFileHeader; 31 32 33 static const unsigned kLzmaPropsSize = 5; 34 static const unsigned kLzmaHeaderSize = 4 + kLzmaPropsSize; 35 36 Z7_CLASS_IMP_NOQIB_3( 37 CLzmaEncoder 38 , ICompressCoder 39 , ICompressSetCoderProperties 40 , ICompressSetCoderPropertiesOpt 41 ) 42 public: 43 CMyComPtr2<ICompressCoder, NCompress::NLzma::CEncoder> Encoder; 44 Byte Header[kLzmaHeaderSize]; 45 }; 46 47 Z7_COM7F_IMF(CLzmaEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps)) 48 { 49 Encoder.Create_if_Empty(); 50 CMyComPtr2_Create<ISequentialOutStream, CBufPtrSeqOutStream> outStream; 51 outStream->Init(Header + 4, kLzmaPropsSize); 52 RINOK(Encoder->SetCoderProperties(propIDs, props, numProps)) 53 RINOK(Encoder->WriteCoderProperties(outStream)) 54 if (outStream->GetPos() != kLzmaPropsSize) 55 return E_FAIL; 56 Header[0] = MY_VER_MAJOR; 57 Header[1] = MY_VER_MINOR; 58 Header[2] = kLzmaPropsSize; 59 Header[3] = 0; 60 return S_OK; 61 } 62 63 Z7_COM7F_IMF(CLzmaEncoder::SetCoderPropertiesOpt(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps)) 64 { 65 return Encoder->SetCoderPropertiesOpt(propIDs, props, numProps); 66 } 67 68 Z7_COM7F_IMF(CLzmaEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, 69 const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)) 70 { 71 RINOK(WriteStream(outStream, Header, kLzmaHeaderSize)) 72 return Encoder.Interface()->Code(inStream, outStream, inSize, outSize, progress); 73 } 74 75 76 CAddCommon::CAddCommon(): 77 _isLzmaEos(false), 78 _buf(NULL) 79 {} 80 81 void CAddCommon::SetOptions(const CCompressionMethodMode &options) 82 { 83 _options = options; 84 } 85 86 CAddCommon::~CAddCommon() 87 { 88 MidFree(_buf); 89 } 90 91 static const UInt32 kBufSize = ((UInt32)1 << 16); 92 93 HRESULT CAddCommon::CalcStreamCRC(ISequentialInStream *inStream, UInt32 &resultCRC) 94 { 95 if (!_buf) 96 { 97 _buf = (Byte *)MidAlloc(kBufSize); 98 if (!_buf) 99 return E_OUTOFMEMORY; 100 } 101 102 UInt32 crc = CRC_INIT_VAL; 103 for (;;) 104 { 105 UInt32 processed; 106 RINOK(inStream->Read(_buf, kBufSize, &processed)) 107 if (processed == 0) 108 { 109 resultCRC = CRC_GET_DIGEST(crc); 110 return S_OK; 111 } 112 crc = CrcUpdate(crc, _buf, (size_t)processed); 113 } 114 } 115 116 117 HRESULT CAddCommon::Set_Pre_CompressionResult(bool inSeqMode, bool outSeqMode, UInt64 unpackSize, 118 CCompressingResult &opRes) const 119 { 120 // We use Zip64, if unPackSize size is larger than 0xF8000000 to support 121 // cases when compressed size can be about 3% larger than uncompressed size 122 123 const UInt32 kUnpackZip64Limit = 0xF8000000; 124 125 opRes.UnpackSize = unpackSize; 126 opRes.PackSize = (UInt64)1 << 60; // we use big value to force Zip64 mode. 127 128 if (unpackSize < kUnpackZip64Limit) 129 opRes.PackSize = (UInt32)0xFFFFFFFF - 1; // it will not use Zip64 for that size 130 131 if (opRes.PackSize < unpackSize) 132 opRes.PackSize = unpackSize; 133 134 const Byte method = _options.MethodSequence[0]; 135 136 if (method == NCompressionMethod::kStore && !_options.Password_Defined) 137 opRes.PackSize = unpackSize; 138 139 opRes.CRC = 0; 140 141 opRes.LzmaEos = false; 142 143 opRes.ExtractVersion = NCompressionMethod::kExtractVersion_Default; 144 opRes.DescriptorMode = outSeqMode; 145 146 if (_options.Password_Defined) 147 { 148 opRes.ExtractVersion = NCompressionMethod::kExtractVersion_ZipCrypto; 149 if (_options.IsAesMode) 150 opRes.ExtractVersion = NCompressionMethod::kExtractVersion_Aes; 151 else 152 { 153 if (inSeqMode) 154 opRes.DescriptorMode = true; 155 } 156 } 157 158 opRes.Method = method; 159 Byte ver = 0; 160 161 switch (method) 162 { 163 case NCompressionMethod::kStore: break; 164 case NCompressionMethod::kDeflate: ver = NCompressionMethod::kExtractVersion_Deflate; break; 165 case NCompressionMethod::kDeflate64: ver = NCompressionMethod::kExtractVersion_Deflate64; break; 166 case NCompressionMethod::kXz : ver = NCompressionMethod::kExtractVersion_Xz; break; 167 case NCompressionMethod::kPPMd : ver = NCompressionMethod::kExtractVersion_PPMd; break; 168 case NCompressionMethod::kBZip2: ver = NCompressionMethod::kExtractVersion_BZip2; break; 169 case NCompressionMethod::kLZMA : 170 { 171 ver = NCompressionMethod::kExtractVersion_LZMA; 172 const COneMethodInfo *oneMethodMain = &_options._methods[0]; 173 opRes.LzmaEos = oneMethodMain->Get_Lzma_Eos(); 174 break; 175 } 176 default: break; 177 } 178 if (opRes.ExtractVersion < ver) 179 opRes.ExtractVersion = ver; 180 181 return S_OK; 182 } 183 184 185 HRESULT CAddCommon::Compress( 186 DECL_EXTERNAL_CODECS_LOC_VARS 187 ISequentialInStream *inStream, IOutStream *outStream, 188 bool inSeqMode, bool outSeqMode, 189 UInt32 fileTime, 190 UInt64 expectedDataSize, bool expectedDataSize_IsConfirmed, 191 ICompressProgressInfo *progress, CCompressingResult &opRes) 192 { 193 // opRes.LzmaEos = false; 194 195 if (!inStream) 196 { 197 // We can create empty stream here. But it was already implemented in caller code in 9.33+ 198 return E_INVALIDARG; 199 } 200 201 CMyComPtr2_Create<ISequentialInStream, CSequentialInStreamWithCRC> inCrcStream; 202 203 CMyComPtr<IInStream> inStream2; 204 if (!inSeqMode) 205 { 206 inStream->QueryInterface(IID_IInStream, (void **)&inStream2); 207 if (!inStream2) 208 { 209 // inSeqMode = true; 210 // inSeqMode must be correct before 211 return E_FAIL; 212 } 213 } 214 215 inCrcStream->SetStream(inStream); 216 inCrcStream->SetFullSize(expectedDataSize_IsConfirmed ? expectedDataSize : (UInt64)(Int64)-1); 217 // inCrcStream->Init(); 218 219 unsigned numTestMethods = _options.MethodSequence.Size(); 220 // numTestMethods != 0 221 222 bool descriptorMode = outSeqMode; 223 224 // ZipCrypto without descriptor requires additional reading pass for 225 // inStream to calculate CRC for password check field. 226 // The descriptor allows to use ZipCrypto check field without CRC (InfoZip's modification). 227 228 if (!outSeqMode) 229 if (inSeqMode && _options.Password_Defined && !_options.IsAesMode) 230 descriptorMode = true; 231 opRes.DescriptorMode = descriptorMode; 232 233 if (numTestMethods > 1) 234 if (inSeqMode || outSeqMode || !inStream2) 235 numTestMethods = 1; 236 237 UInt32 crc = 0; 238 bool crc_IsCalculated = false; 239 240 CFilterCoder::C_OutStream_Releaser outStreamReleaser; 241 // opRes.ExtractVersion = NCompressionMethod::kExtractVersion_Default; 242 243 for (unsigned i = 0; i < numTestMethods; i++) 244 { 245 inCrcStream->Init(); 246 247 if (i != 0) 248 { 249 // if (inStream2) 250 { 251 RINOK(InStream_SeekToBegin(inStream2)) 252 } 253 RINOK(outStream->Seek(0, STREAM_SEEK_SET, NULL)) 254 RINOK(outStream->SetSize(0)) 255 } 256 257 opRes.LzmaEos = false; 258 opRes.ExtractVersion = NCompressionMethod::kExtractVersion_Default; 259 260 const Byte method = _options.MethodSequence[i]; 261 if (method == NCompressionMethod::kStore && descriptorMode) 262 { 263 // we still can create descriptor_mode archives with "Store" method, but they are not good for 100% 264 return E_NOTIMPL; 265 } 266 267 bool needCode = true; 268 269 if (_options.Password_Defined) 270 { 271 opRes.ExtractVersion = NCompressionMethod::kExtractVersion_ZipCrypto; 272 273 if (!_cryptoStream.IsDefined()) 274 _cryptoStream.SetFromCls(new CFilterCoder(true)); 275 276 if (_options.IsAesMode) 277 { 278 opRes.ExtractVersion = NCompressionMethod::kExtractVersion_Aes; 279 if (!_cryptoStream->Filter) 280 { 281 _cryptoStream->Filter = _filterAesSpec = new NCrypto::NWzAes::CEncoder; 282 _filterAesSpec->SetKeyMode(_options.AesKeyMode); 283 RINOK(_filterAesSpec->CryptoSetPassword((const Byte *)(const char *)_options.Password, _options.Password.Len())) 284 } 285 RINOK(_filterAesSpec->WriteHeader(outStream)) 286 } 287 else 288 { 289 if (!_cryptoStream->Filter) 290 { 291 _cryptoStream->Filter = _filterSpec = new NCrypto::NZip::CEncoder; 292 _filterSpec->CryptoSetPassword((const Byte *)(const char *)_options.Password, _options.Password.Len()); 293 } 294 295 UInt32 check; 296 297 if (descriptorMode) 298 { 299 // it's Info-ZIP modification for stream_mode descriptor_mode (bit 3 of the general purpose bit flag is set) 300 check = (fileTime & 0xFFFF); 301 } 302 else 303 { 304 if (!crc_IsCalculated) 305 { 306 RINOK(CalcStreamCRC(inStream, crc)) 307 crc_IsCalculated = true; 308 RINOK(InStream_SeekToBegin(inStream2)) 309 inCrcStream->Init(); 310 } 311 check = (crc >> 16); 312 } 313 314 RINOK(_filterSpec->WriteHeader_Check16(outStream, (UInt16)check)) 315 } 316 317 if (method == NCompressionMethod::kStore) 318 { 319 needCode = false; 320 RINOK(_cryptoStream->Code(inCrcStream, outStream, NULL, NULL, progress)) 321 } 322 else 323 { 324 RINOK(_cryptoStream->SetOutStream(outStream)) 325 RINOK(_cryptoStream->InitEncoder()) 326 outStreamReleaser.FilterCoder = _cryptoStream.ClsPtr(); 327 } 328 } 329 330 if (needCode) 331 { 332 switch (method) 333 { 334 case NCompressionMethod::kStore: 335 { 336 _copyCoder.Create_if_Empty(); 337 CMyComPtr<ISequentialOutStream> outStreamNew; 338 if (_options.Password_Defined) 339 outStreamNew = _cryptoStream; 340 else 341 outStreamNew = outStream; 342 RINOK(_copyCoder.Interface()->Code(inCrcStream, outStreamNew, NULL, NULL, progress)) 343 break; 344 } 345 346 default: 347 { 348 if (!_compressEncoder) 349 { 350 CLzmaEncoder *_lzmaEncoder = NULL; 351 if (method == NCompressionMethod::kLZMA) 352 { 353 _compressExtractVersion = NCompressionMethod::kExtractVersion_LZMA; 354 _lzmaEncoder = new CLzmaEncoder(); 355 _compressEncoder = _lzmaEncoder; 356 } 357 else if (method == NCompressionMethod::kXz) 358 { 359 _compressExtractVersion = NCompressionMethod::kExtractVersion_Xz; 360 NCompress::NXz::CEncoder *encoder = new NCompress::NXz::CEncoder(); 361 _compressEncoder = encoder; 362 } 363 else if (method == NCompressionMethod::kPPMd) 364 { 365 _compressExtractVersion = NCompressionMethod::kExtractVersion_PPMd; 366 NCompress::NPpmdZip::CEncoder *encoder = new NCompress::NPpmdZip::CEncoder(); 367 _compressEncoder = encoder; 368 } 369 else 370 { 371 CMethodId methodId; 372 switch (method) 373 { 374 case NCompressionMethod::kBZip2: 375 methodId = kMethodId_BZip2; 376 _compressExtractVersion = NCompressionMethod::kExtractVersion_BZip2; 377 break; 378 default: 379 _compressExtractVersion = ((method == NCompressionMethod::kDeflate64) ? 380 NCompressionMethod::kExtractVersion_Deflate64 : 381 NCompressionMethod::kExtractVersion_Deflate); 382 methodId = kMethodId_ZipBase + method; 383 break; 384 } 385 RINOK(CreateCoder_Id( 386 EXTERNAL_CODECS_LOC_VARS 387 methodId, true, _compressEncoder)) 388 if (!_compressEncoder) 389 return E_NOTIMPL; 390 391 if (method == NCompressionMethod::kDeflate || 392 method == NCompressionMethod::kDeflate64) 393 { 394 } 395 else if (method == NCompressionMethod::kBZip2) 396 { 397 } 398 } 399 { 400 CMyComPtr<ICompressSetCoderProperties> setCoderProps; 401 _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProps); 402 if (setCoderProps) 403 { 404 if (!_options._methods.IsEmpty()) 405 { 406 COneMethodInfo *oneMethodMain = &_options._methods[0]; 407 408 RINOK(oneMethodMain->SetCoderProps(setCoderProps, 409 _options.DataSizeReduce_Defined ? &_options.DataSizeReduce : NULL)) 410 } 411 } 412 } 413 if (method == NCompressionMethod::kLZMA) 414 _isLzmaEos = _lzmaEncoder->Encoder->IsWriteEndMark(); 415 } 416 417 if (method == NCompressionMethod::kLZMA) 418 opRes.LzmaEos = _isLzmaEos; 419 420 CMyComPtr<ISequentialOutStream> outStreamNew; 421 if (_options.Password_Defined) 422 outStreamNew = _cryptoStream; 423 else 424 outStreamNew = outStream; 425 if (_compressExtractVersion > opRes.ExtractVersion) 426 opRes.ExtractVersion = _compressExtractVersion; 427 428 { 429 CMyComPtr<ICompressSetCoderPropertiesOpt> optProps; 430 _compressEncoder->QueryInterface(IID_ICompressSetCoderPropertiesOpt, (void **)&optProps); 431 if (optProps) 432 { 433 const PROPID propID = NCoderPropID::kExpectedDataSize; 434 NWindows::NCOM::CPropVariant prop = (UInt64)expectedDataSize; 435 RINOK(optProps->SetCoderPropertiesOpt(&propID, &prop, 1)) 436 } 437 } 438 439 try { 440 RINOK(_compressEncoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress)) 441 } catch (...) { return E_FAIL; } 442 break; 443 } 444 } // switch end 445 446 if (_options.Password_Defined) 447 { 448 RINOK(_cryptoStream->OutStreamFinish()) 449 } 450 } 451 452 if (_options.Password_Defined) 453 { 454 if (_options.IsAesMode) 455 { 456 RINOK(_filterAesSpec->WriteFooter(outStream)) 457 } 458 } 459 460 RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &opRes.PackSize)) 461 462 { 463 opRes.CRC = inCrcStream->GetCRC(); 464 opRes.UnpackSize = inCrcStream->GetSize(); 465 opRes.Method = method; 466 } 467 468 if (!inCrcStream->WasFinished()) 469 return E_FAIL; 470 471 if (_options.Password_Defined) 472 { 473 if (opRes.PackSize < opRes.UnpackSize + 474 (_options.IsAesMode ? _filterAesSpec->GetAddPackSize() : NCrypto::NZip::kHeaderSize)) 475 break; 476 } 477 else if (opRes.PackSize < opRes.UnpackSize) 478 break; 479 } 480 481 return S_OK; 482 } 483 484 }} 485