| /* 7zDec.c -- Decoding from 7z folder |
| 2021-02-09 : Igor Pavlov : Public domain */ |
| |
| #include "Precomp.h" |
| |
| #include <string.h> |
| |
| /* #define _7ZIP_PPMD_SUPPPORT */ |
| |
| #include "7z.h" |
| #include "7zCrc.h" |
| |
| #include "Bcj2.h" |
| #include "Bra.h" |
| #include "CpuArch.h" |
| #include "Delta.h" |
| #include "LzmaDec.h" |
| #include "Lzma2Dec.h" |
| #ifdef _7ZIP_PPMD_SUPPPORT |
| #include "Ppmd7.h" |
| #endif |
| |
| #define k_Copy 0 |
| #ifndef _7Z_NO_METHOD_LZMA2 |
| #define k_LZMA2 0x21 |
| #endif |
| #define k_LZMA 0x30101 |
| #define k_BCJ2 0x303011B |
| #ifndef _7Z_NO_METHODS_FILTERS |
| #define k_Delta 3 |
| #define k_BCJ 0x3030103 |
| #define k_PPC 0x3030205 |
| #define k_IA64 0x3030401 |
| #define k_ARM 0x3030501 |
| #define k_ARMT 0x3030701 |
| #define k_SPARC 0x3030805 |
| #endif |
| |
| #ifdef _7ZIP_PPMD_SUPPPORT |
| |
| #define k_PPMD 0x30401 |
| |
| typedef struct |
| { |
| IByteIn vt; |
| const Byte *cur; |
| const Byte *end; |
| const Byte *begin; |
| UInt64 processed; |
| BoolInt extra; |
| SRes res; |
| const ILookInStream *inStream; |
| } CByteInToLook; |
| |
| static Byte ReadByte(const IByteIn *pp) |
| { |
| CByteInToLook *p = CONTAINER_FROM_VTBL(pp, CByteInToLook, vt); |
| if (p->cur != p->end) |
| return *p->cur++; |
| if (p->res == SZ_OK) |
| { |
| size_t size = (size_t)(p->cur - p->begin); |
| p->processed += size; |
| p->res = ILookInStream_Skip(p->inStream, size); |
| size = (1 << 25); |
| p->res = ILookInStream_Look(p->inStream, (const void **)&p->begin, &size); |
| p->cur = p->begin; |
| p->end = p->begin + size; |
| if (size != 0) |
| return *p->cur++;; |
| } |
| p->extra = True; |
| return 0; |
| } |
| |
| static SRes SzDecodePpmd(const Byte *props, unsigned propsSize, UInt64 inSize, const ILookInStream *inStream, |
| Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain) |
| { |
| CPpmd7 ppmd; |
| CByteInToLook s; |
| SRes res = SZ_OK; |
| |
| s.vt.Read = ReadByte; |
| s.inStream = inStream; |
| s.begin = s.end = s.cur = NULL; |
| s.extra = False; |
| s.res = SZ_OK; |
| s.processed = 0; |
| |
| if (propsSize != 5) |
| return SZ_ERROR_UNSUPPORTED; |
| |
| { |
| unsigned order = props[0]; |
| UInt32 memSize = GetUi32(props + 1); |
| if (order < PPMD7_MIN_ORDER || |
| order > PPMD7_MAX_ORDER || |
| memSize < PPMD7_MIN_MEM_SIZE || |
| memSize > PPMD7_MAX_MEM_SIZE) |
| return SZ_ERROR_UNSUPPORTED; |
| Ppmd7_Construct(&ppmd); |
| if (!Ppmd7_Alloc(&ppmd, memSize, allocMain)) |
| return SZ_ERROR_MEM; |
| Ppmd7_Init(&ppmd, order); |
| } |
| { |
| ppmd.rc.dec.Stream = &s.vt; |
| if (!Ppmd7z_RangeDec_Init(&ppmd.rc.dec)) |
| res = SZ_ERROR_DATA; |
| else if (!s.extra) |
| { |
| Byte *buf = outBuffer; |
| const Byte *lim = buf + outSize; |
| for (; buf != lim; buf++) |
| { |
| int sym = Ppmd7z_DecodeSymbol(&ppmd); |
| if (s.extra || sym < 0) |
| break; |
| *buf = (Byte)sym; |
| } |
| if (buf != lim) |
| res = SZ_ERROR_DATA; |
| else if (!Ppmd7z_RangeDec_IsFinishedOK(&ppmd.rc.dec)) |
| { |
| /* if (Ppmd7z_DecodeSymbol(&ppmd) != PPMD7_SYM_END || !Ppmd7z_RangeDec_IsFinishedOK(&ppmd.rc.dec)) */ |
| res = SZ_ERROR_DATA; |
| } |
| } |
| if (s.extra) |
| res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); |
| else if (s.processed + (size_t)(s.cur - s.begin) != inSize) |
| res = SZ_ERROR_DATA; |
| } |
| Ppmd7_Free(&ppmd, allocMain); |
| return res; |
| } |
| |
| #endif |
| |
| |
| static SRes SzDecodeLzma(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, |
| Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain) |
| { |
| CLzmaDec state; |
| SRes res = SZ_OK; |
| |
| LzmaDec_Construct(&state); |
| RINOK(LzmaDec_AllocateProbs(&state, props, propsSize, allocMain)); |
| state.dic = outBuffer; |
| state.dicBufSize = outSize; |
| LzmaDec_Init(&state); |
| |
| for (;;) |
| { |
| const void *inBuf = NULL; |
| size_t lookahead = (1 << 18); |
| if (lookahead > inSize) |
| lookahead = (size_t)inSize; |
| res = ILookInStream_Look(inStream, &inBuf, &lookahead); |
| if (res != SZ_OK) |
| break; |
| |
| { |
| SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; |
| ELzmaStatus status; |
| res = LzmaDec_DecodeToDic(&state, outSize, (const Byte *)inBuf, &inProcessed, LZMA_FINISH_END, &status); |
| lookahead -= inProcessed; |
| inSize -= inProcessed; |
| if (res != SZ_OK) |
| break; |
| |
| if (status == LZMA_STATUS_FINISHED_WITH_MARK) |
| { |
| if (outSize != state.dicPos || inSize != 0) |
| res = SZ_ERROR_DATA; |
| break; |
| } |
| |
| if (outSize == state.dicPos && inSize == 0 && status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) |
| break; |
| |
| if (inProcessed == 0 && dicPos == state.dicPos) |
| { |
| res = SZ_ERROR_DATA; |
| break; |
| } |
| |
| res = ILookInStream_Skip(inStream, inProcessed); |
| if (res != SZ_OK) |
| break; |
| } |
| } |
| |
| LzmaDec_FreeProbs(&state, allocMain); |
| return res; |
| } |
| |
| |
| #ifndef _7Z_NO_METHOD_LZMA2 |
| |
| static SRes SzDecodeLzma2(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, |
| Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain) |
| { |
| CLzma2Dec state; |
| SRes res = SZ_OK; |
| |
| Lzma2Dec_Construct(&state); |
| if (propsSize != 1) |
| return SZ_ERROR_DATA; |
| RINOK(Lzma2Dec_AllocateProbs(&state, props[0], allocMain)); |
| state.decoder.dic = outBuffer; |
| state.decoder.dicBufSize = outSize; |
| Lzma2Dec_Init(&state); |
| |
| for (;;) |
| { |
| const void *inBuf = NULL; |
| size_t lookahead = (1 << 18); |
| if (lookahead > inSize) |
| lookahead = (size_t)inSize; |
| res = ILookInStream_Look(inStream, &inBuf, &lookahead); |
| if (res != SZ_OK) |
| break; |
| |
| { |
| SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos; |
| ELzmaStatus status; |
| res = Lzma2Dec_DecodeToDic(&state, outSize, (const Byte *)inBuf, &inProcessed, LZMA_FINISH_END, &status); |
| lookahead -= inProcessed; |
| inSize -= inProcessed; |
| if (res != SZ_OK) |
| break; |
| |
| if (status == LZMA_STATUS_FINISHED_WITH_MARK) |
| { |
| if (outSize != state.decoder.dicPos || inSize != 0) |
| res = SZ_ERROR_DATA; |
| break; |
| } |
| |
| if (inProcessed == 0 && dicPos == state.decoder.dicPos) |
| { |
| res = SZ_ERROR_DATA; |
| break; |
| } |
| |
| res = ILookInStream_Skip(inStream, inProcessed); |
| if (res != SZ_OK) |
| break; |
| } |
| } |
| |
| Lzma2Dec_FreeProbs(&state, allocMain); |
| return res; |
| } |
| |
| #endif |
| |
| |
| static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) |
| { |
| while (inSize > 0) |
| { |
| const void *inBuf; |
| size_t curSize = (1 << 18); |
| if (curSize > inSize) |
| curSize = (size_t)inSize; |
| RINOK(ILookInStream_Look(inStream, &inBuf, &curSize)); |
| if (curSize == 0) |
| return SZ_ERROR_INPUT_EOF; |
| memcpy(outBuffer, inBuf, curSize); |
| outBuffer += curSize; |
| inSize -= curSize; |
| RINOK(ILookInStream_Skip(inStream, curSize)); |
| } |
| return SZ_OK; |
| } |
| |
| static BoolInt IS_MAIN_METHOD(UInt32 m) |
| { |
| switch (m) |
| { |
| case k_Copy: |
| case k_LZMA: |
| #ifndef _7Z_NO_METHOD_LZMA2 |
| case k_LZMA2: |
| #endif |
| #ifdef _7ZIP_PPMD_SUPPPORT |
| case k_PPMD: |
| #endif |
| return True; |
| } |
| return False; |
| } |
| |
| static BoolInt IS_SUPPORTED_CODER(const CSzCoderInfo *c) |
| { |
| return |
| c->NumStreams == 1 |
| /* && c->MethodID <= (UInt32)0xFFFFFFFF */ |
| && IS_MAIN_METHOD((UInt32)c->MethodID); |
| } |
| |
| #define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumStreams == 4) |
| |
| static SRes CheckSupportedFolder(const CSzFolder *f) |
| { |
| if (f->NumCoders < 1 || f->NumCoders > 4) |
| return SZ_ERROR_UNSUPPORTED; |
| if (!IS_SUPPORTED_CODER(&f->Coders[0])) |
| return SZ_ERROR_UNSUPPORTED; |
| if (f->NumCoders == 1) |
| { |
| if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBonds != 0) |
| return SZ_ERROR_UNSUPPORTED; |
| return SZ_OK; |
| } |
| |
| |
| #ifndef _7Z_NO_METHODS_FILTERS |
| |
| if (f->NumCoders == 2) |
| { |
| const CSzCoderInfo *c = &f->Coders[1]; |
| if ( |
| /* c->MethodID > (UInt32)0xFFFFFFFF || */ |
| c->NumStreams != 1 |
| || f->NumPackStreams != 1 |
| || f->PackStreams[0] != 0 |
| || f->NumBonds != 1 |
| || f->Bonds[0].InIndex != 1 |
| || f->Bonds[0].OutIndex != 0) |
| return SZ_ERROR_UNSUPPORTED; |
| switch ((UInt32)c->MethodID) |
| { |
| case k_Delta: |
| case k_BCJ: |
| case k_PPC: |
| case k_IA64: |
| case k_SPARC: |
| case k_ARM: |
| case k_ARMT: |
| break; |
| default: |
| return SZ_ERROR_UNSUPPORTED; |
| } |
| return SZ_OK; |
| } |
| |
| #endif |
| |
| |
| if (f->NumCoders == 4) |
| { |
| if (!IS_SUPPORTED_CODER(&f->Coders[1]) |
| || !IS_SUPPORTED_CODER(&f->Coders[2]) |
| || !IS_BCJ2(&f->Coders[3])) |
| return SZ_ERROR_UNSUPPORTED; |
| if (f->NumPackStreams != 4 |
| || f->PackStreams[0] != 2 |
| || f->PackStreams[1] != 6 |
| || f->PackStreams[2] != 1 |
| || f->PackStreams[3] != 0 |
| || f->NumBonds != 3 |
| || f->Bonds[0].InIndex != 5 || f->Bonds[0].OutIndex != 0 |
| || f->Bonds[1].InIndex != 4 || f->Bonds[1].OutIndex != 1 |
| || f->Bonds[2].InIndex != 3 || f->Bonds[2].OutIndex != 2) |
| return SZ_ERROR_UNSUPPORTED; |
| return SZ_OK; |
| } |
| |
| return SZ_ERROR_UNSUPPORTED; |
| } |
| |
| #ifndef _7Z_NO_METHODS_FILTERS |
| #define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; |
| #endif |
| |
| static SRes SzFolder_Decode2(const CSzFolder *folder, |
| const Byte *propsData, |
| const UInt64 *unpackSizes, |
| const UInt64 *packPositions, |
| ILookInStream *inStream, UInt64 startPos, |
| Byte *outBuffer, SizeT outSize, ISzAllocPtr allocMain, |
| Byte *tempBuf[]) |
| { |
| UInt32 ci; |
| SizeT tempSizes[3] = { 0, 0, 0}; |
| SizeT tempSize3 = 0; |
| Byte *tempBuf3 = 0; |
| |
| RINOK(CheckSupportedFolder(folder)); |
| |
| for (ci = 0; ci < folder->NumCoders; ci++) |
| { |
| const CSzCoderInfo *coder = &folder->Coders[ci]; |
| |
| if (IS_MAIN_METHOD((UInt32)coder->MethodID)) |
| { |
| UInt32 si = 0; |
| UInt64 offset; |
| UInt64 inSize; |
| Byte *outBufCur = outBuffer; |
| SizeT outSizeCur = outSize; |
| if (folder->NumCoders == 4) |
| { |
| UInt32 indices[] = { 3, 2, 0 }; |
| UInt64 unpackSize = unpackSizes[ci]; |
| si = indices[ci]; |
| if (ci < 2) |
| { |
| Byte *temp; |
| outSizeCur = (SizeT)unpackSize; |
| if (outSizeCur != unpackSize) |
| return SZ_ERROR_MEM; |
| temp = (Byte *)ISzAlloc_Alloc(allocMain, outSizeCur); |
| if (!temp && outSizeCur != 0) |
| return SZ_ERROR_MEM; |
| outBufCur = tempBuf[1 - ci] = temp; |
| tempSizes[1 - ci] = outSizeCur; |
| } |
| else if (ci == 2) |
| { |
| if (unpackSize > outSize) /* check it */ |
| return SZ_ERROR_PARAM; |
| tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); |
| tempSize3 = outSizeCur = (SizeT)unpackSize; |
| } |
| else |
| return SZ_ERROR_UNSUPPORTED; |
| } |
| offset = packPositions[si]; |
| inSize = packPositions[(size_t)si + 1] - offset; |
| RINOK(LookInStream_SeekTo(inStream, startPos + offset)); |
| |
| if (coder->MethodID == k_Copy) |
| { |
| if (inSize != outSizeCur) /* check it */ |
| return SZ_ERROR_DATA; |
| RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); |
| } |
| else if (coder->MethodID == k_LZMA) |
| { |
| RINOK(SzDecodeLzma(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); |
| } |
| #ifndef _7Z_NO_METHOD_LZMA2 |
| else if (coder->MethodID == k_LZMA2) |
| { |
| RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); |
| } |
| #endif |
| #ifdef _7ZIP_PPMD_SUPPPORT |
| else if (coder->MethodID == k_PPMD) |
| { |
| RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); |
| } |
| #endif |
| else |
| return SZ_ERROR_UNSUPPORTED; |
| } |
| else if (coder->MethodID == k_BCJ2) |
| { |
| UInt64 offset = packPositions[1]; |
| UInt64 s3Size = packPositions[2] - offset; |
| |
| if (ci != 3) |
| return SZ_ERROR_UNSUPPORTED; |
| |
| tempSizes[2] = (SizeT)s3Size; |
| if (tempSizes[2] != s3Size) |
| return SZ_ERROR_MEM; |
| tempBuf[2] = (Byte *)ISzAlloc_Alloc(allocMain, tempSizes[2]); |
| if (!tempBuf[2] && tempSizes[2] != 0) |
| return SZ_ERROR_MEM; |
| |
| RINOK(LookInStream_SeekTo(inStream, startPos + offset)); |
| RINOK(SzDecodeCopy(s3Size, inStream, tempBuf[2])); |
| |
| if ((tempSizes[0] & 3) != 0 || |
| (tempSizes[1] & 3) != 0 || |
| tempSize3 + tempSizes[0] + tempSizes[1] != outSize) |
| return SZ_ERROR_DATA; |
| |
| { |
| CBcj2Dec p; |
| |
| p.bufs[0] = tempBuf3; p.lims[0] = tempBuf3 + tempSize3; |
| p.bufs[1] = tempBuf[0]; p.lims[1] = tempBuf[0] + tempSizes[0]; |
| p.bufs[2] = tempBuf[1]; p.lims[2] = tempBuf[1] + tempSizes[1]; |
| p.bufs[3] = tempBuf[2]; p.lims[3] = tempBuf[2] + tempSizes[2]; |
| |
| p.dest = outBuffer; |
| p.destLim = outBuffer + outSize; |
| |
| Bcj2Dec_Init(&p); |
| RINOK(Bcj2Dec_Decode(&p)); |
| |
| { |
| unsigned i; |
| for (i = 0; i < 4; i++) |
| if (p.bufs[i] != p.lims[i]) |
| return SZ_ERROR_DATA; |
| |
| if (!Bcj2Dec_IsFinished(&p)) |
| return SZ_ERROR_DATA; |
| |
| if (p.dest != p.destLim |
| || p.state != BCJ2_STREAM_MAIN) |
| return SZ_ERROR_DATA; |
| } |
| } |
| } |
| #ifndef _7Z_NO_METHODS_FILTERS |
| else if (ci == 1) |
| { |
| if (coder->MethodID == k_Delta) |
| { |
| if (coder->PropsSize != 1) |
| return SZ_ERROR_UNSUPPORTED; |
| { |
| Byte state[DELTA_STATE_SIZE]; |
| Delta_Init(state); |
| Delta_Decode(state, (unsigned)(propsData[coder->PropsOffset]) + 1, outBuffer, outSize); |
| } |
| } |
| else |
| { |
| if (coder->PropsSize != 0) |
| return SZ_ERROR_UNSUPPORTED; |
| switch (coder->MethodID) |
| { |
| case k_BCJ: |
| { |
| UInt32 state; |
| x86_Convert_Init(state); |
| x86_Convert(outBuffer, outSize, 0, &state, 0); |
| break; |
| } |
| CASE_BRA_CONV(PPC) |
| CASE_BRA_CONV(IA64) |
| CASE_BRA_CONV(SPARC) |
| CASE_BRA_CONV(ARM) |
| CASE_BRA_CONV(ARMT) |
| default: |
| return SZ_ERROR_UNSUPPORTED; |
| } |
| } |
| } |
| #endif |
| else |
| return SZ_ERROR_UNSUPPORTED; |
| } |
| |
| return SZ_OK; |
| } |
| |
| |
| SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex, |
| ILookInStream *inStream, UInt64 startPos, |
| Byte *outBuffer, size_t outSize, |
| ISzAllocPtr allocMain) |
| { |
| SRes res; |
| CSzFolder folder; |
| CSzData sd; |
| |
| const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex]; |
| sd.Data = data; |
| sd.Size = p->FoCodersOffsets[(size_t)folderIndex + 1] - p->FoCodersOffsets[folderIndex]; |
| |
| res = SzGetNextFolderItem(&folder, &sd); |
| |
| if (res != SZ_OK) |
| return res; |
| |
| if (sd.Size != 0 |
| || folder.UnpackStream != p->FoToMainUnpackSizeIndex[folderIndex] |
| || outSize != SzAr_GetFolderUnpackSize(p, folderIndex)) |
| return SZ_ERROR_FAIL; |
| { |
| unsigned i; |
| Byte *tempBuf[3] = { 0, 0, 0}; |
| |
| res = SzFolder_Decode2(&folder, data, |
| &p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex]], |
| p->PackPositions + p->FoStartPackStreamIndex[folderIndex], |
| inStream, startPos, |
| outBuffer, (SizeT)outSize, allocMain, tempBuf); |
| |
| for (i = 0; i < 3; i++) |
| ISzAlloc_Free(allocMain, tempBuf[i]); |
| |
| if (res == SZ_OK) |
| if (SzBitWithVals_Check(&p->FolderCRCs, folderIndex)) |
| if (CrcCalc(outBuffer, outSize) != p->FolderCRCs.Vals[folderIndex]) |
| res = SZ_ERROR_CRC; |
| |
| return res; |
| } |
| } |