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 appdata[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_APPDATA, NULL, 0, appdata) == S_OK)
1032 dpsnprintf(userdir, sizeof(userdir), "%s/%s/", appdata, gameuserdirname);
1034 homedir = getenv ("HOME");
1036 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1040 if(!COM_CheckParm("-appdata"))
1042 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT | O_APPEND, 0666);
1046 *userdir = 0; // we have write access to the game dir, so let's use it
1051 if(COM_CheckParm("-nohome"))
1054 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1055 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1058 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1067 const char *FS_FileExtension (const char *in)
1069 const char *separator, *backslash, *colon, *dot;
1071 separator = strrchr(in, '/');
1072 backslash = strrchr(in, '\\');
1073 if (!separator || separator < backslash)
1074 separator = backslash;
1075 colon = strrchr(in, ':');
1076 if (!separator || separator < colon)
1079 dot = strrchr(in, '.');
1080 if (dot == NULL || (separator && (dot < separator)))
1092 const char *FS_FileWithoutPath (const char *in)
1094 const char *separator, *backslash, *colon;
1096 separator = strrchr(in, '/');
1097 backslash = strrchr(in, '\\');
1098 if (!separator || separator < backslash)
1099 separator = backslash;
1100 colon = strrchr(in, ':');
1101 if (!separator || separator < colon)
1103 return separator ? separator + 1 : in;
1112 void FS_ClearSearchPath (void)
1114 // unload all packs and directory information, close all pack files
1115 // (if a qfile is still reading a pack it won't be harmed because it used
1116 // dup() to get its own handle already)
1117 while (fs_searchpaths)
1119 searchpath_t *search = fs_searchpaths;
1120 fs_searchpaths = search->next;
1124 close(search->pack->handle);
1125 // free any memory associated with it
1126 if (search->pack->files)
1127 Mem_Free(search->pack->files);
1128 Mem_Free(search->pack);
1140 void FS_Rescan (void)
1143 qboolean fs_modified = false;
1145 FS_ClearSearchPath();
1147 // add the game-specific paths
1148 // gamedirname1 (typically id1)
1149 FS_AddGameHierarchy (gamedirname1);
1150 // update the com_modname (used for server info)
1151 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1153 // add the game-specific path, if any
1154 // (only used for mission packs and the like, which should set fs_modified)
1158 FS_AddGameHierarchy (gamedirname2);
1162 // Adds basedir/gamedir as an override game
1163 // LordHavoc: now supports multiple -game directories
1164 // set the com_modname (reported in server info)
1165 for (i = 0;i < fs_numgamedirs;i++)
1168 FS_AddGameHierarchy (fs_gamedirs[i]);
1169 // update the com_modname (used server info)
1170 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1173 // set the default screenshot name to either the mod name or the
1174 // gamemode screenshot name
1175 if (strcmp(com_modname, gamedirname1))
1176 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1178 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1180 // If "-condebug" is in the command line, remove the previous log file
1181 if (COM_CheckParm ("-condebug") != 0)
1182 unlink (va("%s/qconsole.log", fs_gamedir));
1184 // look for the pop.lmp file and set registered to true if it is found
1185 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1188 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1190 Con_Print("Playing shareware version.\n");
1194 Cvar_Set ("registered", "1");
1195 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1196 Con_Print("Playing registered version.\n");
1199 // unload all wads so that future queries will return the new data
1203 void FS_Rescan_f(void)
1213 extern void Host_SaveConfig (void);
1214 extern void Host_LoadConfig_f (void);
1215 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1219 if (fs_numgamedirs == numgamedirs)
1221 for (i = 0;i < numgamedirs;i++)
1222 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1224 if (i == numgamedirs)
1225 return true; // already using this set of gamedirs, do nothing
1228 if (numgamedirs > MAX_GAMEDIRS)
1231 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1232 return false; // too many gamedirs
1235 for (i = 0;i < numgamedirs;i++)
1237 // if string is nasty, reject it
1238 if(FS_CheckNastyPath(gamedirs[i], true))
1241 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1242 return false; // nasty gamedirs
1246 for (i = 0;i < numgamedirs;i++)
1248 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1251 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1252 return false; // missing gamedirs
1256 // halt demo playback to close the file
1261 fs_numgamedirs = numgamedirs;
1262 for (i = 0;i < fs_numgamedirs;i++)
1263 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1265 // reinitialize filesystem to detect the new paks
1268 // exec the new config
1269 Host_LoadConfig_f();
1271 // unload all sounds so they will be reloaded from the new files as needed
1272 S_UnloadAllSounds_f();
1274 // reinitialize renderer (this reloads hud/console background/etc)
1275 R_Modules_Restart();
1285 void FS_GameDir_f (void)
1289 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1293 Con_Printf("gamedirs active:");
1294 for (i = 0;i < fs_numgamedirs;i++)
1295 Con_Printf(" %s", fs_gamedirs[i]);
1300 numgamedirs = Cmd_Argc() - 1;
1301 if (numgamedirs > MAX_GAMEDIRS)
1303 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1307 for (i = 0;i < numgamedirs;i++)
1308 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1310 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1312 // actually, changing during game would work fine, but would be stupid
1313 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1317 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1326 qboolean FS_CheckGameDir(const char *gamedir)
1330 stringlistinit(&list);
1331 listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
1332 success = list.numstrings > 0;
1333 stringlistfreecontents(&list);
1347 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1349 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1351 // If the base directory is explicitly defined by the compilation process
1352 #ifdef DP_FS_BASEDIR
1353 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1355 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1358 // FIXME: is there a better way to find the directory outside the .app?
1359 if (strstr(com_argv[0], ".app/"))
1363 split = strstr(com_argv[0], ".app/");
1364 while (split > com_argv[0] && *split != '/')
1366 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1367 fs_basedir[split - com_argv[0]] = 0;
1375 // Overrides the system supplied base directory (under GAMENAME)
1376 // 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)
1377 i = COM_CheckParm ("-basedir");
1378 if (i && i < com_argc-1)
1380 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1381 i = (int)strlen (fs_basedir);
1382 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1383 fs_basedir[i-1] = 0;
1386 // add a path separator to the end of the basedir if it lacks one
1387 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1388 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1390 if (!FS_CheckGameDir(gamedirname1))
1391 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1393 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1394 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1397 // Adds basedir/gamedir as an override game
1398 // LordHavoc: now supports multiple -game directories
1399 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1403 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1406 if (FS_CheckNastyPath(com_argv[i], true))
1407 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1408 if (!FS_CheckGameDir(com_argv[i]))
1409 Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1410 // add the gamedir to the list of active gamedirs
1411 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1416 // generate the searchpath
1420 void FS_Init_Commands(void)
1422 Cvar_RegisterVariable (&scr_screenshot_name);
1423 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1425 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1426 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1427 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1428 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1429 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1437 void FS_Shutdown (void)
1439 // close all pack files and such
1440 // (hopefully there aren't any other open files, but they'll be cleaned up
1441 // by the OS anyway)
1442 FS_ClearSearchPath();
1443 Mem_FreePool (&fs_mempool);
1447 ====================
1450 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1451 ====================
1453 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1459 // Parse the mode string
1468 opt = O_CREAT | O_TRUNC;
1472 opt = O_CREAT | O_APPEND;
1475 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1478 for (ind = 1; mode[ind] != '\0'; ind++)
1489 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1490 filepath, mode, mode[ind]);
1497 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1498 memset (file, 0, sizeof (*file));
1501 file->handle = open (filepath, mod | opt, 0666);
1502 if (file->handle < 0)
1508 file->real_length = lseek (file->handle, 0, SEEK_END);
1510 // For files opened in append mode, we start at the end of the file
1512 file->position = file->real_length;
1514 lseek (file->handle, 0, SEEK_SET);
1524 Open a packed file using its package file descriptor
1527 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1533 pfile = &pack->files[pack_ind];
1535 // If we don't have the true offset, get it now
1536 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1537 if (!PK3_GetTrueFileOffset (pfile, pack))
1540 // No Zlib DLL = no compressed files
1541 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1543 Con_Printf("WARNING: can't open the compressed file %s\n"
1544 "You need the Zlib DLL to use compressed files\n",
1549 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1550 // the dup() call to avoid having to close the dup_handle on error here
1551 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1553 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1554 pfile->name, pack->filename, (int) pfile->offset);
1558 dup_handle = dup (pack->handle);
1561 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1565 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1566 memset (file, 0, sizeof (*file));
1567 file->handle = dup_handle;
1568 file->flags = QFILE_FLAG_PACKED;
1569 file->real_length = pfile->realsize;
1570 file->offset = pfile->offset;
1574 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1578 file->flags |= QFILE_FLAG_DEFLATED;
1580 // We need some more variables
1581 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1583 ztk->comp_length = pfile->packsize;
1585 // Initialize zlib stream
1586 ztk->zstream.next_in = ztk->input;
1587 ztk->zstream.avail_in = 0;
1589 /* From Zlib's "unzip.c":
1591 * windowBits is passed < 0 to tell that there is no zlib header.
1592 * Note that in this case inflate *requires* an extra "dummy" byte
1593 * after the compressed stream in order to complete decompression and
1594 * return Z_STREAM_END.
1595 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1596 * size of both compressed and uncompressed data
1598 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1600 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1606 ztk->zstream.next_out = file->buff;
1607 ztk->zstream.avail_out = sizeof (file->buff);
1616 ====================
1619 Return true if the path should be rejected due to one of the following:
1620 1: path elements that are non-portable
1621 2: path elements that would allow access to files outside the game directory,
1622 or are just not a good idea for a mod to be using.
1623 ====================
1625 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1627 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1631 // Windows: don't allow \ in filenames (windows-only), period.
1632 // (on Windows \ is a directory separator, but / is also supported)
1633 if (strstr(path, "\\"))
1634 return 1; // non-portable
1636 // Mac: don't allow Mac-only filenames - : is a directory separator
1637 // instead of /, but we rely on / working already, so there's no reason to
1638 // support a Mac-only path
1639 // Amiga and Windows: : tries to go to root of drive
1640 if (strstr(path, ":"))
1641 return 1; // non-portable attempt to go to root of drive
1643 // Amiga: // is parent directory
1644 if (strstr(path, "//"))
1645 return 1; // non-portable attempt to go to parent directory
1647 // all: don't allow going to parent directory (../ or /../)
1648 if (strstr(path, ".."))
1649 return 2; // attempt to go outside the game directory
1651 // Windows and UNIXes: don't allow absolute paths
1653 return 2; // attempt to go outside the game directory
1655 // 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
1656 if (strchr(path, '.'))
1660 // gamedir is entirely path elements, so simply forbid . entirely
1663 if (strchr(path, '.') < strrchr(path, '/'))
1664 return 2; // possible attempt to go outside the game directory
1667 // all: forbid trailing slash on gamedir
1668 if (isgamedir && path[strlen(path)-1] == '/')
1671 // all: forbid leading dot on any filename for any reason
1672 if (strstr(path, "/."))
1673 return 2; // attempt to go outside the game directory
1675 // after all these checks we're pretty sure it's a / separated filename
1676 // and won't do much if any harm
1682 ====================
1685 Look for a file in the packages and in the filesystem
1687 Return the searchpath where the file was found (or NULL)
1688 and the file index in the package if relevant
1689 ====================
1691 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1693 searchpath_t *search;
1696 // search through the path, one element at a time
1697 for (search = fs_searchpaths;search;search = search->next)
1699 // is the element a pak file?
1702 int (*strcmp_funct) (const char* str1, const char* str2);
1703 int left, right, middle;
1706 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1708 // Look for the file (binary search)
1710 right = pak->numfiles - 1;
1711 while (left <= right)
1715 middle = (left + right) / 2;
1716 diff = strcmp_funct (pak->files[middle].name, name);
1721 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1723 // yes, but the first one is empty so we treat it as not being there
1724 if (!quiet && developer.integer >= 10)
1725 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1732 if (!quiet && developer.integer >= 10)
1733 Con_Printf("FS_FindFile: %s in %s\n",
1734 pak->files[middle].name, pak->filename);
1741 // If we're too far in the list
1750 char netpath[MAX_OSPATH];
1751 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1752 if (FS_SysFileExists (netpath))
1754 if (!quiet && developer.integer >= 10)
1755 Con_Printf("FS_FindFile: %s\n", netpath);
1764 if (!quiet && developer.integer >= 10)
1765 Con_Printf("FS_FindFile: can't find %s\n", name);
1777 Look for a file in the search paths and open it in read-only mode
1780 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1782 searchpath_t *search;
1785 search = FS_FindFile (filename, &pack_ind, quiet);
1791 // Found in the filesystem?
1794 char path [MAX_OSPATH];
1795 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1796 return FS_SysOpen (path, "rb", nonblocking);
1799 // So, we found it in a package...
1800 return FS_OpenPackedFile (search->pack, pack_ind);
1805 =============================================================================
1807 MAIN PUBLIC FUNCTIONS
1809 =============================================================================
1813 ====================
1816 Open a file. The syntax is the same as fopen
1817 ====================
1819 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1821 if (FS_CheckNastyPath(filepath, false))
1823 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1827 // If the file is opened in "write", "append", or "read/write" mode
1828 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1830 char real_path [MAX_OSPATH];
1832 // Open the file on disk directly
1833 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1835 // Create directories up to the file
1836 FS_CreatePath (real_path);
1838 return FS_SysOpen (real_path, mode, nonblocking);
1840 // Else, we look at the various search paths and open the file in read-only mode
1842 return FS_OpenReadFile (filepath, quiet, nonblocking);
1847 ====================
1851 ====================
1853 int FS_Close (qfile_t* file)
1855 if (close (file->handle))
1860 qz_inflateEnd (&file->ztk->zstream);
1861 Mem_Free (file->ztk);
1870 ====================
1873 Write "datasize" bytes into a file
1874 ====================
1876 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1880 // If necessary, seek to the exact file position we're supposed to be
1881 if (file->buff_ind != file->buff_len)
1882 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1884 // Purge cached data
1887 // Write the buffer and update the position
1888 result = write (file->handle, data, (fs_offset_t)datasize);
1889 file->position = lseek (file->handle, 0, SEEK_CUR);
1890 if (file->real_length < file->position)
1891 file->real_length = file->position;
1901 ====================
1904 Read up to "buffersize" bytes from a file
1905 ====================
1907 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1909 fs_offset_t count, done;
1911 if (buffersize == 0)
1914 // Get rid of the ungetc character
1915 if (file->ungetc != EOF)
1917 ((char*)buffer)[0] = file->ungetc;
1925 // First, we copy as many bytes as we can from "buff"
1926 if (file->buff_ind < file->buff_len)
1928 count = file->buff_len - file->buff_ind;
1929 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1931 memcpy (buffer, &file->buff[file->buff_ind], count);
1932 file->buff_ind += count;
1934 buffersize -= count;
1935 if (buffersize == 0)
1939 // NOTE: at this point, the read buffer is always empty
1941 // If the file isn't compressed
1942 if (! (file->flags & QFILE_FLAG_DEFLATED))
1946 // We must take care to not read after the end of the file
1947 count = file->real_length - file->position;
1949 // If we have a lot of data to get, put them directly into "buffer"
1950 if (buffersize > sizeof (file->buff) / 2)
1952 if (count > (fs_offset_t)buffersize)
1953 count = (fs_offset_t)buffersize;
1954 lseek (file->handle, file->offset + file->position, SEEK_SET);
1955 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1959 file->position += nb;
1961 // Purge cached data
1967 if (count > (fs_offset_t)sizeof (file->buff))
1968 count = (fs_offset_t)sizeof (file->buff);
1969 lseek (file->handle, file->offset + file->position, SEEK_SET);
1970 nb = read (file->handle, file->buff, count);
1973 file->buff_len = nb;
1974 file->position += nb;
1976 // Copy the requested data in "buffer" (as much as we can)
1977 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1978 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1979 file->buff_ind = count;
1987 // If the file is compressed, it's more complicated...
1988 // We cycle through a few operations until we have read enough data
1989 while (buffersize > 0)
1991 ztoolkit_t *ztk = file->ztk;
1994 // NOTE: at this point, the read buffer is always empty
1996 // If "input" is also empty, we need to refill it
1997 if (ztk->in_ind == ztk->in_len)
1999 // If we are at the end of the file
2000 if (file->position == file->real_length)
2003 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2004 if (count > (fs_offset_t)sizeof (ztk->input))
2005 count = (fs_offset_t)sizeof (ztk->input);
2006 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2007 if (read (file->handle, ztk->input, count) != count)
2009 Con_Printf ("FS_Read: unexpected end of file\n");
2014 ztk->in_len = count;
2015 ztk->in_position += count;
2018 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2019 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2021 // Now that we are sure we have compressed data available, we need to determine
2022 // if it's better to inflate it in "file->buff" or directly in "buffer"
2024 // Inflate the data in "file->buff"
2025 if (buffersize < sizeof (file->buff) / 2)
2027 ztk->zstream.next_out = file->buff;
2028 ztk->zstream.avail_out = sizeof (file->buff);
2029 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2030 if (error != Z_OK && error != Z_STREAM_END)
2032 Con_Printf ("FS_Read: Can't inflate file\n");
2035 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2037 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2038 file->position += file->buff_len;
2040 // Copy the requested data in "buffer" (as much as we can)
2041 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2042 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2043 file->buff_ind = count;
2046 // Else, we inflate directly in "buffer"
2049 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2050 ztk->zstream.avail_out = (unsigned int)buffersize;
2051 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2052 if (error != Z_OK && error != Z_STREAM_END)
2054 Con_Printf ("FS_Read: Can't inflate file\n");
2057 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2059 // How much data did it inflate?
2060 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2061 file->position += count;
2063 // Purge cached data
2068 buffersize -= count;
2076 ====================
2079 Print a string into a file
2080 ====================
2082 int FS_Print (qfile_t* file, const char *msg)
2084 return (int)FS_Write (file, msg, strlen (msg));
2088 ====================
2091 Print a string into a file
2092 ====================
2094 int FS_Printf(qfile_t* file, const char* format, ...)
2099 va_start (args, format);
2100 result = FS_VPrintf (file, format, args);
2108 ====================
2111 Print a string into a file
2112 ====================
2114 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2117 fs_offset_t buff_size = MAX_INPUTLINE;
2122 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2123 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2124 if (len >= 0 && len < buff_size)
2126 Mem_Free (tempbuff);
2130 len = write (file->handle, tempbuff, len);
2131 Mem_Free (tempbuff);
2138 ====================
2141 Get the next character of a file
2142 ====================
2144 int FS_Getc (qfile_t* file)
2148 if (FS_Read (file, &c, 1) != 1)
2156 ====================
2159 Put a character back into the read buffer (only supports one character!)
2160 ====================
2162 int FS_UnGetc (qfile_t* file, unsigned char c)
2164 // If there's already a character waiting to be read
2165 if (file->ungetc != EOF)
2174 ====================
2177 Move the position index in a file
2178 ====================
2180 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2183 unsigned char* buffer;
2184 fs_offset_t buffersize;
2186 // Compute the file offset
2190 offset += file->position - file->buff_len + file->buff_ind;
2197 offset += file->real_length;
2203 if (offset < 0 || offset > file->real_length)
2206 // If we have the data in our read buffer, we don't need to actually seek
2207 if (file->position - file->buff_len <= offset && offset <= file->position)
2209 file->buff_ind = offset + file->buff_len - file->position;
2213 // Purge cached data
2216 // Unpacked or uncompressed files can seek directly
2217 if (! (file->flags & QFILE_FLAG_DEFLATED))
2219 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2221 file->position = offset;
2225 // Seeking in compressed files is more a hack than anything else,
2226 // but we need to support it, so here we go.
2229 // If we have to go back in the file, we need to restart from the beginning
2230 if (offset <= file->position)
2234 ztk->in_position = 0;
2236 lseek (file->handle, file->offset, SEEK_SET);
2238 // Reset the Zlib stream
2239 ztk->zstream.next_in = ztk->input;
2240 ztk->zstream.avail_in = 0;
2241 qz_inflateReset (&ztk->zstream);
2244 // We need a big buffer to force inflating into it directly
2245 buffersize = 2 * sizeof (file->buff);
2246 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2248 // Skip all data until we reach the requested offset
2249 while (offset > file->position)
2251 fs_offset_t diff = offset - file->position;
2252 fs_offset_t count, len;
2254 count = (diff > buffersize) ? buffersize : diff;
2255 len = FS_Read (file, buffer, count);
2269 ====================
2272 Give the current position in a file
2273 ====================
2275 fs_offset_t FS_Tell (qfile_t* file)
2277 return file->position - file->buff_len + file->buff_ind;
2282 ====================
2285 Give the total size of a file
2286 ====================
2288 fs_offset_t FS_FileSize (qfile_t* file)
2290 return file->real_length;
2295 ====================
2298 Erases any buffered input or output data
2299 ====================
2301 void FS_Purge (qfile_t* file)
2313 Filename are relative to the quake directory.
2314 Always appends a 0 byte.
2317 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2320 unsigned char *buf = NULL;
2321 fs_offset_t filesize = 0;
2323 file = FS_Open (path, "rb", quiet, false);
2326 filesize = file->real_length;
2327 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2328 buf[filesize] = '\0';
2329 FS_Read (file, buf, filesize);
2333 if (filesizepointer)
2334 *filesizepointer = filesize;
2343 The filename will be prefixed by the current game directory
2346 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2350 file = FS_Open (filename, "wb", false, false);
2353 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2357 Con_DPrintf("FS_WriteFile: %s\n", filename);
2358 FS_Write (file, data, len);
2365 =============================================================================
2367 OTHERS PUBLIC FUNCTIONS
2369 =============================================================================
2377 void FS_StripExtension (const char *in, char *out, size_t size_out)
2385 while ((currentchar = *in) && size_out > 1)
2387 if (currentchar == '.')
2389 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2391 *out++ = currentchar;
2407 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2411 // if path doesn't have a .EXT, append extension
2412 // (extension should include the .)
2413 src = path + strlen(path) - 1;
2415 while (*src != '/' && src != path)
2418 return; // it has an extension
2422 strlcat (path, extension, size_path);
2430 Look for a file in the packages and in the filesystem
2433 qboolean FS_FileExists (const char *filename)
2435 return (FS_FindFile (filename, NULL, true) != NULL);
2443 Look for a file in the filesystem only
2446 qboolean FS_SysFileExists (const char *path)
2451 // TODO: use another function instead, to avoid opening the file
2452 desc = open (path, O_RDONLY | O_BINARY);
2461 if (stat (path,&buf) == -1)
2468 void FS_mkdir (const char *path)
2481 Allocate and fill a search structure with information on matching filenames.
2484 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2487 searchpath_t *searchpath;
2489 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2490 stringlist_t resultlist;
2491 stringlist_t dirlist;
2492 const char *slash, *backslash, *colon, *separator;
2494 char netpath[MAX_OSPATH];
2495 char temp[MAX_OSPATH];
2497 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2502 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2506 stringlistinit(&resultlist);
2507 stringlistinit(&dirlist);
2509 slash = strrchr(pattern, '/');
2510 backslash = strrchr(pattern, '\\');
2511 colon = strrchr(pattern, ':');
2512 separator = max(slash, backslash);
2513 separator = max(separator, colon);
2514 basepathlength = separator ? (separator + 1 - pattern) : 0;
2515 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2517 memcpy(basepath, pattern, basepathlength);
2518 basepath[basepathlength] = 0;
2520 // search through the path, one element at a time
2521 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2523 // is the element a pak file?
2524 if (searchpath->pack)
2526 // look through all the pak file elements
2527 pak = searchpath->pack;
2528 for (i = 0;i < pak->numfiles;i++)
2530 strlcpy(temp, pak->files[i].name, sizeof(temp));
2533 if (matchpattern(temp, (char *)pattern, true))
2535 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2536 if (!strcmp(resultlist.strings[resultlistindex], temp))
2538 if (resultlistindex == resultlist.numstrings)
2540 stringlistappend(&resultlist, temp);
2542 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2545 // strip off one path element at a time until empty
2546 // this way directories are added to the listing if they match the pattern
2547 slash = strrchr(temp, '/');
2548 backslash = strrchr(temp, '\\');
2549 colon = strrchr(temp, ':');
2551 if (separator < slash)
2553 if (separator < backslash)
2554 separator = backslash;
2555 if (separator < colon)
2557 *((char *)separator) = 0;
2563 // get a directory listing and look at each name
2564 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2565 stringlistinit(&dirlist);
2566 listdirectory(&dirlist, netpath);
2567 for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
2569 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
2570 if (matchpattern(temp, (char *)pattern, true))
2572 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2573 if (!strcmp(resultlist.strings[resultlistindex], temp))
2575 if (resultlistindex == resultlist.numstrings)
2577 stringlistappend(&resultlist, temp);
2579 Con_DPrintf("SearchDirFile: %s\n", temp);
2583 stringlistfreecontents(&dirlist);
2587 if (resultlist.numstrings)
2589 stringlistsort(&resultlist);
2590 numfiles = resultlist.numstrings;
2592 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2593 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2594 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2595 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2596 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2597 search->numfilenames = (int)numfiles;
2600 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2603 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2604 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2605 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2607 numchars += (int)textlen;
2610 stringlistfreecontents(&resultlist);
2616 void FS_FreeSearch(fssearch_t *search)
2621 extern int con_linewidth;
2622 int FS_ListDirectory(const char *pattern, int oneperline)
2631 char linebuf[MAX_INPUTLINE];
2633 search = FS_Search(pattern, true, true);
2636 numfiles = search->numfilenames;
2639 // FIXME: the names could be added to one column list and then
2640 // gradually shifted into the next column if they fit, and then the
2641 // next to make a compact variable width listing but it's a lot more
2643 // find width for columns
2645 for (i = 0;i < numfiles;i++)
2647 l = (int)strlen(search->filenames[i]);
2648 if (columnwidth < l)
2651 // count the spacing character
2653 // calculate number of columns
2654 numcolumns = con_linewidth / columnwidth;
2655 // don't bother with the column printing if it's only one column
2656 if (numcolumns >= 2)
2658 numlines = (numfiles + numcolumns - 1) / numcolumns;
2659 for (i = 0;i < numlines;i++)
2662 for (k = 0;k < numcolumns;k++)
2664 l = i * numcolumns + k;
2667 name = search->filenames[l];
2668 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2669 linebuf[linebufpos++] = name[j];
2670 // space out name unless it's the last on the line
2671 if (k + 1 < numcolumns && l + 1 < numfiles)
2672 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2673 linebuf[linebufpos++] = ' ';
2676 linebuf[linebufpos] = 0;
2677 Con_Printf("%s\n", linebuf);
2684 for (i = 0;i < numfiles;i++)
2685 Con_Printf("%s\n", search->filenames[i]);
2686 FS_FreeSearch(search);
2687 return (int)numfiles;
2690 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2692 const char *pattern;
2695 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2698 if (Cmd_Argc() == 2)
2699 pattern = Cmd_Argv(1);
2702 if (!FS_ListDirectory(pattern, oneperline))
2703 Con_Print("No files found.\n");
2708 FS_ListDirectoryCmd("dir", true);
2713 FS_ListDirectoryCmd("ls", false);
2716 const char *FS_WhichPack(const char *filename)
2719 searchpath_t *sp = FS_FindFile(filename, &index, true);
2721 return sp->pack->filename;
2727 ====================
2728 FS_IsRegisteredQuakePack
2730 Look for a proof of purchase file file in the requested package
2732 If it is found, this file should NOT be downloaded.
2733 ====================
2735 qboolean FS_IsRegisteredQuakePack(const char *name)
2737 searchpath_t *search;
2740 // search through the path, one element at a time
2741 for (search = fs_searchpaths;search;search = search->next)
2743 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2745 int (*strcmp_funct) (const char* str1, const char* str2);
2746 int left, right, middle;
2749 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2751 // Look for the file (binary search)
2753 right = pak->numfiles - 1;
2754 while (left <= right)
2758 middle = (left + right) / 2;
2759 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2765 // If we're too far in the list
2772 // we found the requested pack but it is not registered quake
2780 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2783 unsigned char *filedata;
2784 fs_offset_t filesize;
2785 if (filesizepointer)
2786 *filesizepointer = 0;
2787 if (!filename || !*filename)
2789 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2792 if (filesizepointer)
2793 *filesizepointer = filesize;
2794 crc = CRC_Block(filedata, filesize);