Abstracted file i/o into PHYSFS_Io interface.
authorRyan C. Gordon <icculus@icculus.org>
Mon, 30 Aug 2010 03:01:57 -0400
changeset 1118 2e09fc635fdd
parent 1117 968e1567f4f4
child 1119 028559924db4
Abstracted file i/o into PHYSFS_Io interface. This touched a lot of code, and involved cleaning up a lot of stuff.
src/archiver_dir.c
src/archiver_grp.c
src/archiver_hog.c
src/archiver_iso9660.c
src/archiver_lzma.c
src/archiver_mvl.c
src/archiver_qpak.c
src/archiver_wad.c
src/archiver_zip.c
src/physfs.c
src/physfs.h
src/physfs_internal.h
src/platform_os2.c
src/platform_pocketpc.c
src/platform_posix.c
src/platform_windows.c
--- a/src/archiver_dir.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_dir.c	Mon Aug 30 03:01:57 2010 -0400
@@ -14,68 +14,23 @@
 #define __PHYSICSFS_INTERNAL__
 #include "physfs_internal.h"
 
-static PHYSFS_sint64 DIR_read(fvoid *opaque, void *buffer, PHYSFS_uint64 len)
-{
-    return __PHYSFS_platformRead(opaque, buffer, len);
-} /* DIR_read */
-
-
-static PHYSFS_sint64 DIR_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
-{
-    return __PHYSFS_platformWrite(f, buf, len);
-} /* DIR_write */
-
-
-static int DIR_eof(fvoid *opaque)
-{
-    return __PHYSFS_platformEOF(opaque);
-} /* DIR_eof */
-
-
-static PHYSFS_sint64 DIR_tell(fvoid *opaque)
-{
-    return __PHYSFS_platformTell(opaque);
-} /* DIR_tell */
-
+/* There's no PHYSFS_Io interface here. Use __PHYSFS_createNativeIo(). */
 
-static int DIR_seek(fvoid *opaque, PHYSFS_uint64 offset)
-{
-    return __PHYSFS_platformSeek(opaque, offset);
-} /* DIR_seek */
-
-
-static PHYSFS_sint64 DIR_fileLength(fvoid *opaque)
-{
-    return __PHYSFS_platformFileLength(opaque);
-} /* DIR_fileLength */
-
-
-static int DIR_fileClose(fvoid *opaque)
-{
-    /*
-     * we manually flush the buffer, since that's the place a close will
-     *  most likely fail, but that will leave the file handle in an undefined
-     *  state if it fails. Flush failures we can recover from.
-     */
-    BAIL_IF_MACRO(!__PHYSFS_platformFlush(opaque), NULL, 0);
-    BAIL_IF_MACRO(!__PHYSFS_platformClose(opaque), NULL, 0);
-    return 1;
-} /* DIR_fileClose */
-
-
-static void *DIR_openArchive(const char *name, int forWriting)
+static void *DIR_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
     const char *dirsep = PHYSFS_getDirSeparator();
     char *retval = NULL;
-    size_t namelen = strlen(name);
-    size_t seplen = strlen(dirsep);
+    const size_t namelen = strlen(name);
+    const size_t seplen = strlen(dirsep);
 
+    assert(io == NULL);  /* shouldn't create an Io for these. */
     BAIL_IF_MACRO(!__PHYSFS_platformIsDirectory(name), ERR_NOT_AN_ARCHIVE, NULL);
 
     retval = allocator.Malloc(namelen + seplen + 1);
     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
 
-        /* make sure there's a dir separator at the end of the string */
+    /* make sure there's a dir separator at the end of the string */
+    /* !!! FIXME: is there really any place where (seplen != 1)? */
     strcpy(retval, name);
     if (strcmp((name + namelen) - seplen, dirsep) != 0)
         strcat(retval, dirsep);
@@ -84,11 +39,13 @@
 } /* DIR_openArchive */
 
 
+/* !!! FIXME: I would like to smallAlloc() all these conversions somehow. */
+
 static void DIR_enumerateFiles(dvoid *opaque, const char *dname,
                                int omitSymLinks, PHYSFS_EnumFilesCallback cb,
                                const char *origdir, void *callbackdata)
 {
-    char *d = __PHYSFS_platformCvtToDependent((char *)opaque, dname, NULL);
+    char *d = __PHYSFS_platformCvtToDependent((char *) opaque, dname, NULL);
     if (d != NULL)
     {
         __PHYSFS_platformEnumerateFiles(d, omitSymLinks, cb,
@@ -138,47 +95,48 @@
 } /* DIR_isSymLink */
 
 
-static fvoid *doOpen(dvoid *opaque, const char *name,
-                     void *(*openFunc)(const char *filename),
-                     int *fileExists)
+static PHYSFS_Io *doOpen(dvoid *opaque, const char *name,
+                         const int mode, int *fileExists)
 {
     char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
-    void *rc = NULL;
+    PHYSFS_Io *io = NULL;
+    int existtmp = 0;
+
+    if (fileExists == NULL)
+        fileExists = &existtmp;
+
+    *fileExists = 0;
 
     BAIL_IF_MACRO(f == NULL, NULL, NULL);
 
-    if (fileExists != NULL)
+    io = __PHYSFS_createNativeIo(f, mode);
+    allocator.Free(f);
+    if (io == NULL)
     {
         *fileExists = __PHYSFS_platformExists(f);
-        if (!(*fileExists))
-        {
-            allocator.Free(f);
-            return NULL;
-        } /* if */
+        return NULL;
     } /* if */
 
-    rc = openFunc(f);
-    allocator.Free(f);
-
-    return ((fvoid *) rc);
+    *fileExists = 1;
+    return io;
 } /* doOpen */
 
 
-static fvoid *DIR_openRead(dvoid *opaque, const char *fnm, int *exist)
+static PHYSFS_Io *DIR_openRead(dvoid *opaque, const char *fnm, int *exist)
 {
-    return doOpen(opaque, fnm, __PHYSFS_platformOpenRead, exist);
+    return doOpen(opaque, fnm, 'r', exist);
 } /* DIR_openRead */
 
 
-static fvoid *DIR_openWrite(dvoid *opaque, const char *filename)
+static PHYSFS_Io *DIR_openWrite(dvoid *opaque, const char *filename)
 {
-    return doOpen(opaque, filename, __PHYSFS_platformOpenWrite, NULL);
+    return doOpen(opaque, filename, 'w', NULL);
 } /* DIR_openWrite */
 
 
-static fvoid *DIR_openAppend(dvoid *opaque, const char *filename)
+static PHYSFS_Io *DIR_openAppend(dvoid *opaque, const char *filename)
 {
-    return doOpen(opaque, filename, __PHYSFS_platformOpenAppend, NULL);
+    return doOpen(opaque, filename, 'a', NULL);
 } /* DIR_openAppend */
 
 
@@ -248,14 +206,7 @@
     DIR_remove,             /* remove() method         */
     DIR_mkdir,              /* mkdir() method          */
     DIR_dirClose,           /* dirClose() method       */
-    DIR_stat,               /* stat() method           */
-    DIR_read,               /* read() method           */
-    DIR_write,              /* write() method          */
-    DIR_eof,                /* eof() method            */
-    DIR_tell,               /* tell() method           */
-    DIR_seek,               /* seek() method           */
-    DIR_fileLength,         /* fileLength() method     */
-    DIR_fileClose           /* fileClose() method      */
+    DIR_stat                /* stat() method           */
 };
 
 /* end of dir.c ... */
--- a/src/archiver_grp.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_grp.c	Mon Aug 30 03:01:57 2010 -0400
@@ -43,37 +43,37 @@
 
 typedef struct
 {
-    char *filename;
+    PHYSFS_Io *io;
     PHYSFS_uint32 entryCount;
     GRPentry *entries;
 } GRPinfo;
 
 typedef struct
 {
-    void *handle;
+    PHYSFS_Io *io;
     GRPentry *entry;
     PHYSFS_uint32 curPos;
 } GRPfileinfo;
 
 
-static inline int readAll(void *fh, void *buf, const PHYSFS_uint64 len)
+static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
 {
-    return (__PHYSFS_platformRead(fh, buf, len) == len);
+    return (io->read(io, buf, len) == len);
 } /* readAll */
 
 
 static void GRP_dirClose(dvoid *opaque)
 {
     GRPinfo *info = ((GRPinfo *) opaque);
-    allocator.Free(info->filename);
+    info->io->destroy(info->io);
     allocator.Free(info->entries);
     allocator.Free(info);
 } /* GRP_dirClose */
 
 
-static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer, PHYSFS_uint64 len)
+static PHYSFS_sint64 GRP_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
 {
-    GRPfileinfo *finfo = (GRPfileinfo *) opaque;
+    GRPfileinfo *finfo = (GRPfileinfo *) io->opaque;
     const GRPentry *entry = finfo->entry;
     const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
     PHYSFS_sint64 rc;
@@ -81,7 +81,7 @@
     if (bytesLeft < len)
         len = bytesLeft;
 
-    rc = __PHYSFS_platformRead(finfo->handle, buffer, len);
+    rc = finfo->io->read(finfo->io, buffer, len);
     if (rc > 0)
         finfo->curPos += (PHYSFS_uint32) rc;
 
@@ -89,35 +89,26 @@
 } /* GRP_read */
 
 
-static PHYSFS_sint64 GRP_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 GRP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
 } /* GRP_write */
 
 
-static int GRP_eof(fvoid *opaque)
+static PHYSFS_sint64 GRP_tell(PHYSFS_Io *io)
 {
-    GRPfileinfo *finfo = (GRPfileinfo *) opaque;
-    GRPentry *entry = finfo->entry;
-    return (finfo->curPos >= entry->size);
-} /* GRP_eof */
-
-
-static PHYSFS_sint64 GRP_tell(fvoid *opaque)
-{
-    return ((GRPfileinfo *) opaque)->curPos;
+    return ((GRPfileinfo *) io->opaque)->curPos;
 } /* GRP_tell */
 
 
-static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset)
+static int GRP_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
 {
-    GRPfileinfo *finfo = (GRPfileinfo *) opaque;
-    GRPentry *entry = finfo->entry;
+    GRPfileinfo *finfo = (GRPfileinfo *) io->opaque;
+    const GRPentry *entry = finfo->entry;
     int rc;
 
-    BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
     BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
-    rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+    rc = finfo->io->seek(finfo->io, entry->startPos + offset);
     if (rc)
         finfo->curPos = (PHYSFS_uint32) offset;
 
@@ -125,72 +116,77 @@
 } /* GRP_seek */
 
 
-static PHYSFS_sint64 GRP_fileLength(fvoid *opaque)
+static PHYSFS_sint64 GRP_length(PHYSFS_Io *io)
 {
-    GRPfileinfo *finfo = (GRPfileinfo *) opaque;
+    const GRPfileinfo *finfo = (GRPfileinfo *) io->opaque;
     return ((PHYSFS_sint64) finfo->entry->size);
-} /* GRP_fileLength */
-
-
-static int GRP_fileClose(fvoid *opaque)
-{
-    GRPfileinfo *finfo = (GRPfileinfo *) opaque;
-    BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
-    allocator.Free(finfo);
-    return 1;
-} /* GRP_fileClose */
+} /* GRP_length */
 
 
-static int grp_open(const char *filename, int forWriting,
-                    void **fh, PHYSFS_uint32 *count)
+static PHYSFS_Io *GRP_duplicate(PHYSFS_Io *_io)
 {
-    PHYSFS_uint8 buf[12];
-
-    *fh = NULL;
-    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
-
-    *fh = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(*fh == NULL, NULL, 0);
-    
-    if (!readAll(*fh, buf, 12))
-        goto openGrp_failed;
+    GRPfileinfo *origfinfo = (GRPfileinfo *) _io->opaque;
+    PHYSFS_Io *io = NULL;
+    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GRPfileinfo *finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, GRP_duplicate_failed);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, GRP_duplicate_failed);
 
-    if (memcmp(buf, "KenSilverman", 12) != 0)
-    {
-        __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
-        goto openGrp_failed;
-    } /* if */
-
-    if (!readAll(*fh, count, sizeof (PHYSFS_uint32)))
-        goto openGrp_failed;
-
-    *count = PHYSFS_swapULE32(*count);
+    io = origfinfo->io->duplicate(origfinfo->io);
+    GOTO_IF_MACRO(io == NULL, NULL, GRP_duplicate_failed);
+    finfo->io = io;
+    finfo->entry = origfinfo->entry;
+    finfo->curPos = 0;
+    memcpy(retval, _io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+    return retval;
 
-    return 1;
+GRP_duplicate_failed:
+    if (finfo != NULL) allocator.Free(finfo);
+    if (retval != NULL) allocator.Free(retval);
+    if (io != NULL) io->destroy(io);
+    return NULL;
+} /* GRP_duplicate */
 
-openGrp_failed:
-    if (*fh != NULL)
-        __PHYSFS_platformClose(*fh);
+static int GRP_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
 
-    *count = -1;
-    *fh = NULL;
-    return 0;
-} /* grp_open */
+static void GRP_destroy(PHYSFS_Io *io)
+{
+    GRPfileinfo *finfo = (GRPfileinfo *) io->opaque;
+    finfo->io->destroy(finfo->io);
+    allocator.Free(finfo);
+    allocator.Free(io);
+} /* GRP_destroy */
 
 
-static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static const PHYSFS_Io GRP_Io =
+{
+    GRP_read,
+    GRP_write,
+    GRP_seek,
+    GRP_tell,
+    GRP_length,
+    GRP_duplicate,
+    GRP_flush,
+    GRP_destroy,
+    NULL
+};
+
+
+
+static int grpEntryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
         const GRPentry *a = (const GRPentry *) _a;
-        return (strcmp(a[one].name, a[two].name));
+        return __PHYSFS_stricmpASCII(a[one].name, a[two].name);
     } /* if */
 
     return 0;
-} /* grp_entry_cmp */
+} /* grpEntryCmp */
 
 
-static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static void grpEntrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
@@ -201,37 +197,25 @@
         memcpy(first, second, sizeof (GRPentry));
         memcpy(second, &tmp, sizeof (GRPentry));
     } /* if */
-} /* grp_entry_swap */
+} /* grpEntrySwap */
 
 
-static int grp_load_entries(const char *name, int forWriting, GRPinfo *info)
+static int grp_load_entries(PHYSFS_Io *io, GRPinfo *info)
 {
-    void *fh = NULL;
-    PHYSFS_uint32 fileCount;
+    PHYSFS_uint32 fileCount = info->entryCount;
     PHYSFS_uint32 location = 16;  /* sizeof sig. */
     GRPentry *entry;
     char *ptr;
 
-    BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0);
-    info->entryCount = fileCount;
     info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount);
-    if (info->entries == NULL)
-    {
-        __PHYSFS_platformClose(fh);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
-    } /* if */
+    BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
 
     location += (16 * fileCount);
 
     for (entry = info->entries; fileCount > 0; fileCount--, entry++)
     {
-        if ( (!readAll(fh, &entry->name, 12)) ||
-             (!readAll(fh, &entry->size, sizeof (PHYSFS_uint32))) )
-        {
-            __PHYSFS_platformClose(fh);
-            return 0;
-        } /* if */
-
+        BAIL_IF_MACRO(!readAll(io, &entry->name, 12), NULL, 0);
+        BAIL_IF_MACRO(!readAll(io, &entry->size, 4), NULL, 0);
         entry->name[12] = '\0';  /* name isn't null-terminated in file. */
         if ((ptr = strchr(entry->name, ' ')) != NULL)
             *ptr = '\0';  /* trim extra spaces. */
@@ -241,36 +225,40 @@
         location += entry->size;
     } /* for */
 
-    __PHYSFS_platformClose(fh);
-
-    __PHYSFS_sort(info->entries, info->entryCount,
-                  grp_entry_cmp, grp_entry_swap);
+    __PHYSFS_sort(info->entries, info->entryCount, grpEntryCmp, grpEntrySwap);
     return 1;
 } /* grp_load_entries */
 
 
-static void *GRP_openArchive(const char *name, int forWriting)
+static void *GRP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
-    GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo));
+    PHYSFS_uint8 buf[12];
+    GRPinfo *info = NULL;
+    PHYSFS_uint32 val = 0;
+
+    assert(io != NULL);  /* shouldn't ever happen. */
 
-    BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
+    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
 
+    BAIL_IF_MACRO(!readAll(io, buf, sizeof (buf)), NULL, NULL);
+    if (memcmp(buf, "KenSilverman", sizeof (buf)) != 0)
+        GOTO_MACRO(ERR_NOT_AN_ARCHIVE, GRP_openArchive_failed);
+
+    info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo));
+    GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, GRP_openArchive_failed);
     memset(info, '\0', sizeof (GRPinfo));
-    info->filename = (char *) allocator.Malloc(strlen(name) + 1);
-    GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed);
+    info->io = io;
 
-    if (!grp_load_entries(name, forWriting, info))
-        goto GRP_openArchive_failed;
+    GOTO_IF_MACRO(!readAll(io,&val,sizeof(val)), NULL, GRP_openArchive_failed);
+    info->entryCount = PHYSFS_swapULE32(val);
 
-    strcpy(info->filename, name);
+    GOTO_IF_MACRO(!grp_load_entries(io, info), NULL, GRP_openArchive_failed);
 
     return info;
 
 GRP_openArchive_failed:
     if (info != NULL)
     {
-        if (info->filename != NULL)
-            allocator.Free(info->filename);
         if (info->entries != NULL)
             allocator.Free(info->entries);
         allocator.Free(info);
@@ -318,7 +306,7 @@
     while (lo <= hi)
     {
         middle = lo + ((hi - lo) / 2);
-        rc = strcmp(name, a[middle].name);
+        rc = __PHYSFS_stricmpASCII(name, a[middle].name);
         if (rc == 0)  /* found it! */
             return &a[middle];
         else if (rc > 0)
@@ -351,40 +339,58 @@
 } /* GRP_isSymLink */
 
 
-static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+static PHYSFS_Io *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
 {
+    PHYSFS_Io *retval = NULL;
     GRPinfo *info = (GRPinfo *) opaque;
     GRPfileinfo *finfo;
     GRPentry *entry;
 
     entry = grp_find_entry(info, fnm);
     *fileExists = (entry != NULL);
-    BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+    GOTO_IF_MACRO(entry == NULL, NULL, GRP_openRead_failed);
+
+    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, GRP_openRead_failed);
 
     finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo));
-    BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, GRP_openRead_failed);
 
