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