mirror of
https://github.com/dborth/fceugx.git
synced 2024-11-01 15:05:05 +01:00
362 lines
10 KiB
C
362 lines
10 KiB
C
/* 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 <string.h> // 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_OUTOFMEMORYDIC;
|
|
}
|
|
}
|
|
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
|