Fix engine not starting on Windows if linked against SDL > 2.0.5
[xonotic/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 4288ab5..87367a7 100644 (file)
--- a/fs.c
+++ b/fs.c
                Boston, MA  02111-1307, USA
 */
 
-#ifdef __APPLE__
-// include SDL for IPHONEOS code
-# include <TargetConditionals.h>
-# if TARGET_OS_IPHONE
-#  include <SDL.h>
-# endif
-#endif
-
 #include <limits.h>
 #include <fcntl.h>
 
@@ -37,6 +29,8 @@
 # include <direct.h>
 # include <io.h>
 # include <shlobj.h>
+# include <sys/stat.h>
+# include <share.h>
 #else
 # include <pwd.h>
 # include <sys/stat.h>
 
 #include "quakedef.h"
 
+#if TARGET_OS_IPHONE
+// include SDL for IPHONEOS code
+# include <SDL.h>
+#endif
+
+#include "thread.h"
+
 #include "fs.h"
 #include "wad.h"
 
 # define lseek _lseeki64
 #endif
 
-#if _MSC_VER >= 1400
 // suppress deprecated warnings
-# include <sys/stat.h>
-# include <share.h>
+#if _MSC_VER >= 1400
 # define read _read
 # define write _write
 # define close _close
 # define dup _dup
 #endif
 
+#if USE_RWOPS
+# include <SDL.h>
+typedef SDL_RWops *filedesc_t;
+# define FILEDESC_INVALID NULL
+# define FILEDESC_ISVALID(fd) ((fd) != NULL)
+# define FILEDESC_READ(fd,buf,count) ((fs_offset_t)SDL_RWread(fd, buf, 1, count))
+# define FILEDESC_WRITE(fd,buf,count) ((fs_offset_t)SDL_RWwrite(fd, buf, 1, count))
+# define FILEDESC_CLOSE SDL_RWclose
+# define FILEDESC_SEEK SDL_RWseek
+static filedesc_t FILEDESC_DUP(const char *filename, filedesc_t fd) {
+       filedesc_t new_fd = SDL_RWFromFile(filename, "rb");
+       if (SDL_RWseek(new_fd, SDL_RWseek(fd, 0, RW_SEEK_CUR), RW_SEEK_SET) < 0) {
+               SDL_RWclose(new_fd);
+               return NULL;
+       }
+       return new_fd;
+}
+# define unlink(name) Con_DPrintf("Sorry, no unlink support when trying to unlink %s.\n", (name))
+#else
+typedef int filedesc_t;
+# define FILEDESC_INVALID -1
+# define FILEDESC_ISVALID(fd) ((fd) != -1)
+# define FILEDESC_READ read
+# define FILEDESC_WRITE write
+# define FILEDESC_CLOSE close
+# define FILEDESC_SEEK lseek
+static filedesc_t FILEDESC_DUP(const char *filename, filedesc_t fd) {
+       return dup(fd);
+}
+#endif
+
 /** \page fs File System
 
 All of Quake's data access is through a hierchal file system, but the contents
@@ -213,7 +243,7 @@ typedef struct
 struct qfile_s
 {
        int                             flags;
-       int                             handle;                                 ///< file descriptor
+       filedesc_t                      handle;                                 ///< file descriptor
        fs_offset_t             real_length;                    ///< uncompressed file size (for files opened in "read" mode)
        fs_offset_t             position;                               ///< current position in the file
        fs_offset_t             offset;                                 ///< offset into the package (0 if external file)
@@ -287,7 +317,7 @@ typedef struct pack_s
 {
        char filename [MAX_OSPATH];
        char shortname [MAX_QPATH];
-       int handle;
+       filedesc_t handle;
        int ignorecase;  ///< PK3 ignores case
        int numfiles;
        qboolean vpack;
@@ -332,6 +362,7 @@ VARIABLES
 */
 
 mempool_t *fs_mempool;
+void *fs_mutex = NULL;
 
 searchpath_t *fs_searchpaths = NULL;
 const char *const fs_checkgamedir_missing = "missing";
@@ -461,7 +492,7 @@ PK3_CloseLibrary
 Unload the Zlib DLL
 ====================
 */
-void PK3_CloseLibrary (void)
+static void PK3_CloseLibrary (void)
 {
 #ifndef LINK_TO_ZLIB
        Sys_UnloadLibrary (&zlib_dll);
@@ -476,7 +507,7 @@ PK3_OpenLibrary
 Try to load the Zlib DLL
 ====================
 */
-qboolean PK3_OpenLibrary (void)
+static qboolean PK3_OpenLibrary (void)
 {
 #ifdef LINK_TO_ZLIB
        return true;
@@ -532,14 +563,14 @@ PK3_GetEndOfCentralDir
 Extract the end of the central directory from a PK3 package
 ====================
 */
-qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
+static qboolean PK3_GetEndOfCentralDir (const char *packfile, filedesc_t packhandle, pk3_endOfCentralDir_t *eocd)
 {
        fs_offset_t filesize, maxsize;
        unsigned char *buffer, *ptr;
        int ind;
 
        // Get the package size
-       filesize = lseek (packhandle, 0, SEEK_END);
+       filesize = FILEDESC_SEEK (packhandle, 0, SEEK_END);
        if (filesize < ZIP_END_CDIR_SIZE)
                return false;
 
@@ -549,8 +580,8 @@ qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOf
        else
                maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
        buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
-       lseek (packhandle, filesize - maxsize, SEEK_SET);
-       if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
+       FILEDESC_SEEK (packhandle, filesize - maxsize, SEEK_SET);
+       if (FILEDESC_READ (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
        {
                Mem_Free (buffer);
                return false;
@@ -586,6 +617,16 @@ qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOf
 
        Mem_Free (buffer);
 
+       if (
+                       eocd->cdir_size > filesize ||
+                       eocd->cdir_offset >= filesize ||
+                       eocd->cdir_offset + eocd->cdir_size > filesize
+          )
+       {
+               // Obviously invalid central directory.
+               return false;
+       }
+
        return true;
 }
 
@@ -597,7 +638,7 @@ PK3_BuildFileList
 Extract the file list from a PK3 file
 ====================
 */
-int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
+static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
 {
        unsigned char *central_dir, *ptr;
        unsigned int ind;
@@ -605,8 +646,12 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
 
        // Load the central directory in memory
        central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
-       lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
-       if(read (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
+       if (FILEDESC_SEEK (pack->handle, eocd->cdir_offset, SEEK_SET) == -1)
+       {
+               Mem_Free (central_dir);
+               return -1;
+       }
+       if(FILEDESC_READ (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
        {
                Mem_Free (central_dir);
                return -1;
@@ -654,7 +699,7 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
                if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
                {
                        // Still enough bytes for the name?
-                       if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
+                       if (namesize < 0 || remaining < namesize || namesize >= (int)sizeof (*pack->files))
                        {
                                Mem_Free (central_dir);
                                return -1;
@@ -717,7 +762,7 @@ FS_LoadPackPK3
 Create a package entry associated with a PK3 file
 ====================
 */
-pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean silent)
+static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle, qboolean silent)
 {
        pk3_endOfCentralDir_t eocd;
        pack_t *pack;
@@ -727,7 +772,7 @@ pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean sil
        {
                if(!silent)
                        Con_Printf ("%s is not a PK3 file\n", packfile);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
 
@@ -735,7 +780,7 @@ pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean sil
        if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
        {
                Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
 
@@ -745,7 +790,7 @@ pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean sil
        if (eocd.nbentries > MAX_FILES_IN_PACK)
        {
                Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
 #endif
@@ -762,7 +807,7 @@ pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean sil
        if (real_nb_files < 0)
        {
                Con_Printf ("%s is not a valid PK3 file\n", packfile);
-               close(pack->handle);
+               FILEDESC_CLOSE(pack->handle);
                Mem_Free(pack);
                return NULL;
        }
@@ -770,15 +815,13 @@ pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean sil
        Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
        return pack;
 }
-pack_t *FS_LoadPackPK3 (const char *packfile)
+
+static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking);
+static pack_t *FS_LoadPackPK3 (const char *packfile)
 {
-       int packhandle;
-#if _MSC_VER >= 1400
-       _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
-#else
-       packhandle = open (packfile, O_RDONLY | O_BINARY);
-#endif
-       if (packhandle < 0)
+       filedesc_t packhandle;
+       packhandle = FS_SysOpenFiledesc (packfile, "rb", false);
+       if (!FILEDESC_ISVALID(packhandle))
                return NULL;
        return FS_LoadPackPK3FromFD(packfile, packhandle, false);
 }
@@ -791,7 +834,7 @@ PK3_GetTrueFileOffset
 Find where the true file data offset is
 ====================
 */
-qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
+static qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
 {
        unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
        fs_offset_t count;
@@ -801,8 +844,12 @@ qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
                return true;
 
        // Load the local file description
-       lseek (pack->handle, pfile->offset, SEEK_SET);
-       count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
+       if (FILEDESC_SEEK (pack->handle, pfile->offset, SEEK_SET) == -1)
+       {
+               Con_Printf ("Can't seek in package %s\n", pack->filename);
+               return false;
+       }
+       count = FILEDESC_READ (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
        if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
        {
                Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
@@ -879,6 +926,25 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
 }
 
 
+static void FS_mkdir (const char *path)
+{
+       if(COM_CheckParm("-readonly"))
+               return;
+
+#if WIN32
+       if (_mkdir (path) == -1)
+#else
+       if (mkdir (path, 0777) == -1)
+#endif
+       {
+               // No logging for this. The only caller is FS_CreatePath (which
+               // calls it in ways that will intentionally produce EEXIST),
+               // and its own callers always use the directory afterwards and
+               // thus will detect failure that way.
+       }
+}
+
+
 /*
 ============
 FS_CreatePath
@@ -910,7 +976,7 @@ FS_Path_f
 
 ============
 */
-void FS_Path_f (void)
+static void FS_Path_f (void)
 {
        searchpath_t *s;
 
@@ -939,31 +1005,27 @@ FS_LoadPackPAK
  *Loads the header and directory, adding the files at the beginning
  *of the list so they override previous pack files.
  */
-pack_t *FS_LoadPackPAK (const char *packfile)
+static pack_t *FS_LoadPackPAK (const char *packfile)
 {
        dpackheader_t header;
        int i, numpackfiles;
-       int packhandle;
+       filedesc_t packhandle;
        pack_t *pack;
        dpackfile_t *info;
 
-#if _MSC_VER >= 1400
-       _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
-#else
-       packhandle = open (packfile, O_RDONLY | O_BINARY);
-#endif
-       if (packhandle < 0)
+       packhandle = FS_SysOpenFiledesc(packfile, "rb", false);
+       if (!FILEDESC_ISVALID(packhandle))
                return NULL;
-       if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
+       if(FILEDESC_READ (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
        {
                Con_Printf ("%s is not a packfile\n", packfile);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
        if (memcmp(header.id, "PACK", 4))
        {
                Con_Printf ("%s is not a packfile\n", packfile);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
        header.dirofs = LittleLong (header.dirofs);
@@ -972,31 +1034,31 @@ pack_t *FS_LoadPackPAK (const char *packfile)
        if (header.dirlen % sizeof(dpackfile_t))
        {
                Con_Printf ("%s has an invalid directory size\n", packfile);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
 
        numpackfiles = header.dirlen / sizeof(dpackfile_t);
 
-       if (numpackfiles > MAX_FILES_IN_PACK)
+       if (numpackfiles < 0 || numpackfiles > MAX_FILES_IN_PACK)
        {
                Con_Printf ("%s has %i files\n", packfile, numpackfiles);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
 
        info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
-       lseek (packhandle, header.dirofs, SEEK_SET);
-       if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
+       FILEDESC_SEEK (packhandle, header.dirofs, SEEK_SET);
+       if(header.dirlen != FILEDESC_READ (packhandle, (void *)info, header.dirlen))
        {
                Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
                Mem_Free(info);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
 
        pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
-       pack->ignorecase = false; // PAK is case sensitive
+       pack->ignorecase = true; // PAK is sensitive in Quake1 but insensitive in Quake2
        strlcpy (pack->filename, packfile, sizeof (pack->filename));
        pack->handle = packhandle;
        pack->numfiles = 0;
@@ -1008,6 +1070,9 @@ pack_t *FS_LoadPackPAK (const char *packfile)
                fs_offset_t offset = (unsigned int)LittleLong (info[i].filepos);
                fs_offset_t size = (unsigned int)LittleLong (info[i].filelen);
 
+               // Ensure a zero terminated file name (required by format).
+               info[i].name[sizeof(info[i].name) - 1] = 0;
+
                FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
        }
 
@@ -1024,14 +1089,14 @@ FS_LoadPackVirtual
 Create a package entry associated with a directory file
 ====================
 */
-pack_t *FS_LoadPackVirtual (const char *dirname)
+static pack_t *FS_LoadPackVirtual (const char *dirname)
 {
        pack_t *pack;
        pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
        pack->vpack = true;
        pack->ignorecase = false;
        strlcpy (pack->filename, dirname, sizeof(pack->filename));
-       pack->handle = -1;
+       pack->handle = FILEDESC_INVALID;
        pack->numfiles = -1;
        pack->files = NULL;
        Con_DPrintf("Added packfile %s (virtual pack)\n", dirname);
@@ -1079,6 +1144,8 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname,
                pak = FS_LoadPackPAK (pakfile);
        else if(!strcasecmp(ext, "pk3"))
                pak = FS_LoadPackPK3 (pakfile);
+       else if(!strcasecmp(ext, "obb")) // android apk expansion
+               pak = FS_LoadPackPK3 (pakfile);
        else
                Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
 
@@ -1196,7 +1263,7 @@ Sets fs_gamedir, adds the directory to the head of the path,
 then loads and adds pak1.pak pak2.pak ...
 ================
 */
-void FS_AddGameDirectory (const char *dir)
+static void FS_AddGameDirectory (const char *dir)
 {
        int i;
        stringlist_t list;
@@ -1220,7 +1287,7 @@ void FS_AddGameDirectory (const char *dir)
        // add any PK3 package in the directory
        for (i = 0;i < list.numstrings;i++)
        {
-               if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir"))
+               if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "obb") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir"))
                {
                        FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
                }
@@ -1242,13 +1309,14 @@ void FS_AddGameDirectory (const char *dir)
 FS_AddGameHierarchy
 ================
 */
-void FS_AddGameHierarchy (const char *dir)
+static void FS_AddGameHierarchy (const char *dir)
 {
+       char vabuf[1024];
        // Add the common game directory
-       FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
+       FS_AddGameDirectory (va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, dir));
 
        if (*fs_userdir)
-               FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
+               FS_AddGameDirectory(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, dir));
 }
 
 
@@ -1302,7 +1370,7 @@ const char *FS_FileWithoutPath (const char *in)
 FS_ClearSearchPath
 ================
 */
-void FS_ClearSearchPath (void)
+static void FS_ClearSearchPath (void)
 {
        // unload all packs and directory information, close all pack files
        // (if a qfile is still reading a pack it won't be harmed because it used
@@ -1316,7 +1384,7 @@ void FS_ClearSearchPath (void)
                        if(!search->pack->vpack)
                        {
                                // close the file
-                               close(search->pack->handle);
+                               FILEDESC_CLOSE(search->pack->handle);
                                // free any memory associated with it
                                if (search->pack->files)
                                        Mem_Free(search->pack->files);
@@ -1351,6 +1419,7 @@ void FS_Rescan (void)
        qboolean fs_modified = false;
        qboolean reset = false;
        char gamedirbuf[MAX_INPUTLINE];
+       char vabuf[1024];
 
        if (fs_searchpaths)
                reset = true;
@@ -1389,7 +1458,7 @@ void FS_Rescan (void)
                // update the com_modname (used server info)
                strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
                if(i)
-                       strlcat(gamedirbuf, va(" %s", fs_gamedirs[i]), sizeof(gamedirbuf));
+                       strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf));
                else
                        strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
        }
@@ -1410,7 +1479,7 @@ void FS_Rescan (void)
 
        // If "-condebug" is in the command line, remove the previous log file
        if (COM_CheckParm ("-condebug") != 0)
-               unlink (va("%s/qconsole.log", fs_gamedir));
+               unlink (va(vabuf, sizeof(vabuf), "%s/qconsole.log", fs_gamedir));
 
        // look for the pop.lmp file and set registered to true if it is found
        if (FS_FileExists("gfx/pop.lmp"))
@@ -1444,7 +1513,7 @@ void FS_Rescan (void)
        W_UnloadAll();
 }
 
-void FS_Rescan_f(void)
+static void FS_Rescan_f(void)
 {
        FS_Rescan();
 }
@@ -1454,10 +1523,7 @@ void FS_Rescan_f(void)
 FS_ChangeGameDirs
 ================
 */
-extern void Host_SaveConfig (void);
-extern void Host_LoadConfig_f (void);
 extern qboolean vid_opened;
-extern void VID_Stop(void);
 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
 {
        int i;
@@ -1530,7 +1596,7 @@ qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean
 FS_GameDir_f
 ================
 */
-void FS_GameDir_f (void)
+static void FS_GameDir_f (void)
 {
        int i;
        int numgamedirs;
@@ -1568,13 +1634,13 @@ void FS_GameDir_f (void)
        FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
 }
 
-static const char *FS_SysCheckGameDir(const char *gamedir)
+static const char *FS_SysCheckGameDir(const char *gamedir, char *buf, size_t buflength)
 {
-       static char buf[8192];
        qboolean success;
        qfile_t *f;
        stringlist_t list;
        fs_offset_t n;
+       char vabuf[1024];
 
        stringlistinit(&list);
        listdirectory(&list, gamedir, "");
@@ -1583,10 +1649,10 @@ static const char *FS_SysCheckGameDir(const char *gamedir)
 
        if(success)
        {
-               f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
+               f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%smodinfo.txt", gamedir), "r", false);
                if(f)
                {
-                       n = FS_Read (f, buf, sizeof(buf) - 1);
+                       n = FS_Read (f, buf, buflength - 1);
                        if(n >= 0)
                                buf[n] = 0;
                        else
@@ -1609,17 +1675,19 @@ FS_CheckGameDir
 const char *FS_CheckGameDir(const char *gamedir)
 {
        const char *ret;
+       static char buf[8192];
+       char vabuf[1024];
 
        if (FS_CheckNastyPath(gamedir, true))
                return NULL;
 
-       ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
+       ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, gamedir), buf, sizeof(buf));
        if(ret)
        {
                if(!*ret)
                {
                        // get description from basedir
-                       ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
+                       ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
                        if(ret)
                                return ret;
                        return "";
@@ -1627,7 +1695,7 @@ const char *FS_CheckGameDir(const char *gamedir)
                return ret;
        }
 
-       ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
+       ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
        if(ret)
                return ret;
        
@@ -1637,16 +1705,17 @@ const char *FS_CheckGameDir(const char *gamedir)
 static void FS_ListGameDirs(void)
 {
        stringlist_t list, list2;
-       int i, j;
+       int i;
        const char *info;
+       char vabuf[1024];
 
        fs_all_gamedirs_count = 0;
        if(fs_all_gamedirs)
                Mem_Free(fs_all_gamedirs);
 
        stringlistinit(&list);
-       listdirectory(&list, va("%s/", fs_basedir), "");
-       listdirectory(&list, va("%s/", fs_userdir), "");
+       listdirectory(&list, va(vabuf, sizeof(vabuf), "%s/", fs_basedir), "");
+       listdirectory(&list, va(vabuf, sizeof(vabuf), "%s/", fs_userdir), "");
        stringlistsort(&list, false);
 
        stringlistinit(&list2);
@@ -1677,8 +1746,8 @@ static void FS_ListGameDirs(void)
                        continue;
                if(!*info)
                        continue;
-               strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
-               strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
+               strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[fs_all_gamedirs_count].name));
+               strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[fs_all_gamedirs_count].description));
                ++fs_all_gamedirs_count;
        }
 }
@@ -1690,6 +1759,41 @@ static void FS_ListGameDirs(void)
 #endif
 */
 
+static void COM_InsertFlags(const char *buf) {
+       const char *p;
+       char *q;
+       const char **new_argv;
+       int i = 0;
+       int args_left = 256;
+       new_argv = (const char **)Mem_Alloc(fs_mempool, sizeof(*com_argv) * (com_argc + args_left + 2));
+       if(com_argc == 0)
+               new_argv[0] = "dummy";  // Can't really happen.
+       else
+               new_argv[0] = com_argv[0];
+       ++i;
+       p = buf;
+       while(COM_ParseToken_Console(&p))
+       {
+               size_t sz = strlen(com_token) + 1; // shut up clang
+               if(i > args_left)
+                       break;
+               q = (char *)Mem_Alloc(fs_mempool, sz);
+               strlcpy(q, com_token, sz);
+               new_argv[i] = q;
+               ++i;
+       }
+       // Now: i <= args_left + 1.
+       if (com_argc >= 1)
+       {
+               memcpy((char *)(&new_argv[i]), &com_argv[1], sizeof(*com_argv) * (com_argc - 1));
+               i += com_argc - 1;
+       }
+       // Now: i <= args_left + (com_argc || 1).
+       new_argv[i] = NULL;
+       com_argv = new_argv;
+       com_argc = i;
+}
+
 /*
 ================
 FS_Init_SelfPack
@@ -1699,57 +1803,47 @@ void FS_Init_SelfPack (void)
 {
        PK3_OpenLibrary ();
        fs_mempool = Mem_AllocPool("file management", 0, NULL);
-       if(com_selffd >= 0)
+
+       // Load darkplaces.opt from the FS.
+       if (!COM_CheckParm("-noopt"))
+       {
+               char *buf = (char *) FS_SysLoadFile("darkplaces.opt", tempmempool, true, NULL);
+               if(buf)
+                       COM_InsertFlags(buf);
+               Mem_Free(buf);
+       }
+
+#ifndef USE_RWOPS
+       // Provide the SelfPack.
+       if (!COM_CheckParm("-noselfpack"))
        {
-               fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
-               if(fs_selfpack)
+               if (com_selffd >= 0)
                {
-                       char *buf, *q;
-                       const char *p;
-                       FS_AddSelfPack();
-                       buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL);
-                       if(buf)
+                       fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
+                       if(fs_selfpack)
                        {
-                               const char **new_argv;
-                               int i = 0;
-                               int args_left = 256;
-                               new_argv = (const char **)Mem_Alloc(fs_mempool, sizeof(*com_argv) * (com_argc + args_left + 2));
-                               if(com_argc == 0)
-                               {
-                                       new_argv[0] = "dummy";
-                                       com_argc = 1;
-                               }
-                               else
-                               {
-                                       memcpy((char *)(&new_argv[0]), &com_argv[0], sizeof(*com_argv) * com_argc);
-                               }
-                               p = buf;
-                               while(COM_ParseToken_Console(&p))
+                               FS_AddSelfPack();
+                               if (!COM_CheckParm("-noopt"))
                                {
-                                       if(i >= args_left)
-                                               break;
-                                       q = (char *)Mem_Alloc(fs_mempool, strlen(com_token) + 1);
-                                       strlcpy(q, com_token, strlen(com_token) + 1);
-                                       new_argv[com_argc + i] = q;
-                                       ++i;
+                                       char *buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL);
+                                       if(buf)
+                                               COM_InsertFlags(buf);
+                                       Mem_Free(buf);
                                }
-                               new_argv[i+com_argc] = NULL;
-                               com_argv = new_argv;
-                               com_argc = com_argc + i;
                        }
-                       Mem_Free(buf);
                }
        }
+#endif
 }
 
-int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsize)
+static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsize)
 {
 #if defined(__IPHONEOS__)
        if (userdirmode == USERDIRMODE_HOME)
        {
                // fs_basedir is "" by default, to utilize this you can simply add your gamedir to the Resources in xcode
                // fs_userdir stores configurations to the Documents folder of the app
-               strlcpy(userdir, maxlength, "../Documents/");
+               strlcpy(userdir, "../Documents/", MAX_OSPATH);
                return 1;
        }
        return -1;
@@ -1763,7 +1857,8 @@ int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsiz
        wchar_t *savedgamesdirw;
        char savedgamesdir[MAX_OSPATH];
        int fd;
-       
+       char vabuf[1024];
+
        userdir[0] = 0;
        switch(userdirmode)
        {
@@ -1836,6 +1931,7 @@ int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsiz
 #else
        int fd;
        char *homedir;
+       char vabuf[1024];
        userdir[0] = 0;
        switch(userdirmode)
        {
@@ -1876,6 +1972,8 @@ int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsiz
 #endif
 
 
+#if !defined(__IPHONEOS__)
+
 #ifdef WIN32
        // historical behavior...
        if (userdirmode == USERDIRMODE_NOHOME && strcmp(gamedirname1, "id1"))
@@ -1884,19 +1982,16 @@ int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsiz
 
        // see if we can write to this path (note: won't create path)
 #ifdef WIN32
-# if _MSC_VER >= 1400
-       _sopen_s(&fd, va("%s%s/config.cfg", userdir, gamedirname1), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
-# else
-       fd = open (va("%s%s/config.cfg", userdir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
-# endif
+       // no access() here, we must try to open the file for appending
+       fd = FS_SysOpenFiledesc(va(vabuf, sizeof(vabuf), "%s%s/config.cfg", userdir, gamedirname1), "a", false);
        if(fd >= 0)
-               close(fd);
+               FILEDESC_CLOSE(fd);
 #else
        // on Unix, we don't need to ACTUALLY attempt to open the file
-       if(access(va("%s%s/", userdir, gamedirname1), W_OK | X_OK) >= 0)
+       if(access(va(vabuf, sizeof(vabuf), "%s%s/", userdir, gamedirname1), W_OK | X_OK) >= 0)
                fd = 1;
        else
-               fd = 0;
+               fd = -1;
 #endif
        if(fd >= 0)
        {
@@ -1909,6 +2004,7 @@ int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsiz
                else
                        return 0; // probably good - failed to write but maybe we need to create path
        }
+#endif
 }
 
 /*
@@ -1941,6 +2037,8 @@ void FS_Init (void)
 // If the base directory is explicitly defined by the compilation process
 #ifdef DP_FS_BASEDIR
                strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
+#elif defined(__ANDROID__)
+               dpsnprintf(fs_basedir, sizeof(fs_basedir), "/sdcard/%s/", gameuserdirname);
 #elif defined(MACOSX)
                // FIXME: is there a better way to find the directory outside the .app, without using Objective-C?
                if (strstr(com_argv[0], ".app/"))
@@ -1951,10 +2049,11 @@ void FS_Init (void)
                        if (split)
                        {
                                struct stat statresult;
+                               char vabuf[1024];
                                // truncate to just after the .app/
                                split[5] = 0;
                                // see if gamedir exists in Resources
-                               if (stat(va("%s/Contents/Resources/%s", fs_basedir, gamedirname1), &statresult) == 0)
+                               if (stat(va(vabuf, sizeof(vabuf), "%s/Contents/Resources/%s", fs_basedir, gamedirname1), &statresult) == 0)
                                {
                                        // found gamedir inside Resources, use it
                                        strlcat(fs_basedir, "Contents/Resources/", sizeof(fs_basedir));
@@ -1985,15 +2084,18 @@ void FS_Init (void)
                *fs_userdir = 0; // user wants roaming installation, no userdir
        else
        {
+#ifdef DP_FS_USERDIR
+               strlcpy(fs_userdir, DP_FS_USERDIR, sizeof(fs_userdir));
+#else
                int dirmode;
                int highestuserdirmode = USERDIRMODE_COUNT - 1;
                int preferreduserdirmode = USERDIRMODE_COUNT - 1;
                int userdirstatus[USERDIRMODE_COUNT];
-#ifdef WIN32
+# ifdef WIN32
                // historical behavior...
                if (!strcmp(gamedirname1, "id1"))
                        preferreduserdirmode = USERDIRMODE_NOHOME;
-#endif
+# endif
                // check what limitations the user wants to impose
                if (COM_CheckParm("-home")) preferreduserdirmode = USERDIRMODE_HOME;
                if (COM_CheckParm("-mygames")) preferreduserdirmode = USERDIRMODE_MYGAMES;
@@ -2025,6 +2127,7 @@ void FS_Init (void)
                // and finally, we picked one...
                FS_ChooseUserDir((userdirmode_t)dirmode, fs_userdir, sizeof(fs_userdir));
                Con_DPrintf("userdir %i is the winner\n", dirmode);
+#endif
        }
 
        // if userdir equal to basedir, clear it to avoid confusion later
@@ -2067,6 +2170,9 @@ void FS_Init (void)
 
        // generate the searchpath
        FS_Rescan();
+
+       if (Thread_HasThreads())
+               fs_mutex = Thread_CreateMutex();
 }
 
 void FS_Init_Commands(void)
@@ -2095,19 +2201,24 @@ void FS_Shutdown (void)
        //  by the OS anyway)
        FS_ClearSearchPath();
        Mem_FreePool (&fs_mempool);
+       PK3_CloseLibrary ();
 
 #ifdef WIN32
        Sys_UnloadLibrary (&shfolder_dll);
        Sys_UnloadLibrary (&shell32_dll);
        Sys_UnloadLibrary (&ole32_dll);
 #endif
+
+       if (fs_mutex)
+               Thread_DestroyMutex(fs_mutex);
 }
 
-int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
+static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking)
 {
-       int handle;
+       filedesc_t handle = FILEDESC_INVALID;
        int mod, opt;
        unsigned int ind;
+       qboolean dolock = false;
 
        // Parse the mode string
        switch (mode[0])
@@ -2126,7 +2237,7 @@ int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
                        break;
                default:
                        Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
-                       return -1;
+                       return FILEDESC_INVALID;
        }
        for (ind = 1; mode[ind] != '\0'; ind++)
        {
@@ -2138,6 +2249,9 @@ int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
                        case 'b':
                                opt |= O_BINARY;
                                break;
+                       case 'l':
+                               dolock = true;
+                               break;
                        default:
                                Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
                                                        filepath, mode, mode[ind]);
@@ -2147,14 +2261,50 @@ int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
        if (nonblocking)
                opt |= O_NONBLOCK;
 
-#if _MSC_VER >= 1400
-       _sopen_s(&handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+       if(COM_CheckParm("-readonly") && mod != O_RDONLY)
+               return FILEDESC_INVALID;
+
+#if USE_RWOPS
+       if (dolock)
+               return FILEDESC_INVALID;
+       handle = SDL_RWFromFile(filepath, mode);
 #else
+# ifdef WIN32
+#  if _MSC_VER >= 1400
+       _sopen_s(&handle, filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+#  else
+       handle = _sopen (filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+#  endif
+# else
        handle = open (filepath, mod | opt, 0666);
+       if(handle >= 0 && dolock)
+       {
+               struct flock l;
+               l.l_type = ((mod == O_RDONLY) ? F_RDLCK : F_WRLCK);
+               l.l_whence = SEEK_SET;
+               l.l_start = 0;
+               l.l_len = 0;
+               if(fcntl(handle, F_SETLK, &l) == -1)
+               {
+                       FILEDESC_CLOSE(handle);
+                       handle = -1;
+               }
+       }
+# endif
 #endif
+
        return handle;
 }
 
+int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
+{
+#ifdef USE_RWOPS
+       return -1;
+#else
+       return FS_SysOpenFiledesc(filepath, mode, nonblocking);
+#endif
+}
+
 /*
 ====================
 FS_SysOpen
@@ -2168,8 +2318,8 @@ qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblockin
 
        file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
        file->ungetc = EOF;
-       file->handle = FS_SysOpenFD(filepath, mode, nonblocking);
-       if (file->handle < 0)
+       file->handle = FS_SysOpenFiledesc(filepath, mode, nonblocking);
+       if (!FILEDESC_ISVALID(file->handle))
        {
                Mem_Free (file);
                return NULL;
@@ -2177,13 +2327,13 @@ qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblockin
 
        file->filename = Mem_strdup(fs_mempool, filepath);
 
-       file->real_length = lseek (file->handle, 0, SEEK_END);
+       file->real_length = FILEDESC_SEEK (file->handle, 0, SEEK_END);
 
        // For files opened in append mode, we start at the end of the file
        if (mode[0] == 'a')
                file->position = file->real_length;
        else
-               lseek (file->handle, 0, SEEK_SET);
+               FILEDESC_SEEK (file->handle, 0, SEEK_SET);
 
        return file;
 }
@@ -2196,10 +2346,10 @@ FS_OpenPackedFile
 Open a packed file using its package file descriptor
 ===========
 */
-qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
+static qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
 {
        packfile_t *pfile;
-       int dup_handle;
+       filedesc_t dup_handle;
        qfile_t* file;
 
        pfile = &pack->files[pack_ind];
@@ -2220,17 +2370,17 @@ qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
        }
 #endif
 
-       // LordHavoc: lseek affects all duplicates of a handle so we do it before
+       // LordHavoc: FILEDESC_SEEK affects all duplicates of a handle so we do it before
        // the dup() call to avoid having to close the dup_handle on error here
-       if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
+       if (FILEDESC_SEEK (pack->handle, pfile->offset, SEEK_SET) == -1)
        {
                Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %08x%08x)\n",
                                        pfile->name, pack->filename, (unsigned int)(pfile->offset >> 32), (unsigned int)(pfile->offset));
                return NULL;
        }
 
-       dup_handle = dup (pack->handle);
-       if (dup_handle < 0)
+       dup_handle = FILEDESC_DUP (pack->filename, pack->handle);
+       if (!FILEDESC_ISVALID(dup_handle))
        {
                Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
                return NULL;
@@ -2272,7 +2422,7 @@ qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
                if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
                {
                        Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
-                       close(dup_handle);
+                       FILEDESC_CLOSE(dup_handle);
                        Mem_Free(file);
                        return NULL;
                }
@@ -2326,17 +2476,9 @@ int FS_CheckNastyPath (const char *path, qboolean isgamedir)
        if (path[0] == '/')
                return 2; // attempt to go outside the game directory
 
-       // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements), this catches all imaginable cases of ./, ../, .../, etc
-       if (strchr(path, '.'))
-       {
-               if (isgamedir)
-               {
-                       // gamedir is entirely path elements, so simply forbid . entirely
-                       return 2;
-               }
-               if (strchr(path, '.') < strrchr(path, '/'))
-                       return 2; // possible attempt to go outside the game directory
-       }
+       // all: don't allow . character immediately before a slash, this catches all imaginable cases of ./, ../, .../, etc
+       if (strstr(path, "./"))
+               return 2; // possible attempt to go outside the game directory
 
        // all: forbid trailing slash on gamedir
        if (isgamedir && path[strlen(path)-1] == '/')
@@ -2451,7 +2593,7 @@ FS_OpenReadFile
 Look for a file in the search paths and open it in read-only mode
 ===========
 */
-qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
+static qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
 {
        searchpath_t *search;
        int pack_ind;
@@ -2595,13 +2737,17 @@ Open a file. The syntax is the same as fopen
 */
 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
 {
+       qfile_t *result = NULL;
        if (FS_CheckNastyPath(filepath, false))
        {
                Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
                return NULL;
        }
 
-       return FS_OpenReadFile (filepath, quiet, false, 16);
+       if (fs_mutex) Thread_LockMutex(fs_mutex);
+       result = FS_OpenReadFile (filepath, quiet, false, 16);
+       if (fs_mutex) Thread_UnlockMutex(fs_mutex);
+       return result;
 }
 
 
@@ -2639,13 +2785,21 @@ int FS_Close (qfile_t* file)
                return 0;
        }
 
-       if (close (file->handle))
+       if (FILEDESC_CLOSE (file->handle))
                return EOF;
 
        if (file->filename)
        {
                if (file->flags & QFILE_FLAG_REMOVE)
-                       remove(file->filename);
+               {
+                       if (remove(file->filename) == -1)
+                       {
+                               // No need to report this. If removing a just
+                               // written file failed, this most likely means
+                               // someone else deleted it first - which we
+                               // like.
+                       }
+               }
 
                Mem_Free((void *) file->filename);
        }
@@ -2674,25 +2828,41 @@ Write "datasize" bytes into a file
 */
 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
 {
-       fs_offset_t result;
+       fs_offset_t written = 0;
 
        // If necessary, seek to the exact file position we're supposed to be
        if (file->buff_ind != file->buff_len)
-               lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
+       {
+               if (FILEDESC_SEEK (file->handle, file->buff_ind - file->buff_len, SEEK_CUR) == -1)
+               {
+                       Con_Printf("WARNING: could not seek in %s.\n", file->filename);
+               }
+       }
 
        // Purge cached data
        FS_Purge (file);
 
        // Write the buffer and update the position
-       result = write (file->handle, data, (fs_offset_t)datasize);
-       file->position = lseek (file->handle, 0, SEEK_CUR);
+       // LordHavoc: to hush a warning about passing size_t to an unsigned int parameter on Win64 we do this as multiple writes if the size would be too big for an integer (we never write that big in one go, but it's a theory)
+       while (written < (fs_offset_t)datasize)
+       {
+               // figure out how much to write in one chunk
+               fs_offset_t maxchunk = 1<<30; // 1 GiB
+               int chunk = (int)min((fs_offset_t)datasize - written, maxchunk);
+               int result = (int)FILEDESC_WRITE (file->handle, (const unsigned char *)data + written, chunk);
+               // if at least some was written, add it to our accumulator
+               if (result > 0)
+                       written += result;
+               // if the result is not what we expected, consider the write to be incomplete
+               if (result != chunk)
+                       break;
+       }
+       file->position = FILEDESC_SEEK (file->handle, 0, SEEK_CUR);
        if (file->real_length < file->position)
                file->real_length = file->position;
 
-       if (result < 0)
-               return 0;
-
-       return result;
+       // note that this will never be less than 0 even if the write failed
+       return written;
 }
 
 
@@ -2760,8 +2930,13 @@ fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
                {
                        if (count > (fs_offset_t)buffersize)
                                count = (fs_offset_t)buffersize;
-                       lseek (file->handle, file->offset + file->position, SEEK_SET);
-                       nb = read (file->handle, &((unsigned char*)buffer)[done], count);
+                       if (FILEDESC_SEEK (file->handle, file->offset + file->position, SEEK_SET) == -1)
+                       {
+                               // Seek failed. When reading from a pipe, and
+                               // the caller never called FS_Seek, this still
+                               // works fine.  So no reporting this error.
+                       }
+                       nb = FILEDESC_READ (file->handle, &((unsigned char*)buffer)[done], count);
                        if (nb > 0)
                        {
                                done += nb;
@@ -2775,8 +2950,13 @@ fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
                {
                        if (count > (fs_offset_t)sizeof (file->buff))
                                count = (fs_offset_t)sizeof (file->buff);
-                       lseek (file->handle, file->offset + file->position, SEEK_SET);
-                       nb = read (file->handle, file->buff, count);
+                       if (FILEDESC_SEEK (file->handle, file->offset + file->position, SEEK_SET) == -1)
+                       {
+                               // Seek failed. When reading from a pipe, and
+                               // the caller never called FS_Seek, this still
+                               // works fine.  So no reporting this error.
+                       }
+                       nb = FILEDESC_READ (file->handle, file->buff, count);
                        if (nb > 0)
                        {
                                file->buff_len = nb;
@@ -2812,8 +2992,8 @@ fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
                        count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
                        if (count > (fs_offset_t)sizeof (ztk->input))
                                count = (fs_offset_t)sizeof (ztk->input);
-                       lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
-                       if (read (file->handle, ztk->input, count) != count)
+                       FILEDESC_SEEK (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
+                       if (FILEDESC_READ (file->handle, ztk->input, count) != count)
                        {
                                Con_Printf ("FS_Read: unexpected end of file\n");
                                break;
@@ -2936,7 +3116,7 @@ int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
                buff_size *= 2;
        }
 
-       len = write (file->handle, tempbuff, len);
+       len = FILEDESC_WRITE (file->handle, tempbuff, len);
        Mem_Free (tempbuff);
 
        return len;
@@ -3031,7 +3211,7 @@ int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
        // Unpacked or uncompressed files can seek directly
        if (! (file->flags & QFILE_FLAG_DEFLATED))
        {
-               if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
+               if (FILEDESC_SEEK (file->handle, file->offset + offset, SEEK_SET) == -1)
                        return -1;
                file->position = offset;
                return 0;
@@ -3048,7 +3228,8 @@ int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
                ztk->in_len = 0;
                ztk->in_position = 0;
                file->position = 0;
-               lseek (file->handle, file->offset, SEEK_SET);
+               if (FILEDESC_SEEK (file->handle, file->offset, SEEK_SET) == -1)
+                       Con_Printf("IMPOSSIBLE: couldn't seek in already opened pk3 file.\n");
 
                // Reset the Zlib stream
                ztk->zstream.next_in = ztk->input;
@@ -3123,19 +3304,17 @@ void FS_Purge (qfile_t* file)
 
 /*
 ============
-FS_LoadFile
+FS_LoadAndCloseQFile
 
-Filename are relative to the quake directory.
+Loads full content of a qfile_t and closes it.
 Always appends a 0 byte.
 ============
 */
-unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
+static unsigned char *FS_LoadAndCloseQFile (qfile_t *file, const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
 {
-       qfile_t *file;
        unsigned char *buf = NULL;
        fs_offset_t filesize = 0;
 
-       file = FS_OpenVirtualFile(path, quiet);
        if (file)
        {
                filesize = file->real_length;
@@ -3162,6 +3341,36 @@ unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, f
 
 /*
 ============
+FS_LoadFile
+
+Filename are relative to the quake directory.
+Always appends a 0 byte.
+============
+*/
+unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
+{
+       qfile_t *file = FS_OpenVirtualFile(path, quiet);
+       return FS_LoadAndCloseQFile(file, path, pool, quiet, filesizepointer);
+}
+
+
+/*
+============
+FS_SysLoadFile
+
+Filename are OS paths.
+Always appends a 0 byte.
+============
+*/
+unsigned char *FS_SysLoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
+{
+       qfile_t *file = FS_SysOpen(path, "rb", false);
+       return FS_LoadAndCloseQFile(file, path, pool, quiet, filesizepointer);
+}
+
+
+/*
+============
 FS_WriteFile
 
 The filename will be prefixed by the current game directory
@@ -3245,7 +3454,7 @@ void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
 
        // if path doesn't have a .EXT, append extension
        // (extension should include the .)
-       src = path + strlen(path) - 1;
+       src = path + strlen(path);
 
        while (*src != '/' && src != path)
        {
@@ -3340,15 +3549,6 @@ qboolean FS_SysFileExists (const char *path)
        return FS_SysFileType (path) != FS_FILETYPE_NONE;
 }
 
-void FS_mkdir (const char *path)
-{
-#if WIN32
-       _mkdir (path);
-#else
-       mkdir (path, 0777);
-#endif
-}
-
 /*
 ===========
 FS_Search
@@ -3366,7 +3566,6 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
        stringlist_t dirlist;
        const char *slash, *backslash, *colon, *separator;
        char *basepath;
-       char temp[MAX_OSPATH];
 
        for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
                ;
@@ -3401,6 +3600,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
                        pak = searchpath->pack;
                        for (i = 0;i < pak->numfiles;i++)
                        {
+                               char temp[MAX_OSPATH];
                                strlcpy(temp, pak->files[i].name, sizeof(temp));
                                while (temp[0])
                                {
@@ -3484,6 +3684,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
 
                                // for each entry in matchedSet try to open the subdirectories specified in subpath
                                for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
+                                       char temp[MAX_OSPATH];
                                        strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
                                        strlcat( temp, subpath, sizeof(temp) );
                                        listdirectory( &foundSet, searchpath->filename, temp );
@@ -3507,17 +3708,17 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
 
                        for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
                        {
-                               const char *temp = matchedSet.strings[dirlistindex];
-                               if (matchpattern(temp, (char *)pattern, true))
+                               const char *matchtemp = matchedSet.strings[dirlistindex];
+                               if (matchpattern(matchtemp, (char *)pattern, true))
                                {
                                        for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
-                                               if (!strcmp(resultlist.strings[resultlistindex], temp))
+                                               if (!strcmp(resultlist.strings[resultlistindex], matchtemp))
                                                        break;
                                        if (resultlistindex == resultlist.numstrings)
                                        {
-                                               stringlistappend(&resultlist, temp);
+                                               stringlistappend(&resultlist, matchtemp);
                                                if (!quiet && developer_loading.integer)
-                                                       Con_Printf("SearchDirFile: %s\n", temp);
+                                                       Con_Printf("SearchDirFile: %s\n", matchtemp);
                                        }
                                }
                        }
@@ -3560,7 +3761,7 @@ void FS_FreeSearch(fssearch_t *search)
 }
 
 extern int con_linewidth;
-int FS_ListDirectory(const char *pattern, int oneperline)
+static int FS_ListDirectory(const char *pattern, int oneperline)
 {
        int numfiles;
        int numcolumns;
@@ -3688,6 +3889,8 @@ const char *FS_WhichPack(const char *filename)
        searchpath_t *sp = FS_FindFile(filename, &index, true);
        if(sp && sp->pack)
                return sp->pack->shortname;
+       else if(sp)
+               return "";
        else
                return 0;
 }
@@ -3726,7 +3929,7 @@ qboolean FS_IsRegisteredQuakePack(const char *name)
                                int diff;
 
                                middle = (left + right) / 2;
-                               diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
+                               diff = strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
 
                                // Found it
                                if (!diff)
@@ -3794,7 +3997,7 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat
        }
 
        strm.next_in = (unsigned char*)data;
-       strm.avail_in = size;
+       strm.avail_in = (unsigned int)size;
 
        tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
        if(!tmp)
@@ -3805,7 +4008,7 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat
        }
 
        strm.next_out = tmp;
-       strm.avail_out = size;
+       strm.avail_out = (unsigned int)size;
 
        if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
        {
@@ -3837,8 +4040,7 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat
                return NULL;
        }
 
-       if(deflated_size)
-               *deflated_size = (size_t)strm.total_out;
+       *deflated_size = (size_t)strm.total_out;
 
        memcpy(out, tmp, strm.total_out);
        Mem_Free(tmp);
@@ -3895,7 +4097,7 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat
        }
 
        strm.next_in = (unsigned char*)data;
-       strm.avail_in = size;
+       strm.avail_in = (unsigned int)size;
 
        do
        {
@@ -3952,8 +4154,7 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat
        memcpy(out, outbuf.data, outbuf.cursize);
        Mem_Free(outbuf.data);
 
-       if(inflated_size)
-               *inflated_size = (size_t)outbuf.cursize;
+       *inflated_size = (size_t)outbuf.cursize;
        
        return out;
 }