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