1/* LzmaUtil.c -- Test application for LZMA compression
22014-12-31 : Igor Pavlov : Public domain */
3
4#include "../../Precomp.h"
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10#include "../../Alloc.h"
11#include "../../7zFile.h"
12#include "../../7zVersion.h"
13#include "../../LzmaDec.h"
14#include "../../LzmaEnc.h"
15
16const char *kCantReadMessage = "Can not read input file";
17const char *kCantWriteMessage = "Can not write output file";
18const char *kCantAllocateMessage = "Can not allocate memory";
19const char *kDataErrorMessage = "Data error";
20
21static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
22static void SzFree(void *p, void *address) { p = p; MyFree(address); }
23static ISzAlloc g_Alloc = { SzAlloc, SzFree };
24
25void PrintHelp(char *buffer)
26{
27  strcat(buffer, "\nLZMA Utility " MY_VERSION_COPYRIGHT_DATE "\n"
28      "\nUsage:  lzma <e|d> inputFile outputFile\n"
29             "  e: encode file\n"
30             "  d: decode file\n");
31}
32
33int PrintError(char *buffer, const char *message)
34{
35  strcat(buffer, "\nError: ");
36  strcat(buffer, message);
37  strcat(buffer, "\n");
38  return 1;
39}
40
41int PrintErrorNumber(char *buffer, SRes val)
42{
43  sprintf(buffer + strlen(buffer), "\nError code: %x\n", (unsigned)val);
44  return 1;
45}
46
47int PrintUserError(char *buffer)
48{
49  return PrintError(buffer, "Incorrect command");
50}
51
52#define IN_BUF_SIZE (1 << 16)
53#define OUT_BUF_SIZE (1 << 16)
54
55static SRes Decode2(CLzmaDec *state, ISeqOutStream *outStream, ISeqInStream *inStream,
56    UInt64 unpackSize)
57{
58  int thereIsSize = (unpackSize != (UInt64)(Int64)-1);
59  Byte inBuf[IN_BUF_SIZE];
60  Byte outBuf[OUT_BUF_SIZE];
61  size_t inPos = 0, inSize = 0, outPos = 0;
62  LzmaDec_Init(state);
63  for (;;)
64  {
65    if (inPos == inSize)
66    {
67      inSize = IN_BUF_SIZE;
68      RINOK(inStream->Read(inStream, inBuf, &inSize));
69      inPos = 0;
70    }
71    {
72      SRes res;
73      SizeT inProcessed = inSize - inPos;
74      SizeT outProcessed = OUT_BUF_SIZE - outPos;
75      ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
76      ELzmaStatus status;
77      if (thereIsSize && outProcessed > unpackSize)
78      {
79        outProcessed = (SizeT)unpackSize;
80        finishMode = LZMA_FINISH_END;
81      }
82
83      res = LzmaDec_DecodeToBuf(state, outBuf + outPos, &outProcessed,
84        inBuf + inPos, &inProcessed, finishMode, &status);
85      inPos += inProcessed;
86      outPos += outProcessed;
87      unpackSize -= outProcessed;
88
89      if (outStream)
90        if (outStream->Write(outStream, outBuf, outPos) != outPos)
91          return SZ_ERROR_WRITE;
92
93      outPos = 0;
94
95      if (res != SZ_OK || (thereIsSize && unpackSize == 0))
96        return res;
97
98      if (inProcessed == 0 && outProcessed == 0)
99      {
100        if (thereIsSize || status != LZMA_STATUS_FINISHED_WITH_MARK)
101          return SZ_ERROR_DATA;
102        return res;
103      }
104    }
105  }
106}
107
108static SRes Decode(ISeqOutStream *outStream, ISeqInStream *inStream)
109{
110  UInt64 unpackSize;
111  int i;
112  SRes res = 0;
113
114  CLzmaDec state;
115
116  /* header: 5 bytes of LZMA properties and 8 bytes of uncompressed size */
117  unsigned char header[LZMA_PROPS_SIZE + 8];
118
119  /* Read and parse header */
120
121  RINOK(SeqInStream_Read(inStream, header, sizeof(header)));
122
123  unpackSize = 0;
124  for (i = 0; i < 8; i++)
125    unpackSize += (UInt64)header[LZMA_PROPS_SIZE + i] << (i * 8);
126
127  LzmaDec_Construct(&state);
128  RINOK(LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc));
129  res = Decode2(&state, outStream, inStream, unpackSize);
130  LzmaDec_Free(&state, &g_Alloc);
131  return res;
132}
133
134static SRes Encode(ISeqOutStream *outStream, ISeqInStream *inStream, UInt64 fileSize, char *rs)
135{
136  CLzmaEncHandle enc;
137  SRes res;
138  CLzmaEncProps props;
139
140  rs = rs;
141
142  enc = LzmaEnc_Create(&g_Alloc);
143  if (enc == 0)
144    return SZ_ERROR_MEM;
145
146  LzmaEncProps_Init(&props);
147  res = LzmaEnc_SetProps(enc, &props);
148
149  if (res == SZ_OK)
150  {
151    Byte header[LZMA_PROPS_SIZE + 8];
152    size_t headerSize = LZMA_PROPS_SIZE;
153    int i;
154
155    res = LzmaEnc_WriteProperties(enc, header, &headerSize);
156    for (i = 0; i < 8; i++)
157      header[headerSize++] = (Byte)(fileSize >> (8 * i));
158    if (outStream->Write(outStream, header, headerSize) != headerSize)
159      res = SZ_ERROR_WRITE;
160    else
161    {
162      if (res == SZ_OK)
163        res = LzmaEnc_Encode(enc, outStream, inStream, NULL, &g_Alloc, &g_Alloc);
164    }
165  }
166  LzmaEnc_Destroy(enc, &g_Alloc, &g_Alloc);
167  return res;
168}
169
170int main2(int numArgs, const char *args[], char *rs)
171{
172  CFileSeqInStream inStream;
173  CFileOutStream outStream;
174  char c;
175  int res;
176  int encodeMode;
177  Bool useOutFile = False;
178
179  FileSeqInStream_CreateVTable(&inStream);
180  File_Construct(&inStream.file);
181
182  FileOutStream_CreateVTable(&outStream);
183  File_Construct(&outStream.file);
184
185  if (numArgs == 1)
186  {
187    PrintHelp(rs);
188    return 0;
189  }
190
191  if (numArgs < 3 || numArgs > 4 || strlen(args[1]) != 1)
192    return PrintUserError(rs);
193
194  c = args[1][0];
195  encodeMode = (c == 'e' || c == 'E');
196  if (!encodeMode && c != 'd' && c != 'D')
197    return PrintUserError(rs);
198
199  {
200    size_t t4 = sizeof(UInt32);
201    size_t t8 = sizeof(UInt64);
202    if (t4 != 4 || t8 != 8)
203      return PrintError(rs, "Incorrect UInt32 or UInt64");
204  }
205
206  if (InFile_Open(&inStream.file, args[2]) != 0)
207    return PrintError(rs, "Can not open input file");
208
209  if (numArgs > 3)
210  {
211    useOutFile = True;
212    if (OutFile_Open(&outStream.file, args[3]) != 0)
213      return PrintError(rs, "Can not open output file");
214  }
215  else if (encodeMode)
216    PrintUserError(rs);
217
218  if (encodeMode)
219  {
220    UInt64 fileSize;
221    File_GetLength(&inStream.file, &fileSize);
222    res = Encode(&outStream.s, &inStream.s, fileSize, rs);
223  }
224  else
225  {
226    res = Decode(&outStream.s, useOutFile ? &inStream.s : NULL);
227  }
228
229  if (useOutFile)
230    File_Close(&outStream.file);
231  File_Close(&inStream.file);
232
233  if (res != SZ_OK)
234  {
235    if (res == SZ_ERROR_MEM)
236      return PrintError(rs, kCantAllocateMessage);
237    else if (res == SZ_ERROR_DATA)
238      return PrintError(rs, kDataErrorMessage);
239    else if (res == SZ_ERROR_WRITE)
240      return PrintError(rs, kCantWriteMessage);
241    else if (res == SZ_ERROR_READ)
242      return PrintError(rs, kCantReadMessage);
243    return PrintErrorNumber(rs, res);
244  }
245  return 0;
246}
247
248int MY_CDECL main(int numArgs, const char *args[])
249{
250  char rs[800] = { 0 };
251  int res = main2(numArgs, args, rs);
252  fputs(rs, stdout);
253  return res;
254}
255