fix use of variable name "dir"
[divverent/darkplaces.git] / fs.c
1 /*
2         DarkPlaces file system
3
4         Copyright (C) 2003-2006 Mathieu Olivier
5
6         This program is free software; you can redistribute it and/or
7         modify it under the terms of the GNU General Public License
8         as published by the Free Software Foundation; either version 2
9         of the License, or (at your option) any later version.
10
11         This program is distributed in the hope that it will be useful,
12         but WITHOUT ANY WARRANTY; without even the implied warranty of
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15         See the GNU General Public License for more details.
16
17         You should have received a copy of the GNU General Public License
18         along with this program; if not, write to:
19
20                 Free Software Foundation, Inc.
21                 59 Temple Place - Suite 330
22                 Boston, MA  02111-1307, USA
23 */
24
25 #include "quakedef.h"
26
27 #include <limits.h>
28 #include <fcntl.h>
29
30 #ifdef WIN32
31 # include <direct.h>
32 # include <io.h>
33 # include <shlobj.h>
34 #else
35 # include <pwd.h>
36 # include <sys/stat.h>
37 # include <unistd.h>
38 #endif
39
40 #include "fs.h"
41 #include "wad.h"
42
43 // Win32 requires us to add O_BINARY, but the other OSes don't have it
44 #ifndef O_BINARY
45 # define O_BINARY 0
46 #endif
47
48 // In case the system doesn't support the O_NONBLOCK flag
49 #ifndef O_NONBLOCK
50 # define O_NONBLOCK 0
51 #endif
52
53 // largefile support for Win32
54 #ifdef WIN32
55 # define lseek _lseeki64
56 #endif
57
58 #if _MSC_VER >= 1400
59 // suppress deprecated warnings
60 # include <sys/stat.h>
61 # include <share.h>
62 # define read _read
63 # define write _write
64 # define close _close
65 # define unlink _unlink
66 # define dup _dup
67 #endif
68
69 /** \page fs File System
70
71 All of Quake's data access is through a hierchal file system, but the contents
72 of the file system can be transparently merged from several sources.
73
74 The "base directory" is the path to the directory holding the quake.exe and
75 all game directories.  The sys_* files pass this to host_init in
76 quakeparms_t->basedir.  This can be overridden with the "-basedir" command
77 line parm to allow code debugging in a different directory.  The base
78 directory is only used during filesystem initialization.
79
80 The "game directory" is the first tree on the search path and directory that
81 all generated files (savegames, screenshots, demos, config files) will be
82 saved to.  This can be overridden with the "-game" command line parameter.
83 The game directory can never be changed while quake is executing.  This is a
84 precaution against having a malicious server instruct clients to write files
85 over areas they shouldn't.
86
87 */
88
89
90 /*
91 =============================================================================
92
93 CONSTANTS
94
95 =============================================================================
96 */
97
98 // Magic numbers of a ZIP file (big-endian format)
99 #define ZIP_DATA_HEADER 0x504B0304  // "PK\3\4"
100 #define ZIP_CDIR_HEADER 0x504B0102  // "PK\1\2"
101 #define ZIP_END_HEADER  0x504B0506  // "PK\5\6"
102
103 // Other constants for ZIP files
104 #define ZIP_MAX_COMMENTS_SIZE           ((unsigned short)0xFFFF)
105 #define ZIP_END_CDIR_SIZE                       22
106 #define ZIP_CDIR_CHUNK_BASE_SIZE        46
107 #define ZIP_LOCAL_CHUNK_BASE_SIZE       30
108
109 // Zlib constants (from zlib.h)
110 #define Z_SYNC_FLUSH    2
111 #define MAX_WBITS               15
112 #define Z_OK                    0
113 #define Z_STREAM_END    1
114 #define Z_STREAM_ERROR  (-2)
115 #define Z_DATA_ERROR    (-3)
116 #define Z_MEM_ERROR     (-4)
117 #define Z_BUF_ERROR     (-5)
118 #define ZLIB_VERSION    "1.2.3"
119
120 #define Z_BINARY 0
121 #define Z_DEFLATED 8
122 #define Z_MEMLEVEL_DEFAULT 8
123
124 #define Z_NULL 0
125 #define Z_DEFAULT_COMPRESSION (-1)
126 #define Z_NO_FLUSH 0
127 #define Z_SYNC_FLUSH 2
128 #define Z_FULL_FLUSH 3
129 #define Z_FINISH 4
130
131 // Uncomment the following line if the zlib DLL you have still uses
132 // the 1.1.x series calling convention on Win32 (WINAPI)
133 //#define ZLIB_USES_WINAPI
134
135
136 /*
137 =============================================================================
138
139 TYPES
140
141 =============================================================================
142 */
143
144 /*! Zlib stream (from zlib.h)
145  * \warning: some pointers we don't use directly have
146  * been cast to "void*" for a matter of simplicity
147  */
148 typedef struct
149 {
150         unsigned char                   *next_in;       ///< next input byte
151         unsigned int    avail_in;       ///< number of bytes available at next_in
152         unsigned long   total_in;       ///< total nb of input bytes read so far
153
154         unsigned char                   *next_out;      ///< next output byte should be put there
155         unsigned int    avail_out;      ///< remaining free space at next_out
156         unsigned long   total_out;      ///< total nb of bytes output so far
157
158         char                    *msg;           ///< last error message, NULL if no error
159         void                    *state;         ///< not visible by applications
160
161         void                    *zalloc;        ///< used to allocate the internal state
162         void                    *zfree;         ///< used to free the internal state
163         void                    *opaque;        ///< private data object passed to zalloc and zfree
164
165         int                             data_type;      ///< best guess about the data type: ascii or binary
166         unsigned long   adler;          ///< adler32 value of the uncompressed data
167         unsigned long   reserved;       ///< reserved for future use
168 } z_stream;
169
170
171 /// inside a package (PAK or PK3)
172 #define QFILE_FLAG_PACKED (1 << 0)
173 /// file is compressed using the deflate algorithm (PK3 only)
174 #define QFILE_FLAG_DEFLATED (1 << 1)
175 /// file is actually already loaded data
176 #define QFILE_FLAG_DATA (1 << 2)
177
178 #define FILE_BUFF_SIZE 2048
179 typedef struct
180 {
181         z_stream        zstream;
182         size_t          comp_length;                    ///< length of the compressed file
183         size_t          in_ind, in_len;                 ///< input buffer current index and length
184         size_t          in_position;                    ///< position in the compressed file
185         unsigned char           input [FILE_BUFF_SIZE];
186 } ztoolkit_t;
187
188 struct qfile_s
189 {
190         int                             flags;
191         int                             handle;                                 ///< file descriptor
192         fs_offset_t             real_length;                    ///< uncompressed file size (for files opened in "read" mode)
193         fs_offset_t             position;                               ///< current position in the file
194         fs_offset_t             offset;                                 ///< offset into the package (0 if external file)
195         int                             ungetc;                                 ///< single stored character from ungetc, cleared to EOF when read
196
197         // Contents buffer
198         fs_offset_t             buff_ind, buff_len;             ///< buffer current index and length
199         unsigned char                   buff [FILE_BUFF_SIZE];
200
201         ztoolkit_t*             ztk;    ///< For zipped files.
202
203         const unsigned char *data;      ///< For data files.
204 };
205
206
207 // ------ PK3 files on disk ------ //
208
209 // You can get the complete ZIP format description from PKWARE website
210
211 typedef struct pk3_endOfCentralDir_s
212 {
213         unsigned int signature;
214         unsigned short disknum;
215         unsigned short cdir_disknum;    ///< number of the disk with the start of the central directory
216         unsigned short localentries;    ///< number of entries in the central directory on this disk
217         unsigned short nbentries;               ///< total number of entries in the central directory on this disk
218         unsigned int cdir_size;                 ///< size of the central directory
219         unsigned int cdir_offset;               ///< with respect to the starting disk number
220         unsigned short comment_size;
221 } pk3_endOfCentralDir_t;
222
223
224 // ------ PAK files on disk ------ //
225 typedef struct dpackfile_s
226 {
227         char name[56];
228         int filepos, filelen;
229 } dpackfile_t;
230
231 typedef struct dpackheader_s
232 {
233         char id[4];
234         int dirofs;
235         int dirlen;
236 } dpackheader_t;
237
238
239 /*! \name Packages in memory
240  * @{
241  */
242 /// the offset in packfile_t is the true contents offset
243 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
244 /// file compressed using the deflate algorithm
245 #define PACKFILE_FLAG_DEFLATED (1 << 1)
246 /// file is a symbolic link
247 #define PACKFILE_FLAG_SYMLINK (1 << 2)
248
249 typedef struct packfile_s
250 {
251         char name [MAX_QPATH];
252         int flags;
253         fs_offset_t offset;
254         fs_offset_t packsize;   ///< size in the package
255         fs_offset_t realsize;   ///< real file size (uncompressed)
256 } packfile_t;
257
258 typedef struct pack_s
259 {
260         char filename [MAX_OSPATH];
261         char shortname [MAX_QPATH];
262         int handle;
263         int ignorecase;  ///< PK3 ignores case
264         int numfiles;
265         packfile_t *files;
266 } pack_t;
267 //@}
268
269 /// Search paths for files (including packages)
270 typedef struct searchpath_s
271 {
272         // only one of filename / pack will be used
273         char filename[MAX_OSPATH];
274         pack_t *pack;
275         struct searchpath_s *next;
276 } searchpath_t;
277
278
279 /*
280 =============================================================================
281
282 FUNCTION PROTOTYPES
283
284 =============================================================================
285 */
286
287 void FS_Dir_f(void);
288 void FS_Ls_f(void);
289 void FS_Which_f(void);
290
291 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
292 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
293                                                                         fs_offset_t offset, fs_offset_t packsize,
294                                                                         fs_offset_t realsize, int flags);
295
296
297 /*
298 =============================================================================
299
300 VARIABLES
301
302 =============================================================================
303 */
304
305 mempool_t *fs_mempool;
306
307 searchpath_t *fs_searchpaths = NULL;
308 const char *const fs_checkgamedir_missing = "missing";
309
310 #define MAX_FILES_IN_PACK       65536
311
312 char fs_userdir[MAX_OSPATH];
313 char fs_gamedir[MAX_OSPATH];
314 char fs_basedir[MAX_OSPATH];
315
316 // list of active game directories (empty if not running a mod)
317 int fs_numgamedirs = 0;
318 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
319
320 // list of all gamedirs with modinfo.txt
321 gamedir_t *fs_all_gamedirs = NULL;
322 int fs_all_gamedirs_count = 0;
323
324 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running; the date is encoded using strftime escapes)"};
325 cvar_t fs_empty_files_in_pack_mark_deletions = {0, "fs_empty_files_in_pack_mark_deletions", "0", "if enabled, empty files in a pak/pk3 count as not existing but cancel the search in further packs, effectively allowing patch pak/pk3 files to 'delete' files"};
326
327
328 /*
329 =============================================================================
330
331 PRIVATE FUNCTIONS - PK3 HANDLING
332
333 =============================================================================
334 */
335
336 // Functions exported from zlib
337 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
338 # define ZEXPORT WINAPI
339 #else
340 # define ZEXPORT
341 #endif
342
343 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
344 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
345 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
346 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
347 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
348 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
349 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
350
351 #define qz_inflateInit2(strm, windowBits) \
352         qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
353 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
354         qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
355
356 //        qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
357
358 static dllfunction_t zlibfuncs[] =
359 {
360         {"inflate",                     (void **) &qz_inflate},
361         {"inflateEnd",          (void **) &qz_inflateEnd},
362         {"inflateInit2_",       (void **) &qz_inflateInit2_},
363         {"inflateReset",        (void **) &qz_inflateReset},
364         {"deflateInit2_",   (void **) &qz_deflateInit2_},
365         {"deflateEnd",      (void **) &qz_deflateEnd},
366         {"deflate",         (void **) &qz_deflate},
367         {NULL, NULL}
368 };
369
370 /// Handle for Zlib DLL
371 static dllhandle_t zlib_dll = NULL;
372
373 #ifdef WIN32
374 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
375 static dllfunction_t shfolderfuncs[] =
376 {
377         {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
378         {NULL, NULL}
379 };
380 static dllhandle_t shfolder_dll = NULL;
381 #endif
382
383 /*
384 ====================
385 PK3_CloseLibrary
386
387 Unload the Zlib DLL
388 ====================
389 */
390 void PK3_CloseLibrary (void)
391 {
392         Sys_UnloadLibrary (&zlib_dll);
393 }
394
395
396 /*
397 ====================
398 PK3_OpenLibrary
399
400 Try to load the Zlib DLL
401 ====================
402 */
403 qboolean PK3_OpenLibrary (void)
404 {
405         const char* dllnames [] =
406         {
407 #if defined(WIN64)
408                 "zlib64.dll",
409 #elif defined(WIN32)
410 # ifdef ZLIB_USES_WINAPI
411                 "zlibwapi.dll",
412                 "zlib.dll",
413 # else
414                 "zlib1.dll",
415 # endif
416 #elif defined(MACOSX)
417                 "libz.dylib",
418 #else
419                 "libz.so.1",
420                 "libz.so",
421 #endif
422                 NULL
423         };
424
425         // Already loaded?
426         if (zlib_dll)
427                 return true;
428
429         // Load the DLL
430         return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
431 }
432
433 /*
434 ====================
435 FS_HasZlib
436
437 See if zlib is available
438 ====================
439 */
440 qboolean FS_HasZlib(void)
441 {
442         PK3_OpenLibrary(); // to be safe
443         return (zlib_dll != 0);
444 }
445
446 /*
447 ====================
448 PK3_GetEndOfCentralDir
449
450 Extract the end of the central directory from a PK3 package
451 ====================
452 */
453 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
454 {
455         fs_offset_t filesize, maxsize;
456         unsigned char *buffer, *ptr;
457         int ind;
458
459         // Get the package size
460         filesize = lseek (packhandle, 0, SEEK_END);
461         if (filesize < ZIP_END_CDIR_SIZE)
462                 return false;
463
464         // Load the end of the file in memory
465         if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
466                 maxsize = filesize;
467         else
468                 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
469         buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
470         lseek (packhandle, filesize - maxsize, SEEK_SET);
471         if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
472         {
473                 Mem_Free (buffer);
474                 return false;
475         }
476
477         // Look for the end of central dir signature around the end of the file
478         maxsize -= ZIP_END_CDIR_SIZE;
479         ptr = &buffer[maxsize];
480         ind = 0;
481         while (BuffBigLong (ptr) != ZIP_END_HEADER)
482         {
483                 if (ind == maxsize)
484                 {
485                         Mem_Free (buffer);
486                         return false;
487                 }
488
489                 ind++;
490                 ptr--;
491         }
492
493         memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
494         eocd->signature = LittleLong (eocd->signature);
495         eocd->disknum = LittleShort (eocd->disknum);
496         eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
497         eocd->localentries = LittleShort (eocd->localentries);
498         eocd->nbentries = LittleShort (eocd->nbentries);
499         eocd->cdir_size = LittleLong (eocd->cdir_size);
500         eocd->cdir_offset = LittleLong (eocd->cdir_offset);
501         eocd->comment_size = LittleShort (eocd->comment_size);
502
503         Mem_Free (buffer);
504
505         return true;
506 }
507
508
509 /*
510 ====================
511 PK3_BuildFileList
512
513 Extract the file list from a PK3 file
514 ====================
515 */
516 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
517 {
518         unsigned char *central_dir, *ptr;
519         unsigned int ind;
520         fs_offset_t remaining;
521
522         // Load the central directory in memory
523         central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
524         lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
525         if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size)
526         {
527                 Mem_Free (central_dir);
528                 return -1;
529         }
530
531         // Extract the files properties
532         // The parsing is done "by hand" because some fields have variable sizes and
533         // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
534         remaining = eocd->cdir_size;
535         pack->numfiles = 0;
536         ptr = central_dir;
537         for (ind = 0; ind < eocd->nbentries; ind++)
538         {
539                 fs_offset_t namesize, count;
540
541                 // Checking the remaining size
542                 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
543                 {
544                         Mem_Free (central_dir);
545                         return -1;
546                 }
547                 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
548
549                 // Check header
550                 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
551                 {
552                         Mem_Free (central_dir);
553                         return -1;
554                 }
555
556                 namesize = BuffLittleShort (&ptr[28]);  // filename length
557
558                 // Check encryption, compression, and attributes
559                 // 1st uint8  : general purpose bit flag
560                 //    Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
561                 //
562                 // LordHavoc: bit 3 would be a problem if we were scanning the archive
563                 // but is not a problem in the central directory where the values are
564                 // always real.
565                 //
566                 // bit 3 seems to always be set by the standard Mac OSX zip maker
567                 //
568                 // 2nd uint8 : external file attributes
569                 //    Check bits 3 (file is a directory) and 5 (file is a volume (?))
570                 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
571                 {
572                         // Still enough bytes for the name?
573                         if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
574                         {
575                                 Mem_Free (central_dir);
576                                 return -1;
577                         }
578
579                         // WinZip doesn't use the "directory" attribute, so we need to check the name directly
580                         if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
581                         {
582                                 char filename [sizeof (pack->files[0].name)];
583                                 fs_offset_t offset, packsize, realsize;
584                                 int flags;
585
586                                 // Extract the name (strip it if necessary)
587                                 namesize = min(namesize, (int)sizeof (filename) - 1);
588                                 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
589                                 filename[namesize] = '\0';
590
591                                 if (BuffLittleShort (&ptr[10]))
592                                         flags = PACKFILE_FLAG_DEFLATED;
593                                 else
594                                         flags = 0;
595                                 offset = BuffLittleLong (&ptr[42]);
596                                 packsize = BuffLittleLong (&ptr[20]);
597                                 realsize = BuffLittleLong (&ptr[24]);
598
599                                 switch(ptr[5]) // C_VERSION_MADE_BY_1
600                                 {
601                                         case 3: // UNIX_
602                                         case 2: // VMS_
603                                         case 16: // BEOS_
604                                                 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
605                                                         // can't use S_ISLNK here, as this has to compile on non-UNIX too
606                                                         flags |= PACKFILE_FLAG_SYMLINK;
607                                                 break;
608                                 }
609
610                                 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
611                         }
612                 }
613
614                 // Skip the name, additionnal field, and comment
615                 // 1er uint16 : extra field length
616                 // 2eme uint16 : file comment length
617                 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
618                 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
619                 remaining -= count;
620         }
621
622         // If the package is empty, central_dir is NULL here
623         if (central_dir != NULL)
624                 Mem_Free (central_dir);
625         return pack->numfiles;
626 }
627
628
629 /*
630 ====================
631 FS_LoadPackPK3
632
633 Create a package entry associated with a PK3 file
634 ====================
635 */
636 pack_t *FS_LoadPackPK3 (const char *packfile)
637 {
638         int packhandle;
639         pk3_endOfCentralDir_t eocd;
640         pack_t *pack;
641         int real_nb_files;
642
643 #if _MSC_VER >= 1400
644         _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
645 #else
646         packhandle = open (packfile, O_RDONLY | O_BINARY);
647 #endif
648         if (packhandle < 0)
649                 return NULL;
650
651         if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
652         {
653                 Con_Printf ("%s is not a PK3 file\n", packfile);
654                 close(packhandle);
655                 return NULL;
656         }
657
658         // Multi-volume ZIP archives are NOT allowed
659         if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
660         {
661                 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
662                 close(packhandle);
663                 return NULL;
664         }
665
666         // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
667         // since eocd.nbentries is an unsigned 16 bits integer
668 #if MAX_FILES_IN_PACK < 65535
669         if (eocd.nbentries > MAX_FILES_IN_PACK)
670         {
671                 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
672                 close(packhandle);
673                 return NULL;
674         }
675 #endif
676
677         // Create a package structure in memory
678         pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
679         pack->ignorecase = true; // PK3 ignores case
680         strlcpy (pack->filename, packfile, sizeof (pack->filename));
681         pack->handle = packhandle;
682         pack->numfiles = eocd.nbentries;
683         pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
684
685         real_nb_files = PK3_BuildFileList (pack, &eocd);
686         if (real_nb_files < 0)
687         {
688                 Con_Printf ("%s is not a valid PK3 file\n", packfile);
689                 close(pack->handle);
690                 Mem_Free(pack);
691                 return NULL;
692         }
693
694         Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
695         return pack;
696 }
697
698
699 /*
700 ====================
701 PK3_GetTrueFileOffset
702
703 Find where the true file data offset is
704 ====================
705 */
706 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
707 {
708         unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
709         fs_offset_t count;
710
711         // Already found?
712         if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
713                 return true;
714
715         // Load the local file description
716         lseek (pack->handle, pfile->offset, SEEK_SET);
717         count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
718         if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
719         {
720                 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
721                 return false;
722         }
723
724         // Skip name and extra field
725         pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
726
727         pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
728         return true;
729 }
730
731
732 /*
733 =============================================================================
734
735 OTHER PRIVATE FUNCTIONS
736
737 =============================================================================
738 */
739
740
741 /*
742 ====================
743 FS_AddFileToPack
744
745 Add a file to the list of files contained into a package
746 ====================
747 */
748 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
749                                                                          fs_offset_t offset, fs_offset_t packsize,
750                                                                          fs_offset_t realsize, int flags)
751 {
752         int (*strcmp_funct) (const char* str1, const char* str2);
753         int left, right, middle;
754         packfile_t *pfile;
755
756         strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
757
758         // Look for the slot we should put that file into (binary search)
759         left = 0;
760         right = pack->numfiles - 1;
761         while (left <= right)
762         {
763                 int diff;
764
765                 middle = (left + right) / 2;
766                 diff = strcmp_funct (pack->files[middle].name, name);
767
768                 // If we found the file, there's a problem
769                 if (!diff)
770                         Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
771
772                 // If we're too far in the list
773                 if (diff > 0)
774                         right = middle - 1;
775                 else
776                         left = middle + 1;
777         }
778
779         // We have to move the right of the list by one slot to free the one we need
780         pfile = &pack->files[left];
781         memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
782         pack->numfiles++;
783
784         strlcpy (pfile->name, name, sizeof (pfile->name));
785         pfile->offset = offset;
786         pfile->packsize = packsize;
787         pfile->realsize = realsize;
788         pfile->flags = flags;
789
790         return pfile;
791 }
792
793
794 /*
795 ============
796 FS_CreatePath
797
798 Only used for FS_OpenRealFile.
799 ============
800 */
801 void FS_CreatePath (char *path)
802 {
803         char *ofs, save;
804
805         for (ofs = path+1 ; *ofs ; ofs++)
806         {
807                 if (*ofs == '/' || *ofs == '\\')
808                 {
809                         // create the directory
810                         save = *ofs;
811                         *ofs = 0;
812                         FS_mkdir (path);
813                         *ofs = save;
814                 }
815         }
816 }
817
818
819 /*
820 ============
821 FS_Path_f
822
823 ============
824 */
825 void FS_Path_f (void)
826 {
827         searchpath_t *s;
828
829         Con_Print("Current search path:\n");
830         for (s=fs_searchpaths ; s ; s=s->next)
831         {
832                 if (s->pack)
833                         Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
834                 else
835                         Con_Printf("%s\n", s->filename);
836         }
837 }
838
839
840 /*
841 =================
842 FS_LoadPackPAK
843 =================
844 */
845 /*! Takes an explicit (not game tree related) path to a pak file.
846  *Loads the header and directory, adding the files at the beginning
847  *of the list so they override previous pack files.
848  */
849 pack_t *FS_LoadPackPAK (const char *packfile)
850 {
851         dpackheader_t header;
852         int i, numpackfiles;
853         int packhandle;
854         pack_t *pack;
855         dpackfile_t *info;
856
857 #if _MSC_VER >= 1400
858         _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
859 #else
860         packhandle = open (packfile, O_RDONLY | O_BINARY);
861 #endif
862         if (packhandle < 0)
863                 return NULL;
864         if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
865         {
866                 Con_Printf ("%s is not a packfile\n", packfile);
867                 close(packhandle);
868                 return NULL;
869         }
870         if (memcmp(header.id, "PACK", 4))
871         {
872                 Con_Printf ("%s is not a packfile\n", packfile);
873                 close(packhandle);
874                 return NULL;
875         }
876         header.dirofs = LittleLong (header.dirofs);
877         header.dirlen = LittleLong (header.dirlen);
878
879         if (header.dirlen % sizeof(dpackfile_t))
880         {
881                 Con_Printf ("%s has an invalid directory size\n", packfile);
882                 close(packhandle);
883                 return NULL;
884         }
885
886         numpackfiles = header.dirlen / sizeof(dpackfile_t);
887
888         if (numpackfiles > MAX_FILES_IN_PACK)
889         {
890                 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
891                 close(packhandle);
892                 return NULL;
893         }
894
895         info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
896         lseek (packhandle, header.dirofs, SEEK_SET);
897         if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
898         {
899                 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
900                 Mem_Free(info);
901                 close(packhandle);
902                 return NULL;
903         }
904
905         pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
906         pack->ignorecase = false; // PAK is case sensitive
907         strlcpy (pack->filename, packfile, sizeof (pack->filename));
908         pack->handle = packhandle;
909         pack->numfiles = 0;
910         pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
911
912         // parse the directory
913         for (i = 0;i < numpackfiles;i++)
914         {
915                 fs_offset_t offset = LittleLong (info[i].filepos);
916                 fs_offset_t size = LittleLong (info[i].filelen);
917
918                 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
919         }
920
921         Mem_Free(info);
922
923         Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
924         return pack;
925 }
926
927 /*
928 ================
929 FS_AddPack_Fullpath
930 ================
931 */
932 /*! Adds the given pack to the search path.
933  * The pack type is autodetected by the file extension.
934  *
935  * Returns true if the file was successfully added to the
936  * search path or if it was already included.
937  *
938  * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
939  * plain directories.
940  *
941  */
942 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
943 {
944         searchpath_t *search;
945         pack_t *pak = NULL;
946         const char *ext = FS_FileExtension(pakfile);
947
948         for(search = fs_searchpaths; search; search = search->next)
949         {
950                 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
951                 {
952                         if(already_loaded)
953                                 *already_loaded = true;
954                         return true; // already loaded
955                 }
956         }
957
958         if(already_loaded)
959                 *already_loaded = false;
960
961         if(!strcasecmp(ext, "pak"))
962                 pak = FS_LoadPackPAK (pakfile);
963         else if(!strcasecmp(ext, "pk3"))
964                 pak = FS_LoadPackPK3 (pakfile);
965         else
966                 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
967
968         if (pak)
969         {
970                 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
971                 //Con_DPrintf("  Registered pack with short name %s\n", shortname);
972                 if(keep_plain_dirs)
973                 {
974                         // find the first item whose next one is a pack or NULL
975                         searchpath_t *insertion_point = 0;
976                         if(fs_searchpaths && !fs_searchpaths->pack)
977                         {
978                                 insertion_point = fs_searchpaths;
979                                 for(;;)
980                                 {
981                                         if(!insertion_point->next)
982                                                 break;
983                                         if(insertion_point->next->pack)
984                                                 break;
985                                         insertion_point = insertion_point->next;
986                                 }
987                         }
988                         // If insertion_point is NULL, this means that either there is no
989                         // item in the list yet, or that the very first item is a pack. In
990                         // that case, we want to insert at the beginning...
991                         if(!insertion_point)
992                         {
993                                 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
994                                 search->pack = pak;
995                                 search->next = fs_searchpaths;
996                                 fs_searchpaths = search;
997                         }
998                         else
999                         // otherwise we want to append directly after insertion_point.
1000                         {
1001                                 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1002                                 search->pack = pak;
1003                                 search->next = insertion_point->next;
1004                                 insertion_point->next = search;
1005                         }
1006                 }
1007                 else
1008                 {
1009                         search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1010                         search->pack = pak;
1011                         search->next = fs_searchpaths;
1012                         fs_searchpaths = search;
1013                 }
1014                 return true;
1015         }
1016         else
1017         {
1018                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1019                 return false;
1020         }
1021 }
1022
1023
1024 /*
1025 ================
1026 FS_AddPack
1027 ================
1028 */
1029 /*! Adds the given pack to the search path and searches for it in the game path.
1030  * The pack type is autodetected by the file extension.
1031  *
1032  * Returns true if the file was successfully added to the
1033  * search path or if it was already included.
1034  *
1035  * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1036  * plain directories.
1037  */
1038 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1039 {
1040         char fullpath[MAX_QPATH];
1041         int index;
1042         searchpath_t *search;
1043
1044         if(already_loaded)
1045                 *already_loaded = false;
1046
1047         // then find the real name...
1048         search = FS_FindFile(pakfile, &index, true);
1049         if(!search || search->pack)
1050         {
1051                 Con_Printf("could not find pak \"%s\"\n", pakfile);
1052                 return false;
1053         }
1054
1055         dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1056
1057         return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1058 }
1059
1060
1061 /*
1062 ================
1063 FS_AddGameDirectory
1064
1065 Sets fs_gamedir, adds the directory to the head of the path,
1066 then loads and adds pak1.pak pak2.pak ...
1067 ================
1068 */
1069 void FS_AddGameDirectory (const char *dir)
1070 {
1071         int i;
1072         stringlist_t list;
1073         searchpath_t *search;
1074
1075         strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1076
1077         stringlistinit(&list);
1078         listdirectory(&list, "", dir);
1079         stringlistsort(&list);
1080
1081         // add any PAK package in the directory
1082         for (i = 0;i < list.numstrings;i++)
1083         {
1084                 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1085                 {
1086                         FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1087                 }
1088         }
1089
1090         // add any PK3 package in the directory
1091         for (i = 0;i < list.numstrings;i++)
1092         {
1093                 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1094                 {
1095                         FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1096                 }
1097         }
1098
1099         stringlistfreecontents(&list);
1100
1101         // Add the directory to the search path
1102         // (unpacked files have the priority over packed files)
1103         search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1104         strlcpy (search->filename, dir, sizeof (search->filename));
1105         search->next = fs_searchpaths;
1106         fs_searchpaths = search;
1107 }
1108
1109
1110 /*
1111 ================
1112 FS_AddGameHierarchy
1113 ================
1114 */
1115 void FS_AddGameHierarchy (const char *dir)
1116 {
1117         // Add the common game directory
1118         FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1119
1120         if (*fs_userdir)
1121                 FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
1122 }
1123
1124
1125 /*
1126 ============
1127 FS_FileExtension
1128 ============
1129 */
1130 const char *FS_FileExtension (const char *in)
1131 {
1132         const char *separator, *backslash, *colon, *dot;
1133
1134         separator = strrchr(in, '/');
1135         backslash = strrchr(in, '\\');
1136         if (!separator || separator < backslash)
1137                 separator = backslash;
1138         colon = strrchr(in, ':');
1139         if (!separator || separator < colon)
1140                 separator = colon;
1141
1142         dot = strrchr(in, '.');
1143         if (dot == NULL || (separator && (dot < separator)))
1144                 return "";
1145
1146         return dot + 1;
1147 }
1148
1149
1150 /*
1151 ============
1152 FS_FileWithoutPath
1153 ============
1154 */
1155 const char *FS_FileWithoutPath (const char *in)
1156 {
1157         const char *separator, *backslash, *colon;
1158
1159         separator = strrchr(in, '/');
1160         backslash = strrchr(in, '\\');
1161         if (!separator || separator < backslash)
1162                 separator = backslash;
1163         colon = strrchr(in, ':');
1164         if (!separator || separator < colon)
1165                 separator = colon;
1166         return separator ? separator + 1 : in;
1167 }
1168
1169
1170 /*
1171 ================
1172 FS_ClearSearchPath
1173 ================
1174 */
1175 void FS_ClearSearchPath (void)
1176 {
1177         // unload all packs and directory information, close all pack files
1178         // (if a qfile is still reading a pack it won't be harmed because it used
1179         //  dup() to get its own handle already)
1180         while (fs_searchpaths)
1181         {
1182                 searchpath_t *search = fs_searchpaths;
1183                 fs_searchpaths = search->next;
1184                 if (search->pack)
1185                 {
1186                         // close the file
1187                         close(search->pack->handle);
1188                         // free any memory associated with it
1189                         if (search->pack->files)
1190                                 Mem_Free(search->pack->files);
1191                         Mem_Free(search->pack);
1192                 }
1193                 Mem_Free(search);
1194         }
1195 }
1196
1197
1198 /*
1199 ================
1200 FS_Rescan
1201 ================
1202 */
1203 void FS_Rescan (void)
1204 {
1205         int i;
1206         qboolean fs_modified = false;
1207
1208         FS_ClearSearchPath();
1209
1210         // add the game-specific paths
1211         // gamedirname1 (typically id1)
1212         FS_AddGameHierarchy (gamedirname1);
1213         // update the com_modname (used for server info)
1214         strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1215
1216         // add the game-specific path, if any
1217         // (only used for mission packs and the like, which should set fs_modified)
1218         if (gamedirname2)
1219         {
1220                 fs_modified = true;
1221                 FS_AddGameHierarchy (gamedirname2);
1222         }
1223
1224         // -game <gamedir>
1225         // Adds basedir/gamedir as an override game
1226         // LordHavoc: now supports multiple -game directories
1227         // set the com_modname (reported in server info)
1228         for (i = 0;i < fs_numgamedirs;i++)
1229         {
1230                 fs_modified = true;
1231                 FS_AddGameHierarchy (fs_gamedirs[i]);
1232                 // update the com_modname (used server info)
1233                 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1234         }
1235
1236         // set the default screenshot name to either the mod name or the
1237         // gamemode screenshot name
1238         if (strcmp(com_modname, gamedirname1))
1239                 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1240         else
1241                 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1242
1243         // If "-condebug" is in the command line, remove the previous log file
1244         if (COM_CheckParm ("-condebug") != 0)
1245                 unlink (va("%s/qconsole.log", fs_gamedir));
1246
1247         // look for the pop.lmp file and set registered to true if it is found
1248         if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1249         {
1250                 if (fs_modified)
1251                         Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1252                 else
1253                         Con_Print("Playing shareware version.\n");
1254         }
1255         else
1256         {
1257                 Cvar_Set ("registered", "1");
1258                 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1259                         Con_Print("Playing registered version.\n");
1260         }
1261
1262         // unload all wads so that future queries will return the new data
1263         W_UnloadAll();
1264 }
1265
1266 void FS_Rescan_f(void)
1267 {
1268         FS_Rescan();
1269 }
1270
1271 /*
1272 ================
1273 FS_ChangeGameDirs
1274 ================
1275 */
1276 extern void Host_SaveConfig (void);
1277 extern void Host_LoadConfig_f (void);
1278 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1279 {
1280         int i;
1281         const char *p;
1282
1283         if (fs_numgamedirs == numgamedirs)
1284         {
1285                 for (i = 0;i < numgamedirs;i++)
1286                         if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1287                                 break;
1288                 if (i == numgamedirs)
1289                         return true; // already using this set of gamedirs, do nothing
1290         }
1291
1292         if (numgamedirs > MAX_GAMEDIRS)
1293         {
1294                 if (complain)
1295                         Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1296                 return false; // too many gamedirs
1297         }
1298
1299         for (i = 0;i < numgamedirs;i++)
1300         {
1301                 // if string is nasty, reject it
1302                 p = FS_CheckGameDir(gamedirs[i]);
1303                 if(!p)
1304                 {
1305                         if (complain)
1306                                 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1307                         return false; // nasty gamedirs
1308                 }
1309                 if(p == fs_checkgamedir_missing && failmissing)
1310                 {
1311                         if (complain)
1312                                 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1313                         return false; // missing gamedirs
1314                 }
1315         }
1316
1317         Host_SaveConfig();
1318
1319         fs_numgamedirs = numgamedirs;
1320         for (i = 0;i < fs_numgamedirs;i++)
1321                 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1322
1323         // reinitialize filesystem to detect the new paks
1324         FS_Rescan();
1325
1326         // exec the new config
1327         Host_LoadConfig_f();
1328
1329         // unload all sounds so they will be reloaded from the new files as needed
1330         S_UnloadAllSounds_f();
1331
1332         // reinitialize renderer (this reloads hud/console background/etc)
1333         R_Modules_Restart();
1334
1335         return true;
1336 }
1337
1338 /*
1339 ================
1340 FS_GameDir_f
1341 ================
1342 */
1343 void FS_GameDir_f (void)
1344 {
1345         int i;
1346         int numgamedirs;
1347         char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1348
1349         if (Cmd_Argc() < 2)
1350         {
1351                 Con_Printf("gamedirs active:");
1352                 for (i = 0;i < fs_numgamedirs;i++)
1353                         Con_Printf(" %s", fs_gamedirs[i]);
1354                 Con_Printf("\n");
1355                 return;
1356         }
1357
1358         numgamedirs = Cmd_Argc() - 1;
1359         if (numgamedirs > MAX_GAMEDIRS)
1360         {
1361                 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1362                 return;
1363         }
1364
1365         for (i = 0;i < numgamedirs;i++)
1366                 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1367
1368         if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1369         {
1370                 // actually, changing during game would work fine, but would be stupid
1371                 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1372                 return;
1373         }
1374
1375         // halt demo playback to close the file
1376         CL_Disconnect();
1377
1378         FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1379 }
1380
1381 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking);
1382 static const char *FS_SysCheckGameDir(const char *gamedir)
1383 {
1384         static char buf[8192];
1385         qboolean success;
1386         qfile_t *f;
1387         stringlist_t list;
1388         fs_offset_t n;
1389
1390         stringlistinit(&list);
1391         listdirectory(&list, gamedir, "");
1392         success = list.numstrings > 0;
1393         stringlistfreecontents(&list);
1394
1395         if(success)
1396         {
1397                 f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
1398                 if(f)
1399                 {
1400                         n = FS_Read (f, buf, sizeof(buf) - 1);
1401                         if(n >= 0)
1402                                 buf[n] = 0;
1403                         else
1404                                 *buf = 0;
1405                         FS_Close(f);
1406                 }
1407                 else
1408                         *buf = 0;
1409                 return buf;
1410         }
1411
1412         return NULL;
1413 }
1414
1415 /*
1416 ================
1417 FS_CheckGameDir
1418 ================
1419 */
1420 const char *FS_CheckGameDir(const char *gamedir)
1421 {
1422         const char *ret;
1423
1424         if (FS_CheckNastyPath(gamedir, true))
1425                 return NULL;
1426
1427         ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
1428         if(ret)
1429         {
1430                 if(!*ret)
1431                 {
1432                         // get description from basedir
1433                         ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1434                         if(ret)
1435                                 return ret;
1436                         return "";
1437                 }
1438                 return ret;
1439         }
1440
1441         ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1442         if(ret)
1443                 return ret;
1444         
1445         return fs_checkgamedir_missing;
1446 }
1447
1448 static void FS_ListGameDirs()
1449 {
1450         stringlist_t list, list2;
1451         int i, j;
1452         const char *info;
1453
1454         fs_all_gamedirs_count = 0;
1455         if(fs_all_gamedirs)
1456                 Mem_Free(fs_all_gamedirs);
1457
1458         stringlistinit(&list);
1459         listdirectory(&list, va("%s/", fs_basedir), "");
1460         listdirectory(&list, va("%s/", fs_userdir), "");
1461         stringlistsort(&list);
1462
1463         stringlistinit(&list2);
1464         for(i = 0; i < list.numstrings; ++i)
1465         {
1466                 if(i)
1467                         if(!strcmp(list.strings[i-1], list.strings[i]))
1468                                 continue;
1469                 info = FS_CheckGameDir(list.strings[i]);
1470                 if(!info)
1471                         continue;
1472                 if(info == fs_checkgamedir_missing)
1473                         continue;
1474                 if(!*info)
1475                         continue;
1476                 stringlistappend(&list2, list.strings[i]); 
1477         }
1478         stringlistfreecontents(&list);
1479
1480         fs_all_gamedirs = Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
1481         for(i = 0; i < list2.numstrings; ++i)
1482         {
1483                 info = FS_CheckGameDir(list2.strings[i]);
1484                 // all this cannot happen any more, but better be safe than sorry
1485                 if(!info)
1486                         continue;
1487                 if(info == fs_checkgamedir_missing)
1488                         continue;
1489                 if(!*info)
1490                         continue;
1491                 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
1492                 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
1493                 ++fs_all_gamedirs_count;
1494         }
1495 }
1496
1497 /*
1498 ================
1499 FS_Init
1500 ================
1501 */
1502 void FS_Init (void)
1503 {
1504         const char *p;
1505         int i;
1506 #ifdef WIN32
1507         TCHAR mydocsdir[MAX_PATH + 1];
1508 #if _MSC_VER >= 1400
1509         size_t homedirlen;
1510 #endif
1511 #endif
1512         char *homedir;
1513
1514 #ifdef WIN32
1515         const char* dllnames [] =
1516         {
1517                 "shfolder.dll",  // IE 4, or Win NT and higher
1518                 NULL
1519         };
1520         Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1521         // don't care for the result; if it fails, %USERPROFILE% will be used instead
1522 #endif
1523
1524         fs_mempool = Mem_AllocPool("file management", 0, NULL);
1525
1526         // Add the personal game directory
1527         if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1528         {
1529                 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
1530         }
1531         else if(COM_CheckParm("-nohome"))
1532         {
1533                 *fs_userdir = 0;
1534         }
1535         else
1536         {
1537 #ifdef WIN32
1538                 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1539                 {
1540                         dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1541                         Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", fs_userdir);
1542                 }
1543                 else
1544                 {
1545                         // use the environment
1546 #if _MSC_VER >= 1400
1547                         _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1548 #else
1549                         homedir = getenv("USERPROFILE");
1550 #endif
1551
1552                         if(homedir)
1553                         {
1554                                 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1555 #if _MSC_VER >= 1400
1556                                 free(homedir);
1557 #endif
1558                                 Con_DPrintf("Obtained personal directory %s from environment\n", fs_userdir);
1559                         }
1560                 }
1561
1562                 if(!*fs_userdir)
1563                         Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1564 #else
1565                 homedir = getenv ("HOME");
1566                 if(homedir)
1567                         dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/.%s/", homedir, gameuserdirname);
1568
1569                 if(!*fs_userdir)
1570                         Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1571 #endif
1572
1573 #ifdef WIN32
1574                 if(!COM_CheckParm("-mygames"))
1575                 {
1576 #if _MSC_VER >= 1400
1577                         int fd;
1578                         _sopen_s(&fd, va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
1579 #else
1580                         int fd = open (va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1581 #endif
1582                         if(fd >= 0)
1583                         {
1584                                 close(fd);
1585                                 *fs_userdir = 0; // we have write access to the game dir, so let's use it
1586                         }
1587                 }
1588 #endif
1589         }
1590
1591         strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1592
1593 // If the base directory is explicitly defined by the compilation process
1594 #ifdef DP_FS_BASEDIR
1595         strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1596 #else
1597         *fs_basedir = 0;
1598
1599 #ifdef MACOSX
1600         // FIXME: is there a better way to find the directory outside the .app?
1601         if (strstr(com_argv[0], ".app/"))
1602         {
1603                 char *split;
1604
1605                 split = strstr(com_argv[0], ".app/");
1606                 while (split > com_argv[0] && *split != '/')
1607                         split--;
1608                 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1609                 fs_basedir[split - com_argv[0]] = 0;
1610         }
1611 #endif
1612 #endif
1613
1614         PK3_OpenLibrary ();
1615
1616         // -basedir <path>
1617         // Overrides the system supplied base directory (under GAMENAME)
1618 // 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)
1619         i = COM_CheckParm ("-basedir");
1620         if (i && i < com_argc-1)
1621         {
1622                 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1623                 i = (int)strlen (fs_basedir);
1624                 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1625                         fs_basedir[i-1] = 0;
1626         }
1627
1628         // add a path separator to the end of the basedir if it lacks one
1629         if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1630                 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1631
1632         FS_ListGameDirs();
1633
1634         p = FS_CheckGameDir(gamedirname1);
1635         if(!p || p == fs_checkgamedir_missing)
1636                 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1637
1638         if(gamedirname2)
1639         {
1640                 p = FS_CheckGameDir(gamedirname2);
1641                 if(!p || p == fs_checkgamedir_missing)
1642                         Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1643         }
1644
1645         // -game <gamedir>
1646         // Adds basedir/gamedir as an override game
1647         // LordHavoc: now supports multiple -game directories
1648         for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1649         {
1650                 if (!com_argv[i])
1651                         continue;
1652                 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1653                 {
1654                         i++;
1655                         p = FS_CheckGameDir(com_argv[i]);
1656                         if(!p)
1657                                 Sys_Error("Nasty -game name rejected: %s", com_argv[i]);
1658                         if(p == fs_checkgamedir_missing)
1659                                 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1660                         // add the gamedir to the list of active gamedirs
1661                         strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1662                         fs_numgamedirs++;
1663                 }
1664         }
1665
1666         // generate the searchpath
1667         FS_Rescan();
1668 }
1669
1670 void FS_Init_Commands(void)
1671 {
1672         Cvar_RegisterVariable (&scr_screenshot_name);
1673         Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1674
1675         Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1676         Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1677         Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1678         Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1679         Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1680         Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1681 }
1682
1683 /*
1684 ================
1685 FS_Shutdown
1686 ================
1687 */
1688 void FS_Shutdown (void)
1689 {
1690         // close all pack files and such
1691         // (hopefully there aren't any other open files, but they'll be cleaned up
1692         //  by the OS anyway)
1693         FS_ClearSearchPath();
1694         Mem_FreePool (&fs_mempool);
1695
1696 #ifdef WIN32
1697         Sys_UnloadLibrary (&shfolder_dll);
1698 #endif
1699 }
1700
1701 /*
1702 ====================
1703 FS_SysOpen
1704
1705 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1706 ====================
1707 */
1708 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1709 {
1710         qfile_t* file;
1711         int mod, opt;
1712         unsigned int ind;
1713
1714         // Parse the mode string
1715         switch (mode[0])
1716         {
1717                 case 'r':
1718                         mod = O_RDONLY;
1719                         opt = 0;
1720                         break;
1721                 case 'w':
1722                         mod = O_WRONLY;
1723                         opt = O_CREAT | O_TRUNC;
1724                         break;
1725                 case 'a':
1726                         mod = O_WRONLY;
1727                         opt = O_CREAT | O_APPEND;
1728                         break;
1729                 default:
1730                         Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1731                         return NULL;
1732         }
1733         for (ind = 1; mode[ind] != '\0'; ind++)
1734         {
1735                 switch (mode[ind])
1736                 {
1737                         case '+':
1738                                 mod = O_RDWR;
1739                                 break;
1740                         case 'b':
1741                                 opt |= O_BINARY;
1742                                 break;
1743                         default:
1744                                 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1745                                                         filepath, mode, mode[ind]);
1746                 }
1747         }
1748
1749         if (nonblocking)
1750                 opt |= O_NONBLOCK;
1751
1752         file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1753         memset (file, 0, sizeof (*file));
1754         file->ungetc = EOF;
1755
1756 #if _MSC_VER >= 1400
1757         _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1758 #else
1759         file->handle = open (filepath, mod | opt, 0666);
1760 #endif
1761         if (file->handle < 0)
1762         {
1763                 Mem_Free (file);
1764                 return NULL;
1765         }
1766
1767         file->real_length = lseek (file->handle, 0, SEEK_END);
1768
1769         // For files opened in append mode, we start at the end of the file
1770         if (mod & O_APPEND)
1771                 file->position = file->real_length;
1772         else
1773                 lseek (file->handle, 0, SEEK_SET);
1774
1775         return file;
1776 }
1777
1778
1779 /*
1780 ===========
1781 FS_OpenPackedFile
1782
1783 Open a packed file using its package file descriptor
1784 ===========
1785 */
1786 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1787 {
1788         packfile_t *pfile;
1789         int dup_handle;
1790         qfile_t* file;
1791
1792         pfile = &pack->files[pack_ind];
1793
1794         // If we don't have the true offset, get it now
1795         if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1796                 if (!PK3_GetTrueFileOffset (pfile, pack))
1797                         return NULL;
1798
1799         // No Zlib DLL = no compressed files
1800         if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1801         {
1802                 Con_Printf("WARNING: can't open the compressed file %s\n"
1803                                         "You need the Zlib DLL to use compressed files\n",
1804                                         pfile->name);
1805                 return NULL;
1806         }
1807
1808         // LordHavoc: lseek affects all duplicates of a handle so we do it before
1809         // the dup() call to avoid having to close the dup_handle on error here
1810         if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1811         {
1812                 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1813                                         pfile->name, pack->filename, (int) pfile->offset);
1814                 return NULL;
1815         }
1816
1817         dup_handle = dup (pack->handle);
1818         if (dup_handle < 0)
1819         {
1820                 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1821                 return NULL;
1822         }
1823
1824         file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1825         memset (file, 0, sizeof (*file));
1826         file->handle = dup_handle;
1827         file->flags = QFILE_FLAG_PACKED;
1828         file->real_length = pfile->realsize;
1829         file->offset = pfile->offset;
1830         file->position = 0;
1831         file->ungetc = EOF;
1832
1833         if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1834         {
1835                 ztoolkit_t *ztk;
1836
1837                 file->flags |= QFILE_FLAG_DEFLATED;
1838
1839                 // We need some more variables
1840                 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1841
1842                 ztk->comp_length = pfile->packsize;
1843
1844                 // Initialize zlib stream
1845                 ztk->zstream.next_in = ztk->input;
1846                 ztk->zstream.avail_in = 0;
1847
1848                 /* From Zlib's "unzip.c":
1849                  *
1850                  * windowBits is passed < 0 to tell that there is no zlib header.
1851                  * Note that in this case inflate *requires* an extra "dummy" byte
1852                  * after the compressed stream in order to complete decompression and
1853                  * return Z_STREAM_END.
1854                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1855                  * size of both compressed and uncompressed data
1856                  */
1857                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1858                 {
1859                         Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1860                         close(dup_handle);
1861                         Mem_Free(file);
1862                         return NULL;
1863                 }
1864
1865                 ztk->zstream.next_out = file->buff;
1866                 ztk->zstream.avail_out = sizeof (file->buff);
1867
1868                 file->ztk = ztk;
1869         }
1870
1871         return file;
1872 }
1873
1874 /*
1875 ====================
1876 FS_CheckNastyPath
1877
1878 Return true if the path should be rejected due to one of the following:
1879 1: path elements that are non-portable
1880 2: path elements that would allow access to files outside the game directory,
1881    or are just not a good idea for a mod to be using.
1882 ====================
1883 */
1884 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1885 {
1886         // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1887         if (!path[0])
1888                 return 2;
1889
1890         // Windows: don't allow \ in filenames (windows-only), period.
1891         // (on Windows \ is a directory separator, but / is also supported)
1892         if (strstr(path, "\\"))
1893                 return 1; // non-portable
1894
1895         // Mac: don't allow Mac-only filenames - : is a directory separator
1896         // instead of /, but we rely on / working already, so there's no reason to
1897         // support a Mac-only path
1898         // Amiga and Windows: : tries to go to root of drive
1899         if (strstr(path, ":"))
1900                 return 1; // non-portable attempt to go to root of drive
1901
1902         // Amiga: // is parent directory
1903         if (strstr(path, "//"))
1904                 return 1; // non-portable attempt to go to parent directory
1905
1906         // all: don't allow going to parent directory (../ or /../)
1907         if (strstr(path, ".."))
1908                 return 2; // attempt to go outside the game directory
1909
1910         // Windows and UNIXes: don't allow absolute paths
1911         if (path[0] == '/')
1912                 return 2; // attempt to go outside the game directory
1913
1914         // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements), this catches all imaginable cases of ./, ../, .../, etc
1915         if (strchr(path, '.'))
1916         {
1917                 if (isgamedir)
1918                 {
1919                         // gamedir is entirely path elements, so simply forbid . entirely
1920                         return 2;
1921                 }
1922                 if (strchr(path, '.') < strrchr(path, '/'))
1923                         return 2; // possible attempt to go outside the game directory
1924         }
1925
1926         // all: forbid trailing slash on gamedir
1927         if (isgamedir && path[strlen(path)-1] == '/')
1928                 return 2;
1929
1930         // all: forbid leading dot on any filename for any reason
1931         if (strstr(path, "/."))
1932                 return 2; // attempt to go outside the game directory
1933
1934         // after all these checks we're pretty sure it's a / separated filename
1935         // and won't do much if any harm
1936         return false;
1937 }
1938
1939
1940 /*
1941 ====================
1942 FS_FindFile
1943
1944 Look for a file in the packages and in the filesystem
1945
1946 Return the searchpath where the file was found (or NULL)
1947 and the file index in the package if relevant
1948 ====================
1949 */
1950 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1951 {
1952         searchpath_t *search;
1953         pack_t *pak;
1954
1955         // search through the path, one element at a time
1956         for (search = fs_searchpaths;search;search = search->next)
1957         {
1958                 // is the element a pak file?
1959                 if (search->pack)
1960                 {
1961                         int (*strcmp_funct) (const char* str1, const char* str2);
1962                         int left, right, middle;
1963
1964                         pak = search->pack;
1965                         strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1966
1967                         // Look for the file (binary search)
1968                         left = 0;
1969                         right = pak->numfiles - 1;
1970                         while (left <= right)
1971                         {
1972                                 int diff;
1973
1974                                 middle = (left + right) / 2;
1975                                 diff = strcmp_funct (pak->files[middle].name, name);
1976
1977                                 // Found it
1978                                 if (!diff)
1979                                 {
1980                                         if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1981                                         {
1982                                                 // yes, but the first one is empty so we treat it as not being there
1983                                                 if (!quiet && developer.integer >= 10)
1984                                                         Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1985
1986                                                 if (index != NULL)
1987                                                         *index = -1;
1988                                                 return NULL;
1989                                         }
1990
1991                                         if (!quiet && developer.integer >= 10)
1992                                                 Con_Printf("FS_FindFile: %s in %s\n",
1993                                                                         pak->files[middle].name, pak->filename);
1994
1995                                         if (index != NULL)
1996                                                 *index = middle;
1997                                         return search;
1998                                 }
1999
2000                                 // If we're too far in the list
2001                                 if (diff > 0)
2002                                         right = middle - 1;
2003                                 else
2004                                         left = middle + 1;
2005                         }
2006                 }
2007                 else
2008                 {
2009                         char netpath[MAX_OSPATH];
2010                         dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
2011                         if (FS_SysFileExists (netpath))
2012                         {
2013                                 if (!quiet && developer.integer >= 10)
2014                                         Con_Printf("FS_FindFile: %s\n", netpath);
2015
2016                                 if (index != NULL)
2017                                         *index = -1;
2018                                 return search;
2019                         }
2020                 }
2021         }
2022
2023         if (!quiet && developer.integer >= 10)
2024                 Con_Printf("FS_FindFile: can't find %s\n", name);
2025
2026         if (index != NULL)
2027                 *index = -1;
2028         return NULL;
2029 }
2030
2031
2032 /*
2033 ===========
2034 FS_OpenReadFile
2035
2036 Look for a file in the search paths and open it in read-only mode
2037 ===========
2038 */
2039 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
2040 {
2041         searchpath_t *search;
2042         int pack_ind;
2043
2044         search = FS_FindFile (filename, &pack_ind, quiet);
2045
2046         // Not found?
2047         if (search == NULL)
2048                 return NULL;
2049
2050         // Found in the filesystem?
2051         if (pack_ind < 0)
2052         {
2053                 char path [MAX_OSPATH];
2054                 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
2055                 return FS_SysOpen (path, "rb", nonblocking);
2056         }
2057
2058         // So, we found it in a package...
2059
2060         // Is it a PK3 symlink?
2061         // TODO also handle directory symlinks by parsing the whole structure...
2062         // but heck, file symlinks are good enough for now
2063         if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
2064         {
2065                 if(symlinkLevels <= 0)
2066                 {
2067                         Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
2068                         return NULL;
2069                 }
2070                 else
2071                 {
2072                         char linkbuf[MAX_QPATH];
2073                         fs_offset_t count;
2074                         qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
2075                         const char *mergeslash;
2076                         char *mergestart;
2077
2078                         if(!linkfile)
2079                                 return NULL;
2080                         count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
2081                         FS_Close(linkfile);
2082                         if(count < 0)
2083                                 return NULL;
2084                         linkbuf[count] = 0;
2085                         
2086                         // Now combine the paths...
2087                         mergeslash = strrchr(filename, '/');
2088                         mergestart = linkbuf;
2089                         if(!mergeslash)
2090                                 mergeslash = filename;
2091                         while(!strncmp(mergestart, "../", 3))
2092                         {
2093                                 mergestart += 3;
2094                                 while(mergeslash > filename)
2095                                 {
2096                                         --mergeslash;
2097                                         if(*mergeslash == '/')
2098                                                 break;
2099                                 }
2100                         }
2101                         // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
2102                         if(mergeslash == filename)
2103                         {
2104                                 // Either mergeslash == filename, then we just replace the name (done below)
2105                         }
2106                         else
2107                         {
2108                                 // Or, we append the name after mergeslash;
2109                                 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2110                                 int spaceNeeded = mergeslash - filename + 1;
2111                                 int spaceRemoved = mergestart - linkbuf;
2112                                 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2113                                 {
2114                                         Con_DPrintf("symlink: too long path rejected\n");
2115                                         return NULL;
2116                                 }
2117                                 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2118                                 memcpy(linkbuf, filename, spaceNeeded);
2119                                 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2120                                 mergestart = linkbuf;
2121                         }
2122                         if (!quiet && developer_loading.integer)
2123                                 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2124                         if(FS_CheckNastyPath (mergestart, false))
2125                         {
2126                                 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2127                                 return NULL;
2128                         }
2129                         return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2130                 }
2131         }
2132
2133         return FS_OpenPackedFile (search->pack, pack_ind);
2134 }
2135
2136
2137 /*
2138 =============================================================================
2139
2140 MAIN PUBLIC FUNCTIONS
2141
2142 =============================================================================
2143 */
2144
2145 /*
2146 ====================
2147 FS_OpenRealFile
2148
2149 Open a file in the userpath. The syntax is the same as fopen
2150 Used for savegame scanning in menu, and all file writing.
2151 ====================
2152 */
2153 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2154 {
2155         char real_path [MAX_OSPATH];
2156
2157         if (FS_CheckNastyPath(filepath, false))
2158         {
2159                 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2160                 return NULL;
2161         }
2162
2163         dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2164
2165         // If the file is opened in "write", "append", or "read/write" mode,
2166         // create directories up to the file.
2167         if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2168                 FS_CreatePath (real_path);
2169         return FS_SysOpen (real_path, mode, false);
2170 }
2171
2172
2173 /*
2174 ====================
2175 FS_OpenVirtualFile
2176
2177 Open a file. The syntax is the same as fopen
2178 ====================
2179 */
2180 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2181 {
2182         if (FS_CheckNastyPath(filepath, false))
2183         {
2184                 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2185                 return NULL;
2186         }
2187
2188         return FS_OpenReadFile (filepath, quiet, false, 16);
2189 }
2190
2191
2192 /*
2193 ====================
2194 FS_FileFromData
2195
2196 Open a file. The syntax is the same as fopen
2197 ====================
2198 */
2199 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2200 {
2201         qfile_t* file;
2202         file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2203         memset (file, 0, sizeof (*file));
2204         file->flags = QFILE_FLAG_DATA;
2205         file->ungetc = EOF;
2206         file->real_length = size;
2207         file->data = data;
2208         return file;
2209 }
2210
2211 /*
2212 ====================
2213 FS_Close
2214
2215 Close a file
2216 ====================
2217 */
2218 int FS_Close (qfile_t* file)
2219 {
2220         if(file->flags & QFILE_FLAG_DATA)
2221         {
2222                 Mem_Free(file);
2223                 return 0;
2224         }
2225
2226         if (close (file->handle))
2227                 return EOF;
2228
2229         if (file->ztk)
2230         {
2231                 qz_inflateEnd (&file->ztk->zstream);
2232                 Mem_Free (file->ztk);
2233         }
2234
2235         Mem_Free (file);
2236         return 0;
2237 }
2238
2239
2240 /*
2241 ====================
2242 FS_Write
2243
2244 Write "datasize" bytes into a file
2245 ====================
2246 */
2247 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2248 {
2249         fs_offset_t result;
2250
2251         // If necessary, seek to the exact file position we're supposed to be
2252         if (file->buff_ind != file->buff_len)
2253                 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2254
2255         // Purge cached data
2256         FS_Purge (file);
2257
2258         // Write the buffer and update the position
2259         result = write (file->handle, data, (fs_offset_t)datasize);
2260         file->position = lseek (file->handle, 0, SEEK_CUR);
2261         if (file->real_length < file->position)
2262                 file->real_length = file->position;
2263
2264         if (result < 0)
2265                 return 0;
2266
2267         return result;
2268 }
2269
2270
2271 /*
2272 ====================
2273 FS_Read
2274
2275 Read up to "buffersize" bytes from a file
2276 ====================
2277 */
2278 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2279 {
2280         fs_offset_t count, done;
2281
2282         if (buffersize == 0)
2283                 return 0;
2284
2285         // Get rid of the ungetc character
2286         if (file->ungetc != EOF)
2287         {
2288                 ((char*)buffer)[0] = file->ungetc;
2289                 buffersize--;
2290                 file->ungetc = EOF;
2291                 done = 1;
2292         }
2293         else
2294                 done = 0;
2295
2296         if(file->flags & QFILE_FLAG_DATA)
2297         {
2298                 size_t left = file->real_length - file->position;
2299                 if(buffersize > left)
2300                         buffersize = left;
2301                 memcpy(buffer, file->data + file->position, buffersize);
2302                 file->position += buffersize;
2303                 return buffersize;
2304         }
2305
2306         // First, we copy as many bytes as we can from "buff"
2307         if (file->buff_ind < file->buff_len)
2308         {
2309                 count = file->buff_len - file->buff_ind;
2310                 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2311                 done += count;
2312                 memcpy (buffer, &file->buff[file->buff_ind], count);
2313                 file->buff_ind += count;
2314
2315                 buffersize -= count;
2316                 if (buffersize == 0)
2317                         return done;
2318         }
2319
2320         // NOTE: at this point, the read buffer is always empty
2321
2322         // If the file isn't compressed
2323         if (! (file->flags & QFILE_FLAG_DEFLATED))
2324         {
2325                 fs_offset_t nb;
2326
2327                 // We must take care to not read after the end of the file
2328                 count = file->real_length - file->position;
2329
2330                 // If we have a lot of data to get, put them directly into "buffer"
2331                 if (buffersize > sizeof (file->buff) / 2)
2332                 {
2333                         if (count > (fs_offset_t)buffersize)
2334                                 count = (fs_offset_t)buffersize;
2335                         lseek (file->handle, file->offset + file->position, SEEK_SET);
2336                         nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2337                         if (nb > 0)
2338                         {
2339                                 done += nb;
2340                                 file->position += nb;
2341
2342                                 // Purge cached data
2343                                 FS_Purge (file);
2344                         }
2345                 }
2346                 else
2347                 {
2348                         if (count > (fs_offset_t)sizeof (file->buff))
2349                                 count = (fs_offset_t)sizeof (file->buff);
2350                         lseek (file->handle, file->offset + file->position, SEEK_SET);
2351                         nb = read (file->handle, file->buff, count);
2352                         if (nb > 0)
2353                         {
2354                                 file->buff_len = nb;
2355                                 file->position += nb;
2356
2357                                 // Copy the requested data in "buffer" (as much as we can)
2358                                 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2359                                 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2360                                 file->buff_ind = count;
2361                                 done += count;
2362                         }
2363                 }
2364
2365                 return done;
2366         }
2367
2368         // If the file is compressed, it's more complicated...
2369         // We cycle through a few operations until we have read enough data
2370         while (buffersize > 0)
2371         {
2372                 ztoolkit_t *ztk = file->ztk;
2373                 int error;
2374
2375                 // NOTE: at this point, the read buffer is always empty
2376
2377                 // If "input" is also empty, we need to refill it
2378                 if (ztk->in_ind == ztk->in_len)
2379                 {
2380                         // If we are at the end of the file
2381                         if (file->position == file->real_length)
2382                                 return done;
2383
2384                         count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2385                         if (count > (fs_offset_t)sizeof (ztk->input))
2386                                 count = (fs_offset_t)sizeof (ztk->input);
2387                         lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2388                         if (read (file->handle, ztk->input, count) != count)
2389                         {
2390                                 Con_Printf ("FS_Read: unexpected end of file\n");
2391                                 break;
2392                         }
2393
2394                         ztk->in_ind = 0;
2395                         ztk->in_len = count;
2396                         ztk->in_position += count;
2397                 }
2398
2399                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2400                 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2401
2402                 // Now that we are sure we have compressed data available, we need to determine
2403                 // if it's better to inflate it in "file->buff" or directly in "buffer"
2404
2405                 // Inflate the data in "file->buff"
2406                 if (buffersize < sizeof (file->buff) / 2)
2407                 {
2408                         ztk->zstream.next_out = file->buff;
2409                         ztk->zstream.avail_out = sizeof (file->buff);
2410                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2411                         if (error != Z_OK && error != Z_STREAM_END)
2412                         {
2413                                 Con_Printf ("FS_Read: Can't inflate file\n");
2414                                 break;
2415                         }
2416                         ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2417
2418                         file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2419                         file->position += file->buff_len;
2420
2421                         // Copy the requested data in "buffer" (as much as we can)
2422                         count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2423                         memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2424                         file->buff_ind = count;
2425                 }
2426
2427                 // Else, we inflate directly in "buffer"
2428                 else
2429                 {
2430                         ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2431                         ztk->zstream.avail_out = (unsigned int)buffersize;
2432                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2433                         if (error != Z_OK && error != Z_STREAM_END)
2434                         {
2435                                 Con_Printf ("FS_Read: Can't inflate file\n");
2436                                 break;
2437                         }
2438                         ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2439
2440                         // How much data did it inflate?
2441                         count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2442                         file->position += count;
2443
2444                         // Purge cached data
2445                         FS_Purge (file);
2446                 }
2447
2448                 done += count;
2449                 buffersize -= count;
2450         }
2451
2452         return done;
2453 }
2454
2455
2456 /*
2457 ====================
2458 FS_Print
2459
2460 Print a string into a file
2461 ====================
2462 */
2463 int FS_Print (qfile_t* file, const char *msg)
2464 {
2465         return (int)FS_Write (file, msg, strlen (msg));
2466 }
2467
2468 /*
2469 ====================
2470 FS_Printf
2471
2472 Print a string into a file
2473 ====================
2474 */
2475 int FS_Printf(qfile_t* file, const char* format, ...)
2476 {
2477         int result;
2478         va_list args;
2479
2480         va_start (args, format);
2481         result = FS_VPrintf (file, format, args);
2482         va_end (args);
2483
2484         return result;
2485 }
2486
2487
2488 /*
2489 ====================
2490 FS_VPrintf
2491
2492 Print a string into a file
2493 ====================
2494 */
2495 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2496 {
2497         int len;
2498         fs_offset_t buff_size = MAX_INPUTLINE;
2499         char *tempbuff;
2500
2501         for (;;)
2502         {
2503                 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2504                 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2505                 if (len >= 0 && len < buff_size)
2506                         break;
2507                 Mem_Free (tempbuff);
2508                 buff_size *= 2;
2509         }
2510
2511         len = write (file->handle, tempbuff, len);
2512         Mem_Free (tempbuff);
2513
2514         return len;
2515 }
2516
2517
2518 /*
2519 ====================
2520 FS_Getc
2521
2522 Get the next character of a file
2523 ====================
2524 */
2525 int FS_Getc (qfile_t* file)
2526 {
2527         unsigned char c;
2528
2529         if (FS_Read (file, &c, 1) != 1)
2530                 return EOF;
2531
2532         return c;
2533 }
2534
2535
2536 /*
2537 ====================
2538 FS_UnGetc
2539
2540 Put a character back into the read buffer (only supports one character!)
2541 ====================
2542 */
2543 int FS_UnGetc (qfile_t* file, unsigned char c)
2544 {
2545         // If there's already a character waiting to be read
2546         if (file->ungetc != EOF)
2547                 return EOF;
2548
2549         file->ungetc = c;
2550         return c;
2551 }
2552
2553
2554 /*
2555 ====================
2556 FS_Seek
2557
2558 Move the position index in a file
2559 ====================
2560 */
2561 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2562 {
2563         ztoolkit_t *ztk;
2564         unsigned char* buffer;
2565         fs_offset_t buffersize;
2566
2567         // Compute the file offset
2568         switch (whence)
2569         {
2570                 case SEEK_CUR:
2571                         offset += file->position - file->buff_len + file->buff_ind;
2572                         break;
2573
2574                 case SEEK_SET:
2575                         break;
2576
2577                 case SEEK_END:
2578                         offset += file->real_length;
2579                         break;
2580
2581                 default:
2582                         return -1;
2583         }
2584         if (offset < 0 || offset > file->real_length)
2585                 return -1;
2586
2587         if(file->flags & QFILE_FLAG_DATA)
2588         {
2589                 file->position = offset;
2590                 return 0;
2591         }
2592
2593         // If we have the data in our read buffer, we don't need to actually seek
2594         if (file->position - file->buff_len <= offset && offset <= file->position)
2595         {
2596                 file->buff_ind = offset + file->buff_len - file->position;
2597                 return 0;
2598         }
2599
2600         // Purge cached data
2601         FS_Purge (file);
2602
2603         // Unpacked or uncompressed files can seek directly
2604         if (! (file->flags & QFILE_FLAG_DEFLATED))
2605         {
2606                 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2607                         return -1;
2608                 file->position = offset;
2609                 return 0;
2610         }
2611
2612         // Seeking in compressed files is more a hack than anything else,
2613         // but we need to support it, so here we go.
2614         ztk = file->ztk;
2615
2616         // If we have to go back in the file, we need to restart from the beginning
2617         if (offset <= file->position)
2618         {
2619                 ztk->in_ind = 0;
2620                 ztk->in_len = 0;
2621                 ztk->in_position = 0;
2622                 file->position = 0;
2623                 lseek (file->handle, file->offset, SEEK_SET);
2624
2625                 // Reset the Zlib stream
2626                 ztk->zstream.next_in = ztk->input;
2627                 ztk->zstream.avail_in = 0;
2628                 qz_inflateReset (&ztk->zstream);
2629         }
2630
2631         // We need a big buffer to force inflating into it directly
2632         buffersize = 2 * sizeof (file->buff);
2633         buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2634
2635         // Skip all data until we reach the requested offset
2636         while (offset > file->position)
2637         {
2638                 fs_offset_t diff = offset - file->position;
2639                 fs_offset_t count, len;
2640
2641                 count = (diff > buffersize) ? buffersize : diff;
2642                 len = FS_Read (file, buffer, count);
2643                 if (len != count)
2644                 {
2645                         Mem_Free (buffer);
2646                         return -1;
2647                 }
2648         }
2649
2650         Mem_Free (buffer);
2651         return 0;
2652 }
2653
2654
2655 /*
2656 ====================
2657 FS_Tell
2658
2659 Give the current position in a file
2660 ====================
2661 */
2662 fs_offset_t FS_Tell (qfile_t* file)
2663 {
2664         return file->position - file->buff_len + file->buff_ind;
2665 }
2666
2667
2668 /*
2669 ====================
2670 FS_FileSize
2671
2672 Give the total size of a file
2673 ====================
2674 */
2675 fs_offset_t FS_FileSize (qfile_t* file)
2676 {
2677         return file->real_length;
2678 }
2679
2680
2681 /*
2682 ====================
2683 FS_Purge
2684
2685 Erases any buffered input or output data
2686 ====================
2687 */
2688 void FS_Purge (qfile_t* file)
2689 {
2690         file->buff_len = 0;
2691         file->buff_ind = 0;
2692         file->ungetc = EOF;
2693 }
2694
2695
2696 /*
2697 ============
2698 FS_LoadFile
2699
2700 Filename are relative to the quake directory.
2701 Always appends a 0 byte.
2702 ============
2703 */
2704 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2705 {
2706         qfile_t *file;
2707         unsigned char *buf = NULL;
2708         fs_offset_t filesize = 0;
2709
2710         file = FS_OpenVirtualFile(path, quiet);
2711         if (file)
2712         {
2713                 filesize = file->real_length;
2714                 if(filesize < 0)
2715                 {
2716                         Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2717                         FS_Close(file);
2718                         return NULL;
2719                 }
2720
2721                 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2722                 buf[filesize] = '\0';
2723                 FS_Read (file, buf, filesize);
2724                 FS_Close (file);
2725                 if (developer_loadfile.integer)
2726                         Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2727         }
2728
2729         if (filesizepointer)
2730                 *filesizepointer = filesize;
2731         return buf;
2732 }
2733
2734
2735 /*
2736 ============
2737 FS_WriteFile
2738
2739 The filename will be prefixed by the current game directory
2740 ============
2741 */
2742 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2743 {
2744         qfile_t *file;
2745
2746         file = FS_OpenRealFile(filename, "wb", false);
2747         if (!file)
2748         {
2749                 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2750                 return false;
2751         }
2752
2753         Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2754         FS_Write (file, data, len);
2755         FS_Close (file);
2756         return true;
2757 }
2758
2759
2760 /*
2761 =============================================================================
2762
2763 OTHERS PUBLIC FUNCTIONS
2764
2765 =============================================================================
2766 */
2767
2768 /*
2769 ============
2770 FS_StripExtension
2771 ============
2772 */
2773 void FS_StripExtension (const char *in, char *out, size_t size_out)
2774 {
2775         char *last = NULL;
2776         char currentchar;
2777
2778         if (size_out == 0)
2779                 return;
2780
2781         while ((currentchar = *in) && size_out > 1)
2782         {
2783                 if (currentchar == '.')
2784                         last = out;
2785                 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2786                         last = NULL;
2787                 *out++ = currentchar;
2788                 in++;
2789                 size_out--;
2790         }
2791         if (last)
2792                 *last = 0;
2793         else
2794                 *out = 0;
2795 }
2796
2797
2798 /*
2799 ==================
2800 FS_DefaultExtension
2801 ==================
2802 */
2803 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2804 {
2805         const char *src;
2806
2807         // if path doesn't have a .EXT, append extension
2808         // (extension should include the .)
2809         src = path + strlen(path) - 1;
2810
2811         while (*src != '/' && src != path)
2812         {
2813                 if (*src == '.')
2814                         return;                 // it has an extension
2815                 src--;
2816         }
2817
2818         strlcat (path, extension, size_path);
2819 }
2820
2821
2822 /*
2823 ==================
2824 FS_FileType
2825
2826 Look for a file in the packages and in the filesystem
2827 ==================
2828 */
2829 int FS_FileType (const char *filename)
2830 {
2831         searchpath_t *search;
2832         char fullpath[MAX_QPATH];
2833
2834         search = FS_FindFile (filename, NULL, true);
2835         if(!search)
2836                 return FS_FILETYPE_NONE;
2837
2838         if(search->pack)
2839                 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2840
2841         dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2842         return FS_SysFileType(fullpath);
2843 }
2844
2845
2846 /*
2847 ==================
2848 FS_FileExists
2849
2850 Look for a file in the packages and in the filesystem
2851 ==================
2852 */
2853 qboolean FS_FileExists (const char *filename)
2854 {
2855         return (FS_FindFile (filename, NULL, true) != NULL);
2856 }
2857
2858
2859 /*
2860 ==================
2861 FS_SysFileExists
2862
2863 Look for a file in the filesystem only
2864 ==================
2865 */
2866 int FS_SysFileType (const char *path)
2867 {
2868 #if WIN32
2869 // Sajt - some older sdks are missing this define
2870 # ifndef INVALID_FILE_ATTRIBUTES
2871 #  define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2872 # endif
2873
2874         DWORD result = GetFileAttributes(path);
2875
2876         if(result == INVALID_FILE_ATTRIBUTES)
2877                 return FS_FILETYPE_NONE;
2878
2879         if(result & FILE_ATTRIBUTE_DIRECTORY)
2880                 return FS_FILETYPE_DIRECTORY;
2881
2882         return FS_FILETYPE_FILE;
2883 #else
2884         struct stat buf;
2885
2886         if (stat (path,&buf) == -1)
2887                 return FS_FILETYPE_NONE;
2888
2889         if(S_ISDIR(buf.st_mode))
2890                 return FS_FILETYPE_DIRECTORY;
2891
2892         return FS_FILETYPE_FILE;
2893 #endif
2894 }
2895
2896 qboolean FS_SysFileExists (const char *path)
2897 {
2898         return FS_SysFileType (path) != FS_FILETYPE_NONE;
2899 }
2900
2901 void FS_mkdir (const char *path)
2902 {
2903 #if WIN32
2904         _mkdir (path);
2905 #else
2906         mkdir (path, 0777);
2907 #endif
2908 }
2909
2910 /*
2911 ===========
2912 FS_Search
2913
2914 Allocate and fill a search structure with information on matching filenames.
2915 ===========
2916 */
2917 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2918 {
2919         fssearch_t *search;
2920         searchpath_t *searchpath;
2921         pack_t *pak;
2922         int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2923         stringlist_t resultlist;
2924         stringlist_t dirlist;
2925         const char *slash, *backslash, *colon, *separator;
2926         char *basepath;
2927         char temp[MAX_OSPATH];
2928
2929         for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2930                 ;
2931
2932         if (i > 0)
2933         {
2934                 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2935                 return NULL;
2936         }
2937
2938         stringlistinit(&resultlist);
2939         stringlistinit(&dirlist);
2940         search = NULL;
2941         slash = strrchr(pattern, '/');
2942         backslash = strrchr(pattern, '\\');
2943         colon = strrchr(pattern, ':');
2944         separator = max(slash, backslash);
2945         separator = max(separator, colon);
2946         basepathlength = separator ? (separator + 1 - pattern) : 0;
2947         basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2948         if (basepathlength)
2949                 memcpy(basepath, pattern, basepathlength);
2950         basepath[basepathlength] = 0;
2951
2952         // search through the path, one element at a time
2953         for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2954         {
2955                 // is the element a pak file?
2956                 if (searchpath->pack)
2957                 {
2958                         // look through all the pak file elements
2959                         pak = searchpath->pack;
2960                         for (i = 0;i < pak->numfiles;i++)
2961                         {
2962                                 strlcpy(temp, pak->files[i].name, sizeof(temp));
2963                                 while (temp[0])
2964                                 {
2965                                         if (matchpattern(temp, (char *)pattern, true))
2966                                         {
2967                                                 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2968                                                         if (!strcmp(resultlist.strings[resultlistindex], temp))
2969                                                                 break;
2970                                                 if (resultlistindex == resultlist.numstrings)
2971                                                 {
2972                                                         stringlistappend(&resultlist, temp);
2973                                                         if (!quiet && developer_loading.integer)
2974                                                                 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2975                                                 }
2976                                         }
2977                                         // strip off one path element at a time until empty
2978                                         // this way directories are added to the listing if they match the pattern
2979                                         slash = strrchr(temp, '/');
2980                                         backslash = strrchr(temp, '\\');
2981                                         colon = strrchr(temp, ':');
2982                                         separator = temp;
2983                                         if (separator < slash)
2984                                                 separator = slash;
2985                                         if (separator < backslash)
2986                                                 separator = backslash;
2987                                         if (separator < colon)
2988                                                 separator = colon;
2989                                         *((char *)separator) = 0;
2990                                 }
2991                         }
2992                 }
2993                 else
2994                 {
2995                         stringlist_t matchedSet, foundSet;
2996                         const char *start = pattern;
2997
2998                         stringlistinit(&matchedSet);
2999                         stringlistinit(&foundSet);
3000                         // add a first entry to the set
3001                         stringlistappend(&matchedSet, "");
3002                         // iterate through pattern's path
3003                         while (*start)
3004                         {
3005                                 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
3006                                 char subpath[MAX_OSPATH];
3007                                 char subpattern[MAX_OSPATH];
3008
3009                                 // find the next wildcard
3010                                 wildcard = strchr(start, '?');
3011                                 asterisk = strchr(start, '*');
3012                                 if (asterisk && (!wildcard || asterisk < wildcard))
3013                                 {
3014                                         wildcard = asterisk;
3015                                 }
3016
3017                                 if (wildcard)
3018                                 {
3019                                         nextseparator = strchr( wildcard, '/' );
3020                                 }
3021                                 else
3022                                 {
3023                                         nextseparator = NULL;
3024                                 }
3025
3026                                 if( !nextseparator ) {
3027                                         nextseparator = start + strlen( start );
3028                                 }
3029
3030                                 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
3031                                 // copy everything up except nextseperator
3032                                 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
3033                                 // find the last '/' before the wildcard
3034                                 prevseparator = strrchr( subpattern, '/' );
3035                                 if (!prevseparator)
3036                                         prevseparator = subpattern;
3037                                 else
3038                                         prevseparator++;
3039                                 // copy everything from start to the previous including the '/' (before the wildcard)
3040                                 // everything up to start is already included in the path of matchedSet's entries
3041                                 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
3042
3043                                 // for each entry in matchedSet try to open the subdirectories specified in subpath
3044                                 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
3045                                         strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
3046                                         strlcat( temp, subpath, sizeof(temp) );
3047                                         listdirectory( &foundSet, searchpath->filename, temp );
3048                                 }
3049                                 if( dirlistindex == 0 ) {
3050                                         break;
3051                                 }
3052                                 // reset the current result set
3053                                 stringlistfreecontents( &matchedSet );
3054                                 // match against the pattern
3055                                 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
3056                                         const char *direntry = foundSet.strings[ dirlistindex ];
3057                                         if (matchpattern(direntry, subpattern, true)) {
3058                                                 stringlistappend( &matchedSet, direntry );
3059                                         }
3060                                 }
3061                                 stringlistfreecontents( &foundSet );
3062
3063                                 start = nextseparator;
3064                         }
3065
3066                         for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
3067                         {
3068                                 const char *temp = matchedSet.strings[dirlistindex];
3069                                 if (matchpattern(temp, (char *)pattern, true))
3070                                 {
3071                                         for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3072                                                 if (!strcmp(resultlist.strings[resultlistindex], temp))
3073                                                         break;
3074                                         if (resultlistindex == resultlist.numstrings)
3075                                         {
3076                                                 stringlistappend(&resultlist, temp);
3077                                                 if (!quiet && developer_loading.integer)
3078                                                         Con_Printf("SearchDirFile: %s\n", temp);
3079                                         }
3080                                 }
3081                         }
3082                         stringlistfreecontents( &matchedSet );
3083                 }
3084         }
3085
3086         if (resultlist.numstrings)
3087         {
3088                 stringlistsort(&resultlist);
3089                 numfiles = resultlist.numstrings;
3090                 numchars = 0;
3091                 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3092                         numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
3093                 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
3094                 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
3095                 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
3096                 search->numfilenames = (int)numfiles;
3097                 numfiles = 0;
3098                 numchars = 0;
3099                 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3100                 {
3101                         size_t textlen;
3102                         search->filenames[numfiles] = search->filenamesbuffer + numchars;
3103                         textlen = strlen(resultlist.strings[resultlistindex]) + 1;
3104                         memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
3105                         numfiles++;
3106                         numchars += (int)textlen;
3107                 }
3108         }
3109         stringlistfreecontents(&resultlist);
3110
3111         Mem_Free(basepath);
3112         return search;
3113 }
3114
3115 void FS_FreeSearch(fssearch_t *search)
3116 {
3117         Z_Free(search);
3118 }
3119
3120 extern int con_linewidth;
3121 int FS_ListDirectory(const char *pattern, int oneperline)
3122 {
3123         int numfiles;
3124         int numcolumns;
3125         int numlines;
3126         int columnwidth;
3127         int linebufpos;
3128         int i, j, k, l;
3129         const char *name;
3130         char linebuf[MAX_INPUTLINE];
3131         fssearch_t *search;
3132         search = FS_Search(pattern, true, true);
3133         if (!search)
3134                 return 0;
3135         numfiles = search->numfilenames;
3136         if (!oneperline)
3137         {
3138                 // FIXME: the names could be added to one column list and then
3139                 // gradually shifted into the next column if they fit, and then the
3140                 // next to make a compact variable width listing but it's a lot more
3141                 // complicated...
3142                 // find width for columns
3143                 columnwidth = 0;
3144                 for (i = 0;i < numfiles;i++)
3145                 {
3146                         l = (int)strlen(search->filenames[i]);
3147                         if (columnwidth < l)
3148                                 columnwidth = l;
3149                 }
3150                 // count the spacing character
3151                 columnwidth++;
3152                 // calculate number of columns
3153                 numcolumns = con_linewidth / columnwidth;
3154                 // don't bother with the column printing if it's only one column
3155                 if (numcolumns >= 2)
3156                 {
3157                         numlines = (numfiles + numcolumns - 1) / numcolumns;
3158                         for (i = 0;i < numlines;i++)
3159                         {
3160                                 linebufpos = 0;
3161                                 for (k = 0;k < numcolumns;k++)
3162                                 {
3163                                         l = i * numcolumns + k;
3164                                         if (l < numfiles)
3165                                         {
3166                                                 name = search->filenames[l];
3167                                                 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3168                                                         linebuf[linebufpos++] = name[j];
3169                                                 // space out name unless it's the last on the line
3170                                                 if (k + 1 < numcolumns && l + 1 < numfiles)
3171                                                         for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3172                                                                 linebuf[linebufpos++] = ' ';
3173                                         }
3174                                 }
3175                                 linebuf[linebufpos] = 0;
3176                                 Con_Printf("%s\n", linebuf);
3177                         }
3178                 }
3179                 else
3180                         oneperline = true;
3181         }
3182         if (oneperline)
3183                 for (i = 0;i < numfiles;i++)
3184                         Con_Printf("%s\n", search->filenames[i]);
3185         FS_FreeSearch(search);
3186         return (int)numfiles;
3187 }
3188
3189 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3190 {
3191         const char *pattern;
3192         if (Cmd_Argc() > 3)
3193         {
3194                 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3195                 return;
3196         }
3197         if (Cmd_Argc() == 2)
3198                 pattern = Cmd_Argv(1);
3199         else
3200                 pattern = "*";
3201         if (!FS_ListDirectory(pattern, oneperline))
3202                 Con_Print("No files found.\n");
3203 }
3204
3205 void FS_Dir_f(void)
3206 {
3207         FS_ListDirectoryCmd("dir", true);
3208 }
3209
3210 void FS_Ls_f(void)
3211 {
3212         FS_ListDirectoryCmd("ls", false);
3213 }
3214
3215 void FS_Which_f(void)
3216 {
3217         const char *filename;
3218         int index;
3219         searchpath_t *sp;
3220         if (Cmd_Argc() != 2)
3221         {
3222                 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3223                 return;
3224         }  
3225         filename = Cmd_Argv(1);
3226         sp = FS_FindFile(filename, &index, true);
3227         if (!sp) {
3228                 Con_Printf("%s isn't anywhere\n", filename);
3229                 return;
3230         }
3231         if (sp->pack)
3232                 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3233         else
3234                 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3235 }
3236
3237
3238 const char *FS_WhichPack(const char *filename)
3239 {
3240         int index;
3241         searchpath_t *sp = FS_FindFile(filename, &index, true);
3242         if(sp && sp->pack)
3243                 return sp->pack->shortname;
3244         else
3245                 return 0;
3246 }
3247
3248 /*
3249 ====================
3250 FS_IsRegisteredQuakePack
3251
3252 Look for a proof of purchase file file in the requested package
3253
3254 If it is found, this file should NOT be downloaded.
3255 ====================
3256 */
3257 qboolean FS_IsRegisteredQuakePack(const char *name)
3258 {
3259         searchpath_t *search;
3260         pack_t *pak;
3261
3262         // search through the path, one element at a time
3263         for (search = fs_searchpaths;search;search = search->next)
3264         {
3265                 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3266                 {
3267                         int (*strcmp_funct) (const char* str1, const char* str2);
3268                         int left, right, middle;
3269
3270                         pak = search->pack;
3271                         strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3272
3273                         // Look for the file (binary search)
3274                         left = 0;
3275                         right = pak->numfiles - 1;
3276                         while (left <= right)
3277                         {
3278                                 int diff;
3279
3280                                 middle = (left + right) / 2;
3281                                 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3282
3283                                 // Found it
3284                                 if (!diff)
3285                                         return true;
3286
3287                                 // If we're too far in the list
3288                                 if (diff > 0)
3289                                         right = middle - 1;
3290                                 else
3291                                         left = middle + 1;
3292                         }
3293
3294                         // we found the requested pack but it is not registered quake
3295                         return false;
3296                 }
3297         }
3298
3299         return false;
3300 }
3301
3302 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3303 {
3304         int crc = -1;
3305         unsigned char *filedata;
3306         fs_offset_t filesize;
3307         if (filesizepointer)
3308                 *filesizepointer = 0;
3309         if (!filename || !*filename)
3310                 return crc;
3311         filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3312         if (filedata)
3313         {
3314                 if (filesizepointer)
3315                         *filesizepointer = filesize;
3316                 crc = CRC_Block(filedata, filesize);
3317                 Mem_Free(filedata);
3318         }
3319         return crc;
3320 }
3321
3322 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3323 {
3324         z_stream strm;
3325         unsigned char *out = NULL;
3326         unsigned char *tmp;
3327
3328         memset(&strm, 0, sizeof(strm));
3329         strm.zalloc = Z_NULL;
3330         strm.zfree = Z_NULL;
3331         strm.opaque = Z_NULL;
3332
3333         if(level < 0)
3334                 level = Z_DEFAULT_COMPRESSION;
3335
3336         if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3337         {
3338                 Con_Printf("FS_Deflate: deflate init error!\n");
3339                 return NULL;
3340         }
3341
3342         strm.next_in = (unsigned char*)data;
3343         strm.avail_in = size;
3344
3345         tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3346         if(!tmp)
3347         {
3348                 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3349                 qz_deflateEnd(&strm);
3350                 return NULL;
3351         }
3352
3353         strm.next_out = tmp;
3354         strm.avail_out = size;
3355
3356         if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3357         {
3358                 Con_Printf("FS_Deflate: deflate failed!\n");
3359                 qz_deflateEnd(&strm);
3360                 Mem_Free(tmp);
3361                 return NULL;
3362         }
3363         
3364         if(qz_deflateEnd(&strm) != Z_OK)
3365         {
3366                 Con_Printf("FS_Deflate: deflateEnd failed\n");
3367                 Mem_Free(tmp);
3368                 return NULL;
3369         }
3370
3371         if(strm.total_out >= size)
3372         {
3373                 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3374                 Mem_Free(tmp);
3375                 return NULL;
3376         }
3377
3378         out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3379         if(!out)
3380         {
3381                 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3382                 Mem_Free(tmp);
3383                 return NULL;
3384         }
3385
3386         if(deflated_size)
3387                 *deflated_size = (size_t)strm.total_out;
3388
3389         memcpy(out, tmp, strm.total_out);
3390         Mem_Free(tmp);
3391         
3392         return out;
3393 }
3394
3395 static void AssertBufsize(sizebuf_t *buf, int length)
3396 {
3397         if(buf->cursize + length > buf->maxsize)
3398         {
3399                 int oldsize = buf->maxsize;
3400                 unsigned char *olddata;
3401                 olddata = buf->data;
3402                 buf->maxsize += length;
3403                 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3404                 if(olddata)
3405                 {
3406                         memcpy(buf->data, olddata, oldsize);
3407                         Mem_Free(olddata);
3408                 }
3409         }
3410 }
3411
3412 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3413 {
3414         int ret;
3415         z_stream strm;
3416         unsigned char *out = NULL;
3417         unsigned char tmp[2048];
3418         unsigned int have;
3419         sizebuf_t outbuf;
3420
3421         memset(&outbuf, 0, sizeof(outbuf));
3422         outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3423         outbuf.maxsize = sizeof(tmp);
3424
3425         memset(&strm, 0, sizeof(strm));
3426         strm.zalloc = Z_NULL;
3427         strm.zfree = Z_NULL;
3428         strm.opaque = Z_NULL;
3429
3430         if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3431         {
3432                 Con_Printf("FS_Inflate: inflate init error!\n");
3433                 Mem_Free(outbuf.data);
3434                 return NULL;
3435         }
3436
3437         strm.next_in = (unsigned char*)data;
3438         strm.avail_in = size;
3439
3440         do
3441         {
3442                 strm.next_out = tmp;
3443                 strm.avail_out = sizeof(tmp);
3444                 ret = qz_inflate(&strm, Z_NO_FLUSH);
3445                 // it either returns Z_OK on progress, Z_STREAM_END on end
3446                 // or an error code
3447                 switch(ret)
3448                 {
3449                         case Z_STREAM_END:
3450                         case Z_OK:
3451                                 break;
3452                                 
3453                         case Z_STREAM_ERROR:
3454                                 Con_Print("FS_Inflate: stream error!\n");
3455                                 break;
3456                         case Z_DATA_ERROR:
3457                                 Con_Print("FS_Inflate: data error!\n");
3458                                 break;
3459                         case Z_MEM_ERROR:
3460                                 Con_Print("FS_Inflate: mem error!\n");
3461                                 break;
3462                         case Z_BUF_ERROR:
3463                                 Con_Print("FS_Inflate: buf error!\n");
3464                                 break;
3465                         default:
3466                                 Con_Print("FS_Inflate: unknown error!\n");
3467                                 break;
3468                                 
3469                 }
3470                 if(ret != Z_OK && ret != Z_STREAM_END)
3471                 {
3472                         Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3473                         Mem_Free(outbuf.data);
3474                         qz_inflateEnd(&strm);
3475                         return NULL;
3476                 }
3477                 have = sizeof(tmp) - strm.avail_out;
3478                 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3479                 SZ_Write(&outbuf, tmp, have);
3480         } while(ret != Z_STREAM_END);
3481
3482         qz_inflateEnd(&strm);
3483
3484         out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3485         if(!out)
3486         {
3487                 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3488                 Mem_Free(outbuf.data);
3489                 return NULL;
3490         }
3491
3492         memcpy(out, outbuf.data, outbuf.cursize);
3493         Mem_Free(outbuf.data);
3494
3495         if(inflated_size)
3496                 *inflated_size = (size_t)outbuf.cursize;
3497         
3498         return out;
3499 }