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