-    finfo->handle = __PHYSFS_platformOpenRead(info->filename);
-    if ( (finfo->handle == NULL) ||
-         (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
-    {
-        allocator.Free(finfo);
-        return NULL;
-    } /* if */
+    finfo->io = info->io->duplicate(info->io);
+    GOTO_IF_MACRO(finfo->io == NULL, NULL, GRP_openRead_failed);
+
+    if (!finfo->io->seek(finfo->io, entry->startPos))
+        GOTO_MACRO(NULL, GRP_openRead_failed);
 
     finfo->curPos = 0;
     finfo->entry = entry;
-    return finfo;
+
+    memcpy(retval, &GRP_Io, sizeof (*retval));
+    retval->opaque = finfo;
+    return retval;
+
+GRP_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;
 } /* GRP_openRead */
 
 
-static fvoid *GRP_openWrite(dvoid *opaque, const char *name)
+static PHYSFS_Io *GRP_openWrite(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* GRP_openWrite */
 
 
-static fvoid *GRP_openAppend(dvoid *opaque, const char *name)
+static PHYSFS_Io *GRP_openAppend(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* GRP_openAppend */
@@ -446,14 +452,7 @@
     GRP_remove,             /* remove() method         */
     GRP_mkdir,              /* mkdir() method          */
     GRP_dirClose,           /* dirClose() method       */
-    GRP_stat,               /* stat() method           */
-    GRP_read,               /* read() method           */
-    GRP_write,              /* write() method          */
-    GRP_eof,                /* eof() method            */
-    GRP_tell,               /* tell() method           */
-    GRP_seek,               /* seek() method           */
-    GRP_fileLength,         /* fileLength() method     */
-    GRP_fileClose           /* fileClose() method      */
+    GRP_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_GRP */
--- a/src/archiver_hog.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_hog.c	Mon Aug 30 03:01:57 2010 -0400
@@ -54,7 +54,7 @@
  */
 typedef struct
 {
-    char *filename;
+    PHYSFS_Io *io;
     PHYSFS_uint32 entryCount;
     HOGentry *entries;
 } HOGinfo;
@@ -64,30 +64,30 @@
  */
 typedef struct
 {
-    void *handle;
+    PHYSFS_Io *io;
     HOGentry *entry;
     PHYSFS_uint32 curPos;
 } HOGfileinfo;
 
 
-static inline int readAll(void *fh, void *buf, const PHYSFS_uint64 len)
+static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
 {
-    return (__PHYSFS_platformRead(fh, buf, len) == len);
+    return (io->read(io, buf, len) == len);
 } /* readAll */
 
 
 static void HOG_dirClose(dvoid *opaque)
 {
     HOGinfo *info = ((HOGinfo *) opaque);
-    allocator.Free(info->filename);
+    info->io->destroy(info->io);
     allocator.Free(info->entries);
     allocator.Free(info);
 } /* HOG_dirClose */
 
 
-static PHYSFS_sint64 HOG_read(fvoid *opaque, void *buffer, PHYSFS_uint64 len)
+static PHYSFS_sint64 HOG_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
 {
-    HOGfileinfo *finfo = (HOGfileinfo *) opaque;
+    HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
     const HOGentry *entry = finfo->entry;
     const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
     PHYSFS_sint64 rc;
@@ -95,7 +95,7 @@
     if (bytesLeft < len)
         len = bytesLeft;
 
-    rc = __PHYSFS_platformRead(finfo->handle, buffer, len);
+    rc = finfo->io->read(finfo->io, buffer, len);
     if (rc > 0)
         finfo->curPos += (PHYSFS_uint32) rc;
 
@@ -103,35 +103,26 @@
 } /* HOG_read */
 
 
-static PHYSFS_sint64 HOG_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 HOG_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
 } /* HOG_write */
 
 
-static int HOG_eof(fvoid *opaque)
+static PHYSFS_sint64 HOG_tell(PHYSFS_Io *io)
 {
-    HOGfileinfo *finfo = (HOGfileinfo *) opaque;
-    HOGentry *entry = finfo->entry;
-    return (finfo->curPos >= entry->size);
-} /* HOG_eof */
-
-
-static PHYSFS_sint64 HOG_tell(fvoid *opaque)
-{
-    return ((HOGfileinfo *) opaque)->curPos;
+    return ((HOGfileinfo *) io->opaque)->curPos;
 } /* HOG_tell */
 
 
-static int HOG_seek(fvoid *opaque, PHYSFS_uint64 offset)
+static int HOG_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
 {
-    HOGfileinfo *finfo = (HOGfileinfo *) opaque;
-    HOGentry *entry = finfo->entry;
+    HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
+    const HOGentry *entry = finfo->entry;
     int rc;
 
-    BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
     BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
-    rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+    rc = finfo->io->seek(finfo->io, entry->startPos + offset);
     if (rc)
         finfo->curPos = (PHYSFS_uint32) offset;
 
@@ -139,83 +130,65 @@
 } /* HOG_seek */
 
 
-static PHYSFS_sint64 HOG_fileLength(fvoid *opaque)
+static PHYSFS_sint64 HOG_length(PHYSFS_Io *io)
 {
-    HOGfileinfo *finfo = (HOGfileinfo *) opaque;
+    const HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
     return ((PHYSFS_sint64) finfo->entry->size);
-} /* HOG_fileLength */
-
-
-static int HOG_fileClose(fvoid *opaque)
-{
-    HOGfileinfo *finfo = (HOGfileinfo *) opaque;
-    BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
-    allocator.Free(finfo);
-    return 1;
-} /* HOG_fileClose */
+} /* HOG_length */
 
 
-static int hog_open(const char *filename, int forWriting,
-                    void **fh, PHYSFS_uint32 *count)
+static PHYSFS_Io *HOG_duplicate(PHYSFS_Io *_io)
 {
-    PHYSFS_uint8 buf[13];
-    PHYSFS_uint32 size;
-    PHYSFS_sint64 pos;
-
-    *count = 0;
-
-    *fh = NULL;
-    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
-
-    *fh = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(*fh == NULL, NULL, 0);
-
-    if (!readAll(*fh, buf, 3))
-        goto openHog_failed;
-
-    if (memcmp(buf, "DHF", 3) != 0)
-    {
-        __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
-        goto openHog_failed;
-    } /* if */
-
-    while (1)
-    {
-        if (!readAll(*fh, buf, 13))
-            break; /* eof here is ok */
+    HOGfileinfo *origfinfo = (HOGfileinfo *) _io->opaque;
+    PHYSFS_Io *io = NULL;
+    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    HOGfileinfo *finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, HOG_duplicate_failed);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, HOG_duplicate_failed);
 
-        if (!readAll(*fh, &size, sizeof (PHYSFS_uint32)))
-            goto openHog_failed;
-
-        size = PHYSFS_swapULE32(size);
-
-        (*count)++;
-
-        /* Skip over entry... */
-        pos = __PHYSFS_platformTell(*fh);
-        if (pos == -1)
-            goto openHog_failed;
-        if (!__PHYSFS_platformSeek(*fh, pos + size))
-            goto openHog_failed;
-    } /* while */
+    io = origfinfo->io->duplicate(origfinfo->io);
+    GOTO_IF_MACRO(io == NULL, NULL, HOG_duplicate_failed);
+    finfo->io = io;
+    finfo->entry = origfinfo->entry;
+    finfo->curPos = 0;
+    memcpy(retval, _io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+    return retval;
 
-    /* Rewind to start of entries... */
-    if (!__PHYSFS_platformSeek(*fh, 3))
-        goto openHog_failed;
-
-    return 1;
+HOG_duplicate_failed:
+    if (finfo != NULL) allocator.Free(finfo);
+    if (retval != NULL) allocator.Free(retval);
+    if (io != NULL) io->destroy(io);
+    return NULL;
+} /* HOG_duplicate */
 
-openHog_failed:
-    if (*fh != NULL)
-        __PHYSFS_platformClose(*fh);
+static int HOG_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
 
-    *count = -1;
-    *fh = NULL;
-    return 0;
-} /* hog_open */
+static void HOG_destroy(PHYSFS_Io *io)
+{
+    HOGfileinfo *finfo = (HOGfileinfo *) io->opaque;
+    finfo->io->destroy(finfo->io);
+    allocator.Free(finfo);
+    allocator.Free(io);
+} /* HOG_destroy */
 
 
-static int hog_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static const PHYSFS_Io HOG_Io =
+{
+    HOG_read,
+    HOG_write,
+    HOG_seek,
+    HOG_tell,
+    HOG_length,
+    HOG_duplicate,
+    HOG_flush,
+    HOG_destroy,
+    NULL
+};
+
+
+
+static int hogEntryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
@@ -224,10 +197,10 @@
     } /* if */
 
     return 0;
-} /* hog_entry_cmp */
+} /* hogEntryCmp */
 
 
-static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static void hogEntrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
@@ -238,74 +211,70 @@
         memcpy(first, second, sizeof (HOGentry));
         memcpy(second, &tmp, sizeof (HOGentry));
     } /* if */
-} /* hog_entry_swap */
+} /* hogEntrySwap */
 
 
-static int hog_load_entries(const char *name, int forWriting, HOGinfo *info)
+static int hog_load_entries(PHYSFS_Io *io, HOGinfo *info)
 {
-    void *fh = NULL;
-    PHYSFS_uint32 fileCount;
-    HOGentry *entry;
+    const PHYSFS_uint64 iolen = io->length(io);
+    PHYSFS_uint32 entCount = 0;
+    void *ptr = NULL;
+    HOGentry *entry = NULL;
+    PHYSFS_uint32 size = 0;
+    PHYSFS_uint32 pos = 3;
 
-    BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0);
-    info->entryCount = fileCount;
-    info->entries = (HOGentry *) allocator.Malloc(sizeof(HOGentry)*fileCount);
-    if (info->entries == NULL)
-    {
-        __PHYSFS_platformClose(fh);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
-    } /* if */
-
-    for (entry = info->entries; fileCount > 0; fileCount--, entry++)
+    while (pos < iolen)
     {
-        if ( (!readAll(fh, &entry->name, 13)) ||
-             (!readAll(fh, &entry->size, sizeof (PHYSFS_uint32))) )
-        {
-            __PHYSFS_platformClose(fh);
-            return 0;
-        } /* if */
+        entCount++;
+        ptr = allocator.Realloc(ptr, sizeof (HOGentry) * entCount);
+        BAIL_IF_MACRO(ptr == NULL, ERR_OUT_OF_MEMORY, 0);
+        info->entries = (HOGentry *) ptr;
+        entry = &info->entries[entCount-1];
 
-        entry->size = PHYSFS_swapULE32(entry->size);
-        entry->startPos = (unsigned int) __PHYSFS_platformTell(fh);
+        BAIL_IF_MACRO(!readAll(io, &entry->name, 13), NULL, 0);
+        pos += 13;
+        BAIL_IF_MACRO(!readAll(io, &size, 4), NULL, 0);
+        pos += 4;
 
-        /* Skip over entry */
-        if ( (entry->startPos == -1) ||
-             (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size)) )
-        {
-            __PHYSFS_platformClose(fh);
-            return 0;
-        } /* if */
+        entry->size = PHYSFS_swapULE32(size);
+        entry->startPos = pos;
+        pos += size;
+
+        BAIL_IF_MACRO(!io->seek(io, pos), NULL, 0);  /* skip over entry */
     } /* for */
 
-    __PHYSFS_platformClose(fh);
+    info->entryCount = entCount;
 
-    __PHYSFS_sort(info->entries, info->entryCount,
-                  hog_entry_cmp, hog_entry_swap);
+    __PHYSFS_sort(info->entries, entCount, hogEntryCmp, hogEntrySwap);
     return 1;
 } /* hog_load_entries */
 
 
-static void *HOG_openArchive(const char *name, int forWriting)
+static void *HOG_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
-    HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
+    PHYSFS_uint8 buf[3];
+    HOGinfo *info = NULL;
+
+    assert(io != NULL);  /* shouldn't ever happen. */
+
+    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
 
-    BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
+    BAIL_IF_MACRO(!readAll(io, buf, 3), NULL, 0);
+    if (memcmp(buf, "DHF", 3) != 0)
+        GOTO_MACRO(ERR_NOT_AN_ARCHIVE, HOG_openArchive_failed);
+
+    info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
+    GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, HOG_openArchive_failed);
     memset(info, '\0', sizeof (HOGinfo));
-    info->filename = (char *) allocator.Malloc(strlen(name) + 1);
-    GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, HOG_openArchive_failed);
+    info->io = io;
 
-    if (!hog_load_entries(name, forWriting, info))
-        goto HOG_openArchive_failed;
-
-    strcpy(info->filename, name);
+    GOTO_IF_MACRO(!hog_load_entries(io, info), NULL, HOG_openArchive_failed);
 
     return info;
 
 HOG_openArchive_failed:
     if (info != NULL)
     {
-        if (info->filename != NULL)
-            allocator.Free(info->filename);
         if (info->entries != NULL)
             allocator.Free(info->entries);
         allocator.Free(info);
@@ -368,7 +337,7 @@
 
 static int HOG_exists(dvoid *opaque, const char *name)
 {
-    return (hog_find_entry(((HOGinfo *) opaque), name) != NULL);
+    return (hog_find_entry((HOGinfo *) opaque, name) != NULL);
 } /* HOG_exists */
 
 
@@ -386,40 +355,58 @@
 } /* HOG_isSymLink */
 
 
-static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+static PHYSFS_Io *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
 {
-    HOGinfo *info = ((HOGinfo *) opaque);
+    PHYSFS_Io *retval = NULL;
+    HOGinfo *info = (HOGinfo *) opaque;
     HOGfileinfo *finfo;
     HOGentry *entry;
 
     entry = hog_find_entry(info, fnm);
     *fileExists = (entry != NULL);
-    BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+    GOTO_IF_MACRO(entry == NULL, NULL, HOG_openRead_failed);
+
+    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, HOG_openRead_failed);
 
     finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
-    BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, HOG_openRead_failed);
 
-    finfo->handle = __PHYSFS_platformOpenRead(info->filename);
-    if ( (finfo->handle == NULL) ||
-         (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
-    {
-        allocator.Free(finfo);
-        return NULL;
-    } /* if */
+    finfo->io = info->io->duplicate(info->io);
+    GOTO_IF_MACRO(finfo->io == NULL, NULL, HOG_openRead_failed);
+
+    if (!finfo->io->seek(finfo->io, entry->startPos))
+        GOTO_MACRO(NULL, HOG_openRead_failed);
 
     finfo->curPos = 0;
     finfo->entry = entry;
-    return finfo;
+
+    memcpy(retval, &HOG_Io, sizeof (*retval));
+    retval->opaque = finfo;
+    return retval;
+
+HOG_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;
 } /* HOG_openRead */
 
 
-static fvoid *HOG_openWrite(dvoid *opaque, const char *name)
+static PHYSFS_Io *HOG_openWrite(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* HOG_openWrite */
 
 
-static fvoid *HOG_openAppend(dvoid *opaque, const char *name)
+static PHYSFS_Io *HOG_openAppend(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* HOG_openAppend */
@@ -481,14 +468,7 @@
     HOG_remove,             /* remove() method         */
     HOG_mkdir,              /* mkdir() method          */
     HOG_dirClose,           /* dirClose() method       */
-    HOG_stat,               /* stat() method           */
-    HOG_read,               /* read() method           */
-    HOG_write,              /* write() method          */
-    HOG_eof,                /* eof() method            */
-    HOG_tell,               /* tell() method           */
-    HOG_seek,               /* seek() method           */
-    HOG_fileLength,         /* fileLength() method     */
-    HOG_fileClose           /* fileClose() method      */
+    HOG_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_HOG */
--- a/src/archiver_iso9660.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_iso9660.c	Mon Aug 30 03:01:57 2010 -0400
@@ -200,7 +200,7 @@
 
 typedef struct
 {
-    void *fhandle;
+    PHYSFS_Io *io;
     PHYSFS_uint32 rootdirstart;
     PHYSFS_uint32 rootdirsize;
     PHYSFS_uint64 currpos;
@@ -219,12 +219,13 @@
     PHYSFS_uint32 (*read) (struct __ISO9660FileHandle *filehandle, void *buffer,
             PHYSFS_uint64 len);
     int (*seek)(struct __ISO9660FileHandle *filehandle,  PHYSFS_sint64 offset);
-    int (*close)(struct __ISO9660FileHandle *filehandle);
+    void (*close)(struct __ISO9660FileHandle *filehandle);
     /* !!! FIXME: anonymouse union is going to cause problems. */
     union
     {
+        /* !!! FIXME: just use a memory PHYSFS_Io here, unify all this code. */
         char *cacheddata; /* data of file when cached */
-        void *filehandle; /* handle to separate opened file */
+        PHYSFS_Io *io; /* handle to separate opened file */
     };
 } ISO9660FileHandle;
 
@@ -349,9 +350,8 @@
             ERR_LOCK_VIOLATION, -1);
     int rc = -1;
     if (where != handle->currpos)
-        GOTO_IF_MACRO(!__PHYSFS_platformSeek(handle->fhandle,where), NULL,
-                unlockme);
-    rc = __PHYSFS_platformRead(handle->fhandle, buffer, len);
+        GOTO_IF_MACRO(!handle->io->seek(handle->io,where), NULL, unlockme);
+    rc = handle->io->read(handle->io, buffer, len);
     if (rc == -1)
     {
         handle->currpos = (PHYSFS_uint64) -1;
@@ -479,53 +479,94 @@
 } /* iso_read_ext_attributes */
 
 
+static int ISO9660_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
+
+static PHYSFS_Io *ISO9660_duplicate(PHYSFS_Io *_io)
+{
+    BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);  /* !!! FIXME: write me. */
+} /* ISO9660_duplicate */
+
+
+static void ISO9660_destroy(PHYSFS_Io *io)
+{
+    ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque;
+    fhandle->close(fhandle);
+    allocator.Free(io);
+} /* ISO9660_destroy */
+
+
+static PHYSFS_sint64 ISO9660_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
+{
+    ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque;
+    return fhandle->read(fhandle, buf, len);
+} /* ISO9660_read */
+
+
+static PHYSFS_sint64 ISO9660_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 l)
+{
+    BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* ISO9660_write */
+
+
+static PHYSFS_sint64 ISO9660_tell(PHYSFS_Io *io)
+{
+    return ((ISO9660FileHandle*) io->opaque)->currpos;
+} /* ISO9660_tell */
+
+
+static int ISO9660_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
+{
+    ISO9660FileHandle *fhandle = (ISO9660FileHandle*) io->opaque;
+    return fhandle->seek(fhandle, offset);
+} /* ISO9660_seek */
+
+
+static PHYSFS_sint64 ISO9660_length(PHYSFS_Io *io)
+{
+    return ((ISO9660FileHandle*) io->opaque)->filesize;
+} /* ISO9660_length */
+
+
+static const PHYSFS_Io ISO9660_Io =
+{
+    ISO9660_read,
+    ISO9660_write,
+    ISO9660_seek,
+    ISO9660_tell,
+    ISO9660_length,
+    ISO9660_duplicate,
+    ISO9660_flush,
+    ISO9660_destroy,
+    NULL
+};
+
+
 /*******************************************************************************
  * Archive management functions
  ******************************************************************************/
 
-/* !!! FIXME: don't open/close file here, merge with openArchive(). */
-static int isIso(const char *filename)
+static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename, int forWriting)
 {
-    char magicnumber[6] = {0,0,0,0,0,0};
-    void *in;
-
-    in = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(in == NULL, NULL, 0);
-
-    /* Skip system area to magic number in Volume descriptor */
-    if (__PHYSFS_platformSeek(in, 32769) == 0)
-    {
-        __PHYSFS_platformClose(in); /* Don't forget to close the file before returning... */
-        BAIL_MACRO(NULL, 0);
-    } /* if */
-
-    /* Read magic number */
-    if (__PHYSFS_platformRead(in, magicnumber, 5) != 5)
-    {
-        __PHYSFS_platformClose(in); /* Don't forget to close the file before returning... */
-        BAIL_MACRO(NULL, 0);
-    } /* if */
-
-    __PHYSFS_platformClose(in);
-
-    return (strcmp(magicnumber, "CD001") == 0);
-} /* isIso */
-
-
-static void *ISO9660_openArchive(const char *filename, int forWriting)
-{
+    char magicnumber[6];
     ISO9660Handle *handle;
     int founddescriptor = 0;
     int foundjoliet = 0;
 
-    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
-    BAIL_IF_MACRO(!isIso(filename), ERR_UNSUPPORTED_ARCHIVE, NULL);
+    assert(io != NULL);  /* shouldn't ever happen. */
+
+    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
+
+    /* Skip system area to magic number in Volume descriptor */
+    BAIL_IF_MACRO(!io->seek(io, 32769), NULL, NULL);
+    BAIL_IF_MACRO(!io->read(io, magicnumber, 5) != 5, NULL, NULL);
+    if (memcmp(magicnumber, "CD001", 6) != 0)
+        BAIL_MACRO(ERR_NOT_AN_ARCHIVE, NULL);
 
     handle = allocator.Malloc(sizeof(ISO9660Handle));
     GOTO_IF_MACRO(!handle, ERR_OUT_OF_MEMORY, errorcleanup);
     handle->path = 0;
     handle->mutex= 0;
-    handle->fhandle = 0;
+    handle->io = NULL;
 
     handle->path = allocator.Malloc(strlen(filename) + 1);
     GOTO_IF_MACRO(!handle->path, ERR_OUT_OF_MEMORY, errorcleanup);
@@ -534,16 +575,15 @@
     handle->mutex = __PHYSFS_platformCreateMutex();
     GOTO_IF_MACRO(!handle->mutex, "Cannot create Mutex", errorcleanup);
 
-    handle->fhandle = __PHYSFS_platformOpenRead(filename);
-    GOTO_IF_MACRO(!handle->fhandle, NULL, errorcleanup);
+    handle->io = io;
 
     /* seek Primary Volume Descriptor */
-    GOTO_IF_MACRO(!__PHYSFS_platformSeek(handle->fhandle, 32768), ERR_SEEK_ERROR, errorcleanup);
+    GOTO_IF_MACRO(!io->seek(io, 32768), ERR_SEEK_ERROR, errorcleanup);
 
     while (1)
     {
         ISO9660VolumeDescriptor descriptor;
-        GOTO_IF_MACRO(__PHYSFS_platformRead(handle->fhandle, &descriptor, sizeof(ISO9660VolumeDescriptor)) != sizeof(ISO9660VolumeDescriptor), "Cannot read from image", errorcleanup);
+        GOTO_IF_MACRO(io->read(io, &descriptor, sizeof(ISO9660VolumeDescriptor)) != sizeof(ISO9660VolumeDescriptor), "Cannot read from image", errorcleanup);
         GOTO_IF_MACRO(strncmp(descriptor.identifier, "CD001", 5) != 0, ERR_NOT_AN_ARCHIVE, errorcleanup);
 
         if (descriptor.type == 255)
@@ -556,7 +596,7 @@
         } /* if */
         if (descriptor.type == 1 && !founddescriptor)
         {
-            handle->currpos = __PHYSFS_platformTell(handle->fhandle);
+            handle->currpos = io->tell(io);
             handle->rootdirstart =
                     descriptor.rootdirectory.extent_location * 2048;
             handle->rootdirsize =
@@ -575,7 +615,7 @@
             if (!joliet)
                 continue;
 
-            handle->currpos = __PHYSFS_platformTell(handle->fhandle);
+            handle->currpos = io->tell(io);
             handle->rootdirstart =
                     descriptor.rootdirectory.extent_location * 2048;
             handle->rootdirsize =
@@ -591,12 +631,11 @@
 errorcleanup:
     if (handle)
     {
-        if (handle->fhandle)
-            __PHYSFS_platformClose(handle->fhandle);
         if (handle->path)
             allocator.Free(handle->path);
         if (handle->mutex)
             __PHYSFS_platformDestroyMutex(handle->mutex);
+        allocator.Free(handle);
     } /* if */
     return NULL;
 } /* ISO9660_openArchive */
@@ -605,7 +644,7 @@
 static void ISO9660_dirClose(dvoid *opaque)
 {
     ISO9660Handle *handle = (ISO9660Handle*) opaque;
-    __PHYSFS_platformClose(handle->fhandle);
+    handle->io->destroy(handle->io);
     __PHYSFS_platformDestroyMutex(handle->mutex);
     allocator.Free(handle->path);
     allocator.Free(handle);
@@ -645,12 +684,11 @@
 } /* iso_file_seek_mem */
 
 
-static int iso_file_close_mem(ISO9660FileHandle *fhandle)
+static void iso_file_close_mem(ISO9660FileHandle *fhandle)
 {
     allocator.Free(fhandle->cacheddata);
     allocator.Free(fhandle);
-    return -1;
-} /* iso_file_seek_mem */
+} /* iso_file_close_mem */
 
 
 static PHYSFS_uint32 iso_file_read_foreign(ISO9660FileHandle *filehandle,
@@ -661,8 +699,7 @@
     if (bytesleft < len)
         len = bytesleft;
 
-    PHYSFS_sint64 rc =  __PHYSFS_platformRead(filehandle->filehandle, buffer,
-                                              len);
+    const PHYSFS_sint64 rc = filehandle->io->read(filehandle->io, buffer, len);
     BAIL_IF_MACRO(rc == -1, NULL, -1);
 
     filehandle->currpos += rc; /* i trust my internal book keeping */
@@ -678,19 +715,17 @@
     BAIL_IF_MACRO(offset >= fhandle->filesize, ERR_PAST_EOF, 0);
 
     PHYSFS_sint64 pos = fhandle->startblock * 2048 + offset;
-    int rc = __PHYSFS_platformSeek(fhandle->filehandle, pos);
-    BAIL_IF_MACRO(rc, NULL, -1);
+    BAIL_IF_MACRO(!fhandle->io->seek(fhandle->io, pos), NULL, -1);
 
     fhandle->currpos = offset;
     return 0;
 } /* iso_file_seek_foreign */
 
 
