// LzmaAlone.cpp | |
#include "StdAfx.h" | |
#include <stdio.h> | |
#if (defined(_WIN32) || defined(OS2) || defined(MSDOS)) && !defined(UNDER_CE) | |
#include <fcntl.h> | |
#include <io.h> | |
#define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY) | |
#else | |
#define MY_SET_BINARY_MODE(file) | |
#endif | |
// #include "../../../Common/MyWindows.h" | |
#include "../../../Common/MyInitGuid.h" | |
#include "../../../../C/7zVersion.h" | |
#include "../../../../C/Alloc.h" | |
#include "../../../../C/Lzma86.h" | |
#include "../../../Windows/NtCheck.h" | |
#ifndef _7ZIP_ST | |
#include "../../../Windows/System.h" | |
#endif | |
#include "../../../Common/CommandLineParser.h" | |
#include "../../../Common/StringConvert.h" | |
#include "../../../Common/StringToInt.h" | |
#include "../../Common/FileStreams.h" | |
#include "../../Common/StreamUtils.h" | |
#include "../../Compress/LzmaDecoder.h" | |
#include "../../Compress/LzmaEncoder.h" | |
#include "../../UI/Console/BenchCon.h" | |
using namespace NCommandLineParser; | |
static const char *kCantAllocate = "Can not allocate memory"; | |
static const char *kReadError = "Read error"; | |
static const char *kWriteError = "Write error"; | |
namespace NKey { | |
enum Enum | |
{ | |
kHelp1 = 0, | |
kHelp2, | |
kMethod, | |
kLevel, | |
kAlgo, | |
kDict, | |
kFb, | |
kMc, | |
kLc, | |
kLp, | |
kPb, | |
kMatchFinder, | |
kMultiThread, | |
kEOS, | |
kStdIn, | |
kStdOut, | |
kFilter86 | |
}; | |
} | |
static const CSwitchForm kSwitchForms[] = | |
{ | |
{ "?", NSwitchType::kSimple, false }, | |
{ "H", NSwitchType::kSimple, false }, | |
{ "MM", NSwitchType::kString, false, 1 }, | |
{ "X", NSwitchType::kString, false, 1 }, | |
{ "A", NSwitchType::kString, false, 1 }, | |
{ "D", NSwitchType::kString, false, 1 }, | |
{ "FB", NSwitchType::kString, false, 1 }, | |
{ "MC", NSwitchType::kString, false, 1 }, | |
{ "LC", NSwitchType::kString, false, 1 }, | |
{ "LP", NSwitchType::kString, false, 1 }, | |
{ "PB", NSwitchType::kString, false, 1 }, | |
{ "MF", NSwitchType::kString, false, 1 }, | |
{ "MT", NSwitchType::kString, false, 0 }, | |
{ "EOS", NSwitchType::kSimple, false }, | |
{ "SI", NSwitchType::kSimple, false }, | |
{ "SO", NSwitchType::kSimple, false }, | |
{ "F86", NSwitchType::kChar, false, 0, "+" } | |
}; | |
static void PrintMessage(const char *s) | |
{ | |
fputs(s, stderr); | |
} | |
static void PrintHelp() | |
{ | |
PrintMessage("\nUsage: LZMA <e|d> inputFile outputFile [<switches>...]\n" | |
" e: encode file\n" | |
" d: decode file\n" | |
" b: Benchmark\n" | |
"<Switches>\n" | |
" -a{N}: set compression mode - [0, 1], default: 1 (max)\n" | |
" -d{N}: set dictionary size - [12, 30], default: 23 (8MB)\n" | |
" -fb{N}: set number of fast bytes - [5, 273], default: 128\n" | |
" -mc{N}: set number of cycles for match finder\n" | |
" -lc{N}: set number of literal context bits - [0, 8], default: 3\n" | |
" -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" | |
" -pb{N}: set number of pos bits - [0, 4], default: 2\n" | |
" -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4\n" | |
" -mt{N}: set number of CPU threads\n" | |
" -eos: write End Of Stream marker\n" | |
" -si: read data from stdin\n" | |
" -so: write data to stdout\n" | |
); | |
} | |
static void PrintHelpAndExit(const char *s) | |
{ | |
fprintf(stderr, "\nError: %s\n\n", s); | |
PrintHelp(); | |
throw -1; | |
} | |
static void IncorrectCommand() | |
{ | |
PrintHelpAndExit("Incorrect command"); | |
} | |
static void WriteArgumentsToStringList(int numArgs, const char *args[], UStringVector &strings) | |
{ | |
for (int i = 1; i < numArgs; i++) | |
strings.Add(MultiByteToUnicodeString(args[i])); | |
} | |
static bool GetNumber(const wchar_t *s, UInt32 &value) | |
{ | |
value = 0; | |
if (*s == 0) | |
return false; | |
const wchar_t *end; | |
value = ConvertStringToUInt32(s, &end); | |
return *end == 0; | |
} | |
static void ParseUInt32(const CParser &parser, unsigned index, UInt32 &res) | |
{ | |
if (parser[index].ThereIs) | |
if (!GetNumber(parser[index].PostStrings[0], res)) | |
IncorrectCommand(); | |
} | |
#define NT_CHECK_FAIL_ACTION PrintMessage("Unsupported Windows version"); return 1; | |
int main2(int numArgs, const char *args[]) | |
{ | |
NT_CHECK | |
PrintMessage("\nLZMA " MY_VERSION_COPYRIGHT_DATE "\n"); | |
if (numArgs == 1) | |
{ | |
PrintHelp(); | |
return 0; | |
} | |
bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 4); | |
if (unsupportedTypes) | |
{ | |
PrintMessage("Unsupported base types. Edit Common/Types.h and recompile"); | |
return 1; | |
} | |
UStringVector commandStrings; | |
WriteArgumentsToStringList(numArgs, args, commandStrings); | |
CParser parser(ARRAY_SIZE(kSwitchForms)); | |
try | |
{ | |
parser.ParseStrings(kSwitchForms, commandStrings); | |
} | |
catch(...) | |
{ | |
IncorrectCommand(); | |
} | |
if (parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs) | |
{ | |
PrintHelp(); | |
return 0; | |
} | |
const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; | |
unsigned paramIndex = 0; | |
if (paramIndex >= nonSwitchStrings.Size()) | |
IncorrectCommand(); | |
const UString &command = nonSwitchStrings[paramIndex++]; | |
CObjectVector<CProperty> props; | |
bool dictDefined = false; | |
UInt32 dict = (UInt32)(Int32)-1; | |
if (parser[NKey::kDict].ThereIs) | |
{ | |
UInt32 dicLog; | |
const UString &s = parser[NKey::kDict].PostStrings[0]; | |
if (!GetNumber(s, dicLog)) | |
IncorrectCommand(); | |
dict = 1 << dicLog; | |
dictDefined = true; | |
CProperty prop; | |
prop.Name = L"d"; | |
prop.Value = s; | |
props.Add(prop); | |
} | |
if (parser[NKey::kLevel].ThereIs) | |
{ | |
UInt32 level = 5; | |
const UString &s = parser[NKey::kLevel].PostStrings[0]; | |
if (!GetNumber(s, level)) | |
IncorrectCommand(); | |
CProperty prop; | |
prop.Name = L"x"; | |
prop.Value = s; | |
props.Add(prop); | |
} | |
UString mf = L"BT4"; | |
if (parser[NKey::kMatchFinder].ThereIs) | |
mf = parser[NKey::kMatchFinder].PostStrings[0]; | |
UInt32 numThreads = (UInt32)(Int32)-1; | |
#ifndef _7ZIP_ST | |
if (parser[NKey::kMultiThread].ThereIs) | |
{ | |
UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors(); | |
const UString &s = parser[NKey::kMultiThread].PostStrings[0]; | |
if (s.IsEmpty()) | |
numThreads = numCPUs; | |
else | |
if (!GetNumber(s, numThreads)) | |
IncorrectCommand(); | |
CProperty prop; | |
prop.Name = L"mt"; | |
prop.Value = s; | |
props.Add(prop); | |
} | |
#endif | |
if (parser[NKey::kMethod].ThereIs) | |
{ | |
UString s = parser[NKey::kMethod].PostStrings[0]; | |
if (s.IsEmpty() || s[0] != '=') | |
IncorrectCommand(); | |
CProperty prop; | |
prop.Name = L"m"; | |
prop.Value = s.Ptr(1); | |
props.Add(prop); | |
} | |
if (MyStringCompareNoCase(command, L"b") == 0) | |
{ | |
const UInt32 kNumDefaultItereations = 1; | |
UInt32 numIterations = kNumDefaultItereations; | |
{ | |
if (paramIndex < nonSwitchStrings.Size()) | |
if (!GetNumber(nonSwitchStrings[paramIndex++], numIterations)) | |
numIterations = kNumDefaultItereations; | |
} | |
HRESULT res = BenchCon(props, numIterations, stderr); | |
if (res != S_OK) | |
{ | |
if (res != E_ABORT) | |
{ | |
PrintMessage("Benchmark Error"); | |
return 1; | |
} | |
} | |
return 0; | |
} | |
if (numThreads == (UInt32)(Int32)-1) | |
numThreads = 1; | |
bool encodeMode = false; | |
if (MyStringCompareNoCase(command, L"e") == 0) | |
encodeMode = true; | |
else if (MyStringCompareNoCase(command, L"d") == 0) | |
encodeMode = false; | |
else | |
IncorrectCommand(); | |
bool stdInMode = parser[NKey::kStdIn].ThereIs; | |
bool stdOutMode = parser[NKey::kStdOut].ThereIs; | |
CMyComPtr<ISequentialInStream> inStream; | |
CInFileStream *inStreamSpec = 0; | |
if (stdInMode) | |
{ | |
inStream = new CStdInFileStream; | |
MY_SET_BINARY_MODE(stdin); | |
} | |
else | |
{ | |
if (paramIndex >= nonSwitchStrings.Size()) | |
IncorrectCommand(); | |
const UString &inputName = nonSwitchStrings[paramIndex++]; | |
inStreamSpec = new CInFileStream; | |
inStream = inStreamSpec; | |
if (!inStreamSpec->Open(us2fs(inputName))) | |
{ | |
fprintf(stderr, "\nError: can not open input file %s\n", | |
(const char *)GetOemString(inputName)); | |
return 1; | |
} | |
} | |
CMyComPtr<ISequentialOutStream> outStream; | |
COutFileStream *outStreamSpec = NULL; | |
if (stdOutMode) | |
{ | |
outStream = new CStdOutFileStream; | |
MY_SET_BINARY_MODE(stdout); | |
} | |
else | |
{ | |
if (paramIndex >= nonSwitchStrings.Size()) | |
IncorrectCommand(); | |
const UString &outputName = nonSwitchStrings[paramIndex++]; | |
outStreamSpec = new COutFileStream; | |
outStream = outStreamSpec; | |
if (!outStreamSpec->Create(us2fs(outputName), true)) | |
{ | |
fprintf(stderr, "\nError: can not open output file %s\n", | |
(const char *)GetOemString(outputName)); | |
return 1; | |
} | |
} | |
if (parser[NKey::kFilter86].ThereIs) | |
{ | |
// -f86 switch is for x86 filtered mode: BCJ + LZMA. | |
if (parser[NKey::kEOS].ThereIs || stdInMode) | |
throw "Can not use stdin in this mode"; | |
UInt64 fileSize; | |
inStreamSpec->File.GetLength(fileSize); | |
if (fileSize > 0xF0000000) | |
throw "File is too big"; | |
size_t inSize = (size_t)fileSize; | |
Byte *inBuffer = 0; | |
if (inSize != 0) | |
{ | |
inBuffer = (Byte *)MyAlloc((size_t)inSize); | |
if (inBuffer == 0) | |
throw kCantAllocate; | |
} | |
if (ReadStream_FAIL(inStream, inBuffer, inSize) != S_OK) | |
throw "Can not read"; | |
Byte *outBuffer = 0; | |
size_t outSize; | |
if (encodeMode) | |
{ | |
// we allocate 105% of original size for output buffer | |
outSize = (size_t)fileSize / 20 * 21 + (1 << 16); | |
if (outSize != 0) | |
{ | |
outBuffer = (Byte *)MyAlloc((size_t)outSize); | |
if (outBuffer == 0) | |
throw kCantAllocate; | |
} | |
if (!dictDefined) | |
dict = 1 << 23; | |
int res = Lzma86_Encode(outBuffer, &outSize, inBuffer, inSize, | |
5, dict, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO); | |
if (res != 0) | |
{ | |
fprintf(stderr, "\nEncoder error = %d\n", (int)res); | |
return 1; | |
} | |
} | |
else | |
{ | |
UInt64 outSize64; | |
if (Lzma86_GetUnpackSize(inBuffer, inSize, &outSize64) != 0) | |
throw "data error"; | |
outSize = (size_t)outSize64; | |
if (outSize != outSize64) | |
throw "too big"; | |
if (outSize != 0) | |
{ | |
outBuffer = (Byte *)MyAlloc(outSize); | |
if (outBuffer == 0) | |
throw kCantAllocate; | |
} | |
int res = Lzma86_Decode(outBuffer, &outSize, inBuffer, &inSize); | |
if (inSize != (size_t)fileSize) | |
throw "incorrect processed size"; | |
if (res != 0) | |
throw "LzmaDecoder error"; | |
} | |
if (WriteStream(outStream, outBuffer, outSize) != S_OK) | |
throw kWriteError; | |
MyFree(outBuffer); | |
MyFree(inBuffer); | |
return 0; | |
} | |
UInt64 fileSize; | |
if (encodeMode) | |
{ | |
NCompress::NLzma::CEncoder *encoderSpec = new NCompress::NLzma::CEncoder; | |
CMyComPtr<ICompressCoder> encoder = encoderSpec; | |
if (!dictDefined) | |
dict = 1 << 23; | |
UInt32 pb = 2; | |
UInt32 lc = 3; // = 0; for 32-bit data | |
UInt32 lp = 0; // = 2; for 32-bit data | |
UInt32 algo = 1; | |
UInt32 fb = 128; | |
UInt32 mc = 16 + fb / 2; | |
bool mcDefined = false; | |
bool eos = parser[NKey::kEOS].ThereIs || stdInMode; | |
ParseUInt32(parser, NKey::kAlgo, algo); | |
ParseUInt32(parser, NKey::kFb, fb); | |
ParseUInt32(parser, NKey::kLc, lc); | |
ParseUInt32(parser, NKey::kLp, lp); | |
ParseUInt32(parser, NKey::kPb, pb); | |
mcDefined = parser[NKey::kMc].ThereIs; | |
if (mcDefined) | |
if (!GetNumber(parser[NKey::kMc].PostStrings[0], mc)) | |
IncorrectCommand(); | |
const PROPID propIDs[] = | |
{ | |
NCoderPropID::kDictionarySize, | |
NCoderPropID::kPosStateBits, | |
NCoderPropID::kLitContextBits, | |
NCoderPropID::kLitPosBits, | |
NCoderPropID::kAlgorithm, | |
NCoderPropID::kNumFastBytes, | |
NCoderPropID::kMatchFinder, | |
NCoderPropID::kEndMarker, | |
NCoderPropID::kNumThreads, | |
NCoderPropID::kMatchFinderCycles, | |
}; | |
const unsigned kNumPropsMax = ARRAY_SIZE(propIDs); | |
PROPVARIANT props[kNumPropsMax]; | |
for (int p = 0; p < 6; p++) | |
props[p].vt = VT_UI4; | |
props[0].ulVal = (UInt32)dict; | |
props[1].ulVal = (UInt32)pb; | |
props[2].ulVal = (UInt32)lc; | |
props[3].ulVal = (UInt32)lp; | |
props[4].ulVal = (UInt32)algo; | |
props[5].ulVal = (UInt32)fb; | |
props[6].vt = VT_BSTR; | |
props[6].bstrVal = const_cast<BSTR>((const wchar_t *)mf); | |
props[7].vt = VT_BOOL; | |
props[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE; | |
props[8].vt = VT_UI4; | |
props[8].ulVal = (UInt32)numThreads; | |
// it must be last in property list | |
props[9].vt = VT_UI4; | |
props[9].ulVal = (UInt32)mc; | |
unsigned numProps = kNumPropsMax; | |
if (!mcDefined) | |
numProps--; | |
if (encoderSpec->SetCoderProperties(propIDs, props, numProps) != S_OK) | |
IncorrectCommand(); | |
encoderSpec->WriteCoderProperties(outStream); | |
if (eos || stdInMode) | |
fileSize = (UInt64)(Int64)-1; | |
else | |
inStreamSpec->File.GetLength(fileSize); | |
for (int i = 0; i < 8; i++) | |
{ | |
Byte b = Byte(fileSize >> (8 * i)); | |
if (outStream->Write(&b, 1, 0) != S_OK) | |
{ | |
PrintMessage(kWriteError); | |
return 1; | |
} | |
} | |
HRESULT result = encoder->Code(inStream, outStream, 0, 0, 0); | |
if (result == E_OUTOFMEMORY) | |
{ | |
PrintMessage("\nError: Can not allocate memory\n"); | |
return 1; | |
} | |
else if (result != S_OK) | |
{ | |
fprintf(stderr, "\nEncoder error = %X\n", (unsigned)result); | |
return 1; | |
} | |
} | |
else | |
{ | |
NCompress::NLzma::CDecoder *decoderSpec = new NCompress::NLzma::CDecoder; | |
CMyComPtr<ICompressCoder> decoder = decoderSpec; | |
decoderSpec->FinishStream = true; | |
const UInt32 kPropertiesSize = 5; | |
Byte header[kPropertiesSize + 8]; | |
if (ReadStream_FALSE(inStream, header, kPropertiesSize + 8) != S_OK) | |
{ | |
PrintMessage(kReadError); | |
return 1; | |
} | |
if (decoderSpec->SetDecoderProperties2(header, kPropertiesSize) != S_OK) | |
{ | |
PrintMessage("SetDecoderProperties error"); | |
return 1; | |
} | |
fileSize = 0; | |
for (int i = 0; i < 8; i++) | |
fileSize |= ((UInt64)header[kPropertiesSize + i]) << (8 * i); | |
bool isSizeDefined = (fileSize != (UInt64)(Int64)-1); | |
HRESULT res = decoder->Code(inStream, outStream, 0, isSizeDefined ? &fileSize : NULL, 0) != S_OK; | |
if (res != S_OK) | |
{ | |
PrintMessage("Decoder error"); | |
return 1; | |
} | |
if (isSizeDefined && decoderSpec->GetOutputProcessedSize() != fileSize) | |
{ | |
PrintMessage("Error: incorrect uncompressed size in header"); | |
return 1; | |
} | |
} | |
if (outStreamSpec != NULL) | |
{ | |
if (outStreamSpec->Close() != S_OK) | |
{ | |
PrintMessage("File closing error"); | |
return 1; | |
} | |
} | |
return 0; | |
} | |
int MY_CDECL main(int numArgs, const char *args[]) | |
{ | |
try { return main2(numArgs, args); } | |
catch (const char *s) | |
{ | |
fprintf(stderr, "\nError: %s\n", s); | |
return 1; | |
} | |
catch(...) | |
{ | |
PrintMessage("\nError\n"); | |
return 1; | |
} | |
} |