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