vdf: Migrated to new, improved unpacked archiver infrastructure.
authorRyan C. Gordon <icculus@icculus.org>
Sat, 22 Jul 2017 12:42:34 -0400
changeset 1503 5e0afac26199
parent 1502 52f70031d9bc
child 1504 803282f78987
vdf: Migrated to new, improved unpacked archiver infrastructure.
src/archiver_vdf.c
--- a/src/archiver_vdf.c	Sat Jul 22 12:16:37 2017 -0400
+++ b/src/archiver_vdf.c	Sat Jul 22 12:42:34 2017 -0400
@@ -11,499 +11,127 @@
 
 #define __PHYSICSFS_INTERNAL__
 #include "physfs_internal.h"
-#include <time.h>
-#include <ctype.h>
 
 #if PHYSFS_SUPPORTS_VDF
 
-#define VDF_HEADER_COMMENT_LENGTH 256
-#define VDF_HEADER_SIGNATURE_LENGTH 16
+#include <time.h>
+
+#define VDF_COMMENT_LENGTH 256
+#define VDF_SIGNATURE_LENGTH 16
 #define VDF_ENTRY_NAME_LENGTH 64
-#define VDF_ENTRY_DIR 0x80000000
-#define VDF_ENTRY_LAST 0x40000000
-
-#define VDF_CRC16 0x8005
-#define VDF_HASHTABLE_SIZE 65536
 
 static const char* VDF_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n";
 static const char* VDF_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r";
 
-typedef union
-{
-    PHYSFS_uint32 raw;
-    struct
-    {
-        PHYSFS_uint32 seconds : 5;
-        PHYSFS_uint32 minutes : 6;
-        PHYSFS_uint32 hour    : 5;
-        PHYSFS_uint32 day     : 5;
-        PHYSFS_uint32 month   : 4;
-        PHYSFS_uint32 year    : 7;
-    } members;
-} VdfTimestamp;
 
