From a8180f0e4c3920fd359db09ca1a74ea3675f51aa Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 7 Jun 2019 22:27:48 -0400 Subject: [PATCH] hog: Added support for HOG2 (Descent 3) archives. --- src/physfs.h | 2 +- src/physfs_archiver_hog.c | 76 +++++++++++++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/physfs.h b/src/physfs.h index 49d49dae..1fee95b1 100644 --- a/src/physfs.h +++ b/src/physfs.h @@ -145,7 +145,7 @@ * - .ISO (ISO9660 files, CD-ROM images) * - .GRP (Build Engine groupfile archives) * - .PAK (Quake I/II archive format) - * - .HOG (Descent I/II HOG file archives) + * - .HOG (Descent I/II/III HOG file archives) * - .MVL (Descent II movielib archives) * - .WAD (DOOM engine archives) * - .VDF (Gothic I/II engine archives) diff --git a/src/physfs_archiver_hog.c b/src/physfs_archiver_hog.c index 832209f8..345cb722 100644 --- a/src/physfs_archiver_hog.c +++ b/src/physfs_archiver_hog.c @@ -1,9 +1,9 @@ /* * HOG support routines for PhysicsFS. * - * This driver handles Descent I/II HOG archives. + * This driver handles Descent I/II/III HOG archives. * - * The format is very simple: + * The Descent I/II format is very simple: * * The file always starts with the 3-byte signature "DHF" (Descent * HOG file). After that the files of a HOG are just attached after @@ -23,10 +23,23 @@ * * (That info is from http://www.descent2.com/ddn/specs/hog/) * + * Descent 3 moved to HOG2 format, which starts with the chars "HOG2", + * then 32-bits for the number of contained files, 32 bits for the offset + * to the first file's data, then 56 bytes of 0xFF (reserved?). Then for + * each file, there's 36 bytes for filename (null-terminated, rest of bytes + * are garbage), 32-bits unknown/reserved (always zero?), 32-bits of length + * of file data, 32-bits of time since Unix epoch. Then immediately following, + * for each file is their uncompressed content, you can find its offset + * by starting at the initial data offset and adding the filesize of each + * prior file. + * + * This information was found at: + * https://web.archive.org/web/20020213004051/http://descent-3.com/ddn/specs/hog/ + * + * * Please see the file LICENSE.txt in the source's root directory. * - * This file written by Bradley Bell. - * Based on grp.c by Ryan C. Gordon. + * This file written by Bradley Bell and Ryan C. Gordon. */ #define __PHYSICSFS_INTERNAL__ @@ -34,7 +47,15 @@ #if PHYSFS_SUPPORTS_HOG -static int hogLoadEntries(PHYSFS_Io *io, void *arc) +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 */ + +static int hog1LoadEntries(PHYSFS_Io *io, void *arc) { const PHYSFS_uint64 iolen = io->length(io); PHYSFS_uint32 pos = 3; @@ -45,11 +66,10 @@ static int hogLoadEntries(PHYSFS_Io *io, void *arc) char name[13]; BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0); - BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + BAIL_IF_ERRPASS(!readui32(io, &size), 0); name[12] = '\0'; /* just in case. */ pos += 13 + 4; - size = PHYSFS_swapULE32(size); BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); pos += size; @@ -60,24 +80,60 @@ static int hogLoadEntries(PHYSFS_Io *io, void *arc) return 1; } /* hogLoadEntries */ +static int hog2LoadEntries(PHYSFS_Io *io, void *arc) +{ + PHYSFS_uint32 numfiles; + PHYSFS_uint32 pos; + PHYSFS_uint32 i; + + BAIL_IF_ERRPASS(!readui32(io, &numfiles), 0); + BAIL_IF_ERRPASS(!readui32(io, &pos), 0); + BAIL_IF_ERRPASS(!io->seek(io, 68), 0); /* skip to end of header. */ + + for (i = 0; i < numfiles; i++) { + char name[37]; + PHYSFS_uint32 reserved; + PHYSFS_uint32 size; + PHYSFS_uint32 mtime; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 36), 0); + BAIL_IF_ERRPASS(!readui32(io, &reserved), 0); + BAIL_IF_ERRPASS(!readui32(io, &size), 0); + BAIL_IF_ERRPASS(!readui32(io, &mtime), 0); + name[36] = '\0'; /* just in case */ + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, mtime, mtime, pos, size), 0); + pos += size; + } + + return 1; +} /* hogLoadEntries */ + static void *HOG_openArchive(PHYSFS_Io *io, const char *name, int forWriting, int *claimed) { PHYSFS_uint8 buf[3]; void *unpkarc = NULL; + int hog1 = 0; assert(io != NULL); /* shouldn't ever happen. */ BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 3), NULL); - BAIL_IF(memcmp(buf, "DHF", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); + + if (memcmp(buf, "DHF", 3) == 0) + hog1 = 1; /* original HOG (Descent 1 and 2) archive */ + else + { + BAIL_IF(memcmp(buf, "HOG", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 1), NULL); + BAIL_IF(buf[0] != '2', PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */ + } /* else */ *claimed = 1; unpkarc = UNPK_openArchive(io); BAIL_IF_ERRPASS(!unpkarc, NULL); - if (!hogLoadEntries(io, unpkarc)) + if (!(hog1 ? hog1LoadEntries(io, unpkarc) : hog2LoadEntries(io, unpkarc))) { UNPK_abandonArchive(unpkarc); return NULL; @@ -92,7 +148,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_HOG = CURRENT_PHYSFS_ARCHIVER_API_VERSION, { "HOG", - "Descent I/II HOG file format", + "Descent I/II/III HOG file format", "Bradley Bell ", "https://icculus.org/physfs/", 0, /* supportsSymlinks */