/* Princed V3 - Prince of Persia Level Editor for PC Version
Copyright (C) 2003 Princed Development Team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
The authors of this program may be contacted at http://forum.princed.com.ar
*/
/*
compress.c: Princed Resources : Image Compression Library
����������
Copyright 2003, 2004 Princed Development Team
Created: 24 Aug 2003
Author: Enrique Calot <ecalot.cod@princed.com.ar>
Version: 1.01 (2003-Oct-23)
Version: 2.00 (2004-Mar-07)
Note:
DO NOT remove this copyright notice
*/
#include <stdio.h>
#include <string.h>
#include "compress.h"
#include "memory.h"
/*#include "pr.h"*/
#include "disk.h" /* array2short */
/***************************************************************\
| I M P L E M E N T A T I O N |
\***************************************************************/
/***************************************************************\
| Image transpose |
\***************************************************************/
/* Determines where the transposed byte must be saved */
int transpose(int x,int w,int h) {
return ((x%h)*(w))+(int)(x/h);
}
void transposeImage(tImage* image,int size) {
unsigned char* outputaux=getMemory(size);
int cursor=0;
while (cursor<size) {outputaux[transpose(cursor,image->widthInBytes,image->height)]=image->pix[cursor];cursor++;}
free(image->pix);
image->pix=outputaux;
}
void antiTransposeImage(tImage* image,int size) {
unsigned char* outputaux=getMemory(size);
int cursor=0;
while (cursor<size) {outputaux[cursor]=image->pix[transpose(cursor,image->widthInBytes,image->height)];cursor++;}
free(image->pix);
image->pix=outputaux;
}
/***************************************************************\
| Uncompression algorithms |
\***************************************************************/
/* LZG expansion algorithm sub function */
unsigned char popBit(unsigned char *byte) {
unsigned char bit=(unsigned char)((*byte)&1);
(*byte)>>=1;
return bit;
}
/* Expands LZ Groody algorithm. This is the core of PR */
int expandLzg(const unsigned char* array, int arraySize, tImage* image, int imageSize) {
char k;
int location,h,cursor=0,pos=0;
unsigned char maskbyte,rep;
if ((image->pix=getMemory(/*imageSize*/MAX_MOD_SIZE_IN_LZG))==NULL) return COMPRESS_RESULT_FATAL; /* reserve memory */
for(location=0;location<MAX_MOD_SIZE_IN_LZG;image->pix[location]=0,location++); /* clean output garbage */
/* main loop */
while (cursor<imageSize) {
maskbyte=array[pos++];
for (k=8;k&&(cursor<imageSize);k--) {
if (popBit(&maskbyte)) {
image->pix[cursor++]=array[pos++];
} else {
location=66+(((rep=array[pos])&3)<<8)+(unsigned char)array[pos+1];pos+=2;
rep=(unsigned char)((rep>>2)+3);
while (rep--) { /* Be careful in big images */
h=cursor/MAX_MXD_SIZE_IN_LZG-((location%MAX_MXD_SIZE_IN_LZG)>(cursor%MAX_MXD_SIZE_IN_LZG));
image->pix[cursor++]=image->pix[((h<0)?0:h)*MAX_MXD_SIZE_IN_LZG+(location++)%MAX_MXD_SIZE_IN_LZG];
/*
h=((cursor-(location&0x3FF))&(~0x3FF));
image->pix[cursor]=image->pix[((h<0)?0:h)+(location&0x3FF)];
cursor++;location++;
*/
}
}
}
}
return ((pos==arraySize)&(cursor==imageSize))-1; /* WARNING or SUCCESS */
}
/* Expands RLE algorithm */
int expandRle(const unsigned char* array, int arraySize, tImage* image, int imageSize) {
int cursor=0;
register signed char rep;
int pos=0;
if ((image->pix=getMemory(imageSize+128))==NULL) return COMPRESS_RESULT_FATAL; /* reserve memory */
/* main loop */
while (cursor<imageSize) {
rep=(signed char)(array[pos++]);
if (rep<0) {
/* Negative */
while (rep++) image->pix[cursor++]=array[pos];
pos++;
} else {
/* Positive */
rep=~rep;
while (rep++) image->pix[cursor++]=array[pos++];
}
}
return ((pos==arraySize)&(cursor==imageSize))-1; /* WARNING or SUCCESS */
}
/***************************************************************\
| Compression algorithms |
\***************************************************************/
/* Compress using the Run Length Encoding algorithm */
void compressRle(unsigned char* data,tImage* img,int *dataSize) {
/* Declare pointers */
unsigned char* cursorData = data;
char* counter;
unsigned char* cursorPix = img->pix;
unsigned char* imgEnd = img->pix+(*dataSize);
while (cursorPix<imgEnd) {
/* Step 1: Create counter */
*(counter=(char*)(cursorData++))=-1;
/* Step 2: Look and copy the string until a repeated byte is found */
while (
(cursorPix<imgEnd)&&
(
(*cursorPix!=*(cursorPix+1))||
(
/*(*cursorPix==*(cursorPix+1))&&*/
((cursorPix+1)<imgEnd)&&
(*cursorPix!=*(cursorPix+2))
)
)&&
((*counter)!=127)
) {
*(cursorData)=*(cursorPix);
(*counter)++;
cursorPix++;
cursorData++;
}
/* Step 3: If there was a repeated string, let's ignore it and add the cursor with the repetitions */
if (*counter==-1) {
while ((cursorPix+1<imgEnd)&&(*cursorPix==(*(cursorPix+1)))&&((*counter)!=-128)) {
cursorPix++;
(*counter)--;
}
*(cursorData)=*(cursorPix); /* Print repeated char */
cursorPix++;
cursorData++;
}
}
*(cursorData++)=0;
*(cursorData)=*(cursorPix);
*dataSize=(int)((long int)cursorData-(long int)data)-1; /* Note: casted to long for portability with 64 bits architectures */
}
/***************************************************************\
| Main compress and expand graphics |
\***************************************************************/
/*
Header info:
1 byte - checksum char checksum
2 bytes - height short int height
2 bytes - width short int width
1 byte - 00 (char)0
1 byte - compression type unsigned char compressionType
*/
/* Expands an array into an image */
int mExpandGraphic(const unsigned char* data,tImage *image, int dataSizeInBytes) {
/*
Reads data and extracts tImage
returns the next image address or -1 in case of error
*/
int imageSizeInBytes;
int result;
data++;
image->height=array2short(data);/*((unsigned char)data[0])+((unsigned char)data[1]<<8);data+=2;*/
data+=2;
image->width =array2short(data);/*((unsigned char)data[0])+((unsigned char)data[1]<<8);data+=2;*/
data+=2;
if (*(data++)) return COMPRESS_RESULT_FATAL; /* Verify format */
image->type=(unsigned char)(*(data++));
dataSizeInBytes-=7;
if (image->type&0xB0) {
image->widthInBytes=(image->width+1)/2;
} else {
image->widthInBytes=(image->width+7)/8;
}
imageSizeInBytes=image->widthInBytes*image->height;
switch (getAlgor(image->type)) {
case COMPRESS_RAW: /* No Compression Algorithm */
if ((image->pix=getMemory(imageSizeInBytes))==NULL) return COMPRESS_RESULT_FATAL;
memcpy(image->pix,data,imageSizeInBytes);
result=COMPRESS_RESULT_SUCCESS;
break;
case COMPRESS_RLE_LR: /* RLE Left to Right Compression Algorithm */
result=expandRle(data,dataSizeInBytes,image,imageSizeInBytes);
break;
case COMPRESS_RLE_UD: /* RLE Up to Down Compression Algorithm */
result=expandRle(data,dataSizeInBytes,image,imageSizeInBytes);
if (result==COMPRESS_RESULT_FATAL) return COMPRESS_RESULT_FATAL;
transposeImage(image,imageSizeInBytes);
break;
case COMPRESS_LZG_LR: /* LZ Groody Left to Right Compression Algorithm */
result=expandLzg(data,dataSizeInBytes,image,imageSizeInBytes);
break;
case COMPRESS_LZG_UD: /* LZ Groody Up to Down Compression Algorithm */
result=expandLzg(data,dataSizeInBytes,image,imageSizeInBytes);
if (result==COMPRESS_RESULT_FATAL) return COMPRESS_RESULT_FATAL;
transposeImage(image,imageSizeInBytes);
break;
default:
result=COMPRESS_RESULT_FATAL;
break;
}
return result; /* Ok */
}
/* Compress an image into binary data */
int mCompressGraphic(unsigned char* *data,tImage* image, int* dataSizeInBytes) {
/* Declare variables */
unsigned char* compressed [COMPRESS_WORKING_ALGORITHMS];
int compressedSize [COMPRESS_WORKING_ALGORITHMS];
int algorithm;
int i;
int imageSizeInBytes;
/* Initialize variables */
imageSizeInBytes=image->widthInBytes*image->height;
for (i=0;i<COMPRESS_WORKING_ALGORITHMS;i++) compressedSize[i]=imageSizeInBytes;
/*
Perform all compressions
*/
/* COMPRESS_RAW */
compressed[COMPRESS_RAW]=getMemory(compressedSize[COMPRESS_RAW]);
memcpy(compressed[COMPRESS_RAW],image->pix,compressedSize[COMPRESS_RAW]);
/* COMPRESS_RLE_LR */
compressed[COMPRESS_RLE_LR]=getMemory((10*imageSizeInBytes+50)); /* This will reserve 10*(image size)+50 bytes, to allocate the compressed file */
compressRle(compressed[COMPRESS_RLE_LR],image,&(compressedSize[COMPRESS_RLE_LR]));
/* COMPRESS_RLE_UD */
compressed[COMPRESS_RLE_UD]=getMemory(10*imageSizeInBytes+50); /* This will reserve 10*(image size)+50 bytes, to allocate the compressed file */
antiTransposeImage(image,imageSizeInBytes);
compressRle(compressed[COMPRESS_RLE_UD],image,&(compressedSize[COMPRESS_RLE_UD]));
/*
Process results
*/
/* Select the best compression (find minimum) */
*dataSizeInBytes=compressedSize[COMPRESS_RAW];
algorithm=COMPRESS_RAW;
for (i=COMPRESS_RLE_LR;i<COMPRESS_WORKING_ALGORITHMS;i++) {
if ((*dataSizeInBytes)>compressedSize[i]) {
(*dataSizeInBytes)=compressedSize[i];
algorithm=i;
}
}
/* Copy the best algorithm in the compressed data */
*data=getMemory(*dataSizeInBytes+6);
memcpy(*data+6,compressed[algorithm],*dataSizeInBytes);
(*dataSizeInBytes)+=6;
/*
Write header
*/
/* (16 bits)height (Intel short int format) */
(*data)[0]=image->height;
(*data)[1]=image->height>>8;
/* (16 bits)width (Intel short int format) */
(*data)[2]=image->width;
(*data)[3]=image->width>>8;
/* (8 bits)00000000+(4 bits)palette type+(4 bits)algorithm */
(*data)[4]=0;
(*data)[5]=image->type|algorithm;
/* Free all compression attempts */
for (i=COMPRESS_RAW;i<COMPRESS_WORKING_ALGORITHMS;i++) free(compressed[i]);
return 1;
}