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
60 All of Quake's data access is through a hierchal file system, but the contents
61 of the file system can be transparently merged from several sources.
63 The "base directory" is the path to the directory holding the quake.exe and
64 all game directories. The sys_* files pass this to host_init in
65 quakeparms_t->basedir. This can be overridden with the "-basedir" command
66 line parm to allow code debugging in a different directory. The base
67 directory is only used during filesystem initialization.
69 The "game directory" is the first tree on the search path and directory that
70 all generated files (savegames, screenshots, demos, config files) will be
71 saved to. This can be overridden with the "-game" command line parameter.
72 The game directory can never be changed while quake is executing. This is a
73 precaution against having a malicious server instruct clients to write files
74 over areas they shouldn't.
80 =============================================================================
84 =============================================================================
87 // Magic numbers of a ZIP file (big-endian format)
88 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
89 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
90 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
92 // Other constants for ZIP files
93 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
94 #define ZIP_END_CDIR_SIZE 22
95 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
96 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
98 // Zlib constants (from zlib.h)
99 #define Z_SYNC_FLUSH 2
102 #define Z_STREAM_END 1
103 #define ZLIB_VERSION "1.2.3"
105 // Uncomment the following line if the zlib DLL you have still uses
106 // the 1.1.x series calling convention on Win32 (WINAPI)
107 //#define ZLIB_USES_WINAPI
111 =============================================================================
115 =============================================================================
118 // Zlib stream (from zlib.h)
119 // Warning: some pointers we don't use directly have
120 // been cast to "void*" for a matter of simplicity
123 unsigned char *next_in; // next input byte
124 unsigned int avail_in; // number of bytes available at next_in
125 unsigned long total_in; // total nb of input bytes read so far
127 unsigned char *next_out; // next output byte should be put there
128 unsigned int avail_out; // remaining free space at next_out
129 unsigned long total_out; // total nb of bytes output so far
131 char *msg; // last error message, NULL if no error
132 void *state; // not visible by applications
134 void *zalloc; // used to allocate the internal state
135 void *zfree; // used to free the internal state
136 void *opaque; // private data object passed to zalloc and zfree
138 int data_type; // best guess about the data type: ascii or binary
139 unsigned long adler; // adler32 value of the uncompressed data
140 unsigned long reserved; // reserved for future use
144 // inside a package (PAK or PK3)
145 #define QFILE_FLAG_PACKED (1 << 0)
146 // file is compressed using the deflate algorithm (PK3 only)
147 #define QFILE_FLAG_DEFLATED (1 << 1)
149 #define FILE_BUFF_SIZE 2048
153 size_t comp_length; // length of the compressed file
154 size_t in_ind, in_len; // input buffer current index and length
155 size_t in_position; // position in the compressed file
156 unsigned char input [FILE_BUFF_SIZE];
162 int handle; // file descriptor
163 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
164 fs_offset_t position; // current position in the file
165 fs_offset_t offset; // offset into the package (0 if external file)
166 int ungetc; // single stored character from ungetc, cleared to EOF when read
169 fs_offset_t buff_ind, buff_len; // buffer current index and length
170 unsigned char buff [FILE_BUFF_SIZE];
177 // ------ PK3 files on disk ------ //
179 // You can get the complete ZIP format description from PKWARE website
181 typedef struct pk3_endOfCentralDir_s
183 unsigned int signature;
184 unsigned short disknum;
185 unsigned short cdir_disknum; // number of the disk with the start of the central directory
186 unsigned short localentries; // number of entries in the central directory on this disk
187 unsigned short nbentries; // total number of entries in the central directory on this disk
188 unsigned int cdir_size; // size of the central directory
189 unsigned int cdir_offset; // with respect to the starting disk number
190 unsigned short comment_size;
191 } pk3_endOfCentralDir_t;
194 // ------ PAK files on disk ------ //
195 typedef struct dpackfile_s
198 int filepos, filelen;
201 typedef struct dpackheader_s
209 // Packages in memory
210 // the offset in packfile_t is the true contents offset
211 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
212 // file compressed using the deflate algorithm
213 #define PACKFILE_FLAG_DEFLATED (1 << 1)
215 typedef struct packfile_s
217 char name [MAX_QPATH];
220 fs_offset_t packsize; // size in the package
221 fs_offset_t realsize; // real file size (uncompressed)
224 typedef struct pack_s
226 char filename [MAX_OSPATH];
228 int ignorecase; // PK3 ignores case
234 // Search paths for files (including packages)
235 typedef struct searchpath_s
237 // only one of filename / pack will be used
238 char filename[MAX_OSPATH];
240 struct searchpath_s *next;
245 =============================================================================
249 =============================================================================
255 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
256 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
257 fs_offset_t offset, fs_offset_t packsize,
258 fs_offset_t realsize, int flags);
262 =============================================================================
266 =============================================================================
269 mempool_t *fs_mempool;
271 searchpath_t *fs_searchpaths = NULL;
273 #define MAX_FILES_IN_PACK 65536
275 char fs_gamedir[MAX_OSPATH];
276 char fs_basedir[MAX_OSPATH];
278 // list of active game directories (empty if not running a mod)
279 int fs_numgamedirs = 0;
280 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
282 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)"};
283 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"};
287 =============================================================================
289 PRIVATE FUNCTIONS - PK3 HANDLING
291 =============================================================================
294 // Functions exported from zlib
295 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
296 # define ZEXPORT WINAPI
301 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
302 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
303 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
304 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
306 #define qz_inflateInit2(strm, windowBits) \
307 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
309 static dllfunction_t zlibfuncs[] =
311 {"inflate", (void **) &qz_inflate},
312 {"inflateEnd", (void **) &qz_inflateEnd},
313 {"inflateInit2_", (void **) &qz_inflateInit2_},
314 {"inflateReset", (void **) &qz_inflateReset},
318 // Handle for Zlib DLL
319 static dllhandle_t zlib_dll = NULL;
322 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
323 static dllfunction_t shfolderfuncs[] =
325 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
328 static dllhandle_t shfolder_dll = NULL;
338 void PK3_CloseLibrary (void)
340 Sys_UnloadLibrary (&zlib_dll);
348 Try to load the Zlib DLL
351 qboolean PK3_OpenLibrary (void)
353 const char* dllnames [] =
358 # ifdef ZLIB_USES_WINAPI
364 #elif defined(MACOSX)
378 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
384 PK3_GetEndOfCentralDir
386 Extract the end of the central directory from a PK3 package
389 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
391 fs_offset_t filesize, maxsize;
392 unsigned char *buffer, *ptr;
395 // Get the package size
396 filesize = lseek (packhandle, 0, SEEK_END);
397 if (filesize < ZIP_END_CDIR_SIZE)
400 // Load the end of the file in memory
401 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
404 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
405 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
406 lseek (packhandle, filesize - maxsize, SEEK_SET);
407 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
413 // Look for the end of central dir signature around the end of the file
414 maxsize -= ZIP_END_CDIR_SIZE;
415 ptr = &buffer[maxsize];
417 while (BuffBigLong (ptr) != ZIP_END_HEADER)
429 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
430 eocd->signature = LittleLong (eocd->signature);
431 eocd->disknum = LittleShort (eocd->disknum);
432 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
433 eocd->localentries = LittleShort (eocd->localentries);
434 eocd->nbentries = LittleShort (eocd->nbentries);
435 eocd->cdir_size = LittleLong (eocd->cdir_size);
436 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
437 eocd->comment_size = LittleShort (eocd->comment_size);
449 Extract the file list from a PK3 file
452 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
454 unsigned char *central_dir, *ptr;
456 fs_offset_t remaining;
458 // Load the central directory in memory
459 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
460 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
461 read (pack->handle, central_dir, eocd->cdir_size);
463 // Extract the files properties
464 // The parsing is done "by hand" because some fields have variable sizes and
465 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
466 remaining = eocd->cdir_size;
469 for (ind = 0; ind < eocd->nbentries; ind++)
471 fs_offset_t namesize, count;
473 // Checking the remaining size
474 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
476 Mem_Free (central_dir);
479 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
482 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
484 Mem_Free (central_dir);
488 namesize = BuffLittleShort (&ptr[28]); // filename length
490 // Check encryption, compression, and attributes
491 // 1st uint8 : general purpose bit flag
492 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
494 // LordHavoc: bit 3 would be a problem if we were scanning the archive
495 // but is not a problem in the central directory where the values are
498 // bit 3 seems to always be set by the standard Mac OSX zip maker
500 // 2nd uint8 : external file attributes
501 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
502 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
504 // Still enough bytes for the name?
505 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
507 Mem_Free (central_dir);
511 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
512 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
514 char filename [sizeof (pack->files[0].name)];
515 fs_offset_t offset, packsize, realsize;
518 // Extract the name (strip it if necessary)
519 namesize = min(namesize, (int)sizeof (filename) - 1);
520 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
521 filename[namesize] = '\0';
523 if (BuffLittleShort (&ptr[10]))
524 flags = PACKFILE_FLAG_DEFLATED;
527 offset = BuffLittleLong (&ptr[42]);
528 packsize = BuffLittleLong (&ptr[20]);
529 realsize = BuffLittleLong (&ptr[24]);
530 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
534 // Skip the name, additionnal field, and comment
535 // 1er uint16 : extra field length
536 // 2eme uint16 : file comment length
537 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
538 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
542 // If the package is empty, central_dir is NULL here
543 if (central_dir != NULL)
544 Mem_Free (central_dir);
545 return pack->numfiles;
553 Create a package entry associated with a PK3 file
556 pack_t *FS_LoadPackPK3 (const char *packfile)
559 pk3_endOfCentralDir_t eocd;
563 packhandle = open (packfile, O_RDONLY | O_BINARY);
567 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
569 Con_Printf ("%s is not a PK3 file\n", packfile);
574 // Multi-volume ZIP archives are NOT allowed
575 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
577 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
582 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
583 // since eocd.nbentries is an unsigned 16 bits integer
584 #if MAX_FILES_IN_PACK < 65535
585 if (eocd.nbentries > MAX_FILES_IN_PACK)
587 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
593 // Create a package structure in memory
594 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
595 pack->ignorecase = true; // PK3 ignores case
596 strlcpy (pack->filename, packfile, sizeof (pack->filename));
597 pack->handle = packhandle;
598 pack->numfiles = eocd.nbentries;
599 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
601 real_nb_files = PK3_BuildFileList (pack, &eocd);
602 if (real_nb_files < 0)
604 Con_Printf ("%s is not a valid PK3 file\n", packfile);
610 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
617 PK3_GetTrueFileOffset
619 Find where the true file data offset is
622 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
624 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
628 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
631 // Load the local file description
632 lseek (pack->handle, pfile->offset, SEEK_SET);
633 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
634 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
636 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
640 // Skip name and extra field
641 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
643 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
649 =============================================================================
651 OTHER PRIVATE FUNCTIONS
653 =============================================================================
661 Add a file to the list of files contained into a package
664 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
665 fs_offset_t offset, fs_offset_t packsize,
666 fs_offset_t realsize, int flags)
668 int (*strcmp_funct) (const char* str1, const char* str2);
669 int left, right, middle;
672 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
674 // Look for the slot we should put that file into (binary search)
676 right = pack->numfiles - 1;
677 while (left <= right)
681 middle = (left + right) / 2;
682 diff = strcmp_funct (pack->files[middle].name, name);
684 // If we found the file, there's a problem
686 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
688 // If we're too far in the list
695 // We have to move the right of the list by one slot to free the one we need
696 pfile = &pack->files[left];
697 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
700 strlcpy (pfile->name, name, sizeof (pfile->name));
701 pfile->offset = offset;
702 pfile->packsize = packsize;
703 pfile->realsize = realsize;
704 pfile->flags = flags;
714 Only used for FS_Open.
717 void FS_CreatePath (char *path)
721 for (ofs = path+1 ; *ofs ; ofs++)
723 if (*ofs == '/' || *ofs == '\\')
725 // create the directory
741 void FS_Path_f (void)
745 Con_Print("Current search path:\n");
746 for (s=fs_searchpaths ; s ; s=s->next)
749 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
751 Con_Printf("%s\n", s->filename);
760 Takes an explicit (not game tree related) path to a pak file.
762 Loads the header and directory, adding the files at the beginning
763 of the list so they override previous pack files.
766 pack_t *FS_LoadPackPAK (const char *packfile)
768 dpackheader_t header;
774 packhandle = open (packfile, O_RDONLY | O_BINARY);
777 read (packhandle, (void *)&header, sizeof(header));
778 if (memcmp(header.id, "PACK", 4))
780 Con_Printf ("%s is not a packfile\n", packfile);
784 header.dirofs = LittleLong (header.dirofs);
785 header.dirlen = LittleLong (header.dirlen);
787 if (header.dirlen % sizeof(dpackfile_t))
789 Con_Printf ("%s has an invalid directory size\n", packfile);
794 numpackfiles = header.dirlen / sizeof(dpackfile_t);
796 if (numpackfiles > MAX_FILES_IN_PACK)
798 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
803 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
804 lseek (packhandle, header.dirofs, SEEK_SET);
805 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
807 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
813 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
814 pack->ignorecase = false; // PAK is case sensitive
815 strlcpy (pack->filename, packfile, sizeof (pack->filename));
816 pack->handle = packhandle;
818 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
820 // parse the directory
821 for (i = 0;i < numpackfiles;i++)
823 fs_offset_t offset = LittleLong (info[i].filepos);
824 fs_offset_t size = LittleLong (info[i].filelen);
826 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
831 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
839 Adds the given pack to the search path.
840 The pack type is autodetected by the file extension.
842 Returns true if the file was successfully added to the
843 search path or if it was already included.
845 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
849 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
851 searchpath_t *search;
853 const char *ext = FS_FileExtension(pakfile);
855 for(search = fs_searchpaths; search; search = search->next)
857 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
860 *already_loaded = true;
861 return true; // already loaded
866 *already_loaded = false;
868 if(!strcasecmp(ext, "pak"))
869 pak = FS_LoadPackPAK (pakfile);
870 else if(!strcasecmp(ext, "pk3"))
871 pak = FS_LoadPackPK3 (pakfile);
873 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
879 // find the first item whose next one is a pack or NULL
880 searchpath_t *insertion_point = 0;
881 if(fs_searchpaths && !fs_searchpaths->pack)
883 insertion_point = fs_searchpaths;
886 if(!insertion_point->next)
888 if(insertion_point->next->pack)
890 insertion_point = insertion_point->next;
893 // If insertion_point is NULL, this means that either there is no
894 // item in the list yet, or that the very first item is a pack. In
895 // that case, we want to insert at the beginning...
898 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
900 search->next = fs_searchpaths;
901 fs_searchpaths = search;
904 // otherwise we want to append directly after insertion_point.
906 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
908 search->next = insertion_point->next;
909 insertion_point->next = search;
914 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
916 search->next = fs_searchpaths;
917 fs_searchpaths = search;
923 Con_Printf("unable to load pak \"%s\"\n", pakfile);
933 Adds the given pack to the search path and searches for it in the game path.
934 The pack type is autodetected by the file extension.
936 Returns true if the file was successfully added to the
937 search path or if it was already included.
939 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
943 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
945 char fullpath[MAX_QPATH];
947 searchpath_t *search;
950 *already_loaded = false;
952 // then find the real name...
953 search = FS_FindFile(pakfile, &index, true);
954 if(!search || search->pack)
956 Con_Printf("could not find pak \"%s\"\n", pakfile);
960 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
962 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
970 Sets fs_gamedir, adds the directory to the head of the path,
971 then loads and adds pak1.pak pak2.pak ...
974 void FS_AddGameDirectory (const char *dir)
978 searchpath_t *search;
980 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
982 stringlistinit(&list);
983 listdirectory(&list, "", dir);
984 stringlistsort(&list);
986 // add any PAK package in the directory
987 for (i = 0;i < list.numstrings;i++)
989 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
991 FS_AddPack_Fullpath(list.strings[i], NULL, false);
995 // add any PK3 package in the directory
996 for (i = 0;i < list.numstrings;i++)
998 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1000 FS_AddPack_Fullpath(list.strings[i], NULL, false);
1004 stringlistfreecontents(&list);
1006 // Add the directory to the search path
1007 // (unpacked files have the priority over packed files)
1008 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1009 strlcpy (search->filename, dir, sizeof (search->filename));
1010 search->next = fs_searchpaths;
1011 fs_searchpaths = search;
1020 void FS_AddGameHierarchy (const char *dir)
1023 char userdir[MAX_QPATH];
1025 TCHAR mydocsdir[MAX_PATH + 1];
1027 const char *homedir;
1029 // Add the common game directory
1030 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1034 // Add the personal game directory
1036 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1038 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1039 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", userdir);
1043 // use the environment
1044 homedir = getenv ("USERPROFILE");
1047 dpsnprintf(userdir, sizeof(userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1048 Con_DPrintf("Obtained personal directory %s from environment\n", userdir);
1051 *userdir = 0; // just to make sure it hasn't been written to by SHGetFolderPath returning failure
1055 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1057 homedir = getenv ("HOME");
1059 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1062 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1067 if(!COM_CheckParm("-mygames"))
1069 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1073 *userdir = 0; // we have write access to the game dir, so let's use it
1078 if(COM_CheckParm("-nohome"))
1081 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1082 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1085 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1094 const char *FS_FileExtension (const char *in)
1096 const char *separator, *backslash, *colon, *dot;
1098 separator = strrchr(in, '/');
1099 backslash = strrchr(in, '\\');
1100 if (!separator || separator < backslash)
1101 separator = backslash;
1102 colon = strrchr(in, ':');
1103 if (!separator || separator < colon)
1106 dot = strrchr(in, '.');
1107 if (dot == NULL || (separator && (dot < separator)))
1119 const char *FS_FileWithoutPath (const char *in)
1121 const char *separator, *backslash, *colon;
1123 separator = strrchr(in, '/');
1124 backslash = strrchr(in, '\\');
1125 if (!separator || separator < backslash)
1126 separator = backslash;
1127 colon = strrchr(in, ':');
1128 if (!separator || separator < colon)
1130 return separator ? separator + 1 : in;
1139 void FS_ClearSearchPath (void)
1141 // unload all packs and directory information, close all pack files
1142 // (if a qfile is still reading a pack it won't be harmed because it used
1143 // dup() to get its own handle already)
1144 while (fs_searchpaths)
1146 searchpath_t *search = fs_searchpaths;
1147 fs_searchpaths = search->next;
1151 close(search->pack->handle);
1152 // free any memory associated with it
1153 if (search->pack->files)
1154 Mem_Free(search->pack->files);
1155 Mem_Free(search->pack);
1167 void FS_Rescan (void)
1170 qboolean fs_modified = false;
1172 FS_ClearSearchPath();
1174 // add the game-specific paths
1175 // gamedirname1 (typically id1)
1176 FS_AddGameHierarchy (gamedirname1);
1177 // update the com_modname (used for server info)
1178 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1180 // add the game-specific path, if any
1181 // (only used for mission packs and the like, which should set fs_modified)
1185 FS_AddGameHierarchy (gamedirname2);
1189 // Adds basedir/gamedir as an override game
1190 // LordHavoc: now supports multiple -game directories
1191 // set the com_modname (reported in server info)
1192 for (i = 0;i < fs_numgamedirs;i++)
1195 FS_AddGameHierarchy (fs_gamedirs[i]);
1196 // update the com_modname (used server info)
1197 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1200 // set the default screenshot name to either the mod name or the
1201 // gamemode screenshot name
1202 if (strcmp(com_modname, gamedirname1))
1203 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1205 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1207 // If "-condebug" is in the command line, remove the previous log file
1208 if (COM_CheckParm ("-condebug") != 0)
1209 unlink (va("%s/qconsole.log", fs_gamedir));
1211 // look for the pop.lmp file and set registered to true if it is found
1212 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1215 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1217 Con_Print("Playing shareware version.\n");
1221 Cvar_Set ("registered", "1");
1222 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1223 Con_Print("Playing registered version.\n");
1226 // unload all wads so that future queries will return the new data
1230 void FS_Rescan_f(void)
1240 extern void Host_SaveConfig (void);
1241 extern void Host_LoadConfig_f (void);
1242 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1246 if (fs_numgamedirs == numgamedirs)
1248 for (i = 0;i < numgamedirs;i++)
1249 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1251 if (i == numgamedirs)
1252 return true; // already using this set of gamedirs, do nothing
1255 if (numgamedirs > MAX_GAMEDIRS)
1258 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1259 return false; // too many gamedirs
1262 for (i = 0;i < numgamedirs;i++)
1264 // if string is nasty, reject it
1265 if(FS_CheckNastyPath(gamedirs[i], true))
1268 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1269 return false; // nasty gamedirs
1273 for (i = 0;i < numgamedirs;i++)
1275 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1278 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1279 return false; // missing gamedirs
1285 fs_numgamedirs = numgamedirs;
1286 for (i = 0;i < fs_numgamedirs;i++)
1287 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1289 // reinitialize filesystem to detect the new paks
1292 // exec the new config
1293 Host_LoadConfig_f();
1295 // unload all sounds so they will be reloaded from the new files as needed
1296 S_UnloadAllSounds_f();
1298 // reinitialize renderer (this reloads hud/console background/etc)
1299 R_Modules_Restart();
1309 void FS_GameDir_f (void)
1313 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1317 Con_Printf("gamedirs active:");
1318 for (i = 0;i < fs_numgamedirs;i++)
1319 Con_Printf(" %s", fs_gamedirs[i]);
1324 numgamedirs = Cmd_Argc() - 1;
1325 if (numgamedirs > MAX_GAMEDIRS)
1327 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1331 for (i = 0;i < numgamedirs;i++)
1332 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1334 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1336 // actually, changing during game would work fine, but would be stupid
1337 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1341 // halt demo playback to close the file
1344 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1353 qboolean FS_CheckGameDir(const char *gamedir)
1357 stringlistinit(&list);
1358 listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
1359 success = list.numstrings > 0;
1360 stringlistfreecontents(&list);
1375 const char* dllnames [] =
1377 "shfolder.dll", // IE 4, or Win NT and higher
1380 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1381 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1384 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1386 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1388 // If the base directory is explicitly defined by the compilation process
1389 #ifdef DP_FS_BASEDIR
1390 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1392 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1395 // FIXME: is there a better way to find the directory outside the .app?
1396 if (strstr(com_argv[0], ".app/"))
1400 split = strstr(com_argv[0], ".app/");
1401 while (split > com_argv[0] && *split != '/')
1403 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1404 fs_basedir[split - com_argv[0]] = 0;
1412 // Overrides the system supplied base directory (under GAMENAME)
1413 // 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)
1414 i = COM_CheckParm ("-basedir");
1415 if (i && i < com_argc-1)
1417 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1418 i = (int)strlen (fs_basedir);
1419 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1420 fs_basedir[i-1] = 0;
1423 // add a path separator to the end of the basedir if it lacks one
1424 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1425 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1427 if (!FS_CheckGameDir(gamedirname1))
1428 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1430 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1431 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1434 // Adds basedir/gamedir as an override game
1435 // LordHavoc: now supports multiple -game directories
1436 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1440 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1443 if (FS_CheckNastyPath(com_argv[i], true))
1444 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1445 if (!FS_CheckGameDir(com_argv[i]))
1446 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1447 // add the gamedir to the list of active gamedirs
1448 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1453 // generate the searchpath
1457 void FS_Init_Commands(void)
1459 Cvar_RegisterVariable (&scr_screenshot_name);
1460 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1462 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1463 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1464 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1465 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1466 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1474 void FS_Shutdown (void)
1476 // close all pack files and such
1477 // (hopefully there aren't any other open files, but they'll be cleaned up
1478 // by the OS anyway)
1479 FS_ClearSearchPath();
1480 Mem_FreePool (&fs_mempool);
1483 Sys_UnloadLibrary (&shfolder_dll);
1488 ====================
1491 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1492 ====================
1494 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1500 // Parse the mode string
1509 opt = O_CREAT | O_TRUNC;
1513 opt = O_CREAT | O_APPEND;
1516 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1519 for (ind = 1; mode[ind] != '\0'; ind++)
1530 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1531 filepath, mode, mode[ind]);
1538 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1539 memset (file, 0, sizeof (*file));
1542 file->handle = open (filepath, mod | opt, 0666);
1543 if (file->handle < 0)
1549 file->real_length = lseek (file->handle, 0, SEEK_END);
1551 // For files opened in append mode, we start at the end of the file
1553 file->position = file->real_length;
1555 lseek (file->handle, 0, SEEK_SET);
1565 Open a packed file using its package file descriptor
1568 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1574 pfile = &pack->files[pack_ind];
1576 // If we don't have the true offset, get it now
1577 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1578 if (!PK3_GetTrueFileOffset (pfile, pack))
1581 // No Zlib DLL = no compressed files
1582 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1584 Con_Printf("WARNING: can't open the compressed file %s\n"
1585 "You need the Zlib DLL to use compressed files\n",
1590 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1591 // the dup() call to avoid having to close the dup_handle on error here
1592 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1594 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1595 pfile->name, pack->filename, (int) pfile->offset);
1599 dup_handle = dup (pack->handle);
1602 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1606 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1607 memset (file, 0, sizeof (*file));
1608 file->handle = dup_handle;
1609 file->flags = QFILE_FLAG_PACKED;
1610 file->real_length = pfile->realsize;
1611 file->offset = pfile->offset;
1615 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1619 file->flags |= QFILE_FLAG_DEFLATED;
1621 // We need some more variables
1622 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1624 ztk->comp_length = pfile->packsize;
1626 // Initialize zlib stream
1627 ztk->zstream.next_in = ztk->input;
1628 ztk->zstream.avail_in = 0;
1630 /* From Zlib's "unzip.c":
1632 * windowBits is passed < 0 to tell that there is no zlib header.
1633 * Note that in this case inflate *requires* an extra "dummy" byte
1634 * after the compressed stream in order to complete decompression and
1635 * return Z_STREAM_END.
1636 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1637 * size of both compressed and uncompressed data
1639 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1641 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1647 ztk->zstream.next_out = file->buff;
1648 ztk->zstream.avail_out = sizeof (file->buff);
1657 ====================
1660 Return true if the path should be rejected due to one of the following:
1661 1: path elements that are non-portable
1662 2: path elements that would allow access to files outside the game directory,
1663 or are just not a good idea for a mod to be using.
1664 ====================
1666 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1668 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1672 // Windows: don't allow \ in filenames (windows-only), period.
1673 // (on Windows \ is a directory separator, but / is also supported)
1674 if (strstr(path, "\\"))
1675 return 1; // non-portable
1677 // Mac: don't allow Mac-only filenames - : is a directory separator
1678 // instead of /, but we rely on / working already, so there's no reason to
1679 // support a Mac-only path
1680 // Amiga and Windows: : tries to go to root of drive
1681 if (strstr(path, ":"))
1682 return 1; // non-portable attempt to go to root of drive
1684 // Amiga: // is parent directory
1685 if (strstr(path, "//"))
1686 return 1; // non-portable attempt to go to parent directory
1688 // all: don't allow going to parent directory (../ or /../)
1689 if (strstr(path, ".."))
1690 return 2; // attempt to go outside the game directory
1692 // Windows and UNIXes: don't allow absolute paths
1694 return 2; // attempt to go outside the game directory
1696 // 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
1697 if (strchr(path, '.'))
1701 // gamedir is entirely path elements, so simply forbid . entirely
1704 if (strchr(path, '.') < strrchr(path, '/'))
1705 return 2; // possible attempt to go outside the game directory
1708 // all: forbid trailing slash on gamedir
1709 if (isgamedir && path[strlen(path)-1] == '/')
1712 // all: forbid leading dot on any filename for any reason
1713 if (strstr(path, "/."))
1714 return 2; // attempt to go outside the game directory
1716 // after all these checks we're pretty sure it's a / separated filename
1717 // and won't do much if any harm
1723 ====================
1726 Look for a file in the packages and in the filesystem
1728 Return the searchpath where the file was found (or NULL)
1729 and the file index in the package if relevant
1730 ====================
1732 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1734 searchpath_t *search;
1737 // search through the path, one element at a time
1738 for (search = fs_searchpaths;search;search = search->next)
1740 // is the element a pak file?
1743 int (*strcmp_funct) (const char* str1, const char* str2);
1744 int left, right, middle;
1747 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1749 // Look for the file (binary search)
1751 right = pak->numfiles - 1;
1752 while (left <= right)
1756 middle = (left + right) / 2;
1757 diff = strcmp_funct (pak->files[middle].name, name);
1762 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1764 // yes, but the first one is empty so we treat it as not being there
1765 if (!quiet && developer.integer >= 10)
1766 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1773 if (!quiet && developer.integer >= 10)
1774 Con_Printf("FS_FindFile: %s in %s\n",
1775 pak->files[middle].name, pak->filename);
1782 // If we're too far in the list
1791 char netpath[MAX_OSPATH];
1792 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1793 if (FS_SysFileExists (netpath))
1795 if (!quiet && developer.integer >= 10)
1796 Con_Printf("FS_FindFile: %s\n", netpath);
1805 if (!quiet && developer.integer >= 10)
1806 Con_Printf("FS_FindFile: can't find %s\n", name);
1818 Look for a file in the search paths and open it in read-only mode
1821 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1823 searchpath_t *search;
1826 search = FS_FindFile (filename, &pack_ind, quiet);
1832 // Found in the filesystem?
1835 char path [MAX_OSPATH];
1836 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1837 return FS_SysOpen (path, "rb", nonblocking);
1840 // So, we found it in a package...
1841 return FS_OpenPackedFile (search->pack, pack_ind);
1846 =============================================================================
1848 MAIN PUBLIC FUNCTIONS
1850 =============================================================================
1854 ====================
1857 Open a file. The syntax is the same as fopen
1858 ====================
1860 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1862 if (FS_CheckNastyPath(filepath, false))
1864 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1868 // If the file is opened in "write", "append", or "read/write" mode
1869 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1871 char real_path [MAX_OSPATH];
1873 // Open the file on disk directly
1874 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1876 // Create directories up to the file
1877 FS_CreatePath (real_path);
1879 return FS_SysOpen (real_path, mode, nonblocking);
1881 // Else, we look at the various search paths and open the file in read-only mode
1883 return FS_OpenReadFile (filepath, quiet, nonblocking);
1888 ====================
1892 ====================
1894 int FS_Close (qfile_t* file)
1896 if (close (file->handle))
1901 qz_inflateEnd (&file->ztk->zstream);
1902 Mem_Free (file->ztk);
1911 ====================
1914 Write "datasize" bytes into a file
1915 ====================
1917 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1921 // If necessary, seek to the exact file position we're supposed to be
1922 if (file->buff_ind != file->buff_len)
1923 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1925 // Purge cached data
1928 // Write the buffer and update the position
1929 result = write (file->handle, data, (fs_offset_t)datasize);
1930 file->position = lseek (file->handle, 0, SEEK_CUR);
1931 if (file->real_length < file->position)
1932 file->real_length = file->position;
1942 ====================
1945 Read up to "buffersize" bytes from a file
1946 ====================
1948 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1950 fs_offset_t count, done;
1952 if (buffersize == 0)
1955 // Get rid of the ungetc character
1956 if (file->ungetc != EOF)
1958 ((char*)buffer)[0] = file->ungetc;
1966 // First, we copy as many bytes as we can from "buff"
1967 if (file->buff_ind < file->buff_len)
1969 count = file->buff_len - file->buff_ind;
1970 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1972 memcpy (buffer, &file->buff[file->buff_ind], count);
1973 file->buff_ind += count;
1975 buffersize -= count;
1976 if (buffersize == 0)
1980 // NOTE: at this point, the read buffer is always empty
1982 // If the file isn't compressed
1983 if (! (file->flags & QFILE_FLAG_DEFLATED))
1987 // We must take care to not read after the end of the file
1988 count = file->real_length - file->position;
1990 // If we have a lot of data to get, put them directly into "buffer"
1991 if (buffersize > sizeof (file->buff) / 2)
1993 if (count > (fs_offset_t)buffersize)
1994 count = (fs_offset_t)buffersize;
1995 lseek (file->handle, file->offset + file->position, SEEK_SET);
1996 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2000 file->position += nb;
2002 // Purge cached data
2008 if (count > (fs_offset_t)sizeof (file->buff))
2009 count = (fs_offset_t)sizeof (file->buff);
2010 lseek (file->handle, file->offset + file->position, SEEK_SET);
2011 nb = read (file->handle, file->buff, count);
2014 file->buff_len = nb;
2015 file->position += nb;
2017 // Copy the requested data in "buffer" (as much as we can)
2018 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2019 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2020 file->buff_ind = count;
2028 // If the file is compressed, it's more complicated...
2029 // We cycle through a few operations until we have read enough data
2030 while (buffersize > 0)
2032 ztoolkit_t *ztk = file->ztk;
2035 // NOTE: at this point, the read buffer is always empty
2037 // If "input" is also empty, we need to refill it
2038 if (ztk->in_ind == ztk->in_len)
2040 // If we are at the end of the file
2041 if (file->position == file->real_length)
2044 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2045 if (count > (fs_offset_t)sizeof (ztk->input))
2046 count = (fs_offset_t)sizeof (ztk->input);
2047 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2048 if (read (file->handle, ztk->input, count) != count)
2050 Con_Printf ("FS_Read: unexpected end of file\n");
2055 ztk->in_len = count;
2056 ztk->in_position += count;
2059 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2060 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2062 // Now that we are sure we have compressed data available, we need to determine
2063 // if it's better to inflate it in "file->buff" or directly in "buffer"
2065 // Inflate the data in "file->buff"
2066 if (buffersize < sizeof (file->buff) / 2)
2068 ztk->zstream.next_out = file->buff;
2069 ztk->zstream.avail_out = sizeof (file->buff);
2070 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2071 if (error != Z_OK && error != Z_STREAM_END)
2073 Con_Printf ("FS_Read: Can't inflate file\n");
2076 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2078 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2079 file->position += file->buff_len;
2081 // Copy the requested data in "buffer" (as much as we can)
2082 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2083 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2084 file->buff_ind = count;
2087 // Else, we inflate directly in "buffer"
2090 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2091 ztk->zstream.avail_out = (unsigned int)buffersize;
2092 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2093 if (error != Z_OK && error != Z_STREAM_END)
2095 Con_Printf ("FS_Read: Can't inflate file\n");
2098 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2100 // How much data did it inflate?
2101 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2102 file->position += count;
2104 // Purge cached data
2109 buffersize -= count;
2117 ====================
2120 Print a string into a file
2121 ====================
2123 int FS_Print (qfile_t* file, const char *msg)
2125 return (int)FS_Write (file, msg, strlen (msg));
2129 ====================
2132 Print a string into a file
2133 ====================
2135 int FS_Printf(qfile_t* file, const char* format, ...)
2140 va_start (args, format);
2141 result = FS_VPrintf (file, format, args);
2149 ====================
2152 Print a string into a file
2153 ====================
2155 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2158 fs_offset_t buff_size = MAX_INPUTLINE;
2163 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2164 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2165 if (len >= 0 && len < buff_size)
2167 Mem_Free (tempbuff);
2171 len = write (file->handle, tempbuff, len);
2172 Mem_Free (tempbuff);
2179 ====================
2182 Get the next character of a file
2183 ====================
2185 int FS_Getc (qfile_t* file)
2189 if (FS_Read (file, &c, 1) != 1)
2197 ====================
2200 Put a character back into the read buffer (only supports one character!)
2201 ====================
2203 int FS_UnGetc (qfile_t* file, unsigned char c)
2205 // If there's already a character waiting to be read
2206 if (file->ungetc != EOF)
2215 ====================
2218 Move the position index in a file
2219 ====================
2221 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2224 unsigned char* buffer;
2225 fs_offset_t buffersize;
2227 // Compute the file offset
2231 offset += file->position - file->buff_len + file->buff_ind;
2238 offset += file->real_length;
2244 if (offset < 0 || offset > file->real_length)
2247 // If we have the data in our read buffer, we don't need to actually seek
2248 if (file->position - file->buff_len <= offset && offset <= file->position)
2250 file->buff_ind = offset + file->buff_len - file->position;
2254 // Purge cached data
2257 // Unpacked or uncompressed files can seek directly
2258 if (! (file->flags & QFILE_FLAG_DEFLATED))
2260 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2262 file->position = offset;
2266 // Seeking in compressed files is more a hack than anything else,
2267 // but we need to support it, so here we go.
2270 // If we have to go back in the file, we need to restart from the beginning
2271 if (offset <= file->position)
2275 ztk->in_position = 0;
2277 lseek (file->handle, file->offset, SEEK_SET);
2279 // Reset the Zlib stream
2280 ztk->zstream.next_in = ztk->input;
2281 ztk->zstream.avail_in = 0;
2282 qz_inflateReset (&ztk->zstream);
2285 // We need a big buffer to force inflating into it directly
2286 buffersize = 2 * sizeof (file->buff);
2287 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2289 // Skip all data until we reach the requested offset
2290 while (offset > file->position)
2292 fs_offset_t diff = offset - file->position;
2293 fs_offset_t count, len;
2295 count = (diff > buffersize) ? buffersize : diff;
2296 len = FS_Read (file, buffer, count);
2310 ====================
2313 Give the current position in a file
2314 ====================
2316 fs_offset_t FS_Tell (qfile_t* file)
2318 return file->position - file->buff_len + file->buff_ind;
2323 ====================
2326 Give the total size of a file
2327 ====================
2329 fs_offset_t FS_FileSize (qfile_t* file)
2331 return file->real_length;
2336 ====================
2339 Erases any buffered input or output data
2340 ====================
2342 void FS_Purge (qfile_t* file)
2354 Filename are relative to the quake directory.
2355 Always appends a 0 byte.
2358 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2361 unsigned char *buf = NULL;
2362 fs_offset_t filesize = 0;
2364 file = FS_Open (path, "rb", quiet, false);
2367 filesize = file->real_length;
2368 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2369 buf[filesize] = '\0';
2370 FS_Read (file, buf, filesize);
2372 if (developer_loadfile.integer)
2373 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2376 if (filesizepointer)
2377 *filesizepointer = filesize;
2386 The filename will be prefixed by the current game directory
2389 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2393 file = FS_Open (filename, "wb", false, false);
2396 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2400 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2401 FS_Write (file, data, len);
2408 =============================================================================
2410 OTHERS PUBLIC FUNCTIONS
2412 =============================================================================
2420 void FS_StripExtension (const char *in, char *out, size_t size_out)
2428 while ((currentchar = *in) && size_out > 1)
2430 if (currentchar == '.')
2432 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2434 *out++ = currentchar;
2450 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2454 // if path doesn't have a .EXT, append extension
2455 // (extension should include the .)
2456 src = path + strlen(path) - 1;
2458 while (*src != '/' && src != path)
2461 return; // it has an extension
2465 strlcat (path, extension, size_path);
2473 Look for a file in the packages and in the filesystem
2476 int FS_FileType (const char *filename)
2478 searchpath_t *search;
2479 char fullpath[MAX_QPATH];
2481 search = FS_FindFile (filename, NULL, true);
2483 return FS_FILETYPE_NONE;
2486 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2488 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2489 return FS_SysFileType(fullpath);
2497 Look for a file in the packages and in the filesystem
2500 qboolean FS_FileExists (const char *filename)
2502 return (FS_FindFile (filename, NULL, true) != NULL);
2510 Look for a file in the filesystem only
2513 int FS_SysFileType (const char *path)
2516 DWORD result = GetFileAttributes(path);
2518 if(result == INVALID_FILE_ATTRIBUTES)
2519 return FS_FILETYPE_NONE;
2521 if(result & FILE_ATTRIBUTE_DIRECTORY)
2522 return FS_FILETYPE_DIRECTORY;
2524 return FS_FILETYPE_FILE;
2528 if (stat (path,&buf) == -1)
2529 return FS_FILETYPE_NONE;
2531 if(S_ISDIR(buf.st_mode))
2532 return FS_FILETYPE_DIRECTORY;
2534 return FS_FILETYPE_FILE;
2538 qboolean FS_SysFileExists (const char *path)
2540 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2543 void FS_mkdir (const char *path)
2556 Allocate and fill a search structure with information on matching filenames.
2559 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2562 searchpath_t *searchpath;
2564 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2565 stringlist_t resultlist;
2566 stringlist_t dirlist;
2567 const char *slash, *backslash, *colon, *separator;
2569 char temp[MAX_OSPATH];
2571 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2576 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2580 stringlistinit(&resultlist);
2581 stringlistinit(&dirlist);
2583 slash = strrchr(pattern, '/');
2584 backslash = strrchr(pattern, '\\');
2585 colon = strrchr(pattern, ':');
2586 separator = max(slash, backslash);
2587 separator = max(separator, colon);
2588 basepathlength = separator ? (separator + 1 - pattern) : 0;
2589 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2591 memcpy(basepath, pattern, basepathlength);
2592 basepath[basepathlength] = 0;
2594 // search through the path, one element at a time
2595 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2597 // is the element a pak file?
2598 if (searchpath->pack)
2600 // look through all the pak file elements
2601 pak = searchpath->pack;
2602 for (i = 0;i < pak->numfiles;i++)
2604 strlcpy(temp, pak->files[i].name, sizeof(temp));
2607 if (matchpattern(temp, (char *)pattern, true))
2609 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2610 if (!strcmp(resultlist.strings[resultlistindex], temp))
2612 if (resultlistindex == resultlist.numstrings)
2614 stringlistappend(&resultlist, temp);
2615 if (!quiet && developer_loading.integer)
2616 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2619 // strip off one path element at a time until empty
2620 // this way directories are added to the listing if they match the pattern
2621 slash = strrchr(temp, '/');
2622 backslash = strrchr(temp, '\\');
2623 colon = strrchr(temp, ':');
2625 if (separator < slash)
2627 if (separator < backslash)
2628 separator = backslash;
2629 if (separator < colon)
2631 *((char *)separator) = 0;
2637 stringlist_t matchedSet, foundSet;
2638 const char *start = pattern;
2640 stringlistinit(&matchedSet);
2641 stringlistinit(&foundSet);
2642 // add a first entry to the set
2643 stringlistappend(&matchedSet, "");
2644 // iterate through pattern's path
2647 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
2648 char subpath[MAX_OSPATH];
2649 char subpattern[MAX_OSPATH];
2651 // find the next wildcard
2652 wildcard = strchr(start, '?');
2653 asterisk = strchr(start, '*');
2654 if (asterisk && (!wildcard || asterisk < wildcard))
2656 wildcard = asterisk;
2661 nextseparator = strchr( wildcard, '/' );
2665 nextseparator = NULL;
2668 if( !nextseparator ) {
2669 nextseparator = start + strlen( start );
2672 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
2673 // copy everything up except nextseperator
2674 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
2675 // find the last '/' before the wildcard
2676 prevseparator = strrchr( subpattern, '/' );
2678 prevseparator = subpattern;
2681 // copy everything from start to the previous including the '/' (before the wildcard)
2682 // everything up to start is already included in the path of matchedSet's entries
2683 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
2685 // for each entry in matchedSet try to open the subdirectories specified in subpath
2686 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
2687 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
2688 strlcat( temp, subpath, sizeof(temp) );
2689 listdirectory( &foundSet, searchpath->filename, temp );
2691 if( dirlistindex == 0 ) {
2694 // reset the current result set
2695 stringlistfreecontents( &matchedSet );
2696 // match against the pattern
2697 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
2698 const char *direntry = foundSet.strings[ dirlistindex ];
2699 if (matchpattern(direntry, subpattern, true)) {
2700 stringlistappend( &matchedSet, direntry );
2703 stringlistfreecontents( &foundSet );
2705 start = nextseparator;
2708 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
2710 const char *temp = matchedSet.strings[dirlistindex];
2711 if (matchpattern(temp, (char *)pattern, true))
2713 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2714 if (!strcmp(resultlist.strings[resultlistindex], temp))
2716 if (resultlistindex == resultlist.numstrings)
2718 stringlistappend(&resultlist, temp);
2719 if (!quiet && developer_loading.integer)
2720 Con_Printf("SearchDirFile: %s\n", temp);
2724 stringlistfreecontents( &matchedSet );
2728 if (resultlist.numstrings)
2730 stringlistsort(&resultlist);
2731 numfiles = resultlist.numstrings;
2733 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2734 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2735 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2736 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2737 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2738 search->numfilenames = (int)numfiles;
2741 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2744 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2745 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2746 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2748 numchars += (int)textlen;
2751 stringlistfreecontents(&resultlist);
2757 void FS_FreeSearch(fssearch_t *search)
2762 extern int con_linewidth;
2763 int FS_ListDirectory(const char *pattern, int oneperline)
2772 char linebuf[MAX_INPUTLINE];
2774 search = FS_Search(pattern, true, true);
2777 numfiles = search->numfilenames;
2780 // FIXME: the names could be added to one column list and then
2781 // gradually shifted into the next column if they fit, and then the
2782 // next to make a compact variable width listing but it's a lot more
2784 // find width for columns
2786 for (i = 0;i < numfiles;i++)
2788 l = (int)strlen(search->filenames[i]);
2789 if (columnwidth < l)
2792 // count the spacing character
2794 // calculate number of columns
2795 numcolumns = con_linewidth / columnwidth;
2796 // don't bother with the column printing if it's only one column
2797 if (numcolumns >= 2)
2799 numlines = (numfiles + numcolumns - 1) / numcolumns;
2800 for (i = 0;i < numlines;i++)
2803 for (k = 0;k < numcolumns;k++)
2805 l = i * numcolumns + k;
2808 name = search->filenames[l];
2809 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2810 linebuf[linebufpos++] = name[j];
2811 // space out name unless it's the last on the line
2812 if (k + 1 < numcolumns && l + 1 < numfiles)
2813 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2814 linebuf[linebufpos++] = ' ';
2817 linebuf[linebufpos] = 0;
2818 Con_Printf("%s\n", linebuf);
2825 for (i = 0;i < numfiles;i++)
2826 Con_Printf("%s\n", search->filenames[i]);
2827 FS_FreeSearch(search);
2828 return (int)numfiles;
2831 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2833 const char *pattern;
2836 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2839 if (Cmd_Argc() == 2)
2840 pattern = Cmd_Argv(1);
2843 if (!FS_ListDirectory(pattern, oneperline))
2844 Con_Print("No files found.\n");
2849 FS_ListDirectoryCmd("dir", true);
2854 FS_ListDirectoryCmd("ls", false);
2857 const char *FS_WhichPack(const char *filename)
2860 searchpath_t *sp = FS_FindFile(filename, &index, true);
2862 return sp->pack->filename;
2868 ====================
2869 FS_IsRegisteredQuakePack
2871 Look for a proof of purchase file file in the requested package
2873 If it is found, this file should NOT be downloaded.
2874 ====================
2876 qboolean FS_IsRegisteredQuakePack(const char *name)
2878 searchpath_t *search;
2881 // search through the path, one element at a time
2882 for (search = fs_searchpaths;search;search = search->next)
2884 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2886 int (*strcmp_funct) (const char* str1, const char* str2);
2887 int left, right, middle;
2890 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2892 // Look for the file (binary search)
2894 right = pak->numfiles - 1;
2895 while (left <= right)
2899 middle = (left + right) / 2;
2900 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2906 // If we're too far in the list
2913 // we found the requested pack but it is not registered quake
2921 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2924 unsigned char *filedata;
2925 fs_offset_t filesize;
2926 if (filesizepointer)
2927 *filesizepointer = 0;
2928 if (!filename || !*filename)
2930 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2933 if (filesizepointer)
2934 *filesizepointer = filesize;
2935 crc = CRC_Block(filedata, filesize);