Browse Source

Update PhysFS to 3.0.2.

Alex Szpakowski 6 years ago
parent
commit
e7c36022a8

+ 217 - 116
src/libraries/physfs/physfs.c

@@ -46,6 +46,8 @@ typedef struct __PHYSFS_DIRHANDLE__
     void *opaque;  /* Instance data unique to the archiver. */
     void *opaque;  /* Instance data unique to the archiver. */
     char *dirName;  /* Path to archive in platform-dependent notation. */
     char *dirName;  /* Path to archive in platform-dependent notation. */
     char *mountPoint; /* Mountpoint in virtual file tree. */
     char *mountPoint; /* Mountpoint in virtual file tree. */
+    char *root;  /* subdirectory of archiver to use as root of archive (NULL for actual root) */
+    size_t rootlen;  /* subdirectory of archiver to use as root of archive (NULL for actual root) */
     const PHYSFS_Archiver *funcs;  /* Ptr to archiver info for this handle. */
     const PHYSFS_Archiver *funcs;  /* Ptr to archiver info for this handle. */
     struct __PHYSFS_DIRHANDLE__ *next;  /* linked list stuff. */
     struct __PHYSFS_DIRHANDLE__ *next;  /* linked list stuff. */
 } DirHandle;
 } DirHandle;
@@ -86,6 +88,7 @@ static int allowSymLinks = 0;
 static PHYSFS_Archiver **archivers = NULL;
 static PHYSFS_Archiver **archivers = NULL;
 static PHYSFS_ArchiveInfo **archiveInfo = NULL;
 static PHYSFS_ArchiveInfo **archiveInfo = NULL;
 static volatile size_t numArchivers = 0;
 static volatile size_t numArchivers = 0;
+static size_t longest_root = 0;
 
 
 /* mutexes ... */
 /* mutexes ... */
 static void *errorLock = NULL;     /* protects error message list.        */
 static void *errorLock = NULL;     /* protects error message list.        */
@@ -879,13 +882,20 @@ static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
 
 
     if (io == NULL)
     if (io == NULL)
     {
     {
+        /* file doesn't exist, etc? Just fail out. */
+        PHYSFS_Stat statbuf;
+        BAIL_IF_ERRPASS(!__PHYSFS_platformStat(d, &statbuf, 1), NULL);
+
         /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
         /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
-        retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting, &claimed);
-        if (retval || claimed)
-            return retval;
+        if (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY)
+        {
+            retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting, &claimed);
+            if (retval || claimed)
+                return retval;
+        } /* if */
 
 
         io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
         io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
-        BAIL_IF_ERRPASS(!io, 0);
+        BAIL_IF_ERRPASS(!io, NULL);
         created_io = 1;
         created_io = 1;
     } /* if */
     } /* if */
 
 
@@ -973,6 +983,18 @@ static int sanitizePlatformIndependentPath(const char *src, char *dst)
 } /* sanitizePlatformIndependentPath */
 } /* sanitizePlatformIndependentPath */
 
 
 
 
+static inline size_t dirHandleRootLen(const DirHandle *h)
+{
+    return h ? h->rootlen : 0;
+} /* dirHandleRootLen */
+
+static inline int sanitizePlatformIndependentPathWithRoot(const DirHandle *h, const char *src, char *dst)
+{
+    return sanitizePlatformIndependentPath(src, dst + dirHandleRootLen(h));
+} /* sanitizePlatformIndependentPathWithRoot */
+
+
+
 /*
 /*
  * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an
  * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an
  *  output from sanitizePlatformIndependentPath(), so that it is in a known
  *  output from sanitizePlatformIndependentPath(), so that it is in a known
@@ -1371,6 +1393,7 @@ static int doDeinit(void)
         archivers = NULL;
         archivers = NULL;
     } /* if */
     } /* if */
 
 
+    longest_root = 0;
     allowSymLinks = 0;
     allowSymLinks = 0;
     initialized = 0;
     initialized = 0;
 
 
@@ -1677,6 +1700,54 @@ int PHYSFS_setWriteDir(const char *newDir)
 } /* PHYSFS_setWriteDir */
 } /* PHYSFS_setWriteDir */
 
 
 
 
