make infobar height configurable
[divverent/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 5068936..a0dd2c8 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -22,8 +22,6 @@
                Boston, MA  02111-1307, USA
 */
 
-#include "quakedef.h"
-
 #include <limits.h>
 #include <fcntl.h>
 
@@ -37,6 +35,8 @@
 # include <unistd.h>
 #endif
 
+#include "quakedef.h"
+
 #include "fs.h"
 #include "wad.h"
 
@@ -106,6 +106,19 @@ CONSTANTS
 #define ZIP_CDIR_CHUNK_BASE_SIZE       46
 #define ZIP_LOCAL_CHUNK_BASE_SIZE      30
 
+#ifdef LINK_TO_ZLIB
+#include <zlib.h>
+
+#define qz_inflate inflate
+#define qz_inflateEnd inflateEnd
+#define qz_inflateInit2_ inflateInit2_
+#define qz_inflateReset inflateReset
+#define qz_deflateInit2_ deflateInit2_
+#define qz_deflateEnd deflateEnd
+#define qz_deflate deflate
+#define Z_MEMLEVEL_DEFAULT 8
+#else
+
 // Zlib constants (from zlib.h)
 #define Z_SYNC_FLUSH   2
 #define MAX_WBITS              15
@@ -166,6 +179,7 @@ typedef struct
        unsigned long   adler;          ///< adler32 value of the uncompressed data
        unsigned long   reserved;       ///< reserved for future use
 } z_stream;
+#endif
 
 
 /// inside a package (PAK or PK3)
@@ -174,6 +188,8 @@ typedef struct
 #define QFILE_FLAG_DEFLATED (1 << 1)
 /// file is actually already loaded data
 #define QFILE_FLAG_DATA (1 << 2)
+/// real file will be removed on close
+#define QFILE_FLAG_REMOVE (1 << 3)
 
 #define FILE_BUFF_SIZE 2048
 typedef struct
@@ -201,6 +217,8 @@ struct qfile_s
        ztoolkit_t*             ztk;    ///< For zipped files.
 
        const unsigned char *data;      ///< For data files.
+
+       const char *filename; ///< Kept around for QFILE_FLAG_REMOVE, unused otherwise
 };
 
 
@@ -218,6 +236,7 @@ typedef struct pk3_endOfCentralDir_s
        unsigned int cdir_size;                 ///< size of the central directory
        unsigned int cdir_offset;               ///< with respect to the starting disk number
        unsigned short comment_size;
+       fs_offset_t prepended_garbage;
 } pk3_endOfCentralDir_t;
 
 
@@ -262,6 +281,7 @@ typedef struct pack_s
        int handle;
        int ignorecase;  ///< PK3 ignores case
        int numfiles;
+       qboolean vpack;
        packfile_t *files;
 } pack_t;
 //@}
@@ -312,6 +332,7 @@ const char *const fs_checkgamedir_missing = "missing";
 char fs_userdir[MAX_OSPATH];
 char fs_gamedir[MAX_OSPATH];
 char fs_basedir[MAX_OSPATH];
+static pack_t *fs_selfpack = NULL;
 
 // list of active game directories (empty if not running a mod)
 int fs_numgamedirs = 0;
@@ -334,6 +355,7 @@ PRIVATE FUNCTIONS - PK3 HANDLING
 =============================================================================
 */
 
+#ifndef LINK_TO_ZLIB
 // Functions exported from zlib
 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
 # define ZEXPORT WINAPI
@@ -348,12 +370,14 @@ static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
+#endif
 
 #define qz_inflateInit2(strm, windowBits) \
         qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
         qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
 
+#ifndef LINK_TO_ZLIB
 //        qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
 
 static dllfunction_t zlibfuncs[] =
@@ -370,6 +394,7 @@ static dllfunction_t zlibfuncs[] =
 
 /// Handle for Zlib DLL
 static dllhandle_t zlib_dll = NULL;
+#endif
 
 #ifdef WIN32
 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
@@ -390,7 +415,9 @@ Unload the Zlib DLL
 */
 void PK3_CloseLibrary (void)
 {
+#ifndef LINK_TO_ZLIB
        Sys_UnloadLibrary (&zlib_dll);
+#endif
 }
 
 
