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;
329 void PK3_CloseLibrary (void)
331 Sys_UnloadLibrary (&zlib_dll);
339 Try to load the Zlib DLL
342 qboolean PK3_OpenLibrary (void)
344 const char* dllnames [] =
349 # ifdef ZLIB_USES_WINAPI
355 #elif defined(MACOSX)
369 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
371 Con_Printf ("Compressed files support disabled\n");
375 Con_Printf ("Compressed files support enabled\n");
382 PK3_GetEndOfCentralDir
384 Extract the end of the central directory from a PK3 package
387 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
389 fs_offset_t filesize, maxsize;
390 unsigned char *buffer, *ptr;
393 // Get the package size
394 filesize = lseek (packhandle, 0, SEEK_END);
395 if (filesize < ZIP_END_CDIR_SIZE)
398 // Load the end of the file in memory
399 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
402 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
403 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
404 lseek (packhandle, filesize - maxsize, SEEK_SET);
405 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
411 // Look for the end of central dir signature around the end of the file
412 maxsize -= ZIP_END_CDIR_SIZE;
413 ptr = &buffer[maxsize];
415 while (BuffBigLong (ptr) != ZIP_END_HEADER)
427 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
428 eocd->signature = LittleLong (eocd->signature);
429 eocd->disknum = LittleShort (eocd->disknum);
430 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
431 eocd->localentries = LittleShort (eocd->localentries);
432 eocd->nbentries = LittleShort (eocd->nbentries);
433 eocd->cdir_size = LittleLong (eocd->cdir_size);
434 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
435 eocd->comment_size = LittleShort (eocd->comment_size);
447 Extract the file list from a PK3 file
450 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
452 unsigned char *central_dir, *ptr;
454 fs_offset_t remaining;
456 // Load the central directory in memory
457 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
458 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
459 read (pack->handle, central_dir, eocd->cdir_size);
461 // Extract the files properties
462 // The parsing is done "by hand" because some fields have variable sizes and
463 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
464 remaining = eocd->cdir_size;
467 for (ind = 0; ind < eocd->nbentries; ind++)
469 fs_offset_t namesize, count;
471 // Checking the remaining size
472 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
474 Mem_Free (central_dir);
477 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
480 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
482 Mem_Free (central_dir);
486 namesize = BuffLittleShort (&ptr[28]); // filename length
488 // Check encryption, compression, and attributes
489 // 1st uint8 : general purpose bit flag
490 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
491 // 2nd uint8 : external file attributes
492 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
493 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
495 // Still enough bytes for the name?
496 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
498 Mem_Free (central_dir);
502 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
503 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
505 char filename [sizeof (pack->files[0].name)];
506 fs_offset_t offset, packsize, realsize;
509 // Extract the name (strip it if necessary)
510 namesize = min(namesize, (int)sizeof (filename) - 1);
511 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
512 filename[namesize] = '\0';
514 if (BuffLittleShort (&ptr[10]))
515 flags = PACKFILE_FLAG_DEFLATED;
518 offset = BuffLittleLong (&ptr[42]);
519 packsize = BuffLittleLong (&ptr[20]);
520 realsize = BuffLittleLong (&ptr[24]);
521 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
525 // Skip the name, additionnal field, and comment
526 // 1er uint16 : extra field length
527 // 2eme uint16 : file comment length
528 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
529 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
533 // If the package is empty, central_dir is NULL here
534 if (central_dir != NULL)
535 Mem_Free (central_dir);
536 return pack->numfiles;
544 Create a package entry associated with a PK3 file
547 pack_t *FS_LoadPackPK3 (const char *packfile)
550 pk3_endOfCentralDir_t eocd;
554 packhandle = open (packfile, O_RDONLY | O_BINARY);
558 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
560 Con_Printf ("%s is not a PK3 file\n", packfile);
565 // Multi-volume ZIP archives are NOT allowed
566 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
568 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
573 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
574 // since eocd.nbentries is an unsigned 16 bits integer
575 #if MAX_FILES_IN_PACK < 65535
576 if (eocd.nbentries > MAX_FILES_IN_PACK)
578 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
584 // Create a package structure in memory
585 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
586 pack->ignorecase = true; // PK3 ignores case
587 strlcpy (pack->filename, packfile, sizeof (pack->filename));
588 pack->handle = packhandle;
589 pack->numfiles = eocd.nbentries;
590 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
592 real_nb_files = PK3_BuildFileList (pack, &eocd);
593 if (real_nb_files < 0)
595 Con_Printf ("%s is not a valid PK3 file\n", packfile);
601 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
608 PK3_GetTrueFileOffset
610 Find where the true file data offset is
613 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
615 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
619 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
622 // Load the local file description
623 lseek (pack->handle, pfile->offset, SEEK_SET);
624 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
625 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
627 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
631 // Skip name and extra field
632 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
634 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
640 =============================================================================
642 OTHER PRIVATE FUNCTIONS
644 =============================================================================
652 Add a file to the list of files contained into a package
655 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
656 fs_offset_t offset, fs_offset_t packsize,
657 fs_offset_t realsize, int flags)
659 int (*strcmp_funct) (const char* str1, const char* str2);
660 int left, right, middle;
663 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
665 // Look for the slot we should put that file into (binary search)
667 right = pack->numfiles - 1;
668 while (left <= right)
672 middle = (left + right) / 2;
673 diff = strcmp_funct (pack->files[middle].name, name);
675 // If we found the file, there's a problem
677 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
679 // If we're too far in the list
686 // We have to move the right of the list by one slot to free the one we need
687 pfile = &pack->files[left];
688 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
691 strlcpy (pfile->name, name, sizeof (pfile->name));
692 pfile->offset = offset;
693 pfile->packsize = packsize;
694 pfile->realsize = realsize;
695 pfile->flags = flags;
705 Only used for FS_Open.
708 void FS_CreatePath (char *path)
712 for (ofs = path+1 ; *ofs ; ofs++)
714 if (*ofs == '/' || *ofs == '\\')
716 // create the directory
732 void FS_Path_f (void)
736 Con_Print("Current search path:\n");
737 for (s=fs_searchpaths ; s ; s=s->next)
740 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
742 Con_Printf("%s\n", s->filename);
751 Takes an explicit (not game tree related) path to a pak file.
753 Loads the header and directory, adding the files at the beginning
754 of the list so they override previous pack files.
757 pack_t *FS_LoadPackPAK (const char *packfile)
759 dpackheader_t header;
765 packhandle = open (packfile, O_RDONLY | O_BINARY);
768 read (packhandle, (void *)&header, sizeof(header));
769 if (memcmp(header.id, "PACK", 4))
771 Con_Printf ("%s is not a packfile\n", packfile);
775 header.dirofs = LittleLong (header.dirofs);
776 header.dirlen = LittleLong (header.dirlen);
778 if (header.dirlen % sizeof(dpackfile_t))
780 Con_Printf ("%s has an invalid directory size\n", packfile);
785 numpackfiles = header.dirlen / sizeof(dpackfile_t);
787 if (numpackfiles > MAX_FILES_IN_PACK)
789 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
794 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
795 lseek (packhandle, header.dirofs, SEEK_SET);
796 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
798 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
804 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
805 pack->ignorecase = false; // PAK is case sensitive
806 strlcpy (pack->filename, packfile, sizeof (pack->filename));
807 pack->handle = packhandle;
809 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
811 // parse the directory
812 for (i = 0;i < numpackfiles;i++)
814 fs_offset_t offset = LittleLong (info[i].filepos);
815 fs_offset_t size = LittleLong (info[i].filelen);
817 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
822 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
830 Adds the given pack to the search path.
831 The pack type is autodetected by the file extension.
833 Returns true if the file was successfully added to the
834 search path or if it was already included.
836 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
840 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
842 searchpath_t *search;
844 const char *ext = FS_FileExtension(pakfile);
846 for(search = fs_searchpaths; search; search = search->next)
848 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
851 *already_loaded = true;
852 return true; // already loaded
857 *already_loaded = false;
859 if(!strcasecmp(ext, "pak"))
860 pak = FS_LoadPackPAK (pakfile);
861 else if(!strcasecmp(ext, "pk3"))
862 pak = FS_LoadPackPK3 (pakfile);
864 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
870 // find the first item whose next one is a pack or NULL
871 searchpath_t *insertion_point = 0;
872 if(fs_searchpaths && !fs_searchpaths->pack)
874 insertion_point = fs_searchpaths;
877 if(!insertion_point->next)
879 if(insertion_point->next->pack)
881 insertion_point = insertion_point->next;
884 // If insertion_point is NULL, this means that either there is no
885 // item in the list yet, or that the very first item is a pack. In
886 // that case, we want to insert at the beginning...
889 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
891 search->next = fs_searchpaths;
892 fs_searchpaths = search;
895 // otherwise we want to append directly after insertion_point.
897 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
899 search->next = insertion_point->next;
900 insertion_point->next = search;
905 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
907 search->next = fs_searchpaths;
908 fs_searchpaths = search;
914 Con_Printf("unable to load pak \"%s\"\n", pakfile);
924 Adds the given pack to the search path and searches for it in the game path.
925 The pack type is autodetected by the file extension.
927 Returns true if the file was successfully added to the
928 search path or if it was already included.
930 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
934 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
936 char fullpath[MAX_QPATH];
938 searchpath_t *search;
941 *already_loaded = false;
943 // then find the real name...
944 search = FS_FindFile(pakfile, &index, true);
945 if(!search || search->pack)
947 Con_Printf("could not find pak \"%s\"\n", pakfile);
951 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
953 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
961 Sets fs_gamedir, adds the directory to the head of the path,
962 then loads and adds pak1.pak pak2.pak ...
965 void FS_AddGameDirectory (const char *dir)
969 searchpath_t *search;
970 char pakfile[MAX_OSPATH];
972 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
974 stringlistinit(&list);
975 listdirectory(&list, dir);
976 stringlistsort(&list);
978 // add any PAK package in the directory
979 for (i = 0;i < list.numstrings;i++)
981 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
983 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
984 FS_AddPack_Fullpath(pakfile, NULL, false);
988 // add any PK3 package in the directory
989 for (i = 0;i < list.numstrings;i++)
991 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
993 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
994 FS_AddPack_Fullpath(pakfile, NULL, false);
998 stringlistfreecontents(&list);
1000 // Add the directory to the search path
1001 // (unpacked files have the priority over packed files)
1002 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1003 strlcpy (search->filename, dir, sizeof (search->filename));
1004 search->next = fs_searchpaths;
1005 fs_searchpaths = search;
1014 void FS_AddGameHierarchy (const char *dir)
1017 char userdir[MAX_QPATH];
1019 TCHAR mydocsdir[MAX_PATH + 1];
1021 const char *homedir;
1024 // Add the common game directory
1025 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1029 // Add the personal game directory
1031 if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
1032 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1033 fprintf(stderr, "userdir = %s\n", userdir);
1035 homedir = getenv ("HOME");
1037 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1041 if(!COM_CheckParm("-mygames"))
1043 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1047 *userdir = 0; // we have write access to the game dir, so let's use it
1052 if(COM_CheckParm("-nohome"))
1055 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1056 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1059 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1068 const char *FS_FileExtension (const char *in)
1070 const char *separator, *backslash, *colon, *dot;
1072 separator = strrchr(in, '/');
1073 backslash = strrchr(in, '\\');
1074 if (!separator || separator < backslash)
1075 separator = backslash;
1076 colon = strrchr(in, ':');
1077 if (!separator || separator < colon)
1080 dot = strrchr(in, '.');
1081 if (dot == NULL || (separator && (dot < separator)))
1093 const char *FS_FileWithoutPath (const char *in)
1095 const char *separator, *backslash, *colon;
1097 separator = strrchr(in, '/');
1098 backslash = strrchr(in, '\\');
1099 if (!separator || separator < backslash)
1100 separator = backslash;
1101 colon = strrchr(in, ':');
1102 if (!separator || separator < colon)
1104 return separator ? separator + 1 : in;
1113 void FS_ClearSearchPath (void)
1115 // unload all packs and directory information, close all pack files
1116 // (if a qfile is still reading a pack it won't be harmed because it used
1117 // dup() to get its own handle already)
1118 while (fs_searchpaths)
1120 searchpath_t *search = fs_searchpaths;
1121 fs_searchpaths = search->next;
1125 close(search->pack->handle);
1126 // free any memory associated with it
1127 if (search->pack->files)
1128 Mem_Free(search->pack->files);
1129 Mem_Free(search->pack);
1141 void FS_Rescan (void)
1144 qboolean fs_modified = false;
1146 FS_ClearSearchPath();
1148 // add the game-specific paths
1149 // gamedirname1 (typically id1)
1150 FS_AddGameHierarchy (gamedirname1);
1151 // update the com_modname (used for server info)
1152 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1154 // add the game-specific path, if any
1155 // (only used for mission packs and the like, which should set fs_modified)
1159 FS_AddGameHierarchy (gamedirname2);
1163 // Adds basedir/gamedir as an override game
1164 // LordHavoc: now supports multiple -game directories
1165 // set the com_modname (reported in server info)
1166 for (i = 0;i < fs_numgamedirs;i++)
1169 FS_AddGameHierarchy (fs_gamedirs[i]);
1170 // update the com_modname (used server info)
1171 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1174 // set the default screenshot name to either the mod name or the
1175 // gamemode screenshot name
1176 if (strcmp(com_modname, gamedirname1))
1177 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1179 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1181 // If "-condebug" is in the command line, remove the previous log file
1182 if (COM_CheckParm ("-condebug") != 0)
1183 unlink (va("%s/qconsole.log", fs_gamedir));
1185 // look for the pop.lmp file and set registered to true if it is found
1186 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1189 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1191 Con_Print("Playing shareware version.\n");
1195 Cvar_Set ("registered", "1");
1196 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1197 Con_Print("Playing registered version.\n");
1200 // unload all wads so that future queries will return the new data
1204 void FS_Rescan_f(void)
1214 extern void Host_SaveConfig (void);
1215 extern void Host_LoadConfig_f (void);
1216 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1220 if (fs_numgamedirs == numgamedirs)
1222 for (i = 0;i < numgamedirs;i++)
1223 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1225 if (i == numgamedirs)
1226 return true; // already using this set of gamedirs, do nothing
1229 if (numgamedirs > MAX_GAMEDIRS)
1232 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1233 return false; // too many gamedirs
1236 for (i = 0;i < numgamedirs;i++)
1238 // if string is nasty, reject it
1239 if(FS_CheckNastyPath(gamedirs[i], true))
1242 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1243 return false; // nasty gamedirs
1247 for (i = 0;i < numgamedirs;i++)
1249 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1252 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1253 return false; // missing gamedirs
1257 // halt demo playback to close the file
1262 fs_numgamedirs = numgamedirs;
1263 for (i = 0;i < fs_numgamedirs;i++)
1264 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1266 // reinitialize filesystem to detect the new paks
1269 // exec the new config
1270 Host_LoadConfig_f();
1272 // unload all sounds so they will be reloaded from the new files as needed
1273 S_UnloadAllSounds_f();
1275 // reinitialize renderer (this reloads hud/console background/etc)
1276 R_Modules_Restart();
1286 void FS_GameDir_f (void)
1290 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1294 Con_Printf("gamedirs active:");
1295 for (i = 0;i < fs_numgamedirs;i++)
1296 Con_Printf(" %s", fs_gamedirs[i]);
1301 numgamedirs = Cmd_Argc() - 1;
1302 if (numgamedirs > MAX_GAMEDIRS)
1304 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1308 for (i = 0;i < numgamedirs;i++)
1309 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1311 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1313 // actually, changing during game would work fine, but would be stupid
1314 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1318 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1327 qboolean FS_CheckGameDir(const char *gamedir)
1331 stringlistinit(&list);
1332 listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
1333 success = list.numstrings > 0;
1334 stringlistfreecontents(&list);
1348 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1350 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1352 // If the base directory is explicitly defined by the compilation process
1353 #ifdef DP_FS_BASEDIR
1354 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1356 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1359 // FIXME: is there a better way to find the directory outside the .app?
1360 if (strstr(com_argv[0], ".app/"))
1364 split = strstr(com_argv[0], ".app/");
1365 while (split > com_argv[0] && *split != '/')
1367 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1368 fs_basedir[split - com_argv[0]] = 0;
1376 // Overrides the system supplied base directory (under GAMENAME)
1377 // 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)
1378 i = COM_CheckParm ("-basedir");
1379 if (i && i < com_argc-1)
1381 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1382 i = (int)strlen (fs_basedir);
1383 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1384 fs_basedir[i-1] = 0;
1387 // add a path separator to the end of the basedir if it lacks one
1388 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1389 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1391 if (!FS_CheckGameDir(gamedirname1))
1392 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1394 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1395 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1398 // Adds basedir/gamedir as an override game
1399 // LordHavoc: now supports multiple -game directories
1400 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1404 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1407 if (FS_CheckNastyPath(com_argv[i], true))
1408 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1409 if (!FS_CheckGameDir(com_argv[i]))
1410 Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1411 // add the gamedir to the list of active gamedirs
1412 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1417 // generate the searchpath
1421 void FS_Init_Commands(void)
1423 Cvar_RegisterVariable (&scr_screenshot_name);
1424 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1426 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1427 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1428 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1429 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1430 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1438 void FS_Shutdown (void)
1440 // close all pack files and such
1441 // (hopefully there aren't any other open files, but they'll be cleaned up
1442 // by the OS anyway)
1443 FS_ClearSearchPath();
1444 Mem_FreePool (&fs_mempool);
1448 ====================
1451 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1452 ====================
1454 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1460 // Parse the mode string
1469 opt = O_CREAT | O_TRUNC;
1473 opt = O_CREAT | O_APPEND;
1476 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1479 for (ind = 1; mode[ind] != '\0'; ind++)
1490 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1491 filepath, mode, mode[ind]);
1498 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1499 memset (file, 0, sizeof (*file));
1502 file->handle = open (filepath, mod | opt, 0666);
1503 if (file->handle < 0)
1509 file->real_length = lseek (file->handle, 0, SEEK_END);
1511 // For files opened in append mode, we start at the end of the file
1513 file->position = file->real_length;
1515 lseek (file->handle, 0, SEEK_SET);
1525 Open a packed file using its package file descriptor
1528 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1534 pfile = &pack->files[pack_ind];
1536 // If we don't have the true offset, get it now
1537 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1538 if (!PK3_GetTrueFileOffset (pfile, pack))
1541 // No Zlib DLL = no compressed files
1542 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1544 Con_Printf("WARNING: can't open the compressed file %s\n"
1545 "You need the Zlib DLL to use compressed files\n",
1550 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1551 // the dup() call to avoid having to close the dup_handle on error here
1552 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1554 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1555 pfile->name, pack->filename, (int) pfile->offset);
1559 dup_handle = dup (pack->handle);
1562 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1566 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1567 memset (file, 0, sizeof (*file));
1568 file->handle = dup_handle;
1569 file->flags = QFILE_FLAG_PACKED;
1570 file->real_length = pfile->realsize;
1571 file->offset = pfile->offset;
1575 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1579 file->flags |= QFILE_FLAG_DEFLATED;
1581 // We need some more variables
1582 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1584 ztk->comp_length = pfile->packsize;
1586 // Initialize zlib stream
1587 ztk->zstream.next_in = ztk->input;
1588 ztk->zstream.avail_in = 0;
1590 /* From Zlib's "unzip.c":
1592 * windowBits is passed < 0 to tell that there is no zlib header.
1593 * Note that in this case inflate *requires* an extra "dummy" byte
1594 * after the compressed stream in order to complete decompression and
1595 * return Z_STREAM_END.
1596 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1597 * size of both compressed and uncompressed data
1599 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1601 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1607 ztk->zstream.next_out = file->buff;
1608 ztk->zstream.avail_out = sizeof (file->buff);
1617 ====================
1620 Return true if the path should be rejected due to one of the following:
1621 1: path elements that are non-portable
1622 2: path elements that would allow access to files outside the game directory,
1623 or are just not a good idea for a mod to be using.
1624 ====================
1626 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1628 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1632 // Windows: don't allow \ in filenames (windows-only), period.
1633 // (on Windows \ is a directory separator, but / is also supported)
1634 if (strstr(path, "\\"))
1635 return 1; // non-portable
1637 // Mac: don't allow Mac-only filenames - : is a directory separator
1638 // instead of /, but we rely on / working already, so there's no reason to
1639 // support a Mac-only path
1640 // Amiga and Windows: : tries to go to root of drive
1641 if (strstr(path, ":"))
1642 return 1; // non-portable attempt to go to root of drive
1644 // Amiga: // is parent directory
1645 if (strstr(path, "//"))
1646 return 1; // non-portable attempt to go to parent directory
1648 // all: don't allow going to parent directory (../ or /../)
1649 if (strstr(path, ".."))
1650 return 2; // attempt to go outside the game directory
1652 // Windows and UNIXes: don't allow absolute paths
1654 return 2; // attempt to go outside the game directory
1656 // 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
1657 if (strchr(path, '.'))
1661 // gamedir is entirely path elements, so simply forbid . entirely
1664 if (strchr(path, '.') < strrchr(path, '/'))
1665 return 2; // possible attempt to go outside the game directory
1668 // all: forbid trailing slash on gamedir
1669 if (isgamedir && path[strlen(path)-1] == '/')
1672 // all: forbid leading dot on any filename for any reason
1673 if (strstr(path, "/."))
1674 return 2; // attempt to go outside the game directory
1676 // after all these checks we're pretty sure it's a / separated filename
1677 // and won't do much if any harm
1683 ====================
1686 Look for a file in the packages and in the filesystem
1688 Return the searchpath where the file was found (or NULL)
1689 and the file index in the package if relevant
1690 ====================
1692 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1694 searchpath_t *search;
1697 // search through the path, one element at a time
1698 for (search = fs_searchpaths;search;search = search->next)
1700 // is the element a pak file?
1703 int (*strcmp_funct) (const char* str1, const char* str2);
1704 int left, right, middle;
1707 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1709 // Look for the file (binary search)
1711 right = pak->numfiles - 1;
1712 while (left <= right)
1716 middle = (left + right) / 2;
1717 diff = strcmp_funct (pak->files[middle].name, name);
1722 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1724 // yes, but the first one is empty so we treat it as not being there
1725 if (!quiet && developer.integer >= 10)
1726 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1733 if (!quiet && developer.integer >= 10)
1734 Con_Printf("FS_FindFile: %s in %s\n",
1735 pak->files[middle].name, pak->filename);
1742 // If we're too far in the list
1751 char netpath[MAX_OSPATH];
1752 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1753 if (FS_SysFileExists (netpath))
1755 if (!quiet && developer.integer >= 10)
1756 Con_Printf("FS_FindFile: %s\n", netpath);
1765 if (!quiet && developer.integer >= 10)
1766 Con_Printf("FS_FindFile: can't find %s\n", name);
1778 Look for a file in the search paths and open it in read-only mode
1781 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1783 searchpath_t *search;
1786 search = FS_FindFile (filename, &pack_ind, quiet);
1792 // Found in the filesystem?
1795 char path [MAX_OSPATH];
1796 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1797 return FS_SysOpen (path, "rb", nonblocking);
1800 // So, we found it in a package...
1801 return FS_OpenPackedFile (search->pack, pack_ind);
1806 =============================================================================
1808 MAIN PUBLIC FUNCTIONS
1810 =============================================================================
1814 ====================
1817 Open a file. The syntax is the same as fopen
1818 ====================
1820 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1822 if (FS_CheckNastyPath(filepath, false))
1824 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1828 // If the file is opened in "write", "append", or "read/write" mode
1829 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1831 char real_path [MAX_OSPATH];
1833 // Open the file on disk directly
1834 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1836 // Create directories up to the file
1837 FS_CreatePath (real_path);
1839 return FS_SysOpen (real_path, mode, nonblocking);
1841 // Else, we look at the various search paths and open the file in read-only mode
1843 return FS_OpenReadFile (filepath, quiet, nonblocking);
1848 ====================
1852 ====================
1854 int FS_Close (qfile_t* file)
1856 if (close (file->handle))
1861 qz_inflateEnd (&file->ztk->zstream);
1862 Mem_Free (file->ztk);
1871 ====================
1874 Write "datasize" bytes into a file
1875 ====================
1877 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1881 // If necessary, seek to the exact file position we're supposed to be
1882 if (file->buff_ind != file->buff_len)
1883 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1885 // Purge cached data
1888 // Write the buffer and update the position
1889 result = write (file->handle, data, (fs_offset_t)datasize);
1890 file->position = lseek (file->handle, 0, SEEK_CUR);
1891 if (file->real_length < file->position)
1892 file->real_length = file->position;
1902 ====================
1905 Read up to "buffersize" bytes from a file
1906 ====================
1908 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1910 fs_offset_t count, done;
1912 if (buffersize == 0)
1915 // Get rid of the ungetc character
1916 if (file->ungetc != EOF)
1918 ((char*)buffer)[0] = file->ungetc;
1926 // First, we copy as many bytes as we can from "buff"
1927 if (file->buff_ind < file->buff_len)
1929 count = file->buff_len - file->buff_ind;
1930 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1932 memcpy (buffer, &file->buff[file->buff_ind], count);
1933 file->buff_ind += count;
1935 buffersize -= count;
1936 if (buffersize == 0)
1940 // NOTE: at this point, the read buffer is always empty
1942 // If the file isn't compressed
1943 if (! (file->flags & QFILE_FLAG_DEFLATED))
1947 // We must take care to not read after the end of the file
1948 count = file->real_length - file->position;
1950 // If we have a lot of data to get, put them directly into "buffer"
1951 if (buffersize > sizeof (file->buff) / 2)
1953 if (count > (fs_offset_t)buffersize)
1954 count = (fs_offset_t)buffersize;
1955 lseek (file->handle, file->offset + file->position, SEEK_SET);
1956 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1960 file->position += nb;
1962 // Purge cached data
1968 if (count > (fs_offset_t)sizeof (file->buff))
1969 count = (fs_offset_t)sizeof (file->buff);
1970 lseek (file->handle, file->offset + file->position, SEEK_SET);
1971 nb = read (file->handle, file->buff, count);
1974 file->buff_len = nb;
1975 file->position += nb;
1977 // Copy the requested data in "buffer" (as much as we can)
1978 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1979 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1980 file->buff_ind = count;
1988 // If the file is compressed, it's more complicated...
1989 // We cycle through a few operations until we have read enough data
1990 while (buffersize > 0)
1992 ztoolkit_t *ztk = file->ztk;
1995 // NOTE: at this point, the read buffer is always empty
1997 // If "input" is also empty, we need to refill it
1998 if (ztk->in_ind == ztk->in_len)
2000 // If we are at the end of the file
2001 if (file->position == file->real_length)
2004 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2005 if (count > (fs_offset_t)sizeof (ztk->input))
2006 count = (fs_offset_t)sizeof (ztk->input);
2007 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2008 if (read (file->handle, ztk->input, count) != count)
2010 Con_Printf ("FS_Read: unexpected end of file\n");
2015 ztk->in_len = count;
2016 ztk->in_position += count;
2019 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2020 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2022 // Now that we are sure we have compressed data available, we need to determine
2023 // if it's better to inflate it in "file->buff" or directly in "buffer"
2025 // Inflate the data in "file->buff"
2026 if (buffersize < sizeof (file->buff) / 2)
2028 ztk->zstream.next_out = file->buff;
2029 ztk->zstream.avail_out = sizeof (file->buff);
2030 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2031 if (error != Z_OK && error != Z_STREAM_END)
2033 Con_Printf ("FS_Read: Can't inflate file\n");
2036 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2038 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2039 file->position += file->buff_len;
2041 // Copy the requested data in "buffer" (as much as we can)
2042 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2043 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2044 file->buff_ind = count;
2047 // Else, we inflate directly in "buffer"
2050 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2051 ztk->zstream.avail_out = (unsigned int)buffersize;
2052 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2053 if (error != Z_OK && error != Z_STREAM_END)
2055 Con_Printf ("FS_Read: Can't inflate file\n");
2058 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2060 // How much data did it inflate?
2061 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2062 file->position += count;
2064 // Purge cached data
2069 buffersize -= count;
2077 ====================
2080 Print a string into a file
2081 ====================
2083 int FS_Print (qfile_t* file, const char *msg)
2085 return (int)FS_Write (file, msg, strlen (msg));
2089 ====================
2092 Print a string into a file
2093 ====================
2095 int FS_Printf(qfile_t* file, const char* format, ...)
2100 va_start (args, format);
2101 result = FS_VPrintf (file, format, args);
2109 ====================
2112 Print a string into a file
2113 ====================
2115 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2118 fs_offset_t buff_size = MAX_INPUTLINE;
2123 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2124 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2125 if (len >= 0 && len < buff_size)
2127 Mem_Free (tempbuff);
2131 len = write (file->handle, tempbuff, len);
2132 Mem_Free (tempbuff);
2139 ====================
2142 Get the next character of a file
2143 ====================
2145 int FS_Getc (qfile_t* file)
2149 if (FS_Read (file, &c, 1) != 1)
2157 ====================
2160 Put a character back into the read buffer (only supports one character!)
2161 ====================
2163 int FS_UnGetc (qfile_t* file, unsigned char c)
2165 // If there's already a character waiting to be read
2166 if (file->ungetc != EOF)
2175 ====================
2178 Move the position index in a file
2179 ====================
2181 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2184 unsigned char* buffer;
2185 fs_offset_t buffersize;
2187 // Compute the file offset
2191 offset += file->position - file->buff_len + file->buff_ind;
2198 offset += file->real_length;
2204 if (offset < 0 || offset > file->real_length)
2207 // If we have the data in our read buffer, we don't need to actually seek
2208 if (file->position - file->buff_len <= offset && offset <= file->position)
2210 file->buff_ind = offset + file->buff_len - file->position;
2214 // Purge cached data
2217 // Unpacked or uncompressed files can seek directly
2218 if (! (file->flags & QFILE_FLAG_DEFLATED))
2220 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2222 file->position = offset;
2226 // Seeking in compressed files is more a hack than anything else,
2227 // but we need to support it, so here we go.
2230 // If we have to go back in the file, we need to restart from the beginning
2231 if (offset <= file->position)
2235 ztk->in_position = 0;
2237 lseek (file->handle, file->offset, SEEK_SET);
2239 // Reset the Zlib stream
2240 ztk->zstream.next_in = ztk->input;
2241 ztk->zstream.avail_in = 0;
2242 qz_inflateReset (&ztk->zstream);
2245 // We need a big buffer to force inflating into it directly
2246 buffersize = 2 * sizeof (file->buff);
2247 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2249 // Skip all data until we reach the requested offset
2250 while (offset > file->position)
2252 fs_offset_t diff = offset - file->position;
2253 fs_offset_t count, len;
2255 count = (diff > buffersize) ? buffersize : diff;
2256 len = FS_Read (file, buffer, count);
2270 ====================
2273 Give the current position in a file
2274 ====================
2276 fs_offset_t FS_Tell (qfile_t* file)
2278 return file->position - file->buff_len + file->buff_ind;
2283 ====================
2286 Give the total size of a file
2287 ====================
2289 fs_offset_t FS_FileSize (qfile_t* file)
2291 return file->real_length;
2296 ====================
2299 Erases any buffered input or output data
2300 ====================
2302 void FS_Purge (qfile_t* file)
2314 Filename are relative to the quake directory.
2315 Always appends a 0 byte.
2318 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2321 unsigned char *buf = NULL;
2322 fs_offset_t filesize = 0;
2324 file = FS_Open (path, "rb", quiet, false);
2327 filesize = file->real_length;
2328 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2329 buf[filesize] = '\0';
2330 FS_Read (file, buf, filesize);
2334 if (filesizepointer)
2335 *filesizepointer = filesize;
2344 The filename will be prefixed by the current game directory
2347 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2351 file = FS_Open (filename, "wb", false, false);
2354 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2358 Con_DPrintf("FS_WriteFile: %s\n", filename);
2359 FS_Write (file, data, len);
2366 =============================================================================
2368 OTHERS PUBLIC FUNCTIONS
2370 =============================================================================
2378 void FS_StripExtension (const char *in, char *out, size_t size_out)
2386 while ((currentchar = *in) && size_out > 1)
2388 if (currentchar == '.')
2390 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2392 *out++ = currentchar;
2408 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2412 // if path doesn't have a .EXT, append extension
2413 // (extension should include the .)
2414 src = path + strlen(path) - 1;
2416 while (*src != '/' && src != path)
2419 return; // it has an extension
2423 strlcat (path, extension, size_path);
2431 Look for a file in the packages and in the filesystem
2434 int FS_FileType (const char *filename)
2436 searchpath_t *search;
2437 char fullpath[MAX_QPATH];
2439 search = FS_FindFile (filename, NULL, true);
2441 return FS_FILETYPE_NONE;
2444 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2446 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2447 return FS_SysFileType(fullpath);
2455 Look for a file in the packages and in the filesystem
2458 qboolean FS_FileExists (const char *filename)
2460 return (FS_FindFile (filename, NULL, true) != NULL);
2468 Look for a file in the filesystem only
2471 int FS_SysFileType (const char *path)
2474 DWORD result = GetFileAttributes(path);
2476 if(result == INVALID_FILE_ATTRIBUTES)
2477 return FS_FILETYPE_NONE;
2479 if(result & FILE_ATTRIBUTE_DIRECTORY)
2480 return FS_FILETYPE_DIRECTORY;
2482 return FS_FILETYPE_FILE;
2486 if (stat (path,&buf) == -1)
2487 return FS_FILETYPE_NONE;
2489 if(S_ISDIR(buf.st_mode))
2490 return FS_FILETYPE_DIRECTORY;
2492 return FS_FILETYPE_FILE;
2496 qboolean FS_SysFileExists (const char *path)
2498 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2501 void FS_mkdir (const char *path)
2514 Allocate and fill a search structure with information on matching filenames.
2517 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2520 searchpath_t *searchpath;
2522 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2523 stringlist_t resultlist;
2524 stringlist_t dirlist;
2525 const char *slash, *backslash, *colon, *separator;
2527 char netpath[MAX_OSPATH];
2528 char temp[MAX_OSPATH];
2530 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2535 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2539 stringlistinit(&resultlist);
2540 stringlistinit(&dirlist);
2542 slash = strrchr(pattern, '/');
2543 backslash = strrchr(pattern, '\\');
2544 colon = strrchr(pattern, ':');
2545 separator = max(slash, backslash);
2546 separator = max(separator, colon);
2547 basepathlength = separator ? (separator + 1 - pattern) : 0;
2548 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2550 memcpy(basepath, pattern, basepathlength);
2551 basepath[basepathlength] = 0;
2553 // search through the path, one element at a time
2554 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2556 // is the element a pak file?
2557 if (searchpath->pack)
2559 // look through all the pak file elements
2560 pak = searchpath->pack;
2561 for (i = 0;i < pak->numfiles;i++)
2563 strlcpy(temp, pak->files[i].name, sizeof(temp));
2566 if (matchpattern(temp, (char *)pattern, true))
2568 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2569 if (!strcmp(resultlist.strings[resultlistindex], temp))
2571 if (resultlistindex == resultlist.numstrings)
2573 stringlistappend(&resultlist, temp);
2575 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2578 // strip off one path element at a time until empty
2579 // this way directories are added to the listing if they match the pattern
2580 slash = strrchr(temp, '/');
2581 backslash = strrchr(temp, '\\');
2582 colon = strrchr(temp, ':');
2584 if (separator < slash)
2586 if (separator < backslash)
2587 separator = backslash;
2588 if (separator < colon)
2590 *((char *)separator) = 0;
2596 // get a directory listing and look at each name
2597 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2598 stringlistinit(&dirlist);
2599 listdirectory(&dirlist, netpath);
2600 for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
2602 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
2603 if (matchpattern(temp, (char *)pattern, true))
2605 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2606 if (!strcmp(resultlist.strings[resultlistindex], temp))
2608 if (resultlistindex == resultlist.numstrings)
2610 stringlistappend(&resultlist, temp);
2612 Con_DPrintf("SearchDirFile: %s\n", temp);
2616 stringlistfreecontents(&dirlist);
2620 if (resultlist.numstrings)
2622 stringlistsort(&resultlist);
2623 numfiles = resultlist.numstrings;
2625 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2626 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2627 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2628 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2629 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2630 search->numfilenames = (int)numfiles;
2633 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2636 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2637 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2638 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2640 numchars += (int)textlen;
2643 stringlistfreecontents(&resultlist);
2649 void FS_FreeSearch(fssearch_t *search)
2654 extern int con_linewidth;
2655 int FS_ListDirectory(const char *pattern, int oneperline)
2664 char linebuf[MAX_INPUTLINE];
2666 search = FS_Search(pattern, true, true);
2669 numfiles = search->numfilenames;
2672 // FIXME: the names could be added to one column list and then
2673 // gradually shifted into the next column if they fit, and then the
2674 // next to make a compact variable width listing but it's a lot more
2676 // find width for columns
2678 for (i = 0;i < numfiles;i++)
2680 l = (int)strlen(search->filenames[i]);
2681 if (columnwidth < l)
2684 // count the spacing character
2686 // calculate number of columns
2687 numcolumns = con_linewidth / columnwidth;
2688 // don't bother with the column printing if it's only one column
2689 if (numcolumns >= 2)
2691 numlines = (numfiles + numcolumns - 1) / numcolumns;
2692 for (i = 0;i < numlines;i++)
2695 for (k = 0;k < numcolumns;k++)
2697 l = i * numcolumns + k;
2700 name = search->filenames[l];
2701 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2702 linebuf[linebufpos++] = name[j];
2703 // space out name unless it's the last on the line
2704 if (k + 1 < numcolumns && l + 1 < numfiles)
2705 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2706 linebuf[linebufpos++] = ' ';
2709 linebuf[linebufpos] = 0;
2710 Con_Printf("%s\n", linebuf);
2717 for (i = 0;i < numfiles;i++)
2718 Con_Printf("%s\n", search->filenames[i]);
2719 FS_FreeSearch(search);
2720 return (int)numfiles;
2723 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2725 const char *pattern;
2728 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2731 if (Cmd_Argc() == 2)
2732 pattern = Cmd_Argv(1);
2735 if (!FS_ListDirectory(pattern, oneperline))
2736 Con_Print("No files found.\n");
2741 FS_ListDirectoryCmd("dir", true);
2746 FS_ListDirectoryCmd("ls", false);
2749 const char *FS_WhichPack(const char *filename)
2752 searchpath_t *sp = FS_FindFile(filename, &index, true);
2754 return sp->pack->filename;
2760 ====================
2761 FS_IsRegisteredQuakePack
2763 Look for a proof of purchase file file in the requested package
2765 If it is found, this file should NOT be downloaded.
2766 ====================
2768 qboolean FS_IsRegisteredQuakePack(const char *name)
2770 searchpath_t *search;
2773 // search through the path, one element at a time
2774 for (search = fs_searchpaths;search;search = search->next)
2776 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2778 int (*strcmp_funct) (const char* str1, const char* str2);
2779 int left, right, middle;
2782 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2784 // Look for the file (binary search)
2786 right = pak->numfiles - 1;
2787 while (left <= right)
2791 middle = (left + right) / 2;
2792 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2798 // If we're too far in the list
2805 // we found the requested pack but it is not registered quake
2813 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2816 unsigned char *filedata;
2817 fs_offset_t filesize;
2818 if (filesizepointer)
2819 *filesizepointer = 0;
2820 if (!filename || !*filename)
2822 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2825 if (filesizepointer)
2826 *filesizepointer = filesize;
2827 crc = CRC_Block(filedata, filesize);