+int PHYSFS_setRoot(const char *archive, const char *subdir)
+{
+    DirHandle *i;
+
+    BAIL_IF(!archive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+
+    __PHYSFS_platformGrabMutex(stateLock);
+
+    for (i = searchPath; i != NULL; i = i->next)
+    {
+        if ((i->dirName != NULL) && (strcmp(archive, i->dirName) == 0))
+        {
+            if (!subdir || (strcmp(subdir, "/") == 0))
+            {
+                if (i->root)
+                    allocator.Free(i->root);
+                i->root = NULL;
+                i->rootlen = 0;
+            } /* if */
+            else
+            {
+                const size_t len = strlen(subdir) + 1;
+                char *ptr = (char *) allocator.Malloc(len);
+                BAIL_IF_MUTEX(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
+                if (!sanitizePlatformIndependentPath(subdir, ptr))
+                {
+                    allocator.Free(ptr);
+                    BAIL_MUTEX_ERRPASS(stateLock, 0);
+                } /* if */
+
+                if (i->root)
+                    allocator.Free(i->root);
+                i->root = ptr;
+                i->rootlen = len;
+
+                if (longest_root < len)
+                    longest_root = len;
+            } /* else */
+
+            break;
+        } /* if */
+    } /* for */
+
+    __PHYSFS_platformReleaseMutex(stateLock);
+    return 1;
+} /* PHYSFS_setRoot */
+
+
 static int doMount(PHYSFS_Io *io, const char *fname,
 static int doMount(PHYSFS_Io *io, const char *fname,
                    const char *mountPoint, int appendToPath)
                    const char *mountPoint, int appendToPath)
 {
 {
@@ -1994,6 +2065,9 @@ int PHYSFS_symbolicLinksPermitted(void)
  *  like ".." which should be done once instead of once per archive. This also
  *  like ".." which should be done once instead of once per archive. This also
  *  gives us license to treat (fname) as scratch space in this function.
  *  gives us license to treat (fname) as scratch space in this function.
  *
  *
+ * (fname)'s buffer must have enough space available before it for this
+ *  function to prepend any root directory for this DirHandle.
+ *
  * Returns non-zero if string is safe, zero if there's a security issue.
  * Returns non-zero if string is safe, zero if there's a security issue.
  *  PHYSFS_getLastError() will specify what was wrong. (*fname) will be
  *  PHYSFS_getLastError() will specify what was wrong. (*fname) will be
  *  updated to point past any mount point elements so it is prepared to
  *  updated to point past any mount point elements so it is prepared to
@@ -2006,7 +2080,7 @@ static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
     char *start;
     char *start;
     char *end;
     char *end;
 
 
-    if (*fname == '\0')  /* quick rejection. */
+    if ((*fname == '\0') && (!h->root))  /* quick rejection. */
         return 1;
         return 1;
 
 
     /* !!! FIXME: This codeblock sucks. */
     /* !!! FIXME: This codeblock sucks. */
@@ -2029,6 +2103,17 @@ static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
         retval = 1;  /* may be reset, below. */
         retval = 1;  /* may be reset, below. */
     } /* if */
     } /* if */
 
 
+    /* prepend the root directory, if any. */
+    if (h->root)
+    {
+        const int isempty = (*fname == '\0');
+        fname -= h->rootlen - 1;
+        strcpy(fname, h->root);
+        if (!isempty)
+            fname[h->rootlen - 2] = '/';
+        *_fname = fname;
+    } /* if */
+
     start = fname;
     start = fname;
     if (!allowSymLinks)
     if (!allowSymLinks)
     {
     {
@@ -2074,20 +2159,19 @@ static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
 } /* verifyPath */
 } /* verifyPath */
 
 
 
 
+/* This must hold the stateLock before calling. */
 static int doMkdir(const char *_dname, char *dname)
 static int doMkdir(const char *_dname, char *dname)
 {
 {
-    DirHandle *h;
+    DirHandle *h = writeDir;
     char *start;
     char *start;
     char *end;
     char *end;
     int retval = 0;
     int retval = 0;
     int exists = 1;  /* force existance check on first path element. */
     int exists = 1;  /* force existance check on first path element. */
 
 
-    BAIL_IF_ERRPASS(!sanitizePlatformIndependentPath(_dname, dname), 0);
+    assert(h != NULL);
 
 
-    __PHYSFS_platformGrabMutex(stateLock);
-    BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
-    h = writeDir;
-    BAIL_IF_MUTEX_ERRPASS(!verifyPath(h, &dname, 1), stateLock, 0);
+    BAIL_IF_ERRPASS(!sanitizePlatformIndependentPathWithRoot(h, _dname, dname), 0);
+    BAIL_IF_ERRPASS(!verifyPath(h, &dname, 1), 0);
 
 
     start = dname;
     start = dname;
     while (1)
     while (1)
@@ -2119,7 +2203,6 @@ static int doMkdir(const char *_dname, char *dname)
         start = end + 1;
         start = end + 1;
     } /* while */
     } /* while */
 
 
-    __PHYSFS_platformReleaseMutex(stateLock);
     return retval;
     return retval;
 } /* doMkdir */
 } /* doMkdir */
 
 
