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
25 // on UNIX platforms we need to define this so that video saving does not cause a SIGFSZ (file size) signal when a video clip exceeds 2GB
26 #define _FILE_OFFSET_BITS 64
38 # include <sys/stat.h>
45 // Win32 requires us to add O_BINARY, but the other OSes don't have it
50 // In case the system doesn't support the O_NONBLOCK flag
55 // largefile support for Win32
57 # define lseek _lseeki64
62 All of Quake's data access is through a hierchal file system, but the contents
63 of the file system can be transparently merged from several sources.
65 The "base directory" is the path to the directory holding the quake.exe and
66 all game directories. The sys_* files pass this to host_init in
67 quakeparms_t->basedir. This can be overridden with the "-basedir" command
68 line parm to allow code debugging in a different directory. The base
69 directory is only used during filesystem initialization.
71 The "game directory" is the first tree on the search path and directory that
72 all generated files (savegames, screenshots, demos, config files) will be
73 saved to. This can be overridden with the "-game" command line parameter.
74 The game directory can never be changed while quake is executing. This is a
75 precaution against having a malicious server instruct clients to write files
76 over areas they shouldn't.
82 =============================================================================
86 =============================================================================
89 // Magic numbers of a ZIP file (big-endian format)
90 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
91 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
92 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
94 // Other constants for ZIP files
95 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
96 #define ZIP_END_CDIR_SIZE 22
97 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
98 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
100 // Zlib constants (from zlib.h)
101 #define Z_SYNC_FLUSH 2
104 #define Z_STREAM_END 1
105 #define ZLIB_VERSION "1.2.3"
107 // Uncomment the following line if the zlib DLL you have still uses
108 // the 1.1.x series calling convention on Win32 (WINAPI)
109 //#define ZLIB_USES_WINAPI
113 =============================================================================
117 =============================================================================
120 // Zlib stream (from zlib.h)
121 // Warning: some pointers we don't use directly have
122 // been cast to "void*" for a matter of simplicity
125 unsigned char *next_in; // next input byte
126 unsigned int avail_in; // number of bytes available at next_in
127 unsigned long total_in; // total nb of input bytes read so far
129 unsigned char *next_out; // next output byte should be put there
130 unsigned int avail_out; // remaining free space at next_out
131 unsigned long total_out; // total nb of bytes output so far
133 char *msg; // last error message, NULL if no error
134 void *state; // not visible by applications
136 void *zalloc; // used to allocate the internal state
137 void *zfree; // used to free the internal state
138 void *opaque; // private data object passed to zalloc and zfree
140 int data_type; // best guess about the data type: ascii or binary
141 unsigned long adler; // adler32 value of the uncompressed data
142 unsigned long reserved; // reserved for future use
146 // inside a package (PAK or PK3)
147 #define QFILE_FLAG_PACKED (1 << 0)
148 // file is compressed using the deflate algorithm (PK3 only)
149 #define QFILE_FLAG_DEFLATED (1 << 1)
151 #define FILE_BUFF_SIZE 2048
155 size_t comp_length; // length of the compressed file
156 size_t in_ind, in_len; // input buffer current index and length
157 size_t in_position; // position in the compressed file
158 unsigned char input [FILE_BUFF_SIZE];
164 int handle; // file descriptor
165 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
166 fs_offset_t position; // current position in the file
167 fs_offset_t offset; // offset into the package (0 if external file)
168 int ungetc; // single stored character from ungetc, cleared to EOF when read
171 fs_offset_t buff_ind, buff_len; // buffer current index and length
172 unsigned char buff [FILE_BUFF_SIZE];
179 // ------ PK3 files on disk ------ //
181 // You can get the complete ZIP format description from PKWARE website
183 typedef struct pk3_endOfCentralDir_s
185 unsigned int signature;
186 unsigned short disknum;
187 unsigned short cdir_disknum; // number of the disk with the start of the central directory
188 unsigned short localentries; // number of entries in the central directory on this disk
189 unsigned short nbentries; // total number of entries in the central directory on this disk
190 unsigned int cdir_size; // size of the central directory
191 unsigned int cdir_offset; // with respect to the starting disk number
192 unsigned short comment_size;
193 } pk3_endOfCentralDir_t;
196 // ------ PAK files on disk ------ //
197 typedef struct dpackfile_s
200 int filepos, filelen;
203 typedef struct dpackheader_s
211 // Packages in memory
212 // the offset in packfile_t is the true contents offset
213 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
214 // file compressed using the deflate algorithm
215 #define PACKFILE_FLAG_DEFLATED (1 << 1)
217 typedef struct packfile_s
219 char name [MAX_QPATH];
222 fs_offset_t packsize; // size in the package
223 fs_offset_t realsize; // real file size (uncompressed)
226 typedef struct pack_s
228 char filename [MAX_OSPATH];
230 int ignorecase; // PK3 ignores case
236 // Search paths for files (including packages)
237 typedef struct searchpath_s
239 // only one of filename / pack will be used
240 char filename[MAX_OSPATH];
242 struct searchpath_s *next;
247 =============================================================================
251 =============================================================================
257 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
258 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
259 fs_offset_t offset, fs_offset_t packsize,
260 fs_offset_t realsize, int flags);
264 =============================================================================
268 =============================================================================
271 mempool_t *fs_mempool;
273 searchpath_t *fs_searchpaths = NULL;
275 #define MAX_FILES_IN_PACK 65536
277 char fs_gamedir[MAX_OSPATH];
278 char fs_basedir[MAX_OSPATH];
280 // list of active game directories (empty if not running a mod)
281 int fs_numgamedirs = 0;
282 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
284 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)"};
285 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"};
289 =============================================================================
291 PRIVATE FUNCTIONS - PK3 HANDLING
293 =============================================================================
296 // Functions exported from zlib
297 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
298 # define ZEXPORT WINAPI
303 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
304 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
305 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
306 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
308 #define qz_inflateInit2(strm, windowBits) \
309 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
311 static dllfunction_t zlibfuncs[] =
313 {"inflate", (void **) &qz_inflate},
314 {"inflateEnd", (void **) &qz_inflateEnd},
315 {"inflateInit2_", (void **) &qz_inflateInit2_},
316 {"inflateReset", (void **) &qz_inflateReset},
320 // Handle for Zlib DLL
321 static dllhandle_t zlib_dll = NULL;
331 void PK3_CloseLibrary (void)
333 Sys_UnloadLibrary (&zlib_dll);
341 Try to load the Zlib DLL
344 qboolean PK3_OpenLibrary (void)
346 const char* dllnames [] =
351 # ifdef ZLIB_USES_WINAPI
357 #elif defined(MACOSX)
371 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
373 Con_Printf ("Compressed files support disabled\n");
377 Con_Printf ("Compressed files support enabled\n");
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 (?))
493 // 2nd uint8 : external file attributes
494 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
495 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
497 // Still enough bytes for the name?
498 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
500 Mem_Free (central_dir);
504 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
505 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
507 char filename [sizeof (pack->files[0].name)];
508 fs_offset_t offset, packsize, realsize;
511 // Extract the name (strip it if necessary)
512 namesize = min(namesize, (int)sizeof (filename) - 1);
513 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
514 filename[namesize] = '\0';
516 if (BuffLittleShort (&ptr[10]))
517 flags = PACKFILE_FLAG_DEFLATED;
520 offset = BuffLittleLong (&ptr[42]);
521 packsize = BuffLittleLong (&ptr[20]);
522 realsize = BuffLittleLong (&ptr[24]);
523 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
527 // Skip the name, additionnal field, and comment
528 // 1er uint16 : extra field length
529 // 2eme uint16 : file comment length
530 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
531 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
535 // If the package is empty, central_dir is NULL here
536 if (central_dir != NULL)
537 Mem_Free (central_dir);
538 return pack->numfiles;
546 Create a package entry associated with a PK3 file
549 pack_t *FS_LoadPackPK3 (const char *packfile)
552 pk3_endOfCentralDir_t eocd;
556 packhandle = open (packfile, O_RDONLY | O_BINARY);
560 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
562 Con_Printf ("%s is not a PK3 file\n", packfile);
567 // Multi-volume ZIP archives are NOT allowed
568 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
570 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
575 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
576 // since eocd.nbentries is an unsigned 16 bits integer
577 #if MAX_FILES_IN_PACK < 65535
578 if (eocd.nbentries > MAX_FILES_IN_PACK)
580 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
586 // Create a package structure in memory
587 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
588 pack->ignorecase = true; // PK3 ignores case
589 strlcpy (pack->filename, packfile, sizeof (pack->filename));
590 pack->handle = packhandle;
591 pack->numfiles = eocd.nbentries;
592 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
594 real_nb_files = PK3_BuildFileList (pack, &eocd);
595 if (real_nb_files < 0)
597 Con_Printf ("%s is not a valid PK3 file\n", packfile);
603 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
610 PK3_GetTrueFileOffset
612 Find where the true file data offset is
615 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
617 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
621 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
624 // Load the local file description
625 lseek (pack->handle, pfile->offset, SEEK_SET);
626 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
627 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
629 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
633 // Skip name and extra field
634 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
636 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
642 =============================================================================
644 OTHER PRIVATE FUNCTIONS
646 =============================================================================
654 Add a file to the list of files contained into a package
657 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
658 fs_offset_t offset, fs_offset_t packsize,
659 fs_offset_t realsize, int flags)
661 int (*strcmp_funct) (const char* str1, const char* str2);
662 int left, right, middle;
665 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
667 // Look for the slot we should put that file into (binary search)
669 right = pack->numfiles - 1;
670 while (left <= right)
674 middle = (left + right) / 2;
675 diff = strcmp_funct (pack->files[middle].name, name);
677 // If we found the file, there's a problem
679 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
681 // If we're too far in the list
688 // We have to move the right of the list by one slot to free the one we need
689 pfile = &pack->files[left];
690 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
693 strlcpy (pfile->name, name, sizeof (pfile->name));
694 pfile->offset = offset;
695 pfile->packsize = packsize;
696 pfile->realsize = realsize;
697 pfile->flags = flags;
707 Only used for FS_Open.
710 void FS_CreatePath (char *path)
714 for (ofs = path+1 ; *ofs ; ofs++)
716 if (*ofs == '/' || *ofs == '\\')
718 // create the directory
734 void FS_Path_f (void)
738 Con_Print("Current search path:\n");
739 for (s=fs_searchpaths ; s ; s=s->next)
742 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
744 Con_Printf("%s\n", s->filename);
753 Takes an explicit (not game tree related) path to a pak file.
755 Loads the header and directory, adding the files at the beginning
756 of the list so they override previous pack files.
759 pack_t *FS_LoadPackPAK (const char *packfile)
761 dpackheader_t header;
767 packhandle = open (packfile, O_RDONLY | O_BINARY);
770 read (packhandle, (void *)&header, sizeof(header));
771 if (memcmp(header.id, "PACK", 4))
773 Con_Printf ("%s is not a packfile\n", packfile);
777 header.dirofs = LittleLong (header.dirofs);
778 header.dirlen = LittleLong (header.dirlen);
780 if (header.dirlen % sizeof(dpackfile_t))
782 Con_Printf ("%s has an invalid directory size\n", packfile);
787 numpackfiles = header.dirlen / sizeof(dpackfile_t);
789 if (numpackfiles > MAX_FILES_IN_PACK)
791 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
796 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
797 lseek (packhandle, header.dirofs, SEEK_SET);
798 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
800 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
806 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
807 pack->ignorecase = false; // PAK is case sensitive
808 strlcpy (pack->filename, packfile, sizeof (pack->filename));
809 pack->handle = packhandle;
811 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
813 // parse the directory
814 for (i = 0;i < numpackfiles;i++)
816 fs_offset_t offset = LittleLong (info[i].filepos);
817 fs_offset_t size = LittleLong (info[i].filelen);
819 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
824 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
832 Adds the given pack to the search path.
833 The pack type is autodetected by the file extension.
835 Returns true if the file was successfully added to the
836 search path or if it was already included.
838 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
842 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
844 searchpath_t *search;
846 const char *ext = FS_FileExtension(pakfile);
848 for(search = fs_searchpaths; search; search = search->next)
850 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
853 *already_loaded = true;
854 return true; // already loaded
859 *already_loaded = false;
861 if(!strcasecmp(ext, "pak"))
862 pak = FS_LoadPackPAK (pakfile);
863 else if(!strcasecmp(ext, "pk3"))
864 pak = FS_LoadPackPK3 (pakfile);
866 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
872 // find the first item whose next one is a pack or NULL
873 searchpath_t *insertion_point = 0;
874 if(fs_searchpaths && !fs_searchpaths->pack)
876 insertion_point = fs_searchpaths;
879 if(!insertion_point->next)
881 if(insertion_point->next->pack)
883 insertion_point = insertion_point->next;
886 // If insertion_point is NULL, this means that either there is no
887 // item in the list yet, or that the very first item is a pack. In
888 // that case, we want to insert at the beginning...
891 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
893 search->next = fs_searchpaths;
894 fs_searchpaths = search;
897 // otherwise we want to append directly after insertion_point.
899 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
901 search->next = insertion_point->next;
902 insertion_point->next = search;
907 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
909 search->next = fs_searchpaths;
910 fs_searchpaths = search;
916 Con_Printf("unable to load pak \"%s\"\n", pakfile);
926 Adds the given pack to the search path and searches for it in the game path.
927 The pack type is autodetected by the file extension.
929 Returns true if the file was successfully added to the
930 search path or if it was already included.
932 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
936 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
938 char fullpath[MAX_QPATH];
940 searchpath_t *search;
943 *already_loaded = false;
945 // then find the real name...
946 search = FS_FindFile(pakfile, &index, true);
947 if(!search || search->pack)
949 Con_Printf("could not find pak \"%s\"\n", pakfile);
953 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
955 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
963 Sets fs_gamedir, adds the directory to the head of the path,
964 then loads and adds pak1.pak pak2.pak ...
967 void FS_AddGameDirectory (const char *dir)
971 searchpath_t *search;
972 char pakfile[MAX_OSPATH];
974 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
976 stringlistinit(&list);
977 listdirectory(&list, dir);
978 stringlistsort(&list);
980 // add any PAK package in the directory
981 for (i = 0;i < list.numstrings;i++)
983 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
985 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
986 FS_AddPack_Fullpath(pakfile, NULL, false);
990 // add any PK3 package in the directory
991 for (i = 0;i < list.numstrings;i++)
993 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
995 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
996 FS_AddPack_Fullpath(pakfile, NULL, false);
1000 stringlistfreecontents(&list);
1002 // Add the directory to the search path
1003 // (unpacked files have the priority over packed files)
1004 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1005 strlcpy (search->filename, dir, sizeof (search->filename));
1006 search->next = fs_searchpaths;
1007 fs_searchpaths = search;
1016 void FS_AddGameHierarchy (const char *dir)
1019 const char *homedir;
1022 // Add the common game directory
1023 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1026 // Add the personal game directory
1027 homedir = getenv ("HOME");
1028 if (homedir != NULL && homedir[0] != '\0')
1029 FS_AddGameDirectory (va("%s/.%s/%s/", homedir, gameuserdirname, dir));
1039 const char *FS_FileExtension (const char *in)
1041 const char *separator, *backslash, *colon, *dot;
1043 separator = strrchr(in, '/');
1044 backslash = strrchr(in, '\\');
1045 if (!separator || separator < backslash)
1046 separator = backslash;
1047 colon = strrchr(in, ':');
1048 if (!separator || separator < colon)
1051 dot = strrchr(in, '.');
1052 if (dot == NULL || (separator && (dot < separator)))
1064 const char *FS_FileWithoutPath (const char *in)
1066 const char *separator, *backslash, *colon;
1068 separator = strrchr(in, '/');
1069 backslash = strrchr(in, '\\');
1070 if (!separator || separator < backslash)
1071 separator = backslash;
1072 colon = strrchr(in, ':');
1073 if (!separator || separator < colon)
1075 return separator ? separator + 1 : in;
1084 void FS_ClearSearchPath (void)
1086 // unload all packs and directory information, close all pack files
1087 // (if a qfile is still reading a pack it won't be harmed because it used
1088 // dup() to get its own handle already)
1089 while (fs_searchpaths)
1091 searchpath_t *search = fs_searchpaths;
1092 fs_searchpaths = search->next;
1096 close(search->pack->handle);
1097 // free any memory associated with it
1098 if (search->pack->files)
1099 Mem_Free(search->pack->files);
1100 Mem_Free(search->pack);
1112 void FS_Rescan (void)
1115 qboolean fs_modified = false;
1117 FS_ClearSearchPath();
1119 // add the game-specific paths
1120 // gamedirname1 (typically id1)
1121 FS_AddGameHierarchy (gamedirname1);
1122 // update the com_modname (used for server info)
1123 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1125 // add the game-specific path, if any
1126 // (only used for mission packs and the like, which should set fs_modified)
1130 FS_AddGameHierarchy (gamedirname2);
1134 // Adds basedir/gamedir as an override game
1135 // LordHavoc: now supports multiple -game directories
1136 // set the com_modname (reported in server info)
1137 for (i = 0;i < fs_numgamedirs;i++)
1140 FS_AddGameHierarchy (fs_gamedirs[i]);
1141 // update the com_modname (used server info)
1142 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1145 // set the default screenshot name to either the mod name or the
1146 // gamemode screenshot name
1147 if (strcmp(com_modname, gamedirname1))
1148 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1150 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1152 // If "-condebug" is in the command line, remove the previous log file
1153 if (COM_CheckParm ("-condebug") != 0)
1154 unlink (va("%s/qconsole.log", fs_gamedir));
1156 // look for the pop.lmp file and set registered to true if it is found
1157 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1160 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1162 Con_Print("Playing shareware version.\n");
1166 Cvar_Set ("registered", "1");
1167 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1168 Con_Print("Playing registered version.\n");
1171 // unload all wads so that future queries will return the new data
1175 void FS_Rescan_f(void)
1185 extern void Host_SaveConfig_f (void);
1186 extern void Host_LoadConfig_f (void);
1187 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1191 if (fs_numgamedirs == numgamedirs)
1193 for (i = 0;i < numgamedirs;i++)
1194 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1196 if (i == numgamedirs)
1197 return true; // already using this set of gamedirs, do nothing
1200 if (numgamedirs > MAX_GAMEDIRS)
1203 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1204 return false; // too many gamedirs
1207 for (i = 0;i < numgamedirs;i++)
1209 // if string is nasty, reject it
1210 if(FS_CheckNastyPath(gamedirs[i], true))
1213 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1214 return false; // nasty gamedirs
1218 for (i = 0;i < numgamedirs;i++)
1220 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1223 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1224 return false; // missing gamedirs
1228 // halt demo playback to close the file
1231 Host_SaveConfig_f();
1233 fs_numgamedirs = numgamedirs;
1234 for (i = 0;i < fs_numgamedirs;i++)
1235 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1237 // reinitialize filesystem to detect the new paks
1240 // exec the new config
1241 Host_LoadConfig_f();
1243 // unload all sounds so they will be reloaded from the new files as needed
1244 S_UnloadAllSounds_f();
1246 // reinitialize renderer (this reloads hud/console background/etc)
1247 R_Modules_Restart();
1257 void FS_GameDir_f (void)
1261 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1265 Con_Printf("gamedirs active:");
1266 for (i = 0;i < fs_numgamedirs;i++)
1267 Con_Printf(" %s", fs_gamedirs[i]);
1272 numgamedirs = Cmd_Argc() - 1;
1273 if (numgamedirs > MAX_GAMEDIRS)
1275 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1279 for (i = 0;i < numgamedirs;i++)
1280 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1282 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1284 // actually, changing during game would work fine, but would be stupid
1285 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1289 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1298 qboolean FS_CheckGameDir(const char *gamedir)
1302 stringlistinit(&list);
1303 listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
1304 success = list.numstrings > 0;
1305 stringlistfreecontents(&list);
1319 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1321 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1323 // If the base directory is explicitly defined by the compilation process
1324 #ifdef DP_FS_BASEDIR
1325 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1327 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1330 // FIXME: is there a better way to find the directory outside the .app?
1331 if (strstr(com_argv[0], ".app/"))
1335 split = strstr(com_argv[0], ".app/");
1336 while (split > com_argv[0] && *split != '/')
1338 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1339 fs_basedir[split - com_argv[0]] = 0;
1347 // Overrides the system supplied base directory (under GAMENAME)
1348 // 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)
1349 i = COM_CheckParm ("-basedir");
1350 if (i && i < com_argc-1)
1352 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1353 i = (int)strlen (fs_basedir);
1354 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1355 fs_basedir[i-1] = 0;
1358 // add a path separator to the end of the basedir if it lacks one
1359 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1360 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1362 if (!FS_CheckGameDir(gamedirname1))
1363 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1365 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1366 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1369 // Adds basedir/gamedir as an override game
1370 // LordHavoc: now supports multiple -game directories
1371 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1375 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1378 if (FS_CheckNastyPath(com_argv[i], true))
1379 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1380 if (!FS_CheckGameDir(com_argv[i]))
1381 Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1382 // add the gamedir to the list of active gamedirs
1383 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1388 // generate the searchpath
1392 void FS_Init_Commands(void)
1394 Cvar_RegisterVariable (&scr_screenshot_name);
1395 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1397 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1398 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1399 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1400 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1401 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1409 void FS_Shutdown (void)
1411 // close all pack files and such
1412 // (hopefully there aren't any other open files, but they'll be cleaned up
1413 // by the OS anyway)
1414 FS_ClearSearchPath();
1415 Mem_FreePool (&fs_mempool);
1419 ====================
1422 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1423 ====================
1425 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1431 // Parse the mode string
1440 opt = O_CREAT | O_TRUNC;
1444 opt = O_CREAT | O_APPEND;
1447 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1450 for (ind = 1; mode[ind] != '\0'; ind++)
1461 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1462 filepath, mode, mode[ind]);
1469 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1470 memset (file, 0, sizeof (*file));
1473 file->handle = open (filepath, mod | opt, 0666);
1474 if (file->handle < 0)
1480 file->real_length = lseek (file->handle, 0, SEEK_END);
1482 // For files opened in append mode, we start at the end of the file
1484 file->position = file->real_length;
1486 lseek (file->handle, 0, SEEK_SET);
1496 Open a packed file using its package file descriptor
1499 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1505 pfile = &pack->files[pack_ind];
1507 // If we don't have the true offset, get it now
1508 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1509 if (!PK3_GetTrueFileOffset (pfile, pack))
1512 // No Zlib DLL = no compressed files
1513 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1515 Con_Printf("WARNING: can't open the compressed file %s\n"
1516 "You need the Zlib DLL to use compressed files\n",
1521 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1522 // the dup() call to avoid having to close the dup_handle on error here
1523 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1525 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1526 pfile->name, pack->filename, (int) pfile->offset);
1530 dup_handle = dup (pack->handle);
1533 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1537 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1538 memset (file, 0, sizeof (*file));
1539 file->handle = dup_handle;
1540 file->flags = QFILE_FLAG_PACKED;
1541 file->real_length = pfile->realsize;
1542 file->offset = pfile->offset;
1546 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1550 file->flags |= QFILE_FLAG_DEFLATED;
1552 // We need some more variables
1553 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1555 ztk->comp_length = pfile->packsize;
1557 // Initialize zlib stream
1558 ztk->zstream.next_in = ztk->input;
1559 ztk->zstream.avail_in = 0;
1561 /* From Zlib's "unzip.c":
1563 * windowBits is passed < 0 to tell that there is no zlib header.
1564 * Note that in this case inflate *requires* an extra "dummy" byte
1565 * after the compressed stream in order to complete decompression and
1566 * return Z_STREAM_END.
1567 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1568 * size of both compressed and uncompressed data
1570 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1572 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1578 ztk->zstream.next_out = file->buff;
1579 ztk->zstream.avail_out = sizeof (file->buff);
1588 ====================
1591 Return true if the path should be rejected due to one of the following:
1592 1: path elements that are non-portable
1593 2: path elements that would allow access to files outside the game directory,
1594 or are just not a good idea for a mod to be using.
1595 ====================
1597 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1599 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1603 // Windows: don't allow \ in filenames (windows-only), period.
1604 // (on Windows \ is a directory separator, but / is also supported)
1605 if (strstr(path, "\\"))
1606 return 1; // non-portable
1608 // Mac: don't allow Mac-only filenames - : is a directory separator
1609 // instead of /, but we rely on / working already, so there's no reason to
1610 // support a Mac-only path
1611 // Amiga and Windows: : tries to go to root of drive
1612 if (strstr(path, ":"))
1613 return 1; // non-portable attempt to go to root of drive
1615 // Amiga: // is parent directory
1616 if (strstr(path, "//"))
1617 return 1; // non-portable attempt to go to parent directory
1619 // all: don't allow going to parent directory (../ or /../)
1620 if (strstr(path, ".."))
1621 return 2; // attempt to go outside the game directory
1623 // Windows and UNIXes: don't allow absolute paths
1625 return 2; // attempt to go outside the game directory
1627 // 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
1628 if (strchr(path, '.'))
1632 // gamedir is entirely path elements, so simply forbid . entirely
1635 if (strchr(path, '.') < strrchr(path, '/'))
1636 return 2; // possible attempt to go outside the game directory
1639 // all: forbid trailing slash on gamedir
1640 if (isgamedir && path[strlen(path)-1] == '/')
1643 // all: forbid leading dot on any filename for any reason
1644 if (strstr(path, "/."))
1645 return 2; // attempt to go outside the game directory
1647 // after all these checks we're pretty sure it's a / separated filename
1648 // and won't do much if any harm
1654 ====================
1657 Look for a file in the packages and in the filesystem
1659 Return the searchpath where the file was found (or NULL)
1660 and the file index in the package if relevant
1661 ====================
1663 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1665 searchpath_t *search;
1668 // search through the path, one element at a time
1669 for (search = fs_searchpaths;search;search = search->next)
1671 // is the element a pak file?
1674 int (*strcmp_funct) (const char* str1, const char* str2);
1675 int left, right, middle;
1678 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1680 // Look for the file (binary search)
1682 right = pak->numfiles - 1;
1683 while (left <= right)
1687 middle = (left + right) / 2;
1688 diff = strcmp_funct (pak->files[middle].name, name);
1693 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1695 // yes, but the first one is empty so we treat it as not being there
1696 if (!quiet && developer.integer >= 10)
1697 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1704 if (!quiet && developer.integer >= 10)
1705 Con_Printf("FS_FindFile: %s in %s\n",
1706 pak->files[middle].name, pak->filename);
1713 // If we're too far in the list
1722 char netpath[MAX_OSPATH];
1723 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1724 if (FS_SysFileExists (netpath))
1726 if (!quiet && developer.integer >= 10)
1727 Con_Printf("FS_FindFile: %s\n", netpath);
1736 if (!quiet && developer.integer >= 10)
1737 Con_Printf("FS_FindFile: can't find %s\n", name);
1749 Look for a file in the search paths and open it in read-only mode
1752 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1754 searchpath_t *search;
1757 search = FS_FindFile (filename, &pack_ind, quiet);
1763 // Found in the filesystem?
1766 char path [MAX_OSPATH];
1767 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1768 return FS_SysOpen (path, "rb", nonblocking);
1771 // So, we found it in a package...
1772 return FS_OpenPackedFile (search->pack, pack_ind);
1777 =============================================================================
1779 MAIN PUBLIC FUNCTIONS
1781 =============================================================================
1785 ====================
1788 Open a file. The syntax is the same as fopen
1789 ====================
1791 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1793 if (FS_CheckNastyPath(filepath, false))
1795 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1799 // If the file is opened in "write", "append", or "read/write" mode
1800 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1802 char real_path [MAX_OSPATH];
1804 // Open the file on disk directly
1805 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1807 // Create directories up to the file
1808 FS_CreatePath (real_path);
1810 return FS_SysOpen (real_path, mode, nonblocking);
1812 // Else, we look at the various search paths and open the file in read-only mode
1814 return FS_OpenReadFile (filepath, quiet, nonblocking);
1819 ====================
1823 ====================
1825 int FS_Close (qfile_t* file)
1827 if (close (file->handle))
1832 qz_inflateEnd (&file->ztk->zstream);
1833 Mem_Free (file->ztk);
1842 ====================
1845 Write "datasize" bytes into a file
1846 ====================
1848 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1852 // If necessary, seek to the exact file position we're supposed to be
1853 if (file->buff_ind != file->buff_len)
1854 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1856 // Purge cached data
1859 // Write the buffer and update the position
1860 result = write (file->handle, data, (fs_offset_t)datasize);
1861 file->position = lseek (file->handle, 0, SEEK_CUR);
1862 if (file->real_length < file->position)
1863 file->real_length = file->position;
1873 ====================
1876 Read up to "buffersize" bytes from a file
1877 ====================
1879 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1881 fs_offset_t count, done;
1883 if (buffersize == 0)
1886 // Get rid of the ungetc character
1887 if (file->ungetc != EOF)
1889 ((char*)buffer)[0] = file->ungetc;
1897 // First, we copy as many bytes as we can from "buff"
1898 if (file->buff_ind < file->buff_len)
1900 count = file->buff_len - file->buff_ind;
1901 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1903 memcpy (buffer, &file->buff[file->buff_ind], count);
1904 file->buff_ind += count;
1906 buffersize -= count;
1907 if (buffersize == 0)
1911 // NOTE: at this point, the read buffer is always empty
1913 // If the file isn't compressed
1914 if (! (file->flags & QFILE_FLAG_DEFLATED))
1918 // We must take care to not read after the end of the file
1919 count = file->real_length - file->position;
1921 // If we have a lot of data to get, put them directly into "buffer"
1922 if (buffersize > sizeof (file->buff) / 2)
1924 if (count > (fs_offset_t)buffersize)
1925 count = (fs_offset_t)buffersize;
1926 lseek (file->handle, file->offset + file->position, SEEK_SET);
1927 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1931 file->position += nb;
1933 // Purge cached data
1939 if (count > (fs_offset_t)sizeof (file->buff))
1940 count = (fs_offset_t)sizeof (file->buff);
1941 lseek (file->handle, file->offset + file->position, SEEK_SET);
1942 nb = read (file->handle, file->buff, count);
1945 file->buff_len = nb;
1946 file->position += nb;
1948 // Copy the requested data in "buffer" (as much as we can)
1949 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1950 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1951 file->buff_ind = count;
1959 // If the file is compressed, it's more complicated...
1960 // We cycle through a few operations until we have read enough data
1961 while (buffersize > 0)
1963 ztoolkit_t *ztk = file->ztk;
1966 // NOTE: at this point, the read buffer is always empty
1968 // If "input" is also empty, we need to refill it
1969 if (ztk->in_ind == ztk->in_len)
1971 // If we are at the end of the file
1972 if (file->position == file->real_length)
1975 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
1976 if (count > (fs_offset_t)sizeof (ztk->input))
1977 count = (fs_offset_t)sizeof (ztk->input);
1978 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
1979 if (read (file->handle, ztk->input, count) != count)
1981 Con_Printf ("FS_Read: unexpected end of file\n");
1986 ztk->in_len = count;
1987 ztk->in_position += count;
1990 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1991 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
1993 // Now that we are sure we have compressed data available, we need to determine
1994 // if it's better to inflate it in "file->buff" or directly in "buffer"
1996 // Inflate the data in "file->buff"
1997 if (buffersize < sizeof (file->buff) / 2)
1999 ztk->zstream.next_out = file->buff;
2000 ztk->zstream.avail_out = sizeof (file->buff);
2001 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2002 if (error != Z_OK && error != Z_STREAM_END)
2004 Con_Printf ("FS_Read: Can't inflate file\n");
2007 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2009 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2010 file->position += file->buff_len;
2012 // Copy the requested data in "buffer" (as much as we can)
2013 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2014 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2015 file->buff_ind = count;
2018 // Else, we inflate directly in "buffer"
2021 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2022 ztk->zstream.avail_out = (unsigned int)buffersize;
2023 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2024 if (error != Z_OK && error != Z_STREAM_END)
2026 Con_Printf ("FS_Read: Can't inflate file\n");
2029 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2031 // How much data did it inflate?
2032 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2033 file->position += count;
2035 // Purge cached data
2040 buffersize -= count;
2048 ====================
2051 Print a string into a file
2052 ====================
2054 int FS_Print (qfile_t* file, const char *msg)
2056 return (int)FS_Write (file, msg, strlen (msg));
2060 ====================
2063 Print a string into a file
2064 ====================
2066 int FS_Printf(qfile_t* file, const char* format, ...)
2071 va_start (args, format);
2072 result = FS_VPrintf (file, format, args);
2080 ====================
2083 Print a string into a file
2084 ====================
2086 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2089 fs_offset_t buff_size = MAX_INPUTLINE;
2094 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2095 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2096 if (len >= 0 && len < buff_size)
2098 Mem_Free (tempbuff);
2102 len = write (file->handle, tempbuff, len);
2103 Mem_Free (tempbuff);
2110 ====================
2113 Get the next character of a file
2114 ====================
2116 int FS_Getc (qfile_t* file)
2120 if (FS_Read (file, &c, 1) != 1)
2128 ====================
2131 Put a character back into the read buffer (only supports one character!)
2132 ====================
2134 int FS_UnGetc (qfile_t* file, unsigned char c)
2136 // If there's already a character waiting to be read
2137 if (file->ungetc != EOF)
2146 ====================
2149 Move the position index in a file
2150 ====================
2152 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2155 unsigned char* buffer;
2156 fs_offset_t buffersize;
2158 // Compute the file offset
2162 offset += file->position - file->buff_len + file->buff_ind;
2169 offset += file->real_length;
2175 if (offset < 0 || offset > (long) file->real_length)
2178 // If we have the data in our read buffer, we don't need to actually seek
2179 if (file->position - file->buff_len <= offset && offset <= file->position)
2181 file->buff_ind = offset + file->buff_len - file->position;
2185 // Purge cached data
2188 // Unpacked or uncompressed files can seek directly
2189 if (! (file->flags & QFILE_FLAG_DEFLATED))
2191 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2193 file->position = offset;
2197 // Seeking in compressed files is more a hack than anything else,
2198 // but we need to support it, so here we go.
2201 // If we have to go back in the file, we need to restart from the beginning
2202 if (offset <= file->position)
2206 ztk->in_position = 0;
2208 lseek (file->handle, file->offset, SEEK_SET);
2210 // Reset the Zlib stream
2211 ztk->zstream.next_in = ztk->input;
2212 ztk->zstream.avail_in = 0;
2213 qz_inflateReset (&ztk->zstream);
2216 // We need a big buffer to force inflating into it directly
2217 buffersize = 2 * sizeof (file->buff);
2218 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2220 // Skip all data until we reach the requested offset
2221 while (offset > file->position)
2223 fs_offset_t diff = offset - file->position;
2224 fs_offset_t count, len;
2226 count = (diff > buffersize) ? buffersize : diff;
2227 len = FS_Read (file, buffer, count);
2241 ====================
2244 Give the current position in a file
2245 ====================
2247 fs_offset_t FS_Tell (qfile_t* file)
2249 return file->position - file->buff_len + file->buff_ind;
2254 ====================
2257 Give the total size of a file
2258 ====================
2260 fs_offset_t FS_FileSize (qfile_t* file)
2262 return file->real_length;
2267 ====================
2270 Erases any buffered input or output data
2271 ====================
2273 void FS_Purge (qfile_t* file)
2285 Filename are relative to the quake directory.
2286 Always appends a 0 byte.
2289 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2292 unsigned char *buf = NULL;
2293 fs_offset_t filesize = 0;
2295 file = FS_Open (path, "rb", quiet, false);
2298 filesize = file->real_length;
2299 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2300 buf[filesize] = '\0';
2301 FS_Read (file, buf, filesize);
2305 if (filesizepointer)
2306 *filesizepointer = filesize;
2315 The filename will be prefixed by the current game directory
2318 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2322 file = FS_Open (filename, "wb", false, false);
2325 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2329 Con_DPrintf("FS_WriteFile: %s\n", filename);
2330 FS_Write (file, data, len);
2337 =============================================================================
2339 OTHERS PUBLIC FUNCTIONS
2341 =============================================================================
2349 void FS_StripExtension (const char *in, char *out, size_t size_out)
2357 while ((currentchar = *in) && size_out > 1)
2359 if (currentchar == '.')
2361 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2363 *out++ = currentchar;
2379 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2383 // if path doesn't have a .EXT, append extension
2384 // (extension should include the .)
2385 src = path + strlen(path) - 1;
2387 while (*src != '/' && src != path)
2390 return; // it has an extension
2394 strlcat (path, extension, size_path);
2402 Look for a file in the packages and in the filesystem
2405 qboolean FS_FileExists (const char *filename)
2407 return (FS_FindFile (filename, NULL, true) != NULL);
2415 Look for a file in the filesystem only
2418 qboolean FS_SysFileExists (const char *path)
2423 // TODO: use another function instead, to avoid opening the file
2424 desc = open (path, O_RDONLY | O_BINARY);
2433 if (stat (path,&buf) == -1)
2440 void FS_mkdir (const char *path)
2453 Allocate and fill a search structure with information on matching filenames.
2456 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2459 searchpath_t *searchpath;
2461 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2462 stringlist_t resultlist;
2463 stringlist_t dirlist;
2464 const char *slash, *backslash, *colon, *separator;
2466 char netpath[MAX_OSPATH];
2467 char temp[MAX_OSPATH];
2469 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2474 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2478 stringlistinit(&resultlist);
2479 stringlistinit(&dirlist);
2481 slash = strrchr(pattern, '/');
2482 backslash = strrchr(pattern, '\\');
2483 colon = strrchr(pattern, ':');
2484 separator = max(slash, backslash);
2485 separator = max(separator, colon);
2486 basepathlength = separator ? (separator + 1 - pattern) : 0;
2487 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2489 memcpy(basepath, pattern, basepathlength);
2490 basepath[basepathlength] = 0;
2492 // search through the path, one element at a time
2493 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2495 // is the element a pak file?
2496 if (searchpath->pack)
2498 // look through all the pak file elements
2499 pak = searchpath->pack;
2500 for (i = 0;i < pak->numfiles;i++)
2502 strlcpy(temp, pak->files[i].name, sizeof(temp));
2505 if (matchpattern(temp, (char *)pattern, true))
2507 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2508 if (!strcmp(resultlist.strings[resultlistindex], temp))
2510 if (resultlistindex == resultlist.numstrings)
2512 stringlistappend(&resultlist, temp);
2514 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2517 // strip off one path element at a time until empty
2518 // this way directories are added to the listing if they match the pattern
2519 slash = strrchr(temp, '/');
2520 backslash = strrchr(temp, '\\');
2521 colon = strrchr(temp, ':');
2523 if (separator < slash)
2525 if (separator < backslash)
2526 separator = backslash;
2527 if (separator < colon)
2529 *((char *)separator) = 0;
2535 // get a directory listing and look at each name
2536 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2537 stringlistinit(&dirlist);
2538 listdirectory(&dirlist, netpath);
2539 for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
2541 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
2542 if (matchpattern(temp, (char *)pattern, true))
2544 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2545 if (!strcmp(resultlist.strings[resultlistindex], temp))
2547 if (resultlistindex == resultlist.numstrings)
2549 stringlistappend(&resultlist, temp);
2551 Con_DPrintf("SearchDirFile: %s\n", temp);
2555 stringlistfreecontents(&dirlist);
2559 if (resultlist.numstrings)
2561 stringlistsort(&resultlist);
2562 numfiles = resultlist.numstrings;
2564 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2565 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2566 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2567 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2568 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2569 search->numfilenames = (int)numfiles;
2572 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2575 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2576 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2577 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2579 numchars += (int)textlen;
2582 stringlistfreecontents(&resultlist);
2588 void FS_FreeSearch(fssearch_t *search)
2593 extern int con_linewidth;
2594 int FS_ListDirectory(const char *pattern, int oneperline)
2603 char linebuf[MAX_INPUTLINE];
2605 search = FS_Search(pattern, true, true);
2608 numfiles = search->numfilenames;
2611 // FIXME: the names could be added to one column list and then
2612 // gradually shifted into the next column if they fit, and then the
2613 // next to make a compact variable width listing but it's a lot more
2615 // find width for columns
2617 for (i = 0;i < numfiles;i++)
2619 l = (int)strlen(search->filenames[i]);
2620 if (columnwidth < l)
2623 // count the spacing character
2625 // calculate number of columns
2626 numcolumns = con_linewidth / columnwidth;
2627 // don't bother with the column printing if it's only one column
2628 if (numcolumns >= 2)
2630 numlines = (numfiles + numcolumns - 1) / numcolumns;
2631 for (i = 0;i < numlines;i++)
2634 for (k = 0;k < numcolumns;k++)
2636 l = i * numcolumns + k;
2639 name = search->filenames[l];
2640 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2641 linebuf[linebufpos++] = name[j];
2642 // space out name unless it's the last on the line
2643 if (k + 1 < numcolumns && l + 1 < numfiles)
2644 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2645 linebuf[linebufpos++] = ' ';
2648 linebuf[linebufpos] = 0;
2649 Con_Printf("%s\n", linebuf);
2656 for (i = 0;i < numfiles;i++)
2657 Con_Printf("%s\n", search->filenames[i]);
2658 FS_FreeSearch(search);
2659 return (int)numfiles;
2662 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2664 const char *pattern;
2667 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2670 if (Cmd_Argc() == 2)
2671 pattern = Cmd_Argv(1);
2674 if (!FS_ListDirectory(pattern, oneperline))
2675 Con_Print("No files found.\n");
2680 FS_ListDirectoryCmd("dir", true);
2685 FS_ListDirectoryCmd("ls", false);
2688 const char *FS_WhichPack(const char *filename)
2691 searchpath_t *sp = FS_FindFile(filename, &index, true);
2693 return sp->pack->filename;
2699 ====================
2700 FS_IsRegisteredQuakePack
2702 Look for a proof of purchase file file in the requested package
2704 If it is found, this file should NOT be downloaded.
2705 ====================
2707 qboolean FS_IsRegisteredQuakePack(const char *name)
2709 searchpath_t *search;
2712 // search through the path, one element at a time
2713 for (search = fs_searchpaths;search;search = search->next)
2715 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2717 int (*strcmp_funct) (const char* str1, const char* str2);
2718 int left, right, middle;
2721 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2723 // Look for the file (binary search)
2725 right = pak->numfiles - 1;
2726 while (left <= right)
2730 middle = (left + right) / 2;
2731 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2737 // If we're too far in the list
2744 // we found the requested pack but it is not registered quake
2752 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2755 unsigned char *filedata;
2756 fs_offset_t filesize;
2757 if (filesizepointer)
2758 *filesizepointer = 0;
2759 if (!filename || !*filename)
2761 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2764 if (filesizepointer)
2765 *filesizepointer = filesize;
2766 crc = CRC_Block(filedata, filesize);