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