-typedef struct
-{
-    char comment[VDF_HEADER_COMMENT_LENGTH];
-    char signature[VDF_HEADER_SIGNATURE_LENGTH];
-    PHYSFS_uint32 numEntries;
-    PHYSFS_uint32 numFiles;
-    VdfTimestamp timestamp;
-    PHYSFS_uint32 dataSize;
-    PHYSFS_uint32 rootCatOffset;
-    PHYSFS_uint32 version;
-} VdfHeader;
-
-typedef struct
-{
-    char name[VDF_ENTRY_NAME_LENGTH];
-    PHYSFS_uint32 jump;
-    PHYSFS_uint32 size;
-    PHYSFS_uint32 type;
-    PHYSFS_uint32 attributes;
-} VdfEntryInfo;
-
-typedef struct _VdfLinkedList
-{
-    VdfEntryInfo *entry;
-    struct _VdfLinkedList *next, *prev;
-} VdfLinkedList;
-
-typedef struct
-{
-    VdfEntryInfo *entries;
-    int numEntries;
-    VdfLinkedList *table[VDF_HASHTABLE_SIZE];
-    PHYSFS_sint64 timestamp;
-    PHYSFS_Io *io;
-} VdfRecord;
-
-typedef struct
-{
-    PHYSFS_Io *io;
-    VdfEntryInfo *entry;
-    PHYSFS_uint32 curPos;
-} VdfFileinfo;
-
-static const PHYSFS_uint16 vdfCrcTable[256] =
+static inline int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
 {
-    0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
-    0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
-    0x0919, 0x1890, 0x2A0B, 0x3B82, 0x4F3D, 0x5EB4, 0x6C2F, 0x7DA6,
-    0x8551, 0x94D8, 0xA643, 0xB7CA, 0xC375, 0xD2FC, 0xE067, 0xF1EE,
-    0x1232, 0x03BB, 0x3120, 0x20A9, 0x5416, 0x459F, 0x7704, 0x668D,
-    0x9E7A, 0x8FF3, 0xBD68, 0xACE1, 0xD85E, 0xC9D7, 0xFB4C, 0xEAC5,
-    0x1B2B, 0x0AA2, 0x3839, 0x29B0, 0x5D0F, 0x4C86, 0x7E1D, 0x6F94,
-    0x9763, 0x86EA, 0xB471, 0xA5F8, 0xD147, 0xC0CE, 0xF255, 0xE3DC,
-    0x2464, 0x35ED, 0x0776, 0x16FF, 0x6240, 0x73C9, 0x4152, 0x50DB,
-    0xA82C, 0xB9A5, 0x8B3E, 0x9AB7, 0xEE08, 0xFF81, 0xCD1A, 0xDC93,
-    0x2D7D, 0x3CF4, 0x0E6F, 0x1FE6, 0x6B59, 0x7AD0, 0x484B, 0x59C2,
-    0xA135, 0xB0BC, 0x8227, 0x93AE, 0xE711, 0xF698, 0xC403, 0xD58A,
-    0x3656, 0x27DF, 0x1544, 0x04CD, 0x7072, 0x61FB, 0x5360, 0x42E9,
-    0xBA1E, 0xAB97, 0x990C, 0x8885, 0xFC3A, 0xEDB3, 0xDF28, 0xCEA1,
-    0x3F4F, 0x2EC6, 0x1C5D, 0x0DD4, 0x796B, 0x68E2, 0x5A79, 0x4BF0,
-    0xB307, 0xA28E, 0x9015, 0x819C, 0xF523, 0xE4AA, 0xD631, 0xC7B8,
-    0x48C8, 0x5941, 0x6BDA, 0x7A53, 0x0EEC, 0x1F65, 0x2DFE, 0x3C77,
-    0xC480, 0xD509, 0xE792, 0xF61B, 0x82A4, 0x932D, 0xA1B6, 0xB03F,
-    0x41D1, 0x5058, 0x62C3, 0x734A, 0x07F5, 0x167C, 0x24E7, 0x356E,
-    0xCD99, 0xDC10, 0xEE8B, 0xFF02, 0x8BBD, 0x9A34, 0xA8AF, 0xB926,
-    0x5AFA, 0x4B73, 0x79E8, 0x6861, 0x1CDE, 0x0D57, 0x3FCC, 0x2E45,
-    0xD6B2, 0xC73B, 0xF5A0, 0xE429, 0x9096, 0x811F, 0xB384, 0xA20D,
-    0x53E3, 0x426A, 0x70F1, 0x6178, 0x15C7, 0x044E, 0x36D5, 0x275C,
-    0xDFAB, 0xCE22, 0xFCB9, 0xED30, 0x998F, 0x8806, 0xBA9D, 0xAB14,
-    0x6CAC, 0x7D25, 0x4FBE, 0x5E37, 0x2A88, 0x3B01, 0x099A, 0x1813,
-    0xE0E4, 0xF16D, 0xC3F6, 0xD27F, 0xA6C0, 0xB749, 0x85D2, 0x945B,
-    0x65B5, 0x743C, 0x46A7, 0x572E, 0x2391, 0x3218, 0x0083, 0x110A,
-    0xE9FD, 0xF874, 0xCAEF, 0xDB66, 0xAFD9, 0xBE50, 0x8CCB, 0x9D42,
-    0x7E9E, 0x6F17, 0x5D8C, 0x4C05, 0x38BA, 0x2933, 0x1BA8, 0x0A21,
-    0xF2D6, 0xE35F, 0xD1C4, 0xC04D, 0xB4F2, 0xA57B, 0x97E0, 0x8669,
-    0x7787, 0x660E, 0x5495, 0x451C, 0x31A3, 0x202A, 0x12B1, 0x0338,
-    0xFBCF, 0xEA46, 0xD8DD, 0xC954, 0xBDEB, 0xAC62, 0x9EF9, 0x8F70
-};
-
-/*
- * Performs a case-insensitive CRC16 sum.
- */
-static PHYSFS_uint16 vdfGenCrc16(const char *c_ptr)
-{
-    PHYSFS_uint16 crc = 0xFFFF;
-
-    while (*c_ptr)
-        crc = (crc << 8) ^ vdfCrcTable[((crc >> 8) ^ toupper((int)(*c_ptr++)))];
-
-    return crc;
-} /* vdfGenCrc16 */
-
-static PHYSFS_sint64 VDF_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
-{
-    VdfFileinfo *finfo = (VdfFileinfo *)io->opaque;
-    const VdfEntryInfo *entry = finfo->entry;
-    const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size - finfo->curPos);
-    PHYSFS_sint64 rc;
-
-    if (bytesLeft < len)
-        len = bytesLeft;
-
-    rc = finfo->io->read(finfo->io, buffer, len);
-    if (rc > 0)
-        finfo->curPos += (PHYSFS_uint32)rc;
-
-    return rc;
-} /* VDF_read */
+    PHYSFS_uint32 v;
+    BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
+    *val = PHYSFS_swapULE32(v);
+    return 1;
+} /* readui32 */
 
 
-static PHYSFS_sint64 VDF_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
-{
-    BAIL(PHYSFS_ERR_READ_ONLY, -1);
-} /* VDF_write */
-
-
-static PHYSFS_sint64 VDF_tell(PHYSFS_Io *io)
-{
-    return ((VdfFileinfo *)io->opaque)->curPos;
-} /* VDF_tell */
-
-
-static int VDF_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
+static PHYSFS_sint64 vdfDosTimeToEpoch(const PHYSFS_uint32 dostime)
 {
-    VdfFileinfo *finfo = (VdfFileinfo *)io->opaque;
-    const VdfEntryInfo *entry = finfo->entry;
-    int rc;
-
-    BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
-    rc = finfo->io->seek(finfo->io, entry->jump + offset);
-    if (rc)
-        finfo->curPos = (PHYSFS_uint32)offset;
-
-    return rc;
-} /* VDF_seek */
-
-
-static PHYSFS_sint64 VDF_length(PHYSFS_Io *io)
-{
-    const VdfFileinfo *finfo = (VdfFileinfo *)io->opaque;
-    return ((PHYSFS_sint64)finfo->entry->size);
-} /* VDF_length */
+    /* VDF stores timestamps as 32bit DOS dates: the seconds are counted in
+       2-seconds intervals and the years are counted since 1 Jan. 1980 */
+    struct tm t;
+    memset(&t, '\0', sizeof (t));
+    t.tm_year = ((int) ((dostime >> 25) & 0x7F)) + 80; /* 1980 to 1900 */
+    t.tm_mon = ((int) ((dostime >> 21) & 0xF)) - 1;  /* 1-12 to 0-11 */
+    t.tm_mday = (int) ((dostime >> 16) & 0x1F);
+    t.tm_hour = (int) ((dostime >> 11) & 0x1F);
+    t.tm_min = (int) ((dostime >> 5) & 0x3F);
+    t.tm_sec = ((int) ((dostime >> 0) & 0x1F)) * 2;  /* 2 seconds to 1. */
+    return (PHYSFS_sint64) mktime(&t);
+} /* vdfDosTimeToEpoch */
 
 
-static PHYSFS_Io *VDF_duplicate(PHYSFS_Io *_io)
-{
-    VdfFileinfo *origfinfo = (VdfFileinfo *)_io->opaque;
-    PHYSFS_Io *io = NULL;
-    PHYSFS_Io *retval = (PHYSFS_Io *)allocator.Malloc(sizeof(PHYSFS_Io));
-    VdfFileinfo *finfo = (VdfFileinfo *)allocator.Malloc(sizeof(VdfFileinfo));
-    GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, VDF_duplicate_failed);
-    GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, VDF_duplicate_failed);
-
-    io = origfinfo->io->duplicate(origfinfo->io);
-    if (!io) goto VDF_duplicate_failed;
-    finfo->io = io;
-    finfo->entry = origfinfo->entry;
-    finfo->curPos = 0;
-    memcpy(retval, _io, sizeof(PHYSFS_Io));
-    retval->opaque = finfo;
-    return retval;
-
-VDF_duplicate_failed:
-    if (finfo != NULL) allocator.Free(finfo);
-    if (retval != NULL) allocator.Free(retval);
-    if (io != NULL) io->destroy(io);
-    return NULL;
-} /* VDF_duplicate */
-
-static int VDF_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
-
-static void VDF_destroy(PHYSFS_Io *io)
-{
-    VdfFileinfo *finfo = (VdfFileinfo *)io->opaque;
-    finfo->io->destroy(finfo->io);
-    allocator.Free(finfo);
-    allocator.Free(io);
-} /* VDF_destroy */
-
-static const PHYSFS_Io VDF_Io =
+static int vdfLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count,
+                          const PHYSFS_sint64 t, void *arc)
 {
-    CURRENT_PHYSFS_IO_API_VERSION, NULL,
-    VDF_read,
-    VDF_write,
-    VDF_seek,
-    VDF_tell,
-    VDF_length,
-    VDF_duplicate,
-    VDF_flush,
-    VDF_destroy
-};
+    PHYSFS_uint32 i;
+
+    for (i = 0; i < count; i++)
+    {
+        char name[VDF_ENTRY_NAME_LENGTH + 1];
+        int namei;
+        PHYSFS_uint32 jump, size, type, attr;
+
+        BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, sizeof (name) - 1), 0);
+        BAIL_IF_ERRPASS(!readui32(io, &jump), 0);
+        BAIL_IF_ERRPASS(!readui32(io, &size), 0);
+        BAIL_IF_ERRPASS(!readui32(io, &type), 0);
+        BAIL_IF_ERRPASS(!readui32(io, &attr), 0);
 
