|
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 } |