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