1// 7zHandlerOut.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/ComTry.h"
6#include "../../../Common/StringToInt.h"
7#include "../../../Common/Wildcard.h"
8
9#include "../Common/ItemNameUtils.h"
10#include "../Common/ParseProperties.h"
11
12#include "7zHandler.h"
13#include "7zOut.h"
14#include "7zUpdate.h"
15
16#ifndef Z7_EXTRACT_ONLY
17
18using namespace NWindows;
19
20namespace NArchive {
21namespace N7z {
22
23#define k_LZMA_Name "LZMA"
24#define kDefaultMethodName "LZMA2"
25#define k_Copy_Name "Copy"
26
27#define k_MatchFinder_ForHeaders "BT2"
28
29static const UInt32 k_NumFastBytes_ForHeaders = 273;
30static const UInt32 k_Level_ForHeaders = 5;
31static const UInt32 k_Dictionary_ForHeaders =
32  #ifdef UNDER_CE
33  1 << 18;
34  #else
35  1 << 20;
36  #endif
37
38Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *type))
39{
40  *type = NFileTimeType::kWindows;
41  return S_OK;
42}
43
44HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m)
45{
46  bool isFilter;
47  dest.CodecIndex = FindMethod_Index(
48      EXTERNAL_CODECS_VARS
49      m.MethodName, true,
50      dest.Id, dest.NumStreams, isFilter);
51  if (dest.CodecIndex < 0)
52    return E_INVALIDARG;
53  (CProps &)dest = (CProps &)m;
54  return S_OK;
55}
56
57HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod)
58{
59  if (!_compressHeaders)
60    return S_OK;
61  COneMethodInfo m;
62  m.MethodName = k_LZMA_Name;
63  m.AddProp_Ascii(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders);
64  m.AddProp_Level(k_Level_ForHeaders);
65  m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders);
66  m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders);
67  m.AddProp_NumThreads(1);
68
69  CMethodFull &methodFull = headerMethod.Methods.AddNew();
70  return PropsMethod_To_FullMethod(methodFull, m);
71}
72
73
74HRESULT CHandler::SetMainMethod(CCompressionMethodMode &methodMode)
75{
76  methodMode.Bonds = _bonds;
77
78  // we create local copy of _methods. So we can modify it.
79  CObjectVector<COneMethodInfo> methods = _methods;
80
81  {
82    FOR_VECTOR (i, methods)
83    {
84      AString &methodName = methods[i].MethodName;
85      if (methodName.IsEmpty())
86        methodName = kDefaultMethodName;
87    }
88    if (methods.IsEmpty())
89    {
90      COneMethodInfo &m = methods.AddNew();
91      m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName);
92      methodMode.DefaultMethod_was_Inserted = true;
93    }
94  }
95
96  if (!_filterMethod.MethodName.IsEmpty())
97  {
98    // if (methodMode.Bonds.IsEmpty())
99    {
100      FOR_VECTOR (k, methodMode.Bonds)
101      {
102        CBond2 &bond = methodMode.Bonds[k];
103        bond.InCoder++;
104        bond.OutCoder++;
105      }
106      methods.Insert(0, _filterMethod);
107      methodMode.Filter_was_Inserted = true;
108    }
109  }
110
111  const UInt64 kSolidBytes_Min = (1 << 24);
112  const UInt64 kSolidBytes_Max = ((UInt64)1 << 32);
113
114  bool needSolid = false;
115
116  FOR_VECTOR (i, methods)
117  {
118    COneMethodInfo &oneMethodInfo = methods[i];
119
120    SetGlobalLevelTo(oneMethodInfo);
121
122    #ifndef Z7_ST
123    const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0);
124    if (!numThreads_WasSpecifiedInMethod)
125    {
126      // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already
127      CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, methodMode.NumThreads);
128    }
129    #endif
130
131    CMethodFull &methodFull = methodMode.Methods.AddNew();
132    RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo))
133
134    #ifndef Z7_ST
135    methodFull.Set_NumThreads = true;
136    methodFull.NumThreads = methodMode.NumThreads;
137    #endif
138
139    if (methodFull.Id != k_Copy)
140      needSolid = true;
141
142    UInt64 dicSize;
143    switch (methodFull.Id)
144    {
145      case k_LZMA:
146      case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break;
147      case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break;
148      case k_Deflate: dicSize = (UInt32)1 << 15; break;
149      case k_Deflate64: dicSize = (UInt32)1 << 16; break;
150      case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break;
151      // case k_ZSTD: dicSize = 1 << 23; break;
152      default: continue;
153    }
154
155    UInt64 numSolidBytes;
156
157    /*
158    if (methodFull.Id == k_ZSTD)
159    {
160      // continue;
161      NCompress::NZstd::CEncoderProps encoderProps;
162      RINOK(oneMethodInfo.Set_PropsTo_zstd(encoderProps));
163      CZstdEncProps &zstdProps = encoderProps.EncProps;
164      ZstdEncProps_NormalizeFull(&zstdProps);
165      UInt64 cs = (UInt64)(zstdProps.jobSize);
166      UInt32 winSize = (UInt32)(1 << zstdProps.windowLog);
167      if (cs < winSize)
168        cs = winSize;
169      numSolidBytes = cs << 6;
170      const UInt64 kSolidBytes_Zstd_Max = ((UInt64)1 << 34);
171      if (numSolidBytes > kSolidBytes_Zstd_Max)
172        numSolidBytes = kSolidBytes_Zstd_Max;
173
174      methodFull.Set_NumThreads = false; // we don't use ICompressSetCoderMt::SetNumberOfThreads() for LZMA2 encoder
175
176      #ifndef Z7_ST
177      if (!numThreads_WasSpecifiedInMethod
178          && !methodMode.NumThreads_WasForced
179          && methodMode.MemoryUsageLimit_WasSet
180          )
181      {
182        const UInt32 numThreads_Original = methodMode.NumThreads;
183        const UInt32 numThreads_New = ZstdEncProps_GetNumThreads_for_MemUsageLimit(
184            &zstdProps,
185            methodMode.MemoryUsageLimit,
186            numThreads_Original);
187        if (numThreads_Original != numThreads_New)
188        {
189          CMultiMethodProps::SetMethodThreadsTo_Replace(methodFull, numThreads_New);
190        }
191      }
192      #endif
193    }
194    else
195    */
196    if (methodFull.Id == k_LZMA2)
197    {
198      // he we calculate default chunk Size for LZMA2 as defined in LZMA2 encoder code
199      /* lzma2 code use dictionary up to fake 4 GiB to calculate ChunkSize.
200         So we do same */
201      UInt64 cs = (UInt64)dicSize << 2;
202      const UInt32 kMinSize = (UInt32)1 << 20;
203      const UInt32 kMaxSize = (UInt32)1 << 28;
204      if (cs < kMinSize) cs = kMinSize;
205      if (cs > kMaxSize) cs = kMaxSize;
206      if (cs < dicSize) cs = dicSize;
207      cs += (kMinSize - 1);
208      cs &= ~(UInt64)(kMinSize - 1);
209      // we want to use at least 64 chunks (threads) per one solid block.
210
211      // here we don't use chunkSize property
212      numSolidBytes = cs << 6;
213
214      // here we get real chunkSize
215      cs = oneMethodInfo.Get_Xz_BlockSize();
216      if (dicSize > cs)
217        dicSize = cs;
218
219      const UInt64 kSolidBytes_Lzma2_Max = ((UInt64)1 << 34);
220      if (numSolidBytes > kSolidBytes_Lzma2_Max)
221        numSolidBytes = kSolidBytes_Lzma2_Max;
222
223      methodFull.Set_NumThreads = false; // we don't use ICompressSetCoderMt::SetNumberOfThreads() for LZMA2 encoder
224
225      #ifndef Z7_ST
226      if (!numThreads_WasSpecifiedInMethod
227          && !methodMode.NumThreads_WasForced
228          && methodMode.MemoryUsageLimit_WasSet
229          )
230      {
231        const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads();
232        const UInt32 numBlockThreads_Original = methodMode.NumThreads / lzmaThreads;
233
234        if (numBlockThreads_Original > 1)
235        {
236          /*
237            const UInt32 kNumThreads_Max = 1024;
238            if (numBlockThreads > kNumMaxThreads)
239            numBlockThreads = kNumMaxThreads;
240          */
241
242          UInt32 numBlockThreads = numBlockThreads_Original;
243          const UInt64 lzmaMemUsage = oneMethodInfo.Get_Lzma_MemUsage(false); // solid
244
245          for (; numBlockThreads > 1; numBlockThreads--)
246          {
247            UInt64 size = numBlockThreads * (lzmaMemUsage + cs);
248            UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1;
249            if (cs < ((UInt32)1 << 26)) numPackChunks++;
250            if (cs < ((UInt32)1 << 24)) numPackChunks++;
251            if (cs < ((UInt32)1 << 22)) numPackChunks++;
252            size += numPackChunks * cs;
253            // printf("\nnumBlockThreads = %d, size = %d\n", (unsigned)(numBlockThreads), (unsigned)(size >> 20));
254            if (size <= methodMode.MemoryUsageLimit)
255              break;
256          }
257
258          if (numBlockThreads == 0)
259            numBlockThreads = 1;
260          if (numBlockThreads != numBlockThreads_Original)
261          {
262            const UInt32 numThreads_New = numBlockThreads * lzmaThreads;
263            CMultiMethodProps::SetMethodThreadsTo_Replace(methodFull, numThreads_New);
264          }
265        }
266      }
267      #endif
268    }
269    else
270    {
271      numSolidBytes = (UInt64)dicSize << 7;
272      if (numSolidBytes > kSolidBytes_Max)
273        numSolidBytes = kSolidBytes_Max;
274    }
275
276    if (_numSolidBytesDefined)
277      continue;
278
279    if (numSolidBytes < kSolidBytes_Min)
280      numSolidBytes = kSolidBytes_Min;
281    _numSolidBytes = numSolidBytes;
282    _numSolidBytesDefined = true;
283  }
284
285  if (!_numSolidBytesDefined)
286  {
287    if (needSolid)
288      _numSolidBytes = kSolidBytes_Max;
289    else
290      _numSolidBytes = 0;
291  }
292  _numSolidBytesDefined = true;
293
294
295  return S_OK;
296}
297
298
299
300static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, unsigned index, PROPID propID, UInt64 &ft, bool &ftDefined)
301{
302  // ft = 0;
303  // ftDefined = false;
304  NCOM::CPropVariant prop;
305  RINOK(updateCallback->GetProperty(index, propID, &prop))
306  if (prop.vt == VT_FILETIME)
307  {
308    ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
309    ftDefined = true;
310  }
311  else if (prop.vt != VT_EMPTY)
312    return E_INVALIDARG;
313  else
314  {
315    ft = 0;
316    ftDefined = false;
317  }
318  return S_OK;
319}
320
321/*
322
323#ifdef _WIN32
324static const wchar_t kDirDelimiter1 = L'\\';
325#endif
326static const wchar_t kDirDelimiter2 = L'/';
327
328static inline bool IsCharDirLimiter(wchar_t c)
329{
330  return (
331    #ifdef _WIN32
332    c == kDirDelimiter1 ||
333    #endif
334    c == kDirDelimiter2);
335}
336
337static int FillSortIndex(CObjectVector<CTreeFolder> &treeFolders, int cur, int curSortIndex)
338{
339  CTreeFolder &tf = treeFolders[cur];
340  tf.SortIndex = curSortIndex++;
341  for (int i = 0; i < tf.SubFolders.Size(); i++)
342    curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex);
343  tf.SortIndexEnd = curSortIndex;
344  return curSortIndex;
345}
346
347static int FindSubFolder(const CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name, int &insertPos)
348{
349  const CIntVector &subFolders = treeFolders[cur].SubFolders;
350  int left = 0, right = subFolders.Size();
351  insertPos = -1;
352  for (;;)
353  {
354    if (left == right)
355    {
356      insertPos = left;
357      return -1;
358    }
359    int mid = (left + right) / 2;
360    int midFolder = subFolders[mid];
361    int compare = CompareFileNames(name, treeFolders[midFolder].Name);
362    if (compare == 0)
363      return midFolder;
364    if (compare < 0)
365      right = mid;
366    else
367      left = mid + 1;
368  }
369}
370
371static int AddFolder(CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name)
372{
373  int insertPos;
374  int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos);
375  if (folderIndex < 0)
376  {
377    folderIndex = treeFolders.Size();
378    CTreeFolder &newFolder = treeFolders.AddNew();
379    newFolder.Parent = cur;
380    newFolder.Name = name;
381    treeFolders[cur].SubFolders.Insert(insertPos, folderIndex);
382  }
383  // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234;
384  return folderIndex;
385}
386*/
387
388Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
389    IArchiveUpdateCallback *updateCallback))
390{
391  COM_TRY_BEGIN
392
393  const CDbEx *db = NULL;
394  #ifdef Z7_7Z_VOL
395  if (_volumes.Size() > 1)
396    return E_FAIL;
397  const CVolume *volume = 0;
398  if (_volumes.Size() == 1)
399  {
400    volume = &_volumes.Front();
401    db = &volume->Database;
402  }
403  #else
404  if (_inStream)
405    db = &_db;
406  #endif
407
408  if (db && !db->CanUpdate())
409    return E_NOTIMPL;
410
411  /*
412  Z7_DECL_CMyComPtr_QI_FROM(
413      IArchiveGetRawProps,
414      getRawProps, updateCallback)
415
416  CUniqBlocks secureBlocks;
417  secureBlocks.AddUniq(NULL, 0);
418
419  CObjectVector<CTreeFolder> treeFolders;
420  {
421    CTreeFolder folder;
422    folder.Parent = -1;
423    treeFolders.Add(folder);
424  }
425  */
426
427  CObjectVector<CUpdateItem> updateItems;
428
429  bool need_CTime = (TimeOptions.Write_CTime.Def && TimeOptions.Write_CTime.Val);
430  bool need_ATime = (TimeOptions.Write_ATime.Def && TimeOptions.Write_ATime.Val);
431  bool need_MTime = (TimeOptions.Write_MTime.Def ? TimeOptions.Write_MTime.Val : true);
432  bool need_Attrib = (Write_Attrib.Def ? Write_Attrib.Val : true);
433
434  if (db && !db->Files.IsEmpty())
435  {
436    if (!TimeOptions.Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty();
437    if (!TimeOptions.Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty();
438    if (!TimeOptions.Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty();
439    if (!Write_Attrib.Def) need_Attrib = !db->Attrib.Defs.IsEmpty();
440  }
441
442  // UString s;
443  UString name;
444
445  for (UInt32 i = 0; i < numItems; i++)
446  {
447    Int32 newData, newProps;
448    UInt32 indexInArchive;
449    if (!updateCallback)
450      return E_FAIL;
451    RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive))
452    CUpdateItem ui;
453    ui.NewProps = IntToBool(newProps);
454    ui.NewData = IntToBool(newData);
455    ui.IndexInArchive = (int)indexInArchive;
456    ui.IndexInClient = i;
457    ui.IsAnti = false;
458    ui.Size = 0;
459
460    name.Empty();
461    // bool isAltStream = false;
462    if (ui.IndexInArchive != -1)
463    {
464      if (!db || (unsigned)ui.IndexInArchive >= db->Files.Size())
465        return E_INVALIDARG;
466      const CFileItem &fi = db->Files[(unsigned)ui.IndexInArchive];
467      if (!ui.NewProps)
468      {
469        _db.GetPath((unsigned)ui.IndexInArchive, name);
470      }
471      ui.IsDir = fi.IsDir;
472      ui.Size = fi.Size;
473      // isAltStream = fi.IsAltStream;
474      ui.IsAnti = db->IsItemAnti((unsigned)ui.IndexInArchive);
475
476      if (!ui.NewProps)
477      {
478        ui.CTimeDefined = db->CTime.GetItem((unsigned)ui.IndexInArchive, ui.CTime);
479        ui.ATimeDefined = db->ATime.GetItem((unsigned)ui.IndexInArchive, ui.ATime);
480        ui.MTimeDefined = db->MTime.GetItem((unsigned)ui.IndexInArchive, ui.MTime);
481      }
482    }
483
484    if (ui.NewProps)
485    {
486      bool folderStatusIsDefined;
487      if (need_Attrib)
488      {
489        NCOM::CPropVariant prop;
490        RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop))
491        if (prop.vt == VT_EMPTY)
492          ui.AttribDefined = false;
493        else if (prop.vt != VT_UI4)
494          return E_INVALIDARG;
495        else
496        {
497          ui.Attrib = prop.ulVal;
498          ui.AttribDefined = true;
499        }
500      }
501
502      // we need MTime to sort files.
503      if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined))
504      if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined))
505      if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined))
506
507      /*
508      if (getRawProps)
509      {
510        const void *data;
511        UInt32 dataSize;
512        UInt32 propType;
513
514        getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType);
515        if (dataSize != 0 && propType != NPropDataType::kRaw)
516          return E_FAIL;
517        ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize);
518      }
519      */
520
521      {
522        NCOM::CPropVariant prop;
523        RINOK(updateCallback->GetProperty(i, kpidPath, &prop))
524        if (prop.vt == VT_EMPTY)
525        {
526        }
527        else if (prop.vt != VT_BSTR)
528          return E_INVALIDARG;
529        else
530        {
531          name = prop.bstrVal;
532          NItemName::ReplaceSlashes_OsToUnix(name);
533        }
534      }
535      {
536        NCOM::CPropVariant prop;
537        RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop))
538        if (prop.vt == VT_EMPTY)
539          folderStatusIsDefined = false;
540        else if (prop.vt != VT_BOOL)
541          return E_INVALIDARG;
542        else
543        {
544          ui.IsDir = (prop.boolVal != VARIANT_FALSE);
545          folderStatusIsDefined = true;
546        }
547      }
548
549      {
550        NCOM::CPropVariant prop;
551        RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop))
552        if (prop.vt == VT_EMPTY)
553          ui.IsAnti = false;
554        else if (prop.vt != VT_BOOL)
555          return E_INVALIDARG;
556        else
557          ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
558      }
559
560      /*
561      {
562        NCOM::CPropVariant prop;
563        RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop));
564        if (prop.vt == VT_EMPTY)
565          isAltStream = false;
566        else if (prop.vt != VT_BOOL)
567          return E_INVALIDARG;
568        else
569          isAltStream = (prop.boolVal != VARIANT_FALSE);
570      }
571      */
572
573      if (ui.IsAnti)
574      {
575        ui.AttribDefined = false;
576
577        ui.CTimeDefined = false;
578        ui.ATimeDefined = false;
579        ui.MTimeDefined = false;
580
581        ui.Size = 0;
582      }
583
584      if (!folderStatusIsDefined && ui.AttribDefined)
585        ui.SetDirStatusFromAttrib();
586    }
587    else
588    {
589      /*
590      if (_db.SecureIDs.IsEmpty())
591        ui.SecureIndex = secureBlocks.AddUniq(NULL, 0);
592      else
593      {
594        int id = _db.SecureIDs[ui.IndexInArchive];
595        size_t offs = _db.SecureOffsets[id];
596        size_t size = _db.SecureOffsets[id + 1] - offs;
597        ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size);
598      }
599      */
600    }
601
602    /*
603    {
604      int folderIndex = 0;
605      if (_useParents)
606      {
607        int j;
608        s.Empty();
609        for (j = 0; j < name.Len(); j++)
610        {
611          wchar_t c = name[j];
612          if (IsCharDirLimiter(c))
613          {
614            folderIndex = AddFolder(treeFolders, folderIndex, s);
615            s.Empty();
616            continue;
617          }
618          s += c;
619        }
620        if (isAltStream)
621        {
622          int colonPos = s.Find(':');
623          if (colonPos < 0)
624          {
625            // isAltStream = false;
626            return E_INVALIDARG;
627          }
628          UString mainName = s.Left(colonPos);
629          int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName);
630          if (treeFolders[newFolderIndex].UpdateItemIndex < 0)
631          {
632            for (int j = updateItems.Size() - 1; j >= 0; j--)
633            {
634              CUpdateItem &ui2 = updateItems[j];
635              if (ui2.ParentFolderIndex == folderIndex
636                  && ui2.Name == mainName)
637              {
638                ui2.TreeFolderIndex = newFolderIndex;
639                treeFolders[newFolderIndex].UpdateItemIndex = j;
640              }
641            }
642          }
643          folderIndex = newFolderIndex;
644          s.Delete(0, colonPos + 1);
645        }
646        ui.Name = s;
647      }
648      else
649        ui.Name = name;
650      ui.IsAltStream = isAltStream;
651      ui.ParentFolderIndex = folderIndex;
652      ui.TreeFolderIndex = -1;
653      if (ui.IsDir && !s.IsEmpty())
654      {
655        ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s);
656        treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size();
657      }
658    }
659    */
660    ui.Name = name;
661
662    if (ui.NewData)
663    {
664      ui.Size = 0;
665      if (!ui.IsDir)
666      {
667        NCOM::CPropVariant prop;
668        RINOK(updateCallback->GetProperty(i, kpidSize, &prop))
669        if (prop.vt != VT_UI8)
670          return E_INVALIDARG;
671        ui.Size = (UInt64)prop.uhVal.QuadPart;
672        if (ui.Size != 0 && ui.IsAnti)
673          return E_INVALIDARG;
674      }
675    }
676
677    updateItems.Add(ui);
678  }
679
680  /*
681  FillSortIndex(treeFolders, 0, 0);
682  for (i = 0; i < (UInt32)updateItems.Size(); i++)
683  {
684    CUpdateItem &ui = updateItems[i];
685    ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex;
686    ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd;
687  }
688  */
689
690  CCompressionMethodMode methodMode, headerMethod;
691
692  methodMode.MemoryUsageLimit = _memUsage_Compress;
693  methodMode.MemoryUsageLimit_WasSet = _memUsage_WasSet;
694
695  #ifndef Z7_ST
696  {
697    UInt32 numThreads = _numThreads;
698    const UInt32 kNumThreads_Max = 1024;
699    if (numThreads > kNumThreads_Max)
700      numThreads = kNumThreads_Max;
701    methodMode.NumThreads = numThreads;
702    methodMode.NumThreads_WasForced = _numThreads_WasForced;
703    methodMode.MultiThreadMixer = _useMultiThreadMixer;
704    // headerMethod.NumThreads = 1;
705    headerMethod.MultiThreadMixer = _useMultiThreadMixer;
706  }
707  #endif
708
709  const HRESULT res = SetMainMethod(methodMode);
710  RINOK(res)
711
712  RINOK(SetHeaderMethod(headerMethod))
713
714  Z7_DECL_CMyComPtr_QI_FROM(
715    ICryptoGetTextPassword2,
716    getPassword2, updateCallback)
717
718  methodMode.PasswordIsDefined = false;
719  methodMode.Password.Wipe_and_Empty();
720  if (getPassword2)
721  {
722    CMyComBSTR_Wipe password;
723    Int32 passwordIsDefined;
724    RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password))
725    methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
726    if (methodMode.PasswordIsDefined && password)
727      methodMode.Password = password;
728  }
729
730  bool compressMainHeader = _compressHeaders;  // check it
731
732  bool encryptHeaders = false;
733
734  #ifndef Z7_NO_CRYPTO
735  if (!methodMode.PasswordIsDefined && _passwordIsDefined)
736  {
737    // if header is compressed, we use that password for updated archive
738    methodMode.PasswordIsDefined = true;
739    methodMode.Password = _password;
740  }
741  #endif
742
743  if (methodMode.PasswordIsDefined)
744  {
745    if (_encryptHeadersSpecified)
746      encryptHeaders = _encryptHeaders;
747    #ifndef Z7_NO_CRYPTO
748    else
749      encryptHeaders = _passwordIsDefined;
750    #endif
751    compressMainHeader = true;
752    if (encryptHeaders)
753    {
754      headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
755      headerMethod.Password = methodMode.Password;
756    }
757  }
758
759  if (numItems < 2)
760    compressMainHeader = false;
761
762  const int level = GetLevel();
763
764  CUpdateOptions options;
765  options.Need_CTime = need_CTime;
766  options.Need_ATime = need_ATime;
767  options.Need_MTime = need_MTime;
768  options.Need_Attrib = need_Attrib;
769  // options.Need_Crc = (_crcSize != 0); // for debug
770
771  options.Method = &methodMode;
772  options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : NULL;
773  options.UseFilters = (level != 0 && _autoFilter && !methodMode.Filter_was_Inserted);
774  options.MaxFilter = (level >= 8);
775  options.AnalysisLevel = GetAnalysisLevel();
776
777  options.HeaderOptions.CompressMainHeader = compressMainHeader;
778  /*
779  options.HeaderOptions.WriteCTime = Write_CTime;
780  options.HeaderOptions.WriteATime = Write_ATime;
781  options.HeaderOptions.WriteMTime = Write_MTime;
782  options.HeaderOptions.WriteAttrib = Write_Attrib;
783  */
784
785  options.NumSolidFiles = _numSolidFiles;
786  options.NumSolidBytes = _numSolidBytes;
787  options.SolidExtension = _solidExtension;
788  options.UseTypeSorting = _useTypeSorting;
789
790  options.RemoveSfxBlock = _removeSfxBlock;
791  // options.VolumeMode = _volumeMode;
792
793  options.MultiThreadMixer = _useMultiThreadMixer;
794
795  /*
796  if (secureBlocks.Sorted.Size() > 1)
797  {
798    secureBlocks.GetReverseMap();
799    for (int i = 0; i < updateItems.Size(); i++)
800    {
801      int &secureIndex = updateItems[i].SecureIndex;
802      secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex];
803    }
804  }
805  */
806
807  return Update(
808      EXTERNAL_CODECS_VARS
809      #ifdef Z7_7Z_VOL
810      volume ? volume->Stream: 0,
811      volume ? db : 0,
812      #else
813      _inStream,
814      db,
815      #endif
816      updateItems,
817      // treeFolders,
818      // secureBlocks,
819      outStream, updateCallback, options);
820
821  COM_TRY_END
822}
823
824static HRESULT ParseBond(UString &srcString, UInt32 &coder, UInt32 &stream)
825{
826  stream = 0;
827  {
828    const unsigned index = ParseStringToUInt32(srcString, coder);
829    if (index == 0)
830      return E_INVALIDARG;
831    srcString.DeleteFrontal(index);
832  }
833  if (srcString[0] == 's')
834  {
835    srcString.Delete(0);
836    const unsigned index = ParseStringToUInt32(srcString, stream);
837    if (index == 0)
838      return E_INVALIDARG;
839    srcString.DeleteFrontal(index);
840  }
841  return S_OK;
842}
843
844void COutHandler::InitProps7z()
845{
846  _removeSfxBlock = false;
847  _compressHeaders = true;
848  _encryptHeadersSpecified = false;
849  _encryptHeaders = false;
850  // _useParents = false;
851
852  TimeOptions.Init();
853  Write_Attrib.Init();
854
855  _useMultiThreadMixer = true;
856
857  // _volumeMode = false;
858
859  InitSolid();
860  _useTypeSorting = false;
861}
862
863void COutHandler::InitProps()
864{
865  CMultiMethodProps::Init();
866  InitProps7z();
867}
868
869
870
871HRESULT COutHandler::SetSolidFromString(const UString &s)
872{
873  UString s2 = s;
874  s2.MakeLower_Ascii();
875  for (unsigned i = 0; i < s2.Len();)
876  {
877    const wchar_t *start = ((const wchar_t *)s2) + i;
878    const wchar_t *end;
879    UInt64 v = ConvertStringToUInt64(start, &end);
880    if (start == end)
881    {
882      if (s2[i++] != 'e')
883        return E_INVALIDARG;
884      _solidExtension = true;
885      continue;
886    }
887    i += (unsigned)(end - start);
888    if (i == s2.Len())
889      return E_INVALIDARG;
890    const wchar_t c = s2[i++];
891    if (c == 'f')
892    {
893      if (v < 1)
894        v = 1;
895      _numSolidFiles = v;
896    }
897    else
898    {
899      unsigned numBits;
900      switch (c)
901      {
902        case 'b': numBits =  0; break;
903        case 'k': numBits = 10; break;
904        case 'm': numBits = 20; break;
905        case 'g': numBits = 30; break;
906        case 't': numBits = 40; break;
907        default: return E_INVALIDARG;
908      }
909      _numSolidBytes = (v << numBits);
910      _numSolidBytesDefined = true;
911      /*
912      if (_numSolidBytes == 0)
913        _numSolidFiles = 1;
914      */
915    }
916  }
917  return S_OK;
918}
919
920HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value)
921{
922  bool isSolid;
923  switch (value.vt)
924  {
925    case VT_EMPTY: isSolid = true; break;
926    case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
927    case VT_BSTR:
928      if (StringToBool(value.bstrVal, isSolid))
929        break;
930      return SetSolidFromString(value.bstrVal);
931    default: return E_INVALIDARG;
932  }
933  if (isSolid)
934    InitSolid();
935  else
936    _numSolidFiles = 1;
937  return S_OK;
938}
939
940static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest)
941{
942  RINOK(PROPVARIANT_to_bool(prop, dest.Val))
943  dest.Def = true;
944  return S_OK;
945}
946
947HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
948{
949  UString name = nameSpec;
950  name.MakeLower_Ascii();
951  if (name.IsEmpty())
952    return E_INVALIDARG;
953
954  if (name[0] == L's')
955  {
956    name.Delete(0);
957    if (name.IsEmpty())
958      return SetSolidFromPROPVARIANT(value);
959    if (value.vt != VT_EMPTY)
960      return E_INVALIDARG;
961    return SetSolidFromString(name);
962  }
963
964  UInt32 number;
965  const unsigned index = ParseStringToUInt32(name, number);
966  // UString realName = name.Ptr(index);
967  if (index == 0)
968  {
969    if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock);
970    if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders);
971    // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents);
972
973    if (name.IsEqualTo("hcf"))
974    {
975      bool compressHeadersFull = true;
976      RINOK(PROPVARIANT_to_bool(value, compressHeadersFull))
977      return compressHeadersFull ? S_OK: E_INVALIDARG;
978    }
979
980    if (name.IsEqualTo("he"))
981    {
982      RINOK(PROPVARIANT_to_bool(value, _encryptHeaders))
983      _encryptHeadersSpecified = true;
984      return S_OK;
985    }
986
987    {
988      bool processed;
989      RINOK(TimeOptions.Parse(name, value, processed))
990      if (processed)
991      {
992        if (   TimeOptions.Prec != (UInt32)(Int32)-1
993            && TimeOptions.Prec != k_PropVar_TimePrec_0
994            && TimeOptions.Prec != k_PropVar_TimePrec_HighPrec
995            && TimeOptions.Prec != k_PropVar_TimePrec_100ns)
996          return E_INVALIDARG;
997        return S_OK;
998      }
999    }
1000
1001    if (name.IsEqualTo("tr")) return PROPVARIANT_to_BoolPair(value, Write_Attrib);
1002
1003    if (name.IsEqualTo("mtf")) return PROPVARIANT_to_bool(value, _useMultiThreadMixer);
1004
1005    if (name.IsEqualTo("qs")) return PROPVARIANT_to_bool(value, _useTypeSorting);
1006
1007    // if (name.IsEqualTo("v"))  return PROPVARIANT_to_bool(value, _volumeMode);
1008  }
1009  return CMultiMethodProps::SetProperty(name, value);
1010}
1011
1012Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
1013{
1014  COM_TRY_BEGIN
1015  _bonds.Clear();
1016  InitProps();
1017
1018  for (UInt32 i = 0; i < numProps; i++)
1019  {
1020    UString name = names[i];
1021    name.MakeLower_Ascii();
1022    if (name.IsEmpty())
1023      return E_INVALIDARG;
1024
1025    const PROPVARIANT &value = values[i];
1026
1027    if (name.Find(L':') >= 0) // 'b' was used as NCoderPropID::kBlockSize2 before v23
1028    if (name[0] == 'b')
1029    {
1030      if (value.vt != VT_EMPTY)
1031        return E_INVALIDARG;
1032      name.Delete(0);
1033
1034      CBond2 bond;
1035      RINOK(ParseBond(name, bond.OutCoder, bond.OutStream))
1036      if (name[0] != ':')
1037        return E_INVALIDARG;
1038      name.Delete(0);
1039      UInt32 inStream = 0;
1040      RINOK(ParseBond(name, bond.InCoder, inStream))
1041      if (inStream != 0)
1042        return E_INVALIDARG;
1043      if (!name.IsEmpty())
1044        return E_INVALIDARG;
1045      _bonds.Add(bond);
1046      continue;
1047    }
1048
1049    RINOK(SetProperty(name, value))
1050  }
1051
1052  unsigned numEmptyMethods = GetNumEmptyMethods();
1053  if (numEmptyMethods > 0)
1054  {
1055    unsigned k;
1056    for (k = 0; k < _bonds.Size(); k++)
1057    {
1058      const CBond2 &bond = _bonds[k];
1059      if (bond.InCoder < (UInt32)numEmptyMethods ||
1060          bond.OutCoder < (UInt32)numEmptyMethods)
1061        return E_INVALIDARG;
1062    }
1063    for (k = 0; k < _bonds.Size(); k++)
1064    {
1065      CBond2 &bond = _bonds[k];
1066      bond.InCoder -= (UInt32)numEmptyMethods;
1067      bond.OutCoder -= (UInt32)numEmptyMethods;
1068    }
1069    _methods.DeleteFrontal(numEmptyMethods);
1070  }
1071
1072  FOR_VECTOR (k, _bonds)
1073  {
1074    const CBond2 &bond = _bonds[k];
1075    if (bond.InCoder >= (UInt32)_methods.Size() ||
1076        bond.OutCoder >= (UInt32)_methods.Size())
1077      return E_INVALIDARG;
1078  }
1079
1080  return S_OK;
1081  COM_TRY_END
1082}
1083
1084}}
1085
1086#endif
1087