/* 7zDecode.c */ #include /* BEGIN PHYSFS CHANGE */ #include /* END PHYSFS CHANGE */ #include "7zDecode.h" #ifdef _SZ_ONE_DIRECTORY #include "LzmaDecode.h" #else #include "../../Compress/Lzma/LzmaDecode.h" #include "../../Compress/Branch/BranchX86.h" #include "../../Compress/Branch/BranchX86_2.h" #endif #define k_Copy 0 #define k_LZMA 0x30101 #define k_BCJ 0x03030103 #define k_BCJ2 0x0303011B #ifdef _LZMA_IN_CB typedef struct _CLzmaInCallbackImp { ILzmaInCallback InCallback; ISzInStream *InStream; CFileSize Size; } CLzmaInCallbackImp; int LzmaReadImp(void *object, const unsigned char **buffer, SizeT *size) { CLzmaInCallbackImp *cb = (CLzmaInCallbackImp *)object; size_t processedSize; SZ_RESULT res; size_t curSize = (1 << 20); if (curSize > cb->Size) curSize = (size_t)cb->Size; *size = 0; res = cb->InStream->Read((void *)cb->InStream, (void **)buffer, curSize, &processedSize); *size = (SizeT)processedSize; if (processedSize > curSize) return (int)SZE_FAIL; cb->Size -= processedSize; if (res == SZ_OK) return 0; return (int)res; } #endif SZ_RESULT SzDecodeLzma(CCoderInfo *coder, CFileSize inSize, #ifdef _LZMA_IN_CB ISzInStream *inStream, #else const Byte *inBuffer, #endif Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) { #ifdef _LZMA_IN_CB CLzmaInCallbackImp lzmaCallback; #else SizeT inProcessed; #endif CLzmaDecoderState state; /* it's about 24-80 bytes structure, if int is 32-bit */ int result; SizeT outSizeProcessedLoc; #ifdef _LZMA_IN_CB lzmaCallback.Size = inSize; lzmaCallback.InStream = inStream; lzmaCallback.InCallback.Read = LzmaReadImp; #endif if (LzmaDecodeProperties(&state.Properties, coder->Properties.Items, (unsigned)coder->Properties.Capacity) != LZMA_RESULT_OK) return SZE_FAIL; state.Probs = (CProb *)allocMain->Alloc(LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); if (state.Probs == 0) return SZE_OUTOFMEMORY; #ifdef _LZMA_OUT_READ if (state.Properties.DictionarySize == 0) state.Dictionary = 0; else { state.Dictionary = (unsigned char *)allocMain->Alloc(state.Properties.DictionarySize); if (state.Dictionary == 0) { allocMain->Free(state.Probs); return SZE_OUTOFMEMORY; } } LzmaDecoderInit(&state); #endif result = LzmaDecode(&state, #ifdef _LZMA_IN_CB &lzmaCallback.InCallback, #else inBuffer, (SizeT)inSize, &inProcessed, #endif outBuffer, (SizeT)outSize, &outSizeProcessedLoc); allocMain->Free(state.Probs); #ifdef _LZMA_OUT_READ allocMain->Free(state.Dictionary); #endif if (result == LZMA_RESULT_DATA_ERROR) return SZE_DATA_ERROR; if (result != LZMA_RESULT_OK) return SZE_FAIL; return (outSizeProcessedLoc == outSize) ? SZ_OK : SZE_DATA_ERROR; } #ifdef _LZMA_IN_CB SZ_RESULT SzDecodeCopy(CFileSize inSize, ISzInStream *inStream, Byte *outBuffer) { while (inSize > 0) { void *inBuffer; size_t processedSize, curSize = (1 << 18); if (curSize > inSize) curSize = (size_t)(inSize); RINOK(inStream->Read((void *)inStream, (void **)&inBuffer, curSize, &processedSize)); if (processedSize == 0) return SZE_DATA_ERROR; if (processedSize > curSize) return SZE_FAIL; memcpy(outBuffer, inBuffer, processedSize); outBuffer += processedSize; inSize -= processedSize; } return SZ_OK; } #endif #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) SZ_RESULT CheckSupportedFolder(const CFolder *f) { if (f->NumCoders < 1 || f->NumCoders > 4) return SZE_NOTIMPL; if (IS_UNSUPPORTED_CODER(f->Coders[0])) return SZE_NOTIMPL; if (f->NumCoders == 1) { if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) return SZE_NOTIMPL; 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 SZE_NOTIMPL; 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 SZE_NOTIMPL; 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 SZE_NOTIMPL; return SZ_OK; } return SZE_NOTIMPL; } CFileSize GetSum(const CFileSize *values, UInt32 index) { CFileSize sum = 0; UInt32 i; for (i = 0; i < index; i++) sum += values[i]; return sum; } SZ_RESULT SzDecode2(const CFileSize *packSizes, const CFolder *folder, #ifdef _LZMA_IN_CB ISzInStream *inStream, CFileSize startPos, #else const Byte *inBuffer, #endif Byte *outBuffer, size_t outSize, ISzAlloc *allocMain, Byte *tempBuf[]) { UInt32 ci; size_t tempSizes[3] = { 0, 0, 0}; size_t tempSize3 = 0; Byte *tempBuf3 = 0; RINOK(CheckSupportedFolder(folder)); for (ci = 0; ci < folder->NumCoders; ci++) { CCoderInfo *coder = &folder->Coders[ci]; if (coder->MethodID == k_Copy || coder->MethodID == k_LZMA) { UInt32 si = 0; CFileSize offset; CFileSize inSize; Byte *outBufCur = outBuffer; size_t outSizeCur = outSize; if (folder->NumCoders == 4) { UInt32 indices[] = { 3, 2, 0 }; CFileSize unpackSize = folder->UnPackSizes[ci]; si = indices[ci]; if (ci < 2) { Byte *temp; outSizeCur = (size_t)unpackSize; if (outSizeCur != unpackSize) return SZE_OUTOFMEMORY; temp = (Byte *)allocMain->Alloc(outSizeCur); if (temp == 0 && outSizeCur != 0) return SZE_OUTOFMEMORY; outBufCur = tempBuf[1 - ci] = temp; tempSizes[1 - ci] = outSizeCur; } else if (ci == 2) { if (unpackSize > outSize) return SZE_OUTOFMEMORY; tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); tempSize3 = outSizeCur = (size_t)unpackSize; } else return SZE_NOTIMPL; } offset = GetSum(packSizes, si); inSize = packSizes[si]; #ifdef _LZMA_IN_CB RINOK(inStream->Seek(inStream, startPos + offset)); #endif if (coder->MethodID == k_Copy) { if (inSize != outSizeCur) return SZE_DATA_ERROR; #ifdef _LZMA_IN_CB RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); #else memcpy(outBufCur, inBuffer + (size_t)offset, (size_t)inSize); #endif } else { SZ_RESULT res = SzDecodeLzma(coder, inSize, #ifdef _LZMA_IN_CB inStream, #else inBuffer + (size_t)offset, #endif outBufCur, outSizeCur, allocMain); RINOK(res) } } else if (coder->MethodID == k_BCJ) { UInt32 state; if (ci != 1) return SZE_NOTIMPL; x86_Convert_Init(state); x86_Convert(outBuffer, outSize, 0, &state, 0); } else if (coder->MethodID == k_BCJ2) { CFileSize offset = GetSum(packSizes, 1); CFileSize s3Size = packSizes[1]; SZ_RESULT res; if (ci != 3) return SZE_NOTIMPL; #ifdef _LZMA_IN_CB RINOK(inStream->Seek(inStream, startPos + offset)); tempSizes[2] = (size_t)s3Size; if (tempSizes[2] != s3Size) return SZE_OUTOFMEMORY; tempBuf[2] = (Byte *)allocMain->Alloc(tempSizes[2]); if (tempBuf[2] == 0 && tempSizes[2] != 0) return SZE_OUTOFMEMORY; res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); RINOK(res) #endif res = x86_2_Decode( tempBuf3, tempSize3, tempBuf[0], tempSizes[0], tempBuf[1], tempSizes[1], #ifdef _LZMA_IN_CB tempBuf[2], tempSizes[2], #else inBuffer + (size_t)offset, (size_t)s3Size, #endif outBuffer, outSize); RINOK(res) } else return SZE_NOTIMPL; } return SZ_OK; } SZ_RESULT SzDecode(const CFileSize *packSizes, const CFolder *folder, #ifdef _LZMA_IN_CB ISzInStream *inStream, CFileSize startPos, #else const Byte *inBuffer, #endif Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) { Byte *tempBuf[3] = { 0, 0, 0}; int i; SZ_RESULT res = SzDecode2(packSizes, folder, #ifdef _LZMA_IN_CB inStream, startPos, #else inBuffer, #endif outBuffer, outSize, allocMain, tempBuf); for (i = 0; i < 3; i++) allocMain->Free(tempBuf[i]); return res; }