264 lines
6.4 KiB
C
264 lines
6.4 KiB
C
/*
|
|
* Written in 2009 by Lloyd Hilaiel
|
|
*
|
|
* License
|
|
*
|
|
* All the cruft you find here is public domain. You don't have to credit
|
|
* anyone to use this code, but my personal request is that you mention
|
|
* Igor Pavlov for his hard, high quality work.
|
|
*/
|
|
|
|
#include "include/decompress.h"
|
|
#include "pavlov/LzmaDec.h"
|
|
#include "pavlov/7zCrc.h"
|
|
#include "common_internal.h"
|
|
#include "lzma_header.h"
|
|
#include "lzip_header.h"
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#define ELZMA_DECOMPRESS_INPUT_BUFSIZE (1024 * 64)
|
|
#define ELZMA_DECOMPRESS_OUTPUT_BUFSIZE (1024 * 256)
|
|
|
|
/** an opaque handle to an lzma decompressor */
|
|
struct _elzma_decompress_handle
|
|
{
|
|
char inbuf[ELZMA_DECOMPRESS_INPUT_BUFSIZE];
|
|
char outbuf[ELZMA_DECOMPRESS_OUTPUT_BUFSIZE];
|
|
struct elzma_alloc_struct allocStruct;
|
|
};
|
|
|
|
elzma_decompress_handle elzma_decompress_alloc()
|
|
{
|
|
elzma_decompress_handle hand = malloc(sizeof(struct _elzma_decompress_handle));
|
|
memset((void *)hand, 0, sizeof(struct _elzma_decompress_handle));
|
|
init_alloc_struct(&(hand->allocStruct), NULL, NULL, NULL, NULL);
|
|
return hand;
|
|
}
|
|
|
|
void elzma_decompress_set_allocation_callbacks(elzma_decompress_handle hand,
|
|
elzma_malloc mallocFunc, void *mallocFuncContext,
|
|
elzma_free freeFunc, void *freeFuncContext)
|
|
{
|
|
if (hand)
|
|
{
|
|
init_alloc_struct(&(hand->allocStruct), mallocFunc, mallocFuncContext, freeFunc,
|
|
freeFuncContext);
|
|
}
|
|
}
|
|
|
|
void elzma_decompress_free(elzma_decompress_handle *hand)
|
|
{
|
|
if (*hand)
|
|
free(*hand);
|
|
*hand = NULL;
|
|
}
|
|
|
|
int elzma_decompress_run(elzma_decompress_handle hand, elzma_read_callback inputStream,
|
|
void *inputContext, elzma_write_callback outputStream,
|
|
void *outputContext, elzma_file_format format)
|
|
{
|
|
unsigned long long int totalRead = 0; /* total amount read from stream */
|
|
unsigned int crc32 = CRC_INIT_VAL; /* running crc32 (lzip case) */
|
|
CLzmaDec dec;
|
|
unsigned int errorCode = ELZMA_E_OK;
|
|
struct elzma_format_handler formatHandler;
|
|
struct elzma_file_header h;
|
|
struct elzma_file_footer f;
|
|
|
|
/* switch between supported formats */
|
|
if (format == ELZMA_lzma)
|
|
{
|
|
initializeLZMAFormatHandler(&formatHandler);
|
|
}
|
|
else if (format == ELZMA_lzip)
|
|
{
|
|
CrcGenerateTable();
|
|
initializeLZIPFormatHandler(&formatHandler);
|
|
}
|
|
else
|
|
{
|
|
return ELZMA_E_BAD_PARAMS;
|
|
}
|
|
|
|
/* initialize footer */
|
|
f.crc32 = 0;
|
|
f.uncompressedSize = 0;
|
|
|
|
/* initialize decoder memory */
|
|
memset((void *)&dec, 0, sizeof(dec));
|
|
LzmaDec_Init(&dec);
|
|
|
|
/* decode the header. */
|
|
{
|
|
unsigned char *hdr =
|
|
hand->allocStruct.Alloc(&(hand->allocStruct), formatHandler.header_size);
|
|
|
|
size_t sz = formatHandler.header_size;
|
|
|
|
formatHandler.init_header(&h);
|
|
|
|
if (inputStream(inputContext, hdr, &sz) != 0 || sz != formatHandler.header_size)
|
|
{
|
|
hand->allocStruct.Free(&(hand->allocStruct), hdr);
|
|
return ELZMA_E_INPUT_ERROR;
|
|
}
|
|
|
|
if (0 != formatHandler.parse_header(hdr, &h))
|
|
{
|
|
hand->allocStruct.Free(&(hand->allocStruct), hdr);
|
|
return ELZMA_E_CORRUPT_HEADER;
|
|
}
|
|
|
|
/* the LzmaDec_Allocate call requires 5 bytes which have
|
|
* compression properties encoded in them. In the case of
|
|
* lzip, the header format does not already contain what
|
|
* LzmaDec_Allocate expects, so we must craft it, silly */
|
|
{
|
|
unsigned char propsBuf[13];
|
|
const unsigned char *propsPtr = hdr;
|
|
|
|
if (format == ELZMA_lzip)
|
|
{
|
|
struct elzma_format_handler lzmaHand;
|
|
initializeLZMAFormatHandler(&lzmaHand);
|
|
lzmaHand.serialize_header(propsBuf, &h);
|
|
propsPtr = propsBuf;
|
|
}
|
|
|
|
/* now we're ready to allocate the decoder */
|
|
LzmaDec_Allocate(&dec, propsPtr, 5);
|
|
}
|
|
|
|
hand->allocStruct.Free(&(hand->allocStruct), hdr);
|
|
}
|
|
|
|
/* perform the decoding */
|
|
for (;;)
|
|
{
|
|
size_t dstLen = ELZMA_DECOMPRESS_OUTPUT_BUFSIZE;
|
|
size_t srcLen = ELZMA_DECOMPRESS_INPUT_BUFSIZE;
|
|
size_t amt = 0;
|
|
size_t bufOff = 0;
|
|
ELzmaStatus stat;
|
|
|
|
if (0 != inputStream(inputContext, hand->inbuf, &srcLen))
|
|
{
|
|
errorCode = ELZMA_E_INPUT_ERROR;
|
|
goto decompressEnd;
|
|
}
|
|
|
|
/* handle the case where the input prematurely finishes */
|
|
if (srcLen == 0)
|
|
{
|
|
errorCode = ELZMA_E_INSUFFICIENT_INPUT;
|
|
goto decompressEnd;
|
|
}
|
|
|
|
amt = srcLen;
|
|
|
|
/* handle the case where a single read buffer of compressed bytes
|
|
* will translate into multiple buffers of uncompressed bytes,
|
|
* with this inner loop */
|
|
stat = LZMA_STATUS_NOT_SPECIFIED;
|
|
|
|
while (bufOff < srcLen)
|
|
{
|
|
SRes r = LzmaDec_DecodeToBuf(&dec, (uint8_t *)hand->outbuf, &dstLen,
|
|
((uint8_t *)hand->inbuf + bufOff), &amt,
|
|
LZMA_FINISH_ANY, &stat);
|
|
|
|
/* XXX deal with result code more granularly*/
|
|
if (r != SZ_OK)
|
|
{
|
|
errorCode = ELZMA_E_DECOMPRESS_ERROR;
|
|
goto decompressEnd;
|
|
}
|
|
|
|
/* write what we've read */
|
|
{
|
|
size_t wt;
|
|
|
|
/* if decoding lzip, update our crc32 value */
|
|
if (format == ELZMA_lzip && dstLen > 0)
|
|
{
|
|
crc32 = CrcUpdate(crc32, hand->outbuf, dstLen);
|
|
}
|
|
totalRead += dstLen;
|
|
|
|
wt = outputStream(outputContext, hand->outbuf, dstLen);
|
|
if (wt != dstLen)
|
|
{
|
|
errorCode = ELZMA_E_OUTPUT_ERROR;
|
|
goto decompressEnd;
|
|
}
|
|
}
|
|
|
|
/* do we have more data on the input buffer? */
|
|
bufOff += amt;
|
|
assert(bufOff <= srcLen);
|
|
if (bufOff >= srcLen)
|
|
break;
|
|
amt = srcLen - bufOff;
|
|
|
|
/* with lzip, we will have the footer left on the buffer! */
|
|
if (stat == LZMA_STATUS_FINISHED_WITH_MARK)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* now check status */
|
|
if (stat == LZMA_STATUS_FINISHED_WITH_MARK)
|
|
{
|
|
/* read a footer if one is expected and
|
|
* present */
|
|
if (formatHandler.footer_size > 0 && amt >= formatHandler.footer_size &&
|
|
formatHandler.parse_footer != NULL)
|
|
{
|
|
formatHandler.parse_footer((unsigned char *)hand->inbuf + bufOff, &f);
|
|
}
|
|
|
|
break;
|
|
}
|
|
/* for LZMA utils, we don't always have a finished mark */
|
|
if (!h.isStreamed && totalRead >= h.uncompressedSize)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* finish the calculated crc32 */
|
|
crc32 ^= 0xFFFFFFFF;
|
|
|
|
/* if we have a footer, check that the calculated crc32 matches
|
|
* the encoded crc32, and that the sizes match */
|
|
if (formatHandler.footer_size)
|
|
{
|
|
if (f.crc32 != crc32)
|
|
{
|
|
errorCode = ELZMA_E_CRC32_MISMATCH;
|
|
}
|
|
else if (f.uncompressedSize != totalRead)
|
|
{
|
|
errorCode = ELZMA_E_SIZE_MISMATCH;
|
|
}
|
|
}
|
|
else if (!h.isStreamed)
|
|
{
|
|
/* if the format does not support a footer and has an uncompressed
|
|
* size in the header, let's compare that with how much we actually
|
|
* read */
|
|
if (h.uncompressedSize != totalRead)
|
|
{
|
|
errorCode = ELZMA_E_SIZE_MISMATCH;
|
|
}
|
|
}
|
|
|
|
decompressEnd:
|
|
LzmaDec_Free(&dec);
|
|
|
|
return errorCode;
|
|
}
|