@@ -403,11 +430,12 @@ Try to load the Zlib DLL
 */
 qboolean PK3_OpenLibrary (void)
 {
+#ifdef LINK_TO_ZLIB
+       return true;
+#else
        const char* dllnames [] =
        {
-#if defined(WIN64)
-               "zlib64.dll",
-#elif defined(WIN32)
+#if defined(WIN32)
 # ifdef ZLIB_USES_WINAPI
                "zlibwapi.dll",
                "zlib.dll",
@@ -429,6 +457,7 @@ qboolean PK3_OpenLibrary (void)
 
        // Load the DLL
        return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
+#endif
 }
 
 /*
@@ -440,8 +469,12 @@ See if zlib is available
 */
 qboolean FS_HasZlib(void)
 {
+#ifdef LINK_TO_ZLIB
+       return true;
+#else
        PK3_OpenLibrary(); // to be safe
        return (zlib_dll != 0);
+#endif
 }
 
 /*
@@ -500,6 +533,8 @@ qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOf
        eocd->cdir_size = LittleLong (eocd->cdir_size);
        eocd->cdir_offset = LittleLong (eocd->cdir_offset);
        eocd->comment_size = LittleShort (eocd->comment_size);
+       eocd->prepended_garbage = filesize - (ind + ZIP_END_CDIR_SIZE) - eocd->cdir_offset - eocd->cdir_size; // this detects "SFX" zip files
+       eocd->cdir_offset += eocd->prepended_garbage;
 
        Mem_Free (buffer);
 
@@ -523,7 +558,7 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
        // Load the central directory in memory
        central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
        lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
-       if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size)
+       if(read (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
        {
                Mem_Free (central_dir);
                return -1;
@@ -593,7 +628,7 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
                                        flags = PACKFILE_FLAG_DEFLATED;
                                else
                                        flags = 0;
-                               offset = BuffLittleLong (&ptr[42]);
+                               offset = BuffLittleLong (&ptr[42]) + eocd->prepended_garbage;
                                packsize = BuffLittleLong (&ptr[20]);
                                realsize = BuffLittleLong (&ptr[24]);
 
@@ -634,24 +669,16 @@ FS_LoadPackPK3
 Create a package entry associated with a PK3 file
 ====================
 */
-pack_t *FS_LoadPackPK3 (const char *packfile)
+pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean silent)
 {
-       int packhandle;
        pk3_endOfCentralDir_t eocd;
        pack_t *pack;
        int real_nb_files;
 
-#if _MSC_VER >= 1400
-       _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
-#else
-       packhandle = open (packfile, O_RDONLY | O_BINARY);
-#endif
-       if (packhandle < 0)
-               return NULL;
-
        if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
        {
-               Con_Printf ("%s is not a PK3 file\n", packfile);
+               if(!silent)
+                       Con_Printf ("%s is not a PK3 file\n", packfile);
                close(packhandle);
                return NULL;
        }
@@ -692,9 +719,21 @@ pack_t *FS_LoadPackPK3 (const char *packfile)
                return NULL;
        }
 
-       Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
+       Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
        return pack;
 }
+pack_t *FS_LoadPackPK3 (const char *packfile)
+{
+       int packhandle;
+#if _MSC_VER >= 1400
+       _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+#else
+       packhandle = open (packfile, O_RDONLY | O_BINARY);
+#endif
+       if (packhandle < 0)
+               return NULL;
+       return FS_LoadPackPK3FromFD(packfile, packhandle, false);
+}
 
 
 /*
@@ -831,7 +870,12 @@ void FS_Path_f (void)
        for (s=fs_searchpaths ; s ; s=s->next)
        {
                if (s->pack)
-                       Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
+               {
+                       if(s->pack->vpack)
+                               Con_Printf("%sdir (virtual pack)\n", s->pack->filename);
+                       else
+                               Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
+               }
                else
                        Con_Printf("%s\n", s->filename);
        }
@@ -921,7 +965,28 @@ pack_t *FS_LoadPackPAK (const char *packfile)
 
        Mem_Free(info);
 
-       Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
+       Con_DPrintf("Added packfile %s (%i files)\n", packfile, numpackfiles);
+       return pack;
+}
+
+/*
+====================
+FS_LoadPackVirtual
+
+Create a package entry associated with a directory file
+====================
+*/
+pack_t *FS_LoadPackVirtual (const char *dirname)
+{
+       pack_t *pack;
+       pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
+       pack->vpack = true;
+       pack->ignorecase = false;
+       strlcpy (pack->filename, dirname, sizeof(pack->filename));
+       pack->handle = -1;
+       pack->numfiles = -1;
+       pack->files = NULL;
+       Con_DPrintf("Added packfile %s (virtual pack)\n", dirname);
        return pack;
 }
 
