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
15static const UInt64 k_Delta = 0x03;
16static const UInt64 k_BCJ = 0x03030103;
17static const UInt64 k_BCJ2 = 0x0303011B;
18
19namespace NArchive {
20namespace N7z {
21
22static 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
52static 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
61HRESULT 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
141HRESULT 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
297CEncoder::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
308HRESULT 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
459CEncoder::~CEncoder()
460{
461  delete _bindReverseConverter;
462}
463
464}}
465