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