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 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 68 static 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 87 static const int kNumSwitches = sizeof(kSwitchForms) / sizeof(kSwitchForms[0]); 88 89 static void PrintMessage(const char *s) 90 { 91 fputs(s, stderr); 92 } 93 94 static 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 116 static void PrintHelpAndExit(const char *s) 117 { 118 fprintf(stderr, "\nError: %s\n\n", s); 119 PrintHelp(); 120 throw -1; 121 } 122 123 static void IncorrectCommand() 124 { 125 PrintHelpAndExit("Incorrect command"); 126 } 127 128 static 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 134 static 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 149 static 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 158 int 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 512 int 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