]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - fs.c
Cleaned up alot more memory leaks. (still get 720 leaks just running demo1.dem)
[xonotic/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 8ba6759bbc5051ab3c297bbfd80995ec673673a2..376336b7cf199156d526464c2faa6306dd659089 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -25,8 +25,6 @@
 
 #include "quakedef.h"
 
-#include <stdlib.h>
-#include <string.h>
 #include <limits.h>
 #include <fcntl.h>
 
 
 #include "fs.h"
 
+// use syscalls instead of f* functions
+#define FS_USESYSCALLS
+
+// Win32 requires us to add O_BINARY, but the other OSes don't have it
+#ifdef FS_USESYSCALLS
+# ifndef O_BINARY
+#  define O_BINARY 0
+# endif
+#endif
+
 
 /*
 
@@ -152,7 +160,11 @@ typedef struct
 struct qfile_s
 {
        fs_flags_t      flags;
+#ifdef FS_USESYSCALLS
+       int                     stream;
+#else
        FILE*           stream;
+#endif
        size_t          length;         // file size on disk (PACKED only)
        size_t          offset;         // offset into a package (PACKED only)
        size_t          position;       // current position in the file (PACKED only)
@@ -212,7 +224,11 @@ typedef struct
 typedef struct pack_s
 {
        char filename [MAX_OSPATH];
+#ifdef FS_USESYSCALLS
+       int handle;
+#else
        FILE *handle;
+#endif
        int ignorecase; // PK3 ignores case
        int numfiles;
        packfile_t *files;
@@ -262,7 +278,7 @@ int fs_filesize;
 
 pack_t *packlist = NULL;
 
-searchpath_t *fs_searchpaths;
+searchpath_t *fs_searchpaths = NULL;
 
 #define MAX_FILES_IN_PACK      65536
 
@@ -317,11 +333,7 @@ Unload the Zlib DLL
 */
 void PK3_CloseLibrary (void)
 {
-       if (!zlib_dll)
-               return;
-
-       Sys_UnloadLibrary (zlib_dll);
-       zlib_dll = NULL;
+       Sys_UnloadLibrary (&zlib_dll);
 }
 
 
@@ -335,7 +347,6 @@ Try to load the Zlib DLL
 qboolean PK3_OpenLibrary (void)
 {
        const char* dllname;
-       const dllfunction_t *func;
 
        // Already loaded?
        if (zlib_dll)
@@ -343,31 +354,20 @@ qboolean PK3_OpenLibrary (void)
 
 #ifdef WIN32
        dllname = "zlib.dll";
+#elif defined(MACOSX)
+       dllname = "libz.dylib";
 #else
        dllname = "libz.so";
 #endif
 
-       // Initializations
-       for (func = zlibfuncs; func && func->name != NULL; func++)
-               *func->funcvariable = NULL;
-
        // Load the DLL
-       if (! (zlib_dll = Sys_LoadLibrary (dllname)))
+       if (! Sys_LoadLibrary (dllname, &zlib_dll, zlibfuncs))
        {
-               Con_Printf("Can't find %s. Compressed files support disabled\n", dllname);
+               Con_Printf ("Compressed files support disabled\n");
                return false;
        }
 
-       // Get the function adresses
-       for (func = zlibfuncs; func && func->name != NULL; func++)
-               if (!(*func->funcvariable = (void *) Sys_GetProcAddress (zlib_dll, func->name)))
-               {
-                       Con_Printf("missing function \"%s\" - broken Zlib library!\n", func->name);
-                       PK3_CloseLibrary ();
-                       return false;
-               }
-
-       Con_Printf("%s loaded. Compressed files support enabled\n", dllname);
+       Con_Printf ("Compressed files support enabled\n");
        return true;
 }
 
@@ -379,15 +379,23 @@ PK3_GetEndOfCentralDir
 Extract the end of the central directory from a PK3 package
 ====================
 */
+#ifdef FS_USESYSCALLS
+qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
+#else
 qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd)