-static int iso_file_close_foreign(ISO9660FileHandle *fhandle)
+static void iso_file_close_foreign(ISO9660FileHandle *fhandle)
 {
-    void *filehandle = fhandle->filehandle;
+    fhandle->io->destroy(fhandle->io);
     allocator.Free(fhandle);
-    return __PHYSFS_platformClose(filehandle);
 } /* iso_file_close_foreign */
 
 
@@ -717,10 +752,10 @@
 static int iso_file_open_foreign(ISO9660Handle *handle,
                                  ISO9660FileHandle *fhandle)
 {
-    fhandle->filehandle = __PHYSFS_platformOpenRead(handle->path);
-    BAIL_IF_MACRO(!fhandle->filehandle, NULL, -1);
-    int rc = __PHYSFS_platformSeek(fhandle->filehandle,
-            fhandle->startblock * 2048);
+    int rc;
+    fhandle->io = __PHYSFS_createNativeIo(handle->path, 'r');
+    BAIL_IF_MACRO(!fhandle->io, NULL, -1);
+    rc = fhandle->io->seek(fhandle->io, fhandle->startblock * 2048);
     GOTO_IF_MACRO(!rc, NULL, closefile);
 
     fhandle->read = iso_file_read_foreign;
@@ -728,14 +763,15 @@
     fhandle->close = iso_file_close_foreign;
     return 0;
 
-    closefile:
-    __PHYSFS_platformClose(fhandle->filehandle);
+closefile:
+    fhandle->io->destroy(fhandle->io);
     return -1;
 } /* iso_file_open_foreign */
 
 
-static fvoid *ISO9660_openRead(dvoid *opaque, const char *filename, int *exists)
+static PHYSFS_Io *ISO9660_openRead(dvoid *opaque, const char *filename, int *exists)
 {
+    PHYSFS_Io *retval = NULL;
     ISO9660Handle *handle = (ISO9660Handle*) opaque;
     ISO9660FileHandle *fhandle;
     ISO9660FileDescriptor descriptor;
@@ -745,6 +781,9 @@
     BAIL_IF_MACRO(fhandle == 0, ERR_OUT_OF_MEMORY, NULL);
     fhandle->cacheddata = 0;
 
+    retval = allocator.Malloc(sizeof(PHYSFS_Io));
+    GOTO_IF_MACRO(retval == 0, ERR_OUT_OF_MEMORY, errorhandling);
+
     /* find file descriptor */
     rc = iso_find_dir_entry(handle, filename, &descriptor, exists);
     GOTO_IF_MACRO(rc, NULL, errorhandling);
@@ -755,7 +794,7 @@
     fhandle->currpos = 0;
     fhandle->isohandle = handle;
     fhandle->cacheddata = NULL;
-    fhandle->filehandle = NULL;
+    fhandle->io = NULL;
 
     if (descriptor.datalen <= ISO9660_FULLCACHEMAXSIZE)
         rc = iso_file_open_mem(handle, fhandle);
@@ -763,53 +802,17 @@
         rc = iso_file_open_foreign(handle, fhandle);
     GOTO_IF_MACRO(rc, NULL, errorhandling);
 
-    return fhandle;
+    memcpy(retval, &ISO9660_Io, sizeof (PHYSFS_Io));
+    retval->opaque = fhandle;
+    return retval;
 
 errorhandling:
-    if (!fhandle)
-        return NULL;
-    allocator.Free(fhandle);
+    if (retval) allocator.Free(retval);
+    if (fhandle) allocator.Free(fhandle);
     return NULL;
 } /* ISO9660_openRead */
 
 
-static int ISO9660_fileClose(fvoid *opaque)
-{
-    ISO9660FileHandle *fhandle = (ISO9660FileHandle*) opaque;
-    return fhandle->close(fhandle);
-} /* ISO9660_fileClose */
-
-static PHYSFS_sint64 ISO9660_read(fvoid *opaque, void *buf, PHYSFS_uint64 len)
-{
-    ISO9660FileHandle *fhandle = (ISO9660FileHandle*) opaque;
-    return fhandle->read(fhandle, buf, len);
-} /* ISO9660_read */
-
-
-static PHYSFS_sint64 ISO9660_tell(fvoid *opaque)
-{
-    return ((ISO9660FileHandle*) opaque)->currpos;
-} /* ISO9660_tell */
-
-
-static int ISO9660_seek(fvoid *opaque, PHYSFS_uint64 offset)
-{
-    ISO9660FileHandle *fhandle = (ISO9660FileHandle*) opaque;
-    return fhandle->seek(fhandle, offset);
-} /* ISO9660_seek */
-
-
-static int ISO9660_eof(fvoid *opaque)
-{
-    ISO9660FileHandle *fhandle = (ISO9660FileHandle*) opaque;
-    return fhandle->currpos >= fhandle->filesize;
-} /* ISO9660_eof */
-
-
-static PHYSFS_sint64 ISO9660_fileLength(fvoid *opaque)
-{
-    return ((ISO9660FileHandle*) opaque)->filesize;
-} /* ISO9660_fileLength */
 
 /*******************************************************************************
  * Information gathering functions
@@ -945,13 +948,13 @@
  * Not supported functions
  ******************************************************************************/
 
-static fvoid* ISO9660_openWrite(dvoid *opaque, const char *name)
+static PHYSFS_Io *ISO9660_openWrite(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* ISO9660_openWrite */
 
 
-static fvoid* ISO9660_openAppend(dvoid *opaque, const char *name)
+static PHYSFS_Io *ISO9660_openAppend(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* ISO9660_openAppend */
@@ -969,11 +972,6 @@
 } /* ISO9660_mkdir */
 
 
-static PHYSFS_sint64 ISO9660_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
-{
-    BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
-} /* ISO9660_write */
-
 
 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ISO9660 =
 {
@@ -997,14 +995,7 @@
     ISO9660_remove,             /* remove() method         */
     ISO9660_mkdir,              /* mkdir() method          */
     ISO9660_dirClose,           /* dirClose() method       */
-    ISO9660_stat,               /* stat() method           */
-    ISO9660_read,               /* read() method           */
-    ISO9660_write,              /* write() method          */
-    ISO9660_eof,                /* eof() method            */
-    ISO9660_tell,               /* tell() method           */
-    ISO9660_seek,               /* seek() method           */
-    ISO9660_fileLength,         /* fileLength() method     */
-    ISO9660_fileClose           /* fileClose() method      */
+    ISO9660_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_ISO9660 */
--- a/src/archiver_lzma.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_lzma.c	Mon Aug 30 03:01:57 2010 -0400
@@ -40,7 +40,7 @@
     ISzAlloc allocImp; /* Allocation implementation, used by 7z */
     ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */
     ISzInStream inStream; /* Input stream with read callbacks, used by 7z */
-    void *file; /* Filehandle, used by read implementation */
+    PHYSFS_Io *io;  /* Filehandle, used by read implementation */
 #ifdef _LZMA_IN_CB
     Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */
 #endif /* _LZMA_IN_CB */
@@ -113,7 +113,7 @@
 
     if (maxReqSize > BUFFER_SIZE)
         maxReqSize = BUFFER_SIZE;
-    processedSizeLoc = __PHYSFS_platformRead(s->file, s->buffer, maxReqSize);
+    processedSizeLoc = s->io->read(s->io, s->buffer, maxReqSize);
     *buffer = s->buffer;
     if (processedSize != NULL)
         *processedSize = (size_t) processedSizeLoc;
@@ -131,8 +131,8 @@
                         size_t *processedSize)
 {
     FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
-    size_t processedSizeLoc = __PHYSFS_platformRead(s->file, buffer, size);
-    if (processedSize != 0)
+    const size_t processedSizeLoc = s->io->read(s->io, buffer, size);
+    if (processedSize != NULL)
         *processedSize = processedSizeLoc;
     return SZ_OK;
 } /* SzFileReadImp */
@@ -146,7 +146,7 @@
 SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
 {
     FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
-    if (__PHYSFS_platformSeek(s->file, (PHYSFS_uint64) pos))
+    if (s->io->seek(s->io, (PHYSFS_uint64) pos))
         return SZ_OK;
     return SZE_FAIL;
 } /* SzFileSeekImp */
@@ -322,12 +322,12 @@
 } /* lzma_err */
 
 
-static PHYSFS_sint64 LZMA_read(fvoid *opaque, void *outBuf, PHYSFS_uint64 len)
+static PHYSFS_sint64 LZMA_read(PHYSFS_Io *io, void *outBuf, PHYSFS_uint64 len)
 {
-    LZMAfile *file = (LZMAfile *) opaque;
+    LZMAfile *file = (LZMAfile *) io->opaque;
 
     size_t wantedSize = (size_t) len;
-    size_t remainingSize = file->item->Size - file->position;
+    const size_t remainingSize = file->item->Size - file->position;
     size_t fileSize = 0;
 
     BAIL_IF_MACRO(wantedSize == 0, NULL, 0); /* quick rejection. */
@@ -336,10 +336,10 @@
     if (wantedSize > remainingSize)
         wantedSize = remainingSize;
 
-    /* Only decompress the folder if it is not allready cached */
+    /* Only decompress the folder if it is not already cached */
     if (file->folder->cache == NULL)
     {
-        int rc = lzma_err(SzExtract(
+        const int rc = lzma_err(SzExtract(
             &file->archive->stream.inStream, /* compressed data */
             &file->archive->db, /* 7z's database, containing everything */
             file->index, /* Index into database arrays */
@@ -360,9 +360,7 @@
     } /* if */
 
     /* Copy wanted bytes over from cache to outBuf */
-    memcpy(outBuf,
-            (file->folder->cache +
-                    file->offset + file->position),
+    memcpy(outBuf, (file->folder->cache + file->offset + file->position),
             wantedSize);
     file->position += wantedSize; /* Increase virtual position */
 
@@ -370,31 +368,23 @@
 } /* LZMA_read */
 
 
-static PHYSFS_sint64 LZMA_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 LZMA_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
 } /* LZMA_write */
 
 
-static int LZMA_eof(fvoid *opaque)
+static PHYSFS_sint64 LZMA_tell(PHYSFS_Io *io)
 {
-    LZMAfile *file = (LZMAfile *) opaque;
-    return (file->position >= file->item->Size);
-} /* LZMA_eof */
-
-
-static PHYSFS_sint64 LZMA_tell(fvoid *opaque)
-{
-    LZMAfile *file = (LZMAfile *) opaque;
-    return (file->position);
+    LZMAfile *file = (LZMAfile *) io->opaque;
+    return file->position;
 } /* LZMA_tell */
 
 
-static int LZMA_seek(fvoid *opaque, PHYSFS_uint64 offset)
+static int LZMA_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
 {
-    LZMAfile *file = (LZMAfile *) opaque;
+    LZMAfile *file = (LZMAfile *) io->opaque;
 
-    BAIL_IF_MACRO(offset < 0, ERR_SEEK_OUT_OF_RANGE, 0);
     BAIL_IF_MACRO(offset > file->item->Size, ERR_PAST_EOF, 0);
 
     file->position = offset; /* We only use a virtual position... */
@@ -403,75 +393,78 @@
 } /* LZMA_seek */
 
 
-static PHYSFS_sint64 LZMA_fileLength(fvoid *opaque)
+static PHYSFS_sint64 LZMA_length(PHYSFS_Io *io)
 {
-    LZMAfile *file = (LZMAfile *) opaque;
+    const LZMAfile *file = (LZMAfile *) io->opaque;
     return (file->item->Size);
-} /* LZMA_fileLength */
+} /* LZMA_length */
+
+
+static PHYSFS_Io *LZMA_duplicate(PHYSFS_Io *_io)
+{
+    /* !!! FIXME: this archiver needs to be reworked to allow multiple
+     * !!! FIXME:  opens before we worry about duplication. */
+    BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* LZMA_duplicate */
+
+
+static int LZMA_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
 
 
-static int LZMA_fileClose(fvoid *opaque)
+static void LZMA_destroy(PHYSFS_Io *io)
 {
-    LZMAfile *file = (LZMAfile *) opaque;
-
-    BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, 0);
+    LZMAfile *file = (LZMAfile *) io->opaque;
 
-	/* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */
-    if (file->folder->references > 0)
-        file->folder->references--;
-    if (file->folder->references == 0)
+    if (file->folder != NULL)
     {
-        /* Free the cache which might have been allocated by LZMA_read() */
-        allocator.Free(file->folder->cache);
-        file->folder->cache = NULL;
-    }
-
-    return 1;
-} /* LZMA_fileClose */
+        /* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */
+        if (file->folder->references > 0)
+            file->folder->references--;
+        if (file->folder->references == 0)
+        {
+            /* Free the cache which might have been allocated by LZMA_read() */
+            allocator.Free(file->folder->cache);
+            file->folder->cache = NULL;
+        }
+        /* !!! FIXME: we don't free (file) or (file->folder)?! */
+    } /* if */
+} /* LZMA_destroy */
 
 
-/* !!! FIXME: don't open/close file here, merge with openArchive(). */
-static int isLzma(const char *filename)
+static const PHYSFS_Io LZMA_Io =
+{
+    LZMA_read,
+    LZMA_write,
+    LZMA_seek,
+    LZMA_tell,
+    LZMA_length,
+    LZMA_duplicate,
+    LZMA_flush,
+    LZMA_destroy,
+    NULL
+};
+
+
+static void *LZMA_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
     PHYSFS_uint8 sig[k7zSignatureSize];
-    void *in;
-
-    in = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(in == NULL, NULL, 0);
-
-    /* Read signature bytes */
-    if (__PHYSFS_platformRead(in, sig, k7zSignatureSize) != k7zSignatureSize)
-    {
-        __PHYSFS_platformClose(in); /* Don't forget to close the file before returning... */
-        BAIL_MACRO(NULL, 0);
-    }
-
-    __PHYSFS_platformClose(in);
-
-    /* Test whether sig is the 7z signature */
-    return TestSignatureCandidate(sig);
-} /* isLzma */
-
-
-static void *LZMA_openArchive(const char *name, int forWriting)
-{
     size_t len = 0;
     LZMAarchive *archive = NULL;
 
+    assert(io != NULL);  /* shouldn't ever happen. */
+
     BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
-    BAIL_IF_MACRO(!isLzma(name), ERR_UNSUPPORTED_ARCHIVE, NULL);
+
+    if (io->read(io, sig, k7zSignatureSize) != k7zSignatureSize)
+        BAIL_MACRO(NULL, 0);
+    BAIL_IF_MACRO(!TestSignatureCandidate(sig), ERR_NOT_AN_ARCHIVE, NULL);
+    BAIL_IF_MACRO(!io->seek(io, 0), NULL, NULL);
 
     archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive));
     BAIL_IF_MACRO(archive == NULL, ERR_OUT_OF_MEMORY, NULL);
 
     lzma_archive_init(archive);
-
-    if ( (archive->stream.file = __PHYSFS_platformOpenRead(name)) == NULL )
-    {
-        __PHYSFS_platformClose(archive->stream.file);
-        lzma_archive_exit(archive);
-        return NULL; /* Error is set by platformOpenRead! */
-    }
+    archive->stream.io = io;
 
     CrcGenerateTable();
     SzArDbExInit(&archive->db);
@@ -481,7 +474,6 @@
                                &archive->stream.allocTempImp)) != SZ_OK)
     {
         SzArDbExFree(&archive->db, SzFreePhysicsFS);
-        __PHYSFS_platformClose(archive->stream.file);
         lzma_archive_exit(archive);
         return NULL; /* Error is set by lzma_err! */
     } /* if */
@@ -491,7 +483,6 @@
     if (archive->files == NULL)
     {
         SzArDbExFree(&archive->db, SzFreePhysicsFS);
-        __PHYSFS_platformClose(archive->stream.file);
         lzma_archive_exit(archive);
         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
     }
@@ -507,7 +498,6 @@
     if (archive->folders == NULL)
     {
         SzArDbExFree(&archive->db, SzFreePhysicsFS);
-        __PHYSFS_platformClose(archive->stream.file);
         lzma_archive_exit(archive);
         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
     }
@@ -521,7 +511,6 @@
     if(!lzma_files_init(archive))
     {
         SzArDbExFree(&archive->db, SzFreePhysicsFS);
-        __PHYSFS_platformClose(archive->stream.file);
         lzma_archive_exit(archive);
         BAIL_MACRO(ERR_UNKNOWN_ERROR, NULL);
     }
@@ -616,10 +605,11 @@
 } /* LZMA_isSymLink */
 
 
-static fvoid *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists)
+static PHYSFS_Io *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists)
 {
     LZMAarchive *archive = (LZMAarchive *) opaque;
     LZMAfile *file = lzma_find_file(archive, name);
+    PHYSFS_Io *io = NULL;
 
     *fileExists = (file != NULL);
     BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL);
@@ -628,17 +618,22 @@
     file->position = 0;
     file->folder->references++; /* Increase refcount for automatic cleanup... */
 
-    return file;
+    io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    BAIL_IF_MACRO(io == NULL, ERR_OUT_OF_MEMORY, NULL);
+    memcpy(io, &LZMA_Io, sizeof (*io));
+    io->opaque = file;
+
+    return io;
 } /* LZMA_openRead */
 
 
-static fvoid *LZMA_openWrite(dvoid *opaque, const char *filename)
+static PHYSFS_Io *LZMA_openWrite(dvoid *opaque, const char *filename)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* LZMA_openWrite */
 
 
-static fvoid *LZMA_openAppend(dvoid *opaque, const char *filename)
+static PHYSFS_Io *LZMA_openAppend(dvoid *opaque, const char *filename)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* LZMA_openAppend */
@@ -647,15 +642,17 @@
 static void LZMA_dirClose(dvoid *opaque)
 {
     LZMAarchive *archive = (LZMAarchive *) opaque;
+
+#if 0  /* !!! FIXME: you shouldn't have to do this. */
     PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
-
     for (fileIndex = 0; fileIndex < numFiles; fileIndex++)
     {
         LZMA_fileClose(&archive->files[fileIndex]);
     } /* for */
+#endif
 
     SzArDbExFree(&archive->db, SzFreePhysicsFS);
-    __PHYSFS_platformClose(archive->stream.file);
+    archive->stream.io->destroy(archive->stream.io);
     lzma_archive_exit(archive);
 } /* LZMA_dirClose */
 
@@ -731,14 +728,7 @@
     LZMA_remove,             /* remove() method         */
     LZMA_mkdir,              /* mkdir() method          */
     LZMA_dirClose,           /* dirClose() method       */
-    LZMA_stat,               /* stat() method           */
-    LZMA_read,               /* read() method           */
-    LZMA_write,              /* write() method          */
-    LZMA_eof,                /* eof() method            */
-    LZMA_tell,               /* tell() method           */
-    LZMA_seek,               /* seek() method           */
-    LZMA_fileLength,         /* fileLength() method     */
-    LZMA_fileClose           /* fileClose() method      */
+    LZMA_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_7Z */
--- a/src/archiver_mvl.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_mvl.c	Mon Aug 30 03:01:57 2010 -0400
@@ -46,31 +46,37 @@
 
 typedef struct
 {
-    char *filename;
+    PHYSFS_Io *io;
     PHYSFS_uint32 entryCount;
     MVLentry *entries;
 } MVLinfo;
 
 typedef struct
 {
-    void *handle;
+    PHYSFS_Io *io;
     MVLentry *entry;
     PHYSFS_uint32 curPos;
 } MVLfileinfo;
 
 
+static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
+{
+    return (io->read(io, buf, len) == len);
+} /* readAll */
+
+
 static void MVL_dirClose(dvoid *opaque)
 {
     MVLinfo *info = ((MVLinfo *) opaque);
-    allocator.Free(info->filename);
+    info->io->destroy(info->io);
     allocator.Free(info->entries);
     allocator.Free(info);
 } /* MVL_dirClose */
 
 
-static PHYSFS_sint64 MVL_read(fvoid *opaque, void *buffer, PHYSFS_uint64 len)
+static PHYSFS_sint64 MVL_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
 {
-    MVLfileinfo *finfo = (MVLfileinfo *) opaque;
+    MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
     const MVLentry *entry = finfo->entry;
     const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
     PHYSFS_sint64 rc;
@@ -78,7 +84,7 @@
     if (bytesLeft < len)
         len = bytesLeft;
 
-    rc = __PHYSFS_platformRead(finfo->handle, buffer, len);
+    rc = finfo->io->read(finfo->io, buffer, len);
     if (rc > 0)
         finfo->curPos += (PHYSFS_uint32) rc;
 
@@ -86,35 +92,26 @@
 } /* MVL_read */
 
 
-static PHYSFS_sint64 MVL_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 MVL_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
 } /* MVL_write */
 
 
-static int MVL_eof(fvoid *opaque)
+static PHYSFS_sint64 MVL_tell(PHYSFS_Io *io)
 {
-    MVLfileinfo *finfo = (MVLfileinfo *) opaque;
-    MVLentry *entry = finfo->entry;
-    return (finfo->curPos >= entry->size);
-} /* MVL_eof */
-
-
-static PHYSFS_sint64 MVL_tell(fvoid *opaque)
-{
-    return ((MVLfileinfo *) opaque)->curPos;
+    return ((MVLfileinfo *) io->opaque)->curPos;
 } /* MVL_tell */
 
 
