1 // HandlerOut.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/StringToInt.h"
6 
7 #include "../../../Windows/PropVariant.h"
8 
9 #ifdef COMPRESS_MT
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 
AreEqual(const UString & methodName,const wchar_t * s)82 static bool AreEqual(const UString &methodName, const wchar_t *s)
83   { return (methodName.CompareNoCase(s) == 0); }
84 
IsLzma() const85 bool COneMethodInfo::IsLzma() const
86 {
87   return
88     AreEqual(MethodName, kLZMAMethodName) ||
89     AreEqual(MethodName, kLZMA2MethodName);
90 }
91 
IsBZip2Method(const UString & methodName)92 static inline bool IsBZip2Method(const UString &methodName)
93   { return AreEqual(methodName, kBZip2MethodName); }
94 
IsPpmdMethod(const UString & methodName)95 static inline bool IsPpmdMethod(const UString &methodName)
96   { return AreEqual(methodName, kPpmdMethodName); }
97 
IsDeflateMethod(const UString & methodName)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 
ConvertProperty(PROPVARIANT srcProp,VARTYPE varType,NCOM::CPropVariant & destProp)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 
FindPropIdExact(const UString & name)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 
FindPropIdStart(const UString & name)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 
SetMethodProp(COneMethodInfo & m,PROPID propID,const NCOM::CPropVariant & value)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 
SetCompressionMethod2(COneMethodInfo & oneMethodInfo,UInt32 numThreads)192 void COutHandler::SetCompressionMethod2(COneMethodInfo &oneMethodInfo
193     #ifdef COMPRESS_MT
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     #ifdef COMPRESS_MT
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     #ifdef COMPRESS_MT
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 
SplitParams(const UString & srcString,UStringVector & subStrings)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 
SplitParam(const UString & param,UString & name,UString & value)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 
SetParam(COneMethodInfo & oneMethodInfo,const UString & name,const UString & value)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 
SetParams(COneMethodInfo & oneMethodInfo,const UString & srcString)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 
SetSolidSettings(const UString & s)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 
SetSolidSettings(const PROPVARIANT & value)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 
Init()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   #ifdef COMPRESS_MT
480   _numThreads = NSystem::GetNumberOfProcessors();
481   #endif
482 
483   _level = 5;
484   _autoFilter = true;
485   _volumeMode = false;
486   _crcSize = 4;
487   InitSolid();
488 }
489 
BeforeSetProperty()490 void COutHandler::BeforeSetProperty()
491 {
492   Init();
493   #ifdef COMPRESS_MT
494   numProcessors = NSystem::GetNumberOfProcessors();
495   #endif
496 
497   mainDicSize = 0xFFFFFFFF;
498   mainDicMethodIndex = 0xFFFFFFFF;
499   minNumber = 0;
500   _crcSize = 4;
501 }
502 
SetProperty(const wchar_t * nameSpec,const PROPVARIANT & value)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       #ifdef COMPRESS_MT
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