Home | History | Annotate | Download | only in 7z
      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 
     35 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
     36 {
     37   *type = NFileTimeType::kWindows;
     38   return S_OK;
     39 }
     40 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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