]> icculus.org git repositories - divverent/darkplaces.git/blob - fs.c
added -benchmark commandline option
[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 FUNCTION PROTOTYPES
238
239 =============================================================================
240 */
241
242 void FS_Dir_f(void);
243 void FS_Ls_f(void);
244
245 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
246                                                                          size_t offset, size_t packsize,
247                                                                          size_t realsize, file_flags_t flags);
248
249
250 /*
251 =============================================================================
252
253 VARIABLES
254
255 =============================================================================
256 */
257
258 mempool_t *fs_mempool;
259 mempool_t *pak_mempool;
260
261 int fs_filesize;
262
263 pack_t *packlist = NULL;
264
265 searchpath_t *fs_searchpaths;
266
267 #define MAX_FILES_IN_PACK       65536
268
269 char fs_gamedir[MAX_OSPATH];
270 char fs_basedir[MAX_OSPATH];
271
272 qboolean fs_modified;   // set true if using non-id files
273
274
275 /*
276 =============================================================================
277
278 PRIVATE FUNCTIONS - PK3 HANDLING
279
280 =============================================================================
281 */
282
283 // Functions exported from zlib
284 #ifdef WIN32
285 # define ZEXPORT WINAPI
286 #else
287 # define ZEXPORT
288 #endif
289
290 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
291 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
292 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
293 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
294
295 #define qz_inflateInit2(strm, windowBits) \
296         qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
297
298 static dllfunction_t zlibfuncs[] =
299 {
300         {"inflate",                     (void **) &qz_inflate},
301         {"inflateEnd",          (void **) &qz_inflateEnd},
302         {"inflateInit2_",       (void **) &qz_inflateInit2_},
303         {"inflateReset",        (void **) &qz_inflateReset},
304         {NULL, NULL}
305 };
306
307 // Handle for Zlib DLL
308 static dllhandle_t zlib_dll = NULL;
309
310
311 /*
312 ====================
313 PK3_CloseLibrary
314
315 Unload the Zlib DLL
316 ====================
317 */
318 void PK3_CloseLibrary (void)
319 {
320         if (!zlib_dll)
321                 return;
322
323         Sys_UnloadLibrary (zlib_dll);
324         zlib_dll = NULL;
325 }
326
327
328 /*
329 ====================
330 PK3_OpenLibrary
331
332 Try to load the Zlib DLL
333 ====================
334 */
335 qboolean PK3_OpenLibrary (void)
336 {
337         const char* dllname;
338         const dllfunction_t *func;
339
340         // Already loaded?
341         if (zlib_dll)
342                 return true;
343
344 #ifdef WIN32
345         dllname = "zlib.dll";
346 #else
347         dllname = "libz.so";
348 #endif
349
350         // Initializations
351         for (func = zlibfuncs; func && func->name != NULL; func++)
352                 *func->funcvariable = NULL;
353
354         // Load the DLL
355         if (! (zlib_dll = Sys_LoadLibrary (dllname)))
356         {
357                 Con_Printf("Can't find %s. Compressed files support disabled\n", dllname);
358                 return false;
359         }
360
361         // Get the function adresses
362         for (func = zlibfuncs; func && func->name != NULL; func++)
363                 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (zlib_dll, func->name)))
364                 {
365                         Con_Printf("missing function \"%s\" - broken Zlib library!\n", func->name);
366                         PK3_CloseLibrary ();
367                         return false;
368                 }
369
370         Con_Printf("%s loaded. Compressed files support enabled\n", dllname);
371         return true;
372 }
373
374
375 /*
376 ====================
377 PK3_GetEndOfCentralDir
378
379 Extract the end of the central directory from a PK3 package
380 ====================
381 */
382 qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd)
383 {
384         long filesize, maxsize;
385         qbyte *buffer, *ptr;
386         int ind;
387
388         // Get the package size
389         fseek (packhandle, 0, SEEK_END);
390         filesize = ftell (packhandle);
391         if (filesize < ZIP_END_CDIR_SIZE)
392                 return false;
393
394         // Load the end of the file in memory
395         if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
396                 maxsize = filesize;
397         else
398                 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
399         buffer = Mem_Alloc (tempmempool, maxsize);
400         fseek (packhandle, filesize - maxsize, SEEK_SET);
401         if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) maxsize)
402         {
403                 Mem_Free (buffer);
404                 return false;
405         }
406
407         // Look for the end of central dir signature around the end of the file
408         maxsize -= ZIP_END_CDIR_SIZE;
409         ptr = &buffer[maxsize];
410         ind = 0;
411         while (BuffBigLong (ptr) != ZIP_END_HEADER)
412         {
413                 if (ind == maxsize)
414                 {
415                         Mem_Free (buffer);
416                         return false;
417                 }
418
419                 ind++;
420                 ptr--;
421         }
422
423         memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
424         eocd->signature = LittleLong (eocd->signature);
425         eocd->disknum = LittleShort (eocd->disknum);
426         eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
427         eocd->localentries = LittleShort (eocd->localentries);
428         eocd->nbentries = LittleShort (eocd->nbentries);
429         eocd->cdir_size = LittleLong (eocd->cdir_size);
430         eocd->cdir_offset = LittleLong (eocd->cdir_offset);
431         eocd->comment_size = LittleShort (eocd->comment_size);
432
433         Mem_Free (buffer);
434
435         return true;
436 }
437
438
439 /*
440 ====================
441 PK3_BuildFileList
442
443 Extract the file list from a PK3 file
444 ====================
445 */
446 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
447 {
448         qbyte *central_dir, *ptr;
449         unsigned int ind;
450         int remaining;
451
452         // Load the central directory in memory
453         central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
454         fseek (pack->handle, eocd->cdir_offset, SEEK_SET);
455         fread (central_dir, 1, eocd->cdir_size, pack->handle);
456
457         // Extract the files properties
458         // The parsing is done "by hand" because some fields have variable sizes and
459         // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
460         remaining = eocd->cdir_size;
461         pack->numfiles = 0;
462         ptr = central_dir;
463         for (ind = 0; ind < eocd->nbentries; ind++)
464         {
465                 size_t namesize, count;
466
467                 // Checking the remaining size
468                 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
469                 {
470                         Mem_Free (central_dir);
471                         return -1;
472                 }
473                 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
474
475                 // Check header
476                 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
477                 {
478                         Mem_Free (central_dir);
479                         return -1;
480                 }
481
482                 namesize = BuffLittleShort (&ptr[28]);  // filename length
483
484                 // Check encryption, compression, and attributes
485                 // 1st uint8  : general purpose bit flag
486                 //    Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
487                 // 2nd uint8 : external file attributes
488                 //    Check bits 3 (file is a directory) and 5 (file is a volume (?))
489                 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
490                 {
491                         // Still enough bytes for the name?
492                         if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
493                         {
494                                 Mem_Free (central_dir);
495                                 return -1;
496                         }
497
498                         // WinZip doesn't use the "directory" attribute, so we need to check the name directly
499                         if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
500                         {
501                                 char filename [sizeof (pack->files[0].name)];
502                                 size_t offset, packsize, realsize;
503                                 file_flags_t flags;
504
505                                 // Extract the name (strip it if necessary)
506                                 if (namesize >= sizeof (filename))
507                                         namesize = sizeof (filename) - 1;
508                                 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
509                                 filename[namesize] = '\0';
510
511                                 if (BuffLittleShort (&ptr[10]))
512                                         flags = FILE_FLAG_DEFLATED;
513                                 else
514                                         flags = 0;
515                                 offset = BuffLittleLong (&ptr[42]);
516                                 packsize = BuffLittleLong (&ptr[20]);
517                                 realsize = BuffLittleLong (&ptr[24]);
518                                 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
519                         }
520                 }
521
522                 // Skip the name, additionnal field, and comment
523                 // 1er uint16 : extra field length
524                 // 2eme uint16 : file comment length
525                 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
526                 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
527                 remaining -= count;
528         }
529
530         Mem_Free (central_dir);
531         return pack->numfiles;
532 }
533
534
535 /*
536 ====================
537 FS_LoadPackPK3
538
539 Create a package entry associated with a PK3 file
540 ====================
541 */
542 pack_t *FS_LoadPackPK3 (const char *packfile)
543 {
544         FILE *packhandle;
545         pk3_endOfCentralDir_t eocd;
546         pack_t *pack;
547         int real_nb_files;
548
549         packhandle = fopen (packfile, "rb");
550         if (!packhandle)
551                 return NULL;
552
553         if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
554                 Sys_Error ("%s is not a PK3 file", packfile);
555
556         // Multi-volume ZIP archives are NOT allowed
557         if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
558                 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
559
560         // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
561         // since eocd.nbentries is an unsigned 16 bits integer
562         #if MAX_FILES_IN_PACK < 65535
563         if (eocd.nbentries > MAX_FILES_IN_PACK)
564                 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
565         #endif
566
567         // Create a package structure in memory
568         pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
569         pack->ignorecase = true; // PK3 ignores case
570         strlcpy (pack->filename, packfile, sizeof (pack->filename));
571         pack->handle = packhandle;
572         pack->numfiles = eocd.nbentries;
573         pack->mempool = Mem_AllocPool (packfile);
574         pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
575         pack->next = packlist;
576         packlist = pack;
577
578         real_nb_files = PK3_BuildFileList (pack, &eocd);
579         if (real_nb_files <= 0)
580                 Sys_Error ("%s is not a valid PK3 file", packfile);
581
582         Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
583         return pack;
584 }
585
586
587 /*
588 ====================
589 PK3_GetTrueFileOffset
590
591 Find where the true file data offset is
592 ====================
593 */
594 void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
595 {
596         qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
597         size_t count;
598
599         // Already found?
600         if (file->flags & FILE_FLAG_TRUEOFFS)
601                 return;
602
603         // Load the local file description
604         fseek (pack->handle, file->offset, SEEK_SET);
605         count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
606         if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
607                 Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
608
609         // Skip name and extra field
610         file->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
611
612         file->flags |= FILE_FLAG_TRUEOFFS;
613 }
614
615
616 /*
617 =============================================================================
618
619 OTHER PRIVATE FUNCTIONS
620
621 =============================================================================
622 */
623
624
625 /*
626 ====================
627 FS_AddFileToPack
628
629 Add a file to the list of files contained into a package
630 ====================
631 */
632 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
633                                                                          size_t offset, size_t packsize,
634                                                                          size_t realsize, file_flags_t flags)
635 {
636         int (*strcmp_funct) (const char* str1, const char* str2);
637         size_t left, right, middle;
638         int diff;
639         packfile_t *file;
640
641         strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
642
643         // Look for the slot we should put that file into (binary search)
644         left = 0;
645         right = pack->numfiles;
646         while (left != right)
647         {
648                 middle = (left + right - 1) / 2;
649                 diff = strcmp_funct (pack->files[middle].name, name);
650
651                 // If we found the file, there's a problem
652                 if (!diff)
653                         Sys_Error ("Package %s contains several time the file %s\n",
654                                            pack->filename, name);
655
656                 // If we're too far in the list
657                 if (diff > 0)
658                         right = middle;
659                 else
660                         left = middle + 1;
661         }
662
663         // We have to move the right of the list by one slot to free the one we need
664         file = &pack->files[left];
665         memmove (file + 1, file, (pack->numfiles - left) * sizeof (*file));
666         pack->numfiles++;
667
668         strlcpy (file->name, name, sizeof (file->name));
669         file->offset = offset;
670         file->packsize = packsize;
671         file->realsize = realsize;
672         file->flags = flags;
673
674         return file;
675 }
676
677
678 /*
679 ============
680 FS_CreatePath
681
682 Only used for FS_Open.
683 ============
684 */
685 void FS_CreatePath (char *path)
686 {
687         char *ofs, save;
688
689         for (ofs = path+1 ; *ofs ; ofs++)
690         {
691                 if (*ofs == '/' || *ofs == '\\')
692                 {
693                         // create the directory
694                         save = *ofs;
695                         *ofs = 0;
696                         FS_mkdir (path);
697                         *ofs = save;
698                 }
699         }
700 }
701
702
703 /*
704 ============
705 FS_Path_f
706
707 ============
708 */
709 void FS_Path_f (void)
710 {
711         searchpath_t *s;
712
713         Con_Print("Current search path:\n");
714         for (s=fs_searchpaths ; s ; s=s->next)
715         {
716                 if (s->pack)
717                 {
718                         Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
719                 }
720                 else
721                         Con_Printf("%s\n", s->filename);
722         }
723 }
724
725
726 /*
727 =================
728 FS_LoadPackPAK
729
730 Takes an explicit (not game tree related) path to a pak file.
731
732 Loads the header and directory, adding the files at the beginning
733 of the list so they override previous pack files.
734 =================
735 */
736 pack_t *FS_LoadPackPAK (const char *packfile)
737 {
738         dpackheader_t header;
739         int i, numpackfiles;
740         FILE *packhandle;
741         pack_t *pack;
742         dpackfile_t *info;      // temporary alloc, allowing huge pack directories
743
744         packhandle = fopen (packfile, "rb");
745         if (!packhandle)
746                 return NULL;
747
748         fread ((void *)&header, 1, sizeof(header), packhandle);
749         if (memcmp(header.id, "PACK", 4))
750                 Sys_Error ("%s is not a packfile", packfile);
751         header.dirofs = LittleLong (header.dirofs);
752         header.dirlen = LittleLong (header.dirlen);
753
754         if (header.dirlen % sizeof(dpackfile_t))
755                 Sys_Error ("%s has an invalid directory size", packfile);
756
757         numpackfiles = header.dirlen / sizeof(dpackfile_t);
758
759         if (numpackfiles > MAX_FILES_IN_PACK)
760                 Sys_Error ("%s has %i files", packfile, numpackfiles);
761
762         pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
763         pack->ignorecase = false; // PAK is case sensitive
764         strlcpy (pack->filename, packfile, sizeof (pack->filename));
765         pack->handle = packhandle;
766         pack->numfiles = 0;
767         pack->mempool = Mem_AllocPool(packfile);
768         pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
769         pack->next = packlist;
770         packlist = pack;
771
772         info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
773         fseek (packhandle, header.dirofs, SEEK_SET);
774         fread ((void *)info, 1, header.dirlen, packhandle);
775
776         // parse the directory
777         for (i = 0;i < numpackfiles;i++)
778         {
779                 size_t offset = LittleLong (info[i].filepos);
780                 size_t size = LittleLong (info[i].filelen);
781
782                 FS_AddFileToPack (info[i].name, pack, offset, size, size, FILE_FLAG_TRUEOFFS);
783         }
784
785         Mem_Free(info);
786
787         Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
788         return pack;
789 }
790
791
792 /*
793 ================
794 FS_AddGameDirectory
795
796 Sets fs_gamedir, adds the directory to the head of the path,
797 then loads and adds pak1.pak pak2.pak ...
798 ================
799 */
800 void FS_AddGameDirectory (char *dir)
801 {
802         stringlist_t *list, *current;
803         searchpath_t *search;
804         pack_t *pak;
805         char pakfile[MAX_OSPATH];
806
807         strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
808
809 #ifndef AKVERSION
810         // add the directory to the search path
811         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
812         strlcpy (search->filename, dir, sizeof (search->filename));
813         search->next = fs_searchpaths;
814         fs_searchpaths = search;
815 #endif
816
817         list = listdirectory(dir);
818
819         // add any PAK package in the directory
820         for (current = list;current;current = current->next)
821         {
822                 if (matchpattern(current->text, "*.pak", true))
823                 {
824                         snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
825                         pak = FS_LoadPackPAK (pakfile);
826                         if (pak)
827                         {
828                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
829                                 search->pack = pak;
830                                 search->next = fs_searchpaths;
831                                 fs_searchpaths = search;
832                         }
833                         else
834                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
835                 }
836         }
837
838         // add any PK3 package in the director
839         for (current = list;current;current = current->next)
840         {
841                 if (matchpattern(current->text, "*.pk3", true))
842                 {
843                         snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
844                         pak = FS_LoadPackPK3 (pakfile);
845                         if (pak)
846                         {
847                                 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
848                                 search->pack = pak;
849                                 search->next = fs_searchpaths;
850                                 fs_searchpaths = search;
851                         }
852                         else
853                                 Con_Printf("unable to load pak \"%s\"\n", pakfile);
854                 }
855         }
856         freedirectory(list);
857
858 // Unpacked files have the priority over packed files if AKVERSION is defined
859 #ifdef AKVERSION
860         // add the directory to the search path
861         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
862         strlcpy (search->filename, dir, sizeof (search->filename));
863         search->next = fs_searchpaths;
864         fs_searchpaths = search;
865 #endif
866 }
867
868
869 /*
870 ============
871 FS_FileExtension
872 ============
873 */
874 char *FS_FileExtension (const char *in)
875 {
876         static char exten[8];
877         const char *slash, *backslash, *colon, *dot, *separator;
878         int i;
879
880         slash = strrchr(in, '/');
881         backslash = strrchr(in, '\\');
882         colon = strrchr(in, ':');
883         dot = strrchr(in, '.');
884         separator = slash;
885         if (separator < backslash)
886                 separator = backslash;
887         if (separator < colon)
888                 separator = colon;
889         if (dot < separator)
890                 return "";
891         dot++;
892         for (i = 0;i < 7 && dot[i];i++)
893                 exten[i] = dot[i];
894         exten[i] = 0;
895         return exten;
896 }
897
898
899 /*
900 ================
901 FS_Init
902 ================
903 */
904 void FS_Init (void)
905 {
906         int i;
907         searchpath_t *search;
908
909         fs_mempool = Mem_AllocPool("file management");
910         pak_mempool = Mem_AllocPool("paks");
911
912         Cmd_AddCommand ("path", FS_Path_f);
913         Cmd_AddCommand ("dir", FS_Dir_f);
914         Cmd_AddCommand ("ls", FS_Ls_f);
915
916         strcpy(fs_basedir, ".");
917
918         PK3_OpenLibrary ();
919
920         // -basedir <path>
921         // Overrides the system supplied base directory (under GAMENAME)
922         i = COM_CheckParm ("-basedir");
923         if (i && i < com_argc-1)
924                 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
925
926         i = strlen (fs_basedir);
927         if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
928                 fs_basedir[i-1] = 0;
929
930         // start up with GAMENAME by default (id1)
931         strlcpy (com_modname, GAMENAME, sizeof (com_modname));
932         FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
933         if (gamedirname[0])
934         {
935                 fs_modified = true;
936                 strlcpy (com_modname, gamedirname, sizeof (com_modname));
937                 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
938         }
939
940         // -game <gamedir>
941         // Adds basedir/gamedir as an override game
942         i = COM_CheckParm ("-game");
943         if (i && i < com_argc-1)
944         {
945                 fs_modified = true;
946                 strlcpy (com_modname, com_argv[i+1], sizeof (com_modname));
947                 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
948         }
949
950         // -path <dir or packfile> [<dir or packfile>] ...
951         // Fully specifies the exact search path, overriding the generated one
952         i = COM_CheckParm ("-path");
953         if (i)
954         {
955                 fs_modified = true;
956                 fs_searchpaths = NULL;
957                 while (++i < com_argc)
958                 {
959                         if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
960                                 break;
961
962                         search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
963                         if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
964                         {
965                                 search->pack = FS_LoadPackPAK (com_argv[i]);
966                                 if (!search->pack)
967                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
968                         }
969                         else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
970                         {
971                                 search->pack = FS_LoadPackPK3 (com_argv[i]);
972                                 if (!search->pack)
973                                         Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
974                         }
975                         else
976                                 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
977                         search->next = fs_searchpaths;
978                         fs_searchpaths = search;
979                 }
980         }
981 }
982
983
984 /*
985 ====================
986 FS_SysOpen
987
988 Internal function used to create a qfile_t and open the relevant file on disk
989 ====================
990 */
991 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
992 {
993         qfile_t* file;
994
995         file = Mem_Alloc (fs_mempool, sizeof (*file));
996         memset (file, 0, sizeof (*file));
997
998         file->stream = fopen (filepath, mode);
999         if (!file->stream)
1000         {
1001                 Mem_Free (file);
1002                 return NULL;
1003         }
1004
1005         return file;
1006 }
1007
1008
1009 /*
1010 ===========
1011 FS_OpenRead
1012 ===========
1013 */
1014 qfile_t *FS_OpenRead (const char *path, int offs, int len)
1015 {
1016         qfile_t* file;
1017
1018         file = FS_SysOpen (path, "rb");
1019         if (!file)
1020         {
1021                 Sys_Error ("Couldn't open %s", path);
1022                 return NULL;
1023         }
1024
1025         // Normal file
1026         if (offs < 0 || len < 0)
1027         {
1028                 // We set fs_filesize here for normal files
1029                 fseek (file->stream, 0, SEEK_END);
1030                 fs_filesize = ftell (file->stream);
1031                 fseek (file->stream, 0, SEEK_SET);
1032         }
1033         // Packed file
1034         else
1035         {
1036                 fseek (file->stream, offs, SEEK_SET);
1037
1038                 file->flags |= FS_FLAG_PACKED;
1039                 file->length = len;
1040                 file->offset = offs;
1041                 file->position = 0;
1042         }
1043
1044         return file;
1045 }
1046
1047
1048 /*
1049 ====================
1050 FS_FindFile
1051
1052 Look for a file in the packages and in the filesystem
1053
1054 Return the searchpath where the file was found (or NULL)
1055 and the file index in the package if relevant
1056 ====================
1057 */
1058 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1059 {
1060         searchpath_t *search;
1061         pack_t *pak;
1062         int (*strcmp_funct) (const char* str1, const char* str2);
1063
1064         // search through the path, one element at a time
1065         for (search = fs_searchpaths;search;search = search->next)
1066         {
1067                 // is the element a pak file?
1068                 if (search->pack)
1069                 {
1070                         size_t left, right, middle;
1071
1072                         pak = search->pack;
1073                         strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1074
1075                         // Look for the file (binary search)
1076                         left = 0;
1077                         right = pak->numfiles;
1078                         while (left != right)
1079                         {
1080                                 int diff;
1081
1082                                 middle = (left + right - 1) / 2;
1083                                 diff = strcmp_funct (pak->files[middle].name, name);
1084
1085                                 // Found it
1086                                 if (!diff)
1087                                 {
1088                                         if (!quiet)
1089                                                 Sys_Printf("FS_FindFile: %s in %s\n",
1090                                                                         pak->files[middle].name, pak->filename);
1091
1092                                         if (index != NULL)
1093                                                 *index = middle;
1094                                         return search;
1095                                 }
1096
1097                                 // If we're too far in the list
1098                                 if (diff > 0)
1099                                         right = middle;
1100                                 else
1101                                         left = middle + 1;
1102                         }
1103                 }
1104                 else
1105                 {
1106                         char netpath[MAX_OSPATH];
1107                         snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1108                         if (FS_SysFileExists (netpath))
1109                         {
1110                                 if (!quiet)
1111                                         Sys_Printf("FS_FindFile: %s\n", netpath);
1112
1113                                 if (index != NULL)
1114                                         *index = -1;
1115                                 return search;
1116                         }
1117                 }
1118         }
1119
1120         if (!quiet)
1121                 Sys_Printf("FS_FindFile: can't find %s\n", name);
1122
1123         if (index != NULL)
1124                 *index = -1;
1125         return NULL;
1126 }
1127
1128
1129 /*
1130 ===========
1131 FS_FOpenFile
1132
1133 If the requested file is inside a packfile, a new qfile_t* will be opened
1134 into the file.
1135
1136 Sets fs_filesize
1137 ===========
1138 */
1139 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
1140 {
1141         searchpath_t *search;
1142         packfile_t *packfile;
1143         int i;
1144         qfile_t *file;
1145
1146         search = FS_FindFile (filename, &i, quiet);
1147
1148         // Not found?
1149         if (search == NULL)
1150         {
1151                 fs_filesize = -1;
1152                 return NULL;
1153         }
1154
1155         // Found in the filesystem?
1156         if (i < 0)
1157         {
1158                 char netpath[MAX_OSPATH];
1159                 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
1160                 return FS_OpenRead(netpath, -1, -1);
1161         }
1162
1163         // So, we found it in a package...
1164         packfile = &search->pack->files[i];
1165
1166         // If we don't have the true offset, get it now
1167         if (! (packfile->flags & FILE_FLAG_TRUEOFFS))
1168                 PK3_GetTrueFileOffset (packfile, search->pack);
1169
1170         // No Zlib DLL = no compressed files
1171         if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED))
1172         {
1173                 Con_Printf("WARNING: can't open the compressed file %s\n"
1174                                         "You need the Zlib DLL to use compressed files\n",
1175                                         filename);
1176                 fs_filesize = -1;
1177                 return NULL;
1178         }
1179
1180         // open a new file in the pakfile
1181         file = FS_OpenRead (search->pack->filename, packfile->offset, packfile->packsize);
1182         fs_filesize = packfile->realsize;
1183
1184         if (packfile->flags & FILE_FLAG_DEFLATED)
1185         {
1186                 ztoolkit_t *ztk;
1187
1188                 file->flags |= FS_FLAG_DEFLATED;
1189
1190                 // We need some more variables
1191                 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1192
1193                 ztk->real_length = packfile->realsize;
1194
1195                 // Initialize zlib stream
1196                 ztk->zstream.next_in = ztk->input;
1197                 ztk->zstream.avail_in = 0;
1198
1199                 /* From Zlib's "unzip.c":
1200                  *
1201                  * windowBits is passed < 0 to tell that there is no zlib header.
1202                  * Note that in this case inflate *requires* an extra "dummy" byte
1203                  * after the compressed stream in order to complete decompression and
1204                  * return Z_STREAM_END.
1205                  * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1206                  * size of both compressed and uncompressed data
1207                  */
1208                 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1209                         Sys_Error ("inflate init error (file: %s)", filename);
1210
1211                 ztk->zstream.next_out = ztk->output;
1212                 ztk->zstream.avail_out = sizeof (ztk->output);
1213
1214                 file->z = ztk;
1215         }
1216
1217         return file;
1218 }
1219
1220
1221 /*
1222 =============================================================================
1223
1224 MAIN PUBLIC FUNCTIONS
1225
1226 =============================================================================
1227 */
1228
1229 /*
1230 ====================
1231 FS_Open
1232
1233 Open a file. The syntax is the same as fopen
1234 ====================
1235 */
1236 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1237 {
1238         // If the file is opened in "write" or "append" mode
1239         if (strchr (mode, 'w') || strchr (mode, 'a'))
1240         {
1241                 char real_path [MAX_OSPATH];
1242
1243                 // Open the file on disk directly
1244                 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1245
1246                 // Create directories up to the file
1247                 FS_CreatePath (real_path);
1248
1249                 return FS_SysOpen (real_path, mode);
1250         }
1251
1252         // Else, we look at the various search paths
1253         return FS_FOpenFile (filepath, quiet);
1254 }
1255
1256
1257 /*
1258 ====================
1259 FS_Close
1260
1261 Close a file
1262 ====================
1263 */
1264 int FS_Close (qfile_t* file)
1265 {
1266         if (fclose (file->stream))
1267                 return EOF;
1268
1269         if (file->z)
1270         {
1271                 qz_inflateEnd (&file->z->zstream);
1272                 Mem_Free (file->z);
1273         }
1274
1275         Mem_Free (file);
1276         return 0;
1277 }
1278
1279
1280 /*
1281 ====================
1282 FS_Write
1283
1284 Write "datasize" bytes into a file
1285 ====================
1286 */
1287 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1288 {
1289         return fwrite (data, 1, datasize, file->stream);
1290 }
1291
1292
1293 /*
1294 ====================
1295 FS_Read
1296
1297 Read up to "buffersize" bytes from a file
1298 ====================
1299 */
1300 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1301 {
1302         size_t count, nb;
1303         ztoolkit_t *ztk;
1304
1305         // Quick path for unpacked files
1306         if (! (file->flags & FS_FLAG_PACKED))
1307                 return fread (buffer, 1, buffersize, file->stream);
1308
1309         // If the file isn't compressed
1310         if (! (file->flags & FS_FLAG_DEFLATED))
1311         {
1312                 // We must take care to not read after the end of the file
1313                 count = file->length - file->position;
1314                 if (buffersize > count)
1315                         buffersize = count;
1316
1317                 nb = fread (buffer, 1, buffersize, file->stream);
1318
1319                 file->position += nb;
1320                 return nb;
1321         }
1322
1323         // If the file is compressed, it's more complicated...
1324         ztk = file->z;
1325
1326         // First, we copy as many bytes as we can from "output"
1327         if (ztk->out_ind < ztk->out_max)
1328         {
1329                 count = ztk->out_max - ztk->out_ind;
1330
1331                 nb = (buffersize > count) ? count : buffersize;
1332                 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1333                 ztk->out_ind += nb;
1334                 file->position += nb;
1335         }
1336         else
1337                 nb = 0;
1338
1339         // We cycle through a few operations until we have inflated enough data
1340         while (nb < buffersize)
1341         {
1342                 // NOTE: at this point, "output" should always be empty
1343
1344                 // If "input" is also empty, we need to fill it
1345                 if (ztk->in_ind == ztk->in_max)
1346                 {
1347                         size_t remain;
1348
1349                         // If we are at the end of the file
1350                         if (ztk->out_position == ztk->real_length)
1351                                 return nb;
1352
1353                         remain = file->length - ztk->in_position;
1354                         count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1355                         fread (ztk->input, 1, count, file->stream);
1356
1357                         // Update indexes and counters
1358                         ztk->in_ind = 0;
1359                         ztk->in_max = count;
1360                         ztk->in_position += count;
1361                 }
1362
1363                 // Now that we are sure we have compressed data available, we need to determine
1364                 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1365                 // case if we still need more bytes than "output" can contain)
1366
1367                 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1368                 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1369
1370                 // If output will be able to contain at least 1 more byte than the data we need
1371                 if (buffersize - nb < sizeof (ztk->output))
1372                 {
1373                         int error;
1374
1375                         // Inflate the data in "output"
1376                         ztk->zstream.next_out = ztk->output;
1377                         ztk->zstream.avail_out = sizeof (ztk->output);
1378                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1379                         if (error != Z_OK && error != Z_STREAM_END)
1380                                 Sys_Error ("Can't inflate file");
1381                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1382                         ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1383                         ztk->out_position += ztk->out_max;
1384
1385                         // Copy the requested data in "buffer" (as much as we can)
1386                         count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1387                         memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1388                         ztk->out_ind = count;
1389                 }
1390
1391                 // Else, we inflate directly in "buffer"
1392                 else
1393                 {
1394                         int error;
1395
1396                         // Inflate the data in "buffer"
1397                         ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1398                         ztk->zstream.avail_out = buffersize - nb;
1399                         error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1400                         if (error != Z_OK && error != Z_STREAM_END)
1401                                 Sys_Error ("Can't inflate file");
1402                         ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1403
1404                         // Invalidate the output data (for FS_Seek)
1405                         ztk->out_max = 0;
1406                         ztk->out_ind = 0;
1407
1408                         // How much data did it inflate?
1409                         count = buffersize - nb - ztk->zstream.avail_out;
1410                         ztk->out_position += count;
1411                 }
1412
1413                 nb += count;
1414                 file->position += count;
1415         }
1416
1417         return nb;
1418 }
1419
1420
1421 /*
1422 ====================
1423 FS_Flush
1424
1425 Flush the file output stream
1426 ====================
1427 */
1428 int FS_Flush (qfile_t* file)
1429 {
1430         return fflush (file->stream);
1431 }
1432
1433
1434 /*
1435 ====================
1436 FS_Print
1437
1438 Print a string into a file
1439 ====================
1440 */
1441 int FS_Print(qfile_t* file, const char *msg)
1442 {
1443         return FS_Write(file, msg, strlen(msg));
1444 }
1445
1446 /*
1447 ====================
1448 FS_Printf
1449
1450 Print a string into a file
1451 ====================
1452 */
1453 int FS_Printf(qfile_t* file, const char* format, ...)
1454 {
1455         int result;
1456         va_list args;
1457
1458         va_start (args, format);
1459         result = vfprintf (file->stream, format, args);
1460         va_end (args);
1461
1462         return result;
1463 }
1464
1465
1466 /*
1467 ====================
1468 FS_Getc
1469
1470 Get the next character of a file
1471 ====================
1472 */
1473 int FS_Getc (qfile_t* file)
1474 {
1475         char c;
1476
1477         if (FS_Read (file, &c, 1) != 1)
1478                 return EOF;
1479
1480         return c;
1481 }
1482
1483
1484 /*
1485 ====================
1486 FS_Seek
1487
1488 Move the position index in a file
1489 ====================
1490 */
1491 int FS_Seek (qfile_t* file, long offset, int whence)
1492 {
1493         // Quick path for unpacked files
1494         if (! (file->flags & FS_FLAG_PACKED))
1495                 return fseek (file->stream, offset, whence);
1496
1497         // Seeking in compressed files is more a hack than anything else,
1498         // but we need to support it, so here it is.
1499         if (file->flags & FS_FLAG_DEFLATED)
1500         {
1501                 ztoolkit_t *ztk = file->z;
1502                 qbyte buffer [sizeof (ztk->output)];  // it's big to force inflating into buffer directly
1503
1504                 switch (whence)
1505                 {
1506                         case SEEK_CUR:
1507                                 offset += file->position;
1508                                 break;
1509
1510                         case SEEK_SET:
1511                                 break;
1512
1513                         case SEEK_END:
1514                                 offset += ztk->real_length;
1515                                 break;
1516
1517                         default:
1518                                 return -1;
1519                 }
1520                 if (offset < 0 || offset > (long) ztk->real_length)
1521                         return -1;
1522
1523                 // If we need to go back in the file
1524                 if (offset <= (long) file->position)
1525                 {
1526                         // If we still have the data we need in the output buffer
1527                         if (file->position - offset <= ztk->out_ind)
1528                         {
1529                                 ztk->out_ind -= file->position - offset;
1530                                 file->position = offset;
1531                                 return 0;
1532                         }
1533
1534                         // Else, we restart from the beginning of the file
1535                         ztk->in_ind = 0;
1536                         ztk->in_max = 0;
1537                         ztk->in_position = 0;
1538                         ztk->out_ind = 0;
1539                         ztk->out_max = 0;
1540                         ztk->out_position = 0;
1541                         file->position = 0;
1542                         fseek (file->stream, file->offset, SEEK_SET);
1543
1544                         // Reset the Zlib stream
1545                         ztk->zstream.next_in = ztk->input;
1546                         ztk->zstream.avail_in = 0;
1547                         qz_inflateReset (&ztk->zstream);
1548                 }
1549
1550                 // Skip all data until we reach the requested offset
1551                 while ((long) file->position < offset)
1552                 {
1553                         size_t diff = offset - file->position;
1554                         size_t count, len;
1555
1556                         count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1557                         len = FS_Read (file, buffer, count);
1558                         if (len != count)
1559                                 return -1;
1560                 }
1561
1562                 return 0;
1563         }
1564
1565         // Packed files receive a special treatment too, because
1566         // we need to make sure it doesn't go outside of the file
1567         switch (whence)
1568         {
1569                 case SEEK_CUR:
1570                         offset += file->position;
1571                         break;
1572
1573                 case SEEK_SET:
1574                         break;
1575
1576                 case SEEK_END:
1577                         offset += file->length;
1578                         break;
1579
1580                 default:
1581                         return -1;
1582         }
1583         if (offset < 0 || offset > (long) file->length)
1584                 return -1;
1585
1586         if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1587                 return -1;
1588         file->position = offset;
1589         return 0;
1590 }
1591
1592
1593 /*
1594 ====================
1595 FS_Tell
1596
1597 Give the current position in a file
1598 ====================
1599 */
1600 long FS_Tell (qfile_t* file)
1601 {
1602         if (file->flags & FS_FLAG_PACKED)
1603                 return file->position;
1604
1605         return ftell (file->stream);
1606 }
1607
1608
1609 /*
1610 ====================
1611 FS_Gets
1612
1613 Extract a line from a file
1614 ====================
1615 */
1616 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1617 {
1618         size_t ind;
1619
1620         // Quick path for unpacked files
1621         if (! (file->flags & FS_FLAG_PACKED))
1622                 return fgets (buffer, buffersize, file->stream);
1623
1624         for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1625         {
1626                 int c = FS_Getc (file);
1627                 switch (c)
1628                 {
1629                         // End of file
1630                         case -1:
1631                                 if (!ind)
1632                                         return NULL;
1633
1634                                 buffer[ind] = '\0';
1635                                 return buffer;
1636
1637                         // End of line
1638                         case '\r':
1639                         case '\n':
1640                                 buffer[ind] = '\n';
1641                                 buffer[ind + 1] = '\0';
1642                                 return buffer;
1643
1644                         default:
1645                                 buffer[ind] = c;
1646                 }
1647
1648         }
1649
1650         buffer[buffersize - 1] = '\0';
1651         return buffer;
1652 }
1653
1654
1655 /*
1656 ==========
1657 FS_Getline
1658
1659 Dynamic length version of fgets. DO NOT free the buffer.
1660 ==========
1661 */
1662 char *FS_Getline (qfile_t *file)
1663 {
1664         static int  size = 256;
1665         static char *buf = 0;
1666         char        *t;
1667         int         len;
1668
1669         if (!buf)
1670                 buf = Mem_Alloc (fs_mempool, size);
1671
1672         if (!FS_Gets (file, buf, size))
1673                 return 0;
1674
1675         len = strlen (buf);
1676         while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1677         {
1678                 t = Mem_Alloc (fs_mempool, size + 256);
1679                 memcpy(t, buf, size);
1680                 Mem_Free(buf);
1681                 size += 256;
1682                 buf = t;
1683                 if (!FS_Gets (file, buf + len, size - len))
1684                         break;
1685                 len = strlen (buf);
1686         }
1687         while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1688                 buf[len - 1] = 0;
1689         return buf;
1690 }
1691
1692
1693 /*
1694 ====================
1695 FS_Eof
1696
1697 Extract a line from a file
1698 ====================
1699 */
1700 int FS_Eof (qfile_t* file)
1701 {
1702         if (file->flags & FS_FLAG_PACKED)
1703         {
1704                 if (file->flags & FS_FLAG_DEFLATED)
1705                         return (file->position == file->z->real_length);
1706
1707                 return (file->position == file->length);
1708         }
1709
1710         return feof (file->stream);
1711 }
1712
1713
1714 /*
1715 ============
1716 FS_LoadFile
1717
1718 Filename are relative to the quake directory.
1719 Always appends a 0 byte.
1720 ============
1721 */
1722 qbyte *FS_LoadFile (const char *path, qboolean quiet)
1723 {
1724         qfile_t *h;
1725         qbyte *buf;
1726
1727         // look for it in the filesystem or pack files
1728         h = FS_Open (path, "rb", quiet);
1729         if (!h)
1730                 return NULL;
1731
1732         buf = Mem_Alloc(tempmempool, fs_filesize+1);
1733         if (!buf)
1734                 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1735
1736         ((qbyte *)buf)[fs_filesize] = 0;
1737
1738         FS_Read (h, buf, fs_filesize);
1739         FS_Close (h);
1740
1741         return buf;
1742 }
1743
1744
1745 /*
1746 ============
1747 FS_WriteFile
1748
1749 The filename will be prefixed by the current game directory
1750 ============
1751 */
1752 qboolean FS_WriteFile (const char *filename, void *data, int len)
1753 {
1754         qfile_t *handle;
1755
1756         handle = FS_Open (filename, "wb", false);
1757         if (!handle)
1758         {
1759                 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1760                 return false;
1761         }
1762
1763         Con_DPrintf("FS_WriteFile: %s\n", filename);
1764         FS_Write (handle, data, len);
1765         FS_Close (handle);
1766         return true;
1767 }
1768
1769
1770 /*
1771 =============================================================================
1772
1773 OTHERS PUBLIC FUNCTIONS
1774
1775 =============================================================================
1776 */
1777
1778 /*
1779 ============
1780 FS_StripExtension
1781 ============
1782 */
1783 void FS_StripExtension (const char *in, char *out, size_t size_out)
1784 {
1785         char *last = NULL;
1786
1787         if (size_out == 0)
1788                 return;
1789
1790         while (*in && size_out > 1)
1791         {
1792                 if (*in == '.')
1793                         last = out;
1794                 else if (*in == '/' || *in == '\\' || *in == ':')
1795                         last = NULL;
1796                 *out++ = *in++;
1797                 size_out--;
1798         }
1799         if (last)
1800                 *last = 0;
1801         else
1802                 *out = 0;
1803 }
1804
1805
1806 /*
1807 ==================
1808 FS_DefaultExtension
1809 ==================
1810 */
1811 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1812 {
1813         const char *src;
1814
1815         // if path doesn't have a .EXT, append extension
1816         // (extension should include the .)
1817         src = path + strlen(path) - 1;
1818
1819         while (*src != '/' && src != path)
1820         {
1821                 if (*src == '.')
1822                         return;                 // it has an extension
1823                 src--;
1824         }
1825
1826         strlcat (path, extension, size_path);
1827 }
1828
1829
1830 /*
1831 ==================
1832 FS_FileExists
1833
1834 Look for a file in the packages and in the filesystem
1835 ==================
1836 */
1837 qboolean FS_FileExists (const char *filename)
1838 {
1839         return (FS_FindFile (filename, NULL, true) != NULL);
1840 }
1841
1842
1843 /*
1844 ==================
1845 FS_SysFileExists
1846
1847 Look for a file in the filesystem only
1848 ==================
1849 */
1850 qboolean FS_SysFileExists (const char *path)
1851 {
1852 #if WIN32
1853         FILE *f;
1854
1855         f = fopen (path, "rb");
1856         if (f)
1857         {
1858                 fclose (f);
1859                 return true;
1860         }
1861
1862         return false;
1863 #else
1864         struct stat buf;
1865
1866         if (stat (path,&buf) == -1)
1867                 return false;
1868
1869         return true;
1870 #endif
1871 }
1872
1873 void FS_mkdir (const char *path)
1874 {
1875 #if WIN32
1876         _mkdir (path);
1877 #else
1878         mkdir (path, 0777);
1879 #endif
1880 }
1881
1882 /*
1883 ===========
1884 FS_Search
1885
1886 Allocate and fill a search structure with information on matching filenames.
1887 ===========
1888 */
1889 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
1890 {
1891         fssearch_t *search;
1892         searchpath_t *searchpath;
1893         pack_t *pak;
1894         int i, basepathlength, numfiles, numchars;
1895         stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
1896         const char *slash, *backslash, *colon, *separator;
1897         char *basepath;
1898         char netpath[MAX_OSPATH];
1899         char temp[MAX_OSPATH];
1900
1901         while(!strncmp(pattern, "./", 2))
1902                 pattern += 2;
1903         while(!strncmp(pattern, ".\\", 2))
1904                 pattern += 2;
1905
1906         search = NULL;
1907         liststart = NULL;
1908         listcurrent = NULL;
1909         listtemp = NULL;
1910         slash = strrchr(pattern, '/');
1911         backslash = strrchr(pattern, '\\');
1912         colon = strrchr(pattern, ':');
1913         separator = pattern;
1914         if (separator < slash)
1915                 separator = slash;
1916         if (separator < backslash)
1917                 separator = backslash;
1918         if (separator < colon)
1919                 separator = colon;
1920         basepathlength = separator - pattern;
1921         basepath = Z_Malloc(basepathlength + 1);
1922         if (basepathlength)
1923                 memcpy(basepath, pattern, basepathlength);
1924         basepath[basepathlength] = 0;
1925
1926         // search through the path, one element at a time
1927         for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
1928         {
1929                 // is the element a pak file?
1930                 if (searchpath->pack)
1931                 {
1932                         // look through all the pak file elements
1933                         pak = searchpath->pack;
1934                         for (i = 0;i < pak->numfiles;i++)
1935                         {
1936                                 strcpy(temp, pak->files[i].name);
1937                                 while (temp[0])
1938                                 {
1939                                         if (matchpattern(temp, (char *)pattern, true))
1940                                         {
1941                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1942                                                         if (!strcmp(listtemp->text, temp))
1943                                                                 break;
1944                                                 if (listtemp == NULL)
1945                                                 {
1946                                                         listcurrent = stringlistappend(listcurrent, temp);
1947                                                         if (liststart == NULL)
1948                                                                 liststart = listcurrent;
1949                                                         if (!quiet)
1950                                                                 Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
1951                                                 }
1952                                         }
1953                                         // strip off one path element at a time until empty
1954                                         // this way directories are added to the listing if they match the pattern
1955                                         slash = strrchr(temp, '/');
1956                                         backslash = strrchr(temp, '\\');
1957                                         colon = strrchr(temp, ':');
1958                                         separator = temp;
1959                                         if (separator < slash)
1960                                                 separator = slash;
1961                                         if (separator < backslash)
1962                                                 separator = backslash;
1963                                         if (separator < colon)
1964                                                 separator = colon;
1965                                         *((char *)separator) = 0;
1966                                 }
1967                         }
1968                 }
1969                 else
1970                 {
1971                         // get a directory listing and look at each name
1972                         snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
1973                         if ((dir = listdirectory(netpath)))
1974                         {
1975                                 for (dirfile = dir;dirfile;dirfile = dirfile->next)
1976                                 {
1977                                         snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
1978                                         if (matchpattern(temp, (char *)pattern, true))
1979                                         {
1980                                                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1981                                                         if (!strcmp(listtemp->text, temp))
1982                                                                 break;
1983                                                 if (listtemp == NULL)
1984                                                 {
1985                                                         listcurrent = stringlistappend(listcurrent, temp);
1986                                                         if (liststart == NULL)
1987                                                                 liststart = listcurrent;
1988                                                         if (!quiet)
1989                                                                 Sys_Printf("SearchDirFile: %s\n", temp);
1990                                                 }
1991                                         }
1992                                 }
1993                                 freedirectory(dir);
1994                         }
1995                 }
1996         }
1997
1998         if (liststart)
1999         {
2000                 liststart = stringlistsort(liststart);
2001                 numfiles = 0;
2002                 numchars = 0;
2003                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2004                 {
2005                         numfiles++;
2006                         numchars += strlen(listtemp->text) + 1;
2007                 }
2008                 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2009                 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2010                 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2011                 search->numfilenames = numfiles;
2012                 numfiles = 0;
2013                 numchars = 0;
2014                 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2015                 {
2016                         search->filenames[numfiles] = search->filenamesbuffer + numchars;
2017                         strcpy(search->filenames[numfiles], listtemp->text);
2018                         numfiles++;
2019                         numchars += strlen(listtemp->text) + 1;
2020                 }
2021                 if (liststart)
2022                         stringlistfree(liststart);
2023         }
2024
2025         Z_Free(basepath);
2026         return search;
2027 }
2028
2029 void FS_FreeSearch(fssearch_t *search)
2030 {
2031         Z_Free(search);
2032 }
2033
2034 extern int con_linewidth;
2035 int FS_ListDirectory(const char *pattern, int oneperline)
2036 {
2037         int numfiles;
2038         int numcolumns;
2039         int numlines;
2040         int columnwidth;
2041         int linebufpos;
2042         int i, j, k, l;
2043         const char *name;
2044         char linebuf[4096];
2045         fssearch_t *search;
2046         search = FS_Search(pattern, true, false);
2047         if (!search)
2048                 return 0;
2049         numfiles = search->numfilenames;
2050         if (!oneperline)
2051         {
2052                 // FIXME: the names could be added to one column list and then
2053                 // gradually shifted into the next column if they fit, and then the
2054                 // next to make a compact variable width listing but it's a lot more
2055                 // complicated...
2056                 // find width for columns
2057                 columnwidth = 0;
2058                 for (i = 0;i < numfiles;i++)
2059                 {
2060                         l = strlen(search->filenames[i]);
2061                         if (columnwidth < l)
2062                                 columnwidth = l;
2063                 }
2064                 // count the spacing character
2065                 columnwidth++;
2066                 // calculate number of columns
2067                 numcolumns = con_linewidth / columnwidth;
2068                 // don't bother with the column printing if it's only one column
2069                 if (numcolumns >= 2)
2070                 {
2071                         numlines = (numfiles + numcolumns - 1) / numcolumns;
2072                         for (i = 0;i < numlines;i++)
2073                         {
2074                                 linebufpos = 0;
2075                                 for (k = 0;k < numcolumns;k++)
2076                                 {
2077                                         l = i * numcolumns + k;
2078                                         if (l < numfiles)
2079                                         {
2080                                                 name = search->filenames[l];
2081                                                 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2082                                                         linebuf[linebufpos++] = name[j];
2083                                                 // space out name unless it's the last on the line
2084                                                 if (k < (numcolumns - 1) && l < (numfiles - 1))
2085                                                         for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2086                                                                 linebuf[linebufpos++] = ' ';
2087                                         }
2088                                 }
2089                                 linebuf[linebufpos] = 0;
2090                                 Con_Printf("%s\n", linebuf);
2091                         }
2092                 }
2093                 else
2094                         oneperline = true;
2095         }
2096         if (oneperline)
2097                 for (i = 0;i < numfiles;i++)
2098                         Con_Printf("%s\n", search->filenames[i]);
2099         FS_FreeSearch(search);
2100         return numfiles;
2101 }
2102
2103 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2104 {
2105         const char *pattern;
2106         if (Cmd_Argc() > 3)
2107         {
2108                 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2109                 return;
2110         }
2111         if (Cmd_Argc() == 2)
2112                 pattern = Cmd_Argv(1);
2113         else
2114                 pattern = "*";
2115         if (!FS_ListDirectory(pattern, oneperline))
2116                 Con_Print("No files found.\n");
2117 }
2118
2119 void FS_Dir_f(void)
2120 {
2121         FS_ListDirectoryCmd("dir", true);
2122 }
2123
2124 void FS_Ls_f(void)
2125 {
2126         FS_ListDirectoryCmd("ls", false);
2127 }
2128