150 lines
3.5 KiB
C
150 lines
3.5 KiB
C
|
// Copyright (c) 2016 YamaArashi
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdbool.h>
|
||
|
#include "global.h"
|
||
|
#include "rl.h"
|
||
|
|
||
|
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
|
||
|
{
|
||
|
if (srcSize < 4)
|
||
|
goto fail;
|
||
|
|
||
|
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
|
||
|
|
||
|
unsigned char *dest = malloc(destSize);
|
||
|
|
||
|
if (dest == NULL)
|
||
|
goto fail;
|
||
|
|
||
|
int srcPos = 4;
|
||
|
int destPos = 0;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
if (srcPos >= srcSize)
|
||
|
goto fail;
|
||
|
|
||
|
unsigned char flags = src[srcPos++];
|
||
|
bool compressed = ((flags & 0x80) != 0);
|
||
|
|
||
|
if (compressed)
|
||
|
{
|
||
|
int length = (flags & 0x7F) + 3;
|
||
|
unsigned char data = src[srcPos++];
|
||
|
|
||
|
if (destPos + length > destSize)
|
||
|
goto fail;
|
||
|
|
||
|
for (int i = 0; i < length; i++)
|
||
|
dest[destPos++] = data;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int length = (flags & 0x7F) + 1;
|
||
|
|
||
|
if (destPos + length > destSize)
|
||
|
goto fail;
|
||
|
|
||
|
for (int i = 0; i < length; i++)
|
||
|
dest[destPos++] = src[srcPos++];
|
||
|
}
|
||
|
|
||
|
if (destPos == destSize)
|
||
|
{
|
||
|
*uncompressedSize = destSize;
|
||
|
return dest;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fail:
|
||
|
FATAL_ERROR("Fatal error while decompressing RL file.\n");
|
||
|
}
|
||
|
|
||
|
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize)
|
||
|
{
|
||
|
if (srcSize <= 0)
|
||
|
goto fail;
|
||
|
|
||
|
int worstCaseDestSize = 4 + srcSize * 2;
|
||
|
|
||
|
// Round up to the next multiple of four.
|
||
|
worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
|
||
|
|
||
|
unsigned char *dest = malloc(worstCaseDestSize);
|
||
|
|
||
|
if (dest == NULL)
|
||
|
goto fail;
|
||
|
|
||
|
// header
|
||
|
dest[0] = 0x30; // RL compression type
|
||
|
dest[1] = (unsigned char)srcSize;
|
||
|
dest[2] = (unsigned char)(srcSize >> 8);
|
||
|
dest[3] = (unsigned char)(srcSize >> 16);
|
||
|
|
||
|
int srcPos = 0;
|
||
|
int destPos = 4;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
bool compress = false;
|
||
|
int uncompressedStart = srcPos;
|
||
|
int uncompressedLength = 0;
|
||
|
|
||
|
while (srcPos < srcSize && uncompressedLength < (0x7F + 1))
|
||
|
{
|
||
|
compress = (srcPos + 2 < srcSize && src[srcPos] == src[srcPos + 1] && src[srcPos] == src[srcPos + 2]);
|
||
|
|
||
|
if (compress)
|
||
|
break;
|
||
|
|
||
|
srcPos++;
|
||
|
uncompressedLength++;
|
||
|
}
|
||
|
|
||
|
if (uncompressedLength > 0)
|
||
|
{
|
||
|
dest[destPos++] = uncompressedLength - 1;
|
||
|
|
||
|
for (int i = 0; i < uncompressedLength; i++)
|
||
|
dest[destPos++] = src[uncompressedStart + i];
|
||
|
}
|
||
|
|
||
|
if (compress)
|
||
|
{
|
||
|
unsigned char data = src[srcPos];
|
||
|
int compressedLength = 0;
|
||
|
|
||
|
while (compressedLength < (0x7F + 3)
|
||
|
&& srcPos + compressedLength < srcSize
|
||
|
&& src[srcPos + compressedLength] == data)
|
||
|
{
|
||
|
compressedLength++;
|
||
|
}
|
||
|
|
||
|
dest[destPos++] = 0x80 | (compressedLength - 3);
|
||
|
dest[destPos++] = data;
|
||
|
|
||
|
srcPos += compressedLength;
|
||
|
}
|
||
|
|
||
|
if (srcPos == srcSize)
|
||
|
{
|
||
|
// Pad to multiple of 4 bytes.
|
||
|
int remainder = destPos % 4;
|
||
|
|
||
|
if (remainder != 0)
|
||
|
{
|
||
|
for (int i = 0; i < 4 - remainder; i++)
|
||
|
dest[destPos++] = 0;
|
||
|
}
|
||
|
|
||
|
*compressedSize = destPos;
|
||
|
return dest;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fail:
|
||
|
FATAL_ERROR("Fatal error while compressing RL file.\n");
|
||
|
}
|