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