-static int MVL_seek(fvoid *opaque, PHYSFS_uint64 offset)
+static int MVL_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
 {
-    MVLfileinfo *finfo = (MVLfileinfo *) opaque;
-    MVLentry *entry = finfo->entry;
+    MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
+    const MVLentry *entry = finfo->entry;
     int rc;
 
-    BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
     BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
-    rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+    rc = finfo->io->seek(finfo->io, entry->startPos + offset);
     if (rc)
         finfo->curPos = (PHYSFS_uint32) offset;
 
@@ -122,72 +119,76 @@
 } /* MVL_seek */
 
 
-static PHYSFS_sint64 MVL_fileLength(fvoid *opaque)
+static PHYSFS_sint64 MVL_length(PHYSFS_Io *io)
 {
-    MVLfileinfo *finfo = (MVLfileinfo *) opaque;
+    const MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
     return ((PHYSFS_sint64) finfo->entry->size);
-} /* MVL_fileLength */
-
-
-static int MVL_fileClose(fvoid *opaque)
-{
-    MVLfileinfo *finfo = (MVLfileinfo *) opaque;
-    BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
-    allocator.Free(finfo);
-    return 1;
-} /* MVL_fileClose */
+} /* MVL_length */
 
 
-static int mvl_open(const char *filename, int forWriting,
-                    void **fh, PHYSFS_uint32 *count)
+static PHYSFS_Io *MVL_duplicate(PHYSFS_Io *_io)
 {
-    PHYSFS_uint8 buf[4];
-
-    *fh = NULL;
-    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
-
-    *fh = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(*fh == NULL, NULL, 0);
-    
-    if (__PHYSFS_platformRead(*fh, buf, 4) != 4)
-        goto openMvl_failed;
+    MVLfileinfo *origfinfo = (MVLfileinfo *) _io->opaque;
+    PHYSFS_Io *io = NULL;
+    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    MVLfileinfo *finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, MVL_duplicate_failed);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, MVL_duplicate_failed);
 
-    if (memcmp(buf, "DMVL", 4) != 0)
-    {
-        __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
-        goto openMvl_failed;
-    } /* if */
-
-    if (__PHYSFS_platformRead(*fh, count, 4) != 4)
-        goto openMvl_failed;
-
-    *count = PHYSFS_swapULE32(*count);
+    io = origfinfo->io->duplicate(origfinfo->io);
+    GOTO_IF_MACRO(io == NULL, NULL, MVL_duplicate_failed);
+    finfo->io = io;
+    finfo->entry = origfinfo->entry;
+    finfo->curPos = 0;
+    memcpy(retval, _io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+    return retval;
 
-    return 1;
+MVL_duplicate_failed:
+    if (finfo != NULL) allocator.Free(finfo);
+    if (retval != NULL) allocator.Free(retval);
+    if (io != NULL) io->destroy(io);
+    return NULL;
+} /* MVL_duplicate */
 
-openMvl_failed:
-    if (*fh != NULL)
-        __PHYSFS_platformClose(*fh);
+static int MVL_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
 
-    *count = -1;
-    *fh = NULL;
-    return 0;
-} /* mvl_open */
+static void MVL_destroy(PHYSFS_Io *io)
+{
+    MVLfileinfo *finfo = (MVLfileinfo *) io->opaque;
+    finfo->io->destroy(finfo->io);
+    allocator.Free(finfo);
+    allocator.Free(io);
+} /* MVL_destroy */
 
 
-static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static const PHYSFS_Io MVL_Io =
+{
+    MVL_read,
+    MVL_write,
+    MVL_seek,
+    MVL_tell,
+    MVL_length,
+    MVL_duplicate,
+    MVL_flush,
+    MVL_destroy,
+    NULL
+};
+
+
+static int mvlEntryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
         const MVLentry *a = (const MVLentry *) _a;
-        return strcmp(a[one].name, a[two].name);
+        return __PHYSFS_stricmpASCII(a[one].name, a[two].name);
     } /* if */
 
     return 0;
-} /* mvl_entry_cmp */
+} /* mvlEntryCmp */
 
 
-static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static void mvlEntrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
@@ -198,74 +199,63 @@
         memcpy(first, second, sizeof (MVLentry));
         memcpy(second, &tmp, sizeof (MVLentry));
     } /* if */
-} /* mvl_entry_swap */
+} /* mvlEntrySwap */
 
 
-static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info)
+static int mvl_load_entries(PHYSFS_Io *io, MVLinfo *info)
 {
-    void *fh = NULL;
-    PHYSFS_uint32 fileCount;
+    PHYSFS_uint32 fileCount = info->entryCount;
     PHYSFS_uint32 location = 8;  /* sizeof sig. */
     MVLentry *entry;
 
-    BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0);
-    info->entryCount = fileCount;
     info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount);
-    if (info->entries == NULL)
-    {
-        __PHYSFS_platformClose(fh);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
-    } /* if */
+    BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
 
     location += (17 * fileCount);
 
     for (entry = info->entries; fileCount > 0; fileCount--, entry++)
     {
-        if (__PHYSFS_platformRead(fh, &entry->name, 13) != 13)
-        {
-            __PHYSFS_platformClose(fh);
-            return 0;
-        } /* if */
-
-        if (__PHYSFS_platformRead(fh, &entry->size, 4) != 4)
-        {
-            __PHYSFS_platformClose(fh);
-            return 0;
-        } /* if */
-
+        BAIL_IF_MACRO(!readAll(io, &entry->name, 13), NULL, 0);
+        BAIL_IF_MACRO(!readAll(io, &entry->size, 4), NULL, 0);
         entry->size = PHYSFS_swapULE32(entry->size);
         entry->startPos = location;
         location += entry->size;
     } /* for */
 
-    __PHYSFS_platformClose(fh);
-
-    __PHYSFS_sort(info->entries, info->entryCount,
-                  mvl_entry_cmp, mvl_entry_swap);
+    __PHYSFS_sort(info->entries, info->entryCount, mvlEntryCmp, mvlEntrySwap);
     return 1;
 } /* mvl_load_entries */
 
 
-static void *MVL_openArchive(const char *name, int forWriting)
+static void *MVL_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
-    MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
+    PHYSFS_uint8 buf[4];
+    MVLinfo *info = NULL;
+    PHYSFS_uint32 val = 0;
 
-    BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
-    memset(info, '\0', sizeof (MVLinfo));
+    assert(io != NULL);  /* shouldn't ever happen. */
+
+    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
 
-    info->filename = (char *) allocator.Malloc(strlen(name) + 1);
-    GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
-    if (!mvl_load_entries(name, forWriting, info))
-        goto MVL_openArchive_failed;
+    BAIL_IF_MACRO(!readAll(io, buf, 4), NULL, NULL);
+    if (memcmp(buf, "DMVL", 4) != 0)
+        GOTO_MACRO(ERR_NOT_AN_ARCHIVE, MVL_openArchive_failed);
 
-    strcpy(info->filename, name);
+    info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
+    GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
+    memset(info, '\0', sizeof (MVLinfo));
+    info->io = io;
+
+    GOTO_IF_MACRO(!readAll(io,&val,sizeof(val)), NULL, MVL_openArchive_failed);
+    info->entryCount = PHYSFS_swapULE32(val);
+
+    GOTO_IF_MACRO(!mvl_load_entries(io, info), NULL, MVL_openArchive_failed);
+
     return info;
 
 MVL_openArchive_failed:
     if (info != NULL)
     {
-        if (info->filename != NULL)
-            allocator.Free(info->filename);
         if (info->entries != NULL)
             allocator.Free(info->entries);
         allocator.Free(info);
@@ -346,40 +336,58 @@
 } /* MVL_isSymLink */
 
 
-static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+static PHYSFS_Io *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
 {
-    MVLinfo *info = ((MVLinfo *) opaque);
+    PHYSFS_Io *retval = NULL;
+    MVLinfo *info = (MVLinfo *) opaque;
     MVLfileinfo *finfo;
     MVLentry *entry;
 
     entry = mvl_find_entry(info, fnm);
     *fileExists = (entry != NULL);
-    BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+    GOTO_IF_MACRO(entry == NULL, NULL, MVL_openRead_failed);
+
+    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, MVL_openRead_failed);
 
     finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
-    BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, MVL_openRead_failed);
 
-    finfo->handle = __PHYSFS_platformOpenRead(info->filename);
-    if ( (finfo->handle == NULL) ||
-         (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
-    {
-        allocator.Free(finfo);
-        return NULL;
-    } /* if */
+    finfo->io = info->io->duplicate(info->io);
+    GOTO_IF_MACRO(finfo->io == NULL, NULL, MVL_openRead_failed);
+
+    if (!finfo->io->seek(finfo->io, entry->startPos))
+        GOTO_MACRO(NULL, MVL_openRead_failed);
 
     finfo->curPos = 0;
     finfo->entry = entry;
-    return finfo;
+
+    memcpy(retval, &MVL_Io, sizeof (*retval));
+    retval->opaque = finfo;
+    return retval;
+
+MVL_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;
 } /* MVL_openRead */
 
 
-static fvoid *MVL_openWrite(dvoid *opaque, const char *name)
+static PHYSFS_Io *MVL_openWrite(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* MVL_openWrite */
 
 
-static fvoid *MVL_openAppend(dvoid *opaque, const char *name)
+static PHYSFS_Io *MVL_openAppend(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* MVL_openAppend */
@@ -441,14 +449,7 @@
     MVL_remove,             /* remove() method         */
     MVL_mkdir,              /* mkdir() method          */
     MVL_dirClose,           /* dirClose() method       */
-    MVL_stat,               /* stat() method           */
-    MVL_read,               /* read() method           */
-    MVL_write,              /* write() method          */
-    MVL_eof,                /* eof() method            */
-    MVL_tell,               /* tell() method           */
-    MVL_seek,               /* seek() method           */
-    MVL_fileLength,         /* fileLength() method     */
-    MVL_fileClose           /* fileClose() method      */
+    MVL_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_MVL */
--- a/src/archiver_qpak.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_qpak.c	Mon Aug 30 03:01:57 2010 -0400
@@ -39,6 +39,7 @@
 #define __PHYSICSFS_INTERNAL__
 #include "physfs_internal.h"
 
+/* !!! FIXME: what is this here for? */
 #if 1  /* Make this case insensitive? */
 #define QPAK_strcmp(x, y) __PHYSFS_stricmpASCII(x, y)
 #define QPAK_strncmp(x, y, z) __PHYSFS_strnicmpASCII(x, y, z)
@@ -57,14 +58,14 @@
 
 typedef struct
 {
-    char *filename;
+    PHYSFS_Io *io;
     PHYSFS_uint32 entryCount;
     QPAKentry *entries;
 } QPAKinfo;
 
 typedef struct
 {
-    void *handle;
+    PHYSFS_Io *io;
     QPAKentry *entry;
     PHYSFS_uint32 curPos;
 } QPAKfileinfo;
@@ -73,18 +74,24 @@
 #define QPAK_SIG 0x4b434150   /* "PACK" in ASCII. */
 
 
+static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
+{
+    return (io->read(io, buf, len) == len);
+} /* readAll */
+
+
 static void QPAK_dirClose(dvoid *opaque)
 {
     QPAKinfo *info = ((QPAKinfo *) opaque);
-    allocator.Free(info->filename);
+    info->io->destroy(info->io);
     allocator.Free(info->entries);
     allocator.Free(info);
 } /* QPAK_dirClose */
 
 
-static PHYSFS_sint64 QPAK_read(fvoid *opaque, void *buffer, PHYSFS_uint64 len)
+static PHYSFS_sint64 QPAK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
 {
-    QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
+    QPAKfileinfo *finfo = (QPAKfileinfo *) io->opaque;
     const QPAKentry *entry = finfo->entry;
     const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
     PHYSFS_sint64 rc;
@@ -92,7 +99,7 @@
     if (bytesLeft < len)
         len = bytesLeft;
 
-    rc = __PHYSFS_platformRead(finfo->handle, buffer, len);
+    rc = finfo->io->read(finfo->io, buffer, len);
     if (rc > 0)
         finfo->curPos += (PHYSFS_uint32) rc;
 
@@ -100,35 +107,26 @@
 } /* QPAK_read */
 
 
-static PHYSFS_sint64 QPAK_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 QPAK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
 } /* QPAK_write */
 
 
-static int QPAK_eof(fvoid *opaque)
+static PHYSFS_sint64 QPAK_tell(PHYSFS_Io *io)
 {
-    QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
-    QPAKentry *entry = finfo->entry;
-    return (finfo->curPos >= entry->size);
-} /* QPAK_eof */
-
-
-static PHYSFS_sint64 QPAK_tell(fvoid *opaque)
-{
-    return ((QPAKfileinfo *) opaque)->curPos;
+    return ((QPAKfileinfo *) io->opaque)->curPos;
 } /* QPAK_tell */
 
 
-static int QPAK_seek(fvoid *opaque, PHYSFS_uint64 offset)
+static int QPAK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
 {
-    QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
-    QPAKentry *entry = finfo->entry;
+    QPAKfileinfo *finfo = (QPAKfileinfo *) io->opaque;
+    const QPAKentry *entry = finfo->entry;
     int rc;
 
-    BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
     BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
-    rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+    rc = finfo->io->seek(finfo->io, entry->startPos + offset);
     if (rc)
         finfo->curPos = (PHYSFS_uint32) offset;
 
@@ -136,74 +134,66 @@
 } /* QPAK_seek */
 
 
-static PHYSFS_sint64 QPAK_fileLength(fvoid *opaque)
+static PHYSFS_sint64 QPAK_length(PHYSFS_Io *io)
 {
-    QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
+    const QPAKfileinfo *finfo = (QPAKfileinfo *) io->opaque;
     return ((PHYSFS_sint64) finfo->entry->size);
-} /* QPAK_fileLength */
-
-
-static int QPAK_fileClose(fvoid *opaque)
-{
-    QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
-    BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
-    allocator.Free(finfo);
-    return 1;
-} /* QPAK_fileClose */
+} /* QPAK_length */
 
 
-static inline int readAll(void *fh, void *buf, const PHYSFS_uint64 len)
-{
-    return (__PHYSFS_platformRead(fh, buf, len) == len);
-} /* readAll */
-
-static int qpak_open(const char *filename, int forWriting,
-                    void **fh, PHYSFS_uint32 *count)
+static PHYSFS_Io *QPAK_duplicate(PHYSFS_Io *_io)
 {
-    PHYSFS_uint32 buf;
-
-    *fh = NULL;
-    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
-
-    *fh = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(*fh == NULL, NULL, 0);
-    
-    if (!readAll(*fh, &buf, sizeof (PHYSFS_uint32)))
-        goto openQpak_failed;
-
-    buf = PHYSFS_swapULE32(buf);
-    GOTO_IF_MACRO(buf != QPAK_SIG, ERR_UNSUPPORTED_ARCHIVE, openQpak_failed);
-
-    if (!readAll(*fh, &buf, sizeof (PHYSFS_uint32)))
-        goto openQpak_failed;
+    QPAKfileinfo *origfinfo = (QPAKfileinfo *) _io->opaque;
+    PHYSFS_Io *io = NULL;
+    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    QPAKfileinfo *finfo = (QPAKfileinfo *) allocator.Malloc(sizeof (QPAKfileinfo));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, QPAK_duplicate_failed);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, QPAK_duplicate_failed);
 
-    buf = PHYSFS_swapULE32(buf);  /* directory table offset. */
-
-    if (!readAll(*fh, count, sizeof (PHYSFS_uint32)))
-        goto openQpak_failed;
-
-    *count = PHYSFS_swapULE32(*count);
-
-    /* corrupted archive? */
-    GOTO_IF_MACRO((*count % 64) != 0, ERR_CORRUPTED, openQpak_failed);
+    io = origfinfo->io->duplicate(origfinfo->io);
+    GOTO_IF_MACRO(io == NULL, NULL, QPAK_duplicate_failed);
+    finfo->io = io;
+    finfo->entry = origfinfo->entry;
+    finfo->curPos = 0;
+    memcpy(retval, _io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+    return retval;
 
-    if (!__PHYSFS_platformSeek(*fh, buf))
-        goto openQpak_failed;
-
-    *count /= 64;
-    return 1;
+QPAK_duplicate_failed:
+    if (finfo != NULL) allocator.Free(finfo);
+    if (retval != NULL) allocator.Free(retval);
+    if (io != NULL) io->destroy(io);
+    return NULL;
+} /* QPAK_duplicate */
 
-openQpak_failed:
-    if (*fh != NULL)
-        __PHYSFS_platformClose(*fh);
-
-    *count = -1;
-    *fh = NULL;
-    return 0;
-} /* qpak_open */
+static int QPAK_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
 
 
-static int qpak_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static void QPAK_destroy(PHYSFS_Io *io)
+{
+    QPAKfileinfo *finfo = (QPAKfileinfo *) io->opaque;
+    finfo->io->destroy(finfo->io);
+    allocator.Free(finfo);
+    allocator.Free(io);
+} /* QPAK_destroy */
+
+
+static const PHYSFS_Io QPAK_Io =
+{
+    QPAK_read,
+    QPAK_write,
+    QPAK_seek,
+    QPAK_tell,
+    QPAK_length,
+    QPAK_duplicate,
+    QPAK_flush,
+    QPAK_destroy,
+    NULL
+};
+
+
+
+static int qpakEntryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
@@ -212,10 +202,10 @@
     } /* if */
 
     return 0;
-} /* qpak_entry_cmp */
+} /* qpakEntryCmp */
 
 
-static void qpak_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static void qpakEntrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
@@ -226,77 +216,73 @@
         memcpy(first, second, sizeof (QPAKentry));
         memcpy(second, &tmp, sizeof (QPAKentry));
     } /* if */
-} /* qpak_entry_swap */
+} /* qpakEntrySwap */
 
 
-static int qpak_load_entries(const char *name, int forWriting, QPAKinfo *info)
+static int qpak_load_entries(QPAKinfo *info)
 {
-    void *fh = NULL;
-    PHYSFS_uint32 fileCount;
+    PHYSFS_Io *io = info->io;
+    PHYSFS_uint32 fileCount = info->entryCount;
     QPAKentry *entry;
 
-    BAIL_IF_MACRO(!qpak_open(name, forWriting, &fh, &fileCount), NULL, 0);
-    info->entryCount = fileCount;
     info->entries = (QPAKentry*) allocator.Malloc(sizeof(QPAKentry)*fileCount);
-    if (info->entries == NULL)
-    {
-        __PHYSFS_platformClose(fh);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
-    } /* if */
+    BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
 
     for (entry = info->entries; fileCount > 0; fileCount--, entry++)
     {
-        if ( (!readAll(fh, &entry->name, sizeof (entry->name))) ||
-             (!readAll(fh, &entry->startPos, sizeof (entry->startPos))) ||
-             (!readAll(fh, &entry->size, sizeof(entry->size))) )
-        {
-            __PHYSFS_platformClose(fh);
-            return 0;
-        } /* if */
-
+        BAIL_IF_MACRO(!readAll(io, &entry->name, 56), NULL, 0);
+        BAIL_IF_MACRO(!readAll(io, &entry->startPos, 4), NULL, 0);
+        BAIL_IF_MACRO(!readAll(io, &entry->size, 4), NULL, 0);
         entry->size = PHYSFS_swapULE32(entry->size);
         entry->startPos = PHYSFS_swapULE32(entry->startPos);
     } /* for */
 
-    __PHYSFS_platformClose(fh);
-
-    __PHYSFS_sort(info->entries, info->entryCount,
-                  qpak_entry_cmp, qpak_entry_swap);
+    __PHYSFS_sort(info->entries, info->entryCount, qpakEntryCmp, qpakEntrySwap);
     return 1;
 } /* qpak_load_entries */
 
 
-static void *QPAK_openArchive(const char *name, int forWriting)
+static void *QPAK_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
-    QPAKinfo *info = (QPAKinfo *) allocator.Malloc(sizeof (QPAKinfo));
+    QPAKinfo *info = NULL;
+    PHYSFS_uint32 val = 0;
+    PHYSFS_uint32 pos = 0;
+    PHYSFS_uint32 count = 0;
+
+    assert(io != NULL);  /* shouldn't ever happen. */
+
+    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
+
+    BAIL_IF_MACRO(!readAll(io, &val, 4), NULL, NULL);
+    BAIL_IF_MACRO(PHYSFS_swapULE32(val) != QPAK_SIG, ERR_NOT_AN_ARCHIVE, NULL);
 
+    BAIL_IF_MACRO(!readAll(io, &val, 4), NULL, NULL);
+    pos = PHYSFS_swapULE32(val);  /* directory table offset. */
+
+    BAIL_IF_MACRO(!readAll(io, &val, 4), NULL, NULL);
+    count = PHYSFS_swapULE32(val);
+
+    /* corrupted archive? */
+    BAIL_IF_MACRO((count % 64) != 0, ERR_CORRUPTED, NULL);
+    count /= 64;
+
+    BAIL_IF_MACRO(io->seek(io, pos), NULL, NULL);
+
+    info = (QPAKinfo *) allocator.Malloc(sizeof (QPAKinfo));
     BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
     memset(info, '\0', sizeof (QPAKinfo));
-
-    info->filename = (char *) allocator.Malloc(strlen(name) + 1);
-    if (info->filename == NULL)
-    {
-        __PHYSFS_setError(ERR_OUT_OF_MEMORY);
-        goto QPAK_openArchive_failed;
-    } /* if */
+    info->io = io;
+    info->entryCount = count;
 
