4 Copyright (C) 2003-2006 Mathieu Olivier
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to:
20 Free Software Foundation, Inc.
21 59 Temple Place - Suite 330
22 Boston, MA 02111-1307, USA
25 // on UNIX platforms we need to define this so that video saving does not cause a SIGFSZ (file size) signal when a video clip exceeds 2GB
26 #define _FILE_OFFSET_BITS 64
38 # include <sys/stat.h>
45 // Win32 requires us to add O_BINARY, but the other OSes don't have it
50 // In case the system doesn't support the O_NONBLOCK flag
58 All of Quake's data access is through a hierchal file system, but the contents
59 of the file system can be transparently merged from several sources.
61 The "base directory" is the path to the directory holding the quake.exe and
62 all game directories. The sys_* files pass this to host_init in
63 quakeparms_t->basedir. This can be overridden with the "-basedir" command
64 line parm to allow code debugging in a different directory. The base
65 directory is only used during filesystem initialization.
67 The "game directory" is the first tree on the search path and directory that
68 all generated files (savegames, screenshots, demos, config files) will be
69 saved to. This can be overridden with the "-game" command line parameter.
70 The game directory can never be changed while quake is executing. This is a
71 precaution against having a malicious server instruct clients to write files
72 over areas they shouldn't.
78 =============================================================================
82 =============================================================================
85 // Magic numbers of a ZIP file (big-endian format)
86 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
87 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
88 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
90 // Other constants for ZIP files
91 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
92 #define ZIP_END_CDIR_SIZE 22
93 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
94 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
96 // Zlib constants (from zlib.h)
97 #define Z_SYNC_FLUSH 2
100 #define Z_STREAM_END 1
101 #define ZLIB_VERSION "1.2.3"
103 // Uncomment the following line if the zlib DLL you have still uses
104 // the 1.1.x series calling convention on Win32 (WINAPI)
105 //#define ZLIB_USES_WINAPI
109 =============================================================================
113 =============================================================================
116 // Zlib stream (from zlib.h)
117 // Warning: some pointers we don't use directly have
118 // been cast to "void*" for a matter of simplicity
121 unsigned char *next_in; // next input byte
122 unsigned int avail_in; // number of bytes available at next_in
123 unsigned long total_in; // total nb of input bytes read so far
125 unsigned char *next_out; // next output byte should be put there
126 unsigned int avail_out; // remaining free space at next_out
127 unsigned long total_out; // total nb of bytes output so far
129 char *msg; // last error message, NULL if no error
130 void *state; // not visible by applications
132 void *zalloc; // used to allocate the internal state
133 void *zfree; // used to free the internal state
134 void *opaque; // private data object passed to zalloc and zfree
136 int data_type; // best guess about the data type: ascii or binary
137 unsigned long adler; // adler32 value of the uncompressed data
138 unsigned long reserved; // reserved for future use
142 // inside a package (PAK or PK3)
143 #define QFILE_FLAG_PACKED (1 << 0)
144 // file is compressed using the deflate algorithm (PK3 only)
145 #define QFILE_FLAG_DEFLATED (1 << 1)
147 #define FILE_BUFF_SIZE 2048
151 size_t comp_length; // length of the compressed file
152 size_t in_ind, in_len; // input buffer current index and length
153 size_t in_position; // position in the compressed file
154 unsigned char input [FILE_BUFF_SIZE];
160 int handle; // file descriptor
161 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
162 fs_offset_t position; // current position in the file
163 fs_offset_t offset; // offset into the package (0 if external file)
164 int ungetc; // single stored character from ungetc, cleared to EOF when read
167 fs_offset_t buff_ind, buff_len; // buffer current index and length
168 unsigned char buff [FILE_BUFF_SIZE];
175 // ------ PK3 files on disk ------ //
177 // You can get the complete ZIP format description from PKWARE website
179 typedef struct pk3_endOfCentralDir_s
181 unsigned int signature;
182 unsigned short disknum;
183 unsigned short cdir_disknum; // number of the disk with the start of the central directory
184 unsigned short localentries; // number of entries in the central directory on this disk
185 unsigned short nbentries; // total number of entries in the central directory on this disk
186 unsigned int cdir_size; // size of the central directory
187 unsigned int cdir_offset; // with respect to the starting disk number
188 unsigned short comment_size;
189 } pk3_endOfCentralDir_t;
192 // ------ PAK files on disk ------ //
193 typedef struct dpackfile_s
196 int filepos, filelen;
199 typedef struct dpackheader_s
207 // Packages in memory
208 // the offset in packfile_t is the true contents offset
209 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
210 // file compressed using the deflate algorithm
211 #define PACKFILE_FLAG_DEFLATED (1 << 1)
213 typedef struct packfile_s
215 char name [MAX_QPATH];
218 fs_offset_t packsize; // size in the package
219 fs_offset_t realsize; // real file size (uncompressed)
222 typedef struct pack_s
224 char filename [MAX_OSPATH];
226 int ignorecase; // PK3 ignores case
232 // Search paths for files (including packages)
233 typedef struct searchpath_s
235 // only one of filename / pack will be used
236 char filename[MAX_OSPATH];
238 struct searchpath_s *next;
243 =============================================================================
247 =============================================================================
253 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
254 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
255 fs_offset_t offset, fs_offset_t packsize,
256 fs_offset_t realsize, int flags);
260 =============================================================================
264 =============================================================================
267 mempool_t *fs_mempool;
269 searchpath_t *fs_searchpaths = NULL;
271 #define MAX_FILES_IN_PACK 65536
273 char fs_gamedir[MAX_OSPATH];
274 char fs_basedir[MAX_OSPATH];
276 // list of active game directories (empty if not running a mod)
277 #define MAX_GAMEDIRS 16
278 int fs_numgamedirs = 0;
279 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
281 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running)"};
282 cvar_t fs_empty_files_in_pack_mark_deletions = {0, "fs_empty_files_in_pack_mark_deletions", "0", "if enabled, empty files in a pak/pk3 count as not existing but cancel the search in further packs, effectively allowing patch pak/pk3 files to 'delete' files"};
286 =============================================================================
288 PRIVATE FUNCTIONS - PK3 HANDLING
290 =============================================================================
293 // Functions exported from zlib
294 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
295 # define ZEXPORT WINAPI
300 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
301 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
302 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
303 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
305 #define qz_inflateInit2(strm, windowBits) \
306 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
308 static dllfunction_t zlibfuncs[] =
310 {"inflate", (void **) &qz_inflate},
311 {"inflateEnd", (void **) &qz_inflateEnd},
312 {"inflateInit2_", (void **) &qz_inflateInit2_},
313 {"inflateReset", (void **) &qz_inflateReset},
317 // Handle for Zlib DLL
318 static dllhandle_t zlib_dll = NULL;
328 void PK3_CloseLibrary (void)
330 Sys_UnloadLibrary (&zlib_dll);
338 Try to load the Zlib DLL
341 qboolean PK3_OpenLibrary (void)
343 const char* dllnames [] =
348 # ifdef ZLIB_USES_WINAPI
354 #elif defined(MACOSX)
368 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
370 Con_Printf ("Compressed files support disabled\n");
374 Con_Printf ("Compressed files support enabled\n");
381 PK3_GetEndOfCentralDir
383 Extract the end of the central directory from a PK3 package
386 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
388 fs_offset_t filesize, maxsize;
389 unsigned char *buffer, *ptr;
392 // Get the package size
393 filesize = lseek (packhandle, 0, SEEK_END);
394 if (filesize < ZIP_END_CDIR_SIZE)
397 // Load the end of the file in memory
398 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
401 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
402 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
403 lseek (packhandle, filesize - maxsize, SEEK_SET);
404 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
410 // Look for the end of central dir signature around the end of the file
411 maxsize -= ZIP_END_CDIR_SIZE;
412 ptr = &buffer[maxsize];
414 while (BuffBigLong (ptr) != ZIP_END_HEADER)
426 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
427 eocd->signature = LittleLong (eocd->signature);
428 eocd->disknum = LittleShort (eocd->disknum);
429 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
430 eocd->localentries = LittleShort (eocd->localentries);
431 eocd->nbentries = LittleShort (eocd->nbentries);
432 eocd->cdir_size = LittleLong (eocd->cdir_size);
433 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
434 eocd->comment_size = LittleShort (eocd->comment_size);
446 Extract the file list from a PK3 file
449 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
451 unsigned char *central_dir, *ptr;
453 fs_offset_t remaining;
455 // Load the central directory in memory
456 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
457 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
458 read (pack->handle, central_dir, eocd->cdir_size);
460 // Extract the files properties
461 // The parsing is done "by hand" because some fields have variable sizes and
462 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
463 remaining = eocd->cdir_size;
466 for (ind = 0; ind < eocd->nbentries; ind++)
468 fs_offset_t namesize, count;
470 // Checking the remaining size
471 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
473 Mem_Free (central_dir);
476 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
479 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
481 Mem_Free (central_dir);
485 namesize = BuffLittleShort (&ptr[28]); // filename length
487 // Check encryption, compression, and attributes
488 // 1st uint8 : general purpose bit flag
489 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
490 // 2nd uint8 : external file attributes
491 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
492 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
494 // Still enough bytes for the name?
495 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
497 Mem_Free (central_dir);
501 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
502 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
504 char filename [sizeof (pack->files[0].name)];
505 fs_offset_t offset, packsize, realsize;
508 // Extract the name (strip it if necessary)
509 namesize = min(namesize, (int)sizeof (filename) - 1);
510 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
511 filename[namesize] = '\0';
513 if (BuffLittleShort (&ptr[10]))
514 flags = PACKFILE_FLAG_DEFLATED;
517 offset = BuffLittleLong (&ptr[42]);
518 packsize = BuffLittleLong (&ptr[20]);
519 realsize = BuffLittleLong (&ptr[24]);
520 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
524 // Skip the name, additionnal field, and comment
525 // 1er uint16 : extra field length
526 // 2eme uint16 : file comment length
527 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
528 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
532 // If the package is empty, central_dir is NULL here
533 if (central_dir != NULL)
534 Mem_Free (central_dir);
535 return pack->numfiles;
543 Create a package entry associated with a PK3 file
546 pack_t *FS_LoadPackPK3 (const char *packfile)
549 pk3_endOfCentralDir_t eocd;
553 packhandle = open (packfile, O_RDONLY | O_BINARY);
557 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
559 Con_Printf ("%s is not a PK3 file\n", packfile);
564 // Multi-volume ZIP archives are NOT allowed
565 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
567 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
572 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
573 // since eocd.nbentries is an unsigned 16 bits integer
574 #if MAX_FILES_IN_PACK < 65535
575 if (eocd.nbentries > MAX_FILES_IN_PACK)
577 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
583 // Create a package structure in memory
584 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
585 pack->ignorecase = true; // PK3 ignores case
586 strlcpy (pack->filename, packfile, sizeof (pack->filename));
587 pack->handle = packhandle;
588 pack->numfiles = eocd.nbentries;
589 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
591 real_nb_files = PK3_BuildFileList (pack, &eocd);
592 if (real_nb_files < 0)
594 Con_Printf ("%s is not a valid PK3 file\n", packfile);
600 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
607 PK3_GetTrueFileOffset
609 Find where the true file data offset is
612 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
614 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
618 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
621 // Load the local file description
622 lseek (pack->handle, pfile->offset, SEEK_SET);
623 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
624 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
626 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
630 // Skip name and extra field
631 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
633 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
639 =============================================================================
641 OTHER PRIVATE FUNCTIONS
643 =============================================================================
651 Add a file to the list of files contained into a package
654 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
655 fs_offset_t offset, fs_offset_t packsize,
656 fs_offset_t realsize, int flags)
658 int (*strcmp_funct) (const char* str1, const char* str2);
659 int left, right, middle;
662 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
664 // Look for the slot we should put that file into (binary search)
666 right = pack->numfiles - 1;
667 while (left <= right)
671 middle = (left + right) / 2;
672 diff = strcmp_funct (pack->files[middle].name, name);
674 // If we found the file, there's a problem
676 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
678 // If we're too far in the list
685 // We have to move the right of the list by one slot to free the one we need
686 pfile = &pack->files[left];
687 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
690 strlcpy (pfile->name, name, sizeof (pfile->name));
691 pfile->offset = offset;
692 pfile->packsize = packsize;
693 pfile->realsize = realsize;
694 pfile->flags = flags;
704 Only used for FS_Open.
707 void FS_CreatePath (char *path)
711 for (ofs = path+1 ; *ofs ; ofs++)
713 if (*ofs == '/' || *ofs == '\\')
715 // create the directory
731 void FS_Path_f (void)
735 Con_Print("Current search path:\n");
736 for (s=fs_searchpaths ; s ; s=s->next)
739 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
741 Con_Printf("%s\n", s->filename);
750 Takes an explicit (not game tree related) path to a pak file.
752 Loads the header and directory, adding the files at the beginning
753 of the list so they override previous pack files.
756 pack_t *FS_LoadPackPAK (const char *packfile)
758 dpackheader_t header;
764 packhandle = open (packfile, O_RDONLY | O_BINARY);
767 read (packhandle, (void *)&header, sizeof(header));
768 if (memcmp(header.id, "PACK", 4))
770 Con_Printf ("%s is not a packfile\n", packfile);
774 header.dirofs = LittleLong (header.dirofs);
775 header.dirlen = LittleLong (header.dirlen);
777 if (header.dirlen % sizeof(dpackfile_t))
779 Con_Printf ("%s has an invalid directory size\n", packfile);
784 numpackfiles = header.dirlen / sizeof(dpackfile_t);
786 if (numpackfiles > MAX_FILES_IN_PACK)
788 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
793 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
794 lseek (packhandle, header.dirofs, SEEK_SET);
795 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
797 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
803 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
804 pack->ignorecase = false; // PAK is case sensitive
805 strlcpy (pack->filename, packfile, sizeof (pack->filename));
806 pack->handle = packhandle;
808 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
810 // parse the directory
811 for (i = 0;i < numpackfiles;i++)
813 fs_offset_t offset = LittleLong (info[i].filepos);
814 fs_offset_t size = LittleLong (info[i].filelen);
816 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
821 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
829 Adds the given pack to the search path.
830 The pack type is autodetected by the file extension.
832 Returns true if the file was successfully added to the
833 search path or if it was already included.
835 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
839 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
841 searchpath_t *search;
843 const char *ext = FS_FileExtension(pakfile);
845 for(search = fs_searchpaths; search; search = search->next)
847 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
850 *already_loaded = true;
851 return true; // already loaded
856 *already_loaded = false;
858 if(!strcasecmp(ext, "pak"))
859 pak = FS_LoadPackPAK (pakfile);
860 else if(!strcasecmp(ext, "pk3"))
861 pak = FS_LoadPackPK3 (pakfile);
863 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
869 // find the first item whose next one is a pack or NULL
870 searchpath_t *insertion_point = 0;
871 if(fs_searchpaths && !fs_searchpaths->pack)
873 insertion_point = fs_searchpaths;
876 if(!insertion_point->next)
878 if(insertion_point->next->pack)
880 insertion_point = insertion_point->next;
883 // If insertion_point is NULL, this means that either there is no
884 // item in the list yet, or that the very first item is a pack. In
885 // that case, we want to insert at the beginning...
888 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
890 search->next = fs_searchpaths;
891 fs_searchpaths = search;
894 // otherwise we want to append directly after insertion_point.
896 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
898 search->next = insertion_point->next;
899 insertion_point->next = search;
904 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
906 search->next = fs_searchpaths;
907 fs_searchpaths = search;
913 Con_Printf("unable to load pak \"%s\"\n", pakfile);
923 Adds the given pack to the search path and searches for it in the game path.
924 The pack type is autodetected by the file extension.
926 Returns true if the file was successfully added to the
927 search path or if it was already included.
929 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
933 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
935 char fullpath[MAX_QPATH];
937 searchpath_t *search;
940 *already_loaded = false;
942 // then find the real name...
943 search = FS_FindFile(pakfile, &index, true);
944 if(!search || search->pack)
946 Con_Printf("could not find pak \"%s\"\n", pakfile);
950 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
952 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
960 Sets fs_gamedir, adds the directory to the head of the path,
961 then loads and adds pak1.pak pak2.pak ...
964 void FS_AddGameDirectory (const char *dir)
968 searchpath_t *search;
969 char pakfile[MAX_OSPATH];
971 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
973 stringlistinit(&list);
974 listdirectory(&list, dir);
975 stringlistsort(&list);
977 // add any PAK package in the directory
978 for (i = 0;i < list.numstrings;i++)
980 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
982 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
983 FS_AddPack_Fullpath(pakfile, NULL, false);
987 // add any PK3 package in the directory
988 for (i = 0;i < list.numstrings;i++)
990 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
992 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
993 FS_AddPack_Fullpath(pakfile, NULL, false);
997 stringlistfreecontents(&list);
999 // Add the directory to the search path
1000 // (unpacked files have the priority over packed files)
1001 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1002 strlcpy (search->filename, dir, sizeof (search->filename));
1003 search->next = fs_searchpaths;
1004 fs_searchpaths = search;
1013 void FS_AddGameHierarchy (const char *dir)
1016 const char *homedir;
1019 // Add the common game directory
1020 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1023 // Add the personal game directory
1024 homedir = getenv ("HOME");
1025 if (homedir != NULL && homedir[0] != '\0')
1026 FS_AddGameDirectory (va("%s/.%s/%s/", homedir, gameuserdirname, dir));
1036 const char *FS_FileExtension (const char *in)
1038 const char *separator, *backslash, *colon, *dot;
1040 separator = strrchr(in, '/');
1041 backslash = strrchr(in, '\\');
1042 if (!separator || separator < backslash)
1043 separator = backslash;
1044 colon = strrchr(in, ':');
1045 if (!separator || separator < colon)
1048 dot = strrchr(in, '.');
1049 if (dot == NULL || (separator && (dot < separator)))
1061 const char *FS_FileWithoutPath (const char *in)
1063 const char *separator, *backslash, *colon;
1065 separator = strrchr(in, '/');
1066 backslash = strrchr(in, '\\');
1067 if (!separator || separator < backslash)
1068 separator = backslash;
1069 colon = strrchr(in, ':');
1070 if (!separator || separator < colon)
1072 return separator ? separator + 1 : in;
1081 void FS_ClearSearchPath (void)
1083 while (fs_searchpaths)
1085 searchpath_t *search = fs_searchpaths;
1086 fs_searchpaths = search->next;
1089 if (search->pack->files)
1090 Mem_Free(search->pack->files);
1091 Mem_Free(search->pack);
1103 void FS_Rescan (void)
1106 qboolean fs_modified = false;
1108 FS_ClearSearchPath();
1110 // add the game-specific paths
1111 // gamedirname1 (typically id1)
1112 FS_AddGameHierarchy (gamedirname1);
1113 // update the com_modname (used for server info)
1114 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1116 // add the game-specific path, if any
1117 // (only used for mission packs and the like, which should set fs_modified)
1121 FS_AddGameHierarchy (gamedirname2);
1125 // Adds basedir/gamedir as an override game
1126 // LordHavoc: now supports multiple -game directories
1127 // set the com_modname (reported in server info)
1128 for (i = 0;i < fs_numgamedirs;i++)
1131 FS_AddGameHierarchy (fs_gamedirs[i]);
1132 // update the com_modname (used server info)
1133 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1136 // set the default screenshot name to either the mod name or the
1137 // gamemode screenshot name
1138 if (strcmp(com_modname, gamedirname1))
1139 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1141 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1143 // If "-condebug" is in the command line, remove the previous log file
1144 if (COM_CheckParm ("-condebug") != 0)
1145 unlink (va("%s/qconsole.log", fs_gamedir));
1147 // look for the pop.lmp file and set registered to true if it is found
1148 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1151 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1153 Con_Print("Playing shareware version.\n");
1157 Cvar_Set ("registered", "1");
1158 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1159 Con_Print("Playing registered version.\n");
1162 // unload all wads so that future queries will return the new data
1166 void FS_Rescan_f(void)
1176 extern void Host_SaveConfig_f (void);
1177 extern void Host_LoadConfig_f (void);
1178 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1182 if (fs_numgamedirs == numgamedirs)
1184 for (i = 0;i < numgamedirs;i++)
1185 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1187 if (i == numgamedirs)
1188 return true; // already using this set of gamedirs, do nothing
1191 if (numgamedirs > MAX_GAMEDIRS)
1194 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1195 return false; // too many gamedirs
1198 for (i = 0;i < numgamedirs;i++)
1200 // if string is nasty, reject it
1201 if(FS_CheckNastyPath(gamedirs[i], true))
1204 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1205 return false; // nasty gamedirs
1209 for (i = 0;i < numgamedirs;i++)
1211 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1214 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1215 return false; // missing gamedirs
1219 Host_SaveConfig_f();
1221 fs_numgamedirs = numgamedirs;
1222 for (i = 0;i < fs_numgamedirs;i++)
1223 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1225 // reinitialize filesystem to detect the new paks
1228 // exec the new config
1229 Host_LoadConfig_f();
1231 // reinitialize the loaded sounds
1234 // reinitialize renderer (this reloads hud/console background/etc)
1235 R_Modules_Restart();
1245 void FS_GameDir_f (void)
1249 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1253 Con_Printf("gamedirs active:");
1254 for (i = 0;i < fs_numgamedirs;i++)
1255 Con_Printf(" %s", fs_gamedirs[i]);
1260 numgamedirs = Cmd_Argc() - 1;
1261 if (numgamedirs > MAX_GAMEDIRS)
1263 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1267 for (i = 0;i < numgamedirs;i++)
1268 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1270 // allow gamedir change during demo loop
1271 if (cls.demoplayback)
1274 if (cls.state == ca_connected || sv.active)
1276 // actually, changing during game would work fine, but would be stupid
1277 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1281 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1290 qboolean FS_CheckGameDir(const char *gamedir)
1294 stringlistinit(&list);
1295 listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
1296 success = list.numstrings > 0;
1297 stringlistfreecontents(&list);
1311 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1313 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1315 // If the base directory is explicitly defined by the compilation process
1316 #ifdef DP_FS_BASEDIR
1317 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1319 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1322 // FIXME: is there a better way to find the directory outside the .app?
1323 if (strstr(com_argv[0], ".app/"))
1327 split = strstr(com_argv[0], ".app/");
1328 while (split > com_argv[0] && *split != '/')
1330 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1331 fs_basedir[split - com_argv[0]] = 0;
1339 // Overrides the system supplied base directory (under GAMENAME)
1340 // COMMANDLINEOPTION: Filesystem: -basedir <path> chooses what base directory the game data is in, inside this there should be a data directory for the game (for example id1)
1341 i = COM_CheckParm ("-basedir");
1342 if (i && i < com_argc-1)
1344 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1345 i = (int)strlen (fs_basedir);
1346 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1347 fs_basedir[i-1] = 0;
1350 // add a path separator to the end of the basedir if it lacks one
1351 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1352 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1354 if (!FS_CheckGameDir(gamedirname1))
1355 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1357 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1358 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1361 // Adds basedir/gamedir as an override game
1362 // LordHavoc: now supports multiple -game directories
1363 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1367 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1370 if (FS_CheckNastyPath(com_argv[i], true))
1371 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1372 if (!FS_CheckGameDir(com_argv[i]))
1373 Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1374 // add the gamedir to the list of active gamedirs
1375 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1380 // generate the searchpath
1384 void FS_Init_Commands(void)
1386 Cvar_RegisterVariable (&scr_screenshot_name);
1387 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1389 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1390 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1391 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1392 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1393 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1401 void FS_Shutdown (void)
1403 Mem_FreePool (&fs_mempool);
1407 ====================
1410 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1411 ====================
1413 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1419 // Parse the mode string
1428 opt = O_CREAT | O_TRUNC;
1432 opt = O_CREAT | O_APPEND;
1435 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1438 for (ind = 1; mode[ind] != '\0'; ind++)
1449 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1450 filepath, mode, mode[ind]);
1457 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1458 memset (file, 0, sizeof (*file));
1461 file->handle = open (filepath, mod | opt, 0666);
1462 if (file->handle < 0)
1468 file->real_length = lseek (file->handle, 0, SEEK_END);
1470 // For files opened in append mode, we start at the end of the file
1472 file->position = file->real_length;
1474 lseek (file->handle, 0, SEEK_SET);
1484 Open a packed file using its package file descriptor
1487 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1493 pfile = &pack->files[pack_ind];
1495 // If we don't have the true offset, get it now
1496 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1497 if (!PK3_GetTrueFileOffset (pfile, pack))
1500 // No Zlib DLL = no compressed files
1501 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1503 Con_Printf("WARNING: can't open the compressed file %s\n"
1504 "You need the Zlib DLL to use compressed files\n",
1509 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1510 // the dup() call to avoid having to close the dup_handle on error here
1511 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1513 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1514 pfile->name, pack->filename, (int) pfile->offset);
1518 dup_handle = dup (pack->handle);
1521 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1525 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1526 memset (file, 0, sizeof (*file));
1527 file->handle = dup_handle;
1528 file->flags = QFILE_FLAG_PACKED;
1529 file->real_length = pfile->realsize;
1530 file->offset = pfile->offset;
1534 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1538 file->flags |= QFILE_FLAG_DEFLATED;
1540 // We need some more variables
1541 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1543 ztk->comp_length = pfile->packsize;
1545 // Initialize zlib stream
1546 ztk->zstream.next_in = ztk->input;
1547 ztk->zstream.avail_in = 0;
1549 /* From Zlib's "unzip.c":
1551 * windowBits is passed < 0 to tell that there is no zlib header.
1552 * Note that in this case inflate *requires* an extra "dummy" byte
1553 * after the compressed stream in order to complete decompression and
1554 * return Z_STREAM_END.
1555 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1556 * size of both compressed and uncompressed data
1558 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1560 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1566 ztk->zstream.next_out = file->buff;
1567 ztk->zstream.avail_out = sizeof (file->buff);
1576 ====================
1579 Return true if the path should be rejected due to one of the following:
1580 1: path elements that are non-portable
1581 2: path elements that would allow access to files outside the game directory,
1582 or are just not a good idea for a mod to be using.
1583 ====================
1585 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1587 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1591 // Windows: don't allow \ in filenames (windows-only), period.
1592 // (on Windows \ is a directory separator, but / is also supported)
1593 if (strstr(path, "\\"))
1594 return 1; // non-portable
1596 // Mac: don't allow Mac-only filenames - : is a directory separator
1597 // instead of /, but we rely on / working already, so there's no reason to
1598 // support a Mac-only path
1599 // Amiga and Windows: : tries to go to root of drive
1600 if (strstr(path, ":"))
1601 return 1; // non-portable attempt to go to root of drive
1603 // Amiga: // is parent directory
1604 if (strstr(path, "//"))
1605 return 1; // non-portable attempt to go to parent directory
1607 // all: don't allow going to parent directory (../ or /../)
1608 if (strstr(path, ".."))
1609 return 2; // attempt to go outside the game directory
1611 // Windows and UNIXes: don't allow absolute paths
1613 return 2; // attempt to go outside the game directory
1615 // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements), this catches all imaginable cases of ./, ../, .../, etc
1616 if (strchr(path, '.'))
1620 // gamedir is entirely path elements, so simply forbid . entirely
1623 if (strchr(path, '.') < strrchr(path, '/'))
1624 return 2; // possible attempt to go outside the game directory
1627 // all: forbid trailing slash on gamedir
1628 if (isgamedir && path[strlen(path)-1] == '/')
1631 // all: forbid leading dot on any filename for any reason
1632 if (strstr(path, "/."))
1633 return 2; // attempt to go outside the game directory
1635 // after all these checks we're pretty sure it's a / separated filename
1636 // and won't do much if any harm
1642 ====================
1645 Look for a file in the packages and in the filesystem
1647 Return the searchpath where the file was found (or NULL)
1648 and the file index in the package if relevant
1649 ====================
1651 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1653 searchpath_t *search;
1656 // search through the path, one element at a time
1657 for (search = fs_searchpaths;search;search = search->next)
1659 // is the element a pak file?
1662 int (*strcmp_funct) (const char* str1, const char* str2);
1663 int left, right, middle;
1666 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1668 // Look for the file (binary search)
1670 right = pak->numfiles - 1;
1671 while (left <= right)
1675 middle = (left + right) / 2;
1676 diff = strcmp_funct (pak->files[middle].name, name);
1681 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1683 // yes, but the first one is empty so we treat it as not being there
1684 if (!quiet && developer.integer >= 10)
1685 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1692 if (!quiet && developer.integer >= 10)
1693 Con_Printf("FS_FindFile: %s in %s\n",
1694 pak->files[middle].name, pak->filename);
1701 // If we're too far in the list
1710 char netpath[MAX_OSPATH];
1711 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1712 if (FS_SysFileExists (netpath))
1714 if (!quiet && developer.integer >= 10)
1715 Con_Printf("FS_FindFile: %s\n", netpath);
1724 if (!quiet && developer.integer >= 10)
1725 Con_Printf("FS_FindFile: can't find %s\n", name);
1737 Look for a file in the search paths and open it in read-only mode
1740 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1742 searchpath_t *search;
1745 search = FS_FindFile (filename, &pack_ind, quiet);
1751 // Found in the filesystem?
1754 char path [MAX_OSPATH];
1755 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1756 return FS_SysOpen (path, "rb", nonblocking);
1759 // So, we found it in a package...
1760 return FS_OpenPackedFile (search->pack, pack_ind);
1765 =============================================================================
1767 MAIN PUBLIC FUNCTIONS
1769 =============================================================================
1773 ====================
1776 Open a file. The syntax is the same as fopen
1777 ====================
1779 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1781 if (FS_CheckNastyPath(filepath, false))
1783 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1787 // If the file is opened in "write", "append", or "read/write" mode
1788 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1790 char real_path [MAX_OSPATH];
1792 // Open the file on disk directly
1793 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1795 // Create directories up to the file
1796 FS_CreatePath (real_path);
1798 return FS_SysOpen (real_path, mode, nonblocking);
1800 // Else, we look at the various search paths and open the file in read-only mode
1802 return FS_OpenReadFile (filepath, quiet, nonblocking);
1807 ====================
1811 ====================
1813 int FS_Close (qfile_t* file)
1815 if (close (file->handle))
1820 qz_inflateEnd (&file->ztk->zstream);
1821 Mem_Free (file->ztk);
1830 ====================
1833 Write "datasize" bytes into a file
1834 ====================
1836 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1840 // If necessary, seek to the exact file position we're supposed to be
1841 if (file->buff_ind != file->buff_len)
1842 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1844 // Purge cached data
1847 // Write the buffer and update the position
1848 result = write (file->handle, data, (fs_offset_t)datasize);
1849 file->position = lseek (file->handle, 0, SEEK_CUR);
1850 if (file->real_length < file->position)
1851 file->real_length = file->position;
1861 ====================
1864 Read up to "buffersize" bytes from a file
1865 ====================
1867 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1869 fs_offset_t count, done;
1871 if (buffersize == 0)
1874 // Get rid of the ungetc character
1875 if (file->ungetc != EOF)
1877 ((char*)buffer)[0] = file->ungetc;
1885 // First, we copy as many bytes as we can from "buff"
1886 if (file->buff_ind < file->buff_len)
1888 count = file->buff_len - file->buff_ind;
1890 done += ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1891 memcpy (buffer, &file->buff[file->buff_ind], done);
1892 file->buff_ind += done;
1895 if (buffersize == 0)
1899 // NOTE: at this point, the read buffer is always empty
1901 // If the file isn't compressed
1902 if (! (file->flags & QFILE_FLAG_DEFLATED))
1906 // We must take care to not read after the end of the file
1907 count = file->real_length - file->position;
1909 // If we have a lot of data to get, put them directly into "buffer"
1910 if (buffersize > sizeof (file->buff) / 2)
1912 if (count > (fs_offset_t)buffersize)
1913 count = (fs_offset_t)buffersize;
1914 lseek (file->handle, file->offset + file->position, SEEK_SET);
1915 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1919 file->position += nb;
1921 // Purge cached data
1927 if (count > (fs_offset_t)sizeof (file->buff))
1928 count = (fs_offset_t)sizeof (file->buff);
1929 lseek (file->handle, file->offset + file->position, SEEK_SET);
1930 nb = read (file->handle, file->buff, count);
1933 file->buff_len = nb;
1934 file->position += nb;
1936 // Copy the requested data in "buffer" (as much as we can)
1937 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1938 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1939 file->buff_ind = count;
1947 // If the file is compressed, it's more complicated...
1948 // We cycle through a few operations until we have read enough data
1949 while (buffersize > 0)
1951 ztoolkit_t *ztk = file->ztk;
1954 // NOTE: at this point, the read buffer is always empty
1956 // If "input" is also empty, we need to refill it
1957 if (ztk->in_ind == ztk->in_len)
1959 // If we are at the end of the file
1960 if (file->position == file->real_length)
1963 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
1964 if (count > (fs_offset_t)sizeof (ztk->input))
1965 count = (fs_offset_t)sizeof (ztk->input);
1966 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
1967 if (read (file->handle, ztk->input, count) != count)
1969 Con_Printf ("FS_Read: unexpected end of file\n");
1974 ztk->in_len = count;
1975 ztk->in_position += count;
1978 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1979 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
1981 // Now that we are sure we have compressed data available, we need to determine
1982 // if it's better to inflate it in "file->buff" or directly in "buffer"
1984 // Inflate the data in "file->buff"
1985 if (buffersize < sizeof (file->buff) / 2)
1987 ztk->zstream.next_out = file->buff;
1988 ztk->zstream.avail_out = sizeof (file->buff);
1989 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1990 if (error != Z_OK && error != Z_STREAM_END)
1992 Con_Printf ("FS_Read: Can't inflate file\n");
1995 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1997 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
1998 file->position += file->buff_len;
2000 // Copy the requested data in "buffer" (as much as we can)
2001 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2002 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2003 file->buff_ind = count;
2006 // Else, we inflate directly in "buffer"
2009 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2010 ztk->zstream.avail_out = (unsigned int)buffersize;
2011 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2012 if (error != Z_OK && error != Z_STREAM_END)
2014 Con_Printf ("FS_Read: Can't inflate file\n");
2017 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2019 // How much data did it inflate?
2020 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2021 file->position += count;
2023 // Purge cached data
2028 buffersize -= count;
2036 ====================
2039 Print a string into a file
2040 ====================
2042 int FS_Print (qfile_t* file, const char *msg)
2044 return (int)FS_Write (file, msg, strlen (msg));
2048 ====================
2051 Print a string into a file
2052 ====================
2054 int FS_Printf(qfile_t* file, const char* format, ...)
2059 va_start (args, format);
2060 result = FS_VPrintf (file, format, args);
2068 ====================
2071 Print a string into a file
2072 ====================
2074 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2077 fs_offset_t buff_size = MAX_INPUTLINE;
2082 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2083 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2084 if (len >= 0 && len < buff_size)
2086 Mem_Free (tempbuff);
2090 len = write (file->handle, tempbuff, len);
2091 Mem_Free (tempbuff);
2098 ====================
2101 Get the next character of a file
2102 ====================
2104 int FS_Getc (qfile_t* file)
2108 if (FS_Read (file, &c, 1) != 1)
2116 ====================
2119 Put a character back into the read buffer (only supports one character!)
2120 ====================
2122 int FS_UnGetc (qfile_t* file, unsigned char c)
2124 // If there's already a character waiting to be read
2125 if (file->ungetc != EOF)
2134 ====================
2137 Move the position index in a file
2138 ====================
2140 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2143 unsigned char* buffer;
2144 fs_offset_t buffersize;
2146 // Compute the file offset
2150 offset += file->position - file->buff_len + file->buff_ind;
2157 offset += file->real_length;
2163 if (offset < 0 || offset > (long) file->real_length)
2166 // If we have the data in our read buffer, we don't need to actually seek
2167 if (file->position - file->buff_len <= offset && offset <= file->position)
2169 file->buff_ind = offset + file->buff_len - file->position;
2173 // Purge cached data
2176 // Unpacked or uncompressed files can seek directly
2177 if (! (file->flags & QFILE_FLAG_DEFLATED))
2179 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2181 file->position = offset;
2185 // Seeking in compressed files is more a hack than anything else,
2186 // but we need to support it, so here we go.
2189 // If we have to go back in the file, we need to restart from the beginning
2190 if (offset <= file->position)
2194 ztk->in_position = 0;
2196 lseek (file->handle, file->offset, SEEK_SET);
2198 // Reset the Zlib stream
2199 ztk->zstream.next_in = ztk->input;
2200 ztk->zstream.avail_in = 0;
2201 qz_inflateReset (&ztk->zstream);
2204 // We need a big buffer to force inflating into it directly
2205 buffersize = 2 * sizeof (file->buff);
2206 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2208 // Skip all data until we reach the requested offset
2209 while (offset > file->position)
2211 fs_offset_t diff = offset - file->position;
2212 fs_offset_t count, len;
2214 count = (diff > buffersize) ? buffersize : diff;
2215 len = FS_Read (file, buffer, count);
2229 ====================
2232 Give the current position in a file
2233 ====================
2235 fs_offset_t FS_Tell (qfile_t* file)
2237 return file->position - file->buff_len + file->buff_ind;
2242 ====================
2245 Give the total size of a file
2246 ====================
2248 fs_offset_t FS_FileSize (qfile_t* file)
2250 return file->real_length;
2255 ====================
2258 Erases any buffered input or output data
2259 ====================
2261 void FS_Purge (qfile_t* file)
2273 Filename are relative to the quake directory.
2274 Always appends a 0 byte.
2277 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2280 unsigned char *buf = NULL;
2281 fs_offset_t filesize = 0;
2283 file = FS_Open (path, "rb", quiet, false);
2286 filesize = file->real_length;
2287 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2288 buf[filesize] = '\0';
2289 FS_Read (file, buf, filesize);
2293 if (filesizepointer)
2294 *filesizepointer = filesize;
2303 The filename will be prefixed by the current game directory
2306 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2310 file = FS_Open (filename, "wb", false, false);
2313 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2317 Con_DPrintf("FS_WriteFile: %s\n", filename);
2318 FS_Write (file, data, len);
2325 =============================================================================
2327 OTHERS PUBLIC FUNCTIONS
2329 =============================================================================
2337 void FS_StripExtension (const char *in, char *out, size_t size_out)
2345 while ((currentchar = *in) && size_out > 1)
2347 if (currentchar == '.')
2349 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2351 *out++ = currentchar;
2367 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2371 // if path doesn't have a .EXT, append extension
2372 // (extension should include the .)
2373 src = path + strlen(path) - 1;
2375 while (*src != '/' && src != path)
2378 return; // it has an extension
2382 strlcat (path, extension, size_path);
2390 Look for a file in the packages and in the filesystem
2393 qboolean FS_FileExists (const char *filename)
2395 return (FS_FindFile (filename, NULL, true) != NULL);
2403 Look for a file in the filesystem only
2406 qboolean FS_SysFileExists (const char *path)
2411 // TODO: use another function instead, to avoid opening the file
2412 desc = open (path, O_RDONLY | O_BINARY);
2421 if (stat (path,&buf) == -1)
2428 void FS_mkdir (const char *path)
2441 Allocate and fill a search structure with information on matching filenames.
2444 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2447 searchpath_t *searchpath;
2449 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2450 stringlist_t resultlist;
2451 stringlist_t dirlist;
2452 const char *slash, *backslash, *colon, *separator;
2454 char netpath[MAX_OSPATH];
2455 char temp[MAX_OSPATH];
2457 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2462 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2466 stringlistinit(&resultlist);
2467 stringlistinit(&dirlist);
2469 slash = strrchr(pattern, '/');
2470 backslash = strrchr(pattern, '\\');
2471 colon = strrchr(pattern, ':');
2472 separator = max(slash, backslash);
2473 separator = max(separator, colon);
2474 basepathlength = separator ? (separator + 1 - pattern) : 0;
2475 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2477 memcpy(basepath, pattern, basepathlength);
2478 basepath[basepathlength] = 0;
2480 // search through the path, one element at a time
2481 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2483 // is the element a pak file?
2484 if (searchpath->pack)
2486 // look through all the pak file elements
2487 pak = searchpath->pack;
2488 for (i = 0;i < pak->numfiles;i++)
2490 strlcpy(temp, pak->files[i].name, sizeof(temp));
2493 if (matchpattern(temp, (char *)pattern, true))
2495 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2496 if (!strcmp(resultlist.strings[resultlistindex], temp))
2498 if (resultlistindex == resultlist.numstrings)
2500 stringlistappend(&resultlist, temp);
2502 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2505 // strip off one path element at a time until empty
2506 // this way directories are added to the listing if they match the pattern
2507 slash = strrchr(temp, '/');
2508 backslash = strrchr(temp, '\\');
2509 colon = strrchr(temp, ':');
2511 if (separator < slash)
2513 if (separator < backslash)
2514 separator = backslash;
2515 if (separator < colon)
2517 *((char *)separator) = 0;
2523 // get a directory listing and look at each name
2524 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2525 stringlistinit(&dirlist);
2526 listdirectory(&dirlist, netpath);
2527 for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
2529 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
2530 if (matchpattern(temp, (char *)pattern, true))
2532 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2533 if (!strcmp(resultlist.strings[resultlistindex], temp))
2535 if (resultlistindex == resultlist.numstrings)
2537 stringlistappend(&resultlist, temp);
2539 Con_DPrintf("SearchDirFile: %s\n", temp);
2543 stringlistfreecontents(&dirlist);
2547 if (resultlist.numstrings)
2549 stringlistsort(&resultlist);
2550 numfiles = resultlist.numstrings;
2552 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2553 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2554 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2555 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2556 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2557 search->numfilenames = (int)numfiles;
2560 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2563 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2564 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2565 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2567 numchars += (int)textlen;
2570 stringlistfreecontents(&resultlist);
2576 void FS_FreeSearch(fssearch_t *search)
2581 extern int con_linewidth;
2582 int FS_ListDirectory(const char *pattern, int oneperline)
2591 char linebuf[MAX_INPUTLINE];
2593 search = FS_Search(pattern, true, true);
2596 numfiles = search->numfilenames;
2599 // FIXME: the names could be added to one column list and then
2600 // gradually shifted into the next column if they fit, and then the
2601 // next to make a compact variable width listing but it's a lot more
2603 // find width for columns
2605 for (i = 0;i < numfiles;i++)
2607 l = (int)strlen(search->filenames[i]);
2608 if (columnwidth < l)
2611 // count the spacing character
2613 // calculate number of columns
2614 numcolumns = con_linewidth / columnwidth;
2615 // don't bother with the column printing if it's only one column
2616 if (numcolumns >= 2)
2618 numlines = (numfiles + numcolumns - 1) / numcolumns;
2619 for (i = 0;i < numlines;i++)
2622 for (k = 0;k < numcolumns;k++)
2624 l = i * numcolumns + k;
2627 name = search->filenames[l];
2628 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2629 linebuf[linebufpos++] = name[j];
2630 // space out name unless it's the last on the line
2631 if (k + 1 < numcolumns && l + 1 < numfiles)
2632 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2633 linebuf[linebufpos++] = ' ';
2636 linebuf[linebufpos] = 0;
2637 Con_Printf("%s\n", linebuf);
2644 for (i = 0;i < numfiles;i++)
2645 Con_Printf("%s\n", search->filenames[i]);
2646 FS_FreeSearch(search);
2647 return (int)numfiles;
2650 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2652 const char *pattern;
2655 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2658 if (Cmd_Argc() == 2)
2659 pattern = Cmd_Argv(1);
2662 if (!FS_ListDirectory(pattern, oneperline))
2663 Con_Print("No files found.\n");
2668 FS_ListDirectoryCmd("dir", true);
2673 FS_ListDirectoryCmd("ls", false);
2676 const char *FS_WhichPack(const char *filename)
2679 searchpath_t *sp = FS_FindFile(filename, &index, true);
2681 return sp->pack->filename;
2687 ====================
2688 FS_IsRegisteredQuakePack
2690 Look for a proof of purchase file file in the requested package
2692 If it is found, this file should NOT be downloaded.
2693 ====================
2695 qboolean FS_IsRegisteredQuakePack(const char *name)
2697 searchpath_t *search;
2700 // search through the path, one element at a time
2701 for (search = fs_searchpaths;search;search = search->next)
2703 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2705 int (*strcmp_funct) (const char* str1, const char* str2);
2706 int left, right, middle;
2709 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2711 // Look for the file (binary search)
2713 right = pak->numfiles - 1;
2714 while (left <= right)
2718 middle = (left + right) / 2;
2719 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2725 // If we're too far in the list
2732 // we found the requested pack but it is not registered quake
2740 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2743 unsigned char *filedata;
2744 fs_offset_t filesize;
2745 if (filesizepointer)
2746 *filesizepointer = 0;
2747 if (!filename || !*filename)
2749 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2752 if (filesizepointer)
2753 *filesizepointer = filesize;
2754 crc = CRC_Block(filedata, filesize);