]> icculus.org git repositories - divverent/darkplaces.git/blob - fs.c
more preparation for quake2 bsp support
[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;
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) != 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 (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         if (eocd.nbentries > MAX_FILES_IN_PACK)
542                 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
543
544         // Create a package structure in memory
545         pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
546         pack->ignorecase = true; // LordHavoc: pk3 ignores case
547         strcpy (pack->filename, packfile);
548         pack->handle = packhandle;
549         pack->numfiles = eocd.nbentries;
550         pack->mempool = Mem_AllocPool (packfile);
551         pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
552         pack->next = packlist;
553         packlist = pack;
554
555         real_nb_files = PK3_BuildFileList (pack, &eocd);
556         if (real_nb_files <= 0)
557                 Sys_Error ("%s is not a valid PK3 file", packfile);
558
559         Con_Printf ("Added packfile %s (%i files)\n", packfile, real_nb_files);
560         return pack;
561 }
562
563
564 /*
565 ====================
566 PK3_GetTrueFileOffset
567
568 Find where the true file data offset is
569 ====================
570 */
571 void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
572 {
573         qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
574         size_t count;
575
576         // Already found?
577         if (file->flags & FILE_FLAG_TRUEOFFS)
578                 return;
579
580         // Load the local file description
581         fseek (pack->handle, file->offset, SEEK_SET);
582         count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
583         if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
584                 Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
585
586         // Skip name and extra field
587         file->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
588
589         file->flags |= FILE_FLAG_TRUEOFFS;
590 }
591
592
593 /*
594 =============================================================================
595
596 OTHER PRIVATE FUNCTIONS
597
598 =============================================================================
599 */
600
601
602 /*
603 ============
604 FS_CreatePath
605
606 Only used for FS_WriteFile.
607 ============
608 */
609 void FS_CreatePath (char *path)
610 {
611         char *ofs, save;
612
613         for (ofs = path+1 ; *ofs ; ofs++)
614         {
615                 if (*ofs == '/' || *ofs == '\\')
616                 {
617                         // create the directory
618                         save = *ofs;
619                         *ofs = 0;
620                         FS_mkdir (path);
621                         *ofs = save;
622                 }
623         }
624 }
625
626
627 /*
628 ============
629 FS_Path_f
630
631 ============
632 */
633 void FS_Path_f (void)
634 {
635         searchpath_t *s;
636
637         Con_Printf ("Current search path:\n");
638         for (s=fs_searchpaths ; s ; s=s->next)
639         {
640                 if (s->pack)
641                 {
642                         Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
643                 }
644                 else
645                         Con_Printf ("%s\n", s->filename);
646         }
647 }
648
649
650 /*
651 =================
652 FS_LoadPackPAK
653
654 Takes an explicit (not game tree related) path to a pak file.
655
656 Loads the header and directory, adding the files at the beginning
657 of the list so they override previous pack files.
658 =================
659 */
660 pack_t *FS_LoadPackPAK (const char *packfile)
661 {
662         dpackheader_t header;
663         int i, numpackfiles;
664         FILE *packhandle;
665         pack_t *pack;
666         dpackfile_t *info;      // temporary alloc, allowing huge pack directories
667
668         packhandle = fopen (packfile, "rb");
669         if (!packhandle)
670                 return NULL;
671
672         fread ((void *)&header, 1, sizeof(header), packhandle);
673         if (memcmp(header.id, "PACK", 4))
674                 Sys_Error ("%s is not a packfile", packfile);
675         header.dirofs = LittleLong (header.dirofs);
676         header.dirlen = LittleLong (header.dirlen);
677
678         if (header.dirlen % sizeof(dpackfile_t))
679                 Sys_Error ("%s has an invalid directory size", packfile);
680
681         numpackfiles = header.dirlen / sizeof(dpackfile_t);
682
683         if (numpackfiles > MAX_FILES_IN_PACK)
684                 Sys_Error ("%s has %i files", packfile, numpackfiles);
685
686         pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
687         pack->ignorecase = false; // LordHavoc: pak is case sensitive
688         strcpy (pack->filename, packfile);
689         pack->handle = packhandle;
690         pack->numfiles = numpackfiles;
691         pack->mempool = Mem_AllocPool(packfile);
692         pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
693         pack->next = packlist;
694         packlist = pack;
695
696         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
697         fseek (packhandle, header.dirofs, SEEK_SET);
698         fread ((void *)info, 1, header.dirlen, packhandle);
699
700         // parse the directory
701         for (i = 0;i < numpackfiles;i++)
702         {
703                 size_t size;
704                 packfile_t *file = &pack->files[i];
705
706                 strcpy (file->name, info[i].name);
707                 file->offset = LittleLong(info[i].filepos);
708                 size = LittleLong (info[i].filelen);
709                 file->packsize = size;
710                 file->realsize = size;
711                 file->flags = FILE_FLAG_TRUEOFFS;
712         }
713
714         Mem_Free(info);
715
716         Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
717         return pack;
718 }
719
720
721 /*
722 ================
723 FS_AddGameDirectory
724
725 Sets fs_gamedir, adds the directory to the head of the path,
726 then loads and adds pak1.pak pak2.pak ...
727 ================
728 */
729 void FS_AddGameDirectory (char *dir)
730 {
731         stringlist_t *list, *current;
732         searchpath_t *search;
733         pack_t *pak;
734         char pakfile[MAX_OSPATH];
735
736         strcpy (fs_gamedir, dir);
737
738         // add the directory to the search path
739         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
740         strcpy (search->filename, dir);
741         search->next = fs_searchpaths;
742         fs_searchpaths = search;
743
744         list = listdirectory(dir);
745
746         // add any PAK package in the directory
747         for (current = list;current;current = current->next)
748         {
749                 if (matchpattern(current->text, "*.pak", true))
750                 {
751                         sprintf (pakfile, "%s/%s", dir, current->text);
752                         pak = FS_LoadPackPAK (pakfile);
753                         if (pak)
754                         {
755                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
756                                 search->pack = pak;
757                                 search->next = fs_searchpaths;
758                                 fs_searchpaths = search;
759                         }
760                         else
761                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
762                 }
763         }
764
765         // add any PK3 package in the director
766         for (current = list;current;current = current->next)
767         {
768                 if (matchpattern(current->text, "*.pk3", true))
769                 {
770                         sprintf (pakfile, "%s/%s", dir, current->text);
771                         pak = FS_LoadPackPK3 (pakfile);
772                         if (pak)
773                         {
774                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
775                                 search->pack = pak;
776                                 search->next = fs_searchpaths;
777                                 fs_searchpaths = search;
778                         }
779                         else
780                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
781                 }
782         }
783         freedirectory(list);
784 }
785
786
787 /*
788 ============
789 FS_FileExtension
790 ============
791 */
792 char *FS_FileExtension (const char *in)
793 {
794         static char exten[8];
795         const char *slash, *backslash, *colon, *dot, *separator;
796         int i;
797
798         slash = strrchr(in, '/');
799         backslash = strrchr(in, '\\');
800         colon = strrchr(in, ':');
801         dot = strrchr(in, '.');
802         separator = slash;
803         if (separator < backslash)
804                 separator = backslash;
805         if (separator < colon)
806                 separator = colon;
807         if (dot < separator)
808                 return "";
809         dot++;
810         for (i = 0;i < 7 && dot[i];i++)
811                 exten[i] = dot[i];
812         exten[i] = 0;
813         return exten;
814 }
815
816
817 /*
818 ================
819 FS_Init
820 ================
821 */
822 void FS_Init (void)
823 {
824         int i;
825         searchpath_t *search;
826
827         fs_mempool = Mem_AllocPool("file management");
828         pak_mempool = Mem_AllocPool("paks");
829
830         Cmd_AddCommand ("path", FS_Path_f);
831
832         strcpy(fs_basedir, ".");
833
834         PK3_OpenLibrary ();
835
836         // -basedir <path>
837         // Overrides the system supplied base directory (under GAMENAME)
838         i = COM_CheckParm ("-basedir");
839         if (i && i < com_argc-1)
840                 strcpy (fs_basedir, com_argv[i+1]);
841
842         i = strlen (fs_basedir);
843         if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
844                 fs_basedir[i-1] = 0;
845
846         // start up with GAMENAME by default (id1)
847         strcpy(com_modname, GAMENAME);
848         FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
849         if (gamedirname[0])
850         {
851                 fs_modified = true;
852                 strcpy(com_modname, gamedirname);
853                 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
854         }
855
856         // -game <gamedir>
857         // Adds basedir/gamedir as an override game
858         i = COM_CheckParm ("-game");
859         if (i && i < com_argc-1)
860         {
861                 fs_modified = true;
862                 strcpy(com_modname, com_argv[i+1]);
863                 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
864         }
865
866         // -path <dir or packfile> [<dir or packfile>] ...
867         // Fully specifies the exact search path, overriding the generated one
868         i = COM_CheckParm ("-path");
869         if (i)
870         {
871                 fs_modified = true;
872                 fs_searchpaths = NULL;
873                 while (++i < com_argc)
874                 {
875                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
876                                 break;
877
878                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
879                         if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
880                         {
881                                 search->pack = FS_LoadPackPAK (com_argv[i]);
882                                 if (!search->pack)
883                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
884                         }
885                         else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
886                         {
887                                 search->pack = FS_LoadPackPK3 (com_argv[i]);
888                                 if (!search->pack)
889                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
890                         }
891                         else
892                                 strcpy (search->filename, com_argv[i]);
893                         search->next = fs_searchpaths;
894                         fs_searchpaths = search;
895                 }
896         }
897 }
898
899
900 /*
901 ====================
902 FS_SysOpen
903
904 Internal function used to create a qfile_t and open the relevant file on disk
905 ====================
906 */
907 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
908 {
909         qfile_t* file;
910
911         file = Mem_Alloc (fs_mempool, sizeof (*file));
912         memset (file, 0, sizeof (*file));
913
914         file->stream = fopen (filepath, mode);
915         if (!file->stream)
916         {
917                 Mem_Free (file);
918                 return NULL;
919         }
920
921         return file;
922 }
923
924
925 /*
926 ===========
927 FS_OpenRead
928 ===========
929 */
930 qfile_t *FS_OpenRead (const char *path, int offs, int len)
931 {
932         qfile_t* file;
933
934         file = FS_SysOpen (path, "rb");
935         if (!file)
936         {
937                 Sys_Error ("Couldn't open %s", path);
938                 return NULL;
939         }
940
941         // Normal file
942         if (offs < 0 || len < 0)
943         {
944                 // We set fs_filesize here for normal files
945                 fseek (file->stream, 0, SEEK_END);
946                 fs_filesize = ftell (file->stream);
947                 fseek (file->stream, 0, SEEK_SET);
948         }
949         // Packed file
950         else
951         {
952                 fseek (file->stream, offs, SEEK_SET);
953
954                 file->flags |= FS_FLAG_PACKED;
955                 file->length = len;
956                 file->offset = offs;
957                 file->position = 0;
958         }
959
960         return file;
961 }
962
963 /*
964 ===========
965 FS_FOpenFile
966
967 If the requested file is inside a packfile, a new qfile_t* will be opened
968 into the file.
969
970 Sets fs_filesize
971 ===========
972 */
973 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
974 {
975         searchpath_t *search;
976         char netpath[MAX_OSPATH];
977         pack_t *pak;
978         int i, filenamelen, matched;
979
980         filenamelen = strlen (filename);
981
982         // search through the path, one element at a time
983         search = fs_searchpaths;
984
985         for ( ; search ; search = search->next)
986         {
987                 // is the element a pak file?
988                 if (search->pack)
989                 {
990                         // look through all the pak file elements
991                         pak = search->pack;
992                         for (i=0 ; i<pak->numfiles ; i++)
993                         {
994                                 if (pak->ignorecase)
995                                         matched = !strcasecmp (pak->files[i].name, filename);
996                                 else
997                                         matched = !strcmp (pak->files[i].name, filename);
998                                 if (matched)  // found it?
999                                 {
1000                                         qfile_t *file;
1001
1002                                         if (!quiet)
1003                                                 Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name);
1004
1005                                         // If we don't have the true offset, get it now
1006                                         if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS))
1007                                                 PK3_GetTrueFileOffset (&pak->files[i], pak);
1008
1009                                         // No Zlib DLL = no compressed files
1010                                         if (!zlib_dll && (pak->files[i].flags & FILE_FLAG_DEFLATED))
1011                                         {
1012                                                 Con_Printf ("WARNING: can't open the compressed file %s\n"
1013                                                                         "You need the Zlib DLL to use compressed files\n", filename);
1014                                                 fs_filesize = -1;
1015                                                 return NULL;
1016                                         }
1017
1018                                         // open a new file in the pakfile
1019                                         file = FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize);
1020                                         fs_filesize = pak->files[i].realsize;
1021
1022                                         if (pak->files[i].flags & FILE_FLAG_DEFLATED)
1023                                         {
1024                                                 ztoolkit_t *ztk;
1025
1026                                                 file->flags |= FS_FLAG_DEFLATED;
1027
1028                                                 // We need some more variables
1029                                                 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1030
1031                                                 ztk->real_length = pak->files[i].realsize;
1032
1033                                                 // Initialize zlib stream
1034                                                 ztk->zstream.next_in = ztk->input;
1035                                                 ztk->zstream.avail_in = 0;
1036
1037                                                 /* From Zlib's "unzip.c":
1038                                                  *
1039                                                  * windowBits is passed < 0 to tell that there is no zlib header.
1040                                                  * Note that in this case inflate *requires* an extra "dummy" byte
1041                                                  * after the compressed stream in order to complete decompression and
1042                                                  * return Z_STREAM_END.
1043                                                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1044                                                  * size of both compressed and uncompressed data
1045                                                  */
1046                                                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1047                                                         Sys_Error ("inflate init error (file: %s)", filename);
1048
1049                                                 ztk->zstream.next_out = ztk->output;
1050                                                 ztk->zstream.avail_out = sizeof (ztk->output);
1051
1052                                                 file->z = ztk;
1053                                         }
1054
1055                                         return file;
1056                                 }
1057                         }
1058                 }
1059                 else
1060                 {
1061                         sprintf (netpath, "%s/%s",search->filename, filename);
1062
1063                         if (!FS_SysFileExists (netpath))
1064                                 continue;
1065
1066                         if (!quiet)
1067                                 Sys_Printf ("FindFile: %s\n",netpath);
1068                         return FS_OpenRead (netpath, -1, -1);
1069                 }
1070         }
1071
1072         if (!quiet)
1073                 Sys_Printf ("FindFile: can't find %s\n", filename);
1074
1075         fs_filesize = -1;
1076         return NULL;
1077 }
1078
1079
1080 /*
1081 =============================================================================
1082
1083 MAIN PUBLIC FUNCTIONS
1084
1085 =============================================================================
1086 */
1087
1088 /*
1089 ====================
1090 FS_Open
1091
1092 Open a file. The syntax is the same as fopen
1093 ====================
1094 */
1095 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1096 {
1097         // If the file is opened in "write" or "append" mode
1098         if (strchr (mode, 'w') || strchr (mode, 'a'))
1099         {
1100                 char real_path [MAX_OSPATH];
1101
1102                 // Open the file on disk directly
1103                 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1104
1105                 // Create directories up to the file
1106                 FS_CreatePath (real_path);
1107
1108                 return FS_SysOpen (real_path, mode);
1109         }
1110
1111         // Else, we look at the various search paths
1112         return FS_FOpenFile (filepath, quiet);
1113 }
1114
1115
1116 /*
1117 ====================
1118 FS_Close
1119
1120 Close a file
1121 ====================
1122 */
1123 int FS_Close (qfile_t* file)
1124 {
1125         if (fclose (file->stream))
1126                 return EOF;
1127
1128         if (file->z)
1129         {
1130                 qz_inflateEnd (&file->z->zstream);
1131                 Mem_Free (file->z);
1132         }
1133
1134         Mem_Free (file);
1135         return 0;
1136 }
1137
1138
1139 /*
1140 ====================
1141 FS_Write
1142
1143 Write "datasize" bytes into a file
1144 ====================
1145 */
1146 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1147 {
1148         return fwrite (data, 1, datasize, file->stream);
1149 }
1150
1151
1152 /*
1153 ====================
1154 FS_Read
1155
1156 Read up to "buffersize" bytes from a file
1157 ====================
1158 */
1159 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1160 {
1161         size_t count, nb;
1162         ztoolkit_t *ztk;
1163
1164         // Quick path for unpacked files
1165         if (! (file->flags & FS_FLAG_PACKED))
1166                 return fread (buffer, 1, buffersize, file->stream);
1167
1168         // If the file isn't compressed
1169         if (! (file->flags & FS_FLAG_DEFLATED))
1170         {
1171                 // We must take care to not read after the end of the file
1172                 count = file->length - file->position;
1173                 if (buffersize > count)
1174                         buffersize = count;
1175
1176                 nb = fread (buffer, 1, buffersize, file->stream);
1177
1178                 // Update the position index if the file is packed
1179                 file->position += nb;
1180
1181                 return nb;
1182         }
1183
1184         // If the file is compressed, it's more complicated...
1185         ztk = file->z;
1186
1187         // First, we copy as many bytes as we can from "output"
1188         if (ztk->out_ind < ztk->out_max)
1189         {
1190                 count = ztk->out_max - ztk->out_ind;
1191
1192                 nb = (buffersize > count) ? count : buffersize;
1193                 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1194                 ztk->out_ind += nb;
1195         }
1196         else
1197                 nb = 0;
1198
1199         // We cycle through a few operations until we have inflated enough data
1200         while (nb < buffersize)
1201         {
1202                 // NOTE: at this point, "output" should always be empty
1203
1204                 // If "input" is also empty, we need to fill it
1205                 if (ztk->in_ind == ztk->in_max)
1206                 {
1207                         size_t remain = file->length - file->position;
1208
1209                         // If we are at the end of the file
1210                         if (!remain)
1211                                 return nb;
1212
1213                         count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1214                         fread (ztk->input, 1, count, file->stream);
1215
1216                         // Update indexes and counters
1217                         ztk->in_ind = 0;
1218                         ztk->in_max = count;
1219                         file->position += count;
1220                 }
1221
1222                 // Now that we are sure we have compressed data available, we need to determine
1223                 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1224                 // case if we still need more bytes than "output" can contain)
1225
1226                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1227                 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1228
1229                 // If output will be able to contain at least 1 more byte than the data we need
1230                 if (buffersize - nb < sizeof (ztk->output))
1231                 {
1232                         int error;
1233
1234                         // Inflate the data in "output"
1235                         ztk->zstream.next_out = ztk->output;
1236                         ztk->zstream.avail_out = sizeof (ztk->output);
1237                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1238                         if (error != Z_OK && error != Z_STREAM_END)
1239                                 Sys_Error ("Can't inflate file");
1240                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1241                         ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1242                         ztk->out_ind = 0;
1243                         ztk->out_position += ztk->out_max;
1244
1245                         // Copy the requested data in "buffer" (as much as we can)
1246                         count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1247                         memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1248                         ztk->out_ind = count;
1249                 }
1250
1251                 // Else, we inflate directly in "buffer"
1252                 else
1253                 {
1254                         int error;
1255
1256                         // Inflate the data in "buffer"
1257                         ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1258                         ztk->zstream.avail_out = buffersize - nb;
1259                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1260                         if (error != Z_OK && error != Z_STREAM_END)
1261                                 Sys_Error ("Can't inflate file");
1262                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1263
1264                         // Invalidate the output data (for FS_Seek)
1265                         ztk->out_max = 0;
1266                         ztk->out_ind = 0;
1267
1268                         // How much data did it inflate?
1269                         count = buffersize - nb - ztk->zstream.avail_out;
1270                         ztk->out_position += count;
1271                 }
1272
1273                 nb += count;
1274         }
1275
1276         return nb;
1277 }
1278
1279
1280 /*
1281 ====================
1282 FS_Flush
1283
1284 Flush the file output stream
1285 ====================
1286 */
1287 int FS_Flush (qfile_t* file)
1288 {
1289         return fflush (file->stream);
1290 }
1291
1292
1293 /*
1294 ====================
1295 FS_Printf
1296
1297 Print a string into a file
1298 ====================
1299 */
1300 int FS_Printf (qfile_t* file, const char* format, ...)
1301 {
1302         int result;
1303         va_list args;
1304
1305         va_start (args, format);
1306         result = vfprintf (file->stream, format, args);
1307         va_end (args);
1308
1309         return result;
1310 }
1311
1312
1313 /*
1314 ====================
1315 FS_Getc
1316
1317 Get the next character of a file
1318 ====================
1319 */
1320 int FS_Getc (qfile_t* file)
1321 {
1322         char c;
1323
1324         if (FS_Read (file, &c, 1) != 1)
1325                 return EOF;
1326
1327         return c;
1328 }
1329
1330
1331 /*
1332 ====================
1333 FS_Seek
1334
1335 Move the position index in a file
1336 ====================
1337 */
1338 int FS_Seek (qfile_t* file, long offset, int whence)
1339 {
1340         // Quick path for unpacked files
1341         if (! (file->flags & FS_FLAG_PACKED))
1342                 return fseek (file->stream, offset, whence);
1343
1344         // Seeking in compressed files is more a hack than anything else,
1345         // but we need to support it, so here it is.
1346         if (file->flags & FS_FLAG_DEFLATED)
1347         {
1348                 ztoolkit_t *ztk = file->z;
1349                 size_t crt_offset;
1350                 qbyte buffer [sizeof (ztk->output)];  // it's big to force inflating into buffer directly
1351
1352                 crt_offset = ztk->out_position - ztk->out_max + ztk->out_ind;
1353
1354                 switch (whence)
1355                 {
1356                         case SEEK_CUR:
1357                                 offset += crt_offset;
1358                                 break;
1359
1360                         case SEEK_SET:
1361                                 break;
1362
1363                         case SEEK_END:
1364                                 offset += ztk->real_length;
1365                                 break;
1366
1367                         default:
1368                                 return -1;
1369                 }
1370
1371                 // If we need to go back in the file
1372                 if (offset <= crt_offset)
1373                 {
1374                         // If we still have the data we need in the output buffer
1375                         if (crt_offset - offset <= ztk->out_ind)
1376                         {
1377                                 ztk->out_ind -= crt_offset - offset;
1378                                 return 0;
1379                         }
1380
1381                         // Else, we restart from the beginning of the file
1382                         file->position = 0;
1383                         ztk->in_ind = 0;
1384                         ztk->in_max = 0;
1385                         ztk->out_ind = 0;
1386                         ztk->out_max = 0;
1387                         ztk->out_position = 0;
1388                         crt_offset = 0;
1389                         fseek (file->stream, file->offset, SEEK_SET);
1390
1391                         // Reset the Zlib stream
1392                         ztk->zstream.next_in = ztk->input;
1393                         ztk->zstream.avail_in = 0;
1394                         qz_inflateReset (&ztk->zstream);
1395                 }
1396
1397                 // Skip all data until we reach the requested offset
1398                 while (crt_offset < offset)
1399                 {
1400                         size_t diff = offset - crt_offset;
1401                         size_t count, len;
1402
1403                         count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1404                         len = FS_Read (file, buffer, count);
1405                         if (len != count)
1406                                 return -1;
1407                         crt_offset += len;
1408                 }
1409
1410                 return 0;
1411         }
1412
1413         // Packed files receive a special treatment too, because
1414         // we need to make sure it doesn't go outside of the file
1415         switch (whence)
1416         {
1417                 case SEEK_CUR:
1418                         offset += file->position;
1419                         break;
1420
1421                 case SEEK_SET:
1422                         break;
1423
1424                 case SEEK_END:
1425                         offset += file->length;
1426                         break;
1427
1428                 default:
1429                         return -1;
1430         }
1431         if (offset < 0 || offset > file->length)
1432                 return -1;
1433
1434         if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1435                 return -1;
1436         file->position = offset;
1437         return 0;
1438 }
1439
1440
1441 /*
1442 ====================
1443 FS_Tell
1444
1445 Give the current position in a file
1446 ====================
1447 */
1448 long FS_Tell (qfile_t* file)
1449 {
1450         if (file->flags & FS_FLAG_PACKED)
1451         {
1452                 if (file->flags & FS_FLAG_DEFLATED)
1453                 {
1454                         ztoolkit_t *ztk = file->z;
1455                         return ztk->out_position - ztk->out_max + ztk->out_ind;
1456                 }
1457
1458                 return file->position;
1459         }
1460
1461         return ftell (file->stream);
1462 }
1463
1464
1465 /*
1466 ====================
1467 FS_Gets
1468
1469 Extract a line from a file
1470 ====================
1471 */
1472 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1473 {
1474         size_t ind;
1475
1476         // Quick path for unpacked files
1477         if (! (file->flags & FS_FLAG_PACKED))
1478                 return fgets (buffer, buffersize, file->stream);
1479
1480         for (ind = 0; ind < buffersize - 1; ind++)
1481         {
1482                 int c = FS_Getc (file);
1483                 switch (c)
1484                 {
1485                         // End of file
1486                         case -1:
1487                                 if (!ind)
1488                                         return NULL;
1489
1490                                 buffer[ind] = '\0';
1491                                 return buffer;
1492
1493                         // End of line
1494                         case '\r':
1495                         case '\n':
1496                                 buffer[ind] = '\n';
1497                                 buffer[ind + 1] = '\0';
1498                                 return buffer;
1499
1500                         default:
1501                                 buffer[ind] = c;
1502                 }
1503
1504         }
1505
1506         buffer[buffersize - 1] = '\0';
1507         return buffer;
1508 }
1509
1510
1511 /*
1512 ==========
1513 FS_Getline
1514
1515 Dynamic length version of fgets. DO NOT free the buffer.
1516 ==========
1517 */
1518 char *FS_Getline (qfile_t *file)
1519 {
1520         static int  size = 256;
1521         static char *buf = 0;
1522         char        *t;
1523         int         len;
1524
1525         if (!buf)
1526                 buf = Mem_Alloc (fs_mempool, size);
1527
1528         if (!FS_Gets (file, buf, size))
1529                 return 0;
1530
1531         len = strlen (buf);
1532         while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1533         {
1534                 t = Mem_Alloc (fs_mempool, size + 256);
1535                 memcpy(t, buf, size);
1536                 Mem_Free(buf);
1537                 size += 256;
1538                 buf = t;
1539                 if (!FS_Gets (file, buf + len, size - len))
1540                         break;
1541                 len = strlen (buf);
1542         }
1543         while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1544                 buf[len - 1] = 0;
1545         return buf;
1546 }
1547
1548
1549 /*
1550 ====================
1551 FS_Eof
1552
1553 Extract a line from a file
1554 ====================
1555 */
1556 int FS_Eof (qfile_t* file)
1557 {
1558         if (file->flags & FS_FLAG_PACKED)
1559         {
1560                 if (file->flags & FS_FLAG_DEFLATED)
1561                 {
1562                         ztoolkit_t *ztk = file->z;
1563                         return (ztk->out_position - ztk->out_max + ztk->out_ind == ztk->real_length);
1564                 }
1565
1566                 return (file->position == file->length);
1567         }
1568
1569         return feof (file->stream);
1570 }
1571
1572
1573 /*
1574 ============
1575 FS_LoadFile
1576
1577 Filename are relative to the quake directory.
1578 Always appends a 0 byte.
1579 ============
1580 */
1581 qbyte *FS_LoadFile (const char *path, qboolean quiet)
1582 {
1583         qfile_t *h;
1584         qbyte *buf;
1585
1586         // look for it in the filesystem or pack files
1587         h = FS_Open (path, "rb", quiet);
1588         if (!h)
1589                 return NULL;
1590
1591         buf = Mem_Alloc(tempmempool, fs_filesize+1);
1592         if (!buf)
1593                 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1594
1595         ((qbyte *)buf)[fs_filesize] = 0;
1596
1597         FS_Read (h, buf, fs_filesize);
1598         FS_Close (h);
1599
1600         return buf;
1601 }
1602
1603
1604 /*
1605 ============
1606 FS_WriteFile
1607
1608 The filename will be prefixed by the current game directory
1609 ============
1610 */
1611 qboolean FS_WriteFile (const char *filename, void *data, int len)
1612 {
1613         FILE *handle;
1614         char name[MAX_OSPATH];
1615
1616         sprintf (name, "%s/%s", fs_gamedir, filename);
1617
1618         // Create directories up to the file
1619         FS_CreatePath (name);
1620
1621         handle = fopen (name, "wb");
1622         if (!handle)
1623         {
1624                 Con_Printf ("FS_WriteFile: failed on %s\n", name);
1625                 return false;
1626         }
1627
1628         Con_DPrintf ("FS_WriteFile: %s\n", name);
1629         fwrite (data, 1, len, handle);
1630         fclose (handle);
1631         return true;
1632 }
1633
1634
1635 /*
1636 =============================================================================
1637
1638 OTHERS PUBLIC FUNCTIONS
1639
1640 =============================================================================
1641 */
1642
1643 /*
1644 ============
1645 FS_StripExtension
1646 ============
1647 */
1648 void FS_StripExtension (const char *in, char *out)
1649 {
1650         char *last = NULL;
1651         while (*in)
1652         {
1653                 if (*in == '.')
1654                         last = out;
1655                 else if (*in == '/' || *in == '\\' || *in == ':')
1656                         last = NULL;
1657                 *out++ = *in++;
1658         }
1659         if (last)
1660                 *last = 0;
1661         else
1662                 *out = 0;
1663 }
1664
1665
1666 /*
1667 ==================
1668 FS_DefaultExtension
1669 ==================
1670 */
1671 void FS_DefaultExtension (char *path, const char *extension)
1672 {
1673         const char *src;
1674
1675         // if path doesn't have a .EXT, append extension
1676         // (extension should include the .)
1677         src = path + strlen(path) - 1;
1678
1679         while (*src != '/' && src != path)
1680         {
1681                 if (*src == '.')
1682                         return;                 // it has an extension
1683                 src--;
1684         }
1685
1686         strcat (path, extension);
1687 }
1688
1689
1690 qboolean FS_FileExists (const char *filename)
1691 {
1692         searchpath_t *search;
1693         char netpath[MAX_OSPATH];
1694         pack_t *pak;
1695         int i;
1696
1697         for (search = fs_searchpaths;search;search = search->next)
1698         {
1699                 if (search->pack)
1700                 {
1701                         pak = search->pack;
1702                         for (i = 0;i < pak->numfiles;i++)
1703                                 if (!strcmp (pak->files[i].name, filename))
1704                                         return true;
1705                 }
1706                 else
1707                 {
1708                         sprintf (netpath, "%s/%s",search->filename, filename);
1709                         if (FS_SysFileExists (netpath))
1710                                 return true;
1711                 }
1712         }
1713
1714         return false;
1715 }
1716
1717
1718 qboolean FS_SysFileExists (const char *path)
1719 {
1720 #if WIN32
1721         FILE *f;
1722
1723         f = fopen (path, "rb");
1724         if (f)
1725         {
1726                 fclose (f);
1727                 return true;
1728         }
1729
1730         return false;
1731 #else
1732         struct stat buf;
1733
1734         if (stat (path,&buf) == -1)
1735                 return false;
1736
1737         return true;
1738 #endif
1739 }
1740
1741 void FS_mkdir (const char *path)
1742 {
1743 #if WIN32
1744         _mkdir (path);
1745 #else
1746         mkdir (path, 0777);
1747 #endif
1748 }