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