@@ -2131,30 +2214,26 @@ int PHYSFS_mkdir(const char *_dname)
     size_t len;
     size_t len;
 
 
     BAIL_IF(!_dname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!_dname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
-    len = strlen(_dname) + 1;
+
+    __PHYSFS_platformGrabMutex(stateLock);
+    BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
+    len = strlen(_dname) + dirHandleRootLen(writeDir) + 1;
     dname = (char *) __PHYSFS_smallAlloc(len);
     dname = (char *) __PHYSFS_smallAlloc(len);
-    BAIL_IF(!dname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
+    BAIL_IF_MUTEX(!dname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
     retval = doMkdir(_dname, dname);
     retval = doMkdir(_dname, dname);
+    __PHYSFS_platformReleaseMutex(stateLock);
     __PHYSFS_smallFree(dname);
     __PHYSFS_smallFree(dname);
     return retval;
     return retval;
 } /* PHYSFS_mkdir */
 } /* PHYSFS_mkdir */
 
 
 
 
+/* This must hold the stateLock before calling. */
 static int doDelete(const char *_fname, char *fname)
 static int doDelete(const char *_fname, char *fname)
 {
 {
-    int retval;
-    DirHandle *h;
-    BAIL_IF_ERRPASS(!sanitizePlatformIndependentPath(_fname, fname), 0);
-
-    __PHYSFS_platformGrabMutex(stateLock);
-
-    BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
-    h = writeDir;
-    BAIL_IF_MUTEX_ERRPASS(!verifyPath(h, &fname, 0), stateLock, 0);
-    retval = h->funcs->remove(h->opaque, fname);
-
-    __PHYSFS_platformReleaseMutex(stateLock);
-    return retval;
+    DirHandle *h = writeDir;
+    BAIL_IF_ERRPASS(!sanitizePlatformIndependentPathWithRoot(h, _fname, fname), 0);
+    BAIL_IF_ERRPASS(!verifyPath(h, &fname, 0), 0);
+    return h->funcs->remove(h->opaque, fname);
 } /* doDelete */
 } /* doDelete */
 
 
 
 
@@ -2164,11 +2243,13 @@ int PHYSFS_delete(const char *_fname)
     char *fname;
     char *fname;
     size_t len;
     size_t len;
 
 
-    BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
-    len = strlen(_fname) + 1;
+    __PHYSFS_platformGrabMutex(stateLock);
+    BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
+    len = strlen(_fname) + dirHandleRootLen(writeDir) + 1;
     fname = (char *) __PHYSFS_smallAlloc(len);
     fname = (char *) __PHYSFS_smallAlloc(len);
-    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
+    BAIL_IF_MUTEX(!fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
     retval = doDelete(_fname, fname);
     retval = doDelete(_fname, fname);
+    __PHYSFS_platformReleaseMutex(stateLock);
     __PHYSFS_smallFree(fname);
     __PHYSFS_smallFree(fname);
     return retval;
     return retval;
 } /* PHYSFS_delete */
 } /* PHYSFS_delete */
@@ -2177,17 +2258,20 @@ int PHYSFS_delete(const char *_fname)
 static DirHandle *getRealDirHandle(const char *_fname)
 static DirHandle *getRealDirHandle(const char *_fname)
 {
 {
     DirHandle *retval = NULL;
     DirHandle *retval = NULL;
+    char *allocated_fname = NULL;
     char *fname = NULL;
     char *fname = NULL;
     size_t len;
     size_t len;
 
 
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
-    len = strlen(_fname) + 1;
-    fname = __PHYSFS_smallAlloc(len);
-    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
+
+    __PHYSFS_platformGrabMutex(stateLock);
+    len = strlen(_fname) + longest_root + 1;
+    allocated_fname = __PHYSFS_smallAlloc(len);
+    BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, NULL);
+    fname = allocated_fname + longest_root;
     if (sanitizePlatformIndependentPath(_fname, fname))
     if (sanitizePlatformIndependentPath(_fname, fname))
     {
     {
         DirHandle *i;
         DirHandle *i;
-        __PHYSFS_platformGrabMutex(stateLock);
         for (i = searchPath; i != NULL; i = i->next)
         for (i = searchPath; i != NULL; i = i->next)
         {
         {
             char *arcfname = fname;
             char *arcfname = fname;
@@ -2206,10 +2290,10 @@ static DirHandle *getRealDirHandle(const char *_fname)
                 } /* if */
                 } /* if */
             } /* if */
             } /* if */
         } /* for */
         } /* for */
-        __PHYSFS_platformReleaseMutex(stateLock);
     } /* if */
     } /* if */
 
 
-    __PHYSFS_smallFree(fname);
+    __PHYSFS_platformReleaseMutex(stateLock);
+    __PHYSFS_smallFree(allocated_fname);
     return retval;
     return retval;
 } /* getRealDirHandle */
 } /* getRealDirHandle */
 
 