-/*
- * VDF stores timestamps as 32bit DOS dates:
- *  the seconds are counted in 2-seconds intervals
- *  and the years are counted since 1 Jan. 1980
- */
-static PHYSFS_sint64 vdfDosTimeToEpoch(VdfTimestamp time)
-{
-    struct tm t = { 0 };
-    t.tm_year = time.members.year + 80;
-    t.tm_mon = time.members.month - 1; /* DOS time counts months 1-12, we need 0-11 */
-    t.tm_mday = time.members.day;
-    t.tm_hour = time.members.hour;
-    t.tm_min = time.members.minutes;
-    t.tm_sec = time.members.seconds * 2;
-
-    return (PHYSFS_sint64)difftime(mktime(&t), 0);
-} /* vdfDosTimeToEpoch */
+        /* Trim whitespace off the end of the filename */
+        name[VDF_ENTRY_NAME_LENGTH] = '\0';  /* always null-terminated. */
+        for (namei = VDF_ENTRY_NAME_LENGTH - 1; namei >= 0; namei--)
+        {
+            if (name[namei] == ' ')
+                name[namei] = '\0';
+            else
+                break;
+        } /* for */
 
-/*
- * Sets the last bytes to zero until we find a non-empty character
- * FIXME: May corrupt filenames which are actually 64 bytes long!
- */
-static void vdfTruncateFilename(char *s, PHYSFS_uint32 *nameLength)
-{
-    int i;
+        BAIL_IF(!name[0], PHYSFS_ERR_CORRUPT, 0);
 
-    s[VDF_ENTRY_NAME_LENGTH - 1] = '\0';
-    for (i = VDF_ENTRY_NAME_LENGTH - 2; i > 0; i--) {
-        if (isspace((int) s[i]))
-        {
-            s[i] = '\0';
-        } /* if */
-        else
-        {
-            break;
-        } /* else */
+        /* !!! FIXME: we assume the filenames are low-ASCII; if they use
+           any high-ASCII chars, they will be invalid UTF-8. */
+
+        BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, t, t, jump, size), 0);
     } /* for */
 