+#endif
 {
        long filesize, maxsize;
        qbyte *buffer, *ptr;
        int ind;
 
        // Get the package size
+#ifdef FS_USESYSCALLS
+       filesize = lseek (packhandle, 0, SEEK_END);
+#else
        fseek (packhandle, 0, SEEK_END);
-       filesize = ftell (packhandle);
+       filesize = ftell(packhandle);
+#endif
        if (filesize < ZIP_END_CDIR_SIZE)
                return false;
 
@@ -397,8 +405,13 @@ qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_end
        else
                maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
        buffer = Mem_Alloc (tempmempool, maxsize);
+#ifdef FS_USESYSCALLS
+       lseek (packhandle, filesize - maxsize, SEEK_SET);
+       if (read (packhandle, buffer, maxsize) != (ssize_t) maxsize)
+#else
        fseek (packhandle, filesize - maxsize, SEEK_SET);
-       if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) maxsize)
+       if (fread (buffer, 1, maxsize, packhandle) != (size_t) maxsize)
+#endif
        {
                Mem_Free (buffer);
                return false;
@@ -451,8 +464,13 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
 
        // Load the central directory in memory
        central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
+#ifdef FS_USESYSCALLS
+       lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
+       read (pack->handle, central_dir, eocd->cdir_size);
+#else
        fseek (pack->handle, eocd->cdir_offset, SEEK_SET);
        fread (central_dir, 1, eocd->cdir_size, pack->handle);
+#endif
 
        // Extract the files properties
        // The parsing is done "by hand" because some fields have variable sizes and
@@ -527,7 +545,9 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
                remaining -= count;
        }
 
-       Mem_Free (central_dir);
+       // If the package is empty, central_dir is NULL here
+       if (central_dir != NULL)
+               Mem_Free (central_dir);
        return pack->numfiles;
 }
 
@@ -541,14 +561,24 @@ Create a package entry associated with a PK3 file
 */
 pack_t *FS_LoadPackPK3 (const char *packfile)
 {
+#ifdef FS_USESYSCALLS
+       int packhandle;
+#else
        FILE *packhandle;
+#endif
        pk3_endOfCentralDir_t eocd;
        pack_t *pack;
        int real_nb_files;
 
+#ifdef FS_USESYSCALLS
+       packhandle = open (packfile, O_RDONLY | O_BINARY);
+       if (packhandle < 0)
+               return NULL;
+#else
        packhandle = fopen (packfile, "rb");
        if (!packhandle)
                return NULL;
+#endif
 
        if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
                Sys_Error ("%s is not a PK3 file", packfile);
@@ -559,10 +589,10 @@ pack_t *FS_LoadPackPK3 (const char *packfile)
 
        // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
        // since eocd.nbentries is an unsigned 16 bits integer
-       #if MAX_FILES_IN_PACK < 65535
+#if MAX_FILES_IN_PACK < 65535
        if (eocd.nbentries > MAX_FILES_IN_PACK)
                Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
-       #endif
+#endif
 
        // Create a package structure in memory
        pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
@@ -570,16 +600,16 @@ pack_t *FS_LoadPackPK3 (const char *packfile)
        strlcpy (pack->filename, packfile, sizeof (pack->filename));
        pack->handle = packhandle;
        pack->numfiles = eocd.nbentries;
-       pack->mempool = Mem_AllocPool (packfile);
+       pack->mempool = Mem_AllocPool (packfile, 0, NULL);
        pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
        pack->next = packlist;
        packlist = pack;
 
        real_nb_files = PK3_BuildFileList (pack, &eocd);
-       if (real_nb_files <= 0)
+       if (real_nb_files < 0)
                Sys_Error ("%s is not a valid PK3 file", packfile);
 
-       Con_Printf ("Added packfile %s (%i files)\n", packfile, real_nb_files);
+       Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
        return pack;
 }
 
@@ -601,8 +631,13 @@ void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
                return;
 
        // Load the local file description
+#ifdef FS_USESYSCALLS
+       lseek (pack->handle, file->offset, SEEK_SET);
+       count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
+#else
        fseek (pack->handle, file->offset, SEEK_SET);
        count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
+#endif
        if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
                Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
 
