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
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
149 unsigned char *next_in; // next input byte
150 unsigned int avail_in; // number of bytes available at next_in
151 unsigned long total_in; // total nb of input bytes read so far
153 unsigned char *next_out; // next output byte should be put there
154 unsigned int avail_out; // remaining free space at next_out
155 unsigned long total_out; // total nb of bytes output so far
157 char *msg; // last error message, NULL if no error
158 void *state; // not visible by applications
160 void *zalloc; // used to allocate the internal state
161 void *zfree; // used to free the internal state
162 void *opaque; // private data object passed to zalloc and zfree
164 int data_type; // best guess about the data type: ascii or binary
165 unsigned long adler; // adler32 value of the uncompressed data
166 unsigned long reserved; // reserved for future use
170 // inside a package (PAK or PK3)
171 #define QFILE_FLAG_PACKED (1 << 0)
172 // file is compressed using the deflate algorithm (PK3 only)
173 #define QFILE_FLAG_DEFLATED (1 << 1)
174 // file is actually already loaded data
175 #define QFILE_FLAG_DATA (1 << 2)
177 #define FILE_BUFF_SIZE 2048
181 size_t comp_length; // length of the compressed file
182 size_t in_ind, in_len; // input buffer current index and length
183 size_t in_position; // position in the compressed file
184 unsigned char input [FILE_BUFF_SIZE];
190 int handle; // file descriptor
191 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
192 fs_offset_t position; // current position in the file
193 fs_offset_t offset; // offset into the package (0 if external file)
194 int ungetc; // single stored character from ungetc, cleared to EOF when read
197 fs_offset_t buff_ind, buff_len; // buffer current index and length
198 unsigned char buff [FILE_BUFF_SIZE];
204 const unsigned char *data;
208 // ------ PK3 files on disk ------ //
210 // You can get the complete ZIP format description from PKWARE website
212 typedef struct pk3_endOfCentralDir_s
214 unsigned int signature;
215 unsigned short disknum;
216 unsigned short cdir_disknum; // number of the disk with the start of the central directory
217 unsigned short localentries; // number of entries in the central directory on this disk
218 unsigned short nbentries; // total number of entries in the central directory on this disk
219 unsigned int cdir_size; // size of the central directory
220 unsigned int cdir_offset; // with respect to the starting disk number
221 unsigned short comment_size;
222 } pk3_endOfCentralDir_t;
225 // ------ PAK files on disk ------ //
226 typedef struct dpackfile_s
229 int filepos, filelen;
232 typedef struct dpackheader_s
240 // Packages in memory
241 // the offset in packfile_t is the true contents offset
242 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
243 // file compressed using the deflate algorithm
244 #define PACKFILE_FLAG_DEFLATED (1 << 1)
245 // file is a symbolic link
246 #define PACKFILE_FLAG_SYMLINK (1 << 2)
248 typedef struct packfile_s
250 char name [MAX_QPATH];
253 fs_offset_t packsize; // size in the package
254 fs_offset_t realsize; // real file size (uncompressed)
257 typedef struct pack_s
259 char filename [MAX_OSPATH];
260 char shortname [MAX_QPATH];
262 int ignorecase; // PK3 ignores case
268 // Search paths for files (including packages)
269 typedef struct searchpath_s
271 // only one of filename / pack will be used
272 char filename[MAX_OSPATH];
274 struct searchpath_s *next;
279 =============================================================================
283 =============================================================================
288 void FS_Which_f(void);
290 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
291 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
292 fs_offset_t offset, fs_offset_t packsize,
293 fs_offset_t realsize, int flags);
297 =============================================================================
301 =============================================================================
304 mempool_t *fs_mempool;
306 searchpath_t *fs_searchpaths = NULL;
308 #define MAX_FILES_IN_PACK 65536
310 char fs_gamedir[MAX_OSPATH];
311 char fs_basedir[MAX_OSPATH];
313 // list of active game directories (empty if not running a mod)
314 int fs_numgamedirs = 0;
315 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
317 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)"};
318 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"};
322 =============================================================================
324 PRIVATE FUNCTIONS - PK3 HANDLING
326 =============================================================================
329 // Functions exported from zlib
330 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
331 # define ZEXPORT WINAPI
336 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
337 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
338 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
339 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
340 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
341 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
342 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
344 #define qz_inflateInit2(strm, windowBits) \
345 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
346 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
347 qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
349 // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
351 static dllfunction_t zlibfuncs[] =
353 {"inflate", (void **) &qz_inflate},
354 {"inflateEnd", (void **) &qz_inflateEnd},
355 {"inflateInit2_", (void **) &qz_inflateInit2_},
356 {"inflateReset", (void **) &qz_inflateReset},
357 {"deflateInit2_", (void **) &qz_deflateInit2_},
358 {"deflateEnd", (void **) &qz_deflateEnd},
359 {"deflate", (void **) &qz_deflate},
363 // Handle for Zlib DLL
364 static dllhandle_t zlib_dll = NULL;
367 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
368 static dllfunction_t shfolderfuncs[] =
370 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
373 static dllhandle_t shfolder_dll = NULL;
383 void PK3_CloseLibrary (void)
385 Sys_UnloadLibrary (&zlib_dll);
393 Try to load the Zlib DLL
396 qboolean PK3_OpenLibrary (void)
398 const char* dllnames [] =
403 # ifdef ZLIB_USES_WINAPI
409 #elif defined(MACOSX)
423 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
430 See if zlib is available
433 qboolean FS_HasZlib(void)
435 PK3_OpenLibrary(); // to be safe
436 return (zlib_dll != 0);
441 PK3_GetEndOfCentralDir
443 Extract the end of the central directory from a PK3 package
446 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
448 fs_offset_t filesize, maxsize;
449 unsigned char *buffer, *ptr;
452 // Get the package size
453 filesize = lseek (packhandle, 0, SEEK_END);
454 if (filesize < ZIP_END_CDIR_SIZE)
457 // Load the end of the file in memory
458 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
461 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
462 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
463 lseek (packhandle, filesize - maxsize, SEEK_SET);
464 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
470 // Look for the end of central dir signature around the end of the file
471 maxsize -= ZIP_END_CDIR_SIZE;
472 ptr = &buffer[maxsize];
474 while (BuffBigLong (ptr) != ZIP_END_HEADER)
486 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
487 eocd->signature = LittleLong (eocd->signature);
488 eocd->disknum = LittleShort (eocd->disknum);
489 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
490 eocd->localentries = LittleShort (eocd->localentries);
491 eocd->nbentries = LittleShort (eocd->nbentries);
492 eocd->cdir_size = LittleLong (eocd->cdir_size);
493 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
494 eocd->comment_size = LittleShort (eocd->comment_size);
506 Extract the file list from a PK3 file
509 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
511 unsigned char *central_dir, *ptr;
513 fs_offset_t remaining;
515 // Load the central directory in memory
516 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
517 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
518 if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size)
520 Mem_Free (central_dir);
524 // Extract the files properties
525 // The parsing is done "by hand" because some fields have variable sizes and
526 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
527 remaining = eocd->cdir_size;
530 for (ind = 0; ind < eocd->nbentries; ind++)
532 fs_offset_t namesize, count;
534 // Checking the remaining size
535 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
537 Mem_Free (central_dir);
540 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
543 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
545 Mem_Free (central_dir);
549 namesize = BuffLittleShort (&ptr[28]); // filename length
551 // Check encryption, compression, and attributes
552 // 1st uint8 : general purpose bit flag
553 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
555 // LordHavoc: bit 3 would be a problem if we were scanning the archive
556 // but is not a problem in the central directory where the values are
559 // bit 3 seems to always be set by the standard Mac OSX zip maker
561 // 2nd uint8 : external file attributes
562 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
563 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
565 // Still enough bytes for the name?
566 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
568 Mem_Free (central_dir);
572 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
573 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
575 char filename [sizeof (pack->files[0].name)];
576 fs_offset_t offset, packsize, realsize;
579 // Extract the name (strip it if necessary)
580 namesize = min(namesize, (int)sizeof (filename) - 1);
581 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
582 filename[namesize] = '\0';
584 if (BuffLittleShort (&ptr[10]))
585 flags = PACKFILE_FLAG_DEFLATED;
588 offset = BuffLittleLong (&ptr[42]);
589 packsize = BuffLittleLong (&ptr[20]);
590 realsize = BuffLittleLong (&ptr[24]);
592 switch(ptr[5]) // C_VERSION_MADE_BY_1
597 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
598 // can't use S_ISLNK here, as this has to compile on non-UNIX too
599 flags |= PACKFILE_FLAG_SYMLINK;
603 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
607 // Skip the name, additionnal field, and comment
608 // 1er uint16 : extra field length
609 // 2eme uint16 : file comment length
610 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
611 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
615 // If the package is empty, central_dir is NULL here
616 if (central_dir != NULL)
617 Mem_Free (central_dir);
618 return pack->numfiles;
626 Create a package entry associated with a PK3 file
629 pack_t *FS_LoadPackPK3 (const char *packfile)
632 pk3_endOfCentralDir_t eocd;
637 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
639 packhandle = open (packfile, O_RDONLY | O_BINARY);
644 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
646 Con_Printf ("%s is not a PK3 file\n", packfile);
651 // Multi-volume ZIP archives are NOT allowed
652 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
654 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
659 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
660 // since eocd.nbentries is an unsigned 16 bits integer
661 #if MAX_FILES_IN_PACK < 65535
662 if (eocd.nbentries > MAX_FILES_IN_PACK)
664 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
670 // Create a package structure in memory
671 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
672 pack->ignorecase = true; // PK3 ignores case
673 strlcpy (pack->filename, packfile, sizeof (pack->filename));
674 pack->handle = packhandle;
675 pack->numfiles = eocd.nbentries;
676 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
678 real_nb_files = PK3_BuildFileList (pack, &eocd);
679 if (real_nb_files < 0)
681 Con_Printf ("%s is not a valid PK3 file\n", packfile);
687 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
694 PK3_GetTrueFileOffset
696 Find where the true file data offset is
699 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
701 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
705 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
708 // Load the local file description
709 lseek (pack->handle, pfile->offset, SEEK_SET);
710 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
711 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
713 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
717 // Skip name and extra field
718 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
720 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
726 =============================================================================
728 OTHER PRIVATE FUNCTIONS
730 =============================================================================
738 Add a file to the list of files contained into a package
741 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
742 fs_offset_t offset, fs_offset_t packsize,
743 fs_offset_t realsize, int flags)
745 int (*strcmp_funct) (const char* str1, const char* str2);
746 int left, right, middle;
749 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
751 // Look for the slot we should put that file into (binary search)
753 right = pack->numfiles - 1;
754 while (left <= right)
758 middle = (left + right) / 2;
759 diff = strcmp_funct (pack->files[middle].name, name);
761 // If we found the file, there's a problem
763 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
765 // If we're too far in the list
772 // We have to move the right of the list by one slot to free the one we need
773 pfile = &pack->files[left];
774 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
777 strlcpy (pfile->name, name, sizeof (pfile->name));
778 pfile->offset = offset;
779 pfile->packsize = packsize;
780 pfile->realsize = realsize;
781 pfile->flags = flags;
791 Only used for FS_OpenRealFile.
794 void FS_CreatePath (char *path)
798 for (ofs = path+1 ; *ofs ; ofs++)
800 if (*ofs == '/' || *ofs == '\\')
802 // create the directory
818 void FS_Path_f (void)
822 Con_Print("Current search path:\n");
823 for (s=fs_searchpaths ; s ; s=s->next)
826 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
828 Con_Printf("%s\n", s->filename);
837 Takes an explicit (not game tree related) path to a pak file.
839 Loads the header and directory, adding the files at the beginning
840 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);
925 Adds the given pack to the search path.
926 The pack type is autodetected by the file extension.
928 Returns true if the file was successfully added to the
929 search path or if it was already included.
931 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
935 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
937 searchpath_t *search;
939 const char *ext = FS_FileExtension(pakfile);
941 for(search = fs_searchpaths; search; search = search->next)
943 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
946 *already_loaded = true;
947 return true; // already loaded
952 *already_loaded = false;
954 if(!strcasecmp(ext, "pak"))
955 pak = FS_LoadPackPAK (pakfile);
956 else if(!strcasecmp(ext, "pk3"))
957 pak = FS_LoadPackPK3 (pakfile);
959 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
963 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
964 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
967 // find the first item whose next one is a pack or NULL
968 searchpath_t *insertion_point = 0;
969 if(fs_searchpaths && !fs_searchpaths->pack)
971 insertion_point = fs_searchpaths;
974 if(!insertion_point->next)
976 if(insertion_point->next->pack)
978 insertion_point = insertion_point->next;
981 // If insertion_point is NULL, this means that either there is no
982 // item in the list yet, or that the very first item is a pack. In
983 // that case, we want to insert at the beginning...
986 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
988 search->next = fs_searchpaths;
989 fs_searchpaths = search;
992 // otherwise we want to append directly after insertion_point.
994 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
996 search->next = insertion_point->next;
997 insertion_point->next = search;
1002 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1004 search->next = fs_searchpaths;
1005 fs_searchpaths = search;
1011 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1021 Adds the given pack to the search path and searches for it in the game path.
1022 The pack type is autodetected by the file extension.
1024 Returns true if the file was successfully added to the
1025 search path or if it was already included.
1027 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1031 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1033 char fullpath[MAX_QPATH];
1035 searchpath_t *search;
1038 *already_loaded = false;
1040 // then find the real name...
1041 search = FS_FindFile(pakfile, &index, true);
1042 if(!search || search->pack)
1044 Con_Printf("could not find pak \"%s\"\n", pakfile);
1048 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1050 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1058 Sets fs_gamedir, adds the directory to the head of the path,
1059 then loads and adds pak1.pak pak2.pak ...
1062 void FS_AddGameDirectory (const char *dir)
1066 searchpath_t *search;
1068 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1070 stringlistinit(&list);
1071 listdirectory(&list, "", dir);
1072 stringlistsort(&list);
1074 // add any PAK package in the directory
1075 for (i = 0;i < list.numstrings;i++)
1077 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1079 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1083 // add any PK3 package in the directory
1084 for (i = 0;i < list.numstrings;i++)
1086 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1088 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1092 stringlistfreecontents(&list);
1094 // Add the directory to the search path
1095 // (unpacked files have the priority over packed files)
1096 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1097 strlcpy (search->filename, dir, sizeof (search->filename));
1098 search->next = fs_searchpaths;
1099 fs_searchpaths = search;
1108 void FS_AddGameHierarchy (const char *dir)
1111 char userdir[MAX_QPATH];
1113 TCHAR mydocsdir[MAX_PATH + 1];
1114 #if _MSC_VER >= 1400
1120 // Add the common game directory
1121 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1125 // Add the personal game directory
1127 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1129 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1130 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", userdir);
1134 // use the environment
1135 #if _MSC_VER >= 1400
1136 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1138 homedir = getenv("USERPROFILE");
1143 dpsnprintf(userdir, sizeof(userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1144 #if _MSC_VER >= 1400
1147 Con_DPrintf("Obtained personal directory %s from environment\n", userdir);
1150 *userdir = 0; // just to make sure it hasn't been written to by SHGetFolderPath returning failure
1154 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1156 homedir = getenv ("HOME");
1158 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1161 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1166 if(!COM_CheckParm("-mygames"))
1168 #if _MSC_VER >= 1400
1170 _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!
1172 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1177 *userdir = 0; // we have write access to the game dir, so let's use it
1182 if(COM_CheckParm("-nohome"))
1185 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1186 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1189 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1198 const char *FS_FileExtension (const char *in)
1200 const char *separator, *backslash, *colon, *dot;
1202 separator = strrchr(in, '/');
1203 backslash = strrchr(in, '\\');
1204 if (!separator || separator < backslash)
1205 separator = backslash;
1206 colon = strrchr(in, ':');
1207 if (!separator || separator < colon)
1210 dot = strrchr(in, '.');
1211 if (dot == NULL || (separator && (dot < separator)))
1223 const char *FS_FileWithoutPath (const char *in)
1225 const char *separator, *backslash, *colon;
1227 separator = strrchr(in, '/');
1228 backslash = strrchr(in, '\\');
1229 if (!separator || separator < backslash)
1230 separator = backslash;
1231 colon = strrchr(in, ':');
1232 if (!separator || separator < colon)
1234 return separator ? separator + 1 : in;
1243 void FS_ClearSearchPath (void)
1245 // unload all packs and directory information, close all pack files
1246 // (if a qfile is still reading a pack it won't be harmed because it used
1247 // dup() to get its own handle already)
1248 while (fs_searchpaths)
1250 searchpath_t *search = fs_searchpaths;
1251 fs_searchpaths = search->next;
1255 close(search->pack->handle);
1256 // free any memory associated with it
1257 if (search->pack->files)
1258 Mem_Free(search->pack->files);
1259 Mem_Free(search->pack);
1271 void FS_Rescan (void)
1274 qboolean fs_modified = false;
1276 FS_ClearSearchPath();
1278 // add the game-specific paths
1279 // gamedirname1 (typically id1)
1280 FS_AddGameHierarchy (gamedirname1);
1281 // update the com_modname (used for server info)
1282 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1284 // add the game-specific path, if any
1285 // (only used for mission packs and the like, which should set fs_modified)
1289 FS_AddGameHierarchy (gamedirname2);
1293 // Adds basedir/gamedir as an override game
1294 // LordHavoc: now supports multiple -game directories
1295 // set the com_modname (reported in server info)
1296 for (i = 0;i < fs_numgamedirs;i++)
1299 FS_AddGameHierarchy (fs_gamedirs[i]);
1300 // update the com_modname (used server info)
1301 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1304 // set the default screenshot name to either the mod name or the
1305 // gamemode screenshot name
1306 if (strcmp(com_modname, gamedirname1))
1307 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1309 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1311 // If "-condebug" is in the command line, remove the previous log file
1312 if (COM_CheckParm ("-condebug") != 0)
1313 unlink (va("%s/qconsole.log", fs_gamedir));
1315 // look for the pop.lmp file and set registered to true if it is found
1316 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1319 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1321 Con_Print("Playing shareware version.\n");
1325 Cvar_Set ("registered", "1");
1326 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1327 Con_Print("Playing registered version.\n");
1330 // unload all wads so that future queries will return the new data
1334 void FS_Rescan_f(void)
1344 extern void Host_SaveConfig (void);
1345 extern void Host_LoadConfig_f (void);
1346 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1350 if (fs_numgamedirs == numgamedirs)
1352 for (i = 0;i < numgamedirs;i++)
1353 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1355 if (i == numgamedirs)
1356 return true; // already using this set of gamedirs, do nothing
1359 if (numgamedirs > MAX_GAMEDIRS)
1362 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1363 return false; // too many gamedirs
1366 for (i = 0;i < numgamedirs;i++)
1368 // if string is nasty, reject it
1369 if(FS_CheckNastyPath(gamedirs[i], true))
1372 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1373 return false; // nasty gamedirs
1377 for (i = 0;i < numgamedirs;i++)
1379 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1382 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1383 return false; // missing gamedirs
1389 fs_numgamedirs = numgamedirs;
1390 for (i = 0;i < fs_numgamedirs;i++)
1391 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1393 // reinitialize filesystem to detect the new paks
1396 // exec the new config
1397 Host_LoadConfig_f();
1399 // unload all sounds so they will be reloaded from the new files as needed
1400 S_UnloadAllSounds_f();
1402 // reinitialize renderer (this reloads hud/console background/etc)
1403 R_Modules_Restart();
1413 void FS_GameDir_f (void)
1417 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1421 Con_Printf("gamedirs active:");
1422 for (i = 0;i < fs_numgamedirs;i++)
1423 Con_Printf(" %s", fs_gamedirs[i]);
1428 numgamedirs = Cmd_Argc() - 1;
1429 if (numgamedirs > MAX_GAMEDIRS)
1431 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1435 for (i = 0;i < numgamedirs;i++)
1436 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1438 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1440 // actually, changing during game would work fine, but would be stupid
1441 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1445 // halt demo playback to close the file
1448 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1457 qboolean FS_CheckGameDir(const char *gamedir)
1461 stringlistinit(&list);
1462 listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
1463 success = list.numstrings > 0;
1464 stringlistfreecontents(&list);
1479 const char* dllnames [] =
1481 "shfolder.dll", // IE 4, or Win NT and higher
1484 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1485 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1488 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1490 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1492 // If the base directory is explicitly defined by the compilation process
1493 #ifdef DP_FS_BASEDIR
1494 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1496 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1499 // FIXME: is there a better way to find the directory outside the .app?
1500 if (strstr(com_argv[0], ".app/"))
1504 split = strstr(com_argv[0], ".app/");
1505 while (split > com_argv[0] && *split != '/')
1507 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1508 fs_basedir[split - com_argv[0]] = 0;
1516 // Overrides the system supplied base directory (under GAMENAME)
1517 // 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)
1518 i = COM_CheckParm ("-basedir");
1519 if (i && i < com_argc-1)
1521 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1522 i = (int)strlen (fs_basedir);
1523 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1524 fs_basedir[i-1] = 0;
1527 // add a path separator to the end of the basedir if it lacks one
1528 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1529 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1531 if (!FS_CheckGameDir(gamedirname1))
1532 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1534 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1535 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1538 // Adds basedir/gamedir as an override game
1539 // LordHavoc: now supports multiple -game directories
1540 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1544 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1547 if (FS_CheckNastyPath(com_argv[i], true))
1548 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1549 if (!FS_CheckGameDir(com_argv[i]))
1550 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1551 // add the gamedir to the list of active gamedirs
1552 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1557 // generate the searchpath
1561 void FS_Init_Commands(void)
1563 Cvar_RegisterVariable (&scr_screenshot_name);
1564 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1566 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1567 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1568 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1569 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1570 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1571 Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
1579 void FS_Shutdown (void)
1581 // close all pack files and such
1582 // (hopefully there aren't any other open files, but they'll be cleaned up
1583 // by the OS anyway)
1584 FS_ClearSearchPath();
1585 Mem_FreePool (&fs_mempool);
1588 Sys_UnloadLibrary (&shfolder_dll);
1593 ====================
1596 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1597 ====================
1599 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1605 // Parse the mode string
1614 opt = O_CREAT | O_TRUNC;
1618 opt = O_CREAT | O_APPEND;
1621 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1624 for (ind = 1; mode[ind] != '\0'; ind++)
1635 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1636 filepath, mode, mode[ind]);
1643 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1644 memset (file, 0, sizeof (*file));
1647 #if _MSC_VER >= 1400
1648 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1650 file->handle = open (filepath, mod | opt, 0666);
1652 if (file->handle < 0)
1658 file->real_length = lseek (file->handle, 0, SEEK_END);
1660 // For files opened in append mode, we start at the end of the file
1662 file->position = file->real_length;
1664 lseek (file->handle, 0, SEEK_SET);
1674 Open a packed file using its package file descriptor
1677 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1683 pfile = &pack->files[pack_ind];
1685 // If we don't have the true offset, get it now
1686 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1687 if (!PK3_GetTrueFileOffset (pfile, pack))
1690 // No Zlib DLL = no compressed files
1691 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1693 Con_Printf("WARNING: can't open the compressed file %s\n"
1694 "You need the Zlib DLL to use compressed files\n",
1699 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1700 // the dup() call to avoid having to close the dup_handle on error here
1701 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1703 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1704 pfile->name, pack->filename, (int) pfile->offset);
1708 dup_handle = dup (pack->handle);
1711 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1715 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1716 memset (file, 0, sizeof (*file));
1717 file->handle = dup_handle;
1718 file->flags = QFILE_FLAG_PACKED;
1719 file->real_length = pfile->realsize;
1720 file->offset = pfile->offset;
1724 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1728 file->flags |= QFILE_FLAG_DEFLATED;
1730 // We need some more variables
1731 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1733 ztk->comp_length = pfile->packsize;
1735 // Initialize zlib stream
1736 ztk->zstream.next_in = ztk->input;
1737 ztk->zstream.avail_in = 0;
1739 /* From Zlib's "unzip.c":
1741 * windowBits is passed < 0 to tell that there is no zlib header.
1742 * Note that in this case inflate *requires* an extra "dummy" byte
1743 * after the compressed stream in order to complete decompression and
1744 * return Z_STREAM_END.
1745 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1746 * size of both compressed and uncompressed data
1748 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1750 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1756 ztk->zstream.next_out = file->buff;
1757 ztk->zstream.avail_out = sizeof (file->buff);
1766 ====================
1769 Return true if the path should be rejected due to one of the following:
1770 1: path elements that are non-portable
1771 2: path elements that would allow access to files outside the game directory,
1772 or are just not a good idea for a mod to be using.
1773 ====================
1775 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1777 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1781 // Windows: don't allow \ in filenames (windows-only), period.
1782 // (on Windows \ is a directory separator, but / is also supported)
1783 if (strstr(path, "\\"))
1784 return 1; // non-portable
1786 // Mac: don't allow Mac-only filenames - : is a directory separator
1787 // instead of /, but we rely on / working already, so there's no reason to
1788 // support a Mac-only path
1789 // Amiga and Windows: : tries to go to root of drive
1790 if (strstr(path, ":"))
1791 return 1; // non-portable attempt to go to root of drive
1793 // Amiga: // is parent directory
1794 if (strstr(path, "//"))
1795 return 1; // non-portable attempt to go to parent directory
1797 // all: don't allow going to parent directory (../ or /../)
1798 if (strstr(path, ".."))
1799 return 2; // attempt to go outside the game directory
1801 // Windows and UNIXes: don't allow absolute paths
1803 return 2; // attempt to go outside the game directory
1805 // 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
1806 if (strchr(path, '.'))
1810 // gamedir is entirely path elements, so simply forbid . entirely
1813 if (strchr(path, '.') < strrchr(path, '/'))
1814 return 2; // possible attempt to go outside the game directory
1817 // all: forbid trailing slash on gamedir
1818 if (isgamedir && path[strlen(path)-1] == '/')
1821 // all: forbid leading dot on any filename for any reason
1822 if (strstr(path, "/."))
1823 return 2; // attempt to go outside the game directory
1825 // after all these checks we're pretty sure it's a / separated filename
1826 // and won't do much if any harm
1832 ====================
1835 Look for a file in the packages and in the filesystem
1837 Return the searchpath where the file was found (or NULL)
1838 and the file index in the package if relevant
1839 ====================
1841 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1843 searchpath_t *search;
1846 // search through the path, one element at a time
1847 for (search = fs_searchpaths;search;search = search->next)
1849 // is the element a pak file?
1852 int (*strcmp_funct) (const char* str1, const char* str2);
1853 int left, right, middle;
1856 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1858 // Look for the file (binary search)
1860 right = pak->numfiles - 1;
1861 while (left <= right)
1865 middle = (left + right) / 2;
1866 diff = strcmp_funct (pak->files[middle].name, name);
1871 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1873 // yes, but the first one is empty so we treat it as not being there
1874 if (!quiet && developer.integer >= 10)
1875 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1882 if (!quiet && developer.integer >= 10)
1883 Con_Printf("FS_FindFile: %s in %s\n",
1884 pak->files[middle].name, pak->filename);
1891 // If we're too far in the list
1900 char netpath[MAX_OSPATH];
1901 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1902 if (FS_SysFileExists (netpath))
1904 if (!quiet && developer.integer >= 10)
1905 Con_Printf("FS_FindFile: %s\n", netpath);
1914 if (!quiet && developer.integer >= 10)
1915 Con_Printf("FS_FindFile: can't find %s\n", name);
1927 Look for a file in the search paths and open it in read-only mode
1930 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
1932 searchpath_t *search;
1935 search = FS_FindFile (filename, &pack_ind, quiet);
1941 // Found in the filesystem?
1944 char path [MAX_OSPATH];
1945 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1946 return FS_SysOpen (path, "rb", nonblocking);
1949 // So, we found it in a package...
1951 // Is it a PK3 symlink?
1952 // TODO also handle directory symlinks by parsing the whole structure...
1953 // but heck, file symlinks are good enough for now
1954 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
1956 if(symlinkLevels <= 0)
1958 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
1963 char linkbuf[MAX_QPATH];
1965 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
1966 const char *mergeslash;
1971 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
1977 // Now combine the paths...
1978 mergeslash = strrchr(filename, '/');
1979 mergestart = linkbuf;
1981 mergeslash = filename;
1982 while(!strncmp(mergestart, "../", 3))
1985 while(mergeslash > filename)
1988 if(*mergeslash == '/')
1992 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
1993 if(mergeslash == filename)
1995 // Either mergeslash == filename, then we just replace the name (done below)
1999 // Or, we append the name after mergeslash;
2000 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
2001 int spaceNeeded = mergeslash - filename + 1;
2002 int spaceRemoved = mergestart - linkbuf;
2003 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2005 Con_DPrintf("symlink: too long path rejected\n");
2008 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2009 memcpy(linkbuf, filename, spaceNeeded);
2010 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2011 mergestart = linkbuf;
2013 if (!quiet && developer_loading.integer)
2014 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2015 if(FS_CheckNastyPath (mergestart, false))
2017 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2020 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2024 return FS_OpenPackedFile (search->pack, pack_ind);
2029 =============================================================================
2031 MAIN PUBLIC FUNCTIONS
2033 =============================================================================
2037 ====================
2040 Open a file in the userpath. The syntax is the same as fopen
2041 Used for savegame scanning in menu, and all file writing.
2042 ====================
2044 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2046 char real_path [MAX_OSPATH];
2048 if (FS_CheckNastyPath(filepath, false))
2050 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2054 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2056 // If the file is opened in "write", "append", or "read/write" mode,
2057 // create directories up to the file.
2058 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2059 FS_CreatePath (real_path);
2060 return FS_SysOpen (real_path, mode, false);
2065 ====================
2068 Open a file. The syntax is the same as fopen
2069 ====================
2071 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2073 if (FS_CheckNastyPath(filepath, false))
2075 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2079 return FS_OpenReadFile (filepath, quiet, false, 16);
2084 ====================
2087 Open a file. The syntax is the same as fopen
2088 ====================
2090 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2093 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2094 memset (file, 0, sizeof (*file));
2095 file->flags = QFILE_FLAG_DATA;
2097 file->real_length = size;
2103 ====================
2107 ====================
2109 int FS_Close (qfile_t* file)
2111 if(file->flags & QFILE_FLAG_DATA)
2117 if (close (file->handle))
2122 qz_inflateEnd (&file->ztk->zstream);
2123 Mem_Free (file->ztk);
2132 ====================
2135 Write "datasize" bytes into a file
2136 ====================
2138 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2142 // If necessary, seek to the exact file position we're supposed to be
2143 if (file->buff_ind != file->buff_len)
2144 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2146 // Purge cached data
2149 // Write the buffer and update the position
2150 result = write (file->handle, data, (fs_offset_t)datasize);
2151 file->position = lseek (file->handle, 0, SEEK_CUR);
2152 if (file->real_length < file->position)
2153 file->real_length = file->position;
2163 ====================
2166 Read up to "buffersize" bytes from a file
2167 ====================
2169 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2171 fs_offset_t count, done;
2173 if (buffersize == 0)
2176 // Get rid of the ungetc character
2177 if (file->ungetc != EOF)
2179 ((char*)buffer)[0] = file->ungetc;
2187 if(file->flags & QFILE_FLAG_DATA)
2189 size_t left = file->real_length - file->position;
2190 if(buffersize > left)
2192 memcpy(buffer, file->data + file->position, buffersize);
2193 file->position += buffersize;
2197 // First, we copy as many bytes as we can from "buff"
2198 if (file->buff_ind < file->buff_len)
2200 count = file->buff_len - file->buff_ind;
2201 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2203 memcpy (buffer, &file->buff[file->buff_ind], count);
2204 file->buff_ind += count;
2206 buffersize -= count;
2207 if (buffersize == 0)
2211 // NOTE: at this point, the read buffer is always empty
2213 // If the file isn't compressed
2214 if (! (file->flags & QFILE_FLAG_DEFLATED))
2218 // We must take care to not read after the end of the file
2219 count = file->real_length - file->position;
2221 // If we have a lot of data to get, put them directly into "buffer"
2222 if (buffersize > sizeof (file->buff) / 2)
2224 if (count > (fs_offset_t)buffersize)
2225 count = (fs_offset_t)buffersize;
2226 lseek (file->handle, file->offset + file->position, SEEK_SET);
2227 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2231 file->position += nb;
2233 // Purge cached data
2239 if (count > (fs_offset_t)sizeof (file->buff))
2240 count = (fs_offset_t)sizeof (file->buff);
2241 lseek (file->handle, file->offset + file->position, SEEK_SET);
2242 nb = read (file->handle, file->buff, count);
2245 file->buff_len = nb;
2246 file->position += nb;
2248 // Copy the requested data in "buffer" (as much as we can)
2249 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2250 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2251 file->buff_ind = count;
2259 // If the file is compressed, it's more complicated...
2260 // We cycle through a few operations until we have read enough data
2261 while (buffersize > 0)
2263 ztoolkit_t *ztk = file->ztk;
2266 // NOTE: at this point, the read buffer is always empty
2268 // If "input" is also empty, we need to refill it
2269 if (ztk->in_ind == ztk->in_len)
2271 // If we are at the end of the file
2272 if (file->position == file->real_length)
2275 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2276 if (count > (fs_offset_t)sizeof (ztk->input))
2277 count = (fs_offset_t)sizeof (ztk->input);
2278 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2279 if (read (file->handle, ztk->input, count) != count)
2281 Con_Printf ("FS_Read: unexpected end of file\n");
2286 ztk->in_len = count;
2287 ztk->in_position += count;
2290 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2291 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2293 // Now that we are sure we have compressed data available, we need to determine
2294 // if it's better to inflate it in "file->buff" or directly in "buffer"
2296 // Inflate the data in "file->buff"
2297 if (buffersize < sizeof (file->buff) / 2)
2299 ztk->zstream.next_out = file->buff;
2300 ztk->zstream.avail_out = sizeof (file->buff);
2301 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2302 if (error != Z_OK && error != Z_STREAM_END)
2304 Con_Printf ("FS_Read: Can't inflate file\n");
2307 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2309 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2310 file->position += file->buff_len;
2312 // Copy the requested data in "buffer" (as much as we can)
2313 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2314 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2315 file->buff_ind = count;
2318 // Else, we inflate directly in "buffer"
2321 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2322 ztk->zstream.avail_out = (unsigned int)buffersize;
2323 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2324 if (error != Z_OK && error != Z_STREAM_END)
2326 Con_Printf ("FS_Read: Can't inflate file\n");
2329 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2331 // How much data did it inflate?
2332 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2333 file->position += count;
2335 // Purge cached data
2340 buffersize -= count;
2348 ====================
2351 Print a string into a file
2352 ====================
2354 int FS_Print (qfile_t* file, const char *msg)
2356 return (int)FS_Write (file, msg, strlen (msg));
2360 ====================
2363 Print a string into a file
2364 ====================
2366 int FS_Printf(qfile_t* file, const char* format, ...)
2371 va_start (args, format);
2372 result = FS_VPrintf (file, format, args);
2380 ====================
2383 Print a string into a file
2384 ====================
2386 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2389 fs_offset_t buff_size = MAX_INPUTLINE;
2394 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2395 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2396 if (len >= 0 && len < buff_size)
2398 Mem_Free (tempbuff);
2402 len = write (file->handle, tempbuff, len);
2403 Mem_Free (tempbuff);
2410 ====================
2413 Get the next character of a file
2414 ====================
2416 int FS_Getc (qfile_t* file)
2420 if (FS_Read (file, &c, 1) != 1)
2428 ====================
2431 Put a character back into the read buffer (only supports one character!)
2432 ====================
2434 int FS_UnGetc (qfile_t* file, unsigned char c)
2436 // If there's already a character waiting to be read
2437 if (file->ungetc != EOF)
2446 ====================
2449 Move the position index in a file
2450 ====================
2452 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2455 unsigned char* buffer;
2456 fs_offset_t buffersize;
2458 // Compute the file offset
2462 offset += file->position - file->buff_len + file->buff_ind;
2469 offset += file->real_length;
2475 if (offset < 0 || offset > file->real_length)
2478 if(file->flags & QFILE_FLAG_DATA)
2480 file->position = offset;
2484 // If we have the data in our read buffer, we don't need to actually seek
2485 if (file->position - file->buff_len <= offset && offset <= file->position)
2487 file->buff_ind = offset + file->buff_len - file->position;
2491 // Purge cached data
2494 // Unpacked or uncompressed files can seek directly
2495 if (! (file->flags & QFILE_FLAG_DEFLATED))
2497 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2499 file->position = offset;
2503 // Seeking in compressed files is more a hack than anything else,
2504 // but we need to support it, so here we go.
2507 // If we have to go back in the file, we need to restart from the beginning
2508 if (offset <= file->position)
2512 ztk->in_position = 0;
2514 lseek (file->handle, file->offset, SEEK_SET);
2516 // Reset the Zlib stream
2517 ztk->zstream.next_in = ztk->input;
2518 ztk->zstream.avail_in = 0;
2519 qz_inflateReset (&ztk->zstream);
2522 // We need a big buffer to force inflating into it directly
2523 buffersize = 2 * sizeof (file->buff);
2524 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2526 // Skip all data until we reach the requested offset
2527 while (offset > file->position)
2529 fs_offset_t diff = offset - file->position;
2530 fs_offset_t count, len;
2532 count = (diff > buffersize) ? buffersize : diff;
2533 len = FS_Read (file, buffer, count);
2547 ====================
2550 Give the current position in a file
2551 ====================
2553 fs_offset_t FS_Tell (qfile_t* file)
2555 return file->position - file->buff_len + file->buff_ind;
2560 ====================
2563 Give the total size of a file
2564 ====================
2566 fs_offset_t FS_FileSize (qfile_t* file)
2568 return file->real_length;
2573 ====================
2576 Erases any buffered input or output data
2577 ====================
2579 void FS_Purge (qfile_t* file)
2591 Filename are relative to the quake directory.
2592 Always appends a 0 byte.
2595 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2598 unsigned char *buf = NULL;
2599 fs_offset_t filesize = 0;
2601 file = FS_OpenVirtualFile(path, quiet);
2604 filesize = file->real_length;
2607 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2612 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2613 buf[filesize] = '\0';
2614 FS_Read (file, buf, filesize);
2616 if (developer_loadfile.integer)
2617 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2620 if (filesizepointer)
2621 *filesizepointer = filesize;
2630 The filename will be prefixed by the current game directory
2633 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2637 file = FS_OpenRealFile(filename, "wb", false);
2640 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2644 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2645 FS_Write (file, data, len);
2652 =============================================================================
2654 OTHERS PUBLIC FUNCTIONS
2656 =============================================================================
2664 void FS_StripExtension (const char *in, char *out, size_t size_out)
2672 while ((currentchar = *in) && size_out > 1)
2674 if (currentchar == '.')
2676 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2678 *out++ = currentchar;
2694 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2698 // if path doesn't have a .EXT, append extension
2699 // (extension should include the .)
2700 src = path + strlen(path) - 1;
2702 while (*src != '/' && src != path)
2705 return; // it has an extension
2709 strlcat (path, extension, size_path);
2717 Look for a file in the packages and in the filesystem
2720 int FS_FileType (const char *filename)
2722 searchpath_t *search;
2723 char fullpath[MAX_QPATH];
2725 search = FS_FindFile (filename, NULL, true);
2727 return FS_FILETYPE_NONE;
2730 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2732 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2733 return FS_SysFileType(fullpath);
2741 Look for a file in the packages and in the filesystem
2744 qboolean FS_FileExists (const char *filename)
2746 return (FS_FindFile (filename, NULL, true) != NULL);
2754 Look for a file in the filesystem only
2757 int FS_SysFileType (const char *path)
2760 // Sajt - some older sdks are missing this define
2761 # ifndef INVALID_FILE_ATTRIBUTES
2762 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2765 DWORD result = GetFileAttributes(path);
2767 if(result == INVALID_FILE_ATTRIBUTES)
2768 return FS_FILETYPE_NONE;
2770 if(result & FILE_ATTRIBUTE_DIRECTORY)
2771 return FS_FILETYPE_DIRECTORY;
2773 return FS_FILETYPE_FILE;
2777 if (stat (path,&buf) == -1)
2778 return FS_FILETYPE_NONE;
2780 if(S_ISDIR(buf.st_mode))
2781 return FS_FILETYPE_DIRECTORY;
2783 return FS_FILETYPE_FILE;
2787 qboolean FS_SysFileExists (const char *path)
2789 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2792 void FS_mkdir (const char *path)
2805 Allocate and fill a search structure with information on matching filenames.
2808 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2811 searchpath_t *searchpath;
2813 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2814 stringlist_t resultlist;
2815 stringlist_t dirlist;
2816 const char *slash, *backslash, *colon, *separator;
2818 char temp[MAX_OSPATH];
2820 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2825 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2829 stringlistinit(&resultlist);
2830 stringlistinit(&dirlist);
2832 slash = strrchr(pattern, '/');
2833 backslash = strrchr(pattern, '\\');
2834 colon = strrchr(pattern, ':');
2835 separator = max(slash, backslash);
2836 separator = max(separator, colon);
2837 basepathlength = separator ? (separator + 1 - pattern) : 0;
2838 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2840 memcpy(basepath, pattern, basepathlength);
2841 basepath[basepathlength] = 0;
2843 // search through the path, one element at a time
2844 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2846 // is the element a pak file?
2847 if (searchpath->pack)
2849 // look through all the pak file elements
2850 pak = searchpath->pack;
2851 for (i = 0;i < pak->numfiles;i++)
2853 strlcpy(temp, pak->files[i].name, sizeof(temp));
2856 if (matchpattern(temp, (char *)pattern, true))
2858 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2859 if (!strcmp(resultlist.strings[resultlistindex], temp))
2861 if (resultlistindex == resultlist.numstrings)
2863 stringlistappend(&resultlist, temp);
2864 if (!quiet && developer_loading.integer)
2865 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2868 // strip off one path element at a time until empty
2869 // this way directories are added to the listing if they match the pattern
2870 slash = strrchr(temp, '/');
2871 backslash = strrchr(temp, '\\');
2872 colon = strrchr(temp, ':');
2874 if (separator < slash)
2876 if (separator < backslash)
2877 separator = backslash;
2878 if (separator < colon)
2880 *((char *)separator) = 0;
2886 stringlist_t matchedSet, foundSet;
2887 const char *start = pattern;
2889 stringlistinit(&matchedSet);
2890 stringlistinit(&foundSet);
2891 // add a first entry to the set
2892 stringlistappend(&matchedSet, "");
2893 // iterate through pattern's path
2896 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
2897 char subpath[MAX_OSPATH];
2898 char subpattern[MAX_OSPATH];
2900 // find the next wildcard
2901 wildcard = strchr(start, '?');
2902 asterisk = strchr(start, '*');
2903 if (asterisk && (!wildcard || asterisk < wildcard))
2905 wildcard = asterisk;
2910 nextseparator = strchr( wildcard, '/' );
2914 nextseparator = NULL;
2917 if( !nextseparator ) {
2918 nextseparator = start + strlen( start );
2921 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
2922 // copy everything up except nextseperator
2923 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
2924 // find the last '/' before the wildcard
2925 prevseparator = strrchr( subpattern, '/' );
2927 prevseparator = subpattern;
2930 // copy everything from start to the previous including the '/' (before the wildcard)
2931 // everything up to start is already included in the path of matchedSet's entries
2932 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
2934 // for each entry in matchedSet try to open the subdirectories specified in subpath
2935 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
2936 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
2937 strlcat( temp, subpath, sizeof(temp) );
2938 listdirectory( &foundSet, searchpath->filename, temp );
2940 if( dirlistindex == 0 ) {
2943 // reset the current result set
2944 stringlistfreecontents( &matchedSet );
2945 // match against the pattern
2946 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
2947 const char *direntry = foundSet.strings[ dirlistindex ];
2948 if (matchpattern(direntry, subpattern, true)) {
2949 stringlistappend( &matchedSet, direntry );
2952 stringlistfreecontents( &foundSet );
2954 start = nextseparator;
2957 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
2959 const char *temp = matchedSet.strings[dirlistindex];
2960 if (matchpattern(temp, (char *)pattern, true))
2962 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2963 if (!strcmp(resultlist.strings[resultlistindex], temp))
2965 if (resultlistindex == resultlist.numstrings)
2967 stringlistappend(&resultlist, temp);
2968 if (!quiet && developer_loading.integer)
2969 Con_Printf("SearchDirFile: %s\n", temp);
2973 stringlistfreecontents( &matchedSet );
2977 if (resultlist.numstrings)
2979 stringlistsort(&resultlist);
2980 numfiles = resultlist.numstrings;
2982 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2983 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2984 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2985 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2986 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2987 search->numfilenames = (int)numfiles;
2990 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2993 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2994 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2995 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2997 numchars += (int)textlen;
3000 stringlistfreecontents(&resultlist);
3006 void FS_FreeSearch(fssearch_t *search)
3011 extern int con_linewidth;
3012 int FS_ListDirectory(const char *pattern, int oneperline)
3021 char linebuf[MAX_INPUTLINE];
3023 search = FS_Search(pattern, true, true);
3026 numfiles = search->numfilenames;
3029 // FIXME: the names could be added to one column list and then
3030 // gradually shifted into the next column if they fit, and then the
3031 // next to make a compact variable width listing but it's a lot more
3033 // find width for columns
3035 for (i = 0;i < numfiles;i++)
3037 l = (int)strlen(search->filenames[i]);
3038 if (columnwidth < l)
3041 // count the spacing character
3043 // calculate number of columns
3044 numcolumns = con_linewidth / columnwidth;
3045 // don't bother with the column printing if it's only one column
3046 if (numcolumns >= 2)
3048 numlines = (numfiles + numcolumns - 1) / numcolumns;
3049 for (i = 0;i < numlines;i++)
3052 for (k = 0;k < numcolumns;k++)
3054 l = i * numcolumns + k;
3057 name = search->filenames[l];
3058 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3059 linebuf[linebufpos++] = name[j];
3060 // space out name unless it's the last on the line
3061 if (k + 1 < numcolumns && l + 1 < numfiles)
3062 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3063 linebuf[linebufpos++] = ' ';
3066 linebuf[linebufpos] = 0;
3067 Con_Printf("%s\n", linebuf);
3074 for (i = 0;i < numfiles;i++)
3075 Con_Printf("%s\n", search->filenames[i]);
3076 FS_FreeSearch(search);
3077 return (int)numfiles;
3080 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3082 const char *pattern;
3085 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3088 if (Cmd_Argc() == 2)
3089 pattern = Cmd_Argv(1);
3092 if (!FS_ListDirectory(pattern, oneperline))
3093 Con_Print("No files found.\n");
3098 FS_ListDirectoryCmd("dir", true);
3103 FS_ListDirectoryCmd("ls", false);
3106 void FS_Which_f(void)
3108 const char *filename;
3111 if (Cmd_Argc() != 2)
3113 Con_Printf("usage:\n%s <file>\n", Cmd_Argv(0));
3116 filename = Cmd_Argv(1);
3117 sp = FS_FindFile(filename, &index, true);
3119 Con_Printf("%s isn't anywhere\n", filename);
3123 Con_Printf("%s is in package %s\n", filename, sp->pack->shortname);
3125 Con_Printf("%s is file %s%s\n", filename, sp->filename, filename);
3129 const char *FS_WhichPack(const char *filename)
3132 searchpath_t *sp = FS_FindFile(filename, &index, true);
3134 return sp->pack->shortname;
3140 ====================
3141 FS_IsRegisteredQuakePack
3143 Look for a proof of purchase file file in the requested package
3145 If it is found, this file should NOT be downloaded.
3146 ====================
3148 qboolean FS_IsRegisteredQuakePack(const char *name)
3150 searchpath_t *search;
3153 // search through the path, one element at a time
3154 for (search = fs_searchpaths;search;search = search->next)
3156 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3158 int (*strcmp_funct) (const char* str1, const char* str2);
3159 int left, right, middle;
3162 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3164 // Look for the file (binary search)
3166 right = pak->numfiles - 1;
3167 while (left <= right)
3171 middle = (left + right) / 2;
3172 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3178 // If we're too far in the list
3185 // we found the requested pack but it is not registered quake
3193 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3196 unsigned char *filedata;
3197 fs_offset_t filesize;
3198 if (filesizepointer)
3199 *filesizepointer = 0;
3200 if (!filename || !*filename)
3202 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3205 if (filesizepointer)
3206 *filesizepointer = filesize;
3207 crc = CRC_Block(filedata, filesize);
3213 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3216 unsigned char *out = NULL;
3219 memset(&strm, 0, sizeof(strm));
3220 strm.zalloc = Z_NULL;
3221 strm.zfree = Z_NULL;
3222 strm.opaque = Z_NULL;
3225 level = Z_DEFAULT_COMPRESSION;
3227 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3229 Con_Printf("FS_Deflate: deflate init error!\n");
3233 strm.next_in = (unsigned char*)data;
3234 strm.avail_in = size;
3236 tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
3239 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3240 qz_deflateEnd(&strm);
3244 strm.next_out = tmp;
3245 strm.avail_out = size;
3247 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3249 Con_Printf("FS_Deflate: deflate failed!\n");
3250 qz_deflateEnd(&strm);
3255 if(qz_deflateEnd(&strm) != Z_OK)
3257 Con_Printf("FS_Deflate: deflateEnd failed\n");
3262 if(strm.total_out >= size)
3264 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3269 out = (unsigned char *) Mem_Alloc(mempool, strm.total_out);
3272 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3278 *deflated_size = (size_t)strm.total_out;
3280 memcpy(out, tmp, strm.total_out);
3286 static void AssertBufsize(sizebuf_t *buf, int length)
3288 if(buf->cursize + length > buf->maxsize)
3290 int oldsize = buf->maxsize;
3291 unsigned char *olddata;
3292 olddata = buf->data;
3293 buf->maxsize += length;
3294 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
3297 memcpy(buf->data, olddata, oldsize);
3303 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3307 unsigned char *out = NULL;
3308 unsigned char tmp[2048];
3312 memset(&outbuf, 0, sizeof(outbuf));
3313 outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp));
3314 outbuf.maxsize = sizeof(tmp);
3316 memset(&strm, 0, sizeof(strm));
3317 strm.zalloc = Z_NULL;
3318 strm.zfree = Z_NULL;
3319 strm.opaque = Z_NULL;
3321 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3323 Con_Printf("FS_Inflate: inflate init error!\n");
3324 Mem_Free(outbuf.data);
3328 strm.next_in = (unsigned char*)data;
3329 strm.avail_in = size;
3333 strm.next_out = tmp;
3334 strm.avail_out = sizeof(tmp);
3335 ret = qz_inflate(&strm, Z_NO_FLUSH);
3336 // it either returns Z_OK on progress, Z_STREAM_END on end
3344 case Z_STREAM_ERROR:
3345 Con_Print("FS_Inflate: stream error!\n");
3348 Con_Print("FS_Inflate: data error!\n");
3351 Con_Print("FS_Inflate: mem error!\n");
3354 Con_Print("FS_Inflate: buf error!\n");
3357 Con_Print("FS_Inflate: unknown error!\n");
3361 if(ret != Z_OK && ret != Z_STREAM_END)
3363 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3364 Mem_Free(outbuf.data);
3365 qz_inflateEnd(&strm);
3368 have = sizeof(tmp) - strm.avail_out;
3369 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3370 SZ_Write(&outbuf, tmp, have);
3371 } while(ret != Z_STREAM_END);
3373 qz_inflateEnd(&strm);
3375 out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize);
3378 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3379 Mem_Free(outbuf.data);
3383 memcpy(out, outbuf.data, outbuf.cursize);
3384 Mem_Free(outbuf.data);
3387 *inflated_size = (size_t)outbuf.cursize;