-    if (!qpak_load_entries(name, forWriting, info))
-        goto QPAK_openArchive_failed;
-
-    strcpy(info->filename, name);
-    return info;
-
-QPAK_openArchive_failed:
-    if (info != NULL)
+    if (!qpak_load_entries(info))
     {
-        if (info->filename != NULL)
-            allocator.Free(info->filename);
         if (info->entries != NULL)
             allocator.Free(info->entries);
         allocator.Free(info);
+        return NULL;
     } /* if */
 
-    return NULL;
+    return info;
 } /* QPAK_openArchive */
 
 
@@ -495,8 +481,9 @@
 } /* QPAK_isSymLink */
 
 
-static fvoid *QPAK_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+static PHYSFS_Io *QPAK_openRead(dvoid *opaque, const char *fnm, int *fileExists)
 {
+    PHYSFS_Io *io = NULL;
     QPAKinfo *info = ((QPAKinfo *) opaque);
     QPAKfileinfo *finfo;
     QPAKentry *entry;
@@ -510,27 +497,42 @@
     finfo = (QPAKfileinfo *) allocator.Malloc(sizeof (QPAKfileinfo));
     BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
 
-    finfo->handle = __PHYSFS_platformOpenRead(info->filename);
-    if ( (finfo->handle == NULL) ||
-         (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
-    {
-        allocator.Free(finfo);
-        return NULL;
-    } /* if */
+    finfo->io = info->io->duplicate(info->io);
+    GOTO_IF_MACRO(finfo->io == NULL, NULL, QPAK_openRead_failed);
+    if (!finfo->io->seek(finfo->io, entry->startPos))
+        GOTO_MACRO(NULL, QPAK_openRead_failed);
 
     finfo->curPos = 0;
     finfo->entry = entry;
-    return finfo;
+    io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GOTO_IF_MACRO(io == NULL, ERR_OUT_OF_MEMORY, QPAK_openRead_failed);
+    memcpy(io, &QPAK_Io, sizeof (PHYSFS_Io));
+    io->opaque = finfo;
+
+    return io;
+
+QPAK_openRead_failed:
+    if (finfo != NULL)
+    {
+        if (finfo->io != NULL)
+            finfo->io->destroy(finfo->io);
+        allocator.Free(finfo);
+    } /* if */
+
+    if (io != NULL)
+        allocator.Free(io);
+
+    return NULL;
 } /* QPAK_openRead */
 
 
-static fvoid *QPAK_openWrite(dvoid *opaque, const char *name)
+static PHYSFS_Io *QPAK_openWrite(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* QPAK_openWrite */
 
 
-static fvoid *QPAK_openAppend(dvoid *opaque, const char *name)
+static PHYSFS_Io *QPAK_openAppend(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* QPAK_openAppend */
@@ -602,14 +604,7 @@
     QPAK_remove,             /* remove() method         */
     QPAK_mkdir,              /* mkdir() method          */
     QPAK_dirClose,           /* dirClose() method       */
-    QPAK_stat,               /* stat() method           */
-    QPAK_read,               /* read() method           */
-    QPAK_write,              /* write() method          */
-    QPAK_eof,                /* eof() method            */
-    QPAK_tell,               /* tell() method           */
-    QPAK_seek,               /* seek() method           */
-    QPAK_fileLength,         /* fileLength() method     */
-    QPAK_fileClose           /* fileClose() method      */
+    QPAK_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_QPAK */
--- a/src/archiver_wad.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_wad.c	Mon Aug 30 03:01:57 2010 -0400
@@ -61,32 +61,37 @@
 
 typedef struct
 {
-    char *filename;
+    PHYSFS_Io *io;
     PHYSFS_uint32 entryCount;
-    PHYSFS_uint32 entryOffset;
     WADentry *entries;
 } WADinfo;
 
 typedef struct
 {
-    void *handle;
+    PHYSFS_Io *io;
     WADentry *entry;
     PHYSFS_uint32 curPos;
 } WADfileinfo;
 
 
+static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
+{
+    return (io->read(io, buf, len) == len);
+} /* readAll */
+
+
 static void WAD_dirClose(dvoid *opaque)
 {
     WADinfo *info = ((WADinfo *) opaque);
-    allocator.Free(info->filename);
+    info->io->destroy(info->io);
     allocator.Free(info->entries);
     allocator.Free(info);
 } /* WAD_dirClose */
 
 
-static PHYSFS_sint64 WAD_read(fvoid *opaque, void *buffer, PHYSFS_uint64 len)
+static PHYSFS_sint64 WAD_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
 {
-    WADfileinfo *finfo = (WADfileinfo *) opaque;
+    WADfileinfo *finfo = (WADfileinfo *) io->opaque;
     const WADentry *entry = finfo->entry;
     const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
     PHYSFS_sint64 rc;
@@ -94,7 +99,7 @@
     if (bytesLeft < len)
         len = bytesLeft;
 
-    rc = __PHYSFS_platformRead(finfo->handle, buffer, len);
+    rc = finfo->io->read(finfo->io, buffer, len);
     if (rc > 0)
         finfo->curPos += (PHYSFS_uint32) rc;
 
@@ -102,35 +107,26 @@
 } /* WAD_read */
 
 
-static PHYSFS_sint64 WAD_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 WAD_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
 } /* WAD_write */
 
 
-static int WAD_eof(fvoid *opaque)
+static PHYSFS_sint64 WAD_tell(PHYSFS_Io *io)
 {
-    WADfileinfo *finfo = (WADfileinfo *) opaque;
-    WADentry *entry = finfo->entry;
-    return (finfo->curPos >= entry->size);
-} /* WAD_eof */
-
-
-static PHYSFS_sint64 WAD_tell(fvoid *opaque)
-{
-    return ((WADfileinfo *) opaque)->curPos;
+    return ((WADfileinfo *) io->opaque)->curPos;
 } /* WAD_tell */
 
 
-static int WAD_seek(fvoid *opaque, PHYSFS_uint64 offset)
+static int WAD_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
 {
-    WADfileinfo *finfo = (WADfileinfo *) opaque;
-    WADentry *entry = finfo->entry;
+    WADfileinfo *finfo = (WADfileinfo *) io->opaque;
+    const WADentry *entry = finfo->entry;
     int rc;
 
-    BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
     BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
-    rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+    rc = finfo->io->seek(finfo->io, entry->startPos + offset);
     if (rc)
         finfo->curPos = (PHYSFS_uint32) offset;
 
@@ -138,83 +134,77 @@
 } /* WAD_seek */
 
 
-static PHYSFS_sint64 WAD_fileLength(fvoid *opaque)
-{
-    WADfileinfo *finfo = (WADfileinfo *) opaque;
-    return ((PHYSFS_sint64) finfo->entry->size);
-} /* WAD_fileLength */
-
-
-static int WAD_fileClose(fvoid *opaque)
+static PHYSFS_sint64 WAD_length(PHYSFS_Io *io)
 {
-    WADfileinfo *finfo = (WADfileinfo *) opaque;
-    BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
-    allocator.Free(finfo);
-    return 1;
-} /* WAD_fileClose */
-
-
-static inline int readAll(void *fh, void *buf, const PHYSFS_uint64 len)
-{
-    return (__PHYSFS_platformRead(fh, buf, len) == len);
-} /* readAll */
+    const WADfileinfo *finfo = (WADfileinfo *) io->opaque;
+    return ((PHYSFS_sint64) finfo->entry->size);
+} /* WAD_length */
 
 
-static int wad_open(const char *filename, int forWriting,
-                    void **fh, PHYSFS_uint32 *count,PHYSFS_uint32 *offset)
+static PHYSFS_Io *WAD_duplicate(PHYSFS_Io *_io)
 {
-    PHYSFS_uint8 buf[4];
-
-    *fh = NULL;
-    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
-
-    *fh = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(*fh == NULL, NULL, 0);
-    
-    if (!readAll(*fh, buf, 4))
-        goto openWad_failed;
-
-    if (memcmp(buf, "IWAD", 4) != 0 && memcmp(buf, "PWAD", 4) != 0)
-    {
-        __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
-        goto openWad_failed;
-    } /* if */
+    WADfileinfo *origfinfo = (WADfileinfo *) _io->opaque;
+    PHYSFS_Io *io = NULL;
+    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    WADfileinfo *finfo = (WADfileinfo *) allocator.Malloc(sizeof (WADfileinfo));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, WAD_duplicate_failed);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, WAD_duplicate_failed);
 
-    if (!readAll(*fh, count, sizeof (PHYSFS_uint32)))
-        goto openWad_failed;
-
-    *count = PHYSFS_swapULE32(*count);
-
-    if (!readAll(*fh, offset, sizeof (PHYSFS_uint32)))
-        goto openWad_failed;
-
-    *offset = PHYSFS_swapULE32(*offset);
+    io = origfinfo->io->duplicate(origfinfo->io);
+    GOTO_IF_MACRO(io == NULL, NULL, WAD_duplicate_failed);
+    finfo->io = io;
+    finfo->entry = origfinfo->entry;
+    finfo->curPos = 0;
+    memcpy(retval, _io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+    return retval;
 
-    return 1;
+WAD_duplicate_failed:
+    if (finfo != NULL) allocator.Free(finfo);
+    if (retval != NULL) allocator.Free(retval);
+    if (io != NULL) io->destroy(io);
+    return NULL;
+} /* WAD_duplicate */
 
-openWad_failed:
-    if (*fh != NULL)
-        __PHYSFS_platformClose(*fh);
+static int WAD_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
 
-    *count = -1;
-    *fh = NULL;
-    return 0;
-} /* wad_open */
+static void WAD_destroy(PHYSFS_Io *io)
+{
+    WADfileinfo *finfo = (WADfileinfo *) io->opaque;
+    finfo->io->destroy(finfo->io);
+    allocator.Free(finfo);
+    allocator.Free(io);
+} /* WAD_destroy */
 
 
-static int wad_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static const PHYSFS_Io WAD_Io =
+{
+    WAD_read,
+    WAD_write,
+    WAD_seek,
+    WAD_tell,
+    WAD_length,
+    WAD_duplicate,
+    WAD_flush,
+    WAD_destroy,
+    NULL
+};
+
+
+
+static int wadEntryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
         const WADentry *a = (const WADentry *) _a;
-        return strcmp(a[one].name, a[two].name);
+        return __PHYSFS_stricmpASCII(a[one].name, a[two].name);
     } /* if */
 
     return 0;
-} /* wad_entry_cmp */
+} /* wadEntryCmp */
 
 
-static void wad_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+static void wadEntrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
 {
     if (one != two)
     {
@@ -225,74 +215,67 @@
         memcpy(first, second, sizeof (WADentry));
         memcpy(second, &tmp, sizeof (WADentry));
     } /* if */
-} /* wad_entry_swap */
+} /* wadEntrySwap */
 
 
-static int wad_load_entries(const char *name, int forWriting, WADinfo *info)
+static int wad_load_entries(PHYSFS_Io *io, WADinfo *info)
 {
-    void *fh = NULL;
-    PHYSFS_uint32 fileCount;
+    PHYSFS_uint32 fileCount = info->entryCount;
     PHYSFS_uint32 directoryOffset;
     WADentry *entry;
-    char lastDirectory[9];
 
-    lastDirectory[8] = 0; /* Make sure lastDirectory stays null-terminated. */
+    BAIL_IF_MACRO(!readAll(io, &directoryOffset, 4), NULL, 0);
+    directoryOffset = PHYSFS_swapULE32(directoryOffset);
 
-    BAIL_IF_MACRO(!wad_open(name, forWriting, &fh, &fileCount,&directoryOffset), NULL, 0);
-    info->entryCount = fileCount;
     info->entries = (WADentry *) allocator.Malloc(sizeof(WADentry)*fileCount);
-    if (info->entries == NULL)
-    {
-        __PHYSFS_platformClose(fh);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
-    } /* if */
-
-    __PHYSFS_platformSeek(fh,directoryOffset);
+    BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
+    BAIL_IF_MACRO(!io->seek(io, directoryOffset), NULL, 0);
 
     for (entry = info->entries; fileCount > 0; fileCount--, entry++)
     {
-        if ( (!readAll(fh, &entry->startPos, sizeof (PHYSFS_uint32))) ||
-             (!readAll(fh, &entry->size, sizeof (PHYSFS_uint32))) ||
-             (!readAll(fh, &entry->name, 8)) )
-        {
-            __PHYSFS_platformClose(fh);
-            return 0;
-        } /* if */
+        BAIL_IF_MACRO(!readAll(io, &entry->startPos, 4), NULL, 0);
+        BAIL_IF_MACRO(!readAll(io, &entry->size, 4), NULL, 0);
+        BAIL_IF_MACRO(!readAll(io, &entry->name, 8), NULL, 0);
 
         entry->name[8] = '\0'; /* name might not be null-terminated in file. */
         entry->size = PHYSFS_swapULE32(entry->size);
         entry->startPos = PHYSFS_swapULE32(entry->startPos);
     } /* for */
 
-    __PHYSFS_platformClose(fh);
-
-    __PHYSFS_sort(info->entries, info->entryCount,
-                  wad_entry_cmp, wad_entry_swap);
+    __PHYSFS_sort(info->entries, info->entryCount, wadEntryCmp, wadEntrySwap);
     return 1;
 } /* wad_load_entries */
 
 
-static void *WAD_openArchive(const char *name, int forWriting)
+static void *WAD_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
-    WADinfo *info = (WADinfo *) allocator.Malloc(sizeof (WADinfo));
+    PHYSFS_uint8 buf[4];
+    WADinfo *info = NULL;
+    PHYSFS_uint32 val = 0;
 
-    BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
-    memset(info, '\0', sizeof (WADinfo));
+    assert(io != NULL);  /* shouldn't ever happen. */
+
+    BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
 
-    info->filename = (char *) allocator.Malloc(strlen(name) + 1);
-    GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, WAD_openArchive_failed);
+    BAIL_IF_MACRO(!readAll(io, buf, sizeof (buf)), NULL, NULL);
+    if ((memcmp(buf, "IWAD", 4) != 0) && (memcmp(buf, "PWAD", 4) != 0))
+        GOTO_MACRO(ERR_NOT_AN_ARCHIVE, WAD_openArchive_failed);
 
-    if (!wad_load_entries(name, forWriting, info))
-        goto WAD_openArchive_failed;
+    info = (WADinfo *) allocator.Malloc(sizeof (WADinfo));
+    GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, WAD_openArchive_failed);
+    memset(info, '\0', sizeof (WADinfo));
+    info->io = io;
 
-    strcpy(info->filename, name);
+    GOTO_IF_MACRO(!readAll(io,&val,sizeof(val)), NULL, WAD_openArchive_failed);
+    info->entryCount = PHYSFS_swapULE32(val);
+
+    GOTO_IF_MACRO(!wad_load_entries(io, info), NULL, WAD_openArchive_failed);
+
     return info;
 
 WAD_openArchive_failed:
     if (info != NULL)
     {
-        if (info->filename != NULL)
-            allocator.Free(info->filename);
         if (info->entries != NULL)
             allocator.Free(info->entries);
         allocator.Free(info);
@@ -306,50 +289,41 @@
                                int omitSymLinks, PHYSFS_EnumFilesCallback cb,
                                const char *origdir, void *callbackdata)
 {
-    WADinfo *info = ((WADinfo *) opaque);
-    WADentry *entry = info->entries;
-    PHYSFS_uint32 max = info->entryCount;
-    PHYSFS_uint32 i;
-    const char *name;
-    char *sep;
+    /* no directories in WAD files. */
+    if (*dname == '\0')
+    {
+        WADinfo *info = (WADinfo *) opaque;
+        WADentry *entry = info->entries;
+        PHYSFS_uint32 max = info->entryCount;
+        PHYSFS_uint32 i;
 
-    if (*dname == '\0')  /* root directory enumeration? */
-    {
         for (i = 0; i < max; i++, entry++)
-        {
-            name = entry->name;
-            if (strchr(name, '/') == NULL)
-                cb(callbackdata, origdir, name);
-        } /* for */
+            cb(callbackdata, origdir, entry->name);
     } /* if */
-    else
-    {
-        for (i = 0; i < max; i++, entry++)
-        {
-            name = entry->name;
-            sep = strchr(name, '/');
-            if (sep != NULL)
-            {
-                if (strncmp(dname, name, (sep - name)) == 0)
-                    cb(callbackdata, origdir, sep + 1);
-            } /* if */
-        } /* for */
-    } /* else */
 } /* WAD_enumerateFiles */
 
 
 static WADentry *wad_find_entry(const WADinfo *info, const char *name)
 {
+    char *ptr = strchr(name, '.');
     WADentry *a = info->entries;
     PHYSFS_sint32 lo = 0;
     PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
     PHYSFS_sint32 middle;
     int rc;
 
+    /*
+     * Rule out filenames to avoid unneeded processing...no dirs,
+     *   big filenames, or extensions.
+     */
+    BAIL_IF_MACRO(ptr != NULL, ERR_NO_SUCH_FILE, NULL);
+    BAIL_IF_MACRO(strlen(name) > 8, ERR_NO_SUCH_FILE, NULL);
+    BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
+
     while (lo <= hi)
     {
         middle = lo + ((hi - lo) / 2);
-        rc = strcmp(name, a[middle].name);
+        rc = __PHYSFS_stricmpASCII(name, a[middle].name);
         if (rc == 0)  /* found it! */
             return &a[middle];
         else if (rc > 0)
@@ -371,16 +345,18 @@
 static int WAD_isDirectory(dvoid *opaque, const char *name, int *fileExists)
 {
     WADentry *entry = wad_find_entry(((WADinfo *) opaque), name);
-    if (entry != NULL)
+    const int exists = (entry != NULL);
+    *fileExists = exists;
+    if (exists)
     {
         char *n;
 
-        *fileExists = 1;
-
         /* Can't be a directory if it's a subdirectory. */
         if (strchr(entry->name, '/') != NULL)
             return 0;
 
+        /* !!! FIXME: this isn't really something we should do. */
+        /* !!! FIXME: I probably broke enumeration up there, too. */
         /* Check if it matches "MAP??" or "E?M?" ... */
         n = entry->name;
         if ((n[0] == 'E' && n[2] == 'M') ||
@@ -388,13 +364,9 @@
         {
             return 1;
         } /* if */
-        return 0;
     } /* if */
-    else
-    {
-        *fileExists = 0;
-        return 0;
-    } /* else */
+
+    return 0;
 } /* WAD_isDirectory */
 
 
@@ -405,40 +377,58 @@
 } /* WAD_isSymLink */
 
 
-static fvoid *WAD_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+static PHYSFS_Io *WAD_openRead(dvoid *opaque, const char *fnm, int *fileExists)
 {
-    WADinfo *info = ((WADinfo *) opaque);
+    PHYSFS_Io *retval = NULL;
+    WADinfo *info = (WADinfo *) opaque;
     WADfileinfo *finfo;
     WADentry *entry;
 
     entry = wad_find_entry(info, fnm);
     *fileExists = (entry != NULL);
-    BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+    GOTO_IF_MACRO(entry == NULL, NULL, WAD_openRead_failed);
+
+    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, WAD_openRead_failed);
 
     finfo = (WADfileinfo *) allocator.Malloc(sizeof (WADfileinfo));
-    BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, WAD_openRead_failed);
 
-    finfo->handle = __PHYSFS_platformOpenRead(info->filename);
-    if ( (finfo->handle == NULL) ||
-         (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
-    {
-        allocator.Free(finfo);
-        return NULL;
-    } /* if */
+    finfo->io = info->io->duplicate(info->io);
+    GOTO_IF_MACRO(finfo->io == NULL, NULL, WAD_openRead_failed);
+
+    if (!finfo->io->seek(finfo->io, entry->startPos))
+        GOTO_MACRO(NULL, WAD_openRead_failed);
 
     finfo->curPos = 0;
     finfo->entry = entry;
-    return finfo;
+
+    memcpy(retval, &WAD_Io, sizeof (*retval));
+    retval->opaque = finfo;
+    return retval;
+
+WAD_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;
 } /* WAD_openRead */
 
 
-static fvoid *WAD_openWrite(dvoid *opaque, const char *name)
+static PHYSFS_Io *WAD_openWrite(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* WAD_openWrite */
 
 
-static fvoid *WAD_openAppend(dvoid *opaque, const char *name)
+static PHYSFS_Io *WAD_openAppend(dvoid *opaque, const char *name)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* WAD_openAppend */
@@ -468,10 +458,10 @@
 
     stat->filesize = entry->size;
     stat->filetype = PHYSFS_FILETYPE_REGULAR;
-    stat->accesstime = -1;
     stat->modtime = -1;
     stat->createtime = -1;
-    stat->readonly = 1; /* WADs are always readonly */
+    stat->accesstime = -1;
+    stat->readonly = 1;
 
     return 1;
 } /* WAD_stat */
@@ -500,14 +490,7 @@
     WAD_remove,             /* remove() method         */
     WAD_mkdir,              /* mkdir() method          */
     WAD_dirClose,           /* dirClose() method       */
-    WAD_stat,               /* stat() method           */
-    WAD_read,               /* read() method           */
-    WAD_write,              /* write() method          */
-    WAD_eof,                /* eof() method            */
-    WAD_tell,               /* tell() method           */
-    WAD_seek,               /* seek() method           */
-    WAD_fileLength,         /* fileLength() method     */
-    WAD_fileClose           /* fileClose() method      */
+    WAD_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_WAD */
--- a/src/archiver_zip.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/archiver_zip.c	Mon Aug 30 03:01:57 2010 -0400
@@ -79,7 +79,7 @@
  */
 typedef struct
 {
-    char *archiveName;        /* path to ZIP in platform-dependent notation. */
+    PHYSFS_Io *io;
     PHYSFS_uint16 entryCount; /* Number of files in ZIP.                     */
     ZIPentry *entries;        /* info on all files in ZIP.                   */
 } ZIPinfo;
@@ -90,7 +90,7 @@
 typedef struct
 {
     ZIPentry *entry;                      /* Info on file.              */
-    void *handle;                         /* physical file handle.      */
+    PHYSFS_Io *io;                        /* physical file handle.      */
     PHYSFS_uint32 compressed_position;    /* offset in compressed data. */
     PHYSFS_uint32 uncompressed_position;  /* tell() position.           */
     PHYSFS_uint8 *buffer;                 /* decompression buffer.      */
@@ -172,18 +172,18 @@
 } /* zlib_err */
 
 
-static inline int readAll(void *fh, void *buf, const PHYSFS_uint64 len)
+static inline int readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
 {
-    return (__PHYSFS_platformRead(fh, buf, len) == len);
+    return (io->read(io, buf, len) == len);
 } /* readAll */
 
 /*
  * Read an unsigned 32-bit int and swap to native byte order.
  */
-static int readui32(void *in, PHYSFS_uint32 *val)
+static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
 {
     PHYSFS_uint32 v;
-    BAIL_IF_MACRO(!readAll(in, &v, sizeof (v)), NULL, 0);
+    BAIL_IF_MACRO(!readAll(io, &v, sizeof (v)), NULL, 0);
     *val = PHYSFS_swapULE32(v);
     return 1;
 } /* readui32 */
@@ -192,18 +192,19 @@
 /*
  * Read an unsigned 16-bit int and swap to native byte order.
  */
-static int readui16(void *in, PHYSFS_uint16 *val)
+static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val)
 {
     PHYSFS_uint16 v;
-    BAIL_IF_MACRO(!readAll(in, &v, sizeof (v)), NULL, 0);
+    BAIL_IF_MACRO(!readAll(io, &v, sizeof (v)), NULL, 0);
     *val = PHYSFS_swapULE16(v);
     return 1;
 } /* readui16 */
 
 
-static PHYSFS_sint64 ZIP_read(fvoid *opaque, void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len)
 {
-    ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+    ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
+    PHYSFS_Io *io = finfo->io;
     ZIPentry *entry = finfo->entry;
     PHYSFS_sint64 retval = 0;
     PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
@@ -216,7 +217,7 @@
     BAIL_IF_MACRO(maxread == 0, NULL, 0);    /* quick rejection. */
 
     if (entry->compression_method == COMPMETH_NONE)
-        retval = __PHYSFS_platformRead(finfo->handle, buf, maxread);
+        retval = io->read(io, buf, maxread);
     else
     {
         finfo->stream.next_out = buf;
@@ -237,8 +238,7 @@
                     if (br > ZIP_READBUFSIZE)
                         br = ZIP_READBUFSIZE;
 
-                    br = __PHYSFS_platformRead(finfo->handle, finfo->buffer,
-                                               (PHYSFS_uint64) br);
+                    br = io->read(io, finfo->buffer, (PHYSFS_uint64) br);
                     if (br <= 0)
                         break;
 
@@ -263,37 +263,30 @@
 } /* ZIP_read */
 
 
-static PHYSFS_sint64 ZIP_write(fvoid *f, const void *buf, PHYSFS_uint64 len)
+static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
 } /* ZIP_write */
 
 
-static int ZIP_eof(fvoid *opaque)
+static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io)
 {
-    ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
-    return (finfo->uncompressed_position >= finfo->entry->uncompressed_size);
-} /* ZIP_eof */
-
-
-static PHYSFS_sint64 ZIP_tell(fvoid *opaque)
-{
-    return ((ZIPfileinfo *) opaque)->uncompressed_position;
+    return ((ZIPfileinfo *) io->opaque)->uncompressed_position;
 } /* ZIP_tell */
 
 
-static int ZIP_seek(fvoid *opaque, PHYSFS_uint64 offset)
+static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
 {
-    ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+    ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
     ZIPentry *entry = finfo->entry;
-    void *in = finfo->handle;
+    PHYSFS_Io *io = finfo->io;
 
     BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0);
 
     if (entry->compression_method == COMPMETH_NONE)
     {
-        PHYSFS_sint64 newpos = offset + entry->offset;
-        BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, newpos), NULL, 0);
+        const PHYSFS_sint64 newpos = offset + entry->offset;
+        BAIL_IF_MACRO(!io->seek(io, newpos), NULL, 0);
         finfo->uncompressed_position = (PHYSFS_uint32) offset;
     } /* if */
 