-    i = 0;
-    while (s[i])
-    {
-        i++;
-    } /* while */
-    *nameLength = i;
-} /* vdfTruncateFilename */
-
-/*
- * Compares two strings in a case-insensitive way
- * Returns 0 if they are equal, 1 otherwise
- */
-static int vdfStrcmp(const char *a, const char *b)
-{
-    while (*a && *b)
-    {
-        if (toupper((int) *a) != toupper((int) *b)) return 1;
-        a++;
-        b++;
-    } /* while */
-    if (*a == 0 && *b == 0) return 0;
     return 1;
-} /* vdfStrcmp */
-
-/* Adds an entry to the hashtable */
-static void vdfAddEntry(VdfRecord *record, VdfEntryInfo *entry)
-{
-    PHYSFS_uint16 hash = vdfGenCrc16(entry->name);
-    VdfLinkedList *list = record->table[hash];
-    if (list == NULL)
-    {
-        list = (VdfLinkedList*)allocator.Malloc(sizeof(VdfLinkedList));
-        list->next = NULL;
-        list->prev = NULL;
-        list->entry = entry;
-        record->table[hash] = list;
-    }
-    else
-    {
-        VdfLinkedList *newElement = (VdfLinkedList*)allocator.Malloc(sizeof(VdfLinkedList));
-        newElement->next = list;
-        newElement->prev = NULL;
-        list->prev = newElement;
-        newElement->entry = entry;
-        record->table[hash] = newElement;
-    }
-} /* vdfAddEntry */
-
-static VdfRecord *vdfLoadRecord(PHYSFS_Io *io, VdfHeader header)
-{
-    VdfRecord *record = (VdfRecord*)allocator.Malloc(sizeof(VdfRecord));
-    VdfEntryInfo *entries;
-    VdfEntryInfo *entry;
-
-    BAIL_IF(!record, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
-
-    entries = (VdfEntryInfo *)allocator.Malloc(sizeof(VdfEntryInfo) * header.numEntries);
-    GOTO_IF(!entries, PHYSFS_ERR_OUT_OF_MEMORY, failed);
-
-    if (!__PHYSFS_readAll(io, entries, sizeof(VdfEntryInfo) * header.numEntries)) goto failed;
-
-    record->timestamp = vdfDosTimeToEpoch(header.timestamp);
-    record->numEntries = header.numEntries;
-    record->entries = entries;
-    memset(record->table, 0, sizeof(record->table));
+} /* vdfLoadEntries */
 
-    for (entry = entries; header.numEntries > 0; header.numEntries--, entry++)
-    {
-        PHYSFS_uint32 len;
-        entry->jump = PHYSFS_swapULE32(entry->jump);
-        entry->size = PHYSFS_swapULE32(entry->size);
-        entry->type = PHYSFS_swapULE32(entry->type);
-        entry->attributes = PHYSFS_swapULE32(entry->attributes);
-        vdfTruncateFilename(entry->name, &len);
-        vdfAddEntry(record, entry);
-    } /* for */
-
-    return record;
-
-failed:
-    if (!record) allocator.Free(record);
-    if (!entries) allocator.Free(entries);
-    return NULL;
-} /* vdfLoadList */
-
-
-static void VDF_enumerateFiles(void *opaque, const char *dname,
-    PHYSFS_EnumFilesCallback cb,
-    const char *origdir, void *callbackdata)
-{
-    const VdfRecord *record = (const VdfRecord *)opaque;
-    if (strcmp(dname, "") == 0)
-    {
-        int i;
-        for (i = 0; i < VDF_HASHTABLE_SIZE; i++)
-        {
-            VdfLinkedList *list = record->table[i];
-            while (list)
-            {
-                cb(callbackdata, origdir, list->entry->name);
-                list = list->next;
-            } /* while */
-        } /* for */
-    } /* if */
-} /* VDF_enumerateFiles */
-
-/* Retrieves a file from the hashtable */
-static int vdfFindFile(const VdfRecord *record, const char *filename, VdfEntryInfo *outEntry)
-{
-    VdfLinkedList *list = record->table[vdfGenCrc16(filename)];
-    while (list)
-    {
-        if (vdfStrcmp(list->entry->name, filename) == 0 && !(list->entry->type & VDF_ENTRY_DIR))
-        {
-            *outEntry = *(list->entry);
-            return 1;
-        } /* if */
-        list = list->next;
-    } /* while */
-    return 0;
-} /* vdfFindFile */
-
-static int VDF_stat(void *opaque, const char *filename, PHYSFS_Stat *stat)
-{
-    const VdfRecord *record = (const VdfRecord *)opaque;
-    VdfEntryInfo entry;
-    if (vdfFindFile(record, filename, &entry))
-    {
-        stat->readonly = 1;
-        stat->accesstime = -1;
-        stat->createtime = record->timestamp;
-        stat->modtime = record->timestamp;
-        stat->filesize = entry.size;
-        stat->filetype = PHYSFS_FILETYPE_REGULAR;
-
-        return 1;
-    } /* if */
-
-    return 0;
-} /* VDF_stat */
 
 static void *VDF_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
