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