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