@@ -945,6 +1010,7 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname,
        searchpath_t *search;
        pack_t *pak = NULL;
        const char *ext = FS_FileExtension(pakfile);
+       size_t l;
 
        for(search = fs_searchpaths; search; search = search->next)
        {
@@ -959,16 +1025,19 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname,
        if(already_loaded)
                *already_loaded = false;
 
-       if(!strcasecmp(ext, "pak"))
+       if(!strcasecmp(ext, "pk3dir"))
+               pak = FS_LoadPackVirtual (pakfile);
+       else if(!strcasecmp(ext, "pak"))
                pak = FS_LoadPackPAK (pakfile);
        else if(!strcasecmp(ext, "pk3"))
                pak = FS_LoadPackPK3 (pakfile);
        else
                Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
 
-       if (pak)
+       if(pak)
        {
                strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
+
                //Con_DPrintf("  Registered pack with short name %s\n", shortname);
                if(keep_plain_dirs)
                {
@@ -992,7 +1061,6 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname,
                        if(!insertion_point)
                        {
                                search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
-                               search->pack = pak;
                                search->next = fs_searchpaths;
                                fs_searchpaths = search;
                        }
@@ -1000,7 +1068,6 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname,
                        // otherwise we want to append directly after insertion_point.
                        {
                                search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
-                               search->pack = pak;
                                search->next = insertion_point->next;
                                insertion_point->next = search;
                        }
@@ -1008,10 +1075,24 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname,
                else
                {
                        search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
-                       search->pack = pak;
                        search->next = fs_searchpaths;
                        fs_searchpaths = search;
                }
+               search->pack = pak;
+               if(pak->vpack)
+               {
+                       dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile);
+                       // if shortname ends with "pk3dir", strip that suffix to make it just "pk3"
+                       // same goes for the name inside the pack structure
+                       l = strlen(pak->shortname);
+                       if(l >= 7)
+                               if(!strcasecmp(pak->shortname + l - 7, ".pk3dir"))
+                                       pak->shortname[l - 3] = 0;
+                       l = strlen(pak->filename);
+                       if(l >= 7)
+                               if(!strcasecmp(pak->filename + l - 7, ".pk3dir"))
+                                       pak->filename[l - 3] = 0;
+               }
                return true;
        }
        else
@@ -1091,7 +1172,7 @@ void FS_AddGameDirectory (const char *dir)
        // add any PK3 package in the directory
        for (i = 0;i < list.numstrings;i++)
        {
-               if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
+               if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir"))
                {
                        FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
                }
@@ -1182,19 +1263,34 @@ void FS_ClearSearchPath (void)
        {
                searchpath_t *search = fs_searchpaths;
                fs_searchpaths = search->next;
-               if (search->pack)
+               if (search->pack && search->pack != fs_selfpack)
                {
-                       // close the file
-                       close(search->pack->handle);
-                       // free any memory associated with it
-                       if (search->pack->files)
-                               Mem_Free(search->pack->files);
+                       if(!search->pack->vpack)
+                       {
+                               // close the file
+                               close(search->pack->handle);
+                               // free any memory associated with it
+                               if (search->pack->files)
+                                       Mem_Free(search->pack->files);
+                       }
                        Mem_Free(search->pack);
                }
                Mem_Free(search);
        }
 }
 
+static void FS_AddSelfPack(void)
+{
+       if(fs_selfpack)
+       {
+               searchpath_t *search;
+               search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
+               search->next = fs_searchpaths;
+               search->pack = fs_selfpack;
+               fs_searchpaths = search;
+       }
+}
+
 
 /*
 ================
@@ -1241,6 +1337,9 @@ void FS_Rescan (void)
        }
        Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
 
+       // add back the selfpack as new first item
+       FS_AddSelfPack();
+
        // set the default screenshot name to either the mod name or the
        // gamemode screenshot name
        if (strcmp(com_modname, gamedirname1))
@@ -1256,18 +1355,31 @@ void FS_Rescan (void)
                unlink (va("%s/qconsole.log", fs_gamedir));
 
        // look for the pop.lmp file and set registered to true if it is found
-       if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
+       if (FS_FileExists("gfx/pop.lmp"))
+               Cvar_Set ("registered", "1");
+       switch(gamemode)
        {
-               if (fs_modified)
-                       Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
+       case GAME_NORMAL:
+       case GAME_HIPNOTIC:
+       case GAME_ROGUE:
+               if (!registered.integer)
+               {
+                       if (fs_modified)
+                               Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
+                       else
+                               Con_Print("Playing shareware version.\n");
+               }
                else
-                       Con_Print("Playing shareware version.\n");
-       }
-       else
-       {
-               Cvar_Set ("registered", "1");
-               if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
                        Con_Print("Playing registered version.\n");
+               break;
+       case GAME_STEELSTORM:
+               if (registered.integer)
+                       Con_Print("Playing registered version.\n");
+               else
+                       Con_Print("Playing shareware version.\n");
+               break;
+       default:
+               break;
        }
 
        // unload all wads so that future queries will return the new data
@@ -1389,7 +1501,6 @@ void FS_GameDir_f (void)
        FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
 }
 
-static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking);
 static const char *FS_SysCheckGameDir(const char *gamedir)
 {
        static char buf[8192];
@@ -1488,7 +1599,7 @@ static void FS_ListGameDirs(void)
        }
        stringlistfreecontents(&list);
 
-       fs_all_gamedirs = Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
+       fs_all_gamedirs = (gamedir_t *)Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
        for(i = 0; i < list2.numstrings; ++i)
        {
                info = FS_CheckGameDir(list2.strings[i]);
@@ -1505,6 +1616,58 @@ static void FS_ListGameDirs(void)
        }
 }
 
+/*
+================
+FS_Init_SelfPack
+================
+*/
+void FS_Init_SelfPack (void)
+{
+       PK3_OpenLibrary ();
+       fs_mempool = Mem_AllocPool("file management", 0, NULL);
+       if(com_selffd >= 0)
+       {
+               fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
+               if(fs_selfpack)
+               {
+                       char *buf, *q;
+                       const char *p;
+                       FS_AddSelfPack();
+                       buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL);
+                       if(buf)
+                       {
+                               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))
+                               {
+                                       if(i >= args_left)
+                                               break;
+                                       q = (char *)Mem_Alloc(fs_mempool, strlen(com_token) + 1);
+                                       strlcpy(q, com_token, strlen(com_token) + 1);
+                                       new_argv[com_argc + i] = q;
+                                       ++i;
+                               }
+                               new_argv[i+com_argc] = NULL;
+                               com_argv = new_argv;
+                               com_argc = com_argc + i;
+                       }
+                       Mem_Free(buf);
+               }
+       }
+}
+
 /*
 ================
 FS_Init
@@ -1532,8 +1695,6 @@ void FS_Init (void)
        // don't care for the result; if it fails, %USERPROFILE% will be used instead
 #endif
 
-       fs_mempool = Mem_AllocPool("file management", 0, NULL);
-
        // Add the personal game directory
        if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
        {
@@ -1622,8 +1783,6 @@ void FS_Init (void)
 #endif
 #endif
 
-       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)
@@ -1710,16 +1869,9 @@ void FS_Shutdown (void)
 #endif
 }
 
-/*
-====================
-FS_SysOpen
-
-Internal function used to create a qfile_t and open the relevant non-packed file on disk
-====================
-*/
-static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
+int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
 {
-       qfile_t* file;
+       int handle;
        int mod, opt;
        unsigned int ind;
 
@@ -1740,7 +1892,7 @@ static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean non
                        break;
                default:
                        Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
-                       return NULL;
+                       return -1;
        }
        for (ind = 1; mode[ind] != '\0'; ind++)
        {
@@ -1761,25 +1913,40 @@ static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean non
        if (nonblocking)
                opt |= O_NONBLOCK;
 
-       file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
-       memset (file, 0, sizeof (*file));
-       file->ungetc = EOF;
-
 #if _MSC_VER >= 1400
-       _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+       _sopen_s(&handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
 #else
-       file->handle = open (filepath, mod | opt, 0666);
+       handle = open (filepath, mod | opt, 0666);
 #endif
+       return handle;
+}
+
+/*
+====================
+FS_SysOpen
+
+Internal function used to create a qfile_t and open the relevant non-packed file on disk
+====================
+*/
+qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
+{
+       qfile_t* file;
+
+       file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
+       file->ungetc = EOF;
+       file->handle = FS_SysOpenFD(filepath, mode, nonblocking);
        if (file->handle < 0)
        {
                Mem_Free (file);
                return NULL;
        }
 
+       file->filename = Mem_strdup(fs_mempool, filepath);
+
        file->real_length = lseek (file->handle, 0, SEEK_END);
 
        // For files opened in append mode, we start at the end of the file
-       if (mod & O_APPEND)
+       if (mode[0] == 'a')
                file->position = file->real_length;
        else
                lseek (file->handle, 0, SEEK_SET);
@@ -1808,6 +1975,7 @@ qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
                if (!PK3_GetTrueFileOffset (pfile, pack))
                        return NULL;
 
+#ifndef LINK_TO_ZLIB
        // No Zlib DLL = no compressed files
        if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
        {
@@ -1816,6 +1984,7 @@ qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
                                        pfile->name);
                return NULL;
        }
+#endif
 
        // LordHavoc: lseek 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
@@ -1968,7 +2137,7 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
        for (search = fs_searchpaths;search;search = search->next)
        {
                // is the element a pak file?
-               if (search->pack)
+               if (search->pack && !search->pack->vpack)
                {
                        int (*strcmp_funct) (const char* str1, const char* str2);
                        int left, right, middle;
@@ -1992,16 +2161,16 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
                                        if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
                                        {
                                                // yes, but the first one is empty so we treat it as not being there
-                                               if (!quiet && developer.integer >= 10)
-                                                       Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
+                                               if (!quiet && developer_extra.integer)
+                                                       Con_DPrintf("FS_FindFile: %s is marked as deleted\n", name);
 
                                                if (index != NULL)
                                                        *index = -1;
                                                return NULL;
                                        }
 
-                                       if (!quiet && developer.integer >= 10)
-                                               Con_Printf("FS_FindFile: %s in %s\n",
+                                       if (!quiet && developer_extra.integer)
+                                               Con_DPrintf("FS_FindFile: %s in %s\n",
                                                                        pak->files[middle].name, pak->filename);
 
                                        if (index != NULL)
@@ -2022,8 +2191,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
                        dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
                        if (FS_SysFileExists (netpath))
                        {
-                               if (!quiet && developer.integer >= 10)
-                                       Con_Printf("FS_FindFile: %s\n", netpath);
+                               if (!quiet && developer_extra.integer)
+                                       Con_DPrintf("FS_FindFile: %s\n", netpath);
 
                                if (index != NULL)
                                        *index = -1;
@@ -2032,8 +2201,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
                }
        }
 
-       if (!quiet && developer.integer >= 10)
-               Con_Printf("FS_FindFile: can't find %s\n", name);
+       if (!quiet && developer_extra.integer)
+               Con_DPrintf("FS_FindFile: can't find %s\n", name);
 
        if (index != NULL)
                *index = -1;
@@ -2062,6 +2231,7 @@ qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonbloc
        // Found in the filesystem?
        if (pack_ind < 0)
        {
+               // this works with vpacks, so we are fine
                char path [MAX_OSPATH];
                dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
                return FS_SysOpen (path, "rb", nonblocking);
@@ -2172,7 +2342,7 @@ qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet
                return NULL;
        }
 
-       dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
+       dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath); // this is never a vpack
 
        // If the file is opened in "write", "append", or "read/write" mode,
        // create directories up to the file.
@@ -2238,6 +2408,14 @@ int FS_Close (qfile_t* file)
        if (close (file->handle))
                return EOF;
 
+       if (file->filename)
+       {
+               if (file->flags & QFILE_FLAG_REMOVE)
+                       remove(file->filename);
+
+               Mem_Free((void *) file->filename);
+       }
+
        if (file->ztk)
        {
                qz_inflateEnd (&file->ztk->zstream);
@@ -2248,6 +2426,10 @@ int FS_Close (qfile_t* file)
        return 0;
 }
 
+void FS_RemoveOnClose(qfile_t* file)
+{
+       file->flags |= QFILE_FLAG_REMOVE;
+}
 
 /*
 ====================
@@ -2751,9 +2933,11 @@ FS_WriteFile
 The filename will be prefixed by the current game directory
 ============
 */
-qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
+qboolean FS_WriteFileInBlocks (const char *filename, const void *const *data, const fs_offset_t *len, size_t count)
 {
        qfile_t *file;
+       size_t i;
+       fs_offset_t lentotal;
 
        file = FS_OpenRealFile(filename, "wb", false);
        if (!file)
@@ -2762,12 +2946,21 @@ qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
                return false;
        }
 
-       Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
-       FS_Write (file, data, len);
+       lentotal = 0;
+       for(i = 0; i < count; ++i)
+               lentotal += len[i];
+       Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)lentotal);
+       for(i = 0; i < count; ++i)
+               FS_Write (file, data[i], len[i]);
        FS_Close (file);
        return true;
 }
 
