/* 7zDecode.c */ #include "7zDecode.h" #ifdef _SZ_ONE_DIRECTORY #include "LzmaDecode.h" #else #include "../../Compress/LZMA_C/LzmaDecode.h" #endif #ifdef _LZMA_OUT_READ #include // for memcpy #endif CMethodID k_Copy = { { 0x0 }, 1 }; CMethodID k_LZMA = { { 0x3, 0x1, 0x1 }, 3 }; #ifdef _LZMA_IN_CB typedef struct _CLzmaInCallbackImp { ILzmaInCallback InCallback; ISzInStream *InStream; size_t Size; } CLzmaInCallbackImp; int LzmaReadImp(void *object, const unsigned char **buffer, SizeT *size) { CLzmaInCallbackImp *cb = (CLzmaInCallbackImp *)object; size_t processedSize; SZ_RESULT res; *size = 0; res = cb->InStream->Read((void *)cb->InStream, (void **)buffer, cb->Size, &processedSize); *size = (SizeT)processedSize; if (processedSize > cb->Size) return (int)SZE_FAIL; cb->Size -= processedSize; if (res == SZ_OK) return 0; return (int)res; } #endif SZ_RESULT SzDecode(const CFileSize *packSizes, const CFolder *folder, #ifdef _LZMA_IN_CB ISzInStream *inStream, #else const Byte *inBuffer, #endif Byte *outBuffer, size_t outSize, size_t *outSizeProcessed, ISzAlloc *allocMain) { UInt32 si; size_t inSize = 0; CCoderInfo *coder; if (folder->NumPackStreams != 1) return SZE_NOTIMPL; if (folder->NumCoders != 1) return SZE_NOTIMPL; coder = folder->Coders; *outSizeProcessed = 0; for (si = 0; si < folder->NumPackStreams; si++) inSize += (size_t)packSizes[si]; if (AreMethodsEqual(&coder->MethodID, &k_Copy)) { size_t i; if (inSize != outSize) return SZE_DATA_ERROR; #ifdef _LZMA_IN_CB for (i = 0; i < inSize;) { size_t j; Byte *inBuffer; size_t bufferSize; RINOK(inStream->Read((void *)inStream, (void **)&inBuffer, inSize - i, &bufferSize)); if (bufferSize == 0) return SZE_DATA_ERROR; if (bufferSize > inSize - i) return SZE_FAIL; *outSizeProcessed += bufferSize; for (j = 0; j < bufferSize && i < inSize; j++, i++) outBuffer[i] = inBuffer[j]; } #else for (i = 0; i < inSize; i++) outBuffer[i] = inBuffer[i]; *outSizeProcessed = inSize; #endif return SZ_OK; } if (AreMethodsEqual(&coder->MethodID, &k_LZMA)) { #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, 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_OUTOFMEMORYDIC; } } LzmaDecoderInit(&state); #endif result = LzmaDecode(&state, #ifdef _LZMA_IN_CB &lzmaCallback.InCallback, #else inBuffer, (SizeT)inSize, &inProcessed, #endif outBuffer, (SizeT)outSize, &outSizeProcessedLoc); *outSizeProcessed = (size_t)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 SZ_OK; } return SZE_NOTIMPL; } #ifdef _LZMA_OUT_READ // like SzDecode but uses less memory SZ_RESULT SzDecode2(const CFileSize *packSizes, const CFolder *folder, ISzInStream *inStream, Byte *outBuffer, size_t outSize, size_t *outSizeProcessed, ISzAlloc *allocMain, size_t *fileOffset, size_t *fileSize) { UInt32 si; size_t inSize = 0; CCoderInfo *coder; if (folder->NumPackStreams != 1) return SZE_NOTIMPL; if (folder->NumCoders != 1) return SZE_NOTIMPL; coder = folder->Coders; *outSizeProcessed = 0; for (si = 0; si < folder->NumPackStreams; si++) inSize += (size_t)packSizes[si]; if (AreMethodsEqual(&coder->MethodID, &k_Copy)) { size_t i; if (inSize != outSize) return SZE_DATA_ERROR; #ifdef _LZMA_IN_CB for (i = 0; i < inSize;) { size_t j; Byte *inBuffer; size_t bufferSize; RINOK(inStream->Read((void *)inStream, (void **)&inBuffer, inSize - i, &bufferSize)); if (bufferSize == 0) return SZE_DATA_ERROR; if (bufferSize > inSize - i) return SZE_FAIL; *outSizeProcessed += bufferSize; for (j = 0; j < bufferSize && i < inSize; j++, i++) outBuffer[i] = inBuffer[j]; } #else for (i = 0; i < inSize; i++) outBuffer[i] = inBuffer[i]; *outSizeProcessed = inSize; #endif return SZ_OK; } if (AreMethodsEqual(&coder->MethodID, &k_LZMA)) { #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, 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; 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); // allocate memory for the temporary buffer Byte *tmpBuffer = (Byte *)allocMain->Alloc(_LZMA_TEMP_BUFFER_SIZE); // variables containing the number of the first and the last bytes of the buffer size_t bufferStart, bufferEnd; bufferStart = bufferEnd = 0; // integers contains the offset, the size and the already copied data which will be // copied from the tmpBuffer to outBuffer size_t copyOffset, copySize, copyDone; copyOffset = copySize = copyDone = 0; UInt32 i = 0; int bytesToCopy = 0; // decompress data in _LZMA_TEMP_BUFFER_SIZE byte steps and copy the wanted file to outBuffer do { if((*fileSize - copyDone) >= _LZMA_TEMP_BUFFER_SIZE) bytesToCopy = _LZMA_TEMP_BUFFER_SIZE; else bytesToCopy = (*fileSize - copyDone); // decompress next bytes result = LzmaDecode(&state, #ifdef _LZMA_IN_CB &lzmaCallback.InCallback, #else //inBuffer, (SizeT)inSize, &inProcessed, //TODO! #endif tmpBuffer, bytesToCopy, &outSizeProcessedLoc ); // check result if(result == LZMA_RESULT_DATA_ERROR) { return SZE_DATA_ERROR; } if(result != LZMA_RESULT_OK) { return SZE_FAIL; } // normally this should never happen if(outSizeProcessedLoc > _LZMA_TEMP_BUFFER_SIZE) { return SZE_FAIL; } // update bufferStart and bufferEnd bufferStart = _LZMA_TEMP_BUFFER_SIZE * i; bufferEnd = bufferStart + outSizeProcessedLoc; i++; // calculate copy offset and size if(*fileOffset > bufferEnd) { // we haven't reached the start of the file yet continue; } // calculate offset if(*fileOffset < bufferStart) { // the file has already started before this decompression step copyOffset = 0; } else { // the file starts somewhere inside this buffer copyDone = 0; copyOffset = _LZMA_TEMP_BUFFER_SIZE - (bufferEnd - *fileOffset); } // calculate size if((*fileOffset + *fileSize) > bufferEnd) { // we'll need the whole buffer after copyOffset copySize = _LZMA_TEMP_BUFFER_SIZE - copyOffset; } else { // we'll stop somewhere inside the buffer copySize = (*fileOffset + *fileSize) - (bufferStart + copyOffset); } // copy bytes to the real output buffer if(copySize == 0) { continue; } // printf("memcpy(outBuffer + %d, tmpBuffer + %d, %d)\n", copyDone, copyOffset, copySize); memcpy(outBuffer + copyDone, tmpBuffer + copyOffset, copySize); copyDone += copySize; } while((*fileOffset + *fileSize) > bufferEnd); /* result = LzmaDecode(&state, #ifdef _LZMA_IN_CB &lzmaCallback.InCallback, #else inBuffer, (SizeT)inSize, &inProcessed, #endif outBuffer, (SizeT)outSize, &outSizeProcessedLoc);*/ //*outSizeProcessed = (size_t)outSizeProcessedLoc; *outSizeProcessed = copyDone; allocMain->Free(tmpBuffer); // free the temporary buffer again allocMain->Free(state.Probs); allocMain->Free(state.Dictionary); /* if (result == LZMA_RESULT_DATA_ERROR) return SZE_DATA_ERROR; if (result != LZMA_RESULT_OK) return SZE_FAIL;*/ return SZ_OK; } return SZE_NOTIMPL; } #endif