LzmaAlone.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. // LzmaAlone.cpp
  2. #include "StdAfx.h"
  3. #include "../../../Common/MyWindows.h"
  4. #include "../../../Common/MyInitGuid.h"
  5. #include <stdio.h>
  6. #if defined(_WIN32) || defined(OS2) || defined(MSDOS)
  7. #include <fcntl.h>
  8. #include <io.h>
  9. #define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY)
  10. #else
  11. #define MY_SET_BINARY_MODE(file)
  12. #endif
  13. #include "../../../Common/CommandLineParser.h"
  14. #include "../../../Common/StringConvert.h"
  15. #include "../../../Common/StringToInt.h"
  16. #include "../../Common/FileStreams.h"
  17. #include "../../Common/StreamUtils.h"
  18. #include "../LZMA/LZMADecoder.h"
  19. #include "../LZMA/LZMAEncoder.h"
  20. #include "LzmaBenchCon.h"
  21. #include "LzmaRam.h"
  22. #ifdef COMPRESS_MF_MT
  23. #include "../../../Windows/System.h"
  24. #endif
  25. #include "../../MyVersion.h"
  26. extern "C"
  27. {
  28. #include "LzmaRamDecode.h"
  29. }
  30. using namespace NCommandLineParser;
  31. #ifdef _WIN32
  32. bool g_IsNT = false;
  33. static inline bool IsItWindowsNT()
  34. {
  35. OSVERSIONINFO versionInfo;
  36. versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
  37. if (!::GetVersionEx(&versionInfo))
  38. return false;
  39. return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
  40. }
  41. #endif
  42. static const char *kCantAllocate = "Can not allocate memory";
  43. static const char *kReadError = "Read error";
  44. static const char *kWriteError = "Write error";
  45. namespace NKey {
  46. enum Enum
  47. {
  48. kHelp1 = 0,
  49. kHelp2,
  50. kMode,
  51. kDictionary,
  52. kFastBytes,
  53. kMatchFinderCycles,
  54. kLitContext,
  55. kLitPos,
  56. kPosBits,
  57. kMatchFinder,
  58. kMultiThread,
  59. kEOS,
  60. kStdIn,
  61. kStdOut,
  62. kFilter86
  63. };
  64. }
  65. static const CSwitchForm kSwitchForms[] =
  66. {
  67. { L"?", NSwitchType::kSimple, false },
  68. { L"H", NSwitchType::kSimple, false },
  69. { L"A", NSwitchType::kUnLimitedPostString, false, 1 },
  70. { L"D", NSwitchType::kUnLimitedPostString, false, 1 },
  71. { L"FB", NSwitchType::kUnLimitedPostString, false, 1 },
  72. { L"MC", NSwitchType::kUnLimitedPostString, false, 1 },
  73. { L"LC", NSwitchType::kUnLimitedPostString, false, 1 },
  74. { L"LP", NSwitchType::kUnLimitedPostString, false, 1 },
  75. { L"PB", NSwitchType::kUnLimitedPostString, false, 1 },
  76. { L"MF", NSwitchType::kUnLimitedPostString, false, 1 },
  77. { L"MT", NSwitchType::kUnLimitedPostString, false, 0 },
  78. { L"EOS", NSwitchType::kSimple, false },
  79. { L"SI", NSwitchType::kSimple, false },
  80. { L"SO", NSwitchType::kSimple, false },
  81. { L"F86", NSwitchType::kPostChar, false, 0, 0, L"+" }
  82. };
  83. static const int kNumSwitches = sizeof(kSwitchForms) / sizeof(kSwitchForms[0]);
  84. static void PrintHelp()
  85. {
  86. fprintf(stderr, "\nUsage: LZMA <e|d> inputFile outputFile [<switches>...]\n"
  87. " e: encode file\n"
  88. " d: decode file\n"
  89. " b: Benchmark\n"
  90. "<Switches>\n"
  91. " -a{N}: set compression mode - [0, 1], default: 1 (max)\n"
  92. " -d{N}: set dictionary - [0,30], default: 23 (8MB)\n"
  93. " -fb{N}: set number of fast bytes - [5, 273], default: 128\n"
  94. " -mc{N}: set number of cycles for match finder\n"
  95. " -lc{N}: set number of literal context bits - [0, 8], default: 3\n"
  96. " -lp{N}: set number of literal pos bits - [0, 4], default: 0\n"
  97. " -pb{N}: set number of pos bits - [0, 4], default: 2\n"
  98. " -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4\n"
  99. " -mt{N}: set number of CPU threads\n"
  100. " -eos: write End Of Stream marker\n"
  101. " -si: read data from stdin\n"
  102. " -so: write data to stdout\n"
  103. );
  104. }
  105. static void PrintHelpAndExit(const char *s)
  106. {
  107. fprintf(stderr, "\nError: %s\n\n", s);
  108. PrintHelp();
  109. throw -1;
  110. }
  111. static void IncorrectCommand()
  112. {
  113. PrintHelpAndExit("Incorrect command");
  114. }
  115. static void WriteArgumentsToStringList(int numArguments, const char *arguments[],
  116. UStringVector &strings)
  117. {
  118. for(int i = 1; i < numArguments; i++)
  119. strings.Add(MultiByteToUnicodeString(arguments[i]));
  120. }
  121. static bool GetNumber(const wchar_t *s, UInt32 &value)
  122. {
  123. value = 0;
  124. if (MyStringLen(s) == 0)
  125. return false;
  126. const wchar_t *end;
  127. UInt64 res = ConvertStringToUInt64(s, &end);
  128. if (*end != L'\0')
  129. return false;
  130. if (res > 0xFFFFFFFF)
  131. return false;
  132. value = UInt32(res);
  133. return true;
  134. }
  135. int main2(int n, const char *args[])
  136. {
  137. #ifdef _WIN32
  138. g_IsNT = IsItWindowsNT();
  139. #endif
  140. fprintf(stderr, "\nLZMA " MY_VERSION_COPYRIGHT_DATE "\n");
  141. if (n == 1)
  142. {
  143. PrintHelp();
  144. return 0;
  145. }
  146. bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 4);
  147. if (unsupportedTypes)
  148. {
  149. fprintf(stderr, "Unsupported base types. Edit Common/Types.h and recompile");
  150. return 1;
  151. }
  152. UStringVector commandStrings;
  153. WriteArgumentsToStringList(n, args, commandStrings);
  154. CParser parser(kNumSwitches);
  155. try
  156. {
  157. parser.ParseStrings(kSwitchForms, commandStrings);
  158. }
  159. catch(...)
  160. {
  161. IncorrectCommand();
  162. }
  163. if(parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs)
  164. {
  165. PrintHelp();
  166. return 0;
  167. }
  168. const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
  169. int paramIndex = 0;
  170. if (paramIndex >= nonSwitchStrings.Size())
  171. IncorrectCommand();
  172. const UString &command = nonSwitchStrings[paramIndex++];
  173. bool dictionaryIsDefined = false;
  174. UInt32 dictionary = (UInt32)-1;
  175. if(parser[NKey::kDictionary].ThereIs)
  176. {
  177. UInt32 dicLog;
  178. if (!GetNumber(parser[NKey::kDictionary].PostStrings[0], dicLog))
  179. IncorrectCommand();
  180. dictionary = 1 << dicLog;
  181. dictionaryIsDefined = true;
  182. }
  183. UString mf = L"BT4";
  184. if (parser[NKey::kMatchFinder].ThereIs)
  185. mf = parser[NKey::kMatchFinder].PostStrings[0];
  186. UInt32 numThreads = (UInt32)-1;
  187. #ifdef COMPRESS_MF_MT
  188. if (parser[NKey::kMultiThread].ThereIs)
  189. {
  190. UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors();
  191. const UString &s = parser[NKey::kMultiThread].PostStrings[0];
  192. if (s.IsEmpty())
  193. numThreads = numCPUs;
  194. else
  195. if (!GetNumber(s, numThreads))
  196. IncorrectCommand();
  197. }
  198. #endif
  199. if (command.CompareNoCase(L"b") == 0)
  200. {
  201. const UInt32 kNumDefaultItereations = 1;
  202. UInt32 numIterations = kNumDefaultItereations;
  203. {
  204. if (paramIndex < nonSwitchStrings.Size())
  205. if (!GetNumber(nonSwitchStrings[paramIndex++], numIterations))
  206. numIterations = kNumDefaultItereations;
  207. }
  208. return LzmaBenchCon(stderr, numIterations, numThreads, dictionary);
  209. }
  210. if (numThreads == (UInt32)-1)
  211. numThreads = 1;
  212. bool encodeMode = false;
  213. if (command.CompareNoCase(L"e") == 0)
  214. encodeMode = true;
  215. else if (command.CompareNoCase(L"d") == 0)
  216. encodeMode = false;
  217. else
  218. IncorrectCommand();
  219. bool stdInMode = parser[NKey::kStdIn].ThereIs;
  220. bool stdOutMode = parser[NKey::kStdOut].ThereIs;
  221. CMyComPtr<ISequentialInStream> inStream;
  222. CInFileStream *inStreamSpec = 0;
  223. if (stdInMode)
  224. {
  225. inStream = new CStdInFileStream;
  226. MY_SET_BINARY_MODE(stdin);
  227. }
  228. else
  229. {
  230. if (paramIndex >= nonSwitchStrings.Size())
  231. IncorrectCommand();
  232. const UString &inputName = nonSwitchStrings[paramIndex++];
  233. inStreamSpec = new CInFileStream;
  234. inStream = inStreamSpec;
  235. if (!inStreamSpec->Open(GetSystemString(inputName)))
  236. {
  237. fprintf(stderr, "\nError: can not open input file %s\n",
  238. (const char *)GetOemString(inputName));
  239. return 1;
  240. }
  241. }
  242. CMyComPtr<ISequentialOutStream> outStream;
  243. COutFileStream *outStreamSpec = NULL;
  244. if (stdOutMode)
  245. {
  246. outStream = new CStdOutFileStream;
  247. MY_SET_BINARY_MODE(stdout);
  248. }
  249. else
  250. {
  251. if (paramIndex >= nonSwitchStrings.Size())
  252. IncorrectCommand();
  253. const UString &outputName = nonSwitchStrings[paramIndex++];
  254. outStreamSpec = new COutFileStream;
  255. outStream = outStreamSpec;
  256. if (!outStreamSpec->Create(GetSystemString(outputName), true))
  257. {
  258. fprintf(stderr, "\nError: can not open output file %s\n",
  259. (const char *)GetOemString(outputName));
  260. return 1;
  261. }
  262. }
  263. if (parser[NKey::kFilter86].ThereIs)
  264. {
  265. // -f86 switch is for x86 filtered mode: BCJ + LZMA.
  266. if (parser[NKey::kEOS].ThereIs || stdInMode)
  267. throw "Can not use stdin in this mode";
  268. UInt64 fileSize;
  269. inStreamSpec->File.GetLength(fileSize);
  270. if (fileSize > 0xF0000000)
  271. throw "File is too big";
  272. UInt32 inSize = (UInt32)fileSize;
  273. Byte *inBuffer = 0;
  274. if (inSize != 0)
  275. {
  276. inBuffer = (Byte *)MyAlloc((size_t)inSize);
  277. if (inBuffer == 0)
  278. throw kCantAllocate;
  279. }
  280. UInt32 processedSize;
  281. if (ReadStream(inStream, inBuffer, (UInt32)inSize, &processedSize) != S_OK)
  282. throw "Can not read";
  283. if ((UInt32)inSize != processedSize)
  284. throw "Read size error";
  285. Byte *outBuffer = 0;
  286. size_t outSizeProcessed;
  287. if (encodeMode)
  288. {
  289. // we allocate 105% of original size for output buffer
  290. size_t outSize = (size_t)fileSize / 20 * 21 + (1 << 16);
  291. if (outSize != 0)
  292. {
  293. outBuffer = (Byte *)MyAlloc((size_t)outSize);
  294. if (outBuffer == 0)
  295. throw kCantAllocate;
  296. }
  297. if (!dictionaryIsDefined)
  298. dictionary = 1 << 23;
  299. int res = LzmaRamEncode(inBuffer, inSize, outBuffer, outSize, &outSizeProcessed,
  300. dictionary, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO);
  301. if (res != 0)
  302. {
  303. fprintf(stderr, "\nEncoder error = %d\n", (int)res);
  304. return 1;
  305. }
  306. }
  307. else
  308. {
  309. size_t outSize;
  310. if (LzmaRamGetUncompressedSize(inBuffer, inSize, &outSize) != 0)
  311. throw "data error";
  312. if (outSize != 0)
  313. {
  314. outBuffer = (Byte *)MyAlloc(outSize);
  315. if (outBuffer == 0)
  316. throw kCantAllocate;
  317. }
  318. int res = LzmaRamDecompress(inBuffer, inSize, outBuffer, outSize, &outSizeProcessed, malloc, free);
  319. if (res != 0)
  320. throw "LzmaDecoder error";
  321. }
  322. if (WriteStream(outStream, outBuffer, (UInt32)outSizeProcessed, &processedSize) != S_OK)
  323. throw kWriteError;
  324. MyFree(outBuffer);
  325. MyFree(inBuffer);
  326. return 0;
  327. }
  328. UInt64 fileSize;
  329. if (encodeMode)
  330. {
  331. NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder;
  332. CMyComPtr<ICompressCoder> encoder = encoderSpec;
  333. if (!dictionaryIsDefined)
  334. dictionary = 1 << 23;
  335. UInt32 posStateBits = 2;
  336. UInt32 litContextBits = 3; // for normal files
  337. // UInt32 litContextBits = 0; // for 32-bit data
  338. UInt32 litPosBits = 0;
  339. // UInt32 litPosBits = 2; // for 32-bit data
  340. UInt32 algorithm = 1;
  341. UInt32 numFastBytes = 128;
  342. UInt32 matchFinderCycles = 16 + numFastBytes / 2;
  343. bool matchFinderCyclesDefined = false;
  344. bool eos = parser[NKey::kEOS].ThereIs || stdInMode;
  345. if(parser[NKey::kMode].ThereIs)
  346. if (!GetNumber(parser[NKey::kMode].PostStrings[0], algorithm))
  347. IncorrectCommand();
  348. if(parser[NKey::kFastBytes].ThereIs)
  349. if (!GetNumber(parser[NKey::kFastBytes].PostStrings[0], numFastBytes))
  350. IncorrectCommand();
  351. matchFinderCyclesDefined = parser[NKey::kMatchFinderCycles].ThereIs;
  352. if (matchFinderCyclesDefined)
  353. if (!GetNumber(parser[NKey::kMatchFinderCycles].PostStrings[0], matchFinderCycles))
  354. IncorrectCommand();
  355. if(parser[NKey::kLitContext].ThereIs)
  356. if (!GetNumber(parser[NKey::kLitContext].PostStrings[0], litContextBits))
  357. IncorrectCommand();
  358. if(parser[NKey::kLitPos].ThereIs)
  359. if (!GetNumber(parser[NKey::kLitPos].PostStrings[0], litPosBits))
  360. IncorrectCommand();
  361. if(parser[NKey::kPosBits].ThereIs)
  362. if (!GetNumber(parser[NKey::kPosBits].PostStrings[0], posStateBits))
  363. IncorrectCommand();
  364. PROPID propIDs[] =
  365. {
  366. NCoderPropID::kDictionarySize,
  367. NCoderPropID::kPosStateBits,
  368. NCoderPropID::kLitContextBits,
  369. NCoderPropID::kLitPosBits,
  370. NCoderPropID::kAlgorithm,
  371. NCoderPropID::kNumFastBytes,
  372. NCoderPropID::kMatchFinder,
  373. NCoderPropID::kEndMarker,
  374. NCoderPropID::kNumThreads,
  375. NCoderPropID::kMatchFinderCycles,
  376. };
  377. const int kNumPropsMax = sizeof(propIDs) / sizeof(propIDs[0]);
  378. PROPVARIANT properties[kNumPropsMax];
  379. for (int p = 0; p < 6; p++)
  380. properties[p].vt = VT_UI4;
  381. properties[0].ulVal = (UInt32)dictionary;
  382. properties[1].ulVal = (UInt32)posStateBits;
  383. properties[2].ulVal = (UInt32)litContextBits;
  384. properties[3].ulVal = (UInt32)litPosBits;
  385. properties[4].ulVal = (UInt32)algorithm;
  386. properties[5].ulVal = (UInt32)numFastBytes;
  387. properties[6].vt = VT_BSTR;
  388. properties[6].bstrVal = (BSTR)(const wchar_t *)mf;
  389. properties[7].vt = VT_BOOL;
  390. properties[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE;
  391. properties[8].vt = VT_UI4;
  392. properties[8].ulVal = (UInt32)numThreads;
  393. // it must be last in property list
  394. properties[9].vt = VT_UI4;
  395. properties[9].ulVal = (UInt32)matchFinderCycles;
  396. int numProps = kNumPropsMax;
  397. if (!matchFinderCyclesDefined)
  398. numProps--;
  399. if (encoderSpec->SetCoderProperties(propIDs, properties, numProps) != S_OK)
  400. IncorrectCommand();
  401. encoderSpec->WriteCoderProperties(outStream);
  402. if (eos || stdInMode)
  403. fileSize = (UInt64)(Int64)-1;
  404. else
  405. inStreamSpec->File.GetLength(fileSize);
  406. for (int i = 0; i < 8; i++)
  407. {
  408. Byte b = Byte(fileSize >> (8 * i));
  409. if (outStream->Write(&b, 1, 0) != S_OK)
  410. {
  411. fprintf(stderr, kWriteError);
  412. return 1;
  413. }
  414. }
  415. HRESULT result = encoder->Code(inStream, outStream, 0, 0, 0);
  416. if (result == E_OUTOFMEMORY)
  417. {
  418. fprintf(stderr, "\nError: Can not allocate memory\n");
  419. return 1;
  420. }
  421. else if (result != S_OK)
  422. {
  423. fprintf(stderr, "\nEncoder error = %X\n", (unsigned int)result);
  424. return 1;
  425. }
  426. }
  427. else
  428. {
  429. NCompress::NLZMA::CDecoder *decoderSpec = new NCompress::NLZMA::CDecoder;
  430. CMyComPtr<ICompressCoder> decoder = decoderSpec;
  431. const UInt32 kPropertiesSize = 5;
  432. Byte properties[kPropertiesSize];
  433. UInt32 processedSize;
  434. if (ReadStream(inStream, properties, kPropertiesSize, &processedSize) != S_OK)
  435. {
  436. fprintf(stderr, kReadError);
  437. return 1;
  438. }
  439. if (processedSize != kPropertiesSize)
  440. {
  441. fprintf(stderr, kReadError);
  442. return 1;
  443. }
  444. if (decoderSpec->SetDecoderProperties2(properties, kPropertiesSize) != S_OK)
  445. {
  446. fprintf(stderr, "SetDecoderProperties error");
  447. return 1;
  448. }
  449. fileSize = 0;
  450. for (int i = 0; i < 8; i++)
  451. {
  452. Byte b;
  453. if (inStream->Read(&b, 1, &processedSize) != S_OK)
  454. {
  455. fprintf(stderr, kReadError);
  456. return 1;
  457. }
  458. if (processedSize != 1)
  459. {
  460. fprintf(stderr, kReadError);
  461. return 1;
  462. }
  463. fileSize |= ((UInt64)b) << (8 * i);
  464. }
  465. if (decoder->Code(inStream, outStream, 0, &fileSize, 0) != S_OK)
  466. {
  467. fprintf(stderr, "Decoder error");
  468. return 1;
  469. }
  470. }
  471. if (outStreamSpec != NULL)
  472. {
  473. if (outStreamSpec->Close() != S_OK)
  474. {
  475. fprintf(stderr, "File closing error");
  476. return 1;
  477. }
  478. }
  479. return 0;
  480. }
  481. int main(int n, const char *args[])
  482. {
  483. try { return main2(n, args); }
  484. catch(const char *s)
  485. {
  486. fprintf(stderr, "\nError: %s\n", s);
  487. return 1;
  488. }
  489. catch(...)
  490. {
  491. fprintf(stderr, "\nError\n");
  492. return 1;
  493. }
  494. }