git-svn-id: https://clonekeenplus.svn.sourceforge.net/svnroot/clonekeenplus/cgenius/trunk@5 4df4b0f3-56ce-47cb-b001-ed939b7d65a6
151 lines
4.3 KiB
C++
151 lines
4.3 KiB
C++
/* SGRLE.C
|
|
RLE compression and decompression functions for the SGRLE format...
|
|
These are used for saved games (hence the name SG RLE). Could be used
|
|
for other things too, I guess.
|
|
|
|
After opening a file for decompression you must call sgrle_reset() before
|
|
the first time you use sgrle_decompress().
|
|
*/
|
|
#include "keen.h"
|
|
|
|
void sgrle_reset(void);
|
|
void sgrle_compress(FILE *fp, unsigned char *ptr, unsigned long nbytes);
|
|
void sgrle_decompress(FILE *fp, unsigned char *ptr, unsigned long nbytes);
|
|
unsigned char sgrle_get_next_byte(FILE *fp);
|
|
|
|
#define SGRLE_RLEMARKER 255
|
|
#define SGRLE_MAXRLELEN 65530
|
|
int sgrle_runlen;
|
|
unsigned char sgrle_runchar;
|
|
|
|
/* decompresses the next byte from file FP. */
|
|
/* used internally by sgrle_decompress(). */
|
|
unsigned char sgrle_get_next_byte(FILE *fp)
|
|
{
|
|
// are we currently in a RLE run?
|
|
if (sgrle_runlen)
|
|
{
|
|
// decrease length of RLE run and return the previously
|
|
// read char for the run
|
|
sgrle_runlen--;
|
|
return sgrle_runchar;
|
|
}
|
|
else
|
|
{ // not currently in a RLE run
|
|
sgrle_runchar = fgetc(fp);
|
|
if (sgrle_runchar==SGRLE_RLEMARKER)
|
|
{ // start of a RLE run
|
|
sgrle_runlen = fgeti(fp);
|
|
sgrle_runchar = fgetc(fp);
|
|
return sgrle_get_next_byte(fp);
|
|
}
|
|
else return sgrle_runchar;
|
|
}
|
|
}
|
|
|
|
/* decompresses nbytes bytes of SGRLE-compressed data from */
|
|
/* file pointer *fp to the memory area pointed to by *ptr. */
|
|
void sgrle_decompress(FILE *fp, unsigned char *ptr, unsigned long nbytes)
|
|
{
|
|
unsigned long i;
|
|
for(i=0;i<nbytes;i++)
|
|
{
|
|
ptr[i] = sgrle_get_next_byte(fp);
|
|
}
|
|
}
|
|
|
|
/* given a memory area *ptr of length nbytes, compresses the data */
|
|
/* using the SGRLE algorithm and saves it to file *fp */
|
|
void sgrle_compress(FILE *fp, unsigned char *ptr, unsigned long nbytes)
|
|
{
|
|
//long RLErunlen;
|
|
int byt/*, lastbyt*/;
|
|
unsigned long compress_index, run_ahead_index;
|
|
unsigned int runlength;
|
|
int readbyt;
|
|
unsigned int i;
|
|
|
|
|
|
compress_index = 0;
|
|
while(compress_index < nbytes)
|
|
{
|
|
// read a byte from the buffer
|
|
readbyt = ptr[compress_index];
|
|
|
|
/* is the next byte the same? if so find the length of the run */
|
|
if (ptr[compress_index+1] == readbyt)
|
|
{
|
|
|
|
/* find how long readbyt runs */
|
|
run_ahead_index = compress_index + 1;
|
|
runlength = 1;
|
|
do
|
|
{
|
|
byt = ptr[run_ahead_index];
|
|
|
|
// we've run is over when either the byte is different
|
|
// or run_ahead_index is at the end of the buffer,
|
|
// or runlength is approaching FFFF (max possible RLE run length)
|
|
if (byt != readbyt || run_ahead_index >= (nbytes-1) || runlength >= SGRLE_MAXRLELEN)
|
|
{
|
|
// if it's a forced stop because we hit the end of the buffer
|
|
// we need to add 1 to runlength
|
|
if (run_ahead_index >= (nbytes-1))
|
|
{
|
|
runlength++;
|
|
}
|
|
break;
|
|
}
|
|
run_ahead_index++;
|
|
runlength++;
|
|
} while(1);
|
|
|
|
// it takes 4 bytes to code a RLE run, so if the run is less than
|
|
// 4 bytes, it would actually be smaller if we didn't compress it
|
|
if (runlength < 4 && readbyt!=SGRLE_RLEMARKER)
|
|
{
|
|
// RLE run, but too small to bother with
|
|
for(i=0;i<runlength;i++) fputc(readbyt, fp);
|
|
}
|
|
else
|
|
{
|
|
// save a RLE run
|
|
fputc(SGRLE_RLEMARKER, fp);
|
|
fputc(runlength & 255, fp);
|
|
fputc(runlength >> 8, fp);
|
|
fputc(readbyt, fp);
|
|
}
|
|
// advance
|
|
compress_index += runlength;
|
|
}
|
|
else
|
|
{
|
|
// next byte is different, this is not a run it's just a single char
|
|
if (readbyt != SGRLE_RLEMARKER)
|
|
{
|
|
fputc(readbyt, fp);
|
|
}
|
|
else
|
|
{
|
|
// it's a single uncompressed byte which is equal to the RLE marker.
|
|
// delimit it by placing it in a RLE run of length 1.
|
|
runlength = 1;
|
|
fputc(SGRLE_RLEMARKER, fp);
|
|
fputc(runlength & 255, fp);
|
|
fputc(runlength >> 8, fp);
|
|
fputc(readbyt, fp);
|
|
}
|
|
compress_index++;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* resets the decompression engine. (must call this before the first
|
|
time you use sgrle_decompress(), each time you open a new file) */
|
|
void sgrle_reset(void)
|
|
{
|
|
sgrle_runlen = 0;
|
|
}
|