@@ -634,28 +669,29 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
                                                                         size_t realsize, file_flags_t flags)
 {
        int (*strcmp_funct) (const char* str1, const char* str2);
-       size_t left, right, middle;
-       int diff;
+       int left, right, middle;
        packfile_t *file;
 
        strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
 
        // Look for the slot we should put that file into (binary search)
        left = 0;
-       right = pack->numfiles;
-       while (left != right)
+       right = pack->numfiles - 1;
+       while (left <= right)
        {
-               middle = (left + right - 1) / 2;
+               int diff;
+
+               middle = (left + right) / 2;
                diff = strcmp_funct (pack->files[middle].name, name);
 
                // If we found the file, there's a problem
                if (!diff)
-                       Sys_Error ("Package %s contains several time the file %s\n",
+                       Sys_Error ("Package %s contains the file %s several times\n",
                                           pack->filename, name);
 
                // If we're too far in the list
                if (diff > 0)
-                       right = middle;
+                       right = middle - 1;
                else
                        left = middle + 1;
        }
@@ -679,7 +715,7 @@ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
 ============
 FS_CreatePath
 
-Only used for FS_WriteFile.
+Only used for FS_Open.
 ============
 */
 void FS_CreatePath (char *path)
@@ -710,15 +746,15 @@ void FS_Path_f (void)
 {
        searchpath_t *s;
 
-       Con_Print("Current search path:\n");
+       Con_Print("Current search path:\n");
        for (s=fs_searchpaths ; s ; s=s->next)
        {
                if (s->pack)
                {
-                       Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
+                       Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
                }
                else
-                       Con_Printf ("%s\n", s->filename);
+                       Con_Printf("%s\n", s->filename);
        }
 }
 
@@ -737,15 +773,25 @@ pack_t *FS_LoadPackPAK (const char *packfile)
 {
        dpackheader_t header;
        int i, numpackfiles;
+#ifdef FS_USESYSCALLS
+       int packhandle;
+#else
        FILE *packhandle;
+#endif
        pack_t *pack;
        dpackfile_t *info;      // temporary alloc, allowing huge pack directories
 
+#ifdef FS_USESYSCALLS
+       packhandle = open (packfile, O_RDONLY | O_BINARY);
+       if (packhandle < 0)
+               return NULL;
+       read (packhandle, (void *)&header, sizeof(header));
+#else
        packhandle = fopen (packfile, "rb");
        if (!packhandle)
                return NULL;
-
        fread ((void *)&header, 1, sizeof(header), packhandle);
+#endif
        if (memcmp(header.id, "PACK", 4))
                Sys_Error ("%s is not a packfile", packfile);
        header.dirofs = LittleLong (header.dirofs);
@@ -764,14 +810,19 @@ pack_t *FS_LoadPackPAK (const char *packfile)
        strlcpy (pack->filename, packfile, sizeof (pack->filename));
        pack->handle = packhandle;
        pack->numfiles = 0;
-       pack->mempool = Mem_AllocPool(packfile);
+       pack->mempool = Mem_AllocPool(packfile, 0, NULL);
        pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
        pack->next = packlist;
        packlist = pack;
 
        info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
+#ifdef FS_USESYSCALLS
+       lseek (packhandle, header.dirofs, SEEK_SET);
+       read (packhandle, (void *)info, header.dirlen);
+#else
        fseek (packhandle, header.dirofs, SEEK_SET);
        fread ((void *)info, 1, header.dirlen, packhandle);
+#endif
 
        // parse the directory
        for (i = 0;i < numpackfiles;i++)
@@ -784,7 +835,7 @@ pack_t *FS_LoadPackPAK (const char *packfile)
 
        Mem_Free(info);
 
-       Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
+       Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
        return pack;
 }
 
@@ -806,14 +857,6 @@ void FS_AddGameDirectory (char *dir)
 
        strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
 
-#ifndef AKVERSION
-       // add the directory to the search path
-       search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
-       strlcpy (search->filename, dir, sizeof (search->filename));
-       search->next = fs_searchpaths;
-       fs_searchpaths = search;
-#endif
-
        list = listdirectory(dir);
 
        // add any PAK package in the directory
@@ -855,14 +898,12 @@ void FS_AddGameDirectory (char *dir)
        }
        freedirectory(list);
 
-// Unpacked files have the priority over packed files if AKVERSION is defined
-#ifdef AKVERSION
-       // add the directory to the search path
+       // Add the directory to the search path
+       // (unpacked files have the priority over packed files)
        search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
        strlcpy (search->filename, dir, sizeof (search->filename));
        search->next = fs_searchpaths;
        fs_searchpaths = search;
-#endif
 }
 
 
