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
109 // Zlib constants (from zlib.h)
110 #define Z_SYNC_FLUSH 2
113 #define Z_STREAM_END 1
114 #define Z_STREAM_ERROR (-2)
115 #define Z_DATA_ERROR (-3)
116 #define Z_MEM_ERROR (-4)
117 #define Z_BUF_ERROR (-5)
118 #define ZLIB_VERSION "1.2.3"
122 #define Z_MEMLEVEL_DEFAULT 8
125 #define Z_DEFAULT_COMPRESSION (-1)
127 #define Z_SYNC_FLUSH 2
128 #define Z_FULL_FLUSH 3
131 // Uncomment the following line if the zlib DLL you have still uses
132 // the 1.1.x series calling convention on Win32 (WINAPI)
133 //#define ZLIB_USES_WINAPI
137 =============================================================================
141 =============================================================================
144 /*! Zlib stream (from zlib.h)
145 * \warning: some pointers we don't use directly have
146 * been cast to "void*" for a matter of simplicity
150 unsigned char *next_in; ///< next input byte
151 unsigned int avail_in; ///< number of bytes available at next_in
152 unsigned long total_in; ///< total nb of input bytes read so far
154 unsigned char *next_out; ///< next output byte should be put there
155 unsigned int avail_out; ///< remaining free space at next_out
156 unsigned long total_out; ///< total nb of bytes output so far
158 char *msg; ///< last error message, NULL if no error
159 void *state; ///< not visible by applications
161 void *zalloc; ///< used to allocate the internal state
162 void *zfree; ///< used to free the internal state
163 void *opaque; ///< private data object passed to zalloc and zfree
165 int data_type; ///< best guess about the data type: ascii or binary
166 unsigned long adler; ///< adler32 value of the uncompressed data
167 unsigned long reserved; ///< reserved for future use
171 /// inside a package (PAK or PK3)
172 #define QFILE_FLAG_PACKED (1 << 0)
173 /// file is compressed using the deflate algorithm (PK3 only)
174 #define QFILE_FLAG_DEFLATED (1 << 1)
175 /// file is actually already loaded data
176 #define QFILE_FLAG_DATA (1 << 2)
178 #define FILE_BUFF_SIZE 2048
182 size_t comp_length; ///< length of the compressed file
183 size_t in_ind, in_len; ///< input buffer current index and length
184 size_t in_position; ///< position in the compressed file
185 unsigned char input [FILE_BUFF_SIZE];
191 int handle; ///< file descriptor
192 fs_offset_t real_length; ///< uncompressed file size (for files opened in "read" mode)
193 fs_offset_t position; ///< current position in the file
194 fs_offset_t offset; ///< offset into the package (0 if external file)
195 int ungetc; ///< single stored character from ungetc, cleared to EOF when read
198 fs_offset_t buff_ind, buff_len; ///< buffer current index and length
199 unsigned char buff [FILE_BUFF_SIZE];
201 ztoolkit_t* ztk; ///< For zipped files.
203 const unsigned char *data; ///< For data files.
207 // ------ PK3 files on disk ------ //
209 // You can get the complete ZIP format description from PKWARE website
211 typedef struct pk3_endOfCentralDir_s
213 unsigned int signature;
214 unsigned short disknum;
215 unsigned short cdir_disknum; ///< number of the disk with the start of the central directory
216 unsigned short localentries; ///< number of entries in the central directory on this disk
217 unsigned short nbentries; ///< total number of entries in the central directory on this disk
218 unsigned int cdir_size; ///< size of the central directory
219 unsigned int cdir_offset; ///< with respect to the starting disk number
220 unsigned short comment_size;
221 } pk3_endOfCentralDir_t;
224 // ------ PAK files on disk ------ //
225 typedef struct dpackfile_s
228 int filepos, filelen;
231 typedef struct dpackheader_s
239 /*! \name Packages in memory
242 /// the offset in packfile_t is the true contents offset
243 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
244 /// file compressed using the deflate algorithm
245 #define PACKFILE_FLAG_DEFLATED (1 << 1)
246 /// file is a symbolic link
247 #define PACKFILE_FLAG_SYMLINK (1 << 2)
249 typedef struct packfile_s
251 char name [MAX_QPATH];
254 fs_offset_t packsize; ///< size in the package
255 fs_offset_t realsize; ///< real file size (uncompressed)
258 typedef struct pack_s
260 char filename [MAX_OSPATH];
261 char shortname [MAX_QPATH];
263 int ignorecase; ///< PK3 ignores case
269 /// Search paths for files (including packages)
270 typedef struct searchpath_s
272 // only one of filename / pack will be used
273 char filename[MAX_OSPATH];
275 struct searchpath_s *next;
280 =============================================================================
284 =============================================================================
289 void FS_Which_f(void);
291 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
292 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
293 fs_offset_t offset, fs_offset_t packsize,
294 fs_offset_t realsize, int flags);
298 =============================================================================
302 =============================================================================
305 mempool_t *fs_mempool;
307 searchpath_t *fs_searchpaths = NULL;
308 const char *const fs_checkgamedir_missing = "missing";
310 #define MAX_FILES_IN_PACK 65536
312 char fs_userdir[MAX_OSPATH];
313 char fs_gamedir[MAX_OSPATH];
314 char fs_basedir[MAX_OSPATH];
316 // list of active game directories (empty if not running a mod)
317 int fs_numgamedirs = 0;
318 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
320 // list of all gamedirs with modinfo.txt
321 gamedir_t *fs_all_gamedirs = NULL;
322 int fs_all_gamedirs_count = 0;
324 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)"};
325 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"};
326 cvar_t cvar_fs_gamedir = {CVAR_READONLY | CVAR_NORESETTODEFAULTS, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"};
330 =============================================================================
332 PRIVATE FUNCTIONS - PK3 HANDLING
334 =============================================================================
337 // Functions exported from zlib
338 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
339 # define ZEXPORT WINAPI
344 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
345 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
346 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
347 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
348 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
349 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
350 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
352 #define qz_inflateInit2(strm, windowBits) \
353 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
354 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
355 qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
357 // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
359 static dllfunction_t zlibfuncs[] =
361 {"inflate", (void **) &qz_inflate},
362 {"inflateEnd", (void **) &qz_inflateEnd},
363 {"inflateInit2_", (void **) &qz_inflateInit2_},
364 {"inflateReset", (void **) &qz_inflateReset},
365 {"deflateInit2_", (void **) &qz_deflateInit2_},
366 {"deflateEnd", (void **) &qz_deflateEnd},
367 {"deflate", (void **) &qz_deflate},
371 /// Handle for Zlib DLL
372 static dllhandle_t zlib_dll = NULL;
375 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
376 static dllfunction_t shfolderfuncs[] =
378 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
381 static dllhandle_t shfolder_dll = NULL;
391 void PK3_CloseLibrary (void)
393 Sys_UnloadLibrary (&zlib_dll);
401 Try to load the Zlib DLL
404 qboolean PK3_OpenLibrary (void)
406 const char* dllnames [] =
411 # ifdef ZLIB_USES_WINAPI
417 #elif defined(MACOSX)
431 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
438 See if zlib is available
441 qboolean FS_HasZlib(void)
443 PK3_OpenLibrary(); // to be safe
444 return (zlib_dll != 0);
449 PK3_GetEndOfCentralDir
451 Extract the end of the central directory from a PK3 package
454 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
456 fs_offset_t filesize, maxsize;
457 unsigned char *buffer, *ptr;
460 // Get the package size
461 filesize = lseek (packhandle, 0, SEEK_END);
462 if (filesize < ZIP_END_CDIR_SIZE)
465 // Load the end of the file in memory
466 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
469 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
470 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
471 lseek (packhandle, filesize - maxsize, SEEK_SET);
472 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
478 // Look for the end of central dir signature around the end of the file
479 maxsize -= ZIP_END_CDIR_SIZE;
480 ptr = &buffer[maxsize];
482 while (BuffBigLong (ptr) != ZIP_END_HEADER)
494 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
495 eocd->signature = LittleLong (eocd->signature);
496 eocd->disknum = LittleShort (eocd->disknum);
497 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
498 eocd->localentries = LittleShort (eocd->localentries);
499 eocd->nbentries = LittleShort (eocd->nbentries);
500 eocd->cdir_size = LittleLong (eocd->cdir_size);
501 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
502 eocd->comment_size = LittleShort (eocd->comment_size);
514 Extract the file list from a PK3 file
517 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
519 unsigned char *central_dir, *ptr;
521 fs_offset_t remaining;
523 // Load the central directory in memory
524 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
525 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
526 if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size)
528 Mem_Free (central_dir);
532 // Extract the files properties
533 // The parsing is done "by hand" because some fields have variable sizes and
534 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
535 remaining = eocd->cdir_size;
538 for (ind = 0; ind < eocd->nbentries; ind++)
540 fs_offset_t namesize, count;
542 // Checking the remaining size
543 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
545 Mem_Free (central_dir);
548 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
551 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
553 Mem_Free (central_dir);
557 namesize = BuffLittleShort (&ptr[28]); // filename length
559 // Check encryption, compression, and attributes
560 // 1st uint8 : general purpose bit flag
561 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
563 // LordHavoc: bit 3 would be a problem if we were scanning the archive
564 // but is not a problem in the central directory where the values are
567 // bit 3 seems to always be set by the standard Mac OSX zip maker
569 // 2nd uint8 : external file attributes
570 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
571 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
573 // Still enough bytes for the name?
574 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
576 Mem_Free (central_dir);
580 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
581 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
583 char filename [sizeof (pack->files[0].name)];
584 fs_offset_t offset, packsize, realsize;
587 // Extract the name (strip it if necessary)
588 namesize = min(namesize, (int)sizeof (filename) - 1);
589 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
590 filename[namesize] = '\0';
592 if (BuffLittleShort (&ptr[10]))
593 flags = PACKFILE_FLAG_DEFLATED;
596 offset = BuffLittleLong (&ptr[42]);
597 packsize = BuffLittleLong (&ptr[20]);
598 realsize = BuffLittleLong (&ptr[24]);
600 switch(ptr[5]) // C_VERSION_MADE_BY_1
605 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
606 // can't use S_ISLNK here, as this has to compile on non-UNIX too
607 flags |= PACKFILE_FLAG_SYMLINK;
611 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
615 // Skip the name, additionnal field, and comment
616 // 1er uint16 : extra field length
617 // 2eme uint16 : file comment length
618 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
619 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
623 // If the package is empty, central_dir is NULL here
624 if (central_dir != NULL)
625 Mem_Free (central_dir);
626 return pack->numfiles;
634 Create a package entry associated with a PK3 file
637 pack_t *FS_LoadPackPK3 (const char *packfile)
640 pk3_endOfCentralDir_t eocd;
645 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
647 packhandle = open (packfile, O_RDONLY | O_BINARY);
652 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
654 Con_Printf ("%s is not a PK3 file\n", packfile);
659 // Multi-volume ZIP archives are NOT allowed
660 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
662 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
667 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
668 // since eocd.nbentries is an unsigned 16 bits integer
669 #if MAX_FILES_IN_PACK < 65535
670 if (eocd.nbentries > MAX_FILES_IN_PACK)
672 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
678 // Create a package structure in memory
679 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
680 pack->ignorecase = true; // PK3 ignores case
681 strlcpy (pack->filename, packfile, sizeof (pack->filename));
682 pack->handle = packhandle;
683 pack->numfiles = eocd.nbentries;
684 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
686 real_nb_files = PK3_BuildFileList (pack, &eocd);
687 if (real_nb_files < 0)
689 Con_Printf ("%s is not a valid PK3 file\n", packfile);
695 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
702 PK3_GetTrueFileOffset
704 Find where the true file data offset is
707 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
709 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
713 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
716 // Load the local file description
717 lseek (pack->handle, pfile->offset, SEEK_SET);
718 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
719 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
721 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
725 // Skip name and extra field
726 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
728 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
734 =============================================================================
736 OTHER PRIVATE FUNCTIONS
738 =============================================================================
746 Add a file to the list of files contained into a package
749 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
750 fs_offset_t offset, fs_offset_t packsize,
751 fs_offset_t realsize, int flags)
753 int (*strcmp_funct) (const char* str1, const char* str2);
754 int left, right, middle;
757 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
759 // Look for the slot we should put that file into (binary search)
761 right = pack->numfiles - 1;
762 while (left <= right)
766 middle = (left + right) / 2;
767 diff = strcmp_funct (pack->files[middle].name, name);
769 // If we found the file, there's a problem
771 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
773 // If we're too far in the list
780 // We have to move the right of the list by one slot to free the one we need
781 pfile = &pack->files[left];
782 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
785 strlcpy (pfile->name, name, sizeof (pfile->name));
786 pfile->offset = offset;
787 pfile->packsize = packsize;
788 pfile->realsize = realsize;
789 pfile->flags = flags;
799 Only used for FS_OpenRealFile.
802 void FS_CreatePath (char *path)
806 for (ofs = path+1 ; *ofs ; ofs++)
808 if (*ofs == '/' || *ofs == '\\')
810 // create the directory
826 void FS_Path_f (void)
830 Con_Print("Current search path:\n");
831 for (s=fs_searchpaths ; s ; s=s->next)
834 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
836 Con_Printf("%s\n", s->filename);
846 /*! Takes an explicit (not game tree related) path to a pak file.
847 *Loads the header and directory, adding the files at the beginning
848 *of the list so they override previous pack files.
850 pack_t *FS_LoadPackPAK (const char *packfile)
852 dpackheader_t header;
859 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
861 packhandle = open (packfile, O_RDONLY | O_BINARY);
865 if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
867 Con_Printf ("%s is not a packfile\n", packfile);
871 if (memcmp(header.id, "PACK", 4))
873 Con_Printf ("%s is not a packfile\n", packfile);
877 header.dirofs = LittleLong (header.dirofs);
878 header.dirlen = LittleLong (header.dirlen);
880 if (header.dirlen % sizeof(dpackfile_t))
882 Con_Printf ("%s has an invalid directory size\n", packfile);
887 numpackfiles = header.dirlen / sizeof(dpackfile_t);
889 if (numpackfiles > MAX_FILES_IN_PACK)
891 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
896 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
897 lseek (packhandle, header.dirofs, SEEK_SET);
898 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
900 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
906 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
907 pack->ignorecase = false; // PAK is case sensitive
908 strlcpy (pack->filename, packfile, sizeof (pack->filename));
909 pack->handle = packhandle;
911 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
913 // parse the directory
914 for (i = 0;i < numpackfiles;i++)
916 fs_offset_t offset = LittleLong (info[i].filepos);
917 fs_offset_t size = LittleLong (info[i].filelen);
919 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
924 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
933 /*! Adds the given pack to the search path.
934 * The pack type is autodetected by the file extension.
936 * Returns true if the file was successfully added to the
937 * search path or if it was already included.
939 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
943 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
945 searchpath_t *search;
947 const char *ext = FS_FileExtension(pakfile);
949 for(search = fs_searchpaths; search; search = search->next)
951 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
954 *already_loaded = true;
955 return true; // already loaded
960 *already_loaded = false;
962 if(!strcasecmp(ext, "pak"))
963 pak = FS_LoadPackPAK (pakfile);
964 else if(!strcasecmp(ext, "pk3"))
965 pak = FS_LoadPackPK3 (pakfile);
967 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
971 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
972 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
975 // find the first item whose next one is a pack or NULL
976 searchpath_t *insertion_point = 0;
977 if(fs_searchpaths && !fs_searchpaths->pack)
979 insertion_point = fs_searchpaths;
982 if(!insertion_point->next)
984 if(insertion_point->next->pack)
986 insertion_point = insertion_point->next;
989 // If insertion_point is NULL, this means that either there is no
990 // item in the list yet, or that the very first item is a pack. In
991 // that case, we want to insert at the beginning...
994 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
996 search->next = fs_searchpaths;
997 fs_searchpaths = search;
1000 // otherwise we want to append directly after insertion_point.
1002 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1004 search->next = insertion_point->next;
1005 insertion_point->next = search;
1010 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1012 search->next = fs_searchpaths;
1013 fs_searchpaths = search;
1019 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1030 /*! Adds the given pack to the search path and searches for it in the game path.
1031 * The pack type is autodetected by the file extension.
1033 * Returns true if the file was successfully added to the
1034 * search path or if it was already included.
1036 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1037 * plain directories.
1039 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1041 char fullpath[MAX_QPATH];
1043 searchpath_t *search;
1046 *already_loaded = false;
1048 // then find the real name...
1049 search = FS_FindFile(pakfile, &index, true);
1050 if(!search || search->pack)
1052 Con_Printf("could not find pak \"%s\"\n", pakfile);
1056 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1058 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1066 Sets fs_gamedir, adds the directory to the head of the path,
1067 then loads and adds pak1.pak pak2.pak ...
1070 void FS_AddGameDirectory (const char *dir)
1074 searchpath_t *search;
1076 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1078 stringlistinit(&list);
1079 listdirectory(&list, "", dir);
1080 stringlistsort(&list);
1082 // add any PAK package in the directory
1083 for (i = 0;i < list.numstrings;i++)
1085 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1087 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1091 // add any PK3 package in the directory
1092 for (i = 0;i < list.numstrings;i++)
1094 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1096 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1100 stringlistfreecontents(&list);
1102 // Add the directory to the search path
1103 // (unpacked files have the priority over packed files)
1104 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1105 strlcpy (search->filename, dir, sizeof (search->filename));
1106 search->next = fs_searchpaths;
1107 fs_searchpaths = search;
1116 void FS_AddGameHierarchy (const char *dir)
1118 // Add the common game directory
1119 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1122 FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
1131 const char *FS_FileExtension (const char *in)
1133 const char *separator, *backslash, *colon, *dot;
1135 separator = strrchr(in, '/');
1136 backslash = strrchr(in, '\\');
1137 if (!separator || separator < backslash)
1138 separator = backslash;
1139 colon = strrchr(in, ':');
1140 if (!separator || separator < colon)
1143 dot = strrchr(in, '.');
1144 if (dot == NULL || (separator && (dot < separator)))
1156 const char *FS_FileWithoutPath (const char *in)
1158 const char *separator, *backslash, *colon;
1160 separator = strrchr(in, '/');
1161 backslash = strrchr(in, '\\');
1162 if (!separator || separator < backslash)
1163 separator = backslash;
1164 colon = strrchr(in, ':');
1165 if (!separator || separator < colon)
1167 return separator ? separator + 1 : in;
1176 void FS_ClearSearchPath (void)
1178 // unload all packs and directory information, close all pack files
1179 // (if a qfile is still reading a pack it won't be harmed because it used
1180 // dup() to get its own handle already)
1181 while (fs_searchpaths)
1183 searchpath_t *search = fs_searchpaths;
1184 fs_searchpaths = search->next;
1188 close(search->pack->handle);
1189 // free any memory associated with it
1190 if (search->pack->files)
1191 Mem_Free(search->pack->files);
1192 Mem_Free(search->pack);
1204 void FS_Rescan (void)
1207 qboolean fs_modified = false;
1208 char gamedirbuf[MAX_INPUTLINE];
1210 FS_ClearSearchPath();
1212 // add the game-specific paths
1213 // gamedirname1 (typically id1)
1214 FS_AddGameHierarchy (gamedirname1);
1215 // update the com_modname (used for server info)
1216 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1218 // add the game-specific path, if any
1219 // (only used for mission packs and the like, which should set fs_modified)
1223 FS_AddGameHierarchy (gamedirname2);
1227 // Adds basedir/gamedir as an override game
1228 // LordHavoc: now supports multiple -game directories
1229 // set the com_modname (reported in server info)
1231 for (i = 0;i < fs_numgamedirs;i++)
1234 FS_AddGameHierarchy (fs_gamedirs[i]);
1235 // update the com_modname (used server info)
1236 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1238 strlcat(gamedirbuf, va(" %s", fs_gamedirs[i]), sizeof(gamedirbuf));
1240 strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
1242 Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
1244 // set the default screenshot name to either the mod name or the
1245 // gamemode screenshot name
1246 if (strcmp(com_modname, gamedirname1))
1247 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1249 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1251 // If "-condebug" is in the command line, remove the previous log file
1252 if (COM_CheckParm ("-condebug") != 0)
1253 unlink (va("%s/qconsole.log", fs_gamedir));
1255 // look for the pop.lmp file and set registered to true if it is found
1256 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1259 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1261 Con_Print("Playing shareware version.\n");
1265 Cvar_Set ("registered", "1");
1266 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1267 Con_Print("Playing registered version.\n");
1270 // unload all wads so that future queries will return the new data
1274 void FS_Rescan_f(void)
1284 extern void Host_SaveConfig (void);
1285 extern void Host_LoadConfig_f (void);
1286 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1291 if (fs_numgamedirs == numgamedirs)
1293 for (i = 0;i < numgamedirs;i++)
1294 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1296 if (i == numgamedirs)
1297 return true; // already using this set of gamedirs, do nothing
1300 if (numgamedirs > MAX_GAMEDIRS)
1303 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1304 return false; // too many gamedirs
1307 for (i = 0;i < numgamedirs;i++)
1309 // if string is nasty, reject it
1310 p = FS_CheckGameDir(gamedirs[i]);
1314 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1315 return false; // nasty gamedirs
1317 if(p == fs_checkgamedir_missing && failmissing)
1320 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1321 return false; // missing gamedirs
1327 fs_numgamedirs = numgamedirs;
1328 for (i = 0;i < fs_numgamedirs;i++)
1329 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1331 // reinitialize filesystem to detect the new paks
1334 // exec the new config
1335 Host_LoadConfig_f();
1337 // unload all sounds so they will be reloaded from the new files as needed
1338 S_UnloadAllSounds_f();
1340 // reinitialize renderer (this reloads hud/console background/etc)
1341 R_Modules_Restart();
1351 void FS_GameDir_f (void)
1355 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1359 Con_Printf("gamedirs active:");
1360 for (i = 0;i < fs_numgamedirs;i++)
1361 Con_Printf(" %s", fs_gamedirs[i]);
1366 numgamedirs = Cmd_Argc() - 1;
1367 if (numgamedirs > MAX_GAMEDIRS)
1369 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1373 for (i = 0;i < numgamedirs;i++)
1374 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1376 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1378 // actually, changing during game would work fine, but would be stupid
1379 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1383 // halt demo playback to close the file
1386 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1389 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking);
1390 static const char *FS_SysCheckGameDir(const char *gamedir)
1392 static char buf[8192];
1398 stringlistinit(&list);
1399 listdirectory(&list, gamedir, "");
1400 success = list.numstrings > 0;
1401 stringlistfreecontents(&list);
1405 f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
1408 n = FS_Read (f, buf, sizeof(buf) - 1);
1428 const char *FS_CheckGameDir(const char *gamedir)
1432 if (FS_CheckNastyPath(gamedir, true))
1435 ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
1440 // get description from basedir
1441 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1449 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1453 return fs_checkgamedir_missing;
1456 static void FS_ListGameDirs()
1458 stringlist_t list, list2;
1462 fs_all_gamedirs_count = 0;
1464 Mem_Free(fs_all_gamedirs);
1466 stringlistinit(&list);
1467 listdirectory(&list, va("%s/", fs_basedir), "");
1468 listdirectory(&list, va("%s/", fs_userdir), "");
1469 stringlistsort(&list);
1471 stringlistinit(&list2);
1472 for(i = 0; i < list.numstrings; ++i)
1475 if(!strcmp(list.strings[i-1], list.strings[i]))
1477 info = FS_CheckGameDir(list.strings[i]);
1480 if(info == fs_checkgamedir_missing)
1484 stringlistappend(&list2, list.strings[i]);
1486 stringlistfreecontents(&list);
1488 fs_all_gamedirs = Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
1489 for(i = 0; i < list2.numstrings; ++i)
1491 info = FS_CheckGameDir(list2.strings[i]);
1492 // all this cannot happen any more, but better be safe than sorry
1495 if(info == fs_checkgamedir_missing)
1499 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
1500 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
1501 ++fs_all_gamedirs_count;
1515 TCHAR mydocsdir[MAX_PATH + 1];
1516 #if _MSC_VER >= 1400
1523 const char* dllnames [] =
1525 "shfolder.dll", // IE 4, or Win NT and higher
1528 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1529 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1532 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1534 // Add the personal game directory
1535 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1537 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
1539 else if(COM_CheckParm("-nohome"))
1546 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1548 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1549 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", fs_userdir);
1553 // use the environment
1554 #if _MSC_VER >= 1400
1555 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1557 homedir = getenv("USERPROFILE");
1562 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1563 #if _MSC_VER >= 1400
1566 Con_DPrintf("Obtained personal directory %s from environment\n", fs_userdir);
1571 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1573 homedir = getenv ("HOME");
1575 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/.%s/", homedir, gameuserdirname);
1578 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1582 if(!COM_CheckParm("-mygames"))
1584 #if _MSC_VER >= 1400
1586 _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!
1588 int fd = open (va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1593 *fs_userdir = 0; // we have write access to the game dir, so let's use it
1599 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1601 // If the base directory is explicitly defined by the compilation process
1602 #ifdef DP_FS_BASEDIR
1603 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1608 // FIXME: is there a better way to find the directory outside the .app?
1609 if (strstr(com_argv[0], ".app/"))
1613 split = strstr(com_argv[0], ".app/");
1614 while (split > com_argv[0] && *split != '/')
1616 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1617 fs_basedir[split - com_argv[0]] = 0;
1625 // Overrides the system supplied base directory (under GAMENAME)
1626 // 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)
1627 i = COM_CheckParm ("-basedir");
1628 if (i && i < com_argc-1)
1630 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1631 i = (int)strlen (fs_basedir);
1632 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1633 fs_basedir[i-1] = 0;
1636 // add a path separator to the end of the basedir if it lacks one
1637 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1638 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1642 p = FS_CheckGameDir(gamedirname1);
1643 if(!p || p == fs_checkgamedir_missing)
1644 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1648 p = FS_CheckGameDir(gamedirname2);
1649 if(!p || p == fs_checkgamedir_missing)
1650 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1654 // Adds basedir/gamedir as an override game
1655 // LordHavoc: now supports multiple -game directories
1656 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1660 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1663 p = FS_CheckGameDir(com_argv[i]);
1665 Sys_Error("Nasty -game name rejected: %s", com_argv[i]);
1666 if(p == fs_checkgamedir_missing)
1667 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1668 // add the gamedir to the list of active gamedirs
1669 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1674 // generate the searchpath
1678 void FS_Init_Commands(void)
1680 Cvar_RegisterVariable (&scr_screenshot_name);
1681 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1682 Cvar_RegisterVariable (&cvar_fs_gamedir);
1684 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1685 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1686 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1687 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1688 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1689 Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1697 void FS_Shutdown (void)
1699 // close all pack files and such
1700 // (hopefully there aren't any other open files, but they'll be cleaned up
1701 // by the OS anyway)
1702 FS_ClearSearchPath();
1703 Mem_FreePool (&fs_mempool);
1706 Sys_UnloadLibrary (&shfolder_dll);
1711 ====================
1714 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1715 ====================
1717 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1723 // Parse the mode string
1732 opt = O_CREAT | O_TRUNC;
1736 opt = O_CREAT | O_APPEND;
1739 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1742 for (ind = 1; mode[ind] != '\0'; ind++)
1753 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1754 filepath, mode, mode[ind]);
1761 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1762 memset (file, 0, sizeof (*file));
1765 #if _MSC_VER >= 1400
1766 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1768 file->handle = open (filepath, mod | opt, 0666);
1770 if (file->handle < 0)
1776 file->real_length = lseek (file->handle, 0, SEEK_END);
1778 // For files opened in append mode, we start at the end of the file
1780 file->position = file->real_length;
1782 lseek (file->handle, 0, SEEK_SET);
1792 Open a packed file using its package file descriptor
1795 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1801 pfile = &pack->files[pack_ind];
1803 // If we don't have the true offset, get it now
1804 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1805 if (!PK3_GetTrueFileOffset (pfile, pack))
1808 // No Zlib DLL = no compressed files
1809 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1811 Con_Printf("WARNING: can't open the compressed file %s\n"
1812 "You need the Zlib DLL to use compressed files\n",
1817 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1818 // the dup() call to avoid having to close the dup_handle on error here
1819 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1821 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1822 pfile->name, pack->filename, (int) pfile->offset);
1826 dup_handle = dup (pack->handle);
1829 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1833 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1834 memset (file, 0, sizeof (*file));
1835 file->handle = dup_handle;
1836 file->flags = QFILE_FLAG_PACKED;
1837 file->real_length = pfile->realsize;
1838 file->offset = pfile->offset;
1842 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1846 file->flags |= QFILE_FLAG_DEFLATED;
1848 // We need some more variables
1849 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1851 ztk->comp_length = pfile->packsize;
1853 // Initialize zlib stream
1854 ztk->zstream.next_in = ztk->input;
1855 ztk->zstream.avail_in = 0;
1857 /* From Zlib's "unzip.c":
1859 * windowBits is passed < 0 to tell that there is no zlib header.
1860 * Note that in this case inflate *requires* an extra "dummy" byte
1861 * after the compressed stream in order to complete decompression and
1862 * return Z_STREAM_END.
1863 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1864 * size of both compressed and uncompressed data
1866 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1868 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1874 ztk->zstream.next_out = file->buff;
1875 ztk->zstream.avail_out = sizeof (file->buff);
1884 ====================
1887 Return true if the path should be rejected due to one of the following:
1888 1: path elements that are non-portable
1889 2: path elements that would allow access to files outside the game directory,
1890 or are just not a good idea for a mod to be using.
1891 ====================
1893 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1895 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1899 // Windows: don't allow \ in filenames (windows-only), period.
1900 // (on Windows \ is a directory separator, but / is also supported)
1901 if (strstr(path, "\\"))
1902 return 1; // non-portable
1904 // Mac: don't allow Mac-only filenames - : is a directory separator
1905 // instead of /, but we rely on / working already, so there's no reason to
1906 // support a Mac-only path
1907 // Amiga and Windows: : tries to go to root of drive
1908 if (strstr(path, ":"))
1909 return 1; // non-portable attempt to go to root of drive
1911 // Amiga: // is parent directory
1912 if (strstr(path, "//"))
1913 return 1; // non-portable attempt to go to parent directory
1915 // all: don't allow going to parent directory (../ or /../)
1916 if (strstr(path, ".."))
1917 return 2; // attempt to go outside the game directory
1919 // Windows and UNIXes: don't allow absolute paths
1921 return 2; // attempt to go outside the game directory
1923 // 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
1924 if (strchr(path, '.'))
1928 // gamedir is entirely path elements, so simply forbid . entirely
1931 if (strchr(path, '.') < strrchr(path, '/'))
1932 return 2; // possible attempt to go outside the game directory
1935 // all: forbid trailing slash on gamedir
1936 if (isgamedir && path[strlen(path)-1] == '/')
1939 // all: forbid leading dot on any filename for any reason
1940 if (strstr(path, "/."))
1941 return 2; // attempt to go outside the game directory
1943 // after all these checks we're pretty sure it's a / separated filename
1944 // and won't do much if any harm
1950 ====================
1953 Look for a file in the packages and in the filesystem
1955 Return the searchpath where the file was found (or NULL)
1956 and the file index in the package if relevant
1957 ====================
1959 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1961 searchpath_t *search;
1964 // search through the path, one element at a time
1965 for (search = fs_searchpaths;search;search = search->next)
1967 // is the element a pak file?
1970 int (*strcmp_funct) (const char* str1, const char* str2);
1971 int left, right, middle;
1974 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1976 // Look for the file (binary search)
1978 right = pak->numfiles - 1;
1979 while (left <= right)
1983 middle = (left + right) / 2;
1984 diff = strcmp_funct (pak->files[middle].name, name);
1989 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1991 // yes, but the first one is empty so we treat it as not being there
1992 if (!quiet && developer.integer >= 10)
1993 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
2000 if (!quiet && developer.integer >= 10)
2001 Con_Printf("FS_FindFile: %s in %s\n",
2002 pak->files[middle].name, pak->filename);
2009 // If we're too far in the list
2018 char netpath[MAX_OSPATH];
2019 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
2020 if (FS_SysFileExists (netpath))
2022 if (!quiet && developer.integer >= 10)
2023 Con_Printf("FS_FindFile: %s\n", netpath);
2032 if (!quiet && developer.integer >= 10)
2033 Con_Printf("FS_FindFile: can't find %s\n", name);
2045 Look for a file in the search paths and open it in read-only mode
2048 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
2050 searchpath_t *search;
2053 search = FS_FindFile (filename, &pack_ind, quiet);
2059 // Found in the filesystem?
2062 char path [MAX_OSPATH];
2063 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
2064 return FS_SysOpen (path, "rb", nonblocking);
2067 // So, we found it in a package...
2069 // Is it a PK3 symlink?
2070 // TODO also handle directory symlinks by parsing the whole structure...
2071 // but heck, file symlinks are good enough for now
2072 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
2074 if(symlinkLevels <= 0)
2076 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
2081 char linkbuf[MAX_QPATH];
2083 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
2084 const char *mergeslash;
2089 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
2095 // Now combine the paths...
2096 mergeslash = strrchr(filename, '/');
2097 mergestart = linkbuf;
2099 mergeslash = filename;
2100 while(!strncmp(mergestart, "../", 3))
2103 while(mergeslash > filename)
2106 if(*mergeslash == '/')
2110 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
2111 if(mergeslash == filename)
2113 // Either mergeslash == filename, then we just replace the name (done below)
2117 // Or, we append the name after mergeslash;
2118 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2119 int spaceNeeded = mergeslash - filename + 1;
2120 int spaceRemoved = mergestart - linkbuf;
2121 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2123 Con_DPrintf("symlink: too long path rejected\n");
2126 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2127 memcpy(linkbuf, filename, spaceNeeded);
2128 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2129 mergestart = linkbuf;
2131 if (!quiet && developer_loading.integer)
2132 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2133 if(FS_CheckNastyPath (mergestart, false))
2135 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2138 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2142 return FS_OpenPackedFile (search->pack, pack_ind);
2147 =============================================================================
2149 MAIN PUBLIC FUNCTIONS
2151 =============================================================================
2155 ====================
2158 Open a file in the userpath. The syntax is the same as fopen
2159 Used for savegame scanning in menu, and all file writing.
2160 ====================
2162 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2164 char real_path [MAX_OSPATH];
2166 if (FS_CheckNastyPath(filepath, false))
2168 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2172 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2174 // If the file is opened in "write", "append", or "read/write" mode,
2175 // create directories up to the file.
2176 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2177 FS_CreatePath (real_path);
2178 return FS_SysOpen (real_path, mode, false);
2183 ====================
2186 Open a file. The syntax is the same as fopen
2187 ====================
2189 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2191 if (FS_CheckNastyPath(filepath, false))
2193 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2197 return FS_OpenReadFile (filepath, quiet, false, 16);
2202 ====================
2205 Open a file. The syntax is the same as fopen
2206 ====================
2208 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2211 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2212 memset (file, 0, sizeof (*file));
2213 file->flags = QFILE_FLAG_DATA;
2215 file->real_length = size;
2221 ====================
2225 ====================
2227 int FS_Close (qfile_t* file)
2229 if(file->flags & QFILE_FLAG_DATA)
2235 if (close (file->handle))
2240 qz_inflateEnd (&file->ztk->zstream);
2241 Mem_Free (file->ztk);
2250 ====================
2253 Write "datasize" bytes into a file
2254 ====================
2256 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2260 // If necessary, seek to the exact file position we're supposed to be
2261 if (file->buff_ind != file->buff_len)
2262 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2264 // Purge cached data
2267 // Write the buffer and update the position
2268 result = write (file->handle, data, (fs_offset_t)datasize);
2269 file->position = lseek (file->handle, 0, SEEK_CUR);
2270 if (file->real_length < file->position)
2271 file->real_length = file->position;
2281 ====================
2284 Read up to "buffersize" bytes from a file
2285 ====================
2287 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2289 fs_offset_t count, done;
2291 if (buffersize == 0)
2294 // Get rid of the ungetc character
2295 if (file->ungetc != EOF)
2297 ((char*)buffer)[0] = file->ungetc;
2305 if(file->flags & QFILE_FLAG_DATA)
2307 size_t left = file->real_length - file->position;
2308 if(buffersize > left)
2310 memcpy(buffer, file->data + file->position, buffersize);
2311 file->position += buffersize;
2315 // First, we copy as many bytes as we can from "buff"
2316 if (file->buff_ind < file->buff_len)
2318 count = file->buff_len - file->buff_ind;
2319 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2321 memcpy (buffer, &file->buff[file->buff_ind], count);
2322 file->buff_ind += count;
2324 buffersize -= count;
2325 if (buffersize == 0)
2329 // NOTE: at this point, the read buffer is always empty
2331 // If the file isn't compressed
2332 if (! (file->flags & QFILE_FLAG_DEFLATED))
2336 // We must take care to not read after the end of the file
2337 count = file->real_length - file->position;
2339 // If we have a lot of data to get, put them directly into "buffer"
2340 if (buffersize > sizeof (file->buff) / 2)
2342 if (count > (fs_offset_t)buffersize)
2343 count = (fs_offset_t)buffersize;
2344 lseek (file->handle, file->offset + file->position, SEEK_SET);
2345 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2349 file->position += nb;
2351 // Purge cached data
2357 if (count > (fs_offset_t)sizeof (file->buff))
2358 count = (fs_offset_t)sizeof (file->buff);
2359 lseek (file->handle, file->offset + file->position, SEEK_SET);
2360 nb = read (file->handle, file->buff, count);
2363 file->buff_len = nb;
2364 file->position += nb;
2366 // Copy the requested data in "buffer" (as much as we can)
2367 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2368 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2369 file->buff_ind = count;
2377 // If the file is compressed, it's more complicated...
2378 // We cycle through a few operations until we have read enough data
2379 while (buffersize > 0)
2381 ztoolkit_t *ztk = file->ztk;
2384 // NOTE: at this point, the read buffer is always empty
2386 // If "input" is also empty, we need to refill it
2387 if (ztk->in_ind == ztk->in_len)
2389 // If we are at the end of the file
2390 if (file->position == file->real_length)
2393 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2394 if (count > (fs_offset_t)sizeof (ztk->input))
2395 count = (fs_offset_t)sizeof (ztk->input);
2396 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2397 if (read (file->handle, ztk->input, count) != count)
2399 Con_Printf ("FS_Read: unexpected end of file\n");
2404 ztk->in_len = count;
2405 ztk->in_position += count;
2408 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2409 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2411 // Now that we are sure we have compressed data available, we need to determine
2412 // if it's better to inflate it in "file->buff" or directly in "buffer"
2414 // Inflate the data in "file->buff"
2415 if (buffersize < sizeof (file->buff) / 2)
2417 ztk->zstream.next_out = file->buff;
2418 ztk->zstream.avail_out = sizeof (file->buff);
2419 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2420 if (error != Z_OK && error != Z_STREAM_END)
2422 Con_Printf ("FS_Read: Can't inflate file\n");
2425 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2427 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2428 file->position += file->buff_len;
2430 // Copy the requested data in "buffer" (as much as we can)
2431 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2432 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2433 file->buff_ind = count;
2436 // Else, we inflate directly in "buffer"
2439 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2440 ztk->zstream.avail_out = (unsigned int)buffersize;
2441 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2442 if (error != Z_OK && error != Z_STREAM_END)
2444 Con_Printf ("FS_Read: Can't inflate file\n");
2447 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2449 // How much data did it inflate?
2450 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2451 file->position += count;
2453 // Purge cached data
2458 buffersize -= count;
2466 ====================
2469 Print a string into a file
2470 ====================
2472 int FS_Print (qfile_t* file, const char *msg)
2474 return (int)FS_Write (file, msg, strlen (msg));
2478 ====================
2481 Print a string into a file
2482 ====================
2484 int FS_Printf(qfile_t* file, const char* format, ...)
2489 va_start (args, format);
2490 result = FS_VPrintf (file, format, args);
2498 ====================
2501 Print a string into a file
2502 ====================
2504 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2507 fs_offset_t buff_size = MAX_INPUTLINE;
2512 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2513 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2514 if (len >= 0 && len < buff_size)
2516 Mem_Free (tempbuff);
2520 len = write (file->handle, tempbuff, len);
2521 Mem_Free (tempbuff);
2528 ====================
2531 Get the next character of a file
2532 ====================
2534 int FS_Getc (qfile_t* file)
2538 if (FS_Read (file, &c, 1) != 1)
2546 ====================
2549 Put a character back into the read buffer (only supports one character!)
2550 ====================
2552 int FS_UnGetc (qfile_t* file, unsigned char c)
2554 // If there's already a character waiting to be read
2555 if (file->ungetc != EOF)
2564 ====================
2567 Move the position index in a file
2568 ====================
2570 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2573 unsigned char* buffer;
2574 fs_offset_t buffersize;
2576 // Compute the file offset
2580 offset += file->position - file->buff_len + file->buff_ind;
2587 offset += file->real_length;
2593 if (offset < 0 || offset > file->real_length)
2596 if(file->flags & QFILE_FLAG_DATA)
2598 file->position = offset;
2602 // If we have the data in our read buffer, we don't need to actually seek
2603 if (file->position - file->buff_len <= offset && offset <= file->position)
2605 file->buff_ind = offset + file->buff_len - file->position;
2609 // Purge cached data
2612 // Unpacked or uncompressed files can seek directly
2613 if (! (file->flags & QFILE_FLAG_DEFLATED))
2615 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2617 file->position = offset;
2621 // Seeking in compressed files is more a hack than anything else,
2622 // but we need to support it, so here we go.
2625 // If we have to go back in the file, we need to restart from the beginning
2626 if (offset <= file->position)
2630 ztk->in_position = 0;
2632 lseek (file->handle, file->offset, SEEK_SET);
2634 // Reset the Zlib stream
2635 ztk->zstream.next_in = ztk->input;
2636 ztk->zstream.avail_in = 0;
2637 qz_inflateReset (&ztk->zstream);
2640 // We need a big buffer to force inflating into it directly
2641 buffersize = 2 * sizeof (file->buff);
2642 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2644 // Skip all data until we reach the requested offset
2645 while (offset > file->position)
2647 fs_offset_t diff = offset - file->position;
2648 fs_offset_t count, len;
2650 count = (diff > buffersize) ? buffersize : diff;
2651 len = FS_Read (file, buffer, count);
2665 ====================
2668 Give the current position in a file
2669 ====================
2671 fs_offset_t FS_Tell (qfile_t* file)
2673 return file->position - file->buff_len + file->buff_ind;
2678 ====================
2681 Give the total size of a file
2682 ====================
2684 fs_offset_t FS_FileSize (qfile_t* file)
2686 return file->real_length;
2691 ====================
2694 Erases any buffered input or output data
2695 ====================
2697 void FS_Purge (qfile_t* file)
2709 Filename are relative to the quake directory.
2710 Always appends a 0 byte.
2713 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2716 unsigned char *buf = NULL;
2717 fs_offset_t filesize = 0;
2719 file = FS_OpenVirtualFile(path, quiet);
2722 filesize = file->real_length;
2725 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2730 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2731 buf[filesize] = '\0';
2732 FS_Read (file, buf, filesize);
2734 if (developer_loadfile.integer)
2735 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2738 if (filesizepointer)
2739 *filesizepointer = filesize;
2748 The filename will be prefixed by the current game directory
2751 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2755 file = FS_OpenRealFile(filename, "wb", false);
2758 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2762 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2763 FS_Write (file, data, len);
2770 =============================================================================
2772 OTHERS PUBLIC FUNCTIONS
2774 =============================================================================
2782 void FS_StripExtension (const char *in, char *out, size_t size_out)
2790 while ((currentchar = *in) && size_out > 1)
2792 if (currentchar == '.')
2794 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2796 *out++ = currentchar;
2812 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2816 // if path doesn't have a .EXT, append extension
2817 // (extension should include the .)
2818 src = path + strlen(path) - 1;
2820 while (*src != '/' && src != path)
2823 return; // it has an extension
2827 strlcat (path, extension, size_path);
2835 Look for a file in the packages and in the filesystem
2838 int FS_FileType (const char *filename)
2840 searchpath_t *search;
2841 char fullpath[MAX_QPATH];
2843 search = FS_FindFile (filename, NULL, true);
2845 return FS_FILETYPE_NONE;
2848 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2850 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2851 return FS_SysFileType(fullpath);
2859 Look for a file in the packages and in the filesystem
2862 qboolean FS_FileExists (const char *filename)
2864 return (FS_FindFile (filename, NULL, true) != NULL);
2872 Look for a file in the filesystem only
2875 int FS_SysFileType (const char *path)
2878 // Sajt - some older sdks are missing this define
2879 # ifndef INVALID_FILE_ATTRIBUTES
2880 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2883 DWORD result = GetFileAttributes(path);
2885 if(result == INVALID_FILE_ATTRIBUTES)
2886 return FS_FILETYPE_NONE;
2888 if(result & FILE_ATTRIBUTE_DIRECTORY)
2889 return FS_FILETYPE_DIRECTORY;
2891 return FS_FILETYPE_FILE;
2895 if (stat (path,&buf) == -1)
2896 return FS_FILETYPE_NONE;
2898 if(S_ISDIR(buf.st_mode))
2899 return FS_FILETYPE_DIRECTORY;
2901 return FS_FILETYPE_FILE;
2905 qboolean FS_SysFileExists (const char *path)
2907 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2910 void FS_mkdir (const char *path)
2923 Allocate and fill a search structure with information on matching filenames.
2926 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2929 searchpath_t *searchpath;
2931 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2932 stringlist_t resultlist;
2933 stringlist_t dirlist;
2934 const char *slash, *backslash, *colon, *separator;
2936 char temp[MAX_OSPATH];
2938 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2943 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2947 stringlistinit(&resultlist);
2948 stringlistinit(&dirlist);
2950 slash = strrchr(pattern, '/');
2951 backslash = strrchr(pattern, '\\');
2952 colon = strrchr(pattern, ':');
2953 separator = max(slash, backslash);
2954 separator = max(separator, colon);
2955 basepathlength = separator ? (separator + 1 - pattern) : 0;
2956 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2958 memcpy(basepath, pattern, basepathlength);
2959 basepath[basepathlength] = 0;
2961 // search through the path, one element at a time
2962 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2964 // is the element a pak file?
2965 if (searchpath->pack)
2967 // look through all the pak file elements
2968 pak = searchpath->pack;
2969 for (i = 0;i < pak->numfiles;i++)
2971 strlcpy(temp, pak->files[i].name, sizeof(temp));
2974 if (matchpattern(temp, (char *)pattern, true))
2976 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2977 if (!strcmp(resultlist.strings[resultlistindex], temp))
2979 if (resultlistindex == resultlist.numstrings)
2981 stringlistappend(&resultlist, temp);
2982 if (!quiet && developer_loading.integer)
2983 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2986 // strip off one path element at a time until empty
2987 // this way directories are added to the listing if they match the pattern
2988 slash = strrchr(temp, '/');
2989 backslash = strrchr(temp, '\\');
2990 colon = strrchr(temp, ':');
2992 if (separator < slash)
2994 if (separator < backslash)
2995 separator = backslash;
2996 if (separator < colon)
2998 *((char *)separator) = 0;
3004 stringlist_t matchedSet, foundSet;
3005 const char *start = pattern;
3007 stringlistinit(&matchedSet);
3008 stringlistinit(&foundSet);
3009 // add a first entry to the set
3010 stringlistappend(&matchedSet, "");
3011 // iterate through pattern's path
3014 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
3015 char subpath[MAX_OSPATH];
3016 char subpattern[MAX_OSPATH];
3018 // find the next wildcard
3019 wildcard = strchr(start, '?');
3020 asterisk = strchr(start, '*');
3021 if (asterisk && (!wildcard || asterisk < wildcard))
3023 wildcard = asterisk;
3028 nextseparator = strchr( wildcard, '/' );
3032 nextseparator = NULL;
3035 if( !nextseparator ) {
3036 nextseparator = start + strlen( start );
3039 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
3040 // copy everything up except nextseperator
3041 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
3042 // find the last '/' before the wildcard
3043 prevseparator = strrchr( subpattern, '/' );
3045 prevseparator = subpattern;
3048 // copy everything from start to the previous including the '/' (before the wildcard)
3049 // everything up to start is already included in the path of matchedSet's entries
3050 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
3052 // for each entry in matchedSet try to open the subdirectories specified in subpath
3053 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
3054 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
3055 strlcat( temp, subpath, sizeof(temp) );
3056 listdirectory( &foundSet, searchpath->filename, temp );
3058 if( dirlistindex == 0 ) {
3061 // reset the current result set
3062 stringlistfreecontents( &matchedSet );
3063 // match against the pattern
3064 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
3065 const char *direntry = foundSet.strings[ dirlistindex ];
3066 if (matchpattern(direntry, subpattern, true)) {
3067 stringlistappend( &matchedSet, direntry );
3070 stringlistfreecontents( &foundSet );
3072 start = nextseparator;
3075 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
3077 const char *temp = matchedSet.strings[dirlistindex];
3078 if (matchpattern(temp, (char *)pattern, true))
3080 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3081 if (!strcmp(resultlist.strings[resultlistindex], temp))
3083 if (resultlistindex == resultlist.numstrings)
3085 stringlistappend(&resultlist, temp);
3086 if (!quiet && developer_loading.integer)
3087 Con_Printf("SearchDirFile: %s\n", temp);
3091 stringlistfreecontents( &matchedSet );
3095 if (resultlist.numstrings)
3097 stringlistsort(&resultlist);
3098 numfiles = resultlist.numstrings;
3100 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3101 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
3102 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
3103 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
3104 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
3105 search->numfilenames = (int)numfiles;
3108 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3111 search->filenames[numfiles] = search->filenamesbuffer + numchars;
3112 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
3113 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
3115 numchars += (int)textlen;
3118 stringlistfreecontents(&resultlist);
3124 void FS_FreeSearch(fssearch_t *search)
3129 extern int con_linewidth;
3130 int FS_ListDirectory(const char *pattern, int oneperline)
3139 char linebuf[MAX_INPUTLINE];
3141 search = FS_Search(pattern, true, true);
3144 numfiles = search->numfilenames;
3147 // FIXME: the names could be added to one column list and then
3148 // gradually shifted into the next column if they fit, and then the
3149 // next to make a compact variable width listing but it's a lot more
3151 // find width for columns
3153 for (i = 0;i < numfiles;i++)
3155 l = (int)strlen(search->filenames[i]);
3156 if (columnwidth < l)
3159 // count the spacing character
3161 // calculate number of columns
3162 numcolumns = con_linewidth / columnwidth;
3163 // don't bother with the column printing if it's only one column
3164 if (numcolumns >= 2)
3166 numlines = (numfiles + numcolumns - 1) / numcolumns;
3167 for (i = 0;i < numlines;i++)
3170 for (k = 0;k < numcolumns;k++)
3172 l = i * numcolumns + k;
3175 name = search->filenames[l];
3176 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3177 linebuf[linebufpos++] = name[j];
3178 // space out name unless it's the last on the line
3179 if (k + 1 < numcolumns && l + 1 < numfiles)
3180 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3181 linebuf[linebufpos++] = ' ';
3184 linebuf[linebufpos] = 0;
3185 Con_Printf("%s\n", linebuf);
3192 for (i = 0;i < numfiles;i++)
3193 Con_Printf("%s\n", search->filenames[i]);
3194 FS_FreeSearch(search);
3195 return (int)numfiles;
3198 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3200 const char *pattern;
3203 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3206 if (Cmd_Argc() == 2)
3207 pattern = Cmd_Argv(1);
3210 if (!FS_ListDirectory(pattern, oneperline))
3211 Con_Print("No files found.\n");
3216 FS_ListDirectoryCmd("dir", true);
3221 FS_ListDirectoryCmd("ls", false);
3224 void FS_Which_f(void)
3226 const char *filename;
3229 if (Cmd_Argc() != 2)
3231 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3234 filename = Cmd_Argv(1);
3235 sp = FS_FindFile(filename, &index, true);
3237 Con_Printf("%s isn't anywhere\n", filename);
3241 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3243 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3247 const char *FS_WhichPack(const char *filename)
3250 searchpath_t *sp = FS_FindFile(filename, &index, true);
3252 return sp->pack->shortname;
3258 ====================
3259 FS_IsRegisteredQuakePack
3261 Look for a proof of purchase file file in the requested package
3263 If it is found, this file should NOT be downloaded.
3264 ====================
3266 qboolean FS_IsRegisteredQuakePack(const char *name)
3268 searchpath_t *search;
3271 // search through the path, one element at a time
3272 for (search = fs_searchpaths;search;search = search->next)
3274 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3276 int (*strcmp_funct) (const char* str1, const char* str2);
3277 int left, right, middle;
3280 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3282 // Look for the file (binary search)
3284 right = pak->numfiles - 1;
3285 while (left <= right)
3289 middle = (left + right) / 2;
3290 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3296 // If we're too far in the list
3303 // we found the requested pack but it is not registered quake
3311 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3314 unsigned char *filedata;
3315 fs_offset_t filesize;
3316 if (filesizepointer)
3317 *filesizepointer = 0;
3318 if (!filename || !*filename)
3320 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3323 if (filesizepointer)
3324 *filesizepointer = filesize;
3325 crc = CRC_Block(filedata, filesize);
3331 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3334 unsigned char *out = NULL;
3337 memset(&strm, 0, sizeof(strm));
3338 strm.zalloc = Z_NULL;
3339 strm.zfree = Z_NULL;
3340 strm.opaque = Z_NULL;
3343 level = Z_DEFAULT_COMPRESSION;
3345 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3347 Con_Printf("FS_Deflate: deflate init error!\n");
3351 strm.next_in = (unsigned char*)data;
3352 strm.avail_in = size;
3354 tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3357 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3358 qz_deflateEnd(&strm);
3362 strm.next_out = tmp;
3363 strm.avail_out = size;
3365 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3367 Con_Printf("FS_Deflate: deflate failed!\n");
3368 qz_deflateEnd(&strm);
3373 if(qz_deflateEnd(&strm) != Z_OK)
3375 Con_Printf("FS_Deflate: deflateEnd failed\n");
3380 if(strm.total_out >= size)
3382 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3387 out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3390 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3396 *deflated_size = (size_t)strm.total_out;
3398 memcpy(out, tmp, strm.total_out);
3404 static void AssertBufsize(sizebuf_t *buf, int length)
3406 if(buf->cursize + length > buf->maxsize)
3408 int oldsize = buf->maxsize;
3409 unsigned char *olddata;
3410 olddata = buf->data;
3411 buf->maxsize += length;
3412 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3415 memcpy(buf->data, olddata, oldsize);
3421 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3425 unsigned char *out = NULL;
3426 unsigned char tmp[2048];
3430 memset(&outbuf, 0, sizeof(outbuf));
3431 outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3432 outbuf.maxsize = sizeof(tmp);
3434 memset(&strm, 0, sizeof(strm));
3435 strm.zalloc = Z_NULL;
3436 strm.zfree = Z_NULL;
3437 strm.opaque = Z_NULL;
3439 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3441 Con_Printf("FS_Inflate: inflate init error!\n");
3442 Mem_Free(outbuf.data);
3446 strm.next_in = (unsigned char*)data;
3447 strm.avail_in = size;
3451 strm.next_out = tmp;
3452 strm.avail_out = sizeof(tmp);
3453 ret = qz_inflate(&strm, Z_NO_FLUSH);
3454 // it either returns Z_OK on progress, Z_STREAM_END on end
3462 case Z_STREAM_ERROR:
3463 Con_Print("FS_Inflate: stream error!\n");
3466 Con_Print("FS_Inflate: data error!\n");
3469 Con_Print("FS_Inflate: mem error!\n");
3472 Con_Print("FS_Inflate: buf error!\n");
3475 Con_Print("FS_Inflate: unknown error!\n");
3479 if(ret != Z_OK && ret != Z_STREAM_END)
3481 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3482 Mem_Free(outbuf.data);
3483 qz_inflateEnd(&strm);
3486 have = sizeof(tmp) - strm.avail_out;
3487 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3488 SZ_Write(&outbuf, tmp, have);
3489 } while(ret != Z_STREAM_END);
3491 qz_inflateEnd(&strm);
3493 out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3496 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3497 Mem_Free(outbuf.data);
3501 memcpy(out, outbuf.data, outbuf.cursize);
3502 Mem_Free(outbuf.data);
3505 *inflated_size = (size_t)outbuf.cursize;