]> 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 2d81ce39a85e1fd7058865ae74ab17badf76e9a5..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;
@@ -338,6 +354,8 @@ qboolean PK3_OpenLibrary (void)
 
 #ifdef WIN32
        dllname = "zlib.dll";
+#elif defined(MACOSX)
+       dllname = "libz.dylib";
 #else
        dllname = "libz.so";
 #endif
@@ -361,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;
 
@@ -379,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;
@@ -433,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
@@ -509,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;
 }
 
@@ -523,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);
@@ -541,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));
@@ -552,13 +600,13 @@ 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);
@@ -583,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);
 
@@ -616,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;
        }
@@ -719,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);
@@ -746,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++)
@@ -788,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
@@ -837,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
 }
 
 
@@ -888,8 +947,8 @@ 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);
 
@@ -904,6 +963,7 @@ void FS_Init (void)
 
        // -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)
        {
@@ -915,6 +975,7 @@ void FS_Init (void)
 
        // -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)
        {
@@ -945,58 +1006,10 @@ void FS_Init (void)
                return;
        }
 
-       switch(gamemode)
-       {
-               case GAME_NORMAL:
-                       Cvar_SetQuick (&scr_screenshot_name, "dp");
-                       break;
-               case GAME_HIPNOTIC:
-                       Cvar_SetQuick (&scr_screenshot_name, "hipnotic");
-                       break;
-               case GAME_ROGUE:
-                       Cvar_SetQuick (&scr_screenshot_name, "rogue");
-                       break;
-               case GAME_NEHAHRA:
-                       Cvar_SetQuick (&scr_screenshot_name, "nehahra");
-                       break;
-               case GAME_NEXUIZ:
-                       Cvar_SetQuick (&scr_screenshot_name, "nexuiz");
-                       break;
-               case GAME_TRANSFUSION:
-                       Cvar_SetQuick (&scr_screenshot_name, "transfusion");
-                       break;
-               case GAME_GOODVSBAD2:
-                       Cvar_SetQuick (&scr_screenshot_name, "gvb2");
-                       break;
-               case GAME_TEU:
-                       Cvar_SetQuick (&scr_screenshot_name, "teu");
-                       break;
-               case GAME_BATTLEMECH:
-                       Cvar_SetQuick (&scr_screenshot_name, "battlemech");
-                       break;
-               case GAME_ZYMOTIC:
-                       Cvar_SetQuick (&scr_screenshot_name, "zymotic");
-                       break;
-               case GAME_FNIGGIUM:
-                       Cvar_SetQuick (&scr_screenshot_name, "fniggium");
-                       break;
-               case GAME_SETHERAL:
-                       Cvar_SetQuick (&scr_screenshot_name, "setheral");
-                       break;
-               case GAME_SOM:
-                       Cvar_SetQuick (&scr_screenshot_name, "som");
-                       break;
-               case GAME_TENEBRAE:
-                       Cvar_SetQuick (&scr_screenshot_name, "tenebrae");
-                       break;
-               default:
-                       Cvar_SetQuick (&scr_screenshot_name, "dp");
-                       break;
-       }
-
        // 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])
@@ -1008,16 +1021,36 @@ void FS_Init (void)
 
        // -game <gamedir>
        // Adds basedir/gamedir as an override game
-       i = COM_CheckParm ("-game");
-       if (i && i < com_argc-1)
+       // LordHavoc: now supports multiple -game directories
+       for (i = 1;i < com_argc;i++)
        {
-               fs_modified = true;
-               strlcpy (com_modname, com_argv[i+1], sizeof (com_modname));
-               FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
-               Cvar_SetQuick (&scr_screenshot_name, com_modname);
+               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);
+}
 
 /*
 ====================
@@ -1033,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;
 }
@@ -1064,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;
@@ -1082,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;
+}
+
 
 /*
 ====================
@@ -1097,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)
@@ -1105,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)
@@ -1134,7 +1225,7 @@ 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;
                        }
@@ -1146,7 +1237,7 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
                        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;
@@ -1156,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;
@@ -1273,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'))
        {
@@ -1301,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)
@@ -1324,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
 }
 
 
@@ -1342,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))
@@ -1352,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;
@@ -1390,7 +1503,11 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
 
                        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;
@@ -1465,7 +1582,11 @@ Flush the file output stream
 */
 int FS_Flush (qfile_t* file)
 {
+#ifdef FS_USESYSCALLS
+       return 0;
+#else
        return fflush (file->stream);
+#endif
 }
 
 
@@ -1494,7 +1615,7 @@ int FS_Printf(qfile_t* file, const char* format, ...)
        va_list args;
 
        va_start (args, format);
-       result = vfprintf (file->stream, format, args);
+       result = FS_VPrintf(file, format, args);
        va_end (args);
 
        return result;
@@ -1510,7 +1631,26 @@ 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
 }
 
 
@@ -1543,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.
@@ -1590,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;
@@ -1634,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;
 }
@@ -1653,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
 }
 
 
@@ -1669,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++)
        {
@@ -1748,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)
@@ -1758,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
 }
 
 
@@ -1998,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
@@ -2037,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);
                                                }
                                        }
                                }