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 37extern "C" 38{ 39#include "LzmaRamDecode.h" 40} 41 42using namespace NCommandLineParser; 43 44#ifdef _WIN32 45bool g_IsNT = false; 46static 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 56static const char *kCantAllocate = "Can not allocate memory"; 57static const char *kReadError = "Read error"; 58static const char *kWriteError = "Write error"; 59 60namespace NKey { 61enum 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 81static 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 100static const int kNumSwitches = sizeof(kSwitchForms) / sizeof(kSwitchForms[0]); 101 102static 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 124static void PrintHelpAndExit(const char *s) 125{ 126 fprintf(stderr, "\nError: %s\n\n", s); 127 PrintHelp(); 128 throw -1; 129} 130 131static void IncorrectCommand() 132{ 133 PrintHelpAndExit("Incorrect command"); 134} 135 136static 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 143static 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 158int 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 541int 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} 555