-
Notifications
You must be signed in to change notification settings - Fork 9
.DAT (Resident Evil 3)
The .DAT file format is used by Resident Evil 3 (PC version). It contains an archive of various files (compressed or not).
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.
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;
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;
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.
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.
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.
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; }
Files are compressed with a modified LZSS routine, using a 4KB buffer.
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); }