-    VdfHeader header;
-    VdfRecord *record = NULL;
+    PHYSFS_uint8 ignore[16];
+    PHYSFS_uint8 sig[VDF_SIGNATURE_LENGTH];
+    PHYSFS_uint32 count, timestamp, version, dataSize, rootCatOffset;
+    void *unpkarc;
+
     assert(io != NULL); /* shouldn't ever happen. */
-
+    
     BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
-    BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &header, sizeof(VdfHeader)), NULL);
+
+    /* skip the 256-byte comment field. */
+    BAIL_IF_ERRPASS(!io->seek(io, VDF_COMMENT_LENGTH), NULL);
 
-    header.numEntries = PHYSFS_swapULE32(header.numEntries);
-    header.numFiles = PHYSFS_swapULE32(header.numFiles);
-    header.timestamp.raw = PHYSFS_swapULE32(header.timestamp.raw);
-    header.dataSize = PHYSFS_swapULE32(header.dataSize);
-    header.rootCatOffset = PHYSFS_swapULE32(header.rootCatOffset);
-    header.version = PHYSFS_swapULE32(header.version);
+    BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, sig, sizeof (sig)), NULL);
+    BAIL_IF_ERRPASS(!readui32(io, &count), NULL);
+    BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), NULL);  /* numFiles */
+    BAIL_IF_ERRPASS(!readui32(io, &timestamp), NULL);
+    BAIL_IF_ERRPASS(!readui32(io, &dataSize), NULL);  /* dataSize */
+    BAIL_IF_ERRPASS(!readui32(io, &rootCatOffset), NULL);  /* rootCatOff */
+    BAIL_IF_ERRPASS(!readui32(io, &version), NULL);
 
