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