@@ -313,7 +306,7 @@
             if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
                 return 0;
 
-            if (!__PHYSFS_platformSeek(in, entry->offset))
+            if (!io->seek(io, entry->offset))
                 return 0;
 
             inflateEnd(&finfo->stream);
@@ -330,7 +323,7 @@
             if (maxread > sizeof (buf))
                 maxread = sizeof (buf);
 
-            if (ZIP_read(finfo, buf, maxread) != maxread)
+            if (ZIP_read(_io, buf, maxread) != maxread)
                 return 0;
         } /* while */
     } /* else */
@@ -339,17 +332,67 @@
 } /* ZIP_seek */
 
 
-static PHYSFS_sint64 ZIP_fileLength(fvoid *opaque)
+static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
 {
-    ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+    const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
     return finfo->entry->uncompressed_size;
-} /* ZIP_fileLength */
+} /* ZIP_length */
 
 
-static int ZIP_fileClose(fvoid *opaque)
+static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry);
+
+static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
 {
-    ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
-    BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
+    ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque;
+    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
+    memset(finfo, '\0', sizeof (*finfo));
+
+    finfo->entry = origfinfo->entry;
+    finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
+    GOTO_IF_MACRO(finfo->io == NULL, NULL, ZIP_duplicate_failed);
+
+    if (finfo->entry->compression_method != COMPMETH_NONE)
+    {
+        finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
+        GOTO_IF_MACRO(!finfo->buffer, ERR_OUT_OF_MEMORY, ZIP_duplicate_failed);
+        if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
+            goto ZIP_duplicate_failed;
+    } /* if */
+
+    memcpy(retval, io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+    return retval;
+
+ZIP_duplicate_failed:
+    if (finfo != NULL)
+    {
+        if (finfo->io != NULL)
+            finfo->io->destroy(finfo->io);
+
+        if (finfo->buffer != NULL)
+        {
+            allocator.Free(finfo->buffer);
+            inflateEnd(&finfo->stream);
+        } /* if */
+
+        allocator.Free(finfo);
+    } /* if */
+
+    if (retval != NULL)
+        allocator.Free(retval);
+
+    return NULL;
+} /* ZIP_duplicate */
+
+static int ZIP_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
+
+static void ZIP_destroy(PHYSFS_Io *io)
+{
+    ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
+    finfo->io->destroy(finfo->io);
 
     if (finfo->entry->compression_method != COMPMETH_NONE)
         inflateEnd(&finfo->stream);
@@ -358,11 +401,26 @@
         allocator.Free(finfo->buffer);
 
     allocator.Free(finfo);
-    return 1;
-} /* ZIP_fileClose */
+    allocator.Free(io);
+} /* ZIP_destroy */
 
 
-static PHYSFS_sint64 zip_find_end_of_central_dir(void *in, PHYSFS_sint64 *len)
+static const PHYSFS_Io ZIP_Io =
+{
+    ZIP_read,
+    ZIP_write,
+    ZIP_seek,
+    ZIP_tell,
+    ZIP_length,
+    ZIP_duplicate,
+    ZIP_flush,
+    ZIP_destroy,
+    NULL
+};
+
+
+
+static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
 {
     PHYSFS_uint8 buf[256];
     PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
@@ -373,7 +431,7 @@
     PHYSFS_sint32 totalread = 0;
     int found = 0;
 
-    filelen = __PHYSFS_platformFileLength(in);
+    filelen = io->length(io);
     BAIL_IF_MACRO(filelen == -1, NULL, 0);  /* !!! FIXME: unlocalized string */
     BAIL_IF_MACRO(filelen > 0xFFFFFFFF, "ZIP bigger than 4 gigs?!", 0);
 
@@ -402,19 +460,19 @@
 
     while ((totalread < filelen) && (totalread < 65557))
     {
-        BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, filepos), NULL, -1);
+        BAIL_IF_MACRO(!io->seek(io, filepos), NULL, -1);
 
         /* make sure we catch a signature between buffers. */
         if (totalread != 0)
         {
-            if (!readAll(in, buf, maxread - 4))
+            if (!readAll(io, buf, maxread - 4))
                 return -1;
             memcpy(&buf[maxread - 4], &extra, sizeof (extra));
             totalread += maxread - 4;
         } /* if */
         else
         {
-            if (!readAll(in, buf, maxread))
+            if (!readAll(io, buf, maxread))
                 return -1;
             totalread += maxread;
         } /* else */
@@ -450,21 +508,16 @@
 } /* zip_find_end_of_central_dir */
 
 
-/* !!! FIXME: don't open/close file here, merge with openArchive(). */
-static int isZip(const char *filename)
+static int isZip(PHYSFS_Io *io)
 {
-    PHYSFS_uint32 sig;
+    PHYSFS_uint32 sig = 0;
     int retval = 0;
-    void *in;
-
-    in = __PHYSFS_platformOpenRead(filename);
-    BAIL_IF_MACRO(in == NULL, NULL, 0);
 
     /*
      * The first thing in a zip file might be the signature of the
      *  first local file record, so it makes for a quick determination.
      */
-    if (readui32(in, &sig))
+    if (readui32(io, &sig))
     {
         retval = (sig == ZIP_LOCAL_FILE_SIG);
         if (!retval)
@@ -474,11 +527,10 @@
              *  (a self-extracting executable, etc), so we'll have to do
              *  it the hard way...
              */
-            retval = (zip_find_end_of_central_dir(in, NULL) != -1);
+            retval = (zip_find_end_of_central_dir(io, NULL) != -1);
         } /* if */
     } /* if */
 
-    __PHYSFS_platformClose(in);
     return retval;
 } /* isZip */
 
@@ -622,7 +674,7 @@
 } /* zip_expand_symlink_path */
 
 /* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
-static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry);
+static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
 
 /*
  * Look for the entry named by (path). If it exists, resolve it, and return
@@ -631,7 +683,7 @@
  *  If there's a problem, return NULL. (path) is always free()'d by this
  *  function.
  */
-static ZIPentry *zip_follow_symlink(void *in, ZIPinfo *info, char *path)
+static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
 {
     ZIPentry *entry;
 
@@ -639,7 +691,7 @@
     entry = zip_find_entry(info, path, NULL);
     if (entry != NULL)
     {
-        if (!zip_resolve(in, info, entry))  /* recursive! */
+        if (!zip_resolve(io, info, entry))  /* recursive! */
             entry = NULL;
         else
         {
@@ -653,10 +705,10 @@
 } /* zip_follow_symlink */
 
 
-static int zip_resolve_symlink(void *in, ZIPinfo *info, ZIPentry *entry)
+static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
 {
     char *path;
-    PHYSFS_uint32 size = entry->uncompressed_size;
+    const PHYSFS_uint32 size = entry->uncompressed_size;
     int rc = 0;
 
     /*
@@ -665,22 +717,22 @@
      *  follow it.
      */
 
-    BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0);
+    BAIL_IF_MACRO(!io->seek(io, entry->offset), NULL, 0);
 
     path = (char *) allocator.Malloc(size + 1);
     BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, 0);
     
     if (entry->compression_method == COMPMETH_NONE)
-        rc = readAll(in, path, size);
+        rc = readAll(io, path, size);
 
     else  /* symlink target path is compressed... */
     {
         z_stream stream;
-        PHYSFS_uint32 complen = entry->compressed_size;
+        const PHYSFS_uint32 complen = entry->compressed_size;
         PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
         if (compressed != NULL)
         {
-            if (readAll(in, compressed, complen))
+            if (readAll(io, compressed, complen))
             {
                 initializeZStream(&stream);
                 stream.next_in = compressed;
@@ -706,7 +758,7 @@
     {
         path[entry->uncompressed_size] = '\0';    /* null-terminate it. */
         zip_convert_dos_path(entry, path);
-        entry->symlink = zip_follow_symlink(in, info, path);
+        entry->symlink = zip_follow_symlink(io, info, path);
     } /* else */
 
     return (entry->symlink != NULL);
@@ -716,7 +768,7 @@
 /*
  * Parse the local file header of an entry, and update entry->offset.
  */
-static int zip_parse_local(void *in, ZIPentry *entry)
+static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
 {
     PHYSFS_uint32 ui32;
     PHYSFS_uint16 ui16;
@@ -730,30 +782,30 @@
      *  aren't zero. That seems to work well.
      */
 
-    BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0);
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!io->seek(io, entry->offset), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
     BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0);
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
     BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0);
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);  /* general bits. */
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);  /* general bits. */
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
     BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0);
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);  /* date/time */
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);  /* date/time */
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
     BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), ERR_CORRUPTED, 0);
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
     BAIL_IF_MACRO(ui32 && (ui32 != entry->compressed_size), ERR_CORRUPTED, 0);
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
     BAIL_IF_MACRO(ui32 && (ui32 != entry->uncompressed_size),ERR_CORRUPTED,0);
-    BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0);
-    BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &fnamelen), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &extralen), NULL, 0);
 
     entry->offset += fnamelen + extralen + 30;
     return 1;
 } /* zip_parse_local */
 
 
-static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry)
+static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
 {
     int retval = 1;
     ZipResolveType resolve_type = entry->resolved;
@@ -776,7 +828,7 @@
     {
         entry->resolved = ZIP_RESOLVING;
 
-        retval = zip_parse_local(in, entry);
+        retval = zip_parse_local(io, entry);
         if (retval)
         {
             /*
@@ -785,7 +837,7 @@
              *  the real file) if all goes well.
              */
             if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
-                retval = zip_resolve_symlink(in, info, entry);
+                retval = zip_resolve_symlink(io, info, entry);
         } /* if */
 
         if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
@@ -880,7 +932,7 @@
 } /* zip_dos_time_to_physfs_time */
 
 
-static int zip_load_entry(void *in, ZIPentry *entry, PHYSFS_uint32 ofs_fixup)
+static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixup)
 {
     PHYSFS_uint16 fnamelen, extralen, commentlen;
     PHYSFS_uint32 external_attr;
@@ -889,26 +941,26 @@
     PHYSFS_sint64 si64;
 
     /* sanity check with central directory signature... */
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
     BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0);
 
     /* Get the pertinent parts of the record... */
-    BAIL_IF_MACRO(!readui16(in, &entry->version), NULL, 0);
-    BAIL_IF_MACRO(!readui16(in, &entry->version_needed), NULL, 0);
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);  /* general bits */
-    BAIL_IF_MACRO(!readui16(in, &entry->compression_method), NULL, 0);
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &entry->version), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &entry->version_needed), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);  /* general bits */
+    BAIL_IF_MACRO(!readui16(io, &entry->compression_method), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
     entry->last_mod_time = zip_dos_time_to_physfs_time(ui32);
-    BAIL_IF_MACRO(!readui32(in, &entry->crc), NULL, 0);
-    BAIL_IF_MACRO(!readui32(in, &entry->compressed_size), NULL, 0);
-    BAIL_IF_MACRO(!readui32(in, &entry->uncompressed_size), NULL, 0);
-    BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0);
-    BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0);
-    BAIL_IF_MACRO(!readui16(in, &commentlen), NULL, 0);
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);  /* disk number start */
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);  /* internal file attribs */
-    BAIL_IF_MACRO(!readui32(in, &external_attr), NULL, 0);
-    BAIL_IF_MACRO(!readui32(in, &entry->offset), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &entry->crc), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &entry->compressed_size), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &entry->uncompressed_size), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &fnamelen), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &extralen), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &commentlen), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);  /* disk number start */
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);  /* internal file attribs */
+    BAIL_IF_MACRO(!readui32(io, &external_attr), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &entry->offset), NULL, 0);
     entry->offset += ofs_fixup;
 
     entry->symlink = NULL;  /* will be resolved later, if necessary. */
@@ -917,18 +969,18 @@
 
     entry->name = (char *) allocator.Malloc(fnamelen + 1);
     BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0);
-    if (!readAll(in, entry->name, fnamelen))
+    if (!readAll(io, entry->name, fnamelen))
         goto zip_load_entry_puked;
 
     entry->name[fnamelen] = '\0';  /* null-terminate the filename. */
     zip_convert_dos_path(entry, entry->name);
 
-    si64 = __PHYSFS_platformTell(in);
+    si64 = io->tell(io);
     if (si64 == -1)
         goto zip_load_entry_puked;
 
         /* seek to the start of the next entry in the central directory... */
-    if (!__PHYSFS_platformSeek(in, si64 + extralen + commentlen))
+    if (!io->seek(io, si64 + extralen + commentlen))
         goto zip_load_entry_puked;
 
     return 1;  /* success. */
@@ -965,20 +1017,20 @@
 } /* zip_entry_swap */
 
 
-static int zip_load_entries(void *in, ZIPinfo *info,
+static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info,
                             PHYSFS_uint32 data_ofs, PHYSFS_uint32 central_ofs)
 {
     PHYSFS_uint32 max = info->entryCount;
     PHYSFS_uint32 i;
 
-    BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, central_ofs), NULL, 0);
+    BAIL_IF_MACRO(!io->seek(io, central_ofs), NULL, 0);
 
     info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max);
     BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
 
     for (i = 0; i < max; i++)
     {
-        if (!zip_load_entry(in, &info->entries[i], data_ofs))
+        if (!zip_load_entry(io, &info->entries[i], data_ofs))
         {
             zip_free_entries(info->entries, i);
             return 0;
@@ -990,7 +1042,7 @@
 } /* zip_load_entries */
 
 
-static int zip_parse_end_of_central_dir(void *in, ZIPinfo *info,
+static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
                                         PHYSFS_uint32 *data_start,
                                         PHYSFS_uint32 *central_dir_ofs)
 {
@@ -1000,34 +1052,34 @@
     PHYSFS_sint64 pos;
 
     /* find the end-of-central-dir record, and seek to it. */
-    pos = zip_find_end_of_central_dir(in, &len);
+    pos = zip_find_end_of_central_dir(io, &len);
     BAIL_IF_MACRO(pos == -1, NULL, 0);
-    BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, pos), NULL, 0);
+    BAIL_IF_MACRO(!io->seek(io, pos), NULL, 0);
 
     /* check signature again, just in case. */
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
     BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, ERR_NOT_AN_ARCHIVE, 0);
 
     /* number of this disk */
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
     BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0);
 
     /* number of the disk with the start of the central directory */
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
     BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0);
 
     /* total number of entries in the central dir on this disk */
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
 
     /* total number of entries in the central dir */
-    BAIL_IF_MACRO(!readui16(in, &info->entryCount), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &info->entryCount), NULL, 0);
     BAIL_IF_MACRO(ui16 != info->entryCount, ERR_UNSUPPORTED_ARCHIVE, 0);
 
     /* size of the central directory */
-    BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, &ui32), NULL, 0);
 
     /* offset of central directory */
-    BAIL_IF_MACRO(!readui32(in, central_dir_ofs), NULL, 0);
+    BAIL_IF_MACRO(!readui32(io, central_dir_ofs), NULL, 0);
     BAIL_IF_MACRO(pos < *central_dir_ofs + ui32, ERR_UNSUPPORTED_ARCHIVE, 0);
 
     /*
@@ -1044,7 +1096,7 @@
     *central_dir_ofs += *data_start;
 
     /* zipfile comment length */
-    BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+    BAIL_IF_MACRO(!readui16(io, &ui16), NULL, 0);
 
     /*
      * Make sure that the comment length matches to the end of file...
@@ -1057,61 +1109,33 @@
 } /* zip_parse_end_of_central_dir */
 
 
-static ZIPinfo *zip_create_zipinfo(const char *name)
+static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
 {
-    char *ptr;
-    ZIPinfo *info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
-    BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
-    memset(info, '\0', sizeof (ZIPinfo));
-
-    ptr = (char *) allocator.Malloc(strlen(name) + 1);
-    if (ptr == NULL)
-    {
-        allocator.Free(info);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
-    } /* if */
-
-    info->archiveName = ptr;
-    strcpy(info->archiveName, name);
-    return info;
-} /* zip_create_zipinfo */
-
-
-static void *ZIP_openArchive(const char *name, int forWriting)
-{
-    void *in = NULL;
     ZIPinfo *info = NULL;
     PHYSFS_uint32 data_start;
     PHYSFS_uint32 cent_dir_ofs;
 
+    assert(io != NULL);  /* shouldn't ever happen. */
+
     BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
-    BAIL_IF_MACRO(!isZip(name), NULL, NULL);
+    BAIL_IF_MACRO(!isZip(io), NULL, NULL);
 
-    if ((in = __PHYSFS_platformOpenRead(name)) == NULL)
-        goto zip_openarchive_failed;
-    
-    if ((info = zip_create_zipinfo(name)) == NULL)
-        goto zip_openarchive_failed;
+    info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
+    BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
+    memset(info, '\0', sizeof (ZIPinfo));
+    info->io = io;
 
-    if (!zip_parse_end_of_central_dir(in, info, &data_start, &cent_dir_ofs))
-        goto zip_openarchive_failed;
+    if (!zip_parse_end_of_central_dir(io, info, &data_start, &cent_dir_ofs))
+        goto ZIP_openarchive_failed;
 
-    if (!zip_load_entries(in, info, data_start, cent_dir_ofs))
-        goto zip_openarchive_failed;
+    if (!zip_load_entries(io, info, data_start, cent_dir_ofs))
+        goto ZIP_openarchive_failed;
 
-    __PHYSFS_platformClose(in);
     return info;
 
-zip_openarchive_failed:
+ZIP_openarchive_failed:
     if (info != NULL)
-    {
-        if (info->archiveName != NULL)
-            allocator.Free(info->archiveName);
         allocator.Free(info);
-    } /* if */
-
-    if (in != NULL)
-        __PHYSFS_platformClose(in);
 
     return NULL;
 } /* ZIP_openArchive */
