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