Chroma/chroma/lainlib/compression/lzgmini.c

191 lines
6.1 KiB
C
Raw Normal View History

/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; -*- */
/*
* This file is part of liblzg.
*
* Copyright (c) 2010 Marcus Geelnard
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#include <lainlib/lainlib.h>
/*-- PRIVATE -----------------------------------------------------------------*/
/* Internal definitions */
#define LZG_HEADER_SIZE 16
#define LZG_METHOD_COPY 0
#define LZG_METHOD_LZG1 1
/* Endian and alignment independent reader for 32-bit integers */
#define _LZG_GetUINT32(in, offs) \
((((lzg_uint32_t)in[offs]) << 24) | \
(((lzg_uint32_t)in[offs+1]) << 16) | \
(((lzg_uint32_t)in[offs+2]) << 8) | \
((lzg_uint32_t)in[offs+3]))
/* Calculate the checksum */
static lzg_uint32_t _LZG_CalcChecksum(const unsigned char *data, lzg_uint32_t size)
{
unsigned short a = 1, b = 0;
unsigned char *end = (unsigned char *)data + size;
while (data != end)
{
a += *data++;
b += a;
}
return (((lzg_uint32_t)b) << 16) | a;
}
/* LUT for decoding the copy length parameter */
static const unsigned char _LZG_LENGTH_DECODE_LUT[32] = {
2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
18,19,20,21,22,23,24,25,26,27,28,29,35,48,72,128
};
/*-- PUBLIC ------------------------------------------------------------------*/
lzg_uint32_t LZG_DecodedSize(const unsigned char *in, lzg_uint32_t insize)
{
/* Check header */
if ((insize < 7) || (in[0] != 'L') || (in[1] != 'Z') || (in[2] != 'G'))
return 0;
/* Get output buffer size */
return _LZG_GetUINT32(in, 3);
}
lzg_uint32_t LZG_Decode(const unsigned char *in, lzg_uint32_t insize,
unsigned char *out, lzg_uint32_t outsize)
{
unsigned char *src, *inEnd, *dst, *outEnd, *copy, symbol, b, b2;
unsigned char m1, m2, m3, m4, method;
lzg_uint32_t i, length, offset, encodedSize, decodedSize, checksum;
/* Check magic ID */
if ((insize < LZG_HEADER_SIZE) || (in[0] != 'L') || (in[1] != 'Z') || (in[2] != 'G'))
return 0;
/* Get header data */
decodedSize = _LZG_GetUINT32(in, 3);
encodedSize = _LZG_GetUINT32(in, 7);
checksum = _LZG_GetUINT32(in, 11);
/* Check sizes */
if ((outsize < decodedSize) || (encodedSize != (insize - LZG_HEADER_SIZE)))
return 0;
/* Check checksum */
if (_LZG_CalcChecksum(&in[LZG_HEADER_SIZE], encodedSize) != checksum)
return 0;
/* Initialize the byte streams */
src = (unsigned char *)in + LZG_HEADER_SIZE;;
inEnd = ((unsigned char *)in) + insize;
dst = out;
outEnd = out + outsize;
/* Check which method to use */
method = in[15];
if (method == LZG_METHOD_LZG1)
{
if (!((src + 4) <= inEnd)) return 0;
m1 = *src++; m2 = *src++; m3 = *src++; m4 = *src++;
/* Main decompression loop */
while (src < inEnd)
{
symbol = *src++;
if ((symbol != m1) && (symbol != m2) && (symbol != m3) && (symbol != m4))
{
/* Literal copy */
if (!(dst < outEnd)) return 0;
*dst++ = symbol;
}
else
{
/* Decode offset / length parameters */
if (!(src < inEnd)) return 0;
if ((b = *src++))
{
if (symbol == m1)
{
/* Distant copy */
if (!((src + 2) <= inEnd)) return 0;
length = _LZG_LENGTH_DECODE_LUT[b & 0x1f];
b2 = *src++;
offset = (((unsigned int)(b & 0xe0)) << 11) |
(((unsigned int)b2) << 8) |
(*src++);
offset += 2056;
}
else if (symbol == m2)
{
/* Medium copy */
if (!(src < inEnd)) return 0;
length = _LZG_LENGTH_DECODE_LUT[b & 0x1f];
b2 = *src++;
offset = (((unsigned int)(b & 0xe0)) << 3) | b2;
offset += 8;
}
else if (symbol == m3)
{
/* Short copy */
length = (b >> 6) + 3;
offset = (b & 0x3f) + 8;
}
else
{
/* Near copy (including RLE) */
length = _LZG_LENGTH_DECODE_LUT[b & 0x1f];
offset = (b >> 5) + 1;
}
/* Copy the corresponding data from the history window */
copy = dst - offset;
if (!((copy >= out) && ((dst + length) <= outEnd))) return 0;
for (i = 0; i < length; ++i)
*dst++ = *copy++;
}
else
{
/* Literal copy (single occurance of a marker symbol) */
if (!(dst < outEnd)) return 0;
*dst++ = symbol;
}
}
}
}
else if (method == LZG_METHOD_COPY)
{
/* Plain copy */
while ((src < inEnd) && (dst < outEnd))
*dst++ = *src++;
}
/* All OK? */
if ((unsigned int)(dst - out) != decodedSize)
return 0;
else
return decodedSize;
}