]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - fs.c
2f00c1ea34ba7a5bddfea17ec6f3d8ef974376d4
[xonotic/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                                                 Sys_Printf("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                                         Sys_Printf("FS_FindFile: %s\n", netpath);
1190
1191                                 if (index != NULL)
1192                                         *index = -1;
1193                                 return search;
1194                         }
1195                 }
1196         }
1197
1198         if (!quiet)
1199                 Sys_Printf("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                 return lseek (file->stream, offset, whence);
1631 #else
1632                 return fseek (file->stream, offset, whence);
1633 #endif
1634
1635         // Seeking in compressed files is more a hack than anything else,
1636         // but we need to support it, so here it is.
1637         if (file->flags & FS_FLAG_DEFLATED)
1638         {
1639                 ztoolkit_t *ztk = file->z;
1640                 qbyte buffer [sizeof (ztk->output)];  // it's big to force inflating into buffer directly
1641
1642                 switch (whence)
1643                 {
1644                         case SEEK_CUR:
1645                                 offset += file->position;
1646                                 break;
1647
1648                         case SEEK_SET:
1649                                 break;
1650
1651                         case SEEK_END:
1652                                 offset += ztk->real_length;
1653                                 break;
1654
1655                         default:
1656                                 return -1;
1657                 }
1658                 if (offset < 0 || offset > (long) ztk->real_length)
1659                         return -1;
1660
1661                 // If we need to go back in the file
1662                 if (offset <= (long) file->position)
1663                 {
1664                         // If we still have the data we need in the output buffer
1665                         if (file->position - offset <= ztk->out_ind)
1666                         {
1667                                 ztk->out_ind -= file->position - offset;
1668                                 file->position = offset;
1669                                 return 0;
1670                         }
1671
1672                         // Else, we restart from the beginning of the file
1673                         ztk->in_ind = 0;
1674                         ztk->in_max = 0;
1675                         ztk->in_position = 0;
1676                         ztk->out_ind = 0;
1677                         ztk->out_max = 0;
1678                         ztk->out_position = 0;
1679                         file->position = 0;
1680 #ifdef FS_USESYSCALLS
1681                         lseek (file->stream, file->offset, SEEK_SET);
1682 #else
1683                         fseek (file->stream, file->offset, SEEK_SET);
1684 #endif
1685
1686                         // Reset the Zlib stream
1687                         ztk->zstream.next_in = ztk->input;
1688                         ztk->zstream.avail_in = 0;
1689                         qz_inflateReset (&ztk->zstream);
1690                 }
1691
1692                 // Skip all data until we reach the requested offset
1693                 while ((long) file->position < offset)
1694                 {
1695                         size_t diff = offset - file->position;
1696                         size_t count, len;
1697
1698                         count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1699                         len = FS_Read (file, buffer, count);
1700                         if (len != count)
1701                                 return -1;
1702                 }
1703
1704                 return 0;
1705         }
1706
1707         // Packed files receive a special treatment too, because
1708         // we need to make sure it doesn't go outside of the file
1709         switch (whence)
1710         {
1711                 case SEEK_CUR:
1712                         offset += file->position;
1713                         break;
1714
1715                 case SEEK_SET:
1716                         break;
1717
1718                 case SEEK_END:
1719                         offset += file->length;
1720                         break;
1721
1722                 default:
1723                         return -1;
1724         }
1725         if (offset < 0 || offset > (long) file->length)
1726                 return -1;
1727
1728 #ifdef FS_USESYSCALLS
1729         if (lseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1730                 return -1;
1731 #else
1732         if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1733                 return -1;
1734 #endif
1735         file->position = offset;
1736         return 0;
1737 }
1738
1739
1740 /*
1741 ====================
1742 FS_Tell
1743
1744 Give the current position in a file
1745 ====================
1746 */
1747 long FS_Tell (qfile_t* file)
1748 {
1749         if (file->flags & FS_FLAG_PACKED)
1750                 return file->position;
1751
1752 #ifdef FS_USESYSCALLS
1753         return lseek (file->stream, 0, SEEK_CUR);
1754 #else
1755         return ftell (file->stream);
1756 #endif
1757 }
1758
1759
1760 /*
1761 ====================
1762 FS_Gets
1763
1764 Extract a line from a file
1765 ====================
1766 */
1767 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1768 {
1769         size_t ind;
1770
1771         // Quick path for unpacked files
1772 #ifndef FS_USESYSCALLS
1773         if (! (file->flags & FS_FLAG_PACKED))
1774                 return fgets (buffer, buffersize, file->stream);
1775 #endif
1776
1777         for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1778         {
1779                 int c = FS_Getc (file);
1780                 switch (c)
1781                 {
1782                         // End of file
1783                         case -1:
1784                                 if (!ind)
1785                                         return NULL;
1786
1787                                 buffer[ind] = '\0';
1788                                 return buffer;
1789
1790                         // End of line
1791                         case '\r':
1792                         case '\n':
1793                                 buffer[ind] = '\n';
1794                                 buffer[ind + 1] = '\0';
1795                                 return buffer;
1796
1797                         default:
1798                                 buffer[ind] = c;
1799                 }
1800
1801         }
1802
1803         buffer[buffersize - 1] = '\0';
1804         return buffer;
1805 }
1806
1807
1808 /*
1809 ==========
1810 FS_Getline
1811
1812 Dynamic length version of fgets. DO NOT free the buffer.
1813 ==========
1814 */
1815 char *FS_Getline (qfile_t *file)
1816 {
1817         static int  size = 256;
1818         static char *buf = 0;
1819         char        *t;
1820         int         len;
1821
1822         if (!buf)
1823                 buf = Mem_Alloc (fs_mempool, size);
1824
1825         if (!FS_Gets (file, buf, size))
1826                 return 0;
1827
1828         len = strlen (buf);
1829         while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1830         {
1831                 t = Mem_Alloc (fs_mempool, size + 256);
1832                 memcpy(t, buf, size);
1833                 Mem_Free(buf);
1834                 size += 256;
1835                 buf = t;
1836                 if (!FS_Gets (file, buf + len, size - len))
1837                         break;
1838                 len = strlen (buf);
1839         }
1840         while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1841                 buf[len - 1] = 0;
1842         return buf;
1843 }
1844
1845
1846 /*
1847 ====================
1848 FS_Eof
1849
1850 Extract a line from a file
1851 ====================
1852 */
1853 // FIXME: remove this function?
1854 int FS_Eof (qfile_t* file)
1855 {
1856         if (file->flags & FS_FLAG_PACKED)
1857         {
1858                 if (file->flags & FS_FLAG_DEFLATED)
1859                         return (file->position == file->z->real_length);
1860
1861                 return (file->position == file->length);
1862         }
1863
1864 #ifdef FS_USESYSCALLS
1865         Sys_Error("FS_Eof: not implemented using syscalls\n");
1866         return false;
1867 #else
1868         return feof (file->stream);
1869 #endif
1870 }
1871
1872
1873 /*
1874 ============
1875 FS_LoadFile
1876
1877 Filename are relative to the quake directory.
1878 Always appends a 0 byte.
1879 ============
1880 */
1881 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1882 {
1883         qfile_t *h;
1884         qbyte *buf;
1885
1886         // look for it in the filesystem or pack files
1887         h = FS_Open (path, "rb", quiet);
1888         if (!h)
1889                 return NULL;
1890
1891         buf = Mem_Alloc(pool, fs_filesize+1);
1892         if (!buf)
1893                 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1894
1895         ((qbyte *)buf)[fs_filesize] = 0;
1896
1897         FS_Read (h, buf, fs_filesize);
1898         FS_Close (h);
1899
1900         return buf;
1901 }
1902
1903
1904 /*
1905 ============
1906 FS_WriteFile
1907
1908 The filename will be prefixed by the current game directory
1909 ============
1910 */
1911 qboolean FS_WriteFile (const char *filename, void *data, int len)
1912 {
1913         qfile_t *handle;
1914
1915         handle = FS_Open (filename, "wb", false);
1916         if (!handle)
1917         {
1918                 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1919                 return false;
1920         }
1921
1922         Con_DPrintf("FS_WriteFile: %s\n", filename);
1923         FS_Write (handle, data, len);
1924         FS_Close (handle);
1925         return true;
1926 }
1927
1928
1929 /*
1930 =============================================================================
1931
1932 OTHERS PUBLIC FUNCTIONS
1933
1934 =============================================================================
1935 */
1936
1937 /*
1938 ============
1939 FS_StripExtension
1940 ============
1941 */
1942 void FS_StripExtension (const char *in, char *out, size_t size_out)
1943 {
1944         char *last = NULL;
1945
1946         if (size_out == 0)
1947                 return;
1948
1949         while (*in && size_out > 1)
1950         {
1951                 if (*in == '.')
1952                         last = out;
1953                 else if (*in == '/' || *in == '\\' || *in == ':')
1954                         last = NULL;
1955                 *out++ = *in++;
1956                 size_out--;
1957         }
1958         if (last)
1959                 *last = 0;
1960         else
1961                 *out = 0;
1962 }
1963
1964
1965 /*
1966 ==================
1967 FS_DefaultExtension
1968 ==================
1969 */
1970 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1971 {
1972         const char *src;
1973
1974         // if path doesn't have a .EXT, append extension
1975         // (extension should include the .)
1976         src = path + strlen(path) - 1;
1977
1978         while (*src != '/' && src != path)
1979         {
1980                 if (*src == '.')
1981                         return;                 // it has an extension
1982                 src--;
1983         }
1984
1985         strlcat (path, extension, size_path);
1986 }
1987
1988
1989 /*
1990 ==================
1991 FS_FileExists
1992
1993 Look for a file in the packages and in the filesystem
1994 ==================
1995 */
1996 qboolean FS_FileExists (const char *filename)
1997 {
1998         return (FS_FindFile (filename, NULL, true) != NULL);
1999 }
2000
2001
2002 /*
2003 ==================
2004 FS_SysFileExists
2005
2006 Look for a file in the filesystem only
2007 ==================
2008 */
2009 qboolean FS_SysFileExists (const char *path)
2010 {
2011 #if WIN32
2012         FILE *f;
2013
2014         f = fopen (path, "rb");
2015         if (f)
2016         {
2017                 fclose (f);
2018                 return true;
2019         }
2020
2021         return false;
2022 #else
2023         struct stat buf;
2024
2025         if (stat (path,&buf) == -1)
2026                 return false;
2027
2028         return true;
2029 #endif
2030 }
2031
2032 void FS_mkdir (const char *path)
2033 {
2034 #if WIN32
2035         _mkdir (path);
2036 #else
2037         mkdir (path, 0777);
2038 #endif
2039 }
2040
2041 /*
2042 ===========
2043 FS_Search
2044
2045 Allocate and fill a search structure with information on matching filenames.
2046 ===========
2047 */
2048 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2049 {
2050         fssearch_t *search;
2051         searchpath_t *searchpath;
2052         pack_t *pak;
2053         int i, basepathlength, numfiles, numchars;
2054         stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
2055         const char *slash, *backslash, *colon, *separator;
2056         char *basepath;
2057         char netpath[MAX_OSPATH];
2058         char temp[MAX_OSPATH];
2059
2060         while(!strncmp(pattern, "./", 2))
2061                 pattern += 2;
2062         while(!strncmp(pattern, ".\\", 2))
2063                 pattern += 2;
2064
2065         search = NULL;
2066         liststart = NULL;
2067         listcurrent = NULL;
2068         listtemp = NULL;
2069         slash = strrchr(pattern, '/');
2070         backslash = strrchr(pattern, '\\');
2071         colon = strrchr(pattern, ':');
2072         separator = pattern;
2073         if (separator < slash)
2074                 separator = slash;
2075         if (separator < backslash)
2076                 separator = backslash;
2077         if (separator < colon)
2078                 separator = colon;
2079         basepathlength = separator - pattern;
2080         basepath = Mem_Alloc (tempmempool, basepathlength + 1);
2081         if (basepathlength)
2082                 memcpy(basepath, pattern, basepathlength);
2083         basepath[basepathlength] = 0;
2084
2085         // search through the path, one element at a time
2086         for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2087         {
2088                 // is the element a pak file?
2089                 if (searchpath->pack)
2090                 {
2091                         // look through all the pak file elements
2092                         pak = searchpath->pack;
2093                         for (i = 0;i < pak->numfiles;i++)
2094                         {
2095                                 strcpy(temp, pak->files[i].name);
2096                                 while (temp[0])
2097                                 {
2098                                         if (matchpattern(temp, (char *)pattern, true))
2099                                         {
2100                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2101                                                         if (!strcmp(listtemp->text, temp))
2102                                                                 break;
2103                                                 if (listtemp == NULL)
2104                                                 {
2105                                                         listcurrent = stringlistappend(listcurrent, temp);
2106                                                         if (liststart == NULL)
2107                                                                 liststart = listcurrent;
2108                                                         if (!quiet)
2109                                                                 Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2110                                                 }
2111                                         }
2112                                         // strip off one path element at a time until empty
2113                                         // this way directories are added to the listing if they match the pattern
2114                                         slash = strrchr(temp, '/');
2115                                         backslash = strrchr(temp, '\\');
2116                                         colon = strrchr(temp, ':');
2117                                         separator = temp;
2118                                         if (separator < slash)
2119                                                 separator = slash;
2120                                         if (separator < backslash)
2121                                                 separator = backslash;
2122                                         if (separator < colon)
2123                                                 separator = colon;
2124                                         *((char *)separator) = 0;
2125                                 }
2126                         }
2127                 }
2128                 else
2129                 {
2130                         // get a directory listing and look at each name
2131                         snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
2132                         if ((dir = listdirectory(netpath)))
2133                         {
2134                                 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2135                                 {
2136                                         snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
2137                                         if (matchpattern(temp, (char *)pattern, true))
2138                                         {
2139                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2140                                                         if (!strcmp(listtemp->text, temp))
2141                                                                 break;
2142                                                 if (listtemp == NULL)
2143                                                 {
2144                                                         listcurrent = stringlistappend(listcurrent, temp);
2145                                                         if (liststart == NULL)
2146                                                                 liststart = listcurrent;
2147                                                         if (!quiet)
2148                                                                 Sys_Printf("SearchDirFile: %s\n", temp);
2149                                                 }
2150                                         }
2151                                 }
2152                                 freedirectory(dir);
2153                         }
2154                 }
2155         }
2156
2157         if (liststart)
2158         {
2159                 liststart = stringlistsort(liststart);
2160                 numfiles = 0;
2161                 numchars = 0;
2162                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2163                 {
2164                         numfiles++;
2165                         numchars += strlen(listtemp->text) + 1;
2166                 }
2167                 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2168                 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2169                 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2170                 search->numfilenames = numfiles;
2171                 numfiles = 0;
2172                 numchars = 0;
2173                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2174                 {
2175                         search->filenames[numfiles] = search->filenamesbuffer + numchars;
2176                         strcpy(search->filenames[numfiles], listtemp->text);
2177                         numfiles++;
2178                         numchars += strlen(listtemp->text) + 1;
2179                 }
2180                 if (liststart)
2181                         stringlistfree(liststart);
2182         }
2183
2184         Mem_Free(basepath);
2185         return search;
2186 }
2187
2188 void FS_FreeSearch(fssearch_t *search)
2189 {
2190         Z_Free(search);
2191 }
2192
2193 extern int con_linewidth;
2194 int FS_ListDirectory(const char *pattern, int oneperline)
2195 {
2196         int numfiles;
2197         int numcolumns;
2198         int numlines;
2199         int columnwidth;
2200         int linebufpos;
2201         int i, j, k, l;
2202         const char *name;
2203         char linebuf[4096];
2204         fssearch_t *search;
2205         search = FS_Search(pattern, true, true);
2206         if (!search)
2207                 return 0;
2208         numfiles = search->numfilenames;
2209         if (!oneperline)
2210         {
2211                 // FIXME: the names could be added to one column list and then
2212                 // gradually shifted into the next column if they fit, and then the
2213                 // next to make a compact variable width listing but it's a lot more
2214                 // complicated...
2215                 // find width for columns
2216                 columnwidth = 0;
2217                 for (i = 0;i < numfiles;i++)
2218                 {
2219                         l = strlen(search->filenames[i]);
2220                         if (columnwidth < l)
2221                                 columnwidth = l;
2222                 }
2223                 // count the spacing character
2224                 columnwidth++;
2225                 // calculate number of columns
2226                 numcolumns = con_linewidth / columnwidth;
2227                 // don't bother with the column printing if it's only one column
2228                 if (numcolumns >= 2)
2229                 {
2230                         numlines = (numfiles + numcolumns - 1) / numcolumns;
2231                         for (i = 0;i < numlines;i++)
2232                         {
2233                                 linebufpos = 0;
2234                                 for (k = 0;k < numcolumns;k++)
2235                                 {
2236                                         l = i * numcolumns + k;
2237                                         if (l < numfiles)
2238                                         {
2239                                                 name = search->filenames[l];
2240                                                 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2241                                                         linebuf[linebufpos++] = name[j];
2242                                                 // space out name unless it's the last on the line
2243                                                 if (k < (numcolumns - 1) && l < (numfiles - 1))
2244                                                         for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2245                                                                 linebuf[linebufpos++] = ' ';
2246                                         }
2247                                 }
2248                                 linebuf[linebufpos] = 0;
2249                                 Con_Printf("%s\n", linebuf);
2250                         }
2251                 }
2252                 else
2253                         oneperline = true;
2254         }
2255         if (oneperline)
2256                 for (i = 0;i < numfiles;i++)
2257                         Con_Printf("%s\n", search->filenames[i]);
2258         FS_FreeSearch(search);
2259         return numfiles;
2260 }
2261
2262 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2263 {
2264         const char *pattern;
2265         if (Cmd_Argc() > 3)
2266         {
2267                 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2268                 return;
2269         }
2270         if (Cmd_Argc() == 2)
2271                 pattern = Cmd_Argv(1);
2272         else
2273                 pattern = "*";
2274         if (!FS_ListDirectory(pattern, oneperline))
2275                 Con_Print("No files found.\n");
2276 }
2277
2278 void FS_Dir_f(void)
2279 {
2280         FS_ListDirectoryCmd("dir", true);
2281 }
2282
2283 void FS_Ls_f(void)
2284 {
2285         FS_ListDirectoryCmd("ls", false);
2286 }
2287