/* 7zDec.c -- Decoding from 7z folder 2021-02-09 : Igor Pavlov : Public domain */ #include "Precomp.h" #include /* #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; } }