1 // LzmaAlone.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #include "../../../../C/CpuArch.h"
8 
9 #if (defined(_WIN32) || defined(OS2) || defined(MSDOS)) && !defined(UNDER_CE)
10 #include <fcntl.h>
11 #include <io.h>
12 #define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY)
13 #else
14 #define MY_SET_BINARY_MODE(file)
15 #endif
16 
17 #include "../../../Common/MyWindows.h"
18 #include "../../../Common/MyInitGuid.h"
19 
20 #include "../../../../C/7zVersion.h"
21 #include "../../../../C/Alloc.h"
22 #include "../../../../C/Lzma86.h"
23 
24 #include "../../../Windows/NtCheck.h"
25 
26 #ifndef _7ZIP_ST
27 #include "../../../Windows/System.h"
28 #endif
29 
30 #include "../../../Common/IntToString.h"
31 #include "../../../Common/CommandLineParser.h"
32 #include "../../../Common/StringConvert.h"
33 #include "../../../Common/StringToInt.h"
34 
35 #include "../../Common/FileStreams.h"
36 #include "../../Common/StreamUtils.h"
37 
38 #include "../../Compress/LzmaDecoder.h"
39 #include "../../Compress/LzmaEncoder.h"
40 
41 #include "../../UI/Console/BenchCon.h"
42 #include "../../UI/Console/ConsoleClose.h"
43 
44 bool g_LargePagesMode = false;
45 
46 using namespace NCommandLineParser;
47 
48 static const unsigned kDictSizeLog = 24;
49 
50 #define kCopyrightString "\nLZMA " MY_VERSION_CPU " : " MY_COPYRIGHT_DATE "\n\n"
51 
52 static const char * const kHelpString =
53     "Usage:  lzma <command> [inputFile] [outputFile] [<switches>...]\n"
54     "\n"
55     "<command>\n"
56     "  e : Encode file\n"
57     "  d : Decode file\n"
58     "  b : Benchmark\n"
59     "<switches>\n"
60     "  -a{N}  : set compression mode : [0, 1] : default = 1 (max)\n"
61     "  -d{N}  : set dictionary size : [12, 30] : default = 24 (16 MiB)\n"
62     "  -fb{N} : set number of fast bytes : [5, 273] : default = 128\n"
63     "  -mc{N} : set number of cycles for match finder\n"
64     "  -lc{N} : set number of literal context bits : [0, 8] : default = 3\n"
65     "  -lp{N} : set number of literal pos bits : [0, 4] : default = 0\n"
66     "  -pb{N} : set number of pos bits : [0, 4] : default = 2\n"
67     "  -mf{M} : set match finder: [hc4, bt2, bt3, bt4] : default = bt4\n"
68     "  -mt{N} : set number of CPU threads\n"
69     "  -eos   : write end of stream marker\n"
70     "  -si    : read data from stdin\n"
71     "  -so    : write data to stdout\n";
72 
73 
74 static const char * const kCantAllocate = "Cannot allocate memory";
75 static const char * const kReadError = "Read error";
76 static const char * const kWriteError = "Write error";
77 
78 
79 namespace NKey {
80 enum Enum
81 {
82   kHelp1 = 0,
83   kHelp2,
84   kMethod,
85   kLevel,
86   kAlgo,
87   kDict,
88   kFb,
89   kMc,
90   kLc,
91   kLp,
92   kPb,
93   kMatchFinder,
94   kMultiThread,
95   kEOS,
96   kStdIn,
97   kStdOut,
98   kFilter86
99 };
100 }
101 
102 #define SWFRM_3(t, mu, mi) t, mu, mi, NULL
103 
104 #define SWFRM_1(t) SWFRM_3(t, false, 0)
105 #define SWFRM_SIMPLE SWFRM_1(NSwitchType::kSimple)
106 #define SWFRM_STRING SWFRM_1(NSwitchType::kString)
107 
108 #define SWFRM_STRING_SINGL(mi) SWFRM_3(NSwitchType::kString, false, mi)
109 
110 static const CSwitchForm kSwitchForms[] =
111 {
112   { "?",  SWFRM_SIMPLE },
113   { "H",  SWFRM_SIMPLE },
114   { "MM", SWFRM_STRING_SINGL(1) },
115   { "X", SWFRM_STRING_SINGL(1) },
116   { "A", SWFRM_STRING_SINGL(1) },
117   { "D", SWFRM_STRING_SINGL(1) },
118   { "FB", SWFRM_STRING_SINGL(1) },
119   { "MC", SWFRM_STRING_SINGL(1) },
120   { "LC", SWFRM_STRING_SINGL(1) },
121   { "LP", SWFRM_STRING_SINGL(1) },
122   { "PB", SWFRM_STRING_SINGL(1) },
123   { "MF", SWFRM_STRING_SINGL(1) },
124   { "MT", SWFRM_STRING },
125   { "EOS", SWFRM_SIMPLE },
126   { "SI",  SWFRM_SIMPLE },
127   { "SO",  SWFRM_SIMPLE },
128   { "F86",  NSwitchType::kChar, false, 0, "+" }
129 };
130 
131 
Convert_UString_to_AString(const UString & s,AString & temp)132 static void Convert_UString_to_AString(const UString &s, AString &temp)
133 {
134   int codePage = CP_OEMCP;
135   /*
136   int g_CodePage = -1;
137   int codePage = g_CodePage;
138   if (codePage == -1)
139     codePage = CP_OEMCP;
140   if (codePage == CP_UTF8)
141     ConvertUnicodeToUTF8(s, temp);
142   else
143   */
144     UnicodeStringToMultiByte2(temp, s, (UINT)codePage);
145 }
146 
PrintErr(const char * s)147 static void PrintErr(const char *s)
148 {
149   fputs(s, stderr);
150 }
151 
PrintErr_LF(const char * s)152 static void PrintErr_LF(const char *s)
153 {
154   PrintErr(s);
155   fputc('\n', stderr);
156 }
157 
158 
PrintError(const char * s)159 static void PrintError(const char *s)
160 {
161   PrintErr("\nERROR: ");
162   PrintErr_LF(s);
163 }
164 
PrintError2(const char * s1,const UString & s2)165 static void PrintError2(const char *s1, const UString &s2)
166 {
167   PrintError(s1);
168   AString a;
169   Convert_UString_to_AString(s2, a);
170   PrintErr_LF(a);
171 }
172 
PrintError_int(const char * s,int code)173 static void PrintError_int(const char *s, int code)
174 {
175   PrintError(s);
176   char temp[32];
177   ConvertInt64ToString(code, temp);
178   PrintErr("Error code = ");
179   PrintErr_LF(temp);
180 }
181 
182 
183 
Print(const char * s)184 static void Print(const char *s)
185 {
186   fputs(s, stdout);
187 }
188 
Print_UInt64(UInt64 v)189 static void Print_UInt64(UInt64 v)
190 {
191   char temp[32];
192   ConvertUInt64ToString(v, temp);
193   Print(temp);
194 }
195 
Print_MB(UInt64 v)196 static void Print_MB(UInt64 v)
197 {
198   Print_UInt64(v);
199   Print(" MiB");
200 }
201 
Print_Size(const char * s,UInt64 v)202 static void Print_Size(const char *s, UInt64 v)
203 {
204   Print(s);
205   Print_UInt64(v);
206   Print(" (");
207   Print_MB(v >> 20);
208   Print(")\n");
209 }
210 
PrintTitle()211 static void PrintTitle()
212 {
213   Print(kCopyrightString);
214 }
215 
PrintHelp()216 static void PrintHelp()
217 {
218   PrintTitle();
219   Print(kHelpString);
220 }
221 
222 class CProgressPrint:
223   public ICompressProgressInfo,
224   public CMyUnknownImp
225 {
226   UInt64 _size1;
227   UInt64 _size2;
228 public:
CProgressPrint()229   CProgressPrint(): _size1(0), _size2(0) {}
230 
231   void ClosePrint();
232 
233   MY_UNKNOWN_IMP1(ICompressProgressInfo)
234 
235   STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
236 };
237 
238 #define BACK_STR \
239 "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
240 static const char * const kBackSpaces =
241 BACK_STR
242 "                                                                "
243 BACK_STR;
244 
245 
ClosePrint()246 void CProgressPrint::ClosePrint()
247 {
248   Print(kBackSpaces);
249 }
250 
SetRatioInfo(const UInt64 * inSize,const UInt64 * outSize)251 STDMETHODIMP CProgressPrint::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
252 {
253   if (NConsoleClose::TestBreakSignal())
254     return E_ABORT;
255   if (inSize)
256   {
257     UInt64 v1 = *inSize >> 20;
258     UInt64 v2 = _size2;
259     if (outSize)
260       v2 = *outSize >> 20;
261     if (v1 != _size1 || v2 != _size2)
262     {
263       _size1 = v1;
264       _size2 = v2;
265       ClosePrint();
266       Print_MB(_size1);
267       Print(" -> ");
268       Print_MB(_size2);
269     }
270   }
271   return S_OK;
272 }
273 
274 
275 MY_ATTR_NORETURN
IncorrectCommand()276 static void IncorrectCommand()
277 {
278   throw "Incorrect command";
279 }
280 
GetNumber(const wchar_t * s)281 static UInt32 GetNumber(const wchar_t *s)
282 {
283   const wchar_t *end;
284   UInt32 v = ConvertStringToUInt32(s, &end);
285   if (*end != 0)
286     IncorrectCommand();
287   return v;
288 }
289 
ParseUInt32(const CParser & parser,unsigned index,UInt32 & res)290 static void ParseUInt32(const CParser &parser, unsigned index, UInt32 &res)
291 {
292   if (parser[index].ThereIs)
293     res = GetNumber(parser[index].PostStrings[0]);
294 }
295 
296 
Error_HRESULT(const char * s,HRESULT res)297 static int Error_HRESULT(const char *s, HRESULT res)
298 {
299   if (res == E_ABORT)
300   {
301     Print("\n\nBreak signaled\n");
302     return 255;
303   }
304 
305   PrintError(s);
306 
307   if (res == E_OUTOFMEMORY)
308   {
309     PrintErr_LF(kCantAllocate);
310     return 8;
311   }
312   if (res == E_INVALIDARG)
313   {
314     PrintErr_LF("Ununsupported parameter");
315   }
316   else
317   {
318     char temp[32];
319     ConvertUInt32ToHex(res, temp);
320     PrintErr("Error code = 0x");
321     PrintErr_LF(temp);
322   }
323   return 1;
324 }
325 
326 #if defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE)
327 #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1;
328 #endif
329 
AddProp(CObjectVector<CProperty> & props2,const char * name,const wchar_t * val)330 static void AddProp(CObjectVector<CProperty> &props2, const char *name, const wchar_t *val)
331 {
332   CProperty &prop = props2.AddNew();
333   prop.Name = name;
334   prop.Value = val;
335 }
336 
main2(int numArgs,const char * args[])337 static int main2(int numArgs, const char *args[])
338 {
339   NT_CHECK
340 
341   if (numArgs == 1)
342   {
343     PrintHelp();
344     return 0;
345   }
346 
347   /*
348   bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 8);
349   if (unsupportedTypes)
350     throw "Unsupported base types. Edit Common/Types.h and recompile";
351   */
352 
353   UStringVector commandStrings;
354   for (int i = 1; i < numArgs; i++)
355     commandStrings.Add(MultiByteToUnicodeString(args[i]));
356 
357   CParser parser;
358   try
359   {
360     if (!parser.ParseStrings(kSwitchForms, ARRAY_SIZE(kSwitchForms), commandStrings))
361     {
362       PrintError2(parser.ErrorMessage, parser.ErrorLine);
363       return 1;
364     }
365   }
366   catch(...)
367   {
368     IncorrectCommand();
369   }
370 
371   if (parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs)
372   {
373     PrintHelp();
374     return 0;
375   }
376 
377   bool stdInMode = parser[NKey::kStdIn].ThereIs;
378   bool stdOutMode = parser[NKey::kStdOut].ThereIs;
379 
380   if (!stdOutMode)
381     PrintTitle();
382 
383   const UStringVector &params = parser.NonSwitchStrings;
384 
385   unsigned paramIndex = 0;
386   if (paramIndex >= params.Size())
387     IncorrectCommand();
388   const UString &command = params[paramIndex++];
389 
390   CObjectVector<CProperty> props2;
391   bool dictDefined = false;
392   UInt32 dict = (UInt32)(Int32)-1;
393 
394   if (parser[NKey::kDict].ThereIs)
395   {
396     UInt32 dictLog;
397     const UString &s = parser[NKey::kDict].PostStrings[0];
398     dictLog = GetNumber(s);
399     dict = 1 << dictLog;
400     dictDefined = true;
401     AddProp(props2, "d", s);
402   }
403 
404   if (parser[NKey::kLevel].ThereIs)
405   {
406     const UString &s = parser[NKey::kLevel].PostStrings[0];
407     /* UInt32 level = */ GetNumber(s);
408     AddProp(props2, "x", s);
409   }
410 
411   UString mf ("BT4");
412   if (parser[NKey::kMatchFinder].ThereIs)
413     mf = parser[NKey::kMatchFinder].PostStrings[0];
414 
415   UInt32 numThreads = (UInt32)(Int32)-1;
416 
417   #ifndef _7ZIP_ST
418 
419   if (parser[NKey::kMultiThread].ThereIs)
420   {
421     const UString &s = parser[NKey::kMultiThread].PostStrings[0];
422     if (s.IsEmpty())
423       numThreads = NWindows::NSystem::GetNumberOfProcessors();
424     else
425       numThreads = GetNumber(s);
426     AddProp(props2, "mt", s);
427   }
428 
429   #endif
430 
431 
432   if (parser[NKey::kMethod].ThereIs)
433   {
434     const UString &s = parser[NKey::kMethod].PostStrings[0];
435     if (s.IsEmpty() || s[0] != '=')
436       IncorrectCommand();
437     AddProp(props2, "m", s.Ptr(1));
438   }
439 
440   if (StringsAreEqualNoCase_Ascii(command, "b"))
441   {
442     UInt32 numIterations = 1;
443     if (paramIndex < params.Size())
444       numIterations = GetNumber(params[paramIndex++]);
445     if (params.Size() != paramIndex)
446       IncorrectCommand();
447 
448     HRESULT res = BenchCon(props2, numIterations, stdout);
449 
450     if (res == S_OK)
451       return 0;
452     return Error_HRESULT("Benchmark error", res);
453   }
454 
455   {
456     UInt32 needParams = 3;
457     if (stdInMode) needParams--;
458     if (stdOutMode) needParams--;
459     if (needParams != params.Size())
460       IncorrectCommand();
461   }
462 
463   if (numThreads == (UInt32)(Int32)-1)
464     numThreads = 1;
465 
466   bool encodeMode = false;
467 
468   if (StringsAreEqualNoCase_Ascii(command, "e"))
469     encodeMode = true;
470   else if (!StringsAreEqualNoCase_Ascii(command, "d"))
471     IncorrectCommand();
472 
473   CMyComPtr<ISequentialInStream> inStream;
474   CInFileStream *inStreamSpec = NULL;
475 
476   if (stdInMode)
477   {
478     inStream = new CStdInFileStream;
479     MY_SET_BINARY_MODE(stdin);
480   }
481   else
482   {
483     const UString &inputName = params[paramIndex++];
484     inStreamSpec = new CInFileStream;
485     inStream = inStreamSpec;
486     if (!inStreamSpec->Open(us2fs(inputName)))
487     {
488       PrintError2("Cannot open input file", inputName);
489       return 1;
490     }
491   }
492 
493   CMyComPtr<ISequentialOutStream> outStream;
494   COutFileStream *outStreamSpec = NULL;
495 
496   if (stdOutMode)
497   {
498     outStream = new CStdOutFileStream;
499     MY_SET_BINARY_MODE(stdout);
500   }
501   else
502   {
503     const UString &outputName = params[paramIndex++];
504     outStreamSpec = new COutFileStream;
505     outStream = outStreamSpec;
506     if (!outStreamSpec->Create(us2fs(outputName), true))
507     {
508       PrintError2("Cannot open output file", outputName);
509       return 1;
510     }
511   }
512 
513   bool fileSizeDefined = false;
514   UInt64 fileSize = 0;
515 
516   if (inStreamSpec)
517   {
518     if (!inStreamSpec->File.GetLength(fileSize))
519       throw "Cannot get file length";
520     fileSizeDefined = true;
521     if (!stdOutMode)
522       Print_Size("Input size:  ", fileSize);
523   }
524 
525   if (encodeMode && !dictDefined)
526   {
527     dict = 1 << kDictSizeLog;
528     if (fileSizeDefined)
529     {
530       unsigned i;
531       for (i = 16; i < kDictSizeLog; i++)
532         if ((UInt32)((UInt32)1 << i) >= fileSize)
533           break;
534       dict = (UInt32)1 << i;
535     }
536   }
537 
538   if (parser[NKey::kFilter86].ThereIs)
539   {
540     /* -f86 switch is for x86 filtered mode: BCJ + LZMA.
541        It uses modified header format.
542        It's not recommended to use -f86 mode now.
543        You can use xz format instead, if you want to use filters */
544 
545     if (parser[NKey::kEOS].ThereIs || stdInMode)
546       throw "Cannot use stdin in this mode";
547 
548     size_t inSize = (size_t)fileSize;
549 
550     if (inSize != fileSize)
551       throw "File is too big";
552 
553     Byte *inBuffer = NULL;
554 
555     if (inSize != 0)
556     {
557       inBuffer = (Byte *)MyAlloc((size_t)inSize);
558       if (!inBuffer)
559         throw kCantAllocate;
560     }
561 
562     if (ReadStream_FAIL(inStream, inBuffer, inSize) != S_OK)
563       throw "Cannot read";
564 
565     Byte *outBuffer = NULL;
566     size_t outSize;
567 
568     if (encodeMode)
569     {
570       // we allocate 105% of original size for output buffer
571       UInt64 outSize64 = fileSize / 20 * 21 + (1 << 16);
572 
573       outSize = (size_t)outSize64;
574 
575       if (outSize != outSize64)
576         throw "File is too big";
577 
578       if (outSize != 0)
579       {
580         outBuffer = (Byte *)MyAlloc((size_t)outSize);
581         if (!outBuffer)
582           throw kCantAllocate;
583       }
584 
585       int res = Lzma86_Encode(outBuffer, &outSize, inBuffer, inSize,
586           5, dict, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO);
587 
588       if (res != 0)
589       {
590         PrintError_int("Encode error", (int)res);
591         return 1;
592       }
593     }
594     else
595     {
596       UInt64 outSize64;
597 
598       if (Lzma86_GetUnpackSize(inBuffer, inSize, &outSize64) != 0)
599         throw "data error";
600 
601       outSize = (size_t)outSize64;
602       if (outSize != outSize64)
603         throw "Unpack size is too big";
604       if (outSize != 0)
605       {
606         outBuffer = (Byte *)MyAlloc(outSize);
607         if (!outBuffer)
608           throw kCantAllocate;
609       }
610 
611       int res = Lzma86_Decode(outBuffer, &outSize, inBuffer, &inSize);
612 
613       if (inSize != (size_t)fileSize)
614         throw "incorrect processed size";
615       if (res != 0)
616       {
617         PrintError_int("Decode error", (int)res);
618         return 1;
619       }
620     }
621 
622     if (WriteStream(outStream, outBuffer, outSize) != S_OK)
623       throw kWriteError;
624 
625     MyFree(outBuffer);
626     MyFree(inBuffer);
627   }
628   else
629   {
630 
631   CProgressPrint *progressSpec = NULL;
632   CMyComPtr<ICompressProgressInfo> progress;
633 
634   if (!stdOutMode)
635   {
636     progressSpec = new CProgressPrint;
637     progress = progressSpec;
638   }
639 
640   if (encodeMode)
641   {
642     NCompress::NLzma::CEncoder *encoderSpec = new NCompress::NLzma::CEncoder;
643     CMyComPtr<ICompressCoder> encoder = encoderSpec;
644 
645     UInt32 pb = 2;
646     UInt32 lc = 3; // = 0; for 32-bit data
647     UInt32 lp = 0; // = 2; for 32-bit data
648     UInt32 algo = 1;
649     UInt32 fb = 128;
650     UInt32 mc = 16 + fb / 2;
651     bool mcDefined = false;
652 
653     bool eos = parser[NKey::kEOS].ThereIs || stdInMode;
654 
655     ParseUInt32(parser, NKey::kAlgo, algo);
656     ParseUInt32(parser, NKey::kFb, fb);
657     ParseUInt32(parser, NKey::kLc, lc);
658     ParseUInt32(parser, NKey::kLp, lp);
659     ParseUInt32(parser, NKey::kPb, pb);
660 
661     mcDefined = parser[NKey::kMc].ThereIs;
662     if (mcDefined)
663       mc = GetNumber(parser[NKey::kMc].PostStrings[0]);
664 
665     const PROPID propIDs[] =
666     {
667       NCoderPropID::kDictionarySize,
668       NCoderPropID::kPosStateBits,
669       NCoderPropID::kLitContextBits,
670       NCoderPropID::kLitPosBits,
671       NCoderPropID::kAlgorithm,
672       NCoderPropID::kNumFastBytes,
673       NCoderPropID::kMatchFinder,
674       NCoderPropID::kEndMarker,
675       NCoderPropID::kNumThreads,
676       NCoderPropID::kMatchFinderCycles,
677     };
678 
679     const unsigned kNumPropsMax = ARRAY_SIZE(propIDs);
680 
681     PROPVARIANT props[kNumPropsMax];
682     for (int p = 0; p < 6; p++)
683       props[p].vt = VT_UI4;
684 
685     props[0].ulVal = (UInt32)dict;
686     props[1].ulVal = (UInt32)pb;
687     props[2].ulVal = (UInt32)lc;
688     props[3].ulVal = (UInt32)lp;
689     props[4].ulVal = (UInt32)algo;
690     props[5].ulVal = (UInt32)fb;
691 
692     props[6].vt = VT_BSTR;
693     props[6].bstrVal = const_cast<BSTR>((const wchar_t *)mf);
694 
695     props[7].vt = VT_BOOL;
696     props[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE;
697 
698     props[8].vt = VT_UI4;
699     props[8].ulVal = (UInt32)numThreads;
700 
701     // it must be last in property list
702     props[9].vt = VT_UI4;
703     props[9].ulVal = (UInt32)mc;
704 
705     unsigned numProps = kNumPropsMax;
706     if (!mcDefined)
707       numProps--;
708 
709     HRESULT res = encoderSpec->SetCoderProperties(propIDs, props, numProps);
710     if (res != S_OK)
711       return Error_HRESULT("incorrect encoder properties", res);
712 
713     if (encoderSpec->WriteCoderProperties(outStream) != S_OK)
714       throw kWriteError;
715 
716     bool fileSizeWasUsed = true;
717     if (eos || stdInMode)
718     {
719       fileSize = (UInt64)(Int64)-1;
720       fileSizeWasUsed = false;
721     }
722 
723     {
724       Byte temp[8];
725       for (int i = 0; i < 8; i++)
726         temp[i]= (Byte)(fileSize >> (8 * i));
727       if (WriteStream(outStream, temp, 8) != S_OK)
728         throw kWriteError;
729     }
730 
731     res = encoder->Code(inStream, outStream, NULL, NULL, progress);
732     if (progressSpec)
733       progressSpec->ClosePrint();
734 
735     if (res != S_OK)
736       return Error_HRESULT("Encoding error", res);
737 
738     UInt64 processedSize = encoderSpec->GetInputProcessedSize();
739 
740     if (fileSizeWasUsed && processedSize != fileSize)
741       throw "Incorrect size of processed data";
742   }
743   else
744   {
745     NCompress::NLzma::CDecoder *decoderSpec = new NCompress::NLzma::CDecoder;
746     CMyComPtr<ICompressCoder> decoder = decoderSpec;
747 
748     decoderSpec->FinishStream = true;
749 
750     const unsigned kPropertiesSize = 5;
751     Byte header[kPropertiesSize + 8];
752 
753     if (ReadStream_FALSE(inStream, header, kPropertiesSize + 8) != S_OK)
754       throw kReadError;
755 
756     if (decoderSpec->SetDecoderProperties2(header, kPropertiesSize) != S_OK)
757       throw "SetDecoderProperties error";
758 
759     UInt64 unpackSize = 0;
760     for (int i = 0; i < 8; i++)
761       unpackSize |= ((UInt64)header[kPropertiesSize + i]) << (8 * i);
762 
763     bool unpackSizeDefined = (unpackSize != (UInt64)(Int64)-1);
764 
765     HRESULT res = decoder->Code(inStream, outStream, NULL, unpackSizeDefined ? &unpackSize : NULL, progress);
766     if (progressSpec)
767       progressSpec->ClosePrint();
768 
769     if (res != S_OK)
770     {
771       if (res == S_FALSE)
772       {
773         PrintError("Decoding error");
774         return 1;
775       }
776       return Error_HRESULT("Decoding error", res);
777     }
778 
779     if (unpackSizeDefined && unpackSize != decoderSpec->GetOutputProcessedSize())
780       throw "incorrect uncompressed size in header";
781   }
782   }
783 
784   if (outStreamSpec)
785   {
786     if (!stdOutMode)
787       Print_Size("Output size: ", outStreamSpec->ProcessedSize);
788     if (outStreamSpec->Close() != S_OK)
789       throw "File closing error";
790   }
791 
792   return 0;
793 }
794 
main(int numArgs,const char * args[])795 int MY_CDECL main(int numArgs, const char *args[])
796 {
797   NConsoleClose::CCtrlHandlerSetter ctrlHandlerSetter;
798 
799   try { return main2(numArgs, args); }
800   catch (const char *s)
801   {
802     PrintError(s);
803     return 1;
804   }
805   catch(...)
806   {
807     PrintError("Unknown Error");
808     return 1;
809   }
810 }
811