@@ -2404,15 +2488,18 @@ int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data)
 {
 {
     PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
     PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
     size_t len;
     size_t len;
+    char *allocated_fname;
     char *fname;
     char *fname;
 
 
     BAIL_IF(!_fn, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!_fn, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!cb, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!cb, PHYSFS_ERR_INVALID_ARGUMENT, 0);
 
 
-    len = strlen(_fn) + 1;
-    fname = (char *) __PHYSFS_smallAlloc(len);
-    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
+    __PHYSFS_platformGrabMutex(stateLock);
 
 
+    len = strlen(_fn) + longest_root + 1;
+    allocated_fname = (char *) __PHYSFS_smallAlloc(len);
+    BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
+    fname = allocated_fname + longest_root;
     if (!sanitizePlatformIndependentPath(_fn, fname))
     if (!sanitizePlatformIndependentPath(_fn, fname))
         retval = PHYSFS_ENUM_STOP;
         retval = PHYSFS_ENUM_STOP;
     else
     else
@@ -2420,8 +2507,6 @@ int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data)
         DirHandle *i;
         DirHandle *i;
         SymlinkFilterData filterdata;
         SymlinkFilterData filterdata;
 
 
-        __PHYSFS_platformGrabMutex(stateLock);
-
         if (!allowSymLinks)
         if (!allowSymLinks)
         {
         {
             memset(&filterdata, '\0', sizeof (filterdata));
             memset(&filterdata, '\0', sizeof (filterdata));
@@ -2470,10 +2555,11 @@ int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data)
             } /* else if */
             } /* else if */
         } /* for */
         } /* for */
 
 
-        __PHYSFS_platformReleaseMutex(stateLock);
     } /* if */
     } /* if */
 
 
-    __PHYSFS_smallFree(fname);
+    __PHYSFS_platformReleaseMutex(stateLock);
+
+    __PHYSFS_smallFree(allocated_fname);
 
 
     return (retval == PHYSFS_ENUM_ERROR) ? 0 : 1;
     return (retval == PHYSFS_ENUM_ERROR) ? 0 : 1;
 } /* PHYSFS_enumerate */
 } /* PHYSFS_enumerate */
@@ -2534,57 +2620,58 @@ int PHYSFS_isSymbolicLink(const char *fname)
 } /* PHYSFS_isSymbolicLink */
 } /* PHYSFS_isSymbolicLink */
 
 
 
 
