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