+qboolean FS_WriteFile (const char *filename, const void *data, fs_offset_t len)
+{
+       return FS_WriteFileInBlocks(filename, &data, &len, 1);
+}
+
 
 /*
 =============================================================================
@@ -2847,7 +3040,7 @@ int FS_FileType (const char *filename)
        if(!search)
                return FS_FILETYPE_NONE;
 
-       if(search->pack)
+       if(search->pack && !search->pack->vpack)
                return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
 
        dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
@@ -2898,6 +3091,9 @@ int FS_SysFileType (const char *path)
        if (stat (path,&buf) == -1)
                return FS_FILETYPE_NONE;
 
+#ifndef S_ISDIR
+#define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
+#endif
        if(S_ISDIR(buf.st_mode))
                return FS_FILETYPE_DIRECTORY;
 
@@ -2965,7 +3161,7 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
        for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
        {
                // is the element a pak file?
-               if (searchpath->pack)
+               if (searchpath->pack && !searchpath->pack->vpack)
                {
                        // look through all the pak file elements
                        pak = searchpath->pack;
@@ -3241,7 +3437,12 @@ void FS_Which_f(void)
                return;
        }
        if (sp->pack)
-               Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
+       {
+               if(sp->pack->vpack)
+                       Con_Printf("%s is in virtual package %sdir\n", filename, sp->pack->shortname);
+               else
+                       Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
+       }
        else
                Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
 }
@@ -3274,7 +3475,8 @@ qboolean FS_IsRegisteredQuakePack(const char *name)
        // search through the path, one element at a time
        for (search = fs_searchpaths;search;search = search->next)
        {
-               if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
+               if (search->pack && !search->pack->vpack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
+                       // TODO do we want to support vpacks in here too?
                {
                        int (*strcmp_funct) (const char* str1, const char* str2);
                        int left, right, middle;
@@ -3337,6 +3539,12 @@ unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflat
        unsigned char *out = NULL;
        unsigned char *tmp;
 
+       *deflated_size = 0;
+#ifndef LINK_TO_ZLIB
+       if(!zlib_dll)
+               return NULL;
+#endif
+
        memset(&strm, 0, sizeof(strm));
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
@@ -3430,6 +3638,12 @@ unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflat
        unsigned int have;
        sizebuf_t outbuf;
 
+       *inflated_size = 0;
+#ifndef LINK_TO_ZLIB
+       if(!zlib_dll)
+               return NULL;
+#endif
+
        memset(&outbuf, 0, sizeof(outbuf));
        outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
        outbuf.maxsize = sizeof(tmp);