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
59 // suppress deprecated warnings
60 # include <sys/stat.h>
65 # define unlink _unlink
69 /** \page fs File System
71 All of Quake's data access is through a hierchal file system, but the contents
72 of the file system can be transparently merged from several sources.
74 The "base directory" is the path to the directory holding the quake.exe and
75 all game directories. The sys_* files pass this to host_init in
76 quakeparms_t->basedir. This can be overridden with the "-basedir" command
77 line parm to allow code debugging in a different directory. The base
78 directory is only used during filesystem initialization.
80 The "game directory" is the first tree on the search path and directory that
81 all generated files (savegames, screenshots, demos, config files) will be
82 saved to. This can be overridden with the "-game" command line parameter.
83 The game directory can never be changed while quake is executing. This is a
84 precaution against having a malicious server instruct clients to write files
85 over areas they shouldn't.
91 =============================================================================
95 =============================================================================
98 // Magic numbers of a ZIP file (big-endian format)
99 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
100 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
101 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
103 // Other constants for ZIP files
104 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
105 #define ZIP_END_CDIR_SIZE 22
106 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
107 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
112 #define qz_inflate inflate
113 #define qz_inflateEnd inflateEnd
114 #define qz_inflateInit2_ inflateInit2_
115 #define qz_inflateReset inflateReset
116 #define qz_deflateInit2_ deflateInit2_
117 #define qz_deflateEnd deflateEnd
118 #define qz_deflate deflate
119 #define Z_MEMLEVEL_DEFAULT 8
122 // Zlib constants (from zlib.h)
123 #define Z_SYNC_FLUSH 2
126 #define Z_STREAM_END 1
127 #define Z_STREAM_ERROR (-2)
128 #define Z_DATA_ERROR (-3)
129 #define Z_MEM_ERROR (-4)
130 #define Z_BUF_ERROR (-5)
131 #define ZLIB_VERSION "1.2.3"
135 #define Z_MEMLEVEL_DEFAULT 8
138 #define Z_DEFAULT_COMPRESSION (-1)
140 #define Z_SYNC_FLUSH 2
141 #define Z_FULL_FLUSH 3
144 // Uncomment the following line if the zlib DLL you have still uses
145 // the 1.1.x series calling convention on Win32 (WINAPI)
146 //#define ZLIB_USES_WINAPI
150 =============================================================================
154 =============================================================================
157 /*! Zlib stream (from zlib.h)
158 * \warning: some pointers we don't use directly have
159 * been cast to "void*" for a matter of simplicity
163 unsigned char *next_in; ///< next input byte
164 unsigned int avail_in; ///< number of bytes available at next_in
165 unsigned long total_in; ///< total nb of input bytes read so far
167 unsigned char *next_out; ///< next output byte should be put there
168 unsigned int avail_out; ///< remaining free space at next_out
169 unsigned long total_out; ///< total nb of bytes output so far
171 char *msg; ///< last error message, NULL if no error
172 void *state; ///< not visible by applications
174 void *zalloc; ///< used to allocate the internal state
175 void *zfree; ///< used to free the internal state
176 void *opaque; ///< private data object passed to zalloc and zfree
178 int data_type; ///< best guess about the data type: ascii or binary
179 unsigned long adler; ///< adler32 value of the uncompressed data
180 unsigned long reserved; ///< reserved for future use
185 /// inside a package (PAK or PK3)
186 #define QFILE_FLAG_PACKED (1 << 0)
187 /// file is compressed using the deflate algorithm (PK3 only)
188 #define QFILE_FLAG_DEFLATED (1 << 1)
189 /// file is actually already loaded data
190 #define QFILE_FLAG_DATA (1 << 2)
192 #define FILE_BUFF_SIZE 2048
196 size_t comp_length; ///< length of the compressed file
197 size_t in_ind, in_len; ///< input buffer current index and length
198 size_t in_position; ///< position in the compressed file
199 unsigned char input [FILE_BUFF_SIZE];
205 int handle; ///< file descriptor
206 fs_offset_t real_length; ///< uncompressed file size (for files opened in "read" mode)
207 fs_offset_t position; ///< current position in the file
208 fs_offset_t offset; ///< offset into the package (0 if external file)
209 int ungetc; ///< single stored character from ungetc, cleared to EOF when read
212 fs_offset_t buff_ind, buff_len; ///< buffer current index and length
213 unsigned char buff [FILE_BUFF_SIZE];
215 ztoolkit_t* ztk; ///< For zipped files.
217 const unsigned char *data; ///< For data files.
221 // ------ PK3 files on disk ------ //
223 // You can get the complete ZIP format description from PKWARE website
225 typedef struct pk3_endOfCentralDir_s
227 unsigned int signature;
228 unsigned short disknum;
229 unsigned short cdir_disknum; ///< number of the disk with the start of the central directory
230 unsigned short localentries; ///< number of entries in the central directory on this disk
231 unsigned short nbentries; ///< total number of entries in the central directory on this disk
232 unsigned int cdir_size; ///< size of the central directory
233 unsigned int cdir_offset; ///< with respect to the starting disk number
234 unsigned short comment_size;
235 } pk3_endOfCentralDir_t;
238 // ------ PAK files on disk ------ //
239 typedef struct dpackfile_s
242 int filepos, filelen;
245 typedef struct dpackheader_s
253 /*! \name Packages in memory
256 /// the offset in packfile_t is the true contents offset
257 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
258 /// file compressed using the deflate algorithm
259 #define PACKFILE_FLAG_DEFLATED (1 << 1)
260 /// file is a symbolic link
261 #define PACKFILE_FLAG_SYMLINK (1 << 2)
263 typedef struct packfile_s
265 char name [MAX_QPATH];
268 fs_offset_t packsize; ///< size in the package
269 fs_offset_t realsize; ///< real file size (uncompressed)
272 typedef struct pack_s
274 char filename [MAX_OSPATH];
275 char shortname [MAX_QPATH];
277 int ignorecase; ///< PK3 ignores case
283 /// Search paths for files (including packages)
284 typedef struct searchpath_s
286 // only one of filename / pack will be used
287 char filename[MAX_OSPATH];
289 struct searchpath_s *next;
294 =============================================================================
298 =============================================================================
303 void FS_Which_f(void);
305 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
306 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
307 fs_offset_t offset, fs_offset_t packsize,
308 fs_offset_t realsize, int flags);
312 =============================================================================
316 =============================================================================
319 mempool_t *fs_mempool;
321 searchpath_t *fs_searchpaths = NULL;
322 const char *const fs_checkgamedir_missing = "missing";
324 #define MAX_FILES_IN_PACK 65536
326 char fs_userdir[MAX_OSPATH];
327 char fs_gamedir[MAX_OSPATH];
328 char fs_basedir[MAX_OSPATH];
330 // list of active game directories (empty if not running a mod)
331 int fs_numgamedirs = 0;
332 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
334 // list of all gamedirs with modinfo.txt
335 gamedir_t *fs_all_gamedirs = NULL;
336 int fs_all_gamedirs_count = 0;
338 cvar_t scr_screenshot_name = {CVAR_NORESETTODEFAULTS, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running; the date is encoded using strftime escapes)"};
339 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"};
340 cvar_t cvar_fs_gamedir = {CVAR_READONLY | CVAR_NORESETTODEFAULTS, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"};
344 =============================================================================
346 PRIVATE FUNCTIONS - PK3 HANDLING
348 =============================================================================
352 // Functions exported from zlib
353 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
354 # define ZEXPORT WINAPI
359 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
360 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
361 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
362 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
363 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
364 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
365 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
368 #define qz_inflateInit2(strm, windowBits) \
369 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
370 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
371 qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
374 // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
376 static dllfunction_t zlibfuncs[] =
378 {"inflate", (void **) &qz_inflate},
379 {"inflateEnd", (void **) &qz_inflateEnd},
380 {"inflateInit2_", (void **) &qz_inflateInit2_},
381 {"inflateReset", (void **) &qz_inflateReset},
382 {"deflateInit2_", (void **) &qz_deflateInit2_},
383 {"deflateEnd", (void **) &qz_deflateEnd},
384 {"deflate", (void **) &qz_deflate},
388 /// Handle for Zlib DLL
389 static dllhandle_t zlib_dll = NULL;
393 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
394 static dllfunction_t shfolderfuncs[] =
396 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
399 static dllhandle_t shfolder_dll = NULL;
409 void PK3_CloseLibrary (void)
412 Sys_UnloadLibrary (&zlib_dll);
421 Try to load the Zlib DLL
424 qboolean PK3_OpenLibrary (void)
429 const char* dllnames [] =
434 # ifdef ZLIB_USES_WINAPI
440 #elif defined(MACOSX)
454 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
462 See if zlib is available
465 qboolean FS_HasZlib(void)
470 PK3_OpenLibrary(); // to be safe
471 return (zlib_dll != 0);
477 PK3_GetEndOfCentralDir
479 Extract the end of the central directory from a PK3 package
482 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
484 fs_offset_t filesize, maxsize;
485 unsigned char *buffer, *ptr;
488 // Get the package size
489 filesize = lseek (packhandle, 0, SEEK_END);
490 if (filesize < ZIP_END_CDIR_SIZE)
493 // Load the end of the file in memory
494 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
497 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
498 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
499 lseek (packhandle, filesize - maxsize, SEEK_SET);
500 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
506 // Look for the end of central dir signature around the end of the file
507 maxsize -= ZIP_END_CDIR_SIZE;
508 ptr = &buffer[maxsize];
510 while (BuffBigLong (ptr) != ZIP_END_HEADER)
522 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
523 eocd->signature = LittleLong (eocd->signature);
524 eocd->disknum = LittleShort (eocd->disknum);
525 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
526 eocd->localentries = LittleShort (eocd->localentries);
527 eocd->nbentries = LittleShort (eocd->nbentries);
528 eocd->cdir_size = LittleLong (eocd->cdir_size);
529 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
530 eocd->comment_size = LittleShort (eocd->comment_size);
542 Extract the file list from a PK3 file
545 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
547 unsigned char *central_dir, *ptr;
549 fs_offset_t remaining;
551 // Load the central directory in memory
552 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
553 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
554 if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size)
556 Mem_Free (central_dir);
560 // Extract the files properties
561 // The parsing is done "by hand" because some fields have variable sizes and
562 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
563 remaining = eocd->cdir_size;
566 for (ind = 0; ind < eocd->nbentries; ind++)
568 fs_offset_t namesize, count;
570 // Checking the remaining size
571 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
573 Mem_Free (central_dir);
576 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
579 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
581 Mem_Free (central_dir);
585 namesize = BuffLittleShort (&ptr[28]); // filename length
587 // Check encryption, compression, and attributes
588 // 1st uint8 : general purpose bit flag
589 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
591 // LordHavoc: bit 3 would be a problem if we were scanning the archive
592 // but is not a problem in the central directory where the values are
595 // bit 3 seems to always be set by the standard Mac OSX zip maker
597 // 2nd uint8 : external file attributes
598 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
599 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
601 // Still enough bytes for the name?
602 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
604 Mem_Free (central_dir);
608 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
609 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
611 char filename [sizeof (pack->files[0].name)];
612 fs_offset_t offset, packsize, realsize;
615 // Extract the name (strip it if necessary)
616 namesize = min(namesize, (int)sizeof (filename) - 1);
617 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
618 filename[namesize] = '\0';
620 if (BuffLittleShort (&ptr[10]))
621 flags = PACKFILE_FLAG_DEFLATED;
624 offset = BuffLittleLong (&ptr[42]);
625 packsize = BuffLittleLong (&ptr[20]);
626 realsize = BuffLittleLong (&ptr[24]);
628 switch(ptr[5]) // C_VERSION_MADE_BY_1
633 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
634 // can't use S_ISLNK here, as this has to compile on non-UNIX too
635 flags |= PACKFILE_FLAG_SYMLINK;
639 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
643 // Skip the name, additionnal field, and comment
644 // 1er uint16 : extra field length
645 // 2eme uint16 : file comment length
646 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
647 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
651 // If the package is empty, central_dir is NULL here
652 if (central_dir != NULL)
653 Mem_Free (central_dir);
654 return pack->numfiles;
662 Create a package entry associated with a PK3 file
665 pack_t *FS_LoadPackPK3 (const char *packfile)
668 pk3_endOfCentralDir_t eocd;
673 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
675 packhandle = open (packfile, O_RDONLY | O_BINARY);
680 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
682 Con_Printf ("%s is not a PK3 file\n", packfile);
687 // Multi-volume ZIP archives are NOT allowed
688 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
690 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
695 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
696 // since eocd.nbentries is an unsigned 16 bits integer
697 #if MAX_FILES_IN_PACK < 65535
698 if (eocd.nbentries > MAX_FILES_IN_PACK)
700 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
706 // Create a package structure in memory
707 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
708 pack->ignorecase = true; // PK3 ignores case
709 strlcpy (pack->filename, packfile, sizeof (pack->filename));
710 pack->handle = packhandle;
711 pack->numfiles = eocd.nbentries;
712 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
714 real_nb_files = PK3_BuildFileList (pack, &eocd);
715 if (real_nb_files < 0)
717 Con_Printf ("%s is not a valid PK3 file\n", packfile);
723 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
730 PK3_GetTrueFileOffset
732 Find where the true file data offset is
735 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
737 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
741 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
744 // Load the local file description
745 lseek (pack->handle, pfile->offset, SEEK_SET);
746 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
747 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
749 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
753 // Skip name and extra field
754 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
756 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
762 =============================================================================
764 OTHER PRIVATE FUNCTIONS
766 =============================================================================
774 Add a file to the list of files contained into a package
777 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
778 fs_offset_t offset, fs_offset_t packsize,
779 fs_offset_t realsize, int flags)
781 int (*strcmp_funct) (const char* str1, const char* str2);
782 int left, right, middle;
785 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
787 // Look for the slot we should put that file into (binary search)
789 right = pack->numfiles - 1;
790 while (left <= right)
794 middle = (left + right) / 2;
795 diff = strcmp_funct (pack->files[middle].name, name);
797 // If we found the file, there's a problem
799 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
801 // If we're too far in the list
808 // We have to move the right of the list by one slot to free the one we need
809 pfile = &pack->files[left];
810 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
813 strlcpy (pfile->name, name, sizeof (pfile->name));
814 pfile->offset = offset;
815 pfile->packsize = packsize;
816 pfile->realsize = realsize;
817 pfile->flags = flags;
827 Only used for FS_OpenRealFile.
830 void FS_CreatePath (char *path)
834 for (ofs = path+1 ; *ofs ; ofs++)
836 if (*ofs == '/' || *ofs == '\\')
838 // create the directory
854 void FS_Path_f (void)
858 Con_Print("Current search path:\n");
859 for (s=fs_searchpaths ; s ; s=s->next)
862 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
864 Con_Printf("%s\n", s->filename);
874 /*! Takes an explicit (not game tree related) path to a pak file.
875 *Loads the header and directory, adding the files at the beginning
876 *of the list so they override previous pack files.
878 pack_t *FS_LoadPackPAK (const char *packfile)
880 dpackheader_t header;
887 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
889 packhandle = open (packfile, O_RDONLY | O_BINARY);
893 if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
895 Con_Printf ("%s is not a packfile\n", packfile);
899 if (memcmp(header.id, "PACK", 4))
901 Con_Printf ("%s is not a packfile\n", packfile);
905 header.dirofs = LittleLong (header.dirofs);
906 header.dirlen = LittleLong (header.dirlen);
908 if (header.dirlen % sizeof(dpackfile_t))
910 Con_Printf ("%s has an invalid directory size\n", packfile);
915 numpackfiles = header.dirlen / sizeof(dpackfile_t);
917 if (numpackfiles > MAX_FILES_IN_PACK)
919 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
924 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
925 lseek (packhandle, header.dirofs, SEEK_SET);
926 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
928 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
934 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
935 pack->ignorecase = false; // PAK is case sensitive
936 strlcpy (pack->filename, packfile, sizeof (pack->filename));
937 pack->handle = packhandle;
939 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
941 // parse the directory
942 for (i = 0;i < numpackfiles;i++)
944 fs_offset_t offset = LittleLong (info[i].filepos);
945 fs_offset_t size = LittleLong (info[i].filelen);
947 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
952 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
961 /*! Adds the given pack to the search path.
962 * The pack type is autodetected by the file extension.
964 * Returns true if the file was successfully added to the
965 * search path or if it was already included.
967 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
971 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
973 searchpath_t *search;
975 const char *ext = FS_FileExtension(pakfile);
977 for(search = fs_searchpaths; search; search = search->next)
979 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
982 *already_loaded = true;
983 return true; // already loaded
988 *already_loaded = false;
990 if(!strcasecmp(ext, "pak"))
991 pak = FS_LoadPackPAK (pakfile);
992 else if(!strcasecmp(ext, "pk3"))
993 pak = FS_LoadPackPK3 (pakfile);
995 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
999 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
1000 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
1003 // find the first item whose next one is a pack or NULL
1004 searchpath_t *insertion_point = 0;
1005 if(fs_searchpaths && !fs_searchpaths->pack)
1007 insertion_point = fs_searchpaths;
1010 if(!insertion_point->next)
1012 if(insertion_point->next->pack)
1014 insertion_point = insertion_point->next;
1017 // If insertion_point is NULL, this means that either there is no
1018 // item in the list yet, or that the very first item is a pack. In
1019 // that case, we want to insert at the beginning...
1020 if(!insertion_point)
1022 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1024 search->next = fs_searchpaths;
1025 fs_searchpaths = search;
1028 // otherwise we want to append directly after insertion_point.
1030 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1032 search->next = insertion_point->next;
1033 insertion_point->next = search;
1038 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1040 search->next = fs_searchpaths;
1041 fs_searchpaths = search;
1047 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1058 /*! Adds the given pack to the search path and searches for it in the game path.
1059 * The pack type is autodetected by the file extension.
1061 * Returns true if the file was successfully added to the
1062 * search path or if it was already included.
1064 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1065 * plain directories.
1067 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1069 char fullpath[MAX_OSPATH];
1071 searchpath_t *search;
1074 *already_loaded = false;
1076 // then find the real name...
1077 search = FS_FindFile(pakfile, &index, true);
1078 if(!search || search->pack)
1080 Con_Printf("could not find pak \"%s\"\n", pakfile);
1084 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1086 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1094 Sets fs_gamedir, adds the directory to the head of the path,
1095 then loads and adds pak1.pak pak2.pak ...
1098 void FS_AddGameDirectory (const char *dir)
1102 searchpath_t *search;
1104 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1106 stringlistinit(&list);
1107 listdirectory(&list, "", dir);
1108 stringlistsort(&list);
1110 // add any PAK package in the directory
1111 for (i = 0;i < list.numstrings;i++)
1113 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1115 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1119 // add any PK3 package in the directory
1120 for (i = 0;i < list.numstrings;i++)
1122 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1124 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1128 stringlistfreecontents(&list);
1130 // Add the directory to the search path
1131 // (unpacked files have the priority over packed files)
1132 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1133 strlcpy (search->filename, dir, sizeof (search->filename));
1134 search->next = fs_searchpaths;
1135 fs_searchpaths = search;
1144 void FS_AddGameHierarchy (const char *dir)
1146 // Add the common game directory
1147 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1150 FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
1159 const char *FS_FileExtension (const char *in)
1161 const char *separator, *backslash, *colon, *dot;
1163 separator = strrchr(in, '/');
1164 backslash = strrchr(in, '\\');
1165 if (!separator || separator < backslash)
1166 separator = backslash;
1167 colon = strrchr(in, ':');
1168 if (!separator || separator < colon)
1171 dot = strrchr(in, '.');
1172 if (dot == NULL || (separator && (dot < separator)))
1184 const char *FS_FileWithoutPath (const char *in)
1186 const char *separator, *backslash, *colon;
1188 separator = strrchr(in, '/');
1189 backslash = strrchr(in, '\\');
1190 if (!separator || separator < backslash)
1191 separator = backslash;
1192 colon = strrchr(in, ':');
1193 if (!separator || separator < colon)
1195 return separator ? separator + 1 : in;
1204 void FS_ClearSearchPath (void)
1206 // unload all packs and directory information, close all pack files
1207 // (if a qfile is still reading a pack it won't be harmed because it used
1208 // dup() to get its own handle already)
1209 while (fs_searchpaths)
1211 searchpath_t *search = fs_searchpaths;
1212 fs_searchpaths = search->next;
1216 close(search->pack->handle);
1217 // free any memory associated with it
1218 if (search->pack->files)
1219 Mem_Free(search->pack->files);
1220 Mem_Free(search->pack);
1232 void FS_Rescan (void)
1235 qboolean fs_modified = false;
1236 char gamedirbuf[MAX_INPUTLINE];
1238 FS_ClearSearchPath();
1240 // add the game-specific paths
1241 // gamedirname1 (typically id1)
1242 FS_AddGameHierarchy (gamedirname1);
1243 // update the com_modname (used for server info)
1244 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1246 // add the game-specific path, if any
1247 // (only used for mission packs and the like, which should set fs_modified)
1251 FS_AddGameHierarchy (gamedirname2);
1255 // Adds basedir/gamedir as an override game
1256 // LordHavoc: now supports multiple -game directories
1257 // set the com_modname (reported in server info)
1259 for (i = 0;i < fs_numgamedirs;i++)
1262 FS_AddGameHierarchy (fs_gamedirs[i]);
1263 // update the com_modname (used server info)
1264 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1266 strlcat(gamedirbuf, va(" %s", fs_gamedirs[i]), sizeof(gamedirbuf));
1268 strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
1270 Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
1272 // set the default screenshot name to either the mod name or the
1273 // gamemode screenshot name
1274 if (strcmp(com_modname, gamedirname1))
1275 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1277 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1279 if((i = COM_CheckParm("-modname")) && i < com_argc - 1)
1280 strlcpy(com_modname, com_argv[i+1], sizeof(com_modname));
1282 // If "-condebug" is in the command line, remove the previous log file
1283 if (COM_CheckParm ("-condebug") != 0)
1284 unlink (va("%s/qconsole.log", fs_gamedir));
1286 // look for the pop.lmp file and set registered to true if it is found
1287 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1290 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1292 Con_Print("Playing shareware version.\n");
1296 Cvar_Set ("registered", "1");
1297 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1298 Con_Print("Playing registered version.\n");
1301 // unload all wads so that future queries will return the new data
1305 void FS_Rescan_f(void)
1315 extern void Host_SaveConfig (void);
1316 extern void Host_LoadConfig_f (void);
1317 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1322 if (fs_numgamedirs == numgamedirs)
1324 for (i = 0;i < numgamedirs;i++)
1325 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1327 if (i == numgamedirs)
1328 return true; // already using this set of gamedirs, do nothing
1331 if (numgamedirs > MAX_GAMEDIRS)
1334 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1335 return false; // too many gamedirs
1338 for (i = 0;i < numgamedirs;i++)
1340 // if string is nasty, reject it
1341 p = FS_CheckGameDir(gamedirs[i]);
1345 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1346 return false; // nasty gamedirs
1348 if(p == fs_checkgamedir_missing && failmissing)
1351 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1352 return false; // missing gamedirs
1358 fs_numgamedirs = numgamedirs;
1359 for (i = 0;i < fs_numgamedirs;i++)
1360 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1362 // reinitialize filesystem to detect the new paks
1365 // exec the new config
1366 Host_LoadConfig_f();
1368 // unload all sounds so they will be reloaded from the new files as needed
1369 S_UnloadAllSounds_f();
1371 // reinitialize renderer (this reloads hud/console background/etc)
1372 R_Modules_Restart();
1382 void FS_GameDir_f (void)
1386 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1390 Con_Printf("gamedirs active:");
1391 for (i = 0;i < fs_numgamedirs;i++)
1392 Con_Printf(" %s", fs_gamedirs[i]);
1397 numgamedirs = Cmd_Argc() - 1;
1398 if (numgamedirs > MAX_GAMEDIRS)
1400 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1404 for (i = 0;i < numgamedirs;i++)
1405 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1407 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1409 // actually, changing during game would work fine, but would be stupid
1410 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1414 // halt demo playback to close the file
1417 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1420 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking);
1421 static const char *FS_SysCheckGameDir(const char *gamedir)
1423 static char buf[8192];
1429 stringlistinit(&list);
1430 listdirectory(&list, gamedir, "");
1431 success = list.numstrings > 0;
1432 stringlistfreecontents(&list);
1436 f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
1439 n = FS_Read (f, buf, sizeof(buf) - 1);
1459 const char *FS_CheckGameDir(const char *gamedir)
1463 if (FS_CheckNastyPath(gamedir, true))
1466 ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
1471 // get description from basedir
1472 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1480 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1484 return fs_checkgamedir_missing;
1487 static void FS_ListGameDirs(void)
1489 stringlist_t list, list2;
1493 fs_all_gamedirs_count = 0;
1495 Mem_Free(fs_all_gamedirs);
1497 stringlistinit(&list);
1498 listdirectory(&list, va("%s/", fs_basedir), "");
1499 listdirectory(&list, va("%s/", fs_userdir), "");
1500 stringlistsort(&list);
1502 stringlistinit(&list2);
1503 for(i = 0; i < list.numstrings; ++i)
1506 if(!strcmp(list.strings[i-1], list.strings[i]))
1508 info = FS_CheckGameDir(list.strings[i]);
1511 if(info == fs_checkgamedir_missing)
1515 stringlistappend(&list2, list.strings[i]);
1517 stringlistfreecontents(&list);
1519 fs_all_gamedirs = (gamedir_t *)Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
1520 for(i = 0; i < list2.numstrings; ++i)
1522 info = FS_CheckGameDir(list2.strings[i]);
1523 // all this cannot happen any more, but better be safe than sorry
1526 if(info == fs_checkgamedir_missing)
1530 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
1531 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
1532 ++fs_all_gamedirs_count;
1546 TCHAR mydocsdir[MAX_PATH + 1];
1547 #if _MSC_VER >= 1400
1554 const char* dllnames [] =
1556 "shfolder.dll", // IE 4, or Win NT and higher
1559 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1560 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1563 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1565 // Add the personal game directory
1566 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1568 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
1570 else if(COM_CheckParm("-nohome"))
1577 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1579 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1580 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", fs_userdir);
1584 // use the environment
1585 #if _MSC_VER >= 1400
1586 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1588 homedir = getenv("USERPROFILE");
1593 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1594 #if _MSC_VER >= 1400
1597 Con_DPrintf("Obtained personal directory %s from environment\n", fs_userdir);
1602 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1604 homedir = getenv ("HOME");
1606 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/.%s/", homedir, gameuserdirname);
1609 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1613 if(!COM_CheckParm("-mygames"))
1615 #if _MSC_VER >= 1400
1617 _sopen_s(&fd, va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
1619 int fd = open (va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1624 *fs_userdir = 0; // we have write access to the game dir, so let's use it
1630 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1632 // If the base directory is explicitly defined by the compilation process
1633 #ifdef DP_FS_BASEDIR
1634 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1639 // FIXME: is there a better way to find the directory outside the .app?
1640 if (strstr(com_argv[0], ".app/"))
1644 split = strstr(com_argv[0], ".app/");
1645 while (split > com_argv[0] && *split != '/')
1647 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1648 fs_basedir[split - com_argv[0]] = 0;
1656 // Overrides the system supplied base directory (under GAMENAME)
1657 // 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)
1658 i = COM_CheckParm ("-basedir");
1659 if (i && i < com_argc-1)
1661 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1662 i = (int)strlen (fs_basedir);
1663 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1664 fs_basedir[i-1] = 0;
1667 // add a path separator to the end of the basedir if it lacks one
1668 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1669 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1673 p = FS_CheckGameDir(gamedirname1);
1674 if(!p || p == fs_checkgamedir_missing)
1675 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1679 p = FS_CheckGameDir(gamedirname2);
1680 if(!p || p == fs_checkgamedir_missing)
1681 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1685 // Adds basedir/gamedir as an override game
1686 // LordHavoc: now supports multiple -game directories
1687 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1691 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1694 p = FS_CheckGameDir(com_argv[i]);
1696 Sys_Error("Nasty -game name rejected: %s", com_argv[i]);
1697 if(p == fs_checkgamedir_missing)
1698 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1699 // add the gamedir to the list of active gamedirs
1700 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1705 // generate the searchpath
1709 void FS_Init_Commands(void)
1711 Cvar_RegisterVariable (&scr_screenshot_name);
1712 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1713 Cvar_RegisterVariable (&cvar_fs_gamedir);
1715 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1716 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1717 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1718 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1719 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1720 Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1728 void FS_Shutdown (void)
1730 // close all pack files and such
1731 // (hopefully there aren't any other open files, but they'll be cleaned up
1732 // by the OS anyway)
1733 FS_ClearSearchPath();
1734 Mem_FreePool (&fs_mempool);
1737 Sys_UnloadLibrary (&shfolder_dll);
1742 ====================
1745 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1746 ====================
1748 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1754 // Parse the mode string
1763 opt = O_CREAT | O_TRUNC;
1767 opt = O_CREAT | O_APPEND;
1770 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1773 for (ind = 1; mode[ind] != '\0'; ind++)
1784 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1785 filepath, mode, mode[ind]);
1792 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1793 memset (file, 0, sizeof (*file));
1796 #if _MSC_VER >= 1400
1797 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1799 file->handle = open (filepath, mod | opt, 0666);
1801 if (file->handle < 0)
1807 file->real_length = lseek (file->handle, 0, SEEK_END);
1809 // For files opened in append mode, we start at the end of the file
1811 file->position = file->real_length;
1813 lseek (file->handle, 0, SEEK_SET);
1823 Open a packed file using its package file descriptor
1826 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1832 pfile = &pack->files[pack_ind];
1834 // If we don't have the true offset, get it now
1835 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1836 if (!PK3_GetTrueFileOffset (pfile, pack))
1839 #ifndef LINK_TO_ZLIB
1840 // No Zlib DLL = no compressed files
1841 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1843 Con_Printf("WARNING: can't open the compressed file %s\n"
1844 "You need the Zlib DLL to use compressed files\n",
1850 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1851 // the dup() call to avoid having to close the dup_handle on error here
1852 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1854 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1855 pfile->name, pack->filename, (int) pfile->offset);
1859 dup_handle = dup (pack->handle);
1862 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1866 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1867 memset (file, 0, sizeof (*file));
1868 file->handle = dup_handle;
1869 file->flags = QFILE_FLAG_PACKED;
1870 file->real_length = pfile->realsize;
1871 file->offset = pfile->offset;
1875 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1879 file->flags |= QFILE_FLAG_DEFLATED;
1881 // We need some more variables
1882 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1884 ztk->comp_length = pfile->packsize;
1886 // Initialize zlib stream
1887 ztk->zstream.next_in = ztk->input;
1888 ztk->zstream.avail_in = 0;
1890 /* From Zlib's "unzip.c":
1892 * windowBits is passed < 0 to tell that there is no zlib header.
1893 * Note that in this case inflate *requires* an extra "dummy" byte
1894 * after the compressed stream in order to complete decompression and
1895 * return Z_STREAM_END.
1896 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1897 * size of both compressed and uncompressed data
1899 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1901 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1907 ztk->zstream.next_out = file->buff;
1908 ztk->zstream.avail_out = sizeof (file->buff);
1917 ====================
1920 Return true if the path should be rejected due to one of the following:
1921 1: path elements that are non-portable
1922 2: path elements that would allow access to files outside the game directory,
1923 or are just not a good idea for a mod to be using.
1924 ====================
1926 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1928 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1932 // Windows: don't allow \ in filenames (windows-only), period.
1933 // (on Windows \ is a directory separator, but / is also supported)
1934 if (strstr(path, "\\"))
1935 return 1; // non-portable
1937 // Mac: don't allow Mac-only filenames - : is a directory separator
1938 // instead of /, but we rely on / working already, so there's no reason to
1939 // support a Mac-only path
1940 // Amiga and Windows: : tries to go to root of drive
1941 if (strstr(path, ":"))
1942 return 1; // non-portable attempt to go to root of drive
1944 // Amiga: // is parent directory
1945 if (strstr(path, "//"))
1946 return 1; // non-portable attempt to go to parent directory
1948 // all: don't allow going to parent directory (../ or /../)
1949 if (strstr(path, ".."))
1950 return 2; // attempt to go outside the game directory
1952 // Windows and UNIXes: don't allow absolute paths
1954 return 2; // attempt to go outside the game directory
1956 // 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
1957 if (strchr(path, '.'))
1961 // gamedir is entirely path elements, so simply forbid . entirely
1964 if (strchr(path, '.') < strrchr(path, '/'))
1965 return 2; // possible attempt to go outside the game directory
1968 // all: forbid trailing slash on gamedir
1969 if (isgamedir && path[strlen(path)-1] == '/')
1972 // all: forbid leading dot on any filename for any reason
1973 if (strstr(path, "/."))
1974 return 2; // attempt to go outside the game directory
1976 // after all these checks we're pretty sure it's a / separated filename
1977 // and won't do much if any harm
1983 ====================
1986 Look for a file in the packages and in the filesystem
1988 Return the searchpath where the file was found (or NULL)
1989 and the file index in the package if relevant
1990 ====================
1992 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1994 searchpath_t *search;
1997 // search through the path, one element at a time
1998 for (search = fs_searchpaths;search;search = search->next)
2000 // is the element a pak file?
2003 int (*strcmp_funct) (const char* str1, const char* str2);
2004 int left, right, middle;
2007 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2009 // Look for the file (binary search)
2011 right = pak->numfiles - 1;
2012 while (left <= right)
2016 middle = (left + right) / 2;
2017 diff = strcmp_funct (pak->files[middle].name, name);
2022 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
2024 // yes, but the first one is empty so we treat it as not being there
2025 if (!quiet && developer.integer >= 10)
2026 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
2033 if (!quiet && developer.integer >= 10)
2034 Con_Printf("FS_FindFile: %s in %s\n",
2035 pak->files[middle].name, pak->filename);
2042 // If we're too far in the list
2051 char netpath[MAX_OSPATH];
2052 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
2053 if (FS_SysFileExists (netpath))
2055 if (!quiet && developer.integer >= 10)
2056 Con_Printf("FS_FindFile: %s\n", netpath);
2065 if (!quiet && developer.integer >= 10)
2066 Con_Printf("FS_FindFile: can't find %s\n", name);
2078 Look for a file in the search paths and open it in read-only mode
2081 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
2083 searchpath_t *search;
2086 search = FS_FindFile (filename, &pack_ind, quiet);
2092 // Found in the filesystem?
2095 char path [MAX_OSPATH];
2096 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
2097 return FS_SysOpen (path, "rb", nonblocking);
2100 // So, we found it in a package...
2102 // Is it a PK3 symlink?
2103 // TODO also handle directory symlinks by parsing the whole structure...
2104 // but heck, file symlinks are good enough for now
2105 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
2107 if(symlinkLevels <= 0)
2109 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
2114 char linkbuf[MAX_QPATH];
2116 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
2117 const char *mergeslash;
2122 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
2128 // Now combine the paths...
2129 mergeslash = strrchr(filename, '/');
2130 mergestart = linkbuf;
2132 mergeslash = filename;
2133 while(!strncmp(mergestart, "../", 3))
2136 while(mergeslash > filename)
2139 if(*mergeslash == '/')
2143 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
2144 if(mergeslash == filename)
2146 // Either mergeslash == filename, then we just replace the name (done below)
2150 // Or, we append the name after mergeslash;
2151 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2152 int spaceNeeded = mergeslash - filename + 1;
2153 int spaceRemoved = mergestart - linkbuf;
2154 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2156 Con_DPrintf("symlink: too long path rejected\n");
2159 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2160 memcpy(linkbuf, filename, spaceNeeded);
2161 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2162 mergestart = linkbuf;
2164 if (!quiet && developer_loading.integer)
2165 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2166 if(FS_CheckNastyPath (mergestart, false))
2168 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2171 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2175 return FS_OpenPackedFile (search->pack, pack_ind);
2180 =============================================================================
2182 MAIN PUBLIC FUNCTIONS
2184 =============================================================================
2188 ====================
2191 Open a file in the userpath. The syntax is the same as fopen
2192 Used for savegame scanning in menu, and all file writing.
2193 ====================
2195 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2197 char real_path [MAX_OSPATH];
2199 if (FS_CheckNastyPath(filepath, false))
2201 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2205 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2207 // If the file is opened in "write", "append", or "read/write" mode,
2208 // create directories up to the file.
2209 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2210 FS_CreatePath (real_path);
2211 return FS_SysOpen (real_path, mode, false);
2216 ====================
2219 Open a file. The syntax is the same as fopen
2220 ====================
2222 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2224 if (FS_CheckNastyPath(filepath, false))
2226 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2230 return FS_OpenReadFile (filepath, quiet, false, 16);
2235 ====================
2238 Open a file. The syntax is the same as fopen
2239 ====================
2241 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2244 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2245 memset (file, 0, sizeof (*file));
2246 file->flags = QFILE_FLAG_DATA;
2248 file->real_length = size;
2254 ====================
2258 ====================
2260 int FS_Close (qfile_t* file)
2262 if(file->flags & QFILE_FLAG_DATA)
2268 if (close (file->handle))
2273 qz_inflateEnd (&file->ztk->zstream);
2274 Mem_Free (file->ztk);
2283 ====================
2286 Write "datasize" bytes into a file
2287 ====================
2289 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2293 // If necessary, seek to the exact file position we're supposed to be
2294 if (file->buff_ind != file->buff_len)
2295 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2297 // Purge cached data
2300 // Write the buffer and update the position
2301 result = write (file->handle, data, (fs_offset_t)datasize);
2302 file->position = lseek (file->handle, 0, SEEK_CUR);
2303 if (file->real_length < file->position)
2304 file->real_length = file->position;
2314 ====================
2317 Read up to "buffersize" bytes from a file
2318 ====================
2320 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2322 fs_offset_t count, done;
2324 if (buffersize == 0)
2327 // Get rid of the ungetc character
2328 if (file->ungetc != EOF)
2330 ((char*)buffer)[0] = file->ungetc;
2338 if(file->flags & QFILE_FLAG_DATA)
2340 size_t left = file->real_length - file->position;
2341 if(buffersize > left)
2343 memcpy(buffer, file->data + file->position, buffersize);
2344 file->position += buffersize;
2348 // First, we copy as many bytes as we can from "buff"
2349 if (file->buff_ind < file->buff_len)
2351 count = file->buff_len - file->buff_ind;
2352 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2354 memcpy (buffer, &file->buff[file->buff_ind], count);
2355 file->buff_ind += count;
2357 buffersize -= count;
2358 if (buffersize == 0)
2362 // NOTE: at this point, the read buffer is always empty
2364 // If the file isn't compressed
2365 if (! (file->flags & QFILE_FLAG_DEFLATED))
2369 // We must take care to not read after the end of the file
2370 count = file->real_length - file->position;
2372 // If we have a lot of data to get, put them directly into "buffer"
2373 if (buffersize > sizeof (file->buff) / 2)
2375 if (count > (fs_offset_t)buffersize)
2376 count = (fs_offset_t)buffersize;
2377 lseek (file->handle, file->offset + file->position, SEEK_SET);
2378 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2382 file->position += nb;
2384 // Purge cached data
2390 if (count > (fs_offset_t)sizeof (file->buff))
2391 count = (fs_offset_t)sizeof (file->buff);
2392 lseek (file->handle, file->offset + file->position, SEEK_SET);
2393 nb = read (file->handle, file->buff, count);
2396 file->buff_len = nb;
2397 file->position += nb;
2399 // Copy the requested data in "buffer" (as much as we can)
2400 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2401 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2402 file->buff_ind = count;
2410 // If the file is compressed, it's more complicated...
2411 // We cycle through a few operations until we have read enough data
2412 while (buffersize > 0)
2414 ztoolkit_t *ztk = file->ztk;
2417 // NOTE: at this point, the read buffer is always empty
2419 // If "input" is also empty, we need to refill it
2420 if (ztk->in_ind == ztk->in_len)
2422 // If we are at the end of the file
2423 if (file->position == file->real_length)
2426 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2427 if (count > (fs_offset_t)sizeof (ztk->input))
2428 count = (fs_offset_t)sizeof (ztk->input);
2429 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2430 if (read (file->handle, ztk->input, count) != count)
2432 Con_Printf ("FS_Read: unexpected end of file\n");
2437 ztk->in_len = count;
2438 ztk->in_position += count;
2441 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2442 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2444 // Now that we are sure we have compressed data available, we need to determine
2445 // if it's better to inflate it in "file->buff" or directly in "buffer"
2447 // Inflate the data in "file->buff"
2448 if (buffersize < sizeof (file->buff) / 2)
2450 ztk->zstream.next_out = file->buff;
2451 ztk->zstream.avail_out = sizeof (file->buff);
2452 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2453 if (error != Z_OK && error != Z_STREAM_END)
2455 Con_Printf ("FS_Read: Can't inflate file\n");
2458 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2460 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2461 file->position += file->buff_len;
2463 // Copy the requested data in "buffer" (as much as we can)
2464 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2465 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2466 file->buff_ind = count;
2469 // Else, we inflate directly in "buffer"
2472 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2473 ztk->zstream.avail_out = (unsigned int)buffersize;
2474 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2475 if (error != Z_OK && error != Z_STREAM_END)
2477 Con_Printf ("FS_Read: Can't inflate file\n");
2480 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2482 // How much data did it inflate?
2483 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2484 file->position += count;
2486 // Purge cached data
2491 buffersize -= count;
2499 ====================
2502 Print a string into a file
2503 ====================
2505 int FS_Print (qfile_t* file, const char *msg)
2507 return (int)FS_Write (file, msg, strlen (msg));
2511 ====================
2514 Print a string into a file
2515 ====================
2517 int FS_Printf(qfile_t* file, const char* format, ...)
2522 va_start (args, format);
2523 result = FS_VPrintf (file, format, args);
2531 ====================
2534 Print a string into a file
2535 ====================
2537 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2540 fs_offset_t buff_size = MAX_INPUTLINE;
2545 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2546 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2547 if (len >= 0 && len < buff_size)
2549 Mem_Free (tempbuff);
2553 len = write (file->handle, tempbuff, len);
2554 Mem_Free (tempbuff);
2561 ====================
2564 Get the next character of a file
2565 ====================
2567 int FS_Getc (qfile_t* file)
2571 if (FS_Read (file, &c, 1) != 1)
2579 ====================
2582 Put a character back into the read buffer (only supports one character!)
2583 ====================
2585 int FS_UnGetc (qfile_t* file, unsigned char c)
2587 // If there's already a character waiting to be read
2588 if (file->ungetc != EOF)
2597 ====================
2600 Move the position index in a file
2601 ====================
2603 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2606 unsigned char* buffer;
2607 fs_offset_t buffersize;
2609 // Compute the file offset
2613 offset += file->position - file->buff_len + file->buff_ind;
2620 offset += file->real_length;
2626 if (offset < 0 || offset > file->real_length)
2629 if(file->flags & QFILE_FLAG_DATA)
2631 file->position = offset;
2635 // If we have the data in our read buffer, we don't need to actually seek
2636 if (file->position - file->buff_len <= offset && offset <= file->position)
2638 file->buff_ind = offset + file->buff_len - file->position;
2642 // Purge cached data
2645 // Unpacked or uncompressed files can seek directly
2646 if (! (file->flags & QFILE_FLAG_DEFLATED))
2648 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2650 file->position = offset;
2654 // Seeking in compressed files is more a hack than anything else,
2655 // but we need to support it, so here we go.
2658 // If we have to go back in the file, we need to restart from the beginning
2659 if (offset <= file->position)
2663 ztk->in_position = 0;
2665 lseek (file->handle, file->offset, SEEK_SET);
2667 // Reset the Zlib stream
2668 ztk->zstream.next_in = ztk->input;
2669 ztk->zstream.avail_in = 0;
2670 qz_inflateReset (&ztk->zstream);
2673 // We need a big buffer to force inflating into it directly
2674 buffersize = 2 * sizeof (file->buff);
2675 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2677 // Skip all data until we reach the requested offset
2678 while (offset > file->position)
2680 fs_offset_t diff = offset - file->position;
2681 fs_offset_t count, len;
2683 count = (diff > buffersize) ? buffersize : diff;
2684 len = FS_Read (file, buffer, count);
2698 ====================
2701 Give the current position in a file
2702 ====================
2704 fs_offset_t FS_Tell (qfile_t* file)
2706 return file->position - file->buff_len + file->buff_ind;
2711 ====================
2714 Give the total size of a file
2715 ====================
2717 fs_offset_t FS_FileSize (qfile_t* file)
2719 return file->real_length;
2724 ====================
2727 Erases any buffered input or output data
2728 ====================
2730 void FS_Purge (qfile_t* file)
2742 Filename are relative to the quake directory.
2743 Always appends a 0 byte.
2746 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2749 unsigned char *buf = NULL;
2750 fs_offset_t filesize = 0;
2752 file = FS_OpenVirtualFile(path, quiet);
2755 filesize = file->real_length;
2758 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2763 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2764 buf[filesize] = '\0';
2765 FS_Read (file, buf, filesize);
2767 if (developer_loadfile.integer)
2768 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2771 if (filesizepointer)
2772 *filesizepointer = filesize;
2781 The filename will be prefixed by the current game directory
2784 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2788 file = FS_OpenRealFile(filename, "wb", false);
2791 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2795 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2796 FS_Write (file, data, len);
2803 =============================================================================
2805 OTHERS PUBLIC FUNCTIONS
2807 =============================================================================
2815 void FS_StripExtension (const char *in, char *out, size_t size_out)
2823 while ((currentchar = *in) && size_out > 1)
2825 if (currentchar == '.')
2827 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2829 *out++ = currentchar;
2845 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2849 // if path doesn't have a .EXT, append extension
2850 // (extension should include the .)
2851 src = path + strlen(path) - 1;
2853 while (*src != '/' && src != path)
2856 return; // it has an extension
2860 strlcat (path, extension, size_path);
2868 Look for a file in the packages and in the filesystem
2871 int FS_FileType (const char *filename)
2873 searchpath_t *search;
2874 char fullpath[MAX_OSPATH];
2876 search = FS_FindFile (filename, NULL, true);
2878 return FS_FILETYPE_NONE;
2881 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2883 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2884 return FS_SysFileType(fullpath);
2892 Look for a file in the packages and in the filesystem
2895 qboolean FS_FileExists (const char *filename)
2897 return (FS_FindFile (filename, NULL, true) != NULL);
2905 Look for a file in the filesystem only
2908 int FS_SysFileType (const char *path)
2911 // Sajt - some older sdks are missing this define
2912 # ifndef INVALID_FILE_ATTRIBUTES
2913 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2916 DWORD result = GetFileAttributes(path);
2918 if(result == INVALID_FILE_ATTRIBUTES)
2919 return FS_FILETYPE_NONE;
2921 if(result & FILE_ATTRIBUTE_DIRECTORY)
2922 return FS_FILETYPE_DIRECTORY;
2924 return FS_FILETYPE_FILE;
2928 if (stat (path,&buf) == -1)
2929 return FS_FILETYPE_NONE;
2932 #define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
2934 if(S_ISDIR(buf.st_mode))
2935 return FS_FILETYPE_DIRECTORY;
2937 return FS_FILETYPE_FILE;
2941 qboolean FS_SysFileExists (const char *path)
2943 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2946 void FS_mkdir (const char *path)
2959 Allocate and fill a search structure with information on matching filenames.
2962 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2965 searchpath_t *searchpath;
2967 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2968 stringlist_t resultlist;
2969 stringlist_t dirlist;
2970 const char *slash, *backslash, *colon, *separator;
2972 char temp[MAX_OSPATH];
2974 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2979 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2983 stringlistinit(&resultlist);
2984 stringlistinit(&dirlist);
2986 slash = strrchr(pattern, '/');
2987 backslash = strrchr(pattern, '\\');
2988 colon = strrchr(pattern, ':');
2989 separator = max(slash, backslash);
2990 separator = max(separator, colon);
2991 basepathlength = separator ? (separator + 1 - pattern) : 0;
2992 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2994 memcpy(basepath, pattern, basepathlength);
2995 basepath[basepathlength] = 0;
2997 // search through the path, one element at a time
2998 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
3000 // is the element a pak file?
3001 if (searchpath->pack)
3003 // look through all the pak file elements
3004 pak = searchpath->pack;
3005 for (i = 0;i < pak->numfiles;i++)
3007 strlcpy(temp, pak->files[i].name, sizeof(temp));
3010 if (matchpattern(temp, (char *)pattern, true))
3012 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3013 if (!strcmp(resultlist.strings[resultlistindex], temp))
3015 if (resultlistindex == resultlist.numstrings)
3017 stringlistappend(&resultlist, temp);
3018 if (!quiet && developer_loading.integer)
3019 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
3022 // strip off one path element at a time until empty
3023 // this way directories are added to the listing if they match the pattern
3024 slash = strrchr(temp, '/');
3025 backslash = strrchr(temp, '\\');
3026 colon = strrchr(temp, ':');
3028 if (separator < slash)
3030 if (separator < backslash)
3031 separator = backslash;
3032 if (separator < colon)
3034 *((char *)separator) = 0;
3040 stringlist_t matchedSet, foundSet;
3041 const char *start = pattern;
3043 stringlistinit(&matchedSet);
3044 stringlistinit(&foundSet);
3045 // add a first entry to the set
3046 stringlistappend(&matchedSet, "");
3047 // iterate through pattern's path
3050 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
3051 char subpath[MAX_OSPATH];
3052 char subpattern[MAX_OSPATH];
3054 // find the next wildcard
3055 wildcard = strchr(start, '?');
3056 asterisk = strchr(start, '*');
3057 if (asterisk && (!wildcard || asterisk < wildcard))
3059 wildcard = asterisk;
3064 nextseparator = strchr( wildcard, '/' );
3068 nextseparator = NULL;
3071 if( !nextseparator ) {
3072 nextseparator = start + strlen( start );
3075 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
3076 // copy everything up except nextseperator
3077 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
3078 // find the last '/' before the wildcard
3079 prevseparator = strrchr( subpattern, '/' );
3081 prevseparator = subpattern;
3084 // copy everything from start to the previous including the '/' (before the wildcard)
3085 // everything up to start is already included in the path of matchedSet's entries
3086 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
3088 // for each entry in matchedSet try to open the subdirectories specified in subpath
3089 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
3090 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
3091 strlcat( temp, subpath, sizeof(temp) );
3092 listdirectory( &foundSet, searchpath->filename, temp );
3094 if( dirlistindex == 0 ) {
3097 // reset the current result set
3098 stringlistfreecontents( &matchedSet );
3099 // match against the pattern
3100 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
3101 const char *direntry = foundSet.strings[ dirlistindex ];
3102 if (matchpattern(direntry, subpattern, true)) {
3103 stringlistappend( &matchedSet, direntry );
3106 stringlistfreecontents( &foundSet );
3108 start = nextseparator;
3111 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
3113 const char *temp = matchedSet.strings[dirlistindex];
3114 if (matchpattern(temp, (char *)pattern, true))
3116 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3117 if (!strcmp(resultlist.strings[resultlistindex], temp))
3119 if (resultlistindex == resultlist.numstrings)
3121 stringlistappend(&resultlist, temp);
3122 if (!quiet && developer_loading.integer)
3123 Con_Printf("SearchDirFile: %s\n", temp);
3127 stringlistfreecontents( &matchedSet );
3131 if (resultlist.numstrings)
3133 stringlistsort(&resultlist);
3134 numfiles = resultlist.numstrings;
3136 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3137 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
3138 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
3139 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
3140 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
3141 search->numfilenames = (int)numfiles;
3144 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3147 search->filenames[numfiles] = search->filenamesbuffer + numchars;
3148 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
3149 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
3151 numchars += (int)textlen;
3154 stringlistfreecontents(&resultlist);
3160 void FS_FreeSearch(fssearch_t *search)
3165 extern int con_linewidth;
3166 int FS_ListDirectory(const char *pattern, int oneperline)
3175 char linebuf[MAX_INPUTLINE];
3177 search = FS_Search(pattern, true, true);
3180 numfiles = search->numfilenames;
3183 // FIXME: the names could be added to one column list and then
3184 // gradually shifted into the next column if they fit, and then the
3185 // next to make a compact variable width listing but it's a lot more
3187 // find width for columns
3189 for (i = 0;i < numfiles;i++)
3191 l = (int)strlen(search->filenames[i]);
3192 if (columnwidth < l)
3195 // count the spacing character
3197 // calculate number of columns
3198 numcolumns = con_linewidth / columnwidth;
3199 // don't bother with the column printing if it's only one column
3200 if (numcolumns >= 2)
3202 numlines = (numfiles + numcolumns - 1) / numcolumns;
3203 for (i = 0;i < numlines;i++)
3206 for (k = 0;k < numcolumns;k++)
3208 l = i * numcolumns + k;
3211 name = search->filenames[l];
3212 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3213 linebuf[linebufpos++] = name[j];
3214 // space out name unless it's the last on the line
3215 if (k + 1 < numcolumns && l + 1 < numfiles)
3216 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3217 linebuf[linebufpos++] = ' ';
3220 linebuf[linebufpos] = 0;
3221 Con_Printf("%s\n", linebuf);
3228 for (i = 0;i < numfiles;i++)
3229 Con_Printf("%s\n", search->filenames[i]);
3230 FS_FreeSearch(search);
3231 return (int)numfiles;
3234 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3236 const char *pattern;
3239 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3242 if (Cmd_Argc() == 2)
3243 pattern = Cmd_Argv(1);
3246 if (!FS_ListDirectory(pattern, oneperline))
3247 Con_Print("No files found.\n");
3252 FS_ListDirectoryCmd("dir", true);
3257 FS_ListDirectoryCmd("ls", false);
3260 void FS_Which_f(void)
3262 const char *filename;
3265 if (Cmd_Argc() != 2)
3267 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3270 filename = Cmd_Argv(1);
3271 sp = FS_FindFile(filename, &index, true);
3273 Con_Printf("%s isn't anywhere\n", filename);
3277 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3279 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3283 const char *FS_WhichPack(const char *filename)
3286 searchpath_t *sp = FS_FindFile(filename, &index, true);
3288 return sp->pack->shortname;
3294 ====================
3295 FS_IsRegisteredQuakePack
3297 Look for a proof of purchase file file in the requested package
3299 If it is found, this file should NOT be downloaded.
3300 ====================
3302 qboolean FS_IsRegisteredQuakePack(const char *name)
3304 searchpath_t *search;
3307 // search through the path, one element at a time
3308 for (search = fs_searchpaths;search;search = search->next)
3310 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3312 int (*strcmp_funct) (const char* str1, const char* str2);
3313 int left, right, middle;
3316 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3318 // Look for the file (binary search)
3320 right = pak->numfiles - 1;
3321 while (left <= right)
3325 middle = (left + right) / 2;
3326 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3332 // If we're too far in the list
3339 // we found the requested pack but it is not registered quake
3347 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3350 unsigned char *filedata;
3351 fs_offset_t filesize;
3352 if (filesizepointer)
3353 *filesizepointer = 0;
3354 if (!filename || !*filename)
3356 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3359 if (filesizepointer)
3360 *filesizepointer = filesize;
3361 crc = CRC_Block(filedata, filesize);
3367 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3370 unsigned char *out = NULL;
3374 #ifndef LINK_TO_ZLIB
3379 memset(&strm, 0, sizeof(strm));
3380 strm.zalloc = Z_NULL;
3381 strm.zfree = Z_NULL;
3382 strm.opaque = Z_NULL;
3385 level = Z_DEFAULT_COMPRESSION;
3387 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3389 Con_Printf("FS_Deflate: deflate init error!\n");
3393 strm.next_in = (unsigned char*)data;
3394 strm.avail_in = size;
3396 tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3399 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3400 qz_deflateEnd(&strm);
3404 strm.next_out = tmp;
3405 strm.avail_out = size;
3407 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3409 Con_Printf("FS_Deflate: deflate failed!\n");
3410 qz_deflateEnd(&strm);
3415 if(qz_deflateEnd(&strm) != Z_OK)
3417 Con_Printf("FS_Deflate: deflateEnd failed\n");
3422 if(strm.total_out >= size)
3424 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3429 out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3432 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3438 *deflated_size = (size_t)strm.total_out;
3440 memcpy(out, tmp, strm.total_out);
3446 static void AssertBufsize(sizebuf_t *buf, int length)
3448 if(buf->cursize + length > buf->maxsize)
3450 int oldsize = buf->maxsize;
3451 unsigned char *olddata;
3452 olddata = buf->data;
3453 buf->maxsize += length;
3454 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3457 memcpy(buf->data, olddata, oldsize);
3463 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3467 unsigned char *out = NULL;
3468 unsigned char tmp[2048];
3473 #ifndef LINK_TO_ZLIB
3478 memset(&outbuf, 0, sizeof(outbuf));
3479 outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3480 outbuf.maxsize = sizeof(tmp);
3482 memset(&strm, 0, sizeof(strm));
3483 strm.zalloc = Z_NULL;
3484 strm.zfree = Z_NULL;
3485 strm.opaque = Z_NULL;
3487 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3489 Con_Printf("FS_Inflate: inflate init error!\n");
3490 Mem_Free(outbuf.data);
3494 strm.next_in = (unsigned char*)data;
3495 strm.avail_in = size;
3499 strm.next_out = tmp;
3500 strm.avail_out = sizeof(tmp);
3501 ret = qz_inflate(&strm, Z_NO_FLUSH);
3502 // it either returns Z_OK on progress, Z_STREAM_END on end
3510 case Z_STREAM_ERROR:
3511 Con_Print("FS_Inflate: stream error!\n");
3514 Con_Print("FS_Inflate: data error!\n");
3517 Con_Print("FS_Inflate: mem error!\n");
3520 Con_Print("FS_Inflate: buf error!\n");
3523 Con_Print("FS_Inflate: unknown error!\n");
3527 if(ret != Z_OK && ret != Z_STREAM_END)
3529 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3530 Mem_Free(outbuf.data);
3531 qz_inflateEnd(&strm);
3534 have = sizeof(tmp) - strm.avail_out;
3535 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3536 SZ_Write(&outbuf, tmp, have);
3537 } while(ret != Z_STREAM_END);
3539 qz_inflateEnd(&strm);
3541 out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3544 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3545 Mem_Free(outbuf.data);
3549 memcpy(out, outbuf.data, outbuf.cursize);
3550 Mem_Free(outbuf.data);
3553 *inflated_size = (size_t)outbuf.cursize;