• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 7zHandlerOut.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/StringToInt.h"
7 #include "../../../Common/Wildcard.h"
8 
9 #include "../Common/ItemNameUtils.h"
10 #include "../Common/ParseProperties.h"
11 
12 #include "7zHandler.h"
13 #include "7zOut.h"
14 #include "7zUpdate.h"
15 
16 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