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 =============================================================================
289 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
290 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
291 fs_offset_t offset, fs_offset_t packsize,
292 fs_offset_t realsize, int flags);
296 =============================================================================
300 =============================================================================
303 mempool_t *fs_mempool;
305 searchpath_t *fs_searchpaths = NULL;
307 #define MAX_FILES_IN_PACK 65536
309 char fs_gamedir[MAX_OSPATH];
310 char fs_basedir[MAX_OSPATH];
312 // list of active game directories (empty if not running a mod)
313 int fs_numgamedirs = 0;
314 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
316 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)"};
317 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"};
321 =============================================================================
323 PRIVATE FUNCTIONS - PK3 HANDLING
325 =============================================================================
328 // Functions exported from zlib
329 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
330 # define ZEXPORT WINAPI
335 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
336 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
337 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
338 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
339 static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size);
340 static int (ZEXPORT *qz_deflateEnd) (z_stream* strm);
341 static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush);
343 #define qz_inflateInit2(strm, windowBits) \
344 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
345 #define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
346 qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream))
348 // qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
350 static dllfunction_t zlibfuncs[] =
352 {"inflate", (void **) &qz_inflate},
353 {"inflateEnd", (void **) &qz_inflateEnd},
354 {"inflateInit2_", (void **) &qz_inflateInit2_},
355 {"inflateReset", (void **) &qz_inflateReset},
356 {"deflateInit2_", (void **) &qz_deflateInit2_},
357 {"deflateEnd", (void **) &qz_deflateEnd},
358 {"deflate", (void **) &qz_deflate},
362 // Handle for Zlib DLL
363 static dllhandle_t zlib_dll = NULL;
366 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
367 static dllfunction_t shfolderfuncs[] =
369 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
372 static dllhandle_t shfolder_dll = NULL;
382 void PK3_CloseLibrary (void)
384 Sys_UnloadLibrary (&zlib_dll);
392 Try to load the Zlib DLL
395 qboolean PK3_OpenLibrary (void)
397 const char* dllnames [] =
402 # ifdef ZLIB_USES_WINAPI
408 #elif defined(MACOSX)
422 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
429 See if zlib is available
432 qboolean FS_HasZlib(void)
434 PK3_OpenLibrary(); // to be safe
435 return (zlib_dll != 0);
440 PK3_GetEndOfCentralDir
442 Extract the end of the central directory from a PK3 package
445 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
447 fs_offset_t filesize, maxsize;
448 unsigned char *buffer, *ptr;
451 // Get the package size
452 filesize = lseek (packhandle, 0, SEEK_END);
453 if (filesize < ZIP_END_CDIR_SIZE)
456 // Load the end of the file in memory
457 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
460 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
461 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
462 lseek (packhandle, filesize - maxsize, SEEK_SET);
463 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
469 // Look for the end of central dir signature around the end of the file
470 maxsize -= ZIP_END_CDIR_SIZE;
471 ptr = &buffer[maxsize];
473 while (BuffBigLong (ptr) != ZIP_END_HEADER)
485 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
486 eocd->signature = LittleLong (eocd->signature);
487 eocd->disknum = LittleShort (eocd->disknum);
488 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
489 eocd->localentries = LittleShort (eocd->localentries);
490 eocd->nbentries = LittleShort (eocd->nbentries);
491 eocd->cdir_size = LittleLong (eocd->cdir_size);
492 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
493 eocd->comment_size = LittleShort (eocd->comment_size);
505 Extract the file list from a PK3 file
508 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
510 unsigned char *central_dir, *ptr;
512 fs_offset_t remaining;
514 // Load the central directory in memory
515 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
516 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
517 if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size)
519 Mem_Free (central_dir);
523 // Extract the files properties
524 // The parsing is done "by hand" because some fields have variable sizes and
525 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
526 remaining = eocd->cdir_size;
529 for (ind = 0; ind < eocd->nbentries; ind++)
531 fs_offset_t namesize, count;
533 // Checking the remaining size
534 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
536 Mem_Free (central_dir);
539 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
542 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
544 Mem_Free (central_dir);
548 namesize = BuffLittleShort (&ptr[28]); // filename length
550 // Check encryption, compression, and attributes
551 // 1st uint8 : general purpose bit flag
552 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
554 // LordHavoc: bit 3 would be a problem if we were scanning the archive
555 // but is not a problem in the central directory where the values are
558 // bit 3 seems to always be set by the standard Mac OSX zip maker
560 // 2nd uint8 : external file attributes
561 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
562 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
564 // Still enough bytes for the name?
565 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
567 Mem_Free (central_dir);
571 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
572 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
574 char filename [sizeof (pack->files[0].name)];
575 fs_offset_t offset, packsize, realsize;
578 // Extract the name (strip it if necessary)
579 namesize = min(namesize, (int)sizeof (filename) - 1);
580 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
581 filename[namesize] = '\0';
583 if (BuffLittleShort (&ptr[10]))
584 flags = PACKFILE_FLAG_DEFLATED;
587 offset = BuffLittleLong (&ptr[42]);
588 packsize = BuffLittleLong (&ptr[20]);
589 realsize = BuffLittleLong (&ptr[24]);
591 switch(ptr[5]) // C_VERSION_MADE_BY_1
596 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
597 // can't use S_ISLNK here, as this has to compile on non-UNIX too
598 flags |= PACKFILE_FLAG_SYMLINK;
602 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
606 // Skip the name, additionnal field, and comment
607 // 1er uint16 : extra field length
608 // 2eme uint16 : file comment length
609 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
610 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
614 // If the package is empty, central_dir is NULL here
615 if (central_dir != NULL)
616 Mem_Free (central_dir);
617 return pack->numfiles;
625 Create a package entry associated with a PK3 file
628 pack_t *FS_LoadPackPK3 (const char *packfile)
631 pk3_endOfCentralDir_t eocd;
636 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
638 packhandle = open (packfile, O_RDONLY | O_BINARY);
643 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
645 Con_Printf ("%s is not a PK3 file\n", packfile);
650 // Multi-volume ZIP archives are NOT allowed
651 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
653 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
658 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
659 // since eocd.nbentries is an unsigned 16 bits integer
660 #if MAX_FILES_IN_PACK < 65535
661 if (eocd.nbentries > MAX_FILES_IN_PACK)
663 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
669 // Create a package structure in memory
670 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
671 pack->ignorecase = true; // PK3 ignores case
672 strlcpy (pack->filename, packfile, sizeof (pack->filename));
673 pack->handle = packhandle;
674 pack->numfiles = eocd.nbentries;
675 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
677 real_nb_files = PK3_BuildFileList (pack, &eocd);
678 if (real_nb_files < 0)
680 Con_Printf ("%s is not a valid PK3 file\n", packfile);
686 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
693 PK3_GetTrueFileOffset
695 Find where the true file data offset is
698 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
700 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
704 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
707 // Load the local file description
708 lseek (pack->handle, pfile->offset, SEEK_SET);
709 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
710 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
712 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
716 // Skip name and extra field
717 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
719 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
725 =============================================================================
727 OTHER PRIVATE FUNCTIONS
729 =============================================================================
737 Add a file to the list of files contained into a package
740 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
741 fs_offset_t offset, fs_offset_t packsize,
742 fs_offset_t realsize, int flags)
744 int (*strcmp_funct) (const char* str1, const char* str2);
745 int left, right, middle;
748 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
750 // Look for the slot we should put that file into (binary search)
752 right = pack->numfiles - 1;
753 while (left <= right)
757 middle = (left + right) / 2;
758 diff = strcmp_funct (pack->files[middle].name, name);
760 // If we found the file, there's a problem
762 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
764 // If we're too far in the list
771 // We have to move the right of the list by one slot to free the one we need
772 pfile = &pack->files[left];
773 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
776 strlcpy (pfile->name, name, sizeof (pfile->name));
777 pfile->offset = offset;
778 pfile->packsize = packsize;
779 pfile->realsize = realsize;
780 pfile->flags = flags;
790 Only used for FS_OpenRealFile.
793 void FS_CreatePath (char *path)
797 for (ofs = path+1 ; *ofs ; ofs++)
799 if (*ofs == '/' || *ofs == '\\')
801 // create the directory
817 void FS_Path_f (void)
821 Con_Print("Current search path:\n");
822 for (s=fs_searchpaths ; s ; s=s->next)
825 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
827 Con_Printf("%s\n", s->filename);
836 Takes an explicit (not game tree related) path to a pak file.
838 Loads the header and directory, adding the files at the beginning
839 of the list so they override previous pack files.
842 pack_t *FS_LoadPackPAK (const char *packfile)
844 dpackheader_t header;
851 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
853 packhandle = open (packfile, O_RDONLY | O_BINARY);
857 if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
859 Con_Printf ("%s is not a packfile\n", packfile);
863 if (memcmp(header.id, "PACK", 4))
865 Con_Printf ("%s is not a packfile\n", packfile);
869 header.dirofs = LittleLong (header.dirofs);
870 header.dirlen = LittleLong (header.dirlen);
872 if (header.dirlen % sizeof(dpackfile_t))
874 Con_Printf ("%s has an invalid directory size\n", packfile);
879 numpackfiles = header.dirlen / sizeof(dpackfile_t);
881 if (numpackfiles > MAX_FILES_IN_PACK)
883 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
888 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
889 lseek (packhandle, header.dirofs, SEEK_SET);
890 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
892 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
898 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
899 pack->ignorecase = false; // PAK is case sensitive
900 strlcpy (pack->filename, packfile, sizeof (pack->filename));
901 pack->handle = packhandle;
903 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
905 // parse the directory
906 for (i = 0;i < numpackfiles;i++)
908 fs_offset_t offset = LittleLong (info[i].filepos);
909 fs_offset_t size = LittleLong (info[i].filelen);
911 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
916 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
924 Adds the given pack to the search path.
925 The pack type is autodetected by the file extension.
927 Returns true if the file was successfully added to the
928 search path or if it was already included.
930 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
934 static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs)
936 searchpath_t *search;
938 const char *ext = FS_FileExtension(pakfile);
940 for(search = fs_searchpaths; search; search = search->next)
942 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
945 *already_loaded = true;
946 return true; // already loaded
951 *already_loaded = false;
953 if(!strcasecmp(ext, "pak"))
954 pak = FS_LoadPackPAK (pakfile);
955 else if(!strcasecmp(ext, "pk3"))
956 pak = FS_LoadPackPK3 (pakfile);
958 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
962 strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
963 //Con_DPrintf(" Registered pack with short name %s\n", shortname);
966 // find the first item whose next one is a pack or NULL
967 searchpath_t *insertion_point = 0;
968 if(fs_searchpaths && !fs_searchpaths->pack)
970 insertion_point = fs_searchpaths;
973 if(!insertion_point->next)
975 if(insertion_point->next->pack)
977 insertion_point = insertion_point->next;
980 // If insertion_point is NULL, this means that either there is no
981 // item in the list yet, or that the very first item is a pack. In
982 // that case, we want to insert at the beginning...
985 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
987 search->next = fs_searchpaths;
988 fs_searchpaths = search;
991 // otherwise we want to append directly after insertion_point.
993 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
995 search->next = insertion_point->next;
996 insertion_point->next = search;
1001 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1003 search->next = fs_searchpaths;
1004 fs_searchpaths = search;
1010 Con_Printf("unable to load pak \"%s\"\n", pakfile);
1020 Adds the given pack to the search path and searches for it in the game path.
1021 The pack type is autodetected by the file extension.
1023 Returns true if the file was successfully added to the
1024 search path or if it was already included.
1026 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
1030 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
1032 char fullpath[MAX_QPATH];
1034 searchpath_t *search;
1037 *already_loaded = false;
1039 // then find the real name...
1040 search = FS_FindFile(pakfile, &index, true);
1041 if(!search || search->pack)
1043 Con_Printf("could not find pak \"%s\"\n", pakfile);
1047 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
1049 return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
1057 Sets fs_gamedir, adds the directory to the head of the path,
1058 then loads and adds pak1.pak pak2.pak ...
1061 void FS_AddGameDirectory (const char *dir)
1065 searchpath_t *search;
1067 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1069 stringlistinit(&list);
1070 listdirectory(&list, "", dir);
1071 stringlistsort(&list);
1073 // add any PAK package in the directory
1074 for (i = 0;i < list.numstrings;i++)
1076 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1078 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1082 // add any PK3 package in the directory
1083 for (i = 0;i < list.numstrings;i++)
1085 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1087 FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
1091 stringlistfreecontents(&list);
1093 // Add the directory to the search path
1094 // (unpacked files have the priority over packed files)
1095 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1096 strlcpy (search->filename, dir, sizeof (search->filename));
1097 search->next = fs_searchpaths;
1098 fs_searchpaths = search;
1107 void FS_AddGameHierarchy (const char *dir)
1110 char userdir[MAX_QPATH];
1112 TCHAR mydocsdir[MAX_PATH + 1];
1113 #if _MSC_VER >= 1400
1119 // Add the common game directory
1120 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1124 // Add the personal game directory
1126 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1128 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1129 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", userdir);
1133 // use the environment
1134 #if _MSC_VER >= 1400
1135 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1137 homedir = getenv("USERPROFILE");
1142 dpsnprintf(userdir, sizeof(userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1143 #if _MSC_VER >= 1400
1146 Con_DPrintf("Obtained personal directory %s from environment\n", userdir);
1149 *userdir = 0; // just to make sure it hasn't been written to by SHGetFolderPath returning failure
1153 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1155 homedir = getenv ("HOME");
1157 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1160 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1165 if(!COM_CheckParm("-mygames"))
1167 #if _MSC_VER >= 1400
1169 _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!
1171 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1176 *userdir = 0; // we have write access to the game dir, so let's use it
1181 if(COM_CheckParm("-nohome"))
1184 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1185 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1188 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1197 const char *FS_FileExtension (const char *in)
1199 const char *separator, *backslash, *colon, *dot;
1201 separator = strrchr(in, '/');
1202 backslash = strrchr(in, '\\');
1203 if (!separator || separator < backslash)
1204 separator = backslash;
1205 colon = strrchr(in, ':');
1206 if (!separator || separator < colon)
1209 dot = strrchr(in, '.');
1210 if (dot == NULL || (separator && (dot < separator)))
1222 const char *FS_FileWithoutPath (const char *in)
1224 const char *separator, *backslash, *colon;
1226 separator = strrchr(in, '/');
1227 backslash = strrchr(in, '\\');
1228 if (!separator || separator < backslash)
1229 separator = backslash;
1230 colon = strrchr(in, ':');
1231 if (!separator || separator < colon)
1233 return separator ? separator + 1 : in;
1242 void FS_ClearSearchPath (void)
1244 // unload all packs and directory information, close all pack files
1245 // (if a qfile is still reading a pack it won't be harmed because it used
1246 // dup() to get its own handle already)
1247 while (fs_searchpaths)
1249 searchpath_t *search = fs_searchpaths;
1250 fs_searchpaths = search->next;
1254 close(search->pack->handle);
1255 // free any memory associated with it
1256 if (search->pack->files)
1257 Mem_Free(search->pack->files);
1258 Mem_Free(search->pack);
1270 void FS_Rescan (void)
1273 qboolean fs_modified = false;
1275 FS_ClearSearchPath();
1277 // add the game-specific paths
1278 // gamedirname1 (typically id1)
1279 FS_AddGameHierarchy (gamedirname1);
1280 // update the com_modname (used for server info)
1281 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1283 // add the game-specific path, if any
1284 // (only used for mission packs and the like, which should set fs_modified)
1288 FS_AddGameHierarchy (gamedirname2);
1292 // Adds basedir/gamedir as an override game
1293 // LordHavoc: now supports multiple -game directories
1294 // set the com_modname (reported in server info)
1295 for (i = 0;i < fs_numgamedirs;i++)
1298 FS_AddGameHierarchy (fs_gamedirs[i]);
1299 // update the com_modname (used server info)
1300 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1303 // set the default screenshot name to either the mod name or the
1304 // gamemode screenshot name
1305 if (strcmp(com_modname, gamedirname1))
1306 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1308 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1310 // If "-condebug" is in the command line, remove the previous log file
1311 if (COM_CheckParm ("-condebug") != 0)
1312 unlink (va("%s/qconsole.log", fs_gamedir));
1314 // look for the pop.lmp file and set registered to true if it is found
1315 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1318 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1320 Con_Print("Playing shareware version.\n");
1324 Cvar_Set ("registered", "1");
1325 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1326 Con_Print("Playing registered version.\n");
1329 // unload all wads so that future queries will return the new data
1333 void FS_Rescan_f(void)
1343 extern void Host_SaveConfig (void);
1344 extern void Host_LoadConfig_f (void);
1345 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1349 if (fs_numgamedirs == numgamedirs)
1351 for (i = 0;i < numgamedirs;i++)
1352 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1354 if (i == numgamedirs)
1355 return true; // already using this set of gamedirs, do nothing
1358 if (numgamedirs > MAX_GAMEDIRS)
1361 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1362 return false; // too many gamedirs
1365 for (i = 0;i < numgamedirs;i++)
1367 // if string is nasty, reject it
1368 if(FS_CheckNastyPath(gamedirs[i], true))
1371 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1372 return false; // nasty gamedirs
1376 for (i = 0;i < numgamedirs;i++)
1378 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1381 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1382 return false; // missing gamedirs
1388 fs_numgamedirs = numgamedirs;
1389 for (i = 0;i < fs_numgamedirs;i++)
1390 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1392 // reinitialize filesystem to detect the new paks
1395 // exec the new config
1396 Host_LoadConfig_f();
1398 // unload all sounds so they will be reloaded from the new files as needed
1399 S_UnloadAllSounds_f();
1401 // reinitialize renderer (this reloads hud/console background/etc)
1402 R_Modules_Restart();
1412 void FS_GameDir_f (void)
1416 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1420 Con_Printf("gamedirs active:");
1421 for (i = 0;i < fs_numgamedirs;i++)
1422 Con_Printf(" %s", fs_gamedirs[i]);
1427 numgamedirs = Cmd_Argc() - 1;
1428 if (numgamedirs > MAX_GAMEDIRS)
1430 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1434 for (i = 0;i < numgamedirs;i++)
1435 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1437 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1439 // actually, changing during game would work fine, but would be stupid
1440 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1444 // halt demo playback to close the file
1447 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1456 qboolean FS_CheckGameDir(const char *gamedir)
1460 stringlistinit(&list);
1461 listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
1462 success = list.numstrings > 0;
1463 stringlistfreecontents(&list);
1478 const char* dllnames [] =
1480 "shfolder.dll", // IE 4, or Win NT and higher
1483 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1484 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1487 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1489 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1491 // If the base directory is explicitly defined by the compilation process
1492 #ifdef DP_FS_BASEDIR
1493 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1495 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1498 // FIXME: is there a better way to find the directory outside the .app?
1499 if (strstr(com_argv[0], ".app/"))
1503 split = strstr(com_argv[0], ".app/");
1504 while (split > com_argv[0] && *split != '/')
1506 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1507 fs_basedir[split - com_argv[0]] = 0;
1515 // Overrides the system supplied base directory (under GAMENAME)
1516 // 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)
1517 i = COM_CheckParm ("-basedir");
1518 if (i && i < com_argc-1)
1520 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1521 i = (int)strlen (fs_basedir);
1522 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1523 fs_basedir[i-1] = 0;
1526 // add a path separator to the end of the basedir if it lacks one
1527 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1528 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1530 if (!FS_CheckGameDir(gamedirname1))
1531 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1533 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1534 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1537 // Adds basedir/gamedir as an override game
1538 // LordHavoc: now supports multiple -game directories
1539 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1543 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1546 if (FS_CheckNastyPath(com_argv[i], true))
1547 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1548 if (!FS_CheckGameDir(com_argv[i]))
1549 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1550 // add the gamedir to the list of active gamedirs
1551 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1556 // generate the searchpath
1560 void FS_Init_Commands(void)
1562 Cvar_RegisterVariable (&scr_screenshot_name);
1563 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1565 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1566 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1567 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1568 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1569 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1577 void FS_Shutdown (void)
1579 // close all pack files and such
1580 // (hopefully there aren't any other open files, but they'll be cleaned up
1581 // by the OS anyway)
1582 FS_ClearSearchPath();
1583 Mem_FreePool (&fs_mempool);
1586 Sys_UnloadLibrary (&shfolder_dll);
1591 ====================
1594 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1595 ====================
1597 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1603 // Parse the mode string
1612 opt = O_CREAT | O_TRUNC;
1616 opt = O_CREAT | O_APPEND;
1619 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1622 for (ind = 1; mode[ind] != '\0'; ind++)
1633 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1634 filepath, mode, mode[ind]);
1641 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1642 memset (file, 0, sizeof (*file));
1645 #if _MSC_VER >= 1400
1646 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1648 file->handle = open (filepath, mod | opt, 0666);
1650 if (file->handle < 0)
1656 file->real_length = lseek (file->handle, 0, SEEK_END);
1658 // For files opened in append mode, we start at the end of the file
1660 file->position = file->real_length;
1662 lseek (file->handle, 0, SEEK_SET);
1672 Open a packed file using its package file descriptor
1675 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1681 pfile = &pack->files[pack_ind];
1683 // If we don't have the true offset, get it now
1684 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1685 if (!PK3_GetTrueFileOffset (pfile, pack))
1688 // No Zlib DLL = no compressed files
1689 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1691 Con_Printf("WARNING: can't open the compressed file %s\n"
1692 "You need the Zlib DLL to use compressed files\n",
1697 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1698 // the dup() call to avoid having to close the dup_handle on error here
1699 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1701 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1702 pfile->name, pack->filename, (int) pfile->offset);
1706 dup_handle = dup (pack->handle);
1709 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1713 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1714 memset (file, 0, sizeof (*file));
1715 file->handle = dup_handle;
1716 file->flags = QFILE_FLAG_PACKED;
1717 file->real_length = pfile->realsize;
1718 file->offset = pfile->offset;
1722 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1726 file->flags |= QFILE_FLAG_DEFLATED;
1728 // We need some more variables
1729 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1731 ztk->comp_length = pfile->packsize;
1733 // Initialize zlib stream
1734 ztk->zstream.next_in = ztk->input;
1735 ztk->zstream.avail_in = 0;
1737 /* From Zlib's "unzip.c":
1739 * windowBits is passed < 0 to tell that there is no zlib header.
1740 * Note that in this case inflate *requires* an extra "dummy" byte
1741 * after the compressed stream in order to complete decompression and
1742 * return Z_STREAM_END.
1743 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1744 * size of both compressed and uncompressed data
1746 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1748 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1754 ztk->zstream.next_out = file->buff;
1755 ztk->zstream.avail_out = sizeof (file->buff);
1764 ====================
1767 Return true if the path should be rejected due to one of the following:
1768 1: path elements that are non-portable
1769 2: path elements that would allow access to files outside the game directory,
1770 or are just not a good idea for a mod to be using.
1771 ====================
1773 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1775 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1779 // Windows: don't allow \ in filenames (windows-only), period.
1780 // (on Windows \ is a directory separator, but / is also supported)
1781 if (strstr(path, "\\"))
1782 return 1; // non-portable
1784 // Mac: don't allow Mac-only filenames - : is a directory separator
1785 // instead of /, but we rely on / working already, so there's no reason to
1786 // support a Mac-only path
1787 // Amiga and Windows: : tries to go to root of drive
1788 if (strstr(path, ":"))
1789 return 1; // non-portable attempt to go to root of drive
1791 // Amiga: // is parent directory
1792 if (strstr(path, "//"))
1793 return 1; // non-portable attempt to go to parent directory
1795 // all: don't allow going to parent directory (../ or /../)
1796 if (strstr(path, ".."))
1797 return 2; // attempt to go outside the game directory
1799 // Windows and UNIXes: don't allow absolute paths
1801 return 2; // attempt to go outside the game directory
1803 // 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
1804 if (strchr(path, '.'))
1808 // gamedir is entirely path elements, so simply forbid . entirely
1811 if (strchr(path, '.') < strrchr(path, '/'))
1812 return 2; // possible attempt to go outside the game directory
1815 // all: forbid trailing slash on gamedir
1816 if (isgamedir && path[strlen(path)-1] == '/')
1819 // all: forbid leading dot on any filename for any reason
1820 if (strstr(path, "/."))
1821 return 2; // attempt to go outside the game directory
1823 // after all these checks we're pretty sure it's a / separated filename
1824 // and won't do much if any harm
1830 ====================
1833 Look for a file in the packages and in the filesystem
1835 Return the searchpath where the file was found (or NULL)
1836 and the file index in the package if relevant
1837 ====================
1839 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1841 searchpath_t *search;
1844 // search through the path, one element at a time
1845 for (search = fs_searchpaths;search;search = search->next)
1847 // is the element a pak file?
1850 int (*strcmp_funct) (const char* str1, const char* str2);
1851 int left, right, middle;
1854 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1856 // Look for the file (binary search)
1858 right = pak->numfiles - 1;
1859 while (left <= right)
1863 middle = (left + right) / 2;
1864 diff = strcmp_funct (pak->files[middle].name, name);
1869 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1871 // yes, but the first one is empty so we treat it as not being there
1872 if (!quiet && developer.integer >= 10)
1873 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1880 if (!quiet && developer.integer >= 10)
1881 Con_Printf("FS_FindFile: %s in %s\n",
1882 pak->files[middle].name, pak->filename);
1889 // If we're too far in the list
1898 char netpath[MAX_OSPATH];
1899 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1900 if (FS_SysFileExists (netpath))
1902 if (!quiet && developer.integer >= 10)
1903 Con_Printf("FS_FindFile: %s\n", netpath);
1912 if (!quiet && developer.integer >= 10)
1913 Con_Printf("FS_FindFile: can't find %s\n", name);
1925 Look for a file in the search paths and open it in read-only mode
1928 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
1930 searchpath_t *search;
1933 search = FS_FindFile (filename, &pack_ind, quiet);
1939 // Found in the filesystem?
1942 char path [MAX_OSPATH];
1943 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1944 return FS_SysOpen (path, "rb", nonblocking);
1947 // So, we found it in a package...
1949 // Is it a PK3 symlink?
1950 // TODO also handle directory symlinks by parsing the whole structure...
1951 // but heck, file symlinks are good enough for now
1952 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
1954 if(symlinkLevels <= 0)
1956 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
1961 char linkbuf[MAX_QPATH];
1963 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
1964 const char *mergeslash;
1969 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
1975 // Now combine the paths...
1976 mergeslash = strrchr(filename, '/');
1977 mergestart = linkbuf;
1979 mergeslash = filename;
1980 while(!strncmp(mergestart, "../", 3))
1983 while(mergeslash > filename)
1986 if(*mergeslash == '/')
1990 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
1991 if(mergeslash == filename)
1993 // Either mergeslash == filename, then we just replace the name (done below)
1997 // Or, we append the name after mergeslash;
1998 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
1999 int spaceNeeded = mergeslash - filename + 1;
2000 int spaceRemoved = mergestart - linkbuf;
2001 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
2003 Con_DPrintf("symlink: too long path rejected\n");
2006 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
2007 memcpy(linkbuf, filename, spaceNeeded);
2008 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
2009 mergestart = linkbuf;
2011 if (!quiet && developer_loading.integer)
2012 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
2013 if(FS_CheckNastyPath (mergestart, false))
2015 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
2018 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
2022 return FS_OpenPackedFile (search->pack, pack_ind);
2027 =============================================================================
2029 MAIN PUBLIC FUNCTIONS
2031 =============================================================================
2035 ====================
2038 Open a file in the userpath. The syntax is the same as fopen
2039 Used for savegame scanning in menu, and all file writing.
2040 ====================
2042 qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
2044 char real_path [MAX_OSPATH];
2046 if (FS_CheckNastyPath(filepath, false))
2048 Con_Printf("FS_OpenRealFile(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
2052 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2054 // If the file is opened in "write", "append", or "read/write" mode,
2055 // create directories up to the file.
2056 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
2057 FS_CreatePath (real_path);
2058 return FS_SysOpen (real_path, mode, false);
2063 ====================
2066 Open a file. The syntax is the same as fopen
2067 ====================
2069 qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
2071 if (FS_CheckNastyPath(filepath, false))
2073 Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
2077 return FS_OpenReadFile (filepath, quiet, false, 16);
2082 ====================
2085 Open a file. The syntax is the same as fopen
2086 ====================
2088 qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
2091 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
2092 memset (file, 0, sizeof (*file));
2093 file->flags = QFILE_FLAG_DATA;
2095 file->real_length = size;
2101 ====================
2105 ====================
2107 int FS_Close (qfile_t* file)
2109 if(file->flags & QFILE_FLAG_DATA)
2115 if (close (file->handle))
2120 qz_inflateEnd (&file->ztk->zstream);
2121 Mem_Free (file->ztk);
2130 ====================
2133 Write "datasize" bytes into a file
2134 ====================
2136 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2140 // If necessary, seek to the exact file position we're supposed to be
2141 if (file->buff_ind != file->buff_len)
2142 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2144 // Purge cached data
2147 // Write the buffer and update the position
2148 result = write (file->handle, data, (fs_offset_t)datasize);
2149 file->position = lseek (file->handle, 0, SEEK_CUR);
2150 if (file->real_length < file->position)
2151 file->real_length = file->position;
2161 ====================
2164 Read up to "buffersize" bytes from a file
2165 ====================
2167 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2169 fs_offset_t count, done;
2171 if (buffersize == 0)
2174 // Get rid of the ungetc character
2175 if (file->ungetc != EOF)
2177 ((char*)buffer)[0] = file->ungetc;
2185 if(file->flags & QFILE_FLAG_DATA)
2187 size_t left = file->real_length - file->position;
2188 if(buffersize > left)
2190 memcpy(buffer, file->data + file->position, buffersize);
2191 file->position += buffersize;
2195 // First, we copy as many bytes as we can from "buff"
2196 if (file->buff_ind < file->buff_len)
2198 count = file->buff_len - file->buff_ind;
2199 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2201 memcpy (buffer, &file->buff[file->buff_ind], count);
2202 file->buff_ind += count;
2204 buffersize -= count;
2205 if (buffersize == 0)
2209 // NOTE: at this point, the read buffer is always empty
2211 // If the file isn't compressed
2212 if (! (file->flags & QFILE_FLAG_DEFLATED))
2216 // We must take care to not read after the end of the file
2217 count = file->real_length - file->position;
2219 // If we have a lot of data to get, put them directly into "buffer"
2220 if (buffersize > sizeof (file->buff) / 2)
2222 if (count > (fs_offset_t)buffersize)
2223 count = (fs_offset_t)buffersize;
2224 lseek (file->handle, file->offset + file->position, SEEK_SET);
2225 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2229 file->position += nb;
2231 // Purge cached data
2237 if (count > (fs_offset_t)sizeof (file->buff))
2238 count = (fs_offset_t)sizeof (file->buff);
2239 lseek (file->handle, file->offset + file->position, SEEK_SET);
2240 nb = read (file->handle, file->buff, count);
2243 file->buff_len = nb;
2244 file->position += nb;
2246 // Copy the requested data in "buffer" (as much as we can)
2247 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2248 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2249 file->buff_ind = count;
2257 // If the file is compressed, it's more complicated...
2258 // We cycle through a few operations until we have read enough data
2259 while (buffersize > 0)
2261 ztoolkit_t *ztk = file->ztk;
2264 // NOTE: at this point, the read buffer is always empty
2266 // If "input" is also empty, we need to refill it
2267 if (ztk->in_ind == ztk->in_len)
2269 // If we are at the end of the file
2270 if (file->position == file->real_length)
2273 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2274 if (count > (fs_offset_t)sizeof (ztk->input))
2275 count = (fs_offset_t)sizeof (ztk->input);
2276 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2277 if (read (file->handle, ztk->input, count) != count)
2279 Con_Printf ("FS_Read: unexpected end of file\n");
2284 ztk->in_len = count;
2285 ztk->in_position += count;
2288 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2289 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2291 // Now that we are sure we have compressed data available, we need to determine
2292 // if it's better to inflate it in "file->buff" or directly in "buffer"
2294 // Inflate the data in "file->buff"
2295 if (buffersize < sizeof (file->buff) / 2)
2297 ztk->zstream.next_out = file->buff;
2298 ztk->zstream.avail_out = sizeof (file->buff);
2299 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2300 if (error != Z_OK && error != Z_STREAM_END)
2302 Con_Printf ("FS_Read: Can't inflate file\n");
2305 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2307 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2308 file->position += file->buff_len;
2310 // Copy the requested data in "buffer" (as much as we can)
2311 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2312 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2313 file->buff_ind = count;
2316 // Else, we inflate directly in "buffer"
2319 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2320 ztk->zstream.avail_out = (unsigned int)buffersize;
2321 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2322 if (error != Z_OK && error != Z_STREAM_END)
2324 Con_Printf ("FS_Read: Can't inflate file\n");
2327 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2329 // How much data did it inflate?
2330 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2331 file->position += count;
2333 // Purge cached data
2338 buffersize -= count;
2346 ====================
2349 Print a string into a file
2350 ====================
2352 int FS_Print (qfile_t* file, const char *msg)
2354 return (int)FS_Write (file, msg, strlen (msg));
2358 ====================
2361 Print a string into a file
2362 ====================
2364 int FS_Printf(qfile_t* file, const char* format, ...)
2369 va_start (args, format);
2370 result = FS_VPrintf (file, format, args);
2378 ====================
2381 Print a string into a file
2382 ====================
2384 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2387 fs_offset_t buff_size = MAX_INPUTLINE;
2392 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2393 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2394 if (len >= 0 && len < buff_size)
2396 Mem_Free (tempbuff);
2400 len = write (file->handle, tempbuff, len);
2401 Mem_Free (tempbuff);
2408 ====================
2411 Get the next character of a file
2412 ====================
2414 int FS_Getc (qfile_t* file)
2418 if (FS_Read (file, &c, 1) != 1)
2426 ====================
2429 Put a character back into the read buffer (only supports one character!)
2430 ====================
2432 int FS_UnGetc (qfile_t* file, unsigned char c)
2434 // If there's already a character waiting to be read
2435 if (file->ungetc != EOF)
2444 ====================
2447 Move the position index in a file
2448 ====================
2450 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2453 unsigned char* buffer;
2454 fs_offset_t buffersize;
2456 // Compute the file offset
2460 offset += file->position - file->buff_len + file->buff_ind;
2467 offset += file->real_length;
2473 if (offset < 0 || offset > file->real_length)
2476 if(file->flags & QFILE_FLAG_DATA)
2478 file->position = offset;
2482 // If we have the data in our read buffer, we don't need to actually seek
2483 if (file->position - file->buff_len <= offset && offset <= file->position)
2485 file->buff_ind = offset + file->buff_len - file->position;
2489 // Purge cached data
2492 // Unpacked or uncompressed files can seek directly
2493 if (! (file->flags & QFILE_FLAG_DEFLATED))
2495 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2497 file->position = offset;
2501 // Seeking in compressed files is more a hack than anything else,
2502 // but we need to support it, so here we go.
2505 // If we have to go back in the file, we need to restart from the beginning
2506 if (offset <= file->position)
2510 ztk->in_position = 0;
2512 lseek (file->handle, file->offset, SEEK_SET);
2514 // Reset the Zlib stream
2515 ztk->zstream.next_in = ztk->input;
2516 ztk->zstream.avail_in = 0;
2517 qz_inflateReset (&ztk->zstream);
2520 // We need a big buffer to force inflating into it directly
2521 buffersize = 2 * sizeof (file->buff);
2522 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2524 // Skip all data until we reach the requested offset
2525 while (offset > file->position)
2527 fs_offset_t diff = offset - file->position;
2528 fs_offset_t count, len;
2530 count = (diff > buffersize) ? buffersize : diff;
2531 len = FS_Read (file, buffer, count);
2545 ====================
2548 Give the current position in a file
2549 ====================
2551 fs_offset_t FS_Tell (qfile_t* file)
2553 return file->position - file->buff_len + file->buff_ind;
2558 ====================
2561 Give the total size of a file
2562 ====================
2564 fs_offset_t FS_FileSize (qfile_t* file)
2566 return file->real_length;
2571 ====================
2574 Erases any buffered input or output data
2575 ====================
2577 void FS_Purge (qfile_t* file)
2589 Filename are relative to the quake directory.
2590 Always appends a 0 byte.
2593 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2596 unsigned char *buf = NULL;
2597 fs_offset_t filesize = 0;
2599 file = FS_OpenVirtualFile(path, quiet);
2602 filesize = file->real_length;
2605 Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false");
2610 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2611 buf[filesize] = '\0';
2612 FS_Read (file, buf, filesize);
2614 if (developer_loadfile.integer)
2615 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2618 if (filesizepointer)
2619 *filesizepointer = filesize;
2628 The filename will be prefixed by the current game directory
2631 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2635 file = FS_OpenRealFile(filename, "wb", false);
2638 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2642 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2643 FS_Write (file, data, len);
2650 =============================================================================
2652 OTHERS PUBLIC FUNCTIONS
2654 =============================================================================
2662 void FS_StripExtension (const char *in, char *out, size_t size_out)
2670 while ((currentchar = *in) && size_out > 1)
2672 if (currentchar == '.')
2674 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2676 *out++ = currentchar;
2692 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2696 // if path doesn't have a .EXT, append extension
2697 // (extension should include the .)
2698 src = path + strlen(path) - 1;
2700 while (*src != '/' && src != path)
2703 return; // it has an extension
2707 strlcat (path, extension, size_path);
2715 Look for a file in the packages and in the filesystem
2718 int FS_FileType (const char *filename)
2720 searchpath_t *search;
2721 char fullpath[MAX_QPATH];
2723 search = FS_FindFile (filename, NULL, true);
2725 return FS_FILETYPE_NONE;
2728 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2730 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2731 return FS_SysFileType(fullpath);
2739 Look for a file in the packages and in the filesystem
2742 qboolean FS_FileExists (const char *filename)
2744 return (FS_FindFile (filename, NULL, true) != NULL);
2752 Look for a file in the filesystem only
2755 int FS_SysFileType (const char *path)
2758 // Sajt - some older sdks are missing this define
2759 # ifndef INVALID_FILE_ATTRIBUTES
2760 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2763 DWORD result = GetFileAttributes(path);
2765 if(result == INVALID_FILE_ATTRIBUTES)
2766 return FS_FILETYPE_NONE;
2768 if(result & FILE_ATTRIBUTE_DIRECTORY)
2769 return FS_FILETYPE_DIRECTORY;
2771 return FS_FILETYPE_FILE;
2775 if (stat (path,&buf) == -1)
2776 return FS_FILETYPE_NONE;
2778 if(S_ISDIR(buf.st_mode))
2779 return FS_FILETYPE_DIRECTORY;
2781 return FS_FILETYPE_FILE;
2785 qboolean FS_SysFileExists (const char *path)
2787 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2790 void FS_mkdir (const char *path)
2803 Allocate and fill a search structure with information on matching filenames.
2806 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2809 searchpath_t *searchpath;
2811 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2812 stringlist_t resultlist;
2813 stringlist_t dirlist;
2814 const char *slash, *backslash, *colon, *separator;
2816 char temp[MAX_OSPATH];
2818 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2823 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2827 stringlistinit(&resultlist);
2828 stringlistinit(&dirlist);
2830 slash = strrchr(pattern, '/');
2831 backslash = strrchr(pattern, '\\');
2832 colon = strrchr(pattern, ':');
2833 separator = max(slash, backslash);
2834 separator = max(separator, colon);
2835 basepathlength = separator ? (separator + 1 - pattern) : 0;
2836 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2838 memcpy(basepath, pattern, basepathlength);
2839 basepath[basepathlength] = 0;
2841 // search through the path, one element at a time
2842 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2844 // is the element a pak file?
2845 if (searchpath->pack)
2847 // look through all the pak file elements
2848 pak = searchpath->pack;
2849 for (i = 0;i < pak->numfiles;i++)
2851 strlcpy(temp, pak->files[i].name, sizeof(temp));
2854 if (matchpattern(temp, (char *)pattern, true))
2856 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2857 if (!strcmp(resultlist.strings[resultlistindex], temp))
2859 if (resultlistindex == resultlist.numstrings)
2861 stringlistappend(&resultlist, temp);
2862 if (!quiet && developer_loading.integer)
2863 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2866 // strip off one path element at a time until empty
2867 // this way directories are added to the listing if they match the pattern
2868 slash = strrchr(temp, '/');
2869 backslash = strrchr(temp, '\\');
2870 colon = strrchr(temp, ':');
2872 if (separator < slash)
2874 if (separator < backslash)
2875 separator = backslash;
2876 if (separator < colon)
2878 *((char *)separator) = 0;
2884 stringlist_t matchedSet, foundSet;
2885 const char *start = pattern;
2887 stringlistinit(&matchedSet);
2888 stringlistinit(&foundSet);
2889 // add a first entry to the set
2890 stringlistappend(&matchedSet, "");
2891 // iterate through pattern's path
2894 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
2895 char subpath[MAX_OSPATH];
2896 char subpattern[MAX_OSPATH];
2898 // find the next wildcard
2899 wildcard = strchr(start, '?');
2900 asterisk = strchr(start, '*');
2901 if (asterisk && (!wildcard || asterisk < wildcard))
2903 wildcard = asterisk;
2908 nextseparator = strchr( wildcard, '/' );
2912 nextseparator = NULL;
2915 if( !nextseparator ) {
2916 nextseparator = start + strlen( start );
2919 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
2920 // copy everything up except nextseperator
2921 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
2922 // find the last '/' before the wildcard
2923 prevseparator = strrchr( subpattern, '/' );
2925 prevseparator = subpattern;
2928 // copy everything from start to the previous including the '/' (before the wildcard)
2929 // everything up to start is already included in the path of matchedSet's entries
2930 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
2932 // for each entry in matchedSet try to open the subdirectories specified in subpath
2933 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
2934 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
2935 strlcat( temp, subpath, sizeof(temp) );
2936 listdirectory( &foundSet, searchpath->filename, temp );
2938 if( dirlistindex == 0 ) {
2941 // reset the current result set
2942 stringlistfreecontents( &matchedSet );
2943 // match against the pattern
2944 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
2945 const char *direntry = foundSet.strings[ dirlistindex ];
2946 if (matchpattern(direntry, subpattern, true)) {
2947 stringlistappend( &matchedSet, direntry );
2950 stringlistfreecontents( &foundSet );
2952 start = nextseparator;
2955 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
2957 const char *temp = matchedSet.strings[dirlistindex];
2958 if (matchpattern(temp, (char *)pattern, true))
2960 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2961 if (!strcmp(resultlist.strings[resultlistindex], temp))
2963 if (resultlistindex == resultlist.numstrings)
2965 stringlistappend(&resultlist, temp);
2966 if (!quiet && developer_loading.integer)
2967 Con_Printf("SearchDirFile: %s\n", temp);
2971 stringlistfreecontents( &matchedSet );
2975 if (resultlist.numstrings)
2977 stringlistsort(&resultlist);
2978 numfiles = resultlist.numstrings;
2980 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2981 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2982 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2983 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2984 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2985 search->numfilenames = (int)numfiles;
2988 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2991 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2992 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2993 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2995 numchars += (int)textlen;
2998 stringlistfreecontents(&resultlist);
3004 void FS_FreeSearch(fssearch_t *search)
3009 extern int con_linewidth;
3010 int FS_ListDirectory(const char *pattern, int oneperline)
3019 char linebuf[MAX_INPUTLINE];
3021 search = FS_Search(pattern, true, true);
3024 numfiles = search->numfilenames;
3027 // FIXME: the names could be added to one column list and then
3028 // gradually shifted into the next column if they fit, and then the
3029 // next to make a compact variable width listing but it's a lot more
3031 // find width for columns
3033 for (i = 0;i < numfiles;i++)
3035 l = (int)strlen(search->filenames[i]);
3036 if (columnwidth < l)
3039 // count the spacing character
3041 // calculate number of columns
3042 numcolumns = con_linewidth / columnwidth;
3043 // don't bother with the column printing if it's only one column
3044 if (numcolumns >= 2)
3046 numlines = (numfiles + numcolumns - 1) / numcolumns;
3047 for (i = 0;i < numlines;i++)
3050 for (k = 0;k < numcolumns;k++)
3052 l = i * numcolumns + k;
3055 name = search->filenames[l];
3056 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
3057 linebuf[linebufpos++] = name[j];
3058 // space out name unless it's the last on the line
3059 if (k + 1 < numcolumns && l + 1 < numfiles)
3060 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
3061 linebuf[linebufpos++] = ' ';
3064 linebuf[linebufpos] = 0;
3065 Con_Printf("%s\n", linebuf);
3072 for (i = 0;i < numfiles;i++)
3073 Con_Printf("%s\n", search->filenames[i]);
3074 FS_FreeSearch(search);
3075 return (int)numfiles;
3078 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
3080 const char *pattern;
3083 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
3086 if (Cmd_Argc() == 2)
3087 pattern = Cmd_Argv(1);
3090 if (!FS_ListDirectory(pattern, oneperline))
3091 Con_Print("No files found.\n");
3096 FS_ListDirectoryCmd("dir", true);
3101 FS_ListDirectoryCmd("ls", false);
3104 const char *FS_WhichPack(const char *filename)
3107 searchpath_t *sp = FS_FindFile(filename, &index, true);
3109 return sp->pack->shortname;
3115 ====================
3116 FS_IsRegisteredQuakePack
3118 Look for a proof of purchase file file in the requested package
3120 If it is found, this file should NOT be downloaded.
3121 ====================
3123 qboolean FS_IsRegisteredQuakePack(const char *name)
3125 searchpath_t *search;
3128 // search through the path, one element at a time
3129 for (search = fs_searchpaths;search;search = search->next)
3131 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3133 int (*strcmp_funct) (const char* str1, const char* str2);
3134 int left, right, middle;
3137 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3139 // Look for the file (binary search)
3141 right = pak->numfiles - 1;
3142 while (left <= right)
3146 middle = (left + right) / 2;
3147 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3153 // If we're too far in the list
3160 // we found the requested pack but it is not registered quake
3168 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3171 unsigned char *filedata;
3172 fs_offset_t filesize;
3173 if (filesizepointer)
3174 *filesizepointer = 0;
3175 if (!filename || !*filename)
3177 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3180 if (filesizepointer)
3181 *filesizepointer = filesize;
3182 crc = CRC_Block(filedata, filesize);
3188 unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool)
3191 unsigned char *out = NULL;
3194 memset(&strm, 0, sizeof(strm));
3195 strm.zalloc = Z_NULL;
3196 strm.zfree = Z_NULL;
3197 strm.opaque = Z_NULL;
3200 level = Z_DEFAULT_COMPRESSION;
3202 if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK)
3204 Con_Printf("FS_Deflate: deflate init error!\n");
3208 strm.next_in = (unsigned char*)data;
3209 strm.avail_in = size;
3211 tmp = Mem_Alloc(tempmempool, size);
3214 Con_Printf("FS_Deflate: not enough memory in tempmempool!\n");
3215 qz_deflateEnd(&strm);
3219 strm.next_out = tmp;
3220 strm.avail_out = size;
3222 if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
3224 Con_Printf("FS_Deflate: deflate failed!\n");
3225 qz_deflateEnd(&strm);
3230 if(qz_deflateEnd(&strm) != Z_OK)
3232 Con_Printf("FS_Deflate: deflateEnd failed\n");
3237 if(strm.total_out >= size)
3239 Con_Printf("FS_Deflate: deflate is useless on this data!\n");
3244 out = Mem_Alloc(mempool, strm.total_out);
3247 Con_Printf("FS_Deflate: not enough memory in target mempool!\n");
3253 *deflated_size = (size_t)strm.total_out;
3255 memcpy(out, tmp, strm.total_out);
3261 static void AssertBufsize(sizebuf_t *buf, int length)
3263 if(buf->cursize + length > buf->maxsize)
3265 int oldsize = buf->maxsize;
3266 unsigned char *olddata;
3267 olddata = buf->data;
3268 buf->maxsize += length;
3269 buf->data = Mem_Alloc(tempmempool, buf->maxsize);
3272 memcpy(buf->data, olddata, oldsize);
3278 unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool)
3282 unsigned char *out = NULL;
3283 unsigned char tmp[2048];
3287 memset(&outbuf, 0, sizeof(outbuf));
3288 outbuf.data = Mem_Alloc(tempmempool, sizeof(tmp));
3289 outbuf.maxsize = sizeof(tmp);
3291 memset(&strm, 0, sizeof(strm));
3292 strm.zalloc = Z_NULL;
3293 strm.zfree = Z_NULL;
3294 strm.opaque = Z_NULL;
3296 if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK)
3298 Con_Printf("FS_Inflate: inflate init error!\n");
3299 Mem_Free(outbuf.data);
3303 strm.next_in = (unsigned char*)data;
3304 strm.avail_in = size;
3308 strm.next_out = tmp;
3309 strm.avail_out = sizeof(tmp);
3310 ret = qz_inflate(&strm, Z_NO_FLUSH);
3311 // it either returns Z_OK on progress, Z_STREAM_END on end
3319 case Z_STREAM_ERROR:
3320 Con_Print("FS_Inflate: stream error!\n");
3323 Con_Print("FS_Inflate: data error!\n");
3326 Con_Print("FS_Inflate: mem error!\n");
3329 Con_Print("FS_Inflate: buf error!\n");
3332 Con_Print("FS_Inflate: unknown error!\n");
3336 if(ret != Z_OK && ret != Z_STREAM_END)
3338 Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in);
3339 Mem_Free(outbuf.data);
3340 qz_inflateEnd(&strm);
3343 have = sizeof(tmp) - strm.avail_out;
3344 AssertBufsize(&outbuf, max(have, sizeof(tmp)));
3345 SZ_Write(&outbuf, tmp, have);
3346 } while(ret != Z_STREAM_END);
3348 qz_inflateEnd(&strm);
3350 out = Mem_Alloc(mempool, outbuf.cursize);
3353 Con_Printf("FS_Inflate: not enough memory in target mempool!\n");
3354 Mem_Free(outbuf.data);
3358 memcpy(out, outbuf.data, outbuf.cursize);
3359 Mem_Free(outbuf.data);
3362 *inflated_size = (size_t)outbuf.cursize;