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