-static PHYSFS_File *doOpenWrite(const char *_fname, int appending)
+static PHYSFS_File *doOpenWrite(const char *_fname, const int appending)
 {
 {
     FileHandle *fh = NULL;
     FileHandle *fh = NULL;
+    DirHandle *h;
     size_t len;
     size_t len;
     char *fname;
     char *fname;
 
 
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
-    len = strlen(_fname) + 1;
-    fname = (char *) __PHYSFS_smallAlloc(len);
-    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
 
 
-    if (sanitizePlatformIndependentPath(_fname, fname))
-    {
-        PHYSFS_Io *io = NULL;
-        DirHandle *h = NULL;
-        const PHYSFS_Archiver *f;
-
-        __PHYSFS_platformGrabMutex(stateLock);
-
-        GOTO_IF(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, doOpenWriteEnd);
-
-        h = writeDir;
-        GOTO_IF_ERRPASS(!verifyPath(h, &fname, 0), doOpenWriteEnd);
+    __PHYSFS_platformGrabMutex(stateLock);
 
 
-        f = h->funcs;
-        if (appending)
-            io = f->openAppend(h->opaque, fname);
-        else
-            io = f->openWrite(h->opaque, fname);
+    h = writeDir;
+    BAIL_IF_MUTEX(!h, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
 
 
-        GOTO_IF_ERRPASS(!io, doOpenWriteEnd);
+    len = strlen(_fname) + dirHandleRootLen(h) + 1;
+    fname = (char *) __PHYSFS_smallAlloc(len);
+    BAIL_IF_MUTEX(!fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
 
 
-        fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
-        if (fh == NULL)
-        {
-            io->destroy(io);
-            GOTO(PHYSFS_ERR_OUT_OF_MEMORY, doOpenWriteEnd);
-        } /* if */
-        else
+    if (sanitizePlatformIndependentPathWithRoot(h, _fname, fname))
+    {
+        PHYSFS_Io *io = NULL;
+        char *arcfname = fname;
+        if (verifyPath(h, &arcfname, 0))
         {
         {
-            memset(fh, '\0', sizeof (FileHandle));
-            fh->io = io;
-            fh->dirHandle = h;
-            fh->next = openWriteList;
-            openWriteList = fh;
-        } /* else */
+            const PHYSFS_Archiver *f = h->funcs;
+            if (appending)
+                io = f->openAppend(h->opaque, arcfname);
+            else
+                io = f->openWrite(h->opaque, arcfname);
 
 
-        doOpenWriteEnd:
-        __PHYSFS_platformReleaseMutex(stateLock);
+            if (io)
+            {
+                fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
+                if (fh == NULL)
+                {
+                    io->destroy(io);
+                    PHYSFS_setErrorCode(PHYSFS_ERR_OUT_OF_MEMORY);
+                } /* if */
+                else
+                {
+                    memset(fh, '\0', sizeof (FileHandle));
+                    fh->io = io;
+                    fh->dirHandle = h;
+                    fh->next = openWriteList;
+                    openWriteList = fh;
+                } /* else */
+            } /* if */
+        } /* if */
     } /* if */
     } /* if */
 
 
+    __PHYSFS_platformReleaseMutex(stateLock);
+
     __PHYSFS_smallFree(fname);
     __PHYSFS_smallFree(fname);
     return ((PHYSFS_File *) fh);
     return ((PHYSFS_File *) fh);
 } /* doOpenWrite */
 } /* doOpenWrite */
@@ -2605,22 +2692,25 @@ PHYSFS_File *PHYSFS_openAppend(const char *filename)
 PHYSFS_File *PHYSFS_openRead(const char *_fname)
 PHYSFS_File *PHYSFS_openRead(const char *_fname)
 {
 {
     FileHandle *fh = NULL;
     FileHandle *fh = NULL;
+    char *allocated_fname;
     char *fname;
     char *fname;
     size_t len;
     size_t len;
 
 
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
-    len = strlen(_fname) + 1;
-    fname = (char *) __PHYSFS_smallAlloc(len);
-    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
+
+    __PHYSFS_platformGrabMutex(stateLock);
+
+    BAIL_IF_MUTEX(!searchPath, PHYSFS_ERR_NOT_FOUND, stateLock, 0);
+
+    len = strlen(_fname) + longest_root + 1;
+    allocated_fname = (char *) __PHYSFS_smallAlloc(len);
+    BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
+    fname = allocated_fname + longest_root;
 
 
     if (sanitizePlatformIndependentPath(_fname, fname))
     if (sanitizePlatformIndependentPath(_fname, fname))
     {
     {
-        DirHandle *i = NULL;
         PHYSFS_Io *io = NULL;
         PHYSFS_Io *io = NULL;
-
-        __PHYSFS_platformGrabMutex(stateLock);
-
-        GOTO_IF(!searchPath, PHYSFS_ERR_NOT_FOUND, openReadEnd);
+        DirHandle *i;
 
 
         for (i = searchPath; i != NULL; i = i->next)
         for (i = searchPath; i != NULL; i = i->next)
         {
         {
@@ -2633,27 +2723,26 @@ PHYSFS_File *PHYSFS_openRead(const char *_fname)
             } /* if */
             } /* if */
         } /* for */
         } /* for */
 
 
-        GOTO_IF_ERRPASS(!io, openReadEnd);
-
-        fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
-        if (fh == NULL)
+        if (io)
         {
         {
-            io->destroy(io);
-            GOTO(PHYSFS_ERR_OUT_OF_MEMORY, openReadEnd);
-        } /* if */
-
-        memset(fh, '\0', sizeof (FileHandle));
-        fh->io = io;
-        fh->forReading = 1;
-        fh->dirHandle = i;
-        fh->next = openReadList;
-        openReadList = fh;
+            fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
+            if (fh == NULL)
+            {
+                io->destroy(io);
+                PHYSFS_setErrorCode(PHYSFS_ERR_OUT_OF_MEMORY);
+            } /* if */
 
 
-        openReadEnd:
-        __PHYSFS_platformReleaseMutex(stateLock);
+            memset(fh, '\0', sizeof (FileHandle));
+            fh->io = io;
+            fh->forReading = 1;
+            fh->dirHandle = i;
+            fh->next = openReadList;
+            openReadList = fh;
+        } /* if */
     } /* if */
     } /* if */
 
 
-    __PHYSFS_smallFree(fname);
+    __PHYSFS_platformReleaseMutex(stateLock);
+    __PHYSFS_smallFree(allocated_fname);
     return ((PHYSFS_File *) fh);
     return ((PHYSFS_File *) fh);
 } /* PHYSFS_openRead */
 } /* PHYSFS_openRead */
 
 
