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