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