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