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 ZLIB_VERSION "1.2.3"
116 // Uncomment the following line if the zlib DLL you have still uses
117 // the 1.1.x series calling convention on Win32 (WINAPI)
118 //#define ZLIB_USES_WINAPI
122 =============================================================================
126 =============================================================================
129 // Zlib stream (from zlib.h)
130 // Warning: some pointers we don't use directly have
131 // been cast to "void*" for a matter of simplicity
134 unsigned char *next_in; // next input byte
135 unsigned int avail_in; // number of bytes available at next_in
136 unsigned long total_in; // total nb of input bytes read so far
138 unsigned char *next_out; // next output byte should be put there
139 unsigned int avail_out; // remaining free space at next_out
140 unsigned long total_out; // total nb of bytes output so far
142 char *msg; // last error message, NULL if no error
143 void *state; // not visible by applications
145 void *zalloc; // used to allocate the internal state
146 void *zfree; // used to free the internal state
147 void *opaque; // private data object passed to zalloc and zfree
149 int data_type; // best guess about the data type: ascii or binary
150 unsigned long adler; // adler32 value of the uncompressed data
151 unsigned long reserved; // reserved for future use
155 // inside a package (PAK or PK3)
156 #define QFILE_FLAG_PACKED (1 << 0)
157 // file is compressed using the deflate algorithm (PK3 only)
158 #define QFILE_FLAG_DEFLATED (1 << 1)
160 #define FILE_BUFF_SIZE 2048
164 size_t comp_length; // length of the compressed file
165 size_t in_ind, in_len; // input buffer current index and length
166 size_t in_position; // position in the compressed file
167 unsigned char input [FILE_BUFF_SIZE];
173 int handle; // file descriptor
174 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
175 fs_offset_t position; // current position in the file
176 fs_offset_t offset; // offset into the package (0 if external file)
177 int ungetc; // single stored character from ungetc, cleared to EOF when read
180 fs_offset_t buff_ind, buff_len; // buffer current index and length
181 unsigned char buff [FILE_BUFF_SIZE];
188 // ------ PK3 files on disk ------ //
190 // You can get the complete ZIP format description from PKWARE website
192 typedef struct pk3_endOfCentralDir_s
194 unsigned int signature;
195 unsigned short disknum;
196 unsigned short cdir_disknum; // number of the disk with the start of the central directory
197 unsigned short localentries; // number of entries in the central directory on this disk
198 unsigned short nbentries; // total number of entries in the central directory on this disk
199 unsigned int cdir_size; // size of the central directory
200 unsigned int cdir_offset; // with respect to the starting disk number
201 unsigned short comment_size;
202 } pk3_endOfCentralDir_t;
205 // ------ PAK files on disk ------ //
206 typedef struct dpackfile_s
209 int filepos, filelen;
212 typedef struct dpackheader_s
220 // Packages in memory
221 // the offset in packfile_t is the true contents offset
222 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
223 // file compressed using the deflate algorithm
224 #define PACKFILE_FLAG_DEFLATED (1 << 1)
226 typedef struct packfile_s
228 char name [MAX_QPATH];
231 fs_offset_t packsize; // size in the package
232 fs_offset_t realsize; // real file size (uncompressed)
235 typedef struct pack_s
237 char filename [MAX_OSPATH];
239 int ignorecase; // PK3 ignores case
245 // Search paths for files (including packages)
246 typedef struct searchpath_s
248 // only one of filename / pack will be used
249 char filename[MAX_OSPATH];
251 struct searchpath_s *next;
256 =============================================================================
260 =============================================================================
266 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
267 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
268 fs_offset_t offset, fs_offset_t packsize,
269 fs_offset_t realsize, int flags);
273 =============================================================================
277 =============================================================================
280 mempool_t *fs_mempool;
282 searchpath_t *fs_searchpaths = NULL;
284 #define MAX_FILES_IN_PACK 65536
286 char fs_gamedir[MAX_OSPATH];
287 char fs_basedir[MAX_OSPATH];
289 // list of active game directories (empty if not running a mod)
290 int fs_numgamedirs = 0;
291 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
293 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)"};
294 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"};
298 =============================================================================
300 PRIVATE FUNCTIONS - PK3 HANDLING
302 =============================================================================
305 // Functions exported from zlib
306 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
307 # define ZEXPORT WINAPI
312 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
313 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
314 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
315 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
317 #define qz_inflateInit2(strm, windowBits) \
318 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
320 static dllfunction_t zlibfuncs[] =
322 {"inflate", (void **) &qz_inflate},
323 {"inflateEnd", (void **) &qz_inflateEnd},
324 {"inflateInit2_", (void **) &qz_inflateInit2_},
325 {"inflateReset", (void **) &qz_inflateReset},
329 // Handle for Zlib DLL
330 static dllhandle_t zlib_dll = NULL;
333 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
334 static dllfunction_t shfolderfuncs[] =
336 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
339 static dllhandle_t shfolder_dll = NULL;
349 void PK3_CloseLibrary (void)
351 Sys_UnloadLibrary (&zlib_dll);
359 Try to load the Zlib DLL
362 qboolean PK3_OpenLibrary (void)
364 const char* dllnames [] =
369 # ifdef ZLIB_USES_WINAPI
375 #elif defined(MACOSX)
389 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
395 PK3_GetEndOfCentralDir
397 Extract the end of the central directory from a PK3 package
400 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
402 fs_offset_t filesize, maxsize;
403 unsigned char *buffer, *ptr;
406 // Get the package size
407 filesize = lseek (packhandle, 0, SEEK_END);
408 if (filesize < ZIP_END_CDIR_SIZE)
411 // Load the end of the file in memory
412 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
415 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
416 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
417 lseek (packhandle, filesize - maxsize, SEEK_SET);
418 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
424 // Look for the end of central dir signature around the end of the file
425 maxsize -= ZIP_END_CDIR_SIZE;
426 ptr = &buffer[maxsize];
428 while (BuffBigLong (ptr) != ZIP_END_HEADER)
440 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
441 eocd->signature = LittleLong (eocd->signature);
442 eocd->disknum = LittleShort (eocd->disknum);
443 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
444 eocd->localentries = LittleShort (eocd->localentries);
445 eocd->nbentries = LittleShort (eocd->nbentries);
446 eocd->cdir_size = LittleLong (eocd->cdir_size);
447 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
448 eocd->comment_size = LittleShort (eocd->comment_size);
460 Extract the file list from a PK3 file
463 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
465 unsigned char *central_dir, *ptr;
467 fs_offset_t remaining;
469 // Load the central directory in memory
470 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
471 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
472 read (pack->handle, central_dir, eocd->cdir_size);
474 // Extract the files properties
475 // The parsing is done "by hand" because some fields have variable sizes and
476 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
477 remaining = eocd->cdir_size;
480 for (ind = 0; ind < eocd->nbentries; ind++)
482 fs_offset_t namesize, count;
484 // Checking the remaining size
485 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
487 Mem_Free (central_dir);
490 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
493 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
495 Mem_Free (central_dir);
499 namesize = BuffLittleShort (&ptr[28]); // filename length
501 // Check encryption, compression, and attributes
502 // 1st uint8 : general purpose bit flag
503 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
505 // LordHavoc: bit 3 would be a problem if we were scanning the archive
506 // but is not a problem in the central directory where the values are
509 // bit 3 seems to always be set by the standard Mac OSX zip maker
511 // 2nd uint8 : external file attributes
512 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
513 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
515 // Still enough bytes for the name?
516 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
518 Mem_Free (central_dir);
522 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
523 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
525 char filename [sizeof (pack->files[0].name)];
526 fs_offset_t offset, packsize, realsize;
529 // Extract the name (strip it if necessary)
530 namesize = min(namesize, (int)sizeof (filename) - 1);
531 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
532 filename[namesize] = '\0';
534 if (BuffLittleShort (&ptr[10]))
535 flags = PACKFILE_FLAG_DEFLATED;
538 offset = BuffLittleLong (&ptr[42]);
539 packsize = BuffLittleLong (&ptr[20]);
540 realsize = BuffLittleLong (&ptr[24]);
541 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
545 // Skip the name, additionnal field, and comment
546 // 1er uint16 : extra field length
547 // 2eme uint16 : file comment length
548 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
549 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
553 // If the package is empty, central_dir is NULL here
554 if (central_dir != NULL)
555 Mem_Free (central_dir);
556 return pack->numfiles;
564 Create a package entry associated with a PK3 file
567 pack_t *FS_LoadPackPK3 (const char *packfile)
570 pk3_endOfCentralDir_t eocd;
575 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
577 packhandle = open (packfile, O_RDONLY | O_BINARY);
582 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
584 Con_Printf ("%s is not a PK3 file\n", packfile);
589 // Multi-volume ZIP archives are NOT allowed
590 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
592 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
597 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
598 // since eocd.nbentries is an unsigned 16 bits integer
599 #if MAX_FILES_IN_PACK < 65535
600 if (eocd.nbentries > MAX_FILES_IN_PACK)
602 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
608 // Create a package structure in memory
609 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
610 pack->ignorecase = true; // PK3 ignores case
611 strlcpy (pack->filename, packfile, sizeof (pack->filename));
612 pack->handle = packhandle;
613 pack->numfiles = eocd.nbentries;
614 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
616 real_nb_files = PK3_BuildFileList (pack, &eocd);
617 if (real_nb_files < 0)
619 Con_Printf ("%s is not a valid PK3 file\n", packfile);
625 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
632 PK3_GetTrueFileOffset
634 Find where the true file data offset is
637 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
639 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
643 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
646 // Load the local file description
647 lseek (pack->handle, pfile->offset, SEEK_SET);
648 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
649 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
651 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
655 // Skip name and extra field
656 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
658 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
664 =============================================================================
666 OTHER PRIVATE FUNCTIONS
668 =============================================================================
676 Add a file to the list of files contained into a package
679 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
680 fs_offset_t offset, fs_offset_t packsize,
681 fs_offset_t realsize, int flags)
683 int (*strcmp_funct) (const char* str1, const char* str2);
684 int left, right, middle;
687 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
689 // Look for the slot we should put that file into (binary search)
691 right = pack->numfiles - 1;
692 while (left <= right)
696 middle = (left + right) / 2;
697 diff = strcmp_funct (pack->files[middle].name, name);
699 // If we found the file, there's a problem
701 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
703 // If we're too far in the list
710 // We have to move the right of the list by one slot to free the one we need
711 pfile = &pack->files[left];
712 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
715 strlcpy (pfile->name, name, sizeof (pfile->name));
716 pfile->offset = offset;
717 pfile->packsize = packsize;
718 pfile->realsize = realsize;
719 pfile->flags = flags;
729 Only used for FS_Open.
732 void FS_CreatePath (char *path)
736 for (ofs = path+1 ; *ofs ; ofs++)
738 if (*ofs == '/' || *ofs == '\\')
740 // create the directory
756 void FS_Path_f (void)
760 Con_Print("Current search path:\n");
761 for (s=fs_searchpaths ; s ; s=s->next)
764 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
766 Con_Printf("%s\n", s->filename);
775 Takes an explicit (not game tree related) path to a pak file.
777 Loads the header and directory, adding the files at the beginning
778 of the list so they override previous pack files.
781 pack_t *FS_LoadPackPAK (const char *packfile)
783 dpackheader_t header;
790 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
792 packhandle = open (packfile, O_RDONLY | O_BINARY);
796 read (packhandle, (void *)&header, sizeof(header));
797 if (memcmp(header.id, "PACK", 4))
799 Con_Printf ("%s is not a packfile\n", packfile);
803 header.dirofs = LittleLong (header.dirofs);
804 header.dirlen = LittleLong (header.dirlen);
806 if (header.dirlen % sizeof(dpackfile_t))
808 Con_Printf ("%s has an invalid directory size\n", packfile);
813 numpackfiles = header.dirlen / sizeof(dpackfile_t);
815 if (numpackfiles > MAX_FILES_IN_PACK)
817 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
822 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
823 lseek (packhandle, header.dirofs, SEEK_SET);
824 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
826 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
832 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
833 pack->ignorecase = false; // PAK is case sensitive
834 strlcpy (pack->filename, packfile, sizeof (pack->filename));
835 pack->handle = packhandle;
837 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
839 // parse the directory
840 for (i = 0;i < numpackfiles;i++)
842 fs_offset_t offset = LittleLong (info[i].filepos);
843 fs_offset_t size = LittleLong (info[i].filelen);
845 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
850 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
858 Adds the given pack to the search path.
859 The pack type is autodetected by the file extension.
861 Returns true if the file was successfully added to the
862 search path or if it was already included.
864 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
868 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
870 searchpath_t *search;
872 const char *ext = FS_FileExtension(pakfile);
874 for(search = fs_searchpaths; search; search = search->next)
876 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
879 *already_loaded = true;
880 return true; // already loaded
885 *already_loaded = false;
887 if(!strcasecmp(ext, "pak"))
888 pak = FS_LoadPackPAK (pakfile);
889 else if(!strcasecmp(ext, "pk3"))
890 pak = FS_LoadPackPK3 (pakfile);
892 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
898 // find the first item whose next one is a pack or NULL
899 searchpath_t *insertion_point = 0;
900 if(fs_searchpaths && !fs_searchpaths->pack)
902 insertion_point = fs_searchpaths;
905 if(!insertion_point->next)
907 if(insertion_point->next->pack)
909 insertion_point = insertion_point->next;
912 // If insertion_point is NULL, this means that either there is no
913 // item in the list yet, or that the very first item is a pack. In
914 // that case, we want to insert at the beginning...
917 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
919 search->next = fs_searchpaths;
920 fs_searchpaths = search;
923 // otherwise we want to append directly after insertion_point.
925 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
927 search->next = insertion_point->next;
928 insertion_point->next = search;
933 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
935 search->next = fs_searchpaths;
936 fs_searchpaths = search;
942 Con_Printf("unable to load pak \"%s\"\n", pakfile);
952 Adds the given pack to the search path and searches for it in the game path.
953 The pack type is autodetected by the file extension.
955 Returns true if the file was successfully added to the
956 search path or if it was already included.
958 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
962 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
964 char fullpath[MAX_QPATH];
966 searchpath_t *search;
969 *already_loaded = false;
971 // then find the real name...
972 search = FS_FindFile(pakfile, &index, true);
973 if(!search || search->pack)
975 Con_Printf("could not find pak \"%s\"\n", pakfile);
979 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
981 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
989 Sets fs_gamedir, adds the directory to the head of the path,
990 then loads and adds pak1.pak pak2.pak ...
993 void FS_AddGameDirectory (const char *dir)
997 searchpath_t *search;
999 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1001 stringlistinit(&list);
1002 listdirectory(&list, "", dir);
1003 stringlistsort(&list);
1005 // add any PAK package in the directory
1006 for (i = 0;i < list.numstrings;i++)
1008 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1010 FS_AddPack_Fullpath(list.strings[i], NULL, false);
1014 // add any PK3 package in the directory
1015 for (i = 0;i < list.numstrings;i++)
1017 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1019 FS_AddPack_Fullpath(list.strings[i], NULL, false);
1023 stringlistfreecontents(&list);
1025 // Add the directory to the search path
1026 // (unpacked files have the priority over packed files)
1027 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1028 strlcpy (search->filename, dir, sizeof (search->filename));
1029 search->next = fs_searchpaths;
1030 fs_searchpaths = search;
1039 void FS_AddGameHierarchy (const char *dir)
1042 char userdir[MAX_QPATH];
1044 TCHAR mydocsdir[MAX_PATH + 1];
1045 #if _MSC_VER >= 1400
1051 // Add the common game directory
1052 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1056 // Add the personal game directory
1058 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1060 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1061 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", userdir);
1065 // use the environment
1066 #if _MSC_VER >= 1400
1067 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1069 homedir = getenv("USERPROFILE");
1074 dpsnprintf(userdir, sizeof(userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1075 #if _MSC_VER >= 1400
1078 Con_DPrintf("Obtained personal directory %s from environment\n", userdir);
1081 *userdir = 0; // just to make sure it hasn't been written to by SHGetFolderPath returning failure
1085 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1087 homedir = getenv ("HOME");
1089 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1092 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1097 if(!COM_CheckParm("-mygames"))
1099 #if _MSC_VER >= 1400
1101 _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!
1103 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1108 *userdir = 0; // we have write access to the game dir, so let's use it
1113 if(COM_CheckParm("-nohome"))
1116 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1117 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1120 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1129 const char *FS_FileExtension (const char *in)
1131 const char *separator, *backslash, *colon, *dot;
1133 separator = strrchr(in, '/');
1134 backslash = strrchr(in, '\\');
1135 if (!separator || separator < backslash)
1136 separator = backslash;
1137 colon = strrchr(in, ':');
1138 if (!separator || separator < colon)
1141 dot = strrchr(in, '.');
1142 if (dot == NULL || (separator && (dot < separator)))
1154 const char *FS_FileWithoutPath (const char *in)
1156 const char *separator, *backslash, *colon;
1158 separator = strrchr(in, '/');
1159 backslash = strrchr(in, '\\');
1160 if (!separator || separator < backslash)
1161 separator = backslash;
1162 colon = strrchr(in, ':');
1163 if (!separator || separator < colon)
1165 return separator ? separator + 1 : in;
1174 void FS_ClearSearchPath (void)
1176 // unload all packs and directory information, close all pack files
1177 // (if a qfile is still reading a pack it won't be harmed because it used
1178 // dup() to get its own handle already)
1179 while (fs_searchpaths)
1181 searchpath_t *search = fs_searchpaths;
1182 fs_searchpaths = search->next;
1186 close(search->pack->handle);
1187 // free any memory associated with it
1188 if (search->pack->files)
1189 Mem_Free(search->pack->files);
1190 Mem_Free(search->pack);
1202 void FS_Rescan (void)
1205 qboolean fs_modified = false;
1207 FS_ClearSearchPath();
1209 // add the game-specific paths
1210 // gamedirname1 (typically id1)
1211 FS_AddGameHierarchy (gamedirname1);
1212 // update the com_modname (used for server info)
1213 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1215 // add the game-specific path, if any
1216 // (only used for mission packs and the like, which should set fs_modified)
1220 FS_AddGameHierarchy (gamedirname2);
1224 // Adds basedir/gamedir as an override game
1225 // LordHavoc: now supports multiple -game directories
1226 // set the com_modname (reported in server info)
1227 for (i = 0;i < fs_numgamedirs;i++)
1230 FS_AddGameHierarchy (fs_gamedirs[i]);
1231 // update the com_modname (used server info)
1232 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1235 // set the default screenshot name to either the mod name or the
1236 // gamemode screenshot name
1237 if (strcmp(com_modname, gamedirname1))
1238 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1240 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1242 // If "-condebug" is in the command line, remove the previous log file
1243 if (COM_CheckParm ("-condebug") != 0)
1244 unlink (va("%s/qconsole.log", fs_gamedir));
1246 // look for the pop.lmp file and set registered to true if it is found
1247 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1250 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1252 Con_Print("Playing shareware version.\n");
1256 Cvar_Set ("registered", "1");
1257 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1258 Con_Print("Playing registered version.\n");
1261 // unload all wads so that future queries will return the new data
1265 void FS_Rescan_f(void)
1275 extern void Host_SaveConfig (void);
1276 extern void Host_LoadConfig_f (void);
1277 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1281 if (fs_numgamedirs == numgamedirs)
1283 for (i = 0;i < numgamedirs;i++)
1284 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1286 if (i == numgamedirs)
1287 return true; // already using this set of gamedirs, do nothing
1290 if (numgamedirs > MAX_GAMEDIRS)
1293 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1294 return false; // too many gamedirs
1297 for (i = 0;i < numgamedirs;i++)
1299 // if string is nasty, reject it
1300 if(FS_CheckNastyPath(gamedirs[i], true))
1303 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1304 return false; // nasty gamedirs
1308 for (i = 0;i < numgamedirs;i++)
1310 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1313 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1314 return false; // missing gamedirs
1320 fs_numgamedirs = numgamedirs;
1321 for (i = 0;i < fs_numgamedirs;i++)
1322 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1324 // reinitialize filesystem to detect the new paks
1327 // exec the new config
1328 Host_LoadConfig_f();
1330 // unload all sounds so they will be reloaded from the new files as needed
1331 S_UnloadAllSounds_f();
1333 // reinitialize renderer (this reloads hud/console background/etc)
1334 R_Modules_Restart();
1344 void FS_GameDir_f (void)
1348 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1352 Con_Printf("gamedirs active:");
1353 for (i = 0;i < fs_numgamedirs;i++)
1354 Con_Printf(" %s", fs_gamedirs[i]);
1359 numgamedirs = Cmd_Argc() - 1;
1360 if (numgamedirs > MAX_GAMEDIRS)
1362 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1366 for (i = 0;i < numgamedirs;i++)
1367 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1369 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1371 // actually, changing during game would work fine, but would be stupid
1372 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1376 // halt demo playback to close the file
1379 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1388 qboolean FS_CheckGameDir(const char *gamedir)
1392 stringlistinit(&list);
1393 listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
1394 success = list.numstrings > 0;
1395 stringlistfreecontents(&list);
1410 const char* dllnames [] =
1412 "shfolder.dll", // IE 4, or Win NT and higher
1415 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1416 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1419 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1421 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1423 // If the base directory is explicitly defined by the compilation process
1424 #ifdef DP_FS_BASEDIR
1425 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1427 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1430 // FIXME: is there a better way to find the directory outside the .app?
1431 if (strstr(com_argv[0], ".app/"))
1435 split = strstr(com_argv[0], ".app/");
1436 while (split > com_argv[0] && *split != '/')
1438 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1439 fs_basedir[split - com_argv[0]] = 0;
1447 // Overrides the system supplied base directory (under GAMENAME)
1448 // 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)
1449 i = COM_CheckParm ("-basedir");
1450 if (i && i < com_argc-1)
1452 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1453 i = (int)strlen (fs_basedir);
1454 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1455 fs_basedir[i-1] = 0;
1458 // add a path separator to the end of the basedir if it lacks one
1459 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1460 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1462 if (!FS_CheckGameDir(gamedirname1))
1463 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1465 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1466 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1469 // Adds basedir/gamedir as an override game
1470 // LordHavoc: now supports multiple -game directories
1471 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1475 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1478 if (FS_CheckNastyPath(com_argv[i], true))
1479 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1480 if (!FS_CheckGameDir(com_argv[i]))
1481 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1482 // add the gamedir to the list of active gamedirs
1483 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1488 // generate the searchpath
1492 void FS_Init_Commands(void)
1494 Cvar_RegisterVariable (&scr_screenshot_name);
1495 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1497 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1498 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1499 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1500 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1501 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1509 void FS_Shutdown (void)
1511 // close all pack files and such
1512 // (hopefully there aren't any other open files, but they'll be cleaned up
1513 // by the OS anyway)
1514 FS_ClearSearchPath();
1515 Mem_FreePool (&fs_mempool);
1518 Sys_UnloadLibrary (&shfolder_dll);
1523 ====================
1526 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1527 ====================
1529 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1535 // Parse the mode string
1544 opt = O_CREAT | O_TRUNC;
1548 opt = O_CREAT | O_APPEND;
1551 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1554 for (ind = 1; mode[ind] != '\0'; ind++)
1565 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1566 filepath, mode, mode[ind]);
1573 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1574 memset (file, 0, sizeof (*file));
1577 #if _MSC_VER >= 1400
1578 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1580 file->handle = open (filepath, mod | opt, 0666);
1582 if (file->handle < 0)
1588 file->real_length = lseek (file->handle, 0, SEEK_END);
1590 // For files opened in append mode, we start at the end of the file
1592 file->position = file->real_length;
1594 lseek (file->handle, 0, SEEK_SET);
1604 Open a packed file using its package file descriptor
1607 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1613 pfile = &pack->files[pack_ind];
1615 // If we don't have the true offset, get it now
1616 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1617 if (!PK3_GetTrueFileOffset (pfile, pack))
1620 // No Zlib DLL = no compressed files
1621 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1623 Con_Printf("WARNING: can't open the compressed file %s\n"
1624 "You need the Zlib DLL to use compressed files\n",
1629 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1630 // the dup() call to avoid having to close the dup_handle on error here
1631 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1633 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1634 pfile->name, pack->filename, (int) pfile->offset);
1638 dup_handle = dup (pack->handle);
1641 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1645 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1646 memset (file, 0, sizeof (*file));
1647 file->handle = dup_handle;
1648 file->flags = QFILE_FLAG_PACKED;
1649 file->real_length = pfile->realsize;
1650 file->offset = pfile->offset;
1654 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1658 file->flags |= QFILE_FLAG_DEFLATED;
1660 // We need some more variables
1661 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1663 ztk->comp_length = pfile->packsize;
1665 // Initialize zlib stream
1666 ztk->zstream.next_in = ztk->input;
1667 ztk->zstream.avail_in = 0;
1669 /* From Zlib's "unzip.c":
1671 * windowBits is passed < 0 to tell that there is no zlib header.
1672 * Note that in this case inflate *requires* an extra "dummy" byte
1673 * after the compressed stream in order to complete decompression and
1674 * return Z_STREAM_END.
1675 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1676 * size of both compressed and uncompressed data
1678 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1680 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1686 ztk->zstream.next_out = file->buff;
1687 ztk->zstream.avail_out = sizeof (file->buff);
1696 ====================
1699 Return true if the path should be rejected due to one of the following:
1700 1: path elements that are non-portable
1701 2: path elements that would allow access to files outside the game directory,
1702 or are just not a good idea for a mod to be using.
1703 ====================
1705 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1707 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1711 // Windows: don't allow \ in filenames (windows-only), period.
1712 // (on Windows \ is a directory separator, but / is also supported)
1713 if (strstr(path, "\\"))
1714 return 1; // non-portable
1716 // Mac: don't allow Mac-only filenames - : is a directory separator
1717 // instead of /, but we rely on / working already, so there's no reason to
1718 // support a Mac-only path
1719 // Amiga and Windows: : tries to go to root of drive
1720 if (strstr(path, ":"))
1721 return 1; // non-portable attempt to go to root of drive
1723 // Amiga: // is parent directory
1724 if (strstr(path, "//"))
1725 return 1; // non-portable attempt to go to parent directory
1727 // all: don't allow going to parent directory (../ or /../)
1728 if (strstr(path, ".."))
1729 return 2; // attempt to go outside the game directory
1731 // Windows and UNIXes: don't allow absolute paths
1733 return 2; // attempt to go outside the game directory
1735 // 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
1736 if (strchr(path, '.'))
1740 // gamedir is entirely path elements, so simply forbid . entirely
1743 if (strchr(path, '.') < strrchr(path, '/'))
1744 return 2; // possible attempt to go outside the game directory
1747 // all: forbid trailing slash on gamedir
1748 if (isgamedir && path[strlen(path)-1] == '/')
1751 // all: forbid leading dot on any filename for any reason
1752 if (strstr(path, "/."))
1753 return 2; // attempt to go outside the game directory
1755 // after all these checks we're pretty sure it's a / separated filename
1756 // and won't do much if any harm
1762 ====================
1765 Look for a file in the packages and in the filesystem
1767 Return the searchpath where the file was found (or NULL)
1768 and the file index in the package if relevant
1769 ====================
1771 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1773 searchpath_t *search;
1776 // search through the path, one element at a time
1777 for (search = fs_searchpaths;search;search = search->next)
1779 // is the element a pak file?
1782 int (*strcmp_funct) (const char* str1, const char* str2);
1783 int left, right, middle;
1786 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1788 // Look for the file (binary search)
1790 right = pak->numfiles - 1;
1791 while (left <= right)
1795 middle = (left + right) / 2;
1796 diff = strcmp_funct (pak->files[middle].name, name);
1801 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1803 // yes, but the first one is empty so we treat it as not being there
1804 if (!quiet && developer.integer >= 10)
1805 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1812 if (!quiet && developer.integer >= 10)
1813 Con_Printf("FS_FindFile: %s in %s\n",
1814 pak->files[middle].name, pak->filename);
1821 // If we're too far in the list
1830 char netpath[MAX_OSPATH];
1831 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1832 if (FS_SysFileExists (netpath))
1834 if (!quiet && developer.integer >= 10)
1835 Con_Printf("FS_FindFile: %s\n", netpath);
1844 if (!quiet && developer.integer >= 10)
1845 Con_Printf("FS_FindFile: can't find %s\n", name);
1857 Look for a file in the search paths and open it in read-only mode
1860 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1862 searchpath_t *search;
1865 search = FS_FindFile (filename, &pack_ind, quiet);
1871 // Found in the filesystem?
1874 char path [MAX_OSPATH];
1875 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1876 return FS_SysOpen (path, "rb", nonblocking);
1879 // So, we found it in a package...
1880 return FS_OpenPackedFile (search->pack, pack_ind);
1885 =============================================================================
1887 MAIN PUBLIC FUNCTIONS
1889 =============================================================================
1893 ====================
1896 Open a file. The syntax is the same as fopen
1897 ====================
1899 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1901 if (FS_CheckNastyPath(filepath, false))
1903 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1907 // If the file is opened in "write", "append", or "read/write" mode
1908 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1910 char real_path [MAX_OSPATH];
1912 // Open the file on disk directly
1913 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1915 // Create directories up to the file
1916 FS_CreatePath (real_path);
1918 return FS_SysOpen (real_path, mode, nonblocking);
1920 // Else, we look at the various search paths and open the file in read-only mode
1922 return FS_OpenReadFile (filepath, quiet, nonblocking);
1927 ====================
1931 ====================
1933 int FS_Close (qfile_t* file)
1935 if (close (file->handle))
1940 qz_inflateEnd (&file->ztk->zstream);
1941 Mem_Free (file->ztk);
1950 ====================
1953 Write "datasize" bytes into a file
1954 ====================
1956 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1960 // If necessary, seek to the exact file position we're supposed to be
1961 if (file->buff_ind != file->buff_len)
1962 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1964 // Purge cached data
1967 // Write the buffer and update the position
1968 result = write (file->handle, data, (fs_offset_t)datasize);
1969 file->position = lseek (file->handle, 0, SEEK_CUR);
1970 if (file->real_length < file->position)
1971 file->real_length = file->position;
1981 ====================
1984 Read up to "buffersize" bytes from a file
1985 ====================
1987 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1989 fs_offset_t count, done;
1991 if (buffersize == 0)
1994 // Get rid of the ungetc character
1995 if (file->ungetc != EOF)
1997 ((char*)buffer)[0] = file->ungetc;
2005 // First, we copy as many bytes as we can from "buff"
2006 if (file->buff_ind < file->buff_len)
2008 count = file->buff_len - file->buff_ind;
2009 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2011 memcpy (buffer, &file->buff[file->buff_ind], count);
2012 file->buff_ind += count;
2014 buffersize -= count;
2015 if (buffersize == 0)
2019 // NOTE: at this point, the read buffer is always empty
2021 // If the file isn't compressed
2022 if (! (file->flags & QFILE_FLAG_DEFLATED))
2026 // We must take care to not read after the end of the file
2027 count = file->real_length - file->position;
2029 // If we have a lot of data to get, put them directly into "buffer"
2030 if (buffersize > sizeof (file->buff) / 2)
2032 if (count > (fs_offset_t)buffersize)
2033 count = (fs_offset_t)buffersize;
2034 lseek (file->handle, file->offset + file->position, SEEK_SET);
2035 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2039 file->position += nb;
2041 // Purge cached data
2047 if (count > (fs_offset_t)sizeof (file->buff))
2048 count = (fs_offset_t)sizeof (file->buff);
2049 lseek (file->handle, file->offset + file->position, SEEK_SET);
2050 nb = read (file->handle, file->buff, count);
2053 file->buff_len = nb;
2054 file->position += nb;
2056 // Copy the requested data in "buffer" (as much as we can)
2057 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2058 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2059 file->buff_ind = count;
2067 // If the file is compressed, it's more complicated...
2068 // We cycle through a few operations until we have read enough data
2069 while (buffersize > 0)
2071 ztoolkit_t *ztk = file->ztk;
2074 // NOTE: at this point, the read buffer is always empty
2076 // If "input" is also empty, we need to refill it
2077 if (ztk->in_ind == ztk->in_len)
2079 // If we are at the end of the file
2080 if (file->position == file->real_length)
2083 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2084 if (count > (fs_offset_t)sizeof (ztk->input))
2085 count = (fs_offset_t)sizeof (ztk->input);
2086 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2087 if (read (file->handle, ztk->input, count) != count)
2089 Con_Printf ("FS_Read: unexpected end of file\n");
2094 ztk->in_len = count;
2095 ztk->in_position += count;
2098 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2099 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2101 // Now that we are sure we have compressed data available, we need to determine
2102 // if it's better to inflate it in "file->buff" or directly in "buffer"
2104 // Inflate the data in "file->buff"
2105 if (buffersize < sizeof (file->buff) / 2)
2107 ztk->zstream.next_out = file->buff;
2108 ztk->zstream.avail_out = sizeof (file->buff);
2109 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2110 if (error != Z_OK && error != Z_STREAM_END)
2112 Con_Printf ("FS_Read: Can't inflate file\n");
2115 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2117 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2118 file->position += file->buff_len;
2120 // Copy the requested data in "buffer" (as much as we can)
2121 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2122 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2123 file->buff_ind = count;
2126 // Else, we inflate directly in "buffer"
2129 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2130 ztk->zstream.avail_out = (unsigned int)buffersize;
2131 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2132 if (error != Z_OK && error != Z_STREAM_END)
2134 Con_Printf ("FS_Read: Can't inflate file\n");
2137 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2139 // How much data did it inflate?
2140 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2141 file->position += count;
2143 // Purge cached data
2148 buffersize -= count;
2156 ====================
2159 Print a string into a file
2160 ====================
2162 int FS_Print (qfile_t* file, const char *msg)
2164 return (int)FS_Write (file, msg, strlen (msg));
2168 ====================
2171 Print a string into a file
2172 ====================
2174 int FS_Printf(qfile_t* file, const char* format, ...)
2179 va_start (args, format);
2180 result = FS_VPrintf (file, format, args);
2188 ====================
2191 Print a string into a file
2192 ====================
2194 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2197 fs_offset_t buff_size = MAX_INPUTLINE;
2202 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2203 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2204 if (len >= 0 && len < buff_size)
2206 Mem_Free (tempbuff);
2210 len = write (file->handle, tempbuff, len);
2211 Mem_Free (tempbuff);
2218 ====================
2221 Get the next character of a file
2222 ====================
2224 int FS_Getc (qfile_t* file)
2228 if (FS_Read (file, &c, 1) != 1)
2236 ====================
2239 Put a character back into the read buffer (only supports one character!)
2240 ====================
2242 int FS_UnGetc (qfile_t* file, unsigned char c)
2244 // If there's already a character waiting to be read
2245 if (file->ungetc != EOF)
2254 ====================
2257 Move the position index in a file
2258 ====================
2260 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2263 unsigned char* buffer;
2264 fs_offset_t buffersize;
2266 // Compute the file offset
2270 offset += file->position - file->buff_len + file->buff_ind;
2277 offset += file->real_length;
2283 if (offset < 0 || offset > file->real_length)
2286 // If we have the data in our read buffer, we don't need to actually seek
2287 if (file->position - file->buff_len <= offset && offset <= file->position)
2289 file->buff_ind = offset + file->buff_len - file->position;
2293 // Purge cached data
2296 // Unpacked or uncompressed files can seek directly
2297 if (! (file->flags & QFILE_FLAG_DEFLATED))
2299 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2301 file->position = offset;
2305 // Seeking in compressed files is more a hack than anything else,
2306 // but we need to support it, so here we go.
2309 // If we have to go back in the file, we need to restart from the beginning
2310 if (offset <= file->position)
2314 ztk->in_position = 0;
2316 lseek (file->handle, file->offset, SEEK_SET);
2318 // Reset the Zlib stream
2319 ztk->zstream.next_in = ztk->input;
2320 ztk->zstream.avail_in = 0;
2321 qz_inflateReset (&ztk->zstream);
2324 // We need a big buffer to force inflating into it directly
2325 buffersize = 2 * sizeof (file->buff);
2326 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2328 // Skip all data until we reach the requested offset
2329 while (offset > file->position)
2331 fs_offset_t diff = offset - file->position;
2332 fs_offset_t count, len;
2334 count = (diff > buffersize) ? buffersize : diff;
2335 len = FS_Read (file, buffer, count);
2349 ====================
2352 Give the current position in a file
2353 ====================
2355 fs_offset_t FS_Tell (qfile_t* file)
2357 return file->position - file->buff_len + file->buff_ind;
2362 ====================
2365 Give the total size of a file
2366 ====================
2368 fs_offset_t FS_FileSize (qfile_t* file)
2370 return file->real_length;
2375 ====================
2378 Erases any buffered input or output data
2379 ====================
2381 void FS_Purge (qfile_t* file)
2393 Filename are relative to the quake directory.
2394 Always appends a 0 byte.
2397 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2400 unsigned char *buf = NULL;
2401 fs_offset_t filesize = 0;
2403 file = FS_Open (path, "rb", quiet, false);
2406 filesize = file->real_length;
2407 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2408 buf[filesize] = '\0';
2409 FS_Read (file, buf, filesize);
2411 if (developer_loadfile.integer)
2412 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2415 if (filesizepointer)
2416 *filesizepointer = filesize;
2425 The filename will be prefixed by the current game directory
2428 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2432 file = FS_Open (filename, "wb", false, false);
2435 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2439 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2440 FS_Write (file, data, len);
2447 =============================================================================
2449 OTHERS PUBLIC FUNCTIONS
2451 =============================================================================
2459 void FS_StripExtension (const char *in, char *out, size_t size_out)
2467 while ((currentchar = *in) && size_out > 1)
2469 if (currentchar == '.')
2471 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2473 *out++ = currentchar;
2489 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2493 // if path doesn't have a .EXT, append extension
2494 // (extension should include the .)
2495 src = path + strlen(path) - 1;
2497 while (*src != '/' && src != path)
2500 return; // it has an extension
2504 strlcat (path, extension, size_path);
2512 Look for a file in the packages and in the filesystem
2515 int FS_FileType (const char *filename)
2517 searchpath_t *search;
2518 char fullpath[MAX_QPATH];
2520 search = FS_FindFile (filename, NULL, true);
2522 return FS_FILETYPE_NONE;
2525 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2527 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2528 return FS_SysFileType(fullpath);
2536 Look for a file in the packages and in the filesystem
2539 qboolean FS_FileExists (const char *filename)
2541 return (FS_FindFile (filename, NULL, true) != NULL);
2549 Look for a file in the filesystem only
2552 int FS_SysFileType (const char *path)
2555 // Sajt - some older sdks are missing this define
2556 # ifndef INVALID_FILE_ATTRIBUTES
2557 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2560 DWORD result = GetFileAttributes(path);
2562 if(result == INVALID_FILE_ATTRIBUTES)
2563 return FS_FILETYPE_NONE;
2565 if(result & FILE_ATTRIBUTE_DIRECTORY)
2566 return FS_FILETYPE_DIRECTORY;
2568 return FS_FILETYPE_FILE;
2572 if (stat (path,&buf) == -1)
2573 return FS_FILETYPE_NONE;
2575 if(S_ISDIR(buf.st_mode))
2576 return FS_FILETYPE_DIRECTORY;
2578 return FS_FILETYPE_FILE;
2582 qboolean FS_SysFileExists (const char *path)
2584 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2587 void FS_mkdir (const char *path)
2600 Allocate and fill a search structure with information on matching filenames.
2603 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2606 searchpath_t *searchpath;
2608 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2609 stringlist_t resultlist;
2610 stringlist_t dirlist;
2611 const char *slash, *backslash, *colon, *separator;
2613 char temp[MAX_OSPATH];
2615 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2620 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2624 stringlistinit(&resultlist);
2625 stringlistinit(&dirlist);
2627 slash = strrchr(pattern, '/');
2628 backslash = strrchr(pattern, '\\');
2629 colon = strrchr(pattern, ':');
2630 separator = max(slash, backslash);
2631 separator = max(separator, colon);
2632 basepathlength = separator ? (separator + 1 - pattern) : 0;
2633 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2635 memcpy(basepath, pattern, basepathlength);
2636 basepath[basepathlength] = 0;
2638 // search through the path, one element at a time
2639 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2641 // is the element a pak file?
2642 if (searchpath->pack)
2644 // look through all the pak file elements
2645 pak = searchpath->pack;
2646 for (i = 0;i < pak->numfiles;i++)
2648 strlcpy(temp, pak->files[i].name, sizeof(temp));
2651 if (matchpattern(temp, (char *)pattern, true))
2653 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2654 if (!strcmp(resultlist.strings[resultlistindex], temp))
2656 if (resultlistindex == resultlist.numstrings)
2658 stringlistappend(&resultlist, temp);
2659 if (!quiet && developer_loading.integer)
2660 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2663 // strip off one path element at a time until empty
2664 // this way directories are added to the listing if they match the pattern
2665 slash = strrchr(temp, '/');
2666 backslash = strrchr(temp, '\\');
2667 colon = strrchr(temp, ':');
2669 if (separator < slash)
2671 if (separator < backslash)
2672 separator = backslash;
2673 if (separator < colon)
2675 *((char *)separator) = 0;
2681 stringlist_t matchedSet, foundSet;
2682 const char *start = pattern;
2684 stringlistinit(&matchedSet);
2685 stringlistinit(&foundSet);
2686 // add a first entry to the set
2687 stringlistappend(&matchedSet, "");
2688 // iterate through pattern's path
2691 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
2692 char subpath[MAX_OSPATH];
2693 char subpattern[MAX_OSPATH];
2695 // find the next wildcard
2696 wildcard = strchr(start, '?');
2697 asterisk = strchr(start, '*');
2698 if (asterisk && (!wildcard || asterisk < wildcard))
2700 wildcard = asterisk;
2705 nextseparator = strchr( wildcard, '/' );
2709 nextseparator = NULL;
2712 if( !nextseparator ) {
2713 nextseparator = start + strlen( start );
2716 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
2717 // copy everything up except nextseperator
2718 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
2719 // find the last '/' before the wildcard
2720 prevseparator = strrchr( subpattern, '/' );
2722 prevseparator = subpattern;
2725 // copy everything from start to the previous including the '/' (before the wildcard)
2726 // everything up to start is already included in the path of matchedSet's entries
2727 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
2729 // for each entry in matchedSet try to open the subdirectories specified in subpath
2730 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
2731 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
2732 strlcat( temp, subpath, sizeof(temp) );
2733 listdirectory( &foundSet, searchpath->filename, temp );
2735 if( dirlistindex == 0 ) {
2738 // reset the current result set
2739 stringlistfreecontents( &matchedSet );
2740 // match against the pattern
2741 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
2742 const char *direntry = foundSet.strings[ dirlistindex ];
2743 if (matchpattern(direntry, subpattern, true)) {
2744 stringlistappend( &matchedSet, direntry );
2747 stringlistfreecontents( &foundSet );
2749 start = nextseparator;
2752 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
2754 const char *temp = matchedSet.strings[dirlistindex];
2755 if (matchpattern(temp, (char *)pattern, true))
2757 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2758 if (!strcmp(resultlist.strings[resultlistindex], temp))
2760 if (resultlistindex == resultlist.numstrings)
2762 stringlistappend(&resultlist, temp);
2763 if (!quiet && developer_loading.integer)
2764 Con_Printf("SearchDirFile: %s\n", temp);
2768 stringlistfreecontents( &matchedSet );
2772 if (resultlist.numstrings)
2774 stringlistsort(&resultlist);
2775 numfiles = resultlist.numstrings;
2777 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2778 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2779 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2780 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2781 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2782 search->numfilenames = (int)numfiles;
2785 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2788 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2789 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2790 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2792 numchars += (int)textlen;
2795 stringlistfreecontents(&resultlist);
2801 void FS_FreeSearch(fssearch_t *search)
2806 extern int con_linewidth;
2807 int FS_ListDirectory(const char *pattern, int oneperline)
2816 char linebuf[MAX_INPUTLINE];
2818 search = FS_Search(pattern, true, true);
2821 numfiles = search->numfilenames;
2824 // FIXME: the names could be added to one column list and then
2825 // gradually shifted into the next column if they fit, and then the
2826 // next to make a compact variable width listing but it's a lot more
2828 // find width for columns
2830 for (i = 0;i < numfiles;i++)
2832 l = (int)strlen(search->filenames[i]);
2833 if (columnwidth < l)
2836 // count the spacing character
2838 // calculate number of columns
2839 numcolumns = con_linewidth / columnwidth;
2840 // don't bother with the column printing if it's only one column
2841 if (numcolumns >= 2)
2843 numlines = (numfiles + numcolumns - 1) / numcolumns;
2844 for (i = 0;i < numlines;i++)
2847 for (k = 0;k < numcolumns;k++)
2849 l = i * numcolumns + k;
2852 name = search->filenames[l];
2853 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2854 linebuf[linebufpos++] = name[j];
2855 // space out name unless it's the last on the line
2856 if (k + 1 < numcolumns && l + 1 < numfiles)
2857 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2858 linebuf[linebufpos++] = ' ';
2861 linebuf[linebufpos] = 0;
2862 Con_Printf("%s\n", linebuf);
2869 for (i = 0;i < numfiles;i++)
2870 Con_Printf("%s\n", search->filenames[i]);
2871 FS_FreeSearch(search);
2872 return (int)numfiles;
2875 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2877 const char *pattern;
2880 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2883 if (Cmd_Argc() == 2)
2884 pattern = Cmd_Argv(1);
2887 if (!FS_ListDirectory(pattern, oneperline))
2888 Con_Print("No files found.\n");
2893 FS_ListDirectoryCmd("dir", true);
2898 FS_ListDirectoryCmd("ls", false);
2901 const char *FS_WhichPack(const char *filename)
2904 searchpath_t *sp = FS_FindFile(filename, &index, true);
2906 return sp->pack->filename;
2912 ====================
2913 FS_IsRegisteredQuakePack
2915 Look for a proof of purchase file file in the requested package
2917 If it is found, this file should NOT be downloaded.
2918 ====================
2920 qboolean FS_IsRegisteredQuakePack(const char *name)
2922 searchpath_t *search;
2925 // search through the path, one element at a time
2926 for (search = fs_searchpaths;search;search = search->next)
2928 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2930 int (*strcmp_funct) (const char* str1, const char* str2);
2931 int left, right, middle;
2934 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2936 // Look for the file (binary search)
2938 right = pak->numfiles - 1;
2939 while (left <= right)
2943 middle = (left + right) / 2;
2944 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2950 // If we're too far in the list
2957 // we found the requested pack but it is not registered quake
2965 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2968 unsigned char *filedata;
2969 fs_offset_t filesize;
2970 if (filesizepointer)
2971 *filesizepointer = 0;
2972 if (!filename || !*filename)
2974 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2977 if (filesizepointer)
2978 *filesizepointer = filesize;
2979 crc = CRC_Block(filedata, filesize);