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