try to detect a .app package in MacOSX and use a basedir outside the .app
[xonotic/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                 strcpy(fs_basedir, "../../..");
912 #endif
913
914         PK3_OpenLibrary ();
915
916         // -basedir <path>
917         // Overrides the system supplied base directory (under GAMENAME)
918 // 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)
919         i = COM_CheckParm ("-basedir");
920         if (i && i < com_argc-1)
921         {
922                 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
923                 i = strlen (fs_basedir);
924                 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
925                         fs_basedir[i-1] = 0;
926         }
927
928         // -path <dir or packfile> [<dir or packfile>] ...
929         // Fully specifies the exact search path, overriding the generated one
930 // 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)
931         i = COM_CheckParm ("-path");
932         if (i)
933         {
934                 fs_modified = true;
935                 while (++i < com_argc)
936                 {
937                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
938                                 break;
939
940                         search = Mem_Alloc(fs_mempool, sizeof(searchpath_t));
941                         if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
942                         {
943                                 search->pack = FS_LoadPackPAK (com_argv[i]);
944                                 if (!search->pack)
945                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
946                         }
947                         else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
948                         {
949                                 search->pack = FS_LoadPackPK3 (com_argv[i]);
950                                 if (!search->pack)
951                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
952                         }
953                         else
954                                 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
955                         search->next = fs_searchpaths;
956                         fs_searchpaths = search;
957                 }
958                 return;
959         }
960
961         // add the game-specific paths
962         // gamedirname1 (typically id1)
963         FS_AddGameHierarchy (gamedirname1);
964
965         // add the game-specific path, if any
966         if (gamedirname2)
967         {
968                 fs_modified = true;
969                 FS_AddGameHierarchy (gamedirname2);
970         }
971
972         // set the com_modname (reported in server info)
973         strlcpy(com_modname, gamedirname1, sizeof(com_modname));
974
975         // -game <gamedir>
976         // Adds basedir/gamedir as an override game
977         // LordHavoc: now supports multiple -game directories
978         for (i = 1;i < com_argc;i++)
979         {
980                 if (!com_argv[i])
981                         continue;
982                 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
983                 {
984                         i++;
985                         fs_modified = true;
986                         FS_AddGameHierarchy (com_argv[i]);
987                         // update the com_modname
988                         strlcpy (com_modname, com_argv[i], sizeof (com_modname));
989                 }
990         }
991
992         // If "-condebug" is in the command line, remove the previous log file
993         if (COM_CheckParm ("-condebug") != 0)
994                 unlink (va("%s/qconsole.log", fs_gamedir));
995 }
996
997 void FS_Init_Commands(void)
998 {
999         Cvar_RegisterVariable (&scr_screenshot_name);
1000
1001         Cmd_AddCommand ("path", FS_Path_f);
1002         Cmd_AddCommand ("dir", FS_Dir_f);
1003         Cmd_AddCommand ("ls", FS_Ls_f);
1004
1005         // set the default screenshot name to either the mod name or the
1006         // gamemode screenshot name
1007         if (fs_modified)
1008                 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1009         else
1010                 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1011 }
1012
1013 /*
1014 ================
1015 FS_Shutdown
1016 ================
1017 */
1018 void FS_Shutdown (void)
1019 {
1020         Mem_FreePool (&fs_mempool);
1021 }
1022
1023 /*
1024 ====================
1025 FS_SysOpen
1026
1027 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1028 ====================
1029 */
1030 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1031 {
1032         qfile_t* file;
1033         int mod, opt;
1034         unsigned int ind;
1035
1036         // Parse the mode string
1037         switch (mode[0])
1038         {
1039                 case 'r':
1040                         mod = O_RDONLY;
1041                         opt = 0;
1042                         break;
1043                 case 'w':
1044                         mod = O_WRONLY;
1045                         opt = O_CREAT | O_TRUNC;
1046                         break;
1047                 case 'a':
1048                         mod = O_WRONLY;
1049                         opt = O_CREAT | O_APPEND;
1050                         break;
1051                 default:
1052                         Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1053                         return NULL;
1054         }
1055         for (ind = 1; mode[ind] != '\0'; ind++)
1056         {
1057                 switch (mode[ind])
1058                 {
1059                         case '+':
1060                                 mod = O_RDWR;
1061                                 break;
1062                         case 'b':
1063                                 opt |= O_BINARY;
1064                                 break;
1065                         default:
1066                                 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1067                                                         filepath, mode, mode[ind]);
1068                 }
1069         }
1070
1071 #ifndef WIN32
1072         if (nonblocking)
1073                 opt |= O_NONBLOCK;
1074 #endif
1075
1076         file = Mem_Alloc (fs_mempool, sizeof (*file));
1077         memset (file, 0, sizeof (*file));
1078         file->ungetc = EOF;
1079
1080         file->handle = open (filepath, mod | opt, 0666);
1081         if (file->handle < 0)
1082         {
1083                 Mem_Free (file);
1084                 return NULL;
1085         }
1086
1087         file->real_length = lseek (file->handle, 0, SEEK_END);
1088
1089         // For files opened in append mode, we start at the end of the file
1090         if (mod & O_APPEND)
1091                 file->position = file->real_length;
1092         else
1093                 lseek (file->handle, 0, SEEK_SET);
1094
1095         return file;
1096 }
1097
1098
1099 /*
1100 ===========
1101 FS_OpenPackedFile
1102
1103 Open a packed file using its package file descriptor
1104 ===========
1105 */
1106 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1107 {
1108         packfile_t *pfile;
1109         int dup_handle;
1110         qfile_t* file;
1111
1112         pfile = &pack->files[pack_ind];
1113
1114         // If we don't have the true offset, get it now
1115         if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1116                 PK3_GetTrueFileOffset (pfile, pack);
1117
1118         // No Zlib DLL = no compressed files
1119         if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1120         {
1121                 Con_Printf("WARNING: can't open the compressed file %s\n"
1122                                         "You need the Zlib DLL to use compressed files\n",
1123                                         pfile->name);
1124                 fs_filesize = -1;
1125                 return NULL;
1126         }
1127
1128         dup_handle = dup (pack->handle);
1129         if (dup_handle < 0)
1130                 Sys_Error ("FS_OpenPackedFile: can't dup package's handle (pack: %s)", pack->filename);
1131
1132         file = Mem_Alloc (fs_mempool, sizeof (*file));
1133         memset (file, 0, sizeof (*file));
1134         file->handle = dup_handle;
1135         file->flags = QFILE_FLAG_PACKED;
1136         file->real_length = pfile->realsize;
1137         file->offset = pfile->offset;
1138         file->position = 0;
1139         file->ungetc = EOF;
1140
1141         if (lseek (file->handle, file->offset, SEEK_SET) == -1)
1142                 Sys_Error ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)",
1143                                         pfile->name, pack->filename, file->offset);
1144
1145         if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1146         {
1147                 ztoolkit_t *ztk;
1148
1149                 file->flags |= QFILE_FLAG_DEFLATED;
1150
1151                 // We need some more variables
1152                 ztk = Mem_Alloc (fs_mempool, sizeof (*ztk));
1153
1154                 ztk->comp_length = pfile->packsize;
1155
1156                 // Initialize zlib stream
1157                 ztk->zstream.next_in = ztk->input;
1158                 ztk->zstream.avail_in = 0;
1159
1160                 /* From Zlib's "unzip.c":
1161                  *
1162                  * windowBits is passed < 0 to tell that there is no zlib header.
1163                  * Note that in this case inflate *requires* an extra "dummy" byte
1164                  * after the compressed stream in order to complete decompression and
1165                  * return Z_STREAM_END.
1166                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1167                  * size of both compressed and uncompressed data
1168                  */
1169                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1170                         Sys_Error ("FS_OpenPackedFile: inflate init error (file: %s)", pfile->name);
1171
1172                 ztk->zstream.next_out = file->buff;
1173                 ztk->zstream.avail_out = sizeof (file->buff);
1174
1175                 file->ztk = ztk;
1176         }
1177
1178         fs_filesize = pfile->realsize;
1179
1180         return file;
1181 }
1182
1183 /*
1184 ====================
1185 FS_CheckNastyPath
1186
1187 Return true if the path should be rejected due to one of the following:
1188 1: path elements that are non-portable
1189 2: path elements that would allow access to files outside the game directory,
1190    or are just not a good idea for a mod to be using.
1191 ====================
1192 */
1193 int FS_CheckNastyPath (const char *path)
1194 {
1195         // Windows: don't allow \ in filenames (windows-only), period.
1196         // (on Windows \ is a directory separator, but / is also supported)
1197         if (strstr(path, "\\"))
1198                 return 1; // non-portable
1199
1200         // Mac: don't allow Mac-only filenames - : is a directory separator
1201         // instead of /, but we rely on / working already, so there's no reason to
1202         // support a Mac-only path
1203         // Amiga and Windows: : tries to go to root of drive
1204         if (strstr(path, ":"))
1205                 return 1; // non-portable attempt to go to root of drive
1206
1207         // Amiga: // is parent directory
1208         if (strstr(path, "//"))
1209                 return 1; // non-portable attempt to go to parent directory
1210
1211         // all: don't allow going to current directory (./) or parent directory (../ or /../)
1212         if (strstr(path, "./"))
1213                 return 2; // attempt to go outside the game directory
1214
1215         // Windows and UNIXes: don't allow absolute paths
1216         if (path[0] == '/')
1217                 return 2; // attempt to go outside the game directory
1218
1219         // after all these checks we're pretty sure it's a / separated filename
1220         // and won't do much if any harm
1221         return false;
1222 }
1223
1224
1225 /*
1226 ====================
1227 FS_FindFile
1228
1229 Look for a file in the packages and in the filesystem
1230
1231 Return the searchpath where the file was found (or NULL)
1232 and the file index in the package if relevant
1233 ====================
1234 */
1235 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1236 {
1237         searchpath_t *search;
1238         pack_t *pak;
1239
1240         // search through the path, one element at a time
1241         for (search = fs_searchpaths;search;search = search->next)
1242         {
1243                 // is the element a pak file?
1244                 if (search->pack)
1245                 {
1246                         int (*strcmp_funct) (const char* str1, const char* str2);
1247                         int left, right, middle;
1248
1249                         pak = search->pack;
1250                         strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1251
1252                         // Look for the file (binary search)
1253                         left = 0;
1254                         right = pak->numfiles - 1;
1255                         while (left <= right)
1256                         {
1257                                 int diff;
1258
1259                                 middle = (left + right) / 2;
1260                                 diff = strcmp_funct (pak->files[middle].name, name);
1261
1262                                 // Found it
1263                                 if (!diff)
1264                                 {
1265                                         if (!quiet)
1266                                                 Con_DPrintf("FS_FindFile: %s in %s\n",
1267                                                                         pak->files[middle].name, pak->filename);
1268
1269                                         if (index != NULL)
1270                                                 *index = middle;
1271                                         return search;
1272                                 }
1273
1274                                 // If we're too far in the list
1275                                 if (diff > 0)
1276                                         right = middle - 1;
1277                                 else
1278                                         left = middle + 1;
1279                         }
1280                 }
1281                 else
1282                 {
1283                         char netpath[MAX_OSPATH];
1284                         dpsnprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1285                         if (FS_SysFileExists (netpath))
1286                         {
1287                                 if (!quiet)
1288                                         Con_DPrintf("FS_FindFile: %s\n", netpath);
1289
1290                                 if (index != NULL)
1291                                         *index = -1;
1292                                 return search;
1293                         }
1294                 }
1295         }
1296
1297         if (!quiet)
1298                 Con_DPrintf("FS_FindFile: can't find %s\n", name);
1299
1300         if (index != NULL)
1301                 *index = -1;
1302         return NULL;
1303 }
1304
1305
1306 /*
1307 ===========
1308 FS_OpenReadFile
1309
1310 Look for a file in the search paths and open it in read-only mode
1311
1312 Sets fs_filesize
1313 ===========
1314 */
1315 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1316 {
1317         searchpath_t *search;
1318         int pack_ind;
1319
1320         search = FS_FindFile (filename, &pack_ind, quiet);
1321
1322         // Not found?
1323         if (search == NULL)
1324         {
1325                 fs_filesize = -1;
1326                 return NULL;
1327         }
1328
1329         // Found in the filesystem?
1330         if (pack_ind < 0)
1331         {
1332                 char path [MAX_OSPATH];
1333                 dpsnprintf (path, sizeof (path), "%s/%s", search->filename, filename);
1334                 return FS_SysOpen (path, "rb", nonblocking);
1335         }
1336
1337         // So, we found it in a package...
1338         return FS_OpenPackedFile (search->pack, pack_ind);
1339 }
1340
1341
1342 /*
1343 =============================================================================
1344
1345 MAIN PUBLIC FUNCTIONS
1346
1347 =============================================================================
1348 */
1349
1350 /*
1351 ====================
1352 FS_Open
1353
1354 Open a file. The syntax is the same as fopen
1355 ====================
1356 */
1357 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1358 {
1359         qfile_t* file;
1360
1361         if (FS_CheckNastyPath(filepath))
1362         {
1363                 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1364                 return NULL;
1365         }
1366
1367         // If the file is opened in "write", "append", or "read/write" mode
1368         if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1369         {
1370                 char real_path [MAX_OSPATH];
1371
1372                 // Open the file on disk directly
1373                 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1374
1375                 // Create directories up to the file
1376                 FS_CreatePath (real_path);
1377
1378                 return FS_SysOpen (real_path, mode, nonblocking);
1379         }
1380
1381         // Else, we look at the various search paths and open the file in read-only mode
1382         file = FS_OpenReadFile (filepath, quiet, nonblocking);
1383         if (file != NULL)
1384                 fs_filesize = file->real_length;
1385
1386         return file;
1387 }
1388
1389
1390 /*
1391 ====================
1392 FS_Close
1393
1394 Close a file
1395 ====================
1396 */
1397 int FS_Close (qfile_t* file)
1398 {
1399         if (close (file->handle))
1400                 return EOF;
1401
1402         if (file->ztk)
1403         {
1404                 qz_inflateEnd (&file->ztk->zstream);
1405                 Mem_Free (file->ztk);
1406         }
1407
1408         Mem_Free (file);
1409         return 0;
1410 }
1411
1412
1413 /*
1414 ====================
1415 FS_Write
1416
1417 Write "datasize" bytes into a file
1418 ====================
1419 */
1420 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1421 {
1422         ssize_t result;
1423
1424         // If necessary, seek to the exact file position we're supposed to be
1425         if (file->buff_ind != file->buff_len)
1426                 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1427
1428         // Purge cached data
1429         FS_Purge (file);
1430
1431         // Write the buffer and update the position
1432         result = write (file->handle, data, datasize);
1433         file->position = lseek (file->handle, 0, SEEK_CUR);
1434         if (file->real_length < file->position)
1435                 file->real_length = file->position;
1436
1437         if (result < 0)
1438                 return 0;
1439
1440         return result;
1441 }
1442
1443
1444 /*
1445 ====================
1446 FS_Read
1447
1448 Read up to "buffersize" bytes from a file
1449 ====================
1450 */
1451 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1452 {
1453         size_t count, done;
1454
1455         if (buffersize == 0)
1456                 return 0;
1457
1458         // Get rid of the ungetc character
1459         if (file->ungetc != EOF)
1460         {
1461                 ((char*)buffer)[0] = file->ungetc;
1462                 buffersize--;
1463                 file->ungetc = EOF;
1464                 done = 1;
1465         }
1466         else
1467                 done = 0;
1468
1469         // First, we copy as many bytes as we can from "buff"
1470         if (file->buff_ind < file->buff_len)
1471         {
1472                 count = file->buff_len - file->buff_ind;
1473
1474                 done += (buffersize > count) ? count : buffersize;
1475                 memcpy (buffer, &file->buff[file->buff_ind], done);
1476                 file->buff_ind += done;
1477
1478                 buffersize -= done;
1479                 if (buffersize == 0)
1480                         return done;
1481         }
1482
1483         // NOTE: at this point, the read buffer is always empty
1484
1485         // If the file isn't compressed
1486         if (! (file->flags & QFILE_FLAG_DEFLATED))
1487         {
1488                 int nb;
1489
1490                 // We must take care to not read after the end of the file
1491                 count = file->real_length - file->position;
1492
1493                 // If we have a lot of data to get, put them directly into "buffer"
1494                 if (buffersize > sizeof (file->buff) / 2)
1495                 {
1496                         if (count > buffersize)
1497                                 count = buffersize;
1498                         lseek (file->handle, file->offset + file->position, SEEK_SET);
1499                         nb = read (file->handle, &((qbyte*)buffer)[done], count);
1500                         if (nb > 0)
1501                         {
1502                                 done += nb;
1503                                 file->position += nb;
1504
1505                                 // Purge cached data
1506                                 FS_Purge (file);
1507                         }
1508                 }
1509                 else
1510                 {
1511                         if (count > sizeof (file->buff))
1512                                 count = sizeof (file->buff);
1513                         lseek (file->handle, file->offset + file->position, SEEK_SET);
1514                         nb = read (file->handle, file->buff, count);
1515                         if (nb > 0)
1516                         {
1517                                 file->buff_len = nb;
1518                                 file->position += nb;
1519
1520                                 // Copy the requested data in "buffer" (as much as we can)
1521                                 count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1522                                 memcpy (&((qbyte*)buffer)[done], file->buff, count);
1523                                 file->buff_ind = count;
1524                                 done += count;
1525                         }
1526                 }
1527
1528                 return done;
1529         }
1530
1531         // If the file is compressed, it's more complicated...
1532         // We cycle through a few operations until we have read enough data
1533         while (buffersize > 0)
1534         {
1535                 ztoolkit_t *ztk = file->ztk;
1536                 int error;
1537
1538                 // NOTE: at this point, the read buffer is always empty
1539
1540                 // If "input" is also empty, we need to refill it
1541                 if (ztk->in_ind == ztk->in_len)
1542                 {
1543                         // If we are at the end of the file
1544                         if (file->position == file->real_length)
1545                                 return done;
1546
1547                         count = ztk->comp_length - ztk->in_position;
1548                         if (count > sizeof (ztk->input))
1549                                 count = sizeof (ztk->input);
1550                         lseek (file->handle, file->offset + ztk->in_position, SEEK_SET);
1551                         if (read (file->handle, ztk->input, count) != (ssize_t)count)
1552                                 Sys_Error ("FS_Read: unexpected end of file");
1553
1554                         ztk->in_ind = 0;
1555                         ztk->in_len = count;
1556                         ztk->in_position += count;
1557                 }
1558
1559                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1560                 ztk->zstream.avail_in = ztk->in_len - ztk->in_ind;
1561
1562                 // Now that we are sure we have compressed data available, we need to determine
1563                 // if it's better to inflate it in "file->buff" or directly in "buffer"
1564
1565                 // Inflate the data in "file->buff"
1566                 if (buffersize < sizeof (file->buff) / 2)
1567                 {
1568                         ztk->zstream.next_out = file->buff;
1569                         ztk->zstream.avail_out = sizeof (file->buff);
1570                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1571                         if (error != Z_OK && error != Z_STREAM_END)
1572                                 Sys_Error ("Can't inflate file");
1573                         ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1574
1575                         file->buff_len = sizeof (file->buff) - ztk->zstream.avail_out;
1576                         file->position += file->buff_len;
1577
1578                         // Copy the requested data in "buffer" (as much as we can)
1579                         count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1580                         memcpy (&((qbyte*)buffer)[done], file->buff, count);
1581                         file->buff_ind = count;
1582                 }
1583
1584                 // Else, we inflate directly in "buffer"
1585                 else
1586                 {
1587                         ztk->zstream.next_out = &((qbyte*)buffer)[done];
1588                         ztk->zstream.avail_out = buffersize;
1589                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1590                         if (error != Z_OK && error != Z_STREAM_END)
1591                                 Sys_Error ("Can't inflate file");
1592                         ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1593
1594                         // How much data did it inflate?
1595                         count = buffersize - ztk->zstream.avail_out;
1596                         file->position += count;
1597
1598                         // Purge cached data
1599                         FS_Purge (file);
1600                 }
1601
1602                 done += count;
1603                 buffersize -= count;
1604         }
1605
1606         return done;
1607 }
1608
1609
1610 /*
1611 ====================
1612 FS_Print
1613
1614 Print a string into a file
1615 ====================
1616 */
1617 int FS_Print (qfile_t* file, const char *msg)
1618 {
1619         return FS_Write (file, msg, strlen (msg));
1620 }
1621
1622 /*
1623 ====================
1624 FS_Printf
1625
1626 Print a string into a file
1627 ====================
1628 */
1629 int FS_Printf(qfile_t* file, const char* format, ...)
1630 {
1631         int result;
1632         va_list args;
1633
1634         va_start (args, format);
1635         result = FS_VPrintf (file, format, args);
1636         va_end (args);
1637
1638         return result;
1639 }
1640
1641
1642 /*
1643 ====================
1644 FS_VPrintf
1645
1646 Print a string into a file
1647 ====================
1648 */
1649 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
1650 {
1651         int len;
1652         size_t buff_size;
1653         char *tempbuff = NULL;
1654
1655         buff_size = 1024;
1656         tempbuff = Mem_Alloc (tempmempool, buff_size);
1657         len = dpvsnprintf (tempbuff, buff_size, format, ap);
1658         while (len < 0)
1659         {
1660                 Mem_Free (tempbuff);
1661                 buff_size *= 2;
1662                 tempbuff = Mem_Alloc (tempmempool, buff_size);
1663                 len = dpvsnprintf (tempbuff, buff_size, format, ap);
1664         }
1665
1666         len = write (file->handle, tempbuff, len);
1667         Mem_Free (tempbuff);
1668
1669         return len;
1670 }
1671
1672
1673 /*
1674 ====================
1675 FS_Getc
1676
1677 Get the next character of a file
1678 ====================
1679 */
1680 int FS_Getc (qfile_t* file)
1681 {
1682         char c;
1683
1684         if (FS_Read (file, &c, 1) != 1)
1685                 return EOF;
1686
1687         return c;
1688 }
1689
1690
1691 /*
1692 ====================
1693 FS_UnGetc
1694
1695 Put a character back into the read buffer (only supports one character!)
1696 ====================
1697 */
1698 int FS_UnGetc (qfile_t* file, unsigned char c)
1699 {
1700         // If there's already a character waiting to be read
1701         if (file->ungetc != EOF)
1702                 return EOF;
1703
1704         file->ungetc = c;
1705         return c;
1706 }
1707
1708
1709 /*
1710 ====================
1711 FS_Seek
1712
1713 Move the position index in a file
1714 ====================
1715 */
1716 int FS_Seek (qfile_t* file, long offset, int whence)
1717 {
1718         ztoolkit_t *ztk;
1719         qbyte* buffer;
1720         size_t buffersize;
1721
1722         // Compute the file offset
1723         switch (whence)
1724         {
1725                 case SEEK_CUR:
1726                         offset += file->position - file->buff_len + file->buff_ind;
1727                         break;
1728
1729                 case SEEK_SET:
1730                         break;
1731
1732                 case SEEK_END:
1733                         offset += file->real_length;
1734                         break;
1735
1736                 default:
1737                         return -1;
1738         }
1739         if (offset < 0 || offset > (long) file->real_length)
1740                 return -1;
1741
1742         // If we have the data in our read buffer, we don't need to actually seek
1743         if (file->position - file->buff_len <= (size_t)offset &&
1744                 (size_t)offset <= file->position)
1745         {
1746                 file->buff_ind = offset + file->buff_len - file->position;
1747                 return 0;
1748         }
1749
1750         // Purge cached data
1751         FS_Purge (file);
1752
1753         // Unpacked or uncompressed files can seek directly
1754         if (! (file->flags & QFILE_FLAG_DEFLATED))
1755         {
1756                 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
1757                         return -1;
1758                 file->position = offset;
1759                 return 0;
1760         }
1761
1762         // Seeking in compressed files is more a hack than anything else,
1763         // but we need to support it, so here we go.
1764         ztk = file->ztk;
1765
1766         // If we have to go back in the file, we need to restart from the beginning
1767         if ((size_t)offset <= file->position)
1768         {
1769                 ztk->in_ind = 0;
1770                 ztk->in_len = 0;
1771                 ztk->in_position = 0;
1772                 file->position = 0;
1773                 lseek (file->handle, file->offset, SEEK_SET);
1774
1775                 // Reset the Zlib stream
1776                 ztk->zstream.next_in = ztk->input;
1777                 ztk->zstream.avail_in = 0;
1778                 qz_inflateReset (&ztk->zstream);
1779         }
1780
1781         // We need a big buffer to force inflating into it directly
1782         buffersize = 2 * sizeof (file->buff);
1783         buffer = Mem_Alloc (tempmempool, buffersize);
1784
1785         // Skip all data until we reach the requested offset
1786         while ((size_t)offset > file->position)
1787         {
1788                 size_t diff = offset - file->position;
1789                 size_t count, len;
1790
1791                 count = (diff > buffersize) ? buffersize : diff;
1792                 len = FS_Read (file, buffer, count);
1793                 if (len != count)
1794                 {
1795                         Mem_Free (buffer);
1796                         return -1;
1797                 }
1798         }
1799
1800         Mem_Free (buffer);
1801         return 0;
1802 }
1803
1804
1805 /*
1806 ====================
1807 FS_Tell
1808
1809 Give the current position in a file
1810 ====================
1811 */
1812 long FS_Tell (qfile_t* file)
1813 {
1814         return file->position - file->buff_len + file->buff_ind;
1815 }
1816
1817
1818 /*
1819 ====================
1820 FS_Purge
1821
1822 Erases any buffered input or output data
1823 ====================
1824 */
1825 void FS_Purge (qfile_t* file)
1826 {
1827         file->buff_len = 0;
1828         file->buff_ind = 0;
1829         file->ungetc = EOF;
1830 }
1831
1832
1833 /*
1834 ============
1835 FS_LoadFile
1836
1837 Filename are relative to the quake directory.
1838 Always appends a 0 byte.
1839 ============
1840 */
1841 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1842 {
1843         qfile_t *file;
1844         qbyte *buf;
1845
1846         file = FS_Open (path, "rb", quiet, false);
1847         if (!file)
1848                 return NULL;
1849
1850         buf = Mem_Alloc (pool, fs_filesize + 1);
1851         buf[fs_filesize] = '\0';
1852
1853         FS_Read (file, buf, fs_filesize);
1854         FS_Close (file);
1855
1856         return buf;
1857 }
1858
1859
1860 /*
1861 ============
1862 FS_WriteFile
1863
1864 The filename will be prefixed by the current game directory
1865 ============
1866 */
1867 qboolean FS_WriteFile (const char *filename, void *data, int len)
1868 {
1869         qfile_t *file;
1870
1871         file = FS_Open (filename, "wb", false, false);
1872         if (!file)
1873         {
1874                 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1875                 return false;
1876         }
1877
1878         Con_DPrintf("FS_WriteFile: %s\n", filename);
1879         FS_Write (file, data, len);
1880         FS_Close (file);
1881         return true;
1882 }
1883
1884
1885 /*
1886 =============================================================================
1887
1888 OTHERS PUBLIC FUNCTIONS
1889
1890 =============================================================================
1891 */
1892
1893 /*
1894 ============
1895 FS_StripExtension
1896 ============
1897 */
1898 void FS_StripExtension (const char *in, char *out, size_t size_out)
1899 {
1900         char *last = NULL;
1901
1902         if (size_out == 0)
1903                 return;
1904
1905         while (*in && size_out > 1)
1906         {
1907                 if (*in == '.')
1908                         last = out;
1909                 else if (*in == '/' || *in == '\\' || *in == ':')
1910                         last = NULL;
1911                 *out++ = *in++;
1912                 size_out--;
1913         }
1914         if (last)
1915                 *last = 0;
1916         else
1917                 *out = 0;
1918 }
1919
1920
1921 /*
1922 ==================
1923 FS_DefaultExtension
1924 ==================
1925 */
1926 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1927 {
1928         const char *src;
1929
1930         // if path doesn't have a .EXT, append extension
1931         // (extension should include the .)
1932         src = path + strlen(path) - 1;
1933
1934         while (*src != '/' && src != path)
1935         {
1936                 if (*src == '.')
1937                         return;                 // it has an extension
1938                 src--;
1939         }
1940
1941         strlcat (path, extension, size_path);
1942 }
1943
1944
1945 /*
1946 ==================
1947 FS_FileExists
1948
1949 Look for a file in the packages and in the filesystem
1950 ==================
1951 */
1952 qboolean FS_FileExists (const char *filename)
1953 {
1954         return (FS_FindFile (filename, NULL, true) != NULL);
1955 }
1956
1957
1958 /*
1959 ==================
1960 FS_SysFileExists
1961
1962 Look for a file in the filesystem only
1963 ==================
1964 */
1965 qboolean FS_SysFileExists (const char *path)
1966 {
1967 #if WIN32
1968         int desc;
1969
1970         // TODO: use another function instead, to avoid opening the file
1971         desc = open (path, O_RDONLY | O_BINARY);
1972         if (desc < 0)
1973                 return false;
1974
1975         close (desc);
1976         return true;
1977 #else
1978         struct stat buf;
1979
1980         if (stat (path,&buf) == -1)
1981                 return false;
1982
1983         return true;
1984 #endif
1985 }
1986
1987 void FS_mkdir (const char *path)
1988 {
1989 #if WIN32
1990         _mkdir (path);
1991 #else
1992         mkdir (path, 0777);
1993 #endif
1994 }
1995
1996 /*
1997 ===========
1998 FS_Search
1999
2000 Allocate and fill a search structure with information on matching filenames.
2001 ===========
2002 */
2003 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2004 {
2005         fssearch_t *search;
2006         searchpath_t *searchpath;
2007         pack_t *pak;
2008         int i, basepathlength, numfiles, numchars;
2009         stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
2010         const char *slash, *backslash, *colon, *separator;
2011         char *basepath;
2012         char netpath[MAX_OSPATH];
2013         char temp[MAX_OSPATH];
2014
2015         while(!strncmp(pattern, "./", 2))
2016                 pattern += 2;
2017         while(!strncmp(pattern, ".\\", 2))
2018                 pattern += 2;
2019
2020         search = NULL;
2021         liststart = NULL;
2022         listcurrent = NULL;
2023         listtemp = NULL;
2024         slash = strrchr(pattern, '/');
2025         backslash = strrchr(pattern, '\\');
2026         colon = strrchr(pattern, ':');
2027         separator = pattern;
2028         if (separator < slash)
2029                 separator = slash;
2030         if (separator < backslash)
2031                 separator = backslash;
2032         if (separator < colon)
2033                 separator = colon;
2034         basepathlength = separator - pattern;
2035         basepath = Mem_Alloc (tempmempool, basepathlength + 1);
2036         if (basepathlength)
2037                 memcpy(basepath, pattern, basepathlength);
2038         basepath[basepathlength] = 0;
2039
2040         // search through the path, one element at a time
2041         for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2042         {
2043                 // is the element a pak file?
2044                 if (searchpath->pack)
2045                 {
2046                         // look through all the pak file elements
2047                         pak = searchpath->pack;
2048                         for (i = 0;i < pak->numfiles;i++)
2049                         {
2050                                 strcpy(temp, pak->files[i].name);
2051                                 while (temp[0])
2052                                 {
2053                                         if (matchpattern(temp, (char *)pattern, true))
2054                                         {
2055                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2056                                                         if (!strcmp(listtemp->text, temp))
2057                                                                 break;
2058                                                 if (listtemp == NULL)
2059                                                 {
2060                                                         listcurrent = stringlistappend(listcurrent, temp);
2061                                                         if (liststart == NULL)
2062                                                                 liststart = listcurrent;
2063                                                         if (!quiet)
2064                                                                 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2065                                                 }
2066                                         }
2067                                         // strip off one path element at a time until empty
2068                                         // this way directories are added to the listing if they match the pattern
2069                                         slash = strrchr(temp, '/');
2070                                         backslash = strrchr(temp, '\\');
2071                                         colon = strrchr(temp, ':');
2072                                         separator = temp;
2073                                         if (separator < slash)
2074                                                 separator = slash;
2075                                         if (separator < backslash)
2076                                                 separator = backslash;
2077                                         if (separator < colon)
2078                                                 separator = colon;
2079                                         *((char *)separator) = 0;
2080                                 }
2081                         }
2082                 }
2083                 else
2084                 {
2085                         // get a directory listing and look at each name
2086                         dpsnprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
2087                         if ((dir = listdirectory(netpath)))
2088                         {
2089                                 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2090                                 {
2091                                         dpsnprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
2092                                         if (matchpattern(temp, (char *)pattern, true))
2093                                         {
2094                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2095                                                         if (!strcmp(listtemp->text, temp))
2096                                                                 break;
2097                                                 if (listtemp == NULL)
2098                                                 {
2099                                                         listcurrent = stringlistappend(listcurrent, temp);
2100                                                         if (liststart == NULL)
2101                                                                 liststart = listcurrent;
2102                                                         if (!quiet)
2103                                                                 Con_DPrintf("SearchDirFile: %s\n", temp);
2104                                                 }
2105                                         }
2106                                 }
2107                                 freedirectory(dir);
2108                         }
2109                 }
2110         }
2111
2112         if (liststart)
2113         {
2114                 liststart = stringlistsort(liststart);
2115                 numfiles = 0;
2116                 numchars = 0;
2117                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2118                 {
2119                         numfiles++;
2120                         numchars += strlen(listtemp->text) + 1;
2121                 }
2122                 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2123                 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2124                 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2125                 search->numfilenames = numfiles;
2126                 numfiles = 0;
2127                 numchars = 0;
2128                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2129                 {
2130                         search->filenames[numfiles] = search->filenamesbuffer + numchars;
2131                         strcpy(search->filenames[numfiles], listtemp->text);
2132                         numfiles++;
2133                         numchars += strlen(listtemp->text) + 1;
2134                 }
2135                 if (liststart)
2136                         stringlistfree(liststart);
2137         }
2138
2139         Mem_Free(basepath);
2140         return search;
2141 }
2142
2143 void FS_FreeSearch(fssearch_t *search)
2144 {
2145         Z_Free(search);
2146 }
2147
2148 extern int con_linewidth;
2149 int FS_ListDirectory(const char *pattern, int oneperline)
2150 {
2151         int numfiles;
2152         int numcolumns;
2153         int numlines;
2154         int columnwidth;
2155         int linebufpos;
2156         int i, j, k, l;
2157         const char *name;
2158         char linebuf[4096];
2159         fssearch_t *search;
2160         search = FS_Search(pattern, true, true);
2161         if (!search)
2162                 return 0;
2163         numfiles = search->numfilenames;
2164         if (!oneperline)
2165         {
2166                 // FIXME: the names could be added to one column list and then
2167                 // gradually shifted into the next column if they fit, and then the
2168                 // next to make a compact variable width listing but it's a lot more
2169                 // complicated...
2170                 // find width for columns
2171                 columnwidth = 0;
2172                 for (i = 0;i < numfiles;i++)
2173                 {
2174                         l = strlen(search->filenames[i]);
2175                         if (columnwidth < l)
2176                                 columnwidth = l;
2177                 }
2178                 // count the spacing character
2179                 columnwidth++;
2180                 // calculate number of columns
2181                 numcolumns = con_linewidth / columnwidth;
2182                 // don't bother with the column printing if it's only one column
2183                 if (numcolumns >= 2)
2184                 {
2185                         numlines = (numfiles + numcolumns - 1) / numcolumns;
2186                         for (i = 0;i < numlines;i++)
2187                         {
2188                                 linebufpos = 0;
2189                                 for (k = 0;k < numcolumns;k++)
2190                                 {
2191                                         l = i * numcolumns + k;
2192                                         if (l < numfiles)
2193                                         {
2194                                                 name = search->filenames[l];
2195                                                 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2196                                                         linebuf[linebufpos++] = name[j];
2197                                                 // space out name unless it's the last on the line
2198                                                 if (k < (numcolumns - 1) && l < (numfiles - 1))
2199                                                         for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2200                                                                 linebuf[linebufpos++] = ' ';
2201                                         }
2202                                 }
2203                                 linebuf[linebufpos] = 0;
2204                                 Con_Printf("%s\n", linebuf);
2205                         }
2206                 }
2207                 else
2208                         oneperline = true;
2209         }
2210         if (oneperline)
2211                 for (i = 0;i < numfiles;i++)
2212                         Con_Printf("%s\n", search->filenames[i]);
2213         FS_FreeSearch(search);
2214         return numfiles;
2215 }
2216
2217 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2218 {
2219         const char *pattern;
2220         if (Cmd_Argc() > 3)
2221         {
2222                 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2223                 return;
2224         }
2225         if (Cmd_Argc() == 2)
2226                 pattern = Cmd_Argv(1);
2227         else
2228                 pattern = "*";
2229         if (!FS_ListDirectory(pattern, oneperline))
2230                 Con_Print("No files found.\n");
2231 }
2232
2233 void FS_Dir_f(void)
2234 {
2235         FS_ListDirectoryCmd("dir", true);
2236 }
2237
2238 void FS_Ls_f(void)
2239 {
2240         FS_ListDirectoryCmd("ls", false);
2241 }
2242