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