Skip to content

.DAT (Resident Evil 3)

Patrice Mandin edited this page Jan 13, 2019 · 1 revision

D

Table of Contents

Games

The .DAT file format is used by Resident Evil 3 (PC version). It contains an archive of various files (compressed or not).

Structure

The values listed below are stored in Little-Endian order.

A .DAT file is made of 4 KB blocks.

The first block contains the top-level directory name of the archive.

The following blocks contain the list of file names of the archive, along with position in archive and other informations.

The remaining blocks contain the files, all aligned on this 4 KB boundary.

Block with directory names

Archive header

Each archive starts with this constant header:

typedef struct {
  unsigned long unknown1[3]; /* Constants: 0x03, 0x01, 0x04 */
  unsigned short unknown2;   /* Constant: 0x0100 */
} re3_dat_header_t;

Directory entries

Following the header, there is a list of directories. Each directory listed is a subdirectory of the previous one.

typedef struct {
  unsigned char unknown1[3]; /* Level ? */
  unsigned char unknown2[3]; /* Size/length ? */
  unsigned char unknown3;    /* Attributes ? */
  unsigned char name[];   /* NULL terminated directory name, aligned on WORD boundary */
} re3_dat_dir_t;

Block with file names

The first long is the number of files.

Then for each file you got the following scheme:

typedef struct {
  unsigned long offset; /* Absolute position of file in 8-byte blocks inside archive */
  unsigned long length; /* Length of file+header in bytes */
  char filename[]; /* NULL terminated filename string */
} re3_dat_file_t;
For example, if offset value is 0x300, it means the file resides at offset 0x300*8 = 0x1800 in the archive.

Blocks with file data

Each file has an extra header, which does not belong to original file. It contains information about compression and decryption.

Ident strings found:
- "NotComp\0" -> The file is not compressed.
- "Hi_Comp\0" -> The file is compressed, length is the uncompressed length.

File decryption

Following this header, are two arrays:

unsigned long process_keys[num_keys]; /* 32 bits key to start decryption */
unsigned long process_length[num_keys]; /* File block length on which to apply decryption */
                                        /* before using next key */

It means you use 'process_keys[0]' to decrypt 'process_length[0]' bytes of the file. Summing all process_length[] values will give you the total length of file in archive. If the file is compressed, it is the length of the compressed file.

Decryption routine
typedef struct {
	unsigned short offset;
	unsigned short num_keys;
	unsigned long length;
	unsigned char ident[8];
} re3_dat_file_t;

const unsigned short base_array[64]={
	0x00e6, 0x01a4, 0x00e6, 0x01c5,
	0x0130, 0x00e8, 0x03db, 0x008b,
	0x0141, 0x018e, 0x03ae, 0x0139,
	0x00f0, 0x027a, 0x02c9, 0x01b0,
	0x01f7, 0x0081, 0x0138, 0x0285,
	0x025a, 0x015b, 0x030f, 0x0335,
	0x02e4, 0x01f6, 0x0143, 0x00d1,
	0x0337, 0x0385, 0x007b, 0x00c6,
	0x0335, 0x0141, 0x0186, 0x02a1,
	0x024d, 0x0342, 0x01fb, 0x03e5,
	0x01b0, 0x006d, 0x0140, 0x00c0,
	0x0386, 0x016b, 0x020b, 0x009a,
	0x0241, 0x00de, 0x015e, 0x035a,
	0x025b, 0x0154, 0x0068, 0x02e8,
	0x0321, 0x0071, 0x01b0, 0x0232,
	0x02d9, 0x0263, 0x0164, 0x0290
};

unsigned char *dstPointer = NULL;
unsigned long dstLength = 0;

/* 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;
}

unsigned char re3_next_key(unsigned long *key)
{
	*key *= 0x5d588b65;
	*key += 0x8000000b;

	return (*key >> 24);
}

void re3_decrypt_block(unsigned char *src, unsigned long key, unsigned long length)
{
	unsigned char xor_key, base_index, modulo;
	int i, block_index;

	xor_key = re3_next_key(&key);
	modulo = re3_next_key(&key);
	base_index = modulo % 0x3f;

	block_index = 0;
	for (i=0; i<length; i++) {
		if (block_index>base_array[base_index]) {
			modulo = re3_next_key(&key);
			base_index = modulo % 0x3f;
			xor_key = re3_next_key(&key);
			block_index = 0;
		}
		src[i] ^= xor_key;
		block_index++;
	}
}

void re3_decrypt(unsigned char *src)
{
	re3_dat_file_t *header = (re3_dat_file_t *) src;	
	int i, offset;
	unsigned long *array_keys, *array_length;

	/* Decrypt ident */
	for (i=0; i<8; i++) {
		header->ident[i] ^= header->ident[7];
	}

	offset = header->offset;

	/* array_keys follows the file header */
	array_keys = (unsigned long *) &src[sizeof(re3_dat_file_t)];
	/* and array_length follows array_keys */
	array_length = &array_keys[header->num_keys];

	/* Decrypt each block with its specific key */
	for (i=0; i<header->num_keys; i++) {
		re3_decrypt_block(&src[offset], array_keys[i], array_length[i]);		
		offset += array_length[i];
	}

	dstPointer = &src[header->offset];
	dstLength = offset - header->offset;
}

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;
	}

	re3_decrypt(fileInMem);

	/* Now you have the decrypted file at dstPointer */
	/*  and length is dstLength */

	free(fileInMem);

	return 0;
}

File decompression

Files are compressed with a modified LZSS routine, using a 4KB buffer.

Decompression routine
typedef struct {
	unsigned long a,b,c,d;
} re3_hic_t;

re3_hic_t tmp4k[256];

unsigned long tmp8[64];

void re3_depack(unsigned char *src, unsigned char *dst, int length)
{
	int i,j;

	for (i=0; i<256; i++) {
		unsigned long j = (i<<24)|(i<<16)|(i<<8)|i;
		tmp4k[i].a = tmp4k[i].b = tmp4k[i].c = tmp4k[i].d = j;
	}

	memset(tmp8, 0, sizeof(tmp8));

	if (length<=0) {
		return;
	}

	i = j = 0;
	do {	
		++i;

		edx &= 0xffffff00;
		edx |= *src++;
		edx <<= i;

		eax &= 0xffffff00;
		eax |= *src;
		eax >>= 8-i;

		edx |= eax;		

		if (i==8) {
			src++;
			i=0;
		}

		if ((edx & (1<<16))==0) {
			*dst++ = tmp4k[j++] = edx;
			len--;
		} else {
			eax &= 0xffffff00;
			eax |= (*src++ << i) & 0xff;			

			edx &= 0xff;
			edx <<= 4;
			
			edi = dest;

			eax &= 0xff;
			eax |= *src >> (8-i); 
			esi = eax;

			eax &= 0xf; /* low 4 bits */
			eax += 2;
			esi >>= 4; /* high 4 bits */
			esi &= 0xfff;

			esi |= edx;

			memcpy(dest, &tmp[esi], eax);
			dest += eax;
			esi += eax;

			memcpy(dest, &tmp[j], eax);
			dest += eax;

			j += eax;
			length -= eax;
		}

		if (j>=4096) {
			j = 0;
		}
	} while (length>0);
}