@@ -1257,12 +1281,7 @@
 
     if (entry->resolved == ZIP_UNRESOLVED_SYMLINK) /* gotta resolve it. */
     {
-        int rc;
-        void *in = __PHYSFS_platformOpenRead(info->archiveName);
-        BAIL_IF_MACRO(in == NULL, NULL, 0);
-        rc = zip_resolve(in, info, entry);
-        __PHYSFS_platformClose(in);
-        if (!rc)
+        if (!zip_resolve(info->io, info, entry))
             return 0;
     } /* if */
 
@@ -1283,81 +1302,95 @@
 } /* ZIP_isSymLink */
 
 
-static void *zip_get_file_handle(const char *fn, ZIPinfo *inf, ZIPentry *entry)
+static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry)
 {
     int success;
-    void *retval = __PHYSFS_platformOpenRead(fn);
+    PHYSFS_Io *retval = io->duplicate(io);
     BAIL_IF_MACRO(retval == NULL, NULL, NULL);
 
-    success = zip_resolve(retval, inf, entry);
+    /* (inf) can be NULL if we already resolved. */
+    success = (inf == NULL) || zip_resolve(retval, inf, entry);
     if (success)
     {
         PHYSFS_sint64 offset;
         offset = ((entry->symlink) ? entry->symlink->offset : entry->offset);
-        success = __PHYSFS_platformSeek(retval, offset);
+        success = retval->seek(retval, offset);
     } /* if */
 
     if (!success)
     {
-        __PHYSFS_platformClose(retval);
+        retval->destroy(retval);
         retval = NULL;
     } /* if */
 
     return retval;
-} /* zip_get_file_handle */
+} /* zip_get_io */
 
 
-static fvoid *ZIP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+static PHYSFS_Io *ZIP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
 {
+    PHYSFS_Io *retval = NULL;
     ZIPinfo *info = (ZIPinfo *) opaque;
     ZIPentry *entry = zip_find_entry(info, fnm, NULL);
     ZIPfileinfo *finfo = NULL;
-    void *in;
 
     *fileExists = (entry != NULL);
     BAIL_IF_MACRO(entry == NULL, NULL, NULL);
 
-    in = zip_get_file_handle(info->archiveName, info, entry);
-    BAIL_IF_MACRO(in == NULL, NULL, NULL);
+    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GOTO_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
 
     finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
-    if (finfo == NULL)
-    {
-        __PHYSFS_platformClose(in);
-        BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
-    } /* if */
+    GOTO_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
+    memset(finfo, '\0', sizeof (ZIPfileinfo));
 
-    memset(finfo, '\0', sizeof (ZIPfileinfo));
-    finfo->handle = in;
+    finfo->io = zip_get_io(info->io, info, entry);
+    GOTO_IF_MACRO(finfo->io == NULL, NULL, ZIP_openRead_failed);
     finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry);
     initializeZStream(&finfo->stream);
+
     if (finfo->entry->compression_method != COMPMETH_NONE)
     {
+        finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
+        GOTO_IF_MACRO(!finfo->buffer, ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
         if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
+            goto ZIP_openRead_failed;
+    } /* if */
+
+    memcpy(retval, &ZIP_Io, sizeof (PHYSFS_Io));
+    retval->opaque = finfo;
+
+    return retval;
+
+ZIP_openRead_failed:
+    if (finfo != NULL)
+    {
+        if (finfo->io != NULL)
+            finfo->io->destroy(finfo->io);
+
+        if (finfo->buffer != NULL)
         {
-            ZIP_fileClose(finfo);
-            return NULL;
+            allocator.Free(finfo->buffer);
+            inflateEnd(&finfo->stream);
         } /* if */
 
-        finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
-        if (finfo->buffer == NULL)
-        {
-            ZIP_fileClose(finfo);
-            BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
-        } /* if */
+        allocator.Free(finfo);
     } /* if */
 
-    return finfo;
+    if (retval != NULL)
+        allocator.Free(retval);
+
+    return NULL;
 } /* ZIP_openRead */
 
 
-static fvoid *ZIP_openWrite(dvoid *opaque, const char *filename)
+static PHYSFS_Io *ZIP_openWrite(dvoid *opaque, const char *filename)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* ZIP_openWrite */
 
 
-static fvoid *ZIP_openAppend(dvoid *opaque, const char *filename)
+static PHYSFS_Io *ZIP_openAppend(dvoid *opaque, const char *filename)
 {
     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
 } /* ZIP_openAppend */
@@ -1366,8 +1399,8 @@
 static void ZIP_dirClose(dvoid *opaque)
 {
     ZIPinfo *zi = (ZIPinfo *) (opaque);
+    zi->io->destroy(zi->io);
     zip_free_entries(zi->entries, zi->entryCount);
-    allocator.Free(zi->archiveName);
     allocator.Free(zi);
 } /* ZIP_dirClose */
 
@@ -1445,14 +1478,7 @@
     ZIP_remove,             /* remove() method         */
     ZIP_mkdir,              /* mkdir() method          */
     ZIP_dirClose,           /* dirClose() method       */
-    ZIP_stat,               /* stat() method           */
-    ZIP_read,               /* read() method           */
-    ZIP_write,              /* write() method          */
-    ZIP_eof,                /* eof() method            */
-    ZIP_tell,               /* tell() method           */
-    ZIP_seek,               /* seek() method           */
-    ZIP_fileLength,         /* fileLength() method     */
-    ZIP_fileClose           /* fileClose() method      */
+    ZIP_stat                /* stat() method           */
 };
 
 #endif  /* defined PHYSFS_SUPPORTS_ZIP */
--- a/src/physfs.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/physfs.c	Mon Aug 30 03:01:57 2010 -0400
@@ -31,10 +31,9 @@
 
 typedef struct __PHYSFS_FILEHANDLE__
 {
-    void *opaque;  /* Instance data unique to the archiver for this file. */
+    PHYSFS_Io *io;  /* Instance data unique to the archiver for this file. */
     PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */
     const DirHandle *dirHandle;  /* Archiver instance that created this */
-    const PHYSFS_Archiver *funcs;  /* Ptr to archiver info for this handle. */
     PHYSFS_uint8 *buffer;  /* Buffer, if set (NULL otherwise). Don't touch! */
     PHYSFS_uint32 bufsize;  /* Bufsize, if set (0 otherwise). Don't touch! */
     PHYSFS_uint32 buffill;  /* Buffer fill size. Don't touch! */
@@ -152,6 +151,122 @@
 PHYSFS_Allocator allocator;
 
 
+/* PHYSFS_Io implementation for i/o to physical filesystem... */
+
+/* !!! FIXME: maybe refcount the paths in a string pool? */
+typedef struct __PHYSFS_NativeIoInfo
+{
+    void *handle;
+    const char *path;
+    int mode;   /* 'r', 'w', or 'a' */
+} NativeIoInfo;
+
+static PHYSFS_sint64 nativeIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
+{
+    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
+    return __PHYSFS_platformRead(info->handle, buf, len);
+} /* nativeIo_read */
+
+static PHYSFS_sint64 nativeIo_write(PHYSFS_Io *io, const void *buffer,
+                                    PHYSFS_uint64 len)
+{
+    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
+    return __PHYSFS_platformWrite(info->handle, buffer, len);
+} /* nativeIo_write */
+
+static int nativeIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
+{
+    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
+    return __PHYSFS_platformSeek(info->handle, offset);
+} /* nativeIo_seek */
+
+static PHYSFS_sint64 nativeIo_tell(PHYSFS_Io *io)
+{
+    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
+    return __PHYSFS_platformTell(info->handle);
+} /* nativeIo_tell */
+
+static PHYSFS_sint64 nativeIo_length(PHYSFS_Io *io)
+{
+    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
+    return __PHYSFS_platformFileLength(info->handle);
+} /* nativeIo_length */
+
+static PHYSFS_Io *nativeIo_duplicate(PHYSFS_Io *io)
+{
+    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
+    return __PHYSFS_createNativeIo(info->path, info->mode);
+} /* nativeIo_duplicate */
+
+static int nativeIo_flush(PHYSFS_Io *io)
+{
+    return __PHYSFS_platformFlush(io->opaque);
+} /* nativeIo_flush */
+
+static void nativeIo_destroy(PHYSFS_Io *io)
+{
+    NativeIoInfo *info = (NativeIoInfo *) io->opaque;
+    __PHYSFS_platformClose(info->handle);
+    allocator.Free((void *) info->path);
+    allocator.Free(info);
+    allocator.Free(io);
+} /* nativeIo_destroy */
+
+static const PHYSFS_Io __PHYSFS_nativeIoInterface =
+{
+    nativeIo_read,
+    nativeIo_write,
+    nativeIo_seek,
+    nativeIo_tell,
+    nativeIo_length,
+    nativeIo_duplicate,
+    nativeIo_flush,
+    nativeIo_destroy,
+    NULL
+};
+
+PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode)
+{
+    PHYSFS_Io *io = NULL;
+    NativeIoInfo *info = NULL;
+    void *handle = NULL;
+    char *pathdup = NULL;
+
+    assert((mode == 'r') || (mode == 'w') || (mode == 'a'));
+
+    io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
+    GOTO_IF_MACRO(io == NULL, ERR_OUT_OF_MEMORY, createNativeIo_failed);
+    info = (NativeIoInfo *) allocator.Malloc(sizeof (NativeIoInfo));
+    GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, createNativeIo_failed);
+    pathdup = (char *) allocator.Malloc(strlen(path) + 1);
+    GOTO_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, createNativeIo_failed);
+
+    if (mode == 'r')
+        handle = __PHYSFS_platformOpenRead(path);
+    else if (mode == 'w')
+        handle = __PHYSFS_platformOpenWrite(path);
+    else if (mode == 'a')
+        handle = __PHYSFS_platformOpenAppend(path);
+
+    GOTO_IF_MACRO(handle == NULL, NULL, createNativeIo_failed);
+
+    strcpy(pathdup, path);
+    info->handle = handle;
+    info->path = pathdup;
+    info->mode = mode;
+    memcpy(io, &__PHYSFS_nativeIoInterface, sizeof (*io));
+    io->opaque = info;
+    return io;
+
+createNativeIo_failed:
+    if (handle != NULL) __PHYSFS_platformClose(handle);
+    if (pathdup != NULL) allocator.Free(pathdup);
+    if (info != NULL) allocator.Free(info);
+    if (io != NULL) allocator.Free(io);
+    return NULL;
+} /* __PHYSFS_createNativeIo */
+
+
 /* functions ... */
 
 typedef struct
@@ -379,28 +494,37 @@
 
 static const char *find_filename_extension(const char *fname)
 {
-    const char *retval = strchr(fname, '.');
-    const char *p = retval;
-
-    while (p != NULL)
+    const char *retval = NULL;
+    if (fname != NULL)
     {
-        p = strchr(p + 1, '.');
-        if (p != NULL)
-            retval = p;
-    } /* while */
-
-    if (retval != NULL)
-        retval++;  /* skip '.' */
+        retval = strchr(fname, '.');
+        const char *p = retval;
+
+        while (p != NULL)
+        {
+            p = strchr(p + 1, '.');
+            if (p != NULL)
+                retval = p;
+        } /* while */
+
+        if (retval != NULL)
+            retval++;  /* skip '.' */
+    } /* if */
 
     return retval;
 } /* find_filename_extension */
 
 
-static DirHandle *tryOpenDir(const PHYSFS_Archiver *funcs,
+static DirHandle *tryOpenDir(PHYSFS_Io *io, const PHYSFS_Archiver *funcs,
                              const char *d, int forWriting)
 {
     DirHandle *retval = NULL;
-    void *opaque = funcs->openArchive(d, forWriting);
+    void *opaque = NULL;
+
+    if (io != NULL)
+        BAIL_IF_MACRO(!io->seek(io, 0), NULL, NULL);
+
+    opaque = funcs->openArchive(io, d, forWriting);
     if (opaque != NULL)
     {
         retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle));
@@ -419,18 +543,26 @@
 } /* tryOpenDir */
 
 
-static DirHandle *openDirectory(const char *d, int forWriting)
+static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
 {
     DirHandle *retval = NULL;
     const PHYSFS_Archiver **i;
     const char *ext;
 
-    BAIL_IF_MACRO(!__PHYSFS_platformExists(d), ERR_NO_SUCH_FILE, NULL);
-
-    /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
-    retval = tryOpenDir(&__PHYSFS_Archiver_DIR, d, forWriting);
-    if (retval != NULL)
-        return retval;
+    assert((io != NULL) || (d != NULL));
+
+    if (io == NULL)
+    {
+        BAIL_IF_MACRO(!__PHYSFS_platformExists(d), ERR_NO_SUCH_FILE, NULL);
+
+        /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
+        retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting);
+        if (retval != NULL)
+            return retval;
+
+        io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
+        BAIL_IF_MACRO_MUTEX(io == NULL, NULL, stateLock, 0);
+    } /* if */
 
     ext = find_filename_extension(d);
     if (ext != NULL)
@@ -439,21 +571,21 @@
         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
         {
             if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) == 0)
-                retval = tryOpenDir(*i, d, forWriting);
+                retval = tryOpenDir(io, *i, d, forWriting);
         } /* for */
 
         /* failing an exact file extension match, try all the others... */
         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
         {
             if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) != 0)
-                retval = tryOpenDir(*i, d, forWriting);
+                retval = tryOpenDir(io, *i, d, forWriting);
         } /* for */
     } /* if */
 
     else  /* no extension? Try them all. */
     {
         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
-            retval = tryOpenDir(*i, d, forWriting);
+            retval = tryOpenDir(io, *i, d, forWriting);
     } /* else */
 
     BAIL_IF_MACRO(retval == NULL, ERR_UNSUPPORTED_ARCHIVE, NULL);
@@ -545,14 +677,12 @@
 } /* partOfMountPoint */
 
 
-static DirHandle *createDirHandle(const char *newDir,
-                                  const char *mountPoint,
-                                  int forWriting)
+static DirHandle *createDirHandle(PHYSFS_Io *io, const char *newDir,
+                                  const char *mountPoint, int forWriting)
 {
     DirHandle *dirHandle = NULL;
     char *tmpmntpnt = NULL;
 
-    GOTO_IF_MACRO(!newDir, ERR_INVALID_ARGUMENT, badDirHandle);
     if (mountPoint != NULL)
     {
         const size_t len = strlen(mountPoint) + 1;
@@ -563,12 +693,17 @@
         mountPoint = tmpmntpnt;  /* sanitized version. */
     } /* if */
 
-    dirHandle = openDirectory(newDir, forWriting);
+    dirHandle = openDirectory(io, newDir, forWriting);
     GOTO_IF_MACRO(!dirHandle, NULL, badDirHandle);
 
-    dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
-    GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle);
-    strcpy(dirHandle->dirName, newDir);
+    if (newDir == NULL)
+        dirHandle->dirName = NULL;
+    else
+    {
+        dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
+        GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle);
+        strcpy(dirHandle->dirName, newDir);
+    } /* else */
 
     if ((mountPoint != NULL) && (*mountPoint != '\0'))
     {
@@ -791,13 +926,16 @@
 
     for (i = *list; i != NULL; i = next)
     {
+        PHYSFS_Io *io = i->io;
         next = i->next;
-        if (!i->funcs->fileClose(i->opaque))
+
+        if (!io->flush(io))
         {
             *list = i;
             return 0;
         } /* if */
 
+        io->destroy(io);
         allocator.Free(i);
     } /* for */
 
@@ -946,7 +1084,8 @@
 
     if (newDir != NULL)
     {
-        writeDir = createDirHandle(newDir, NULL, 1);
+        /* !!! FIXME: PHYSFS_Io shouldn't be NULL */
+        writeDir = createDirHandle(NULL, newDir, NULL, 1);
         retval = (writeDir != NULL);
     } /* if */
 
@@ -976,7 +1115,7 @@
         prev = i;
     } /* for */
 
-    dh = createDirHandle(newDir, mountPoint, 0);
+    dh = createDirHandle(NULL, newDir, mountPoint, 0);
     BAIL_IF_MACRO_MUTEX(dh == NULL, NULL, stateLock, 0);
 
     if (appendToPath)
@@ -1735,7 +1874,7 @@
 
     if (sanitizePlatformIndependentPath(_fname, fname))
     {
-        void *opaque = NULL;
+        PHYSFS_Io *io = NULL;
         DirHandle *h = NULL;
         const PHYSFS_Archiver *f;
 
@@ -1748,24 +1887,23 @@
 
         f = h->funcs;
         if (appending)
-            opaque = f->openAppend(h->opaque, fname);
+            io = f->openAppend(h->opaque, fname);
         else
-            opaque = f->openWrite(h->opaque, fname);
-
-        GOTO_IF_MACRO(opaque == NULL, NULL, doOpenWriteEnd);
+            io = f->openWrite(h->opaque, fname);
+
+        GOTO_IF_MACRO(io == NULL, NULL, doOpenWriteEnd);
 
         fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
         if (fh == NULL)
         {
-            f->fileClose(opaque);
+            io->destroy(io);
             GOTO_MACRO(ERR_OUT_OF_MEMORY, doOpenWriteEnd);
         } /* if */
         else
         {
             memset(fh, '\0', sizeof (FileHandle));
-            fh->opaque = opaque;
+            fh->io = io;
             fh->dirHandle = h;
-            fh->funcs = h->funcs;
             fh->next = openWriteList;
             openWriteList = fh;
         } /* else */
@@ -1806,7 +1944,7 @@
     {
         int fileExists = 0;
         DirHandle *i = NULL;
-        fvoid *opaque = NULL;
+        PHYSFS_Io *io = NULL;
 
         __PHYSFS_platformGrabMutex(stateLock);
 
@@ -1820,28 +1958,27 @@
             char *arcfname = fname;
             if (verifyPath(i, &arcfname, 0))
             {
-                opaque = i->funcs->openRead(i->opaque, arcfname, &fileExists);
-                if (opaque)
+                io = i->funcs->openRead(i->opaque, arcfname, &fileExists);
+                if (io)
                     break;
             } /* if */
             i = i->next;
         } while ((i != NULL) && (!fileExists));
 
         /* !!! FIXME: may not set an error if openRead didn't fail. */
-        GOTO_IF_MACRO(opaque == NULL, NULL, openReadEnd);
+        GOTO_IF_MACRO(io == NULL, NULL, openReadEnd);
 
         fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
         if (fh == NULL)
         {
-            i->funcs->fileClose(opaque);
+            io->destroy(io);
             GOTO_MACRO(ERR_OUT_OF_MEMORY, openReadEnd);
         } /* if */
 
         memset(fh, '\0', sizeof (FileHandle));
-        fh->opaque = opaque;
+        fh->io = io;
         fh->forReading = 1;
         fh->dirHandle = i;
-        fh->funcs = i->funcs;
         fh->next = openReadList;
         openReadList = fh;
 
@@ -1864,12 +2001,12 @@
     {
         if (i == handle)  /* handle is in this list? */
         {
+            PHYSFS_Io *io = handle->io;
             PHYSFS_uint8 *tmp = handle->buffer;
             rc = PHYSFS_flush((PHYSFS_File *) handle);
-            if (rc)
-                rc = handle->funcs->fileClose(handle->opaque);
             if (!rc)
                 return -1;
+            io->destroy(io);
 
             if (tmp != NULL)  /* free any associated buffer. */
                 allocator.Free(tmp);
@@ -1914,6 +2051,7 @@
 static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *buffer,
                                     PHYSFS_uint64 len)
 {
+    PHYSFS_Io *io = NULL;
     PHYSFS_sint64 retval = 0;
     PHYSFS_uint32 buffered = 0;
     PHYSFS_sint64 rc = 0;
@@ -1942,17 +2080,18 @@
     assert(buffered == 0);
     assert(len > 0);
 
+    io = fh->io;
     if (len >= fh->bufsize)  /* need more than the buffer takes. */
     {
         /* leave buffer empty, go right to output instead. */
-        rc = fh->funcs->read(fh->opaque, buffer, len);
+        rc = io->read(io, buffer, len);
         if (rc < 0)
             return ((retval == 0) ? rc : retval);
         return retval + rc;
     } /* if */
 
     /* need less than buffer can take. Fill buffer. */
-    rc = fh->funcs->read(fh->opaque, fh->buffer, fh->bufsize);
+    rc = io->read(io, fh->buffer, fh->bufsize);
     if (rc < 0)
         return ((retval == 0) ? rc : retval);
 
@@ -1993,7 +2132,7 @@
     if (fh->buffer != NULL)
         return doBufferedRead(fh, buffer, len);
 
-    return fh->funcs->read(fh->opaque, buffer, len);
+    return fh->io->read(fh->io, buffer, len);
 } /* PHYSFS_readBytes */
 
 
@@ -2012,7 +2151,7 @@
 
     /* would overflow buffer. Flush and then write the new objects, too. */
     BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, -1);
-    return fh->funcs->write(fh->opaque, buffer, len);
+    return fh->io->write(fh->io, buffer, len);
 } /* doBufferedWrite */
 
 
@@ -2043,7 +2182,7 @@
     if (fh->buffer != NULL)
         return doBufferedWrite(handle, buffer, len);
 
-    return fh->funcs->write(fh->opaque, buffer, len);
+    return fh->io->write(fh->io, buffer, len);
 } /* PHYSFS_write */
 
 
@@ -2054,18 +2193,29 @@
     if (!fh->forReading)  /* never EOF on files opened for write/append. */
         return 0;
 
