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 using namespace NWindows;
17
18 namespace NArchive {
19 namespace N7z {
20
21 static const char *k_LZMA_Name = "LZMA";
22 static const char *kDefaultMethodName = "LZMA2";
23 static const char *k_Copy_Name = "Copy";
24
25 static const char *k_MatchFinder_ForHeaders = "BT2";
26 static const UInt32 k_NumFastBytes_ForHeaders = 273;
27 static const UInt32 k_Level_ForHeaders = 5;
28 static const UInt32 k_Dictionary_ForHeaders =
29 #ifdef UNDER_CE
30 1 << 18;
31 #else
32 1 << 20;
33 #endif
34
GetFileTimeType(UInt32 * type)35 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
36 {
37 *type = NFileTimeType::kWindows;
38 return S_OK;
39 }
40
PropsMethod_To_FullMethod(CMethodFull & dest,const COneMethodInfo & m)41 HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m)
42 {
43 if (!FindMethod(
44 EXTERNAL_CODECS_VARS
45 m.MethodName, dest.Id, dest.NumStreams))
46 return E_INVALIDARG;
47 (CProps &)dest = (CProps &)m;
48 return S_OK;
49 }
50
SetHeaderMethod(CCompressionMethodMode & headerMethod)51 HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod)
52 {
53 if (!_compressHeaders)
54 return S_OK;
55 COneMethodInfo m;
56 m.MethodName = k_LZMA_Name;
57 m.AddProp_Ascii(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders);
58 m.AddProp_Level(k_Level_ForHeaders);
59 m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders);
60 m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders);
61 m.AddProp_NumThreads(1);
62
63 CMethodFull &methodFull = headerMethod.Methods.AddNew();
64 return PropsMethod_To_FullMethod(methodFull, m);
65 }
66
SetMainMethod(CCompressionMethodMode & methodMode,UInt32 numThreads)67 HRESULT CHandler::SetMainMethod(
68 CCompressionMethodMode &methodMode
69 #ifndef _7ZIP_ST
70 , UInt32 numThreads
71 #endif
72 )
73 {
74 methodMode.Bonds = _bonds;
75
76 CObjectVector<COneMethodInfo> methods = _methods;
77
78 {
79 FOR_VECTOR (i, methods)
80 {
81 AString &methodName = methods[i].MethodName;
82 if (methodName.IsEmpty())
83 methodName = kDefaultMethodName;
84 }
85 if (methods.IsEmpty())
86 {
87 COneMethodInfo &m = methods.AddNew();
88 m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName);
89 methodMode.DefaultMethod_was_Inserted = true;
90 }
91 }
92
93 if (!_filterMethod.MethodName.IsEmpty())
94 {
95 // if (methodMode.Bonds.IsEmpty())
96 {
97 FOR_VECTOR (k, methodMode.Bonds)
98 {
99 CBond2 &bond = methodMode.Bonds[k];
100 bond.InCoder++;
101 bond.OutCoder++;
102 }
103 methods.Insert(0, _filterMethod);
104 methodMode.Filter_was_Inserted = true;
105 }
106 }
107
108 const UInt64 kSolidBytes_Min = (1 << 24);
109 const UInt64 kSolidBytes_Max = ((UInt64)1 << 32) - 1;
110
111 bool needSolid = false;
112
113 FOR_VECTOR (i, methods)
114 {
115 COneMethodInfo &oneMethodInfo = methods[i];
116 SetGlobalLevelAndThreads(oneMethodInfo
117 #ifndef _7ZIP_ST
118 , numThreads
119 #endif
120 );
121
122 CMethodFull &methodFull = methodMode.Methods.AddNew();
123 RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo));
124
125 if (methodFull.Id != k_Copy)
126 needSolid = true;
127
128 if (_numSolidBytesDefined)
129 continue;
130
131 UInt32 dicSize;
132 switch (methodFull.Id)
133 {
134 case k_LZMA:
135 case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break;
136 case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break;
137 case k_Deflate: dicSize = (UInt32)1 << 15; break;
138 case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break;
139 default: continue;
140 }
141
142 _numSolidBytes = (UInt64)dicSize << 7;
143 if (_numSolidBytes < kSolidBytes_Min) _numSolidBytes = kSolidBytes_Min;
144 if (_numSolidBytes > kSolidBytes_Max) _numSolidBytes = kSolidBytes_Max;
145 _numSolidBytesDefined = true;
146 }
147
148 if (!_numSolidBytesDefined)
149 if (needSolid)
150 _numSolidBytes = kSolidBytes_Max;
151 else
152 _numSolidBytes = 0;
153 _numSolidBytesDefined = true;
154 return S_OK;
155 }
156
GetTime(IArchiveUpdateCallback * updateCallback,int index,PROPID propID,UInt64 & ft,bool & ftDefined)157 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, UInt64 &ft, bool &ftDefined)
158 {
159 // ft = 0;
160 // ftDefined = false;
161 NCOM::CPropVariant prop;
162 RINOK(updateCallback->GetProperty(index, propID, &prop));
163 if (prop.vt == VT_FILETIME)
164 {
165 ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
166 ftDefined = true;
167 }
168 else if (prop.vt != VT_EMPTY)
169 return E_INVALIDARG;
170 else
171 {
172 ft = 0;
173 ftDefined = false;
174 }
175 return S_OK;
176 }
177
178 /*
179
180 #ifdef _WIN32
181 static const wchar_t kDirDelimiter1 = L'\\';
182 #endif
183 static const wchar_t kDirDelimiter2 = L'/';
184
185 static inline bool IsCharDirLimiter(wchar_t c)
186 {
187 return (
188 #ifdef _WIN32
189 c == kDirDelimiter1 ||
190 #endif
191 c == kDirDelimiter2);
192 }
193
194 static int FillSortIndex(CObjectVector<CTreeFolder> &treeFolders, int cur, int curSortIndex)
195 {
196 CTreeFolder &tf = treeFolders[cur];
197 tf.SortIndex = curSortIndex++;
198 for (int i = 0; i < tf.SubFolders.Size(); i++)
199 curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex);
200 tf.SortIndexEnd = curSortIndex;
201 return curSortIndex;
202 }
203
204 static int FindSubFolder(const CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name, int &insertPos)
205 {
206 const CIntVector &subFolders = treeFolders[cur].SubFolders;
207 int left = 0, right = subFolders.Size();
208 insertPos = -1;
209 for (;;)
210 {
211 if (left == right)
212 {
213 insertPos = left;
214 return -1;
215 }
216 int mid = (left + right) / 2;
217 int midFolder = subFolders[mid];
218 int compare = CompareFileNames(name, treeFolders[midFolder].Name);
219 if (compare == 0)
220 return midFolder;
221 if (compare < 0)
222 right = mid;
223 else
224 left = mid + 1;
225 }
226 }
227
228 static int AddFolder(CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name)
229 {
230 int insertPos;
231 int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos);
232 if (folderIndex < 0)
233 {
234 folderIndex = treeFolders.Size();
235 CTreeFolder &newFolder = treeFolders.AddNew();
236 newFolder.Parent = cur;
237 newFolder.Name = name;
238 treeFolders[cur].SubFolders.Insert(insertPos, folderIndex);
239 }
240 // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234;
241 return folderIndex;
242 }
243 */
244
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)245 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
246 IArchiveUpdateCallback *updateCallback)
247 {
248 COM_TRY_BEGIN
249
250 const CDbEx *db = 0;
251 #ifdef _7Z_VOL
252 if (_volumes.Size() > 1)
253 return E_FAIL;
254 const CVolume *volume = 0;
255 if (_volumes.Size() == 1)
256 {
257 volume = &_volumes.Front();
258 db = &volume->Database;
259 }
260 #else
261 if (_inStream != 0)
262 db = &_db;
263 #endif
264
265 /*
266 CMyComPtr<IArchiveGetRawProps> getRawProps;
267 updateCallback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps);
268
269 CUniqBlocks secureBlocks;
270 secureBlocks.AddUniq(NULL, 0);
271
272 CObjectVector<CTreeFolder> treeFolders;
273 {
274 CTreeFolder folder;
275 folder.Parent = -1;
276 treeFolders.Add(folder);
277 }
278 */
279
280 CObjectVector<CUpdateItem> updateItems;
281
282 bool need_CTime = (Write_CTime.Def && Write_CTime.Val);
283 bool need_ATime = (Write_ATime.Def && Write_ATime.Val);
284 bool need_MTime = (Write_MTime.Def && Write_MTime.Val || !Write_MTime.Def);
285
286 if (db && !db->Files.IsEmpty())
287 {
288 if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty();
289 if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty();
290 if (!Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty();
291 }
292
293 UString s;
294
295 for (UInt32 i = 0; i < numItems; i++)
296 {
297 Int32 newData, newProps;
298 UInt32 indexInArchive;
299 if (!updateCallback)
300 return E_FAIL;
301 RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
302 CUpdateItem ui;
303 ui.NewProps = IntToBool(newProps);
304 ui.NewData = IntToBool(newData);
305 ui.IndexInArchive = indexInArchive;
306 ui.IndexInClient = i;
307 ui.IsAnti = false;
308 ui.Size = 0;
309
310 UString name;
311 // bool isAltStream = false;
312 if (ui.IndexInArchive != -1)
313 {
314 if (db == 0 || (unsigned)ui.IndexInArchive >= db->Files.Size())
315 return E_INVALIDARG;
316 const CFileItem &fi = db->Files[ui.IndexInArchive];
317 if (!ui.NewProps)
318 {
319 _db.GetPath(ui.IndexInArchive, name);
320 }
321 ui.IsDir = fi.IsDir;
322 ui.Size = fi.Size;
323 // isAltStream = fi.IsAltStream;
324 ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
325
326 if (!ui.NewProps)
327 {
328 ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
329 ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
330 ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
331 }
332 }
333
334 if (ui.NewProps)
335 {
336 bool folderStatusIsDefined;
337 {
338 NCOM::CPropVariant prop;
339 RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
340 if (prop.vt == VT_EMPTY)
341 ui.AttribDefined = false;
342 else if (prop.vt != VT_UI4)
343 return E_INVALIDARG;
344 else
345 {
346 ui.Attrib = prop.ulVal;
347 ui.AttribDefined = true;
348 }
349 }
350
351 // we need MTime to sort files.
352 if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined));
353 if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined));
354 if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined));
355
356 /*
357 if (getRawProps)
358 {
359 const void *data;
360 UInt32 dataSize;
361 UInt32 propType;
362
363 getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType);
364 if (dataSize != 0 && propType != NPropDataType::kRaw)
365 return E_FAIL;
366 ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize);
367 }
368 */
369
370 {
371 NCOM::CPropVariant prop;
372 RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
373 if (prop.vt == VT_EMPTY)
374 {
375 }
376 else if (prop.vt != VT_BSTR)
377 return E_INVALIDARG;
378 else
379 {
380 name = NItemName::MakeLegalName(prop.bstrVal);
381 }
382 }
383 {
384 NCOM::CPropVariant prop;
385 RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
386 if (prop.vt == VT_EMPTY)
387 folderStatusIsDefined = false;
388 else if (prop.vt != VT_BOOL)
389 return E_INVALIDARG;
390 else
391 {
392 ui.IsDir = (prop.boolVal != VARIANT_FALSE);
393 folderStatusIsDefined = true;
394 }
395 }
396
397 {
398 NCOM::CPropVariant prop;
399 RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
400 if (prop.vt == VT_EMPTY)
401 ui.IsAnti = false;
402 else if (prop.vt != VT_BOOL)
403 return E_INVALIDARG;
404 else
405 ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
406 }
407
408 /*
409 {
410 NCOM::CPropVariant prop;
411 RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop));
412 if (prop.vt == VT_EMPTY)
413 isAltStream = false;
414 else if (prop.vt != VT_BOOL)
415 return E_INVALIDARG;
416 else
417 isAltStream = (prop.boolVal != VARIANT_FALSE);
418 }
419 */
420
421 if (ui.IsAnti)
422 {
423 ui.AttribDefined = false;
424
425 ui.CTimeDefined = false;
426 ui.ATimeDefined = false;
427 ui.MTimeDefined = false;
428
429 ui.Size = 0;
430 }
431
432 if (!folderStatusIsDefined && ui.AttribDefined)
433 ui.SetDirStatusFromAttrib();
434 }
435 else
436 {
437 /*
438 if (_db.SecureIDs.IsEmpty())
439 ui.SecureIndex = secureBlocks.AddUniq(NULL, 0);
440 else
441 {
442 int id = _db.SecureIDs[ui.IndexInArchive];
443 size_t offs = _db.SecureOffsets[id];
444 size_t size = _db.SecureOffsets[id + 1] - offs;
445 ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size);
446 }
447 */
448 }
449
450 /*
451 {
452 int folderIndex = 0;
453 if (_useParents)
454 {
455 int j;
456 s.Empty();
457 for (j = 0; j < name.Len(); j++)
458 {
459 wchar_t c = name[j];
460 if (IsCharDirLimiter(c))
461 {
462 folderIndex = AddFolder(treeFolders, folderIndex, s);
463 s.Empty();
464 continue;
465 }
466 s += c;
467 }
468 if (isAltStream)
469 {
470 int colonPos = s.Find(':');
471 if (colonPos < 0)
472 {
473 // isAltStream = false;
474 return E_INVALIDARG;
475 }
476 UString mainName = s.Left(colonPos);
477 int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName);
478 if (treeFolders[newFolderIndex].UpdateItemIndex < 0)
479 {
480 for (int j = updateItems.Size() - 1; j >= 0; j--)
481 {
482 CUpdateItem &ui2 = updateItems[j];
483 if (ui2.ParentFolderIndex == folderIndex
484 && ui2.Name == mainName)
485 {
486 ui2.TreeFolderIndex = newFolderIndex;
487 treeFolders[newFolderIndex].UpdateItemIndex = j;
488 }
489 }
490 }
491 folderIndex = newFolderIndex;
492 s.Delete(0, colonPos + 1);
493 }
494 ui.Name = s;
495 }
496 else
497 ui.Name = name;
498 ui.IsAltStream = isAltStream;
499 ui.ParentFolderIndex = folderIndex;
500 ui.TreeFolderIndex = -1;
501 if (ui.IsDir && !s.IsEmpty())
502 {
503 ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s);
504 treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size();
505 }
506 }
507 */
508 ui.Name = name;
509
510 if (ui.NewData)
511 {
512 ui.Size = 0;
513 if (!ui.IsDir)
514 {
515 NCOM::CPropVariant prop;
516 RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
517 if (prop.vt != VT_UI8)
518 return E_INVALIDARG;
519 ui.Size = (UInt64)prop.uhVal.QuadPart;
520 if (ui.Size != 0 && ui.IsAnti)
521 return E_INVALIDARG;
522 }
523 }
524
525 updateItems.Add(ui);
526 }
527
528 /*
529 FillSortIndex(treeFolders, 0, 0);
530 for (i = 0; i < (UInt32)updateItems.Size(); i++)
531 {
532 CUpdateItem &ui = updateItems[i];
533 ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex;
534 ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd;
535 }
536 */
537
538 CCompressionMethodMode methodMode, headerMethod;
539
540 HRESULT res = SetMainMethod(methodMode
541 #ifndef _7ZIP_ST
542 , _numThreads
543 #endif
544 );
545 RINOK(res);
546
547 RINOK(SetHeaderMethod(headerMethod));
548
549 #ifndef _7ZIP_ST
550 methodMode.NumThreads = _numThreads;
551 methodMode.MultiThreadMixer = _useMultiThreadMixer;
552 headerMethod.NumThreads = 1;
553 headerMethod.MultiThreadMixer = _useMultiThreadMixer;
554 #endif
555
556 CMyComPtr<ICryptoGetTextPassword2> getPassword2;
557 updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);
558
559 methodMode.PasswordIsDefined = false;
560 methodMode.Password.Empty();
561 if (getPassword2)
562 {
563 CMyComBSTR password;
564 Int32 passwordIsDefined;
565 RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
566 methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
567 if (methodMode.PasswordIsDefined && password)
568 methodMode.Password = password;
569 }
570
571 bool compressMainHeader = _compressHeaders; // check it
572
573 bool encryptHeaders = false;
574
575 #ifndef _NO_CRYPTO
576 if (!methodMode.PasswordIsDefined && _passwordIsDefined)
577 {
578 // if header is compressed, we use that password for updated archive
579 methodMode.PasswordIsDefined = true;
580 methodMode.Password = _password;
581 }
582 #endif
583
584 if (methodMode.PasswordIsDefined)
585 {
586 if (_encryptHeadersSpecified)
587 encryptHeaders = _encryptHeaders;
588 #ifndef _NO_CRYPTO
589 else
590 encryptHeaders = _passwordIsDefined;
591 #endif
592 compressMainHeader = true;
593 if (encryptHeaders)
594 {
595 headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
596 headerMethod.Password = methodMode.Password;
597 }
598 }
599
600 if (numItems < 2)
601 compressMainHeader = false;
602
603 int level = GetLevel();
604
605 CUpdateOptions options;
606 options.Method = &methodMode;
607 options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : NULL;
608 options.UseFilters = (level != 0 && _autoFilter && !methodMode.Filter_was_Inserted);
609 options.MaxFilter = (level >= 8);
610 options.AnalysisLevel = GetAnalysisLevel();
611
612 options.HeaderOptions.CompressMainHeader = compressMainHeader;
613 /*
614 options.HeaderOptions.WriteCTime = Write_CTime;
615 options.HeaderOptions.WriteATime = Write_ATime;
616 options.HeaderOptions.WriteMTime = Write_MTime;
617 */
618
619 options.NumSolidFiles = _numSolidFiles;
620 options.NumSolidBytes = _numSolidBytes;
621 options.SolidExtension = _solidExtension;
622 options.UseTypeSorting = _useTypeSorting;
623
624 options.RemoveSfxBlock = _removeSfxBlock;
625 // options.VolumeMode = _volumeMode;
626
627 options.MultiThreadMixer = _useMultiThreadMixer;
628
629 COutArchive archive;
630 CArchiveDatabaseOut newDatabase;
631
632 CMyComPtr<ICryptoGetTextPassword> getPassword;
633 updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
634
635 /*
636 if (secureBlocks.Sorted.Size() > 1)
637 {
638 secureBlocks.GetReverseMap();
639 for (int i = 0; i < updateItems.Size(); i++)
640 {
641 int &secureIndex = updateItems[i].SecureIndex;
642 secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex];
643 }
644 }
645 */
646
647 res = Update(
648 EXTERNAL_CODECS_VARS
649 #ifdef _7Z_VOL
650 volume ? volume->Stream: 0,
651 volume ? db : 0,
652 #else
653 _inStream,
654 db,
655 #endif
656 updateItems,
657 // treeFolders,
658 // secureBlocks,
659 archive, newDatabase, outStream, updateCallback, options
660 #ifndef _NO_CRYPTO
661 , getPassword
662 #endif
663 );
664
665 RINOK(res);
666
667 updateItems.ClearAndFree();
668
669 return archive.WriteDatabase(EXTERNAL_CODECS_VARS
670 newDatabase, options.HeaderMethod, options.HeaderOptions);
671
672 COM_TRY_END
673 }
674
ParseBond(UString & srcString,UInt32 & coder,UInt32 & stream)675 static HRESULT ParseBond(UString &srcString, UInt32 &coder, UInt32 &stream)
676 {
677 stream = 0;
678 {
679 unsigned index = ParseStringToUInt32(srcString, coder);
680 if (index == 0)
681 return E_INVALIDARG;
682 srcString.DeleteFrontal(index);
683 }
684 if (srcString[0] == 's')
685 {
686 srcString.Delete(0);
687 unsigned index = ParseStringToUInt32(srcString, stream);
688 if (index == 0)
689 return E_INVALIDARG;
690 srcString.DeleteFrontal(index);
691 }
692 return S_OK;
693 }
694
InitProps()695 void COutHandler::InitProps()
696 {
697 CMultiMethodProps::Init();
698
699 _removeSfxBlock = false;
700 _compressHeaders = true;
701 _encryptHeadersSpecified = false;
702 _encryptHeaders = false;
703 // _useParents = false;
704
705 Write_CTime.Init();
706 Write_ATime.Init();
707 Write_MTime.Init();
708
709 _useMultiThreadMixer = true;
710
711 // _volumeMode = false;
712
713 InitSolid();
714 _useTypeSorting = false;
715 }
716
SetSolidFromString(const UString & s)717 HRESULT COutHandler::SetSolidFromString(const UString &s)
718 {
719 UString s2 = s;
720 s2.MakeLower_Ascii();
721 for (unsigned i = 0; i < s2.Len();)
722 {
723 const wchar_t *start = ((const wchar_t *)s2) + i;
724 const wchar_t *end;
725 UInt64 v = ConvertStringToUInt64(start, &end);
726 if (start == end)
727 {
728 if (s2[i++] != 'e')
729 return E_INVALIDARG;
730 _solidExtension = true;
731 continue;
732 }
733 i += (int)(end - start);
734 if (i == s2.Len())
735 return E_INVALIDARG;
736 wchar_t c = s2[i++];
737 if (c == 'f')
738 {
739 if (v < 1)
740 v = 1;
741 _numSolidFiles = v;
742 }
743 else
744 {
745 unsigned numBits;
746 switch (c)
747 {
748 case 'b': numBits = 0; break;
749 case 'k': numBits = 10; break;
750 case 'm': numBits = 20; break;
751 case 'g': numBits = 30; break;
752 case 't': numBits = 40; break;
753 default: return E_INVALIDARG;
754 }
755 _numSolidBytes = (v << numBits);
756 _numSolidBytesDefined = true;
757 }
758 }
759 return S_OK;
760 }
761
SetSolidFromPROPVARIANT(const PROPVARIANT & value)762 HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value)
763 {
764 bool isSolid;
765 switch (value.vt)
766 {
767 case VT_EMPTY: isSolid = true; break;
768 case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
769 case VT_BSTR:
770 if (StringToBool(value.bstrVal, isSolid))
771 break;
772 return SetSolidFromString(value.bstrVal);
773 default: return E_INVALIDARG;
774 }
775 if (isSolid)
776 InitSolid();
777 else
778 _numSolidFiles = 1;
779 return S_OK;
780 }
781
PROPVARIANT_to_BoolPair(const PROPVARIANT & prop,CBoolPair & dest)782 static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest)
783 {
784 RINOK(PROPVARIANT_to_bool(prop, dest.Val));
785 dest.Def = true;
786 return S_OK;
787 }
788
SetProperty(const wchar_t * nameSpec,const PROPVARIANT & value)789 HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
790 {
791 UString name = nameSpec;
792 name.MakeLower_Ascii();
793 if (name.IsEmpty())
794 return E_INVALIDARG;
795
796 if (name[0] == L's')
797 {
798 name.Delete(0);
799 if (name.IsEmpty())
800 return SetSolidFromPROPVARIANT(value);
801 if (value.vt != VT_EMPTY)
802 return E_INVALIDARG;
803 return SetSolidFromString(name);
804 }
805
806 UInt32 number;
807 int index = ParseStringToUInt32(name, number);
808 // UString realName = name.Ptr(index);
809 if (index == 0)
810 {
811 if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock);
812 if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders);
813 // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents);
814
815 if (name.IsEqualTo("hcf"))
816 {
817 bool compressHeadersFull = true;
818 RINOK(PROPVARIANT_to_bool(value, compressHeadersFull));
819 return compressHeadersFull ? S_OK: E_INVALIDARG;
820 }
821
822 if (name.IsEqualTo("he"))
823 {
824 RINOK(PROPVARIANT_to_bool(value, _encryptHeaders));
825 _encryptHeadersSpecified = true;
826 return S_OK;
827 }
828
829 if (name.IsEqualTo("tc")) return PROPVARIANT_to_BoolPair(value, Write_CTime);
830 if (name.IsEqualTo("ta")) return PROPVARIANT_to_BoolPair(value, Write_ATime);
831 if (name.IsEqualTo("tm")) return PROPVARIANT_to_BoolPair(value, Write_MTime);
832
833 if (name.IsEqualTo("mtf")) return PROPVARIANT_to_bool(value, _useMultiThreadMixer);
834
835 if (name.IsEqualTo("qs")) return PROPVARIANT_to_bool(value, _useTypeSorting);
836
837 // if (name.IsEqualTo("v")) return PROPVARIANT_to_bool(value, _volumeMode);
838 }
839 return CMultiMethodProps::SetProperty(name, value);
840 }
841
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)842 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
843 {
844 COM_TRY_BEGIN
845 _bonds.Clear();
846 InitProps();
847
848 for (UInt32 i = 0; i < numProps; i++)
849 {
850 UString name = names[i];
851 name.MakeLower_Ascii();
852 if (name.IsEmpty())
853 return E_INVALIDARG;
854
855 const PROPVARIANT &value = values[i];
856
857 if (name[0] == 'b')
858 {
859 if (value.vt != VT_EMPTY)
860 return E_INVALIDARG;
861 name.Delete(0);
862
863 CBond2 bond;
864 RINOK(ParseBond(name, bond.OutCoder, bond.OutStream));
865 if (name[0] != ':')
866 return E_INVALIDARG;
867 name.Delete(0);
868 UInt32 inStream = 0;
869 RINOK(ParseBond(name, bond.InCoder, inStream));
870 if (inStream != 0)
871 return E_INVALIDARG;
872 if (!name.IsEmpty())
873 return E_INVALIDARG;
874 _bonds.Add(bond);
875 continue;
876 }
877
878 RINOK(SetProperty(name, value));
879 }
880
881 unsigned numEmptyMethods = GetNumEmptyMethods();
882 if (numEmptyMethods > 0)
883 {
884 unsigned k;
885 for (k = 0; k < _bonds.Size(); k++)
886 {
887 const CBond2 &bond = _bonds[k];
888 if (bond.InCoder < (UInt32)numEmptyMethods ||
889 bond.OutCoder < (UInt32)numEmptyMethods)
890 return E_INVALIDARG;
891 }
892 for (k = 0; k < _bonds.Size(); k++)
893 {
894 CBond2 &bond = _bonds[k];
895 bond.InCoder -= (UInt32)numEmptyMethods;
896 bond.OutCoder -= (UInt32)numEmptyMethods;
897 }
898 _methods.DeleteFrontal(numEmptyMethods);
899 }
900
901 FOR_VECTOR (k, _bonds)
902 {
903 const CBond2 &bond = _bonds[k];
904 if (bond.InCoder >= (UInt32)_methods.Size() ||
905 bond.OutCoder >= (UInt32)_methods.Size())
906 return E_INVALIDARG;
907 }
908
909 return S_OK;
910 COM_TRY_END
911 }
912
913 }}
914