@@ -2662,7 +2751,6 @@ static int closeHandleInOpenList(FileHandle **list, FileHandle *handle)
 {
 {
     FileHandle *prev = NULL;
     FileHandle *prev = NULL;
     FileHandle *i;
     FileHandle *i;
-    int rc = 1;
 
 
     for (i = *list; i != NULL; i = i->next)
     for (i = *list; i != NULL; i = i->next)
     {
     {
@@ -2670,9 +2758,19 @@ static int closeHandleInOpenList(FileHandle **list, FileHandle *handle)
         {
         {
             PHYSFS_Io *io = handle->io;
             PHYSFS_Io *io = handle->io;
             PHYSFS_uint8 *tmp = handle->buffer;
             PHYSFS_uint8 *tmp = handle->buffer;
-            rc = PHYSFS_flush((PHYSFS_File *) handle);
-            if (!rc)
-                return -1;
+
+            /* send our buffer to io... */
+            if (!handle->forReading)
+            {
+                if (!PHYSFS_flush((PHYSFS_File *) handle))
+                    return -1;
+
+                /* ...then have io send it to the disk... */
+                else if (io->flush && !io->flush(io))
+                    return -1;
+            } /* if */
+
+            /* ...then close the underlying file. */
             io->destroy(io);
             io->destroy(io);
 
 
             if (tmp != NULL)  /* free any associated buffer. */
             if (tmp != NULL)  /* free any associated buffer. */
@@ -2970,21 +3068,19 @@ int PHYSFS_flush(PHYSFS_File *handle)
     rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
     rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
     BAIL_IF_ERRPASS(rc <= 0, 0);
     BAIL_IF_ERRPASS(rc <= 0, 0);
     fh->bufpos = fh->buffill = 0;
     fh->bufpos = fh->buffill = 0;
-    return io->flush ? io->flush(io) : 1;
+    return 1;
 } /* PHYSFS_flush */
 } /* PHYSFS_flush */
 
 
 
 
 int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
 int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
 {
 {
     int retval = 0;
     int retval = 0;
+    char *allocated_fname;
     char *fname;
     char *fname;
     size_t len;
     size_t len;
 
 
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     BAIL_IF(!stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
-    len = strlen(_fname) + 1;
-    fname = (char *) __PHYSFS_smallAlloc(len);
-    BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
 
 
     /* set some sane defaults... */
     /* set some sane defaults... */
     stat->filesize = -1;
     stat->filesize = -1;
@@ -2994,6 +3090,12 @@ int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
     stat->filetype = PHYSFS_FILETYPE_OTHER;
     stat->filetype = PHYSFS_FILETYPE_OTHER;
     stat->readonly = 1;
     stat->readonly = 1;
 
 
+    __PHYSFS_platformGrabMutex(stateLock);
+    len = strlen(_fname) + longest_root + 1;
+    allocated_fname = (char *) __PHYSFS_smallAlloc(len);
+    BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
+    fname = allocated_fname + longest_root;
+
     if (sanitizePlatformIndependentPath(_fname, fname))
     if (sanitizePlatformIndependentPath(_fname, fname))
     {
     {
         if (*fname == '\0')
         if (*fname == '\0')
@@ -3006,7 +3108,6 @@ int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
         {
         {
             DirHandle *i;
             DirHandle *i;
             int exists = 0;
             int exists = 0;
-            __PHYSFS_platformGrabMutex(stateLock);
             for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
             for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
             {
             {
                 char *arcfname = fname;
                 char *arcfname = fname;
@@ -3024,11 +3125,11 @@ int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
                         exists = 1;
                         exists = 1;
                 } /* else if */
                 } /* else if */
             } /* for */
             } /* for */
-            __PHYSFS_platformReleaseMutex(stateLock);
         } /* else */
         } /* else */
     } /* if */
     } /* if */
 
 
-    __PHYSFS_smallFree(fname);
+    __PHYSFS_platformReleaseMutex(stateLock);
+    __PHYSFS_smallFree(allocated_fname);
     return retval;
     return retval;
 } /* PHYSFS_stat */
 } /* PHYSFS_stat */
 
 

+ 45 - 5
src/libraries/physfs/physfs.h

@@ -225,11 +225,11 @@ extern "C" {
 
 
 #if defined(PHYSFS_DECL)
 #if defined(PHYSFS_DECL)
 /* do nothing. */
 /* do nothing. */
-#elif (defined _MSC_VER)
+#elif defined(_MSC_VER)
 #define PHYSFS_DECL __declspec(dllexport)
 #define PHYSFS_DECL __declspec(dllexport)
-#elif (defined __SUNPRO_C)
+#elif defined(__SUNPRO_C)
 #define PHYSFS_DECL __global
 #define PHYSFS_DECL __global
-#elif ((__GNUC__ >= 3) && (!__EMX__) && (!sun))
+#elif ((__GNUC__ >= 3) && (!defined(__EMX__)) && (!defined(sun)))
 #define PHYSFS_DECL __attribute__((visibility("default")))
 #define PHYSFS_DECL __attribute__((visibility("default")))
 #else
 #else
 #define PHYSFS_DECL
 #define PHYSFS_DECL
@@ -433,8 +433,8 @@ typedef struct PHYSFS_Version
 
 
 #ifndef DOXYGEN_SHOULD_IGNORE_THIS
 #ifndef DOXYGEN_SHOULD_IGNORE_THIS
 #define PHYSFS_VER_MAJOR 3
 #define PHYSFS_VER_MAJOR 3
-#define PHYSFS_VER_MINOR 0
-#define PHYSFS_VER_PATCH 1
+#define PHYSFS_VER_MINOR 1
+#define PHYSFS_VER_PATCH 0
 #endif  /* DOXYGEN_SHOULD_IGNORE_THIS */
 #endif  /* DOXYGEN_SHOULD_IGNORE_THIS */
 
 
 
 
@@ -3844,6 +3844,46 @@ PHYSFS_DECL int PHYSFS_deregisterArchiver(const char *ext);
 
 
 /* Everything above this line is part of the PhysicsFS 2.1 API. */
 /* Everything above this line is part of the PhysicsFS 2.1 API. */
 
 
+
+/**
+ * \fn int PHYSFS_setRoot(const char *archive, const char *subdir)
+ * \brief Make a subdirectory of an archive its root directory.
+ *
+ * This lets you narrow down the accessible files in a specific archive. For
+ *  example, if you have x.zip with a file in y/z.txt, mounted to /a, if you
+ *  call PHYSFS_setRoot("x.zip", "/y"), then the call
+ *  PHYSFS_openRead("/a/z.txt") will succeed.
+ *
+ * You can change an archive's root at any time, altering the interpolated
+ *  file tree (depending on where paths shift, a different archive may be
+ *  providing various files). If you set the root to NULL or "/", the
+ *  archive will be treated as if no special root was set (as if the archive
+ *  was just mounted normally).
+ *
+ * Changing the root only affects future operations on pathnames; a file
+ *  that was opened from a path that changed due to a setRoot will not be
+ *  affected.
+ *
+ * Setting a new root is not limited to archives in the search path; you may
+ *  set one on the write dir, too, which might be useful if you have files
+ *  open for write and thus can't change the write dir at the moment.
+ *
+ * It is not an error to set a subdirectory that does not exist to be the
+ *  root of an archive; however, no files will be visible in this case. If
+ *  the missing directories end up getting created (a mkdir to the physical
+ *  filesystem, etc) then this will be reflected in the interpolated tree.
+ *
+ *    \param archive dir/archive on which to change root.
+ *    \param subdir new subdirectory to make the root of this archive.
+ *   \return nonzero on success, zero on failure. Use
+ *           PHYSFS_getLastErrorCode() to obtain the specific error.
+ */
+PHYSFS_DECL int PHYSFS_setRoot(const char *archive, const char *subdir);
+
+
+/* Everything above this line is part of the PhysicsFS 3.1 API. */
+
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 2 - 0
src/libraries/physfs/physfs_archiver_7z.c

@@ -203,6 +203,8 @@ static void SZIP_closeArchive(void *opaque)
     SZIPinfo *info = (SZIPinfo *) opaque;
     SZIPinfo *info = (SZIPinfo *) opaque;
     if (info)
     if (info)
     {
     {
+        if (info->io)
+            info->io->destroy(info->io);
         SzArEx_Free(&info->db, &SZIP_SzAlloc);
         SzArEx_Free(&info->db, &SZIP_SzAlloc);
         __PHYSFS_DirTreeDeinit(&info->tree);
         __PHYSFS_DirTreeDeinit(&info->tree);
         allocator.Free(info);
         allocator.Free(info);

+ 19 - 10
src/libraries/physfs/physfs_internal.h

@@ -162,35 +162,44 @@ void __PHYSFS_smallFree(void *ptr);
 #define free(x) Do not use free() directly.
 #define free(x) Do not use free() directly.
 /* !!! FIXME: add alloca check here. */
 /* !!! FIXME: add alloca check here. */
 
 
+
+/* by default, enable things, so builds can opt out of a few things they
+   want to avoid. But you can build with this #defined to 0 if you would
+   like to turn off everything except a handful of things you opt into. */
+#ifndef PHYSFS_SUPPORTS_DEFAULT
+#define PHYSFS_SUPPORTS_DEFAULT 1
+#endif
+
+
 #ifndef PHYSFS_SUPPORTS_ZIP
 #ifndef PHYSFS_SUPPORTS_ZIP
-#define PHYSFS_SUPPORTS_ZIP 1
+#define PHYSFS_SUPPORTS_ZIP PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_7Z
 #ifndef PHYSFS_SUPPORTS_7Z
-#define PHYSFS_SUPPORTS_7Z 1
+#define PHYSFS_SUPPORTS_7Z PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_GRP
 #ifndef PHYSFS_SUPPORTS_GRP
-#define PHYSFS_SUPPORTS_GRP 1
+#define PHYSFS_SUPPORTS_GRP PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_HOG
 #ifndef PHYSFS_SUPPORTS_HOG
-#define PHYSFS_SUPPORTS_HOG 1
+#define PHYSFS_SUPPORTS_HOG PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_MVL
 #ifndef PHYSFS_SUPPORTS_MVL
-#define PHYSFS_SUPPORTS_MVL 1
+#define PHYSFS_SUPPORTS_MVL PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_WAD
 #ifndef PHYSFS_SUPPORTS_WAD
-#define PHYSFS_SUPPORTS_WAD 1
+#define PHYSFS_SUPPORTS_WAD PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_QPAK
 #ifndef PHYSFS_SUPPORTS_QPAK
-#define PHYSFS_SUPPORTS_QPAK 1
+#define PHYSFS_SUPPORTS_QPAK PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_SLB
 #ifndef PHYSFS_SUPPORTS_SLB
-#define PHYSFS_SUPPORTS_SLB 1
+#define PHYSFS_SUPPORTS_SLB PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_ISO9660
 #ifndef PHYSFS_SUPPORTS_ISO9660
-#define PHYSFS_SUPPORTS_ISO9660 1
+#define PHYSFS_SUPPORTS_ISO9660 PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 #ifndef PHYSFS_SUPPORTS_VDF
 #ifndef PHYSFS_SUPPORTS_VDF
-#define PHYSFS_SUPPORTS_VDF 1
+#define PHYSFS_SUPPORTS_VDF PHYSFS_SUPPORTS_DEFAULT
 #endif
 #endif
 
 
 #if PHYSFS_SUPPORTS_7Z
 #if PHYSFS_SUPPORTS_7Z

+ 1 - 0
src/libraries/physfs/physfs_miniz.h

@@ -699,3 +699,4 @@ static int mz_inflateEnd(mz_streamp pStream)
 
 
   For more information, please refer to <https://unlicense.org/>
   For more information, please refer to <https://unlicense.org/>
 */
 */
+

+ 1 - 1
src/libraries/physfs/physfs_platform_apple.m

@@ -50,7 +50,7 @@ char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
     {
     {
         NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, TRUE);
         NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, TRUE);
         BAIL_IF(!paths, PHYSFS_ERR_OS_ERROR, NULL);
         BAIL_IF(!paths, PHYSFS_ERR_OS_ERROR, NULL);
-        NSString *path = (NSString *) paths[0];
+        NSString *path = (NSString *) [paths objectAtIndex:0];
         BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL);
         BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL);
         size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
         size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
         const size_t applen = strlen(app);
         const size_t applen = strlen(app);

+ 10 - 8
src/libraries/physfs/physfs_platform_windows.c

@@ -572,18 +572,20 @@ char *__PHYSFS_platformCalcUserDir(void)
         /*
         /*
          * Should fail. Will write the size of the profile path in
          * Should fail. Will write the size of the profile path in
          *  psize. Also note that the second parameter can't be
          *  psize. Also note that the second parameter can't be
-         *  NULL or the function fails.
-         */
-        /*
-         *  EDIT: (03.10.2018) after Windows 10 Update 1809 psize will be zero 
-         *  if something other than NULL is passed for the second argument.
-         *  Passing NULL now makes GetUserProfileDirectoryW fail (rc == 0)
-         *  and psize receives the correct user directory length.
-         *  It seems to work fine on Windows 7 and Windows 10 Update 1803 too
+         *  NULL or the function fails on Windows XP, but has to be NULL on
+         *  Windows 10 or it will fail.  :(
          */
          */
         rc = pGetDir(accessToken, NULL, &psize);
         rc = pGetDir(accessToken, NULL, &psize);
         GOTO_IF(rc, PHYSFS_ERR_OS_ERROR, done);  /* should have failed! */
         GOTO_IF(rc, PHYSFS_ERR_OS_ERROR, done);  /* should have failed! */
 
 
+        if (psize == 0)  /* probably on Windows XP, try a different way. */
+        {
+            WCHAR x = 0;
+            rc = pGetDir(accessToken, &x, &psize);
+            GOTO_IF(rc, PHYSFS_ERR_OS_ERROR, done);  /* should have failed! */
+            GOTO_IF(!psize, PHYSFS_ERR_OS_ERROR, done);  /* Uhoh... */
+        } /* if */
+
         /* Allocate memory for the profile directory */
         /* Allocate memory for the profile directory */
         wstr = (LPWSTR) __PHYSFS_smallAlloc((psize + 1) * sizeof (WCHAR));
         wstr = (LPWSTR) __PHYSFS_smallAlloc((psize + 1) * sizeof (WCHAR));
         if (wstr != NULL)
         if (wstr != NULL)