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 [] =
432 # ifdef ZLIB_USES_WINAPI
438 #elif defined(MACOSX)
452 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
460 See if zlib is available
463 qboolean FS_HasZlib(void)
468 PK3_OpenLibrary(); // to be safe
469 return (zlib_dll != 0);
475 PK3_GetEndOfCentralDir
477 Extract the end of the central directory from a PK3 package
480 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
482 fs_offset_t filesize, maxsize;
483 unsigned char *buffer, *ptr;
486 // Get the package size
487 filesize = lseek (packhandle, 0, SEEK_END);
488 if (filesize < ZIP_END_CDIR_SIZE)
491 // Load the end of the file in memory
492 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
495 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
496 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
497 lseek (packhandle, filesize - maxsize, SEEK_SET);
498 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
504 // Look for the end of central dir signature around the end of the file
505 maxsize -= ZIP_END_CDIR_SIZE;
506 ptr = &buffer[maxsize];
508 while (BuffBigLong (ptr) != ZIP_END_HEADER)
520 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
521 eocd->signature = LittleLong (eocd->signature);
522 eocd->disknum = LittleShort (eocd->disknum);
523 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
524 eocd->localentries = LittleShort (eocd->localentries);
525 eocd->nbentries = LittleShort (eocd->nbentries);
526 eocd->cdir_size = LittleLong (eocd->cdir_size);
527 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
528 eocd->comment_size = LittleShort (eocd->comment_size);
540 Extract the file list from a PK3 file
543 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
545 unsigned char *central_dir, *ptr;
547 fs_offset_t remaining;
549 // Load the central directory in memory
550 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
551 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
552 if(read (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
554 Mem_Free (central_dir);
558 // Extract the files properties
559 // The parsing is done "by hand" because some fields have variable sizes and
560 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
561 remaining = eocd->cdir_size;
564 for (ind = 0; ind < eocd->nbentries; ind++)
566 fs_offset_t namesize, count;
568 // Checking the remaining size
569 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
571 Mem_Free (central_dir);
574 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
577 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
579 Mem_Free (central_dir);
583 namesize = BuffLittleShort (&ptr[28]); // filename length
585 // Check encryption, compression, and attributes
586 // 1st uint8 : general purpose bit flag
587 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
589 // LordHavoc: bit 3 would be a problem if we were scanning the archive
590 // but is not a problem in the central directory where the values are
593 // bit 3 seems to always be set by the standard Mac OSX zip maker
595 // 2nd uint8 : external file attributes
596 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
597 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
599 // Still enough bytes for the name?
600 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
602 Mem_Free (central_dir);
606 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
607 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
609 char filename [sizeof (pack->files[0].name)];
610 fs_offset_t offset, packsize, realsize;
613 // Extract the name (strip it if necessary)
614 namesize = min(namesize, (int)sizeof (filename) - 1);
615 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
616 filename[namesize] = '\0';
618 if (BuffLittleShort (&ptr[10]))
619 flags = PACKFILE_FLAG_DEFLATED;
622 offset = BuffLittleLong (&ptr[42]);
623 packsize = BuffLittleLong (&ptr[20]);
624 realsize = BuffLittleLong (&ptr[24]);
626 switch(ptr[5]) // C_VERSION_MADE_BY_1
631 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
632 // can't use S_ISLNK here, as this has to compile on non-UNIX too
633 flags |= PACKFILE_FLAG_SYMLINK;
637 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
641 // Skip the name, additionnal field, and comment
642 // 1er uint16 : extra field length
643 // 2eme uint16 : file comment length
644 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
645 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
649 // If the package is empty, central_dir is NULL here
650 if (central_dir != NULL)
651 Mem_Free (central_dir);
652 return pack->numfiles;
660 Create a package entry associated with a PK3 file
663 pack_t *FS_LoadPackPK3 (const char *packfile)
666 pk3_endOfCentralDir_t eocd;
671 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
673 packhandle = open (packfile, O_RDONLY | O_BINARY);
678 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
680 Con_Printf ("%s is not a PK3 file\n", packfile);
685 // Multi-volume ZIP archives are NOT allowed
686 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
688 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
693 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
694 // since eocd.nbentries is an unsigned 16 bits integer
695 #if MAX_FILES_IN_PACK < 65535
696 if (eocd.nbentries > MAX_FILES_IN_PACK)
698 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
704 // Create a package structure in memory
705 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
706 pack->ignorecase = true; // PK3 ignores case
707 strlcpy (pack->filename, packfile, sizeof (pack->filename));
708 pack->handle = packhandle;
709 pack->numfiles = eocd.nbentries;
710 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
712 real_nb_files = PK3_BuildFileList (pack, &eocd);
713 if (real_nb_files < 0)
715 Con_Printf ("%s is not a valid PK3 file\n", packfile);
721 Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
728 PK3_GetTrueFileOffset
730 Find where the true file data offset is
733 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
735 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
739 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
742 // Load the local file description
743 lseek (pack->handle, pfile->offset, SEEK_SET);
744 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
745 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
747 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
751 // Skip name and extra field
752 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
754 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
760 =============================================================================
762 OTHER PRIVATE FUNCTIONS
764 =============================================================================
772 Add a file to the list of files contained into a package
775 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
776 fs_offset_t offset, fs_offset_t packsize,
777 fs_offset_t realsize, int flags)
779 int (*strcmp_funct) (const char* str1, const char* str2);
780 int left, right, middle;
783 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
785 // Look for the slot we should put that file into (binary search)
787 right = pack->numfiles - 1;
788 while (left <= right)
792 middle = (left + right) / 2;
793 diff = strcmp_funct (pack->files[middle].name, name);
795 // If we found the file, there's a problem
797 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
799 // If we're too far in the list
806 // We have to move the right of the list by one slot to free the one we need
807 pfile = &pack->files[left];
808 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
811 strlcpy (pfile->name, name, sizeof (pfile->name));
812 pfile->offset = offset;
813 pfile->packsize = packsize;
814 pfile->realsize = realsize;
815 pfile->flags = flags;
825 Only used for FS_OpenRealFile.
828 void FS_CreatePath (char *path)
832 for (ofs = path+1 ; *ofs ; ofs++)
834 if (*ofs == '/' || *ofs == '\\')
836 // create the directory
852 void FS_Path_f (void)
856 Con_Print("Current search path:\n");
857 for (s=fs_searchpaths ; s ; s=s->next)
860 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
862 Con_Printf("%s\n", s->filename);
872 /*! Takes an explicit (not game tree related) path to a pak file.
873 *Loads the header and directory, adding the files at the beginning
874 *of the list so they override previous pack files.
876 pack_t *FS_LoadPackPAK (const char *packfile)
878 dpackheader_t header;
885 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
887 packhandle = open (packfile, O_RDONLY | O_BINARY);
891 if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
893 Con_Printf ("%s is not a packfile\n", packfile);
897 if (memcmp(header.id, "PACK", 4))
899 Con_Printf ("%s is not a packfile\n", packfile);
903 header.dirofs = LittleLong (header.dirofs);
904 header.dirlen = LittleLong (header.dirlen);
906 if (header.dirlen % sizeof(dpackfile_t))
908 Con_Printf ("%s has an invalid directory size\n", packfile);
913 numpackfiles = header.dirlen / sizeof(dpackfile_t);
915 if (numpackfiles > MAX_FILES_IN_PACK)
917 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
922 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
923 lseek (packhandle, header.dirofs, SEEK_SET);
924 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
926 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
932 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
933 pack->ignorecase = false; // PAK is case sensitive
934 strlcpy (pack->filename, packfile, sizeof (pack->filename));
935 pack->handle = packhandle;
937 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
939 // parse the directory
940 for (i = 0;i < numpackfiles;i++)
942 fs_offset_t offset = LittleLong (info[i].filepos);
943 fs_offset_t size = LittleLong (info[i].filelen);
945 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
950 Con_DPrintf("Added packfile %s (%i files)\n", packfile, numpackfiles);
959 /*! Adds the given pack to the search path.
960 * The pack type is autodetected by the file extension.
962 * Returns true if the file was successfully added to the
963 * search path or if it was already included.
965 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
969 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
971 searchpath_t *search;
973 const char *ext = FS_FileExtension(pakfile);
975 for(search = fs_searchpaths; search; search = search->next)
977 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
980 *already_loaded = true;
981 return true; // already loaded
986 *already_loaded = false;
988 if(!strcasecmp(ext, "pak"))
989 pak = FS_LoadPackPAK (pakfile);
990 else if(!strcasecmp(ext, "pk3"))
991 pak = FS_LoadPackPK3 (pakfile);
993 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
997 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
998 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
1001 // find the first item whose next one is a pack or NULL
1002 searchpath_t *insertion_point = 0;
1003 if(fs_searchpaths && !fs_searchpaths->pack)
1005 insertion_point = fs_searchpaths;
1008 if(!insertion_point->next)
1010 if(insertion_point->next->pack)
1012 insertion_point = insertion_point->next;
1015 // If insertion_point is NULL, this means that either there is no
1016 // item in the list yet, or that the very first item is a pack. In
1017 // that case, we want to insert at the beginning...
1018 if(!insertion_point)
1020 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1022 search->next = fs_searchpaths;
1023 fs_searchpaths = search;
1026 // otherwise we want to append directly after insertion_point.
1028 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1030 search->next = insertion_point->next;
1031 insertion_point->next = search;
1036 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1038 search->next = fs_searchpaths;
1039 fs_searchpaths = search;
1045 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1056 /*! Adds the given pack to the search path and searches for it in the game path.
1057 * The pack type is autodetected by the file extension.
1059 * Returns true if the file was successfully added to the
1060 * search path or if it was already included.
1062 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1063 * plain directories.
1065 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1067 char fullpath[MAX_OSPATH];
1069 searchpath_t *search;
1072 *already_loaded = false;
1074 // then find the real name...
1075 search = FS_FindFile(pakfile, &index, true);
1076 if(!search || search->pack)
1078 Con_Printf("could not find pak \"%s\"\n", pakfile);
1082 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1084 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1092 Sets fs_gamedir, adds the directory to the head of the path,
1093 then loads and adds pak1.pak pak2.pak ...
1096 void FS_AddGameDirectory (const char *dir)
1100 searchpath_t *search;
1102 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1104 stringlistinit(&list);
1105 listdirectory(&list, "", dir);
1106 stringlistsort(&list);
1108 // add any PAK package in the directory
1109 for (i = 0;i < list.numstrings;i++)
1111 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1113 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1117 // add any PK3 package in the directory
1118 for (i = 0;i < list.numstrings;i++)
1120 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1122 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1126 stringlistfreecontents(&list);
1128 // Add the directory to the search path
1129 // (unpacked files have the priority over packed files)
1130 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1131 strlcpy (search->filename, dir, sizeof (search->filename));
1132 search->next = fs_searchpaths;
1133 fs_searchpaths = search;
1142 void FS_AddGameHierarchy (const char *dir)
1144 // Add the common game directory
1145 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1148 FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
1157 const char *FS_FileExtension (const char *in)
1159 const char *separator, *backslash, *colon, *dot;
1161 separator = strrchr(in, '/');
1162 backslash = strrchr(in, '\\');
1163 if (!separator || separator < backslash)
1164 separator = backslash;
1165 colon = strrchr(in, ':');
1166 if (!separator || separator < colon)
1169 dot = strrchr(in, '.');
1170 if (dot == NULL || (separator && (dot < separator)))
1182 const char *FS_FileWithoutPath (const char *in)
1184 const char *separator, *backslash, *colon;
1186 separator = strrchr(in, '/');
1187 backslash = strrchr(in, '\\');
1188 if (!separator || separator < backslash)
1189 separator = backslash;
1190 colon = strrchr(in, ':');
1191 if (!separator || separator < colon)
1193 return separator ? separator + 1 : in;
1202 void FS_ClearSearchPath (void)
1204 // unload all packs and directory information, close all pack files
1205 // (if a qfile is still reading a pack it won't be harmed because it used
1206 // dup() to get its own handle already)
1207 while (fs_searchpaths)
1209 searchpath_t *search = fs_searchpaths;
1210 fs_searchpaths = search->next;
1214 close(search->pack->handle);
1215 // free any memory associated with it
1216 if (search->pack->files)
1217 Mem_Free(search->pack->files);
1218 Mem_Free(search->pack);
1230 void FS_Rescan (void)
1233 qboolean fs_modified = false;
1234 char gamedirbuf[MAX_INPUTLINE];
1236 FS_ClearSearchPath();
1238 // add the game-specific paths
1239 // gamedirname1 (typically id1)
1240 FS_AddGameHierarchy (gamedirname1);
1241 // update the com_modname (used for server info)
1242 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1244 // add the game-specific path, if any
1245 // (only used for mission packs and the like, which should set fs_modified)
1249 FS_AddGameHierarchy (gamedirname2);
1253 // Adds basedir/gamedir as an override game
1254 // LordHavoc: now supports multiple -game directories
1255 // set the com_modname (reported in server info)
1257 for (i = 0;i < fs_numgamedirs;i++)
1260 FS_AddGameHierarchy (fs_gamedirs[i]);
1261 // update the com_modname (used server info)
1262 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1264 strlcat(gamedirbuf, va(" %s", fs_gamedirs[i]), sizeof(gamedirbuf));
1266 strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
1268 Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
1270 // set the default screenshot name to either the mod name or the
1271 // gamemode screenshot name
1272 if (strcmp(com_modname, gamedirname1))
1273 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1275 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1277 if((i = COM_CheckParm("-modname")) && i < com_argc - 1)
1278 strlcpy(com_modname, com_argv[i+1], sizeof(com_modname));
1280 // If "-condebug" is in the command line, remove the previous log file
1281 if (COM_CheckParm ("-condebug") != 0)
1282 unlink (va("%s/qconsole.log", fs_gamedir));
1284 // look for the pop.lmp file and set registered to true if it is found
1285 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1288 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1290 Con_Print("Playing shareware version.\n");
1294 Cvar_Set ("registered", "1");
1295 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1296 Con_Print("Playing registered version.\n");
1299 // unload all wads so that future queries will return the new data
1303 void FS_Rescan_f(void)
1313 extern void Host_SaveConfig (void);
1314 extern void Host_LoadConfig_f (void);
1315 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1320 if (fs_numgamedirs == numgamedirs)
1322 for (i = 0;i < numgamedirs;i++)
1323 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1325 if (i == numgamedirs)
1326 return true; // already using this set of gamedirs, do nothing
1329 if (numgamedirs > MAX_GAMEDIRS)
1332 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1333 return false; // too many gamedirs
1336 for (i = 0;i < numgamedirs;i++)
1338 // if string is nasty, reject it
1339 p = FS_CheckGameDir(gamedirs[i]);
1343 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1344 return false; // nasty gamedirs
1346 if(p == fs_checkgamedir_missing && failmissing)
1349 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1350 return false; // missing gamedirs
1356 fs_numgamedirs = numgamedirs;
1357 for (i = 0;i < fs_numgamedirs;i++)
1358 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1360 // reinitialize filesystem to detect the new paks
1363 // exec the new config
1364 Host_LoadConfig_f();
1366 // unload all sounds so they will be reloaded from the new files as needed
1367 S_UnloadAllSounds_f();
1369 // reinitialize renderer (this reloads hud/console background/etc)
1370 R_Modules_Restart();
1380 void FS_GameDir_f (void)
1384 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1388 Con_Printf("gamedirs active:");
1389 for (i = 0;i < fs_numgamedirs;i++)
1390 Con_Printf(" %s", fs_gamedirs[i]);
1395 numgamedirs = Cmd_Argc() - 1;
1396 if (numgamedirs > MAX_GAMEDIRS)
1398 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1402 for (i = 0;i < numgamedirs;i++)
1403 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1405 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1407 // actually, changing during game would work fine, but would be stupid
1408 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1412 // halt demo playback to close the file
1415 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1418 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking);
1419 static const char *FS_SysCheckGameDir(const char *gamedir)
1421 static char buf[8192];
1427 stringlistinit(&list);
1428 listdirectory(&list, gamedir, "");
1429 success = list.numstrings > 0;
1430 stringlistfreecontents(&list);
1434 f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
1437 n = FS_Read (f, buf, sizeof(buf) - 1);
1457 const char *FS_CheckGameDir(const char *gamedir)
1461 if (FS_CheckNastyPath(gamedir, true))
1464 ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
1469 // get description from basedir
1470 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1478 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1482 return fs_checkgamedir_missing;
1485 static void FS_ListGameDirs(void)
1487 stringlist_t list, list2;
1491 fs_all_gamedirs_count = 0;
1493 Mem_Free(fs_all_gamedirs);
1495 stringlistinit(&list);
1496 listdirectory(&list, va("%s/", fs_basedir), "");
1497 listdirectory(&list, va("%s/", fs_userdir), "");
1498 stringlistsort(&list);
1500 stringlistinit(&list2);
1501 for(i = 0; i < list.numstrings; ++i)
1504 if(!strcmp(list.strings[i-1], list.strings[i]))
1506 info = FS_CheckGameDir(list.strings[i]);
1509 if(info == fs_checkgamedir_missing)
1513 stringlistappend(&list2, list.strings[i]);
1515 stringlistfreecontents(&list);
1517 fs_all_gamedirs = (gamedir_t *)Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
1518 for(i = 0; i < list2.numstrings; ++i)
1520 info = FS_CheckGameDir(list2.strings[i]);
1521 // all this cannot happen any more, but better be safe than sorry
1524 if(info == fs_checkgamedir_missing)
1528 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
1529 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
1530 ++fs_all_gamedirs_count;
1544 TCHAR mydocsdir[MAX_PATH + 1];
1545 #if _MSC_VER >= 1400
1552 const char* dllnames [] =
1554 "shfolder.dll", // IE 4, or Win NT and higher
1557 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1558 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1561 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1563 // Add the personal game directory
1564 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1566 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
1568 else if(COM_CheckParm("-nohome"))
1575 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1577 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1578 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", fs_userdir);
1582 // use the environment
1583 #if _MSC_VER >= 1400
1584 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1586 homedir = getenv("USERPROFILE");
1591 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1592 #if _MSC_VER >= 1400
1595 Con_DPrintf("Obtained personal directory %s from environment\n", fs_userdir);
1600 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1602 homedir = getenv ("HOME");
1604 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/.%s/", homedir, gameuserdirname);
1607 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1611 if(!COM_CheckParm("-mygames"))
1613 #if _MSC_VER >= 1400
1615 _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!
1617 int fd = open (va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1622 *fs_userdir = 0; // we have write access to the game dir, so let's use it
1628 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1630 // If the base directory is explicitly defined by the compilation process
1631 #ifdef DP_FS_BASEDIR
1632 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1637 // FIXME: is there a better way to find the directory outside the .app?
1638 if (strstr(com_argv[0], ".app/"))
1642 split = strstr(com_argv[0], ".app/");
1643 while (split > com_argv[0] && *split != '/')
1645 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1646 fs_basedir[split - com_argv[0]] = 0;
1654 // Overrides the system supplied base directory (under GAMENAME)
1655 // 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)
1656 i = COM_CheckParm ("-basedir");
1657 if (i && i < com_argc-1)
1659 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1660 i = (int)strlen (fs_basedir);
1661 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1662 fs_basedir[i-1] = 0;
1665 // add a path separator to the end of the basedir if it lacks one
1666 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1667 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1671 p = FS_CheckGameDir(gamedirname1);
1672 if(!p || p == fs_checkgamedir_missing)
1673 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1677 p = FS_CheckGameDir(gamedirname2);
1678 if(!p || p == fs_checkgamedir_missing)
1679 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1683 // Adds basedir/gamedir as an override game
1684 // LordHavoc: now supports multiple -game directories
1685 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1689 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1692 p = FS_CheckGameDir(com_argv[i]);
1694 Sys_Error("Nasty -game name rejected: %s", com_argv[i]);
1695 if(p == fs_checkgamedir_missing)
1696 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1697 // add the gamedir to the list of active gamedirs
1698 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1703 // generate the searchpath
1707 void FS_Init_Commands(void)
1709 Cvar_RegisterVariable (&scr_screenshot_name);
1710 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1711 Cvar_RegisterVariable (&cvar_fs_gamedir);
1713 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1714 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1715 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1716 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1717 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1718 Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1726 void FS_Shutdown (void)
1728 // close all pack files and such
1729 // (hopefully there aren't any other open files, but they'll be cleaned up
1730 // by the OS anyway)
1731 FS_ClearSearchPath();
1732 Mem_FreePool (&fs_mempool);
1735 Sys_UnloadLibrary (&shfolder_dll);
1740 ====================
1743 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1744 ====================
1746 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1752 // Parse the mode string
1761 opt = O_CREAT | O_TRUNC;
1765 opt = O_CREAT | O_APPEND;
1768 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1771 for (ind = 1; mode[ind] != '\0'; ind++)
1782 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1783 filepath, mode, mode[ind]);
1790 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1791 memset (file, 0, sizeof (*file));
1794 #if _MSC_VER >= 1400
1795 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1797 file->handle = open (filepath, mod | opt, 0666);
1799 if (file->handle < 0)
1805 file->real_length = lseek (file->handle, 0, SEEK_END);
1807 // For files opened in append mode, we start at the end of the file
1809 file->position = file->real_length;
1811 lseek (file->handle, 0, SEEK_SET);
1821 Open a packed file using its package file descriptor
1824 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1830 pfile = &pack->files[pack_ind];
1832 // If we don't have the true offset, get it now
1833 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1834 if (!PK3_GetTrueFileOffset (pfile, pack))
1837 #ifndef LINK_TO_ZLIB
1838 // No Zlib DLL = no compressed files
1839 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1841 Con_Printf("WARNING: can't open the compressed file %s\n"
1842 "You need the Zlib DLL to use compressed files\n",
1848 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1849 // the dup() call to avoid having to close the dup_handle on error here
1850 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1852 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1853 pfile->name, pack->filename, (int) pfile->offset);
1857 dup_handle = dup (pack->handle);
1860 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1864 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1865 memset (file, 0, sizeof (*file));
1866 file->handle = dup_handle;
1867 file->flags = QFILE_FLAG_PACKED;
1868 file->real_length = pfile->realsize;
1869 file->offset = pfile->offset;
1873 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1877 file->flags |= QFILE_FLAG_DEFLATED;
1879 // We need some more variables
1880 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1882 ztk->comp_length = pfile->packsize;
1884 // Initialize zlib stream
1885 ztk->zstream.next_in = ztk->input;
1886 ztk->zstream.avail_in = 0;
1888 /* From Zlib's "unzip.c":
1890 * windowBits is passed < 0 to tell that there is no zlib header.
1891 * Note that in this case inflate *requires* an extra "dummy" byte
1892 * after the compressed stream in order to complete decompression and
1893 * return Z_STREAM_END.
1894 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1895 * size of both compressed and uncompressed data
1897 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1899 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1905 ztk->zstream.next_out = file->buff;
1906 ztk->zstream.avail_out = sizeof (file->buff);
1915 ====================
1918 Return true if the path should be rejected due to one of the following:
1919 1: path elements that are non-portable
1920 2: path elements that would allow access to files outside the game directory,
1921 or are just not a good idea for a mod to be using.
1922 ====================
1924 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1926 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1930 // Windows: don't allow \ in filenames (windows-only), period.
1931 // (on Windows \ is a directory separator, but / is also supported)
1932 if (strstr(path, "\\"))
1933 return 1; // non-portable
1935 // Mac: don't allow Mac-only filenames - : is a directory separator
1936 // instead of /, but we rely on / working already, so there's no reason to
1937 // support a Mac-only path
1938 // Amiga and Windows: : tries to go to root of drive
1939 if (strstr(path, ":"))
1940 return 1; // non-portable attempt to go to root of drive
1942 // Amiga: // is parent directory
1943 if (strstr(path, "//"))
1944 return 1; // non-portable attempt to go to parent directory
1946 // all: don't allow going to parent directory (../ or /../)
1947 if (strstr(path, ".."))
1948 return 2; // attempt to go outside the game directory
1950 // Windows and UNIXes: don't allow absolute paths
1952 return 2; // attempt to go outside the game directory
1954 // 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
1955 if (strchr(path, '.'))
1959 // gamedir is entirely path elements, so simply forbid . entirely
1962 if (strchr(path, '.') < strrchr(path, '/'))
1963 return 2; // possible attempt to go outside the game directory
1966 // all: forbid trailing slash on gamedir
1967 if (isgamedir && path[strlen(path)-1] == '/')
1970 // all: forbid leading dot on any filename for any reason
1971 if (strstr(path, "/."))
1972 return 2; // attempt to go outside the game directory
1974 // after all these checks we're pretty sure it's a / separated filename
1975 // and won't do much if any harm
1981 ====================
1984 Look for a file in the packages and in the filesystem
1986 Return the searchpath where the file was found (or NULL)
1987 and the file index in the package if relevant
1988 ====================
1990 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1992 searchpath_t *search;
1995 // search through the path, one element at a time
1996 for (search = fs_searchpaths;search;search = search->next)
1998 // is the element a pak file?
2001 int (*strcmp_funct) (const char* str1, const char* str2);
2002 int left, right, middle;
2005 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2007 // Look for the file (binary search)
2009 right = pak->numfiles - 1;
2010 while (left <= right)
2014 middle = (left + right) / 2;
2015 diff = strcmp_funct (pak->files[middle].name, name);
2020 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
2022 // yes, but the first one is empty so we treat it as not being there
2023 if (!quiet && developer_extra.integer)
2024 Con_DPrintf("FS_FindFile: %s is marked as deleted\n", name);
2031 if (!quiet && developer_extra.integer)
2032 Con_DPrintf("FS_FindFile: %s in %s\n",
2033 pak->files[middle].name, pak->filename);
2040 // If we're too far in the list
2049 char netpath[MAX_OSPATH];
2050 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
2051 if (FS_SysFileExists (netpath))
2053 if (!quiet && developer_extra.integer)
2054 Con_DPrintf("FS_FindFile: %s\n", netpath);
2063 if (!quiet && developer_extra.integer)
2064 Con_DPrintf("FS_FindFile: can't find %s\n", name);
2076 Look for a file in the search paths and open it in read-only mode
2079 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
2081 searchpath_t *search;
2084 search = FS_FindFile (filename, &pack_ind, quiet);
2090 // Found in the filesystem?
2093 char path [MAX_OSPATH];
2094 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
2095 return FS_SysOpen (path, "rb", nonblocking);
2098 // So, we found it in a package...
2100 // Is it a PK3 symlink?
2101 // TODO also handle directory symlinks by parsing the whole structure...
2102 // but heck, file symlinks are good enough for now
2103 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
2105 if(symlinkLevels <= 0)
2107 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
2112 char linkbuf[MAX_QPATH];
2114 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
2115 const char *mergeslash;
2120 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
2126 // Now combine the paths...
2127 mergeslash = strrchr(filename, '/');
2128 mergestart = linkbuf;
2130 mergeslash = filename;
2131 while(!strncmp(mergestart, "../", 3))
2134 while(mergeslash > filename)
2137 if(*mergeslash == '/')
2141 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
2142 if(mergeslash == filename)
2144 // Either mergeslash == filename, then we just replace the name (done below)
2148 // Or, we append the name after mergeslash;
2149 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2150 int spaceNeeded = mergeslash - filename + 1;
2151 int spaceRemoved = mergestart - linkbuf;
2152 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2154 Con_DPrintf("symlink: too long path rejected\n");
2157 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2158 memcpy(linkbuf, filename, spaceNeeded);
2159 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2160 mergestart = linkbuf;
2162 if (!quiet && developer_loading.integer)
2163 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2164 if(FS_CheckNastyPath (mergestart, false))
2166 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2169 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2173 return FS_OpenPackedFile (search->pack, pack_ind);
2178 =============================================================================
2180 MAIN PUBLIC FUNCTIONS
2182 =============================================================================
2186 ====================
2189 Open a file in the userpath. The syntax is the same as fopen
2190 Used for savegame scanning in menu, and all file writing.
2191 ====================
2193 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2195 char real_path [MAX_OSPATH];
2197 if (FS_CheckNastyPath(filepath, false))
2199 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2203 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2205 // If the file is opened in "write", "append", or "read/write" mode,
2206 // create directories up to the file.
2207 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2208 FS_CreatePath (real_path);
2209 return FS_SysOpen (real_path, mode, false);
2214 ====================
2217 Open a file. The syntax is the same as fopen
2218 ====================
2220 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2222 if (FS_CheckNastyPath(filepath, false))
2224 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2228 return FS_OpenReadFile (filepath, quiet, false, 16);
2233 ====================
2236 Open a file. The syntax is the same as fopen
2237 ====================
2239 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2242 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2243 memset (file, 0, sizeof (*file));
2244 file->flags = QFILE_FLAG_DATA;
2246 file->real_length = size;
2252 ====================
2256 ====================
2258 int FS_Close (qfile_t* file)
2260 if(file->flags & QFILE_FLAG_DATA)
2266 if (close (file->handle))
2271 qz_inflateEnd (&file->ztk->zstream);
2272 Mem_Free (file->ztk);
2281 ====================
2284 Write "datasize" bytes into a file
2285 ====================
2287 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2291 // If necessary, seek to the exact file position we're supposed to be
2292 if (file->buff_ind != file->buff_len)
2293 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2295 // Purge cached data
2298 // Write the buffer and update the position
2299 result = write (file->handle, data, (fs_offset_t)datasize);
2300 file->position = lseek (file->handle, 0, SEEK_CUR);
2301 if (file->real_length < file->position)
2302 file->real_length = file->position;
2312 ====================
2315 Read up to "buffersize" bytes from a file
2316 ====================
2318 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2320 fs_offset_t count, done;
2322 if (buffersize == 0)
2325 // Get rid of the ungetc character
2326 if (file->ungetc != EOF)
2328 ((char*)buffer)[0] = file->ungetc;
2336 if(file->flags & QFILE_FLAG_DATA)
2338 size_t left = file->real_length - file->position;
2339 if(buffersize > left)
2341 memcpy(buffer, file->data + file->position, buffersize);
2342 file->position += buffersize;
2346 // First, we copy as many bytes as we can from "buff"
2347 if (file->buff_ind < file->buff_len)
2349 count = file->buff_len - file->buff_ind;
2350 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2352 memcpy (buffer, &file->buff[file->buff_ind], count);
2353 file->buff_ind += count;
2355 buffersize -= count;
2356 if (buffersize == 0)
2360 // NOTE: at this point, the read buffer is always empty
2362 // If the file isn't compressed
2363 if (! (file->flags & QFILE_FLAG_DEFLATED))
2367 // We must take care to not read after the end of the file
2368 count = file->real_length - file->position;
2370 // If we have a lot of data to get, put them directly into "buffer"
2371 if (buffersize > sizeof (file->buff) / 2)
2373 if (count > (fs_offset_t)buffersize)
2374 count = (fs_offset_t)buffersize;
2375 lseek (file->handle, file->offset + file->position, SEEK_SET);
2376 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2380 file->position += nb;
2382 // Purge cached data
2388 if (count > (fs_offset_t)sizeof (file->buff))
2389 count = (fs_offset_t)sizeof (file->buff);
2390 lseek (file->handle, file->offset + file->position, SEEK_SET);
2391 nb = read (file->handle, file->buff, count);
2394 file->buff_len = nb;
2395 file->position += nb;
2397 // Copy the requested data in "buffer" (as much as we can)
2398 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2399 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2400 file->buff_ind = count;
2408 // If the file is compressed, it's more complicated...
2409 // We cycle through a few operations until we have read enough data
2410 while (buffersize > 0)
2412 ztoolkit_t *ztk = file->ztk;
2415 // NOTE: at this point, the read buffer is always empty
2417 // If "input" is also empty, we need to refill it
2418 if (ztk->in_ind == ztk->in_len)
2420 // If we are at the end of the file
2421 if (file->position == file->real_length)
2424 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2425 if (count > (fs_offset_t)sizeof (ztk->input))
2426 count = (fs_offset_t)sizeof (ztk->input);
2427 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2428 if (read (file->handle, ztk->input, count) != count)
2430 Con_Printf ("FS_Read: unexpected end of file\n");
2435 ztk->in_len = count;
2436 ztk->in_position += count;
2439 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2440 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2442 // Now that we are sure we have compressed data available, we need to determine
2443 // if it's better to inflate it in "file->buff" or directly in "buffer"
2445 // Inflate the data in "file->buff"
2446 if (buffersize < sizeof (file->buff) / 2)
2448 ztk->zstream.next_out = file->buff;
2449 ztk->zstream.avail_out = sizeof (file->buff);
2450 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2451 if (error != Z_OK && error != Z_STREAM_END)
2453 Con_Printf ("FS_Read: Can't inflate file\n");
2456 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2458 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2459 file->position += file->buff_len;
2461 // Copy the requested data in "buffer" (as much as we can)
2462 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2463 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2464 file->buff_ind = count;
2467 // Else, we inflate directly in "buffer"
2470 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2471 ztk->zstream.avail_out = (unsigned int)buffersize;
2472 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2473 if (error != Z_OK && error != Z_STREAM_END)
2475 Con_Printf ("FS_Read: Can't inflate file\n");
2478 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2480 // How much data did it inflate?
2481 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2482 file->position += count;
2484 // Purge cached data
2489 buffersize -= count;
2497 ====================
2500 Print a string into a file
2501 ====================
2503 int FS_Print (qfile_t* file, const char *msg)
2505 return (int)FS_Write (file, msg, strlen (msg));
2509 ====================
2512 Print a string into a file
2513 ====================
2515 int FS_Printf(qfile_t* file, const char* format, ...)
2520 va_start (args, format);
2521 result = FS_VPrintf (file, format, args);
2529 ====================
2532 Print a string into a file
2533 ====================
2535 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2538 fs_offset_t buff_size = MAX_INPUTLINE;
2543 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2544 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2545 if (len >= 0 && len < buff_size)
2547 Mem_Free (tempbuff);
2551 len = write (file->handle, tempbuff, len);
2552 Mem_Free (tempbuff);
2559 ====================
2562 Get the next character of a file
2563 ====================
2565 int FS_Getc (qfile_t* file)
2569 if (FS_Read (file, &c, 1) != 1)
2577 ====================
2580 Put a character back into the read buffer (only supports one character!)
2581 ====================
2583 int FS_UnGetc (qfile_t* file, unsigned char c)
2585 // If there's already a character waiting to be read
2586 if (file->ungetc != EOF)
2595 ====================
2598 Move the position index in a file
2599 ====================
2601 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2604 unsigned char* buffer;
2605 fs_offset_t buffersize;
2607 // Compute the file offset
2611 offset += file->position - file->buff_len + file->buff_ind;
2618 offset += file->real_length;
2624 if (offset < 0 || offset > file->real_length)
2627 if(file->flags & QFILE_FLAG_DATA)
2629 file->position = offset;
2633 // If we have the data in our read buffer, we don't need to actually seek
2634 if (file->position - file->buff_len <= offset && offset <= file->position)
2636 file->buff_ind = offset + file->buff_len - file->position;
2640 // Purge cached data
2643 // Unpacked or uncompressed files can seek directly
2644 if (! (file->flags & QFILE_FLAG_DEFLATED))
2646 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2648 file->position = offset;
2652 // Seeking in compressed files is more a hack than anything else,
2653 // but we need to support it, so here we go.
2656 // If we have to go back in the file, we need to restart from the beginning
2657 if (offset <= file->position)
2661 ztk->in_position = 0;
2663 lseek (file->handle, file->offset, SEEK_SET);
2665 // Reset the Zlib stream
2666 ztk->zstream.next_in = ztk->input;
2667 ztk->zstream.avail_in = 0;
2668 qz_inflateReset (&ztk->zstream);
2671 // We need a big buffer to force inflating into it directly
2672 buffersize = 2 * sizeof (file->buff);
2673 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2675 // Skip all data until we reach the requested offset
2676 while (offset > file->position)
2678 fs_offset_t diff = offset - file->position;
2679 fs_offset_t count, len;
2681 count = (diff > buffersize) ? buffersize : diff;
2682 len = FS_Read (file, buffer, count);
2696 ====================
2699 Give the current position in a file
2700 ====================
2702 fs_offset_t FS_Tell (qfile_t* file)
2704 return file->position - file->buff_len + file->buff_ind;
2709 ====================
2712 Give the total size of a file
2713 ====================
2715 fs_offset_t FS_FileSize (qfile_t* file)
2717 return file->real_length;
2722 ====================
2725 Erases any buffered input or output data
2726 ====================
2728 void FS_Purge (qfile_t* file)
2740 Filename are relative to the quake directory.
2741 Always appends a 0 byte.
2744 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2747 unsigned char *buf = NULL;
2748 fs_offset_t filesize = 0;
2750 file = FS_OpenVirtualFile(path, quiet);
2753 filesize = file->real_length;
2756 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2761 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2762 buf[filesize] = '\0';
2763 FS_Read (file, buf, filesize);
2765 if (developer_loadfile.integer)
2766 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2769 if (filesizepointer)
2770 *filesizepointer = filesize;
2779 The filename will be prefixed by the current game directory
2782 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2786 file = FS_OpenRealFile(filename, "wb", false);
2789 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2793 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2794 FS_Write (file, data, len);
2801 =============================================================================
2803 OTHERS PUBLIC FUNCTIONS
2805 =============================================================================
2813 void FS_StripExtension (const char *in, char *out, size_t size_out)
2821 while ((currentchar = *in) && size_out > 1)
2823 if (currentchar == '.')
2825 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2827 *out++ = currentchar;
2843 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2847 // if path doesn't have a .EXT, append extension
2848 // (extension should include the .)
2849 src = path + strlen(path) - 1;
2851 while (*src != '/' && src != path)
2854 return; // it has an extension
2858 strlcat (path, extension, size_path);
2866 Look for a file in the packages and in the filesystem
2869 int FS_FileType (const char *filename)
2871 searchpath_t *search;
2872 char fullpath[MAX_OSPATH];
2874 search = FS_FindFile (filename, NULL, true);
2876 return FS_FILETYPE_NONE;
2879 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2881 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2882 return FS_SysFileType(fullpath);
2890 Look for a file in the packages and in the filesystem
2893 qboolean FS_FileExists (const char *filename)
2895 return (FS_FindFile (filename, NULL, true) != NULL);
2903 Look for a file in the filesystem only
2906 int FS_SysFileType (const char *path)
2909 // Sajt - some older sdks are missing this define
2910 # ifndef INVALID_FILE_ATTRIBUTES
2911 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2914 DWORD result = GetFileAttributes(path);
2916 if(result == INVALID_FILE_ATTRIBUTES)
2917 return FS_FILETYPE_NONE;
2919 if(result & FILE_ATTRIBUTE_DIRECTORY)
2920 return FS_FILETYPE_DIRECTORY;
2922 return FS_FILETYPE_FILE;
2926 if (stat (path,&buf) == -1)
2927 return FS_FILETYPE_NONE;
2930 #define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
2932 if(S_ISDIR(buf.st_mode))
2933 return FS_FILETYPE_DIRECTORY;
2935 return FS_FILETYPE_FILE;
2939 qboolean FS_SysFileExists (const char *path)
2941 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2944 void FS_mkdir (const char *path)
2957 Allocate and fill a search structure with information on matching filenames.
2960 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2963 searchpath_t *searchpath;
2965 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2966 stringlist_t resultlist;
2967 stringlist_t dirlist;
2968 const char *slash, *backslash, *colon, *separator;
2970 char temp[MAX_OSPATH];
2972 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2977 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2981 stringlistinit(&resultlist);
2982 stringlistinit(&dirlist);
2984 slash = strrchr(pattern, '/');
2985 backslash = strrchr(pattern, '\\');
2986 colon = strrchr(pattern, ':');
2987 separator = max(slash, backslash);
2988 separator = max(separator, colon);
2989 basepathlength = separator ? (separator + 1 - pattern) : 0;
2990 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2992 memcpy(basepath, pattern, basepathlength);
2993 basepath[basepathlength] = 0;
2995 // search through the path, one element at a time
2996 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2998 // is the element a pak file?
2999 if (searchpath->pack)
3001 // look through all the pak file elements
3002 pak = searchpath->pack;
3003 for (i = 0;i < pak->numfiles;i++)
3005 strlcpy(temp, pak->files[i].name, sizeof(temp));
3008 if (matchpattern(temp, (char *)pattern, true))
3010 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3011 if (!strcmp(resultlist.strings[resultlistindex], temp))
3013 if (resultlistindex == resultlist.numstrings)
3015 stringlistappend(&resultlist, temp);
3016 if (!quiet && developer_loading.integer)
3017 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
3020 // strip off one path element at a time until empty
3021 // this way directories are added to the listing if they match the pattern
3022 slash = strrchr(temp, '/');
3023 backslash = strrchr(temp, '\\');
3024 colon = strrchr(temp, ':');
3026 if (separator < slash)
3028 if (separator < backslash)
3029 separator = backslash;
3030 if (separator < colon)
3032 *((char *)separator) = 0;
3038 stringlist_t matchedSet, foundSet;
3039 const char *start = pattern;
3041 stringlistinit(&matchedSet);
3042 stringlistinit(&foundSet);
3043 // add a first entry to the set
3044 stringlistappend(&matchedSet, "");
3045 // iterate through pattern's path
3048 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
3049 char subpath[MAX_OSPATH];
3050 char subpattern[MAX_OSPATH];
3052 // find the next wildcard
3053 wildcard = strchr(start, '?');
3054 asterisk = strchr(start, '*');
3055 if (asterisk && (!wildcard || asterisk < wildcard))
3057 wildcard = asterisk;
3062 nextseparator = strchr( wildcard, '/' );
3066 nextseparator = NULL;
3069 if( !nextseparator ) {
3070 nextseparator = start + strlen( start );
3073 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
3074 // copy everything up except nextseperator
3075 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
3076 // find the last '/' before the wildcard
3077 prevseparator = strrchr( subpattern, '/' );
3079 prevseparator = subpattern;
3082 // copy everything from start to the previous including the '/' (before the wildcard)
3083 // everything up to start is already included in the path of matchedSet's entries
3084 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
3086 // for each entry in matchedSet try to open the subdirectories specified in subpath
3087 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
3088 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
3089 strlcat( temp, subpath, sizeof(temp) );
3090 listdirectory( &foundSet, searchpath->filename, temp );
3092 if( dirlistindex == 0 ) {
3095 // reset the current result set
3096 stringlistfreecontents( &matchedSet );
3097 // match against the pattern
3098 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
3099 const char *direntry = foundSet.strings[ dirlistindex ];
3100 if (matchpattern(direntry, subpattern, true)) {
3101 stringlistappend( &matchedSet, direntry );
3104 stringlistfreecontents( &foundSet );
3106 start = nextseparator;
3109 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
3111 const char *temp = matchedSet.strings[dirlistindex];
3112 if (matchpattern(temp, (char *)pattern, true))
3114 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3115 if (!strcmp(resultlist.strings[resultlistindex], temp))
3117 if (resultlistindex == resultlist.numstrings)
3119 stringlistappend(&resultlist, temp);
3120 if (!quiet && developer_loading.integer)
3121 Con_Printf("SearchDirFile: %s\n", temp);
3125 stringlistfreecontents( &matchedSet );
3129 if (resultlist.numstrings)
3131 stringlistsort(&resultlist);
3132 numfiles = resultlist.numstrings;
3134 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3135 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
3136 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
3137 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
3138 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
3139 search->numfilenames = (int)numfiles;
3142 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3145 search->filenames[numfiles] = search->filenamesbuffer + numchars;
3146 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
3147 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
3149 numchars += (int)textlen;
3152 stringlistfreecontents(&resultlist);
3158 void FS_FreeSearch(fssearch_t *search)
3163 extern int con_linewidth;
3164 int FS_ListDirectory(const char *pattern, int oneperline)
3173 char linebuf[MAX_INPUTLINE];
3175 search = FS_Search(pattern, true, true);
3178 numfiles = search->numfilenames;
3181 // FIXME: the names could be added to one column list and then
3182 // gradually shifted into the next column if they fit, and then the
3183 // next to make a compact variable width listing but it's a lot more
3185 // find width for columns
3187 for (i = 0;i < numfiles;i++)
3189 l = (int)strlen(search->filenames[i]);
3190 if (columnwidth < l)
3193 // count the spacing character
3195 // calculate number of columns
3196 numcolumns = con_linewidth / columnwidth;
3197 // don't bother with the column printing if it's only one column
3198 if (numcolumns >= 2)
3200 numlines = (numfiles + numcolumns - 1) / numcolumns;
3201 for (i = 0;i < numlines;i++)
3204 for (k = 0;k < numcolumns;k++)
3206 l = i * numcolumns + k;
3209 name = search->filenames[l];
3210 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3211 linebuf[linebufpos++] = name[j];
3212 // space out name unless it's the last on the line
3213 if (k + 1 < numcolumns && l + 1 < numfiles)
3214 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3215 linebuf[linebufpos++] = ' ';
3218 linebuf[linebufpos] = 0;
3219 Con_Printf("%s\n", linebuf);
3226 for (i = 0;i < numfiles;i++)
3227 Con_Printf("%s\n", search->filenames[i]);
3228 FS_FreeSearch(search);
3229 return (int)numfiles;
3232 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3234 const char *pattern;
3237 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3240 if (Cmd_Argc() == 2)
3241 pattern = Cmd_Argv(1);
3244 if (!FS_ListDirectory(pattern, oneperline))
3245 Con_Print("No files found.\n");
3250 FS_ListDirectoryCmd("dir", true);
3255 FS_ListDirectoryCmd("ls", false);
3258 void FS_Which_f(void)
3260 const char *filename;
3263 if (Cmd_Argc() != 2)
3265 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3268 filename = Cmd_Argv(1);
3269 sp = FS_FindFile(filename, &index, true);
3271 Con_Printf("%s isn't anywhere\n", filename);
3275 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3277 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3281 const char *FS_WhichPack(const char *filename)
3284 searchpath_t *sp = FS_FindFile(filename, &index, true);
3286 return sp->pack->shortname;
3292 ====================
3293 FS_IsRegisteredQuakePack
3295 Look for a proof of purchase file file in the requested package
3297 If it is found, this file should NOT be downloaded.
3298 ====================
3300 qboolean FS_IsRegisteredQuakePack(const char *name)
3302 searchpath_t *search;
3305 // search through the path, one element at a time
3306 for (search = fs_searchpaths;search;search = search->next)
3308 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3310 int (*strcmp_funct) (const char* str1, const char* str2);
3311 int left, right, middle;
3314 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3316 // Look for the file (binary search)
3318 right = pak->numfiles - 1;
3319 while (left <= right)
3323 middle = (left + right) / 2;
3324 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3330 // If we're too far in the list
3337 // we found the requested pack but it is not registered quake
3345 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3348 unsigned char *filedata;
3349 fs_offset_t filesize;
3350 if (filesizepointer)
3351 *filesizepointer = 0;
3352 if (!filename || !*filename)
3354 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3357 if (filesizepointer)
3358 *filesizepointer = filesize;
3359 crc = CRC_Block(filedata, filesize);
3365 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3368 unsigned char *out = NULL;
3372 #ifndef LINK_TO_ZLIB
3377 memset(&strm, 0, sizeof(strm));
3378 strm.zalloc = Z_NULL;
3379 strm.zfree = Z_NULL;
3380 strm.opaque = Z_NULL;
3383 level = Z_DEFAULT_COMPRESSION;
3385 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3387 Con_Printf("FS_Deflate: deflate init error!\n");
3391 strm.next_in = (unsigned char*)data;
3392 strm.avail_in = size;
3394 tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3397 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3398 qz_deflateEnd(&strm);
3402 strm.next_out = tmp;
3403 strm.avail_out = size;
3405 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3407 Con_Printf("FS_Deflate: deflate failed!\n");
3408 qz_deflateEnd(&strm);
3413 if(qz_deflateEnd(&strm) != Z_OK)
3415 Con_Printf("FS_Deflate: deflateEnd failed\n");
3420 if(strm.total_out >= size)
3422 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3427 out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3430 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3436 *deflated_size = (size_t)strm.total_out;
3438 memcpy(out, tmp, strm.total_out);
3444 static void AssertBufsize(sizebuf_t *buf, int length)
3446 if(buf->cursize + length > buf->maxsize)
3448 int oldsize = buf->maxsize;
3449 unsigned char *olddata;
3450 olddata = buf->data;
3451 buf->maxsize += length;
3452 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3455 memcpy(buf->data, olddata, oldsize);
3461 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3465 unsigned char *out = NULL;
3466 unsigned char tmp[2048];
3471 #ifndef LINK_TO_ZLIB
3476 memset(&outbuf, 0, sizeof(outbuf));
3477 outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3478 outbuf.maxsize = sizeof(tmp);
3480 memset(&strm, 0, sizeof(strm));
3481 strm.zalloc = Z_NULL;
3482 strm.zfree = Z_NULL;
3483 strm.opaque = Z_NULL;
3485 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3487 Con_Printf("FS_Inflate: inflate init error!\n");
3488 Mem_Free(outbuf.data);
3492 strm.next_in = (unsigned char*)data;
3493 strm.avail_in = size;
3497 strm.next_out = tmp;
3498 strm.avail_out = sizeof(tmp);
3499 ret = qz_inflate(&strm, Z_NO_FLUSH);
3500 // it either returns Z_OK on progress, Z_STREAM_END on end
3508 case Z_STREAM_ERROR:
3509 Con_Print("FS_Inflate: stream error!\n");
3512 Con_Print("FS_Inflate: data error!\n");
3515 Con_Print("FS_Inflate: mem error!\n");
3518 Con_Print("FS_Inflate: buf error!\n");
3521 Con_Print("FS_Inflate: unknown error!\n");
3525 if(ret != Z_OK && ret != Z_STREAM_END)
3527 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3528 Mem_Free(outbuf.data);
3529 qz_inflateEnd(&strm);
3532 have = sizeof(tmp) - strm.avail_out;
3533 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3534 SZ_Write(&outbuf, tmp, have);
3535 } while(ret != Z_STREAM_END);
3537 qz_inflateEnd(&strm);
3539 out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3542 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3543 Mem_Free(outbuf.data);
3547 memcpy(out, outbuf.data, outbuf.cursize);
3548 Mem_Free(outbuf.data);
3551 *inflated_size = (size_t)outbuf.cursize;