1 // LzmaAlone.cpp
2 
3 #include "StdAfx.h"
4 
5 #include <stdio.h>
6 
7 #if (defined(_WIN32) || defined(OS2) || defined(MSDOS)) && !defined(UNDER_CE)
8 #include <fcntl.h>
9 #include <io.h>
10 #define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY)
11 #else
12 #define MY_SET_BINARY_MODE(file)
13 #endif
14 
15 // #include "../../../Common/MyWindows.h"
16 #include "../../../Common/MyInitGuid.h"
17 
18 #include "../../../../C/7zVersion.h"
19 #include "../../../../C/Alloc.h"
20 #include "../../../../C/Lzma86.h"
21 
22 #include "../../../Windows/NtCheck.h"
23 
24 #ifndef _7ZIP_ST
25 #include "../../../Windows/System.h"
26 #endif
27 
28 #include "../../../Common/CommandLineParser.h"
29 #include "../../../Common/StringConvert.h"
30 #include "../../../Common/StringToInt.h"
31 
32 #include "../../Common/FileStreams.h"
33 #include "../../Common/StreamUtils.h"
34 
35 #include "../../Compress/LzmaDecoder.h"
36 #include "../../Compress/LzmaEncoder.h"
37 
38 #include "../../UI/Console/BenchCon.h"
39 
40 
41 using namespace NCommandLineParser;
42 
43 static const char *kCantAllocate = "Can not allocate memory";
44 static const char *kReadError = "Read error";
45 static const char *kWriteError = "Write error";
46 
47 namespace NKey {
48 enum Enum
49 {
50   kHelp1 = 0,
51   kHelp2,
52   kMethod,
53   kLevel,
54   kAlgo,
55   kDict,
56   kFb,
57   kMc,
58   kLc,
59   kLp,
60   kPb,
61   kMatchFinder,
62   kMultiThread,
63   kEOS,
64   kStdIn,
65   kStdOut,
66   kFilter86
67 };
68 }
69 
70 static const CSwitchForm kSwitchForms[] =
71 {
72   { "?",  NSwitchType::kSimple, false },
73   { "H",  NSwitchType::kSimple, false },
74   { "MM", NSwitchType::kString, false, 1 },
75   { "X", NSwitchType::kString, false, 1 },
76   { "A", NSwitchType::kString, false, 1 },
77   { "D", NSwitchType::kString, false, 1 },
78   { "FB", NSwitchType::kString, false, 1 },
79   { "MC", NSwitchType::kString, false, 1 },
80   { "LC", NSwitchType::kString, false, 1 },
81   { "LP", NSwitchType::kString, false, 1 },
82   { "PB", NSwitchType::kString, false, 1 },
83   { "MF", NSwitchType::kString, false, 1 },
84   { "MT", NSwitchType::kString, false, 0 },
85   { "EOS", NSwitchType::kSimple, false },
86   { "SI",  NSwitchType::kSimple, false },
87   { "SO",  NSwitchType::kSimple, false },
88   { "F86",  NSwitchType::kChar, false, 0, "+" }
89 };
90 
PrintMessage(const char * s)91 static void PrintMessage(const char *s)
92 {
93   fputs(s, stderr);
94 }
95 
PrintHelp()96 static void PrintHelp()
97 {
98   PrintMessage("\nUsage:  LZMA <e|d> inputFile outputFile [<switches>...]\n"
99              "  e: encode file\n"
100              "  d: decode file\n"
101              "  b: Benchmark\n"
102     "<Switches>\n"
103     "  -a{N}:  set compression mode - [0, 1], default: 1 (max)\n"
104     "  -d{N}:  set dictionary size - [12, 30], default: 23 (8MB)\n"
105     "  -fb{N}: set number of fast bytes - [5, 273], default: 128\n"
106     "  -mc{N}: set number of cycles for match finder\n"
107     "  -lc{N}: set number of literal context bits - [0, 8], default: 3\n"
108     "  -lp{N}: set number of literal pos bits - [0, 4], default: 0\n"
109     "  -pb{N}: set number of pos bits - [0, 4], default: 2\n"
110     "  -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4\n"
111     "  -mt{N}: set number of CPU threads\n"
112     "  -eos:   write End Of Stream marker\n"
113     "  -si:    read data from stdin\n"
114     "  -so:    write data to stdout\n"
115     );
116 }
117 
PrintHelpAndExit(const char * s)118 static void PrintHelpAndExit(const char *s)
119 {
120   fprintf(stderr, "\nError: %s\n\n", s);
121   PrintHelp();
122   throw -1;
123 }
124 
IncorrectCommand()125 static void IncorrectCommand()
126 {
127   PrintHelpAndExit("Incorrect command");
128 }
129 
WriteArgumentsToStringList(int numArgs,const char * args[],UStringVector & strings)130 static void WriteArgumentsToStringList(int numArgs, const char *args[], UStringVector &strings)
131 {
132   for (int i = 1; i < numArgs; i++)
133     strings.Add(MultiByteToUnicodeString(args[i]));
134 }
135 
GetNumber(const wchar_t * s,UInt32 & value)136 static bool GetNumber(const wchar_t *s, UInt32 &value)
137 {
138   value = 0;
139   if (*s == 0)
140     return false;
141   const wchar_t *end;
142   value = ConvertStringToUInt32(s, &end);
143   return *end == 0;
144 }
145 
ParseUInt32(const CParser & parser,unsigned index,UInt32 & res)146 static void ParseUInt32(const CParser &parser, unsigned index, UInt32 &res)
147 {
148   if (parser[index].ThereIs)
149     if (!GetNumber(parser[index].PostStrings[0], res))
150       IncorrectCommand();
151 }
152 
153 #define NT_CHECK_FAIL_ACTION PrintMessage("Unsupported Windows version"); return 1;
154 
main2(int numArgs,const char * args[])155 int main2(int numArgs, const char *args[])
156 {
157   NT_CHECK
158 
159   PrintMessage("\nLZMA " MY_VERSION_COPYRIGHT_DATE "\n");
160 
161   if (numArgs == 1)
162   {
163     PrintHelp();
164     return 0;
165   }
166 
167   bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 4);
168   if (unsupportedTypes)
169   {
170     PrintMessage("Unsupported base types. Edit Common/Types.h and recompile");
171     return 1;
172   }
173 
174   UStringVector commandStrings;
175   WriteArgumentsToStringList(numArgs, args, commandStrings);
176 
177   CParser parser(ARRAY_SIZE(kSwitchForms));
178   try
179   {
180     parser.ParseStrings(kSwitchForms, commandStrings);
181   }
182   catch(...)
183   {
184     IncorrectCommand();
185   }
186 
187   if (parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs)
188   {
189     PrintHelp();
190     return 0;
191   }
192   const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
193 
194   unsigned paramIndex = 0;
195   if (paramIndex >= nonSwitchStrings.Size())
196     IncorrectCommand();
197   const UString &command = nonSwitchStrings[paramIndex++];
198 
199   CObjectVector<CProperty> props;
200   bool dictDefined = false;
201   UInt32 dict = (UInt32)(Int32)-1;
202   if (parser[NKey::kDict].ThereIs)
203   {
204     UInt32 dicLog;
205     const UString &s = parser[NKey::kDict].PostStrings[0];
206     if (!GetNumber(s, dicLog))
207       IncorrectCommand();
208     dict = 1 << dicLog;
209     dictDefined = true;
210     CProperty prop;
211     prop.Name = L"d";
212     prop.Value = s;
213     props.Add(prop);
214   }
215   if (parser[NKey::kLevel].ThereIs)
216   {
217     UInt32 level = 5;
218     const UString &s = parser[NKey::kLevel].PostStrings[0];
219     if (!GetNumber(s, level))
220       IncorrectCommand();
221     CProperty prop;
222     prop.Name = L"x";
223     prop.Value = s;
224     props.Add(prop);
225   }
226   UString mf = L"BT4";
227   if (parser[NKey::kMatchFinder].ThereIs)
228     mf = parser[NKey::kMatchFinder].PostStrings[0];
229 
230   UInt32 numThreads = (UInt32)(Int32)-1;
231 
232   #ifndef _7ZIP_ST
233   if (parser[NKey::kMultiThread].ThereIs)
234   {
235     UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors();
236     const UString &s = parser[NKey::kMultiThread].PostStrings[0];
237     if (s.IsEmpty())
238       numThreads = numCPUs;
239     else
240       if (!GetNumber(s, numThreads))
241         IncorrectCommand();
242     CProperty prop;
243     prop.Name = L"mt";
244     prop.Value = s;
245     props.Add(prop);
246   }
247   #endif
248 
249   if (parser[NKey::kMethod].ThereIs)
250   {
251     UString s = parser[NKey::kMethod].PostStrings[0];
252     if (s.IsEmpty() || s[0] != '=')
253       IncorrectCommand();
254     CProperty prop;
255     prop.Name = L"m";
256     prop.Value = s.Ptr(1);
257     props.Add(prop);
258   }
259 
260   if (MyStringCompareNoCase(command, L"b") == 0)
261   {
262     const UInt32 kNumDefaultItereations = 1;
263     UInt32 numIterations = kNumDefaultItereations;
264     {
265       if (paramIndex < nonSwitchStrings.Size())
266         if (!GetNumber(nonSwitchStrings[paramIndex++], numIterations))
267           numIterations = kNumDefaultItereations;
268     }
269     HRESULT res = BenchCon(props, numIterations, stderr);
270     if (res != S_OK)
271     {
272       if (res != E_ABORT)
273       {
274         PrintMessage("Benchmark Error");
275         return 1;
276       }
277     }
278     return 0;
279   }
280 
281   if (numThreads == (UInt32)(Int32)-1)
282     numThreads = 1;
283 
284   bool encodeMode = false;
285   if (MyStringCompareNoCase(command, L"e") == 0)
286     encodeMode = true;
287   else if (MyStringCompareNoCase(command, L"d") == 0)
288     encodeMode = false;
289   else
290     IncorrectCommand();
291 
292   bool stdInMode = parser[NKey::kStdIn].ThereIs;
293   bool stdOutMode = parser[NKey::kStdOut].ThereIs;
294 
295   CMyComPtr<ISequentialInStream> inStream;
296   CInFileStream *inStreamSpec = 0;
297   if (stdInMode)
298   {
299     inStream = new CStdInFileStream;
300     MY_SET_BINARY_MODE(stdin);
301   }
302   else
303   {
304     if (paramIndex >= nonSwitchStrings.Size())
305       IncorrectCommand();
306     const UString &inputName = nonSwitchStrings[paramIndex++];
307     inStreamSpec = new CInFileStream;
308     inStream = inStreamSpec;
309     if (!inStreamSpec->Open(us2fs(inputName)))
310     {
311       fprintf(stderr, "\nError: can not open input file %s\n",
312           (const char *)GetOemString(inputName));
313       return 1;
314     }
315   }
316 
317   CMyComPtr<ISequentialOutStream> outStream;
318   COutFileStream *outStreamSpec = NULL;
319   if (stdOutMode)
320   {
321     outStream = new CStdOutFileStream;
322     MY_SET_BINARY_MODE(stdout);
323   }
324   else
325   {
326     if (paramIndex >= nonSwitchStrings.Size())
327       IncorrectCommand();
328     const UString &outputName = nonSwitchStrings[paramIndex++];
329     outStreamSpec = new COutFileStream;
330     outStream = outStreamSpec;
331     if (!outStreamSpec->Create(us2fs(outputName), true))
332     {
333       fprintf(stderr, "\nError: can not open output file %s\n",
334         (const char *)GetOemString(outputName));
335       return 1;
336     }
337   }
338 
339   if (parser[NKey::kFilter86].ThereIs)
340   {
341     // -f86 switch is for x86 filtered mode: BCJ + LZMA.
342     if (parser[NKey::kEOS].ThereIs || stdInMode)
343       throw "Can not use stdin in this mode";
344     UInt64 fileSize;
345     inStreamSpec->File.GetLength(fileSize);
346     if (fileSize > 0xF0000000)
347       throw "File is too big";
348     size_t inSize = (size_t)fileSize;
349     Byte *inBuffer = 0;
350     if (inSize != 0)
351     {
352       inBuffer = (Byte *)MyAlloc((size_t)inSize);
353       if (inBuffer == 0)
354         throw kCantAllocate;
355     }
356 
357     if (ReadStream_FAIL(inStream, inBuffer, inSize) != S_OK)
358       throw "Can not read";
359 
360     Byte *outBuffer = 0;
361     size_t outSize;
362     if (encodeMode)
363     {
364       // we allocate 105% of original size for output buffer
365       outSize = (size_t)fileSize / 20 * 21 + (1 << 16);
366       if (outSize != 0)
367       {
368         outBuffer = (Byte *)MyAlloc((size_t)outSize);
369         if (outBuffer == 0)
370           throw kCantAllocate;
371       }
372       if (!dictDefined)
373         dict = 1 << 23;
374       int res = Lzma86_Encode(outBuffer, &outSize, inBuffer, inSize,
375           5, dict, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO);
376       if (res != 0)
377       {
378         fprintf(stderr, "\nEncoder error = %d\n", (int)res);
379         return 1;
380       }
381     }
382     else
383     {
384       UInt64 outSize64;
385       if (Lzma86_GetUnpackSize(inBuffer, inSize, &outSize64) != 0)
386         throw "data error";
387       outSize = (size_t)outSize64;
388       if (outSize != outSize64)
389         throw "too big";
390       if (outSize != 0)
391       {
392         outBuffer = (Byte *)MyAlloc(outSize);
393         if (outBuffer == 0)
394           throw kCantAllocate;
395       }
396       int res = Lzma86_Decode(outBuffer, &outSize, inBuffer, &inSize);
397       if (inSize != (size_t)fileSize)
398         throw "incorrect processed size";
399       if (res != 0)
400         throw "LzmaDecoder error";
401     }
402     if (WriteStream(outStream, outBuffer, outSize) != S_OK)
403       throw kWriteError;
404     MyFree(outBuffer);
405     MyFree(inBuffer);
406     return 0;
407   }
408 
409 
410   UInt64 fileSize;
411   if (encodeMode)
412   {
413     NCompress::NLzma::CEncoder *encoderSpec = new NCompress::NLzma::CEncoder;
414     CMyComPtr<ICompressCoder> encoder = encoderSpec;
415 
416     if (!dictDefined)
417       dict = 1 << 23;
418 
419     UInt32 pb = 2;
420     UInt32 lc = 3; // = 0; for 32-bit data
421     UInt32 lp = 0; // = 2; for 32-bit data
422     UInt32 algo = 1;
423     UInt32 fb = 128;
424     UInt32 mc = 16 + fb / 2;
425     bool mcDefined = false;
426 
427     bool eos = parser[NKey::kEOS].ThereIs || stdInMode;
428 
429     ParseUInt32(parser, NKey::kAlgo, algo);
430     ParseUInt32(parser, NKey::kFb, fb);
431     ParseUInt32(parser, NKey::kLc, lc);
432     ParseUInt32(parser, NKey::kLp, lp);
433     ParseUInt32(parser, NKey::kPb, pb);
434 
435     mcDefined = parser[NKey::kMc].ThereIs;
436     if (mcDefined)
437       if (!GetNumber(parser[NKey::kMc].PostStrings[0], mc))
438         IncorrectCommand();
439 
440     const PROPID propIDs[] =
441     {
442       NCoderPropID::kDictionarySize,
443       NCoderPropID::kPosStateBits,
444       NCoderPropID::kLitContextBits,
445       NCoderPropID::kLitPosBits,
446       NCoderPropID::kAlgorithm,
447       NCoderPropID::kNumFastBytes,
448       NCoderPropID::kMatchFinder,
449       NCoderPropID::kEndMarker,
450       NCoderPropID::kNumThreads,
451       NCoderPropID::kMatchFinderCycles,
452     };
453     const unsigned kNumPropsMax = ARRAY_SIZE(propIDs);
454 
455     PROPVARIANT props[kNumPropsMax];
456     for (int p = 0; p < 6; p++)
457       props[p].vt = VT_UI4;
458 
459     props[0].ulVal = (UInt32)dict;
460     props[1].ulVal = (UInt32)pb;
461     props[2].ulVal = (UInt32)lc;
462     props[3].ulVal = (UInt32)lp;
463     props[4].ulVal = (UInt32)algo;
464     props[5].ulVal = (UInt32)fb;
465 
466     props[6].vt = VT_BSTR;
467     props[6].bstrVal = const_cast<BSTR>((const wchar_t *)mf);
468 
469     props[7].vt = VT_BOOL;
470     props[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE;
471 
472     props[8].vt = VT_UI4;
473     props[8].ulVal = (UInt32)numThreads;
474 
475     // it must be last in property list
476     props[9].vt = VT_UI4;
477     props[9].ulVal = (UInt32)mc;
478 
479     unsigned numProps = kNumPropsMax;
480     if (!mcDefined)
481       numProps--;
482 
483     if (encoderSpec->SetCoderProperties(propIDs, props, numProps) != S_OK)
484       IncorrectCommand();
485     encoderSpec->WriteCoderProperties(outStream);
486 
487     if (eos || stdInMode)
488       fileSize = (UInt64)(Int64)-1;
489     else
490       inStreamSpec->File.GetLength(fileSize);
491 
492     for (int i = 0; i < 8; i++)
493     {
494       Byte b = Byte(fileSize >> (8 * i));
495       if (outStream->Write(&b, 1, 0) != S_OK)
496       {
497         PrintMessage(kWriteError);
498         return 1;
499       }
500     }
501     HRESULT result = encoder->Code(inStream, outStream, 0, 0, 0);
502     if (result == E_OUTOFMEMORY)
503     {
504       PrintMessage("\nError: Can not allocate memory\n");
505       return 1;
506     }
507     else if (result != S_OK)
508     {
509       fprintf(stderr, "\nEncoder error = %X\n", (unsigned)result);
510       return 1;
511     }
512   }
513   else
514   {
515     NCompress::NLzma::CDecoder *decoderSpec = new NCompress::NLzma::CDecoder;
516     CMyComPtr<ICompressCoder> decoder = decoderSpec;
517     decoderSpec->FinishStream = true;
518     const UInt32 kPropertiesSize = 5;
519     Byte header[kPropertiesSize + 8];
520     if (ReadStream_FALSE(inStream, header, kPropertiesSize + 8) != S_OK)
521     {
522       PrintMessage(kReadError);
523       return 1;
524     }
525     if (decoderSpec->SetDecoderProperties2(header, kPropertiesSize) != S_OK)
526     {
527       PrintMessage("SetDecoderProperties error");
528       return 1;
529     }
530     fileSize = 0;
531     for (int i = 0; i < 8; i++)
532       fileSize |= ((UInt64)header[kPropertiesSize + i]) << (8 * i);
533 
534     bool isSizeDefined = (fileSize != (UInt64)(Int64)-1);
535     HRESULT res = decoder->Code(inStream, outStream, 0, isSizeDefined ? &fileSize : NULL, 0) != S_OK;
536     if (res != S_OK)
537     {
538       PrintMessage("Decoder error");
539       return 1;
540     }
541     if (isSizeDefined && decoderSpec->GetOutputProcessedSize() != fileSize)
542     {
543       PrintMessage("Error: incorrect uncompressed size in header");
544       return 1;
545     }
546   }
547   if (outStreamSpec != NULL)
548   {
549     if (outStreamSpec->Close() != S_OK)
550     {
551       PrintMessage("File closing error");
552       return 1;
553     }
554   }
555   return 0;
556 }
557 
main(int numArgs,const char * args[])558 int MY_CDECL main(int numArgs, const char *args[])
559 {
560   try { return main2(numArgs, args); }
561   catch (const char *s)
562   {
563     fprintf(stderr, "\nError: %s\n", s);
564     return 1;
565   }
566   catch(...)
567   {
568     PrintMessage("\nError\n");
569     return 1;
570   }
571 }
572