1 // 7zEncode.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../Common/CreateCoder.h"
6 #include "../../Common/FilterCoder.h"
7 #include "../../Common/LimitedStreams.h"
8 #include "../../Common/InOutTempBuffer.h"
9 #include "../../Common/ProgressUtils.h"
10 #include "../../Common/StreamObjects.h"
11 
12 #include "7zEncode.h"
13 #include "7zSpecStream.h"
14 
15 static const UInt64 k_Delta = 0x03;
16 static const UInt64 k_BCJ = 0x03030103;
17 static const UInt64 k_BCJ2 = 0x0303011B;
18 
19 namespace NArchive {
20 namespace N7z {
21 
ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo & bindInfo,const CRecordVector<CMethodId> decompressionMethods,CFolder & folder)22 static void ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo &bindInfo,
23     const CRecordVector<CMethodId> decompressionMethods,
24     CFolder &folder)
25 {
26   // bindInfo.CoderMethodIDs.Clear();
27   // folder.OutStreams.Clear();
28   folder.BindPairs.SetSize(bindInfo.BindPairs.Size());
29   unsigned i;
30   for (i = 0; i < bindInfo.BindPairs.Size(); i++)
31   {
32     CBindPair &bp = folder.BindPairs[i];
33     const NCoderMixer::CBindPair &mixerBp = bindInfo.BindPairs[i];
34     bp.InIndex = mixerBp.InIndex;
35     bp.OutIndex = mixerBp.OutIndex;
36   }
37   folder.Coders.SetSize(bindInfo.Coders.Size());
38   for (i = 0; i < bindInfo.Coders.Size(); i++)
39   {
40     CCoderInfo &coderInfo = folder.Coders[i];
41     const NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i];
42     coderInfo.NumInStreams = coderStreamsInfo.NumInStreams;
43     coderInfo.NumOutStreams = coderStreamsInfo.NumOutStreams;
44     coderInfo.MethodID = decompressionMethods[i];
45     // coderInfo.Props can be nonFree;
46   }
47   folder.PackStreams.SetSize(bindInfo.InStreams.Size());
48   for (i = 0; i < bindInfo.InStreams.Size(); i++)
49     folder.PackStreams[i] = bindInfo.InStreams[i];
50 }
51 
SetCoderProps2(const CProps & props,const UInt64 * dataSizeReduce,IUnknown * coder)52 static HRESULT SetCoderProps2(const CProps &props, const UInt64 *dataSizeReduce, IUnknown *coder)
53 {
54   CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
55   coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties);
56   if (setCoderProperties)
57     return props.SetCoderProps(setCoderProperties, dataSizeReduce);
58   return props.AreThereNonOptionalProps() ? E_INVALIDARG : S_OK;
59 }
60 
CreateMixerCoder(DECL_EXTERNAL_CODECS_LOC_VARS const UInt64 * inSizeForReduce)61 HRESULT CEncoder::CreateMixerCoder(
62     DECL_EXTERNAL_CODECS_LOC_VARS
63     const UInt64 *inSizeForReduce)
64 {
65   _mixerCoderSpec = new NCoderMixer::CCoderMixer2MT;
66   _mixerCoder = _mixerCoderSpec;
67   RINOK(_mixerCoderSpec->SetBindInfo(_bindInfo));
68   FOR_VECTOR (i, _options.Methods)
69   {
70     const CMethodFull &methodFull = _options.Methods[i];
71     CCoderInfo &encodingInfo = _codersInfo.AddNew();
72     encodingInfo.MethodID = methodFull.Id;
73     CMyComPtr<ICompressCoder> encoder;
74     CMyComPtr<ICompressCoder2> encoder2;
75 
76 
77     RINOK(CreateCoder(
78         EXTERNAL_CODECS_LOC_VARS
79         methodFull.Id, encoder, encoder2, true));
80 
81     if (!encoder && !encoder2)
82       return E_FAIL;
83 
84     CMyComPtr<IUnknown> encoderCommon = encoder ? (IUnknown *)encoder : (IUnknown *)encoder2;
85 
86     #ifndef _7ZIP_ST
87     {
88       CMyComPtr<ICompressSetCoderMt> setCoderMt;
89       encoderCommon.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt);
90       if (setCoderMt)
91       {
92         RINOK(setCoderMt->SetNumberOfThreads(_options.NumThreads));
93       }
94     }
95     #endif
96 
97     RINOK(SetCoderProps2(methodFull, inSizeForReduce, encoderCommon));
98 
99     /*
100     CMyComPtr<ICryptoResetSalt> resetSalt;
101     encoderCommon.QueryInterface(IID_ICryptoResetSalt, (void **)&resetSalt);
102     if (resetSalt)
103     {
104       resetSalt->ResetSalt();
105     }
106     */
107 
108     #ifdef EXTERNAL_CODECS
109     CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
110     encoderCommon.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
111     if (setCompressCodecsInfo)
112     {
113       RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs));
114     }
115     #endif
116 
117     CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
118     encoderCommon.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword);
119 
120     if (cryptoSetPassword)
121     {
122       const UInt32 sizeInBytes = _options.Password.Len() * 2;
123       CByteBuffer buffer(sizeInBytes);
124       for (unsigned i = 0; i < _options.Password.Len(); i++)
125       {
126         wchar_t c = _options.Password[i];
127         ((Byte *)buffer)[i * 2] = (Byte)c;
128         ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
129       }
130       RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes));
131     }
132 
133     if (encoder)
134       _mixerCoderSpec->AddCoder(encoder);
135     else
136       _mixerCoderSpec->AddCoder2(encoder2);
137   }
138   return S_OK;
139 }
140 
Encode(DECL_EXTERNAL_CODECS_LOC_VARS ISequentialInStream * inStream,const UInt64 * inStreamSize,const UInt64 * inSizeForReduce,CFolder & folderItem,CRecordVector<UInt64> & coderUnpackSizes,UInt64 & unpackSize,ISequentialOutStream * outStream,CRecordVector<UInt64> & packSizes,ICompressProgressInfo * compressProgress)141 HRESULT CEncoder::Encode(
142     DECL_EXTERNAL_CODECS_LOC_VARS
143     ISequentialInStream *inStream,
144     const UInt64 *inStreamSize, const UInt64 *inSizeForReduce,
145     CFolder &folderItem,
146     CRecordVector<UInt64> &coderUnpackSizes,
147     UInt64 &unpackSize,
148     ISequentialOutStream *outStream,
149     CRecordVector<UInt64> &packSizes,
150     ICompressProgressInfo *compressProgress)
151 {
152   RINOK(EncoderConstr());
153 
154   if (!_mixerCoderSpec)
155   {
156     RINOK(CreateMixerCoder(EXTERNAL_CODECS_LOC_VARS inSizeForReduce));
157   }
158   _mixerCoderSpec->ReInit();
159   // _mixerCoderSpec->SetCoderInfo(0, NULL, NULL, progress);
160 
161   CObjectVector<CInOutTempBuffer> inOutTempBuffers;
162   CObjectVector<CSequentialOutTempBufferImp *> tempBufferSpecs;
163   CObjectVector<CMyComPtr<ISequentialOutStream> > tempBuffers;
164   unsigned numMethods = _bindInfo.Coders.Size();
165   unsigned i;
166   for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
167   {
168     CInOutTempBuffer &iotb = inOutTempBuffers.AddNew();
169     iotb.Create();
170     iotb.InitWriting();
171   }
172   for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
173   {
174     CSequentialOutTempBufferImp *tempBufferSpec = new CSequentialOutTempBufferImp;
175     CMyComPtr<ISequentialOutStream> tempBuffer = tempBufferSpec;
176     tempBufferSpec->Init(&inOutTempBuffers[i - 1]);
177     tempBuffers.Add(tempBuffer);
178     tempBufferSpecs.Add(tempBufferSpec);
179   }
180 
181   for (i = 0; i < numMethods; i++)
182     _mixerCoderSpec->SetCoderInfo(i, NULL, NULL);
183 
184   if (_bindInfo.InStreams.IsEmpty())
185     return E_FAIL;
186   UInt32 mainCoderIndex, mainStreamIndex;
187   _bindInfo.FindInStream(_bindInfo.InStreams[0], mainCoderIndex, mainStreamIndex);
188 
189   if (inStreamSize)
190   {
191     CRecordVector<const UInt64 *> sizePointers;
192     for (UInt32 i = 0; i < _bindInfo.Coders[mainCoderIndex].NumInStreams; i++)
193       if (i == mainStreamIndex)
194         sizePointers.Add(inStreamSize);
195       else
196         sizePointers.Add(NULL);
197     _mixerCoderSpec->SetCoderInfo(mainCoderIndex, &sizePointers.Front(), NULL);
198   }
199 
200 
201   // UInt64 outStreamStartPos;
202   // RINOK(stream->Seek(0, STREAM_SEEK_CUR, &outStreamStartPos));
203 
204   CSequentialInStreamSizeCount2 *inStreamSizeCountSpec = new CSequentialInStreamSizeCount2;
205   CMyComPtr<ISequentialInStream> inStreamSizeCount = inStreamSizeCountSpec;
206   CSequentialOutStreamSizeCount *outStreamSizeCountSpec = NULL;
207   CMyComPtr<ISequentialOutStream> outStreamSizeCount;
208 
209   inStreamSizeCountSpec->Init(inStream);
210 
211   CRecordVector<ISequentialInStream *> inStreamPointers;
212   CRecordVector<ISequentialOutStream *> outStreamPointers;
213   inStreamPointers.Add(inStreamSizeCount);
214 
215   if (_bindInfo.OutStreams.Size() != 0)
216   {
217     outStreamSizeCountSpec = new CSequentialOutStreamSizeCount;
218     outStreamSizeCount = outStreamSizeCountSpec;
219     outStreamSizeCountSpec->SetStream(outStream);
220     outStreamSizeCountSpec->Init();
221     outStreamPointers.Add(outStreamSizeCount);
222   }
223 
224   for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
225     outStreamPointers.Add(tempBuffers[i - 1]);
226 
227   for (i = 0; i < _codersInfo.Size(); i++)
228   {
229     CCoderInfo &encodingInfo = _codersInfo[i];
230 
231     CMyComPtr<ICryptoResetInitVector> resetInitVector;
232     _mixerCoderSpec->_coders[i].QueryInterface(IID_ICryptoResetInitVector, (void **)&resetInitVector);
233     if (resetInitVector)
234     {
235       resetInitVector->ResetInitVector();
236     }
237 
238     CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties;
239     _mixerCoderSpec->_coders[i].QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties);
240     if (writeCoderProperties)
241     {
242       CDynBufSeqOutStream *outStreamSpec = new CDynBufSeqOutStream;
243       CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
244       outStreamSpec->Init();
245       writeCoderProperties->WriteCoderProperties(outStream);
246       outStreamSpec->CopyToBuffer(encodingInfo.Props);
247     }
248   }
249 
250   UInt32 progressIndex = mainCoderIndex;
251 
252   for (i = 0; i + 1 < _codersInfo.Size(); i++)
253   {
254     UInt64 m = _codersInfo[i].MethodID;
255     if (m == k_Delta || m == k_BCJ || m == k_BCJ2)
256       progressIndex = i + 1;
257   }
258 
259   _mixerCoderSpec->SetProgressCoderIndex(progressIndex);
260 
261   RINOK(_mixerCoder->Code(&inStreamPointers.Front(), NULL, 1,
262     &outStreamPointers.Front(), NULL, outStreamPointers.Size(), compressProgress));
263 
264   ConvertBindInfoToFolderItemInfo(_decompressBindInfo, _decompressionMethods, folderItem);
265 
266   if (_bindInfo.OutStreams.Size() != 0)
267     packSizes.Add(outStreamSizeCountSpec->GetSize());
268 
269   for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
270   {
271     CInOutTempBuffer &inOutTempBuffer = inOutTempBuffers[i - 1];
272     RINOK(inOutTempBuffer.WriteToStream(outStream));
273     packSizes.Add(inOutTempBuffer.GetDataSize());
274   }
275 
276   unpackSize = 0;
277   for (i = 0; i < (int)_bindReverseConverter->NumSrcInStreams; i++)
278   {
279     int binder = _bindInfo.FindBinderForInStream(
280         _bindReverseConverter->DestOutToSrcInMap[i]);
281     UInt64 streamSize;
282     if (binder < 0)
283     {
284       streamSize = inStreamSizeCountSpec->GetSize();
285       unpackSize = streamSize;
286     }
287     else
288       streamSize = _mixerCoderSpec->GetWriteProcessedSize(binder);
289     coderUnpackSizes.Add(streamSize);
290   }
291   for (i = 0; i < numMethods; i++)
292     folderItem.Coders[numMethods - 1 - i].Props = _codersInfo[i].Props;
293   return S_OK;
294 }
295 
296 
CEncoder(const CCompressionMethodMode & options)297 CEncoder::CEncoder(const CCompressionMethodMode &options):
298   _bindReverseConverter(0),
299   _constructed(false)
300 {
301   if (options.IsEmpty())
302     throw 1;
303 
304   _options = options;
305   _mixerCoderSpec = NULL;
306 }
307 
EncoderConstr()308 HRESULT CEncoder::EncoderConstr()
309 {
310   if (_constructed)
311     return S_OK;
312   if (_options.Methods.IsEmpty())
313   {
314     // it has only password method;
315     if (!_options.PasswordIsDefined)
316       throw 1;
317     if (!_options.Binds.IsEmpty())
318       throw 1;
319     NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
320     CMethodFull method;
321 
322     method.NumInStreams = 1;
323     method.NumOutStreams = 1;
324     coderStreamsInfo.NumInStreams = 1;
325     coderStreamsInfo.NumOutStreams = 1;
326     method.Id = k_AES;
327 
328     _options.Methods.Add(method);
329     _bindInfo.Coders.Add(coderStreamsInfo);
330 
331     _bindInfo.InStreams.Add(0);
332     _bindInfo.OutStreams.Add(0);
333   }
334   else
335   {
336 
337   UInt32 numInStreams = 0, numOutStreams = 0;
338   unsigned i;
339   for (i = 0; i < _options.Methods.Size(); i++)
340   {
341     const CMethodFull &methodFull = _options.Methods[i];
342     NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
343     coderStreamsInfo.NumInStreams = methodFull.NumOutStreams;
344     coderStreamsInfo.NumOutStreams = methodFull.NumInStreams;
345     if (_options.Binds.IsEmpty())
346     {
347       if (i < _options.Methods.Size() - 1)
348       {
349         NCoderMixer::CBindPair bindPair;
350         bindPair.InIndex = numInStreams + coderStreamsInfo.NumInStreams;
351         bindPair.OutIndex = numOutStreams;
352         _bindInfo.BindPairs.Add(bindPair);
353       }
354       else if (coderStreamsInfo.NumOutStreams != 0)
355         _bindInfo.OutStreams.Insert(0, numOutStreams);
356       for (UInt32 j = 1; j < coderStreamsInfo.NumOutStreams; j++)
357         _bindInfo.OutStreams.Add(numOutStreams + j);
358     }
359 
360     numInStreams += coderStreamsInfo.NumInStreams;
361     numOutStreams += coderStreamsInfo.NumOutStreams;
362 
363     _bindInfo.Coders.Add(coderStreamsInfo);
364   }
365 
366   if (!_options.Binds.IsEmpty())
367   {
368     for (i = 0; i < _options.Binds.Size(); i++)
369     {
370       NCoderMixer::CBindPair bindPair;
371       const CBind &bind = _options.Binds[i];
372       bindPair.InIndex = _bindInfo.GetCoderInStreamIndex(bind.InCoder) + bind.InStream;
373       bindPair.OutIndex = _bindInfo.GetCoderOutStreamIndex(bind.OutCoder) + bind.OutStream;
374       _bindInfo.BindPairs.Add(bindPair);
375     }
376     for (i = 0; i < (int)numOutStreams; i++)
377       if (_bindInfo.FindBinderForOutStream(i) == -1)
378         _bindInfo.OutStreams.Add(i);
379   }
380 
381   for (i = 0; i < (int)numInStreams; i++)
382     if (_bindInfo.FindBinderForInStream(i) == -1)
383       _bindInfo.InStreams.Add(i);
384 
385   if (_bindInfo.InStreams.IsEmpty())
386     throw 1; // this is error
387 
388   // Make main stream first in list
389   int inIndex = _bindInfo.InStreams[0];
390   for (;;)
391   {
392     UInt32 coderIndex, coderStreamIndex;
393     _bindInfo.FindInStream(inIndex, coderIndex, coderStreamIndex);
394     UInt32 outIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex);
395     int binder = _bindInfo.FindBinderForOutStream(outIndex);
396     if (binder >= 0)
397     {
398       inIndex = _bindInfo.BindPairs[binder].InIndex;
399       continue;
400     }
401     for (i = 0; i < _bindInfo.OutStreams.Size(); i++)
402       if (_bindInfo.OutStreams[i] == outIndex)
403       {
404         _bindInfo.OutStreams.Delete(i);
405         _bindInfo.OutStreams.Insert(0, outIndex);
406         break;
407       }
408     break;
409   }
410 
411   if (_options.PasswordIsDefined)
412   {
413     unsigned numCryptoStreams = _bindInfo.OutStreams.Size();
414 
415     for (i = 0; i < numCryptoStreams; i++)
416     {
417       NCoderMixer::CBindPair bindPair;
418       bindPair.InIndex = numInStreams + i;
419       bindPair.OutIndex = _bindInfo.OutStreams[i];
420       _bindInfo.BindPairs.Add(bindPair);
421     }
422     _bindInfo.OutStreams.Clear();
423 
424     /*
425     if (numCryptoStreams == 0)
426       numCryptoStreams = 1;
427     */
428 
429     for (i = 0; i < numCryptoStreams; i++)
430     {
431       NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
432       CMethodFull method;
433       method.NumInStreams = 1;
434       method.NumOutStreams = 1;
435       coderStreamsInfo.NumInStreams = method.NumOutStreams;
436       coderStreamsInfo.NumOutStreams = method.NumInStreams;
437       method.Id = k_AES;
438 
439       _options.Methods.Add(method);
440       _bindInfo.Coders.Add(coderStreamsInfo);
441       _bindInfo.OutStreams.Add(numOutStreams + i);
442     }
443   }
444 
445   }
446 
447   for (int i = _options.Methods.Size() - 1; i >= 0; i--)
448   {
449     const CMethodFull &methodFull = _options.Methods[i];
450     _decompressionMethods.Add(methodFull.Id);
451   }
452 
453   _bindReverseConverter = new NCoderMixer::CBindReverseConverter(_bindInfo);
454   _bindReverseConverter->CreateReverseBindInfo(_decompressBindInfo);
455   _constructed = true;
456   return S_OK;
457 }
458 
~CEncoder()459 CEncoder::~CEncoder()
460 {
461   delete _bindReverseConverter;
462 }
463 
464 }}
465