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