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
25 #define _LARGEFILE64_SOURCE 1
28 // include SDL for IPHONEOS code
29 # include <TargetConditionals.h>
44 # include <sys/stat.h>
53 // Win32 requires us to add O_BINARY, but the other OSes don't have it
58 // In case the system doesn't support the O_NONBLOCK flag
63 // largefile support for Win32
65 # define lseek _lseeki64
66 #elif defined(O_LARGEFILE)
67 # define lseek lseek64
75 // suppress deprecated warnings
76 # include <sys/stat.h>
81 # define unlink _unlink
85 /** \page fs File System
87 All of Quake's data access is through a hierchal file system, but the contents
88 of the file system can be transparently merged from several sources.
90 The "base directory" is the path to the directory holding the quake.exe and
91 all game directories. The sys_* files pass this to host_init in
92 quakeparms_t->basedir. This can be overridden with the "-basedir" command
93 line parm to allow code debugging in a different directory. The base
94 directory is only used during filesystem initialization.
96 The "game directory" is the first tree on the search path and directory that
97 all generated files (savegames, screenshots, demos, config files) will be
98 saved to. This can be overridden with the "-game" command line parameter.
99 The game directory can never be changed while quake is executing. This is a
100 precaution against having a malicious server instruct clients to write files
101 over areas they shouldn't.
107 =============================================================================
111 =============================================================================
114 // Magic numbers of a ZIP file (big-endian format)
115 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
116 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
117 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
119 // Other constants for ZIP files
120 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
121 #define ZIP_END_CDIR_SIZE 22
122 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
123 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
128 #define qz_inflate inflate
129 #define qz_inflateEnd inflateEnd
130 #define qz_inflateInit2_ inflateInit2_
131 #define qz_inflateReset inflateReset
132 #define qz_deflateInit2_ deflateInit2_
133 #define qz_deflateEnd deflateEnd
134 #define qz_deflate deflate
135 #define Z_MEMLEVEL_DEFAULT 8
138 // Zlib constants (from zlib.h)
139 #define Z_SYNC_FLUSH 2
142 #define Z_STREAM_END 1
143 #define Z_STREAM_ERROR (-2)
144 #define Z_DATA_ERROR (-3)
145 #define Z_MEM_ERROR (-4)
146 #define Z_BUF_ERROR (-5)
147 #define ZLIB_VERSION "1.2.3"
151 #define Z_MEMLEVEL_DEFAULT 8
154 #define Z_DEFAULT_COMPRESSION (-1)
156 #define Z_SYNC_FLUSH 2
157 #define Z_FULL_FLUSH 3
160 // Uncomment the following line if the zlib DLL you have still uses
161 // the 1.1.x series calling convention on Win32 (WINAPI)
162 //#define ZLIB_USES_WINAPI
166 =============================================================================
170 =============================================================================
173 /*! Zlib stream (from zlib.h)
174 * \warning: some pointers we don't use directly have
175 * been cast to "void*" for a matter of simplicity
179 unsigned char *next_in; ///< next input byte
180 unsigned int avail_in; ///< number of bytes available at next_in
181 unsigned long total_in; ///< total nb of input bytes read so far
183 unsigned char *next_out; ///< next output byte should be put there
184 unsigned int avail_out; ///< remaining free space at next_out
185 unsigned long total_out; ///< total nb of bytes output so far
187 char *msg; ///< last error message, NULL if no error
188 void *state; ///< not visible by applications
190 void *zalloc; ///< used to allocate the internal state
191 void *zfree; ///< used to free the internal state
192 void *opaque; ///< private data object passed to zalloc and zfree
194 int data_type; ///< best guess about the data type: ascii or binary
195 unsigned long adler; ///< adler32 value of the uncompressed data
196 unsigned long reserved; ///< reserved for future use
201 /// inside a package (PAK or PK3)
202 #define QFILE_FLAG_PACKED (1 << 0)
203 /// file is compressed using the deflate algorithm (PK3 only)
204 #define QFILE_FLAG_DEFLATED (1 << 1)
205 /// file is actually already loaded data
206 #define QFILE_FLAG_DATA (1 << 2)
207 /// real file will be removed on close
208 #define QFILE_FLAG_REMOVE (1 << 3)
210 #define FILE_BUFF_SIZE 2048
214 size_t comp_length; ///< length of the compressed file
215 size_t in_ind, in_len; ///< input buffer current index and length
216 size_t in_position; ///< position in the compressed file
217 unsigned char input [FILE_BUFF_SIZE];
223 int handle; ///< file descriptor
224 fs_offset_t real_length; ///< uncompressed file size (for files opened in "read" mode)
225 fs_offset_t position; ///< current position in the file
226 fs_offset_t offset; ///< offset into the package (0 if external file)
227 int ungetc; ///< single stored character from ungetc, cleared to EOF when read
230 fs_offset_t buff_ind, buff_len; ///< buffer current index and length
231 unsigned char buff [FILE_BUFF_SIZE];
233 ztoolkit_t* ztk; ///< For zipped files.
235 const unsigned char *data; ///< For data files.
237 const char *filename; ///< Kept around for QFILE_FLAG_REMOVE, unused otherwise
241 // ------ PK3 files on disk ------ //
243 // You can get the complete ZIP format description from PKWARE website
245 typedef struct pk3_endOfCentralDir_s
247 unsigned int signature;
248 unsigned short disknum;
249 unsigned short cdir_disknum; ///< number of the disk with the start of the central directory
250 unsigned short localentries; ///< number of entries in the central directory on this disk
251 unsigned short nbentries; ///< total number of entries in the central directory on this disk
252 unsigned int cdir_size; ///< size of the central directory
253 unsigned int cdir_offset; ///< with respect to the starting disk number
254 unsigned short comment_size;
255 fs_offset_t prepended_garbage;
256 } pk3_endOfCentralDir_t;
259 // ------ PAK files on disk ------ //
260 typedef struct dpackfile_s
263 int filepos, filelen;
266 typedef struct dpackheader_s
274 /*! \name Packages in memory
277 /// the offset in packfile_t is the true contents offset
278 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
279 /// file compressed using the deflate algorithm
280 #define PACKFILE_FLAG_DEFLATED (1 << 1)
281 /// file is a symbolic link
282 #define PACKFILE_FLAG_SYMLINK (1 << 2)
284 typedef struct packfile_s
286 char name [MAX_QPATH];
289 fs_offset_t packsize; ///< size in the package
290 fs_offset_t realsize; ///< real file size (uncompressed)
293 typedef struct pack_s
295 char filename [MAX_OSPATH];
296 char shortname [MAX_QPATH];
298 int ignorecase; ///< PK3 ignores case
305 /// Search paths for files (including packages)
306 typedef struct searchpath_s
308 // only one of filename / pack will be used
309 char filename[MAX_OSPATH];
311 struct searchpath_s *next;
316 =============================================================================
320 =============================================================================
325 void FS_Which_f(void);
327 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
328 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
329 fs_offset_t offset, fs_offset_t packsize,
330 fs_offset_t realsize, int flags);
334 =============================================================================
338 =============================================================================
341 mempool_t *fs_mempool;
343 searchpath_t *fs_searchpaths = NULL;
344 const char *const fs_checkgamedir_missing = "missing";
346 #define MAX_FILES_IN_PACK 65536
348 char fs_userdir[MAX_OSPATH];
349 char fs_gamedir[MAX_OSPATH];
350 char fs_basedir[MAX_OSPATH];
351 static pack_t *fs_selfpack = NULL;
353 // list of active game directories (empty if not running a mod)
354 int fs_numgamedirs = 0;
355 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
357 // list of all gamedirs with modinfo.txt
358 gamedir_t *fs_all_gamedirs = NULL;
359 int fs_all_gamedirs_count = 0;
361 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)"};
362 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"};
363 cvar_t cvar_fs_gamedir = {CVAR_READONLY | CVAR_NORESETTODEFAULTS, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"};
367 =============================================================================
369 PRIVATE FUNCTIONS - PK3 HANDLING
371 =============================================================================
375 // Functions exported from zlib
376 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
377 # define ZEXPORT WINAPI
382 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
383 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
384 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
385 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
386 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
387 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
388 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
391 #define qz_inflateInit2(strm, windowBits) \
392 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
393 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
394 qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
397 // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
399 static dllfunction_t zlibfuncs[] =
401 {"inflate", (void **) &qz_inflate},
402 {"inflateEnd", (void **) &qz_inflateEnd},
403 {"inflateInit2_", (void **) &qz_inflateInit2_},
404 {"inflateReset", (void **) &qz_inflateReset},
405 {"deflateInit2_", (void **) &qz_deflateInit2_},
406 {"deflateEnd", (void **) &qz_deflateEnd},
407 {"deflate", (void **) &qz_deflate},
411 /// Handle for Zlib DLL
412 static dllhandle_t zlib_dll = NULL;
416 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
417 static dllfunction_t shfolderfuncs[] =
419 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
422 static dllhandle_t shfolder_dll = NULL;
432 void PK3_CloseLibrary (void)
435 Sys_UnloadLibrary (&zlib_dll);
444 Try to load the Zlib DLL
447 qboolean PK3_OpenLibrary (void)
452 const char* dllnames [] =
455 # ifdef ZLIB_USES_WINAPI
461 #elif defined(MACOSX)
475 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
483 See if zlib is available
486 qboolean FS_HasZlib(void)
491 PK3_OpenLibrary(); // to be safe
492 return (zlib_dll != 0);
498 PK3_GetEndOfCentralDir
500 Extract the end of the central directory from a PK3 package
503 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
505 fs_offset_t filesize, maxsize;
506 unsigned char *buffer, *ptr;
509 // Get the package size
510 filesize = lseek (packhandle, 0, SEEK_END);
511 if (filesize < ZIP_END_CDIR_SIZE)
514 // Load the end of the file in memory
515 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
518 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
519 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
520 lseek (packhandle, filesize - maxsize, SEEK_SET);
521 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
527 // Look for the end of central dir signature around the end of the file
528 maxsize -= ZIP_END_CDIR_SIZE;
529 ptr = &buffer[maxsize];
531 while (BuffBigLong (ptr) != ZIP_END_HEADER)
543 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
544 eocd->signature = LittleLong (eocd->signature);
545 eocd->disknum = LittleShort (eocd->disknum);
546 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
547 eocd->localentries = LittleShort (eocd->localentries);
548 eocd->nbentries = LittleShort (eocd->nbentries);
549 eocd->cdir_size = LittleLong (eocd->cdir_size);
550 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
551 eocd->comment_size = LittleShort (eocd->comment_size);
552 eocd->prepended_garbage = filesize - (ind + ZIP_END_CDIR_SIZE) - eocd->cdir_offset - eocd->cdir_size; // this detects "SFX" zip files
553 eocd->cdir_offset += eocd->prepended_garbage;
565 Extract the file list from a PK3 file
568 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
570 unsigned char *central_dir, *ptr;
572 fs_offset_t remaining;
574 // Load the central directory in memory
575 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
576 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
577 if(read (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
579 Mem_Free (central_dir);
583 // Extract the files properties
584 // The parsing is done "by hand" because some fields have variable sizes and
585 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
586 remaining = eocd->cdir_size;
589 for (ind = 0; ind < eocd->nbentries; ind++)
591 fs_offset_t namesize, count;
593 // Checking the remaining size
594 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
596 Mem_Free (central_dir);
599 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
602 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
604 Mem_Free (central_dir);
608 namesize = BuffLittleShort (&ptr[28]); // filename length
610 // Check encryption, compression, and attributes
611 // 1st uint8 : general purpose bit flag
612 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
614 // LordHavoc: bit 3 would be a problem if we were scanning the archive
615 // but is not a problem in the central directory where the values are
618 // bit 3 seems to always be set by the standard Mac OSX zip maker
620 // 2nd uint8 : external file attributes
621 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
622 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
624 // Still enough bytes for the name?
625 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
627 Mem_Free (central_dir);
631 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
632 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
634 char filename [sizeof (pack->files[0].name)];
635 fs_offset_t offset, packsize, realsize;
638 // Extract the name (strip it if necessary)
639 namesize = min(namesize, (int)sizeof (filename) - 1);
640 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
641 filename[namesize] = '\0';
643 if (BuffLittleShort (&ptr[10]))
644 flags = PACKFILE_FLAG_DEFLATED;
647 offset = (unsigned int)(BuffLittleLong (&ptr[42]) + eocd->prepended_garbage);
648 packsize = (unsigned int)BuffLittleLong (&ptr[20]);
649 realsize = (unsigned int)BuffLittleLong (&ptr[24]);
651 switch(ptr[5]) // C_VERSION_MADE_BY_1
656 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
657 // can't use S_ISLNK here, as this has to compile on non-UNIX too
658 flags |= PACKFILE_FLAG_SYMLINK;
662 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
666 // Skip the name, additionnal field, and comment
667 // 1er uint16 : extra field length
668 // 2eme uint16 : file comment length
669 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
670 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
674 // If the package is empty, central_dir is NULL here
675 if (central_dir != NULL)
676 Mem_Free (central_dir);
677 return pack->numfiles;
685 Create a package entry associated with a PK3 file
688 pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean silent)
690 pk3_endOfCentralDir_t eocd;
694 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
697 Con_Printf ("%s is not a PK3 file\n", packfile);
702 // Multi-volume ZIP archives are NOT allowed
703 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
705 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
710 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
711 // since eocd.nbentries is an unsigned 16 bits integer
712 #if MAX_FILES_IN_PACK < 65535
713 if (eocd.nbentries > MAX_FILES_IN_PACK)
715 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
721 // Create a package structure in memory
722 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
723 pack->ignorecase = true; // PK3 ignores case
724 strlcpy (pack->filename, packfile, sizeof (pack->filename));
725 pack->handle = packhandle;
726 pack->numfiles = eocd.nbentries;
727 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
729 real_nb_files = PK3_BuildFileList (pack, &eocd);
730 if (real_nb_files < 0)
732 Con_Printf ("%s is not a valid PK3 file\n", packfile);
738 Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
741 pack_t *FS_LoadPackPK3 (const char *packfile)
745 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
747 packhandle = open (packfile, O_RDONLY | O_BINARY | O_LARGEFILE);
751 return FS_LoadPackPK3FromFD(packfile, packhandle, false);
757 PK3_GetTrueFileOffset
759 Find where the true file data offset is
762 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
764 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
768 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
771 // Load the local file description
772 lseek (pack->handle, pfile->offset, SEEK_SET);
773 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
774 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
776 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
780 // Skip name and extra field
781 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
783 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
789 =============================================================================
791 OTHER PRIVATE FUNCTIONS
793 =============================================================================
801 Add a file to the list of files contained into a package
804 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
805 fs_offset_t offset, fs_offset_t packsize,
806 fs_offset_t realsize, int flags)
808 int (*strcmp_funct) (const char* str1, const char* str2);
809 int left, right, middle;
812 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
814 // Look for the slot we should put that file into (binary search)
816 right = pack->numfiles - 1;
817 while (left <= right)
821 middle = (left + right) / 2;
822 diff = strcmp_funct (pack->files[middle].name, name);
824 // If we found the file, there's a problem
826 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
828 // If we're too far in the list
835 // We have to move the right of the list by one slot to free the one we need
836 pfile = &pack->files[left];
837 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
840 strlcpy (pfile->name, name, sizeof (pfile->name));
841 pfile->offset = offset;
842 pfile->packsize = packsize;
843 pfile->realsize = realsize;
844 pfile->flags = flags;
854 Only used for FS_OpenRealFile.
857 void FS_CreatePath (char *path)
861 for (ofs = path+1 ; *ofs ; ofs++)
863 if (*ofs == '/' || *ofs == '\\')
865 // create the directory
881 void FS_Path_f (void)
885 Con_Print("Current search path:\n");
886 for (s=fs_searchpaths ; s ; s=s->next)
891 Con_Printf("%sdir (virtual pack)\n", s->pack->filename);
893 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
896 Con_Printf("%s\n", s->filename);
906 /*! Takes an explicit (not game tree related) path to a pak file.
907 *Loads the header and directory, adding the files at the beginning
908 *of the list so they override previous pack files.
910 pack_t *FS_LoadPackPAK (const char *packfile)
912 dpackheader_t header;
919 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
921 packhandle = open (packfile, O_RDONLY | O_BINARY | O_LARGEFILE);
925 if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
927 Con_Printf ("%s is not a packfile\n", packfile);
931 if (memcmp(header.id, "PACK", 4))
933 Con_Printf ("%s is not a packfile\n", packfile);
937 header.dirofs = LittleLong (header.dirofs);
938 header.dirlen = LittleLong (header.dirlen);
940 if (header.dirlen % sizeof(dpackfile_t))
942 Con_Printf ("%s has an invalid directory size\n", packfile);
947 numpackfiles = header.dirlen / sizeof(dpackfile_t);
949 if (numpackfiles > MAX_FILES_IN_PACK)
951 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
956 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
957 lseek (packhandle, header.dirofs, SEEK_SET);
958 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
960 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
966 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
967 pack->ignorecase = false; // PAK is case sensitive
968 strlcpy (pack->filename, packfile, sizeof (pack->filename));
969 pack->handle = packhandle;
971 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
973 // parse the directory
974 for (i = 0;i < numpackfiles;i++)
976 fs_offset_t offset = (unsigned int)LittleLong (info[i].filepos);
977 fs_offset_t size = (unsigned int)LittleLong (info[i].filelen);
979 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
984 Con_DPrintf("Added packfile %s (%i files)\n", packfile, numpackfiles);
992 Create a package entry associated with a directory file
995 pack_t *FS_LoadPackVirtual (const char *dirname)
998 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
1000 pack->ignorecase = false;
1001 strlcpy (pack->filename, dirname, sizeof(pack->filename));
1003 pack->numfiles = -1;
1005 Con_DPrintf("Added packfile %s (virtual pack)\n", dirname);
1014 /*! Adds the given pack to the search path.
1015 * The pack type is autodetected by the file extension.
1017 * Returns true if the file was successfully added to the
1018 * search path or if it was already included.
1020 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1021 * plain directories.
1024 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
1026 searchpath_t *search;
1028 const char *ext = FS_FileExtension(pakfile);
1031 for(search = fs_searchpaths; search; search = search->next)
1033 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
1036 *already_loaded = true;
1037 return true; // already loaded
1042 *already_loaded = false;
1044 if(!strcasecmp(ext, "pk3dir"))
1045 pak = FS_LoadPackVirtual (pakfile);
1046 else if(!strcasecmp(ext, "pak"))
1047 pak = FS_LoadPackPAK (pakfile);
1048 else if(!strcasecmp(ext, "pk3"))
1049 pak = FS_LoadPackPK3 (pakfile);
1051 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
1055 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
1057 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
1060 // find the first item whose next one is a pack or NULL
1061 searchpath_t *insertion_point = 0;
1062 if(fs_searchpaths && !fs_searchpaths->pack)
1064 insertion_point = fs_searchpaths;
1067 if(!insertion_point->next)
1069 if(insertion_point->next->pack)
1071 insertion_point = insertion_point->next;
1074 // If insertion_point is NULL, this means that either there is no
1075 // item in the list yet, or that the very first item is a pack. In
1076 // that case, we want to insert at the beginning...
1077 if(!insertion_point)
1079 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1080 search->next = fs_searchpaths;
1081 fs_searchpaths = search;
1084 // otherwise we want to append directly after insertion_point.
1086 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1087 search->next = insertion_point->next;
1088 insertion_point->next = search;
1093 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1094 search->next = fs_searchpaths;
1095 fs_searchpaths = search;
1100 dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile);
1101 // if shortname ends with "pk3dir", strip that suffix to make it just "pk3"
1102 // same goes for the name inside the pack structure
1103 l = strlen(pak->shortname);
1105 if(!strcasecmp(pak->shortname + l - 7, ".pk3dir"))
1106 pak->shortname[l - 3] = 0;
1107 l = strlen(pak->filename);
1109 if(!strcasecmp(pak->filename + l - 7, ".pk3dir"))
1110 pak->filename[l - 3] = 0;
1116 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1127 /*! Adds the given pack to the search path and searches for it in the game path.
1128 * The pack type is autodetected by the file extension.
1130 * Returns true if the file was successfully added to the
1131 * search path or if it was already included.
1133 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1134 * plain directories.
1136 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1138 char fullpath[MAX_OSPATH];
1140 searchpath_t *search;
1143 *already_loaded = false;
1145 // then find the real name...
1146 search = FS_FindFile(pakfile, &index, true);
1147 if(!search || search->pack)
1149 Con_Printf("could not find pak \"%s\"\n", pakfile);
1153 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1155 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1163 Sets fs_gamedir, adds the directory to the head of the path,
1164 then loads and adds pak1.pak pak2.pak ...
1167 void FS_AddGameDirectory (const char *dir)
1171 searchpath_t *search;
1173 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1175 stringlistinit(&list);
1176 listdirectory(&list, "", dir);
1177 stringlistsort(&list);
1179 // add any PAK package in the directory
1180 for (i = 0;i < list.numstrings;i++)
1182 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1184 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1188 // add any PK3 package in the directory
1189 for (i = 0;i < list.numstrings;i++)
1191 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir"))
1193 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1197 stringlistfreecontents(&list);
1199 // Add the directory to the search path
1200 // (unpacked files have the priority over packed files)
1201 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1202 strlcpy (search->filename, dir, sizeof (search->filename));
1203 search->next = fs_searchpaths;
1204 fs_searchpaths = search;
1213 void FS_AddGameHierarchy (const char *dir)
1215 // Add the common game directory
1216 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1219 FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
1228 const char *FS_FileExtension (const char *in)
1230 const char *separator, *backslash, *colon, *dot;
1232 separator = strrchr(in, '/');
1233 backslash = strrchr(in, '\\');
1234 if (!separator || separator < backslash)
1235 separator = backslash;
1236 colon = strrchr(in, ':');
1237 if (!separator || separator < colon)
1240 dot = strrchr(in, '.');
1241 if (dot == NULL || (separator && (dot < separator)))
1253 const char *FS_FileWithoutPath (const char *in)
1255 const char *separator, *backslash, *colon;
1257 separator = strrchr(in, '/');
1258 backslash = strrchr(in, '\\');
1259 if (!separator || separator < backslash)
1260 separator = backslash;
1261 colon = strrchr(in, ':');
1262 if (!separator || separator < colon)
1264 return separator ? separator + 1 : in;
1273 void FS_ClearSearchPath (void)
1275 // unload all packs and directory information, close all pack files
1276 // (if a qfile is still reading a pack it won't be harmed because it used
1277 // dup() to get its own handle already)
1278 while (fs_searchpaths)
1280 searchpath_t *search = fs_searchpaths;
1281 fs_searchpaths = search->next;
1282 if (search->pack && search->pack != fs_selfpack)
1284 if(!search->pack->vpack)
1287 close(search->pack->handle);
1288 // free any memory associated with it
1289 if (search->pack->files)
1290 Mem_Free(search->pack->files);
1292 Mem_Free(search->pack);
1298 static void FS_AddSelfPack(void)
1302 searchpath_t *search;
1303 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1304 search->next = fs_searchpaths;
1305 search->pack = fs_selfpack;
1306 fs_searchpaths = search;
1316 void FS_Rescan (void)
1319 qboolean fs_modified = false;
1320 qboolean reset = false;
1321 char gamedirbuf[MAX_INPUTLINE];
1325 FS_ClearSearchPath();
1327 // automatically activate gamemode for the gamedirs specified
1329 COM_ChangeGameTypeForGameDirs();
1331 // add the game-specific paths
1332 // gamedirname1 (typically id1)
1333 FS_AddGameHierarchy (gamedirname1);
1334 // update the com_modname (used for server info)
1335 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1337 // add the game-specific path, if any
1338 // (only used for mission packs and the like, which should set fs_modified)
1342 FS_AddGameHierarchy (gamedirname2);
1346 // Adds basedir/gamedir as an override game
1347 // LordHavoc: now supports multiple -game directories
1348 // set the com_modname (reported in server info)
1350 for (i = 0;i < fs_numgamedirs;i++)
1353 FS_AddGameHierarchy (fs_gamedirs[i]);
1354 // update the com_modname (used server info)
1355 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1357 strlcat(gamedirbuf, va(" %s", fs_gamedirs[i]), sizeof(gamedirbuf));
1359 strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
1361 Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
1363 // add back the selfpack as new first item
1366 // set the default screenshot name to either the mod name or the
1367 // gamemode screenshot name
1368 if (strcmp(com_modname, gamedirname1))
1369 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1371 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1373 if((i = COM_CheckParm("-modname")) && i < com_argc - 1)
1374 strlcpy(com_modname, com_argv[i+1], sizeof(com_modname));
1376 // If "-condebug" is in the command line, remove the previous log file
1377 if (COM_CheckParm ("-condebug") != 0)
1378 unlink (va("%s/qconsole.log", fs_gamedir));
1380 // look for the pop.lmp file and set registered to true if it is found
1381 if (FS_FileExists("gfx/pop.lmp"))
1382 Cvar_Set ("registered", "1");
1388 if (!registered.integer)
1391 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1393 Con_Print("Playing shareware version.\n");
1396 Con_Print("Playing registered version.\n");
1398 case GAME_STEELSTORM:
1399 if (registered.integer)
1400 Con_Print("Playing registered version.\n");
1402 Con_Print("Playing shareware version.\n");
1408 // unload all wads so that future queries will return the new data
1412 void FS_Rescan_f(void)
1422 extern void Host_SaveConfig (void);
1423 extern void Host_LoadConfig_f (void);
1424 extern qboolean vid_opened;
1425 extern void VID_Stop(void);
1426 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1431 if (fs_numgamedirs == numgamedirs)
1433 for (i = 0;i < numgamedirs;i++)
1434 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1436 if (i == numgamedirs)
1437 return true; // already using this set of gamedirs, do nothing
1440 if (numgamedirs > MAX_GAMEDIRS)
1443 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1444 return false; // too many gamedirs
1447 for (i = 0;i < numgamedirs;i++)
1449 // if string is nasty, reject it
1450 p = FS_CheckGameDir(gamedirs[i]);
1454 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1455 return false; // nasty gamedirs
1457 if(p == fs_checkgamedir_missing && failmissing)
1460 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1461 return false; // missing gamedirs
1467 fs_numgamedirs = numgamedirs;
1468 for (i = 0;i < fs_numgamedirs;i++)
1469 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1471 // reinitialize filesystem to detect the new paks
1474 if (cls.demoplayback)
1480 // unload all sounds so they will be reloaded from the new files as needed
1481 S_UnloadAllSounds_f();
1483 // close down the video subsystem, it will start up again when the config finishes...
1487 // restart the video subsystem after the config is executed
1488 Cbuf_InsertText("\nloadconfig\nvid_restart\n\n");
1498 void FS_GameDir_f (void)
1502 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1506 Con_Printf("gamedirs active:");
1507 for (i = 0;i < fs_numgamedirs;i++)
1508 Con_Printf(" %s", fs_gamedirs[i]);
1513 numgamedirs = Cmd_Argc() - 1;
1514 if (numgamedirs > MAX_GAMEDIRS)
1516 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1520 for (i = 0;i < numgamedirs;i++)
1521 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1523 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1525 // actually, changing during game would work fine, but would be stupid
1526 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1530 // halt demo playback to close the file
1533 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1536 static const char *FS_SysCheckGameDir(const char *gamedir)
1538 static char buf[8192];
1544 stringlistinit(&list);
1545 listdirectory(&list, gamedir, "");
1546 success = list.numstrings > 0;
1547 stringlistfreecontents(&list);
1551 f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
1554 n = FS_Read (f, buf, sizeof(buf) - 1);
1574 const char *FS_CheckGameDir(const char *gamedir)
1578 if (FS_CheckNastyPath(gamedir, true))
1581 ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
1586 // get description from basedir
1587 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1595 ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
1599 return fs_checkgamedir_missing;
1602 static void FS_ListGameDirs(void)
1604 stringlist_t list, list2;
1608 fs_all_gamedirs_count = 0;
1610 Mem_Free(fs_all_gamedirs);
1612 stringlistinit(&list);
1613 listdirectory(&list, va("%s/", fs_basedir), "");
1614 listdirectory(&list, va("%s/", fs_userdir), "");
1615 stringlistsort(&list);
1617 stringlistinit(&list2);
1618 for(i = 0; i < list.numstrings; ++i)
1621 if(!strcmp(list.strings[i-1], list.strings[i]))
1623 info = FS_CheckGameDir(list.strings[i]);
1626 if(info == fs_checkgamedir_missing)
1630 stringlistappend(&list2, list.strings[i]);
1632 stringlistfreecontents(&list);
1634 fs_all_gamedirs = (gamedir_t *)Mem_Alloc(fs_mempool, list2.numstrings * sizeof(*fs_all_gamedirs));
1635 for(i = 0; i < list2.numstrings; ++i)
1637 info = FS_CheckGameDir(list2.strings[i]);
1638 // all this cannot happen any more, but better be safe than sorry
1641 if(info == fs_checkgamedir_missing)
1645 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
1646 strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
1647 ++fs_all_gamedirs_count;
1656 void FS_Init_SelfPack (void)
1659 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1662 fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
1668 buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL);
1671 const char **new_argv;
1673 int args_left = 256;
1674 new_argv = (const char **)Mem_Alloc(fs_mempool, sizeof(*com_argv) * (com_argc + args_left + 2));
1677 new_argv[0] = "dummy";
1682 memcpy((char *)(&new_argv[0]), &com_argv[0], sizeof(*com_argv) * com_argc);
1685 while(COM_ParseToken_Console(&p))
1689 q = (char *)Mem_Alloc(fs_mempool, strlen(com_token) + 1);
1690 strlcpy(q, com_token, strlen(com_token) + 1);
1691 new_argv[com_argc + i] = q;
1694 new_argv[i+com_argc] = NULL;
1695 com_argv = new_argv;
1696 com_argc = com_argc + i;
1713 TCHAR mydocsdir[MAX_PATH + 1];
1714 #if _MSC_VER >= 1400
1718 #ifndef __IPHONEOS__
1723 const char* dllnames [] =
1725 "shfolder.dll", // IE 4, or Win NT and higher
1728 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1729 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1737 // fs_basedir is "" by default, to utilize this you can simply add your gamedir to the Resources in xcode
1738 // fs_userdir stores configurations to the Documents folder of the app
1739 strlcpy(fs_userdir, "../Documents/", sizeof(fs_userdir));
1741 // Add the personal game directory
1742 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1744 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
1746 else if(COM_CheckParm("-nohome"))
1753 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1755 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1756 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", fs_userdir);
1760 // use the environment
1761 #if _MSC_VER >= 1400
1762 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1764 homedir = getenv("USERPROFILE");
1769 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1770 #if _MSC_VER >= 1400
1773 Con_DPrintf("Obtained personal directory %s from environment\n", fs_userdir);
1778 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1780 homedir = getenv ("HOME");
1782 dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/.%s/", homedir, gameuserdirname);
1785 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1789 if(!COM_CheckParm("-mygames"))
1791 #if _MSC_VER >= 1400
1793 _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!
1795 int fd = open (va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1800 *fs_userdir = 0; // we have write access to the game dir, so let's use it
1806 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1808 // If the base directory is explicitly defined by the compilation process
1809 #ifdef DP_FS_BASEDIR
1810 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1815 // FIXME: is there a better way to find the directory outside the .app?
1816 if (strstr(com_argv[0], ".app/"))
1820 split = strstr(com_argv[0], ".app/");
1821 while (split > com_argv[0] && *split != '/')
1823 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1824 fs_basedir[split - com_argv[0]] = 0;
1831 // Overrides the system supplied base directory (under GAMENAME)
1832 // 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)
1833 i = COM_CheckParm ("-basedir");
1834 if (i && i < com_argc-1)
1836 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1837 i = (int)strlen (fs_basedir);
1838 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1839 fs_basedir[i-1] = 0;
1842 // add a path separator to the end of the basedir if it lacks one
1843 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1844 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1848 p = FS_CheckGameDir(gamedirname1);
1849 if(!p || p == fs_checkgamedir_missing)
1850 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1854 p = FS_CheckGameDir(gamedirname2);
1855 if(!p || p == fs_checkgamedir_missing)
1856 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1860 // Adds basedir/gamedir as an override game
1861 // LordHavoc: now supports multiple -game directories
1862 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1866 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1869 p = FS_CheckGameDir(com_argv[i]);
1871 Sys_Error("Nasty -game name rejected: %s", com_argv[i]);
1872 if(p == fs_checkgamedir_missing)
1873 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1874 // add the gamedir to the list of active gamedirs
1875 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1880 // generate the searchpath
1884 void FS_Init_Commands(void)
1886 Cvar_RegisterVariable (&scr_screenshot_name);
1887 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1888 Cvar_RegisterVariable (&cvar_fs_gamedir);
1890 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1891 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1892 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1893 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1894 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1895 Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1903 void FS_Shutdown (void)
1905 // close all pack files and such
1906 // (hopefully there aren't any other open files, but they'll be cleaned up
1907 // by the OS anyway)
1908 FS_ClearSearchPath();
1909 Mem_FreePool (&fs_mempool);
1912 Sys_UnloadLibrary (&shfolder_dll);
1916 int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
1922 // Parse the mode string
1931 opt = O_CREAT | O_TRUNC;
1935 opt = O_CREAT | O_APPEND;
1938 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1941 for (ind = 1; mode[ind] != '\0'; ind++)
1952 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1953 filepath, mode, mode[ind]);
1962 #if _MSC_VER >= 1400
1963 _sopen_s(&handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1965 handle = open (filepath, mod | opt, 0666);
1971 ====================
1974 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1975 ====================
1977 qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1981 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1983 file->handle = FS_SysOpenFD(filepath, mode, nonblocking);
1984 if (file->handle < 0)
1990 file->filename = Mem_strdup(fs_mempool, filepath);
1992 file->real_length = lseek (file->handle, 0, SEEK_END);
1994 // For files opened in append mode, we start at the end of the file
1996 file->position = file->real_length;
1998 lseek (file->handle, 0, SEEK_SET);
2008 Open a packed file using its package file descriptor
2011 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
2017 pfile = &pack->files[pack_ind];
2019 // If we don't have the true offset, get it now
2020 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
2021 if (!PK3_GetTrueFileOffset (pfile, pack))
2024 #ifndef LINK_TO_ZLIB
2025 // No Zlib DLL = no compressed files
2026 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
2028 Con_Printf("WARNING: can't open the compressed file %s\n"
2029 "You need the Zlib DLL to use compressed files\n",
2035 // LordHavoc: lseek affects all duplicates of a handle so we do it before
2036 // the dup() call to avoid having to close the dup_handle on error here
2037 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
2039 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %08x%08x)\n",
2040 pfile->name, pack->filename, (unsigned int)(pfile->offset >> 32), (unsigned int)(pfile->offset));
2044 dup_handle = dup (pack->handle);
2047 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
2051 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2052 memset (file, 0, sizeof (*file));
2053 file->handle = dup_handle;
2054 file->flags = QFILE_FLAG_PACKED;
2055 file->real_length = pfile->realsize;
2056 file->offset = pfile->offset;
2060 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
2064 file->flags |= QFILE_FLAG_DEFLATED;
2066 // We need some more variables
2067 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
2069 ztk->comp_length = pfile->packsize;
2071 // Initialize zlib stream
2072 ztk->zstream.next_in = ztk->input;
2073 ztk->zstream.avail_in = 0;
2075 /* From Zlib's "unzip.c":
2077 * windowBits is passed < 0 to tell that there is no zlib header.
2078 * Note that in this case inflate *requires* an extra "dummy" byte
2079 * after the compressed stream in order to complete decompression and
2080 * return Z_STREAM_END.
2081 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
2082 * size of both compressed and uncompressed data
2084 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
2086 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
2092 ztk->zstream.next_out = file->buff;
2093 ztk->zstream.avail_out = sizeof (file->buff);
2102 ====================
2105 Return true if the path should be rejected due to one of the following:
2106 1: path elements that are non-portable
2107 2: path elements that would allow access to files outside the game directory,
2108 or are just not a good idea for a mod to be using.
2109 ====================
2111 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
2113 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
2117 // Windows: don't allow \ in filenames (windows-only), period.
2118 // (on Windows \ is a directory separator, but / is also supported)
2119 if (strstr(path, "\\"))
2120 return 1; // non-portable
2122 // Mac: don't allow Mac-only filenames - : is a directory separator
2123 // instead of /, but we rely on / working already, so there's no reason to
2124 // support a Mac-only path
2125 // Amiga and Windows: : tries to go to root of drive
2126 if (strstr(path, ":"))
2127 return 1; // non-portable attempt to go to root of drive
2129 // Amiga: // is parent directory
2130 if (strstr(path, "//"))
2131 return 1; // non-portable attempt to go to parent directory
2133 // all: don't allow going to parent directory (../ or /../)
2134 if (strstr(path, ".."))
2135 return 2; // attempt to go outside the game directory
2137 // Windows and UNIXes: don't allow absolute paths
2139 return 2; // attempt to go outside the game directory
2141 // 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
2142 if (strchr(path, '.'))
2146 // gamedir is entirely path elements, so simply forbid . entirely
2149 if (strchr(path, '.') < strrchr(path, '/'))
2150 return 2; // possible attempt to go outside the game directory
2153 // all: forbid trailing slash on gamedir
2154 if (isgamedir && path[strlen(path)-1] == '/')
2157 // all: forbid leading dot on any filename for any reason
2158 if (strstr(path, "/."))
2159 return 2; // attempt to go outside the game directory
2161 // after all these checks we're pretty sure it's a / separated filename
2162 // and won't do much if any harm
2168 ====================
2171 Look for a file in the packages and in the filesystem
2173 Return the searchpath where the file was found (or NULL)
2174 and the file index in the package if relevant
2175 ====================
2177 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
2179 searchpath_t *search;
2182 // search through the path, one element at a time
2183 for (search = fs_searchpaths;search;search = search->next)
2185 // is the element a pak file?
2186 if (search->pack && !search->pack->vpack)
2188 int (*strcmp_funct) (const char* str1, const char* str2);
2189 int left, right, middle;
2192 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2194 // Look for the file (binary search)
2196 right = pak->numfiles - 1;
2197 while (left <= right)
2201 middle = (left + right) / 2;
2202 diff = strcmp_funct (pak->files[middle].name, name);
2207 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
2209 // yes, but the first one is empty so we treat it as not being there
2210 if (!quiet && developer_extra.integer)
2211 Con_DPrintf("FS_FindFile: %s is marked as deleted\n", name);
2218 if (!quiet && developer_extra.integer)
2219 Con_DPrintf("FS_FindFile: %s in %s\n",
2220 pak->files[middle].name, pak->filename);
2227 // If we're too far in the list
2236 char netpath[MAX_OSPATH];
2237 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
2238 if (FS_SysFileExists (netpath))
2240 if (!quiet && developer_extra.integer)
2241 Con_DPrintf("FS_FindFile: %s\n", netpath);
2250 if (!quiet && developer_extra.integer)
2251 Con_DPrintf("FS_FindFile: can't find %s\n", name);
2263 Look for a file in the search paths and open it in read-only mode
2266 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
2268 searchpath_t *search;
2271 search = FS_FindFile (filename, &pack_ind, quiet);
2277 // Found in the filesystem?
2280 // this works with vpacks, so we are fine
2281 char path [MAX_OSPATH];
2282 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
2283 return FS_SysOpen (path, "rb", nonblocking);
2286 // So, we found it in a package...
2288 // Is it a PK3 symlink?
2289 // TODO also handle directory symlinks by parsing the whole structure...
2290 // but heck, file symlinks are good enough for now
2291 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
2293 if(symlinkLevels <= 0)
2295 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
2300 char linkbuf[MAX_QPATH];
2302 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
2303 const char *mergeslash;
2308 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
2314 // Now combine the paths...
2315 mergeslash = strrchr(filename, '/');
2316 mergestart = linkbuf;
2318 mergeslash = filename;
2319 while(!strncmp(mergestart, "../", 3))
2322 while(mergeslash > filename)
2325 if(*mergeslash == '/')
2329 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
2330 if(mergeslash == filename)
2332 // Either mergeslash == filename, then we just replace the name (done below)
2336 // Or, we append the name after mergeslash;
2337 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2338 int spaceNeeded = mergeslash - filename + 1;
2339 int spaceRemoved = mergestart - linkbuf;
2340 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2342 Con_DPrintf("symlink: too long path rejected\n");
2345 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2346 memcpy(linkbuf, filename, spaceNeeded);
2347 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2348 mergestart = linkbuf;
2350 if (!quiet && developer_loading.integer)
2351 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2352 if(FS_CheckNastyPath (mergestart, false))
2354 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2357 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2361 return FS_OpenPackedFile (search->pack, pack_ind);
2366 =============================================================================
2368 MAIN PUBLIC FUNCTIONS
2370 =============================================================================
2374 ====================
2377 Open a file in the userpath. The syntax is the same as fopen
2378 Used for savegame scanning in menu, and all file writing.
2379 ====================
2381 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2383 char real_path [MAX_OSPATH];
2385 if (FS_CheckNastyPath(filepath, false))
2387 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2391 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath); // this is never a vpack
2393 // If the file is opened in "write", "append", or "read/write" mode,
2394 // create directories up to the file.
2395 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2396 FS_CreatePath (real_path);
2397 return FS_SysOpen (real_path, mode, false);
2402 ====================
2405 Open a file. The syntax is the same as fopen
2406 ====================
2408 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2410 if (FS_CheckNastyPath(filepath, false))
2412 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2416 return FS_OpenReadFile (filepath, quiet, false, 16);
2421 ====================
2424 Open a file. The syntax is the same as fopen
2425 ====================
2427 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2430 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2431 memset (file, 0, sizeof (*file));
2432 file->flags = QFILE_FLAG_DATA;
2434 file->real_length = size;
2440 ====================
2444 ====================
2446 int FS_Close (qfile_t* file)
2448 if(file->flags & QFILE_FLAG_DATA)
2454 if (close (file->handle))
2459 if (file->flags & QFILE_FLAG_REMOVE)
2460 remove(file->filename);
2462 Mem_Free((void *) file->filename);
2467 qz_inflateEnd (&file->ztk->zstream);
2468 Mem_Free (file->ztk);
2475 void FS_RemoveOnClose(qfile_t* file)
2477 file->flags |= QFILE_FLAG_REMOVE;
2481 ====================
2484 Write "datasize" bytes into a file
2485 ====================
2487 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2491 // If necessary, seek to the exact file position we're supposed to be
2492 if (file->buff_ind != file->buff_len)
2493 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2495 // Purge cached data
2498 // Write the buffer and update the position
2499 result = write (file->handle, data, (fs_offset_t)datasize);
2500 file->position = lseek (file->handle, 0, SEEK_CUR);
2501 if (file->real_length < file->position)
2502 file->real_length = file->position;
2512 ====================
2515 Read up to "buffersize" bytes from a file
2516 ====================
2518 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2520 fs_offset_t count, done;
2522 if (buffersize == 0)
2525 // Get rid of the ungetc character
2526 if (file->ungetc != EOF)
2528 ((char*)buffer)[0] = file->ungetc;
2536 if(file->flags & QFILE_FLAG_DATA)
2538 size_t left = file->real_length - file->position;
2539 if(buffersize > left)
2541 memcpy(buffer, file->data + file->position, buffersize);
2542 file->position += buffersize;
2546 // First, we copy as many bytes as we can from "buff"
2547 if (file->buff_ind < file->buff_len)
2549 count = file->buff_len - file->buff_ind;
2550 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2552 memcpy (buffer, &file->buff[file->buff_ind], count);
2553 file->buff_ind += count;
2555 buffersize -= count;
2556 if (buffersize == 0)
2560 // NOTE: at this point, the read buffer is always empty
2562 // If the file isn't compressed
2563 if (! (file->flags & QFILE_FLAG_DEFLATED))
2567 // We must take care to not read after the end of the file
2568 count = file->real_length - file->position;
2570 // If we have a lot of data to get, put them directly into "buffer"
2571 if (buffersize > sizeof (file->buff) / 2)
2573 if (count > (fs_offset_t)buffersize)
2574 count = (fs_offset_t)buffersize;
2575 lseek (file->handle, file->offset + file->position, SEEK_SET);
2576 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2580 file->position += nb;
2582 // Purge cached data
2588 if (count > (fs_offset_t)sizeof (file->buff))
2589 count = (fs_offset_t)sizeof (file->buff);
2590 lseek (file->handle, file->offset + file->position, SEEK_SET);
2591 nb = read (file->handle, file->buff, count);
2594 file->buff_len = nb;
2595 file->position += nb;
2597 // Copy the requested data in "buffer" (as much as we can)
2598 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2599 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2600 file->buff_ind = count;
2608 // If the file is compressed, it's more complicated...
2609 // We cycle through a few operations until we have read enough data
2610 while (buffersize > 0)
2612 ztoolkit_t *ztk = file->ztk;
2615 // NOTE: at this point, the read buffer is always empty
2617 // If "input" is also empty, we need to refill it
2618 if (ztk->in_ind == ztk->in_len)
2620 // If we are at the end of the file
2621 if (file->position == file->real_length)
2624 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2625 if (count > (fs_offset_t)sizeof (ztk->input))
2626 count = (fs_offset_t)sizeof (ztk->input);
2627 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2628 if (read (file->handle, ztk->input, count) != count)
2630 Con_Printf ("FS_Read: unexpected end of file\n");
2635 ztk->in_len = count;
2636 ztk->in_position += count;
2639 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2640 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2642 // Now that we are sure we have compressed data available, we need to determine
2643 // if it's better to inflate it in "file->buff" or directly in "buffer"
2645 // Inflate the data in "file->buff"
2646 if (buffersize < sizeof (file->buff) / 2)
2648 ztk->zstream.next_out = file->buff;
2649 ztk->zstream.avail_out = sizeof (file->buff);
2650 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2651 if (error != Z_OK && error != Z_STREAM_END)
2653 Con_Printf ("FS_Read: Can't inflate file\n");
2656 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2658 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2659 file->position += file->buff_len;
2661 // Copy the requested data in "buffer" (as much as we can)
2662 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2663 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2664 file->buff_ind = count;
2667 // Else, we inflate directly in "buffer"
2670 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2671 ztk->zstream.avail_out = (unsigned int)buffersize;
2672 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2673 if (error != Z_OK && error != Z_STREAM_END)
2675 Con_Printf ("FS_Read: Can't inflate file\n");
2678 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2680 // How much data did it inflate?
2681 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2682 file->position += count;
2684 // Purge cached data
2689 buffersize -= count;
2697 ====================
2700 Print a string into a file
2701 ====================
2703 int FS_Print (qfile_t* file, const char *msg)
2705 return (int)FS_Write (file, msg, strlen (msg));
2709 ====================
2712 Print a string into a file
2713 ====================
2715 int FS_Printf(qfile_t* file, const char* format, ...)
2720 va_start (args, format);
2721 result = FS_VPrintf (file, format, args);
2729 ====================
2732 Print a string into a file
2733 ====================
2735 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2738 fs_offset_t buff_size = MAX_INPUTLINE;
2743 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2744 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2745 if (len >= 0 && len < buff_size)
2747 Mem_Free (tempbuff);
2751 len = write (file->handle, tempbuff, len);
2752 Mem_Free (tempbuff);
2759 ====================
2762 Get the next character of a file
2763 ====================
2765 int FS_Getc (qfile_t* file)
2769 if (FS_Read (file, &c, 1) != 1)
2777 ====================
2780 Put a character back into the read buffer (only supports one character!)
2781 ====================
2783 int FS_UnGetc (qfile_t* file, unsigned char c)
2785 // If there's already a character waiting to be read
2786 if (file->ungetc != EOF)
2795 ====================
2798 Move the position index in a file
2799 ====================
2801 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2804 unsigned char* buffer;
2805 fs_offset_t buffersize;
2807 // Compute the file offset
2811 offset += file->position - file->buff_len + file->buff_ind;
2818 offset += file->real_length;
2824 if (offset < 0 || offset > file->real_length)
2827 if(file->flags & QFILE_FLAG_DATA)
2829 file->position = offset;
2833 // If we have the data in our read buffer, we don't need to actually seek
2834 if (file->position - file->buff_len <= offset && offset <= file->position)
2836 file->buff_ind = offset + file->buff_len - file->position;
2840 // Purge cached data
2843 // Unpacked or uncompressed files can seek directly
2844 if (! (file->flags & QFILE_FLAG_DEFLATED))
2846 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2848 file->position = offset;
2852 // Seeking in compressed files is more a hack than anything else,
2853 // but we need to support it, so here we go.
2856 // If we have to go back in the file, we need to restart from the beginning
2857 if (offset <= file->position)
2861 ztk->in_position = 0;
2863 lseek (file->handle, file->offset, SEEK_SET);
2865 // Reset the Zlib stream
2866 ztk->zstream.next_in = ztk->input;
2867 ztk->zstream.avail_in = 0;
2868 qz_inflateReset (&ztk->zstream);
2871 // We need a big buffer to force inflating into it directly
2872 buffersize = 2 * sizeof (file->buff);
2873 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2875 // Skip all data until we reach the requested offset
2876 while (offset > file->position)
2878 fs_offset_t diff = offset - file->position;
2879 fs_offset_t count, len;
2881 count = (diff > buffersize) ? buffersize : diff;
2882 len = FS_Read (file, buffer, count);
2896 ====================
2899 Give the current position in a file
2900 ====================
2902 fs_offset_t FS_Tell (qfile_t* file)
2904 return file->position - file->buff_len + file->buff_ind;
2909 ====================
2912 Give the total size of a file
2913 ====================
2915 fs_offset_t FS_FileSize (qfile_t* file)
2917 return file->real_length;
2922 ====================
2925 Erases any buffered input or output data
2926 ====================
2928 void FS_Purge (qfile_t* file)
2940 Filename are relative to the quake directory.
2941 Always appends a 0 byte.
2944 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2947 unsigned char *buf = NULL;
2948 fs_offset_t filesize = 0;
2950 file = FS_OpenVirtualFile(path, quiet);
2953 filesize = file->real_length;
2956 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2961 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2962 buf[filesize] = '\0';
2963 FS_Read (file, buf, filesize);
2965 if (developer_loadfile.integer)
2966 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2969 if (filesizepointer)
2970 *filesizepointer = filesize;
2979 The filename will be prefixed by the current game directory
2982 qboolean FS_WriteFileInBlocks (const char *filename, const void *const *data, const fs_offset_t *len, size_t count)
2986 fs_offset_t lentotal;
2988 file = FS_OpenRealFile(filename, "wb", false);
2991 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2996 for(i = 0; i < count; ++i)
2998 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)lentotal);
2999 for(i = 0; i < count; ++i)
3000 FS_Write (file, data[i], len[i]);
3005 qboolean FS_WriteFile (const char *filename, const void *data, fs_offset_t len)
3007 return FS_WriteFileInBlocks(filename, &data, &len, 1);
3012 =============================================================================
3014 OTHERS PUBLIC FUNCTIONS
3016 =============================================================================
3024 void FS_StripExtension (const char *in, char *out, size_t size_out)
3032 while ((currentchar = *in) && size_out > 1)
3034 if (currentchar == '.')
3036 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
3038 *out++ = currentchar;
3054 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
3058 // if path doesn't have a .EXT, append extension
3059 // (extension should include the .)
3060 src = path + strlen(path) - 1;
3062 while (*src != '/' && src != path)
3065 return; // it has an extension
3069 strlcat (path, extension, size_path);
3077 Look for a file in the packages and in the filesystem
3080 int FS_FileType (const char *filename)
3082 searchpath_t *search;
3083 char fullpath[MAX_OSPATH];
3085 search = FS_FindFile (filename, NULL, true);
3087 return FS_FILETYPE_NONE;
3089 if(search->pack && !search->pack->vpack)
3090 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
3092 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
3093 return FS_SysFileType(fullpath);
3101 Look for a file in the packages and in the filesystem
3104 qboolean FS_FileExists (const char *filename)
3106 return (FS_FindFile (filename, NULL, true) != NULL);
3114 Look for a file in the filesystem only
3117 int FS_SysFileType (const char *path)
3120 // Sajt - some older sdks are missing this define
3121 # ifndef INVALID_FILE_ATTRIBUTES
3122 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
3125 DWORD result = GetFileAttributes(path);
3127 if(result == INVALID_FILE_ATTRIBUTES)
3128 return FS_FILETYPE_NONE;
3130 if(result & FILE_ATTRIBUTE_DIRECTORY)
3131 return FS_FILETYPE_DIRECTORY;
3133 return FS_FILETYPE_FILE;
3137 if (stat (path,&buf) == -1)
3138 return FS_FILETYPE_NONE;
3141 #define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
3143 if(S_ISDIR(buf.st_mode))
3144 return FS_FILETYPE_DIRECTORY;
3146 return FS_FILETYPE_FILE;
3150 qboolean FS_SysFileExists (const char *path)
3152 return FS_SysFileType (path) != FS_FILETYPE_NONE;
3155 void FS_mkdir (const char *path)
3168 Allocate and fill a search structure with information on matching filenames.
3171 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
3174 searchpath_t *searchpath;
3176 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
3177 stringlist_t resultlist;
3178 stringlist_t dirlist;
3179 const char *slash, *backslash, *colon, *separator;
3181 char temp[MAX_OSPATH];
3183 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
3188 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
3192 stringlistinit(&resultlist);
3193 stringlistinit(&dirlist);
3195 slash = strrchr(pattern, '/');
3196 backslash = strrchr(pattern, '\\');
3197 colon = strrchr(pattern, ':');
3198 separator = max(slash, backslash);
3199 separator = max(separator, colon);
3200 basepathlength = separator ? (separator + 1 - pattern) : 0;
3201 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
3203 memcpy(basepath, pattern, basepathlength);
3204 basepath[basepathlength] = 0;
3206 // search through the path, one element at a time
3207 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
3209 // is the element a pak file?
3210 if (searchpath->pack && !searchpath->pack->vpack)
3212 // look through all the pak file elements
3213 pak = searchpath->pack;
3214 for (i = 0;i < pak->numfiles;i++)
3216 strlcpy(temp, pak->files[i].name, sizeof(temp));
3219 if (matchpattern(temp, (char *)pattern, true))
3221 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3222 if (!strcmp(resultlist.strings[resultlistindex], temp))
3224 if (resultlistindex == resultlist.numstrings)
3226 stringlistappend(&resultlist, temp);
3227 if (!quiet && developer_loading.integer)
3228 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
3231 // strip off one path element at a time until empty
3232 // this way directories are added to the listing if they match the pattern
3233 slash = strrchr(temp, '/');
3234 backslash = strrchr(temp, '\\');
3235 colon = strrchr(temp, ':');
3237 if (separator < slash)
3239 if (separator < backslash)
3240 separator = backslash;
3241 if (separator < colon)
3243 *((char *)separator) = 0;
3249 stringlist_t matchedSet, foundSet;
3250 const char *start = pattern;
3252 stringlistinit(&matchedSet);
3253 stringlistinit(&foundSet);
3254 // add a first entry to the set
3255 stringlistappend(&matchedSet, "");
3256 // iterate through pattern's path
3259 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
3260 char subpath[MAX_OSPATH];
3261 char subpattern[MAX_OSPATH];
3263 // find the next wildcard
3264 wildcard = strchr(start, '?');
3265 asterisk = strchr(start, '*');
3266 if (asterisk && (!wildcard || asterisk < wildcard))
3268 wildcard = asterisk;
3273 nextseparator = strchr( wildcard, '/' );
3277 nextseparator = NULL;
3280 if( !nextseparator ) {
3281 nextseparator = start + strlen( start );
3284 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
3285 // copy everything up except nextseperator
3286 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
3287 // find the last '/' before the wildcard
3288 prevseparator = strrchr( subpattern, '/' );
3290 prevseparator = subpattern;
3293 // copy everything from start to the previous including the '/' (before the wildcard)
3294 // everything up to start is already included in the path of matchedSet's entries
3295 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
3297 // for each entry in matchedSet try to open the subdirectories specified in subpath
3298 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
3299 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
3300 strlcat( temp, subpath, sizeof(temp) );
3301 listdirectory( &foundSet, searchpath->filename, temp );
3303 if( dirlistindex == 0 ) {
3306 // reset the current result set
3307 stringlistfreecontents( &matchedSet );
3308 // match against the pattern
3309 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
3310 const char *direntry = foundSet.strings[ dirlistindex ];
3311 if (matchpattern(direntry, subpattern, true)) {
3312 stringlistappend( &matchedSet, direntry );
3315 stringlistfreecontents( &foundSet );
3317 start = nextseparator;
3320 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
3322 const char *temp = matchedSet.strings[dirlistindex];
3323 if (matchpattern(temp, (char *)pattern, true))
3325 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3326 if (!strcmp(resultlist.strings[resultlistindex], temp))
3328 if (resultlistindex == resultlist.numstrings)
3330 stringlistappend(&resultlist, temp);
3331 if (!quiet && developer_loading.integer)
3332 Con_Printf("SearchDirFile: %s\n", temp);
3336 stringlistfreecontents( &matchedSet );
3340 if (resultlist.numstrings)
3342 stringlistsort(&resultlist);
3343 numfiles = resultlist.numstrings;
3345 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3346 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
3347 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
3348 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
3349 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
3350 search->numfilenames = (int)numfiles;
3353 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
3356 search->filenames[numfiles] = search->filenamesbuffer + numchars;
3357 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
3358 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
3360 numchars += (int)textlen;
3363 stringlistfreecontents(&resultlist);
3369 void FS_FreeSearch(fssearch_t *search)
3374 extern int con_linewidth;
3375 int FS_ListDirectory(const char *pattern, int oneperline)
3384 char linebuf[MAX_INPUTLINE];
3386 search = FS_Search(pattern, true, true);
3389 numfiles = search->numfilenames;
3392 // FIXME: the names could be added to one column list and then
3393 // gradually shifted into the next column if they fit, and then the
3394 // next to make a compact variable width listing but it's a lot more
3396 // find width for columns
3398 for (i = 0;i < numfiles;i++)
3400 l = (int)strlen(search->filenames[i]);
3401 if (columnwidth < l)
3404 // count the spacing character
3406 // calculate number of columns
3407 numcolumns = con_linewidth / columnwidth;
3408 // don't bother with the column printing if it's only one column
3409 if (numcolumns >= 2)
3411 numlines = (numfiles + numcolumns - 1) / numcolumns;
3412 for (i = 0;i < numlines;i++)
3415 for (k = 0;k < numcolumns;k++)
3417 l = i * numcolumns + k;
3420 name = search->filenames[l];
3421 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3422 linebuf[linebufpos++] = name[j];
3423 // space out name unless it's the last on the line
3424 if (k + 1 < numcolumns && l + 1 < numfiles)
3425 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3426 linebuf[linebufpos++] = ' ';
3429 linebuf[linebufpos] = 0;
3430 Con_Printf("%s\n", linebuf);
3437 for (i = 0;i < numfiles;i++)
3438 Con_Printf("%s\n", search->filenames[i]);
3439 FS_FreeSearch(search);
3440 return (int)numfiles;
3443 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3445 const char *pattern;
3448 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3451 if (Cmd_Argc() == 2)
3452 pattern = Cmd_Argv(1);
3455 if (!FS_ListDirectory(pattern, oneperline))
3456 Con_Print("No files found.\n");
3461 FS_ListDirectoryCmd("dir", true);
3466 FS_ListDirectoryCmd("ls", false);
3469 void FS_Which_f(void)
3471 const char *filename;
3474 if (Cmd_Argc() != 2)
3476 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3479 filename = Cmd_Argv(1);
3480 sp = FS_FindFile(filename, &index, true);
3482 Con_Printf("%s isn't anywhere\n", filename);
3488 Con_Printf("%s is in virtual package %sdir\n", filename, sp->pack->shortname);
3490 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3493 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3497 const char *FS_WhichPack(const char *filename)
3500 searchpath_t *sp = FS_FindFile(filename, &index, true);
3502 return sp->pack->shortname;
3508 ====================
3509 FS_IsRegisteredQuakePack
3511 Look for a proof of purchase file file in the requested package
3513 If it is found, this file should NOT be downloaded.
3514 ====================
3516 qboolean FS_IsRegisteredQuakePack(const char *name)
3518 searchpath_t *search;
3521 // search through the path, one element at a time
3522 for (search = fs_searchpaths;search;search = search->next)
3524 if (search->pack && !search->pack->vpack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3525 // TODO do we want to support vpacks in here too?
3527 int (*strcmp_funct) (const char* str1, const char* str2);
3528 int left, right, middle;
3531 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3533 // Look for the file (binary search)
3535 right = pak->numfiles - 1;
3536 while (left <= right)
3540 middle = (left + right) / 2;
3541 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3547 // If we're too far in the list
3554 // we found the requested pack but it is not registered quake
3562 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3565 unsigned char *filedata;
3566 fs_offset_t filesize;
3567 if (filesizepointer)
3568 *filesizepointer = 0;
3569 if (!filename || !*filename)
3571 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3574 if (filesizepointer)
3575 *filesizepointer = filesize;
3576 crc = CRC_Block(filedata, filesize);
3582 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3585 unsigned char *out = NULL;
3589 #ifndef LINK_TO_ZLIB
3594 memset(&strm, 0, sizeof(strm));
3595 strm.zalloc = Z_NULL;
3596 strm.zfree = Z_NULL;
3597 strm.opaque = Z_NULL;
3600 level = Z_DEFAULT_COMPRESSION;
3602 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3604 Con_Printf("FS_Deflate: deflate init error!\n");
3608 strm.next_in = (unsigned char*)data;
3609 strm.avail_in = size;
3611 tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3614 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3615 qz_deflateEnd(&strm);
3619 strm.next_out = tmp;
3620 strm.avail_out = size;
3622 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3624 Con_Printf("FS_Deflate: deflate failed!\n");
3625 qz_deflateEnd(&strm);
3630 if(qz_deflateEnd(&strm) != Z_OK)
3632 Con_Printf("FS_Deflate: deflateEnd failed\n");
3637 if(strm.total_out >= size)
3639 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3644 out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3647 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3653 *deflated_size = (size_t)strm.total_out;
3655 memcpy(out, tmp, strm.total_out);
3661 static void AssertBufsize(sizebuf_t *buf, int length)
3663 if(buf->cursize + length > buf->maxsize)
3665 int oldsize = buf->maxsize;
3666 unsigned char *olddata;
3667 olddata = buf->data;
3668 buf->maxsize += length;
3669 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3672 memcpy(buf->data, olddata, oldsize);
3678 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3682 unsigned char *out = NULL;
3683 unsigned char tmp[2048];
3688 #ifndef LINK_TO_ZLIB
3693 memset(&outbuf, 0, sizeof(outbuf));
3694 outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3695 outbuf.maxsize = sizeof(tmp);
3697 memset(&strm, 0, sizeof(strm));
3698 strm.zalloc = Z_NULL;
3699 strm.zfree = Z_NULL;
3700 strm.opaque = Z_NULL;
3702 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3704 Con_Printf("FS_Inflate: inflate init error!\n");
3705 Mem_Free(outbuf.data);
3709 strm.next_in = (unsigned char*)data;
3710 strm.avail_in = size;
3714 strm.next_out = tmp;
3715 strm.avail_out = sizeof(tmp);
3716 ret = qz_inflate(&strm, Z_NO_FLUSH);
3717 // it either returns Z_OK on progress, Z_STREAM_END on end
3725 case Z_STREAM_ERROR:
3726 Con_Print("FS_Inflate: stream error!\n");
3729 Con_Print("FS_Inflate: data error!\n");
3732 Con_Print("FS_Inflate: mem error!\n");
3735 Con_Print("FS_Inflate: buf error!\n");
3738 Con_Print("FS_Inflate: unknown error!\n");
3742 if(ret != Z_OK && ret != Z_STREAM_END)
3744 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3745 Mem_Free(outbuf.data);
3746 qz_inflateEnd(&strm);
3749 have = sizeof(tmp) - strm.avail_out;
3750 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3751 SZ_Write(&outbuf, tmp, have);
3752 } while(ret != Z_STREAM_END);
3754 qz_inflateEnd(&strm);
3756 out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3759 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3760 Mem_Free(outbuf.data);
3764 memcpy(out, outbuf.data, outbuf.cursize);
3765 Mem_Free(outbuf.data);
3768 *inflated_size = (size_t)outbuf.cursize;