1/* 7zDec.c -- Decoding from 7z folder
22014-06-16 : Igor Pavlov : Public domain */
3
4#include "Precomp.h"
5
6#include <string.h>
7
8/* #define _7ZIP_PPMD_SUPPPORT */
9
10#include "7z.h"
11
12#include "Bcj2.h"
13#include "Bra.h"
14#include "CpuArch.h"
15#include "LzmaDec.h"
16#include "Lzma2Dec.h"
17#ifdef _7ZIP_PPMD_SUPPPORT
18#include "Ppmd7.h"
19#endif
20
21#define k_Copy 0
22#define k_LZMA2 0x21
23#define k_LZMA  0x30101
24#define k_BCJ   0x03030103
25#define k_PPC   0x03030205
26#define k_ARM   0x03030501
27#define k_ARMT  0x03030701
28#define k_SPARC 0x03030805
29#define k_BCJ2  0x0303011B
30
31#ifdef _7ZIP_PPMD_SUPPPORT
32
33#define k_PPMD 0x30401
34
35typedef struct
36{
37  IByteIn p;
38  const Byte *cur;
39  const Byte *end;
40  const Byte *begin;
41  UInt64 processed;
42  Bool extra;
43  SRes res;
44  ILookInStream *inStream;
45} CByteInToLook;
46
47static Byte ReadByte(void *pp)
48{
49  CByteInToLook *p = (CByteInToLook *)pp;
50  if (p->cur != p->end)
51    return *p->cur++;
52  if (p->res == SZ_OK)
53  {
54    size_t size = p->cur - p->begin;
55    p->processed += size;
56    p->res = p->inStream->Skip(p->inStream, size);
57    size = (1 << 25);
58    p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size);
59    p->cur = p->begin;
60    p->end = p->begin + size;
61    if (size != 0)
62      return *p->cur++;;
63  }
64  p->extra = True;
65  return 0;
66}
67
68static SRes SzDecodePpmd(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
69    Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
70{
71  CPpmd7 ppmd;
72  CByteInToLook s;
73  SRes res = SZ_OK;
74
75  s.p.Read = ReadByte;
76  s.inStream = inStream;
77  s.begin = s.end = s.cur = NULL;
78  s.extra = False;
79  s.res = SZ_OK;
80  s.processed = 0;
81
82  if (propsSize != 5)
83    return SZ_ERROR_UNSUPPORTED;
84
85  {
86    unsigned order = props[0];
87    UInt32 memSize = GetUi32(props + 1);
88    if (order < PPMD7_MIN_ORDER ||
89        order > PPMD7_MAX_ORDER ||
90        memSize < PPMD7_MIN_MEM_SIZE ||
91        memSize > PPMD7_MAX_MEM_SIZE)
92      return SZ_ERROR_UNSUPPORTED;
93    Ppmd7_Construct(&ppmd);
94    if (!Ppmd7_Alloc(&ppmd, memSize, allocMain))
95      return SZ_ERROR_MEM;
96    Ppmd7_Init(&ppmd, order);
97  }
98  {
99    CPpmd7z_RangeDec rc;
100    Ppmd7z_RangeDec_CreateVTable(&rc);
101    rc.Stream = &s.p;
102    if (!Ppmd7z_RangeDec_Init(&rc))
103      res = SZ_ERROR_DATA;
104    else if (s.extra)
105      res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
106    else
107    {
108      SizeT i;
109      for (i = 0; i < outSize; i++)
110      {
111        int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p);
112        if (s.extra || sym < 0)
113          break;
114        outBuffer[i] = (Byte)sym;
115      }
116      if (i != outSize)
117        res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
118      else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc))
119        res = SZ_ERROR_DATA;
120    }
121  }
122  Ppmd7_Free(&ppmd, allocMain);
123  return res;
124}
125
126#endif
127
128
129static SRes SzDecodeLzma(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
130    Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
131{
132  CLzmaDec state;
133  SRes res = SZ_OK;
134
135  LzmaDec_Construct(&state);
136  RINOK(LzmaDec_AllocateProbs(&state, props, propsSize, allocMain));
137  state.dic = outBuffer;
138  state.dicBufSize = outSize;
139  LzmaDec_Init(&state);
140
141  for (;;)
142  {
143    Byte *inBuf = NULL;
144    size_t lookahead = (1 << 18);
145    if (lookahead > inSize)
146      lookahead = (size_t)inSize;
147    res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead);
148    if (res != SZ_OK)
149      break;
150
151    {
152      SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos;
153      ELzmaStatus status;
154      res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
155      lookahead -= inProcessed;
156      inSize -= inProcessed;
157      if (res != SZ_OK)
158        break;
159      if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos))
160      {
161        if (state.dicBufSize != outSize || lookahead != 0 ||
162            (status != LZMA_STATUS_FINISHED_WITH_MARK &&
163             status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
164          res = SZ_ERROR_DATA;
165        break;
166      }
167      res = inStream->Skip((void *)inStream, inProcessed);
168      if (res != SZ_OK)
169        break;
170    }
171  }
172
173  LzmaDec_FreeProbs(&state, allocMain);
174  return res;
175}
176
177static SRes SzDecodeLzma2(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
178    Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
179{
180  CLzma2Dec state;
181  SRes res = SZ_OK;
182
183  Lzma2Dec_Construct(&state);
184  if (propsSize != 1)
185    return SZ_ERROR_DATA;
186  RINOK(Lzma2Dec_AllocateProbs(&state, props[0], allocMain));
187  state.decoder.dic = outBuffer;
188  state.decoder.dicBufSize = outSize;
189  Lzma2Dec_Init(&state);
190
191  for (;;)
192  {
193    Byte *inBuf = NULL;
194    size_t lookahead = (1 << 18);
195    if (lookahead > inSize)
196      lookahead = (size_t)inSize;
197    res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead);
198    if (res != SZ_OK)
199      break;
200
201    {
202      SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos;
203      ELzmaStatus status;
204      res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
205      lookahead -= inProcessed;
206      inSize -= inProcessed;
207      if (res != SZ_OK)
208        break;
209      if (state.decoder.dicPos == state.decoder.dicBufSize || (inProcessed == 0 && dicPos == state.decoder.dicPos))
210      {
211        if (state.decoder.dicBufSize != outSize || lookahead != 0 ||
212            (status != LZMA_STATUS_FINISHED_WITH_MARK))
213          res = SZ_ERROR_DATA;
214        break;
215      }
216      res = inStream->Skip((void *)inStream, inProcessed);
217      if (res != SZ_OK)
218        break;
219    }
220  }
221
222  Lzma2Dec_FreeProbs(&state, allocMain);
223  return res;
224}
225
226static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer)
227{
228  while (inSize > 0)
229  {
230    void *inBuf;
231    size_t curSize = (1 << 18);
232    if (curSize > inSize)
233      curSize = (size_t)inSize;
234    RINOK(inStream->Look((void *)inStream, (const void **)&inBuf, &curSize));
235    if (curSize == 0)
236      return SZ_ERROR_INPUT_EOF;
237    memcpy(outBuffer, inBuf, curSize);
238    outBuffer += curSize;
239    inSize -= curSize;
240    RINOK(inStream->Skip((void *)inStream, curSize));
241  }
242  return SZ_OK;
243}
244
245static Bool IS_MAIN_METHOD(UInt32 m)
246{
247  switch (m)
248  {
249    case k_Copy:
250    case k_LZMA:
251    case k_LZMA2:
252    #ifdef _7ZIP_PPMD_SUPPPORT
253    case k_PPMD:
254    #endif
255      return True;
256  }
257  return False;
258}
259
260static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c)
261{
262  return
263      c->NumInStreams == 1 &&
264      c->NumOutStreams == 1 &&
265      /* c->MethodID <= (UInt32)0xFFFFFFFF && */
266      IS_MAIN_METHOD((UInt32)c->MethodID);
267}
268
269#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1)
270
271static SRes CheckSupportedFolder(const CSzFolder *f)
272{
273  if (f->NumCoders < 1 || f->NumCoders > 4)
274    return SZ_ERROR_UNSUPPORTED;
275  if (!IS_SUPPORTED_CODER(&f->Coders[0]))
276    return SZ_ERROR_UNSUPPORTED;
277  if (f->NumCoders == 1)
278  {
279    if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0)
280      return SZ_ERROR_UNSUPPORTED;
281    return SZ_OK;
282  }
283  if (f->NumCoders == 2)
284  {
285    const CSzCoderInfo *c = &f->Coders[1];
286    if (
287        /* c->MethodID > (UInt32)0xFFFFFFFF || */
288        c->NumInStreams != 1 ||
289        c->NumOutStreams != 1 ||
290        f->NumPackStreams != 1 ||
291        f->PackStreams[0] != 0 ||
292        f->NumBindPairs != 1 ||
293        f->BindPairs[0].InIndex != 1 ||
294        f->BindPairs[0].OutIndex != 0)
295      return SZ_ERROR_UNSUPPORTED;
296    switch ((UInt32)c->MethodID)
297    {
298      case k_BCJ:
299      case k_ARM:
300        break;
301      default:
302        return SZ_ERROR_UNSUPPORTED;
303    }
304    return SZ_OK;
305  }
306  if (f->NumCoders == 4)
307  {
308    if (!IS_SUPPORTED_CODER(&f->Coders[1]) ||
309        !IS_SUPPORTED_CODER(&f->Coders[2]) ||
310        !IS_BCJ2(&f->Coders[3]))
311      return SZ_ERROR_UNSUPPORTED;
312    if (f->NumPackStreams != 4 ||
313        f->PackStreams[0] != 2 ||
314        f->PackStreams[1] != 6 ||
315        f->PackStreams[2] != 1 ||
316        f->PackStreams[3] != 0 ||
317        f->NumBindPairs != 3 ||
318        f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 ||
319        f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 ||
320        f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2)
321      return SZ_ERROR_UNSUPPORTED;
322    return SZ_OK;
323  }
324  return SZ_ERROR_UNSUPPORTED;
325}
326
327#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break;
328
329static SRes SzFolder_Decode2(const CSzFolder *folder,
330    const Byte *propsData,
331    const UInt64 *unpackSizes,
332    const UInt64 *packPositions,
333    ILookInStream *inStream, UInt64 startPos,
334    Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain,
335    Byte *tempBuf[])
336{
337  UInt32 ci;
338  SizeT tempSizes[3] = { 0, 0, 0};
339  SizeT tempSize3 = 0;
340  Byte *tempBuf3 = 0;
341
342  RINOK(CheckSupportedFolder(folder));
343
344  for (ci = 0; ci < folder->NumCoders; ci++)
345  {
346    const CSzCoderInfo *coder = &folder->Coders[ci];
347
348    if (IS_MAIN_METHOD((UInt32)coder->MethodID))
349    {
350      UInt32 si = 0;
351      UInt64 offset;
352      UInt64 inSize;
353      Byte *outBufCur = outBuffer;
354      SizeT outSizeCur = outSize;
355      if (folder->NumCoders == 4)
356      {
357        UInt32 indices[] = { 3, 2, 0 };
358        UInt64 unpackSize = unpackSizes[ci];
359        si = indices[ci];
360        if (ci < 2)
361        {
362          Byte *temp;
363          outSizeCur = (SizeT)unpackSize;
364          if (outSizeCur != unpackSize)
365            return SZ_ERROR_MEM;
366          temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur);
367          if (temp == 0 && outSizeCur != 0)
368            return SZ_ERROR_MEM;
369          outBufCur = tempBuf[1 - ci] = temp;
370          tempSizes[1 - ci] = outSizeCur;
371        }
372        else if (ci == 2)
373        {
374          if (unpackSize > outSize) /* check it */
375            return SZ_ERROR_PARAM;
376          tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize);
377          tempSize3 = outSizeCur = (SizeT)unpackSize;
378        }
379        else
380          return SZ_ERROR_UNSUPPORTED;
381      }
382      offset = packPositions[si];
383      inSize = packPositions[si + 1] - offset;
384      RINOK(LookInStream_SeekTo(inStream, startPos + offset));
385
386      if (coder->MethodID == k_Copy)
387      {
388        if (inSize != outSizeCur) /* check it */
389          return SZ_ERROR_DATA;
390        RINOK(SzDecodeCopy(inSize, inStream, outBufCur));
391      }
392      else if (coder->MethodID == k_LZMA)
393      {
394        RINOK(SzDecodeLzma(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
395      }
396      else if (coder->MethodID == k_LZMA2)
397      {
398        RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
399      }
400      else
401      {
402        #ifdef _7ZIP_PPMD_SUPPPORT
403        RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
404        #else
405        return SZ_ERROR_UNSUPPORTED;
406        #endif
407      }
408    }
409    else if (coder->MethodID == k_BCJ2)
410    {
411      UInt64 offset = packPositions[1];
412      UInt64 s3Size = packPositions[2] - offset;
413      SRes res;
414      if (ci != 3)
415        return SZ_ERROR_UNSUPPORTED;
416      RINOK(LookInStream_SeekTo(inStream, startPos + offset));
417      tempSizes[2] = (SizeT)s3Size;
418      if (tempSizes[2] != s3Size)
419        return SZ_ERROR_MEM;
420      tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]);
421      if (tempBuf[2] == 0 && tempSizes[2] != 0)
422        return SZ_ERROR_MEM;
423      res = SzDecodeCopy(s3Size, inStream, tempBuf[2]);
424      RINOK(res)
425
426      res = Bcj2_Decode(
427          tempBuf3, tempSize3,
428          tempBuf[0], tempSizes[0],
429          tempBuf[1], tempSizes[1],
430          tempBuf[2], tempSizes[2],
431          outBuffer, outSize);
432      RINOK(res)
433    }
434    else
435    {
436      if (ci != 1)
437        return SZ_ERROR_UNSUPPORTED;
438      switch (coder->MethodID)
439      {
440        case k_BCJ:
441        {
442          UInt32 state;
443          x86_Convert_Init(state);
444          x86_Convert(outBuffer, outSize, 0, &state, 0);
445          break;
446        }
447        CASE_BRA_CONV(ARM)
448        default:
449          return SZ_ERROR_UNSUPPORTED;
450      }
451    }
452  }
453  return SZ_OK;
454}
455
456SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex,
457    ILookInStream *inStream, UInt64 startPos,
458    Byte *outBuffer, size_t outSize,
459    ISzAlloc *allocMain)
460{
461  SRes res;
462  CSzFolder folder;
463  CSzData sd;
464  CSzData sdSizes;
465
466  const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex];
467  sd.Data = data;
468  sd.Size = p->FoCodersOffsets[folderIndex + 1] - p->FoCodersOffsets[folderIndex];
469
470  sdSizes.Data = p->UnpackSizesData + p->FoSizesOffsets[folderIndex];
471  sdSizes.Size =
472      p->FoSizesOffsets[folderIndex + 1] -
473      p->FoSizesOffsets[folderIndex];
474
475  res = SzGetNextFolderItem(&folder, &sd, &sdSizes);
476
477  if (res != SZ_OK)
478    return res;
479
480  if (sd.Size != 0 || outSize != folder.CodersUnpackSizes[folder.MainOutStream])
481    return SZ_ERROR_FAIL;
482  {
483    int i;
484    Byte *tempBuf[3] = { 0, 0, 0};
485    res = SzFolder_Decode2(&folder, data, folder.CodersUnpackSizes,
486        p->PackPositions + p->FoStartPackStreamIndex[folderIndex],
487        inStream, startPos,
488        outBuffer, (SizeT)outSize, allocMain, tempBuf);
489    for (i = 0; i < 3; i++)
490      IAlloc_Free(allocMain, tempBuf[i]);
491    return res;
492  }
493}
494