4 Copyright (C) 2003-2006 Mathieu Olivier
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to:
20 Free Software Foundation, Inc.
21 59 Temple Place - Suite 330
22 Boston, MA 02111-1307, USA
36 # include <sys/stat.h>
43 // Win32 requires us to add O_BINARY, but the other OSes don't have it
48 // In case the system doesn't support the O_NONBLOCK flag
53 // largefile support for Win32
55 # define lseek _lseeki64
60 All of Quake's data access is through a hierchal file system, but the contents
61 of the file system can be transparently merged from several sources.
63 The "base directory" is the path to the directory holding the quake.exe and
64 all game directories. The sys_* files pass this to host_init in
65 quakeparms_t->basedir. This can be overridden with the "-basedir" command
66 line parm to allow code debugging in a different directory. The base
67 directory is only used during filesystem initialization.
69 The "game directory" is the first tree on the search path and directory that
70 all generated files (savegames, screenshots, demos, config files) will be
71 saved to. This can be overridden with the "-game" command line parameter.
72 The game directory can never be changed while quake is executing. This is a
73 precaution against having a malicious server instruct clients to write files
74 over areas they shouldn't.
80 =============================================================================
84 =============================================================================
87 // Magic numbers of a ZIP file (big-endian format)
88 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
89 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
90 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
92 // Other constants for ZIP files
93 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
94 #define ZIP_END_CDIR_SIZE 22
95 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
96 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
98 // Zlib constants (from zlib.h)
99 #define Z_SYNC_FLUSH 2
102 #define Z_STREAM_END 1
103 #define ZLIB_VERSION "1.2.3"
105 // Uncomment the following line if the zlib DLL you have still uses
106 // the 1.1.x series calling convention on Win32 (WINAPI)
107 //#define ZLIB_USES_WINAPI
111 =============================================================================
115 =============================================================================
118 // Zlib stream (from zlib.h)
119 // Warning: some pointers we don't use directly have
120 // been cast to "void*" for a matter of simplicity
123 unsigned char *next_in; // next input byte
124 unsigned int avail_in; // number of bytes available at next_in
125 unsigned long total_in; // total nb of input bytes read so far
127 unsigned char *next_out; // next output byte should be put there
128 unsigned int avail_out; // remaining free space at next_out
129 unsigned long total_out; // total nb of bytes output so far
131 char *msg; // last error message, NULL if no error
132 void *state; // not visible by applications
134 void *zalloc; // used to allocate the internal state
135 void *zfree; // used to free the internal state
136 void *opaque; // private data object passed to zalloc and zfree
138 int data_type; // best guess about the data type: ascii or binary
139 unsigned long adler; // adler32 value of the uncompressed data
140 unsigned long reserved; // reserved for future use
144 // inside a package (PAK or PK3)
145 #define QFILE_FLAG_PACKED (1 << 0)
146 // file is compressed using the deflate algorithm (PK3 only)
147 #define QFILE_FLAG_DEFLATED (1 << 1)
149 #define FILE_BUFF_SIZE 2048
153 size_t comp_length; // length of the compressed file
154 size_t in_ind, in_len; // input buffer current index and length
155 size_t in_position; // position in the compressed file
156 unsigned char input [FILE_BUFF_SIZE];
162 int handle; // file descriptor
163 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
164 fs_offset_t position; // current position in the file
165 fs_offset_t offset; // offset into the package (0 if external file)
166 int ungetc; // single stored character from ungetc, cleared to EOF when read
169 fs_offset_t buff_ind, buff_len; // buffer current index and length
170 unsigned char buff [FILE_BUFF_SIZE];
177 // ------ PK3 files on disk ------ //
179 // You can get the complete ZIP format description from PKWARE website
181 typedef struct pk3_endOfCentralDir_s
183 unsigned int signature;
184 unsigned short disknum;
185 unsigned short cdir_disknum; // number of the disk with the start of the central directory
186 unsigned short localentries; // number of entries in the central directory on this disk
187 unsigned short nbentries; // total number of entries in the central directory on this disk
188 unsigned int cdir_size; // size of the central directory
189 unsigned int cdir_offset; // with respect to the starting disk number
190 unsigned short comment_size;
191 } pk3_endOfCentralDir_t;
194 // ------ PAK files on disk ------ //
195 typedef struct dpackfile_s
198 int filepos, filelen;
201 typedef struct dpackheader_s
209 // Packages in memory
210 // the offset in packfile_t is the true contents offset
211 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
212 // file compressed using the deflate algorithm
213 #define PACKFILE_FLAG_DEFLATED (1 << 1)
215 typedef struct packfile_s
217 char name [MAX_QPATH];
220 fs_offset_t packsize; // size in the package
221 fs_offset_t realsize; // real file size (uncompressed)
224 typedef struct pack_s
226 char filename [MAX_OSPATH];
228 int ignorecase; // PK3 ignores case
234 // Search paths for files (including packages)
235 typedef struct searchpath_s
237 // only one of filename / pack will be used
238 char filename[MAX_OSPATH];
240 struct searchpath_s *next;
245 =============================================================================
249 =============================================================================
255 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
256 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
257 fs_offset_t offset, fs_offset_t packsize,
258 fs_offset_t realsize, int flags);
262 =============================================================================
266 =============================================================================
269 mempool_t *fs_mempool;
271 searchpath_t *fs_searchpaths = NULL;
273 #define MAX_FILES_IN_PACK 65536
275 char fs_gamedir[MAX_OSPATH];
276 char fs_basedir[MAX_OSPATH];
278 // list of active game directories (empty if not running a mod)
279 int fs_numgamedirs = 0;
280 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
282 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running)"};
283 cvar_t fs_empty_files_in_pack_mark_deletions = {0, "fs_empty_files_in_pack_mark_deletions", "0", "if enabled, empty files in a pak/pk3 count as not existing but cancel the search in further packs, effectively allowing patch pak/pk3 files to 'delete' files"};
287 =============================================================================
289 PRIVATE FUNCTIONS - PK3 HANDLING
291 =============================================================================
294 // Functions exported from zlib
295 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
296 # define ZEXPORT WINAPI
301 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
302 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
303 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
304 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
306 #define qz_inflateInit2(strm, windowBits) \
307 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
309 static dllfunction_t zlibfuncs[] =
311 {"inflate", (void **) &qz_inflate},
312 {"inflateEnd", (void **) &qz_inflateEnd},
313 {"inflateInit2_", (void **) &qz_inflateInit2_},
314 {"inflateReset", (void **) &qz_inflateReset},
318 // Handle for Zlib DLL
319 static dllhandle_t zlib_dll = NULL;
329 void PK3_CloseLibrary (void)
331 Sys_UnloadLibrary (&zlib_dll);
339 Try to load the Zlib DLL
342 qboolean PK3_OpenLibrary (void)
344 const char* dllnames [] =
349 # ifdef ZLIB_USES_WINAPI
355 #elif defined(MACOSX)
369 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
371 Con_Printf ("Compressed files support disabled\n");
375 Con_Printf ("Compressed files support enabled\n");
382 PK3_GetEndOfCentralDir
384 Extract the end of the central directory from a PK3 package
387 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
389 fs_offset_t filesize, maxsize;
390 unsigned char *buffer, *ptr;
393 // Get the package size
394 filesize = lseek (packhandle, 0, SEEK_END);
395 if (filesize < ZIP_END_CDIR_SIZE)
398 // Load the end of the file in memory
399 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
402 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
403 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
404 lseek (packhandle, filesize - maxsize, SEEK_SET);
405 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
411 // Look for the end of central dir signature around the end of the file
412 maxsize -= ZIP_END_CDIR_SIZE;
413 ptr = &buffer[maxsize];
415 while (BuffBigLong (ptr) != ZIP_END_HEADER)
427 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
428 eocd->signature = LittleLong (eocd->signature);
429 eocd->disknum = LittleShort (eocd->disknum);
430 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
431 eocd->localentries = LittleShort (eocd->localentries);
432 eocd->nbentries = LittleShort (eocd->nbentries);
433 eocd->cdir_size = LittleLong (eocd->cdir_size);
434 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
435 eocd->comment_size = LittleShort (eocd->comment_size);
447 Extract the file list from a PK3 file
450 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
452 unsigned char *central_dir, *ptr;
454 fs_offset_t remaining;
456 // Load the central directory in memory
457 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
458 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
459 read (pack->handle, central_dir, eocd->cdir_size);
461 // Extract the files properties
462 // The parsing is done "by hand" because some fields have variable sizes and
463 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
464 remaining = eocd->cdir_size;
467 for (ind = 0; ind < eocd->nbentries; ind++)
469 fs_offset_t namesize, count;
471 // Checking the remaining size
472 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
474 Mem_Free (central_dir);
477 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
480 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
482 Mem_Free (central_dir);
486 namesize = BuffLittleShort (&ptr[28]); // filename length
488 // Check encryption, compression, and attributes
489 // 1st uint8 : general purpose bit flag
490 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
491 // 2nd uint8 : external file attributes
492 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
493 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
495 // Still enough bytes for the name?
496 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
498 Mem_Free (central_dir);
502 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
503 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
505 char filename [sizeof (pack->files[0].name)];
506 fs_offset_t offset, packsize, realsize;
509 // Extract the name (strip it if necessary)
510 namesize = min(namesize, (int)sizeof (filename) - 1);
511 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
512 filename[namesize] = '\0';
514 if (BuffLittleShort (&ptr[10]))
515 flags = PACKFILE_FLAG_DEFLATED;
518 offset = BuffLittleLong (&ptr[42]);
519 packsize = BuffLittleLong (&ptr[20]);
520 realsize = BuffLittleLong (&ptr[24]);
521 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
525 // Skip the name, additionnal field, and comment
526 // 1er uint16 : extra field length
527 // 2eme uint16 : file comment length
528 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
529 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
533 // If the package is empty, central_dir is NULL here
534 if (central_dir != NULL)
535 Mem_Free (central_dir);
536 return pack->numfiles;
544 Create a package entry associated with a PK3 file
547 pack_t *FS_LoadPackPK3 (const char *packfile)
550 pk3_endOfCentralDir_t eocd;
554 packhandle = open (packfile, O_RDONLY | O_BINARY);
558 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
560 Con_Printf ("%s is not a PK3 file\n", packfile);
565 // Multi-volume ZIP archives are NOT allowed
566 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
568 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
573 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
574 // since eocd.nbentries is an unsigned 16 bits integer
575 #if MAX_FILES_IN_PACK < 65535
576 if (eocd.nbentries > MAX_FILES_IN_PACK)
578 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
584 // Create a package structure in memory
585 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
586 pack->ignorecase = true; // PK3 ignores case
587 strlcpy (pack->filename, packfile, sizeof (pack->filename));
588 pack->handle = packhandle;
589 pack->numfiles = eocd.nbentries;
590 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
592 real_nb_files = PK3_BuildFileList (pack, &eocd);
593 if (real_nb_files < 0)
595 Con_Printf ("%s is not a valid PK3 file\n", packfile);
601 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
608 PK3_GetTrueFileOffset
610 Find where the true file data offset is
613 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
615 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
619 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
622 // Load the local file description
623 lseek (pack->handle, pfile->offset, SEEK_SET);
624 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
625 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
627 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
631 // Skip name and extra field
632 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
634 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
640 =============================================================================
642 OTHER PRIVATE FUNCTIONS
644 =============================================================================
652 Add a file to the list of files contained into a package
655 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
656 fs_offset_t offset, fs_offset_t packsize,
657 fs_offset_t realsize, int flags)
659 int (*strcmp_funct) (const char* str1, const char* str2);
660 int left, right, middle;
663 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
665 // Look for the slot we should put that file into (binary search)
667 right = pack->numfiles - 1;
668 while (left <= right)
672 middle = (left + right) / 2;
673 diff = strcmp_funct (pack->files[middle].name, name);
675 // If we found the file, there's a problem
677 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
679 // If we're too far in the list
686 // We have to move the right of the list by one slot to free the one we need
687 pfile = &pack->files[left];
688 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
691 strlcpy (pfile->name, name, sizeof (pfile->name));
692 pfile->offset = offset;
693 pfile->packsize = packsize;
694 pfile->realsize = realsize;
695 pfile->flags = flags;
705 Only used for FS_Open.
708 void FS_CreatePath (char *path)
712 for (ofs = path+1 ; *ofs ; ofs++)
714 if (*ofs == '/' || *ofs == '\\')
716 // create the directory
732 void FS_Path_f (void)
736 Con_Print("Current search path:\n");
737 for (s=fs_searchpaths ; s ; s=s->next)
740 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
742 Con_Printf("%s\n", s->filename);
751 Takes an explicit (not game tree related) path to a pak file.
753 Loads the header and directory, adding the files at the beginning
754 of the list so they override previous pack files.
757 pack_t *FS_LoadPackPAK (const char *packfile)
759 dpackheader_t header;
765 packhandle = open (packfile, O_RDONLY | O_BINARY);
768 read (packhandle, (void *)&header, sizeof(header));
769 if (memcmp(header.id, "PACK", 4))
771 Con_Printf ("%s is not a packfile\n", packfile);
775 header.dirofs = LittleLong (header.dirofs);
776 header.dirlen = LittleLong (header.dirlen);
778 if (header.dirlen % sizeof(dpackfile_t))
780 Con_Printf ("%s has an invalid directory size\n", packfile);
785 numpackfiles = header.dirlen / sizeof(dpackfile_t);
787 if (numpackfiles > MAX_FILES_IN_PACK)
789 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
794 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
795 lseek (packhandle, header.dirofs, SEEK_SET);
796 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
798 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
804 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
805 pack->ignorecase = false; // PAK is case sensitive
806 strlcpy (pack->filename, packfile, sizeof (pack->filename));
807 pack->handle = packhandle;
809 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
811 // parse the directory
812 for (i = 0;i < numpackfiles;i++)
814 fs_offset_t offset = LittleLong (info[i].filepos);
815 fs_offset_t size = LittleLong (info[i].filelen);
817 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
822 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
830 Adds the given pack to the search path.
831 The pack type is autodetected by the file extension.
833 Returns true if the file was successfully added to the
834 search path or if it was already included.
836 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
840 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
842 searchpath_t *search;
844 const char *ext = FS_FileExtension(pakfile);
846 for(search = fs_searchpaths; search; search = search->next)
848 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
851 *already_loaded = true;
852 return true; // already loaded
857 *already_loaded = false;
859 if(!strcasecmp(ext, "pak"))
860 pak = FS_LoadPackPAK (pakfile);
861 else if(!strcasecmp(ext, "pk3"))
862 pak = FS_LoadPackPK3 (pakfile);
864 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
870 // find the first item whose next one is a pack or NULL
871 searchpath_t *insertion_point = 0;
872 if(fs_searchpaths && !fs_searchpaths->pack)
874 insertion_point = fs_searchpaths;
877 if(!insertion_point->next)
879 if(insertion_point->next->pack)
881 insertion_point = insertion_point->next;
884 // If insertion_point is NULL, this means that either there is no
885 // item in the list yet, or that the very first item is a pack. In
886 // that case, we want to insert at the beginning...
889 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
891 search->next = fs_searchpaths;
892 fs_searchpaths = search;
895 // otherwise we want to append directly after insertion_point.
897 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
899 search->next = insertion_point->next;
900 insertion_point->next = search;
905 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
907 search->next = fs_searchpaths;
908 fs_searchpaths = search;
914 Con_Printf("unable to load pak \"%s\"\n", pakfile);
924 Adds the given pack to the search path and searches for it in the game path.
925 The pack type is autodetected by the file extension.
927 Returns true if the file was successfully added to the
928 search path or if it was already included.
930 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
934 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
936 char fullpath[MAX_QPATH];
938 searchpath_t *search;
941 *already_loaded = false;
943 // then find the real name...
944 search = FS_FindFile(pakfile, &index, true);
945 if(!search || search->pack)
947 Con_Printf("could not find pak \"%s\"\n", pakfile);
951 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
953 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
961 Sets fs_gamedir, adds the directory to the head of the path,
962 then loads and adds pak1.pak pak2.pak ...
965 void FS_AddGameDirectory (const char *dir)
969 searchpath_t *search;
970 char pakfile[MAX_OSPATH];
972 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
974 stringlistinit(&list);
975 listdirectory(&list, dir);
976 stringlistsort(&list);
978 // add any PAK package in the directory
979 for (i = 0;i < list.numstrings;i++)
981 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
983 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
984 FS_AddPack_Fullpath(pakfile, NULL, false);
988 // add any PK3 package in the directory
989 for (i = 0;i < list.numstrings;i++)
991 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
993 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
994 FS_AddPack_Fullpath(pakfile, NULL, false);
998 stringlistfreecontents(&list);
1000 // Add the directory to the search path
1001 // (unpacked files have the priority over packed files)
1002 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1003 strlcpy (search->filename, dir, sizeof (search->filename));
1004 search->next = fs_searchpaths;
1005 fs_searchpaths = search;
1014 void FS_AddGameHierarchy (const char *dir)
1017 char userdir[MAX_QPATH];
1019 TCHAR mydocsdir[MAX_PATH + 1];
1021 const char *homedir;
1024 // Add the common game directory
1025 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1029 // Add the personal game directory
1031 if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
1032 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1033 fprintf(stderr, "userdir = %s\n", userdir);
1035 homedir = getenv ("HOME");
1037 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1041 if(!COM_CheckParm("-mygames"))
1043 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1047 *userdir = 0; // we have write access to the game dir, so let's use it
1052 if(COM_CheckParm("-nohome"))
1055 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1056 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1059 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1068 const char *FS_FileExtension (const char *in)
1070 const char *separator, *backslash, *colon, *dot;
1072 separator = strrchr(in, '/');
1073 backslash = strrchr(in, '\\');
1074 if (!separator || separator < backslash)
1075 separator = backslash;
1076 colon = strrchr(in, ':');
1077 if (!separator || separator < colon)
1080 dot = strrchr(in, '.');
1081 if (dot == NULL || (separator && (dot < separator)))
1093 const char *FS_FileWithoutPath (const char *in)
1095 const char *separator, *backslash, *colon;
1097 separator = strrchr(in, '/');
1098 backslash = strrchr(in, '\\');
1099 if (!separator || separator < backslash)
1100 separator = backslash;
1101 colon = strrchr(in, ':');
1102 if (!separator || separator < colon)
1104 return separator ? separator + 1 : in;
1113 void FS_ClearSearchPath (void)
1115 // unload all packs and directory information, close all pack files
1116 // (if a qfile is still reading a pack it won't be harmed because it used
1117 // dup() to get its own handle already)
1118 while (fs_searchpaths)
1120 searchpath_t *search = fs_searchpaths;
1121 fs_searchpaths = search->next;
1125 close(search->pack->handle);
1126 // free any memory associated with it
1127 if (search->pack->files)
1128 Mem_Free(search->pack->files);
1129 Mem_Free(search->pack);
1141 void FS_Rescan (void)
1144 qboolean fs_modified = false;
1146 FS_ClearSearchPath();
1148 // add the game-specific paths
1149 // gamedirname1 (typically id1)
1150 FS_AddGameHierarchy (gamedirname1);
1151 // update the com_modname (used for server info)
1152 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1154 // add the game-specific path, if any
1155 // (only used for mission packs and the like, which should set fs_modified)
1159 FS_AddGameHierarchy (gamedirname2);
1163 // Adds basedir/gamedir as an override game
1164 // LordHavoc: now supports multiple -game directories
1165 // set the com_modname (reported in server info)
1166 for (i = 0;i < fs_numgamedirs;i++)
1169 FS_AddGameHierarchy (fs_gamedirs[i]);
1170 // update the com_modname (used server info)
1171 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1174 // set the default screenshot name to either the mod name or the
1175 // gamemode screenshot name
1176 if (strcmp(com_modname, gamedirname1))
1177 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1179 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1181 // If "-condebug" is in the command line, remove the previous log file
1182 if (COM_CheckParm ("-condebug") != 0)
1183 unlink (va("%s/qconsole.log", fs_gamedir));
1185 // look for the pop.lmp file and set registered to true if it is found
1186 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1189 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1191 Con_Print("Playing shareware version.\n");
1195 Cvar_Set ("registered", "1");
1196 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1197 Con_Print("Playing registered version.\n");
1200 // unload all wads so that future queries will return the new data
1204 void FS_Rescan_f(void)
1214 extern void Host_SaveConfig (void);
1215 extern void Host_LoadConfig_f (void);
1216 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1220 if (fs_numgamedirs == numgamedirs)
1222 for (i = 0;i < numgamedirs;i++)
1223 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1225 if (i == numgamedirs)
1226 return true; // already using this set of gamedirs, do nothing
1229 if (numgamedirs > MAX_GAMEDIRS)
1232 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1233 return false; // too many gamedirs
1236 for (i = 0;i < numgamedirs;i++)
1238 // if string is nasty, reject it
1239 if(FS_CheckNastyPath(gamedirs[i], true))
1242 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1243 return false; // nasty gamedirs
1247 for (i = 0;i < numgamedirs;i++)
1249 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1252 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1253 return false; // missing gamedirs
1257 // halt demo playback to close the file
1262 fs_numgamedirs = numgamedirs;
1263 for (i = 0;i < fs_numgamedirs;i++)
1264 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1266 // reinitialize filesystem to detect the new paks
1269 // exec the new config
1270 Host_LoadConfig_f();
1272 // unload all sounds so they will be reloaded from the new files as needed
1273 S_UnloadAllSounds_f();
1275 // reinitialize renderer (this reloads hud/console background/etc)
1276 R_Modules_Restart();
1286 void FS_GameDir_f (void)
1290 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1294 Con_Printf("gamedirs active:");
1295 for (i = 0;i < fs_numgamedirs;i++)
1296 Con_Printf(" %s", fs_gamedirs[i]);
1301 numgamedirs = Cmd_Argc() - 1;
1302 if (numgamedirs > MAX_GAMEDIRS)
1304 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1308 for (i = 0;i < numgamedirs;i++)
1309 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1311 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1313 // actually, changing during game would work fine, but would be stupid
1314 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1318 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1327 qboolean FS_CheckGameDir(const char *gamedir)
1331 stringlistinit(&list);
1332 listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
1333 success = list.numstrings > 0;
1334 stringlistfreecontents(&list);
1348 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1350 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1352 // If the base directory is explicitly defined by the compilation process
1353 #ifdef DP_FS_BASEDIR
1354 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1356 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1359 // FIXME: is there a better way to find the directory outside the .app?
1360 if (strstr(com_argv[0], ".app/"))
1364 split = strstr(com_argv[0], ".app/");
1365 while (split > com_argv[0] && *split != '/')
1367 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1368 fs_basedir[split - com_argv[0]] = 0;
1376 // Overrides the system supplied base directory (under GAMENAME)
1377 // COMMANDLINEOPTION: Filesystem: -basedir <path> chooses what base directory the game data is in, inside this there should be a data directory for the game (for example id1)
1378 i = COM_CheckParm ("-basedir");
1379 if (i && i < com_argc-1)
1381 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1382 i = (int)strlen (fs_basedir);
1383 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1384 fs_basedir[i-1] = 0;
1387 // add a path separator to the end of the basedir if it lacks one
1388 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1389 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1391 if (!FS_CheckGameDir(gamedirname1))
1392 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1394 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1395 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1398 // Adds basedir/gamedir as an override game
1399 // LordHavoc: now supports multiple -game directories
1400 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1404 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1407 if (FS_CheckNastyPath(com_argv[i], true))
1408 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1409 if (!FS_CheckGameDir(com_argv[i]))
1410 Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1411 // add the gamedir to the list of active gamedirs
1412 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1417 // generate the searchpath
1421 void FS_Init_Commands(void)
1423 Cvar_RegisterVariable (&scr_screenshot_name);
1424 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1426 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1427 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1428 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1429 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1430 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1438 void FS_Shutdown (void)
1440 // close all pack files and such
1441 // (hopefully there aren't any other open files, but they'll be cleaned up
1442 // by the OS anyway)
1443 FS_ClearSearchPath();
1444 Mem_FreePool (&fs_mempool);
1448 ====================
1451 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1452 ====================
1454 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1460 // Parse the mode string
1469 opt = O_CREAT | O_TRUNC;
1473 opt = O_CREAT | O_APPEND;
1476 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1479 for (ind = 1; mode[ind] != '\0'; ind++)
1490 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1491 filepath, mode, mode[ind]);
1498 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1499 memset (file, 0, sizeof (*file));
1502 file->handle = open (filepath, mod | opt, 0666);
1503 if (file->handle < 0)
1509 file->real_length = lseek (file->handle, 0, SEEK_END);
1511 // For files opened in append mode, we start at the end of the file
1513 file->position = file->real_length;
1515 lseek (file->handle, 0, SEEK_SET);
1525 Open a packed file using its package file descriptor
1528 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1534 pfile = &pack->files[pack_ind];
1536 // If we don't have the true offset, get it now
1537 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1538 if (!PK3_GetTrueFileOffset (pfile, pack))
1541 // No Zlib DLL = no compressed files
1542 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1544 Con_Printf("WARNING: can't open the compressed file %s\n"
1545 "You need the Zlib DLL to use compressed files\n",
1550 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1551 // the dup() call to avoid having to close the dup_handle on error here
1552 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1554 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1555 pfile->name, pack->filename, (int) pfile->offset);
1559 dup_handle = dup (pack->handle);
1562 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1566 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1567 memset (file, 0, sizeof (*file));
1568 file->handle = dup_handle;
1569 file->flags = QFILE_FLAG_PACKED;
1570 file->real_length = pfile->realsize;
1571 file->offset = pfile->offset;
1575 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1579 file->flags |= QFILE_FLAG_DEFLATED;
1581 // We need some more variables
1582 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1584 ztk->comp_length = pfile->packsize;
1586 // Initialize zlib stream
1587 ztk->zstream.next_in = ztk->input;
1588 ztk->zstream.avail_in = 0;
1590 /* From Zlib's "unzip.c":
1592 * windowBits is passed < 0 to tell that there is no zlib header.
1593 * Note that in this case inflate *requires* an extra "dummy" byte
1594 * after the compressed stream in order to complete decompression and
1595 * return Z_STREAM_END.
1596 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1597 * size of both compressed and uncompressed data
1599 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1601 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1607 ztk->zstream.next_out = file->buff;
1608 ztk->zstream.avail_out = sizeof (file->buff);
1617 ====================
1620 Return true if the path should be rejected due to one of the following:
1621 1: path elements that are non-portable
1622 2: path elements that would allow access to files outside the game directory,
1623 or are just not a good idea for a mod to be using.
1624 ====================
1626 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1628 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1632 // Windows: don't allow \ in filenames (windows-only), period.
1633 // (on Windows \ is a directory separator, but / is also supported)
1634 if (strstr(path, "\\"))
1635 return 1; // non-portable
1637 // Mac: don't allow Mac-only filenames - : is a directory separator
1638 // instead of /, but we rely on / working already, so there's no reason to
1639 // support a Mac-only path
1640 // Amiga and Windows: : tries to go to root of drive
1641 if (strstr(path, ":"))
1642 return 1; // non-portable attempt to go to root of drive
1644 // Amiga: // is parent directory
1645 if (strstr(path, "//"))
1646 return 1; // non-portable attempt to go to parent directory
1648 // all: don't allow going to parent directory (../ or /../)
1649 if (strstr(path, ".."))
1650 return 2; // attempt to go outside the game directory
1652 // Windows and UNIXes: don't allow absolute paths
1654 return 2; // attempt to go outside the game directory
1656 // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements), this catches all imaginable cases of ./, ../, .../, etc
1657 if (strchr(path, '.'))
1661 // gamedir is entirely path elements, so simply forbid . entirely
1664 if (strchr(path, '.') < strrchr(path, '/'))
1665 return 2; // possible attempt to go outside the game directory
1668 // all: forbid trailing slash on gamedir
1669 if (isgamedir && path[strlen(path)-1] == '/')
1672 // all: forbid leading dot on any filename for any reason
1673 if (strstr(path, "/."))
1674 return 2; // attempt to go outside the game directory
1676 // after all these checks we're pretty sure it's a / separated filename
1677 // and won't do much if any harm
1683 ====================
1686 Look for a file in the packages and in the filesystem
1688 Return the searchpath where the file was found (or NULL)
1689 and the file index in the package if relevant
1690 ====================
1692 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1694 searchpath_t *search;
1697 // search through the path, one element at a time
1698 for (search = fs_searchpaths;search;search = search->next)
1700 // is the element a pak file?
1703 int (*strcmp_funct) (const char* str1, const char* str2);
1704 int left, right, middle;
1707 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1709 // Look for the file (binary search)
1711 right = pak->numfiles - 1;
1712 while (left <= right)
1716 middle = (left + right) / 2;
1717 diff = strcmp_funct (pak->files[middle].name, name);
1722 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1724 // yes, but the first one is empty so we treat it as not being there
1725 if (!quiet && developer.integer >= 10)
1726 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1733 if (!quiet && developer.integer >= 10)
1734 Con_Printf("FS_FindFile: %s in %s\n",
1735 pak->files[middle].name, pak->filename);
1742 // If we're too far in the list
1751 char netpath[MAX_OSPATH];
1752 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1753 if (FS_SysFileExists (netpath))
1755 if (!quiet && developer.integer >= 10)
1756 Con_Printf("FS_FindFile: %s\n", netpath);
1765 if (!quiet && developer.integer >= 10)
1766 Con_Printf("FS_FindFile: can't find %s\n", name);
1778 Look for a file in the search paths and open it in read-only mode
1781 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1783 searchpath_t *search;
1786 search = FS_FindFile (filename, &pack_ind, quiet);
1792 // Found in the filesystem?
1795 char path [MAX_OSPATH];
1796 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1797 return FS_SysOpen (path, "rb", nonblocking);
1800 // So, we found it in a package...
1801 return FS_OpenPackedFile (search->pack, pack_ind);
1806 =============================================================================
1808 MAIN PUBLIC FUNCTIONS
1810 =============================================================================
1814 ====================
1817 Open a file. The syntax is the same as fopen
1818 ====================
1820 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1823 char fixedFileName[MAX_QPATH];
1825 strlcpy( fixedFileName, filepath, MAX_QPATH );
1826 // try to fix common mistakes (\ instead of /)
1827 for( d = fixedFileName ; *d ; d++ )
1830 filepath = fixedFileName;
1833 if (FS_CheckNastyPath(filepath, false))
1835 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1839 // If the file is opened in "write", "append", or "read/write" mode
1840 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1842 char real_path [MAX_OSPATH];
1844 // Open the file on disk directly
1845 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1847 // Create directories up to the file
1848 FS_CreatePath (real_path);
1850 return FS_SysOpen (real_path, mode, nonblocking);
1852 // Else, we look at the various search paths and open the file in read-only mode
1854 return FS_OpenReadFile (filepath, quiet, nonblocking);
1859 ====================
1863 ====================
1865 int FS_Close (qfile_t* file)
1867 if (close (file->handle))
1872 qz_inflateEnd (&file->ztk->zstream);
1873 Mem_Free (file->ztk);
1882 ====================
1885 Write "datasize" bytes into a file
1886 ====================
1888 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1892 // If necessary, seek to the exact file position we're supposed to be
1893 if (file->buff_ind != file->buff_len)
1894 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1896 // Purge cached data
1899 // Write the buffer and update the position
1900 result = write (file->handle, data, (fs_offset_t)datasize);
1901 file->position = lseek (file->handle, 0, SEEK_CUR);
1902 if (file->real_length < file->position)
1903 file->real_length = file->position;
1913 ====================
1916 Read up to "buffersize" bytes from a file
1917 ====================
1919 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1921 fs_offset_t count, done;
1923 if (buffersize == 0)
1926 // Get rid of the ungetc character
1927 if (file->ungetc != EOF)
1929 ((char*)buffer)[0] = file->ungetc;
1937 // First, we copy as many bytes as we can from "buff"
1938 if (file->buff_ind < file->buff_len)
1940 count = file->buff_len - file->buff_ind;
1941 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1943 memcpy (buffer, &file->buff[file->buff_ind], count);
1944 file->buff_ind += count;
1946 buffersize -= count;
1947 if (buffersize == 0)
1951 // NOTE: at this point, the read buffer is always empty
1953 // If the file isn't compressed
1954 if (! (file->flags & QFILE_FLAG_DEFLATED))
1958 // We must take care to not read after the end of the file
1959 count = file->real_length - file->position;
1961 // If we have a lot of data to get, put them directly into "buffer"
1962 if (buffersize > sizeof (file->buff) / 2)
1964 if (count > (fs_offset_t)buffersize)
1965 count = (fs_offset_t)buffersize;
1966 lseek (file->handle, file->offset + file->position, SEEK_SET);
1967 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1971 file->position += nb;
1973 // Purge cached data
1979 if (count > (fs_offset_t)sizeof (file->buff))
1980 count = (fs_offset_t)sizeof (file->buff);
1981 lseek (file->handle, file->offset + file->position, SEEK_SET);
1982 nb = read (file->handle, file->buff, count);
1985 file->buff_len = nb;
1986 file->position += nb;
1988 // Copy the requested data in "buffer" (as much as we can)
1989 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1990 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1991 file->buff_ind = count;
1999 // If the file is compressed, it's more complicated...
2000 // We cycle through a few operations until we have read enough data
2001 while (buffersize > 0)
2003 ztoolkit_t *ztk = file->ztk;
2006 // NOTE: at this point, the read buffer is always empty
2008 // If "input" is also empty, we need to refill it
2009 if (ztk->in_ind == ztk->in_len)
2011 // If we are at the end of the file
2012 if (file->position == file->real_length)
2015 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2016 if (count > (fs_offset_t)sizeof (ztk->input))
2017 count = (fs_offset_t)sizeof (ztk->input);
2018 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2019 if (read (file->handle, ztk->input, count) != count)
2021 Con_Printf ("FS_Read: unexpected end of file\n");
2026 ztk->in_len = count;
2027 ztk->in_position += count;
2030 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2031 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2033 // Now that we are sure we have compressed data available, we need to determine
2034 // if it's better to inflate it in "file->buff" or directly in "buffer"
2036 // Inflate the data in "file->buff"
2037 if (buffersize < sizeof (file->buff) / 2)
2039 ztk->zstream.next_out = file->buff;
2040 ztk->zstream.avail_out = sizeof (file->buff);
2041 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2042 if (error != Z_OK && error != Z_STREAM_END)
2044 Con_Printf ("FS_Read: Can't inflate file\n");
2047 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2049 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2050 file->position += file->buff_len;
2052 // Copy the requested data in "buffer" (as much as we can)
2053 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2054 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2055 file->buff_ind = count;
2058 // Else, we inflate directly in "buffer"
2061 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2062 ztk->zstream.avail_out = (unsigned int)buffersize;
2063 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2064 if (error != Z_OK && error != Z_STREAM_END)
2066 Con_Printf ("FS_Read: Can't inflate file\n");
2069 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2071 // How much data did it inflate?
2072 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2073 file->position += count;
2075 // Purge cached data
2080 buffersize -= count;
2088 ====================
2091 Print a string into a file
2092 ====================
2094 int FS_Print (qfile_t* file, const char *msg)
2096 return (int)FS_Write (file, msg, strlen (msg));
2100 ====================
2103 Print a string into a file
2104 ====================
2106 int FS_Printf(qfile_t* file, const char* format, ...)
2111 va_start (args, format);
2112 result = FS_VPrintf (file, format, args);
2120 ====================
2123 Print a string into a file
2124 ====================
2126 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2129 fs_offset_t buff_size = MAX_INPUTLINE;
2134 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2135 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2136 if (len >= 0 && len < buff_size)
2138 Mem_Free (tempbuff);
2142 len = write (file->handle, tempbuff, len);
2143 Mem_Free (tempbuff);
2150 ====================
2153 Get the next character of a file
2154 ====================
2156 int FS_Getc (qfile_t* file)
2160 if (FS_Read (file, &c, 1) != 1)
2168 ====================
2171 Put a character back into the read buffer (only supports one character!)
2172 ====================
2174 int FS_UnGetc (qfile_t* file, unsigned char c)
2176 // If there's already a character waiting to be read
2177 if (file->ungetc != EOF)
2186 ====================
2189 Move the position index in a file
2190 ====================
2192 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2195 unsigned char* buffer;
2196 fs_offset_t buffersize;
2198 // Compute the file offset
2202 offset += file->position - file->buff_len + file->buff_ind;
2209 offset += file->real_length;
2215 if (offset < 0 || offset > file->real_length)
2218 // If we have the data in our read buffer, we don't need to actually seek
2219 if (file->position - file->buff_len <= offset && offset <= file->position)
2221 file->buff_ind = offset + file->buff_len - file->position;
2225 // Purge cached data
2228 // Unpacked or uncompressed files can seek directly
2229 if (! (file->flags & QFILE_FLAG_DEFLATED))
2231 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2233 file->position = offset;
2237 // Seeking in compressed files is more a hack than anything else,
2238 // but we need to support it, so here we go.
2241 // If we have to go back in the file, we need to restart from the beginning
2242 if (offset <= file->position)
2246 ztk->in_position = 0;
2248 lseek (file->handle, file->offset, SEEK_SET);
2250 // Reset the Zlib stream
2251 ztk->zstream.next_in = ztk->input;
2252 ztk->zstream.avail_in = 0;
2253 qz_inflateReset (&ztk->zstream);
2256 // We need a big buffer to force inflating into it directly
2257 buffersize = 2 * sizeof (file->buff);
2258 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2260 // Skip all data until we reach the requested offset
2261 while (offset > file->position)
2263 fs_offset_t diff = offset - file->position;
2264 fs_offset_t count, len;
2266 count = (diff > buffersize) ? buffersize : diff;
2267 len = FS_Read (file, buffer, count);
2281 ====================
2284 Give the current position in a file
2285 ====================
2287 fs_offset_t FS_Tell (qfile_t* file)
2289 return file->position - file->buff_len + file->buff_ind;
2294 ====================
2297 Give the total size of a file
2298 ====================
2300 fs_offset_t FS_FileSize (qfile_t* file)
2302 return file->real_length;
2307 ====================
2310 Erases any buffered input or output data
2311 ====================
2313 void FS_Purge (qfile_t* file)
2325 Filename are relative to the quake directory.
2326 Always appends a 0 byte.
2329 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2332 unsigned char *buf = NULL;
2333 fs_offset_t filesize = 0;
2335 file = FS_Open (path, "rb", quiet, false);
2338 filesize = file->real_length;
2339 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2340 buf[filesize] = '\0';
2341 FS_Read (file, buf, filesize);
2345 if (filesizepointer)
2346 *filesizepointer = filesize;
2355 The filename will be prefixed by the current game directory
2358 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2362 file = FS_Open (filename, "wb", false, false);
2365 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2369 Con_DPrintf("FS_WriteFile: %s\n", filename);
2370 FS_Write (file, data, len);
2377 =============================================================================
2379 OTHERS PUBLIC FUNCTIONS
2381 =============================================================================
2389 void FS_StripExtension (const char *in, char *out, size_t size_out)
2397 while ((currentchar = *in) && size_out > 1)
2399 if (currentchar == '.')
2401 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2403 *out++ = currentchar;
2419 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2423 // if path doesn't have a .EXT, append extension
2424 // (extension should include the .)
2425 src = path + strlen(path) - 1;
2427 while (*src != '/' && src != path)
2430 return; // it has an extension
2434 strlcat (path, extension, size_path);
2442 Look for a file in the packages and in the filesystem
2445 int FS_FileType (const char *filename)
2447 searchpath_t *search;
2448 char fullpath[MAX_QPATH];
2450 search = FS_FindFile (filename, NULL, true);
2452 return FS_FILETYPE_NONE;
2455 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2457 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2458 return FS_SysFileType(fullpath);
2466 Look for a file in the packages and in the filesystem
2469 qboolean FS_FileExists (const char *filename)
2471 return (FS_FindFile (filename, NULL, true) != NULL);
2479 Look for a file in the filesystem only
2482 int FS_SysFileType (const char *path)
2485 DWORD result = GetFileAttributes(path);
2487 if(result == INVALID_FILE_ATTRIBUTES)
2488 return FS_FILETYPE_NONE;
2490 if(result & FILE_ATTRIBUTE_DIRECTORY)
2491 return FS_FILETYPE_DIRECTORY;
2493 return FS_FILETYPE_FILE;
2497 if (stat (path,&buf) == -1)
2498 return FS_FILETYPE_NONE;
2500 if(S_ISDIR(buf.st_mode))
2501 return FS_FILETYPE_DIRECTORY;
2503 return FS_FILETYPE_FILE;
2507 qboolean FS_SysFileExists (const char *path)
2509 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2512 void FS_mkdir (const char *path)
2525 Allocate and fill a search structure with information on matching filenames.
2528 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2531 searchpath_t *searchpath;
2533 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2534 stringlist_t resultlist;
2535 stringlist_t dirlist;
2536 const char *slash, *backslash, *colon, *separator;
2538 char netpath[MAX_OSPATH];
2539 char temp[MAX_OSPATH];
2541 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2546 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2550 stringlistinit(&resultlist);
2551 stringlistinit(&dirlist);
2553 slash = strrchr(pattern, '/');
2554 backslash = strrchr(pattern, '\\');
2555 colon = strrchr(pattern, ':');
2556 separator = max(slash, backslash);
2557 separator = max(separator, colon);
2558 basepathlength = separator ? (separator + 1 - pattern) : 0;
2559 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2561 memcpy(basepath, pattern, basepathlength);
2562 basepath[basepathlength] = 0;
2564 // search through the path, one element at a time
2565 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2567 // is the element a pak file?
2568 if (searchpath->pack)
2570 // look through all the pak file elements
2571 pak = searchpath->pack;
2572 for (i = 0;i < pak->numfiles;i++)
2574 strlcpy(temp, pak->files[i].name, sizeof(temp));
2577 if (matchpattern(temp, (char *)pattern, true))
2579 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2580 if (!strcmp(resultlist.strings[resultlistindex], temp))
2582 if (resultlistindex == resultlist.numstrings)
2584 stringlistappend(&resultlist, temp);
2586 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2589 // strip off one path element at a time until empty
2590 // this way directories are added to the listing if they match the pattern
2591 slash = strrchr(temp, '/');
2592 backslash = strrchr(temp, '\\');
2593 colon = strrchr(temp, ':');
2595 if (separator < slash)
2597 if (separator < backslash)
2598 separator = backslash;
2599 if (separator < colon)
2601 *((char *)separator) = 0;
2607 // get a directory listing and look at each name
2608 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2609 stringlistinit(&dirlist);
2610 listdirectory(&dirlist, netpath);
2611 for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
2613 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
2614 if (matchpattern(temp, (char *)pattern, true))
2616 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2617 if (!strcmp(resultlist.strings[resultlistindex], temp))
2619 if (resultlistindex == resultlist.numstrings)
2621 stringlistappend(&resultlist, temp);
2623 Con_DPrintf("SearchDirFile: %s\n", temp);
2627 stringlistfreecontents(&dirlist);
2631 if (resultlist.numstrings)
2633 stringlistsort(&resultlist);
2634 numfiles = resultlist.numstrings;
2636 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2637 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2638 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2639 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2640 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2641 search->numfilenames = (int)numfiles;
2644 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2647 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2648 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2649 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2651 numchars += (int)textlen;
2654 stringlistfreecontents(&resultlist);
2660 void FS_FreeSearch(fssearch_t *search)
2665 extern int con_linewidth;
2666 int FS_ListDirectory(const char *pattern, int oneperline)
2675 char linebuf[MAX_INPUTLINE];
2677 search = FS_Search(pattern, true, true);
2680 numfiles = search->numfilenames;
2683 // FIXME: the names could be added to one column list and then
2684 // gradually shifted into the next column if they fit, and then the
2685 // next to make a compact variable width listing but it's a lot more
2687 // find width for columns
2689 for (i = 0;i < numfiles;i++)
2691 l = (int)strlen(search->filenames[i]);
2692 if (columnwidth < l)
2695 // count the spacing character
2697 // calculate number of columns
2698 numcolumns = con_linewidth / columnwidth;
2699 // don't bother with the column printing if it's only one column
2700 if (numcolumns >= 2)
2702 numlines = (numfiles + numcolumns - 1) / numcolumns;
2703 for (i = 0;i < numlines;i++)
2706 for (k = 0;k < numcolumns;k++)
2708 l = i * numcolumns + k;
2711 name = search->filenames[l];
2712 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2713 linebuf[linebufpos++] = name[j];
2714 // space out name unless it's the last on the line
2715 if (k + 1 < numcolumns && l + 1 < numfiles)
2716 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2717 linebuf[linebufpos++] = ' ';
2720 linebuf[linebufpos] = 0;
2721 Con_Printf("%s\n", linebuf);
2728 for (i = 0;i < numfiles;i++)
2729 Con_Printf("%s\n", search->filenames[i]);
2730 FS_FreeSearch(search);
2731 return (int)numfiles;
2734 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2736 const char *pattern;
2739 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2742 if (Cmd_Argc() == 2)
2743 pattern = Cmd_Argv(1);
2746 if (!FS_ListDirectory(pattern, oneperline))
2747 Con_Print("No files found.\n");
2752 FS_ListDirectoryCmd("dir", true);
2757 FS_ListDirectoryCmd("ls", false);
2760 const char *FS_WhichPack(const char *filename)
2763 searchpath_t *sp = FS_FindFile(filename, &index, true);
2765 return sp->pack->filename;
2771 ====================
2772 FS_IsRegisteredQuakePack
2774 Look for a proof of purchase file file in the requested package
2776 If it is found, this file should NOT be downloaded.
2777 ====================
2779 qboolean FS_IsRegisteredQuakePack(const char *name)
2781 searchpath_t *search;
2784 // search through the path, one element at a time
2785 for (search = fs_searchpaths;search;search = search->next)
2787 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2789 int (*strcmp_funct) (const char* str1, const char* str2);
2790 int left, right, middle;
2793 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2795 // Look for the file (binary search)
2797 right = pak->numfiles - 1;
2798 while (left <= right)
2802 middle = (left + right) / 2;
2803 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2809 // If we're too far in the list
2816 // we found the requested pack but it is not registered quake
2824 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2827 unsigned char *filedata;
2828 fs_offset_t filesize;
2829 if (filesizepointer)
2830 *filesizepointer = 0;
2831 if (!filename || !*filename)
2833 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2836 if (filesizepointer)
2837 *filesizepointer = filesize;
2838 crc = CRC_Block(filedata, filesize);