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