]> icculus.org git repositories - divverent/darkplaces.git/blob - fs.c
removed pragma that disabled signed/unsigned mismatch warnings in MSVC4, as these...
[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.1";
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
820 /*
821 ================
822 FS_Init
823 ================
824 */
825 void FS_Init (void)
826 {
827         int i;
828         searchpath_t *search;
829
830         fs_mempool = Mem_AllocPool("file management");
831         pak_mempool = Mem_AllocPool("paks");
832
833         Cmd_AddCommand ("path", FS_Path_f);
834
835         strcpy(fs_basedir, ".");
836
837         PK3_OpenLibrary ();
838
839         // -basedir <path>
840         // Overrides the system supplied base directory (under GAMENAME)
841         i = COM_CheckParm ("-basedir");
842         if (i && i < com_argc-1)
843                 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
844
845         i = strlen (fs_basedir);
846         if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
847                 fs_basedir[i-1] = 0;
848
849         // start up with GAMENAME by default (id1)
850         strlcpy (com_modname, GAMENAME, sizeof (com_modname));
851         FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
852         if (gamedirname[0])
853         {
854                 fs_modified = true;
855                 strlcpy (com_modname, gamedirname, sizeof (com_modname));
856                 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
857         }
858
859         // -game <gamedir>
860         // Adds basedir/gamedir as an override game
861         i = COM_CheckParm ("-game");
862         if (i && i < com_argc-1)
863         {
864                 fs_modified = true;
865                 strlcpy (com_modname, com_argv[i+1], sizeof (com_modname));
866                 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
867         }
868
869         // -path <dir or packfile> [<dir or packfile>] ...
870         // Fully specifies the exact search path, overriding the generated one
871         i = COM_CheckParm ("-path");
872         if (i)
873         {
874                 fs_modified = true;
875                 fs_searchpaths = NULL;
876                 while (++i < com_argc)
877                 {
878                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
879                                 break;
880
881                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
882                         if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
883                         {
884                                 search->pack = FS_LoadPackPAK (com_argv[i]);
885                                 if (!search->pack)
886                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
887                         }
888                         else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
889                         {
890                                 search->pack = FS_LoadPackPK3 (com_argv[i]);
891                                 if (!search->pack)
892                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
893                         }
894                         else
895                                 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
896                         search->next = fs_searchpaths;
897                         fs_searchpaths = search;
898                 }
899         }
900 }
901
902
903 /*
904 ====================
905 FS_SysOpen
906
907 Internal function used to create a qfile_t and open the relevant file on disk
908 ====================
909 */
910 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
911 {
912         qfile_t* file;
913
914         file = Mem_Alloc (fs_mempool, sizeof (*file));
915         memset (file, 0, sizeof (*file));
916
917         file->stream = fopen (filepath, mode);
918         if (!file->stream)
919         {
920                 Mem_Free (file);
921                 return NULL;
922         }
923
924         return file;
925 }
926
927
928 /*
929 ===========
930 FS_OpenRead
931 ===========
932 */
933 qfile_t *FS_OpenRead (const char *path, int offs, int len)
934 {
935         qfile_t* file;
936
937         file = FS_SysOpen (path, "rb");
938         if (!file)
939         {
940                 Sys_Error ("Couldn't open %s", path);
941                 return NULL;
942         }
943
944         // Normal file
945         if (offs < 0 || len < 0)
946         {
947                 // We set fs_filesize here for normal files
948                 fseek (file->stream, 0, SEEK_END);
949                 fs_filesize = ftell (file->stream);
950                 fseek (file->stream, 0, SEEK_SET);
951         }
952         // Packed file
953         else
954         {
955                 fseek (file->stream, offs, SEEK_SET);
956
957                 file->flags |= FS_FLAG_PACKED;
958                 file->length = len;
959                 file->offset = offs;
960                 file->position = 0;
961         }
962
963         return file;
964 }
965
966 /*
967 ===========
968 FS_FOpenFile
969
970 If the requested file is inside a packfile, a new qfile_t* will be opened
971 into the file.
972
973 Sets fs_filesize
974 ===========
975 */
976 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
977 {
978         searchpath_t *search;
979         char netpath[MAX_OSPATH];
980         pack_t *pak;
981         int i, filenamelen, matched;
982
983         filenamelen = strlen (filename);
984
985                 // search through the path, one element at a time
986         search = fs_searchpaths;
987
988         for( ; search ; search = search->next)
989                 if(!search->pack)
990                 {
991                         snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename);
992                         
993                         if (!FS_SysFileExists (netpath))
994                                 continue;
995                         
996                         if (!quiet)
997                                 Sys_Printf ("FindFile: %s\n",netpath);
998                         return FS_OpenRead (netpath, -1, -1);
999                 }
1000                 
1001 #ifdef AKVERSION                
1002         search = fs_searchpaths;
1003         for ( ; search ; search = search->next)
1004                 // is the element a pak file?
1005                 if (search->pack)
1006                 {
1007                         // look through all the pak file elements
1008                         pak = search->pack;
1009                         for (i=0 ; i<pak->numfiles ; i++)
1010                         {
1011                                 if (pak->ignorecase)
1012                                         matched = !strcasecmp (pak->files[i].name, filename);
1013                                 else
1014                                         matched = !strcmp (pak->files[i].name, filename);
1015                                 if (matched)  // found it?
1016                                 {
1017                                         qfile_t *file;
1018
1019                                         if (!quiet)
1020                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1021
1022                                         // If we don't have the true offset, get it now
1023                                         if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS))
1024                                                 PK3_GetTrueFileOffset (&pak->files[i], pak);
1025
1026                                         // No Zlib DLL = no compressed files
1027                                         if (!zlib_dll && (pak->files[i].flags & FILE_FLAG_DEFLATED))
1028                                         {
1029                                                 Con_Printf ("WARNING: can't open the compressed file %s\n"
1030                                                                         "You need the Zlib DLL to use compressed files\n", filename);
1031                                                 fs_filesize = -1;
1032                                                 return NULL;
1033                                         }
1034
1035                                         // open a new file in the pakfile
1036                                         file = FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize);
1037                                         fs_filesize = pak->files[i].realsize;
1038
1039                                         if (pak->files[i].flags & FILE_FLAG_DEFLATED)
1040                                         {
1041                                                 ztoolkit_t *ztk;
1042
1043                                                 file->flags |= FS_FLAG_DEFLATED;
1044
1045                                                 // We need some more variables
1046                                                 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1047
1048                                                 ztk->real_length = pak->files[i].realsize;
1049
1050                                                 // Initialize zlib stream
1051                                                 ztk->zstream.next_in = ztk->input;
1052                                                 ztk->zstream.avail_in = 0;
1053
1054                                                 /* From Zlib's "unzip.c":
1055                                                  *
1056                                                  * windowBits is passed < 0 to tell that there is no zlib header.
1057                                                  * Note that in this case inflate *requires* an extra "dummy" byte
1058                                                  * after the compressed stream in order to complete decompression and
1059                                                  * return Z_STREAM_END.
1060                                                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1061                                                  * size of both compressed and uncompressed data
1062                                                  */
1063                                                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1064                                                         Sys_Error ("inflate init error (file: %s)", filename);
1065
1066                                                 ztk->zstream.next_out = ztk->output;
1067                                                 ztk->zstream.avail_out = sizeof (ztk->output);
1068
1069                                                 file->z = ztk;
1070                                         }
1071
1072                                         return file;
1073                                 }
1074                         }
1075                 }
1076 #else
1077         // search through the path, one element at a time
1078         search = fs_searchpaths;
1079
1080         for ( ; search ; search = search->next)
1081         {
1082                 // is the element a pak file?
1083                 if (search->pack)
1084                 {
1085                         // look through all the pak file elements
1086                         pak = search->pack;
1087                         for (i=0 ; i<pak->numfiles ; i++)
1088                         {
1089                                 if (pak->ignorecase)
1090                                         matched = !strcasecmp (pak->files[i].name, filename);
1091                                 else
1092                                         matched = !strcmp (pak->files[i].name, filename);
1093                                 if (matched)  // found it?
1094                                 {
1095                                         qfile_t *file;
1096
1097                                         if (!quiet)
1098                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1099
1100                                         // If we don't have the true offset, get it now
1101                                         if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS))
1102                                                 PK3_GetTrueFileOffset (&pak->files[i], pak);
1103
1104                                         // No Zlib DLL = no compressed files
1105                                         if (!zlib_dll && (pak->files[i].flags & FILE_FLAG_DEFLATED))
1106                                         {
1107                                                 Con_Printf ("WARNING: can't open the compressed file %s\n"
1108                                                                         "You need the Zlib DLL to use compressed files\n", filename);
1109                                                 fs_filesize = -1;
1110                                                 return NULL;
1111                                         }
1112
1113                                         // open a new file in the pakfile
1114                                         file = FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize);
1115                                         fs_filesize = pak->files[i].realsize;
1116
1117                                         if (pak->files[i].flags & FILE_FLAG_DEFLATED)
1118                                         {
1119                                                 ztoolkit_t *ztk;
1120
1121                                                 file->flags |= FS_FLAG_DEFLATED;
1122
1123                                                 // We need some more variables
1124                                                 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1125
1126                                                 ztk->real_length = pak->files[i].realsize;
1127
1128                                                 // Initialize zlib stream
1129                                                 ztk->zstream.next_in = ztk->input;
1130                                                 ztk->zstream.avail_in = 0;
1131
1132                                                 /* From Zlib's "unzip.c":
1133                                                  *
1134                                                  * windowBits is passed < 0 to tell that there is no zlib header.
1135                                                  * Note that in this case inflate *requires* an extra "dummy" byte
1136                                                  * after the compressed stream in order to complete decompression and
1137                                                  * return Z_STREAM_END.
1138                                                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1139                                                  * size of both compressed and uncompressed data
1140                                                  */
1141                                                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1142                                                         Sys_Error ("inflate init error (file: %s)", filename);
1143
1144                                                 ztk->zstream.next_out = ztk->output;
1145                                                 ztk->zstream.avail_out = sizeof (ztk->output);
1146
1147                                                 file->z = ztk;
1148                                         }
1149
1150                                         return file;
1151                                 }
1152                         }
1153                 }
1154                 else
1155                 {
1156                         snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename);
1157
1158                         if (!FS_SysFileExists (netpath))
1159                                 continue;
1160
1161                         if (!quiet)
1162                                 Sys_Printf ("FindFile: %s\n",netpath);
1163                         return FS_OpenRead (netpath, -1, -1);
1164                 }
1165         }
1166 #endif
1167         
1168         if (!quiet)
1169                 Sys_Printf ("FindFile: can't find %s\n", filename);
1170
1171         fs_filesize = -1;
1172         return NULL;
1173 }
1174
1175
1176 /*
1177 =============================================================================
1178
1179 MAIN PUBLIC FUNCTIONS
1180
1181 =============================================================================
1182 */
1183
1184 /*
1185 ====================
1186 FS_Open
1187
1188 Open a file. The syntax is the same as fopen
1189 ====================
1190 */
1191 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1192 {
1193         // If the file is opened in "write" or "append" mode
1194         if (strchr (mode, 'w') || strchr (mode, 'a'))
1195         {
1196                 char real_path [MAX_OSPATH];
1197
1198                 // Open the file on disk directly
1199                 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1200
1201                 // Create directories up to the file
1202                 FS_CreatePath (real_path);
1203
1204                 return FS_SysOpen (real_path, mode);
1205         }
1206
1207         // Else, we look at the various search paths
1208         return FS_FOpenFile (filepath, quiet);
1209 }
1210
1211
1212 /*
1213 ====================
1214 FS_Close
1215
1216 Close a file
1217 ====================
1218 */
1219 int FS_Close (qfile_t* file)
1220 {
1221         if (fclose (file->stream))
1222                 return EOF;
1223
1224         if (file->z)
1225         {
1226                 qz_inflateEnd (&file->z->zstream);
1227                 Mem_Free (file->z);
1228         }
1229
1230         Mem_Free (file);
1231         return 0;
1232 }
1233
1234
1235 /*
1236 ====================
1237 FS_Write
1238
1239 Write "datasize" bytes into a file
1240 ====================
1241 */
1242 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1243 {
1244         return fwrite (data, 1, datasize, file->stream);
1245 }
1246
1247
1248 /*
1249 ====================
1250 FS_Read
1251
1252 Read up to "buffersize" bytes from a file
1253 ====================
1254 */
1255 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1256 {
1257         size_t count, nb;
1258         ztoolkit_t *ztk;
1259
1260         // Quick path for unpacked files
1261         if (! (file->flags & FS_FLAG_PACKED))
1262                 return fread (buffer, 1, buffersize, file->stream);
1263
1264         // If the file isn't compressed
1265         if (! (file->flags & FS_FLAG_DEFLATED))
1266         {
1267                 // We must take care to not read after the end of the file
1268                 count = file->length - file->position;
1269                 if (buffersize > count)
1270                         buffersize = count;
1271
1272                 nb = fread (buffer, 1, buffersize, file->stream);
1273
1274                 file->position += nb;
1275                 return nb;
1276         }
1277
1278         // If the file is compressed, it's more complicated...
1279         ztk = file->z;
1280
1281         // First, we copy as many bytes as we can from "output"
1282         if (ztk->out_ind < ztk->out_max)
1283         {
1284                 count = ztk->out_max - ztk->out_ind;
1285
1286                 nb = (buffersize > count) ? count : buffersize;
1287                 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1288                 ztk->out_ind += nb;
1289                 file->position += nb;
1290         }
1291         else
1292                 nb = 0;
1293
1294         // We cycle through a few operations until we have inflated enough data
1295         while (nb < buffersize)
1296         {
1297                 // NOTE: at this point, "output" should always be empty
1298
1299                 // If "input" is also empty, we need to fill it
1300                 if (ztk->in_ind == ztk->in_max)
1301                 {
1302                         size_t remain = file->length - ztk->in_position;
1303
1304                         // If we are at the end of the file
1305                         if (!remain)
1306                                 return nb;
1307
1308                         count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1309                         fread (ztk->input, 1, count, file->stream);
1310
1311                         // Update indexes and counters
1312                         ztk->in_ind = 0;
1313                         ztk->in_max = count;
1314                         ztk->in_position += count;
1315                 }
1316
1317                 // Now that we are sure we have compressed data available, we need to determine
1318                 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1319                 // case if we still need more bytes than "output" can contain)
1320
1321                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1322                 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1323
1324                 // If output will be able to contain at least 1 more byte than the data we need
1325                 if (buffersize - nb < sizeof (ztk->output))
1326                 {
1327                         int error;
1328
1329                         // Inflate the data in "output"
1330                         ztk->zstream.next_out = ztk->output;
1331                         ztk->zstream.avail_out = sizeof (ztk->output);
1332                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1333                         if (error != Z_OK && error != Z_STREAM_END)
1334                                 Sys_Error ("Can't inflate file");
1335                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1336                         ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1337                         ztk->out_position += ztk->out_max;
1338
1339                         // Copy the requested data in "buffer" (as much as we can)
1340                         count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1341                         memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1342                         ztk->out_ind = count;
1343                 }
1344
1345                 // Else, we inflate directly in "buffer"
1346                 else
1347                 {
1348                         int error;
1349
1350                         // Inflate the data in "buffer"
1351                         ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1352                         ztk->zstream.avail_out = buffersize - nb;
1353                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1354                         if (error != Z_OK && error != Z_STREAM_END)
1355                                 Sys_Error ("Can't inflate file");
1356                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1357
1358                         // Invalidate the output data (for FS_Seek)
1359                         ztk->out_max = 0;
1360                         ztk->out_ind = 0;
1361
1362                         // How much data did it inflate?
1363                         count = buffersize - nb - ztk->zstream.avail_out;
1364                         ztk->out_position += count;
1365                 }
1366
1367                 nb += count;
1368                 file->position += count;
1369         }
1370
1371         return nb;
1372 }
1373
1374
1375 /*
1376 ====================
1377 FS_Flush
1378
1379 Flush the file output stream
1380 ====================
1381 */
1382 int FS_Flush (qfile_t* file)
1383 {
1384         return fflush (file->stream);
1385 }
1386
1387
1388 /*
1389 ====================
1390 FS_Printf
1391
1392 Print a string into a file
1393 ====================
1394 */
1395 int FS_Printf (qfile_t* file, const char* format, ...)
1396 {
1397         int result;
1398         va_list args;
1399
1400         va_start (args, format);
1401         result = vfprintf (file->stream, format, args);
1402         va_end (args);
1403
1404         return result;
1405 }
1406
1407
1408 /*
1409 ====================
1410 FS_Getc
1411
1412 Get the next character of a file
1413 ====================
1414 */
1415 int FS_Getc (qfile_t* file)
1416 {
1417         char c;
1418
1419         if (FS_Read (file, &c, 1) != 1)
1420                 return EOF;
1421
1422         return c;
1423 }
1424
1425
1426 /*
1427 ====================
1428 FS_Seek
1429
1430 Move the position index in a file
1431 ====================
1432 */
1433 int FS_Seek (qfile_t* file, long offset, int whence)
1434 {
1435         // Quick path for unpacked files
1436         if (! (file->flags & FS_FLAG_PACKED))
1437                 return fseek (file->stream, offset, whence);
1438
1439         // Seeking in compressed files is more a hack than anything else,
1440         // but we need to support it, so here it is.
1441         if (file->flags & FS_FLAG_DEFLATED)
1442         {
1443                 ztoolkit_t *ztk = file->z;
1444                 qbyte buffer [sizeof (ztk->output)];  // it's big to force inflating into buffer directly
1445
1446                 switch (whence)
1447                 {
1448                         case SEEK_CUR:
1449                                 offset += file->position;
1450                                 break;
1451
1452                         case SEEK_SET:
1453                                 break;
1454
1455                         case SEEK_END:
1456                                 offset += ztk->real_length;
1457                                 break;
1458
1459                         default:
1460                                 return -1;
1461                 }
1462                 if (offset < 0 || offset > (long) ztk->real_length)
1463                         return -1;
1464
1465                 // If we need to go back in the file
1466                 if (offset <= (long) file->position)
1467                 {
1468                         // If we still have the data we need in the output buffer
1469                         if (file->position - offset <= ztk->out_ind)
1470                         {
1471                                 ztk->out_ind -= file->position - offset;
1472                                 file->position = offset;
1473                                 return 0;
1474                         }
1475
1476                         // Else, we restart from the beginning of the file
1477                         ztk->in_ind = 0;
1478                         ztk->in_max = 0;
1479                         ztk->in_position = 0;
1480                         ztk->out_ind = 0;
1481                         ztk->out_max = 0;
1482                         ztk->out_position = 0;
1483                         file->position = 0;
1484                         fseek (file->stream, file->offset, SEEK_SET);
1485
1486                         // Reset the Zlib stream
1487                         ztk->zstream.next_in = ztk->input;
1488                         ztk->zstream.avail_in = 0;
1489                         qz_inflateReset (&ztk->zstream);
1490                 }
1491
1492                 // Skip all data until we reach the requested offset
1493                 while ((long) file->position < offset)
1494                 {
1495                         size_t diff = offset - file->position;
1496                         size_t count, len;
1497
1498                         count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1499                         len = FS_Read (file, buffer, count);
1500                         if (len != count)
1501                                 return -1;
1502                 }
1503
1504                 return 0;
1505         }
1506
1507         // Packed files receive a special treatment too, because
1508         // we need to make sure it doesn't go outside of the file
1509         switch (whence)
1510         {
1511                 case SEEK_CUR:
1512                         offset += file->position;
1513                         break;
1514
1515                 case SEEK_SET:
1516                         break;
1517
1518                 case SEEK_END:
1519                         offset += file->length;
1520                         break;
1521
1522                 default:
1523                         return -1;
1524         }
1525         if (offset < 0 || offset > (long) file->length)
1526                 return -1;
1527
1528         if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1529                 return -1;
1530         file->position = offset;
1531         return 0;
1532 }
1533
1534
1535 /*
1536 ====================
1537 FS_Tell
1538
1539 Give the current position in a file
1540 ====================
1541 */
1542 long FS_Tell (qfile_t* file)
1543 {
1544         if (file->flags & FS_FLAG_PACKED)
1545                 return file->position;
1546
1547         return ftell (file->stream);
1548 }
1549
1550
1551 /*
1552 ====================
1553 FS_Gets
1554
1555 Extract a line from a file
1556 ====================
1557 */
1558 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1559 {
1560         size_t ind;
1561
1562         // Quick path for unpacked files
1563         if (! (file->flags & FS_FLAG_PACKED))
1564                 return fgets (buffer, buffersize, file->stream);
1565
1566         for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1567         {
1568                 int c = FS_Getc (file);
1569                 switch (c)
1570                 {
1571                         // End of file
1572                         case -1:
1573                                 if (!ind)
1574                                         return NULL;
1575
1576                                 buffer[ind] = '\0';
1577                                 return buffer;
1578
1579                         // End of line
1580                         case '\r':
1581                         case '\n':
1582                                 buffer[ind] = '\n';
1583                                 buffer[ind + 1] = '\0';
1584                                 return buffer;
1585
1586                         default:
1587                                 buffer[ind] = c;
1588                 }
1589
1590         }
1591
1592         buffer[buffersize - 1] = '\0';
1593         return buffer;
1594 }
1595
1596
1597 /*
1598 ==========
1599 FS_Getline
1600
1601 Dynamic length version of fgets. DO NOT free the buffer.
1602 ==========
1603 */
1604 char *FS_Getline (qfile_t *file)
1605 {
1606         static int  size = 256;
1607         static char *buf = 0;
1608         char        *t;
1609         int         len;
1610
1611         if (!buf)
1612                 buf = Mem_Alloc (fs_mempool, size);
1613
1614         if (!FS_Gets (file, buf, size))
1615                 return 0;
1616
1617         len = strlen (buf);
1618         while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1619         {
1620                 t = Mem_Alloc (fs_mempool, size + 256);
1621                 memcpy(t, buf, size);
1622                 Mem_Free(buf);
1623                 size += 256;
1624                 buf = t;
1625                 if (!FS_Gets (file, buf + len, size - len))
1626                         break;
1627                 len = strlen (buf);
1628         }
1629         while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1630                 buf[len - 1] = 0;
1631         return buf;
1632 }
1633
1634
1635 /*
1636 ====================
1637 FS_Eof
1638
1639 Extract a line from a file
1640 ====================
1641 */
1642 int FS_Eof (qfile_t* file)
1643 {
1644         if (file->flags & FS_FLAG_PACKED)
1645         {
1646                 if (file->flags & FS_FLAG_DEFLATED)
1647                         return (file->position == file->z->real_length);
1648
1649                 return (file->position == file->length);
1650         }
1651
1652         return feof (file->stream);
1653 }
1654
1655
1656 /*
1657 ============
1658 FS_LoadFile
1659
1660 Filename are relative to the quake directory.
1661 Always appends a 0 byte.
1662 ============
1663 */
1664 qbyte *FS_LoadFile (const char *path, qboolean quiet)
1665 {
1666         qfile_t *h;
1667         qbyte *buf;
1668
1669         // look for it in the filesystem or pack files
1670         h = FS_Open (path, "rb", quiet);
1671         if (!h)
1672                 return NULL;
1673
1674         buf = Mem_Alloc(tempmempool, fs_filesize+1);
1675         if (!buf)
1676                 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1677
1678         ((qbyte *)buf)[fs_filesize] = 0;
1679
1680         FS_Read (h, buf, fs_filesize);
1681         FS_Close (h);
1682
1683         return buf;
1684 }
1685
1686
1687 /*
1688 ============
1689 FS_WriteFile
1690
1691 The filename will be prefixed by the current game directory
1692 ============
1693 */
1694 qboolean FS_WriteFile (const char *filename, void *data, int len)
1695 {
1696         FILE *handle;
1697         char name[MAX_OSPATH];
1698
1699         snprintf (name, sizeof (name), "%s/%s", fs_gamedir, filename);
1700
1701         // Create directories up to the file
1702         FS_CreatePath (name);
1703
1704         handle = fopen (name, "wb");
1705         if (!handle)
1706         {
1707                 Con_Printf ("FS_WriteFile: failed on %s\n", name);
1708                 return false;
1709         }
1710
1711         Con_DPrintf ("FS_WriteFile: %s\n", name);
1712         fwrite (data, 1, len, handle);
1713         fclose (handle);
1714         return true;
1715 }
1716
1717
1718 /*
1719 =============================================================================
1720
1721 OTHERS PUBLIC FUNCTIONS
1722
1723 =============================================================================
1724 */
1725
1726 /*
1727 ============
1728 FS_StripExtension
1729 ============
1730 */
1731 void FS_StripExtension (const char *in, char *out, size_t size_out)
1732 {
1733         char *last = NULL;
1734
1735         if (size_out == 0)
1736                 return;
1737
1738         while (*in && size_out > 1)
1739         {
1740                 if (*in == '.')
1741                         last = out;
1742                 else if (*in == '/' || *in == '\\' || *in == ':')
1743                         last = NULL;
1744                 *out++ = *in++;
1745                 size_out--;
1746         }
1747         if (last)
1748                 *last = 0;
1749         else
1750                 *out = 0;
1751 }
1752
1753
1754 /*
1755 ==================
1756 FS_DefaultExtension
1757 ==================
1758 */
1759 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1760 {
1761         const char *src;
1762
1763         // if path doesn't have a .EXT, append extension
1764         // (extension should include the .)
1765         src = path + strlen(path) - 1;
1766
1767         while (*src != '/' && src != path)
1768         {
1769                 if (*src == '.')
1770                         return;                 // it has an extension
1771                 src--;
1772         }
1773
1774         strlcat (path, extension, size_path);
1775 }
1776
1777
1778 qboolean FS_FileExists (const char *filename)
1779 {
1780         searchpath_t *search;
1781         char netpath[MAX_OSPATH];
1782         pack_t *pak;
1783         int i;
1784
1785         for (search = fs_searchpaths;search;search = search->next)
1786         {
1787                 if (search->pack)
1788                 {
1789                         pak = search->pack;
1790                         for (i = 0;i < pak->numfiles;i++)
1791                                 if (!strcmp (pak->files[i].name, filename))
1792                                         return true;
1793                 }
1794                 else
1795                 {
1796                         snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename);
1797                         if (FS_SysFileExists (netpath))
1798                                 return true;
1799                 }
1800         }
1801
1802         return false;
1803 }
1804
1805
1806 qboolean FS_SysFileExists (const char *path)
1807 {
1808 #if WIN32
1809         FILE *f;
1810
1811         f = fopen (path, "rb");
1812         if (f)
1813         {
1814                 fclose (f);
1815                 return true;
1816         }
1817
1818         return false;
1819 #else
1820         struct stat buf;
1821
1822         if (stat (path,&buf) == -1)
1823                 return false;
1824
1825         return true;
1826 #endif
1827 }
1828
1829 void FS_mkdir (const char *path)
1830 {
1831 #if WIN32
1832         _mkdir (path);
1833 #else
1834         mkdir (path, 0777);
1835 #endif
1836 }