PAK (Resident Evil 1997)

From Just Solve the File Format Problem
Revision as of 16:01, 27 August 2023 by Jsummers (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
File Format
Name PAK (Resident Evil 1997)
Ontology
Extension(s) .pak

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;
}
Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox