pick apart com_argv[0] to find basedir in Mac OSX when running from a .app package
[divverent/darkplaces.git] / fs.c
1 /*
2         DarkPlaces file system
3
4         Copyright (C) 2003-2005 Mathieu Olivier
5         Copyright (C) 1999,2000  contributors of the QuakeForge project
6
7         This program is free software; you can redistribute it and/or
8         modify it under the terms of the GNU General Public License
9         as published by the Free Software Foundation; either version 2
10         of the License, or (at your option) any later version.
11
12         This program is distributed in the hope that it will be useful,
13         but WITHOUT ANY WARRANTY; without even the implied warranty of
14         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16         See the GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with this program; if not, write to:
20
21                 Free Software Foundation, Inc.
22                 59 Temple Place - Suite 330
23                 Boston, MA  02111-1307, USA
24 */
25
26 #include "quakedef.h"
27
28 #include <limits.h>
29 #include <fcntl.h>
30
31 #ifdef WIN32
32 # include <direct.h>
33 # include <io.h>
34 #else
35 # include <pwd.h>
36 # include <sys/stat.h>
37 # include <unistd.h>
38 #endif
39
40 #include "fs.h"
41
42 // Win32 requires us to add O_BINARY, but the other OSes don't have it
43 #ifndef O_BINARY
44 # define O_BINARY 0
45 #endif
46
47
48 /*
49
50 All of Quake's data access is through a hierchal file system, but the contents
51 of the file system can be transparently merged from several sources.
52
53 The "base directory" is the path to the directory holding the quake.exe and
54 all game directories.  The sys_* files pass this to host_init in
55 quakeparms_t->basedir.  This can be overridden with the "-basedir" command
56 line parm to allow code debugging in a different directory.  The base
57 directory is only used during filesystem initialization.
58
59 The "game directory" is the first tree on the search path and directory that
60 all generated files (savegames, screenshots, demos, config files) will be
61 saved to.  This can be overridden with the "-game" command line parameter.
62 The game directory can never be changed while quake is executing.  This is a
63 precaution against having a malicious server instruct clients to write files
64 over areas they shouldn't.
65
66 */
67
68
69 /*
70 =============================================================================
71
72 CONSTANTS
73
74 =============================================================================
75 */
76
77 // Magic numbers of a ZIP file (big-endian format)
78 #define ZIP_DATA_HEADER 0x504B0304  // "PK\3\4"
79 #define ZIP_CDIR_HEADER 0x504B0102  // "PK\1\2"
80 #define ZIP_END_HEADER  0x504B0506  // "PK\5\6"
81
82 // Other constants for ZIP files
83 #define ZIP_MAX_COMMENTS_SIZE           ((unsigned short)0xFFFF)
84 #define ZIP_END_CDIR_SIZE                       22
85 #define ZIP_CDIR_CHUNK_BASE_SIZE        46
86 #define ZIP_LOCAL_CHUNK_BASE_SIZE       30
87
88 // Zlib constants (from zlib.h)
89 #define Z_SYNC_FLUSH    2
90 #define MAX_WBITS               15
91 #define Z_OK                    0
92 #define Z_STREAM_END    1
93 #define ZLIB_VERSION    "1.1.4"
94
95
96 /*
97 =============================================================================
98
99 TYPES
100
101 =============================================================================
102 */
103
104 // Zlib stream (from zlib.h)
105 // Warning: some pointers we don't use directly have
106 // been cast to "void*" for a matter of simplicity
107 typedef struct
108 {
109         qbyte                   *next_in;       // next input byte
110         unsigned int    avail_in;       // number of bytes available at next_in
111         unsigned long   total_in;       // total nb of input bytes read so far
112
113         qbyte                   *next_out;      // next output byte should be put there
114         unsigned int    avail_out;      // remaining free space at next_out
115         unsigned long   total_out;      // total nb of bytes output so far
116
117         char                    *msg;           // last error message, NULL if no error
118         void                    *state;         // not visible by applications
119
120         void                    *zalloc;        // used to allocate the internal state
121         void                    *zfree;         // used to free the internal state
122         void                    *opaque;        // private data object passed to zalloc and zfree
123
124         int                             data_type;      // best guess about the data type: ascii or binary
125         unsigned long   adler;          // adler32 value of the uncompressed data
126         unsigned long   reserved;       // reserved for future use
127 } z_stream;
128
129
130 typedef enum
131 {
132         QFILE_FLAG_NONE         = 0,
133         QFILE_FLAG_PACKED       = (1 << 0),     // inside a package (PAK or PK3)
134         QFILE_FLAG_DEFLATED     = (1 << 1)      // file is compressed using the deflate algorithm (PK3 only)
135 } qfile_flags_t;
136
137 #define FILE_BUFF_SIZE 2048
138 typedef struct
139 {
140         z_stream        zstream;
141         size_t          comp_length;                    // length of the compressed file
142         size_t          in_ind, in_len;                 // input buffer current index and length
143         size_t          in_position;                    // position in the compressed file
144         qbyte           input [FILE_BUFF_SIZE];
145 } ztoolkit_t;
146
147 struct qfile_s
148 {
149         qfile_flags_t   flags;
150         int                             handle;                                 // file descriptor
151         size_t                  real_length;                    // uncompressed file size (for files opened in "read" mode)
152         size_t                  position;                               // current position in the file
153         size_t                  offset;                                 // offset into the package (0 if external file)
154         int                             ungetc;                                 // single stored character from ungetc, cleared to EOF when read
155
156         // Contents buffer
157         size_t                  buff_ind, buff_len;             // buffer current index and length
158         qbyte                   buff [FILE_BUFF_SIZE];
159
160         // For zipped files
161         ztoolkit_t*             ztk;
162 };
163
164
165 // ------ PK3 files on disk ------ //
166
167 // You can get the complete ZIP format description from PKWARE website
168
169 typedef struct
170 {
171         unsigned int signature;
172         unsigned short disknum;
173         unsigned short cdir_disknum;    // number of the disk with the start of the central directory
174         unsigned short localentries;    // number of entries in the central directory on this disk
175         unsigned short nbentries;               // total number of entries in the central directory on this disk
176         unsigned int cdir_size;                 // size of the central directory
177         unsigned int cdir_offset;               // with respect to the starting disk number
178         unsigned short comment_size;
179 } pk3_endOfCentralDir_t;
180
181
182 // ------ PAK files on disk ------ //
183 typedef struct
184 {
185         char name[56];
186         int filepos, filelen;
187 } dpackfile_t;
188
189 typedef struct
190 {
191         char id[4];
192         int dirofs;
193         int dirlen;
194 } dpackheader_t;
195
196
197 // Packages in memory
198 typedef enum
199 {
200         PACKFILE_FLAG_NONE              = 0,
201         PACKFILE_FLAG_TRUEOFFS  = (1 << 0),     // the offset in packfile_t is the true contents offset
202         PACKFILE_FLAG_DEFLATED  = (1 << 1)      // file compressed using the deflate algorithm
203 } packfile_flags_t;
204
205 typedef struct
206 {
207         char name [MAX_QPATH];
208         packfile_flags_t flags;
209         size_t offset;
210         size_t packsize;        // size in the package
211         size_t realsize;        // real file size (uncompressed)
212 } packfile_t;
213
214 typedef struct pack_s
215 {
216         char filename [MAX_OSPATH];
217         int handle;
218         int ignorecase;  // PK3 ignores case
219         int numfiles;
220         packfile_t *files;
221         struct pack_s *next;
222 } pack_t;
223
224
225 // Search paths for files (including packages)
226 typedef struct searchpath_s
227 {
228         // only one of filename / pack will be used
229         char filename[MAX_OSPATH];
230         pack_t *pack;
231         struct searchpath_s *next;
232 } searchpath_t;
233
234
235 /*
236 =============================================================================
237
238 FUNCTION PROTOTYPES
239
240 =============================================================================
241 */
242
243 void FS_Dir_f(void);
244 void FS_Ls_f(void);
245
246 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
247                                                                          size_t offset, size_t packsize,
248                                                                          size_t realsize, packfile_flags_t flags);
249
250
251 /*
252 =============================================================================
253
254 VARIABLES
255
256 =============================================================================
257 */
258
259 mempool_t *fs_mempool;
260
261 int fs_filesize;
262
263 pack_t *packlist = NULL;
264
265 searchpath_t *fs_searchpaths = NULL;
266
267 #define MAX_FILES_IN_PACK       65536
268
269 char fs_gamedir[MAX_OSPATH];
270 char fs_basedir[MAX_OSPATH];
271
272 qboolean fs_modified;   // set true if using non-id files
273
274
275 /*
276 =============================================================================
277
278 PRIVATE FUNCTIONS - PK3 HANDLING
279
280 =============================================================================
281 */
282
283 // Functions exported from zlib
284 #ifdef WIN32
285 # define ZEXPORT WINAPI
286 #else
287 # define ZEXPORT
288 #endif
289
290 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
291 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
292 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
293 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
294
295 #define qz_inflateInit2(strm, windowBits) \
296         qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
297
298 static dllfunction_t zlibfuncs[] =
299 {
300         {"inflate",                     (void **) &qz_inflate},
301         {"inflateEnd",          (void **) &qz_inflateEnd},
302         {"inflateInit2_",       (void **) &qz_inflateInit2_},
303         {"inflateReset",        (void **) &qz_inflateReset},
304         {NULL, NULL}
305 };
306
307 // Handle for Zlib DLL
308 static dllhandle_t zlib_dll = NULL;
309
310
311 /*
312 ====================
313 PK3_CloseLibrary
314
315 Unload the Zlib DLL
316 ====================
317 */
318 void PK3_CloseLibrary (void)
319 {
320         Sys_UnloadLibrary (&zlib_dll);
321 }
322
323
324 /*
325 ====================
326 PK3_OpenLibrary
327
328 Try to load the Zlib DLL
329 ====================
330 */
331 qboolean PK3_OpenLibrary (void)
332 {
333         const char* dllnames [] =
334         {
335 #ifdef WIN32
336                 "zlib.dll",
337 #elif defined(MACOSX)
338                 "libz.dylib",
339 #else
340                 "libz.so.1",
341                 "libz.so",
342 #endif
343                 NULL
344         };
345
346         // Already loaded?
347         if (zlib_dll)
348                 return true;
349
350         // Load the DLL
351         if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
352         {
353                 Con_Printf ("Compressed files support disabled\n");
354                 return false;
355         }
356
357         Con_Printf ("Compressed files support enabled\n");
358         return true;
359 }
360
361
362 /*
363 ====================
364 PK3_GetEndOfCentralDir
365
366 Extract the end of the central directory from a PK3 package
367 ====================
368 */
369 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
370 {
371         long filesize, maxsize;
372         qbyte *buffer, *ptr;
373         int ind;
374
375         // Get the package size
376         filesize = lseek (packhandle, 0, SEEK_END);
377         if (filesize < ZIP_END_CDIR_SIZE)
378                 return false;
379
380         // Load the end of the file in memory
381         if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
382                 maxsize = filesize;
383         else
384                 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
385         buffer = Mem_Alloc (tempmempool, maxsize);
386         lseek (packhandle, filesize - maxsize, SEEK_SET);
387         if (read (packhandle, buffer, maxsize) != (ssize_t) maxsize)
388         {
389                 Mem_Free (buffer);
390                 return false;
391         }
392
393         // Look for the end of central dir signature around the end of the file
394         maxsize -= ZIP_END_CDIR_SIZE;
395         ptr = &buffer[maxsize];
396         ind = 0;
397         while (BuffBigLong (ptr) != ZIP_END_HEADER)
398         {
399                 if (ind == maxsize)
400                 {
401                         Mem_Free (buffer);
402                         return false;
403                 }
404
405                 ind++;
406                 ptr--;
407         }
408
409         memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
410         eocd->signature = LittleLong (eocd->signature);
411         eocd->disknum = LittleShort (eocd->disknum);
412         eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
413         eocd->localentries = LittleShort (eocd->localentries);
414         eocd->nbentries = LittleShort (eocd->nbentries);
415         eocd->cdir_size = LittleLong (eocd->cdir_size);
416         eocd->cdir_offset = LittleLong (eocd->cdir_offset);
417         eocd->comment_size = LittleShort (eocd->comment_size);
418
419         Mem_Free (buffer);
420
421         return true;
422 }
423
424
425 /*
426 ====================
427 PK3_BuildFileList
428
429 Extract the file list from a PK3 file
430 ====================
431 */
432 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
433 {
434         qbyte *central_dir, *ptr;
435         unsigned int ind;
436         int remaining;
437
438         // Load the central directory in memory
439         central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
440         lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
441         read (pack->handle, central_dir, eocd->cdir_size);
442
443         // Extract the files properties
444         // The parsing is done "by hand" because some fields have variable sizes and
445         // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
446         remaining = eocd->cdir_size;
447         pack->numfiles = 0;
448         ptr = central_dir;
449         for (ind = 0; ind < eocd->nbentries; ind++)
450         {
451                 size_t namesize, count;
452
453                 // Checking the remaining size
454                 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
455                 {
456                         Mem_Free (central_dir);
457                         return -1;
458                 }
459                 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
460
461                 // Check header
462                 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
463                 {
464                         Mem_Free (central_dir);
465                         return -1;
466                 }
467
468                 namesize = BuffLittleShort (&ptr[28]);  // filename length
469
470                 // Check encryption, compression, and attributes
471                 // 1st uint8  : general purpose bit flag
472                 //    Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
473                 // 2nd uint8 : external file attributes
474                 //    Check bits 3 (file is a directory) and 5 (file is a volume (?))
475                 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
476                 {
477                         // Still enough bytes for the name?
478                         if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
479                         {
480                                 Mem_Free (central_dir);
481                                 return -1;
482                         }
483
484                         // WinZip doesn't use the "directory" attribute, so we need to check the name directly
485                         if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
486                         {
487                                 char filename [sizeof (pack->files[0].name)];
488                                 size_t offset, packsize, realsize;
489                                 packfile_flags_t flags;
490
491                                 // Extract the name (strip it if necessary)
492                                 if (namesize >= sizeof (filename))
493                                         namesize = sizeof (filename) - 1;
494                                 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
495                                 filename[namesize] = '\0';
496
497                                 if (BuffLittleShort (&ptr[10]))
498                                         flags = PACKFILE_FLAG_DEFLATED;
499                                 else
500                                         flags = 0;
501                                 offset = BuffLittleLong (&ptr[42]);
502                                 packsize = BuffLittleLong (&ptr[20]);
503                                 realsize = BuffLittleLong (&ptr[24]);
504                                 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
505                         }
506                 }
507
508                 // Skip the name, additionnal field, and comment
509                 // 1er uint16 : extra field length
510                 // 2eme uint16 : file comment length
511                 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
512                 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
513                 remaining -= count;
514         }
515
516         // If the package is empty, central_dir is NULL here
517         if (central_dir != NULL)
518                 Mem_Free (central_dir);
519         return pack->numfiles;
520 }
521
522
523 /*
524 ====================
525 FS_LoadPackPK3
526
527 Create a package entry associated with a PK3 file
528 ====================
529 */
530 pack_t *FS_LoadPackPK3 (const char *packfile)
531 {
532         int packhandle;
533         pk3_endOfCentralDir_t eocd;
534         pack_t *pack;
535         int real_nb_files;
536
537         packhandle = open (packfile, O_RDONLY | O_BINARY);
538         if (packhandle < 0)
539                 return NULL;
540
541         if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
542                 Sys_Error ("%s is not a PK3 file", packfile);
543
544         // Multi-volume ZIP archives are NOT allowed
545         if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
546                 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
547
548         // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
549         // since eocd.nbentries is an unsigned 16 bits integer
550 #if MAX_FILES_IN_PACK < 65535
551         if (eocd.nbentries > MAX_FILES_IN_PACK)
552                 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
553 #endif
554
555         // Create a package structure in memory
556         pack = Mem_Alloc(fs_mempool, sizeof (pack_t));
557         pack->ignorecase = true; // PK3 ignores case
558         strlcpy (pack->filename, packfile, sizeof (pack->filename));
559         pack->handle = packhandle;
560         pack->numfiles = eocd.nbentries;
561         pack->files = Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
562         pack->next = packlist;
563         packlist = pack;
564
565         real_nb_files = PK3_BuildFileList (pack, &eocd);
566         if (real_nb_files < 0)
567                 Sys_Error ("%s is not a valid PK3 file", packfile);
568
569         Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
570         return pack;
571 }
572
573
574 /*
575 ====================
576 PK3_GetTrueFileOffset
577
578 Find where the true file data offset is
579 ====================
580 */
581 void PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
582 {
583         qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
584         size_t count;
585
586         // Already found?
587         if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
588                 return;
589
590         // Load the local file description
591         lseek (pack->handle, pfile->offset, SEEK_SET);
592         count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
593         if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
594                 Sys_Error ("Can't retrieve file %s in package %s", pfile->name, pack->filename);
595
596         // Skip name and extra field
597         pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
598
599         pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
600 }
601
602
603 /*
604 =============================================================================
605
606 OTHER PRIVATE FUNCTIONS
607
608 =============================================================================
609 */
610
611
612 /*
613 ====================
614 FS_AddFileToPack
615
616 Add a file to the list of files contained into a package
617 ====================
618 */
619 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
620                                                                          size_t offset, size_t packsize,
621                                                                          size_t realsize, packfile_flags_t flags)
622 {
623         int (*strcmp_funct) (const char* str1, const char* str2);
624         int left, right, middle;
625         packfile_t *pfile;
626
627         strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
628
629         // Look for the slot we should put that file into (binary search)
630         left = 0;
631         right = pack->numfiles - 1;
632         while (left <= right)
633         {
634                 int diff;
635
636                 middle = (left + right) / 2;
637                 diff = strcmp_funct (pack->files[middle].name, name);
638
639                 // If we found the file, there's a problem
640                 if (!diff)
641                         Sys_Error ("Package %s contains the file %s several times\n",
642                                            pack->filename, name);
643
644                 // If we're too far in the list
645                 if (diff > 0)
646                         right = middle - 1;
647                 else
648                         left = middle + 1;
649         }
650
651         // We have to move the right of the list by one slot to free the one we need
652         pfile = &pack->files[left];
653         memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
654         pack->numfiles++;
655
656         strlcpy (pfile->name, name, sizeof (pfile->name));
657         pfile->offset = offset;
658         pfile->packsize = packsize;
659         pfile->realsize = realsize;
660         pfile->flags = flags;
661
662         return pfile;
663 }
664
665
666 /*
667 ============
668 FS_CreatePath
669
670 Only used for FS_Open.
671 ============
672 */
673 void FS_CreatePath (char *path)
674 {
675         char *ofs, save;
676
677         for (ofs = path+1 ; *ofs ; ofs++)
678         {
679                 if (*ofs == '/' || *ofs == '\\')
680                 {
681                         // create the directory
682                         save = *ofs;
683                         *ofs = 0;
684                         FS_mkdir (path);
685                         *ofs = save;
686                 }
687         }
688 }
689
690
691 /*
692 ============
693 FS_Path_f
694
695 ============
696 */
697 void FS_Path_f (void)
698 {
699         searchpath_t *s;
700
701         Con_Print("Current search path:\n");
702         for (s=fs_searchpaths ; s ; s=s->next)
703         {
704                 if (s->pack)
705                 {
706                         Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
707                 }
708                 else
709                         Con_Printf("%s\n", s->filename);
710         }
711 }
712
713
714 /*
715 =================
716 FS_LoadPackPAK
717
718 Takes an explicit (not game tree related) path to a pak file.
719
720 Loads the header and directory, adding the files at the beginning
721 of the list so they override previous pack files.
722 =================
723 */
724 pack_t *FS_LoadPackPAK (const char *packfile)
725 {
726         dpackheader_t header;
727         int i, numpackfiles;
728         int packhandle;
729         pack_t *pack;
730         dpackfile_t *info;
731
732         packhandle = open (packfile, O_RDONLY | O_BINARY);
733         if (packhandle < 0)
734                 return NULL;
735         read (packhandle, (void *)&header, sizeof(header));
736         if (memcmp(header.id, "PACK", 4))
737                 Sys_Error ("%s is not a packfile", packfile);
738         header.dirofs = LittleLong (header.dirofs);
739         header.dirlen = LittleLong (header.dirlen);
740
741         if (header.dirlen % sizeof(dpackfile_t))
742                 Sys_Error ("%s has an invalid directory size", packfile);
743
744         numpackfiles = header.dirlen / sizeof(dpackfile_t);
745
746         if (numpackfiles > MAX_FILES_IN_PACK)
747                 Sys_Error ("%s has %i files", packfile, numpackfiles);
748
749         pack = Mem_Alloc(fs_mempool, sizeof (pack_t));
750         pack->ignorecase = false; // PAK is case sensitive
751         strlcpy (pack->filename, packfile, sizeof (pack->filename));
752         pack->handle = packhandle;
753         pack->numfiles = 0;
754         pack->files = Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
755         pack->next = packlist;
756         packlist = pack;
757
758         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
759         lseek (packhandle, header.dirofs, SEEK_SET);
760         read (packhandle, (void *)info, header.dirlen);
761
762         // parse the directory
763         for (i = 0;i < numpackfiles;i++)
764         {
765                 size_t offset = LittleLong (info[i].filepos);
766                 size_t size = LittleLong (info[i].filelen);
767
768                 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
769         }
770
771         Mem_Free(info);
772
773         Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
774         return pack;
775 }
776
777
778 /*
779 ================
780 FS_AddGameDirectory
781
782 Sets fs_gamedir, adds the directory to the head of the path,
783 then loads and adds pak1.pak pak2.pak ...
784 ================
785 */
786 void FS_AddGameDirectory (const char *dir)
787 {
788         stringlist_t *list, *current;
789         searchpath_t *search;
790         pack_t *pak;
791         char pakfile[MAX_OSPATH];
792
793         strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
794
795         list = listdirectory(dir);
796
797         // add any PAK package in the directory
798         for (current = list;current;current = current->next)
799         {
800                 if (matchpattern(current->text, "*.pak", true))
801                 {
802                         dpsnprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
803                         pak = FS_LoadPackPAK (pakfile);
804                         if (pak)
805                         {
806                                 search = Mem_Alloc(fs_mempool, sizeof(searchpath_t));
807                                 search->pack = pak;
808                                 search->next = fs_searchpaths;
809                                 fs_searchpaths = search;
810                         }
811                         else
812                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
813                 }
814         }
815
816         // add any PK3 package in the director
817         for (current = list;current;current = current->next)
818         {
819                 if (matchpattern(current->text, "*.pk3", true))
820                 {
821                         dpsnprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
822                         pak = FS_LoadPackPK3 (pakfile);
823                         if (pak)
824                         {
825                                 search = Mem_Alloc(fs_mempool, sizeof(searchpath_t));
826                                 search->pack = pak;
827                                 search->next = fs_searchpaths;
828                                 fs_searchpaths = search;
829                         }
830                         else
831                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
832                 }
833         }
834         freedirectory(list);
835
836         // Add the directory to the search path
837         // (unpacked files have the priority over packed files)
838         search = Mem_Alloc(fs_mempool, sizeof(searchpath_t));
839         strlcpy (search->filename, dir, sizeof (search->filename));
840         search->next = fs_searchpaths;
841         fs_searchpaths = search;
842 }
843
844
845 /*
846 ================
847 FS_AddGameHierarchy
848 ================
849 */
850 void FS_AddGameHierarchy (const char *dir)
851 {
852 #ifndef WIN32
853         const char *homedir;
854 #endif
855
856         // Add the common game directory
857         FS_AddGameDirectory (va("%s/%s", fs_basedir, dir));
858
859 #ifndef WIN32
860         // Add the personal game directory
861         homedir = getenv ("HOME");
862         if (homedir != NULL && homedir[0] != '\0')
863                 FS_AddGameDirectory (va("%s/.%s/%s", homedir, gameuserdirname, dir));
864 #endif
865 }
866
867
868 /*
869 ============
870 FS_FileExtension
871 ============
872 */
873 static const char *FS_FileExtension (const char *in)
874 {
875         const char *separator, *backslash, *colon, *dot;
876
877         separator = strrchr(in, '/');
878         backslash = strrchr(in, '\\');
879         if (separator < backslash)
880                 separator = backslash;
881         colon = strrchr(in, ':');
882         if (separator < colon)
883                 separator = colon;
884
885         dot = strrchr(in, '.');
886         if (dot == NULL || dot < separator)
887                 return "";
888
889         return dot + 1;
890 }
891
892
893 /*
894 ================
895 FS_Init
896 ================
897 */
898 void FS_Init (void)
899 {
900         int i;
901         searchpath_t *search;
902
903         fs_mempool = Mem_AllocPool("file management", 0, NULL);
904
905         strcpy(fs_basedir, ".");
906         strcpy(fs_gamedir, "");
907
908 #ifdef MACOSX
909         // FIXME: is there a better way to find the directory outside the .app?
910         if (strstr(com_argv[0], ".app/"))
911         {
912                 char *split;
913                 char temp[4096];
914                 split = strstr(com_argv[0], ".app/");
915                 while (split > com_argv[0] && *split != '/')
916                         split--;
917                 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
918                 fs_basedir[split - com_argv[0]] = 0;
919         }
920 #endif
921
922         PK3_OpenLibrary ();
923
924         // -basedir <path>
925         // Overrides the system supplied base directory (under GAMENAME)
926 // 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)
927         i = COM_CheckParm ("-basedir");
928         if (i && i < com_argc-1)
929         {
930                 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
931                 i = strlen (fs_basedir);
932                 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
933                         fs_basedir[i-1] = 0;
934         }
935
936         // -path <dir or packfile> [<dir or packfile>] ...
937         // Fully specifies the exact search path, overriding the generated one
938 // COMMANDLINEOPTION: Filesystem: -path <path ..> specifies the full search path manually, overriding the generated one, example: -path c:\quake\id1 c:\quake\pak0.pak c:\quake\pak1.pak (not recommended)
939         i = COM_CheckParm ("-path");
940         if (i)
941         {
942                 fs_modified = true;
943                 while (++i < com_argc)
944                 {
945                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
946                                 break;
947
948                         search = Mem_Alloc(fs_mempool, sizeof(searchpath_t));
949                         if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
950                         {
951                                 search->pack = FS_LoadPackPAK (com_argv[i]);
952                                 if (!search->pack)
953                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
954                         }
955                         else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
956                         {
957                                 search->pack = FS_LoadPackPK3 (com_argv[i]);
958                                 if (!search->pack)
959                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
960                         }
961                         else
962                                 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
963                         search->next = fs_searchpaths;
964                         fs_searchpaths = search;
965                 }
966                 return;
967         }
968
969         // add the game-specific paths
970         // gamedirname1 (typically id1)
971         FS_AddGameHierarchy (gamedirname1);
972
973         // add the game-specific path, if any
974         if (gamedirname2)
975         {
976                 fs_modified = true;
977                 FS_AddGameHierarchy (gamedirname2);
978         }
979
980         // set the com_modname (reported in server info)
981         strlcpy(com_modname, gamedirname1, sizeof(com_modname));
982
983         // -game <gamedir>
984         // Adds basedir/gamedir as an override game
985         // LordHavoc: now supports multiple -game directories
986         for (i = 1;i < com_argc;i++)
987         {
988                 if (!com_argv[i])
989                         continue;
990                 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
991                 {
992                         i++;
993                         fs_modified = true;
994                         FS_AddGameHierarchy (com_argv[i]);
995                         // update the com_modname
996                         strlcpy (com_modname, com_argv[i], sizeof (com_modname));
997                 }
998         }
999
1000         // If "-condebug" is in the command line, remove the previous log file
1001         if (COM_CheckParm ("-condebug") != 0)
1002                 unlink (va("%s/qconsole.log", fs_gamedir));
1003 }
1004
1005 void FS_Init_Commands(void)
1006 {
1007         Cvar_RegisterVariable (&scr_screenshot_name);
1008
1009         Cmd_AddCommand ("path", FS_Path_f);
1010         Cmd_AddCommand ("dir", FS_Dir_f);
1011         Cmd_AddCommand ("ls", FS_Ls_f);
1012
1013         // set the default screenshot name to either the mod name or the
1014         // gamemode screenshot name
1015         if (fs_modified)
1016                 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1017         else
1018                 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1019 }
1020
1021 /*
1022 ================
1023 FS_Shutdown
1024 ================
1025 */
1026 void FS_Shutdown (void)
1027 {
1028         Mem_FreePool (&fs_mempool);
1029 }
1030
1031 /*
1032 ====================
1033 FS_SysOpen
1034
1035 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1036 ====================
1037 */
1038 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1039 {
1040         qfile_t* file;
1041         int mod, opt;
1042         unsigned int ind;
1043
1044         // Parse the mode string
1045         switch (mode[0])
1046         {
1047                 case 'r':
1048                         mod = O_RDONLY;
1049                         opt = 0;
1050                         break;
1051                 case 'w':
1052                         mod = O_WRONLY;
1053                         opt = O_CREAT | O_TRUNC;
1054                         break;
1055                 case 'a':
1056                         mod = O_WRONLY;
1057                         opt = O_CREAT | O_APPEND;
1058                         break;
1059                 default:
1060                         Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1061                         return NULL;
1062         }
1063         for (ind = 1; mode[ind] != '\0'; ind++)
1064         {
1065                 switch (mode[ind])
1066                 {
1067                         case '+':
1068                                 mod = O_RDWR;
1069                                 break;
1070                         case 'b':
1071                                 opt |= O_BINARY;
1072                                 break;
1073                         default:
1074                                 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1075                                                         filepath, mode, mode[ind]);
1076                 }
1077         }
1078
1079 #ifndef WIN32
1080         if (nonblocking)
1081                 opt |= O_NONBLOCK;
1082 #endif
1083
1084         file = Mem_Alloc (fs_mempool, sizeof (*file));
1085         memset (file, 0, sizeof (*file));
1086         file->ungetc = EOF;
1087
1088         file->handle = open (filepath, mod | opt, 0666);
1089         if (file->handle < 0)
1090         {
1091                 Mem_Free (file);
1092                 return NULL;
1093         }
1094
1095         file->real_length = lseek (file->handle, 0, SEEK_END);
1096
1097         // For files opened in append mode, we start at the end of the file
1098         if (mod & O_APPEND)
1099                 file->position = file->real_length;
1100         else
1101                 lseek (file->handle, 0, SEEK_SET);
1102
1103         return file;
1104 }
1105
1106
1107 /*
1108 ===========
1109 FS_OpenPackedFile
1110
1111 Open a packed file using its package file descriptor
1112 ===========
1113 */
1114 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1115 {
1116         packfile_t *pfile;
1117         int dup_handle;
1118         qfile_t* file;
1119
1120         pfile = &pack->files[pack_ind];
1121
1122         // If we don't have the true offset, get it now
1123         if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1124                 PK3_GetTrueFileOffset (pfile, pack);
1125
1126         // No Zlib DLL = no compressed files
1127         if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1128         {
1129                 Con_Printf("WARNING: can't open the compressed file %s\n"
1130                                         "You need the Zlib DLL to use compressed files\n",
1131                                         pfile->name);
1132                 fs_filesize = -1;
1133                 return NULL;
1134         }
1135
1136         dup_handle = dup (pack->handle);
1137         if (dup_handle < 0)
1138                 Sys_Error ("FS_OpenPackedFile: can't dup package's handle (pack: %s)", pack->filename);
1139
1140         file = Mem_Alloc (fs_mempool, sizeof (*file));
1141         memset (file, 0, sizeof (*file));
1142         file->handle = dup_handle;
1143         file->flags = QFILE_FLAG_PACKED;
1144         file->real_length = pfile->realsize;
1145         file->offset = pfile->offset;
1146         file->position = 0;
1147         file->ungetc = EOF;
1148
1149         if (lseek (file->handle, file->offset, SEEK_SET) == -1)
1150                 Sys_Error ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)",
1151                                         pfile->name, pack->filename, file->offset);
1152
1153         if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1154         {
1155                 ztoolkit_t *ztk;
1156
1157                 file->flags |= QFILE_FLAG_DEFLATED;
1158
1159                 // We need some more variables
1160                 ztk = Mem_Alloc (fs_mempool, sizeof (*ztk));
1161
1162                 ztk->comp_length = pfile->packsize;
1163
1164                 // Initialize zlib stream
1165                 ztk->zstream.next_in = ztk->input;
1166                 ztk->zstream.avail_in = 0;
1167
1168                 /* From Zlib's "unzip.c":
1169                  *
1170                  * windowBits is passed < 0 to tell that there is no zlib header.
1171                  * Note that in this case inflate *requires* an extra "dummy" byte
1172                  * after the compressed stream in order to complete decompression and
1173                  * return Z_STREAM_END.
1174                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1175                  * size of both compressed and uncompressed data
1176                  */
1177                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1178                         Sys_Error ("FS_OpenPackedFile: inflate init error (file: %s)", pfile->name);
1179
1180                 ztk->zstream.next_out = file->buff;
1181                 ztk->zstream.avail_out = sizeof (file->buff);
1182
1183                 file->ztk = ztk;
1184         }
1185
1186         fs_filesize = pfile->realsize;
1187
1188         return file;
1189 }
1190
1191 /*
1192 ====================
1193 FS_CheckNastyPath
1194
1195 Return true if the path should be rejected due to one of the following:
1196 1: path elements that are non-portable
1197 2: path elements that would allow access to files outside the game directory,
1198    or are just not a good idea for a mod to be using.
1199 ====================
1200 */
1201 int FS_CheckNastyPath (const char *path)
1202 {
1203         // Windows: don't allow \ in filenames (windows-only), period.
1204         // (on Windows \ is a directory separator, but / is also supported)
1205         if (strstr(path, "\\"))
1206                 return 1; // non-portable
1207
1208         // Mac: don't allow Mac-only filenames - : is a directory separator
1209         // instead of /, but we rely on / working already, so there's no reason to
1210         // support a Mac-only path
1211         // Amiga and Windows: : tries to go to root of drive
1212         if (strstr(path, ":"))
1213                 return 1; // non-portable attempt to go to root of drive
1214
1215         // Amiga: // is parent directory
1216         if (strstr(path, "//"))
1217                 return 1; // non-portable attempt to go to parent directory
1218
1219         // all: don't allow going to current directory (./) or parent directory (../ or /../)
1220         if (strstr(path, "./"))
1221                 return 2; // attempt to go outside the game directory
1222
1223         // Windows and UNIXes: don't allow absolute paths
1224         if (path[0] == '/')
1225                 return 2; // attempt to go outside the game directory
1226
1227         // after all these checks we're pretty sure it's a / separated filename
1228         // and won't do much if any harm
1229         return false;
1230 }
1231
1232
1233 /*
1234 ====================
1235 FS_FindFile
1236
1237 Look for a file in the packages and in the filesystem
1238
1239 Return the searchpath where the file was found (or NULL)
1240 and the file index in the package if relevant
1241 ====================
1242 */
1243 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1244 {
1245         searchpath_t *search;
1246         pack_t *pak;
1247
1248         // search through the path, one element at a time
1249         for (search = fs_searchpaths;search;search = search->next)
1250         {
1251                 // is the element a pak file?
1252                 if (search->pack)
1253                 {
1254                         int (*strcmp_funct) (const char* str1, const char* str2);
1255                         int left, right, middle;
1256
1257                         pak = search->pack;
1258                         strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1259
1260                         // Look for the file (binary search)
1261                         left = 0;
1262                         right = pak->numfiles - 1;
1263                         while (left <= right)
1264                         {
1265                                 int diff;
1266
1267                                 middle = (left + right) / 2;
1268                                 diff = strcmp_funct (pak->files[middle].name, name);
1269
1270                                 // Found it
1271                                 if (!diff)
1272                                 {
1273                                         if (!quiet)
1274                                                 Con_DPrintf("FS_FindFile: %s in %s\n",
1275                                                                         pak->files[middle].name, pak->filename);
1276
1277                                         if (index != NULL)
1278                                                 *index = middle;
1279                                         return search;
1280                                 }
1281
1282                                 // If we're too far in the list
1283                                 if (diff > 0)
1284                                         right = middle - 1;
1285                                 else
1286                                         left = middle + 1;
1287                         }
1288                 }
1289                 else
1290                 {
1291                         char netpath[MAX_OSPATH];
1292                         dpsnprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1293                         if (FS_SysFileExists (netpath))
1294                         {
1295                                 if (!quiet)
1296                                         Con_DPrintf("FS_FindFile: %s\n", netpath);
1297
1298                                 if (index != NULL)
1299                                         *index = -1;
1300                                 return search;
1301                         }
1302                 }
1303         }
1304
1305         if (!quiet)
1306                 Con_DPrintf("FS_FindFile: can't find %s\n", name);
1307
1308         if (index != NULL)
1309                 *index = -1;
1310         return NULL;
1311 }
1312
1313
1314 /*
1315 ===========
1316 FS_OpenReadFile
1317
1318 Look for a file in the search paths and open it in read-only mode
1319
1320 Sets fs_filesize
1321 ===========
1322 */
1323 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1324 {
1325         searchpath_t *search;
1326         int pack_ind;
1327
1328         search = FS_FindFile (filename, &pack_ind, quiet);
1329
1330         // Not found?
1331         if (search == NULL)
1332         {
1333                 fs_filesize = -1;
1334                 return NULL;
1335         }
1336
1337         // Found in the filesystem?
1338         if (pack_ind < 0)
1339         {
1340                 char path [MAX_OSPATH];
1341                 dpsnprintf (path, sizeof (path), "%s/%s", search->filename, filename);
1342                 return FS_SysOpen (path, "rb", nonblocking);
1343         }
1344
1345         // So, we found it in a package...
1346         return FS_OpenPackedFile (search->pack, pack_ind);
1347 }
1348
1349
1350 /*
1351 =============================================================================
1352
1353 MAIN PUBLIC FUNCTIONS
1354
1355 =============================================================================
1356 */
1357
1358 /*
1359 ====================
1360 FS_Open
1361
1362 Open a file. The syntax is the same as fopen
1363 ====================
1364 */
1365 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1366 {
1367         qfile_t* file;
1368
1369         if (FS_CheckNastyPath(filepath))
1370         {
1371                 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1372                 return NULL;
1373         }
1374
1375         // If the file is opened in "write", "append", or "read/write" mode
1376         if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1377         {
1378                 char real_path [MAX_OSPATH];
1379
1380                 // Open the file on disk directly
1381                 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1382
1383                 // Create directories up to the file
1384                 FS_CreatePath (real_path);
1385
1386                 return FS_SysOpen (real_path, mode, nonblocking);
1387         }
1388
1389         // Else, we look at the various search paths and open the file in read-only mode
1390         file = FS_OpenReadFile (filepath, quiet, nonblocking);
1391         if (file != NULL)
1392                 fs_filesize = file->real_length;
1393
1394         return file;
1395 }
1396
1397
1398 /*
1399 ====================
1400 FS_Close
1401
1402 Close a file
1403 ====================
1404 */
1405 int FS_Close (qfile_t* file)
1406 {
1407         if (close (file->handle))
1408                 return EOF;
1409
1410         if (file->ztk)
1411         {
1412                 qz_inflateEnd (&file->ztk->zstream);
1413                 Mem_Free (file->ztk);
1414         }
1415
1416         Mem_Free (file);
1417         return 0;
1418 }
1419
1420
1421 /*
1422 ====================
1423 FS_Write
1424
1425 Write "datasize" bytes into a file
1426 ====================
1427 */
1428 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1429 {
1430         ssize_t result;
1431
1432         // If necessary, seek to the exact file position we're supposed to be
1433         if (file->buff_ind != file->buff_len)
1434                 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1435
1436         // Purge cached data
1437         FS_Purge (file);
1438
1439         // Write the buffer and update the position
1440         result = write (file->handle, data, datasize);
1441         file->position = lseek (file->handle, 0, SEEK_CUR);
1442         if (file->real_length < file->position)
1443                 file->real_length = file->position;
1444
1445         if (result < 0)
1446                 return 0;
1447
1448         return result;
1449 }
1450
1451
1452 /*
1453 ====================
1454 FS_Read
1455
1456 Read up to "buffersize" bytes from a file
1457 ====================
1458 */
1459 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1460 {
1461         size_t count, done;
1462
1463         if (buffersize == 0)
1464                 return 0;
1465
1466         // Get rid of the ungetc character
1467         if (file->ungetc != EOF)
1468         {
1469                 ((char*)buffer)[0] = file->ungetc;
1470                 buffersize--;
1471                 file->ungetc = EOF;
1472                 done = 1;
1473         }
1474         else
1475                 done = 0;
1476
1477         // First, we copy as many bytes as we can from "buff"
1478         if (file->buff_ind < file->buff_len)
1479         {
1480                 count = file->buff_len - file->buff_ind;
1481
1482                 done += (buffersize > count) ? count : buffersize;
1483                 memcpy (buffer, &file->buff[file->buff_ind], done);
1484                 file->buff_ind += done;
1485
1486                 buffersize -= done;
1487                 if (buffersize == 0)
1488                         return done;
1489         }
1490
1491         // NOTE: at this point, the read buffer is always empty
1492
1493         // If the file isn't compressed
1494         if (! (file->flags & QFILE_FLAG_DEFLATED))
1495         {
1496                 int nb;
1497
1498                 // We must take care to not read after the end of the file
1499                 count = file->real_length - file->position;
1500
1501                 // If we have a lot of data to get, put them directly into "buffer"
1502                 if (buffersize > sizeof (file->buff) / 2)
1503                 {
1504                         if (count > buffersize)
1505                                 count = buffersize;
1506                         lseek (file->handle, file->offset + file->position, SEEK_SET);
1507                         nb = read (file->handle, &((qbyte*)buffer)[done], count);
1508                         if (nb > 0)
1509                         {
1510                                 done += nb;
1511                                 file->position += nb;
1512
1513                                 // Purge cached data
1514                                 FS_Purge (file);
1515                         }
1516                 }
1517                 else
1518                 {
1519                         if (count > sizeof (file->buff))
1520                                 count = sizeof (file->buff);
1521                         lseek (file->handle, file->offset + file->position, SEEK_SET);
1522                         nb = read (file->handle, file->buff, count);
1523                         if (nb > 0)
1524                         {
1525                                 file->buff_len = nb;
1526                                 file->position += nb;
1527
1528                                 // Copy the requested data in "buffer" (as much as we can)
1529                                 count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1530                                 memcpy (&((qbyte*)buffer)[done], file->buff, count);
1531                                 file->buff_ind = count;
1532                                 done += count;
1533                         }
1534                 }
1535
1536                 return done;
1537         }
1538
1539         // If the file is compressed, it's more complicated...
1540         // We cycle through a few operations until we have read enough data
1541         while (buffersize > 0)
1542         {
1543                 ztoolkit_t *ztk = file->ztk;
1544                 int error;
1545
1546                 // NOTE: at this point, the read buffer is always empty
1547
1548                 // If "input" is also empty, we need to refill it
1549                 if (ztk->in_ind == ztk->in_len)
1550                 {
1551                         // If we are at the end of the file
1552                         if (file->position == file->real_length)
1553                                 return done;
1554
1555                         count = ztk->comp_length - ztk->in_position;
1556                         if (count > sizeof (ztk->input))
1557                                 count = sizeof (ztk->input);
1558                         lseek (file->handle, file->offset + ztk->in_position, SEEK_SET);
1559                         if (read (file->handle, ztk->input, count) != (ssize_t)count)
1560                                 Sys_Error ("FS_Read: unexpected end of file");
1561
1562                         ztk->in_ind = 0;
1563                         ztk->in_len = count;
1564                         ztk->in_position += count;
1565                 }
1566
1567                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1568                 ztk->zstream.avail_in = ztk->in_len - ztk->in_ind;
1569
1570                 // Now that we are sure we have compressed data available, we need to determine
1571                 // if it's better to inflate it in "file->buff" or directly in "buffer"
1572
1573                 // Inflate the data in "file->buff"
1574                 if (buffersize < sizeof (file->buff) / 2)
1575                 {
1576                         ztk->zstream.next_out = file->buff;
1577                         ztk->zstream.avail_out = sizeof (file->buff);
1578                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1579                         if (error != Z_OK && error != Z_STREAM_END)
1580                                 Sys_Error ("Can't inflate file");
1581                         ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1582
1583                         file->buff_len = sizeof (file->buff) - ztk->zstream.avail_out;
1584                         file->position += file->buff_len;
1585
1586                         // Copy the requested data in "buffer" (as much as we can)
1587                         count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1588                         memcpy (&((qbyte*)buffer)[done], file->buff, count);
1589                         file->buff_ind = count;
1590                 }
1591
1592                 // Else, we inflate directly in "buffer"
1593                 else
1594                 {
1595                         ztk->zstream.next_out = &((qbyte*)buffer)[done];
1596                         ztk->zstream.avail_out = buffersize;
1597                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1598                         if (error != Z_OK && error != Z_STREAM_END)
1599                                 Sys_Error ("Can't inflate file");
1600                         ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1601
1602                         // How much data did it inflate?
1603                         count = buffersize - ztk->zstream.avail_out;
1604                         file->position += count;
1605
1606                         // Purge cached data
1607                         FS_Purge (file);
1608                 }
1609
1610                 done += count;
1611                 buffersize -= count;
1612         }
1613
1614         return done;
1615 }
1616
1617
1618 /*
1619 ====================
1620 FS_Print
1621
1622 Print a string into a file
1623 ====================
1624 */
1625 int FS_Print (qfile_t* file, const char *msg)
1626 {
1627         return FS_Write (file, msg, strlen (msg));
1628 }
1629
1630 /*
1631 ====================
1632 FS_Printf
1633
1634 Print a string into a file
1635 ====================
1636 */
1637 int FS_Printf(qfile_t* file, const char* format, ...)
1638 {
1639         int result;
1640         va_list args;
1641
1642         va_start (args, format);
1643         result = FS_VPrintf (file, format, args);
1644         va_end (args);
1645
1646         return result;
1647 }
1648
1649
1650 /*
1651 ====================
1652 FS_VPrintf
1653
1654 Print a string into a file
1655 ====================
1656 */
1657 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
1658 {
1659         int len;
1660         size_t buff_size;
1661         char *tempbuff = NULL;
1662
1663         buff_size = 1024;
1664         tempbuff = Mem_Alloc (tempmempool, buff_size);
1665         len = dpvsnprintf (tempbuff, buff_size, format, ap);
1666         while (len < 0)
1667         {
1668                 Mem_Free (tempbuff);
1669                 buff_size *= 2;
1670                 tempbuff = Mem_Alloc (tempmempool, buff_size);
1671                 len = dpvsnprintf (tempbuff, buff_size, format, ap);
1672         }
1673
1674         len = write (file->handle, tempbuff, len);
1675         Mem_Free (tempbuff);
1676
1677         return len;
1678 }
1679
1680
1681 /*
1682 ====================
1683 FS_Getc
1684
1685 Get the next character of a file
1686 ====================
1687 */
1688 int FS_Getc (qfile_t* file)
1689 {
1690         char c;
1691
1692         if (FS_Read (file, &c, 1) != 1)
1693                 return EOF;
1694
1695         return c;
1696 }
1697
1698
1699 /*
1700 ====================
1701 FS_UnGetc
1702
1703 Put a character back into the read buffer (only supports one character!)
1704 ====================
1705 */
1706 int FS_UnGetc (qfile_t* file, unsigned char c)
1707 {
1708         // If there's already a character waiting to be read
1709         if (file->ungetc != EOF)
1710                 return EOF;
1711
1712         file->ungetc = c;
1713         return c;
1714 }
1715
1716
1717 /*
1718 ====================
1719 FS_Seek
1720
1721 Move the position index in a file
1722 ====================
1723 */
1724 int FS_Seek (qfile_t* file, long offset, int whence)
1725 {
1726         ztoolkit_t *ztk;
1727         qbyte* buffer;
1728         size_t buffersize;
1729
1730         // Compute the file offset
1731         switch (whence)
1732         {
1733                 case SEEK_CUR:
1734                         offset += file->position - file->buff_len + file->buff_ind;
1735                         break;
1736
1737                 case SEEK_SET:
1738                         break;
1739
1740                 case SEEK_END:
1741                         offset += file->real_length;
1742                         break;
1743
1744                 default:
1745                         return -1;
1746         }
1747         if (offset < 0 || offset > (long) file->real_length)
1748                 return -1;
1749
1750         // If we have the data in our read buffer, we don't need to actually seek
1751         if (file->position - file->buff_len <= (size_t)offset &&
1752                 (size_t)offset <= file->position)
1753         {
1754                 file->buff_ind = offset + file->buff_len - file->position;
1755                 return 0;
1756         }
1757
1758         // Purge cached data
1759         FS_Purge (file);
1760
1761         // Unpacked or uncompressed files can seek directly
1762         if (! (file->flags & QFILE_FLAG_DEFLATED))
1763         {
1764                 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
1765                         return -1;
1766                 file->position = offset;
1767                 return 0;
1768         }
1769
1770         // Seeking in compressed files is more a hack than anything else,
1771         // but we need to support it, so here we go.
1772         ztk = file->ztk;
1773
1774         // If we have to go back in the file, we need to restart from the beginning
1775         if ((size_t)offset <= file->position)
1776         {
1777                 ztk->in_ind = 0;
1778                 ztk->in_len = 0;
1779                 ztk->in_position = 0;
1780                 file->position = 0;
1781                 lseek (file->handle, file->offset, SEEK_SET);
1782
1783                 // Reset the Zlib stream
1784                 ztk->zstream.next_in = ztk->input;
1785                 ztk->zstream.avail_in = 0;
1786                 qz_inflateReset (&ztk->zstream);
1787         }
1788
1789         // We need a big buffer to force inflating into it directly
1790         buffersize = 2 * sizeof (file->buff);
1791         buffer = Mem_Alloc (tempmempool, buffersize);
1792
1793         // Skip all data until we reach the requested offset
1794         while ((size_t)offset > file->position)
1795         {
1796                 size_t diff = offset - file->position;
1797                 size_t count, len;
1798
1799                 count = (diff > buffersize) ? buffersize : diff;
1800                 len = FS_Read (file, buffer, count);
1801                 if (len != count)
1802                 {
1803                         Mem_Free (buffer);
1804                         return -1;
1805                 }
1806         }
1807
1808         Mem_Free (buffer);
1809         return 0;
1810 }
1811
1812
1813 /*
1814 ====================
1815 FS_Tell
1816
1817 Give the current position in a file
1818 ====================
1819 */
1820 long FS_Tell (qfile_t* file)
1821 {
1822         return file->position - file->buff_len + file->buff_ind;
1823 }
1824
1825
1826 /*
1827 ====================
1828 FS_Purge
1829
1830 Erases any buffered input or output data
1831 ====================
1832 */
1833 void FS_Purge (qfile_t* file)
1834 {
1835         file->buff_len = 0;
1836         file->buff_ind = 0;
1837         file->ungetc = EOF;
1838 }
1839
1840
1841 /*
1842 ============
1843 FS_LoadFile
1844
1845 Filename are relative to the quake directory.
1846 Always appends a 0 byte.
1847 ============
1848 */
1849 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1850 {
1851         qfile_t *file;
1852         qbyte *buf;
1853
1854         file = FS_Open (path, "rb", quiet, false);
1855         if (!file)
1856                 return NULL;
1857
1858         buf = Mem_Alloc (pool, fs_filesize + 1);
1859         buf[fs_filesize] = '\0';
1860
1861         FS_Read (file, buf, fs_filesize);
1862         FS_Close (file);
1863
1864         return buf;
1865 }
1866
1867
1868 /*
1869 ============
1870 FS_WriteFile
1871
1872 The filename will be prefixed by the current game directory
1873 ============
1874 */
1875 qboolean FS_WriteFile (const char *filename, void *data, int len)
1876 {
1877         qfile_t *file;
1878
1879         file = FS_Open (filename, "wb", false, false);
1880         if (!file)
1881         {
1882                 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1883                 return false;
1884         }
1885
1886         Con_DPrintf("FS_WriteFile: %s\n", filename);
1887         FS_Write (file, data, len);
1888         FS_Close (file);
1889         return true;
1890 }
1891
1892
1893 /*
1894 =============================================================================
1895
1896 OTHERS PUBLIC FUNCTIONS
1897
1898 =============================================================================
1899 */
1900
1901 /*
1902 ============
1903 FS_StripExtension
1904 ============
1905 */
1906 void FS_StripExtension (const char *in, char *out, size_t size_out)
1907 {
1908         char *last = NULL;
1909
1910         if (size_out == 0)
1911                 return;
1912
1913         while (*in && size_out > 1)
1914         {
1915                 if (*in == '.')
1916                         last = out;
1917                 else if (*in == '/' || *in == '\\' || *in == ':')
1918                         last = NULL;
1919                 *out++ = *in++;
1920                 size_out--;
1921         }
1922         if (last)
1923                 *last = 0;
1924         else
1925                 *out = 0;
1926 }
1927
1928
1929 /*
1930 ==================
1931 FS_DefaultExtension
1932 ==================
1933 */
1934 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1935 {
1936         const char *src;
1937
1938         // if path doesn't have a .EXT, append extension
1939         // (extension should include the .)
1940         src = path + strlen(path) - 1;
1941
1942         while (*src != '/' && src != path)
1943         {
1944                 if (*src == '.')
1945                         return;                 // it has an extension
1946                 src--;
1947         }
1948
1949         strlcat (path, extension, size_path);
1950 }
1951
1952
1953 /*
1954 ==================
1955 FS_FileExists
1956
1957 Look for a file in the packages and in the filesystem
1958 ==================
1959 */
1960 qboolean FS_FileExists (const char *filename)
1961 {
1962         return (FS_FindFile (filename, NULL, true) != NULL);
1963 }
1964
1965
1966 /*
1967 ==================
1968 FS_SysFileExists
1969
1970 Look for a file in the filesystem only
1971 ==================
1972 */
1973 qboolean FS_SysFileExists (const char *path)
1974 {
1975 #if WIN32
1976         int desc;
1977
1978         // TODO: use another function instead, to avoid opening the file
1979         desc = open (path, O_RDONLY | O_BINARY);
1980         if (desc < 0)
1981                 return false;
1982
1983         close (desc);
1984         return true;
1985 #else
1986         struct stat buf;
1987
1988         if (stat (path,&buf) == -1)
1989                 return false;
1990
1991         return true;
1992 #endif
1993 }
1994
1995 void FS_mkdir (const char *path)
1996 {
1997 #if WIN32
1998         _mkdir (path);
1999 #else
2000         mkdir (path, 0777);
2001 #endif
2002 }
2003
2004 /*
2005 ===========
2006 FS_Search
2007
2008 Allocate and fill a search structure with information on matching filenames.
2009 ===========
2010 */
2011 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2012 {
2013         fssearch_t *search;
2014         searchpath_t *searchpath;
2015         pack_t *pak;
2016         int i, basepathlength, numfiles, numchars;
2017         stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
2018         const char *slash, *backslash, *colon, *separator;
2019         char *basepath;
2020         char netpath[MAX_OSPATH];
2021         char temp[MAX_OSPATH];
2022
2023         while(!strncmp(pattern, "./", 2))
2024                 pattern += 2;
2025         while(!strncmp(pattern, ".\\", 2))
2026                 pattern += 2;
2027
2028         search = NULL;
2029         liststart = NULL;
2030         listcurrent = NULL;
2031         listtemp = NULL;
2032         slash = strrchr(pattern, '/');
2033         backslash = strrchr(pattern, '\\');
2034         colon = strrchr(pattern, ':');
2035         separator = pattern;
2036         if (separator < slash)
2037                 separator = slash;
2038         if (separator < backslash)
2039                 separator = backslash;
2040         if (separator < colon)
2041                 separator = colon;
2042         basepathlength = separator - pattern;
2043         basepath = Mem_Alloc (tempmempool, basepathlength + 1);
2044         if (basepathlength)
2045                 memcpy(basepath, pattern, basepathlength);
2046         basepath[basepathlength] = 0;
2047
2048         // search through the path, one element at a time
2049         for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2050         {
2051                 // is the element a pak file?
2052                 if (searchpath->pack)
2053                 {
2054                         // look through all the pak file elements
2055                         pak = searchpath->pack;
2056                         for (i = 0;i < pak->numfiles;i++)
2057                         {
2058                                 strcpy(temp, pak->files[i].name);
2059                                 while (temp[0])
2060                                 {
2061                                         if (matchpattern(temp, (char *)pattern, true))
2062                                         {
2063                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2064                                                         if (!strcmp(listtemp->text, temp))
2065                                                                 break;
2066                                                 if (listtemp == NULL)
2067                                                 {
2068                                                         listcurrent = stringlistappend(listcurrent, temp);
2069                                                         if (liststart == NULL)
2070                                                                 liststart = listcurrent;
2071                                                         if (!quiet)
2072                                                                 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2073                                                 }
2074                                         }
2075                                         // strip off one path element at a time until empty
2076                                         // this way directories are added to the listing if they match the pattern
2077                                         slash = strrchr(temp, '/');
2078                                         backslash = strrchr(temp, '\\');
2079                                         colon = strrchr(temp, ':');
2080                                         separator = temp;
2081                                         if (separator < slash)
2082                                                 separator = slash;
2083                                         if (separator < backslash)
2084                                                 separator = backslash;
2085                                         if (separator < colon)
2086                                                 separator = colon;
2087                                         *((char *)separator) = 0;
2088                                 }
2089                         }
2090                 }
2091                 else
2092                 {
2093                         // get a directory listing and look at each name
2094                         dpsnprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
2095                         if ((dir = listdirectory(netpath)))
2096                         {
2097                                 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2098                                 {
2099                                         dpsnprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
2100                                         if (matchpattern(temp, (char *)pattern, true))
2101                                         {
2102                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2103                                                         if (!strcmp(listtemp->text, temp))
2104                                                                 break;
2105                                                 if (listtemp == NULL)
2106                                                 {
2107                                                         listcurrent = stringlistappend(listcurrent, temp);
2108                                                         if (liststart == NULL)
2109                                                                 liststart = listcurrent;
2110                                                         if (!quiet)
2111                                                                 Con_DPrintf("SearchDirFile: %s\n", temp);
2112                                                 }
2113                                         }
2114                                 }
2115                                 freedirectory(dir);
2116                         }
2117                 }
2118         }
2119
2120         if (liststart)
2121         {
2122                 liststart = stringlistsort(liststart);
2123                 numfiles = 0;
2124                 numchars = 0;
2125                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2126                 {
2127                         numfiles++;
2128                         numchars += strlen(listtemp->text) + 1;
2129                 }
2130                 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2131                 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2132                 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2133                 search->numfilenames = numfiles;
2134                 numfiles = 0;
2135                 numchars = 0;
2136                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2137                 {
2138                         search->filenames[numfiles] = search->filenamesbuffer + numchars;
2139                         strcpy(search->filenames[numfiles], listtemp->text);
2140                         numfiles++;
2141                         numchars += strlen(listtemp->text) + 1;
2142                 }
2143                 if (liststart)
2144                         stringlistfree(liststart);
2145         }
2146
2147         Mem_Free(basepath);
2148         return search;
2149 }
2150
2151 void FS_FreeSearch(fssearch_t *search)
2152 {
2153         Z_Free(search);
2154 }
2155
2156 extern int con_linewidth;
2157 int FS_ListDirectory(const char *pattern, int oneperline)
2158 {
2159         int numfiles;
2160         int numcolumns;
2161         int numlines;
2162         int columnwidth;
2163         int linebufpos;
2164         int i, j, k, l;
2165         const char *name;
2166         char linebuf[4096];
2167         fssearch_t *search;
2168         search = FS_Search(pattern, true, true);
2169         if (!search)
2170                 return 0;
2171         numfiles = search->numfilenames;
2172         if (!oneperline)
2173         {
2174                 // FIXME: the names could be added to one column list and then
2175                 // gradually shifted into the next column if they fit, and then the
2176                 // next to make a compact variable width listing but it's a lot more
2177                 // complicated...
2178                 // find width for columns
2179                 columnwidth = 0;
2180                 for (i = 0;i < numfiles;i++)
2181                 {
2182                         l = strlen(search->filenames[i]);
2183                         if (columnwidth < l)
2184                                 columnwidth = l;
2185                 }
2186                 // count the spacing character
2187                 columnwidth++;
2188                 // calculate number of columns
2189                 numcolumns = con_linewidth / columnwidth;
2190                 // don't bother with the column printing if it's only one column
2191                 if (numcolumns >= 2)
2192                 {
2193                         numlines = (numfiles + numcolumns - 1) / numcolumns;
2194                         for (i = 0;i < numlines;i++)
2195                         {
2196                                 linebufpos = 0;
2197                                 for (k = 0;k < numcolumns;k++)
2198                                 {
2199                                         l = i * numcolumns + k;
2200                                         if (l < numfiles)
2201                                         {
2202                                                 name = search->filenames[l];
2203                                                 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2204                                                         linebuf[linebufpos++] = name[j];
2205                                                 // space out name unless it's the last on the line
2206                                                 if (k < (numcolumns - 1) && l < (numfiles - 1))
2207                                                         for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2208                                                                 linebuf[linebufpos++] = ' ';
2209                                         }
2210                                 }
2211                                 linebuf[linebufpos] = 0;
2212                                 Con_Printf("%s\n", linebuf);
2213                         }
2214                 }
2215                 else
2216                         oneperline = true;
2217         }
2218         if (oneperline)
2219                 for (i = 0;i < numfiles;i++)
2220                         Con_Printf("%s\n", search->filenames[i]);
2221         FS_FreeSearch(search);
2222         return numfiles;
2223 }
2224
2225 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2226 {
2227         const char *pattern;
2228         if (Cmd_Argc() > 3)
2229         {
2230                 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2231                 return;
2232         }
2233         if (Cmd_Argc() == 2)
2234                 pattern = Cmd_Argv(1);
2235         else
2236                 pattern = "*";
2237         if (!FS_ListDirectory(pattern, oneperline))
2238                 Con_Print("No files found.\n");
2239 }
2240
2241 void FS_Dir_f(void)
2242 {
2243         FS_ListDirectoryCmd("dir", true);
2244 }
2245
2246 void FS_Ls_f(void)
2247 {
2248         FS_ListDirectoryCmd("ls", false);
2249 }
2250