-    BAIL_IF(header.version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL);
+    BAIL_IF(version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL);
 
-    if ((memcmp(header.signature, VDF_SIGNATURE_G1, VDF_HEADER_SIGNATURE_LENGTH) != 0) &&
-        (memcmp(header.signature, VDF_SIGNATURE_G2, VDF_HEADER_SIGNATURE_LENGTH) != 0))
+    if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) &&
+        (memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0))
     {
         BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
     } /* if */
 
-    record = vdfLoadRecord(io, header);
-    BAIL_IF_ERRPASS(!record, NULL);
-    record->io = io;
-    return record;
+    BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL);
+
+    unpkarc = UNPK_openArchive(io);
+    BAIL_IF_ERRPASS(!unpkarc, NULL);
+
+    if (!vdfLoadEntries(io, count, vdfDosTimeToEpoch(timestamp), unpkarc))
+    {
+        UNPK_abandonArchive(unpkarc);
+        return NULL;
+    } /* if */
+
+    return unpkarc;
 } /* VDF_openArchive */
 
-static void VDF_closeArchive(void *opaque)
-{
-    VdfRecord *record = ((VdfRecord *)opaque);
-    int i;
-    for (i = 0; i < VDF_HASHTABLE_SIZE; i++)
-    {
-        VdfLinkedList *list = record->table[i];
-        while (list)
-        {
-            VdfLinkedList *next = list->next;
-            allocator.Free(list);
-            list = next;
-        }
-    }
-    allocator.Free(record->entries);
-    allocator.Free(record);
-} /* VDF_closeArchive */
-
-static PHYSFS_Io *VDF_openRead(void *opaque, const char *name)
-{
-    PHYSFS_Io *retval = NULL;
-    VdfRecord *record = (VdfRecord *)opaque;
-    VdfFileinfo *finfo = NULL;
-    VdfEntryInfo entry;
-    GOTO_IF(!vdfFindFile(record, name, &entry), PHYSFS_ERR_NOT_FOUND, VDF_openRead_failed);
-
-    retval = (PHYSFS_Io *)allocator.Malloc(sizeof(PHYSFS_Io));
-    GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, VDF_openRead_failed);
-
-    finfo = (VdfFileinfo *)allocator.Malloc(sizeof(VdfFileinfo));
-    GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, VDF_openRead_failed);
-
-    finfo->io = record->io->duplicate(record->io);
-    GOTO_IF_ERRPASS(!finfo->io, VDF_openRead_failed);
-
-    if (!finfo->io->seek(finfo->io, entry.jump))
-        goto VDF_openRead_failed;
-
-    finfo->curPos = 0;
-
-    finfo->entry = (VdfEntryInfo *)allocator.Malloc(sizeof(VdfEntryInfo));
-    GOTO_IF(!finfo->entry, PHYSFS_ERR_OUT_OF_MEMORY, VDF_openRead_failed);
-    memcpy(finfo->entry, &entry, sizeof(VdfEntryInfo));
-
-    memcpy(retval, &VDF_Io, sizeof(*retval));
-    retval->opaque = finfo;
-    return retval;
-
-VDF_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;
-} /* VDF_openRead */
 
 const PHYSFS_Archiver __PHYSFS_Archiver_VDF =
 {
@@ -516,14 +144,14 @@
         0,  /* supportsSymlinks */
     },
     VDF_openArchive,
-    VDF_enumerateFiles,
-    VDF_openRead,
+    UNPK_enumerateFiles,
+    UNPK_openRead,
     UNPK_openWrite,
     UNPK_openAppend,
     UNPK_remove,
     UNPK_mkdir,
-    VDF_stat,
-    VDF_closeArchive
+    UNPK_stat,
+    UNPK_closeArchive
 };
 
 #endif /* defined PHYSFS_SUPPORTS_VDF */