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