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 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 70static 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 91static void PrintMessage(const char *s) 92{ 93 fputs(s, stderr); 94} 95 96static 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 118static void PrintHelpAndExit(const char *s) 119{ 120 fprintf(stderr, "\nError: %s\n\n", s); 121 PrintHelp(); 122 throw -1; 123} 124 125static void IncorrectCommand() 126{ 127 PrintHelpAndExit("Incorrect command"); 128} 129 130static 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 136static 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 146static 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 155int 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 558int 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