Home | History | Annotate | Download | only in C
      1 /* 7zDec.c -- Decoding from 7z folder
      2 2010-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 
     33 typedef 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 
     45 static 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 
     66 static 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 
    127 static 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 
    175 static 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 
    224 static 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 
    243 static 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 
    258 static 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 
    269 static 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 
    324 static 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 
    335 static 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 
    459 SRes 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