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