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