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