4 Copyright (C) 2003-2006 Mathieu Olivier
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to:
20 Free Software Foundation, Inc.
21 59 Temple Place - Suite 330
22 Boston, MA 02111-1307, USA
36 # include <sys/stat.h>
43 // Win32 requires us to add O_BINARY, but the other OSes don't have it
48 // In case the system doesn't support the O_NONBLOCK flag
53 // largefile support for Win32
55 # define lseek _lseeki64
59 // suppress deprecated warnings
60 # include <sys/stat.h>
65 # define unlink _unlink
69 /** \page fs File System
71 All of Quake's data access is through a hierchal file system, but the contents
72 of the file system can be transparently merged from several sources.
74 The "base directory" is the path to the directory holding the quake.exe and
75 all game directories. The sys_* files pass this to host_init in
76 quakeparms_t->basedir. This can be overridden with the "-basedir" command
77 line parm to allow code debugging in a different directory. The base
78 directory is only used during filesystem initialization.
80 The "game directory" is the first tree on the search path and directory that
81 all generated files (savegames, screenshots, demos, config files) will be
82 saved to. This can be overridden with the "-game" command line parameter.
83 The game directory can never be changed while quake is executing. This is a
84 precaution against having a malicious server instruct clients to write files
85 over areas they shouldn't.
91 =============================================================================
95 =============================================================================
98 // Magic numbers of a ZIP file (big-endian format)
99 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
100 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
101 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
103 // Other constants for ZIP files
104 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
105 #define ZIP_END_CDIR_SIZE 22
106 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
107 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
109 // Zlib constants (from zlib.h)
110 #define Z_SYNC_FLUSH 2
113 #define Z_STREAM_END 1
114 #define Z_STREAM_ERROR (-2)
115 #define Z_DATA_ERROR (-3)
116 #define Z_MEM_ERROR (-4)
117 #define Z_BUF_ERROR (-5)
118 #define ZLIB_VERSION "1.2.3"
122 #define Z_MEMLEVEL_DEFAULT 8
125 #define Z_DEFAULT_COMPRESSION (-1)
127 #define Z_SYNC_FLUSH 2
128 #define Z_FULL_FLUSH 3
131 // Uncomment the following line if the zlib DLL you have still uses
132 // the 1.1.x series calling convention on Win32 (WINAPI)
133 //#define ZLIB_USES_WINAPI
137 =============================================================================
141 =============================================================================
144 /*! Zlib stream (from zlib.h)
145 * \warning: some pointers we don't use directly have
146 * been cast to "void*" for a matter of simplicity
150 unsigned char *next_in; ///< next input byte
151 unsigned int avail_in; ///< number of bytes available at next_in
152 unsigned long total_in; ///< total nb of input bytes read so far
154 unsigned char *next_out; ///< next output byte should be put there
155 unsigned int avail_out; ///< remaining free space at next_out
156 unsigned long total_out; ///< total nb of bytes output so far
158 char *msg; ///< last error message, NULL if no error
159 void *state; ///< not visible by applications
161 void *zalloc; ///< used to allocate the internal state
162 void *zfree; ///< used to free the internal state
163 void *opaque; ///< private data object passed to zalloc and zfree
165 int data_type; ///< best guess about the data type: ascii or binary
166 unsigned long adler; ///< adler32 value of the uncompressed data
167 unsigned long reserved; ///< reserved for future use
171 /// inside a package (PAK or PK3)
172 #define QFILE_FLAG_PACKED (1 << 0)
173 /// file is compressed using the deflate algorithm (PK3 only)
174 #define QFILE_FLAG_DEFLATED (1 << 1)
175 /// file is actually already loaded data
176 #define QFILE_FLAG_DATA (1 << 2)
178 #define FILE_BUFF_SIZE 2048
182 size_t comp_length; ///< length of the compressed file
183 size_t in_ind, in_len; ///< input buffer current index and length
184 size_t in_position; ///< position in the compressed file
185 unsigned char input [FILE_BUFF_SIZE];
191 int handle; ///< file descriptor
192 fs_offset_t real_length; ///< uncompressed file size (for files opened in "read" mode)
193 fs_offset_t position; ///< current position in the file
194 fs_offset_t offset; ///< offset into the package (0 if external file)
195 int ungetc; ///< single stored character from ungetc, cleared to EOF when read
198 fs_offset_t buff_ind, buff_len; ///< buffer current index and length
199 unsigned char buff [FILE_BUFF_SIZE];
201 ztoolkit_t* ztk; ///< For zipped files.
203 const unsigned char *data; ///< For data files.
207 // ------ PK3 files on disk ------ //
209 // You can get the complete ZIP format description from PKWARE website
211 typedef struct pk3_endOfCentralDir_s
213 unsigned int signature;
214 unsigned short disknum;
215 unsigned short cdir_disknum; ///< number of the disk with the start of the central directory
216 unsigned short localentries; ///< number of entries in the central directory on this disk
217 unsigned short nbentries; ///< total number of entries in the central directory on this disk
218 unsigned int cdir_size; ///< size of the central directory
219 unsigned int cdir_offset; ///< with respect to the starting disk number
220 unsigned short comment_size;
221 } pk3_endOfCentralDir_t;
224 // ------ PAK files on disk ------ //
225 typedef struct dpackfile_s
228 int filepos, filelen;
231 typedef struct dpackheader_s
239 /*! \name Packages in memory
242 /// the offset in packfile_t is the true contents offset
243 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
244 /// file compressed using the deflate algorithm
245 #define PACKFILE_FLAG_DEFLATED (1 << 1)
246 /// file is a symbolic link
247 #define PACKFILE_FLAG_SYMLINK (1 << 2)
249 typedef struct packfile_s
251 char name [MAX_QPATH];
254 fs_offset_t packsize; ///< size in the package
255 fs_offset_t realsize; ///< real file size (uncompressed)
258 typedef struct pack_s
260 char filename [MAX_OSPATH];
261 char shortname [MAX_QPATH];
263 int ignorecase; ///< PK3 ignores case
269 /// Search paths for files (including packages)
270 typedef struct searchpath_s
272 // only one of filename / pack will be used
273 char filename[MAX_OSPATH];
275 struct searchpath_s *next;
280 =============================================================================
284 =============================================================================
289 void FS_Which_f(void);
291 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
292 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
293 fs_offset_t offset, fs_offset_t packsize,
294 fs_offset_t realsize, int flags);
298 =============================================================================
302 =============================================================================
305 mempool_t *fs_mempool;
307 searchpath_t *fs_searchpaths = NULL;
309 #define MAX_FILES_IN_PACK 65536
311 char fs_gamedir[MAX_OSPATH];
312 char fs_basedir[MAX_OSPATH];
314 // list of active game directories (empty if not running a mod)
315 int fs_numgamedirs = 0;
316 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
318 cvar_t scr_screenshot_name = {0, "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)"};
319 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"};
323 =============================================================================
325 PRIVATE FUNCTIONS - PK3 HANDLING
327 =============================================================================
330 // Functions exported from zlib
331 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
332 # define ZEXPORT WINAPI
337 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
338 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
339 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
340 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
341 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
342 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
343 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
345 #define qz_inflateInit2(strm, windowBits) \
346 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
347 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
348 qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
350 // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
352 static dllfunction_t zlibfuncs[] =
354 {"inflate", (void **) &qz_inflate},
355 {"inflateEnd", (void **) &qz_inflateEnd},
356 {"inflateInit2_", (void **) &qz_inflateInit2_},
357 {"inflateReset", (void **) &qz_inflateReset},
358 {"deflateInit2_", (void **) &qz_deflateInit2_},
359 {"deflateEnd", (void **) &qz_deflateEnd},
360 {"deflate", (void **) &qz_deflate},
364 /// Handle for Zlib DLL
365 static dllhandle_t zlib_dll = NULL;
368 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
369 static dllfunction_t shfolderfuncs[] =
371 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
374 static dllhandle_t shfolder_dll = NULL;
384 void PK3_CloseLibrary (void)
386 Sys_UnloadLibrary (&zlib_dll);
394 Try to load the Zlib DLL
397 qboolean PK3_OpenLibrary (void)
399 const char* dllnames [] =
404 # ifdef ZLIB_USES_WINAPI
410 #elif defined(MACOSX)
424 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
431 See if zlib is available
434 qboolean FS_HasZlib(void)
436 PK3_OpenLibrary(); // to be safe
437 return (zlib_dll != 0);
442 PK3_GetEndOfCentralDir
444 Extract the end of the central directory from a PK3 package
447 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
449 fs_offset_t filesize, maxsize;
450 unsigned char *buffer, *ptr;
453 // Get the package size
454 filesize = lseek (packhandle, 0, SEEK_END);
455 if (filesize < ZIP_END_CDIR_SIZE)
458 // Load the end of the file in memory
459 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
462 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
463 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
464 lseek (packhandle, filesize - maxsize, SEEK_SET);
465 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
471 // Look for the end of central dir signature around the end of the file
472 maxsize -= ZIP_END_CDIR_SIZE;
473 ptr = &buffer[maxsize];
475 while (BuffBigLong (ptr) != ZIP_END_HEADER)
487 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
488 eocd->signature = LittleLong (eocd->signature);
489 eocd->disknum = LittleShort (eocd->disknum);
490 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
491 eocd->localentries = LittleShort (eocd->localentries);
492 eocd->nbentries = LittleShort (eocd->nbentries);
493 eocd->cdir_size = LittleLong (eocd->cdir_size);
494 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
495 eocd->comment_size = LittleShort (eocd->comment_size);
507 Extract the file list from a PK3 file
510 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
512 unsigned char *central_dir, *ptr;
514 fs_offset_t remaining;
516 // Load the central directory in memory
517 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
518 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
519 if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size)
521 Mem_Free (central_dir);
525 // Extract the files properties
526 // The parsing is done "by hand" because some fields have variable sizes and
527 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
528 remaining = eocd->cdir_size;
531 for (ind = 0; ind < eocd->nbentries; ind++)
533 fs_offset_t namesize, count;
535 // Checking the remaining size
536 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
538 Mem_Free (central_dir);
541 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
544 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
546 Mem_Free (central_dir);
550 namesize = BuffLittleShort (&ptr[28]); // filename length
552 // Check encryption, compression, and attributes
553 // 1st uint8 : general purpose bit flag
554 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
556 // LordHavoc: bit 3 would be a problem if we were scanning the archive
557 // but is not a problem in the central directory where the values are
560 // bit 3 seems to always be set by the standard Mac OSX zip maker
562 // 2nd uint8 : external file attributes
563 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
564 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
566 // Still enough bytes for the name?
567 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
569 Mem_Free (central_dir);
573 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
574 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
576 char filename [sizeof (pack->files[0].name)];
577 fs_offset_t offset, packsize, realsize;
580 // Extract the name (strip it if necessary)
581 namesize = min(namesize, (int)sizeof (filename) - 1);
582 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
583 filename[namesize] = '\0';
585 if (BuffLittleShort (&ptr[10]))
586 flags = PACKFILE_FLAG_DEFLATED;
589 offset = BuffLittleLong (&ptr[42]);
590 packsize = BuffLittleLong (&ptr[20]);
591 realsize = BuffLittleLong (&ptr[24]);
593 switch(ptr[5]) // C_VERSION_MADE_BY_1
598 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
599 // can't use S_ISLNK here, as this has to compile on non-UNIX too
600 flags |= PACKFILE_FLAG_SYMLINK;
604 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
608 // Skip the name, additionnal field, and comment
609 // 1er uint16 : extra field length
610 // 2eme uint16 : file comment length
611 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
612 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
616 // If the package is empty, central_dir is NULL here
617 if (central_dir != NULL)
618 Mem_Free (central_dir);
619 return pack->numfiles;
627 Create a package entry associated with a PK3 file
630 pack_t *FS_LoadPackPK3 (const char *packfile)
633 pk3_endOfCentralDir_t eocd;
638 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
640 packhandle = open (packfile, O_RDONLY | O_BINARY);
645 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
647 Con_Printf ("%s is not a PK3 file\n", packfile);
652 // Multi-volume ZIP archives are NOT allowed
653 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
655 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
660 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
661 // since eocd.nbentries is an unsigned 16 bits integer
662 #if MAX_FILES_IN_PACK < 65535
663 if (eocd.nbentries > MAX_FILES_IN_PACK)
665 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
671 // Create a package structure in memory
672 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
673 pack->ignorecase = true; // PK3 ignores case
674 strlcpy (pack->filename, packfile, sizeof (pack->filename));
675 pack->handle = packhandle;
676 pack->numfiles = eocd.nbentries;
677 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
679 real_nb_files = PK3_BuildFileList (pack, &eocd);
680 if (real_nb_files < 0)
682 Con_Printf ("%s is not a valid PK3 file\n", packfile);
688 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
695 PK3_GetTrueFileOffset
697 Find where the true file data offset is
700 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
702 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
706 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
709 // Load the local file description
710 lseek (pack->handle, pfile->offset, SEEK_SET);
711 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
712 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
714 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
718 // Skip name and extra field
719 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
721 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
727 =============================================================================
729 OTHER PRIVATE FUNCTIONS
731 =============================================================================
739 Add a file to the list of files contained into a package
742 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
743 fs_offset_t offset, fs_offset_t packsize,
744 fs_offset_t realsize, int flags)
746 int (*strcmp_funct) (const char* str1, const char* str2);
747 int left, right, middle;
750 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
752 // Look for the slot we should put that file into (binary search)
754 right = pack->numfiles - 1;
755 while (left <= right)
759 middle = (left + right) / 2;
760 diff = strcmp_funct (pack->files[middle].name, name);
762 // If we found the file, there's a problem
764 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
766 // If we're too far in the list
773 // We have to move the right of the list by one slot to free the one we need
774 pfile = &pack->files[left];
775 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
778 strlcpy (pfile->name, name, sizeof (pfile->name));
779 pfile->offset = offset;
780 pfile->packsize = packsize;
781 pfile->realsize = realsize;
782 pfile->flags = flags;
792 Only used for FS_OpenRealFile.
795 void FS_CreatePath (char *path)
799 for (ofs = path+1 ; *ofs ; ofs++)
801 if (*ofs == '/' || *ofs == '\\')
803 // create the directory
819 void FS_Path_f (void)
823 Con_Print("Current search path:\n");
824 for (s=fs_searchpaths ; s ; s=s->next)
827 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
829 Con_Printf("%s\n", s->filename);
839 /*! Takes an explicit (not game tree related) path to a pak file.
840 *Loads the header and directory, adding the files at the beginning
841 *of the list so they override previous pack files.
843 pack_t *FS_LoadPackPAK (const char *packfile)
845 dpackheader_t header;
852 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
854 packhandle = open (packfile, O_RDONLY | O_BINARY);
858 if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
860 Con_Printf ("%s is not a packfile\n", packfile);
864 if (memcmp(header.id, "PACK", 4))
866 Con_Printf ("%s is not a packfile\n", packfile);
870 header.dirofs = LittleLong (header.dirofs);
871 header.dirlen = LittleLong (header.dirlen);
873 if (header.dirlen % sizeof(dpackfile_t))
875 Con_Printf ("%s has an invalid directory size\n", packfile);
880 numpackfiles = header.dirlen / sizeof(dpackfile_t);
882 if (numpackfiles > MAX_FILES_IN_PACK)
884 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
889 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
890 lseek (packhandle, header.dirofs, SEEK_SET);
891 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
893 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
899 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
900 pack->ignorecase = false; // PAK is case sensitive
901 strlcpy (pack->filename, packfile, sizeof (pack->filename));
902 pack->handle = packhandle;
904 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
906 // parse the directory
907 for (i = 0;i < numpackfiles;i++)
909 fs_offset_t offset = LittleLong (info[i].filepos);
910 fs_offset_t size = LittleLong (info[i].filelen);
912 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
917 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
926 /*! Adds the given pack to the search path.
927 * The pack type is autodetected by the file extension.
929 * Returns true if the file was successfully added to the
930 * search path or if it was already included.
932 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
936 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
938 searchpath_t *search;
940 const char *ext = FS_FileExtension(pakfile);
942 for(search = fs_searchpaths; search; search = search->next)
944 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
947 *already_loaded = true;
948 return true; // already loaded
953 *already_loaded = false;
955 if(!strcasecmp(ext, "pak"))
956 pak = FS_LoadPackPAK (pakfile);
957 else if(!strcasecmp(ext, "pk3"))
958 pak = FS_LoadPackPK3 (pakfile);
960 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
964 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
965 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
968 // find the first item whose next one is a pack or NULL
969 searchpath_t *insertion_point = 0;
970 if(fs_searchpaths && !fs_searchpaths->pack)
972 insertion_point = fs_searchpaths;
975 if(!insertion_point->next)
977 if(insertion_point->next->pack)
979 insertion_point = insertion_point->next;
982 // If insertion_point is NULL, this means that either there is no
983 // item in the list yet, or that the very first item is a pack. In
984 // that case, we want to insert at the beginning...
987 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
989 search->next = fs_searchpaths;
990 fs_searchpaths = search;
993 // otherwise we want to append directly after insertion_point.
995 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
997 search->next = insertion_point->next;
998 insertion_point->next = search;
1003 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1005 search->next = fs_searchpaths;
1006 fs_searchpaths = search;
1012 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1023 /*! Adds the given pack to the search path and searches for it in the game path.
1024 * The pack type is autodetected by the file extension.
1026 * Returns true if the file was successfully added to the
1027 * search path or if it was already included.
1029 * If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1030 * plain directories.
1032 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1034 char fullpath[MAX_QPATH];
1036 searchpath_t *search;
1039 *already_loaded = false;
1041 // then find the real name...
1042 search = FS_FindFile(pakfile, &index, true);
1043 if(!search || search->pack)
1045 Con_Printf("could not find pak \"%s\"\n", pakfile);
1049 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1051 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1059 Sets fs_gamedir, adds the directory to the head of the path,
1060 then loads and adds pak1.pak pak2.pak ...
1063 void FS_AddGameDirectory (const char *dir)
1067 searchpath_t *search;
1069 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1071 stringlistinit(&list);
1072 listdirectory(&list, "", dir);
1073 stringlistsort(&list);
1075 // add any PAK package in the directory
1076 for (i = 0;i < list.numstrings;i++)
1078 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1080 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1084 // add any PK3 package in the directory
1085 for (i = 0;i < list.numstrings;i++)
1087 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1089 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1093 stringlistfreecontents(&list);
1095 // Add the directory to the search path
1096 // (unpacked files have the priority over packed files)
1097 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1098 strlcpy (search->filename, dir, sizeof (search->filename));
1099 search->next = fs_searchpaths;
1100 fs_searchpaths = search;
1109 void FS_AddGameHierarchy (const char *dir)
1112 char userdir[MAX_QPATH];
1114 TCHAR mydocsdir[MAX_PATH + 1];
1115 #if _MSC_VER >= 1400
1121 // Add the common game directory
1122 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1126 // Add the personal game directory
1128 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1130 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1131 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", userdir);
1135 // use the environment
1136 #if _MSC_VER >= 1400
1137 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1139 homedir = getenv("USERPROFILE");
1144 dpsnprintf(userdir, sizeof(userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1145 #if _MSC_VER >= 1400
1148 Con_DPrintf("Obtained personal directory %s from environment\n", userdir);
1151 *userdir = 0; // just to make sure it hasn't been written to by SHGetFolderPath returning failure
1155 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1157 homedir = getenv ("HOME");
1159 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1162 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1167 if(!COM_CheckParm("-mygames"))
1169 #if _MSC_VER >= 1400
1171 _sopen_s(&fd, va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
1173 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1178 *userdir = 0; // we have write access to the game dir, so let's use it
1183 if(COM_CheckParm("-nohome"))
1186 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1187 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1190 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1199 const char *FS_FileExtension (const char *in)
1201 const char *separator, *backslash, *colon, *dot;
1203 separator = strrchr(in, '/');
1204 backslash = strrchr(in, '\\');
1205 if (!separator || separator < backslash)
1206 separator = backslash;
1207 colon = strrchr(in, ':');
1208 if (!separator || separator < colon)
1211 dot = strrchr(in, '.');
1212 if (dot == NULL || (separator && (dot < separator)))
1224 const char *FS_FileWithoutPath (const char *in)
1226 const char *separator, *backslash, *colon;
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)
1235 return separator ? separator + 1 : in;
1244 void FS_ClearSearchPath (void)
1246 // unload all packs and directory information, close all pack files
1247 // (if a qfile is still reading a pack it won't be harmed because it used
1248 // dup() to get its own handle already)
1249 while (fs_searchpaths)
1251 searchpath_t *search = fs_searchpaths;
1252 fs_searchpaths = search->next;
1256 close(search->pack->handle);
1257 // free any memory associated with it
1258 if (search->pack->files)
1259 Mem_Free(search->pack->files);
1260 Mem_Free(search->pack);
1272 void FS_Rescan (void)
1275 qboolean fs_modified = false;
1277 FS_ClearSearchPath();
1279 // add the game-specific paths
1280 // gamedirname1 (typically id1)
1281 FS_AddGameHierarchy (gamedirname1);
1282 // update the com_modname (used for server info)
1283 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1285 // add the game-specific path, if any
1286 // (only used for mission packs and the like, which should set fs_modified)
1290 FS_AddGameHierarchy (gamedirname2);
1294 // Adds basedir/gamedir as an override game
1295 // LordHavoc: now supports multiple -game directories
1296 // set the com_modname (reported in server info)
1297 for (i = 0;i < fs_numgamedirs;i++)
1300 FS_AddGameHierarchy (fs_gamedirs[i]);
1301 // update the com_modname (used server info)
1302 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1305 // set the default screenshot name to either the mod name or the
1306 // gamemode screenshot name
1307 if (strcmp(com_modname, gamedirname1))
1308 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1310 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1312 // If "-condebug" is in the command line, remove the previous log file
1313 if (COM_CheckParm ("-condebug") != 0)
1314 unlink (va("%s/qconsole.log", fs_gamedir));
1316 // look for the pop.lmp file and set registered to true if it is found
1317 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1320 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1322 Con_Print("Playing shareware version.\n");
1326 Cvar_Set ("registered", "1");
1327 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1328 Con_Print("Playing registered version.\n");
1331 // unload all wads so that future queries will return the new data
1335 void FS_Rescan_f(void)
1345 extern void Host_SaveConfig (void);
1346 extern void Host_LoadConfig_f (void);
1347 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1351 if (fs_numgamedirs == numgamedirs)
1353 for (i = 0;i < numgamedirs;i++)
1354 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1356 if (i == numgamedirs)
1357 return true; // already using this set of gamedirs, do nothing
1360 if (numgamedirs > MAX_GAMEDIRS)
1363 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1364 return false; // too many gamedirs
1367 for (i = 0;i < numgamedirs;i++)
1369 // if string is nasty, reject it
1370 if(FS_CheckNastyPath(gamedirs[i], true))
1373 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1374 return false; // nasty gamedirs
1378 for (i = 0;i < numgamedirs;i++)
1380 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1383 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1384 return false; // missing gamedirs
1390 fs_numgamedirs = numgamedirs;
1391 for (i = 0;i < fs_numgamedirs;i++)
1392 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1394 // reinitialize filesystem to detect the new paks
1397 // exec the new config
1398 Host_LoadConfig_f();
1400 // unload all sounds so they will be reloaded from the new files as needed
1401 S_UnloadAllSounds_f();
1403 // reinitialize renderer (this reloads hud/console background/etc)
1404 R_Modules_Restart();
1414 void FS_GameDir_f (void)
1418 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1422 Con_Printf("gamedirs active:");
1423 for (i = 0;i < fs_numgamedirs;i++)
1424 Con_Printf(" %s", fs_gamedirs[i]);
1429 numgamedirs = Cmd_Argc() - 1;
1430 if (numgamedirs > MAX_GAMEDIRS)
1432 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1436 for (i = 0;i < numgamedirs;i++)
1437 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1439 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1441 // actually, changing during game would work fine, but would be stupid
1442 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1446 // halt demo playback to close the file
1449 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1458 qboolean FS_CheckGameDir(const char *gamedir)
1462 stringlistinit(&list);
1463 listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
1464 success = list.numstrings > 0;
1465 stringlistfreecontents(&list);
1480 const char* dllnames [] =
1482 "shfolder.dll", // IE 4, or Win NT and higher
1485 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1486 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1489 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1491 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1493 // If the base directory is explicitly defined by the compilation process
1494 #ifdef DP_FS_BASEDIR
1495 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1497 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1500 // FIXME: is there a better way to find the directory outside the .app?
1501 if (strstr(com_argv[0], ".app/"))
1505 split = strstr(com_argv[0], ".app/");
1506 while (split > com_argv[0] && *split != '/')
1508 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1509 fs_basedir[split - com_argv[0]] = 0;
1517 // Overrides the system supplied base directory (under GAMENAME)
1518 // 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)
1519 i = COM_CheckParm ("-basedir");
1520 if (i && i < com_argc-1)
1522 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1523 i = (int)strlen (fs_basedir);
1524 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1525 fs_basedir[i-1] = 0;
1528 // add a path separator to the end of the basedir if it lacks one
1529 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1530 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1532 if (!FS_CheckGameDir(gamedirname1))
1533 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1535 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1536 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1539 // Adds basedir/gamedir as an override game
1540 // LordHavoc: now supports multiple -game directories
1541 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1545 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1548 if (FS_CheckNastyPath(com_argv[i], true))
1549 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1550 if (!FS_CheckGameDir(com_argv[i]))
1551 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1552 // add the gamedir to the list of active gamedirs
1553 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1558 // generate the searchpath
1562 void FS_Init_Commands(void)
1564 Cvar_RegisterVariable (&scr_screenshot_name);
1565 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1567 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1568 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1569 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1570 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1571 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1572 Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1580 void FS_Shutdown (void)
1582 // close all pack files and such
1583 // (hopefully there aren't any other open files, but they'll be cleaned up
1584 // by the OS anyway)
1585 FS_ClearSearchPath();
1586 Mem_FreePool (&fs_mempool);
1589 Sys_UnloadLibrary (&shfolder_dll);
1594 ====================
1597 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1598 ====================
1600 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1606 // Parse the mode string
1615 opt = O_CREAT | O_TRUNC;
1619 opt = O_CREAT | O_APPEND;
1622 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1625 for (ind = 1; mode[ind] != '\0'; ind++)
1636 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1637 filepath, mode, mode[ind]);
1644 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1645 memset (file, 0, sizeof (*file));
1648 #if _MSC_VER >= 1400
1649 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1651 file->handle = open (filepath, mod | opt, 0666);
1653 if (file->handle < 0)
1659 file->real_length = lseek (file->handle, 0, SEEK_END);
1661 // For files opened in append mode, we start at the end of the file
1663 file->position = file->real_length;
1665 lseek (file->handle, 0, SEEK_SET);
1675 Open a packed file using its package file descriptor
1678 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1684 pfile = &pack->files[pack_ind];
1686 // If we don't have the true offset, get it now
1687 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1688 if (!PK3_GetTrueFileOffset (pfile, pack))
1691 // No Zlib DLL = no compressed files
1692 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1694 Con_Printf("WARNING: can't open the compressed file %s\n"
1695 "You need the Zlib DLL to use compressed files\n",
1700 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1701 // the dup() call to avoid having to close the dup_handle on error here
1702 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1704 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1705 pfile->name, pack->filename, (int) pfile->offset);
1709 dup_handle = dup (pack->handle);
1712 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1716 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1717 memset (file, 0, sizeof (*file));
1718 file->handle = dup_handle;
1719 file->flags = QFILE_FLAG_PACKED;
1720 file->real_length = pfile->realsize;
1721 file->offset = pfile->offset;
1725 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1729 file->flags |= QFILE_FLAG_DEFLATED;
1731 // We need some more variables
1732 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1734 ztk->comp_length = pfile->packsize;
1736 // Initialize zlib stream
1737 ztk->zstream.next_in = ztk->input;
1738 ztk->zstream.avail_in = 0;
1740 /* From Zlib's "unzip.c":
1742 * windowBits is passed < 0 to tell that there is no zlib header.
1743 * Note that in this case inflate *requires* an extra "dummy" byte
1744 * after the compressed stream in order to complete decompression and
1745 * return Z_STREAM_END.
1746 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1747 * size of both compressed and uncompressed data
1749 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1751 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1757 ztk->zstream.next_out = file->buff;
1758 ztk->zstream.avail_out = sizeof (file->buff);
1767 ====================
1770 Return true if the path should be rejected due to one of the following:
1771 1: path elements that are non-portable
1772 2: path elements that would allow access to files outside the game directory,
1773 or are just not a good idea for a mod to be using.
1774 ====================
1776 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1778 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1782 // Windows: don't allow \ in filenames (windows-only), period.
1783 // (on Windows \ is a directory separator, but / is also supported)
1784 if (strstr(path, "\\"))
1785 return 1; // non-portable
1787 // Mac: don't allow Mac-only filenames - : is a directory separator
1788 // instead of /, but we rely on / working already, so there's no reason to
1789 // support a Mac-only path
1790 // Amiga and Windows: : tries to go to root of drive
1791 if (strstr(path, ":"))
1792 return 1; // non-portable attempt to go to root of drive
1794 // Amiga: // is parent directory
1795 if (strstr(path, "//"))
1796 return 1; // non-portable attempt to go to parent directory
1798 // all: don't allow going to parent directory (../ or /../)
1799 if (strstr(path, ".."))
1800 return 2; // attempt to go outside the game directory
1802 // Windows and UNIXes: don't allow absolute paths
1804 return 2; // attempt to go outside the game directory
1806 // 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
1807 if (strchr(path, '.'))
1811 // gamedir is entirely path elements, so simply forbid . entirely
1814 if (strchr(path, '.') < strrchr(path, '/'))
1815 return 2; // possible attempt to go outside the game directory
1818 // all: forbid trailing slash on gamedir
1819 if (isgamedir && path[strlen(path)-1] == '/')
1822 // all: forbid leading dot on any filename for any reason
1823 if (strstr(path, "/."))
1824 return 2; // attempt to go outside the game directory
1826 // after all these checks we're pretty sure it's a / separated filename
1827 // and won't do much if any harm
1833 ====================
1836 Look for a file in the packages and in the filesystem
1838 Return the searchpath where the file was found (or NULL)
1839 and the file index in the package if relevant
1840 ====================
1842 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1844 searchpath_t *search;
1847 // search through the path, one element at a time
1848 for (search = fs_searchpaths;search;search = search->next)
1850 // is the element a pak file?
1853 int (*strcmp_funct) (const char* str1, const char* str2);
1854 int left, right, middle;
1857 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1859 // Look for the file (binary search)
1861 right = pak->numfiles - 1;
1862 while (left <= right)
1866 middle = (left + right) / 2;
1867 diff = strcmp_funct (pak->files[middle].name, name);
1872 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1874 // yes, but the first one is empty so we treat it as not being there
1875 if (!quiet && developer.integer >= 10)
1876 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1883 if (!quiet && developer.integer >= 10)
1884 Con_Printf("FS_FindFile: %s in %s\n",
1885 pak->files[middle].name, pak->filename);
1892 // If we're too far in the list
1901 char netpath[MAX_OSPATH];
1902 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1903 if (FS_SysFileExists (netpath))
1905 if (!quiet && developer.integer >= 10)
1906 Con_Printf("FS_FindFile: %s\n", netpath);
1915 if (!quiet && developer.integer >= 10)
1916 Con_Printf("FS_FindFile: can't find %s\n", name);
1928 Look for a file in the search paths and open it in read-only mode
1931 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
1933 searchpath_t *search;
1936 search = FS_FindFile (filename, &pack_ind, quiet);
1942 // Found in the filesystem?
1945 char path [MAX_OSPATH];
1946 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1947 return FS_SysOpen (path, "rb", nonblocking);
1950 // So, we found it in a package...
1952 // Is it a PK3 symlink?
1953 // TODO also handle directory symlinks by parsing the whole structure...
1954 // but heck, file symlinks are good enough for now
1955 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
1957 if(symlinkLevels <= 0)
1959 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
1964 char linkbuf[MAX_QPATH];
1966 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
1967 const char *mergeslash;
1972 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
1978 // Now combine the paths...
1979 mergeslash = strrchr(filename, '/');
1980 mergestart = linkbuf;
1982 mergeslash = filename;
1983 while(!strncmp(mergestart, "../", 3))
1986 while(mergeslash > filename)
1989 if(*mergeslash == '/')
1993 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
1994 if(mergeslash == filename)
1996 // Either mergeslash == filename, then we just replace the name (done below)
2000 // Or, we append the name after mergeslash;
2001 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2002 int spaceNeeded = mergeslash - filename + 1;
2003 int spaceRemoved = mergestart - linkbuf;
2004 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2006 Con_DPrintf("symlink: too long path rejected\n");
2009 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2010 memcpy(linkbuf, filename, spaceNeeded);
2011 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2012 mergestart = linkbuf;
2014 if (!quiet && developer_loading.integer)
2015 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2016 if(FS_CheckNastyPath (mergestart, false))
2018 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2021 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2025 return FS_OpenPackedFile (search->pack, pack_ind);
2030 =============================================================================
2032 MAIN PUBLIC FUNCTIONS
2034 =============================================================================
2038 ====================
2041 Open a file in the userpath. The syntax is the same as fopen
2042 Used for savegame scanning in menu, and all file writing.
2043 ====================
2045 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2047 char real_path [MAX_OSPATH];
2049 if (FS_CheckNastyPath(filepath, false))
2051 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2055 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2057 // If the file is opened in "write", "append", or "read/write" mode,
2058 // create directories up to the file.
2059 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2060 FS_CreatePath (real_path);
2061 return FS_SysOpen (real_path, mode, false);
2066 ====================
2069 Open a file. The syntax is the same as fopen
2070 ====================
2072 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2074 if (FS_CheckNastyPath(filepath, false))
2076 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2080 return FS_OpenReadFile (filepath, quiet, false, 16);
2085 ====================
2088 Open a file. The syntax is the same as fopen
2089 ====================
2091 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2094 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2095 memset (file, 0, sizeof (*file));
2096 file->flags = QFILE_FLAG_DATA;
2098 file->real_length = size;
2104 ====================
2108 ====================
2110 int FS_Close (qfile_t* file)
2112 if(file->flags & QFILE_FLAG_DATA)
2118 if (close (file->handle))
2123 qz_inflateEnd (&file->ztk->zstream);
2124 Mem_Free (file->ztk);
2133 ====================
2136 Write "datasize" bytes into a file
2137 ====================
2139 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2143 // If necessary, seek to the exact file position we're supposed to be
2144 if (file->buff_ind != file->buff_len)
2145 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2147 // Purge cached data
2150 // Write the buffer and update the position
2151 result = write (file->handle, data, (fs_offset_t)datasize);
2152 file->position = lseek (file->handle, 0, SEEK_CUR);
2153 if (file->real_length < file->position)
2154 file->real_length = file->position;
2164 ====================
2167 Read up to "buffersize" bytes from a file
2168 ====================
2170 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2172 fs_offset_t count, done;
2174 if (buffersize == 0)
2177 // Get rid of the ungetc character
2178 if (file->ungetc != EOF)
2180 ((char*)buffer)[0] = file->ungetc;
2188 if(file->flags & QFILE_FLAG_DATA)
2190 size_t left = file->real_length - file->position;
2191 if(buffersize > left)
2193 memcpy(buffer, file->data + file->position, buffersize);
2194 file->position += buffersize;
2198 // First, we copy as many bytes as we can from "buff"
2199 if (file->buff_ind < file->buff_len)
2201 count = file->buff_len - file->buff_ind;
2202 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2204 memcpy (buffer, &file->buff[file->buff_ind], count);
2205 file->buff_ind += count;
2207 buffersize -= count;
2208 if (buffersize == 0)
2212 // NOTE: at this point, the read buffer is always empty
2214 // If the file isn't compressed
2215 if (! (file->flags & QFILE_FLAG_DEFLATED))
2219 // We must take care to not read after the end of the file
2220 count = file->real_length - file->position;
2222 // If we have a lot of data to get, put them directly into "buffer"
2223 if (buffersize > sizeof (file->buff) / 2)
2225 if (count > (fs_offset_t)buffersize)
2226 count = (fs_offset_t)buffersize;
2227 lseek (file->handle, file->offset + file->position, SEEK_SET);
2228 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2232 file->position += nb;
2234 // Purge cached data
2240 if (count > (fs_offset_t)sizeof (file->buff))
2241 count = (fs_offset_t)sizeof (file->buff);
2242 lseek (file->handle, file->offset + file->position, SEEK_SET);
2243 nb = read (file->handle, file->buff, count);
2246 file->buff_len = nb;
2247 file->position += nb;
2249 // Copy the requested data in "buffer" (as much as we can)
2250 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2251 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2252 file->buff_ind = count;
2260 // If the file is compressed, it's more complicated...
2261 // We cycle through a few operations until we have read enough data
2262 while (buffersize > 0)
2264 ztoolkit_t *ztk = file->ztk;
2267 // NOTE: at this point, the read buffer is always empty
2269 // If "input" is also empty, we need to refill it
2270 if (ztk->in_ind == ztk->in_len)
2272 // If we are at the end of the file
2273 if (file->position == file->real_length)
2276 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2277 if (count > (fs_offset_t)sizeof (ztk->input))
2278 count = (fs_offset_t)sizeof (ztk->input);
2279 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2280 if (read (file->handle, ztk->input, count) != count)
2282 Con_Printf ("FS_Read: unexpected end of file\n");
2287 ztk->in_len = count;
2288 ztk->in_position += count;
2291 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2292 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2294 // Now that we are sure we have compressed data available, we need to determine
2295 // if it's better to inflate it in "file->buff" or directly in "buffer"
2297 // Inflate the data in "file->buff"
2298 if (buffersize < sizeof (file->buff) / 2)
2300 ztk->zstream.next_out = file->buff;
2301 ztk->zstream.avail_out = sizeof (file->buff);
2302 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2303 if (error != Z_OK && error != Z_STREAM_END)
2305 Con_Printf ("FS_Read: Can't inflate file\n");
2308 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2310 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2311 file->position += file->buff_len;
2313 // Copy the requested data in "buffer" (as much as we can)
2314 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2315 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2316 file->buff_ind = count;
2319 // Else, we inflate directly in "buffer"
2322 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2323 ztk->zstream.avail_out = (unsigned int)buffersize;
2324 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2325 if (error != Z_OK && error != Z_STREAM_END)
2327 Con_Printf ("FS_Read: Can't inflate file\n");
2330 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2332 // How much data did it inflate?
2333 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2334 file->position += count;
2336 // Purge cached data
2341 buffersize -= count;
2349 ====================
2352 Print a string into a file
2353 ====================
2355 int FS_Print (qfile_t* file, const char *msg)
2357 return (int)FS_Write (file, msg, strlen (msg));
2361 ====================
2364 Print a string into a file
2365 ====================
2367 int FS_Printf(qfile_t* file, const char* format, ...)
2372 va_start (args, format);
2373 result = FS_VPrintf (file, format, args);
2381 ====================
2384 Print a string into a file
2385 ====================
2387 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2390 fs_offset_t buff_size = MAX_INPUTLINE;
2395 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2396 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2397 if (len >= 0 && len < buff_size)
2399 Mem_Free (tempbuff);
2403 len = write (file->handle, tempbuff, len);
2404 Mem_Free (tempbuff);
2411 ====================
2414 Get the next character of a file
2415 ====================
2417 int FS_Getc (qfile_t* file)
2421 if (FS_Read (file, &c, 1) != 1)
2429 ====================
2432 Put a character back into the read buffer (only supports one character!)
2433 ====================
2435 int FS_UnGetc (qfile_t* file, unsigned char c)
2437 // If there's already a character waiting to be read
2438 if (file->ungetc != EOF)
2447 ====================
2450 Move the position index in a file
2451 ====================
2453 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2456 unsigned char* buffer;
2457 fs_offset_t buffersize;
2459 // Compute the file offset
2463 offset += file->position - file->buff_len + file->buff_ind;
2470 offset += file->real_length;
2476 if (offset < 0 || offset > file->real_length)
2479 if(file->flags & QFILE_FLAG_DATA)
2481 file->position = offset;
2485 // If we have the data in our read buffer, we don't need to actually seek
2486 if (file->position - file->buff_len <= offset && offset <= file->position)
2488 file->buff_ind = offset + file->buff_len - file->position;
2492 // Purge cached data
2495 // Unpacked or uncompressed files can seek directly
2496 if (! (file->flags & QFILE_FLAG_DEFLATED))
2498 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2500 file->position = offset;
2504 // Seeking in compressed files is more a hack than anything else,
2505 // but we need to support it, so here we go.
2508 // If we have to go back in the file, we need to restart from the beginning
2509 if (offset <= file->position)
2513 ztk->in_position = 0;
2515 lseek (file->handle, file->offset, SEEK_SET);
2517 // Reset the Zlib stream
2518 ztk->zstream.next_in = ztk->input;
2519 ztk->zstream.avail_in = 0;
2520 qz_inflateReset (&ztk->zstream);
2523 // We need a big buffer to force inflating into it directly
2524 buffersize = 2 * sizeof (file->buff);
2525 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2527 // Skip all data until we reach the requested offset
2528 while (offset > file->position)
2530 fs_offset_t diff = offset - file->position;
2531 fs_offset_t count, len;
2533 count = (diff > buffersize) ? buffersize : diff;
2534 len = FS_Read (file, buffer, count);
2548 ====================
2551 Give the current position in a file
2552 ====================
2554 fs_offset_t FS_Tell (qfile_t* file)
2556 return file->position - file->buff_len + file->buff_ind;
2561 ====================
2564 Give the total size of a file
2565 ====================
2567 fs_offset_t FS_FileSize (qfile_t* file)
2569 return file->real_length;
2574 ====================
2577 Erases any buffered input or output data
2578 ====================
2580 void FS_Purge (qfile_t* file)
2592 Filename are relative to the quake directory.
2593 Always appends a 0 byte.
2596 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2599 unsigned char *buf = NULL;
2600 fs_offset_t filesize = 0;
2602 file = FS_OpenVirtualFile(path, quiet);
2605 filesize = file->real_length;
2608 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2613 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2614 buf[filesize] = '\0';
2615 FS_Read (file, buf, filesize);
2617 if (developer_loadfile.integer)
2618 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2621 if (filesizepointer)
2622 *filesizepointer = filesize;
2631 The filename will be prefixed by the current game directory
2634 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2638 file = FS_OpenRealFile(filename, "wb", false);
2641 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2645 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2646 FS_Write (file, data, len);
2653 =============================================================================
2655 OTHERS PUBLIC FUNCTIONS
2657 =============================================================================
2665 void FS_StripExtension (const char *in, char *out, size_t size_out)
2673 while ((currentchar = *in) && size_out > 1)
2675 if (currentchar == '.')
2677 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2679 *out++ = currentchar;
2695 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2699 // if path doesn't have a .EXT, append extension
2700 // (extension should include the .)
2701 src = path + strlen(path) - 1;
2703 while (*src != '/' && src != path)
2706 return; // it has an extension
2710 strlcat (path, extension, size_path);
2718 Look for a file in the packages and in the filesystem
2721 int FS_FileType (const char *filename)
2723 searchpath_t *search;
2724 char fullpath[MAX_QPATH];
2726 search = FS_FindFile (filename, NULL, true);
2728 return FS_FILETYPE_NONE;
2731 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2733 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2734 return FS_SysFileType(fullpath);
2742 Look for a file in the packages and in the filesystem
2745 qboolean FS_FileExists (const char *filename)
2747 return (FS_FindFile (filename, NULL, true) != NULL);
2755 Look for a file in the filesystem only
2758 int FS_SysFileType (const char *path)
2761 // Sajt - some older sdks are missing this define
2762 # ifndef INVALID_FILE_ATTRIBUTES
2763 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2766 DWORD result = GetFileAttributes(path);
2768 if(result == INVALID_FILE_ATTRIBUTES)
2769 return FS_FILETYPE_NONE;
2771 if(result & FILE_ATTRIBUTE_DIRECTORY)
2772 return FS_FILETYPE_DIRECTORY;
2774 return FS_FILETYPE_FILE;
2778 if (stat (path,&buf) == -1)
2779 return FS_FILETYPE_NONE;
2781 if(S_ISDIR(buf.st_mode))
2782 return FS_FILETYPE_DIRECTORY;
2784 return FS_FILETYPE_FILE;
2788 qboolean FS_SysFileExists (const char *path)
2790 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2793 void FS_mkdir (const char *path)
2806 Allocate and fill a search structure with information on matching filenames.
2809 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2812 searchpath_t *searchpath;
2814 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2815 stringlist_t resultlist;
2816 stringlist_t dirlist;
2817 const char *slash, *backslash, *colon, *separator;
2819 char temp[MAX_OSPATH];
2821 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2826 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2830 stringlistinit(&resultlist);
2831 stringlistinit(&dirlist);
2833 slash = strrchr(pattern, '/');
2834 backslash = strrchr(pattern, '\\');
2835 colon = strrchr(pattern, ':');
2836 separator = max(slash, backslash);
2837 separator = max(separator, colon);
2838 basepathlength = separator ? (separator + 1 - pattern) : 0;
2839 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2841 memcpy(basepath, pattern, basepathlength);
2842 basepath[basepathlength] = 0;
2844 // search through the path, one element at a time
2845 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2847 // is the element a pak file?
2848 if (searchpath->pack)
2850 // look through all the pak file elements
2851 pak = searchpath->pack;
2852 for (i = 0;i < pak->numfiles;i++)
2854 strlcpy(temp, pak->files[i].name, sizeof(temp));
2857 if (matchpattern(temp, (char *)pattern, true))
2859 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2860 if (!strcmp(resultlist.strings[resultlistindex], temp))
2862 if (resultlistindex == resultlist.numstrings)
2864 stringlistappend(&resultlist, temp);
2865 if (!quiet && developer_loading.integer)
2866 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2869 // strip off one path element at a time until empty
2870 // this way directories are added to the listing if they match the pattern
2871 slash = strrchr(temp, '/');
2872 backslash = strrchr(temp, '\\');
2873 colon = strrchr(temp, ':');
2875 if (separator < slash)
2877 if (separator < backslash)
2878 separator = backslash;
2879 if (separator < colon)
2881 *((char *)separator) = 0;
2887 stringlist_t matchedSet, foundSet;
2888 const char *start = pattern;
2890 stringlistinit(&matchedSet);
2891 stringlistinit(&foundSet);
2892 // add a first entry to the set
2893 stringlistappend(&matchedSet, "");
2894 // iterate through pattern's path
2897 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
2898 char subpath[MAX_OSPATH];
2899 char subpattern[MAX_OSPATH];
2901 // find the next wildcard
2902 wildcard = strchr(start, '?');
2903 asterisk = strchr(start, '*');
2904 if (asterisk && (!wildcard || asterisk < wildcard))
2906 wildcard = asterisk;
2911 nextseparator = strchr( wildcard, '/' );
2915 nextseparator = NULL;
2918 if( !nextseparator ) {
2919 nextseparator = start + strlen( start );
2922 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
2923 // copy everything up except nextseperator
2924 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
2925 // find the last '/' before the wildcard
2926 prevseparator = strrchr( subpattern, '/' );
2928 prevseparator = subpattern;
2931 // copy everything from start to the previous including the '/' (before the wildcard)
2932 // everything up to start is already included in the path of matchedSet's entries
2933 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
2935 // for each entry in matchedSet try to open the subdirectories specified in subpath
2936 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
2937 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
2938 strlcat( temp, subpath, sizeof(temp) );
2939 listdirectory( &foundSet, searchpath->filename, temp );
2941 if( dirlistindex == 0 ) {
2944 // reset the current result set
2945 stringlistfreecontents( &matchedSet );
2946 // match against the pattern
2947 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
2948 const char *direntry = foundSet.strings[ dirlistindex ];
2949 if (matchpattern(direntry, subpattern, true)) {
2950 stringlistappend( &matchedSet, direntry );
2953 stringlistfreecontents( &foundSet );
2955 start = nextseparator;
2958 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
2960 const char *temp = matchedSet.strings[dirlistindex];
2961 if (matchpattern(temp, (char *)pattern, true))
2963 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2964 if (!strcmp(resultlist.strings[resultlistindex], temp))
2966 if (resultlistindex == resultlist.numstrings)
2968 stringlistappend(&resultlist, temp);
2969 if (!quiet && developer_loading.integer)
2970 Con_Printf("SearchDirFile: %s\n", temp);
2974 stringlistfreecontents( &matchedSet );
2978 if (resultlist.numstrings)
2980 stringlistsort(&resultlist);
2981 numfiles = resultlist.numstrings;
2983 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2984 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2985 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2986 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2987 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2988 search->numfilenames = (int)numfiles;
2991 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2994 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2995 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2996 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2998 numchars += (int)textlen;
3001 stringlistfreecontents(&resultlist);
3007 void FS_FreeSearch(fssearch_t *search)
3012 extern int con_linewidth;
3013 int FS_ListDirectory(const char *pattern, int oneperline)
3022 char linebuf[MAX_INPUTLINE];
3024 search = FS_Search(pattern, true, true);
3027 numfiles = search->numfilenames;
3030 // FIXME: the names could be added to one column list and then
3031 // gradually shifted into the next column if they fit, and then the
3032 // next to make a compact variable width listing but it's a lot more
3034 // find width for columns
3036 for (i = 0;i < numfiles;i++)
3038 l = (int)strlen(search->filenames[i]);
3039 if (columnwidth < l)
3042 // count the spacing character
3044 // calculate number of columns
3045 numcolumns = con_linewidth / columnwidth;
3046 // don't bother with the column printing if it's only one column
3047 if (numcolumns >= 2)
3049 numlines = (numfiles + numcolumns - 1) / numcolumns;
3050 for (i = 0;i < numlines;i++)
3053 for (k = 0;k < numcolumns;k++)
3055 l = i * numcolumns + k;
3058 name = search->filenames[l];
3059 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3060 linebuf[linebufpos++] = name[j];
3061 // space out name unless it's the last on the line
3062 if (k + 1 < numcolumns && l + 1 < numfiles)
3063 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3064 linebuf[linebufpos++] = ' ';
3067 linebuf[linebufpos] = 0;
3068 Con_Printf("%s\n", linebuf);
3075 for (i = 0;i < numfiles;i++)
3076 Con_Printf("%s\n", search->filenames[i]);
3077 FS_FreeSearch(search);
3078 return (int)numfiles;
3081 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3083 const char *pattern;
3086 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3089 if (Cmd_Argc() == 2)
3090 pattern = Cmd_Argv(1);
3093 if (!FS_ListDirectory(pattern, oneperline))
3094 Con_Print("No files found.\n");
3099 FS_ListDirectoryCmd("dir", true);
3104 FS_ListDirectoryCmd("ls", false);
3107 void FS_Which_f(void)
3109 const char *filename;
3112 if (Cmd_Argc() != 2)
3114 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3117 filename = Cmd_Argv(1);
3118 sp = FS_FindFile(filename, &index, true);
3120 Con_Printf("%s isn't anywhere\n", filename);
3124 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3126 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3130 const char *FS_WhichPack(const char *filename)
3133 searchpath_t *sp = FS_FindFile(filename, &index, true);
3135 return sp->pack->shortname;
3141 ====================
3142 FS_IsRegisteredQuakePack
3144 Look for a proof of purchase file file in the requested package
3146 If it is found, this file should NOT be downloaded.
3147 ====================
3149 qboolean FS_IsRegisteredQuakePack(const char *name)
3151 searchpath_t *search;
3154 // search through the path, one element at a time
3155 for (search = fs_searchpaths;search;search = search->next)
3157 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3159 int (*strcmp_funct) (const char* str1, const char* str2);
3160 int left, right, middle;
3163 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3165 // Look for the file (binary search)
3167 right = pak->numfiles - 1;
3168 while (left <= right)
3172 middle = (left + right) / 2;
3173 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3179 // If we're too far in the list
3186 // we found the requested pack but it is not registered quake
3194 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3197 unsigned char *filedata;
3198 fs_offset_t filesize;
3199 if (filesizepointer)
3200 *filesizepointer = 0;
3201 if (!filename || !*filename)
3203 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3206 if (filesizepointer)
3207 *filesizepointer = filesize;
3208 crc = CRC_Block(filedata, filesize);
3214 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3217 unsigned char *out = NULL;
3220 memset(&strm, 0, sizeof(strm));
3221 strm.zalloc = Z_NULL;
3222 strm.zfree = Z_NULL;
3223 strm.opaque = Z_NULL;
3226 level = Z_DEFAULT_COMPRESSION;
3228 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3230 Con_Printf("FS_Deflate: deflate init error!\n");
3234 strm.next_in = (unsigned char*)data;
3235 strm.avail_in = size;
3237 tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3240 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3241 qz_deflateEnd(&strm);
3245 strm.next_out = tmp;
3246 strm.avail_out = size;
3248 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3250 Con_Printf("FS_Deflate: deflate failed!\n");
3251 qz_deflateEnd(&strm);
3256 if(qz_deflateEnd(&strm) != Z_OK)
3258 Con_Printf("FS_Deflate: deflateEnd failed\n");
3263 if(strm.total_out >= size)
3265 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3270 out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3273 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3279 *deflated_size = (size_t)strm.total_out;
3281 memcpy(out, tmp, strm.total_out);
3287 static void AssertBufsize(sizebuf_t *buf, int length)
3289 if(buf->cursize + length > buf->maxsize)
3291 int oldsize = buf->maxsize;
3292 unsigned char *olddata;
3293 olddata = buf->data;
3294 buf->maxsize += length;
3295 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3298 memcpy(buf->data, olddata, oldsize);
3304 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3308 unsigned char *out = NULL;
3309 unsigned char tmp[2048];
3313 memset(&outbuf, 0, sizeof(outbuf));
3314 outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3315 outbuf.maxsize = sizeof(tmp);
3317 memset(&strm, 0, sizeof(strm));
3318 strm.zalloc = Z_NULL;
3319 strm.zfree = Z_NULL;
3320 strm.opaque = Z_NULL;
3322 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3324 Con_Printf("FS_Inflate: inflate init error!\n");
3325 Mem_Free(outbuf.data);
3329 strm.next_in = (unsigned char*)data;
3330 strm.avail_in = size;
3334 strm.next_out = tmp;
3335 strm.avail_out = sizeof(tmp);
3336 ret = qz_inflate(&strm, Z_NO_FLUSH);
3337 // it either returns Z_OK on progress, Z_STREAM_END on end
3345 case Z_STREAM_ERROR:
3346 Con_Print("FS_Inflate: stream error!\n");
3349 Con_Print("FS_Inflate: data error!\n");
3352 Con_Print("FS_Inflate: mem error!\n");
3355 Con_Print("FS_Inflate: buf error!\n");
3358 Con_Print("FS_Inflate: unknown error!\n");
3362 if(ret != Z_OK && ret != Z_STREAM_END)
3364 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3365 Mem_Free(outbuf.data);
3366 qz_inflateEnd(&strm);
3369 have = sizeof(tmp) - strm.avail_out;
3370 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3371 SZ_Write(&outbuf, tmp, have);
3372 } while(ret != Z_STREAM_END);
3374 qz_inflateEnd(&strm);
3376 out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3379 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3380 Mem_Free(outbuf.data);
3384 memcpy(out, outbuf.data, outbuf.cursize);
3385 Mem_Free(outbuf.data);
3388 *inflated_size = (size_t)outbuf.cursize;