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