]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - fs.c
Quick hack to fix the calls to "open" on Win32
[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) != (unsigned long) maxsize)
409 #else
410         fseek (packhandle, filesize - maxsize, SEEK_SET);
411         if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) 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         size_t left, right, middle;
669         int diff;
670         packfile_t *file;
671
672         strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
673
674         // Look for the slot we should put that file into (binary search)
675         left = 0;
676         right = pack->numfiles;
677         while (left != right)
678         {
679                 middle = (left + right - 1) / 2;
680                 diff = strcmp_funct (pack->files[middle].name, name);
681
682                 // If we found the file, there's a problem
683                 if (!diff)
684                         Sys_Error ("Package %s contains several time the file %s\n",
685                                            pack->filename, name);
686
687                 // If we're too far in the list
688                 if (diff > 0)
689                         right = middle;
690                 else
691                         left = middle + 1;
692         }
693
694         // We have to move the right of the list by one slot to free the one we need
695         file = &pack->files[left];
696         memmove (file + 1, file, (pack->numfiles - left) * sizeof (*file));
697         pack->numfiles++;
698
699         strlcpy (file->name, name, sizeof (file->name));
700         file->offset = offset;
701         file->packsize = packsize;
702         file->realsize = realsize;
703         file->flags = flags;
704
705         return file;
706 }
707
708
709 /*
710 ============
711 FS_CreatePath
712
713 Only used for FS_Open.
714 ============
715 */
716 void FS_CreatePath (char *path)
717 {
718         char *ofs, save;
719
720         for (ofs = path+1 ; *ofs ; ofs++)
721         {
722                 if (*ofs == '/' || *ofs == '\\')
723                 {
724                         // create the directory
725                         save = *ofs;
726                         *ofs = 0;
727                         FS_mkdir (path);
728                         *ofs = save;
729                 }
730         }
731 }
732
733
734 /*
735 ============
736 FS_Path_f
737
738 ============
739 */
740 void FS_Path_f (void)
741 {
742         searchpath_t *s;
743
744         Con_Print("Current search path:\n");
745         for (s=fs_searchpaths ; s ; s=s->next)
746         {
747                 if (s->pack)
748                 {
749                         Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
750                 }
751                 else
752                         Con_Printf("%s\n", s->filename);
753         }
754 }
755
756
757 /*
758 =================
759 FS_LoadPackPAK
760
761 Takes an explicit (not game tree related) path to a pak file.
762
763 Loads the header and directory, adding the files at the beginning
764 of the list so they override previous pack files.
765 =================
766 */
767 pack_t *FS_LoadPackPAK (const char *packfile)
768 {
769         dpackheader_t header;
770         int i, numpackfiles;
771 #ifdef FS_USESYSCALLS
772         int packhandle;
773 #else
774         FILE *packhandle;
775 #endif
776         pack_t *pack;
777         dpackfile_t *info;      // temporary alloc, allowing huge pack directories
778
779 #ifdef FS_USESYSCALLS
780         packhandle = open (packfile, O_RDONLY | O_BINARY);
781         if (packhandle < 0)
782                 return NULL;
783         read (packhandle, (void *)&header, sizeof(header));
784 #else
785         packhandle = fopen (packfile, "rb");
786         if (!packhandle)
787                 return NULL;
788         fread ((void *)&header, 1, sizeof(header), packhandle);
789 #endif
790         if (memcmp(header.id, "PACK", 4))
791                 Sys_Error ("%s is not a packfile", packfile);
792         header.dirofs = LittleLong (header.dirofs);
793         header.dirlen = LittleLong (header.dirlen);
794
795         if (header.dirlen % sizeof(dpackfile_t))
796                 Sys_Error ("%s has an invalid directory size", packfile);
797
798         numpackfiles = header.dirlen / sizeof(dpackfile_t);
799
800         if (numpackfiles > MAX_FILES_IN_PACK)
801                 Sys_Error ("%s has %i files", packfile, numpackfiles);
802
803         pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
804         pack->ignorecase = false; // PAK is case sensitive
805         strlcpy (pack->filename, packfile, sizeof (pack->filename));
806         pack->handle = packhandle;
807         pack->numfiles = 0;
808         pack->mempool = Mem_AllocPool(packfile, 0, NULL);
809         pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
810         pack->next = packlist;
811         packlist = pack;
812
813         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
814 #ifdef FS_USESYSCALLS
815         lseek (packhandle, header.dirofs, SEEK_SET);
816         read (packhandle, (void *)info, header.dirlen);
817 #else
818         fseek (packhandle, header.dirofs, SEEK_SET);
819         fread ((void *)info, 1, header.dirlen, packhandle);
820 #endif
821
822         // parse the directory
823         for (i = 0;i < numpackfiles;i++)
824         {
825                 size_t offset = LittleLong (info[i].filepos);
826                 size_t size = LittleLong (info[i].filelen);
827
828                 FS_AddFileToPack (info[i].name, pack, offset, size, size, FILE_FLAG_TRUEOFFS);
829         }
830
831         Mem_Free(info);
832
833         Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
834         return pack;
835 }
836
837
838 /*
839 ================
840 FS_AddGameDirectory
841
842 Sets fs_gamedir, adds the directory to the head of the path,
843 then loads and adds pak1.pak pak2.pak ...
844 ================
845 */
846 void FS_AddGameDirectory (char *dir)
847 {
848         stringlist_t *list, *current;
849         searchpath_t *search;
850         pack_t *pak;
851         char pakfile[MAX_OSPATH];
852
853         strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
854
855         list = listdirectory(dir);
856
857         // add any PAK package in the directory
858         for (current = list;current;current = current->next)
859         {
860                 if (matchpattern(current->text, "*.pak", true))
861                 {
862                         snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
863                         pak = FS_LoadPackPAK (pakfile);
864                         if (pak)
865                         {
866                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
867                                 search->pack = pak;
868                                 search->next = fs_searchpaths;
869                                 fs_searchpaths = search;
870                         }
871                         else
872                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
873                 }
874         }
875
876         // add any PK3 package in the director
877         for (current = list;current;current = current->next)
878         {
879                 if (matchpattern(current->text, "*.pk3", true))
880                 {
881                         snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
882                         pak = FS_LoadPackPK3 (pakfile);
883                         if (pak)
884                         {
885                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
886                                 search->pack = pak;
887                                 search->next = fs_searchpaths;
888                                 fs_searchpaths = search;
889                         }
890                         else
891                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
892                 }
893         }
894         freedirectory(list);
895
896         // Add the directory to the search path
897         // (unpacked files have the priority over packed files)
898         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
899         strlcpy (search->filename, dir, sizeof (search->filename));
900         search->next = fs_searchpaths;
901         fs_searchpaths = search;
902 }
903
904
905 /*
906 ============
907 FS_FileExtension
908 ============
909 */
910 char *FS_FileExtension (const char *in)
911 {
912         static char exten[8];
913         const char *slash, *backslash, *colon, *dot, *separator;
914         int i;
915
916         slash = strrchr(in, '/');
917         backslash = strrchr(in, '\\');
918         colon = strrchr(in, ':');
919         dot = strrchr(in, '.');
920         separator = slash;
921         if (separator < backslash)
922                 separator = backslash;
923         if (separator < colon)
924                 separator = colon;
925         if (dot == NULL || dot < separator)
926                 return "";
927         dot++;
928         for (i = 0;i < 7 && dot[i];i++)
929                 exten[i] = dot[i];
930         exten[i] = 0;
931         return exten;
932 }
933
934
935 /*
936 ================
937 FS_Init
938 ================
939 */
940 void FS_Init (void)
941 {
942         int i;
943         searchpath_t *search;
944
945         fs_mempool = Mem_AllocPool("file management", 0, NULL);
946         pak_mempool = Mem_AllocPool("paks", 0, NULL);
947
948         Cvar_RegisterVariable (&scr_screenshot_name);
949
950         Cmd_AddCommand ("path", FS_Path_f);
951         Cmd_AddCommand ("dir", FS_Dir_f);
952         Cmd_AddCommand ("ls", FS_Ls_f);
953
954         strcpy(fs_basedir, ".");
955         strcpy(fs_gamedir, ".");
956
957         PK3_OpenLibrary ();
958
959         // -basedir <path>
960         // Overrides the system supplied base directory (under GAMENAME)
961 // 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)
962         i = COM_CheckParm ("-basedir");
963         if (i && i < com_argc-1)
964         {
965                 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
966                 i = strlen (fs_basedir);
967                 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
968                         fs_basedir[i-1] = 0;
969         }
970
971         // -path <dir or packfile> [<dir or packfile>] ...
972         // Fully specifies the exact search path, overriding the generated one
973 // 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)
974         i = COM_CheckParm ("-path");
975         if (i)
976         {
977                 fs_modified = true;
978                 while (++i < com_argc)
979                 {
980                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
981                                 break;
982
983                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
984                         if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
985                         {
986                                 search->pack = FS_LoadPackPAK (com_argv[i]);
987                                 if (!search->pack)
988                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
989                         }
990                         else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
991                         {
992                                 search->pack = FS_LoadPackPK3 (com_argv[i]);
993                                 if (!search->pack)
994                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
995                         }
996                         else
997                                 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
998                         search->next = fs_searchpaths;
999                         fs_searchpaths = search;
1000                 }
1001                 return;
1002         }
1003
1004         // start up with GAMENAME by default (id1)
1005         strlcpy (com_modname, GAMENAME, sizeof (com_modname));
1006         FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
1007         Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1008
1009         // add the game-specific path, if any
1010         if (gamedirname[0])
1011         {
1012                 fs_modified = true;
1013                 strlcpy (com_modname, gamedirname, sizeof (com_modname));
1014                 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
1015         }
1016
1017         // -game <gamedir>
1018         // Adds basedir/gamedir as an override game
1019         // LordHavoc: now supports multiple -game directories
1020         for (i = 1;i < com_argc;i++)
1021         {
1022                 if (!com_argv[i])
1023                         continue;
1024                 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1025                 {
1026                         i++;
1027                         fs_modified = true;
1028                         strlcpy (com_modname, com_argv[i], sizeof (com_modname));
1029                         FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i]));
1030                         Cvar_SetQuick (&scr_screenshot_name, com_modname);
1031                 }
1032         }
1033 }
1034
1035
1036 /*
1037 ====================
1038 FS_SysOpen
1039
1040 Internal function used to create a qfile_t and open the relevant file on disk
1041 ====================
1042 */
1043 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
1044 {
1045         qfile_t* file;
1046
1047         file = Mem_Alloc (fs_mempool, sizeof (*file));
1048         memset (file, 0, sizeof (*file));
1049
1050 #ifdef FS_USESYSCALLS
1051         if (strchr(mode, 'r'))
1052                 file->stream = open (filepath, O_RDONLY | O_BINARY);
1053         else if (strchr(mode, 'w'))
1054                 file->stream = open (filepath, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666);
1055         else if (strchr(mode, 'a'))
1056                 file->stream = open (filepath, O_RDWR | O_BINARY | O_CREAT | O_APPEND, 0666);
1057         else
1058                 file->stream = -1;
1059         if (file->stream < 0)
1060         {
1061                 Mem_Free (file);
1062                 return NULL;
1063         }
1064 #else
1065         file->stream = fopen (filepath, mode);
1066         if (!file->stream)
1067         {
1068                 Mem_Free (file);
1069                 return NULL;
1070         }
1071 #endif
1072
1073         return file;
1074 }
1075
1076
1077 /*
1078 ===========
1079 FS_OpenRead
1080 ===========
1081 */
1082 qfile_t *FS_OpenRead (const char *path, int offs, int len)
1083 {
1084         qfile_t* file;
1085
1086         file = FS_SysOpen (path, "rb");
1087         if (!file)
1088         {
1089                 Sys_Error ("Couldn't open %s", path);
1090                 return NULL;
1091         }
1092
1093         // Normal file
1094         if (offs < 0 || len < 0)
1095         {
1096                 // We set fs_filesize here for normal files
1097 #ifdef FS_USESYSCALLS
1098                 fs_filesize = lseek (file->stream, 0, SEEK_END);
1099                 lseek (file->stream, 0, SEEK_SET);
1100 #else
1101                 fseek (file->stream, 0, SEEK_END);
1102                 fs_filesize = ftell (file->stream);
1103                 fseek (file->stream, 0, SEEK_SET);
1104 #endif
1105         }
1106         // Packed file
1107         else
1108         {
1109 #ifdef FS_USESYSCALLS
1110                 lseek (file->stream, offs, SEEK_SET);
1111 #else
1112                 fseek (file->stream, offs, SEEK_SET);
1113 #endif
1114
1115                 file->flags |= FS_FLAG_PACKED;
1116                 file->length = len;
1117                 file->offset = offs;
1118                 file->position = 0;
1119         }
1120
1121         return file;
1122 }
1123
1124
1125 /*
1126 ====================
1127 FS_FindFile
1128
1129 Look for a file in the packages and in the filesystem
1130
1131 Return the searchpath where the file was found (or NULL)
1132 and the file index in the package if relevant
1133 ====================
1134 */
1135 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1136 {
1137         searchpath_t *search;
1138         pack_t *pak;
1139         int (*strcmp_funct) (const char* str1, const char* str2);
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                         size_t left, right, middle;
1148
1149                         pak = search->pack;
1150                         strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1151
1152                         // Look for the file (binary search)
1153                         left = 0;
1154                         right = pak->numfiles;
1155                         while (left != right)
1156                         {
1157                                 int diff;
1158
1159                                 middle = (left + right - 1) / 2;
1160                                 diff = strcmp_funct (pak->files[middle].name, name);
1161
1162                                 // Found it
1163                                 if (!diff)
1164                                 {
1165                                         if (!quiet)
1166                                                 Sys_Printf("FS_FindFile: %s in %s\n",
1167                                                                         pak->files[middle].name, pak->filename);
1168
1169                                         if (index != NULL)
1170                                                 *index = middle;
1171                                         return search;
1172                                 }
1173
1174                                 // If we're too far in the list
1175                                 if (diff > 0)
1176                                         right = middle;
1177                                 else
1178                                         left = middle + 1;
1179                         }
1180                 }
1181                 else
1182                 {
1183                         char netpath[MAX_OSPATH];
1184                         snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1185                         if (FS_SysFileExists (netpath))
1186                         {
1187                                 if (!quiet)
1188                                         Sys_Printf("FS_FindFile: %s\n", netpath);
1189
1190                                 if (index != NULL)
1191                                         *index = -1;
1192                                 return search;
1193                         }
1194                 }
1195         }
1196
1197         if (!quiet)
1198                 Sys_Printf("FS_FindFile: can't find %s\n", name);
1199
1200         if (index != NULL)
1201                 *index = -1;
1202         return NULL;
1203 }
1204
1205
1206 /*
1207 ===========
1208 FS_FOpenFile
1209
1210 If the requested file is inside a packfile, a new qfile_t* will be opened
1211 into the file.
1212
1213 Sets fs_filesize
1214 ===========
1215 */
1216 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
1217 {
1218         searchpath_t *search;
1219         packfile_t *packfile;
1220         int i;
1221         qfile_t *file;
1222
1223         search = FS_FindFile (filename, &i, quiet);
1224
1225         // Not found?
1226         if (search == NULL)
1227         {
1228                 fs_filesize = -1;
1229                 return NULL;
1230         }
1231
1232         // Found in the filesystem?
1233         if (i < 0)
1234         {
1235                 char netpath[MAX_OSPATH];
1236                 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
1237                 return FS_OpenRead(netpath, -1, -1);
1238         }
1239
1240         // So, we found it in a package...
1241         packfile = &search->pack->files[i];
1242
1243         // If we don't have the true offset, get it now
1244         if (! (packfile->flags & FILE_FLAG_TRUEOFFS))
1245                 PK3_GetTrueFileOffset (packfile, search->pack);
1246
1247         // No Zlib DLL = no compressed files
1248         if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED))
1249         {
1250                 Con_Printf("WARNING: can't open the compressed file %s\n"
1251                                         "You need the Zlib DLL to use compressed files\n",
1252                                         filename);
1253                 fs_filesize = -1;
1254                 return NULL;
1255         }
1256
1257         // open a new file in the pakfile
1258         file = FS_OpenRead (search->pack->filename, packfile->offset, packfile->packsize);
1259         fs_filesize = packfile->realsize;
1260
1261         if (packfile->flags & FILE_FLAG_DEFLATED)
1262         {
1263                 ztoolkit_t *ztk;
1264
1265                 file->flags |= FS_FLAG_DEFLATED;
1266
1267                 // We need some more variables
1268                 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1269
1270                 ztk->real_length = packfile->realsize;
1271
1272                 // Initialize zlib stream
1273                 ztk->zstream.next_in = ztk->input;
1274                 ztk->zstream.avail_in = 0;
1275
1276                 /* From Zlib's "unzip.c":
1277                  *
1278                  * windowBits is passed < 0 to tell that there is no zlib header.
1279                  * Note that in this case inflate *requires* an extra "dummy" byte
1280                  * after the compressed stream in order to complete decompression and
1281                  * return Z_STREAM_END.
1282                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1283                  * size of both compressed and uncompressed data
1284                  */
1285                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1286                         Sys_Error ("inflate init error (file: %s)", filename);
1287
1288                 ztk->zstream.next_out = ztk->output;
1289                 ztk->zstream.avail_out = sizeof (ztk->output);
1290
1291                 file->z = ztk;
1292         }
1293
1294         return file;
1295 }
1296
1297
1298 /*
1299 =============================================================================
1300
1301 MAIN PUBLIC FUNCTIONS
1302
1303 =============================================================================
1304 */
1305
1306 /*
1307 ====================
1308 FS_Open
1309
1310 Open a file. The syntax is the same as fopen
1311 ====================
1312 */
1313 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1314 {
1315         // If the file is opened in "write" or "append" mode
1316         if (strchr (mode, 'w') || strchr (mode, 'a'))
1317         {
1318                 char real_path [MAX_OSPATH];
1319
1320                 // Open the file on disk directly
1321                 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1322
1323                 // Create directories up to the file
1324                 FS_CreatePath (real_path);
1325
1326                 return FS_SysOpen (real_path, mode);
1327         }
1328
1329         // Else, we look at the various search paths
1330         return FS_FOpenFile (filepath, quiet);
1331 }
1332
1333
1334 /*
1335 ====================
1336 FS_Close
1337
1338 Close a file
1339 ====================
1340 */
1341 int FS_Close (qfile_t* file)
1342 {
1343 #ifdef FS_USESYSCALLS
1344         if (close (file->stream))
1345 #else
1346         if (fclose (file->stream))
1347 #endif
1348                 return EOF;
1349
1350         if (file->z)
1351         {
1352                 qz_inflateEnd (&file->z->zstream);
1353                 Mem_Free (file->z);
1354         }
1355
1356         Mem_Free (file);
1357         return 0;
1358 }
1359
1360
1361 /*
1362 ====================
1363 FS_Write
1364
1365 Write "datasize" bytes into a file
1366 ====================
1367 */
1368 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1369 {
1370 #ifdef FS_USESYSCALLS
1371         return write (file->stream, data, datasize);
1372 #else
1373         return fwrite (data, 1, datasize, file->stream);
1374 #endif
1375 }
1376
1377
1378 /*
1379 ====================
1380 FS_Read
1381
1382 Read up to "buffersize" bytes from a file
1383 ====================
1384 */
1385 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1386 {
1387         size_t count, nb;
1388         ztoolkit_t *ztk;
1389
1390         // Quick path for unpacked files
1391         if (! (file->flags & FS_FLAG_PACKED))
1392 #ifdef FS_USESYSCALLS
1393                 return read (file->stream, buffer, buffersize);
1394 #else
1395                 return fread (buffer, 1, buffersize, file->stream);
1396 #endif
1397
1398         // If the file isn't compressed
1399         if (! (file->flags & FS_FLAG_DEFLATED))
1400         {
1401                 // We must take care to not read after the end of the file
1402                 count = file->length - file->position;
1403                 if (buffersize > count)
1404                         buffersize = count;
1405
1406 #ifdef FS_USESYSCALLS
1407                 nb = read (file->stream, buffer, buffersize);
1408 #else
1409                 nb = fread (buffer, 1, buffersize, file->stream);
1410 #endif
1411
1412                 file->position += nb;
1413                 return nb;
1414         }
1415
1416         // If the file is compressed, it's more complicated...
1417         ztk = file->z;
1418
1419         // First, we copy as many bytes as we can from "output"
1420         if (ztk->out_ind < ztk->out_max)
1421         {
1422                 count = ztk->out_max - ztk->out_ind;
1423
1424                 nb = (buffersize > count) ? count : buffersize;
1425                 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1426                 ztk->out_ind += nb;
1427                 file->position += nb;
1428         }
1429         else
1430                 nb = 0;
1431
1432         // We cycle through a few operations until we have inflated enough data
1433         while (nb < buffersize)
1434         {
1435                 // NOTE: at this point, "output" should always be empty
1436
1437                 // If "input" is also empty, we need to fill it
1438                 if (ztk->in_ind == ztk->in_max)
1439                 {
1440                         size_t remain;
1441
1442                         // If we are at the end of the file
1443                         if (ztk->out_position == ztk->real_length)
1444                                 return nb;
1445
1446                         remain = file->length - ztk->in_position;
1447                         count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1448 #ifdef FS_USESYSCALLS
1449                         read (file->stream, ztk->input, count);
1450 #else
1451                         fread (ztk->input, 1, count, file->stream);
1452 #endif
1453
1454                         // Update indexes and counters
1455                         ztk->in_ind = 0;
1456                         ztk->in_max = count;
1457                         ztk->in_position += count;
1458                 }
1459
1460                 // Now that we are sure we have compressed data available, we need to determine
1461                 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1462                 // case if we still need more bytes than "output" can contain)
1463
1464                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1465                 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1466
1467                 // If output will be able to contain at least 1 more byte than the data we need
1468                 if (buffersize - nb < sizeof (ztk->output))
1469                 {
1470                         int error;
1471
1472                         // Inflate the data in "output"
1473                         ztk->zstream.next_out = ztk->output;
1474                         ztk->zstream.avail_out = sizeof (ztk->output);
1475                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1476                         if (error != Z_OK && error != Z_STREAM_END)
1477                                 Sys_Error ("Can't inflate file");
1478                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1479                         ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1480                         ztk->out_position += ztk->out_max;
1481
1482                         // Copy the requested data in "buffer" (as much as we can)
1483                         count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1484                         memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1485                         ztk->out_ind = count;
1486                 }
1487
1488                 // Else, we inflate directly in "buffer"
1489                 else
1490                 {
1491                         int error;
1492
1493                         // Inflate the data in "buffer"
1494                         ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1495                         ztk->zstream.avail_out = buffersize - nb;
1496                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1497                         if (error != Z_OK && error != Z_STREAM_END)
1498                                 Sys_Error ("Can't inflate file");
1499                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1500
1501                         // Invalidate the output data (for FS_Seek)
1502                         ztk->out_max = 0;
1503                         ztk->out_ind = 0;
1504
1505                         // How much data did it inflate?
1506                         count = buffersize - nb - ztk->zstream.avail_out;
1507                         ztk->out_position += count;
1508                 }
1509
1510                 nb += count;
1511                 file->position += count;
1512         }
1513
1514         return nb;
1515 }
1516
1517
1518 /*
1519 ====================
1520 FS_Flush
1521
1522 Flush the file output stream
1523 ====================
1524 */
1525 int FS_Flush (qfile_t* file)
1526 {
1527 #ifdef FS_USESYSCALLS
1528         return 0;
1529 #else
1530         return fflush (file->stream);
1531 #endif
1532 }
1533
1534
1535 /*
1536 ====================
1537 FS_Print
1538
1539 Print a string into a file
1540 ====================
1541 */
1542 int FS_Print(qfile_t* file, const char *msg)
1543 {
1544         return FS_Write(file, msg, strlen(msg));
1545 }
1546
1547 /*
1548 ====================
1549 FS_Printf
1550
1551 Print a string into a file
1552 ====================
1553 */
1554 int FS_Printf(qfile_t* file, const char* format, ...)
1555 {
1556         int result;
1557         va_list args;
1558
1559         va_start (args, format);
1560         result = FS_VPrintf(file, format, args);
1561         va_end (args);
1562
1563         return result;
1564 }
1565
1566
1567 /*
1568 ====================
1569 FS_VPrintf
1570
1571 Print a string into a file
1572 ====================
1573 */
1574 int FS_VPrintf(qfile_t* file, const char* format, va_list ap)
1575 {
1576 #ifdef FS_USESYSCALLS
1577 {
1578         int len;
1579         char tempstring[1024];
1580         len = vsnprintf (tempstring, sizeof(tempstring), format, ap);
1581         if (len >= sizeof(tempstring))
1582         {
1583                 int result;
1584                 char *temp = Mem_Alloc(tempmempool, len + 1);
1585                 len = vsnprintf (temp, len + 1, format, ap);
1586                 result = write (file->stream, temp, len);
1587                 Mem_Free(temp);
1588                 return result;
1589         }
1590         else
1591                 return write (file->stream, tempstring, len);
1592 }
1593 #else
1594         return vfprintf (file->stream, format, ap);
1595 #endif
1596 }
1597
1598
1599 /*
1600 ====================
1601 FS_Getc
1602
1603 Get the next character of a file
1604 ====================
1605 */
1606 int FS_Getc (qfile_t* file)
1607 {
1608         char c;
1609
1610         if (FS_Read (file, &c, 1) != 1)
1611                 return EOF;
1612
1613         return c;
1614 }
1615
1616
1617 /*
1618 ====================
1619 FS_Seek
1620
1621 Move the position index in a file
1622 ====================
1623 */
1624 int FS_Seek (qfile_t* file, long offset, int whence)
1625 {
1626         // Quick path for unpacked files
1627         if (! (file->flags & FS_FLAG_PACKED))
1628 #ifdef FS_USESYSCALLS
1629                 return lseek (file->stream, offset, whence);
1630 #else
1631                 return fseek (file->stream, offset, whence);
1632 #endif
1633
1634         // Seeking in compressed files is more a hack than anything else,
1635         // but we need to support it, so here it is.
1636         if (file->flags & FS_FLAG_DEFLATED)
1637         {
1638                 ztoolkit_t *ztk = file->z;
1639                 qbyte buffer [sizeof (ztk->output)];  // it's big to force inflating into buffer directly
1640
1641                 switch (whence)
1642                 {
1643                         case SEEK_CUR:
1644                                 offset += file->position;
1645                                 break;
1646
1647                         case SEEK_SET:
1648                                 break;
1649
1650                         case SEEK_END:
1651                                 offset += ztk->real_length;
1652                                 break;
1653
1654                         default:
1655                                 return -1;
1656                 }
1657                 if (offset < 0 || offset > (long) ztk->real_length)
1658                         return -1;
1659
1660                 // If we need to go back in the file
1661                 if (offset <= (long) file->position)
1662                 {
1663                         // If we still have the data we need in the output buffer
1664                         if (file->position - offset <= ztk->out_ind)
1665                         {
1666                                 ztk->out_ind -= file->position - offset;
1667                                 file->position = offset;
1668                                 return 0;
1669                         }
1670
1671                         // Else, we restart from the beginning of the file
1672                         ztk->in_ind = 0;
1673                         ztk->in_max = 0;
1674                         ztk->in_position = 0;
1675                         ztk->out_ind = 0;
1676                         ztk->out_max = 0;
1677                         ztk->out_position = 0;
1678                         file->position = 0;
1679 #ifdef FS_USESYSCALLS
1680                         lseek (file->stream, file->offset, SEEK_SET);
1681 #else
1682                         fseek (file->stream, file->offset, SEEK_SET);
1683 #endif
1684
1685                         // Reset the Zlib stream
1686                         ztk->zstream.next_in = ztk->input;
1687                         ztk->zstream.avail_in = 0;
1688                         qz_inflateReset (&ztk->zstream);
1689                 }
1690
1691                 // Skip all data until we reach the requested offset
1692                 while ((long) file->position < offset)
1693                 {
1694                         size_t diff = offset - file->position;
1695                         size_t count, len;
1696
1697                         count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1698                         len = FS_Read (file, buffer, count);
1699                         if (len != count)
1700                                 return -1;
1701                 }
1702
1703                 return 0;
1704         }
1705
1706         // Packed files receive a special treatment too, because
1707         // we need to make sure it doesn't go outside of the file
1708         switch (whence)
1709         {
1710                 case SEEK_CUR:
1711                         offset += file->position;
1712                         break;
1713
1714                 case SEEK_SET:
1715                         break;
1716
1717                 case SEEK_END:
1718                         offset += file->length;
1719                         break;
1720
1721                 default:
1722                         return -1;
1723         }
1724         if (offset < 0 || offset > (long) file->length)
1725                 return -1;
1726
1727 #ifdef FS_USESYSCALLS
1728         if (lseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1729                 return -1;
1730 #else
1731         if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1732                 return -1;
1733 #endif
1734         file->position = offset;
1735         return 0;
1736 }
1737
1738
1739 /*
1740 ====================
1741 FS_Tell
1742
1743 Give the current position in a file
1744 ====================
1745 */
1746 long FS_Tell (qfile_t* file)
1747 {
1748         if (file->flags & FS_FLAG_PACKED)
1749                 return file->position;
1750
1751 #ifdef FS_USESYSCALLS
1752         return lseek (file->stream, 0, SEEK_CUR);
1753 #else
1754         return ftell (file->stream);
1755 #endif
1756 }
1757
1758
1759 /*
1760 ====================
1761 FS_Gets
1762
1763 Extract a line from a file
1764 ====================
1765 */
1766 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1767 {
1768         size_t ind;
1769
1770         // Quick path for unpacked files
1771 #ifndef FS_USESYSCALLS
1772         if (! (file->flags & FS_FLAG_PACKED))
1773                 return fgets (buffer, buffersize, file->stream);
1774 #endif
1775
1776         for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1777         {
1778                 int c = FS_Getc (file);
1779                 switch (c)
1780                 {
1781                         // End of file
1782                         case -1:
1783                                 if (!ind)
1784                                         return NULL;
1785
1786                                 buffer[ind] = '\0';
1787                                 return buffer;
1788
1789                         // End of line
1790                         case '\r':
1791                         case '\n':
1792                                 buffer[ind] = '\n';
1793                                 buffer[ind + 1] = '\0';
1794                                 return buffer;
1795
1796                         default:
1797                                 buffer[ind] = c;
1798                 }
1799
1800         }
1801
1802         buffer[buffersize - 1] = '\0';
1803         return buffer;
1804 }
1805
1806
1807 /*
1808 ==========
1809 FS_Getline
1810
1811 Dynamic length version of fgets. DO NOT free the buffer.
1812 ==========
1813 */
1814 char *FS_Getline (qfile_t *file)
1815 {
1816         static int  size = 256;
1817         static char *buf = 0;
1818         char        *t;
1819         int         len;
1820
1821         if (!buf)
1822                 buf = Mem_Alloc (fs_mempool, size);
1823
1824         if (!FS_Gets (file, buf, size))
1825                 return 0;
1826
1827         len = strlen (buf);
1828         while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1829         {
1830                 t = Mem_Alloc (fs_mempool, size + 256);
1831                 memcpy(t, buf, size);
1832                 Mem_Free(buf);
1833                 size += 256;
1834                 buf = t;
1835                 if (!FS_Gets (file, buf + len, size - len))
1836                         break;
1837                 len = strlen (buf);
1838         }
1839         while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1840                 buf[len - 1] = 0;
1841         return buf;
1842 }
1843
1844
1845 /*
1846 ====================
1847 FS_Eof
1848
1849 Extract a line from a file
1850 ====================
1851 */
1852 // FIXME: remove this function?
1853 int FS_Eof (qfile_t* file)
1854 {
1855         if (file->flags & FS_FLAG_PACKED)
1856         {
1857                 if (file->flags & FS_FLAG_DEFLATED)
1858                         return (file->position == file->z->real_length);
1859
1860                 return (file->position == file->length);
1861         }
1862
1863 #ifdef FS_USESYSCALLS
1864         Sys_Error("FS_Eof: not implemented using syscalls\n");
1865         return false;
1866 #else
1867         return feof (file->stream);
1868 #endif
1869 }
1870
1871
1872 /*
1873 ============
1874 FS_LoadFile
1875
1876 Filename are relative to the quake directory.
1877 Always appends a 0 byte.
1878 ============
1879 */
1880 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1881 {
1882         qfile_t *h;
1883         qbyte *buf;
1884
1885         // look for it in the filesystem or pack files
1886         h = FS_Open (path, "rb", quiet);
1887         if (!h)
1888                 return NULL;
1889
1890         buf = Mem_Alloc(pool, fs_filesize+1);
1891         if (!buf)
1892                 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1893
1894         ((qbyte *)buf)[fs_filesize] = 0;
1895
1896         FS_Read (h, buf, fs_filesize);
1897         FS_Close (h);
1898
1899         return buf;
1900 }
1901
1902
1903 /*
1904 ============
1905 FS_WriteFile
1906
1907 The filename will be prefixed by the current game directory
1908 ============
1909 */
1910 qboolean FS_WriteFile (const char *filename, void *data, int len)
1911 {
1912         qfile_t *handle;
1913
1914         handle = FS_Open (filename, "wb", false);
1915         if (!handle)
1916         {
1917                 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1918                 return false;
1919         }
1920
1921         Con_DPrintf("FS_WriteFile: %s\n", filename);
1922         FS_Write (handle, data, len);
1923         FS_Close (handle);
1924         return true;
1925 }
1926
1927
1928 /*
1929 =============================================================================
1930
1931 OTHERS PUBLIC FUNCTIONS
1932
1933 =============================================================================
1934 */
1935
1936 /*
1937 ============
1938 FS_StripExtension
1939 ============
1940 */
1941 void FS_StripExtension (const char *in, char *out, size_t size_out)
1942 {
1943         char *last = NULL;
1944
1945         if (size_out == 0)
1946                 return;
1947
1948         while (*in && size_out > 1)
1949         {
1950                 if (*in == '.')
1951                         last = out;
1952                 else if (*in == '/' || *in == '\\' || *in == ':')
1953                         last = NULL;
1954                 *out++ = *in++;
1955                 size_out--;
1956         }
1957         if (last)
1958                 *last = 0;
1959         else
1960                 *out = 0;
1961 }
1962
1963
1964 /*
1965 ==================
1966 FS_DefaultExtension
1967 ==================
1968 */
1969 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1970 {
1971         const char *src;
1972
1973         // if path doesn't have a .EXT, append extension
1974         // (extension should include the .)
1975         src = path + strlen(path) - 1;
1976
1977         while (*src != '/' && src != path)
1978         {
1979                 if (*src == '.')
1980                         return;                 // it has an extension
1981                 src--;
1982         }
1983
1984         strlcat (path, extension, size_path);
1985 }
1986
1987
1988 /*
1989 ==================
1990 FS_FileExists
1991
1992 Look for a file in the packages and in the filesystem
1993 ==================
1994 */
1995 qboolean FS_FileExists (const char *filename)
1996 {
1997         return (FS_FindFile (filename, NULL, true) != NULL);
1998 }
1999
2000
2001 /*
2002 ==================
2003 FS_SysFileExists
2004
2005 Look for a file in the filesystem only
2006 ==================
2007 */
2008 qboolean FS_SysFileExists (const char *path)
2009 {
2010 #if WIN32
2011         FILE *f;
2012
2013         f = fopen (path, "rb");
2014         if (f)
2015         {
2016                 fclose (f);
2017                 return true;
2018         }
2019
2020         return false;
2021 #else
2022         struct stat buf;
2023
2024         if (stat (path,&buf) == -1)
2025                 return false;
2026
2027         return true;
2028 #endif
2029 }
2030
2031 void FS_mkdir (const char *path)
2032 {
2033 #if WIN32
2034         _mkdir (path);
2035 #else
2036         mkdir (path, 0777);
2037 #endif
2038 }
2039
2040 /*
2041 ===========
2042 FS_Search
2043
2044 Allocate and fill a search structure with information on matching filenames.
2045 ===========
2046 */
2047 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2048 {
2049         fssearch_t *search;
2050         searchpath_t *searchpath;
2051         pack_t *pak;
2052         int i, basepathlength, numfiles, numchars;
2053         stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
2054         const char *slash, *backslash, *colon, *separator;
2055         char *basepath;
2056         char netpath[MAX_OSPATH];
2057         char temp[MAX_OSPATH];
2058
2059         while(!strncmp(pattern, "./", 2))
2060                 pattern += 2;
2061         while(!strncmp(pattern, ".\\", 2))
2062                 pattern += 2;
2063
2064         search = NULL;
2065         liststart = NULL;
2066         listcurrent = NULL;
2067         listtemp = NULL;
2068         slash = strrchr(pattern, '/');
2069         backslash = strrchr(pattern, '\\');
2070         colon = strrchr(pattern, ':');
2071         separator = pattern;
2072         if (separator < slash)
2073                 separator = slash;
2074         if (separator < backslash)
2075                 separator = backslash;
2076         if (separator < colon)
2077                 separator = colon;
2078         basepathlength = separator - pattern;
2079         basepath = Mem_Alloc (tempmempool, basepathlength + 1);
2080         if (basepathlength)
2081                 memcpy(basepath, pattern, basepathlength);
2082         basepath[basepathlength] = 0;
2083
2084         // search through the path, one element at a time
2085         for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2086         {
2087                 // is the element a pak file?
2088                 if (searchpath->pack)
2089                 {
2090                         // look through all the pak file elements
2091                         pak = searchpath->pack;
2092                         for (i = 0;i < pak->numfiles;i++)
2093                         {
2094                                 strcpy(temp, pak->files[i].name);
2095                                 while (temp[0])
2096                                 {
2097                                         if (matchpattern(temp, (char *)pattern, true))
2098                                         {
2099                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2100                                                         if (!strcmp(listtemp->text, temp))
2101                                                                 break;
2102                                                 if (listtemp == NULL)
2103                                                 {
2104                                                         listcurrent = stringlistappend(listcurrent, temp);
2105                                                         if (liststart == NULL)
2106                                                                 liststart = listcurrent;
2107                                                         if (!quiet)
2108                                                                 Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2109                                                 }
2110                                         }
2111                                         // strip off one path element at a time until empty
2112                                         // this way directories are added to the listing if they match the pattern
2113                                         slash = strrchr(temp, '/');
2114                                         backslash = strrchr(temp, '\\');
2115                                         colon = strrchr(temp, ':');
2116                                         separator = temp;
2117                                         if (separator < slash)
2118                                                 separator = slash;
2119                                         if (separator < backslash)
2120                                                 separator = backslash;
2121                                         if (separator < colon)
2122                                                 separator = colon;
2123                                         *((char *)separator) = 0;
2124                                 }
2125                         }
2126                 }
2127                 else
2128                 {
2129                         // get a directory listing and look at each name
2130                         snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
2131                         if ((dir = listdirectory(netpath)))
2132                         {
2133                                 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2134                                 {
2135                                         snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
2136                                         if (matchpattern(temp, (char *)pattern, true))
2137                                         {
2138                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2139                                                         if (!strcmp(listtemp->text, temp))
2140                                                                 break;
2141                                                 if (listtemp == NULL)
2142                                                 {
2143                                                         listcurrent = stringlistappend(listcurrent, temp);
2144                                                         if (liststart == NULL)
2145                                                                 liststart = listcurrent;
2146                                                         if (!quiet)
2147                                                                 Sys_Printf("SearchDirFile: %s\n", temp);
2148                                                 }
2149                                         }
2150                                 }
2151                                 freedirectory(dir);
2152                         }
2153                 }
2154         }
2155
2156         if (liststart)
2157         {
2158                 liststart = stringlistsort(liststart);
2159                 numfiles = 0;
2160                 numchars = 0;
2161                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2162                 {
2163                         numfiles++;
2164                         numchars += strlen(listtemp->text) + 1;
2165                 }
2166                 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2167                 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2168                 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2169                 search->numfilenames = numfiles;
2170                 numfiles = 0;
2171                 numchars = 0;
2172                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2173                 {
2174                         search->filenames[numfiles] = search->filenamesbuffer + numchars;
2175                         strcpy(search->filenames[numfiles], listtemp->text);
2176                         numfiles++;
2177                         numchars += strlen(listtemp->text) + 1;
2178                 }
2179                 if (liststart)
2180                         stringlistfree(liststart);
2181         }
2182
2183         Mem_Free(basepath);
2184         return search;
2185 }
2186
2187 void FS_FreeSearch(fssearch_t *search)
2188 {
2189         Z_Free(search);
2190 }
2191
2192 extern int con_linewidth;
2193 int FS_ListDirectory(const char *pattern, int oneperline)
2194 {
2195         int numfiles;
2196         int numcolumns;
2197         int numlines;
2198         int columnwidth;
2199         int linebufpos;
2200         int i, j, k, l;
2201         const char *name;
2202         char linebuf[4096];
2203         fssearch_t *search;
2204         search = FS_Search(pattern, true, true);
2205         if (!search)
2206                 return 0;
2207         numfiles = search->numfilenames;
2208         if (!oneperline)
2209         {
2210                 // FIXME: the names could be added to one column list and then
2211                 // gradually shifted into the next column if they fit, and then the
2212                 // next to make a compact variable width listing but it's a lot more
2213                 // complicated...
2214                 // find width for columns
2215                 columnwidth = 0;
2216                 for (i = 0;i < numfiles;i++)
2217                 {
2218                         l = strlen(search->filenames[i]);
2219                         if (columnwidth < l)
2220                                 columnwidth = l;
2221                 }
2222                 // count the spacing character
2223                 columnwidth++;
2224                 // calculate number of columns
2225                 numcolumns = con_linewidth / columnwidth;
2226                 // don't bother with the column printing if it's only one column
2227                 if (numcolumns >= 2)
2228                 {
2229                         numlines = (numfiles + numcolumns - 1) / numcolumns;
2230                         for (i = 0;i < numlines;i++)
2231                         {
2232                                 linebufpos = 0;
2233                                 for (k = 0;k < numcolumns;k++)
2234                                 {
2235                                         l = i * numcolumns + k;
2236                                         if (l < numfiles)
2237                                         {
2238                                                 name = search->filenames[l];
2239                                                 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2240                                                         linebuf[linebufpos++] = name[j];
2241                                                 // space out name unless it's the last on the line
2242                                                 if (k < (numcolumns - 1) && l < (numfiles - 1))
2243                                                         for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2244                                                                 linebuf[linebufpos++] = ' ';
2245                                         }
2246                                 }
2247                                 linebuf[linebufpos] = 0;
2248                                 Con_Printf("%s\n", linebuf);
2249                         }
2250                 }
2251                 else
2252                         oneperline = true;
2253         }
2254         if (oneperline)
2255                 for (i = 0;i < numfiles;i++)
2256                         Con_Printf("%s\n", search->filenames[i]);
2257         FS_FreeSearch(search);
2258         return numfiles;
2259 }
2260
2261 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2262 {
2263         const char *pattern;
2264         if (Cmd_Argc() > 3)
2265         {
2266                 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2267                 return;
2268         }
2269         if (Cmd_Argc() == 2)
2270                 pattern = Cmd_Argv(1);
2271         else
2272                 pattern = "*";
2273         if (!FS_ListDirectory(pattern, oneperline))
2274                 Con_Print("No files found.\n");
2275 }
2276
2277 void FS_Dir_f(void)
2278 {
2279         FS_ListDirectoryCmd("dir", true);
2280 }
2281
2282 void FS_Ls_f(void)
2283 {
2284         FS_ListDirectoryCmd("ls", false);
2285 }
2286