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