@@ -886,7 +927,7 @@ char *FS_FileExtension (const char *in)
                separator = backslash;
        if (separator < colon)
                separator = colon;
-       if (dot < separator)
+       if (dot == NULL || dot < separator)
                return "";
        dot++;
        for (i = 0;i < 7 && dot[i];i++)
@@ -906,54 +947,39 @@ void FS_Init (void)
        int i;
        searchpath_t *search;
 
-       fs_mempool = Mem_AllocPool("file management");
-       pak_mempool = Mem_AllocPool("paks");
+       fs_mempool = Mem_AllocPool("file management", 0, NULL);
+       pak_mempool = Mem_AllocPool("paks", 0, NULL);
+
+       Cvar_RegisterVariable (&scr_screenshot_name);
 
        Cmd_AddCommand ("path", FS_Path_f);
        Cmd_AddCommand ("dir", FS_Dir_f);
        Cmd_AddCommand ("ls", FS_Ls_f);
 
        strcpy(fs_basedir, ".");
+       strcpy(fs_gamedir, ".");
 
        PK3_OpenLibrary ();
 
        // -basedir <path>
        // Overrides the system supplied base directory (under GAMENAME)
+// COMMANDLINEOPTION: Filesystem: -basedir <path> chooses what base directory the game data is in, inside this there should be a data directory for the game (for example id1)
        i = COM_CheckParm ("-basedir");
-       if (i && i < com_argc-1)
-               strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
-
-       i = strlen (fs_basedir);
-       if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
-               fs_basedir[i-1] = 0;
-
-       // start up with GAMENAME by default (id1)
-       strlcpy (com_modname, GAMENAME, sizeof (com_modname));
-       FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
-       if (gamedirname[0])
-       {
-               fs_modified = true;
-               strlcpy (com_modname, gamedirname, sizeof (com_modname));
-               FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
-       }
-
-       // -game <gamedir>
-       // Adds basedir/gamedir as an override game
-       i = COM_CheckParm ("-game");
        if (i && i < com_argc-1)
        {
-               fs_modified = true;
-               strlcpy (com_modname, com_argv[i+1], sizeof (com_modname));
-               FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
+               strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
+               i = strlen (fs_basedir);
+               if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
+                       fs_basedir[i-1] = 0;
        }
 
        // -path <dir or packfile> [<dir or packfile>] ...
        // Fully specifies the exact search path, overriding the generated one
+// COMMANDLINEOPTION: Filesystem: -path <path ..> specifies the full search path manually, overriding the generated one, example: -path c:\quake\id1 c:\quake\pak0.pak c:\quake\pak1.pak (not recommended)
        i = COM_CheckParm ("-path");
        if (i)
        {
                fs_modified = true;
-               fs_searchpaths = NULL;
                while (++i < com_argc)
                {
                        if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
@@ -977,9 +1003,54 @@ void FS_Init (void)
                        search->next = fs_searchpaths;
                        fs_searchpaths = search;
                }
+               return;
+       }
+
+       // start up with GAMENAME by default (id1)
+       strlcpy (com_modname, GAMENAME, sizeof (com_modname));
+       FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
+       Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
+
+       // add the game-specific path, if any
+       if (gamedirname[0])
+       {
+               fs_modified = true;
+               strlcpy (com_modname, gamedirname, sizeof (com_modname));
+               FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
+       }
+
+       // -game <gamedir>
+       // Adds basedir/gamedir as an override game
+       // LordHavoc: now supports multiple -game directories
+       for (i = 1;i < com_argc;i++)
+       {
+               if (!com_argv[i])
+                       continue;
+               if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
+               {
+                       i++;
+                       fs_modified = true;
+                       strlcpy (com_modname, com_argv[i], sizeof (com_modname));
+                       FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i]));
+                       Cvar_SetQuick (&scr_screenshot_name, com_modname);
+               }
        }
+
+       // If "-condebug" is in the command line, remove the previous log file
+       if (COM_CheckParm ("-condebug") != 0)
+               unlink (va("%s/qconsole.log", fs_gamedir));
 }
 
