]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - fs.c
Fix setinfo.
[xonotic/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 8c8059ca59c629c07c600bb23d22d07cdde965e8..87367a77c28ed8a6c61803519a1a6a46033cd2da 100644 (file)
--- a/fs.c
+++ b/fs.c
 # 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
@@ -212,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)
@@ -286,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;
@@ -532,14 +563,14 @@ PK3_GetEndOfCentralDir
 Extract the end of the central directory from a PK3 package
 ====================
 */
-static 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 @@ static qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk
        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;
@@ -587,8 +618,8 @@ static qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk
        Mem_Free (buffer);
 
        if (
-                       eocd->cdir_size < 0 || eocd->cdir_size > filesize ||
-                       eocd->cdir_offset < 0 || eocd->cdir_offset >= filesize ||
+                       eocd->cdir_size > filesize ||
+                       eocd->cdir_offset >= filesize ||
                        eocd->cdir_offset + eocd->cdir_size > filesize
           )
        {
@@ -615,12 +646,12 @@ static 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);
-       if (lseek (pack->handle, eocd->cdir_offset, SEEK_SET) == -1)
+       if (FILEDESC_SEEK (pack->handle, eocd->cdir_offset, SEEK_SET) == -1)
        {
                Mem_Free (central_dir);
                return -1;
        }
-       if(read (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
+       if(FILEDESC_READ (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
        {
                Mem_Free (central_dir);
                return -1;
@@ -668,7 +699,7 @@ static 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;
@@ -731,7 +762,7 @@ FS_LoadPackPK3
 Create a package entry associated with a PK3 file
 ====================
 */
-static 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;
@@ -741,7 +772,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qbool
        {
                if(!silent)
                        Con_Printf ("%s is not a PK3 file\n", packfile);
-               close(packhandle);
+               FILEDESC_CLOSE(packhandle);
                return NULL;
        }
 
@@ -749,7 +780,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qbool
        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;
        }
 
@@ -759,7 +790,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qbool
        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
@@ -776,7 +807,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qbool
        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;
        }
@@ -784,11 +815,13 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qbool
        Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
        return pack;
 }
+
+static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking);
 static pack_t *FS_LoadPackPK3 (const char *packfile)
 {
-       int packhandle;
-       packhandle = FS_SysOpenFD (packfile, "rb", false);
-       if (packhandle < 0)
+       filedesc_t packhandle;
+       packhandle = FS_SysOpenFiledesc (packfile, "rb", false);
+       if (!FILEDESC_ISVALID(packhandle))
                return NULL;
        return FS_LoadPackPK3FromFD(packfile, packhandle, false);
 }
@@ -811,12 +844,12 @@ static qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
                return true;
 
        // Load the local file description
-       if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
+       if (FILEDESC_SEEK (pack->handle, pfile->offset, SEEK_SET) == -1)
        {
                Con_Printf ("Can't seek in package %s\n", pack->filename);
                return false;
        }
-       count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
+       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);
@@ -893,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
@@ -957,23 +1009,23 @@ 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;
 
-       packhandle = FS_SysOpenFD(packfile, "rb", false);
-       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);
@@ -982,26 +1034,26 @@ static 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;
        }
 
@@ -1018,6 +1070,9 @@ static 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);
        }
 
@@ -1041,7 +1096,7 @@ static pack_t *FS_LoadPackVirtual (const char *dirname)
        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);
