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
31 // on *x, we MUST include "quakedef.h" before anything that may include IO
32 // functions to get _FILE_OFFSET_BITS
36 // include SDL for IPHONEOS code
37 # include <TargetConditionals.h>
48 # include <sys/stat.h>
55 // Win32 requires us to add O_BINARY, but the other OSes don't have it
60 // In case the system doesn't support the O_NONBLOCK flag
65 // largefile support for Win32
67 # define lseek _lseeki64
71 // suppress deprecated warnings
72 # include <sys/stat.h>
77 # define unlink _unlink
81 /** \page fs File System
83 All of Quake's data access is through a hierchal file system, but the contents
84 of the file system can be transparently merged from several sources.
86 The "base directory" is the path to the directory holding the quake.exe and
87 all game directories. The sys_* files pass this to host_init in
88 quakeparms_t->basedir. This can be overridden with the "-basedir" command
89 line parm to allow code debugging in a different directory. The base
90 directory is only used during filesystem initialization.
92 The "game directory" is the first tree on the search path and directory that
93 all generated files (savegames, screenshots, demos, config files) will be
94 saved to. This can be overridden with the "-game" command line parameter.
95 The game directory can never be changed while quake is executing. This is a
96 precaution against having a malicious server instruct clients to write files
97 over areas they shouldn't.
103 =============================================================================
107 =============================================================================
110 // Magic numbers of a ZIP file (big-endian format)
111 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
112 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
113 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
115 // Other constants for ZIP files
116 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
117 #define ZIP_END_CDIR_SIZE 22
118 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
119 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
124 #define qz_inflate inflate
125 #define qz_inflateEnd inflateEnd
126 #define qz_inflateInit2_ inflateInit2_
127 #define qz_inflateReset inflateReset
128 #define qz_deflateInit2_ deflateInit2_
129 #define qz_deflateEnd deflateEnd
130 #define qz_deflate deflate
131 #define Z_MEMLEVEL_DEFAULT 8
134 // Zlib constants (from zlib.h)
135 #define Z_SYNC_FLUSH 2
138 #define Z_STREAM_END 1
139 #define Z_STREAM_ERROR (-2)
140 #define Z_DATA_ERROR (-3)
141 #define Z_MEM_ERROR (-4)
142 #define Z_BUF_ERROR (-5)
143 #define ZLIB_VERSION "1.2.3"
147 #define Z_MEMLEVEL_DEFAULT 8
150 #define Z_DEFAULT_COMPRESSION (-1)
152 #define Z_SYNC_FLUSH 2
153 #define Z_FULL_FLUSH 3
156 // Uncomment the following line if the zlib DLL you have still uses
157 // the 1.1.x series calling convention on Win32 (WINAPI)
158 //#define ZLIB_USES_WINAPI
162 =============================================================================
166 =============================================================================
169 /*! Zlib stream (from zlib.h)
170 * \warning: some pointers we don't use directly have
171 * been cast to "void*" for a matter of simplicity
175 unsigned char *next_in; ///< next input byte
176 unsigned int avail_in; ///< number of bytes available at next_in
177 unsigned long total_in; ///< total nb of input bytes read so far
179 unsigned char *next_out; ///< next output byte should be put there
180 unsigned int avail_out; ///< remaining free space at next_out
181 unsigned long total_out; ///< total nb of bytes output so far
183 char *msg; ///< last error message, NULL if no error
184 void *state; ///< not visible by applications
186 void *zalloc; ///< used to allocate the internal state
187 void *zfree; ///< used to free the internal state
188 void *opaque; ///< private data object passed to zalloc and zfree
190 int data_type; ///< best guess about the data type: ascii or binary
191 unsigned long adler; ///< adler32 value of the uncompressed data
192 unsigned long reserved; ///< reserved for future use
197 /// inside a package (PAK or PK3)
198 #define QFILE_FLAG_PACKED (1 << 0)
199 /// file is compressed using the deflate algorithm (PK3 only)
200 #define QFILE_FLAG_DEFLATED (1 << 1)
201 /// file is actually already loaded data
202 #define QFILE_FLAG_DATA (1 << 2)
203 /// real file will be removed on close
204 #define QFILE_FLAG_REMOVE (1 << 3)
206 #define FILE_BUFF_SIZE 2048
210 size_t comp_length; ///< length of the compressed file
211 size_t in_ind, in_len; ///< input buffer current index and length
212 size_t in_position; ///< position in the compressed file
213 unsigned char input [FILE_BUFF_SIZE];
219 int handle; ///< file descriptor
220 fs_offset_t real_length; ///< uncompressed file size (for files opened in "read" mode)
221 fs_offset_t position; ///< current position in the file
222 fs_offset_t offset; ///< offset into the package (0 if external file)
223 int ungetc; ///< single stored character from ungetc, cleared to EOF when read
226 fs_offset_t buff_ind, buff_len; ///< buffer current index and length
227 unsigned char buff [FILE_BUFF_SIZE];
229 ztoolkit_t* ztk; ///< For zipped files.
231 const unsigned char *data; ///< For data files.
233 const char *filename; ///< Kept around for QFILE_FLAG_REMOVE, unused otherwise
237 // ------ PK3 files on disk ------ //
239 // You can get the complete ZIP format description from PKWARE website
241 typedef struct pk3_endOfCentralDir_s
243 unsigned int signature;
244 unsigned short disknum;
245 unsigned short cdir_disknum; ///< number of the disk with the start of the central directory
246 unsigned short localentries; ///< number of entries in the central directory on this disk
247 unsigned short nbentries; ///< total number of entries in the central directory on this disk
248 unsigned int cdir_size; ///< size of the central directory
249 unsigned int cdir_offset; ///< with respect to the starting disk number
250 unsigned short comment_size;
251 fs_offset_t prepended_garbage;
252 } pk3_endOfCentralDir_t;
255 // ------ PAK files on disk ------ //
256 typedef struct dpackfile_s
259 int filepos, filelen;
262 typedef struct dpackheader_s
270 /*! \name Packages in memory
273 /// the offset in packfile_t is the true contents offset
274 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
275 /// file compressed using the deflate algorithm
276 #define PACKFILE_FLAG_DEFLATED (1 << 1)
277 /// file is a symbolic link
278 #define PACKFILE_FLAG_SYMLINK (1 << 2)
280 typedef struct packfile_s
282 char name [MAX_QPATH];
285 fs_offset_t packsize; ///< size in the package
286 fs_offset_t realsize; ///< real file size (uncompressed)
289 typedef struct pack_s
291 char filename [MAX_OSPATH];
292 char shortname [MAX_QPATH];
294 int ignorecase; ///< PK3 ignores case
301 /// Search paths for files (including packages)
302 typedef struct searchpath_s
304 // only one of filename / pack will be used
305 char filename[MAX_OSPATH];
307 struct searchpath_s *next;
312 =============================================================================
316 =============================================================================
321 void FS_Which_f(void);
323 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
324 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
325 fs_offset_t offset, fs_offset_t packsize,
326 fs_offset_t realsize, int flags);
330 =============================================================================
334 =============================================================================
337 mempool_t *fs_mempool;
339 searchpath_t *fs_searchpaths = NULL;
340 const char *const fs_checkgamedir_missing = "missing";
342 #define MAX_FILES_IN_PACK 65536
344 char fs_userdir[MAX_OSPATH];
345 char fs_gamedir[MAX_OSPATH];
346 char fs_basedir[MAX_OSPATH];
347 static pack_t *fs_selfpack = NULL;
349 // list of active game directories (empty if not running a mod)
350 int fs_numgamedirs = 0;
351 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
353 // list of all gamedirs with modinfo.txt
354 gamedir_t *fs_all_gamedirs = NULL;
355 int fs_all_gamedirs_count = 0;
357 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)"};
358 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"};
359 cvar_t cvar_fs_gamedir = {CVAR_READONLY | CVAR_NORESETTODEFAULTS, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"};
363 =============================================================================
365 PRIVATE FUNCTIONS - PK3 HANDLING
367 =============================================================================
371 // Functions exported from zlib
372 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
373 # define ZEXPORT WINAPI
378 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
379 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
380 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
381 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
382 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
383 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
384 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
387 #define qz_inflateInit2(strm, windowBits) \
388 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
389 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
390 qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
393 // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
395 static dllfunction_t zlibfuncs[] =
397 {"inflate", (void **) &qz_inflate},
398 {"inflateEnd", (void **) &qz_inflateEnd},
399 {"inflateInit2_", (void **) &qz_inflateInit2_},
400 {"inflateReset", (void **) &qz_inflateReset},
401 {"deflateInit2_", (void **) &qz_deflateInit2_},
402 {"deflateEnd", (void **) &qz_deflateEnd},
403 {"deflate", (void **) &qz_deflate},
407 /// Handle for Zlib DLL
408 static dllhandle_t zlib_dll = NULL;
412 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
413 static dllfunction_t shfolderfuncs[] =
415 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
418 static dllhandle_t shfolder_dll = NULL;
428 void PK3_CloseLibrary (void)
431 Sys_UnloadLibrary (&zlib_dll);
440 Try to load the Zlib DLL
443 qboolean PK3_OpenLibrary (void)
448 const char* dllnames [] =
451 # ifdef ZLIB_USES_WINAPI
457 #elif defined(MACOSX)
471 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
479 See if zlib is available
482 qboolean FS_HasZlib(void)
487 PK3_OpenLibrary(); // to be safe
488 return (zlib_dll != 0);
494 PK3_GetEndOfCentralDir
496 Extract the end of the central directory from a PK3 package
499 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
501 fs_offset_t filesize, maxsize;
502 unsigned char *buffer, *ptr;
505 // Get the package size
506 filesize = lseek (packhandle, 0, SEEK_END);
507 if (filesize < ZIP_END_CDIR_SIZE)
510 // Load the end of the file in memory
511 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
514 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
515 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
516 lseek (packhandle, filesize - maxsize, SEEK_SET);
517 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
523 // Look for the end of central dir signature around the end of the file
524 maxsize -= ZIP_END_CDIR_SIZE;
525 ptr = &buffer[maxsize];
527 while (BuffBigLong (ptr) != ZIP_END_HEADER)
539 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
540 eocd->signature = LittleLong (eocd->signature);
541 eocd->disknum = LittleShort (eocd->disknum);
542 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
543 eocd->localentries = LittleShort (eocd->localentries);
544 eocd->nbentries = LittleShort (eocd->nbentries);
545 eocd->cdir_size = LittleLong (eocd->cdir_size);
546 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
547 eocd->comment_size = LittleShort (eocd->comment_size);
548 eocd->prepended_garbage = filesize - (ind + ZIP_END_CDIR_SIZE) - eocd->cdir_offset - eocd->cdir_size; // this detects "SFX" zip files
549 eocd->cdir_offset += eocd->prepended_garbage;
561 Extract the file list from a PK3 file
564 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
566 unsigned char *central_dir, *ptr;
568 fs_offset_t remaining;
570 // Load the central directory in memory
571 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
572 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
573 if(read (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
575 Mem_Free (central_dir);
579 // Extract the files properties
580 // The parsing is done "by hand" because some fields have variable sizes and
581 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
582 remaining = eocd->cdir_size;
585 for (ind = 0; ind < eocd->nbentries; ind++)
587 fs_offset_t namesize, count;
589 // Checking the remaining size
590 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
592 Mem_Free (central_dir);
595 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
598 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
600 Mem_Free (central_dir);
604 namesize = BuffLittleShort (&ptr[28]); // filename length
606 // Check encryption, compression, and attributes
607 // 1st uint8 : general purpose bit flag
608 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
610 // LordHavoc: bit 3 would be a problem if we were scanning the archive
611 // but is not a problem in the central directory where the values are
614 // bit 3 seems to always be set by the standard Mac OSX zip maker
616 // 2nd uint8 : external file attributes
617 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
618 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
620 // Still enough bytes for the name?
621 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
623 Mem_Free (central_dir);
627 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
628 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
630 char filename [sizeof (pack->files[0].name)];
631 fs_offset_t offset, packsize, realsize;
634 // Extract the name (strip it if necessary)
635 namesize = min(namesize, (int)sizeof (filename) - 1);
636 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
637 filename[namesize] = '\0';
639 if (BuffLittleShort (&ptr[10]))
640 flags = PACKFILE_FLAG_DEFLATED;
643 offset = (unsigned int)(BuffLittleLong (&ptr[42]) + eocd->prepended_garbage);
644 packsize = (unsigned int)BuffLittleLong (&ptr[20]);
645 realsize = (unsigned int)BuffLittleLong (&ptr[24]);
647 switch(ptr[5]) // C_VERSION_MADE_BY_1
652 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
653 // can't use S_ISLNK here, as this has to compile on non-UNIX too
654 flags |= PACKFILE_FLAG_SYMLINK;
658 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
662 // Skip the name, additionnal field, and comment
663 // 1er uint16 : extra field length
664 // 2eme uint16 : file comment length
665 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
666 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
670 // If the package is empty, central_dir is NULL here
671 if (central_dir != NULL)
672 Mem_Free (central_dir);
673 return pack->numfiles;
681 Create a package entry associated with a PK3 file
684 pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean silent)
686 pk3_endOfCentralDir_t eocd;
690 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
693 Con_Printf ("%s is not a PK3 file\n", packfile);
698 // Multi-volume ZIP archives are NOT allowed
699 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
701 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
706 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
707 // since eocd.nbentries is an unsigned 16 bits integer
708 #if MAX_FILES_IN_PACK < 65535
709 if (eocd.nbentries > MAX_FILES_IN_PACK)
711 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
717 // Create a package structure in memory
718 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
719 pack->ignorecase = true; // PK3 ignores case
720 strlcpy (pack->filename, packfile, sizeof (pack->filename));
721 pack->handle = packhandle;
722 pack->numfiles = eocd.nbentries;
723 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
725 real_nb_files = PK3_BuildFileList (pack, &eocd);
726 if (real_nb_files < 0)
728 Con_Printf ("%s is not a valid PK3 file\n", packfile);
734 Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
737 pack_t *FS_LoadPackPK3 (const char *packfile)
741 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
743 packhandle = open (packfile, O_RDONLY | O_BINARY);
747 return FS_LoadPackPK3FromFD(packfile, packhandle, false);
753 PK3_GetTrueFileOffset
755 Find where the true file data offset is
758 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
760 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
764 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
767 // Load the local file description
768 lseek (pack->handle, pfile->offset, SEEK_SET);
769 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
770 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
772 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
776 // Skip name and extra field
777 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
779 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
785 =============================================================================
787 OTHER PRIVATE FUNCTIONS
789 =============================================================================
797 Add a file to the list of files contained into a package
800 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
801 fs_offset_t offset, fs_offset_t packsize,
802 fs_offset_t realsize, int flags)
804 int (*strcmp_funct) (const char* str1, const char* str2);
805 int left, right, middle;
808 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
810 // Look for the slot we should put that file into (binary search)
812 right = pack->numfiles - 1;
813 while (left <= right)
817 middle = (left + right) / 2;
818 diff = strcmp_funct (pack->files[middle].name, name);
820 // If we found the file, there's a problem
822 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
824 // If we're too far in the list
831 // We have to move the right of the list by one slot to free the one we need
832 pfile = &pack->files[left];
833 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
836 strlcpy (pfile->name, name, sizeof (pfile->name));
837 pfile->offset = offset;
838 pfile->packsize = packsize;
839 pfile->realsize = realsize;
840 pfile->flags = flags;
850 Only used for FS_OpenRealFile.
853 void FS_CreatePath (char *path)
857 for (ofs = path+1 ; *ofs ; ofs++)
859 if (*ofs == '/' || *ofs == '\\')
861 // create the directory
877 void FS_Path_f (void)
881 Con_Print("Current search path:\n");
882 for (s=fs_searchpaths ; s ; s=s->next)
887 Con_Printf("%sdir (virtual pack)\n", s->pack->filename);
889 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
892 Con_Printf("%s\n", s->filename);
902 /*! Takes an explicit (not game tree related) path to a pak file.
903 *Loads the header and directory, adding the files at the beginning
904 *of the list so they override previous pack files.
906 pack_t *FS_LoadPackPAK (const char *packfile)
908 dpackheader_t header;
915 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
917 packhandle = open (packfile, O_RDONLY | O_BINARY);
921 if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
923 Con_Printf ("%s is not a packfile\n", packfile);
927 if (memcmp(header.id, "PACK", 4))
929 Con_Printf ("%s is not a packfile\n", packfile);
933 header.dirofs = LittleLong (header.dirofs);
934 header.dirlen = LittleLong (header.dirlen);
936 if (header.dirlen % sizeof(dpackfile_t))
938 Con_Printf ("%s has an invalid directory size\n", packfile);
943 numpackfiles = header.dirlen / sizeof(dpackfile_t);
945 if (numpackfiles > MAX_FILES_IN_PACK)
947 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
952 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
953 lseek (packhandle, header.dirofs, SEEK_SET);
954 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
956 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
962 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
963 pack->ignorecase = false; // PAK is case sensitive
964 strlcpy (pack->filename, packfile, sizeof (pack->filename));
965 pack->handle = packhandle;
967 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
969 // parse the directory
970 for (i = 0;i < numpackfiles;i++)
972 fs_offset_t offset = (unsigned int)LittleLong (info[i].filepos);
973 fs_offset_t size = (unsigned int)LittleLong (info[i].filelen);
975 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
980 Con_DPrintf("Added packfile %s (%i files)\n", packfile, numpackfiles);
988 Create a package entry associated with a directory file
991 pack_t *FS_LoadPackVirtual (const char *dirname)
994 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
996 pack->ignorecase = false;
997 strlcpy (pack->filename, dirname, sizeof(pack->filename));
1001 Con_DPrintf("Added packfile %s (virtual pack)\n", dirname);
1010 /*! Adds the given pack to the search path.
1011 * The pack type is autodetected by the file extension.
1013 * Returns true if the file was successfully added to the
1014 * search path or if it was already included.
1016 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1017 * plain directories.
1020 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
1022 searchpath_t *search;
1024 const char *ext = FS_FileExtension(pakfile);
1027 for(search = fs_searchpaths; search; search = search->next)
1029 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
1032 *already_loaded = true;
1033 return true; // already loaded
1038 *already_loaded = false;
1040 if(!strcasecmp(ext, "pk3dir"))
1041 pak = FS_LoadPackVirtual (pakfile);
1042 else if(!strcasecmp(ext, "pak"))
1043 pak = FS_LoadPackPAK (pakfile);
1044 else if(!strcasecmp(ext, "pk3"))
1045 pak = FS_LoadPackPK3 (pakfile);
1047 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
1051 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
1053 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
1056 // find the first item whose next one is a pack or NULL
1057 searchpath_t *insertion_point = 0;
1058 if(fs_searchpaths && !fs_searchpaths->pack)
1060 insertion_point = fs_searchpaths;
1063 if(!insertion_point->next)
1065 if(insertion_point->next->pack)
1067 insertion_point = insertion_point->next;
1070 // If insertion_point is NULL, this means that either there is no
1071 // item in the list yet, or that the very first item is a pack. In
1072 // that case, we want to insert at the beginning...
1073 if(!insertion_point)
1075 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1076 search->next = fs_searchpaths;
1077 fs_searchpaths = search;
1080 // otherwise we want to append directly after insertion_point.
1082 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1083 search->next = insertion_point->next;
1084 insertion_point->next = search;
1089 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1090 search->next = fs_searchpaths;
1091 fs_searchpaths = search;
1096 dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile);
1097 // if shortname ends with "pk3dir", strip that suffix to make it just "pk3"
1098 // same goes for the name inside the pack structure
1099 l = strlen(pak->shortname);
1101 if(!strcasecmp(pak->shortname + l - 7, ".pk3dir"))
1102 pak->shortname[l - 3] = 0;
1103 l = strlen(pak->filename);
1105 if(!strcasecmp(pak->filename + l - 7, ".pk3dir"))
1106 pak->filename[l - 3] = 0;
1112 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1123 /*! Adds the given pack to the search path and searches for it in the game path.
1124 * The pack type is autodetected by the file extension.
1126 * Returns true if the file was successfully added to the
1127 * search path or if it was already included.
1129 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1130 * plain directories.
1132 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1134 char fullpath[MAX_OSPATH];
1136 searchpath_t *search;
1139 *already_loaded = false;
1141 // then find the real name...
1142 search = FS_FindFile(pakfile, &index, true);
1143 if(!search || search->pack)
1145 Con_Printf("could not find pak \"%s\"\n", pakfile);
1149 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1151 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1159 Sets fs_gamedir, adds the directory to the head of the path,
1160 then loads and adds pak1.pak pak2.pak ...
1163 void FS_AddGameDirectory (const char *dir)
1167 searchpath_t *search;
1169 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1171 stringlistinit(&list);
1172 listdirectory(&list, "", dir);
1173 stringlistsort(&list);
1175 // add any PAK package in the directory
1176 for (i = 0;i < list.numstrings;i++)
1178 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1180 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1184 // add any PK3 package in the directory
1185 for (i = 0;i < list.numstrings;i++)
1187 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir"))
1189 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1193 stringlistfreecontents(&list);
1195 // Add the directory to the search path
1196 // (unpacked files have the priority over packed files)
1197 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1198 strlcpy (search->filename, dir, sizeof (search->filename));
1199 search->next = fs_searchpaths;
1200 fs_searchpaths = search;
1209 void FS_AddGameHierarchy (const char *dir)
1211 // Add the common game directory
1212 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1215 FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
1224 const char *FS_FileExtension (const char *in)
1226 const char *separator, *backslash, *colon, *dot;
1228 separator = strrchr(in, '/');
1229 backslash = strrchr(in, '\\');
1230 if (!separator || separator < backslash)
1231 separator = backslash;
1232 colon = strrchr(in, ':');
1233 if (!separator || separator < colon)
1236 dot = strrchr(in, '.');
1237 if (dot == NULL || (separator && (dot < separator)))
1249 const char *FS_FileWithoutPath (const char *in)
1251 const char *separator, *backslash, *colon;
1253 separator = strrchr(in, '/');
1254 backslash = strrchr(in, '\\');
1255 if (!separator || separator < backslash)
1256 separator = backslash;
1257 colon = strrchr(in, ':');
1258 if (!separator || separator < colon)
1260 return separator ? separator + 1 : in;
1269 void FS_ClearSearchPath (void)
1271 // unload all packs and directory information, close all pack files
1272 // (if a qfile is still reading a pack it won't be harmed because it used
1273 // dup() to get its own handle already)
1274 while (fs_searchpaths)
1276 searchpath_t *search = fs_searchpaths;
1277 fs_searchpaths = search->next;
1278 if (search->pack && search->pack != fs_selfpack)
1280 if(!search->pack->vpack)
1283 close(search->pack->handle);
1284 // free any memory associated with it
1285 if (search->pack->files)
1286 Mem_Free(search->pack->files);
1288 Mem_Free(search->pack);
1294 static void FS_AddSelfPack(void)
1298 searchpath_t *search;
1299 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1300 search->next = fs_searchpaths;
1301 search->pack = fs_selfpack;
1302 fs_searchpaths = search;
1312 void FS_Rescan (void)
1315 qboolean fs_modified = false;
1316 qboolean reset = false;
1317 char gamedirbuf[MAX_INPUTLINE];
1321 FS_ClearSearchPath();
1323 // automatically activate gamemode for the gamedirs specified
1325 COM_ChangeGameTypeForGameDirs();
1327 // add the game-specific paths
1328 // gamedirname1 (typically id1)
1329 FS_AddGameHierarchy (gamedirname1);
1330 // update the com_modname (used for server info)
1331 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1333 // add the game-specific path, if any
1334 // (only used for mission packs and the like, which should set fs_modified)
1338 FS_AddGameHierarchy (gamedirname2);
1342 // Adds basedir/gamedir as an override game
1343 // LordHavoc: now supports multiple -game directories
1344 // set the com_modname (reported in server info)
1346 for (i = 0;i < fs_numgamedirs;i++)
1349 FS_AddGameHierarchy (fs_gamedirs[i]);
1350 // update the com_modname (used server info)
1351 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1353 strlcat(gamedirbuf, va(" %s", fs_gamedirs[i]), sizeof(gamedirbuf));
1355 strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
1357 Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
1359 // add back the selfpack as new first item
1362 // set the default screenshot name to either the mod name or the
1363 // gamemode screenshot name
1364 if (strcmp(com_modname, gamedirname1))
1365 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1367 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1369 if((i = COM_CheckParm("-modname")) && i < com_argc - 1)
1370 strlcpy(com_modname, com_argv[i+1], sizeof(com_modname));
1372 // If "-condebug" is in the command line, remove the previous log file
1373 if (COM_CheckParm ("-condebug") != 0)
1374 unlink (va("%s/qconsole.log", fs_gamedir));
1376 // look for the pop.lmp file and set registered to true if it is found
1377 if (FS_FileExists("gfx/pop.lmp"))
1378 Cvar_Set ("registered", "1");
1384 if (!registered.integer)
1387 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1389 Con_Print("Playing shareware version.\n");
1392 Con_Print("Playing registered version.\n");
1394 case GAME_STEELSTORM:
1395 if (registered.integer)
1396 Con_Print("Playing registered version.\n");
1398 Con_Print("Playing shareware version.\n");
1404 // unload all wads so that future queries will return the new data
1408 void FS_Rescan_f(void)
1418 extern void Host_SaveConfig (void);
1419 extern void Host_LoadConfig_f (void);
1420 extern qboolean vid_opened;
1421 extern void VID_Stop(void);
1422 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1427 if (fs_numgamedirs == numgamedirs)
1429 for (i = 0;i < numgamedirs;i++)
1430 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1432 if (i == numgamedirs)
1433 return true; // already using this set of gamedirs, do nothing
1436 if (numgamedirs > MAX_GAMEDIRS)
1439 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1440 return false; // too many gamedirs
1443 for (i = 0;i < numgamedirs;i++)
1445 // if string is nasty, reject it
1446 p = FS_CheckGameDir(gamedirs[i]);
1450 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1451 return false; // nasty gamedirs
1453 if(p == fs_checkgamedir_missing && failmissing)
1456 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1457 return false; // missing gamedirs
1463 fs_numgamedirs = numgamedirs;
1464 for (i = 0;i < fs_numgamedirs;i++)
1465 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1467 // reinitialize filesystem to detect the new paks
1470 if (cls.demoplayback)
1476 // unload all sounds so they will be reloaded from the new files as needed
1477 S_UnloadAllSounds_f();
1479 // close down the video subsystem, it will start up again when the config finishes...
1483 // restart the video subsystem after the config is executed
1484 Cbuf_InsertText("\nloadconfig\nvid_restart\n\n");
1494 void FS_GameDir_f (void)
1498 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1502 Con_Printf("gamedirs active:");
1503 for (i = 0;i < fs_numgamedirs;i++)
1504 Con_Printf(" %s", fs_gamedirs[i]);
1509 numgamedirs = Cmd_Argc() - 1;
1510 if (numgamedirs > MAX_GAMEDIRS)
1512 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1516 for (i = 0;i < numgamedirs;i++)
1517 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1519 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1521 // actually, changing during game would work fine, but would be stupid
1522 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1526 // halt demo playback to close the file
1529 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1532 static const char *FS_SysCheckGameDir(const char *gamedir)
1534 static char buf[8192];
1540 stringlistinit(&list);
1541 listdirectory(&list, gamedir, "");
1542 success = list.numstrings > 0;
1543 stringlistfreecontents(&list);
1547 f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
1550 n = FS_Read (f, buf, sizeof(buf) - 1);
1570 const char *FS_CheckGameDir(const char *gamedir)
1574 if (FS_CheckNastyPath(gamedir, true))
1577 ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
1582 // get description from basedir
1583 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1591 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1595 return fs_checkgamedir_missing;
1598 static void FS_ListGameDirs(void)
1600 stringlist_t list, list2;
1604 fs_all_gamedirs_count = 0;
1606 Mem_Free(fs_all_gamedirs);
1608 stringlistinit(&list);
1609 listdirectory(&list, va("%s/", fs_basedir), "");
1610 listdirectory(&list, va("%s/", fs_userdir), "");
1611 stringlistsort(&list);
1613 stringlistinit(&list2);
1614 for(i = 0; i < list.numstrings; ++i)
1617 if(!strcmp(list.strings[i-1], list.strings[i]))
1619 info = FS_CheckGameDir(list.strings[i]);
1622 if(info == fs_checkgamedir_missing)
1626 stringlistappend(&list2, list.strings[i]);
1628 stringlistfreecontents(&list);
1630 fs_all_gamedirs = (gamedir_t *)Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
1631 for(i = 0; i < list2.numstrings; ++i)
1633 info = FS_CheckGameDir(list2.strings[i]);
1634 // all this cannot happen any more, but better be safe than sorry
1637 if(info == fs_checkgamedir_missing)
1641 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
1642 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
1643 ++fs_all_gamedirs_count;
1652 void FS_Init_SelfPack (void)
1655 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1658 fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
1664 buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL);
1667 const char **new_argv;
1669 int args_left = 256;
1670 new_argv = (const char **)Mem_Alloc(fs_mempool, sizeof(*com_argv) * (com_argc + args_left + 2));
1673 new_argv[0] = "dummy";
1678 memcpy((char *)(&new_argv[0]), &com_argv[0], sizeof(*com_argv) * com_argc);
1681 while(COM_ParseToken_Console(&p))
1685 q = (char *)Mem_Alloc(fs_mempool, strlen(com_token) + 1);
1686 strlcpy(q, com_token, strlen(com_token) + 1);
1687 new_argv[com_argc + i] = q;
1690 new_argv[i+com_argc] = NULL;
1691 com_argv = new_argv;
1692 com_argc = com_argc + i;
1709 TCHAR mydocsdir[MAX_PATH + 1];
1710 #if _MSC_VER >= 1400
1714 #ifndef __IPHONEOS__
1719 const char* dllnames [] =
1721 "shfolder.dll", // IE 4, or Win NT and higher
1724 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1725 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1733 // fs_basedir is "" by default, to utilize this you can simply add your gamedir to the Resources in xcode
1734 // fs_userdir stores configurations to the Documents folder of the app
1735 strlcpy(fs_userdir, "../Documents/", sizeof(fs_userdir));
1737 // Add the personal game directory
1738 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1740 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
1742 else if(COM_CheckParm("-nohome"))
1749 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1751 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1752 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", fs_userdir);
1756 // use the environment
1757 #if _MSC_VER >= 1400
1758 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1760 homedir = getenv("USERPROFILE");
1765 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1766 #if _MSC_VER >= 1400
1769 Con_DPrintf("Obtained personal directory %s from environment\n", fs_userdir);
1774 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1776 homedir = getenv ("HOME");
1778 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/.%s/", homedir, gameuserdirname);
1781 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1785 if(!COM_CheckParm("-mygames"))
1787 #if _MSC_VER >= 1400
1789 _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!
1791 int fd = open (va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1796 *fs_userdir = 0; // we have write access to the game dir, so let's use it
1802 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1804 // If the base directory is explicitly defined by the compilation process
1805 #ifdef DP_FS_BASEDIR
1806 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1811 // FIXME: is there a better way to find the directory outside the .app?
1812 if (strstr(com_argv[0], ".app/"))
1816 split = strstr(com_argv[0], ".app/");
1817 while (split > com_argv[0] && *split != '/')
1819 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1820 fs_basedir[split - com_argv[0]] = 0;
1827 // Overrides the system supplied base directory (under GAMENAME)
1828 // 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)
1829 i = COM_CheckParm ("-basedir");
1830 if (i && i < com_argc-1)
1832 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1833 i = (int)strlen (fs_basedir);
1834 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1835 fs_basedir[i-1] = 0;
1838 // add a path separator to the end of the basedir if it lacks one
1839 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1840 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1844 p = FS_CheckGameDir(gamedirname1);
1845 if(!p || p == fs_checkgamedir_missing)
1846 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1850 p = FS_CheckGameDir(gamedirname2);
1851 if(!p || p == fs_checkgamedir_missing)
1852 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1856 // Adds basedir/gamedir as an override game
1857 // LordHavoc: now supports multiple -game directories
1858 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1862 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1865 p = FS_CheckGameDir(com_argv[i]);
1867 Sys_Error("Nasty -game name rejected: %s", com_argv[i]);
1868 if(p == fs_checkgamedir_missing)
1869 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1870 // add the gamedir to the list of active gamedirs
1871 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1876 // generate the searchpath
1880 void FS_Init_Commands(void)
1882 Cvar_RegisterVariable (&scr_screenshot_name);
1883 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1884 Cvar_RegisterVariable (&cvar_fs_gamedir);
1886 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1887 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1888 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1889 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1890 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1891 Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1899 void FS_Shutdown (void)
1901 // close all pack files and such
1902 // (hopefully there aren't any other open files, but they'll be cleaned up
1903 // by the OS anyway)
1904 FS_ClearSearchPath();
1905 Mem_FreePool (&fs_mempool);
1908 Sys_UnloadLibrary (&shfolder_dll);
1912 int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
1918 // Parse the mode string
1927 opt = O_CREAT | O_TRUNC;
1931 opt = O_CREAT | O_APPEND;
1934 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1937 for (ind = 1; mode[ind] != '\0'; ind++)
1948 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1949 filepath, mode, mode[ind]);
1956 #if _MSC_VER >= 1400
1957 _sopen_s(&handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1959 handle = open (filepath, mod | opt, 0666);
1965 ====================
1968 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1969 ====================
1971 qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1975 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1977 file->handle = FS_SysOpenFD(filepath, mode, nonblocking);
1978 if (file->handle < 0)
1984 file->filename = Mem_strdup(fs_mempool, filepath);
1986 file->real_length = lseek (file->handle, 0, SEEK_END);
1988 // For files opened in append mode, we start at the end of the file
1990 file->position = file->real_length;
1992 lseek (file->handle, 0, SEEK_SET);
2002 Open a packed file using its package file descriptor
2005 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
2011 pfile = &pack->files[pack_ind];
2013 // If we don't have the true offset, get it now
2014 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
2015 if (!PK3_GetTrueFileOffset (pfile, pack))
2018 #ifndef LINK_TO_ZLIB
2019 // No Zlib DLL = no compressed files
2020 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
2022 Con_Printf("WARNING: can't open the compressed file %s\n"
2023 "You need the Zlib DLL to use compressed files\n",
2029 // LordHavoc: lseek affects all duplicates of a handle so we do it before
2030 // the dup() call to avoid having to close the dup_handle on error here
2031 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
2033 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %08x%08x)\n",
2034 pfile->name, pack->filename, (unsigned int)(pfile->offset >> 32), (unsigned int)(pfile->offset));
2038 dup_handle = dup (pack->handle);
2041 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
2045 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2046 memset (file, 0, sizeof (*file));
2047 file->handle = dup_handle;
2048 file->flags = QFILE_FLAG_PACKED;
2049 file->real_length = pfile->realsize;
2050 file->offset = pfile->offset;
2054 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
2058 file->flags |= QFILE_FLAG_DEFLATED;
2060 // We need some more variables
2061 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
2063 ztk->comp_length = pfile->packsize;
2065 // Initialize zlib stream
2066 ztk->zstream.next_in = ztk->input;
2067 ztk->zstream.avail_in = 0;
2069 /* From Zlib's "unzip.c":
2071 * windowBits is passed < 0 to tell that there is no zlib header.
2072 * Note that in this case inflate *requires* an extra "dummy" byte
2073 * after the compressed stream in order to complete decompression and
2074 * return Z_STREAM_END.
2075 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
2076 * size of both compressed and uncompressed data
2078 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
2080 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
2086 ztk->zstream.next_out = file->buff;
2087 ztk->zstream.avail_out = sizeof (file->buff);
2096 ====================
2099 Return true if the path should be rejected due to one of the following:
2100 1: path elements that are non-portable
2101 2: path elements that would allow access to files outside the game directory,
2102 or are just not a good idea for a mod to be using.
2103 ====================
2105 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
2107 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
2111 // Windows: don't allow \ in filenames (windows-only), period.
2112 // (on Windows \ is a directory separator, but / is also supported)
2113 if (strstr(path, "\\"))
2114 return 1; // non-portable
2116 // Mac: don't allow Mac-only filenames - : is a directory separator
2117 // instead of /, but we rely on / working already, so there's no reason to
2118 // support a Mac-only path
2119 // Amiga and Windows: : tries to go to root of drive
2120 if (strstr(path, ":"))
2121 return 1; // non-portable attempt to go to root of drive
2123 // Amiga: // is parent directory
2124 if (strstr(path, "//"))
2125 return 1; // non-portable attempt to go to parent directory
2127 // all: don't allow going to parent directory (../ or /../)
2128 if (strstr(path, ".."))
2129 return 2; // attempt to go outside the game directory
2131 // Windows and UNIXes: don't allow absolute paths
2133 return 2; // attempt to go outside the game directory
2135 // 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
2136 if (strchr(path, '.'))
2140 // gamedir is entirely path elements, so simply forbid . entirely
2143 if (strchr(path, '.') < strrchr(path, '/'))
2144 return 2; // possible attempt to go outside the game directory
2147 // all: forbid trailing slash on gamedir
2148 if (isgamedir && path[strlen(path)-1] == '/')
2151 // all: forbid leading dot on any filename for any reason
2152 if (strstr(path, "/."))
2153 return 2; // attempt to go outside the game directory
2155 // after all these checks we're pretty sure it's a / separated filename
2156 // and won't do much if any harm
2162 ====================
2165 Look for a file in the packages and in the filesystem
2167 Return the searchpath where the file was found (or NULL)
2168 and the file index in the package if relevant
2169 ====================
2171 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
2173 searchpath_t *search;
2176 // search through the path, one element at a time
2177 for (search = fs_searchpaths;search;search = search->next)
2179 // is the element a pak file?
2180 if (search->pack && !search->pack->vpack)
2182 int (*strcmp_funct) (const char* str1, const char* str2);
2183 int left, right, middle;
2186 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2188 // Look for the file (binary search)
2190 right = pak->numfiles - 1;
2191 while (left <= right)
2195 middle = (left + right) / 2;
2196 diff = strcmp_funct (pak->files[middle].name, name);
2201 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
2203 // yes, but the first one is empty so we treat it as not being there
2204 if (!quiet && developer_extra.integer)
2205 Con_DPrintf("FS_FindFile: %s is marked as deleted\n", name);
2212 if (!quiet && developer_extra.integer)
2213 Con_DPrintf("FS_FindFile: %s in %s\n",
2214 pak->files[middle].name, pak->filename);
2221 // If we're too far in the list
2230 char netpath[MAX_OSPATH];
2231 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
2232 if (FS_SysFileExists (netpath))
2234 if (!quiet && developer_extra.integer)
2235 Con_DPrintf("FS_FindFile: %s\n", netpath);
2244 if (!quiet && developer_extra.integer)
2245 Con_DPrintf("FS_FindFile: can't find %s\n", name);
2257 Look for a file in the search paths and open it in read-only mode
2260 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
2262 searchpath_t *search;
2265 search = FS_FindFile (filename, &pack_ind, quiet);
2271 // Found in the filesystem?
2274 // this works with vpacks, so we are fine
2275 char path [MAX_OSPATH];
2276 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
2277 return FS_SysOpen (path, "rb", nonblocking);
2280 // So, we found it in a package...
2282 // Is it a PK3 symlink?
2283 // TODO also handle directory symlinks by parsing the whole structure...
2284 // but heck, file symlinks are good enough for now
2285 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
2287 if(symlinkLevels <= 0)
2289 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
2294 char linkbuf[MAX_QPATH];
2296 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
2297 const char *mergeslash;
2302 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
2308 // Now combine the paths...
2309 mergeslash = strrchr(filename, '/');
2310 mergestart = linkbuf;
2312 mergeslash = filename;
2313 while(!strncmp(mergestart, "../", 3))
2316 while(mergeslash > filename)
2319 if(*mergeslash == '/')
2323 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
2324 if(mergeslash == filename)
2326 // Either mergeslash == filename, then we just replace the name (done below)
2330 // Or, we append the name after mergeslash;
2331 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2332 int spaceNeeded = mergeslash - filename + 1;
2333 int spaceRemoved = mergestart - linkbuf;
2334 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2336 Con_DPrintf("symlink: too long path rejected\n");
2339 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2340 memcpy(linkbuf, filename, spaceNeeded);
2341 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2342 mergestart = linkbuf;
2344 if (!quiet && developer_loading.integer)
2345 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2346 if(FS_CheckNastyPath (mergestart, false))
2348 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2351 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2355 return FS_OpenPackedFile (search->pack, pack_ind);
2360 =============================================================================
2362 MAIN PUBLIC FUNCTIONS
2364 =============================================================================
2368 ====================
2371 Open a file in the userpath. The syntax is the same as fopen
2372 Used for savegame scanning in menu, and all file writing.
2373 ====================
2375 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2377 char real_path [MAX_OSPATH];
2379 if (FS_CheckNastyPath(filepath, false))
2381 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2385 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath); // this is never a vpack
2387 // If the file is opened in "write", "append", or "read/write" mode,
2388 // create directories up to the file.
2389 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2390 FS_CreatePath (real_path);
2391 return FS_SysOpen (real_path, mode, false);
2396 ====================
2399 Open a file. The syntax is the same as fopen
2400 ====================
2402 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2404 if (FS_CheckNastyPath(filepath, false))
2406 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2410 return FS_OpenReadFile (filepath, quiet, false, 16);
2415 ====================
2418 Open a file. The syntax is the same as fopen
2419 ====================
2421 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2424 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2425 memset (file, 0, sizeof (*file));
2426 file->flags = QFILE_FLAG_DATA;
2428 file->real_length = size;
2434 ====================
2438 ====================
2440 int FS_Close (qfile_t* file)
2442 if(file->flags & QFILE_FLAG_DATA)
2448 if (close (file->handle))
2453 if (file->flags & QFILE_FLAG_REMOVE)
2454 remove(file->filename);
2456 Mem_Free((void *) file->filename);
2461 qz_inflateEnd (&file->ztk->zstream);
2462 Mem_Free (file->ztk);
2469 void FS_RemoveOnClose(qfile_t* file)
2471 file->flags |= QFILE_FLAG_REMOVE;
2475 ====================
2478 Write "datasize" bytes into a file
2479 ====================
2481 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2485 // If necessary, seek to the exact file position we're supposed to be
2486 if (file->buff_ind != file->buff_len)
2487 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2489 // Purge cached data
2492 // Write the buffer and update the position
2493 result = write (file->handle, data, (fs_offset_t)datasize);
2494 file->position = lseek (file->handle, 0, SEEK_CUR);
2495 if (file->real_length < file->position)
2496 file->real_length = file->position;
2506 ====================
2509 Read up to "buffersize" bytes from a file
2510 ====================
2512 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2514 fs_offset_t count, done;
2516 if (buffersize == 0)
2519 // Get rid of the ungetc character
2520 if (file->ungetc != EOF)
2522 ((char*)buffer)[0] = file->ungetc;
2530 if(file->flags & QFILE_FLAG_DATA)
2532 size_t left = file->real_length - file->position;
2533 if(buffersize > left)
2535 memcpy(buffer, file->data + file->position, buffersize);
2536 file->position += buffersize;
2540 // First, we copy as many bytes as we can from "buff"
2541 if (file->buff_ind < file->buff_len)
2543 count = file->buff_len - file->buff_ind;
2544 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2546 memcpy (buffer, &file->buff[file->buff_ind], count);
2547 file->buff_ind += count;
2549 buffersize -= count;
2550 if (buffersize == 0)
2554 // NOTE: at this point, the read buffer is always empty
2556 // If the file isn't compressed
2557 if (! (file->flags & QFILE_FLAG_DEFLATED))
2561 // We must take care to not read after the end of the file
2562 count = file->real_length - file->position;
2564 // If we have a lot of data to get, put them directly into "buffer"
2565 if (buffersize > sizeof (file->buff) / 2)
2567 if (count > (fs_offset_t)buffersize)
2568 count = (fs_offset_t)buffersize;
2569 lseek (file->handle, file->offset + file->position, SEEK_SET);
2570 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2574 file->position += nb;
2576 // Purge cached data
2582 if (count > (fs_offset_t)sizeof (file->buff))
2583 count = (fs_offset_t)sizeof (file->buff);
2584 lseek (file->handle, file->offset + file->position, SEEK_SET);
2585 nb = read (file->handle, file->buff, count);
2588 file->buff_len = nb;
2589 file->position += nb;
2591 // Copy the requested data in "buffer" (as much as we can)
2592 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2593 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2594 file->buff_ind = count;
2602 // If the file is compressed, it's more complicated...
2603 // We cycle through a few operations until we have read enough data
2604 while (buffersize > 0)
2606 ztoolkit_t *ztk = file->ztk;
2609 // NOTE: at this point, the read buffer is always empty
2611 // If "input" is also empty, we need to refill it
2612 if (ztk->in_ind == ztk->in_len)
2614 // If we are at the end of the file
2615 if (file->position == file->real_length)
2618 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2619 if (count > (fs_offset_t)sizeof (ztk->input))
2620 count = (fs_offset_t)sizeof (ztk->input);
2621 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2622 if (read (file->handle, ztk->input, count) != count)
2624 Con_Printf ("FS_Read: unexpected end of file\n");
2629 ztk->in_len = count;
2630 ztk->in_position += count;
2633 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2634 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2636 // Now that we are sure we have compressed data available, we need to determine
2637 // if it's better to inflate it in "file->buff" or directly in "buffer"
2639 // Inflate the data in "file->buff"
2640 if (buffersize < sizeof (file->buff) / 2)
2642 ztk->zstream.next_out = file->buff;
2643 ztk->zstream.avail_out = sizeof (file->buff);
2644 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2645 if (error != Z_OK && error != Z_STREAM_END)
2647 Con_Printf ("FS_Read: Can't inflate file\n");
2650 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2652 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2653 file->position += file->buff_len;
2655 // Copy the requested data in "buffer" (as much as we can)
2656 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2657 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2658 file->buff_ind = count;
2661 // Else, we inflate directly in "buffer"
2664 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2665 ztk->zstream.avail_out = (unsigned int)buffersize;
2666 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2667 if (error != Z_OK && error != Z_STREAM_END)
2669 Con_Printf ("FS_Read: Can't inflate file\n");
2672 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2674 // How much data did it inflate?
2675 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2676 file->position += count;
2678 // Purge cached data
2683 buffersize -= count;
2691 ====================
2694 Print a string into a file
2695 ====================
2697 int FS_Print (qfile_t* file, const char *msg)
2699 return (int)FS_Write (file, msg, strlen (msg));
2703 ====================
2706 Print a string into a file
2707 ====================
2709 int FS_Printf(qfile_t* file, const char* format, ...)
2714 va_start (args, format);
2715 result = FS_VPrintf (file, format, args);
2723 ====================
2726 Print a string into a file
2727 ====================
2729 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2732 fs_offset_t buff_size = MAX_INPUTLINE;
2737 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2738 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2739 if (len >= 0 && len < buff_size)
2741 Mem_Free (tempbuff);
2745 len = write (file->handle, tempbuff, len);
2746 Mem_Free (tempbuff);
2753 ====================
2756 Get the next character of a file
2757 ====================
2759 int FS_Getc (qfile_t* file)
2763 if (FS_Read (file, &c, 1) != 1)
2771 ====================
2774 Put a character back into the read buffer (only supports one character!)
2775 ====================
2777 int FS_UnGetc (qfile_t* file, unsigned char c)
2779 // If there's already a character waiting to be read
2780 if (file->ungetc != EOF)
2789 ====================
2792 Move the position index in a file
2793 ====================
2795 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2798 unsigned char* buffer;
2799 fs_offset_t buffersize;
2801 // Compute the file offset
2805 offset += file->position - file->buff_len + file->buff_ind;
2812 offset += file->real_length;
2818 if (offset < 0 || offset > file->real_length)
2821 if(file->flags & QFILE_FLAG_DATA)
2823 file->position = offset;
2827 // If we have the data in our read buffer, we don't need to actually seek
2828 if (file->position - file->buff_len <= offset && offset <= file->position)
2830 file->buff_ind = offset + file->buff_len - file->position;
2834 // Purge cached data
2837 // Unpacked or uncompressed files can seek directly
2838 if (! (file->flags & QFILE_FLAG_DEFLATED))
2840 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2842 file->position = offset;
2846 // Seeking in compressed files is more a hack than anything else,
2847 // but we need to support it, so here we go.
2850 // If we have to go back in the file, we need to restart from the beginning
2851 if (offset <= file->position)
2855 ztk->in_position = 0;
2857 lseek (file->handle, file->offset, SEEK_SET);
2859 // Reset the Zlib stream
2860 ztk->zstream.next_in = ztk->input;
2861 ztk->zstream.avail_in = 0;
2862 qz_inflateReset (&ztk->zstream);
2865 // We need a big buffer to force inflating into it directly
2866 buffersize = 2 * sizeof (file->buff);
2867 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2869 // Skip all data until we reach the requested offset
2870 while (offset > file->position)
2872 fs_offset_t diff = offset - file->position;
2873 fs_offset_t count, len;
2875 count = (diff > buffersize) ? buffersize : diff;
2876 len = FS_Read (file, buffer, count);
2890 ====================
2893 Give the current position in a file
2894 ====================
2896 fs_offset_t FS_Tell (qfile_t* file)
2898 return file->position - file->buff_len + file->buff_ind;
2903 ====================
2906 Give the total size of a file
2907 ====================
2909 fs_offset_t FS_FileSize (qfile_t* file)
2911 return file->real_length;
2916 ====================
2919 Erases any buffered input or output data
2920 ====================
2922 void FS_Purge (qfile_t* file)
2934 Filename are relative to the quake directory.
2935 Always appends a 0 byte.
2938 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2941 unsigned char *buf = NULL;
2942 fs_offset_t filesize = 0;
2944 file = FS_OpenVirtualFile(path, quiet);
2947 filesize = file->real_length;
2950 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2955 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2956 buf[filesize] = '\0';
2957 FS_Read (file, buf, filesize);
2959 if (developer_loadfile.integer)
2960 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2963 if (filesizepointer)
2964 *filesizepointer = filesize;
2973 The filename will be prefixed by the current game directory
2976 qboolean FS_WriteFileInBlocks (const char *filename, const void *const *data, const fs_offset_t *len, size_t count)
2980 fs_offset_t lentotal;
2982 file = FS_OpenRealFile(filename, "wb", false);
2985 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2990 for(i = 0; i < count; ++i)
2992 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)lentotal);
2993 for(i = 0; i < count; ++i)
2994 FS_Write (file, data[i], len[i]);
2999 qboolean FS_WriteFile (const char *filename, const void *data, fs_offset_t len)
3001 return FS_WriteFileInBlocks(filename, &data, &len, 1);
3006 =============================================================================
3008 OTHERS PUBLIC FUNCTIONS
3010 =============================================================================
3018 void FS_StripExtension (const char *in, char *out, size_t size_out)
3026 while ((currentchar = *in) && size_out > 1)
3028 if (currentchar == '.')
3030 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
3032 *out++ = currentchar;
3048 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
3052 // if path doesn't have a .EXT, append extension
3053 // (extension should include the .)
3054 src = path + strlen(path) - 1;
3056 while (*src != '/' && src != path)
3059 return; // it has an extension
3063 strlcat (path, extension, size_path);
3071 Look for a file in the packages and in the filesystem
3074 int FS_FileType (const char *filename)
3076 searchpath_t *search;
3077 char fullpath[MAX_OSPATH];
3079 search = FS_FindFile (filename, NULL, true);
3081 return FS_FILETYPE_NONE;
3083 if(search->pack && !search->pack->vpack)
3084 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
3086 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
3087 return FS_SysFileType(fullpath);
3095 Look for a file in the packages and in the filesystem
3098 qboolean FS_FileExists (const char *filename)
3100 return (FS_FindFile (filename, NULL, true) != NULL);
3108 Look for a file in the filesystem only
3111 int FS_SysFileType (const char *path)
3114 // Sajt - some older sdks are missing this define
3115 # ifndef INVALID_FILE_ATTRIBUTES
3116 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
3119 DWORD result = GetFileAttributes(path);
3121 if(result == INVALID_FILE_ATTRIBUTES)
3122 return FS_FILETYPE_NONE;
3124 if(result & FILE_ATTRIBUTE_DIRECTORY)
3125 return FS_FILETYPE_DIRECTORY;
3127 return FS_FILETYPE_FILE;
3131 if (stat (path,&buf) == -1)
3132 return FS_FILETYPE_NONE;
3135 #define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
3137 if(S_ISDIR(buf.st_mode))
3138 return FS_FILETYPE_DIRECTORY;
3140 return FS_FILETYPE_FILE;
3144 qboolean FS_SysFileExists (const char *path)
3146 return FS_SysFileType (path) != FS_FILETYPE_NONE;
3149 void FS_mkdir (const char *path)
3162 Allocate and fill a search structure with information on matching filenames.
3165 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
3168 searchpath_t *searchpath;
3170 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
3171 stringlist_t resultlist;
3172 stringlist_t dirlist;
3173 const char *slash, *backslash, *colon, *separator;
3175 char temp[MAX_OSPATH];
3177 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
3182 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
3186 stringlistinit(&resultlist);
3187 stringlistinit(&dirlist);
3189 slash = strrchr(pattern, '/');
3190 backslash = strrchr(pattern, '\\');
3191 colon = strrchr(pattern, ':');
3192 separator = max(slash, backslash);
3193 separator = max(separator, colon);
3194 basepathlength = separator ? (separator + 1 - pattern) : 0;
3195 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
3197 memcpy(basepath, pattern, basepathlength);
3198 basepath[basepathlength] = 0;
3200 // search through the path, one element at a time
3201 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
3203 // is the element a pak file?
3204 if (searchpath->pack && !searchpath->pack->vpack)
3206 // look through all the pak file elements
3207 pak = searchpath->pack;
3208 for (i = 0;i < pak->numfiles;i++)
3210 strlcpy(temp, pak->files[i].name, sizeof(temp));
3213 if (matchpattern(temp, (char *)pattern, true))
3215 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3216 if (!strcmp(resultlist.strings[resultlistindex], temp))
3218 if (resultlistindex == resultlist.numstrings)
3220 stringlistappend(&resultlist, temp);
3221 if (!quiet && developer_loading.integer)
3222 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
3225 // strip off one path element at a time until empty
3226 // this way directories are added to the listing if they match the pattern
3227 slash = strrchr(temp, '/');
3228 backslash = strrchr(temp, '\\');
3229 colon = strrchr(temp, ':');
3231 if (separator < slash)
3233 if (separator < backslash)
3234 separator = backslash;
3235 if (separator < colon)
3237 *((char *)separator) = 0;
3243 stringlist_t matchedSet, foundSet;
3244 const char *start = pattern;
3246 stringlistinit(&matchedSet);
3247 stringlistinit(&foundSet);
3248 // add a first entry to the set
3249 stringlistappend(&matchedSet, "");
3250 // iterate through pattern's path
3253 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
3254 char subpath[MAX_OSPATH];
3255 char subpattern[MAX_OSPATH];
3257 // find the next wildcard
3258 wildcard = strchr(start, '?');
3259 asterisk = strchr(start, '*');
3260 if (asterisk && (!wildcard || asterisk < wildcard))
3262 wildcard = asterisk;
3267 nextseparator = strchr( wildcard, '/' );
3271 nextseparator = NULL;
3274 if( !nextseparator ) {
3275 nextseparator = start + strlen( start );
3278 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
3279 // copy everything up except nextseperator
3280 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
3281 // find the last '/' before the wildcard
3282 prevseparator = strrchr( subpattern, '/' );
3284 prevseparator = subpattern;
3287 // copy everything from start to the previous including the '/' (before the wildcard)
3288 // everything up to start is already included in the path of matchedSet's entries
3289 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
3291 // for each entry in matchedSet try to open the subdirectories specified in subpath
3292 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
3293 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
3294 strlcat( temp, subpath, sizeof(temp) );
3295 listdirectory( &foundSet, searchpath->filename, temp );
3297 if( dirlistindex == 0 ) {
3300 // reset the current result set
3301 stringlistfreecontents( &matchedSet );
3302 // match against the pattern
3303 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
3304 const char *direntry = foundSet.strings[ dirlistindex ];
3305 if (matchpattern(direntry, subpattern, true)) {
3306 stringlistappend( &matchedSet, direntry );
3309 stringlistfreecontents( &foundSet );
3311 start = nextseparator;
3314 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
3316 const char *temp = matchedSet.strings[dirlistindex];
3317 if (matchpattern(temp, (char *)pattern, true))
3319 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3320 if (!strcmp(resultlist.strings[resultlistindex], temp))
3322 if (resultlistindex == resultlist.numstrings)
3324 stringlistappend(&resultlist, temp);
3325 if (!quiet && developer_loading.integer)
3326 Con_Printf("SearchDirFile: %s\n", temp);
3330 stringlistfreecontents( &matchedSet );
3334 if (resultlist.numstrings)
3336 stringlistsort(&resultlist);
3337 numfiles = resultlist.numstrings;
3339 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3340 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
3341 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
3342 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
3343 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
3344 search->numfilenames = (int)numfiles;
3347 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3350 search->filenames[numfiles] = search->filenamesbuffer + numchars;
3351 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
3352 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
3354 numchars += (int)textlen;
3357 stringlistfreecontents(&resultlist);
3363 void FS_FreeSearch(fssearch_t *search)
3368 extern int con_linewidth;
3369 int FS_ListDirectory(const char *pattern, int oneperline)
3378 char linebuf[MAX_INPUTLINE];
3380 search = FS_Search(pattern, true, true);
3383 numfiles = search->numfilenames;
3386 // FIXME: the names could be added to one column list and then
3387 // gradually shifted into the next column if they fit, and then the
3388 // next to make a compact variable width listing but it's a lot more
3390 // find width for columns
3392 for (i = 0;i < numfiles;i++)
3394 l = (int)strlen(search->filenames[i]);
3395 if (columnwidth < l)
3398 // count the spacing character
3400 // calculate number of columns
3401 numcolumns = con_linewidth / columnwidth;
3402 // don't bother with the column printing if it's only one column
3403 if (numcolumns >= 2)
3405 numlines = (numfiles + numcolumns - 1) / numcolumns;
3406 for (i = 0;i < numlines;i++)
3409 for (k = 0;k < numcolumns;k++)
3411 l = i * numcolumns + k;
3414 name = search->filenames[l];
3415 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3416 linebuf[linebufpos++] = name[j];
3417 // space out name unless it's the last on the line
3418 if (k + 1 < numcolumns && l + 1 < numfiles)
3419 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3420 linebuf[linebufpos++] = ' ';
3423 linebuf[linebufpos] = 0;
3424 Con_Printf("%s\n", linebuf);
3431 for (i = 0;i < numfiles;i++)
3432 Con_Printf("%s\n", search->filenames[i]);
3433 FS_FreeSearch(search);
3434 return (int)numfiles;
3437 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3439 const char *pattern;
3442 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3445 if (Cmd_Argc() == 2)
3446 pattern = Cmd_Argv(1);
3449 if (!FS_ListDirectory(pattern, oneperline))
3450 Con_Print("No files found.\n");
3455 FS_ListDirectoryCmd("dir", true);
3460 FS_ListDirectoryCmd("ls", false);
3463 void FS_Which_f(void)
3465 const char *filename;
3468 if (Cmd_Argc() != 2)
3470 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3473 filename = Cmd_Argv(1);
3474 sp = FS_FindFile(filename, &index, true);
3476 Con_Printf("%s isn't anywhere\n", filename);
3482 Con_Printf("%s is in virtual package %sdir\n", filename, sp->pack->shortname);
3484 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3487 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3491 const char *FS_WhichPack(const char *filename)
3494 searchpath_t *sp = FS_FindFile(filename, &index, true);
3496 return sp->pack->shortname;
3502 ====================
3503 FS_IsRegisteredQuakePack
3505 Look for a proof of purchase file file in the requested package
3507 If it is found, this file should NOT be downloaded.
3508 ====================
3510 qboolean FS_IsRegisteredQuakePack(const char *name)
3512 searchpath_t *search;
3515 // search through the path, one element at a time
3516 for (search = fs_searchpaths;search;search = search->next)
3518 if (search->pack && !search->pack->vpack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3519 // TODO do we want to support vpacks in here too?
3521 int (*strcmp_funct) (const char* str1, const char* str2);
3522 int left, right, middle;
3525 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3527 // Look for the file (binary search)
3529 right = pak->numfiles - 1;
3530 while (left <= right)
3534 middle = (left + right) / 2;
3535 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3541 // If we're too far in the list
3548 // we found the requested pack but it is not registered quake
3556 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3559 unsigned char *filedata;
3560 fs_offset_t filesize;
3561 if (filesizepointer)
3562 *filesizepointer = 0;
3563 if (!filename || !*filename)
3565 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3568 if (filesizepointer)
3569 *filesizepointer = filesize;
3570 crc = CRC_Block(filedata, filesize);
3576 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3579 unsigned char *out = NULL;
3583 #ifndef LINK_TO_ZLIB
3588 memset(&strm, 0, sizeof(strm));
3589 strm.zalloc = Z_NULL;
3590 strm.zfree = Z_NULL;
3591 strm.opaque = Z_NULL;
3594 level = Z_DEFAULT_COMPRESSION;
3596 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3598 Con_Printf("FS_Deflate: deflate init error!\n");
3602 strm.next_in = (unsigned char*)data;
3603 strm.avail_in = size;
3605 tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3608 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3609 qz_deflateEnd(&strm);
3613 strm.next_out = tmp;
3614 strm.avail_out = size;
3616 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3618 Con_Printf("FS_Deflate: deflate failed!\n");
3619 qz_deflateEnd(&strm);
3624 if(qz_deflateEnd(&strm) != Z_OK)
3626 Con_Printf("FS_Deflate: deflateEnd failed\n");
3631 if(strm.total_out >= size)
3633 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3638 out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3641 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3647 *deflated_size = (size_t)strm.total_out;
3649 memcpy(out, tmp, strm.total_out);
3655 static void AssertBufsize(sizebuf_t *buf, int length)
3657 if(buf->cursize + length > buf->maxsize)
3659 int oldsize = buf->maxsize;
3660 unsigned char *olddata;
3661 olddata = buf->data;
3662 buf->maxsize += length;
3663 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3666 memcpy(buf->data, olddata, oldsize);
3672 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3676 unsigned char *out = NULL;
3677 unsigned char tmp[2048];
3682 #ifndef LINK_TO_ZLIB
3687 memset(&outbuf, 0, sizeof(outbuf));
3688 outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3689 outbuf.maxsize = sizeof(tmp);
3691 memset(&strm, 0, sizeof(strm));
3692 strm.zalloc = Z_NULL;
3693 strm.zfree = Z_NULL;
3694 strm.opaque = Z_NULL;
3696 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3698 Con_Printf("FS_Inflate: inflate init error!\n");
3699 Mem_Free(outbuf.data);
3703 strm.next_in = (unsigned char*)data;
3704 strm.avail_in = size;
3708 strm.next_out = tmp;
3709 strm.avail_out = sizeof(tmp);
3710 ret = qz_inflate(&strm, Z_NO_FLUSH);
3711 // it either returns Z_OK on progress, Z_STREAM_END on end
3719 case Z_STREAM_ERROR:
3720 Con_Print("FS_Inflate: stream error!\n");
3723 Con_Print("FS_Inflate: data error!\n");
3726 Con_Print("FS_Inflate: mem error!\n");
3729 Con_Print("FS_Inflate: buf error!\n");
3732 Con_Print("FS_Inflate: unknown error!\n");
3736 if(ret != Z_OK && ret != Z_STREAM_END)
3738 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3739 Mem_Free(outbuf.data);
3740 qz_inflateEnd(&strm);
3743 have = sizeof(tmp) - strm.avail_out;
3744 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3745 SZ_Write(&outbuf, tmp, have);
3746 } while(ret != Z_STREAM_END);
3748 qz_inflateEnd(&strm);
3750 out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3753 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3754 Mem_Free(outbuf.data);
3758 memcpy(out, outbuf.data, outbuf.cursize);
3759 Mem_Free(outbuf.data);
3762 *inflated_size = (size_t)outbuf.cursize;