Home | History | Annotate | Download | only in Common
      1 // HandlerOut.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../Common/StringToInt.h"
      6 
      7 #include "../../../Windows/PropVariant.h"
      8 
      9 #ifndef _7ZIP_ST
     10 #include "../../../Windows/System.h"
     11 #endif
     12 
     13 #include "../../ICoder.h"
     14 
     15 #include "../Common/ParseProperties.h"
     16 
     17 #include "HandlerOut.h"
     18 
     19 using namespace NWindows;
     20 
     21 namespace NArchive {
     22 
     23 static const wchar_t *kCopyMethod = L"Copy";
     24 static const wchar_t *kLZMAMethodName = L"LZMA";
     25 static const wchar_t *kLZMA2MethodName = L"LZMA2";
     26 static const wchar_t *kBZip2MethodName = L"BZip2";
     27 static const wchar_t *kPpmdMethodName = L"PPMd";
     28 static const wchar_t *kDeflateMethodName = L"Deflate";
     29 static const wchar_t *kDeflate64MethodName = L"Deflate64";
     30 
     31 static const wchar_t *kLzmaMatchFinderX1 = L"HC4";
     32 static const wchar_t *kLzmaMatchFinderX5 = L"BT4";
     33 
     34 static const UInt32 kLzmaAlgoX1 = 0;
     35 static const UInt32 kLzmaAlgoX5 = 1;
     36 
     37 static const UInt32 kLzmaDicSizeX1 = 1 << 16;
     38 static const UInt32 kLzmaDicSizeX3 = 1 << 20;
     39 static const UInt32 kLzmaDicSizeX5 = 1 << 24;
     40 static const UInt32 kLzmaDicSizeX7 = 1 << 25;
     41 static const UInt32 kLzmaDicSizeX9 = 1 << 26;
     42 
     43 static const UInt32 kLzmaFastBytesX1 = 32;
     44 static const UInt32 kLzmaFastBytesX7 = 64;
     45 
     46 static const UInt32 kPpmdMemSizeX1 = (1 << 22);
     47 static const UInt32 kPpmdMemSizeX5 = (1 << 24);
     48 static const UInt32 kPpmdMemSizeX7 = (1 << 26);
     49 static const UInt32 kPpmdMemSizeX9 = (192 << 20);
     50 
     51 static const UInt32 kPpmdOrderX1 = 4;
     52 static const UInt32 kPpmdOrderX5 = 6;
     53 static const UInt32 kPpmdOrderX7 = 16;
     54 static const UInt32 kPpmdOrderX9 = 32;
     55 
     56 static const UInt32 kDeflateAlgoX1 = 0;
     57 static const UInt32 kDeflateAlgoX5 = 1;
     58 
     59 static const UInt32 kDeflateFastBytesX1 = 32;
     60 static const UInt32 kDeflateFastBytesX7 = 64;
     61 static const UInt32 kDeflateFastBytesX9 = 128;
     62 
     63 static const UInt32 kDeflatePassesX1 = 1;
     64 static const UInt32 kDeflatePassesX7 = 3;
     65 static const UInt32 kDeflatePassesX9 = 10;
     66 
     67 static const UInt32 kBZip2NumPassesX1 = 1;
     68 static const UInt32 kBZip2NumPassesX7 = 2;
     69 static const UInt32 kBZip2NumPassesX9 = 7;
     70 
     71 static const UInt32 kBZip2DicSizeX1 = 100000;
     72 static const UInt32 kBZip2DicSizeX3 = 500000;
     73 static const UInt32 kBZip2DicSizeX5 = 900000;
     74 
     75 static const wchar_t *kDefaultMethodName = kLZMAMethodName;
     76 
     77 static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
     78 static const UInt32 kDictionaryForHeaders = 1 << 20;
     79 static const UInt32 kNumFastBytesForHeaders = 273;
     80 static const UInt32 kAlgorithmForHeaders = kLzmaAlgoX5;
     81 
     82 static bool AreEqual(const UString &methodName, const wchar_t *s)
     83   { return (methodName.CompareNoCase(s) == 0); }
     84 
     85 bool COneMethodInfo::IsLzma() const
     86 {
     87   return
     88     AreEqual(MethodName, kLZMAMethodName) ||
     89     AreEqual(MethodName, kLZMA2MethodName);
     90 }
     91 
     92 static inline bool IsBZip2Method(const UString &methodName)
     93   { return AreEqual(methodName, kBZip2MethodName); }
     94 
     95 static inline bool IsPpmdMethod(const UString &methodName)
     96   { return AreEqual(methodName, kPpmdMethodName); }
     97 
     98 static inline bool IsDeflateMethod(const UString &methodName)
     99 {
    100   return
    101     AreEqual(methodName, kDeflateMethodName) ||
    102     AreEqual(methodName, kDeflate64MethodName);
    103 }
    104 
    105 struct CNameToPropID
    106 {
    107   PROPID PropID;
    108   VARTYPE VarType;
    109   const wchar_t *Name;
    110 };
    111 
    112 static CNameToPropID g_NameToPropID[] =
    113 {
    114   { NCoderPropID::kBlockSize, VT_UI4, L"C" },
    115   { NCoderPropID::kDictionarySize, VT_UI4, L"D" },
    116   { NCoderPropID::kUsedMemorySize, VT_UI4, L"MEM" },
    117 
    118   { NCoderPropID::kOrder, VT_UI4, L"O" },
    119   { NCoderPropID::kPosStateBits, VT_UI4, L"PB" },
    120   { NCoderPropID::kLitContextBits, VT_UI4, L"LC" },
    121   { NCoderPropID::kLitPosBits, VT_UI4, L"LP" },
    122   { NCoderPropID::kEndMarker, VT_BOOL, L"eos" },
    123 
    124   { NCoderPropID::kNumPasses, VT_UI4, L"Pass" },
    125   { NCoderPropID::kNumFastBytes, VT_UI4, L"fb" },
    126   { NCoderPropID::kMatchFinderCycles, VT_UI4, L"mc" },
    127   { NCoderPropID::kAlgorithm, VT_UI4, L"a" },
    128   { NCoderPropID::kMatchFinder, VT_BSTR, L"mf" },
    129   { NCoderPropID::kNumThreads, VT_UI4, L"mt" },
    130   { NCoderPropID::kDefaultProp, VT_UI4, L"" }
    131 };
    132 
    133 static bool ConvertProperty(PROPVARIANT srcProp, VARTYPE varType, NCOM::CPropVariant &destProp)
    134 {
    135   if (varType == srcProp.vt)
    136   {
    137     destProp = srcProp;
    138     return true;
    139   }
    140   if (varType == VT_UI1)
    141   {
    142     if (srcProp.vt == VT_UI4)
    143     {
    144       UInt32 value = srcProp.ulVal;
    145       if (value > 0xFF)
    146         return false;
    147       destProp = (Byte)value;
    148       return true;
    149     }
    150   }
    151   else if (varType == VT_BOOL)
    152   {
    153     bool res;
    154     if (SetBoolProperty(res, srcProp) != S_OK)
    155       return false;
    156     destProp = res;
    157     return true;
    158   }
    159   return false;
    160 }
    161 
    162 static int FindPropIdExact(const UString &name)
    163 {
    164   for (int i = 0; i < sizeof(g_NameToPropID) / sizeof(g_NameToPropID[0]); i++)
    165     if (name.CompareNoCase(g_NameToPropID[i].Name) == 0)
    166       return i;
    167   return -1;
    168 }
    169 
    170 static int FindPropIdStart(const UString &name)
    171 {
    172   for (int i = 0; i < sizeof(g_NameToPropID) / sizeof(g_NameToPropID[0]); i++)
    173   {
    174     UString t = g_NameToPropID[i].Name;
    175     if (t.CompareNoCase(name.Left(t.Length())) == 0)
    176       return i;
    177   }
    178   return -1;
    179 }
    180 
    181 static void SetMethodProp(COneMethodInfo &m, PROPID propID, const NCOM::CPropVariant &value)
    182 {
    183   for (int j = 0; j < m.Props.Size(); j++)
    184     if (m.Props[j].Id == propID)
    185       return;
    186   CProp prop;
    187   prop.Id = propID;
    188   prop.Value = value;
    189   m.Props.Add(prop);
    190 }
    191 
    192 void COutHandler::SetCompressionMethod2(COneMethodInfo &oneMethodInfo
    193     #ifndef _7ZIP_ST
    194     , UInt32 numThreads
    195     #endif
    196     )
    197 {
    198   UInt32 level = _level;
    199   if (oneMethodInfo.MethodName.IsEmpty())
    200     oneMethodInfo.MethodName = kDefaultMethodName;
    201 
    202   if (oneMethodInfo.IsLzma())
    203   {
    204     UInt32 dicSize =
    205       (level >= 9 ? kLzmaDicSizeX9 :
    206       (level >= 7 ? kLzmaDicSizeX7 :
    207       (level >= 5 ? kLzmaDicSizeX5 :
    208       (level >= 3 ? kLzmaDicSizeX3 :
    209                     kLzmaDicSizeX1))));
    210 
    211     UInt32 algo =
    212       (level >= 5 ? kLzmaAlgoX5 :
    213                     kLzmaAlgoX1);
    214 
    215     UInt32 fastBytes =
    216       (level >= 7 ? kLzmaFastBytesX7 :
    217                     kLzmaFastBytesX1);
    218 
    219     const wchar_t *matchFinder =
    220       (level >= 5 ? kLzmaMatchFinderX5 :
    221                     kLzmaMatchFinderX1);
    222 
    223     SetMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize);
    224     SetMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo);
    225     SetMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes);
    226     SetMethodProp(oneMethodInfo, NCoderPropID::kMatchFinder, matchFinder);
    227     #ifndef _7ZIP_ST
    228     SetMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads);
    229     #endif
    230   }
    231   else if (IsDeflateMethod(oneMethodInfo.MethodName))
    232   {
    233     UInt32 fastBytes =
    234       (level >= 9 ? kDeflateFastBytesX9 :
    235       (level >= 7 ? kDeflateFastBytesX7 :
    236                     kDeflateFastBytesX1));
    237 
    238     UInt32 numPasses =
    239       (level >= 9 ? kDeflatePassesX9 :
    240       (level >= 7 ? kDeflatePassesX7 :
    241                     kDeflatePassesX1));
    242 
    243     UInt32 algo =
    244       (level >= 5 ? kDeflateAlgoX5 :
    245                     kDeflateAlgoX1);
    246 
    247     SetMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo);
    248     SetMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes);
    249     SetMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses);
    250   }
    251   else if (IsBZip2Method(oneMethodInfo.MethodName))
    252   {
    253     UInt32 numPasses =
    254       (level >= 9 ? kBZip2NumPassesX9 :
    255       (level >= 7 ? kBZip2NumPassesX7 :
    256                     kBZip2NumPassesX1));
    257 
    258     UInt32 dicSize =
    259       (level >= 5 ? kBZip2DicSizeX5 :
    260       (level >= 3 ? kBZip2DicSizeX3 :
    261                     kBZip2DicSizeX1));
    262 
    263     SetMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses);
    264     SetMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize);
    265     #ifndef _7ZIP_ST
    266     SetMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads);
    267     #endif
    268   }
    269   else if (IsPpmdMethod(oneMethodInfo.MethodName))
    270   {
    271     UInt32 useMemSize =
    272       (level >= 9 ? kPpmdMemSizeX9 :
    273       (level >= 7 ? kPpmdMemSizeX7 :
    274       (level >= 5 ? kPpmdMemSizeX5 :
    275                     kPpmdMemSizeX1)));
    276 
    277     UInt32 order =
    278       (level >= 9 ? kPpmdOrderX9 :
    279       (level >= 7 ? kPpmdOrderX7 :
    280       (level >= 5 ? kPpmdOrderX5 :
    281                     kPpmdOrderX1)));
    282 
    283     SetMethodProp(oneMethodInfo, NCoderPropID::kUsedMemorySize, useMemSize);
    284     SetMethodProp(oneMethodInfo, NCoderPropID::kOrder, order);
    285   }
    286 }
    287 
    288 static void SplitParams(const UString &srcString, UStringVector &subStrings)
    289 {
    290   subStrings.Clear();
    291   UString name;
    292   int len = srcString.Length();
    293   if (len == 0)
    294     return;
    295   for (int i = 0; i < len; i++)
    296   {
    297     wchar_t c = srcString[i];
    298     if (c == L':')
    299     {
    300       subStrings.Add(name);
    301       name.Empty();
    302     }
    303     else
    304       name += c;
    305   }
    306   subStrings.Add(name);
    307 }
    308 
    309 static void SplitParam(const UString &param, UString &name, UString &value)
    310 {
    311   int eqPos = param.Find(L'=');
    312   if (eqPos >= 0)
    313   {
    314     name = param.Left(eqPos);
    315     value = param.Mid(eqPos + 1);
    316     return;
    317   }
    318   for(int i = 0; i < param.Length(); i++)
    319   {
    320     wchar_t c = param[i];
    321     if (c >= L'0' && c <= L'9')
    322     {
    323       name = param.Left(i);
    324       value = param.Mid(i);
    325       return;
    326     }
    327   }
    328   name = param;
    329 }
    330 
    331 HRESULT COutHandler::SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value)
    332 {
    333   CProp prop;
    334   int index = FindPropIdExact(name);
    335   if (index < 0)
    336     return E_INVALIDARG;
    337   const CNameToPropID &nameToPropID = g_NameToPropID[index];
    338   prop.Id = nameToPropID.PropID;
    339 
    340   if (prop.Id == NCoderPropID::kBlockSize ||
    341       prop.Id == NCoderPropID::kDictionarySize ||
    342       prop.Id == NCoderPropID::kUsedMemorySize)
    343   {
    344     UInt32 dicSize;
    345     RINOK(ParsePropDictionaryValue(value, dicSize));
    346     prop.Value = dicSize;
    347   }
    348   else
    349   {
    350     NCOM::CPropVariant propValue;
    351 
    352     if (nameToPropID.VarType == VT_BSTR)
    353       propValue = value;
    354     else if (nameToPropID.VarType == VT_BOOL)
    355     {
    356       bool res;
    357       if (!StringToBool(value, res))
    358         return E_INVALIDARG;
    359       propValue = res;
    360     }
    361     else
    362     {
    363       UInt32 number;
    364       if (ParseStringToUInt32(value, number) == value.Length())
    365         propValue = number;
    366       else
    367         propValue = value;
    368     }
    369 
    370     if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value))
    371       return E_INVALIDARG;
    372   }
    373   oneMethodInfo.Props.Add(prop);
    374   return S_OK;
    375 }
    376 
    377 HRESULT COutHandler::SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString)
    378 {
    379   UStringVector params;
    380   SplitParams(srcString, params);
    381   if (params.Size() > 0)
    382     oneMethodInfo.MethodName = params[0];
    383   for (int i = 1; i < params.Size(); i++)
    384   {
    385     const UString &param = params[i];
    386     UString name, value;
    387     SplitParam(param, name, value);
    388     RINOK(SetParam(oneMethodInfo, name, value));
    389   }
    390   return S_OK;
    391 }
    392 
    393 HRESULT COutHandler::SetSolidSettings(const UString &s)
    394 {
    395   UString s2 = s;
    396   s2.MakeUpper();
    397   for (int i = 0; i < s2.Length();)
    398   {
    399     const wchar_t *start = ((const wchar_t *)s2) + i;
    400     const wchar_t *end;
    401     UInt64 v = ConvertStringToUInt64(start, &end);
    402     if (start == end)
    403     {
    404       if (s2[i++] != 'E')
    405         return E_INVALIDARG;
    406       _solidExtension = true;
    407       continue;
    408     }
    409     i += (int)(end - start);
    410     if (i == s2.Length())
    411       return E_INVALIDARG;
    412     wchar_t c = s2[i++];
    413     switch(c)
    414     {
    415       case 'F':
    416         if (v < 1)
    417           v = 1;
    418         _numSolidFiles = v;
    419         break;
    420       case 'B':
    421         _numSolidBytes = v;
    422         _numSolidBytesDefined = true;
    423         break;
    424       case 'K':
    425         _numSolidBytes = (v << 10);
    426         _numSolidBytesDefined = true;
    427         break;
    428       case 'M':
    429         _numSolidBytes = (v << 20);
    430         _numSolidBytesDefined = true;
    431         break;
    432       case 'G':
    433         _numSolidBytes = (v << 30);
    434         _numSolidBytesDefined = true;
    435         break;
    436       default:
    437         return E_INVALIDARG;
    438     }
    439   }
    440   return S_OK;
    441 }
    442 
    443 HRESULT COutHandler::SetSolidSettings(const PROPVARIANT &value)
    444 {
    445   bool isSolid;
    446   switch(value.vt)
    447   {
    448     case VT_EMPTY:
    449       isSolid = true;
    450       break;
    451     case VT_BOOL:
    452       isSolid = (value.boolVal != VARIANT_FALSE);
    453       break;
    454     case VT_BSTR:
    455       if (StringToBool(value.bstrVal, isSolid))
    456         break;
    457       return SetSolidSettings(value.bstrVal);
    458     default:
    459       return E_INVALIDARG;
    460   }
    461   if (isSolid)
    462     InitSolid();
    463   else
    464     _numSolidFiles = 1;
    465   return S_OK;
    466 }
    467 
    468 void COutHandler::Init()
    469 {
    470   _removeSfxBlock = false;
    471   _compressHeaders = true;
    472   _encryptHeadersSpecified = false;
    473   _encryptHeaders = false;
    474 
    475   WriteCTime = false;
    476   WriteATime = false;
    477   WriteMTime = true;
    478 
    479   #ifndef _7ZIP_ST
    480   _numThreads = NSystem::GetNumberOfProcessors();
    481   #endif
    482 
    483   _level = 5;
    484   _autoFilter = true;
    485   _volumeMode = false;
    486   _crcSize = 4;
    487   InitSolid();
    488 }
    489 
    490 void COutHandler::BeforeSetProperty()
    491 {
    492   Init();
    493   #ifndef _7ZIP_ST
    494   numProcessors = NSystem::GetNumberOfProcessors();
    495   #endif
    496 
    497   mainDicSize = 0xFFFFFFFF;
    498   mainDicMethodIndex = 0xFFFFFFFF;
    499   minNumber = 0;
    500   _crcSize = 4;
    501 }
    502 
    503 HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
    504 {
    505   UString name = nameSpec;
    506   name.MakeUpper();
    507   if (name.IsEmpty())
    508     return E_INVALIDARG;
    509 
    510   if (name[0] == 'X')
    511   {
    512     name.Delete(0);
    513     _level = 9;
    514     return ParsePropValue(name, value, _level);
    515   }
    516 
    517   if (name[0] == L'S')
    518   {
    519     name.Delete(0);
    520     if (name.IsEmpty())
    521       return SetSolidSettings(value);
    522     if (value.vt != VT_EMPTY)
    523       return E_INVALIDARG;
    524     return SetSolidSettings(name);
    525   }
    526 
    527   if (name == L"CRC")
    528   {
    529     _crcSize = 4;
    530     name.Delete(0, 3);
    531     return ParsePropValue(name, value, _crcSize);
    532   }
    533 
    534   UInt32 number;
    535   int index = ParseStringToUInt32(name, number);
    536   UString realName = name.Mid(index);
    537   if (index == 0)
    538   {
    539     if(name.Left(2).CompareNoCase(L"MT") == 0)
    540     {
    541       #ifndef _7ZIP_ST
    542       RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads));
    543       #endif
    544       return S_OK;
    545     }
    546     if (name.CompareNoCase(L"RSFX") == 0)  return SetBoolProperty(_removeSfxBlock, value);
    547     if (name.CompareNoCase(L"F") == 0) return SetBoolProperty(_autoFilter, value);
    548     if (name.CompareNoCase(L"HC") == 0) return SetBoolProperty(_compressHeaders, value);
    549     if (name.CompareNoCase(L"HCF") == 0)
    550     {
    551       bool compressHeadersFull = true;
    552       RINOK(SetBoolProperty(compressHeadersFull, value));
    553       if (!compressHeadersFull)
    554         return E_INVALIDARG;
    555       return S_OK;
    556     }
    557     if (name.CompareNoCase(L"HE") == 0)
    558     {
    559       RINOK(SetBoolProperty(_encryptHeaders, value));
    560       _encryptHeadersSpecified = true;
    561       return S_OK;
    562     }
    563     if (name.CompareNoCase(L"TC") == 0) return SetBoolProperty(WriteCTime, value);
    564     if (name.CompareNoCase(L"TA") == 0) return SetBoolProperty(WriteATime, value);
    565     if (name.CompareNoCase(L"TM") == 0) return SetBoolProperty(WriteMTime, value);
    566     if (name.CompareNoCase(L"V") == 0) return SetBoolProperty(_volumeMode, value);
    567     number = 0;
    568   }
    569   if (number > 10000)
    570     return E_FAIL;
    571   if (number < minNumber)
    572     return E_INVALIDARG;
    573   number -= minNumber;
    574   for(int j = _methods.Size(); j <= (int)number; j++)
    575   {
    576     COneMethodInfo oneMethodInfo;
    577     _methods.Add(oneMethodInfo);
    578   }
    579 
    580   COneMethodInfo &oneMethodInfo = _methods[number];
    581 
    582   if (realName.Length() == 0)
    583   {
    584     if (value.vt != VT_BSTR)
    585       return E_INVALIDARG;
    586 
    587     RINOK(SetParams(oneMethodInfo, value.bstrVal));
    588   }
    589   else
    590   {
    591     int index = FindPropIdStart(realName);
    592     if (index < 0)
    593       return E_INVALIDARG;
    594     const CNameToPropID &nameToPropID = g_NameToPropID[index];
    595     CProp prop;
    596     prop.Id = nameToPropID.PropID;
    597 
    598     if (prop.Id == NCoderPropID::kBlockSize ||
    599         prop.Id == NCoderPropID::kDictionarySize ||
    600         prop.Id == NCoderPropID::kUsedMemorySize)
    601     {
    602       UInt32 dicSize;
    603       RINOK(ParsePropDictionaryValue(realName.Mid(MyStringLen(nameToPropID.Name)), value, dicSize));
    604       prop.Value = dicSize;
    605       if (number <= mainDicMethodIndex)
    606         mainDicSize = dicSize;
    607     }
    608     else
    609     {
    610       int index = FindPropIdExact(realName);
    611       if (index < 0)
    612         return E_INVALIDARG;
    613       const CNameToPropID &nameToPropID = g_NameToPropID[index];
    614       prop.Id = nameToPropID.PropID;
    615       if (!ConvertProperty(value, nameToPropID.VarType, prop.Value))
    616         return E_INVALIDARG;
    617     }
    618     oneMethodInfo.Props.Add(prop);
    619   }
    620   return S_OK;
    621 }
    622 
    623 }
    624