• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 using namespace NWindows;
19 
20 namespace NArchive {
21 namespace 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 
29 static const UInt32 k_NumFastBytes_ForHeaders = 273;
30 static const UInt32 k_Level_ForHeaders = 5;
31 static const UInt32 k_Dictionary_ForHeaders =
32   #ifdef UNDER_CE
33   1 << 18;
34   #else
35   1 << 20;
36   #endif
37 
Z7_COM7F_IMF(CHandler::GetFileTimeType (UInt32 * type))38 Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *type))
39 {
40   *type = NFileTimeType::kWindows;
41   return S_OK;
42 }
43 
PropsMethod_To_FullMethod(CMethodFull & dest,const COneMethodInfo & m)44 HRESULT 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 
SetHeaderMethod(CCompressionMethodMode & headerMethod)57 HRESULT 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 
SetMainMethod(CCompressionMethodMode & methodMode)74 HRESULT 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 
GetTime(IArchiveUpdateCallback * updateCallback,unsigned index,PROPID propID,UInt64 & ft,bool & ftDefined)300 static 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
324 static const wchar_t kDirDelimiter1 = L'\\';
325 #endif
326 static const wchar_t kDirDelimiter2 = L'/';
327 
328 static inline bool IsCharDirLimiter(wchar_t c)
329 {
330   return (
331     #ifdef _WIN32
332     c == kDirDelimiter1 ||
333     #endif
334     c == kDirDelimiter2);
335 }
336 
337 static 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 
347 static 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 
371 static 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 
Z7_COM7F_IMF(CHandler::UpdateItems (ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback))388 Z7_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 
ParseBond(UString & srcString,UInt32 & coder,UInt32 & stream)824 static 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 
InitProps7z()844 void 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 
InitProps()863 void COutHandler::InitProps()
864 {
865   CMultiMethodProps::Init();
866   InitProps7z();
867 }
868 
869 
870 
SetSolidFromString(const UString & s)871 HRESULT 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 
SetSolidFromPROPVARIANT(const PROPVARIANT & value)920 HRESULT 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 
PROPVARIANT_to_BoolPair(const PROPVARIANT & prop,CBoolPair & dest)940 static 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 
SetProperty(const wchar_t * nameSpec,const PROPVARIANT & value)947 HRESULT 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 
Z7_COM7F_IMF(CHandler::SetProperties (const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps))1012 Z7_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