@@ -1329,7 +1384,7 @@ static 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);
@@ -1620,7 +1675,7 @@ FS_CheckGameDir
 const char *FS_CheckGameDir(const char *gamedir)
 {
        const char *ret;
-       char buf[8192];
+       static char buf[8192];
        char vabuf[1024];
 
        if (FS_CheckNastyPath(gamedir, true))
@@ -1704,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
@@ -1713,48 +1803,37 @@ 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"))
        {
-               fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
-               if(fs_selfpack)
+               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"))
+       {
+               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"))
                                {
-                                       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[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
 }
 
 static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsize)
@@ -1904,15 +1983,15 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use
        // see if we can write to this path (note: won't create path)
 #ifdef WIN32
        // no access() here, we must try to open the file for appending
-       fd = FS_SysOpenFD(va(vabuf, sizeof(vabuf), "%s%s/config.cfg", userdir, gamedirname1), "a", false);
+       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(vabuf, sizeof(vabuf), "%s%s/", userdir, gamedirname1), W_OK | X_OK) >= 0)
                fd = 1;
        else
-               fd = 0;
+               fd = -1;
 #endif
        if(fd >= 0)
        {
@@ -2005,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;
@@ -2045,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
@@ -2130,9 +2213,9 @@ void FS_Shutdown (void)
                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 = -1;
+       filedesc_t handle = FILEDESC_INVALID;
        int mod, opt;
        unsigned int ind;
        qboolean dolock = false;
@@ -2154,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++)
        {
@@ -2179,15 +2262,20 @@ int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
                opt |= O_NONBLOCK;
 
        if(COM_CheckParm("-readonly") && mod != O_RDONLY)
-               return -1;
+               return FILEDESC_INVALID;
 
-#ifdef WIN32
-# if _MSC_VER >= 1400
+#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
+#  else
        handle = _sopen (filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
-# endif
-#else
+#  endif
+# else
        handle = open (filepath, mod | opt, 0666);
        if(handle >= 0 && dolock)
        {
@@ -2198,15 +2286,25 @@ int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
                l.l_len = 0;
                if(fcntl(handle, F_SETLK, &l) == -1)
                {
-                       close(handle);
+                       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
@@ -2220,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;
@@ -2229,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;
 }
@@ -2251,7 +2349,7 @@ Open a packed file using its package file descriptor
 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];
@@ -2272,17 +2370,17 @@ static 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;
@@ -2324,7 +2422,7 @@ static 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;
                }
@@ -2687,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);
        }
@@ -2727,9 +2833,9 @@ fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
        // If necessary, seek to the exact file position we're supposed to be
        if (file->buff_ind != file->buff_len)
        {
-               if (lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR) == -1)
+               if (FILEDESC_SEEK (file->handle, file->buff_ind - file->buff_len, SEEK_CUR) == -1)
                {
-                       Con_Printf("WARNING: could not seek in %s.\n");
+                       Con_Printf("WARNING: could not seek in %s.\n", file->filename);
                }
        }
 
@@ -2743,7 +2849,7 @@ fs_offset_t FS_Write (qfile_t* file, const void* data, size_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)write (file->handle, (const unsigned char *)data + written, chunk);
+               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;
@@ -2751,7 +2857,7 @@ fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
                if (result != chunk)
                        break;
        }
-       file->position = lseek (file->handle, 0, SEEK_CUR);
+       file->position = FILEDESC_SEEK (file->handle, 0, SEEK_CUR);
        if (file->real_length < file->position)
                file->real_length = file->position;
 
@@ -2824,13 +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;
-                       if (lseek (file->handle, file->offset + file->position, SEEK_SET) == -1)
+                       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 = read (file->handle, &((unsigned char*)buffer)[done], count);
+                       nb = FILEDESC_READ (file->handle, &((unsigned char*)buffer)[done], count);
                        if (nb > 0)
                        {
                                done += nb;
@@ -2844,13 +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);
-                       if (lseek (file->handle, file->offset + file->position, SEEK_SET) == -1)
+                       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 = read (file->handle, file->buff, count);
+                       nb = FILEDESC_READ (file->handle, file->buff, count);
                        if (nb > 0)
                        {
                                file->buff_len = nb;
@@ -2886,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;
@@ -3010,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;
@@ -3105,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;
@@ -3122,7 +3228,7 @@ int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
                ztk->in_len = 0;
                ztk->in_position = 0;
                file->position = 0;
-               if (lseek (file->handle, file->offset, SEEK_SET) == -1)
+               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
@@ -3198,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;
@@ -3235,6 +3339,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
@@ -3320,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)
        {
@@ -3415,18 +3549,6 @@ qboolean FS_SysFileExists (const char *path)
        return FS_SysFileType (path) != FS_FILETYPE_NONE;
 }
 
-void FS_mkdir (const char *path)
-{
-       if(COM_CheckParm("-readonly"))
-               return;
-
-#if WIN32
-       _mkdir (path);
-#else
-       mkdir (path, 0777);
-#endif
-}
-
 /*
 ===========
 FS_Search
@@ -3444,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++)
                ;
@@ -3479,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])
                                {
@@ -3562,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 );
@@ -3585,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);
                                        }
                                }
                        }
@@ -3806,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)
@@ -3917,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);
@@ -4032,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;
 }