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 ¶m, 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 ¶m = 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