/* 7zDecode.c -- Decoding from 7z folder 2008-11-23 : Igor Pavlov : Public domain */ #include <string.h> #include "../../Bcj2.h" #include "../../Bra.h" #include "../../LzmaDec.h" #include "7zDecode.h" #define k_Copy 0 #define k_LZMA 0x30101 #define k_BCJ 0x03030103 #define k_BCJ2 0x0303011B static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) { CLzmaDec state; SRes res = SZ_OK; LzmaDec_Construct(&state); RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain)); state.dic = outBuffer; state.dicBufSize = outSize; LzmaDec_Init(&state); for (;;) { Byte *inBuf = NULL; size_t lookahead = (1 << 18); if (lookahead > inSize) lookahead = (size_t)inSize; res = inStream->Look((void *)inStream, (void **)&inBuf, &lookahead); if (res != SZ_OK) break; { SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; ELzmaStatus status; res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); lookahead -= inProcessed; inSize -= inProcessed; if (res != SZ_OK) break; if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos)) { if (state.dicBufSize != outSize || lookahead != 0 || (status != LZMA_STATUS_FINISHED_WITH_MARK && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) res = SZ_ERROR_DATA; break; } res = inStream->Skip((void *)inStream, inProcessed); if (res != SZ_OK) break; } } LzmaDec_FreeProbs(&state, allocMain); return res; } static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) { while (inSize > 0) { void *inBuf; size_t curSize = (1 << 18); if (curSize > inSize) curSize = (size_t)inSize; RINOK(inStream->Look((void *)inStream, (void **)&inBuf, &curSize)); if (curSize == 0) return SZ_ERROR_INPUT_EOF; memcpy(outBuffer, inBuf, curSize); outBuffer += curSize; inSize -= curSize; RINOK(inStream->Skip((void *)inStream, curSize)); } return SZ_OK; } #define IS_UNSUPPORTED_METHOD(m) ((m) != k_Copy && (m) != k_LZMA) #define IS_UNSUPPORTED_CODER(c) (IS_UNSUPPORTED_METHOD(c.MethodID) || c.NumInStreams != 1 || c.NumOutStreams != 1) #define IS_NO_BCJ(c) (c.MethodID != k_BCJ || c.NumInStreams != 1 || c.NumOutStreams != 1) #define IS_NO_BCJ2(c) (c.MethodID != k_BCJ2 || c.NumInStreams != 4 || c.NumOutStreams != 1) SRes CheckSupportedFolder(const CSzFolder *f) { if (f->NumCoders < 1 || f->NumCoders > 4) return SZ_ERROR_UNSUPPORTED; if (IS_UNSUPPORTED_CODER(f->Coders[0])) return SZ_ERROR_UNSUPPORTED; if (f->NumCoders == 1) { if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) return SZ_ERROR_UNSUPPORTED; return SZ_OK; } if (f->NumCoders == 2) { if (IS_NO_BCJ(f->Coders[1]) || f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 1 || f->BindPairs[0].InIndex != 1 || f->BindPairs[0].OutIndex != 0) return SZ_ERROR_UNSUPPORTED; return SZ_OK; } if (f->NumCoders == 4) { if (IS_UNSUPPORTED_CODER(f->Coders[1]) || IS_UNSUPPORTED_CODER(f->Coders[2]) || IS_NO_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->NumBindPairs != 3 || f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) return SZ_ERROR_UNSUPPORTED; return SZ_OK; } return SZ_ERROR_UNSUPPORTED; } UInt64 GetSum(const UInt64 *values, UInt32 index) { UInt64 sum = 0; UInt32 i; for (i = 0; i < index; i++) sum += values[i]; return sum; } SRes SzDecode2(const UInt64 *packSizes, const CSzFolder *folder, ILookInStream *inStream, UInt64 startPos, Byte *outBuffer, SizeT outSize, ISzAlloc *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++) { CSzCoderInfo *coder = &folder->Coders[ci]; if (coder->MethodID == k_Copy || coder->MethodID == k_LZMA) { UInt32 si = 0; UInt64 offset; UInt64 inSize; Byte *outBufCur = outBuffer; SizeT outSizeCur = outSize; if (folder->NumCoders == 4) { UInt32 indices[] = { 3, 2, 0 }; UInt64 unpackSize = folder->UnpackSizes[ci]; si = indices[ci]; if (ci < 2) { Byte *temp; outSizeCur = (SizeT)unpackSize; if (outSizeCur != unpackSize) return SZ_ERROR_MEM; temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); if (temp == 0 && 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 = GetSum(packSizes, si); inSize = packSizes[si]; 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 { RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); } } else if (coder->MethodID == k_BCJ) { UInt32 state; if (ci != 1) return SZ_ERROR_UNSUPPORTED; x86_Convert_Init(state); x86_Convert(outBuffer, outSize, 0, &state, 0); } else if (coder->MethodID == k_BCJ2) { UInt64 offset = GetSum(packSizes, 1); UInt64 s3Size = packSizes[1]; SRes res; if (ci != 3) return SZ_ERROR_UNSUPPORTED; RINOK(LookInStream_SeekTo(inStream, startPos + offset)); tempSizes[2] = (SizeT)s3Size; if (tempSizes[2] != s3Size) return SZ_ERROR_MEM; tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); if (tempBuf[2] == 0 && tempSizes[2] != 0) return SZ_ERROR_MEM; res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); RINOK(res) res = Bcj2_Decode( tempBuf3, tempSize3, tempBuf[0], tempSizes[0], tempBuf[1], tempSizes[1], tempBuf[2], tempSizes[2], outBuffer, outSize); RINOK(res) } else return SZ_ERROR_UNSUPPORTED; } return SZ_OK; } SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder, ILookInStream *inStream, UInt64 startPos, Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) { Byte *tempBuf[3] = { 0, 0, 0}; int i; SRes res = SzDecode2(packSizes, folder, inStream, startPos, outBuffer, (SizeT)outSize, allocMain, tempBuf); for (i = 0; i < 3; i++) IAlloc_Free(allocMain, tempBuf[i]); return res; }