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
36 # include <sys/stat.h>
43 // Win32 requires us to add O_BINARY, but the other OSes don't have it
48 // In case the system doesn't support the O_NONBLOCK flag
53 // largefile support for Win32
55 # define lseek _lseeki64
60 All of Quake's data access is through a hierchal file system, but the contents
61 of the file system can be transparently merged from several sources.
63 The "base directory" is the path to the directory holding the quake.exe and
64 all game directories. The sys_* files pass this to host_init in
65 quakeparms_t->basedir. This can be overridden with the "-basedir" command
66 line parm to allow code debugging in a different directory. The base
67 directory is only used during filesystem initialization.
69 The "game directory" is the first tree on the search path and directory that
70 all generated files (savegames, screenshots, demos, config files) will be
71 saved to. This can be overridden with the "-game" command line parameter.
72 The game directory can never be changed while quake is executing. This is a
73 precaution against having a malicious server instruct clients to write files
74 over areas they shouldn't.
80 =============================================================================
84 =============================================================================
87 // Magic numbers of a ZIP file (big-endian format)
88 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
89 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
90 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
92 // Other constants for ZIP files
93 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
94 #define ZIP_END_CDIR_SIZE 22
95 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
96 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
98 // Zlib constants (from zlib.h)
99 #define Z_SYNC_FLUSH 2
102 #define Z_STREAM_END 1
103 #define ZLIB_VERSION "1.2.3"
105 // Uncomment the following line if the zlib DLL you have still uses
106 // the 1.1.x series calling convention on Win32 (WINAPI)
107 //#define ZLIB_USES_WINAPI
111 =============================================================================
115 =============================================================================
118 // Zlib stream (from zlib.h)
119 // Warning: some pointers we don't use directly have
120 // been cast to "void*" for a matter of simplicity
123 unsigned char *next_in; // next input byte
124 unsigned int avail_in; // number of bytes available at next_in
125 unsigned long total_in; // total nb of input bytes read so far
127 unsigned char *next_out; // next output byte should be put there
128 unsigned int avail_out; // remaining free space at next_out
129 unsigned long total_out; // total nb of bytes output so far
131 char *msg; // last error message, NULL if no error
132 void *state; // not visible by applications
134 void *zalloc; // used to allocate the internal state
135 void *zfree; // used to free the internal state
136 void *opaque; // private data object passed to zalloc and zfree
138 int data_type; // best guess about the data type: ascii or binary
139 unsigned long adler; // adler32 value of the uncompressed data
140 unsigned long reserved; // reserved for future use
144 // inside a package (PAK or PK3)
145 #define QFILE_FLAG_PACKED (1 << 0)
146 // file is compressed using the deflate algorithm (PK3 only)
147 #define QFILE_FLAG_DEFLATED (1 << 1)
149 #define FILE_BUFF_SIZE 2048
153 size_t comp_length; // length of the compressed file
154 size_t in_ind, in_len; // input buffer current index and length
155 size_t in_position; // position in the compressed file
156 unsigned char input [FILE_BUFF_SIZE];
162 int handle; // file descriptor
163 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
164 fs_offset_t position; // current position in the file
165 fs_offset_t offset; // offset into the package (0 if external file)
166 int ungetc; // single stored character from ungetc, cleared to EOF when read
169 fs_offset_t buff_ind, buff_len; // buffer current index and length
170 unsigned char buff [FILE_BUFF_SIZE];
177 // ------ PK3 files on disk ------ //
179 // You can get the complete ZIP format description from PKWARE website
181 typedef struct pk3_endOfCentralDir_s
183 unsigned int signature;
184 unsigned short disknum;
185 unsigned short cdir_disknum; // number of the disk with the start of the central directory
186 unsigned short localentries; // number of entries in the central directory on this disk
187 unsigned short nbentries; // total number of entries in the central directory on this disk
188 unsigned int cdir_size; // size of the central directory
189 unsigned int cdir_offset; // with respect to the starting disk number
190 unsigned short comment_size;
191 } pk3_endOfCentralDir_t;
194 // ------ PAK files on disk ------ //
195 typedef struct dpackfile_s
198 int filepos, filelen;
201 typedef struct dpackheader_s
209 // Packages in memory
210 // the offset in packfile_t is the true contents offset
211 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
212 // file compressed using the deflate algorithm
213 #define PACKFILE_FLAG_DEFLATED (1 << 1)
215 typedef struct packfile_s
217 char name [MAX_QPATH];
220 fs_offset_t packsize; // size in the package
221 fs_offset_t realsize; // real file size (uncompressed)
224 typedef struct pack_s
226 char filename [MAX_OSPATH];
228 int ignorecase; // PK3 ignores case
234 // Search paths for files (including packages)
235 typedef struct searchpath_s
237 // only one of filename / pack will be used
238 char filename[MAX_OSPATH];
240 struct searchpath_s *next;
245 =============================================================================
249 =============================================================================
255 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
256 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
257 fs_offset_t offset, fs_offset_t packsize,
258 fs_offset_t realsize, int flags);
262 =============================================================================
266 =============================================================================
269 mempool_t *fs_mempool;
271 searchpath_t *fs_searchpaths = NULL;
273 #define MAX_FILES_IN_PACK 65536
275 char fs_gamedir[MAX_OSPATH];
276 char fs_basedir[MAX_OSPATH];
278 // list of active game directories (empty if not running a mod)
279 int fs_numgamedirs = 0;
280 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
282 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)"};
283 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"};
287 =============================================================================
289 PRIVATE FUNCTIONS - PK3 HANDLING
291 =============================================================================
294 // Functions exported from zlib
295 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
296 # define ZEXPORT WINAPI
301 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
302 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
303 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
304 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
306 #define qz_inflateInit2(strm, windowBits) \
307 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
309 static dllfunction_t zlibfuncs[] =
311 {"inflate", (void **) &qz_inflate},
312 {"inflateEnd", (void **) &qz_inflateEnd},
313 {"inflateInit2_", (void **) &qz_inflateInit2_},
314 {"inflateReset", (void **) &qz_inflateReset},
318 // Handle for Zlib DLL
319 static dllhandle_t zlib_dll = NULL;
329 void PK3_CloseLibrary (void)
331 Sys_UnloadLibrary (&zlib_dll);
339 Try to load the Zlib DLL
342 qboolean PK3_OpenLibrary (void)
344 const char* dllnames [] =
349 # ifdef ZLIB_USES_WINAPI
355 #elif defined(MACOSX)
369 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
375 PK3_GetEndOfCentralDir
377 Extract the end of the central directory from a PK3 package
380 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
382 fs_offset_t filesize, maxsize;
383 unsigned char *buffer, *ptr;
386 // Get the package size
387 filesize = lseek (packhandle, 0, SEEK_END);
388 if (filesize < ZIP_END_CDIR_SIZE)
391 // Load the end of the file in memory
392 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
395 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
396 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
397 lseek (packhandle, filesize - maxsize, SEEK_SET);
398 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
404 // Look for the end of central dir signature around the end of the file
405 maxsize -= ZIP_END_CDIR_SIZE;
406 ptr = &buffer[maxsize];
408 while (BuffBigLong (ptr) != ZIP_END_HEADER)
420 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
421 eocd->signature = LittleLong (eocd->signature);
422 eocd->disknum = LittleShort (eocd->disknum);
423 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
424 eocd->localentries = LittleShort (eocd->localentries);
425 eocd->nbentries = LittleShort (eocd->nbentries);
426 eocd->cdir_size = LittleLong (eocd->cdir_size);
427 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
428 eocd->comment_size = LittleShort (eocd->comment_size);
440 Extract the file list from a PK3 file
443 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
445 unsigned char *central_dir, *ptr;
447 fs_offset_t remaining;
449 // Load the central directory in memory
450 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
451 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
452 read (pack->handle, central_dir, eocd->cdir_size);
454 // Extract the files properties
455 // The parsing is done "by hand" because some fields have variable sizes and
456 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
457 remaining = eocd->cdir_size;
460 for (ind = 0; ind < eocd->nbentries; ind++)
462 fs_offset_t namesize, count;
464 // Checking the remaining size
465 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
467 Mem_Free (central_dir);
470 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
473 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
475 Mem_Free (central_dir);
479 namesize = BuffLittleShort (&ptr[28]); // filename length
481 // Check encryption, compression, and attributes
482 // 1st uint8 : general purpose bit flag
483 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
485 // LordHavoc: bit 3 would be a problem if we were scanning the archive
486 // but is not a problem in the central directory where the values are
489 // bit 3 seems to always be set by the standard Mac OSX zip maker
491 // 2nd uint8 : external file attributes
492 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
493 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
495 // Still enough bytes for the name?
496 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
498 Mem_Free (central_dir);
502 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
503 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
505 char filename [sizeof (pack->files[0].name)];
506 fs_offset_t offset, packsize, realsize;
509 // Extract the name (strip it if necessary)
510 namesize = min(namesize, (int)sizeof (filename) - 1);
511 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
512 filename[namesize] = '\0';
514 if (BuffLittleShort (&ptr[10]))
515 flags = PACKFILE_FLAG_DEFLATED;
518 offset = BuffLittleLong (&ptr[42]);
519 packsize = BuffLittleLong (&ptr[20]);
520 realsize = BuffLittleLong (&ptr[24]);
521 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
525 // Skip the name, additionnal field, and comment
526 // 1er uint16 : extra field length
527 // 2eme uint16 : file comment length
528 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
529 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
533 // If the package is empty, central_dir is NULL here
534 if (central_dir != NULL)
535 Mem_Free (central_dir);
536 return pack->numfiles;
544 Create a package entry associated with a PK3 file
547 pack_t *FS_LoadPackPK3 (const char *packfile)
550 pk3_endOfCentralDir_t eocd;
554 packhandle = open (packfile, O_RDONLY | O_BINARY);
558 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
560 Con_Printf ("%s is not a PK3 file\n", packfile);
565 // Multi-volume ZIP archives are NOT allowed
566 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
568 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
573 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
574 // since eocd.nbentries is an unsigned 16 bits integer
575 #if MAX_FILES_IN_PACK < 65535
576 if (eocd.nbentries > MAX_FILES_IN_PACK)
578 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
584 // Create a package structure in memory
585 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
586 pack->ignorecase = true; // PK3 ignores case
587 strlcpy (pack->filename, packfile, sizeof (pack->filename));
588 pack->handle = packhandle;
589 pack->numfiles = eocd.nbentries;
590 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
592 real_nb_files = PK3_BuildFileList (pack, &eocd);
593 if (real_nb_files < 0)
595 Con_Printf ("%s is not a valid PK3 file\n", packfile);
601 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
608 PK3_GetTrueFileOffset
610 Find where the true file data offset is
613 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
615 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
619 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
622 // Load the local file description
623 lseek (pack->handle, pfile->offset, SEEK_SET);
624 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
625 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
627 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
631 // Skip name and extra field
632 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
634 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
640 =============================================================================
642 OTHER PRIVATE FUNCTIONS
644 =============================================================================
652 Add a file to the list of files contained into a package
655 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
656 fs_offset_t offset, fs_offset_t packsize,
657 fs_offset_t realsize, int flags)
659 int (*strcmp_funct) (const char* str1, const char* str2);
660 int left, right, middle;
663 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
665 // Look for the slot we should put that file into (binary search)
667 right = pack->numfiles - 1;
668 while (left <= right)
672 middle = (left + right) / 2;
673 diff = strcmp_funct (pack->files[middle].name, name);
675 // If we found the file, there's a problem
677 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
679 // If we're too far in the list
686 // We have to move the right of the list by one slot to free the one we need
687 pfile = &pack->files[left];
688 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
691 strlcpy (pfile->name, name, sizeof (pfile->name));
692 pfile->offset = offset;
693 pfile->packsize = packsize;
694 pfile->realsize = realsize;
695 pfile->flags = flags;
705 Only used for FS_Open.
708 void FS_CreatePath (char *path)
712 for (ofs = path+1 ; *ofs ; ofs++)
714 if (*ofs == '/' || *ofs == '\\')
716 // create the directory
732 void FS_Path_f (void)
736 Con_Print("Current search path:\n");
737 for (s=fs_searchpaths ; s ; s=s->next)
740 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
742 Con_Printf("%s\n", s->filename);
751 Takes an explicit (not game tree related) path to a pak file.
753 Loads the header and directory, adding the files at the beginning
754 of the list so they override previous pack files.
757 pack_t *FS_LoadPackPAK (const char *packfile)
759 dpackheader_t header;
765 packhandle = open (packfile, O_RDONLY | O_BINARY);
768 read (packhandle, (void *)&header, sizeof(header));
769 if (memcmp(header.id, "PACK", 4))
771 Con_Printf ("%s is not a packfile\n", packfile);
775 header.dirofs = LittleLong (header.dirofs);
776 header.dirlen = LittleLong (header.dirlen);
778 if (header.dirlen % sizeof(dpackfile_t))
780 Con_Printf ("%s has an invalid directory size\n", packfile);
785 numpackfiles = header.dirlen / sizeof(dpackfile_t);
787 if (numpackfiles > MAX_FILES_IN_PACK)
789 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
794 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
795 lseek (packhandle, header.dirofs, SEEK_SET);
796 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
798 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
804 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
805 pack->ignorecase = false; // PAK is case sensitive
806 strlcpy (pack->filename, packfile, sizeof (pack->filename));
807 pack->handle = packhandle;
809 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
811 // parse the directory
812 for (i = 0;i < numpackfiles;i++)
814 fs_offset_t offset = LittleLong (info[i].filepos);
815 fs_offset_t size = LittleLong (info[i].filelen);
817 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
822 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
830 Adds the given pack to the search path.
831 The pack type is autodetected by the file extension.
833 Returns true if the file was successfully added to the
834 search path or if it was already included.
836 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
840 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
842 searchpath_t *search;
844 const char *ext = FS_FileExtension(pakfile);
846 for(search = fs_searchpaths; search; search = search->next)
848 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
851 *already_loaded = true;
852 return true; // already loaded
857 *already_loaded = false;
859 if(!strcasecmp(ext, "pak"))
860 pak = FS_LoadPackPAK (pakfile);
861 else if(!strcasecmp(ext, "pk3"))
862 pak = FS_LoadPackPK3 (pakfile);
864 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
870 // find the first item whose next one is a pack or NULL
871 searchpath_t *insertion_point = 0;
872 if(fs_searchpaths && !fs_searchpaths->pack)
874 insertion_point = fs_searchpaths;
877 if(!insertion_point->next)
879 if(insertion_point->next->pack)
881 insertion_point = insertion_point->next;
884 // If insertion_point is NULL, this means that either there is no
885 // item in the list yet, or that the very first item is a pack. In
886 // that case, we want to insert at the beginning...
889 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
891 search->next = fs_searchpaths;
892 fs_searchpaths = search;
895 // otherwise we want to append directly after insertion_point.
897 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
899 search->next = insertion_point->next;
900 insertion_point->next = search;
905 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
907 search->next = fs_searchpaths;
908 fs_searchpaths = search;
914 Con_Printf("unable to load pak \"%s\"\n", pakfile);
924 Adds the given pack to the search path and searches for it in the game path.
925 The pack type is autodetected by the file extension.
927 Returns true if the file was successfully added to the
928 search path or if it was already included.
930 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
934 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
936 char fullpath[MAX_QPATH];
938 searchpath_t *search;
941 *already_loaded = false;
943 // then find the real name...
944 search = FS_FindFile(pakfile, &index, true);
945 if(!search || search->pack)
947 Con_Printf("could not find pak \"%s\"\n", pakfile);
951 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
953 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
961 Sets fs_gamedir, adds the directory to the head of the path,
962 then loads and adds pak1.pak pak2.pak ...
965 void FS_AddGameDirectory (const char *dir)
969 searchpath_t *search;
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 FS_AddPack_Fullpath(list.strings[i], NULL, false);
986 // add any PK3 package in the directory
987 for (i = 0;i < list.numstrings;i++)
989 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
991 FS_AddPack_Fullpath(list.strings[i], NULL, false);
995 stringlistfreecontents(&list);
997 // Add the directory to the search path
998 // (unpacked files have the priority over packed files)
999 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1000 strlcpy (search->filename, dir, sizeof (search->filename));
1001 search->next = fs_searchpaths;
1002 fs_searchpaths = search;
1011 void FS_AddGameHierarchy (const char *dir)
1014 char userdir[MAX_QPATH];
1016 TCHAR mydocsdir[MAX_PATH + 1];
1018 const char *homedir;
1021 // Add the common game directory
1022 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1026 // Add the personal game directory
1028 if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
1029 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1031 homedir = getenv ("HOME");
1033 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1037 if(!COM_CheckParm("-mygames"))
1039 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1043 *userdir = 0; // we have write access to the game dir, so let's use it
1048 if(COM_CheckParm("-nohome"))
1051 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1052 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1055 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1064 const char *FS_FileExtension (const char *in)
1066 const char *separator, *backslash, *colon, *dot;
1068 separator = strrchr(in, '/');
1069 backslash = strrchr(in, '\\');
1070 if (!separator || separator < backslash)
1071 separator = backslash;
1072 colon = strrchr(in, ':');
1073 if (!separator || separator < colon)
1076 dot = strrchr(in, '.');
1077 if (dot == NULL || (separator && (dot < separator)))
1089 const char *FS_FileWithoutPath (const char *in)
1091 const char *separator, *backslash, *colon;
1093 separator = strrchr(in, '/');
1094 backslash = strrchr(in, '\\');
1095 if (!separator || separator < backslash)
1096 separator = backslash;
1097 colon = strrchr(in, ':');
1098 if (!separator || separator < colon)
1100 return separator ? separator + 1 : in;
1109 void FS_ClearSearchPath (void)
1111 // unload all packs and directory information, close all pack files
1112 // (if a qfile is still reading a pack it won't be harmed because it used
1113 // dup() to get its own handle already)
1114 while (fs_searchpaths)
1116 searchpath_t *search = fs_searchpaths;
1117 fs_searchpaths = search->next;
1121 close(search->pack->handle);
1122 // free any memory associated with it
1123 if (search->pack->files)
1124 Mem_Free(search->pack->files);
1125 Mem_Free(search->pack);
1137 void FS_Rescan (void)
1140 qboolean fs_modified = false;
1142 FS_ClearSearchPath();
1144 // add the game-specific paths
1145 // gamedirname1 (typically id1)
1146 FS_AddGameHierarchy (gamedirname1);
1147 // update the com_modname (used for server info)
1148 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1150 // add the game-specific path, if any
1151 // (only used for mission packs and the like, which should set fs_modified)
1155 FS_AddGameHierarchy (gamedirname2);
1159 // Adds basedir/gamedir as an override game
1160 // LordHavoc: now supports multiple -game directories
1161 // set the com_modname (reported in server info)
1162 for (i = 0;i < fs_numgamedirs;i++)
1165 FS_AddGameHierarchy (fs_gamedirs[i]);
1166 // update the com_modname (used server info)
1167 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1170 // set the default screenshot name to either the mod name or the
1171 // gamemode screenshot name
1172 if (strcmp(com_modname, gamedirname1))
1173 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1175 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1177 // If "-condebug" is in the command line, remove the previous log file
1178 if (COM_CheckParm ("-condebug") != 0)
1179 unlink (va("%s/qconsole.log", fs_gamedir));
1181 // look for the pop.lmp file and set registered to true if it is found
1182 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1185 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1187 Con_Print("Playing shareware version.\n");
1191 Cvar_Set ("registered", "1");
1192 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1193 Con_Print("Playing registered version.\n");
1196 // unload all wads so that future queries will return the new data
1200 void FS_Rescan_f(void)
1210 extern void Host_SaveConfig (void);
1211 extern void Host_LoadConfig_f (void);
1212 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1216 if (fs_numgamedirs == numgamedirs)
1218 for (i = 0;i < numgamedirs;i++)
1219 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1221 if (i == numgamedirs)
1222 return true; // already using this set of gamedirs, do nothing
1225 if (numgamedirs > MAX_GAMEDIRS)
1228 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1229 return false; // too many gamedirs
1232 for (i = 0;i < numgamedirs;i++)
1234 // if string is nasty, reject it
1235 if(FS_CheckNastyPath(gamedirs[i], true))
1238 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1239 return false; // nasty gamedirs
1243 for (i = 0;i < numgamedirs;i++)
1245 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1248 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1249 return false; // missing gamedirs
1255 fs_numgamedirs = numgamedirs;
1256 for (i = 0;i < fs_numgamedirs;i++)
1257 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1259 // reinitialize filesystem to detect the new paks
1262 // exec the new config
1263 Host_LoadConfig_f();
1265 // unload all sounds so they will be reloaded from the new files as needed
1266 S_UnloadAllSounds_f();
1268 // reinitialize renderer (this reloads hud/console background/etc)
1269 R_Modules_Restart();
1279 void FS_GameDir_f (void)
1283 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1287 Con_Printf("gamedirs active:");
1288 for (i = 0;i < fs_numgamedirs;i++)
1289 Con_Printf(" %s", fs_gamedirs[i]);
1294 numgamedirs = Cmd_Argc() - 1;
1295 if (numgamedirs > MAX_GAMEDIRS)
1297 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1301 for (i = 0;i < numgamedirs;i++)
1302 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1304 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1306 // actually, changing during game would work fine, but would be stupid
1307 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1311 // halt demo playback to close the file
1314 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1323 qboolean FS_CheckGameDir(const char *gamedir)
1327 stringlistinit(&list);
1328 listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
1329 success = list.numstrings > 0;
1330 stringlistfreecontents(&list);
1344 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1346 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1348 // If the base directory is explicitly defined by the compilation process
1349 #ifdef DP_FS_BASEDIR
1350 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1352 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1355 // FIXME: is there a better way to find the directory outside the .app?
1356 if (strstr(com_argv[0], ".app/"))
1360 split = strstr(com_argv[0], ".app/");
1361 while (split > com_argv[0] && *split != '/')
1363 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1364 fs_basedir[split - com_argv[0]] = 0;
1372 // Overrides the system supplied base directory (under GAMENAME)
1373 // 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)
1374 i = COM_CheckParm ("-basedir");
1375 if (i && i < com_argc-1)
1377 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1378 i = (int)strlen (fs_basedir);
1379 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1380 fs_basedir[i-1] = 0;
1383 // add a path separator to the end of the basedir if it lacks one
1384 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1385 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1387 if (!FS_CheckGameDir(gamedirname1))
1388 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1390 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1391 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1394 // Adds basedir/gamedir as an override game
1395 // LordHavoc: now supports multiple -game directories
1396 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1400 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1403 if (FS_CheckNastyPath(com_argv[i], true))
1404 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1405 if (!FS_CheckGameDir(com_argv[i]))
1406 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1407 // add the gamedir to the list of active gamedirs
1408 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1413 // generate the searchpath
1417 void FS_Init_Commands(void)
1419 Cvar_RegisterVariable (&scr_screenshot_name);
1420 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1422 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1423 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1424 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1425 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1426 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1434 void FS_Shutdown (void)
1436 // close all pack files and such
1437 // (hopefully there aren't any other open files, but they'll be cleaned up
1438 // by the OS anyway)
1439 FS_ClearSearchPath();
1440 Mem_FreePool (&fs_mempool);
1444 ====================
1447 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1448 ====================
1450 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1456 // Parse the mode string
1465 opt = O_CREAT | O_TRUNC;
1469 opt = O_CREAT | O_APPEND;
1472 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1475 for (ind = 1; mode[ind] != '\0'; ind++)
1486 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1487 filepath, mode, mode[ind]);
1494 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1495 memset (file, 0, sizeof (*file));
1498 file->handle = open (filepath, mod | opt, 0666);
1499 if (file->handle < 0)
1505 file->real_length = lseek (file->handle, 0, SEEK_END);
1507 // For files opened in append mode, we start at the end of the file
1509 file->position = file->real_length;
1511 lseek (file->handle, 0, SEEK_SET);
1521 Open a packed file using its package file descriptor
1524 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1530 pfile = &pack->files[pack_ind];
1532 // If we don't have the true offset, get it now
1533 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1534 if (!PK3_GetTrueFileOffset (pfile, pack))
1537 // No Zlib DLL = no compressed files
1538 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1540 Con_Printf("WARNING: can't open the compressed file %s\n"
1541 "You need the Zlib DLL to use compressed files\n",
1546 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1547 // the dup() call to avoid having to close the dup_handle on error here
1548 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1550 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1551 pfile->name, pack->filename, (int) pfile->offset);
1555 dup_handle = dup (pack->handle);
1558 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1562 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1563 memset (file, 0, sizeof (*file));
1564 file->handle = dup_handle;
1565 file->flags = QFILE_FLAG_PACKED;
1566 file->real_length = pfile->realsize;
1567 file->offset = pfile->offset;
1571 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1575 file->flags |= QFILE_FLAG_DEFLATED;
1577 // We need some more variables
1578 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1580 ztk->comp_length = pfile->packsize;
1582 // Initialize zlib stream
1583 ztk->zstream.next_in = ztk->input;
1584 ztk->zstream.avail_in = 0;
1586 /* From Zlib's "unzip.c":
1588 * windowBits is passed < 0 to tell that there is no zlib header.
1589 * Note that in this case inflate *requires* an extra "dummy" byte
1590 * after the compressed stream in order to complete decompression and
1591 * return Z_STREAM_END.
1592 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1593 * size of both compressed and uncompressed data
1595 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1597 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1603 ztk->zstream.next_out = file->buff;
1604 ztk->zstream.avail_out = sizeof (file->buff);
1613 ====================
1616 Return true if the path should be rejected due to one of the following:
1617 1: path elements that are non-portable
1618 2: path elements that would allow access to files outside the game directory,
1619 or are just not a good idea for a mod to be using.
1620 ====================
1622 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1624 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1628 // Windows: don't allow \ in filenames (windows-only), period.
1629 // (on Windows \ is a directory separator, but / is also supported)
1630 if (strstr(path, "\\"))
1631 return 1; // non-portable
1633 // Mac: don't allow Mac-only filenames - : is a directory separator
1634 // instead of /, but we rely on / working already, so there's no reason to
1635 // support a Mac-only path
1636 // Amiga and Windows: : tries to go to root of drive
1637 if (strstr(path, ":"))
1638 return 1; // non-portable attempt to go to root of drive
1640 // Amiga: // is parent directory
1641 if (strstr(path, "//"))
1642 return 1; // non-portable attempt to go to parent directory
1644 // all: don't allow going to parent directory (../ or /../)
1645 if (strstr(path, ".."))
1646 return 2; // attempt to go outside the game directory
1648 // Windows and UNIXes: don't allow absolute paths
1650 return 2; // attempt to go outside the game directory
1652 // 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
1653 if (strchr(path, '.'))
1657 // gamedir is entirely path elements, so simply forbid . entirely
1660 if (strchr(path, '.') < strrchr(path, '/'))
1661 return 2; // possible attempt to go outside the game directory
1664 // all: forbid trailing slash on gamedir
1665 if (isgamedir && path[strlen(path)-1] == '/')
1668 // all: forbid leading dot on any filename for any reason
1669 if (strstr(path, "/."))
1670 return 2; // attempt to go outside the game directory
1672 // after all these checks we're pretty sure it's a / separated filename
1673 // and won't do much if any harm
1679 ====================
1682 Look for a file in the packages and in the filesystem
1684 Return the searchpath where the file was found (or NULL)
1685 and the file index in the package if relevant
1686 ====================
1688 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1690 searchpath_t *search;
1693 // search through the path, one element at a time
1694 for (search = fs_searchpaths;search;search = search->next)
1696 // is the element a pak file?
1699 int (*strcmp_funct) (const char* str1, const char* str2);
1700 int left, right, middle;
1703 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1705 // Look for the file (binary search)
1707 right = pak->numfiles - 1;
1708 while (left <= right)
1712 middle = (left + right) / 2;
1713 diff = strcmp_funct (pak->files[middle].name, name);
1718 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1720 // yes, but the first one is empty so we treat it as not being there
1721 if (!quiet && developer.integer >= 10)
1722 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1729 if (!quiet && developer.integer >= 10)
1730 Con_Printf("FS_FindFile: %s in %s\n",
1731 pak->files[middle].name, pak->filename);
1738 // If we're too far in the list
1747 char netpath[MAX_OSPATH];
1748 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1749 if (FS_SysFileExists (netpath))
1751 if (!quiet && developer.integer >= 10)
1752 Con_Printf("FS_FindFile: %s\n", netpath);
1761 if (!quiet && developer.integer >= 10)
1762 Con_Printf("FS_FindFile: can't find %s\n", name);
1774 Look for a file in the search paths and open it in read-only mode
1777 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1779 searchpath_t *search;
1782 search = FS_FindFile (filename, &pack_ind, quiet);
1788 // Found in the filesystem?
1791 char path [MAX_OSPATH];
1792 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1793 return FS_SysOpen (path, "rb", nonblocking);
1796 // So, we found it in a package...
1797 return FS_OpenPackedFile (search->pack, pack_ind);
1802 =============================================================================
1804 MAIN PUBLIC FUNCTIONS
1806 =============================================================================
1810 ====================
1813 Open a file. The syntax is the same as fopen
1814 ====================
1816 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1818 if (FS_CheckNastyPath(filepath, false))
1820 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1824 // If the file is opened in "write", "append", or "read/write" mode
1825 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1827 char real_path [MAX_OSPATH];
1829 // Open the file on disk directly
1830 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1832 // Create directories up to the file
1833 FS_CreatePath (real_path);
1835 return FS_SysOpen (real_path, mode, nonblocking);
1837 // Else, we look at the various search paths and open the file in read-only mode
1839 return FS_OpenReadFile (filepath, quiet, nonblocking);
1844 ====================
1848 ====================
1850 int FS_Close (qfile_t* file)
1852 if (close (file->handle))
1857 qz_inflateEnd (&file->ztk->zstream);
1858 Mem_Free (file->ztk);
1867 ====================
1870 Write "datasize" bytes into a file
1871 ====================
1873 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1877 // If necessary, seek to the exact file position we're supposed to be
1878 if (file->buff_ind != file->buff_len)
1879 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1881 // Purge cached data
1884 // Write the buffer and update the position
1885 result = write (file->handle, data, (fs_offset_t)datasize);
1886 file->position = lseek (file->handle, 0, SEEK_CUR);
1887 if (file->real_length < file->position)
1888 file->real_length = file->position;
1898 ====================
1901 Read up to "buffersize" bytes from a file
1902 ====================
1904 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1906 fs_offset_t count, done;
1908 if (buffersize == 0)
1911 // Get rid of the ungetc character
1912 if (file->ungetc != EOF)
1914 ((char*)buffer)[0] = file->ungetc;
1922 // First, we copy as many bytes as we can from "buff"
1923 if (file->buff_ind < file->buff_len)
1925 count = file->buff_len - file->buff_ind;
1926 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1928 memcpy (buffer, &file->buff[file->buff_ind], count);
1929 file->buff_ind += count;
1931 buffersize -= count;
1932 if (buffersize == 0)
1936 // NOTE: at this point, the read buffer is always empty
1938 // If the file isn't compressed
1939 if (! (file->flags & QFILE_FLAG_DEFLATED))
1943 // We must take care to not read after the end of the file
1944 count = file->real_length - file->position;
1946 // If we have a lot of data to get, put them directly into "buffer"
1947 if (buffersize > sizeof (file->buff) / 2)
1949 if (count > (fs_offset_t)buffersize)
1950 count = (fs_offset_t)buffersize;
1951 lseek (file->handle, file->offset + file->position, SEEK_SET);
1952 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1956 file->position += nb;
1958 // Purge cached data
1964 if (count > (fs_offset_t)sizeof (file->buff))
1965 count = (fs_offset_t)sizeof (file->buff);
1966 lseek (file->handle, file->offset + file->position, SEEK_SET);
1967 nb = read (file->handle, file->buff, count);
1970 file->buff_len = nb;
1971 file->position += nb;
1973 // Copy the requested data in "buffer" (as much as we can)
1974 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1975 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1976 file->buff_ind = count;
1984 // If the file is compressed, it's more complicated...
1985 // We cycle through a few operations until we have read enough data
1986 while (buffersize > 0)
1988 ztoolkit_t *ztk = file->ztk;
1991 // NOTE: at this point, the read buffer is always empty
1993 // If "input" is also empty, we need to refill it
1994 if (ztk->in_ind == ztk->in_len)
1996 // If we are at the end of the file
1997 if (file->position == file->real_length)
2000 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2001 if (count > (fs_offset_t)sizeof (ztk->input))
2002 count = (fs_offset_t)sizeof (ztk->input);
2003 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2004 if (read (file->handle, ztk->input, count) != count)
2006 Con_Printf ("FS_Read: unexpected end of file\n");
2011 ztk->in_len = count;
2012 ztk->in_position += count;
2015 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2016 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2018 // Now that we are sure we have compressed data available, we need to determine
2019 // if it's better to inflate it in "file->buff" or directly in "buffer"
2021 // Inflate the data in "file->buff"
2022 if (buffersize < sizeof (file->buff) / 2)
2024 ztk->zstream.next_out = file->buff;
2025 ztk->zstream.avail_out = sizeof (file->buff);
2026 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2027 if (error != Z_OK && error != Z_STREAM_END)
2029 Con_Printf ("FS_Read: Can't inflate file\n");
2032 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2034 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2035 file->position += file->buff_len;
2037 // Copy the requested data in "buffer" (as much as we can)
2038 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2039 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2040 file->buff_ind = count;
2043 // Else, we inflate directly in "buffer"
2046 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2047 ztk->zstream.avail_out = (unsigned int)buffersize;
2048 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2049 if (error != Z_OK && error != Z_STREAM_END)
2051 Con_Printf ("FS_Read: Can't inflate file\n");
2054 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2056 // How much data did it inflate?
2057 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2058 file->position += count;
2060 // Purge cached data
2065 buffersize -= count;
2073 ====================
2076 Print a string into a file
2077 ====================
2079 int FS_Print (qfile_t* file, const char *msg)
2081 return (int)FS_Write (file, msg, strlen (msg));
2085 ====================
2088 Print a string into a file
2089 ====================
2091 int FS_Printf(qfile_t* file, const char* format, ...)
2096 va_start (args, format);
2097 result = FS_VPrintf (file, format, args);
2105 ====================
2108 Print a string into a file
2109 ====================
2111 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2114 fs_offset_t buff_size = MAX_INPUTLINE;
2119 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2120 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2121 if (len >= 0 && len < buff_size)
2123 Mem_Free (tempbuff);
2127 len = write (file->handle, tempbuff, len);
2128 Mem_Free (tempbuff);
2135 ====================
2138 Get the next character of a file
2139 ====================
2141 int FS_Getc (qfile_t* file)
2145 if (FS_Read (file, &c, 1) != 1)
2153 ====================
2156 Put a character back into the read buffer (only supports one character!)
2157 ====================
2159 int FS_UnGetc (qfile_t* file, unsigned char c)
2161 // If there's already a character waiting to be read
2162 if (file->ungetc != EOF)
2171 ====================
2174 Move the position index in a file
2175 ====================
2177 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2180 unsigned char* buffer;
2181 fs_offset_t buffersize;
2183 // Compute the file offset
2187 offset += file->position - file->buff_len + file->buff_ind;
2194 offset += file->real_length;
2200 if (offset < 0 || offset > file->real_length)
2203 // If we have the data in our read buffer, we don't need to actually seek
2204 if (file->position - file->buff_len <= offset && offset <= file->position)
2206 file->buff_ind = offset + file->buff_len - file->position;
2210 // Purge cached data
2213 // Unpacked or uncompressed files can seek directly
2214 if (! (file->flags & QFILE_FLAG_DEFLATED))
2216 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2218 file->position = offset;
2222 // Seeking in compressed files is more a hack than anything else,
2223 // but we need to support it, so here we go.
2226 // If we have to go back in the file, we need to restart from the beginning
2227 if (offset <= file->position)
2231 ztk->in_position = 0;
2233 lseek (file->handle, file->offset, SEEK_SET);
2235 // Reset the Zlib stream
2236 ztk->zstream.next_in = ztk->input;
2237 ztk->zstream.avail_in = 0;
2238 qz_inflateReset (&ztk->zstream);
2241 // We need a big buffer to force inflating into it directly
2242 buffersize = 2 * sizeof (file->buff);
2243 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2245 // Skip all data until we reach the requested offset
2246 while (offset > file->position)
2248 fs_offset_t diff = offset - file->position;
2249 fs_offset_t count, len;
2251 count = (diff > buffersize) ? buffersize : diff;
2252 len = FS_Read (file, buffer, count);
2266 ====================
2269 Give the current position in a file
2270 ====================
2272 fs_offset_t FS_Tell (qfile_t* file)
2274 return file->position - file->buff_len + file->buff_ind;
2279 ====================
2282 Give the total size of a file
2283 ====================
2285 fs_offset_t FS_FileSize (qfile_t* file)
2287 return file->real_length;
2292 ====================
2295 Erases any buffered input or output data
2296 ====================
2298 void FS_Purge (qfile_t* file)
2310 Filename are relative to the quake directory.
2311 Always appends a 0 byte.
2314 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2317 unsigned char *buf = NULL;
2318 fs_offset_t filesize = 0;
2320 file = FS_Open (path, "rb", quiet, false);
2323 filesize = file->real_length;
2324 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2325 buf[filesize] = '\0';
2326 FS_Read (file, buf, filesize);
2328 if (developer_loadfile.integer)
2329 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2332 if (filesizepointer)
2333 *filesizepointer = filesize;
2342 The filename will be prefixed by the current game directory
2345 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2349 file = FS_Open (filename, "wb", false, false);
2352 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2356 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2357 FS_Write (file, data, len);
2364 =============================================================================
2366 OTHERS PUBLIC FUNCTIONS
2368 =============================================================================
2376 void FS_StripExtension (const char *in, char *out, size_t size_out)
2384 while ((currentchar = *in) && size_out > 1)
2386 if (currentchar == '.')
2388 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2390 *out++ = currentchar;
2406 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2410 // if path doesn't have a .EXT, append extension
2411 // (extension should include the .)
2412 src = path + strlen(path) - 1;
2414 while (*src != '/' && src != path)
2417 return; // it has an extension
2421 strlcat (path, extension, size_path);
2429 Look for a file in the packages and in the filesystem
2432 int FS_FileType (const char *filename)
2434 searchpath_t *search;
2435 char fullpath[MAX_QPATH];
2437 search = FS_FindFile (filename, NULL, true);
2439 return FS_FILETYPE_NONE;
2442 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2444 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2445 return FS_SysFileType(fullpath);
2453 Look for a file in the packages and in the filesystem
2456 qboolean FS_FileExists (const char *filename)
2458 return (FS_FindFile (filename, NULL, true) != NULL);
2466 Look for a file in the filesystem only
2469 int FS_SysFileType (const char *path)
2472 DWORD result = GetFileAttributes(path);
2474 if(result == INVALID_FILE_ATTRIBUTES)
2475 return FS_FILETYPE_NONE;
2477 if(result & FILE_ATTRIBUTE_DIRECTORY)
2478 return FS_FILETYPE_DIRECTORY;
2480 return FS_FILETYPE_FILE;
2484 if (stat (path,&buf) == -1)
2485 return FS_FILETYPE_NONE;
2487 if(S_ISDIR(buf.st_mode))
2488 return FS_FILETYPE_DIRECTORY;
2490 return FS_FILETYPE_FILE;
2494 qboolean FS_SysFileExists (const char *path)
2496 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2499 void FS_mkdir (const char *path)
2512 Allocate and fill a search structure with information on matching filenames.
2515 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2518 searchpath_t *searchpath;
2520 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2521 stringlist_t resultlist;
2522 stringlist_t dirlist;
2523 const char *slash, *backslash, *colon, *separator;
2525 char temp[MAX_OSPATH];
2527 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2532 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2536 stringlistinit(&resultlist);
2537 stringlistinit(&dirlist);
2539 slash = strrchr(pattern, '/');
2540 backslash = strrchr(pattern, '\\');
2541 colon = strrchr(pattern, ':');
2542 separator = max(slash, backslash);
2543 separator = max(separator, colon);
2544 basepathlength = separator ? (separator + 1 - pattern) : 0;
2545 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2547 memcpy(basepath, pattern, basepathlength);
2548 basepath[basepathlength] = 0;
2550 // search through the path, one element at a time
2551 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2553 // is the element a pak file?
2554 if (searchpath->pack)
2556 // look through all the pak file elements
2557 pak = searchpath->pack;
2558 for (i = 0;i < pak->numfiles;i++)
2560 strlcpy(temp, pak->files[i].name, sizeof(temp));
2563 if (matchpattern(temp, (char *)pattern, true))
2565 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2566 if (!strcmp(resultlist.strings[resultlistindex], temp))
2568 if (resultlistindex == resultlist.numstrings)
2570 stringlistappend(&resultlist, temp);
2571 if (!quiet && developer_loading.integer)
2572 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2575 // strip off one path element at a time until empty
2576 // this way directories are added to the listing if they match the pattern
2577 slash = strrchr(temp, '/');
2578 backslash = strrchr(temp, '\\');
2579 colon = strrchr(temp, ':');
2581 if (separator < slash)
2583 if (separator < backslash)
2584 separator = backslash;
2585 if (separator < colon)
2587 *((char *)separator) = 0;
2593 stringlist_t matchedSet, foundSet;
2594 const char *start = pattern;
2596 stringlistinit(&matchedSet);
2597 stringlistinit(&foundSet);
2598 // add a first entry to the set
2599 stringlistappend(&matchedSet, "");
2600 // iterate through pattern's path
2603 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
2604 char subpath[MAX_OSPATH];
2605 char subpattern[MAX_OSPATH];
2607 // find the next wildcard
2608 wildcard = strchr(start, '?');
2609 asterisk = strchr(start, '*');
2610 if (asterisk && (!wildcard || asterisk < wildcard))
2612 wildcard = asterisk;
2617 nextseparator = strchr( wildcard, '/' );
2621 nextseparator = NULL;
2624 if( !nextseparator ) {
2625 nextseparator = start + strlen( start );
2628 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
2629 // copy everything up except nextseperator
2630 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
2631 // find the last '/' before the wildcard
2632 prevseparator = strrchr( subpattern, '/' );
2634 prevseparator = subpattern;
2637 // copy everything from start to the previous including the '/' (before the wildcard)
2638 // everything up to start is already included in the path of matchedSet's entries
2639 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
2641 // for each entry in matchedSet try to open the subdirectories specified in subpath
2642 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
2643 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
2644 strlcat( temp, subpath, sizeof(temp) );
2645 listdirectory( &foundSet, searchpath->filename, temp );
2647 if( dirlistindex == 0 ) {
2650 // reset the current result set
2651 stringlistfreecontents( &matchedSet );
2652 // match against the pattern
2653 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
2654 const char *direntry = foundSet.strings[ dirlistindex ];
2655 if (matchpattern(direntry, subpattern, true)) {
2656 stringlistappend( &matchedSet, direntry );
2659 stringlistfreecontents( &foundSet );
2661 start = nextseparator;
2664 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
2666 const char *temp = matchedSet.strings[dirlistindex];
2667 if (matchpattern(temp, (char *)pattern, true))
2669 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2670 if (!strcmp(resultlist.strings[resultlistindex], temp))
2672 if (resultlistindex == resultlist.numstrings)
2674 stringlistappend(&resultlist, temp);
2675 if (!quiet && developer_loading.integer)
2676 Con_Printf("SearchDirFile: %s\n", temp);
2680 stringlistfreecontents( &matchedSet );
2684 if (resultlist.numstrings)
2686 stringlistsort(&resultlist);
2687 numfiles = resultlist.numstrings;
2689 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2690 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2691 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2692 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2693 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2694 search->numfilenames = (int)numfiles;
2697 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2700 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2701 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2702 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2704 numchars += (int)textlen;
2707 stringlistfreecontents(&resultlist);
2713 void FS_FreeSearch(fssearch_t *search)
2718 extern int con_linewidth;
2719 int FS_ListDirectory(const char *pattern, int oneperline)
2728 char linebuf[MAX_INPUTLINE];
2730 search = FS_Search(pattern, true, true);
2733 numfiles = search->numfilenames;
2736 // FIXME: the names could be added to one column list and then
2737 // gradually shifted into the next column if they fit, and then the
2738 // next to make a compact variable width listing but it's a lot more
2740 // find width for columns
2742 for (i = 0;i < numfiles;i++)
2744 l = (int)strlen(search->filenames[i]);
2745 if (columnwidth < l)
2748 // count the spacing character
2750 // calculate number of columns
2751 numcolumns = con_linewidth / columnwidth;
2752 // don't bother with the column printing if it's only one column
2753 if (numcolumns >= 2)
2755 numlines = (numfiles + numcolumns - 1) / numcolumns;
2756 for (i = 0;i < numlines;i++)
2759 for (k = 0;k < numcolumns;k++)
2761 l = i * numcolumns + k;
2764 name = search->filenames[l];
2765 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2766 linebuf[linebufpos++] = name[j];
2767 // space out name unless it's the last on the line
2768 if (k + 1 < numcolumns && l + 1 < numfiles)
2769 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2770 linebuf[linebufpos++] = ' ';
2773 linebuf[linebufpos] = 0;
2774 Con_Printf("%s\n", linebuf);
2781 for (i = 0;i < numfiles;i++)
2782 Con_Printf("%s\n", search->filenames[i]);
2783 FS_FreeSearch(search);
2784 return (int)numfiles;
2787 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2789 const char *pattern;
2792 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2795 if (Cmd_Argc() == 2)
2796 pattern = Cmd_Argv(1);
2799 if (!FS_ListDirectory(pattern, oneperline))
2800 Con_Print("No files found.\n");
2805 FS_ListDirectoryCmd("dir", true);
2810 FS_ListDirectoryCmd("ls", false);
2813 const char *FS_WhichPack(const char *filename)
2816 searchpath_t *sp = FS_FindFile(filename, &index, true);
2818 return sp->pack->filename;
2824 ====================
2825 FS_IsRegisteredQuakePack
2827 Look for a proof of purchase file file in the requested package
2829 If it is found, this file should NOT be downloaded.
2830 ====================
2832 qboolean FS_IsRegisteredQuakePack(const char *name)
2834 searchpath_t *search;
2837 // search through the path, one element at a time
2838 for (search = fs_searchpaths;search;search = search->next)
2840 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2842 int (*strcmp_funct) (const char* str1, const char* str2);
2843 int left, right, middle;
2846 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2848 // Look for the file (binary search)
2850 right = pak->numfiles - 1;
2851 while (left <= right)
2855 middle = (left + right) / 2;
2856 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2862 // If we're too far in the list
2869 // we found the requested pack but it is not registered quake
2877 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2880 unsigned char *filedata;
2881 fs_offset_t filesize;
2882 if (filesizepointer)
2883 *filesizepointer = 0;
2884 if (!filename || !*filename)
2886 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2889 if (filesizepointer)
2890 *filesizepointer = filesize;
2891 crc = CRC_Block(filedata, filesize);