]> icculus.org git repositories - divverent/darkplaces.git/blob - fs.c
don't warn about svc_precache failing to precache sounds if snd_initialized is false...
[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         // If "-condebug" is in the command line, remove the previous log file
1036         if (COM_CheckParm ("-condebug") != 0)
1037                 unlink (va("%s/qconsole.log", fs_gamedir));
1038 }
1039
1040
1041 /*
1042 ====================
1043 FS_SysOpen
1044
1045 Internal function used to create a qfile_t and open the relevant file on disk
1046 ====================
1047 */
1048 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
1049 {
1050         qfile_t* file;
1051
1052         file = Mem_Alloc (fs_mempool, sizeof (*file));
1053         memset (file, 0, sizeof (*file));
1054
1055 #ifdef FS_USESYSCALLS
1056         if (strchr(mode, 'r'))
1057                 file->stream = open (filepath, O_RDONLY | O_BINARY);
1058         else if (strchr(mode, 'w'))
1059                 file->stream = open (filepath, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666);
1060         else if (strchr(mode, 'a'))
1061                 file->stream = open (filepath, O_RDWR | O_BINARY | O_CREAT | O_APPEND, 0666);
1062         else
1063                 file->stream = -1;
1064         if (file->stream < 0)
1065         {
1066                 Mem_Free (file);
1067                 return NULL;
1068         }
1069 #else
1070         file->stream = fopen (filepath, mode);
1071         if (!file->stream)
1072         {
1073                 Mem_Free (file);
1074                 return NULL;
1075         }
1076 #endif
1077
1078         return file;
1079 }
1080
1081
1082 /*
1083 ===========
1084 FS_OpenRead
1085 ===========
1086 */
1087 qfile_t *FS_OpenRead (const char *path, int offs, int len)
1088 {
1089         qfile_t* file;
1090
1091         file = FS_SysOpen (path, "rb");
1092         if (!file)
1093         {
1094                 Sys_Error ("Couldn't open %s", path);
1095                 return NULL;
1096         }
1097
1098         // Normal file
1099         if (offs < 0 || len < 0)
1100         {
1101                 // We set fs_filesize here for normal files
1102 #ifdef FS_USESYSCALLS
1103                 fs_filesize = lseek (file->stream, 0, SEEK_END);
1104                 lseek (file->stream, 0, SEEK_SET);
1105 #else
1106                 fseek (file->stream, 0, SEEK_END);
1107                 fs_filesize = ftell (file->stream);
1108                 fseek (file->stream, 0, SEEK_SET);
1109 #endif
1110         }
1111         // Packed file
1112         else
1113         {
1114 #ifdef FS_USESYSCALLS
1115                 lseek (file->stream, offs, SEEK_SET);
1116 #else
1117                 fseek (file->stream, offs, SEEK_SET);
1118 #endif
1119
1120                 file->flags |= FS_FLAG_PACKED;
1121                 file->length = len;
1122                 file->offset = offs;
1123                 file->position = 0;
1124         }
1125
1126         return file;
1127 }
1128
1129
1130 /*
1131 ====================
1132 FS_FindFile
1133
1134 Look for a file in the packages and in the filesystem
1135
1136 Return the searchpath where the file was found (or NULL)
1137 and the file index in the package if relevant
1138 ====================
1139 */
1140 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1141 {
1142         searchpath_t *search;
1143         pack_t *pak;
1144
1145         // search through the path, one element at a time
1146         for (search = fs_searchpaths;search;search = search->next)
1147         {
1148                 // is the element a pak file?
1149                 if (search->pack)
1150                 {
1151                         int (*strcmp_funct) (const char* str1, const char* str2);
1152                         int left, right, middle;
1153
1154                         pak = search->pack;
1155                         strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1156
1157                         // Look for the file (binary search)
1158                         left = 0;
1159                         right = pak->numfiles - 1;
1160                         while (left <= right)
1161                         {
1162                                 int diff;
1163
1164                                 middle = (left + right) / 2;
1165                                 diff = strcmp_funct (pak->files[middle].name, name);
1166
1167                                 // Found it
1168                                 if (!diff)
1169                                 {
1170                                         if (!quiet)
1171                                                 Con_DPrintf("FS_FindFile: %s in %s\n",
1172                                                                         pak->files[middle].name, pak->filename);
1173
1174                                         if (index != NULL)
1175                                                 *index = middle;
1176                                         return search;
1177                                 }
1178
1179                                 // If we're too far in the list
1180                                 if (diff > 0)
1181                                         right = middle - 1;
1182                                 else
1183                                         left = middle + 1;
1184                         }
1185                 }
1186                 else
1187                 {
1188                         char netpath[MAX_OSPATH];
1189                         snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1190                         if (FS_SysFileExists (netpath))
1191                         {
1192                                 if (!quiet)
1193                                         Con_DPrintf("FS_FindFile: %s\n", netpath);
1194
1195                                 if (index != NULL)
1196                                         *index = -1;
1197                                 return search;
1198                         }
1199                 }
1200         }
1201
1202         if (!quiet)
1203                 Con_DPrintf("FS_FindFile: can't find %s\n", name);
1204
1205         if (index != NULL)
1206                 *index = -1;
1207         return NULL;
1208 }
1209
1210
1211 /*
1212 ===========
1213 FS_FOpenFile
1214
1215 If the requested file is inside a packfile, a new qfile_t* will be opened
1216 into the file.
1217
1218 Sets fs_filesize
1219 ===========
1220 */
1221 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
1222 {
1223         searchpath_t *search;
1224         packfile_t *packfile;
1225         int i;
1226         qfile_t *file;
1227
1228         search = FS_FindFile (filename, &i, quiet);
1229
1230         // Not found?
1231         if (search == NULL)
1232         {
1233                 fs_filesize = -1;
1234                 return NULL;
1235         }
1236
1237         // Found in the filesystem?
1238         if (i < 0)
1239         {
1240                 char netpath[MAX_OSPATH];
1241                 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
1242                 return FS_OpenRead(netpath, -1, -1);
1243         }
1244
1245         // So, we found it in a package...
1246         packfile = &search->pack->files[i];
1247
1248         // If we don't have the true offset, get it now
1249         if (! (packfile->flags & FILE_FLAG_TRUEOFFS))
1250                 PK3_GetTrueFileOffset (packfile, search->pack);
1251
1252         // No Zlib DLL = no compressed files
1253         if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED))
1254         {
1255                 Con_Printf("WARNING: can't open the compressed file %s\n"
1256                                         "You need the Zlib DLL to use compressed files\n",
1257                                         filename);
1258                 fs_filesize = -1;
1259                 return NULL;
1260         }
1261
1262         // open a new file in the pakfile
1263         file = FS_OpenRead (search->pack->filename, packfile->offset, packfile->packsize);
1264         fs_filesize = packfile->realsize;
1265
1266         if (packfile->flags & FILE_FLAG_DEFLATED)
1267         {
1268                 ztoolkit_t *ztk;
1269
1270                 file->flags |= FS_FLAG_DEFLATED;
1271
1272                 // We need some more variables
1273                 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1274
1275                 ztk->real_length = packfile->realsize;
1276
1277                 // Initialize zlib stream
1278                 ztk->zstream.next_in = ztk->input;
1279                 ztk->zstream.avail_in = 0;
1280
1281                 /* From Zlib's "unzip.c":
1282                  *
1283                  * windowBits is passed < 0 to tell that there is no zlib header.
1284                  * Note that in this case inflate *requires* an extra "dummy" byte
1285                  * after the compressed stream in order to complete decompression and
1286                  * return Z_STREAM_END.
1287                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1288                  * size of both compressed and uncompressed data
1289                  */
1290                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1291                         Sys_Error ("inflate init error (file: %s)", filename);
1292
1293                 ztk->zstream.next_out = ztk->output;
1294                 ztk->zstream.avail_out = sizeof (ztk->output);
1295
1296                 file->z = ztk;
1297         }
1298
1299         return file;
1300 }
1301
1302
1303 /*
1304 =============================================================================
1305
1306 MAIN PUBLIC FUNCTIONS
1307
1308 =============================================================================
1309 */
1310
1311 /*
1312 ====================
1313 FS_Open
1314
1315 Open a file. The syntax is the same as fopen
1316 ====================
1317 */
1318 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1319 {
1320         // If the file is opened in "write" or "append" mode
1321         if (strchr (mode, 'w') || strchr (mode, 'a'))
1322         {
1323                 char real_path [MAX_OSPATH];
1324
1325                 // Open the file on disk directly
1326                 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1327
1328                 // Create directories up to the file
1329                 FS_CreatePath (real_path);
1330
1331                 return FS_SysOpen (real_path, mode);
1332         }
1333
1334         // Else, we look at the various search paths
1335         return FS_FOpenFile (filepath, quiet);
1336 }
1337
1338
1339 /*
1340 ====================
1341 FS_Close
1342
1343 Close a file
1344 ====================
1345 */
1346 int FS_Close (qfile_t* file)
1347 {
1348 #ifdef FS_USESYSCALLS
1349         if (close (file->stream))
1350 #else
1351         if (fclose (file->stream))
1352 #endif
1353                 return EOF;
1354
1355         if (file->z)
1356         {
1357                 qz_inflateEnd (&file->z->zstream);
1358                 Mem_Free (file->z);
1359         }
1360
1361         Mem_Free (file);
1362         return 0;
1363 }
1364
1365
1366 /*
1367 ====================
1368 FS_Write
1369
1370 Write "datasize" bytes into a file
1371 ====================
1372 */
1373 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1374 {
1375 #ifdef FS_USESYSCALLS
1376         return write (file->stream, data, datasize);
1377 #else
1378         return fwrite (data, 1, datasize, file->stream);
1379 #endif
1380 }
1381
1382
1383 /*
1384 ====================
1385 FS_Read
1386
1387 Read up to "buffersize" bytes from a file
1388 ====================
1389 */
1390 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1391 {
1392         size_t count, nb;
1393         ztoolkit_t *ztk;
1394
1395         // Quick path for unpacked files
1396         if (! (file->flags & FS_FLAG_PACKED))
1397 #ifdef FS_USESYSCALLS
1398                 return read (file->stream, buffer, buffersize);
1399 #else
1400                 return fread (buffer, 1, buffersize, file->stream);
1401 #endif
1402
1403         // If the file isn't compressed
1404         if (! (file->flags & FS_FLAG_DEFLATED))
1405         {
1406                 // We must take care to not read after the end of the file
1407                 count = file->length - file->position;
1408                 if (buffersize > count)
1409                         buffersize = count;
1410
1411 #ifdef FS_USESYSCALLS
1412                 nb = read (file->stream, buffer, buffersize);
1413 #else
1414                 nb = fread (buffer, 1, buffersize, file->stream);
1415 #endif
1416
1417                 file->position += nb;
1418                 return nb;
1419         }
1420
1421         // If the file is compressed, it's more complicated...
1422         ztk = file->z;
1423
1424         // First, we copy as many bytes as we can from "output"
1425         if (ztk->out_ind < ztk->out_max)
1426         {
1427                 count = ztk->out_max - ztk->out_ind;
1428
1429                 nb = (buffersize > count) ? count : buffersize;
1430                 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1431                 ztk->out_ind += nb;
1432                 file->position += nb;
1433         }
1434         else
1435                 nb = 0;
1436
1437         // We cycle through a few operations until we have inflated enough data
1438         while (nb < buffersize)
1439         {
1440                 // NOTE: at this point, "output" should always be empty
1441
1442                 // If "input" is also empty, we need to fill it
1443                 if (ztk->in_ind == ztk->in_max)
1444                 {
1445                         size_t remain;
1446
1447                         // If we are at the end of the file
1448                         if (ztk->out_position == ztk->real_length)
1449                                 return nb;
1450
1451                         remain = file->length - ztk->in_position;
1452                         count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1453 #ifdef FS_USESYSCALLS
1454                         read (file->stream, ztk->input, count);
1455 #else
1456                         fread (ztk->input, 1, count, file->stream);
1457 #endif
1458
1459                         // Update indexes and counters
1460                         ztk->in_ind = 0;
1461                         ztk->in_max = count;
1462                         ztk->in_position += count;
1463                 }
1464
1465                 // Now that we are sure we have compressed data available, we need to determine
1466                 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1467                 // case if we still need more bytes than "output" can contain)
1468
1469                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1470                 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1471
1472                 // If output will be able to contain at least 1 more byte than the data we need
1473                 if (buffersize - nb < sizeof (ztk->output))
1474                 {
1475                         int error;
1476
1477                         // Inflate the data in "output"
1478                         ztk->zstream.next_out = ztk->output;
1479                         ztk->zstream.avail_out = sizeof (ztk->output);
1480                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1481                         if (error != Z_OK && error != Z_STREAM_END)
1482                                 Sys_Error ("Can't inflate file");
1483                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1484                         ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1485                         ztk->out_position += ztk->out_max;
1486
1487                         // Copy the requested data in "buffer" (as much as we can)
1488                         count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1489                         memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1490                         ztk->out_ind = count;
1491                 }
1492
1493                 // Else, we inflate directly in "buffer"
1494                 else
1495                 {
1496                         int error;
1497
1498                         // Inflate the data in "buffer"
1499                         ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1500                         ztk->zstream.avail_out = buffersize - nb;
1501                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1502                         if (error != Z_OK && error != Z_STREAM_END)
1503                                 Sys_Error ("Can't inflate file");
1504                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1505
1506                         // Invalidate the output data (for FS_Seek)
1507                         ztk->out_max = 0;
1508                         ztk->out_ind = 0;
1509
1510                         // How much data did it inflate?
1511                         count = buffersize - nb - ztk->zstream.avail_out;
1512                         ztk->out_position += count;
1513                 }
1514
1515                 nb += count;
1516                 file->position += count;
1517         }
1518
1519         return nb;
1520 }
1521
1522
1523 /*
1524 ====================
1525 FS_Flush
1526
1527 Flush the file output stream
1528 ====================
1529 */
1530 int FS_Flush (qfile_t* file)
1531 {
1532 #ifdef FS_USESYSCALLS
1533         return 0;
1534 #else
1535         return fflush (file->stream);
1536 #endif
1537 }
1538
1539
1540 /*
1541 ====================
1542 FS_Print
1543
1544 Print a string into a file
1545 ====================
1546 */
1547 int FS_Print(qfile_t* file, const char *msg)
1548 {
1549         return FS_Write(file, msg, strlen(msg));
1550 }
1551
1552 /*
1553 ====================
1554 FS_Printf
1555
1556 Print a string into a file
1557 ====================
1558 */
1559 int FS_Printf(qfile_t* file, const char* format, ...)
1560 {
1561         int result;
1562         va_list args;
1563
1564         va_start (args, format);
1565         result = FS_VPrintf(file, format, args);
1566         va_end (args);
1567
1568         return result;
1569 }
1570
1571
1572 /*
1573 ====================
1574 FS_VPrintf
1575
1576 Print a string into a file
1577 ====================
1578 */
1579 int FS_VPrintf(qfile_t* file, const char* format, va_list ap)
1580 {
1581 #ifdef FS_USESYSCALLS
1582 {
1583         int len;
1584         char tempstring[1024];
1585         len = vsnprintf (tempstring, sizeof(tempstring), format, ap);
1586         if (len >= sizeof(tempstring))
1587         {
1588                 int result;
1589                 char *temp = Mem_Alloc(tempmempool, len + 1);
1590                 len = vsnprintf (temp, len + 1, format, ap);
1591                 result = write (file->stream, temp, len);
1592                 Mem_Free(temp);
1593                 return result;
1594         }
1595         else
1596                 return write (file->stream, tempstring, len);
1597 }
1598 #else
1599         return vfprintf (file->stream, format, ap);
1600 #endif
1601 }
1602
1603
1604 /*
1605 ====================
1606 FS_Getc
1607
1608 Get the next character of a file
1609 ====================
1610 */
1611 int FS_Getc (qfile_t* file)
1612 {
1613         char c;
1614
1615         if (FS_Read (file, &c, 1) != 1)
1616                 return EOF;
1617
1618         return c;
1619 }
1620
1621
1622 /*
1623 ====================
1624 FS_Seek
1625
1626 Move the position index in a file
1627 ====================
1628 */
1629 int FS_Seek (qfile_t* file, long offset, int whence)
1630 {
1631         // Quick path for unpacked files
1632         if (! (file->flags & FS_FLAG_PACKED))
1633 #ifdef FS_USESYSCALLS
1634         {
1635                 if (lseek (file->stream, offset, whence) == -1)
1636                         return -1;
1637                 return 0;
1638         }
1639 #else
1640                 return fseek (file->stream, offset, whence);
1641 #endif
1642
1643         // Seeking in compressed files is more a hack than anything else,
1644         // but we need to support it, so here it is.
1645         if (file->flags & FS_FLAG_DEFLATED)
1646         {
1647                 ztoolkit_t *ztk = file->z;
1648                 qbyte buffer [sizeof (ztk->output)];  // it's big to force inflating into buffer directly
1649
1650                 switch (whence)
1651                 {
1652                         case SEEK_CUR:
1653                                 offset += file->position;
1654                                 break;
1655
1656                         case SEEK_SET:
1657                                 break;
1658
1659                         case SEEK_END:
1660                                 offset += ztk->real_length;
1661                                 break;
1662
1663                         default:
1664                                 return -1;
1665                 }
1666                 if (offset < 0 || offset > (long) ztk->real_length)
1667                         return -1;
1668
1669                 // If we need to go back in the file
1670                 if (offset <= (long) file->position)
1671                 {
1672                         // If we still have the data we need in the output buffer
1673                         if (file->position - offset <= ztk->out_ind)
1674                         {
1675                                 ztk->out_ind -= file->position - offset;
1676                                 file->position = offset;
1677                                 return 0;
1678                         }
1679
1680                         // Else, we restart from the beginning of the file
1681                         ztk->in_ind = 0;
1682                         ztk->in_max = 0;
1683                         ztk->in_position = 0;
1684                         ztk->out_ind = 0;
1685                         ztk->out_max = 0;
1686                         ztk->out_position = 0;
1687                         file->position = 0;
1688 #ifdef FS_USESYSCALLS
1689                         lseek (file->stream, file->offset, SEEK_SET);
1690 #else
1691                         fseek (file->stream, file->offset, SEEK_SET);
1692 #endif
1693
1694                         // Reset the Zlib stream
1695                         ztk->zstream.next_in = ztk->input;
1696                         ztk->zstream.avail_in = 0;
1697                         qz_inflateReset (&ztk->zstream);
1698                 }
1699
1700                 // Skip all data until we reach the requested offset
1701                 while ((long) file->position < offset)
1702                 {
1703                         size_t diff = offset - file->position;
1704                         size_t count, len;
1705
1706                         count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1707                         len = FS_Read (file, buffer, count);
1708                         if (len != count)
1709                                 return -1;
1710                 }
1711
1712                 return 0;
1713         }
1714
1715         // Packed files receive a special treatment too, because
1716         // we need to make sure it doesn't go outside of the file
1717         switch (whence)
1718         {
1719                 case SEEK_CUR:
1720                         offset += file->position;
1721                         break;
1722
1723                 case SEEK_SET:
1724                         break;
1725
1726                 case SEEK_END:
1727                         offset += file->length;
1728                         break;
1729
1730                 default:
1731                         return -1;
1732         }
1733         if (offset < 0 || offset > (long) file->length)
1734                 return -1;
1735
1736 #ifdef FS_USESYSCALLS
1737         if (lseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1738                 return -1;
1739 #else
1740         if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1741                 return -1;
1742 #endif
1743         file->position = offset;
1744         return 0;
1745 }
1746
1747
1748 /*
1749 ====================
1750 FS_Tell
1751
1752 Give the current position in a file
1753 ====================
1754 */
1755 long FS_Tell (qfile_t* file)
1756 {
1757         if (file->flags & FS_FLAG_PACKED)
1758                 return file->position;
1759
1760 #ifdef FS_USESYSCALLS
1761         return lseek (file->stream, 0, SEEK_CUR);
1762 #else
1763         return ftell (file->stream);
1764 #endif
1765 }
1766
1767
1768 /*
1769 ====================
1770 FS_Gets
1771
1772 Extract a line from a file
1773 ====================
1774 */
1775 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1776 {
1777         size_t ind;
1778
1779         // Quick path for unpacked files
1780 #ifndef FS_USESYSCALLS
1781         if (! (file->flags & FS_FLAG_PACKED))
1782                 return fgets (buffer, buffersize, file->stream);
1783 #endif
1784
1785         for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1786         {
1787                 int c = FS_Getc (file);
1788                 switch (c)
1789                 {
1790                         // End of file
1791                         case -1:
1792                                 if (!ind)
1793                                         return NULL;
1794
1795                                 buffer[ind] = '\0';
1796                                 return buffer;
1797
1798                         // End of line
1799                         case '\r':
1800                         case '\n':
1801                                 buffer[ind] = '\n';
1802                                 buffer[ind + 1] = '\0';
1803                                 return buffer;
1804
1805                         default:
1806                                 buffer[ind] = c;
1807                 }
1808
1809         }
1810
1811         buffer[buffersize - 1] = '\0';
1812         return buffer;
1813 }
1814
1815
1816 /*
1817 ==========
1818 FS_Getline
1819
1820 Dynamic length version of fgets. DO NOT free the buffer.
1821 ==========
1822 */
1823 char *FS_Getline (qfile_t *file)
1824 {
1825         static int  size = 256;
1826         static char *buf = 0;
1827         char        *t;
1828         int         len;
1829
1830         if (!buf)
1831                 buf = Mem_Alloc (fs_mempool, size);
1832
1833         if (!FS_Gets (file, buf, size))
1834                 return 0;
1835
1836         len = strlen (buf);
1837         while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1838         {
1839                 t = Mem_Alloc (fs_mempool, size + 256);
1840                 memcpy(t, buf, size);
1841                 Mem_Free(buf);
1842                 size += 256;
1843                 buf = t;
1844                 if (!FS_Gets (file, buf + len, size - len))
1845                         break;
1846                 len = strlen (buf);
1847         }
1848         while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1849                 buf[len - 1] = 0;
1850         return buf;
1851 }
1852
1853
1854 /*
1855 ====================
1856 FS_Eof
1857
1858 Extract a line from a file
1859 ====================
1860 */
1861 // FIXME: remove this function?
1862 int FS_Eof (qfile_t* file)
1863 {
1864         if (file->flags & FS_FLAG_PACKED)
1865         {
1866                 if (file->flags & FS_FLAG_DEFLATED)
1867                         return (file->position == file->z->real_length);
1868
1869                 return (file->position == file->length);
1870         }
1871
1872 #ifdef FS_USESYSCALLS
1873         Sys_Error("FS_Eof: not implemented using syscalls\n");
1874         return false;
1875 #else
1876         return feof (file->stream);
1877 #endif
1878 }
1879
1880
1881 /*
1882 ============
1883 FS_LoadFile
1884
1885 Filename are relative to the quake directory.
1886 Always appends a 0 byte.
1887 ============
1888 */
1889 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1890 {
1891         qfile_t *h;
1892         qbyte *buf;
1893
1894         // look for it in the filesystem or pack files
1895         h = FS_Open (path, "rb", quiet);
1896         if (!h)
1897                 return NULL;
1898
1899         buf = Mem_Alloc(pool, fs_filesize+1);
1900         if (!buf)
1901                 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1902
1903         ((qbyte *)buf)[fs_filesize] = 0;
1904
1905         FS_Read (h, buf, fs_filesize);
1906         FS_Close (h);
1907
1908         return buf;
1909 }
1910
1911
1912 /*
1913 ============
1914 FS_WriteFile
1915
1916 The filename will be prefixed by the current game directory
1917 ============
1918 */
1919 qboolean FS_WriteFile (const char *filename, void *data, int len)
1920 {
1921         qfile_t *handle;
1922
1923         handle = FS_Open (filename, "wb", false);
1924         if (!handle)
1925         {
1926                 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1927                 return false;
1928         }
1929
1930         Con_DPrintf("FS_WriteFile: %s\n", filename);
1931         FS_Write (handle, data, len);
1932         FS_Close (handle);
1933         return true;
1934 }
1935
1936
1937 /*
1938 =============================================================================
1939
1940 OTHERS PUBLIC FUNCTIONS
1941
1942 =============================================================================
1943 */
1944
1945 /*
1946 ============
1947 FS_StripExtension
1948 ============
1949 */
1950 void FS_StripExtension (const char *in, char *out, size_t size_out)
1951 {
1952         char *last = NULL;
1953
1954         if (size_out == 0)
1955                 return;
1956
1957         while (*in && size_out > 1)
1958         {
1959                 if (*in == '.')
1960                         last = out;
1961                 else if (*in == '/' || *in == '\\' || *in == ':')
1962                         last = NULL;
1963                 *out++ = *in++;
1964                 size_out--;
1965         }
1966         if (last)
1967                 *last = 0;
1968         else
1969                 *out = 0;
1970 }
1971
1972
1973 /*
1974 ==================
1975 FS_DefaultExtension
1976 ==================
1977 */
1978 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1979 {
1980         const char *src;
1981
1982         // if path doesn't have a .EXT, append extension
1983         // (extension should include the .)
1984         src = path + strlen(path) - 1;
1985
1986         while (*src != '/' && src != path)
1987         {
1988                 if (*src == '.')
1989                         return;                 // it has an extension
1990                 src--;
1991         }
1992
1993         strlcat (path, extension, size_path);
1994 }
1995
1996
1997 /*
1998 ==================
1999 FS_FileExists
2000
2001 Look for a file in the packages and in the filesystem
2002 ==================
2003 */
2004 qboolean FS_FileExists (const char *filename)
2005 {
2006         return (FS_FindFile (filename, NULL, true) != NULL);
2007 }
2008
2009
2010 /*
2011 ==================
2012 FS_SysFileExists
2013
2014 Look for a file in the filesystem only
2015 ==================
2016 */
2017 qboolean FS_SysFileExists (const char *path)
2018 {
2019 #if WIN32
2020         FILE *f;
2021
2022         f = fopen (path, "rb");
2023         if (f)
2024         {
2025                 fclose (f);
2026                 return true;
2027         }
2028
2029         return false;
2030 #else
2031         struct stat buf;
2032
2033         if (stat (path,&buf) == -1)
2034                 return false;
2035
2036         return true;
2037 #endif
2038 }
2039
2040 void FS_mkdir (const char *path)
2041 {
2042 #if WIN32
2043         _mkdir (path);
2044 #else
2045         mkdir (path, 0777);
2046 #endif
2047 }
2048
2049 /*
2050 ===========
2051 FS_Search
2052
2053 Allocate and fill a search structure with information on matching filenames.
2054 ===========
2055 */
2056 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2057 {
2058         fssearch_t *search;
2059         searchpath_t *searchpath;
2060         pack_t *pak;
2061         int i, basepathlength, numfiles, numchars;
2062         stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
2063         const char *slash, *backslash, *colon, *separator;
2064         char *basepath;
2065         char netpath[MAX_OSPATH];
2066         char temp[MAX_OSPATH];
2067
2068         while(!strncmp(pattern, "./", 2))
2069                 pattern += 2;
2070         while(!strncmp(pattern, ".\\", 2))
2071                 pattern += 2;
2072
2073         search = NULL;
2074         liststart = NULL;
2075         listcurrent = NULL;
2076         listtemp = NULL;
2077         slash = strrchr(pattern, '/');
2078         backslash = strrchr(pattern, '\\');
2079         colon = strrchr(pattern, ':');
2080         separator = pattern;
2081         if (separator < slash)
2082                 separator = slash;
2083         if (separator < backslash)
2084                 separator = backslash;
2085         if (separator < colon)
2086                 separator = colon;
2087         basepathlength = separator - pattern;
2088         basepath = Mem_Alloc (tempmempool, basepathlength + 1);
2089         if (basepathlength)
2090                 memcpy(basepath, pattern, basepathlength);
2091         basepath[basepathlength] = 0;
2092
2093         // search through the path, one element at a time
2094         for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2095         {
2096                 // is the element a pak file?
2097                 if (searchpath->pack)
2098                 {
2099                         // look through all the pak file elements
2100                         pak = searchpath->pack;
2101                         for (i = 0;i < pak->numfiles;i++)
2102                         {
2103                                 strcpy(temp, pak->files[i].name);
2104                                 while (temp[0])
2105                                 {
2106                                         if (matchpattern(temp, (char *)pattern, true))
2107                                         {
2108                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2109                                                         if (!strcmp(listtemp->text, temp))
2110                                                                 break;
2111                                                 if (listtemp == NULL)
2112                                                 {
2113                                                         listcurrent = stringlistappend(listcurrent, temp);
2114                                                         if (liststart == NULL)
2115                                                                 liststart = listcurrent;
2116                                                         if (!quiet)
2117                                                                 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2118                                                 }
2119                                         }
2120                                         // strip off one path element at a time until empty
2121                                         // this way directories are added to the listing if they match the pattern
2122                                         slash = strrchr(temp, '/');
2123                                         backslash = strrchr(temp, '\\');
2124                                         colon = strrchr(temp, ':');
2125                                         separator = temp;
2126                                         if (separator < slash)
2127                                                 separator = slash;
2128                                         if (separator < backslash)
2129                                                 separator = backslash;
2130                                         if (separator < colon)
2131                                                 separator = colon;
2132                                         *((char *)separator) = 0;
2133                                 }
2134                         }
2135                 }
2136                 else
2137                 {
2138                         // get a directory listing and look at each name
2139                         snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
2140                         if ((dir = listdirectory(netpath)))
2141                         {
2142                                 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2143                                 {
2144                                         snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
2145                                         if (matchpattern(temp, (char *)pattern, true))
2146                                         {
2147                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2148                                                         if (!strcmp(listtemp->text, temp))
2149                                                                 break;
2150                                                 if (listtemp == NULL)
2151                                                 {
2152                                                         listcurrent = stringlistappend(listcurrent, temp);
2153                                                         if (liststart == NULL)
2154                                                                 liststart = listcurrent;
2155                                                         if (!quiet)
2156                                                                 Con_DPrintf("SearchDirFile: %s\n", temp);
2157                                                 }
2158                                         }
2159                                 }
2160                                 freedirectory(dir);
2161                         }
2162                 }
2163         }
2164
2165         if (liststart)
2166         {
2167                 liststart = stringlistsort(liststart);
2168                 numfiles = 0;
2169                 numchars = 0;
2170                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2171                 {
2172                         numfiles++;
2173                         numchars += strlen(listtemp->text) + 1;
2174                 }
2175                 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2176                 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2177                 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2178                 search->numfilenames = numfiles;
2179                 numfiles = 0;
2180                 numchars = 0;
2181                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2182                 {
2183                         search->filenames[numfiles] = search->filenamesbuffer + numchars;
2184                         strcpy(search->filenames[numfiles], listtemp->text);
2185                         numfiles++;
2186                         numchars += strlen(listtemp->text) + 1;
2187                 }
2188                 if (liststart)
2189                         stringlistfree(liststart);
2190         }
2191
2192         Mem_Free(basepath);
2193         return search;
2194 }
2195
2196 void FS_FreeSearch(fssearch_t *search)
2197 {
2198         Z_Free(search);
2199 }
2200
2201 extern int con_linewidth;
2202 int FS_ListDirectory(const char *pattern, int oneperline)
2203 {
2204         int numfiles;
2205         int numcolumns;
2206         int numlines;
2207         int columnwidth;
2208         int linebufpos;
2209         int i, j, k, l;
2210         const char *name;
2211         char linebuf[4096];
2212         fssearch_t *search;
2213         search = FS_Search(pattern, true, true);
2214         if (!search)
2215                 return 0;
2216         numfiles = search->numfilenames;
2217         if (!oneperline)
2218         {
2219                 // FIXME: the names could be added to one column list and then
2220                 // gradually shifted into the next column if they fit, and then the
2221                 // next to make a compact variable width listing but it's a lot more
2222                 // complicated...
2223                 // find width for columns
2224                 columnwidth = 0;
2225                 for (i = 0;i < numfiles;i++)
2226                 {
2227                         l = strlen(search->filenames[i]);
2228                         if (columnwidth < l)
2229                                 columnwidth = l;
2230                 }
2231                 // count the spacing character
2232                 columnwidth++;
2233                 // calculate number of columns
2234                 numcolumns = con_linewidth / columnwidth;
2235                 // don't bother with the column printing if it's only one column
2236                 if (numcolumns >= 2)
2237                 {
2238                         numlines = (numfiles + numcolumns - 1) / numcolumns;
2239                         for (i = 0;i < numlines;i++)
2240                         {
2241                                 linebufpos = 0;
2242                                 for (k = 0;k < numcolumns;k++)
2243                                 {
2244                                         l = i * numcolumns + k;
2245                                         if (l < numfiles)
2246                                         {
2247                                                 name = search->filenames[l];
2248                                                 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2249                                                         linebuf[linebufpos++] = name[j];
2250                                                 // space out name unless it's the last on the line
2251                                                 if (k < (numcolumns - 1) && l < (numfiles - 1))
2252                                                         for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2253                                                                 linebuf[linebufpos++] = ' ';
2254                                         }
2255                                 }
2256                                 linebuf[linebufpos] = 0;
2257                                 Con_Printf("%s\n", linebuf);
2258                         }
2259                 }
2260                 else
2261                         oneperline = true;
2262         }
2263         if (oneperline)
2264                 for (i = 0;i < numfiles;i++)
2265                         Con_Printf("%s\n", search->filenames[i]);
2266         FS_FreeSearch(search);
2267         return numfiles;
2268 }
2269
2270 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2271 {
2272         const char *pattern;
2273         if (Cmd_Argc() > 3)
2274         {
2275                 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2276                 return;
2277         }
2278         if (Cmd_Argc() == 2)
2279                 pattern = Cmd_Argv(1);
2280         else
2281                 pattern = "*";
2282         if (!FS_ListDirectory(pattern, oneperline))
2283                 Con_Print("No files found.\n");
2284 }
2285
2286 void FS_Dir_f(void)
2287 {
2288         FS_ListDirectoryCmd("dir", true);
2289 }
2290
2291 void FS_Ls_f(void)
2292 {
2293         FS_ListDirectoryCmd("ls", false);
2294 }
2295