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
35 # include <sys/stat.h>
42 // Win32 requires us to add O_BINARY, but the other OSes don't have it
47 // In case the system doesn't support the O_NONBLOCK flag
52 // largefile support for Win32
54 # define lseek _lseeki64
59 All of Quake's data access is through a hierchal file system, but the contents
60 of the file system can be transparently merged from several sources.
62 The "base directory" is the path to the directory holding the quake.exe and
63 all game directories. The sys_* files pass this to host_init in
64 quakeparms_t->basedir. This can be overridden with the "-basedir" command
65 line parm to allow code debugging in a different directory. The base
66 directory is only used during filesystem initialization.
68 The "game directory" is the first tree on the search path and directory that
69 all generated files (savegames, screenshots, demos, config files) will be
70 saved to. This can be overridden with the "-game" command line parameter.
71 The game directory can never be changed while quake is executing. This is a
72 precaution against having a malicious server instruct clients to write files
73 over areas they shouldn't.
79 =============================================================================
83 =============================================================================
86 // Magic numbers of a ZIP file (big-endian format)
87 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
88 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
89 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
91 // Other constants for ZIP files
92 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
93 #define ZIP_END_CDIR_SIZE 22
94 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
95 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
97 // Zlib constants (from zlib.h)
98 #define Z_SYNC_FLUSH 2
101 #define Z_STREAM_END 1
102 #define ZLIB_VERSION "1.2.3"
104 // Uncomment the following line if the zlib DLL you have still uses
105 // the 1.1.x series calling convention on Win32 (WINAPI)
106 //#define ZLIB_USES_WINAPI
110 =============================================================================
114 =============================================================================
117 // Zlib stream (from zlib.h)
118 // Warning: some pointers we don't use directly have
119 // been cast to "void*" for a matter of simplicity
122 unsigned char *next_in; // next input byte
123 unsigned int avail_in; // number of bytes available at next_in
124 unsigned long total_in; // total nb of input bytes read so far
126 unsigned char *next_out; // next output byte should be put there
127 unsigned int avail_out; // remaining free space at next_out
128 unsigned long total_out; // total nb of bytes output so far
130 char *msg; // last error message, NULL if no error
131 void *state; // not visible by applications
133 void *zalloc; // used to allocate the internal state
134 void *zfree; // used to free the internal state
135 void *opaque; // private data object passed to zalloc and zfree
137 int data_type; // best guess about the data type: ascii or binary
138 unsigned long adler; // adler32 value of the uncompressed data
139 unsigned long reserved; // reserved for future use
143 // inside a package (PAK or PK3)
144 #define QFILE_FLAG_PACKED (1 << 0)
145 // file is compressed using the deflate algorithm (PK3 only)
146 #define QFILE_FLAG_DEFLATED (1 << 1)
148 #define FILE_BUFF_SIZE 2048
152 size_t comp_length; // length of the compressed file
153 size_t in_ind, in_len; // input buffer current index and length
154 size_t in_position; // position in the compressed file
155 unsigned char input [FILE_BUFF_SIZE];
161 int handle; // file descriptor
162 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
163 fs_offset_t position; // current position in the file
164 fs_offset_t offset; // offset into the package (0 if external file)
165 int ungetc; // single stored character from ungetc, cleared to EOF when read
168 fs_offset_t buff_ind, buff_len; // buffer current index and length
169 unsigned char buff [FILE_BUFF_SIZE];
176 // ------ PK3 files on disk ------ //
178 // You can get the complete ZIP format description from PKWARE website
180 typedef struct pk3_endOfCentralDir_s
182 unsigned int signature;
183 unsigned short disknum;
184 unsigned short cdir_disknum; // number of the disk with the start of the central directory
185 unsigned short localentries; // number of entries in the central directory on this disk
186 unsigned short nbentries; // total number of entries in the central directory on this disk
187 unsigned int cdir_size; // size of the central directory
188 unsigned int cdir_offset; // with respect to the starting disk number
189 unsigned short comment_size;
190 } pk3_endOfCentralDir_t;
193 // ------ PAK files on disk ------ //
194 typedef struct dpackfile_s
197 int filepos, filelen;
200 typedef struct dpackheader_s
208 // Packages in memory
209 // the offset in packfile_t is the true contents offset
210 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
211 // file compressed using the deflate algorithm
212 #define PACKFILE_FLAG_DEFLATED (1 << 1)
214 typedef struct packfile_s
216 char name [MAX_QPATH];
219 fs_offset_t packsize; // size in the package
220 fs_offset_t realsize; // real file size (uncompressed)
223 typedef struct pack_s
225 char filename [MAX_OSPATH];
227 int ignorecase; // PK3 ignores case
233 // Search paths for files (including packages)
234 typedef struct searchpath_s
236 // only one of filename / pack will be used
237 char filename[MAX_OSPATH];
239 struct searchpath_s *next;
244 =============================================================================
248 =============================================================================
254 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
255 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
256 fs_offset_t offset, fs_offset_t packsize,
257 fs_offset_t realsize, int flags);
261 =============================================================================
265 =============================================================================
268 mempool_t *fs_mempool;
270 searchpath_t *fs_searchpaths = NULL;
272 #define MAX_FILES_IN_PACK 65536
274 char fs_gamedir[MAX_OSPATH];
275 char fs_basedir[MAX_OSPATH];
277 // list of active game directories (empty if not running a mod)
278 int fs_numgamedirs = 0;
279 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
281 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)"};
282 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"};
286 =============================================================================
288 PRIVATE FUNCTIONS - PK3 HANDLING
290 =============================================================================
293 // Functions exported from zlib
294 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
295 # define ZEXPORT WINAPI
300 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
301 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
302 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
303 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
305 #define qz_inflateInit2(strm, windowBits) \
306 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
308 static dllfunction_t zlibfuncs[] =
310 {"inflate", (void **) &qz_inflate},
311 {"inflateEnd", (void **) &qz_inflateEnd},
312 {"inflateInit2_", (void **) &qz_inflateInit2_},
313 {"inflateReset", (void **) &qz_inflateReset},
317 // Handle for Zlib DLL
318 static dllhandle_t zlib_dll = NULL;
328 void PK3_CloseLibrary (void)
330 Sys_UnloadLibrary (&zlib_dll);
338 Try to load the Zlib DLL
341 qboolean PK3_OpenLibrary (void)
343 const char* dllnames [] =
348 # ifdef ZLIB_USES_WINAPI
354 #elif defined(MACOSX)
368 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
370 Con_Printf ("Compressed files support disabled\n");
374 Con_Printf ("Compressed files support enabled\n");
381 PK3_GetEndOfCentralDir
383 Extract the end of the central directory from a PK3 package
386 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
388 fs_offset_t filesize, maxsize;
389 unsigned char *buffer, *ptr;
392 // Get the package size
393 filesize = lseek (packhandle, 0, SEEK_END);
394 if (filesize < ZIP_END_CDIR_SIZE)
397 // Load the end of the file in memory
398 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
401 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
402 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
403 lseek (packhandle, filesize - maxsize, SEEK_SET);
404 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
410 // Look for the end of central dir signature around the end of the file
411 maxsize -= ZIP_END_CDIR_SIZE;
412 ptr = &buffer[maxsize];
414 while (BuffBigLong (ptr) != ZIP_END_HEADER)
426 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
427 eocd->signature = LittleLong (eocd->signature);
428 eocd->disknum = LittleShort (eocd->disknum);
429 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
430 eocd->localentries = LittleShort (eocd->localentries);
431 eocd->nbentries = LittleShort (eocd->nbentries);
432 eocd->cdir_size = LittleLong (eocd->cdir_size);
433 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
434 eocd->comment_size = LittleShort (eocd->comment_size);
446 Extract the file list from a PK3 file
449 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
451 unsigned char *central_dir, *ptr;
453 fs_offset_t remaining;
455 // Load the central directory in memory
456 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
457 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
458 read (pack->handle, central_dir, eocd->cdir_size);
460 // Extract the files properties
461 // The parsing is done "by hand" because some fields have variable sizes and
462 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
463 remaining = eocd->cdir_size;
466 for (ind = 0; ind < eocd->nbentries; ind++)
468 fs_offset_t namesize, count;
470 // Checking the remaining size
471 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
473 Mem_Free (central_dir);
476 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
479 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
481 Mem_Free (central_dir);
485 namesize = BuffLittleShort (&ptr[28]); // filename length
487 // Check encryption, compression, and attributes
488 // 1st uint8 : general purpose bit flag
489 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
490 // 2nd uint8 : external file attributes
491 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
492 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
494 // Still enough bytes for the name?
495 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
497 Mem_Free (central_dir);
501 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
502 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
504 char filename [sizeof (pack->files[0].name)];
505 fs_offset_t offset, packsize, realsize;
508 // Extract the name (strip it if necessary)
509 namesize = min(namesize, (int)sizeof (filename) - 1);
510 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
511 filename[namesize] = '\0';
513 if (BuffLittleShort (&ptr[10]))
514 flags = PACKFILE_FLAG_DEFLATED;
517 offset = BuffLittleLong (&ptr[42]);
518 packsize = BuffLittleLong (&ptr[20]);
519 realsize = BuffLittleLong (&ptr[24]);
520 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
524 // Skip the name, additionnal field, and comment
525 // 1er uint16 : extra field length
526 // 2eme uint16 : file comment length
527 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
528 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
532 // If the package is empty, central_dir is NULL here
533 if (central_dir != NULL)
534 Mem_Free (central_dir);
535 return pack->numfiles;
543 Create a package entry associated with a PK3 file
546 pack_t *FS_LoadPackPK3 (const char *packfile)
549 pk3_endOfCentralDir_t eocd;
553 packhandle = open (packfile, O_RDONLY | O_BINARY);
557 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
559 Con_Printf ("%s is not a PK3 file\n", packfile);
564 // Multi-volume ZIP archives are NOT allowed
565 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
567 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
572 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
573 // since eocd.nbentries is an unsigned 16 bits integer
574 #if MAX_FILES_IN_PACK < 65535
575 if (eocd.nbentries > MAX_FILES_IN_PACK)
577 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
583 // Create a package structure in memory
584 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
585 pack->ignorecase = true; // PK3 ignores case
586 strlcpy (pack->filename, packfile, sizeof (pack->filename));
587 pack->handle = packhandle;
588 pack->numfiles = eocd.nbentries;
589 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
591 real_nb_files = PK3_BuildFileList (pack, &eocd);
592 if (real_nb_files < 0)
594 Con_Printf ("%s is not a valid PK3 file\n", packfile);
600 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
607 PK3_GetTrueFileOffset
609 Find where the true file data offset is
612 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
614 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
618 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
621 // Load the local file description
622 lseek (pack->handle, pfile->offset, SEEK_SET);
623 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
624 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
626 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
630 // Skip name and extra field
631 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
633 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
639 =============================================================================
641 OTHER PRIVATE FUNCTIONS
643 =============================================================================
651 Add a file to the list of files contained into a package
654 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
655 fs_offset_t offset, fs_offset_t packsize,
656 fs_offset_t realsize, int flags)
658 int (*strcmp_funct) (const char* str1, const char* str2);
659 int left, right, middle;
662 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
664 // Look for the slot we should put that file into (binary search)
666 right = pack->numfiles - 1;
667 while (left <= right)
671 middle = (left + right) / 2;
672 diff = strcmp_funct (pack->files[middle].name, name);
674 // If we found the file, there's a problem
676 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
678 // If we're too far in the list
685 // We have to move the right of the list by one slot to free the one we need
686 pfile = &pack->files[left];
687 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
690 strlcpy (pfile->name, name, sizeof (pfile->name));
691 pfile->offset = offset;
692 pfile->packsize = packsize;
693 pfile->realsize = realsize;
694 pfile->flags = flags;
704 Only used for FS_Open.
707 void FS_CreatePath (char *path)
711 for (ofs = path+1 ; *ofs ; ofs++)
713 if (*ofs == '/' || *ofs == '\\')
715 // create the directory
731 void FS_Path_f (void)
735 Con_Print("Current search path:\n");
736 for (s=fs_searchpaths ; s ; s=s->next)
739 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
741 Con_Printf("%s\n", s->filename);
750 Takes an explicit (not game tree related) path to a pak file.
752 Loads the header and directory, adding the files at the beginning
753 of the list so they override previous pack files.
756 pack_t *FS_LoadPackPAK (const char *packfile)
758 dpackheader_t header;
764 packhandle = open (packfile, O_RDONLY | O_BINARY);
767 read (packhandle, (void *)&header, sizeof(header));
768 if (memcmp(header.id, "PACK", 4))
770 Con_Printf ("%s is not a packfile\n", packfile);
774 header.dirofs = LittleLong (header.dirofs);
775 header.dirlen = LittleLong (header.dirlen);
777 if (header.dirlen % sizeof(dpackfile_t))
779 Con_Printf ("%s has an invalid directory size\n", packfile);
784 numpackfiles = header.dirlen / sizeof(dpackfile_t);
786 if (numpackfiles > MAX_FILES_IN_PACK)
788 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
793 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
794 lseek (packhandle, header.dirofs, SEEK_SET);
795 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
797 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
803 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
804 pack->ignorecase = false; // PAK is case sensitive
805 strlcpy (pack->filename, packfile, sizeof (pack->filename));
806 pack->handle = packhandle;
808 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
810 // parse the directory
811 for (i = 0;i < numpackfiles;i++)
813 fs_offset_t offset = LittleLong (info[i].filepos);
814 fs_offset_t size = LittleLong (info[i].filelen);
816 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
821 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
829 Adds the given pack to the search path.
830 The pack type is autodetected by the file extension.
832 Returns true if the file was successfully added to the
833 search path or if it was already included.
835 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
839 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
841 searchpath_t *search;
843 const char *ext = FS_FileExtension(pakfile);
845 for(search = fs_searchpaths; search; search = search->next)
847 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
850 *already_loaded = true;
851 return true; // already loaded
856 *already_loaded = false;
858 if(!strcasecmp(ext, "pak"))
859 pak = FS_LoadPackPAK (pakfile);
860 else if(!strcasecmp(ext, "pk3"))
861 pak = FS_LoadPackPK3 (pakfile);
863 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
869 // find the first item whose next one is a pack or NULL
870 searchpath_t *insertion_point = 0;
871 if(fs_searchpaths && !fs_searchpaths->pack)
873 insertion_point = fs_searchpaths;
876 if(!insertion_point->next)
878 if(insertion_point->next->pack)
880 insertion_point = insertion_point->next;
883 // If insertion_point is NULL, this means that either there is no
884 // item in the list yet, or that the very first item is a pack. In
885 // that case, we want to insert at the beginning...
888 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
890 search->next = fs_searchpaths;
891 fs_searchpaths = search;
894 // otherwise we want to append directly after insertion_point.
896 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
898 search->next = insertion_point->next;
899 insertion_point->next = search;
904 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
906 search->next = fs_searchpaths;
907 fs_searchpaths = search;
913 Con_Printf("unable to load pak \"%s\"\n", pakfile);
923 Adds the given pack to the search path and searches for it in the game path.
924 The pack type is autodetected by the file extension.
926 Returns true if the file was successfully added to the
927 search path or if it was already included.
929 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
933 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
935 char fullpath[MAX_QPATH];
937 searchpath_t *search;
940 *already_loaded = false;
942 // then find the real name...
943 search = FS_FindFile(pakfile, &index, true);
944 if(!search || search->pack)
946 Con_Printf("could not find pak \"%s\"\n", pakfile);
950 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
952 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
960 Sets fs_gamedir, adds the directory to the head of the path,
961 then loads and adds pak1.pak pak2.pak ...
964 void FS_AddGameDirectory (const char *dir)
968 searchpath_t *search;
969 char pakfile[MAX_OSPATH];
971 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
973 stringlistinit(&list);
974 listdirectory(&list, dir);
975 stringlistsort(&list);
977 // add any PAK package in the directory
978 for (i = 0;i < list.numstrings;i++)
980 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
982 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
983 FS_AddPack_Fullpath(pakfile, NULL, false);
987 // add any PK3 package in the directory
988 for (i = 0;i < list.numstrings;i++)
990 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
992 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
993 FS_AddPack_Fullpath(pakfile, NULL, false);
997 stringlistfreecontents(&list);
999 // Add the directory to the search path
1000 // (unpacked files have the priority over packed files)
1001 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1002 strlcpy (search->filename, dir, sizeof (search->filename));
1003 search->next = fs_searchpaths;
1004 fs_searchpaths = search;
1013 void FS_AddGameHierarchy (const char *dir)
1016 const char *homedir;
1019 // Add the common game directory
1020 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1023 // Add the personal game directory
1024 homedir = getenv ("HOME");
1025 if (homedir != NULL && homedir[0] != '\0')
1026 FS_AddGameDirectory (va("%s/.%s/%s/", homedir, gameuserdirname, dir));
1036 const char *FS_FileExtension (const char *in)
1038 const char *separator, *backslash, *colon, *dot;
1040 separator = strrchr(in, '/');
1041 backslash = strrchr(in, '\\');
1042 if (!separator || separator < backslash)
1043 separator = backslash;
1044 colon = strrchr(in, ':');
1045 if (!separator || separator < colon)
1048 dot = strrchr(in, '.');
1049 if (dot == NULL || (separator && (dot < separator)))
1061 const char *FS_FileWithoutPath (const char *in)
1063 const char *separator, *backslash, *colon;
1065 separator = strrchr(in, '/');
1066 backslash = strrchr(in, '\\');
1067 if (!separator || separator < backslash)
1068 separator = backslash;
1069 colon = strrchr(in, ':');
1070 if (!separator || separator < colon)
1072 return separator ? separator + 1 : in;
1081 void FS_ClearSearchPath (void)
1083 // unload all packs and directory information, close all pack files
1084 // (if a qfile is still reading a pack it won't be harmed because it used
1085 // dup() to get its own handle already)
1086 while (fs_searchpaths)
1088 searchpath_t *search = fs_searchpaths;
1089 fs_searchpaths = search->next;
1093 close(search->pack->handle);
1094 // free any memory associated with it
1095 if (search->pack->files)
1096 Mem_Free(search->pack->files);
1097 Mem_Free(search->pack);
1109 void FS_Rescan (void)
1112 qboolean fs_modified = false;
1114 FS_ClearSearchPath();
1116 // add the game-specific paths
1117 // gamedirname1 (typically id1)
1118 FS_AddGameHierarchy (gamedirname1);
1119 // update the com_modname (used for server info)
1120 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1122 // add the game-specific path, if any
1123 // (only used for mission packs and the like, which should set fs_modified)
1127 FS_AddGameHierarchy (gamedirname2);
1131 // Adds basedir/gamedir as an override game
1132 // LordHavoc: now supports multiple -game directories
1133 // set the com_modname (reported in server info)
1134 for (i = 0;i < fs_numgamedirs;i++)
1137 FS_AddGameHierarchy (fs_gamedirs[i]);
1138 // update the com_modname (used server info)
1139 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1142 // set the default screenshot name to either the mod name or the
1143 // gamemode screenshot name
1144 if (strcmp(com_modname, gamedirname1))
1145 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1147 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1149 // If "-condebug" is in the command line, remove the previous log file
1150 if (COM_CheckParm ("-condebug") != 0)
1151 unlink (va("%s/qconsole.log", fs_gamedir));
1153 // look for the pop.lmp file and set registered to true if it is found
1154 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1157 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1159 Con_Print("Playing shareware version.\n");
1163 Cvar_Set ("registered", "1");
1164 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1165 Con_Print("Playing registered version.\n");
1168 // unload all wads so that future queries will return the new data
1172 void FS_Rescan_f(void)
1182 extern void Host_SaveConfig_f (void);
1183 extern void Host_LoadConfig_f (void);
1184 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1188 if (fs_numgamedirs == numgamedirs)
1190 for (i = 0;i < numgamedirs;i++)
1191 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1193 if (i == numgamedirs)
1194 return true; // already using this set of gamedirs, do nothing
1197 if (numgamedirs > MAX_GAMEDIRS)
1200 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1201 return false; // too many gamedirs
1204 for (i = 0;i < numgamedirs;i++)
1206 // if string is nasty, reject it
1207 if(FS_CheckNastyPath(gamedirs[i], true))
1210 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1211 return false; // nasty gamedirs
1215 for (i = 0;i < numgamedirs;i++)
1217 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1220 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1221 return false; // missing gamedirs
1225 // halt demo playback to close the file
1228 Host_SaveConfig_f();
1230 fs_numgamedirs = numgamedirs;
1231 for (i = 0;i < fs_numgamedirs;i++)
1232 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1234 // reinitialize filesystem to detect the new paks
1237 // exec the new config
1238 Host_LoadConfig_f();
1240 // unload all sounds so they will be reloaded from the new files as needed
1241 S_UnloadAllSounds_f();
1243 // reinitialize renderer (this reloads hud/console background/etc)
1244 R_Modules_Restart();
1254 void FS_GameDir_f (void)
1258 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1262 Con_Printf("gamedirs active:");
1263 for (i = 0;i < fs_numgamedirs;i++)
1264 Con_Printf(" %s", fs_gamedirs[i]);
1269 numgamedirs = Cmd_Argc() - 1;
1270 if (numgamedirs > MAX_GAMEDIRS)
1272 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1276 for (i = 0;i < numgamedirs;i++)
1277 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1279 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1281 // actually, changing during game would work fine, but would be stupid
1282 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1286 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1295 qboolean FS_CheckGameDir(const char *gamedir)
1299 stringlistinit(&list);
1300 listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
1301 success = list.numstrings > 0;
1302 stringlistfreecontents(&list);
1316 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1318 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1320 // If the base directory is explicitly defined by the compilation process
1321 #ifdef DP_FS_BASEDIR
1322 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1324 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1327 // FIXME: is there a better way to find the directory outside the .app?
1328 if (strstr(com_argv[0], ".app/"))
1332 split = strstr(com_argv[0], ".app/");
1333 while (split > com_argv[0] && *split != '/')
1335 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1336 fs_basedir[split - com_argv[0]] = 0;
1344 // Overrides the system supplied base directory (under GAMENAME)
1345 // 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)
1346 i = COM_CheckParm ("-basedir");
1347 if (i && i < com_argc-1)
1349 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1350 i = (int)strlen (fs_basedir);
1351 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1352 fs_basedir[i-1] = 0;
1355 // add a path separator to the end of the basedir if it lacks one
1356 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1357 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1359 if (!FS_CheckGameDir(gamedirname1))
1360 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1362 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1363 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1366 // Adds basedir/gamedir as an override game
1367 // LordHavoc: now supports multiple -game directories
1368 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1372 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1375 if (FS_CheckNastyPath(com_argv[i], true))
1376 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1377 if (!FS_CheckGameDir(com_argv[i]))
1378 Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1379 // add the gamedir to the list of active gamedirs
1380 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1385 // generate the searchpath
1389 void FS_Init_Commands(void)
1391 Cvar_RegisterVariable (&scr_screenshot_name);
1392 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1394 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1395 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1396 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1397 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1398 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1406 void FS_Shutdown (void)
1408 // close all pack files and such
1409 // (hopefully there aren't any other open files, but they'll be cleaned up
1410 // by the OS anyway)
1411 FS_ClearSearchPath();
1412 Mem_FreePool (&fs_mempool);
1416 ====================
1419 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1420 ====================
1422 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1428 // Parse the mode string
1437 opt = O_CREAT | O_TRUNC;
1441 opt = O_CREAT | O_APPEND;
1444 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1447 for (ind = 1; mode[ind] != '\0'; ind++)
1458 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1459 filepath, mode, mode[ind]);
1466 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1467 memset (file, 0, sizeof (*file));
1470 file->handle = open (filepath, mod | opt, 0666);
1471 if (file->handle < 0)
1477 file->real_length = lseek (file->handle, 0, SEEK_END);
1479 // For files opened in append mode, we start at the end of the file
1481 file->position = file->real_length;
1483 lseek (file->handle, 0, SEEK_SET);
1493 Open a packed file using its package file descriptor
1496 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1502 pfile = &pack->files[pack_ind];
1504 // If we don't have the true offset, get it now
1505 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1506 if (!PK3_GetTrueFileOffset (pfile, pack))
1509 // No Zlib DLL = no compressed files
1510 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1512 Con_Printf("WARNING: can't open the compressed file %s\n"
1513 "You need the Zlib DLL to use compressed files\n",
1518 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1519 // the dup() call to avoid having to close the dup_handle on error here
1520 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1522 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1523 pfile->name, pack->filename, (int) pfile->offset);
1527 dup_handle = dup (pack->handle);
1530 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1534 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1535 memset (file, 0, sizeof (*file));
1536 file->handle = dup_handle;
1537 file->flags = QFILE_FLAG_PACKED;
1538 file->real_length = pfile->realsize;
1539 file->offset = pfile->offset;
1543 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1547 file->flags |= QFILE_FLAG_DEFLATED;
1549 // We need some more variables
1550 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1552 ztk->comp_length = pfile->packsize;
1554 // Initialize zlib stream
1555 ztk->zstream.next_in = ztk->input;
1556 ztk->zstream.avail_in = 0;
1558 /* From Zlib's "unzip.c":
1560 * windowBits is passed < 0 to tell that there is no zlib header.
1561 * Note that in this case inflate *requires* an extra "dummy" byte
1562 * after the compressed stream in order to complete decompression and
1563 * return Z_STREAM_END.
1564 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1565 * size of both compressed and uncompressed data
1567 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1569 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1575 ztk->zstream.next_out = file->buff;
1576 ztk->zstream.avail_out = sizeof (file->buff);
1585 ====================
1588 Return true if the path should be rejected due to one of the following:
1589 1: path elements that are non-portable
1590 2: path elements that would allow access to files outside the game directory,
1591 or are just not a good idea for a mod to be using.
1592 ====================
1594 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1596 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1600 // Windows: don't allow \ in filenames (windows-only), period.
1601 // (on Windows \ is a directory separator, but / is also supported)
1602 if (strstr(path, "\\"))
1603 return 1; // non-portable
1605 // Mac: don't allow Mac-only filenames - : is a directory separator
1606 // instead of /, but we rely on / working already, so there's no reason to
1607 // support a Mac-only path
1608 // Amiga and Windows: : tries to go to root of drive
1609 if (strstr(path, ":"))
1610 return 1; // non-portable attempt to go to root of drive
1612 // Amiga: // is parent directory
1613 if (strstr(path, "//"))
1614 return 1; // non-portable attempt to go to parent directory
1616 // all: don't allow going to parent directory (../ or /../)
1617 if (strstr(path, ".."))
1618 return 2; // attempt to go outside the game directory
1620 // Windows and UNIXes: don't allow absolute paths
1622 return 2; // attempt to go outside the game directory
1624 // 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
1625 if (strchr(path, '.'))
1629 // gamedir is entirely path elements, so simply forbid . entirely
1632 if (strchr(path, '.') < strrchr(path, '/'))
1633 return 2; // possible attempt to go outside the game directory
1636 // all: forbid trailing slash on gamedir
1637 if (isgamedir && path[strlen(path)-1] == '/')
1640 // all: forbid leading dot on any filename for any reason
1641 if (strstr(path, "/."))
1642 return 2; // attempt to go outside the game directory
1644 // after all these checks we're pretty sure it's a / separated filename
1645 // and won't do much if any harm
1651 ====================
1654 Look for a file in the packages and in the filesystem
1656 Return the searchpath where the file was found (or NULL)
1657 and the file index in the package if relevant
1658 ====================
1660 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1662 searchpath_t *search;
1665 // search through the path, one element at a time
1666 for (search = fs_searchpaths;search;search = search->next)
1668 // is the element a pak file?
1671 int (*strcmp_funct) (const char* str1, const char* str2);
1672 int left, right, middle;
1675 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1677 // Look for the file (binary search)
1679 right = pak->numfiles - 1;
1680 while (left <= right)
1684 middle = (left + right) / 2;
1685 diff = strcmp_funct (pak->files[middle].name, name);
1690 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1692 // yes, but the first one is empty so we treat it as not being there
1693 if (!quiet && developer.integer >= 10)
1694 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1701 if (!quiet && developer.integer >= 10)
1702 Con_Printf("FS_FindFile: %s in %s\n",
1703 pak->files[middle].name, pak->filename);
1710 // If we're too far in the list
1719 char netpath[MAX_OSPATH];
1720 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1721 if (FS_SysFileExists (netpath))
1723 if (!quiet && developer.integer >= 10)
1724 Con_Printf("FS_FindFile: %s\n", netpath);
1733 if (!quiet && developer.integer >= 10)
1734 Con_Printf("FS_FindFile: can't find %s\n", name);
1746 Look for a file in the search paths and open it in read-only mode
1749 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1751 searchpath_t *search;
1754 search = FS_FindFile (filename, &pack_ind, quiet);
1760 // Found in the filesystem?
1763 char path [MAX_OSPATH];
1764 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1765 return FS_SysOpen (path, "rb", nonblocking);
1768 // So, we found it in a package...
1769 return FS_OpenPackedFile (search->pack, pack_ind);
1774 =============================================================================
1776 MAIN PUBLIC FUNCTIONS
1778 =============================================================================
1782 ====================
1785 Open a file. The syntax is the same as fopen
1786 ====================
1788 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1790 if (FS_CheckNastyPath(filepath, false))
1792 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1796 // If the file is opened in "write", "append", or "read/write" mode
1797 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1799 char real_path [MAX_OSPATH];
1801 // Open the file on disk directly
1802 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1804 // Create directories up to the file
1805 FS_CreatePath (real_path);
1807 return FS_SysOpen (real_path, mode, nonblocking);
1809 // Else, we look at the various search paths and open the file in read-only mode
1811 return FS_OpenReadFile (filepath, quiet, nonblocking);
1816 ====================
1820 ====================
1822 int FS_Close (qfile_t* file)
1824 if (close (file->handle))
1829 qz_inflateEnd (&file->ztk->zstream);
1830 Mem_Free (file->ztk);
1839 ====================
1842 Write "datasize" bytes into a file
1843 ====================
1845 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1849 // If necessary, seek to the exact file position we're supposed to be
1850 if (file->buff_ind != file->buff_len)
1851 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1853 // Purge cached data
1856 // Write the buffer and update the position
1857 result = write (file->handle, data, (fs_offset_t)datasize);
1858 file->position = lseek (file->handle, 0, SEEK_CUR);
1859 if (file->real_length < file->position)
1860 file->real_length = file->position;
1870 ====================
1873 Read up to "buffersize" bytes from a file
1874 ====================
1876 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1878 fs_offset_t count, done;
1880 if (buffersize == 0)
1883 // Get rid of the ungetc character
1884 if (file->ungetc != EOF)
1886 ((char*)buffer)[0] = file->ungetc;
1894 // First, we copy as many bytes as we can from "buff"
1895 if (file->buff_ind < file->buff_len)
1897 count = file->buff_len - file->buff_ind;
1898 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1900 memcpy (buffer, &file->buff[file->buff_ind], count);
1901 file->buff_ind += count;
1903 buffersize -= count;
1904 if (buffersize == 0)
1908 // NOTE: at this point, the read buffer is always empty
1910 // If the file isn't compressed
1911 if (! (file->flags & QFILE_FLAG_DEFLATED))
1915 // We must take care to not read after the end of the file
1916 count = file->real_length - file->position;
1918 // If we have a lot of data to get, put them directly into "buffer"
1919 if (buffersize > sizeof (file->buff) / 2)
1921 if (count > (fs_offset_t)buffersize)
1922 count = (fs_offset_t)buffersize;
1923 lseek (file->handle, file->offset + file->position, SEEK_SET);
1924 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1928 file->position += nb;
1930 // Purge cached data
1936 if (count > (fs_offset_t)sizeof (file->buff))
1937 count = (fs_offset_t)sizeof (file->buff);
1938 lseek (file->handle, file->offset + file->position, SEEK_SET);
1939 nb = read (file->handle, file->buff, count);
1942 file->buff_len = nb;
1943 file->position += nb;
1945 // Copy the requested data in "buffer" (as much as we can)
1946 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1947 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1948 file->buff_ind = count;
1956 // If the file is compressed, it's more complicated...
1957 // We cycle through a few operations until we have read enough data
1958 while (buffersize > 0)
1960 ztoolkit_t *ztk = file->ztk;
1963 // NOTE: at this point, the read buffer is always empty
1965 // If "input" is also empty, we need to refill it
1966 if (ztk->in_ind == ztk->in_len)
1968 // If we are at the end of the file
1969 if (file->position == file->real_length)
1972 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
1973 if (count > (fs_offset_t)sizeof (ztk->input))
1974 count = (fs_offset_t)sizeof (ztk->input);
1975 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
1976 if (read (file->handle, ztk->input, count) != count)
1978 Con_Printf ("FS_Read: unexpected end of file\n");
1983 ztk->in_len = count;
1984 ztk->in_position += count;
1987 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1988 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
1990 // Now that we are sure we have compressed data available, we need to determine
1991 // if it's better to inflate it in "file->buff" or directly in "buffer"
1993 // Inflate the data in "file->buff"
1994 if (buffersize < sizeof (file->buff) / 2)
1996 ztk->zstream.next_out = file->buff;
1997 ztk->zstream.avail_out = sizeof (file->buff);
1998 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1999 if (error != Z_OK && error != Z_STREAM_END)
2001 Con_Printf ("FS_Read: Can't inflate file\n");
2004 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2006 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2007 file->position += file->buff_len;
2009 // Copy the requested data in "buffer" (as much as we can)
2010 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2011 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2012 file->buff_ind = count;
2015 // Else, we inflate directly in "buffer"
2018 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2019 ztk->zstream.avail_out = (unsigned int)buffersize;
2020 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2021 if (error != Z_OK && error != Z_STREAM_END)
2023 Con_Printf ("FS_Read: Can't inflate file\n");
2026 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2028 // How much data did it inflate?
2029 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2030 file->position += count;
2032 // Purge cached data
2037 buffersize -= count;
2045 ====================
2048 Print a string into a file
2049 ====================
2051 int FS_Print (qfile_t* file, const char *msg)
2053 return (int)FS_Write (file, msg, strlen (msg));
2057 ====================
2060 Print a string into a file
2061 ====================
2063 int FS_Printf(qfile_t* file, const char* format, ...)
2068 va_start (args, format);
2069 result = FS_VPrintf (file, format, args);
2077 ====================
2080 Print a string into a file
2081 ====================
2083 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2086 fs_offset_t buff_size = MAX_INPUTLINE;
2091 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2092 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2093 if (len >= 0 && len < buff_size)
2095 Mem_Free (tempbuff);
2099 len = write (file->handle, tempbuff, len);
2100 Mem_Free (tempbuff);
2107 ====================
2110 Get the next character of a file
2111 ====================
2113 int FS_Getc (qfile_t* file)
2117 if (FS_Read (file, &c, 1) != 1)
2125 ====================
2128 Put a character back into the read buffer (only supports one character!)
2129 ====================
2131 int FS_UnGetc (qfile_t* file, unsigned char c)
2133 // If there's already a character waiting to be read
2134 if (file->ungetc != EOF)
2143 ====================
2146 Move the position index in a file
2147 ====================
2149 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2152 unsigned char* buffer;
2153 fs_offset_t buffersize;
2155 // Compute the file offset
2159 offset += file->position - file->buff_len + file->buff_ind;
2166 offset += file->real_length;
2172 if (offset < 0 || offset > file->real_length)
2175 // If we have the data in our read buffer, we don't need to actually seek
2176 if (file->position - file->buff_len <= offset && offset <= file->position)
2178 file->buff_ind = offset + file->buff_len - file->position;
2182 // Purge cached data
2185 // Unpacked or uncompressed files can seek directly
2186 if (! (file->flags & QFILE_FLAG_DEFLATED))
2188 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2190 file->position = offset;
2194 // Seeking in compressed files is more a hack than anything else,
2195 // but we need to support it, so here we go.
2198 // If we have to go back in the file, we need to restart from the beginning
2199 if (offset <= file->position)
2203 ztk->in_position = 0;
2205 lseek (file->handle, file->offset, SEEK_SET);
2207 // Reset the Zlib stream
2208 ztk->zstream.next_in = ztk->input;
2209 ztk->zstream.avail_in = 0;
2210 qz_inflateReset (&ztk->zstream);
2213 // We need a big buffer to force inflating into it directly
2214 buffersize = 2 * sizeof (file->buff);
2215 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2217 // Skip all data until we reach the requested offset
2218 while (offset > file->position)
2220 fs_offset_t diff = offset - file->position;
2221 fs_offset_t count, len;
2223 count = (diff > buffersize) ? buffersize : diff;
2224 len = FS_Read (file, buffer, count);
2238 ====================
2241 Give the current position in a file
2242 ====================
2244 fs_offset_t FS_Tell (qfile_t* file)
2246 return file->position - file->buff_len + file->buff_ind;
2251 ====================
2254 Give the total size of a file
2255 ====================
2257 fs_offset_t FS_FileSize (qfile_t* file)
2259 return file->real_length;
2264 ====================
2267 Erases any buffered input or output data
2268 ====================
2270 void FS_Purge (qfile_t* file)
2282 Filename are relative to the quake directory.
2283 Always appends a 0 byte.
2286 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2289 unsigned char *buf = NULL;
2290 fs_offset_t filesize = 0;
2292 file = FS_Open (path, "rb", quiet, false);
2295 filesize = file->real_length;
2296 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2297 buf[filesize] = '\0';
2298 FS_Read (file, buf, filesize);
2302 if (filesizepointer)
2303 *filesizepointer = filesize;
2312 The filename will be prefixed by the current game directory
2315 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2319 file = FS_Open (filename, "wb", false, false);
2322 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2326 Con_DPrintf("FS_WriteFile: %s\n", filename);
2327 FS_Write (file, data, len);
2334 =============================================================================
2336 OTHERS PUBLIC FUNCTIONS
2338 =============================================================================
2346 void FS_StripExtension (const char *in, char *out, size_t size_out)
2354 while ((currentchar = *in) && size_out > 1)
2356 if (currentchar == '.')
2358 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2360 *out++ = currentchar;
2376 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2380 // if path doesn't have a .EXT, append extension
2381 // (extension should include the .)
2382 src = path + strlen(path) - 1;
2384 while (*src != '/' && src != path)
2387 return; // it has an extension
2391 strlcat (path, extension, size_path);
2399 Look for a file in the packages and in the filesystem
2402 qboolean FS_FileExists (const char *filename)
2404 return (FS_FindFile (filename, NULL, true) != NULL);
2412 Look for a file in the filesystem only
2415 qboolean FS_SysFileExists (const char *path)
2420 // TODO: use another function instead, to avoid opening the file
2421 desc = open (path, O_RDONLY | O_BINARY);
2430 if (stat (path,&buf) == -1)
2437 void FS_mkdir (const char *path)
2450 Allocate and fill a search structure with information on matching filenames.
2453 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2456 searchpath_t *searchpath;
2458 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2459 stringlist_t resultlist;
2460 stringlist_t dirlist;
2461 const char *slash, *backslash, *colon, *separator;
2463 char netpath[MAX_OSPATH];
2464 char temp[MAX_OSPATH];
2466 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2471 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2475 stringlistinit(&resultlist);
2476 stringlistinit(&dirlist);
2478 slash = strrchr(pattern, '/');
2479 backslash = strrchr(pattern, '\\');
2480 colon = strrchr(pattern, ':');
2481 separator = max(slash, backslash);
2482 separator = max(separator, colon);
2483 basepathlength = separator ? (separator + 1 - pattern) : 0;
2484 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2486 memcpy(basepath, pattern, basepathlength);
2487 basepath[basepathlength] = 0;
2489 // search through the path, one element at a time
2490 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2492 // is the element a pak file?
2493 if (searchpath->pack)
2495 // look through all the pak file elements
2496 pak = searchpath->pack;
2497 for (i = 0;i < pak->numfiles;i++)
2499 strlcpy(temp, pak->files[i].name, sizeof(temp));
2502 if (matchpattern(temp, (char *)pattern, true))
2504 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2505 if (!strcmp(resultlist.strings[resultlistindex], temp))
2507 if (resultlistindex == resultlist.numstrings)
2509 stringlistappend(&resultlist, temp);
2511 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2514 // strip off one path element at a time until empty
2515 // this way directories are added to the listing if they match the pattern
2516 slash = strrchr(temp, '/');
2517 backslash = strrchr(temp, '\\');
2518 colon = strrchr(temp, ':');
2520 if (separator < slash)
2522 if (separator < backslash)
2523 separator = backslash;
2524 if (separator < colon)
2526 *((char *)separator) = 0;
2532 // get a directory listing and look at each name
2533 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2534 stringlistinit(&dirlist);
2535 listdirectory(&dirlist, netpath);
2536 for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
2538 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
2539 if (matchpattern(temp, (char *)pattern, true))
2541 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2542 if (!strcmp(resultlist.strings[resultlistindex], temp))
2544 if (resultlistindex == resultlist.numstrings)
2546 stringlistappend(&resultlist, temp);
2548 Con_DPrintf("SearchDirFile: %s\n", temp);
2552 stringlistfreecontents(&dirlist);
2556 if (resultlist.numstrings)
2558 stringlistsort(&resultlist);
2559 numfiles = resultlist.numstrings;
2561 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2562 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2563 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2564 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2565 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2566 search->numfilenames = (int)numfiles;
2569 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2572 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2573 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2574 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2576 numchars += (int)textlen;
2579 stringlistfreecontents(&resultlist);
2585 void FS_FreeSearch(fssearch_t *search)
2590 extern int con_linewidth;
2591 int FS_ListDirectory(const char *pattern, int oneperline)
2600 char linebuf[MAX_INPUTLINE];
2602 search = FS_Search(pattern, true, true);
2605 numfiles = search->numfilenames;
2608 // FIXME: the names could be added to one column list and then
2609 // gradually shifted into the next column if they fit, and then the
2610 // next to make a compact variable width listing but it's a lot more
2612 // find width for columns
2614 for (i = 0;i < numfiles;i++)
2616 l = (int)strlen(search->filenames[i]);
2617 if (columnwidth < l)
2620 // count the spacing character
2622 // calculate number of columns
2623 numcolumns = con_linewidth / columnwidth;
2624 // don't bother with the column printing if it's only one column
2625 if (numcolumns >= 2)
2627 numlines = (numfiles + numcolumns - 1) / numcolumns;
2628 for (i = 0;i < numlines;i++)
2631 for (k = 0;k < numcolumns;k++)
2633 l = i * numcolumns + k;
2636 name = search->filenames[l];
2637 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2638 linebuf[linebufpos++] = name[j];
2639 // space out name unless it's the last on the line
2640 if (k + 1 < numcolumns && l + 1 < numfiles)
2641 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2642 linebuf[linebufpos++] = ' ';
2645 linebuf[linebufpos] = 0;
2646 Con_Printf("%s\n", linebuf);
2653 for (i = 0;i < numfiles;i++)
2654 Con_Printf("%s\n", search->filenames[i]);
2655 FS_FreeSearch(search);
2656 return (int)numfiles;
2659 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2661 const char *pattern;
2664 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2667 if (Cmd_Argc() == 2)
2668 pattern = Cmd_Argv(1);
2671 if (!FS_ListDirectory(pattern, oneperline))
2672 Con_Print("No files found.\n");
2677 FS_ListDirectoryCmd("dir", true);
2682 FS_ListDirectoryCmd("ls", false);
2685 const char *FS_WhichPack(const char *filename)
2688 searchpath_t *sp = FS_FindFile(filename, &index, true);
2690 return sp->pack->filename;
2696 ====================
2697 FS_IsRegisteredQuakePack
2699 Look for a proof of purchase file file in the requested package
2701 If it is found, this file should NOT be downloaded.
2702 ====================
2704 qboolean FS_IsRegisteredQuakePack(const char *name)
2706 searchpath_t *search;
2709 // search through the path, one element at a time
2710 for (search = fs_searchpaths;search;search = search->next)
2712 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2714 int (*strcmp_funct) (const char* str1, const char* str2);
2715 int left, right, middle;
2718 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2720 // Look for the file (binary search)
2722 right = pak->numfiles - 1;
2723 while (left <= right)
2727 middle = (left + right) / 2;
2728 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2734 // If we're too far in the list
2741 // we found the requested pack but it is not registered quake
2749 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2752 unsigned char *filedata;
2753 fs_offset_t filesize;
2754 if (filesizepointer)
2755 *filesizepointer = 0;
2756 if (!filename || !*filename)
2758 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2761 if (filesizepointer)
2762 *filesizepointer = filesize;
2763 crc = CRC_Block(filedata, filesize);