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