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