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