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