+/*
+================
+FS_Shutdown
+================
+*/
+void FS_Shutdown (void)
+{
+       Mem_FreePool (&pak_mempool);
+       Mem_FreePool (&fs_mempool);
+}
 
 /*
 ====================
@@ -995,12 +1066,28 @@ static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
        file = Mem_Alloc (fs_mempool, sizeof (*file));
        memset (file, 0, sizeof (*file));
 
+#ifdef FS_USESYSCALLS
+       if (strchr(mode, 'r'))
+               file->stream = open (filepath, O_RDONLY | O_BINARY);
+       else if (strchr(mode, 'w'))
+               file->stream = open (filepath, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666);
+       else if (strchr(mode, 'a'))
+               file->stream = open (filepath, O_RDWR | O_BINARY | O_CREAT | O_APPEND, 0666);
+       else
+               file->stream = -1;
+       if (file->stream < 0)
+       {
+               Mem_Free (file);
+               return NULL;
+       }
+#else
        file->stream = fopen (filepath, mode);
        if (!file->stream)
        {
                Mem_Free (file);
                return NULL;
        }
+#endif
 
        return file;
 }
@@ -1026,14 +1113,23 @@ qfile_t *FS_OpenRead (const char *path, int offs, int len)
        if (offs < 0 || len < 0)
        {
                // We set fs_filesize here for normal files
+#ifdef FS_USESYSCALLS
+               fs_filesize = lseek (file->stream, 0, SEEK_END);
+               lseek (file->stream, 0, SEEK_SET);
+#else
                fseek (file->stream, 0, SEEK_END);
                fs_filesize = ftell (file->stream);
                fseek (file->stream, 0, SEEK_SET);
+#endif
        }
        // Packed file
        else
        {
+#ifdef FS_USESYSCALLS
+               lseek (file->stream, offs, SEEK_SET);
+#else
                fseek (file->stream, offs, SEEK_SET);
+#endif
 
                file->flags |= FS_FLAG_PACKED;
                file->length = len;
@@ -1044,6 +1140,39 @@ qfile_t *FS_OpenRead (const char *path, int offs, int len)
        return file;
 }
 
+/*
+====================
+FS_CheckNastyPath
+
+Return true if the path should be rejected due to one of the following:
+1: path elements that are non-portable
+2: path elements that would allow access to files outside the game directory,
+   or are just not a good idea for a mod to be using.
+====================
+*/
+int FS_CheckNastyPath (const char *path)
+{
+       // Windows: don't allow \ in filenames (windows-only), period.
+       // (on Windows \ is a directory separator, but / is also supported)
+       if (strstr(path, "\\"))
+               return 1; // non-portable
+       // Mac: don't allow Mac-only filenames - : is a directory separator
+       // instead of /, but we rely on / working already, so there's no reason to
+       // support a Mac-only path
+       // Amiga and Windows: : tries to go to root of drive
+       if (strstr(path, ":"))
+               return 1; // non-portable attempt to go to root of drive
+       // Amiga: // is parent directory
+       if (strstr(path, "//"))
+               return 1; // non-portable attempt to go to parent directory
+       // all: don't allow going to current directory (./) or parent directory (../ or /../)
+       if (strstr(path, "./"))
+               return 2; // attempt to go to parent directory
+       // after all these checks we're pretty sure it's a / separated filename
+       // and won't do much if any harm
+       return false;
+}
+
 
 /*
 ====================
@@ -1059,7 +1188,6 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
 {
        searchpath_t *search;
        pack_t *pak;
-       int (*strcmp_funct) (const char* str1, const char* str2);
 
        // search through the path, one element at a time
        for (search = fs_searchpaths;search;search = search->next)
@@ -1067,26 +1195,27 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
                // is the element a pak file?
                if (search->pack)
                {
-                       size_t left, right, middle;
+                       int (*strcmp_funct) (const char* str1, const char* str2);
+                       int left, right, middle;
 
                        pak = search->pack;
                        strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
 
                        // Look for the file (binary search)
                        left = 0;
-                       right = pak->numfiles;
-                       while (left != right)
+                       right = pak->numfiles - 1;
+                       while (left <= right)
                        {
                                int diff;
 
-                               middle = (left + right - 1) / 2;
+                               middle = (left + right) / 2;
                                diff = strcmp_funct (pak->files[middle].name, name);
 
                                // Found it
                                if (!diff)
                                {
                                        if (!quiet)
-                                               Sys_Printf ("FS_FindFile: %s in %s\n",
+                                               Con_DPrintf("FS_FindFile: %s in %s\n",
                                                                        pak->files[middle].name, pak->filename);
 
                                        if (index != NULL)
@@ -1096,20 +1225,19 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
 
                                // If we're too far in the list
                                if (diff > 0)
-                                       right = middle;
+                                       right = middle - 1;
                                else
                                        left = middle + 1;
                        }
                }
                else
                {
-                       char* netpath;
-
-                       netpath = va ("%s/%s", search->filename, name);
+                       char netpath[MAX_OSPATH];
+                       snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
                        if (FS_SysFileExists (netpath))
                        {
                                if (!quiet)
-                                       Sys_Printf ("FS_FindFile: %s\n", netpath);
+                                       Con_DPrintf("FS_FindFile: %s\n", netpath);
 
                                if (index != NULL)
                                        *index = -1;
@@ -1119,7 +1247,7 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
        }
 
        if (!quiet)
-               Sys_Printf ("FS_FindFile: can't find %s\n", name);
+               Con_DPrintf("FS_FindFile: can't find %s\n", name);
 
        if (index != NULL)
                *index = -1;
@@ -1155,7 +1283,11 @@ qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
 
        // Found in the filesystem?
        if (i < 0)
-               return FS_OpenRead (va ("%s/%s", search->filename, filename), -1, -1);
+       {
+               char netpath[MAX_OSPATH];
+               snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
+               return FS_OpenRead(netpath, -1, -1);
+       }
 
        // So, we found it in a package...
        packfile = &search->pack->files[i];
@@ -1167,7 +1299,7 @@ qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
        // No Zlib DLL = no compressed files
        if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED))
        {
-               Con_Printf ("WARNING: can't open the compressed file %s\n"
+               Con_Printf("WARNING: can't open the compressed file %s\n"
                                        "You need the Zlib DLL to use compressed files\n",
                                        filename);
                fs_filesize = -1;
@@ -1232,6 +1364,12 @@ Open a file. The syntax is the same as fopen
 */
 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
 {
+       if (FS_CheckNastyPath(filepath))
+       {
+               Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
+               return NULL;
+       }
+
        // If the file is opened in "write" or "append" mode
        if (strchr (mode, 'w') || strchr (mode, 'a'))
        {
@@ -1260,7 +1398,11 @@ Close a file
 */
 int FS_Close (qfile_t* file)
 {
+#ifdef FS_USESYSCALLS
+       if (close (file->stream))
+#else
        if (fclose (file->stream))
+#endif
                return EOF;
 
        if (file->z)
@@ -1283,7 +1425,11 @@ Write "datasize" bytes into a file
 */
 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
 {
+#ifdef FS_USESYSCALLS
+       return write (file->stream, data, datasize);
+#else
        return fwrite (data, 1, datasize, file->stream);
+#endif
 }
 
 
@@ -1301,7 +1447,11 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
 
        // Quick path for unpacked files
        if (! (file->flags & FS_FLAG_PACKED))
+#ifdef FS_USESYSCALLS
+               return read (file->stream, buffer, buffersize);
+#else
                return fread (buffer, 1, buffersize, file->stream);
+#endif
 
        // If the file isn't compressed
        if (! (file->flags & FS_FLAG_DEFLATED))
@@ -1311,7 +1461,11 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
                if (buffersize > count)
                        buffersize = count;
 
+#ifdef FS_USESYSCALLS
+               nb = read (file->stream, buffer, buffersize);
+#else
                nb = fread (buffer, 1, buffersize, file->stream);
+#endif
 
                file->position += nb;
                return nb;
@@ -1341,14 +1495,19 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
                // If "input" is also empty, we need to fill it
                if (ztk->in_ind == ztk->in_max)
                {
-                       size_t remain = file->length - ztk->in_position;
+                       size_t remain;
 
                        // If we are at the end of the file
-                       if (!remain)
+                       if (ztk->out_position == ztk->real_length)
                                return nb;
 
+                       remain = file->length - ztk->in_position;
                        count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
+#ifdef FS_USESYSCALLS
+                       read (file->stream, ztk->input, count);
+#else
                        fread (ztk->input, 1, count, file->stream);
+#endif
 
                        // Update indexes and counters
                        ztk->in_ind = 0;
@@ -1423,10 +1582,26 @@ Flush the file output stream
 */
 int FS_Flush (qfile_t* file)
 {
+#ifdef FS_USESYSCALLS
+       return 0;
+#else
        return fflush (file->stream);
+#endif
 }
 
 
+/*
+====================
+FS_Print
+
+Print a string into a file
+====================
+*/
+int FS_Print(qfile_t* file, const char *msg)
+{
+       return FS_Write(file, msg, strlen(msg));
+}
+
 /*
 ====================
 FS_Printf
@@ -1434,19 +1609,51 @@ FS_Printf
 Print a string into a file
 ====================
 */
-int FS_Printf (qfile_t* file, const char* format, ...)
+int FS_Printf(qfile_t* file, const char* format, ...)
 {
        int result;
        va_list args;
 
        va_start (args, format);
-       result = vfprintf (file->stream, format, args);
+       result = FS_VPrintf(file, format, args);
        va_end (args);
 
        return result;
 }
 
 
+/*
+====================
+FS_VPrintf
+
+Print a string into a file
+====================
+*/
+int FS_VPrintf(qfile_t* file, const char* format, va_list ap)
+{
+#ifdef FS_USESYSCALLS
+{
+       int len;
+       char tempstring[1024];
+       len = vsnprintf (tempstring, sizeof(tempstring), format, ap);
+       if (len >= sizeof(tempstring))
+       {
+               int result;
+               char *temp = Mem_Alloc(tempmempool, len + 1);
+               len = vsnprintf (temp, len + 1, format, ap);
+               result = write (file->stream, temp, len);
+               Mem_Free(temp);
+               return result;
+       }
+       else
+               return write (file->stream, tempstring, len);
+}
+#else
+       return vfprintf (file->stream, format, ap);
+#endif
+}
+
+
 /*
 ====================
 FS_Getc
@@ -1476,7 +1683,15 @@ int FS_Seek (qfile_t* file, long offset, int whence)
 {
        // Quick path for unpacked files
        if (! (file->flags & FS_FLAG_PACKED))
+#ifdef FS_USESYSCALLS
+       {
+               if (lseek (file->stream, offset, whence) == -1)
+                       return -1;
+               return 0;
+       }
+#else
                return fseek (file->stream, offset, whence);
+#endif
 
        // Seeking in compressed files is more a hack than anything else,
        // but we need to support it, so here it is.
@@ -1523,7 +1738,11 @@ int FS_Seek (qfile_t* file, long offset, int whence)
                        ztk->out_max = 0;
                        ztk->out_position = 0;
                        file->position = 0;
+#ifdef FS_USESYSCALLS
+                       lseek (file->stream, file->offset, SEEK_SET);
+#else
                        fseek (file->stream, file->offset, SEEK_SET);
+#endif
 
                        // Reset the Zlib stream
                        ztk->zstream.next_in = ztk->input;
@@ -1567,8 +1786,13 @@ int FS_Seek (qfile_t* file, long offset, int whence)
        if (offset < 0 || offset > (long) file->length)
                return -1;
 
+#ifdef FS_USESYSCALLS
+       if (lseek (file->stream, file->offset + offset, SEEK_SET) == -1)
+               return -1;
+#else
        if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
                return -1;
+#endif
        file->position = offset;
        return 0;
 }
@@ -1586,7 +1810,11 @@ long FS_Tell (qfile_t* file)
        if (file->flags & FS_FLAG_PACKED)
                return file->position;
 
+#ifdef FS_USESYSCALLS
+       return lseek (file->stream, 0, SEEK_CUR);
+#else
        return ftell (file->stream);
+#endif
 }
 
 
@@ -1602,8 +1830,10 @@ char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
        size_t ind;
 
        // Quick path for unpacked files
+#ifndef FS_USESYSCALLS
        if (! (file->flags & FS_FLAG_PACKED))
                return fgets (buffer, buffersize, file->stream);
+#endif
 
        for (ind = 0; ind < (size_t) buffersize - 1; ind++)
        {
@@ -1681,6 +1911,7 @@ FS_Eof
 Extract a line from a file
 ====================
 */
+// FIXME: remove this function?
 int FS_Eof (qfile_t* file)
 {
        if (file->flags & FS_FLAG_PACKED)
@@ -1691,7 +1922,12 @@ int FS_Eof (qfile_t* file)
                return (file->position == file->length);
        }
 
+#ifdef FS_USESYSCALLS
+       Sys_Error("FS_Eof: not implemented using syscalls\n");
+       return false;
+#else
        return feof (file->stream);
+#endif
 }
 
 
@@ -1703,7 +1939,7 @@ Filename are relative to the quake directory.
 Always appends a 0 byte.
 ============
 */
-qbyte *FS_LoadFile (const char *path, qboolean quiet)
+qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
 {
        qfile_t *h;
        qbyte *buf;
@@ -1713,7 +1949,7 @@ qbyte *FS_LoadFile (const char *path, qboolean quiet)
        if (!h)
                return NULL;
 
-       buf = Mem_Alloc(tempmempool, fs_filesize+1);
+       buf = Mem_Alloc(pool, fs_filesize+1);
        if (!buf)
                Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
 
@@ -1740,11 +1976,11 @@ qboolean FS_WriteFile (const char *filename, void *data, int len)
        handle = FS_Open (filename, "wb", false);
        if (!handle)
        {
-               Con_Printf ("FS_WriteFile: failed on %s\n", filename);
+               Con_Printf("FS_WriteFile: failed on %s\n", filename);
                return false;
        }
 
-       Con_DPrintf ("FS_WriteFile: %s\n", filename);
+       Con_DPrintf("FS_WriteFile: %s\n", filename);
        FS_Write (handle, data, len);
        FS_Close (handle);
        return true;
@@ -1902,7 +2138,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
        if (separator < colon)
                separator = colon;
        basepathlength = separator - pattern;
-       basepath = Z_Malloc(basepathlength + 1);
+       basepath = Mem_Alloc (tempmempool, basepathlength + 1);
        if (basepathlength)
                memcpy(basepath, pattern, basepathlength);
        basepath[basepathlength] = 0;
@@ -1931,7 +2167,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
                                                        if (liststart == NULL)
                                                                liststart = listcurrent;
                                                        if (!quiet)
-                                                               Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
+                                                               Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
                                                }
                                        }
                                        // strip off one path element at a time until empty
@@ -1970,7 +2206,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
                                                        if (liststart == NULL)
                                                                liststart = listcurrent;
                                                        if (!quiet)
-                                                               Sys_Printf("SearchDirFile: %s\n", temp);
+                                                               Con_DPrintf("SearchDirFile: %s\n", temp);
                                                }
                                        }
                                }
@@ -2006,7 +2242,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
                        stringlistfree(liststart);
        }
 
-       Z_Free(basepath);
+       Mem_Free(basepath);
        return search;
 }
 
@@ -2027,7 +2263,7 @@ int FS_ListDirectory(const char *pattern, int oneperline)
        const char *name;
        char linebuf[4096];
        fssearch_t *search;
-       search = FS_Search(pattern, true, false);
+       search = FS_Search(pattern, true, true);
        if (!search)
                return 0;
        numfiles = search->numfilenames;
@@ -2086,18 +2322,18 @@ int FS_ListDirectory(const char *pattern, int oneperline)
 
 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
 {
-       char pattern[MAX_OSPATH];
+       const char *pattern;
        if (Cmd_Argc() > 3)
        {
                Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
                return;
        }
        if (Cmd_Argc() == 2)
-               snprintf(pattern, sizeof(pattern), "%s", Cmd_Argv(1));
+               pattern = Cmd_Argv(1);
        else
-               strcpy(pattern, "*");
+               pattern = "*";
        if (!FS_ListDirectory(pattern, oneperline))
-               Con_Printf("No files found.\n");
+               Con_Print("No files found.\n");
 }
 
 void FS_Dir_f(void)