PAK (Resident Evil 1997)
From Just Solve the File Format Problem
This content was first retrieved from the Internet Archive's Wayback Machine for the date 2016-12-22 from the following URL: https://web.archive.org/web/20161222090005/http://rewiki.regengedanken.de/wiki/.PAK_(Resident_Evil)
The .PAK file format is used by the PC version of Resident Evil (1997). It contains a compressed TIM image, using an algorithm similar to LZW.
Structure
The file is a bitstream, without any header.
Example decompression routine
This source is partly based on LZW decoder from scummvm engine.
#include <stdio.h>
#include <fcntl.h>
unsigned char dstPointer[512<<10];
int dstOffset;
unsigned long dstLength;
unsigned char *srcPointer;
int srcOffset;
unsigned char srcByte;
int tmpMask;
#define MAX_LENGTH 35024
typedef struct {
long flag;
long index;
long value;
} re1_pack_t;
re1_pack_t tmpArray2[MAX_LENGTH];
unsigned char decodeStack[MAX_LENGTH];
/* Load file in mem from filename, return buffer, update length */
char *loadFile(char *filename, int *length)
{
int handle;
char *buffer;
/* Load file */
handle = open(filename, O_RDONLY);
if (handle<0) {
fprintf(stderr, "Unable to open %s\n", filename);
return NULL;
}
*length = lseek(handle, 0, SEEK_END);
lseek(handle, 0, SEEK_SET);
buffer = (char *)malloc(*length);
if (buffer==NULL) {
fprintf(stderr, "Unable to allocate %d bytes\n", length);
return NULL;
}
read(handle, buffer, *length);
close(handle);
return buffer;
}
int re1_read_bits(int num_bits)
{
unsigned long value=0, mask;
mask = 1<<(--num_bits);
while (mask>0) {
if (tmpMask == 0x80) {
srcByte = srcPointer[srcOffset++];
}
if ((tmpMask & srcByte)!=0) {
value |= mask;
}
tmpMask >>= 1;
mask >>= 1;
if (tmpMask == 0) {
tmpMask = 0x80;
}
}
return value;
}
int decodeString(int decodeStackOffset, unsigned long code)
{
int i;
for (i=0; code>255; ) {
decodeStack[decodeStackOffset++] = tmpArray2[code].value;
code = tmpArray2[code].index;
i++;
}
decodeStack[decodeStackOffset] = code;
return decodeStackOffset;
}
void re1_depack(unsigned char *src, int src_length)
{
int num_bits_to_read, i;
int lzwnew, c, lzwold, lzwnext;
srcPointer = src;
srcOffset = 0;
tmpMask = 0x80;
srcByte = 0;
dstOffset = 0;
memset(tmpArray2, 0, sizeof(tmpArray2));
for(;;) {
for (i=0; i<MAX_LENGTH; i++) {
tmpArray2[i].flag = 0xffffffff;
}
lzwnext = 0x103;
num_bits_to_read = 9;
c = lzwold = re1_read_bits(num_bits_to_read);
if (lzwold == 0x100) {
break;
}
dstPointer[dstOffset++] = c;
for(;;) {
lzwnew = re1_read_bits(num_bits_to_read);
if (lzwnew == 0x100) {
dstLength = dstOffset;
return;
}
if (lzwnew == 0x102) {
break;
}
if (lzwnew == 0x101) {
num_bits_to_read++;
continue;
}
if (lzwnew >= lzwnext) {
decodeStack[0] = c;
i = decodeString(1, lzwold);
} else {
i = decodeString(0, lzwnew);
}
c = decodeStack[i];
while (i>=0) {
dstPointer[dstOffset++] = decodeStack[i--];
}
tmpArray2[lzwnext].index = lzwold;
tmpArray2[lzwnext].value = c;
lzwnext++;
lzwold = lzwnew;
}
}
dstLength = dstOffset;
}
int main(int argc, char **argv)
{
int length;
unsigned char *fileInMem;
if (argc<2) {
return 1;
}
fileInMem = loadFile(argv[1], &length);
if (fileInMem==NULL) {
return 1;
}
re1_depack(fileInMem, length);
/* Now you have the decompressed file at dstPointer */
/* and length is dstLength */
free(fileInMem);
return 0;
}