From 56e0f659ac243c4f684bcdb2df3acd9e5b5b8b6f Mon Sep 17 00:00:00 2001 From: pmdata Date: Sat, 12 Aug 2023 11:18:12 +0200 Subject: [PATCH 1/4] ROFS: Add Resident Evil 3 ROFS archive format --- CMakeLists.txt | 7 + src/physfs.c | 3 + src/physfs_archiver_rofs.c | 817 +++++++++++++++++++++++++++++++++++++ src/physfs_internal.h | 4 + 4 files changed, 831 insertions(+) create mode 100644 src/physfs_archiver_rofs.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a5e7c18..05c5a99e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,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 @@ -145,6 +146,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) @@ -328,6 +334,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) diff --git a/src/physfs.c b/src/physfs.c index 7d4545a3..fbc844ce 100644 --- a/src/physfs.c +++ b/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 diff --git a/src/physfs_archiver_rofs.c b/src/physfs_archiver_rofs.c new file mode 100644 index 00000000..68faa415 --- /dev/null +++ b/src/physfs_archiver_rofs.c @@ -0,0 +1,817 @@ +/* + * Resident Evil 3 rofs.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; idepack.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; idecrypt.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 < 0, PHYSFS_ERR_INVALID_ARGUMENT, 0); + 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; iblock_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; iio->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.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; iseek(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 ", + "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 ... */ diff --git a/src/physfs_internal.h b/src/physfs_internal.h index b29830ec..32eea2ee 100644 --- a/src/physfs_internal.h +++ b/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; @@ -207,6 +208,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 From dabd405b79557943c124a0be92bb62eed59fdb3c Mon Sep 17 00:00:00 2001 From: pmdata Date: Sat, 12 Aug 2023 11:35:22 +0200 Subject: [PATCH 2/4] ROFS: Add myself to credits --- docs/CREDITS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/CREDITS.txt b/docs/CREDITS.txt index c88c50bc..f4ba4be2 100644 --- a/docs/CREDITS.txt +++ b/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 ... From c1e566c22a268369846c674d802f544b42149b62 Mon Sep 17 00:00:00 2001 From: pmdata Date: Sat, 12 Aug 2023 11:48:40 +0200 Subject: [PATCH 3/4] ROFS: Forgot the makefile for OS2 --- src/Makefile.os2 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile.os2 b/src/Makefile.os2 index dc9ece41..f96ef013 100644 --- a/src/Makefile.os2 +++ b/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 & From f4ccf72d7e00346769b8ad9125dd7463fe29638d Mon Sep 17 00:00:00 2001 From: pmdata Date: Sat, 12 Aug 2023 12:03:50 +0200 Subject: [PATCH 4/4] ROFS: Remove bogus statement. And also checks EOF when reaching file size --- src/physfs_archiver_rofs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/physfs_archiver_rofs.c b/src/physfs_archiver_rofs.c index 68faa415..b16c8ca6 100644 --- a/src/physfs_archiver_rofs.c +++ b/src/physfs_archiver_rofs.c @@ -375,8 +375,7 @@ static int ROFS_seek(PHYSFS_Io *io, PHYSFS_uint64 offset) ROFSentry *entry = finfo->entry; int rc = 1, i, srcBlock, dstBlock, remaining; - BAIL_IF(offset < 0, PHYSFS_ERR_INVALID_ARGUMENT, 0); - BAIL_IF(offset > entry->size, PHYSFS_ERR_PAST_EOF, 0); + BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0); /* Skip to a given 32KB block */ srcBlock = finfo->curPos >> 15;