-    /* eof if buffer is empty and archiver says so. */
-    return (fh->bufpos == fh->buffill && (fh->funcs->eof(fh->opaque)));
+    /* can't be eof if buffer isn't empty */
+    if (fh->bufpos == fh->buffill)
+    {
+        /* check the Io. */
+        PHYSFS_Io *io = fh->io;
+        const PHYSFS_sint64 pos = io->tell(io);
+        const PHYSFS_sint64 len = io->length(io);
+        if ((pos < 0) || (len < 0))
+            return 0;  /* beats me. */
+        return (pos >= len);
+    } /* if */
+
+    return 0;
 } /* PHYSFS_eof */
 
 
 PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle)
 {
     FileHandle *fh = (FileHandle *) handle;
-    PHYSFS_sint64 pos = fh->funcs->tell(fh->opaque);
-    PHYSFS_sint64 retval = fh->forReading ?
-                            (pos - fh->buffill) + fh->bufpos :
-                            (pos + fh->buffill);
+    const PHYSFS_sint64 pos = fh->io->tell(fh->io);
+    const PHYSFS_sint64 retval = fh->forReading ?
+                                 (pos - fh->buffill) + fh->bufpos :
+                                 (pos + fh->buffill);
     return retval;
 } /* PHYSFS_tell */
 
@@ -2090,14 +2240,14 @@
 
     /* we have to fall back to a 'raw' seek. */
     fh->buffill = fh->bufpos = 0;
-    return fh->funcs->seek(fh->opaque, pos);
+    return fh->io->seek(fh->io, pos);
 } /* PHYSFS_seek */
 
 
 PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle)
 {
-    FileHandle *fh = (FileHandle *) handle;
-    return fh->funcs->fileLength(fh->opaque);
+    PHYSFS_Io *io = ((FileHandle *) handle)->io;
+    return io->length(io);
 } /* PHYSFS_filelength */
 
 
@@ -2121,10 +2271,10 @@
     if ((fh->forReading) && (fh->buffill != fh->bufpos))
     {
         PHYSFS_uint64 pos;
-        PHYSFS_sint64 curpos = fh->funcs->tell(fh->opaque);
+        const PHYSFS_sint64 curpos = fh->io->tell(fh->io);
         BAIL_IF_MACRO(curpos == -1, NULL, 0);
         pos = ((curpos - fh->buffill) + fh->bufpos);
-        BAIL_IF_MACRO(!fh->funcs->seek(fh->opaque, pos), NULL, 0);
+        BAIL_IF_MACRO(!fh->io->seek(fh->io, pos), NULL, 0);
     } /* if */
 
     if (bufsize == 0)  /* delete existing buffer. */
@@ -2153,17 +2303,18 @@
 int PHYSFS_flush(PHYSFS_File *handle)
 {
     FileHandle *fh = (FileHandle *) handle;
+    PHYSFS_Io *io;
     PHYSFS_sint64 rc;
 
     if ((fh->forReading) || (fh->bufpos == fh->buffill))
         return 1;  /* open for read or buffer empty are successful no-ops. */
 
     /* dump buffer to disk. */
-    rc = fh->funcs->write(fh->opaque, fh->buffer + fh->bufpos,
-                          fh->buffill - fh->bufpos);
+    io = fh->io;
+    rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
     BAIL_IF_MACRO(rc <= 0, NULL, 0);
     fh->bufpos = fh->buffill = 0;
-    return 1;
+    return io->flush(io);
 } /* PHYSFS_flush */
 
 
--- a/src/physfs.h	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/physfs.h	Mon Aug 30 03:01:57 2010 -0400
@@ -1262,7 +1262,7 @@
  *             function just wraps it anyhow. This function never clarified
  *             what would happen if you managed to read a partial object, so
  *             working at the byte level makes this cleaner for everyone,
- *             especially now that data streams can be supplied by the
+ *             especially now that PHYSFS_Io interfaces can be supplied by the
  *             application.
  *
  *   \param handle handle returned from PHYSFS_openRead().
@@ -1292,7 +1292,7 @@
  *             function just wraps it anyhow. This function never clarified
  *             what would happen if you managed to write a partial object, so
  *             working at the byte level makes this cleaner for everyone,
- *             especially now that data streams can be supplied by the
+ *             especially now that PHYSFS_Io interfaces can be supplied by the
  *             application.
  *
  *   \param handle retval from PHYSFS_openWrite() or PHYSFS_openAppend().
@@ -1362,11 +1362,9 @@
  * \fn PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle)
  * \brief Get total length of a file in bytes.
  *
- * Note that if the file size can't be determined (since the archive is
- *  "streamed" or whatnot) than this will report (-1). Also note that if
- *  another process/thread is writing to this file at the same time, then
- *  the information this function supplies could be incorrect before you
- *  get it. Use with caution, or better yet, don't use at all.
+ * Note that if another process/thread is writing to this file at the same
+ *  time, then the information this function supplies could be incorrect
+ *  before you get it. Use with caution, or better yet, don't use at all.
  *
  *   \param handle handle returned from PHYSFS_open*().
  *  \return size in bytes of the file. -1 if can't be determined.
@@ -2149,6 +2147,7 @@
  * \sa PHYSFS_removeFromSearchPath
  * \sa PHYSFS_getSearchPath
  * \sa PHYSFS_getMountPoint
+ * \sa PHYSFS_mountIo
  */
 PHYSFS_DECL int PHYSFS_mount(const char *newDir,
                              const char *mountPoint,
@@ -2719,6 +2718,184 @@
                                             PHYSFS_uint64 len);
 
 
+/**
+ * \struct PHYSFS_Io
+ * \brief An abstract i/o interface.
+ *
+ * \warning This is advanced, hardcore stuff. You don't need this unless you
+ *          really know what you're doing. Most apps will not need this.
+ *
+ * Historically, PhysicsFS provided access to the physical filesystem and
+ *  archives within that filesystem. However, sometimes you need more power
+ *  than this. Perhaps you need to provide an archive that is entirely
+ *  contained in RAM, or you need to brige some other file i/o API to
+ *  PhysicsFS, or you need to translate the bits (perhaps you have a
+ *  a standard .zip file that's encrypted, and you need to decrypt on the fly
+ *  for the unsuspecting zip archiver).
+ *
+ * A PHYSFS_Io is the interface that Archivers use to get archive data.
+ *  Historically, this has mapped to file i/o to the physical filesystem, but
+ *  as of PhysicsFS 2.1, applications can provide their own i/o implementations
+ *  at runtime.
+ *
+ * This interface isn't necessarily a good universal fit for i/o. There are a
+ *  few requirements of note:
+ *
+ *  - They only do blocking i/o (at least, for now).
+ *  - They need to be able to duplicate. If you have a file handle from
+ *    fopen(), you need to be able to create a unique clone of it (so we
+ *    have two handles to the same file that can both seek/read/etc without
+ *    stepping on each other).
+ *  - They need to know the size of their entire data set.
+ *  - They need to be able to seek and rewind on demand.
+ *
+ * ...in short, you're probably not going to write an HTTP implementation.
+ *
+ * Thread safety: TO BE DECIDED.  !!! FIXME
+ *
+ * \sa PHYSFS_mountIo
+ */
+typedef struct PHYSFS_Io
+{
+    /**
+     * \brief Read more data.
+     *
+     * Read (len) bytes from the interface, at the current i/o position, and
+     *  store them in (buffer). The current i/o position should move ahead
+     *  by the number of bytes successfully read.
+     *
+     * You don't have to implement this; set it to NULL if not implemented.
+     *  This will only be used if the file is opened for reading. If set to
+     *  NULL, a default implementation that immediately reports failure will
+     *  be used.
+     *
+     *   \param io The i/o instance to read from.
+     *   \param buf The buffer to store data into. It must be at least
+     *                 (len) bytes long and can't be NULL.
+     *   \param len The number of bytes to read from the interface.
+     *  \return number of bytes read from file, 0 on EOF, -1 if complete
+     *          failure.
+     */
+    PHYSFS_sint64 (*read)(struct PHYSFS_Io *io, void *buf, PHYSFS_uint64 len);
+
+    /**
+     * \brief Write more data.
+     *
+     * Write (len) bytes from (buffer) to the interface at the current i/o
+     *  position. The current i/o position should move ahead by the number of
+     *  bytes successfully written.
+     *
+     * You don't have to implement this; set it to NULL if not implemented.
+     *  This will only be used if the file is opened for writing. If set to
+     *  NULL, a default implementation that immediately reports failure will
+     *  be used.
+     *
+     * You are allowed to buffer; a write can succeed here and then later
+     *  fail when flushing. Note that PHYSFS_setBuffer() may be operating a
+     *  level above your i/o, so you should usually not implement your
+     *  own buffering routines.
+     *
+     *   \param io The i/o instance to write to.
+     *   \param buffer The buffer to read data from. It must be at least
+     *                 (len) bytes long and can't be NULL.
+     *   \param len The number of bytes to read from (buffer).
+     *  \return number of bytes written to file, -1 if complete failure.
+     */
+    PHYSFS_sint64 (*write)(struct PHYSFS_Io *io, const void *buffer,
+                           PHYSFS_uint64 len);
+
+    /**
+     * \brief Move i/o position to a given byte offset from start.
+     *
+     * This method moves the i/o position, so the next read/write will
+     *  be of the byte at (offset) offset. Seeks past the end of file should
+     *  be treated as an error condition.
+     *
+     *   \param io The i/o instance to seek.
+     *   \param offset The new byte offset for the i/o position.
+     *  \return non-zero on success, zero on error.
+     */
+    int (*seek)(struct PHYSFS_Io *io, PHYSFS_uint64 offset);
+
+    /**
+     * \brief Report current i/o position.
+     *
+     * Return bytes offset, or -1 if you aren't able to determine. A failure
+     *  will almost certainly be fatal to further use of this stream, so you
+     *  may not leave this unimplemented.
+     *
+     *   \param io The i/o instance to query.
+     *  \return The current byte offset for the i/o position, -1 if unknown.
+     */
+    PHYSFS_sint64 (*tell)(struct PHYSFS_Io *io);
+
+    /**
+     * \brief Determine size of the i/o instance's dataset.
+     *
+     * Return number of bytes available in the file, or -1 if you
+     *  aren't able to determine. A failure will almost certainly be fatal
+     *  to further use of this stream, so you may not leave this unimplemented.
+     *
+     *   \param io The i/o instance to query.
+     *  \return Total size, in bytes, of the dataset.
+     */
+    PHYSFS_sint64 (*length)(struct PHYSFS_Io *io);
+
+    /**
+     * \brief Duplicate this i/o instance.
+     *
+     *  // !!! FIXME: write me.
+     *
+     *   \param io The i/o instance to duplicate.
+     *  \return A new value for a stream's (opaque) field, or NULL on error.
+     */
+    struct PHYSFS_Io *(*duplicate)(struct PHYSFS_Io *io);
+
+    /**
+     * \brief Flush resources to media, or wherever.
+     *
+     * This is the chance to report failure for writes that had claimed
+     *  success earlier, but still had a chance to actually fail. This method
+     *  can be NULL if flushing isn't necessary.
+     *
+     * This function may be called before destroy(), as it can report failure
+     *  and destroy() can not. It may be called at other times, too.
+     *
+     *   \param io The i/o instance to flush.
+     *  \return Zero on error, non-zero on success.
+     */
+    int (*flush)(struct PHYSFS_Io *io);
+
+    /**
+     * \brief Cleanup and deallocate i/o instance.
+     *
+     * Free associated resources, including (opaque) if applicable.
+     *
+     * This function must always succeed: as such, it returns void. The
+     *  system may call your flush() method before this. You may report
+     *  failure there if necessary. This method may still be called if
+     *  flush() fails, in which case you'll have to abandon unflushed data
+     *  and other failing conditions and clean up.
+     *
+     * Once this method is called for a given instance, the system will assume
+     *  it is unsafe to touch that instance again and will discard any
+     *  references to it.
+     *
+     *   \param s The i/o instance to destroy.
+     */
+    void (*destroy)(struct PHYSFS_Io *io);
+
+    /**
+     * \brief Instance data for this struct.
+     *
+     * Each instance has a pointer associated with it that can be used to
+     *  store anything it likes. This pointer is per-instance of the stream,
+     *  so presumably it will change when calling duplicate(). This can be
+     *  deallocated during the destroy() method.
+     */
+    void *opaque;
+} PHYSFS_Io;
+
 /* Everything above this line is part of the PhysicsFS 2.1 API. */
 
 
--- a/src/physfs_internal.h	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/physfs_internal.h	Mon Aug 30 03:01:57 2010 -0400
@@ -711,14 +711,10 @@
 
 /* end LANG section. */
 
-struct __PHYSFS_DIRHANDLE__;
-struct __PHYSFS_FILEFUNCTIONS__;
-
 
 /* !!! FIXME: find something better than "dvoid" and "fvoid" ... */
 /* Opaque data for file and dir handlers... */
 typedef void dvoid;
-typedef void fvoid;
 
 
 typedef struct
@@ -741,16 +737,18 @@
      */
 
         /*
-         * Open a dirhandle for dir/archive (name).
-         *  This filename is in platform-dependent notation.
-         *  forWriting is non-zero if this is to be used for
+         * Open a dirhandle for dir/archive data provided by (io).
+         *  (name) is a filename associated with (io), but doesn't necessarily
+         *  map to anything, let alone a real filename. This possibly-
+         *  meaningless name is in platform-dependent notation.
+         * (forWriting) is non-zero if this is to be used for
          *  the write directory, and zero if this is to be used for an
          *  element of the search path.
          * Returns NULL on failure, and calls __PHYSFS_setError().
          *  Returns non-NULL on success. The pointer returned will be
          *  passed as the "opaque" parameter for later calls.
          */
-    void *(*openArchive)(const char *name, int forWriting);
+    dvoid *(*openArchive)(PHYSFS_Io *io, const char *name, int forWriting);
 
         /*
          * List all files in (dirname). Each file is passed to (callback),
@@ -810,7 +808,7 @@
          *  non-zero if the file existed (even if it's a broken symlink!),
          *  zero if it did not.
          */
-    fvoid *(*openRead)(dvoid *opaque, const char *fname, int *fileExists);
+    PHYSFS_Io *(*openRead)(dvoid *opaque, const char *fname, int *fileExists);
 
         /*
          * Open file for writing.
@@ -824,7 +822,7 @@
          *  Returns non-NULL on success. The pointer returned will be
          *  passed as the "opaque" parameter for later file calls.
          */
-    fvoid *(*openWrite)(dvoid *opaque, const char *filename);
+    PHYSFS_Io *(*openWrite)(dvoid *opaque, const char *filename);
 
         /*
          * Open file for appending.
@@ -837,7 +835,7 @@
          *  Returns non-NULL on success. The pointer returned will be
          *  passed as the "opaque" parameter for later file calls.
          */
-    fvoid *(*openAppend)(dvoid *opaque, const char *filename);
+    PHYSFS_Io *(*openAppend)(dvoid *opaque, const char *filename);
 
         /*
          * Delete a file in the archive/directory.
@@ -862,9 +860,9 @@
 
         /*
          * Close directories/archives, and free any associated memory,
-         *  including (opaque) itself if applicable. Implementation can assume
-         *  that it won't be called if there are still files open from
-         *  this archive.
+         *  including the original PHYSFS_Io and (opaque) itself, if
+         *  applicable. Implementation can assume that it won't be called if
+         *  there are still files open from this archive.
          */
     void (*dirClose)(dvoid *opaque);
 
@@ -874,60 +872,6 @@
          *  On failure, call __PHYSFS_setError().
          */
     int (*stat)(dvoid *opaque, const char *fn, int *exists, PHYSFS_Stat *stat);
-
-
-    /*
-     * FILE ROUTINES:
-     * These functions are for file handles generated by the open*() methods.
-     *  They are distinguished by taking a "fvoid" instead of a "dvoid" for
-     *  the opaque handle.
-     */
-
-        /*
-         * Read (len) bytes from the file.
-         * Returns number of bytes read from file, -1 if complete failure.
-         * On failure, call __PHYSFS_setError().
-         */
-    PHYSFS_sint64 (*read)(fvoid *opaque, void *buffer, PHYSFS_uint64 len);
-
-        /*
-         * Write (len) bytes to the file. Archives don't have to implement
-         *  this; set it to NULL if not implemented.
-         * Returns number of bytes written to file, -1 if complete failure.
-         * On failure, call __PHYSFS_setError().
-         */
-    PHYSFS_sint64 (*write)(fvoid *opaque, const void *buf, PHYSFS_uint64 len);
-
-        /*
-         * Returns non-zero if at end of file.
-         */
-    int (*eof)(fvoid *opaque);
-
-        /*
-         * Returns byte offset from start of file.
-         */
-    PHYSFS_sint64 (*tell)(fvoid *opaque);
-
-        /*
-         * Move read/write pointer to byte offset from start of file.
-         *  Returns non-zero on success, zero on error.
-         * On failure, call __PHYSFS_setError().
-         */
-    int (*seek)(fvoid *opaque, PHYSFS_uint64 offset);
-
-        /*
-         * Return number of bytes available in the file, or -1 if you
-         *  aren't able to determine.
-         * On failure, call __PHYSFS_setError().
-         */
-    PHYSFS_sint64 (*fileLength)(fvoid *opaque);
-
-        /*
-         * Close the file, and free associated resources, including (opaque)
-         *  if applicable. Returns non-zero on success, zero if can't close
-         *  file. On failure, call __PHYSFS_setError().
-         */
-    int (*fileClose)(fvoid *opaque);
 } PHYSFS_Archiver;
 
 
@@ -1082,6 +1026,14 @@
 /* convenience macro to make this less cumbersome internally... */
 #define allocator __PHYSFS_AllocatorHooks
 
+/*
+ * Create a PHYSFS_Io for a file in the physical filesystem.
+ *  This path is in platform-dependent notation. (mode) must be 'r', 'w', or
+ *  'a' for Read, Write, or Append.
+ */
+PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode);
+
+
 /*--------------------------------------------------------------------------*/
 /*--------------------------------------------------------------------------*/
 /*------------                                              ----------------*/
@@ -1172,7 +1124,6 @@
  */
 void *__PHYSFS_platformOpenAppend(const char *filename);
 
-
 /*
  * Read more data from a platform-specific file handle. (opaque) should be
  *  cast to whatever data type your platform uses. Read a maximum of (len)
@@ -1267,16 +1218,14 @@
 int __PHYSFS_platformFlush(void *opaque);
 
 /*
- * Flush and close a file. (opaque) should be cast to whatever data type
- *  your platform uses. Be sure to check for errors when closing; the
- *  caller expects that this function can fail if there was a flushing
- *  error, etc.
+ * Close file and deallocate resources. (opaque) should be cast to whatever
+ *  data type your platform uses. This should close the file in any scenario:
+ *  flushing is a separate function call, and this function should never fail.
  *
- * You should clean up all resources associated with (opaque).
- *
- *  Return zero on failure, non-zero on success.
+ * You should clean up all resources associated with (opaque); the pointer
+ *  will be considered invalid after this call.
  */
-int __PHYSFS_platformClose(void *opaque);
+void __PHYSFS_platformClose(void *opaque);
 
 /*
  * Platform implementation of PHYSFS_getCdRomDirsCallback()...
@@ -1331,7 +1280,6 @@
  */
 int __PHYSFS_platformIsSymLink(const char *fname);
 
-
 /*
  * Return non-zero if filename (in platform-dependent notation) is a symlink.
  *  Symlinks should be followed; if what the symlink points to is missing,
--- a/src/platform_os2.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/platform_os2.c	Mon Aug 30 03:01:57 2010 -0400
@@ -590,9 +590,9 @@
 } /* __PHYSFS_platformFlush */
 
 
-int __PHYSFS_platformClose(void *opaque)
+void __PHYSFS_platformClose(void *opaque)
 {
-    return (os2err(DosClose((HFILE) opaque)) == NO_ERROR);
+    DosClose((HFILE) opaque);  /* ignore errors. You should have flushed! */
 } /* __PHYSFS_platformClose */
 
 
--- a/src/platform_pocketpc.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/platform_pocketpc.c	Mon Aug 30 03:01:57 2010 -0400
@@ -506,12 +506,11 @@
 } /* __PHYSFS_platformFlush */
 
 
-int __PHYSFS_platformClose(void *opaque)
+void __PHYSFS_platformClose(void *opaque)
 {
     HANDLE Handle = ((winCEfile *) opaque)->handle;
-    BAIL_IF_MACRO(!CloseHandle(Handle), win32strerror(), 0);
+    (void) CloseHandle(Handle); /* ignore errors. You should have flushed! */
     allocator.Free(opaque);
-    return 1;
 } /* __PHYSFS_platformClose */
 
 
--- a/src/platform_posix.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/platform_posix.c	Mon Aug 30 03:01:57 2010 -0400
@@ -393,12 +393,11 @@
 } /* __PHYSFS_platformFlush */
 
 
-int __PHYSFS_platformClose(void *opaque)
+void __PHYSFS_platformClose(void *opaque)
 {
-    int fd = *((int *) opaque);
-    BAIL_IF_MACRO(close(fd) == -1, strerror(errno), 0);
+    const int fd = *((int *) opaque);
+    (void) close(fd);  /* we don't check this. You should have used flush! */
     allocator.Free(opaque);
-    return 1;
 } /* __PHYSFS_platformClose */
 
 
--- a/src/platform_windows.c	Sun Aug 29 01:55:30 2010 -0400
+++ b/src/platform_windows.c	Mon Aug 30 03:01:57 2010 -0400
@@ -1182,12 +1182,11 @@
 } /* __PHYSFS_platformFlush */
 
 
-int __PHYSFS_platformClose(void *opaque)
+void __PHYSFS_platformClose(void *opaque)
 {
     HANDLE Handle = ((WinApiFile *) opaque)->handle;
-    BAIL_IF_MACRO(!CloseHandle(Handle), winApiStrError(), 0);
+    (void) CloseHandle(Handle); /* ignore errors. You should have flushed! */
     allocator.Free(opaque);
-    return 1;
 } /* __PHYSFS_platformClose */