141 lines
3.6 KiB
C
141 lines
3.6 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.
|
|
*/
|
|
|
|
/* XXX: clean this up, it's mostly lifted from pavel */
|
|
|
|
#include "lzma_header.h"
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#define ELZMA_LZMA_HEADER_SIZE 13
|
|
#define ELZMA_LZMA_PROPSBUF_SIZE 5
|
|
|
|
/****************
|
|
Header parsing
|
|
****************/
|
|
|
|
#ifndef UINT64_MAX
|
|
#define UINT64_MAX ((unsigned long long) -1)
|
|
#endif
|
|
|
|
/* Parse the properties byte */
|
|
static char
|
|
lzmadec_header_properties (
|
|
unsigned char *pb, unsigned char *lp, unsigned char *lc, const unsigned char c)
|
|
{
|
|
/* pb, lp and lc are encoded into a single byte. */
|
|
if (c > (9 * 5 * 5))
|
|
return -1;
|
|
*pb = c / (9 * 5); /* 0 <= pb <= 4 */
|
|
*lp = (c % (9 * 5)) / 9; /* 0 <= lp <= 4 */
|
|
*lc = c % 9; /* 0 <= lc <= 8 */
|
|
|
|
assert (*pb < 5 && *lp < 5 && *lc < 9);
|
|
return 0;
|
|
}
|
|
|
|
/* Parse the dictionary size (4 bytes, little endian) */
|
|
static char
|
|
lzmadec_header_dictionary (unsigned int *size, const unsigned char *buffer)
|
|
{
|
|
unsigned int i;
|
|
*size = 0;
|
|
for (i = 0; i < 4; i++)
|
|
*size += (unsigned int)(*buffer++) << (i * 8);
|
|
/* The dictionary size is limited to 256 MiB (checked from
|
|
* LZMA SDK 4.30) */
|
|
if (*size > (1 << 28))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* Parse the uncompressed size field (8 bytes, little endian) */
|
|
static void
|
|
lzmadec_header_uncompressed (unsigned long long *size,
|
|
unsigned char *is_streamed,
|
|
const unsigned char *buffer)
|
|
{
|
|
unsigned int i;
|
|
|
|
/* Streamed files have all 64 bits set in the size field.
|
|
* We don't know the uncompressed size beforehand. */
|
|
*is_streamed = 1; /* Assume streamed. */
|
|
*size = 0;
|
|
for (i = 0; i < 8; i++) {
|
|
*size += (unsigned long long)buffer[i] << (i * 8);
|
|
if (buffer[i] != 255)
|
|
*is_streamed = 0;
|
|
}
|
|
assert ((*is_streamed == 1 && *size == UINT64_MAX)
|
|
|| (*is_streamed == 0 && *size < UINT64_MAX));
|
|
}
|
|
|
|
static void
|
|
initLzmaHeader(struct elzma_file_header * hdr)
|
|
{
|
|
memset((void *) hdr, 0, sizeof(struct elzma_file_header));
|
|
}
|
|
|
|
static int
|
|
parseLzmaHeader(const unsigned char * hdrBuf,
|
|
struct elzma_file_header * hdr)
|
|
{
|
|
if (lzmadec_header_properties(&(hdr->pb), &(hdr->lp), &(hdr->lc),
|
|
*hdrBuf) ||
|
|
lzmadec_header_dictionary(&(hdr->dictSize), hdrBuf + 1))
|
|
{
|
|
return 1;
|
|
}
|
|
lzmadec_header_uncompressed(&(hdr->uncompressedSize),
|
|
&(hdr->isStreamed),
|
|
hdrBuf + 5);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
serializeLzmaHeader(unsigned char * hdrBuf,
|
|
const struct elzma_file_header * hdr)
|
|
{
|
|
unsigned int i;
|
|
|
|
memset((void *) hdrBuf, 0, ELZMA_LZMA_HEADER_SIZE);
|
|
|
|
/* encode lc, pb, and lp */
|
|
*hdrBuf++ = hdr->lc + (hdr->pb * 45) + (hdr->lp * 45 * 9);
|
|
|
|
/* encode dictionary size */
|
|
for (i = 0; i < 4; i++) {
|
|
*(hdrBuf++) = (unsigned char) (hdr->dictSize >> (i * 8));
|
|
}
|
|
|
|
/* encode uncompressed size */
|
|
for (i = 0; i < 8; i++) {
|
|
if (hdr->isStreamed) {
|
|
*(hdrBuf++) = 0xff;
|
|
} else {
|
|
*(hdrBuf++) = (unsigned char) (hdr->uncompressedSize >> (i * 8));
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
initializeLZMAFormatHandler(struct elzma_format_handler * hand)
|
|
{
|
|
hand->header_size = ELZMA_LZMA_HEADER_SIZE;
|
|
hand->init_header = initLzmaHeader;
|
|
hand->parse_header = parseLzmaHeader;
|
|
hand->serialize_header = serializeLzmaHeader;
|
|
hand->footer_size = 0;
|
|
hand->serialize_footer = NULL;
|
|
}
|