// 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;
}
}