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
19using namespace NWindows;
20
21namespace NArchive {
22
23static const wchar_t *kCopyMethod = L"Copy";
24static const wchar_t *kLZMAMethodName = L"LZMA";
25static const wchar_t *kLZMA2MethodName = L"LZMA2";
26static const wchar_t *kBZip2MethodName = L"BZip2";
27static const wchar_t *kPpmdMethodName = L"PPMd";
28static const wchar_t *kDeflateMethodName = L"Deflate";
29static const wchar_t *kDeflate64MethodName = L"Deflate64";
30
31static const wchar_t *kLzmaMatchFinderX1 = L"HC4";
32static const wchar_t *kLzmaMatchFinderX5 = L"BT4";
33
34static const UInt32 kLzmaAlgoX1 = 0;
35static const UInt32 kLzmaAlgoX5 = 1;
36
37static const UInt32 kLzmaDicSizeX1 = 1 << 16;
38static const UInt32 kLzmaDicSizeX3 = 1 << 20;
39static const UInt32 kLzmaDicSizeX5 = 1 << 24;
40static const UInt32 kLzmaDicSizeX7 = 1 << 25;
41static const UInt32 kLzmaDicSizeX9 = 1 << 26;
42
43static const UInt32 kLzmaFastBytesX1 = 32;
44static const UInt32 kLzmaFastBytesX7 = 64;
45
46static const UInt32 kPpmdMemSizeX1 = (1 << 22);
47static const UInt32 kPpmdMemSizeX5 = (1 << 24);
48static const UInt32 kPpmdMemSizeX7 = (1 << 26);
49static const UInt32 kPpmdMemSizeX9 = (192 << 20);
50
51static const UInt32 kPpmdOrderX1 = 4;
52static const UInt32 kPpmdOrderX5 = 6;
53static const UInt32 kPpmdOrderX7 = 16;
54static const UInt32 kPpmdOrderX9 = 32;
55
56static const UInt32 kDeflateAlgoX1 = 0;
57static const UInt32 kDeflateAlgoX5 = 1;
58
59static const UInt32 kDeflateFastBytesX1 = 32;
60static const UInt32 kDeflateFastBytesX7 = 64;
61static const UInt32 kDeflateFastBytesX9 = 128;
62
63static const UInt32 kDeflatePassesX1 = 1;
64static const UInt32 kDeflatePassesX7 = 3;
65static const UInt32 kDeflatePassesX9 = 10;
66
67static const UInt32 kBZip2NumPassesX1 = 1;
68static const UInt32 kBZip2NumPassesX7 = 2;
69static const UInt32 kBZip2NumPassesX9 = 7;
70
71static const UInt32 kBZip2DicSizeX1 = 100000;
72static const UInt32 kBZip2DicSizeX3 = 500000;
73static const UInt32 kBZip2DicSizeX5 = 900000;
74
75static const wchar_t *kDefaultMethodName = kLZMAMethodName;
76
77static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
78static const UInt32 kDictionaryForHeaders = 1 << 20;
79static const UInt32 kNumFastBytesForHeaders = 273;
80static const UInt32 kAlgorithmForHeaders = kLzmaAlgoX5;
81
82static bool AreEqual(const UString &methodName, const wchar_t *s)
83  { return (methodName.CompareNoCase(s) == 0); }
84
85bool COneMethodInfo::IsLzma() const
86{
87  return
88    AreEqual(MethodName, kLZMAMethodName) ||
89    AreEqual(MethodName, kLZMA2MethodName);
90}
91
92static inline bool IsBZip2Method(const UString &methodName)
93  { return AreEqual(methodName, kBZip2MethodName); }
94
95static inline bool IsPpmdMethod(const UString &methodName)
96  { return AreEqual(methodName, kPpmdMethodName); }
97
98static inline bool IsDeflateMethod(const UString &methodName)
99{
100  return
101    AreEqual(methodName, kDeflateMethodName) ||
102    AreEqual(methodName, kDeflate64MethodName);
103}
104
105struct CNameToPropID
106{
107  PROPID PropID;
108  VARTYPE VarType;
109  const wchar_t *Name;
110};
111
112static 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
133static 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
162static 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
170static 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
181static 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
192void 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
288static 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
309static 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
331HRESULT 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
377HRESULT 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
393HRESULT 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
443HRESULT 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
468void 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
490void 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
503HRESULT 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