瀏覽代碼

rofs: Resident Evil 3 (PC, 1999) rofs.dat archive support

Patrice Mandin 2 月之前
父節點
當前提交
fd22fc2580
共有 6 個文件被更改,包括 834 次插入0 次删除
  1. 7 0
      CMakeLists.txt
  2. 3 0
      docs/CREDITS.txt
  3. 1 0
      src/Makefile.os2
  4. 3 0
      src/physfs.c
  5. 816 0
      src/physfs_archiver_rofs.c
  6. 4 0
      src/physfs_internal.h

+ 7 - 0
CMakeLists.txt

@@ -99,6 +99,7 @@ set(PHYSFS_SRCS
     src/physfs_archiver_7z.c
     src/physfs_archiver_mvl.c
     src/physfs_archiver_qpak.c
+    src/physfs_archiver_rofs.c
     src/physfs_archiver_wad.c
     src/physfs_archiver_csm.c
     src/physfs_archiver_zip.c
@@ -157,6 +158,11 @@ if(NOT PHYSFS_ARCHIVE_QPAK)
     add_definitions(-DPHYSFS_SUPPORTS_QPAK=0)
 endif()
 
+option(PHYSFS_ARCHIVE_ROFS "Enable Resident Evil 3 ROFS support" TRUE)
+if(NOT PHYSFS_ARCHIVE_ROFS)
+    add_definitions(-DPHYSFS_SUPPORTS_ROFS=0)
+endif()
+
 option(PHYSFS_ARCHIVE_SLB "Enable I-War / Independence War SLB support" TRUE)
 if(NOT PHYSFS_ARCHIVE_SLB)
     add_definitions(-DPHYSFS_SUPPORTS_SLB=0)
@@ -415,6 +421,7 @@ message_bool_option("CSM support" PHYSFS_ARCHIVE_CSM)
 message_bool_option("HOG support" PHYSFS_ARCHIVE_HOG)
 message_bool_option("MVL support" PHYSFS_ARCHIVE_MVL)
 message_bool_option("QPAK support" PHYSFS_ARCHIVE_QPAK)
+message_bool_option("ROFS support" PHYSFS_ARCHIVE_ROFS)
 message_bool_option("SLB support" PHYSFS_ARCHIVE_SLB)
 message_bool_option("VDF support" PHYSFS_ARCHIVE_VDF)
 message_bool_option("ISO9660 support" PHYSFS_ARCHIVE_ISO9660)

+ 3 - 0
docs/CREDITS.txt

@@ -167,6 +167,9 @@ Bug fixes,
 Bug fixes:
     Rob Loach
 
+ROFS archiver:
+    Patrice Mandin
+
 Other stuff:
     Your name here! Patches go to icculus@icculus.org ...
 

+ 1 - 0
src/Makefile.os2

@@ -21,6 +21,7 @@ SRCS = physfs.c                   &
        physfs_archiver_7z.c       &
        physfs_archiver_mvl.c      &
        physfs_archiver_qpak.c     &
+       physfs_archiver_rofs.c     &
        physfs_archiver_wad.c      &
        physfs_archiver_zip.c      &
        physfs_archiver_slb.c      &

+ 3 - 0
src/physfs.c

@@ -1179,6 +1179,9 @@ static int initStaticArchivers(void)
     #if PHYSFS_SUPPORTS_QPAK
         REGISTER_STATIC_ARCHIVER(QPAK);
     #endif
+    #if PHYSFS_SUPPORTS_ROFS
+        REGISTER_STATIC_ARCHIVER(ROFS);
+    #endif
     #if PHYSFS_SUPPORTS_HOG
         REGISTER_STATIC_ARCHIVER(HOG);
     #endif

+ 816 - 0
src/physfs_archiver_rofs.c

@@ -0,0 +1,816 @@
+/*
+ * Resident Evil 3 rofs<n>.dat support routines for PhysicsFS.
+ *
+ * This driver handles Resident Evil 3 archives.
+ *
+ * Please see the file LICENSE in the source's root directory.
+ *
+ * This file written by Patrice Mandin, based on the QPAK archiver by
+ *  Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+#if PHYSFS_SUPPORTS_ROFS
+
+typedef struct
+{
+	__PHYSFS_DirTreeEntry tree;	/* manages directory tree */
+
+	PHYSFS_uint32 startPos;
+	PHYSFS_uint32 size;
+	char name[48];	/* room to store /dir1/dir2/filename */
+
+	int compression;
+
+	/* Block infos */
+	int num_blocks;
+	PHYSFS_uint32 *block_alloc;
+	PHYSFS_uint32 *block_startkey;
+	PHYSFS_uint32 *block_length;
+} ROFSentry;
+
+/* rofsinfo -> rofsentry level1 -> rofsentry level2 -> rofsentry files */
+
+typedef struct
+{
+	__PHYSFS_DirTree tree;	/* manages directory tree. */
+	PHYSFS_Io *io;			/* the i/o interface for this archive. */
+
+	PHYSFS_uint32 entryCount;
+	ROFSentry dirs[2];
+	ROFSentry *entries;
+} ROFSinfo;
+
+typedef struct {
+	/* Current decryption infos */
+	PHYSFS_uint32 curblock, curoffset;
+	PHYSFS_uint32 curkey, blockindex;
+	PHYSFS_uint8 xorkey, baseindex;
+} rofs_decrypt_t;
+
+enum {
+	ROFS_STATE_UNPACK_BYTE,
+	ROFS_STATE_UNPACK_BLOCK,
+	ROFS_STATE_COPY_BLOCK,
+	ROFS_STATE_ERROR
+};
+
+typedef struct {
+	int state;	/* above enum */
+
+	int src_num_bit, src_byte_num;
+	PHYSFS_uint8 src_bytes[2];
+	int lzss_pos, lzss_start, lzss_length, copy_length;
+	PHYSFS_uint8 *lzss_dict;
+} rofs_depack_t;
+
+typedef struct
+{
+	ROFSentry *entry;
+	PHYSFS_Io *io;
+
+	PHYSFS_uint32 curPos;
+
+	rofs_decrypt_t decrypt;
+	rofs_depack_t depack;
+} ROFSfileinfo;
+
+typedef struct {
+	PHYSFS_uint16 offset;
+	PHYSFS_uint16 num_keys;
+	PHYSFS_uint32 length;
+	char ident[8];
+} rofs_dat_file_t;
+
+static PHYSFS_sint64 ROFS_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len);
+
+/*
+ * Read an unsigned 32-bit int and swap to native byte order.
+ */
+static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
+{
+	PHYSFS_uint32 v;
+	BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
+	*val = PHYSFS_swapULE32(v);
+	return 1;
+} /* readui32 */
+
+
+/*--- Decryption ---*/
+
+static 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
+};
+
+static PHYSFS_uint8 rofs_next_key(PHYSFS_uint32 *key)
+{
+	*key *= 0x5d588b65;
+	*key += 0x8000000b;
+
+	return (*key >> 24);
+}
+
+static PHYSFS_uint8 rofs_decrypt_byte(ROFSfileinfo *finfo, PHYSFS_uint8 value)
+{
+	if (finfo->decrypt.blockindex>base_array[finfo->decrypt.baseindex]) {
+		finfo->decrypt.baseindex = rofs_next_key(&finfo->decrypt.curkey) % 0x3f;
+		finfo->decrypt.xorkey = rofs_next_key(&finfo->decrypt.curkey);
+		finfo->decrypt.blockindex = 0;
+	}
+	finfo->decrypt.blockindex++;
+
+	return (value ^ finfo->decrypt.xorkey);
+}
+
+static void rofs_decrypt_block_init(ROFSfileinfo *finfo)
+{
+	ROFSentry *entry = finfo->entry;
+
+	finfo->decrypt.curoffset = 0;
+	finfo->decrypt.curkey = entry->block_startkey[finfo->decrypt.curblock];
+	finfo->decrypt.blockindex = 0;
+	finfo->decrypt.xorkey = rofs_next_key(&finfo->decrypt.curkey);
+	finfo->decrypt.baseindex = rofs_next_key(&finfo->decrypt.curkey) % 0x3f;
+}
+
+
+/*--- Decompression ---*/
+
+static void rofs_depack_block_init(ROFSfileinfo *finfo)
+{
+	int i;
+
+	if (!finfo->depack.lzss_dict) {
+		finfo->depack.lzss_dict = (PHYSFS_uint8 *) allocator.Malloc(4096+256);
+	}
+
+	if (finfo->depack.lzss_dict) {
+		for (i=0; i<256; i++) {
+			memset(&(finfo->depack.lzss_dict)[i*16], i, 16);
+		}
+		memset(&(finfo->depack.lzss_dict)[4096], 0, 256);
+	}
+
+	finfo->depack.state = ROFS_STATE_UNPACK_BYTE;
+	finfo->depack.src_num_bit = 8;
+	finfo->depack.src_byte_num = 1;
+	finfo->depack.src_bytes[0] = 0;
+	finfo->depack.src_bytes[1] = 0;
+	finfo->depack.lzss_pos = 0;
+	finfo->depack.lzss_start = 0;
+	finfo->depack.lzss_length = 0;
+}
+
+/*	if (__PHYSFS_platformRead(finfo->handle, &dummy, 1) != 1) {	\*/
+
+#define ROFS_READ_BYTE() \
+{	\
+	PHYSFS_uint8 dummy;	\
+	\
+	if (finfo->decrypt.curoffset >= entry->block_length[finfo->decrypt.curblock]) {	\
+		finfo->depack.state = ROFS_STATE_ERROR;	\
+		break;	\
+	}	\
+	\
+	if (finfo->io->read(finfo->io, &dummy, 1) != 1) {	\
+		finfo->depack.state = ROFS_STATE_ERROR;	\
+		break;	\
+	}	\
+	\
+	dummy = rofs_decrypt_byte(finfo, dummy);	\
+	finfo->decrypt.curoffset++;	\
+	\
+	finfo->depack.src_byte_num ^= 1;	\
+	finfo->depack.src_bytes[finfo->depack.src_byte_num] = dummy;	\
+}
+
+static int rofs_depack_block(ROFSfileinfo *finfo, void *buffer,
+							 PHYSFS_uint32 srcLength, PHYSFS_uint32 dstLength)
+{
+	ROFSentry *entry = finfo->entry;
+	PHYSFS_uint8 *dstBuffer = (PHYSFS_uint8 *) buffer;
+	int dstIndex = 0;
+
+	if (!finfo->depack.lzss_dict) {
+		return(0);
+	}
+
+	while ((dstIndex < dstLength) && (finfo->depack.state!=ROFS_STATE_ERROR)) {
+		switch(finfo->depack.state) {
+			case ROFS_STATE_UNPACK_BYTE:
+			{
+				PHYSFS_uint16 src_bitfield;
+
+				if (finfo->depack.lzss_pos > 4095) {
+					finfo->depack.lzss_pos = 0;
+				}
+
+				if (finfo->depack.src_num_bit==8) {
+					finfo->depack.src_num_bit = 0;
+					ROFS_READ_BYTE();
+				}
+
+				finfo->depack.src_num_bit++;
+
+				src_bitfield =
+				finfo->depack.src_bytes[finfo->depack.src_byte_num] << finfo->depack.src_num_bit;
+
+				ROFS_READ_BYTE();
+
+				src_bitfield |=
+				finfo->depack.src_bytes[finfo->depack.src_byte_num] >> (8-finfo->depack.src_num_bit);
+
+				if (src_bitfield & (1<<8)) {
+					/* Init copy from lzss dict */
+					int value2;
+
+					value2 =
+					finfo->depack.src_bytes[finfo->depack.src_byte_num] << finfo->depack.src_num_bit;
+
+					ROFS_READ_BYTE();
+
+					value2 |=
+					finfo->depack.src_bytes[finfo->depack.src_byte_num] >> (8-finfo->depack.src_num_bit);
+
+					finfo->depack.lzss_start = (value2 >> 4) & 0xfff;
+					finfo->depack.lzss_start |= (src_bitfield & 0xff) << 4;
+					finfo->depack.lzss_length = (value2 & 0x0f)+2;
+					finfo->depack.copy_length = finfo->depack.lzss_length;
+					finfo->depack.state = ROFS_STATE_UNPACK_BLOCK;
+				} else {
+					/* Copy byte */
+					dstBuffer[dstIndex++] =
+					finfo->depack.lzss_dict[finfo->depack.lzss_pos++] =
+					src_bitfield;
+				}
+			}
+			break;
+			case ROFS_STATE_UNPACK_BLOCK:
+			{
+				/* Copy from dictionnary */
+				dstBuffer[dstIndex++] =
+				finfo->depack.lzss_dict[finfo->depack.lzss_start++];
+				if (--finfo->depack.lzss_length <= 0) {
+					finfo->depack.state = ROFS_STATE_COPY_BLOCK;
+				}
+			}
+			break;
+			case ROFS_STATE_COPY_BLOCK:
+			{
+				int i;
+				PHYSFS_uint8 *src =
+				&(dstBuffer[dstIndex - finfo->depack.copy_length]);
+
+				for (i=0; i<finfo->depack.copy_length; i++) {
+					finfo->depack.lzss_dict[finfo->depack.lzss_pos++] = *src++;
+				}
+				finfo->depack.state = ROFS_STATE_UNPACK_BYTE;
+			}
+			break;
+		}
+	}
+
+	return(dstIndex);
+}
+
+
+/*--- Physfs driver ---*/
+
+static PHYSFS_sint64 ROFS_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
+{
+	ROFSfileinfo *finfo = (ROFSfileinfo *) io->opaque;
+	ROFSentry *entry = finfo->entry;
+	PHYSFS_uint32 curPos = finfo->curPos;
+	PHYSFS_uint8 *dstBuffer = (PHYSFS_uint8 *) buf;
+	int srcLength, dstLength;
+	PHYSFS_sint64 retval = 0;
+	PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
+	PHYSFS_sint64 avail = entry->size - finfo->curPos;
+
+	if (avail < maxread)
+		maxread = avail;
+
+	BAIL_IF_ERRPASS(maxread == 0, 0);    /* quick rejection. */
+
+	dstLength = maxread;
+	while (dstLength>0) {
+		/* Limit to number of bytes to read */
+		srcLength = entry->block_length[finfo->decrypt.curblock] - finfo->decrypt.curoffset;
+
+		if (entry->compression) {
+			/* read max srcLength, or depack max 32K */
+			int dstDepacked;
+
+			/* Read till end of current block */
+			dstDepacked = rofs_depack_block(finfo, dstBuffer, srcLength, dstLength);
+
+			dstBuffer += dstDepacked;
+			finfo->curPos += dstDepacked;
+			dstLength -= dstDepacked;
+		} else {
+			int i;
+
+			if (srcLength > dstLength) {
+				srcLength = dstLength;
+			}
+
+			retval = finfo->io->read(finfo->io, dstBuffer, srcLength);
+
+			for (i=0; i<retval; i++) {
+				PHYSFS_uint8 c = rofs_decrypt_byte(finfo, *dstBuffer);
+				*dstBuffer++ = c;
+			}
+
+			finfo->decrypt.curoffset += (PHYSFS_uint32) retval;
+			finfo->curPos += (PHYSFS_uint32) retval;
+			dstLength -= (PHYSFS_uint32) retval;
+		}
+
+		/* Next block ? */
+		if (finfo->decrypt.curoffset >= entry->block_length[finfo->decrypt.curblock]) {
+			finfo->decrypt.curblock++;
+			rofs_decrypt_block_init(finfo);
+			if (entry->compression) {
+				rofs_depack_block_init(finfo);
+			}
+		}
+	}
+
+	return finfo->curPos - curPos;
+} /* ROFS_read */
+
+
+static PHYSFS_sint64 ROFS_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
+{
+	BAIL(PHYSFS_ERR_READ_ONLY, -1);
+} /* ROFS_write */
+
+
+static PHYSFS_sint64 ROFS_tell(PHYSFS_Io *io)
+{
+	return ((ROFSfileinfo *) io->opaque)->curPos;
+} /* ROFS_tell */
+
+
+static int ROFS_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
+{
+	ROFSfileinfo *finfo = (ROFSfileinfo *) io->opaque;
+	ROFSentry *entry = finfo->entry;
+	int rc = 1, i, srcBlock, dstBlock, remaining;
+
+	BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
+
+	/* Skip to a given 32KB block */
+	srcBlock = finfo->curPos >> 15;
+	dstBlock = offset >> 15;
+	if ((srcBlock != dstBlock) || (offset < finfo->curPos)) {
+		PHYSFS_uint64 startOffset;
+
+		finfo->decrypt.curblock = dstBlock;
+
+		rofs_decrypt_block_init(finfo);
+		if (entry->compression) {
+			rofs_depack_block_init(finfo);
+		}
+
+		/* Seek to start of block */
+		startOffset = 0;
+		for (i=0; i<dstBlock; i++) {
+			startOffset += entry->block_length[i];
+		}
+
+		finfo->curPos = startOffset;
+
+		finfo->io->seek(finfo->io, entry->startPos + startOffset);
+	}
+
+	if (!rc) {
+		return(0);
+	}
+
+	/* Then advance in the block */
+	remaining = offset - finfo->curPos;
+	if (entry->compression) {
+		for (i=0; i<remaining; i++) {
+			PHYSFS_uint8 dummy;
+			if (!rofs_depack_block(finfo, &dummy, 32768, 1)) {
+				return(0);
+			}
+		}
+	} else {
+		/* Run decryption steps */
+		for (i=0; i<remaining; i++) {
+			PHYSFS_uint8 dummy;
+
+			finfo->io->read(finfo->io, &dummy, 1);
+			dummy = rofs_decrypt_byte(finfo, dummy);
+		}
+		finfo->curPos += i;
+		finfo->decrypt.curoffset += i;
+	}
+
+	return(rc);
+} /* ROFS_seek */
+
+
+static PHYSFS_sint64 ROFS_length(PHYSFS_Io *io)
+{
+	ROFSfileinfo *finfo = (ROFSfileinfo *) io->opaque;
+	return((PHYSFS_sint64) finfo->entry->size);
+} /* ROFS_length */
+
+
+static PHYSFS_Io *ROFS_duplicate(PHYSFS_Io *_io)
+{
+    ROFSfileinfo *origfinfo = (ROFSfileinfo *) _io->opaque;
+    PHYSFS_Io *io = NULL;
+    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    ROFSfileinfo *finfo = (ROFSfileinfo *) allocator.Malloc(sizeof (ROFSfileinfo));
+    GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ROFS_duplicate_failed);
+    GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ROFS_duplicate_failed);
+
+    io = origfinfo->io->duplicate(origfinfo->io);
+    if (!io) goto ROFS_duplicate_failed;
+    finfo->io = io;
+    finfo->entry = origfinfo->entry;
+    finfo->curPos = 0;
+
+		/* Init decryption */
+	finfo->decrypt.curblock = 0;
+	rofs_decrypt_block_init(finfo);
+
+	/* Init decompression */
+	if (finfo->entry->compression) {
+		rofs_depack_block_init(finfo);
+	}
+
+	memcpy(retval, _io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+    return retval;
+
+ROFS_duplicate_failed:
+    if (finfo != NULL) allocator.Free(finfo);
+    if (retval != NULL) allocator.Free(retval);
+    if (io != NULL) io->destroy(io);
+    return NULL;
+} /* ROFS_duplicate */
+
+
+static int ROFS_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
+
+
+static void ROFS_destroy(PHYSFS_Io *io)
+{
+	ROFSfileinfo *finfo = (ROFSfileinfo *) io->opaque;
+	finfo->io->destroy(finfo->io);
+
+	if (finfo->depack.lzss_dict) {
+		allocator.Free(finfo->depack.lzss_dict);
+		finfo->depack.lzss_dict = NULL;
+	}
+
+	allocator.Free(finfo);
+	allocator.Free(io);
+} /* ROFS_destroy */
+
+
+static const PHYSFS_Io ROFS_Io =
+{
+	0x12345678, NULL,
+	ROFS_read,
+	ROFS_write,
+	ROFS_seek,
+	ROFS_tell,
+	ROFS_length,
+	ROFS_duplicate,
+	ROFS_flush,
+	ROFS_destroy
+};
+
+
+/* All rofs<n>.dat files start with this */
+static const char rofs_id[21]={
+	3, 0, 0, 0,
+	1, 0, 0, 0,
+	4, 0, 0, 0,
+	0, 1, 1, 0,
+	0, 4, 0, 0,
+	0
+};
+
+static int isRofs(PHYSFS_Io *io)
+{
+	PHYSFS_uint8 buf[21];
+
+	/* Check if known header */
+	BAIL_IF_ERRPASS(!io->seek(io, 0), 0);
+
+	if (io->read(io, buf, sizeof(rofs_id)) != sizeof(rofs_id))
+		return 0;
+
+	if (memcmp(buf, rofs_id, sizeof(rofs_id)) != 0)
+		return 0;
+
+	return(1);
+} /* isRofs */
+
+
+static int rofs_read_filename(PHYSFS_Io *io, char *dst, PHYSFS_uint32 dst_len)
+{
+	int i = 0;
+
+	memset(dst, '\0', dst_len);
+
+	do {
+		char c;
+
+		if (!__PHYSFS_readAll(io, &c, 1))
+			return(0);
+
+		dst[i] = c;
+	} while (dst[i++] != '\0');
+
+	return(1);
+}
+
+
+static ROFSentry *rofs_load_entry(ROFSinfo *info)
+{
+    PHYSFS_Io *io = info->io;
+	rofs_dat_file_t fileHeader;
+	char shortname[16];
+	PHYSFS_sint64 location;
+	int i;
+	ROFSentry entry, *retval;
+
+    memset(&entry, '\0', sizeof (entry));
+
+	BAIL_IF_ERRPASS(!readui32(io, &entry.startPos), 0);
+	entry.startPos <<= 3;
+
+	BAIL_IF_ERRPASS(!readui32(io, &entry.size), 0);
+	entry.size = 0; /* This was compressed size */
+
+	if (!rofs_read_filename(io, shortname, sizeof(shortname))) {
+		return 0;
+	}
+
+	/* Generate long filename */
+	i = snprintf(entry.name, sizeof(entry.name), "%s/%s/%s", info->dirs[0].name,
+			info->dirs[1].name, shortname);
+
+	/* Now go read real file size */
+	location = io->tell(io);
+
+	BAIL_IF_ERRPASS(!io->seek(io, entry.startPos), 0);
+
+	io->read(io, &fileHeader, sizeof(fileHeader));
+
+	fileHeader.offset = PHYSFS_swapULE16(fileHeader.offset);
+	fileHeader.num_keys = PHYSFS_swapULE16(fileHeader.num_keys);
+	fileHeader.length = PHYSFS_swapULE32(fileHeader.length);
+	for (i=0; i<8; i++) {
+		fileHeader.ident[i] ^= fileHeader.ident[7];
+	}
+
+	entry.size = fileHeader.length;
+
+	/*--- Decryption info ---*/
+	entry.num_blocks = fileHeader.num_keys;
+	entry.block_alloc = (PHYSFS_uint32 *) allocator.Malloc(fileHeader.num_keys*2*sizeof(PHYSFS_uint32));
+    BAIL_IF(!entry.block_alloc, PHYSFS_ERR_OUT_OF_MEMORY, 0);
+
+	io->read(io, entry.block_alloc, fileHeader.num_keys*2*sizeof(PHYSFS_uint32));
+
+	entry.block_startkey = entry.block_alloc;
+	entry.block_length = &entry.block_alloc[fileHeader.num_keys];
+	for (i=0; i<entry.num_blocks; i++) {
+		entry.block_startkey[i] = PHYSFS_swapULE32(entry.block_startkey[i]);
+		entry.block_length[i] = PHYSFS_swapULE32(entry.block_length[i]);
+	}
+
+	/*--- Decompression info ---*/
+	entry.compression = (strcmp("Hi_Comp", fileHeader.ident)==0);
+
+	/* Fix offset to start of encrypted/packed data */
+	entry.startPos += fileHeader.offset;
+
+	if (!io->seek(io, location))
+		goto failed;
+
+    retval = (ROFSentry *) __PHYSFS_DirTreeAdd(&info->tree, entry.name, 0);
+	GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, failed);
+
+    /* Move the data we already read into place in the official object. */
+    memcpy(((PHYSFS_uint8 *) retval) + sizeof (__PHYSFS_DirTreeEntry),
+           ((PHYSFS_uint8 *) &entry) + sizeof (__PHYSFS_DirTreeEntry),
+           sizeof (*retval) - sizeof (__PHYSFS_DirTreeEntry));
+
+	return retval;  /* success. */
+
+failed:
+	allocator.Free(entry.block_alloc);
+	return NULL;  /* failure. */
+} /* rofs_load_entry */
+
+
+static int rofs_load_header(ROFSinfo *info)
+{
+    PHYSFS_Io *io = info->io;
+	PHYSFS_uint32 dir_location, dir_length;
+
+	/* Till now all tested rofs.dat file archives have 2 levels */
+	if (!rofs_read_filename(io, info->dirs[0].name, sizeof(info->dirs[0].name))) {
+		return 0;
+	}
+
+	BAIL_IF_ERRPASS(!readui32(io, &dir_location), -1);
+	dir_location <<= 3;
+
+	BAIL_IF_ERRPASS(!readui32(io, &dir_length), -1);
+
+	if (!rofs_read_filename(io, info->dirs[1].name, sizeof(info->dirs[1].name))) {
+		return 0;
+	}
+
+	info->dirs[0].startPos = info->dirs[1].startPos = dir_location;
+	info->dirs[0].size = info->dirs[1].size = dir_length;
+
+	return(1);
+}
+
+
+static int rofs_load_entries(ROFSinfo *info)
+{
+    PHYSFS_Io *io = info->io;
+	int i;
+
+	BAIL_IF_ERRPASS(!io->seek(io, sizeof(rofs_id)), 0);
+	BAIL_IF_ERRPASS(!rofs_load_header(info), 0);
+
+	BAIL_IF_ERRPASS(!io->seek(io, info->dirs[0].startPos), 0);
+	BAIL_IF_ERRPASS(!readui32(io, &info->entryCount), -1);
+
+	for (i = 0; i < info->entryCount; i++)
+	{
+		ROFSentry *entry = rofs_load_entry(info);
+        BAIL_IF_ERRPASS(!entry, 0);
+	} /* for */
+
+	return 1;
+} /* rofs_load_entries */
+
+
+static void *ROFS_openArchive(PHYSFS_Io *io, const char *name, int forWriting, int *claimed)
+{
+	ROFSinfo *info = NULL;
+
+	assert(io != NULL);  /* shouldn't ever happen. */
+
+	BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
+	BAIL_IF_ERRPASS(!isRofs(io), NULL);
+
+	*claimed = 1;
+
+	info = (ROFSinfo *) allocator.Malloc(sizeof (ROFSinfo));
+	BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
+	memset(info, '\0', sizeof (ROFSinfo));
+
+	info->io = io;
+
+	if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (ROFSentry), 1, 1))
+        goto ROFS_openarchive_failed;
+
+	if (!rofs_load_entries(info))
+		goto ROFS_openarchive_failed;
+
+	assert(info->tree.root->sibling == NULL);
+
+	return info;
+
+ROFS_openarchive_failed:
+	if (info != NULL)
+		allocator.Free(info);
+
+	return NULL;
+} /* ROFS_openArchive */
+
+
+static inline ROFSentry *rofs_find_entry(ROFSinfo *info, const char *path)
+{
+    return (ROFSentry *) __PHYSFS_DirTreeFind(&info->tree, path);
+} /* rofs_find_entry */
+
+
+static PHYSFS_Io *ROFS_openRead(void *opaque, const char *filename)
+{
+	PHYSFS_Io *retval = NULL;
+	ROFSinfo *info = (ROFSinfo *) opaque;
+	ROFSentry *entry = rofs_find_entry(info, filename);
+	ROFSfileinfo *finfo = NULL;
+
+	BAIL_IF_ERRPASS(!entry, NULL);
+
+	retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+	GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ROFS_openRead_failed);
+
+	finfo = (ROFSfileinfo *) allocator.Malloc(sizeof (ROFSfileinfo));
+	GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ROFS_openRead_failed);
+	memset(finfo, '\0', sizeof (ROFSfileinfo));
+
+	finfo->io = info->io->duplicate(info->io);
+	GOTO_IF_ERRPASS(!finfo->io, ROFS_openRead_failed);
+
+	if (!finfo->io->seek(finfo->io, entry->startPos))
+		goto ROFS_openRead_failed;
+
+	finfo->entry = entry;
+	finfo->curPos = 0;
+
+	/* Init decryption */
+	finfo->decrypt.curblock = 0;
+	rofs_decrypt_block_init(finfo);
+
+	/* Init decompression */
+	if (entry->compression) {
+		rofs_depack_block_init(finfo);
+	}
+
+	memcpy(retval, &ROFS_Io, sizeof (PHYSFS_Io));
+	retval->opaque = finfo;
+
+	return retval;
+
+ROFS_openRead_failed:
+	if (finfo != NULL)
+	{
+		if (finfo->io != NULL)
+			finfo->io->destroy(finfo->io);
+
+		allocator.Free(finfo);
+	} /* if */
+
+	if (retval != NULL)
+		allocator.Free(retval);
+
+	return NULL;
+} /* ROFS_openRead */
+
+
+static void ROFS_closeArchive(void *opaque)
+{
+	ROFSinfo *info = (ROFSinfo *) opaque;
+
+	if (!info)
+		return;
+
+	if (info->io)
+		info->io->destroy(info->io);
+
+    __PHYSFS_DirTreeDeinit(&info->tree);
+
+	allocator.Free(info);
+} /* ROFS_closeArchive */
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_ROFS =
+{
+	CURRENT_PHYSFS_ARCHIVER_API_VERSION,
+	{
+		"ROFS",
+		"Resident Evil 3 ROFS format",
+		"Patrice Mandin <patmandin@gmail.com>",
+		"http://pmandin.atari.org/",
+		0	/* supportsSymlinks */
+	},
+	ROFS_openArchive,
+	UNPK_enumerate,
+	ROFS_openRead,
+    UNPK_openWrite,
+    UNPK_openAppend,
+    UNPK_remove,
+    UNPK_mkdir,
+	UNPK_stat,
+	ROFS_closeArchive
+};
+
+#endif  /* defined PHYSFS_SUPPORTS_ROFS */
+
+/* end of physfs_archiver_rofs.c ... */

+ 4 - 0
src/physfs_internal.h

@@ -85,6 +85,7 @@ extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
 extern const PHYSFS_Archiver __PHYSFS_Archiver_7Z;
 extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
 extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_ROFS;
 extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
 extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
 extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
@@ -216,6 +217,9 @@ void __PHYSFS_smallFree(void *ptr);
 #ifndef PHYSFS_SUPPORTS_QPAK
 #define PHYSFS_SUPPORTS_QPAK PHYSFS_SUPPORTS_DEFAULT
 #endif
+#ifndef PHYSFS_SUPPORTS_ROFS
+#define PHYSFS_SUPPORTS_ROFS PHYSFS_SUPPORTS_DEFAULT
+#endif
 #ifndef PHYSFS_SUPPORTS_SLB
 #define PHYSFS_SUPPORTS_SLB PHYSFS_SUPPORTS_DEFAULT
 #endif