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