Allow application-supplied archivers.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 28 Nov 2012 01:36:13 -0500
changeset 1322 5476917b8ddf
parent 1321 6929d3c0a347
child 1323 61ca5d6011df
Allow application-supplied archivers. This lets an application supply its own archivers, where they will work like any built-in archiver. This allows abstract directory interfaces the same way that PHYSFS_Io allows stream implementations. This is a work in progress still. The API is still changing, and will remain at version 0 until it is finalized (a theoretical future version 1 will be for when the final public interface changes, not when we evolve the initial API design).
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_slb.c
src/archiver_unpacked.c
src/archiver_wad.c
src/archiver_zip.c
src/physfs.c
src/physfs.h
src/physfs_internal.h
src/platform_posix.c
src/platform_windows.c
--- a/src/archiver_dir.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_dir.c	Wed Nov 28 01:36:13 2012 -0500
@@ -66,7 +66,7 @@
 } /* DIR_openArchive */
 
 
-static void DIR_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
+static void DIR_enumerateFiles(void *opaque, const char *dname,
                                int omitSymLinks, PHYSFS_EnumFilesCallback cb,
                                const char *origdir, void *callbackdata)
 {
@@ -82,7 +82,7 @@
 } /* DIR_enumerateFiles */
 
 
-static PHYSFS_Io *doOpen(PHYSFS_Dir *opaque, const char *name,
+static PHYSFS_Io *doOpen(void *opaque, const char *name,
                          const int mode, int *fileExists)
 {
     char *f;
@@ -114,25 +114,25 @@
 } /* doOpen */
 
 
-static PHYSFS_Io *DIR_openRead(PHYSFS_Dir *opaque, const char *fnm, int *exist)
+static PHYSFS_Io *DIR_openRead(void *opaque, const char *fnm, int *exist)
 {
     return doOpen(opaque, fnm, 'r', exist);
 } /* DIR_openRead */
 
 
-static PHYSFS_Io *DIR_openWrite(PHYSFS_Dir *opaque, const char *filename)
+static PHYSFS_Io *DIR_openWrite(void *opaque, const char *filename)
 {
     return doOpen(opaque, filename, 'w', NULL);
 } /* DIR_openWrite */
 
 
-static PHYSFS_Io *DIR_openAppend(PHYSFS_Dir *opaque, const char *filename)
+static PHYSFS_Io *DIR_openAppend(void *opaque, const char *filename)
 {
     return doOpen(opaque, filename, 'a', NULL);
 } /* DIR_openAppend */
 
 
-static int DIR_remove(PHYSFS_Dir *opaque, const char *name)
+static int DIR_remove(void *opaque, const char *name)
 {
     int retval;
     char *f;
@@ -145,7 +145,7 @@
 } /* DIR_remove */
 
 
-static int DIR_mkdir(PHYSFS_Dir *opaque, const char *name)
+static int DIR_mkdir(void *opaque, const char *name)
 {
     int retval;
     char *f;
@@ -158,13 +158,13 @@
 } /* DIR_mkdir */
 
 
-static void DIR_closeArchive(PHYSFS_Dir *opaque)
+static void DIR_closeArchive(void *opaque)
 {
     allocator.Free(opaque);
 } /* DIR_closeArchive */
 
 
-static int DIR_stat(PHYSFS_Dir *opaque, const char *name,
+static int DIR_stat(void *opaque, const char *name,
                     int *exists, PHYSFS_Stat *stat)
 {
     int retval = 0;
@@ -180,6 +180,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_DIR =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "",
         "Non-archive, direct filesystem I/O",
--- a/src/archiver_grp.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_grp.c	Wed Nov 28 01:36:13 2012 -0500
@@ -87,6 +87,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "GRP",
         "Build engine Groupfile format",
--- a/src/archiver_hog.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_hog.c	Wed Nov 28 01:36:13 2012 -0500
@@ -93,6 +93,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "HOG",
         "Descent I/II HOG file format",
--- a/src/archiver_iso9660.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_iso9660.c	Wed Nov 28 01:36:13 2012 -0500
@@ -291,8 +291,8 @@
         for(;pos < descriptor->filenamelen; pos++)
             if (descriptor->filename[pos] == ';')
                 lastfound = pos;
-        BAIL_IF_MACRO(lastfound < 1, PHYSFS_ERR_NO_SUCH_PATH /* !!! FIXME: PHYSFS_ERR_BAD_FILENAME */, -1);
-        BAIL_IF_MACRO(lastfound == (descriptor->filenamelen -1), PHYSFS_ERR_NO_SUCH_PATH /* !!! PHYSFS_ERR_BAD_FILENAME */, -1);
+        BAIL_IF_MACRO(lastfound < 1, PHYSFS_ERR_NOT_FOUND /* !!! FIXME: PHYSFS_ERR_BAD_FILENAME */, -1);
+        BAIL_IF_MACRO(lastfound == (descriptor->filenamelen -1), PHYSFS_ERR_NOT_FOUND /* !!! PHYSFS_ERR_BAD_FILENAME */, -1);
         strncpy(filename, descriptor->filename, lastfound);
         if (filename[lastfound - 1] == '.')
             filename[lastfound - 1] = '\0'; /* consume trailing ., as done in all implementations */
@@ -638,7 +638,7 @@
 } /* ISO9660_openArchive */
 
 
-static void ISO9660_closeArchive(PHYSFS_Dir *opaque)
+static void ISO9660_closeArchive(void *opaque)
 {
     ISO9660Handle *handle = (ISO9660Handle*) opaque;
     handle->io->destroy(handle->io);
@@ -766,7 +766,7 @@
 } /* iso_file_open_foreign */
 
 
-static PHYSFS_Io *ISO9660_openRead(PHYSFS_Dir *opaque, const char *filename,
+static PHYSFS_Io *ISO9660_openRead(void *opaque, const char *filename,
                                    int *exists)
 {
     PHYSFS_Io *retval = NULL;
@@ -785,7 +785,7 @@
     /* find file descriptor */
     rc = iso_find_dir_entry(handle, filename, &descriptor, exists);
     GOTO_IF_MACRO(rc, ERRPASS, errorhandling);
-    GOTO_IF_MACRO(!*exists, PHYSFS_ERR_NO_SUCH_PATH, errorhandling);
+    GOTO_IF_MACRO(!*exists, PHYSFS_ERR_NOT_FOUND, errorhandling);
 
     fhandle->startblock = descriptor.extentpos + descriptor.extattributelen;
     fhandle->filesize = descriptor.datalen;
@@ -816,7 +816,7 @@
  * Information gathering functions
  ******************************************************************************/
 
-static void ISO9660_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
+static void ISO9660_enumerateFiles(void *opaque, const char *dname,
                                    int omitSymLinks,
                                    PHYSFS_EnumFilesCallback cb,
                                    const char *origdir, void *callbackdata)
@@ -873,7 +873,7 @@
 } /* ISO9660_enumerateFiles */
 
 
-static int ISO9660_stat(PHYSFS_Dir *opaque, const char *name, int *exists,
+static int ISO9660_stat(void *opaque, const char *name, int *exists,
                         PHYSFS_Stat *stat)
 {
     ISO9660Handle *handle = (ISO9660Handle*) opaque;
@@ -920,25 +920,25 @@
  * Not supported functions
  ******************************************************************************/
 
-static PHYSFS_Io *ISO9660_openWrite(PHYSFS_Dir *opaque, const char *name)
+static PHYSFS_Io *ISO9660_openWrite(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* ISO9660_openWrite */
 
 
-static PHYSFS_Io *ISO9660_openAppend(PHYSFS_Dir *opaque, const char *name)
+static PHYSFS_Io *ISO9660_openAppend(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* ISO9660_openAppend */
 
 
-static int ISO9660_remove(PHYSFS_Dir *opaque, const char *name)
+static int ISO9660_remove(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* ISO9660_remove */
 
 
-static int ISO9660_mkdir(PHYSFS_Dir *opaque, const char *name)
+static int ISO9660_mkdir(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* ISO9660_mkdir */
@@ -946,6 +946,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660 =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "ISO",
         "ISO9660 image file",
--- a/src/archiver_lzma.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_lzma.c	Wed Nov 28 01:36:13 2012 -0500
@@ -205,7 +205,7 @@
 {
     LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */
 
-    BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NO_SUCH_PATH, NULL);
+    BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NOT_FOUND, NULL);
 
     return file;
 } /* lzma_find_file */
@@ -531,7 +531,7 @@
 } /* doEnumCallback */
 
 
-static void LZMA_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
+static void LZMA_enumerateFiles(void *opaque, const char *dname,
                                 int omitSymLinks, PHYSFS_EnumFilesCallback cb,
                                 const char *origdir, void *callbackdata)
 {
@@ -551,7 +551,7 @@
             file = archive->files;
         }
 
-    BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NO_SUCH_PATH, );
+    BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NOT_FOUND, );
 
     while (file < lastFile)
     {
@@ -575,7 +575,7 @@
 } /* LZMA_enumerateFiles */
 
 
-static PHYSFS_Io *LZMA_openRead(PHYSFS_Dir *opaque, const char *name,
+static PHYSFS_Io *LZMA_openRead(void *opaque, const char *name,
                                 int *fileExists)
 {
     LZMAarchive *archive = (LZMAarchive *) opaque;
@@ -583,7 +583,7 @@
     PHYSFS_Io *io = NULL;
 
     *fileExists = (file != NULL);
-    BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NO_SUCH_PATH, NULL);
+    BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NOT_FOUND, NULL);
     BAIL_IF_MACRO(file->folder == NULL, PHYSFS_ERR_NOT_A_FILE, NULL);
 
     file->position = 0;
@@ -598,19 +598,19 @@
 } /* LZMA_openRead */
 
 
-static PHYSFS_Io *LZMA_openWrite(PHYSFS_Dir *opaque, const char *filename)
+static PHYSFS_Io *LZMA_openWrite(void *opaque, const char *filename)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* LZMA_openWrite */
 
 
-static PHYSFS_Io *LZMA_openAppend(PHYSFS_Dir *opaque, const char *filename)
+static PHYSFS_Io *LZMA_openAppend(void *opaque, const char *filename)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* LZMA_openAppend */
 
 
-static void LZMA_closeArchive(PHYSFS_Dir *opaque)
+static void LZMA_closeArchive(void *opaque)
 {
     LZMAarchive *archive = (LZMAarchive *) opaque;
 
@@ -628,18 +628,18 @@
 } /* LZMA_closeArchive */
 
 
-static int LZMA_remove(PHYSFS_Dir *opaque, const char *name)
+static int LZMA_remove(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* LZMA_remove */
 
 
-static int LZMA_mkdir(PHYSFS_Dir *opaque, const char *name)
+static int LZMA_mkdir(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* LZMA_mkdir */
 
-static int LZMA_stat(PHYSFS_Dir *opaque, const char *filename,
+static int LZMA_stat(void *opaque, const char *filename,
                      int *exists, PHYSFS_Stat *stat)
 {
     const LZMAarchive *archive = (const LZMAarchive *) opaque;
@@ -678,6 +678,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_LZMA =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "7Z",
         "LZMA (7zip) format",
--- a/src/archiver_mvl.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_mvl.c	Wed Nov 28 01:36:13 2012 -0500
@@ -80,6 +80,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "MVL",
         "Descent II Movielib format",
--- a/src/archiver_qpak.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_qpak.c	Wed Nov 28 01:36:13 2012 -0500
@@ -96,6 +96,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_QPAK =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "PAK",
         "Quake I/II format",
--- a/src/archiver_slb.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_slb.c	Wed Nov 28 01:36:13 2012 -0500
@@ -101,6 +101,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_SLB =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "SLB",
         "I-War / Independence War Slab file",
--- a/src/archiver_unpacked.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_unpacked.c	Wed Nov 28 01:36:13 2012 -0500
@@ -34,7 +34,7 @@
 } UNPKfileinfo;
 
 
-void UNPK_closeArchive(PHYSFS_Dir *opaque)
+void UNPK_closeArchive(void *opaque)
 {
     UNPKinfo *info = ((UNPKinfo *) opaque);
     info->io->destroy(info->io);
@@ -242,7 +242,7 @@
 } /* doEnumCallback */
 
 
-void UNPK_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
+void UNPK_enumerateFiles(void *opaque, const char *dname,
                          int omitSymLinks, PHYSFS_EnumFilesCallback cb,
                          const char *origdir, void *callbackdata)
 {
@@ -340,11 +340,11 @@
     if (isDir != NULL)
         *isDir = 0;
 
-    BAIL_MACRO(PHYSFS_ERR_NO_SUCH_PATH, NULL);
+    BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, NULL);
 } /* findEntry */
 
 
-PHYSFS_Io *UNPK_openRead(PHYSFS_Dir *opaque, const char *fnm, int *fileExists)
+PHYSFS_Io *UNPK_openRead(void *opaque, const char *fnm, int *fileExists)
 {
     PHYSFS_Io *retval = NULL;
     UNPKinfo *info = (UNPKinfo *) opaque;
@@ -390,31 +390,31 @@
 } /* UNPK_openRead */
 
 
-PHYSFS_Io *UNPK_openWrite(PHYSFS_Dir *opaque, const char *name)
+PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* UNPK_openWrite */
 
 
-PHYSFS_Io *UNPK_openAppend(PHYSFS_Dir *opaque, const char *name)
+PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* UNPK_openAppend */
 
 
-int UNPK_remove(PHYSFS_Dir *opaque, const char *name)
+int UNPK_remove(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* UNPK_remove */
 
 
-int UNPK_mkdir(PHYSFS_Dir *opaque, const char *name)
+int UNPK_mkdir(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* UNPK_mkdir */
 
 
-int UNPK_stat(PHYSFS_Dir *opaque, const char *filename,
+int UNPK_stat(void *opaque, const char *filename,
               int *exists, PHYSFS_Stat *stat)
 {
     int isDir = 0;
@@ -448,8 +448,7 @@
 } /* UNPK_stat */
 
 
-PHYSFS_Dir *UNPK_openArchive(PHYSFS_Io *io, UNPKentry *e,
-                             const PHYSFS_uint32 num)
+void *UNPK_openArchive(PHYSFS_Io *io, UNPKentry *e, const PHYSFS_uint32 num)
 {
     UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo));
     if (info == NULL)
--- a/src/archiver_wad.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_wad.c	Wed Nov 28 01:36:13 2012 -0500
@@ -104,6 +104,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_WAD =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "WAD",
         "DOOM engine format",
--- a/src/archiver_zip.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/archiver_zip.c	Wed Nov 28 01:36:13 2012 -0500
@@ -600,7 +600,7 @@
     if (isDir != NULL)
         *isDir = 0;
 
-    BAIL_MACRO(PHYSFS_ERR_NO_SUCH_PATH, NULL);
+    BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, NULL);
 } /* zip_find_entry */
 
 
@@ -1487,7 +1487,7 @@
 } /* doEnumCallback */
 
 
-static void ZIP_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
+static void ZIP_enumerateFiles(void *opaque, const char *dname,
                                int omitSymLinks, PHYSFS_EnumFilesCallback cb,
                                const char *origdir, void *callbackdata)
 {
@@ -1560,7 +1560,7 @@
 } /* zip_get_io */
 
 
-static PHYSFS_Io *ZIP_openRead(PHYSFS_Dir *opaque, const char *fnm,
+static PHYSFS_Io *ZIP_openRead(void *opaque, const char *fnm,
                                int *fileExists)
 {
     PHYSFS_Io *retval = NULL;
@@ -1619,19 +1619,19 @@
 } /* ZIP_openRead */
 
 
-static PHYSFS_Io *ZIP_openWrite(PHYSFS_Dir *opaque, const char *filename)
+static PHYSFS_Io *ZIP_openWrite(void *opaque, const char *filename)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* ZIP_openWrite */
 
 
-static PHYSFS_Io *ZIP_openAppend(PHYSFS_Dir *opaque, const char *filename)
+static PHYSFS_Io *ZIP_openAppend(void *opaque, const char *filename)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
 } /* ZIP_openAppend */
 
 
-static void ZIP_closeArchive(PHYSFS_Dir *opaque)
+static void ZIP_closeArchive(void *opaque)
 {
     ZIPinfo *zi = (ZIPinfo *) (opaque);
     zi->io->destroy(zi->io);
@@ -1640,19 +1640,19 @@
 } /* ZIP_closeArchive */
 
 
-static int ZIP_remove(PHYSFS_Dir *opaque, const char *name)
+static int ZIP_remove(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* ZIP_remove */
 
 
-static int ZIP_mkdir(PHYSFS_Dir *opaque, const char *name)
+static int ZIP_mkdir(void *opaque, const char *name)
 {
     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
 } /* ZIP_mkdir */
 
 
-static int ZIP_stat(PHYSFS_Dir *opaque, const char *filename, int *exists,
+static int ZIP_stat(void *opaque, const char *filename, int *exists,
                     PHYSFS_Stat *stat)
 {
     int isDir = 0;
@@ -1694,6 +1694,7 @@
 
 const PHYSFS_Archiver __PHYSFS_Archiver_ZIP =
 {
+    CURRENT_PHYSFS_ARCHIVER_API_VERSION,
     {
         "ZIP",
         "PkZip/WinZip/Info-Zip compatible",
--- a/src/physfs.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/physfs.c	Wed Nov 28 01:36:13 2012 -0500
@@ -58,7 +58,7 @@
 extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
 extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
 
-static const PHYSFS_Archiver *staticArchivers[] =
+static const PHYSFS_Archiver * const staticArchivers[] =
 {
 #if PHYSFS_SUPPORTS_ZIP
     &__PHYSFS_Archiver_ZIP,
@@ -105,6 +105,7 @@
 static int allowSymLinks = 0;
 static const PHYSFS_Archiver **archivers = NULL;
 static const PHYSFS_ArchiveInfo **archiveInfo = NULL;
+static volatile size_t numArchivers = 0;
 
 /* mutexes ... */
 static void *errorLock = NULL;     /* protects error message list.        */
@@ -752,7 +753,7 @@
         case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open";
         case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument";
         case PHYSFS_ERR_NOT_MOUNTED: return "not mounted";
-        case PHYSFS_ERR_NO_SUCH_PATH: return "no such path";
+        case PHYSFS_ERR_NOT_FOUND: return "not found";
         case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden";
         case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set";
         case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading";
@@ -768,6 +769,7 @@
         case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs";
         case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty";
         case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
+        case PHYSFS_ERR_DUPLICATE: return "duplicate resource";
     } /* switch */
 
     return NULL;  /* don't know this error code. */
@@ -890,14 +892,14 @@
         /* Look for archivers with matching file extensions first... */
         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
         {
-            if (__PHYSFS_stricmpASCII(ext, (*i)->info.extension) == 0)
+            if (__PHYSFS_utf8stricmp(ext, (*i)->info.extension) == 0)
                 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)
+            if (__PHYSFS_utf8stricmp(ext, (*i)->info.extension) != 0)
                 retval = tryOpenDir(io, *i, d, forWriting);
         } /* for */
     } /* if */
@@ -1125,32 +1127,26 @@
 } /* initializeMutexes */
 
 
-static void setDefaultAllocator(void);
+static int doRegisterArchiver(const PHYSFS_Archiver *_archiver);
 
 static int initStaticArchivers(void)
 {
-    const size_t numStaticArchivers = __PHYSFS_ARRAYLEN(staticArchivers);
-    const size_t len = numStaticArchivers * sizeof (void *);
-    size_t i;
-
-    assert(numStaticArchivers > 0);  /* seriously, none at all?! */
-    assert(staticArchivers[numStaticArchivers - 1] == NULL);
-
-    archiveInfo = (const PHYSFS_ArchiveInfo **) allocator.Malloc(len);
-    BAIL_IF_MACRO(!archiveInfo, PHYSFS_ERR_OUT_OF_MEMORY, 0);
-    archivers = (const PHYSFS_Archiver **) allocator.Malloc(len);
-    BAIL_IF_MACRO(!archivers, PHYSFS_ERR_OUT_OF_MEMORY, 0);
-
-    for (i = 0; i < numStaticArchivers - 1; i++)
-        archiveInfo[i] = &staticArchivers[i]->info;
-    archiveInfo[numStaticArchivers - 1] = NULL;
-
-    memcpy(archivers, staticArchivers, len);
+    const PHYSFS_Archiver * const *i;
+
+    assert(__PHYSFS_ARRAYLEN(staticArchivers) > 0);  /* at least a NULL. */
+    assert(staticArchivers[__PHYSFS_ARRAYLEN(staticArchivers) - 1] == NULL);
+
+    for (i = staticArchivers; *i != NULL; i++)
+    {
+        if (!doRegisterArchiver(*i))
+            return 0;
+    } /* for */
 
     return 1;
 } /* initStaticArchivers */
 
 
+static void setDefaultAllocator(void);
 static int doDeinit(void);
 
 int PHYSFS_init(const char *argv0)
@@ -1243,6 +1239,63 @@
 } /* freeSearchPath */
 
 
+/* MAKE SURE you hold stateLock before calling this! */
+static int archiverInUse(const PHYSFS_Archiver *arc, const DirHandle *list)
+{
+    const DirHandle *i;
+    for (i = list; i != NULL; i = i->next)
+    {
+        if (i->funcs == arc)
+            return 1;
+    } /* for */
+
+    return 0;  /* not in use */
+} /* archiverInUse */
+
+
+/* MAKE SURE you hold stateLock before calling this! */
+static int doDeregisterArchiver(const size_t idx)
+{
+    const size_t len = (numArchivers - idx) * sizeof (void *);
+    const PHYSFS_ArchiveInfo *info = archiveInfo[idx];
+    const PHYSFS_Archiver *arc = archivers[idx];
+
+    /* make sure nothing is still using this archiver */
+    if (archiverInUse(arc, searchPath) || archiverInUse(arc, writeDir))
+        BAIL_MACRO(PHYSFS_ERR_FILES_STILL_OPEN, 0);
+
+    allocator.Free((void *) info->extension);
+    allocator.Free((void *) info->description);
+    allocator.Free((void *) info->author);
+    allocator.Free((void *) info->url);
+    allocator.Free((void *) arc);
+
+    memmove(&archiveInfo[idx], &archiveInfo[idx+1], len);
+    memmove(&archivers[idx], &archivers[idx+1], len);
+
+    assert(numArchivers > 0);
+    numArchivers--;
+
+    return 1;
+} /* doDeregisterArchiver */
+
+
+/* Does NOT hold the state lock; we're shutting down. */
+static void freeArchivers(void)
+{
+    while (numArchivers > 0)
+    {
+        const int rc = doDeregisterArchiver(numArchivers - 1);
+        assert(rc);  /* nothing should be mounted during shutdown. */
+    } /* while */
+
+    allocator.Free(archivers);
+    allocator.Free(archiveInfo);
+    archivers = NULL;
+    archiveInfo = NULL;
+} /* freeArchivers */
+
+
 static int doDeinit(void)
 {
     BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), ERRPASS, 0);
@@ -1251,6 +1304,7 @@
     BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), PHYSFS_ERR_FILES_STILL_OPEN, 0);
 
     freeSearchPath();
+    freeArchivers();
     freeErrorStates();
 
     if (baseDir != NULL)
@@ -1310,6 +1364,133 @@
 } /* PHYSFS_isInit */
 
 
+static char *PHYSFS_strdup(const char *str)
+{
+    char *retval = (char *) allocator.Malloc(strlen(str) + 1);
+    if (retval)
+        strcpy(retval, str);
+    return retval;
+} /* PHYSFS_strdup */
+
+
+/* MAKE SURE you hold stateLock before calling this! */
+static int doRegisterArchiver(const PHYSFS_Archiver *_archiver)
+{
+    const PHYSFS_uint32 maxver = CURRENT_PHYSFS_ARCHIVER_API_VERSION;
+    const size_t len = (numArchivers + 2) * sizeof (void *);
+    PHYSFS_Archiver *archiver = NULL;
+    PHYSFS_ArchiveInfo *info = NULL;
+    const char *ext = NULL;
+    void *ptr = NULL;
+    size_t i;
+
+    BAIL_IF_MACRO(!_archiver, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(_archiver->version > maxver, PHYSFS_ERR_UNSUPPORTED, 0);
+    BAIL_IF_MACRO(!_archiver->info.extension, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->info.description, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->info.author, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->info.url, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->enumerateFiles, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openRead, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openWrite, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openAppend, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->remove, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->mkdir, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->closeArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+
+    ext = _archiver->info.extension;
+    for (i = 0; i < numArchivers; i++)
+    {
+        if (__PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
+            BAIL_MACRO(PHYSFS_ERR_DUPLICATE, 0);  // !!! FIXME: better error? ERR_IN_USE?
+    } /* for */
+
+    /* make a copy of the data. */
+    archiver = (PHYSFS_Archiver *) allocator.Malloc(sizeof (*archiver));
+    GOTO_IF_MACRO(!archiver, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+
+    /* Must copy sizeof (OLD_VERSION_OF_STRUCT) when version changes! */
+    memcpy(archiver, _archiver, sizeof (*archiver));
+
+    info = (PHYSFS_ArchiveInfo *) &archiver->info;
+    memset(info, '\0', sizeof (*info));  /* NULL in case an alloc fails. */
+    #define CPYSTR(item) \
+        info->item = PHYSFS_strdup(_archiver->info.item); \
+        GOTO_IF_MACRO(!info->item, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+    CPYSTR(extension);
+    CPYSTR(description);
+    CPYSTR(author);
+    CPYSTR(url);
+    #undef CPYSTR
+
+    ptr = allocator.Realloc(archiveInfo, len);
+    GOTO_IF_MACRO(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+    archiveInfo = (const PHYSFS_ArchiveInfo **) ptr;
+
+    ptr = allocator.Realloc(archivers, len);
+    GOTO_IF_MACRO(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+    archivers = (const PHYSFS_Archiver **) ptr;
+
+    archiveInfo[numArchivers] = info;
+    archiveInfo[numArchivers + 1] = NULL;
+
+    archivers[numArchivers] = archiver;
+    archivers[numArchivers + 1] = NULL;
+
+    numArchivers++;
+
+    return 1;
+
+regfailed:
+    if (info != NULL)
+    {
+        allocator.Free((void *) info->extension);
+        allocator.Free((void *) info->description);
+        allocator.Free((void *) info->author);
+        allocator.Free((void *) info->url);
+    } /* if */
+    allocator.Free(archiver);
+
+    return 0;
+} /* doRegisterArchiver */
+
+
+int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver)
+{
+    int retval;
+    BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
+    __PHYSFS_platformGrabMutex(stateLock);
+    retval = doRegisterArchiver(archiver);
+    __PHYSFS_platformReleaseMutex(stateLock);
+    return retval;
+} /* PHYSFS_registerArchiver */
+
+
+int PHYSFS_deregisterArchiver(const char *ext)
+{
+    size_t i;
+
+    BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
+    BAIL_IF_MACRO(!ext, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+
+    __PHYSFS_platformGrabMutex(stateLock);
+    for (i = 0; i < numArchivers; i++)
+    {
+        if (__PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
+        {
+            const int retval = doDeregisterArchiver(i);
+            __PHYSFS_platformReleaseMutex(stateLock);
+            return retval;
+        } /* if */
+    } /* for */
+    __PHYSFS_platformReleaseMutex(stateLock);
+
+    BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, 0);
+} /* PHYSFS_deregisterArchiver */
+
+
 const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
 {
     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
@@ -1702,7 +1883,7 @@
             if ((l > extlen) && ((*i)[l - extlen - 1] == '.'))
             {
                 ext = (*i) + (l - extlen);
-                if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0)
+                if (__PHYSFS_utf8stricmp(ext, archiveExt) == 0)
                     setSaneCfgAddPath(*i, l, dirsep, archivesFirst);
             } /* if */
         } /* for */
@@ -1763,12 +1944,12 @@
         size_t len = strlen(fname);
         assert(mntpntlen > 1); /* root mount points should be NULL. */
         /* not under the mountpoint, so skip this archive. */
-        BAIL_IF_MACRO(len < mntpntlen-1, PHYSFS_ERR_NO_SUCH_PATH, 0);
+        BAIL_IF_MACRO(len < mntpntlen-1, PHYSFS_ERR_NOT_FOUND, 0);
         /* !!! FIXME: Case insensitive? */
         retval = strncmp(h->mountPoint, fname, mntpntlen-1);
-        BAIL_IF_MACRO(retval != 0, PHYSFS_ERR_NO_SUCH_PATH, 0);
+        BAIL_IF_MACRO(retval != 0, PHYSFS_ERR_NOT_FOUND, 0);
         if (len > mntpntlen-1)  /* corner case... */
-            BAIL_IF_MACRO(fname[mntpntlen-1]!='/', PHYSFS_ERR_NO_SUCH_PATH, 0);
+            BAIL_IF_MACRO(fname[mntpntlen-1]!='/', PHYSFS_ERR_NOT_FOUND, 0);
         fname += mntpntlen-1;  /* move to start of actual archive path. */
         if (*fname == '/')
             fname++;
@@ -2222,7 +2403,7 @@
 
         __PHYSFS_platformGrabMutex(stateLock);
 
-        GOTO_IF_MACRO(!searchPath, PHYSFS_ERR_NO_SUCH_PATH, openReadEnd);
+        GOTO_IF_MACRO(!searchPath, PHYSFS_ERR_NOT_FOUND, openReadEnd);
 
         for (i = searchPath; (i != NULL) && (!fileExists); i = i->next)
         {
--- a/src/physfs.h	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/physfs.h	Wed Nov 28 01:36:13 2012 -0500
@@ -399,6 +399,8 @@
  *          supported.
  *
  * \sa PHYSFS_supportedArchiveTypes
+ * \sa PHYSFS_registerArchiver
+ * \sa PHYSFS_deregisterArchiver
  */
 typedef struct PHYSFS_ArchiveInfo
 {
@@ -573,9 +575,13 @@
  *
  * The return values are pointers to internal memory, and should
  *  be considered READ ONLY, and never freed. The returned values are
- *  valid until the next call to PHYSFS_deinit().
+ *  valid until the next call to PHYSFS_deinit(), PHYSFS_registerArchiver(),
+ *  or PHYSFS_deregisterArchiver().
  *
  *   \return READ ONLY Null-terminated array of READ ONLY structures.
+ *
+ * \sa PHYSFS_registerArchiver
+ * \sa PHYSFS_deregisterArchiver
  */
 PHYSFS_DECL const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void);
 
@@ -3129,7 +3135,7 @@
     PHYSFS_ERR_FILES_STILL_OPEN, /**< Files still open.                     */
     PHYSFS_ERR_INVALID_ARGUMENT, /**< Bad parameter passed to an function.  */
     PHYSFS_ERR_NOT_MOUNTED,      /**< Requested archive/dir not mounted.    */
-    PHYSFS_ERR_NO_SUCH_PATH,     /**< No such file, directory, or parent.   */
+    PHYSFS_ERR_NOT_FOUND,        /**< File (or whatever) not found.         */
     PHYSFS_ERR_SYMLINK_FORBIDDEN,/**< Symlink seen when not permitted.      */
     PHYSFS_ERR_NO_WRITE_DIR,     /**< No write dir has been specified.      */
     PHYSFS_ERR_OPEN_FOR_READING, /**< Wrote to a file opened for reading.   */
@@ -3144,7 +3150,8 @@
     PHYSFS_ERR_BAD_FILENAME,     /**< Filename is bogus/insecure.           */
     PHYSFS_ERR_BUSY,             /**< Tried to modify a file the OS needs.  */
     PHYSFS_ERR_DIR_NOT_EMPTY,    /**< Tried to delete dir with files in it. */
-    PHYSFS_ERR_OS_ERROR          /**< Unspecified OS-level error.           */
+    PHYSFS_ERR_OS_ERROR,         /**< Unspecified OS-level error.           */
+    PHYSFS_ERR_DUPLICATE         /**< Duplicate entry.                      */
 } PHYSFS_ErrorCode;
 
 
@@ -3307,9 +3314,233 @@
 PHYSFS_DECL const char *PHYSFS_getPrefDir(const char *org, const char *app);
 
 
+/**
+ * \struct PHYSFS_Archiver
+ * \brief Abstract interface to provide support for user-defined archives.
+ *
+ * \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 a means to mount various archive file
+ *  formats, and physical directories in the native filesystem. However,
+ *  applications have been limited to the file formats provided by the
+ *  library. This interface allows an application to provide their own
+ *  archive file types.
+ *
+ * Conceptually, a PHYSFS_Archiver provides directory entries, while
+ *  PHYSFS_Io provides data streams for those directory entries. The most
+ *  obvious use of PHYSFS_Archiver is to provide support for an archive
+ *  file type that isn't provided by PhysicsFS directly: perhaps some
+ *  proprietary format that only your application needs to understand.
+ *
+ * Internally, all the built-in archive support uses this interface, so the
+ *  best examples for building a PHYSFS_Archiver is the source code to
+ *  PhysicsFS itself.
+ *
+ * An archiver is added to the system with PHYSFS_registerArchiver(), and then
+ *  it will be available for use automatically with PHYSFS_mount(); if a
+ *  given archive can be handled with your archiver, it will be given control
+ *  as appropriate.
+ *
+ * These methods deal with dir handles. You have one instance of your
+ *  archiver, and it generates a unique, opaque handle for each opened
+ *  archive in its openArchive() method. Since the lifetime of an Archiver
+ *  (not an archive) is generally the entire lifetime of the process, and it's
+ *  assumed to be a singleton, we do not provide any instance data for the
+ *  archiver itself; the app can just use some static variables if necessary.
+ *
+ * Symlinks should always be followed (except in stat()); PhysicsFS will
+ *  use the stat() method to check for symlinks and make a judgement on
+ *  whether to continue to call other methods based on that.
+ *
+ * Archivers, when necessary, should set the PhysicsFS error state with
+ *  PHYSFS_setErrorCode() before returning. PhysicsFS will pass these errors
+ *  back to the application unmolested in most cases.
+ *
+ * Thread safety: TO BE DECIDED.  !!! FIXME
+ *
+ * \sa PHYSFS_registerArchiver
+ * \sa PHYSFS_deregisterArchiver
+ * \sa PHYSFS_supportedArchiveTypes
+ */
+typedef struct PHYSFS_Archiver
+{
+
+// !!! FIXME: split read/write interfaces?
+
+    /**
+     * \brief Binary compatibility information.
+     *
+     * This must be set to zero at this time. Future versions of this
+     *  struct will increment this field, so we know what a given
+     *  implementation supports. We'll presumably keep supporting older
+     *  versions as we offer new features, though.
+     */
+    PHYSFS_uint32 version;
+
+    /**
+     * \brief Basic info about this archiver.
+     *
+     * This is used to identify your archive, and is returned in
+     *  PHYSFS_supportedArchiveTypes().
+     */
+    const PHYSFS_ArchiveInfo info;
+
+    /**
+     * \brief
+     *
+     * Open an archive 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.
+     * (forWrite) 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.
+     * Return NULL on failure. We ignore any error code you set here;
+     *  when PHYSFS_mount() returns, the error will be PHYSFS_ERR_UNSUPPORTED
+     *  (no Archivers could handle this data).  // !!! FIXME: yeah?
+     *  Returns non-NULL on success. The pointer returned will be
+     *  passed as the "opaque" parameter for later calls.
+     */
+    void *(*openArchive)(PHYSFS_Io *io, const char *name, int forWrite);
+
+    /**
+     * List all files in (dirname). Each file is passed to (cb),
+     *  where a copy is made if appropriate, so you should dispose of
+     *  it properly upon return from the callback.
+     * You should omit symlinks if (omitSymLinks) is non-zero.
+     * If you have a failure, report as much as you can.
+     *  (dirname) is in platform-independent notation.
+     */
+
+// !!! FIXME: get rid of this omitsymlinks nonsense.
+    void (*enumerateFiles)(void *opaque, const char *dirname,
+                           int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+                           const char *origdir, void *callbackdata);
+
+    /**
+     * Open file for reading.
+     *  This filename, (fnm), is in platform-independent notation.
+     * If you can't handle multiple opens of the same file,
+     *  you can opt to fail for the second call.
+     * Fail if the file does not exist.
+     * Returns NULL on failure, and calls PHYSFS_setErrorCode().
+     *  Returns non-NULL on success. The pointer returned will be
+     *  passed as the "opaque" parameter for later file calls.
+     *
+     * Regardless of success or failure, please set *exists to
+     *  non-zero if the file existed (even if it's a broken symlink!),
+     *  zero if it did not.
+     */
+// !!! FIXME: get rid of the exists nonsense, check error code instead.
+    PHYSFS_Io *(*openRead)(void *opaque, const char *fnm, int *exists);
+
+    /**
+     * Open file for writing.
+     * If the file does not exist, it should be created. If it exists,
+     *  it should be truncated to zero bytes. The writing
+     *  offset should be the start of the file.
+     * This filename is in platform-independent notation.
+     * If you can't handle multiple opens of the same file,
+     *  you can opt to fail for the second call.
+     * Returns NULL on failure, and calls PHYSFS_setErrorCode().
+     *  Returns non-NULL on success. The pointer returned will be
+     *  passed as the "opaque" parameter for later file calls.
+     */
+    PHYSFS_Io *(*openWrite)(void *opaque, const char *filename);
+
+    /**
+     * Open file for appending.
+     * If the file does not exist, it should be created. The writing
+     *  offset should be the end of the file.
+     * This filename is in platform-independent notation.
+     * If you can't handle multiple opens of the same file,
+     *  you can opt to fail for the second call.
+     * Returns NULL on failure, and calls PHYSFS_setErrorCode().
+     *  Returns non-NULL on success. The pointer returned will be
+     *  passed as the "opaque" parameter for later file calls.
+     */
+    PHYSFS_Io *(*openAppend)(void *opaque, const char *filename);
+
+    /**
+     * Delete a file in the archive/directory.
+     *  Return non-zero on success, zero on failure.
+     *  This filename is in platform-independent notation.
+     *  This method may be NULL.
+     * On failure, call PHYSFS_setErrorCode().
+     */
+    int (*remove)(void *opaque, const char *filename);
+
+    /**
+     * Create a directory in the archive/directory.
+     *  If the application is trying to make multiple dirs, PhysicsFS
+     *  will split them up into multiple calls before passing them to
+     *  your driver.
+     *  Return non-zero on success, zero on failure.
+     *  This filename is in platform-independent notation.
+     *  This method may be NULL.
+     * On failure, call PHYSFS_setErrorCode().
+     */
+    int (*mkdir)(void *opaque, const char *filename);
+
+    // !!! FIXME: reorder these methods.
+    /**
+     * Close directories/archives, and free any associated memory,
+     *  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 (*closeArchive)(void *opaque);
+
+    /**
+     * Obtain basic file metadata.
+     *  Returns non-zero on success, zero on failure.
+     *  On failure, call PHYSFS_setErrorCode().
+     */
+// !!! FIXME: remove this exists nonsense (check error code instead)
+    int (*stat)(void *opaque, const char *fn, int *exists, PHYSFS_Stat *stat);
+} PHYSFS_Archiver;
+
+/**
+ * \fn int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver)
+ * \brief Add a new archiver to the system.
+ *
+ * !!! FIXME: write me.
+ *
+ * You may not have two archivers that handle the same extension. If you are
+ *  going to have a clash, you can deregister the other archiver (including
+ *  built-in ones) with PHYSFS_deregisterArchiver().
+ *
+ * The data in (archiver) is copied; you may free this pointer when this
+ *  function returns.
+ *
+ *   \param archiver The archiver to register.
+ *  \return Zero on error, non-zero on success.
+ *
+ * \sa PHYSFS_Archiver
+ * \sa PHYSFS_deregisterArchiver
+ */
+PHYSFS_DECL int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver);
+
+/**
+ * \fn int PHYSFS_deregisterArchiver(const char *ext)
+ * \brief Remove an archiver from the system.
+ *
+ * !!! FIXME: write me.
+ *
+ * This fails if there are any archives still open that use this archiver.
+ *
+ *   \param ext Filename extension that the archiver handles.
+ *  \return Zero on error, non-zero on success.
+ *
+ * \sa PHYSFS_Archiver
+ * \sa PHYSFS_registerArchiver
+ */
+PHYSFS_DECL int PHYSFS_deregisterArchiver(const char *ext);
+
+
 /* Everything above this line is part of the PhysicsFS 2.1 API. */
 
-
 #ifdef __cplusplus
 }
 #endif
--- a/src/physfs_internal.h	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/physfs_internal.h	Wed Nov 28 01:36:13 2012 -0500
@@ -124,137 +124,10 @@
 /* The latest supported PHYSFS_Io::version value. */
 #define CURRENT_PHYSFS_IO_API_VERSION 0
 
-/* Opaque data for file and dir handlers... */
-typedef void PHYSFS_Dir;
-
-typedef struct
-{
-    /*
-     * Basic info about this archiver...
-     */
-    const PHYSFS_ArchiveInfo info;
-
-
-    /*
-     * DIRECTORY ROUTINES:
-     * These functions are for dir handles. Generate a handle with the
-     *  openArchive() method, then pass it as the "opaque" PHYSFS_Dir to the
-     *  others.
-     *
-     * Symlinks should always be followed (except in stat()); PhysicsFS will
-     *  use the stat() method to check for symlinks and make a judgement on
-     *  whether to continue to call other methods based on that.
-     */
-
-        /*
-         * 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.
-         * (forWrite) 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. We ignore any error code you set here.
-         *  Returns non-NULL on success. The pointer returned will be
-         *  passed as the "opaque" parameter for later calls.
-         */
-    PHYSFS_Dir *(*openArchive)(PHYSFS_Io *io, const char *name, int forWrite);
-
-        /*
-         * List all files in (dirname). Each file is passed to (cb),
-         *  where a copy is made if appropriate, so you should dispose of
-         *  it properly upon return from the callback.
-         * You should omit symlinks if (omitSymLinks) is non-zero.
-         * If you have a failure, report as much as you can.
-         *  (dirname) is in platform-independent notation.
-         */
-    void (*enumerateFiles)(PHYSFS_Dir *opaque, const char *dirname,
-                           int omitSymLinks, PHYSFS_EnumFilesCallback cb,
-                           const char *origdir, void *callbackdata);
-
-        /*
-         * Open file for reading.
-         *  This filename, (fnm), is in platform-independent notation.
-         * If you can't handle multiple opens of the same file,
-         *  you can opt to fail for the second call.
-         * Fail if the file does not exist.
-         * 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 file calls.
-         *
-         * Regardless of success or failure, please set *exists to
-         *  non-zero if the file existed (even if it's a broken symlink!),
-         *  zero if it did not.
-         */
-    PHYSFS_Io *(*openRead)(PHYSFS_Dir *opaque, const char *fnm, int *exists);
+/* The latest supported PHYSFS_Archiver::version value. */
+#define CURRENT_PHYSFS_ARCHIVER_API_VERSION 0
 
-        /*
-         * Open file for writing.
-         * If the file does not exist, it should be created. If it exists,
-         *  it should be truncated to zero bytes. The writing
-         *  offset should be the start of the file.
-         * This filename is in platform-independent notation.
-         * If you can't handle multiple opens of the same file,
-         *  you can opt to fail for the second call.
-         * 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 file calls.
-         */
-    PHYSFS_Io *(*openWrite)(PHYSFS_Dir *opaque, const char *filename);
-
-        /*
-         * Open file for appending.
-         * If the file does not exist, it should be created. The writing
-         *  offset should be the end of the file.
-         * This filename is in platform-independent notation.
-         * If you can't handle multiple opens of the same file,
-         *  you can opt to fail for the second call.
-         * 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 file calls.
-         */
-    PHYSFS_Io *(*openAppend)(PHYSFS_Dir *opaque, const char *filename);
-
-        /*
-         * Delete a file in the archive/directory.
-         *  Return non-zero on success, zero on failure.
-         *  This filename is in platform-independent notation.
-         *  This method may be NULL.
-         * On failure, call __PHYSFS_setError().
-         */
-    int (*remove)(PHYSFS_Dir *opaque, const char *filename);
-
-        /*
-         * Create a directory in the archive/directory.
-         *  If the application is trying to make multiple dirs, PhysicsFS
-         *  will split them up into multiple calls before passing them to
-         *  your driver.
-         *  Return non-zero on success, zero on failure.
-         *  This filename is in platform-independent notation.
-         *  This method may be NULL.
-         * On failure, call __PHYSFS_setError().
-         */
-    int (*mkdir)(PHYSFS_Dir *opaque, const char *filename);
-
-        /*
-         * Close directories/archives, and free any associated memory,
-         *  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 (*closeArchive)(PHYSFS_Dir *opaque);
-
-        /*
-         * Obtain basic file metadata.
-         *  Returns non-zero on success, zero on failure.
-         *  On failure, call __PHYSFS_setError().
-         */
-    int (*stat)(PHYSFS_Dir *opaque, const char *fn,
-                int *exists, PHYSFS_Stat *stat);
-} PHYSFS_Archiver;
-
-
-/*
+/* !!! FIXME: update this documentation.
  * Call this to set the message returned by PHYSFS_getLastError().
  *  Please only use the ERR_* constants above, or add new constants to the
  *  above group, but I want these all in one place.
@@ -425,17 +298,17 @@
     PHYSFS_uint32 size;
 } UNPKentry;
 
-void UNPK_closeArchive(PHYSFS_Dir *opaque);
-PHYSFS_Dir *UNPK_openArchive(PHYSFS_Io *io,UNPKentry *e,const PHYSFS_uint32 n);
-void UNPK_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
+void UNPK_closeArchive(void *opaque);
+void *UNPK_openArchive(PHYSFS_Io *io,UNPKentry *e,const PHYSFS_uint32 n);
+void UNPK_enumerateFiles(void *opaque, const char *dname,
                          int omitSymLinks, PHYSFS_EnumFilesCallback cb,
                          const char *origdir, void *callbackdata);
-PHYSFS_Io *UNPK_openRead(PHYSFS_Dir *opaque, const char *fnm, int *fileExists);
-PHYSFS_Io *UNPK_openWrite(PHYSFS_Dir *opaque, const char *name);
-PHYSFS_Io *UNPK_openAppend(PHYSFS_Dir *opaque, const char *name);
-int UNPK_remove(PHYSFS_Dir *opaque, const char *name);
-int UNPK_mkdir(PHYSFS_Dir *opaque, const char *name);
-int UNPK_stat(PHYSFS_Dir *opaque, const char *fn, int *exist, PHYSFS_Stat *st);
+PHYSFS_Io *UNPK_openRead(void *opaque, const char *fnm, int *fileExists);
+PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name);
+PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name);
+int UNPK_remove(void *opaque, const char *name);
+int UNPK_mkdir(void *opaque, const char *name);
+int UNPK_stat(void *opaque, const char *fn, int *exist, PHYSFS_Stat *st);
 
 
 /*--------------------------------------------------------------------------*/
--- a/src/platform_posix.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/platform_posix.c	Wed Nov 28 01:36:13 2012 -0500
@@ -41,9 +41,9 @@
         case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
         case EMLINK: return PHYSFS_ERR_NO_SPACE;
         case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
-        case ENOENT: return PHYSFS_ERR_NO_SUCH_PATH;
+        case ENOENT: return PHYSFS_ERR_NOT_FOUND;
         case ENOSPC: return PHYSFS_ERR_NO_SPACE;
-        case ENOTDIR: return PHYSFS_ERR_NO_SUCH_PATH;
+        case ENOTDIR: return PHYSFS_ERR_NOT_FOUND;
         case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
         case EROFS: return PHYSFS_ERR_READ_ONLY;
         case ETXTBSY: return PHYSFS_ERR_BUSY;
--- a/src/platform_windows.c	Wed Nov 28 01:30:29 2012 -0500
+++ b/src/platform_windows.c	Wed Nov 28 01:36:13 2012 -0500
@@ -124,13 +124,13 @@
         case ERROR_INVALID_NAME: return PHYSFS_ERR_BAD_FILENAME;
         case ERROR_BAD_PATHNAME: return PHYSFS_ERR_BAD_FILENAME;
         case ERROR_DIRECTORY: return PHYSFS_ERR_BAD_FILENAME;
-        case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NO_SUCH_PATH;
-        case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NO_SUCH_PATH;
-        case ERROR_DELETE_PENDING: return PHYSFS_ERR_NO_SUCH_PATH;
-        case ERROR_INVALID_DRIVE: return PHYSFS_ERR_NO_SUCH_PATH;
+        case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
+        case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
+        case ERROR_DELETE_PENDING: return PHYSFS_ERR_NOT_FOUND;
+        case ERROR_INVALID_DRIVE: return PHYSFS_ERR_NOT_FOUND;
         case ERROR_HANDLE_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
         case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
-        /* !!! FIXME: ?? case ENOTDIR: return PHYSFS_ERR_NO_SUCH_PATH; */
+        /* !!! FIXME: ?? case ENOTDIR: return PHYSFS_ERR_NOT_FOUND; */
         /* !!! FIXME: ?? case EISDIR: return PHYSFS_ERR_NOT_A_FILE; */
         case ERROR_WRITE_PROTECT: return PHYSFS_ERR_READ_ONLY;
         case ERROR_LOCK_VIOLATION: return PHYSFS_ERR_BUSY;