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