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;
1017 char userdir[MAX_QPATH];
1019 // Add the common game directory
1020 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1024 // Add the personal game directory
1026 homedir = getenv ("APPDATA");
1027 dpsnprintf(userdir, sizeof(userdir), "%s/%s/", homedir, gameuserdirname);
1029 homedir = getenv ("HOME");
1030 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1034 if(!COM_CheckParm("-appdata")) // TODO make this the default when fs_basedir isn't writable
1036 if(COM_CheckParm("-nohome"))
1040 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1041 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1044 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1053 const char *FS_FileExtension (const char *in)
1055 const char *separator, *backslash, *colon, *dot;
1057 separator = strrchr(in, '/');
1058 backslash = strrchr(in, '\\');
1059 if (!separator || separator < backslash)
1060 separator = backslash;
1061 colon = strrchr(in, ':');
1062 if (!separator || separator < colon)
1065 dot = strrchr(in, '.');
1066 if (dot == NULL || (separator && (dot < separator)))
1078 const char *FS_FileWithoutPath (const char *in)
1080 const char *separator, *backslash, *colon;
1082 separator = strrchr(in, '/');
1083 backslash = strrchr(in, '\\');
1084 if (!separator || separator < backslash)
1085 separator = backslash;
1086 colon = strrchr(in, ':');
1087 if (!separator || separator < colon)
1089 return separator ? separator + 1 : in;
1098 void FS_ClearSearchPath (void)
1100 // unload all packs and directory information, close all pack files
1101 // (if a qfile is still reading a pack it won't be harmed because it used
1102 // dup() to get its own handle already)
1103 while (fs_searchpaths)
1105 searchpath_t *search = fs_searchpaths;
1106 fs_searchpaths = search->next;
1110 close(search->pack->handle);
1111 // free any memory associated with it
1112 if (search->pack->files)
1113 Mem_Free(search->pack->files);
1114 Mem_Free(search->pack);
1126 void FS_Rescan (void)
1129 qboolean fs_modified = false;
1131 FS_ClearSearchPath();
1133 // add the game-specific paths
1134 // gamedirname1 (typically id1)
1135 FS_AddGameHierarchy (gamedirname1);
1136 // update the com_modname (used for server info)
1137 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1139 // add the game-specific path, if any
1140 // (only used for mission packs and the like, which should set fs_modified)
1144 FS_AddGameHierarchy (gamedirname2);
1148 // Adds basedir/gamedir as an override game
1149 // LordHavoc: now supports multiple -game directories
1150 // set the com_modname (reported in server info)
1151 for (i = 0;i < fs_numgamedirs;i++)
1154 FS_AddGameHierarchy (fs_gamedirs[i]);
1155 // update the com_modname (used server info)
1156 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1159 // set the default screenshot name to either the mod name or the
1160 // gamemode screenshot name
1161 if (strcmp(com_modname, gamedirname1))
1162 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1164 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1166 // If "-condebug" is in the command line, remove the previous log file
1167 if (COM_CheckParm ("-condebug") != 0)
1168 unlink (va("%s/qconsole.log", fs_gamedir));
1170 // look for the pop.lmp file and set registered to true if it is found
1171 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1174 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1176 Con_Print("Playing shareware version.\n");
1180 Cvar_Set ("registered", "1");
1181 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1182 Con_Print("Playing registered version.\n");
1185 // unload all wads so that future queries will return the new data
1189 void FS_Rescan_f(void)
1199 extern void Host_SaveConfig (void);
1200 extern void Host_LoadConfig_f (void);
1201 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1205 if (fs_numgamedirs == numgamedirs)
1207 for (i = 0;i < numgamedirs;i++)
1208 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1210 if (i == numgamedirs)
1211 return true; // already using this set of gamedirs, do nothing
1214 if (numgamedirs > MAX_GAMEDIRS)
1217 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1218 return false; // too many gamedirs
1221 for (i = 0;i < numgamedirs;i++)
1223 // if string is nasty, reject it
1224 if(FS_CheckNastyPath(gamedirs[i], true))
1227 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1228 return false; // nasty gamedirs
1232 for (i = 0;i < numgamedirs;i++)
1234 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1237 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1238 return false; // missing gamedirs
1242 // halt demo playback to close the file
1247 fs_numgamedirs = numgamedirs;
1248 for (i = 0;i < fs_numgamedirs;i++)
1249 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1251 // reinitialize filesystem to detect the new paks
1254 // exec the new config
1255 Host_LoadConfig_f();
1257 // unload all sounds so they will be reloaded from the new files as needed
1258 S_UnloadAllSounds_f();
1260 // reinitialize renderer (this reloads hud/console background/etc)
1261 R_Modules_Restart();
1271 void FS_GameDir_f (void)
1275 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1279 Con_Printf("gamedirs active:");
1280 for (i = 0;i < fs_numgamedirs;i++)
1281 Con_Printf(" %s", fs_gamedirs[i]);
1286 numgamedirs = Cmd_Argc() - 1;
1287 if (numgamedirs > MAX_GAMEDIRS)
1289 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1293 for (i = 0;i < numgamedirs;i++)
1294 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1296 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1298 // actually, changing during game would work fine, but would be stupid
1299 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1303 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1312 qboolean FS_CheckGameDir(const char *gamedir)
1316 stringlistinit(&list);
1317 listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
1318 success = list.numstrings > 0;
1319 stringlistfreecontents(&list);
1333 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1335 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1337 // If the base directory is explicitly defined by the compilation process
1338 #ifdef DP_FS_BASEDIR
1339 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1341 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1344 // FIXME: is there a better way to find the directory outside the .app?
1345 if (strstr(com_argv[0], ".app/"))
1349 split = strstr(com_argv[0], ".app/");
1350 while (split > com_argv[0] && *split != '/')
1352 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1353 fs_basedir[split - com_argv[0]] = 0;
1361 // Overrides the system supplied base directory (under GAMENAME)
1362 // 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)
1363 i = COM_CheckParm ("-basedir");
1364 if (i && i < com_argc-1)
1366 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1367 i = (int)strlen (fs_basedir);
1368 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1369 fs_basedir[i-1] = 0;
1372 // add a path separator to the end of the basedir if it lacks one
1373 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1374 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1376 if (!FS_CheckGameDir(gamedirname1))
1377 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1379 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1380 Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1383 // Adds basedir/gamedir as an override game
1384 // LordHavoc: now supports multiple -game directories
1385 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1389 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1392 if (FS_CheckNastyPath(com_argv[i], true))
1393 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1394 if (!FS_CheckGameDir(com_argv[i]))
1395 Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1396 // add the gamedir to the list of active gamedirs
1397 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1402 // generate the searchpath
1406 void FS_Init_Commands(void)
1408 Cvar_RegisterVariable (&scr_screenshot_name);
1409 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1411 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1412 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1413 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1414 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1415 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1423 void FS_Shutdown (void)
1425 // close all pack files and such
1426 // (hopefully there aren't any other open files, but they'll be cleaned up
1427 // by the OS anyway)
1428 FS_ClearSearchPath();
1429 Mem_FreePool (&fs_mempool);
1433 ====================
1436 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1437 ====================
1439 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1445 // Parse the mode string
1454 opt = O_CREAT | O_TRUNC;
1458 opt = O_CREAT | O_APPEND;
1461 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1464 for (ind = 1; mode[ind] != '\0'; ind++)
1475 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1476 filepath, mode, mode[ind]);
1483 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1484 memset (file, 0, sizeof (*file));
1487 file->handle = open (filepath, mod | opt, 0666);
1488 if (file->handle < 0)
1494 file->real_length = lseek (file->handle, 0, SEEK_END);
1496 // For files opened in append mode, we start at the end of the file
1498 file->position = file->real_length;
1500 lseek (file->handle, 0, SEEK_SET);
1510 Open a packed file using its package file descriptor
1513 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1519 pfile = &pack->files[pack_ind];
1521 // If we don't have the true offset, get it now
1522 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1523 if (!PK3_GetTrueFileOffset (pfile, pack))
1526 // No Zlib DLL = no compressed files
1527 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1529 Con_Printf("WARNING: can't open the compressed file %s\n"
1530 "You need the Zlib DLL to use compressed files\n",
1535 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1536 // the dup() call to avoid having to close the dup_handle on error here
1537 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1539 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1540 pfile->name, pack->filename, (int) pfile->offset);
1544 dup_handle = dup (pack->handle);
1547 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1551 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1552 memset (file, 0, sizeof (*file));
1553 file->handle = dup_handle;
1554 file->flags = QFILE_FLAG_PACKED;
1555 file->real_length = pfile->realsize;
1556 file->offset = pfile->offset;
1560 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1564 file->flags |= QFILE_FLAG_DEFLATED;
1566 // We need some more variables
1567 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1569 ztk->comp_length = pfile->packsize;
1571 // Initialize zlib stream
1572 ztk->zstream.next_in = ztk->input;
1573 ztk->zstream.avail_in = 0;
1575 /* From Zlib's "unzip.c":
1577 * windowBits is passed < 0 to tell that there is no zlib header.
1578 * Note that in this case inflate *requires* an extra "dummy" byte
1579 * after the compressed stream in order to complete decompression and
1580 * return Z_STREAM_END.
1581 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1582 * size of both compressed and uncompressed data
1584 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1586 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1592 ztk->zstream.next_out = file->buff;
1593 ztk->zstream.avail_out = sizeof (file->buff);
1602 ====================
1605 Return true if the path should be rejected due to one of the following:
1606 1: path elements that are non-portable
1607 2: path elements that would allow access to files outside the game directory,
1608 or are just not a good idea for a mod to be using.
1609 ====================
1611 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1613 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1617 // Windows: don't allow \ in filenames (windows-only), period.
1618 // (on Windows \ is a directory separator, but / is also supported)
1619 if (strstr(path, "\\"))
1620 return 1; // non-portable
1622 // Mac: don't allow Mac-only filenames - : is a directory separator
1623 // instead of /, but we rely on / working already, so there's no reason to
1624 // support a Mac-only path
1625 // Amiga and Windows: : tries to go to root of drive
1626 if (strstr(path, ":"))
1627 return 1; // non-portable attempt to go to root of drive
1629 // Amiga: // is parent directory
1630 if (strstr(path, "//"))
1631 return 1; // non-portable attempt to go to parent directory
1633 // all: don't allow going to parent directory (../ or /../)
1634 if (strstr(path, ".."))
1635 return 2; // attempt to go outside the game directory
1637 // Windows and UNIXes: don't allow absolute paths
1639 return 2; // attempt to go outside the game directory
1641 // 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
1642 if (strchr(path, '.'))
1646 // gamedir is entirely path elements, so simply forbid . entirely
1649 if (strchr(path, '.') < strrchr(path, '/'))
1650 return 2; // possible attempt to go outside the game directory
1653 // all: forbid trailing slash on gamedir
1654 if (isgamedir && path[strlen(path)-1] == '/')
1657 // all: forbid leading dot on any filename for any reason
1658 if (strstr(path, "/."))
1659 return 2; // attempt to go outside the game directory
1661 // after all these checks we're pretty sure it's a / separated filename
1662 // and won't do much if any harm
1668 ====================
1671 Look for a file in the packages and in the filesystem
1673 Return the searchpath where the file was found (or NULL)
1674 and the file index in the package if relevant
1675 ====================
1677 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1679 searchpath_t *search;
1682 // search through the path, one element at a time
1683 for (search = fs_searchpaths;search;search = search->next)
1685 // is the element a pak file?
1688 int (*strcmp_funct) (const char* str1, const char* str2);
1689 int left, right, middle;
1692 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1694 // Look for the file (binary search)
1696 right = pak->numfiles - 1;
1697 while (left <= right)
1701 middle = (left + right) / 2;
1702 diff = strcmp_funct (pak->files[middle].name, name);
1707 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1709 // yes, but the first one is empty so we treat it as not being there
1710 if (!quiet && developer.integer >= 10)
1711 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1718 if (!quiet && developer.integer >= 10)
1719 Con_Printf("FS_FindFile: %s in %s\n",
1720 pak->files[middle].name, pak->filename);
1727 // If we're too far in the list
1736 char netpath[MAX_OSPATH];
1737 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1738 if (FS_SysFileExists (netpath))
1740 if (!quiet && developer.integer >= 10)
1741 Con_Printf("FS_FindFile: %s\n", netpath);
1750 if (!quiet && developer.integer >= 10)
1751 Con_Printf("FS_FindFile: can't find %s\n", name);
1763 Look for a file in the search paths and open it in read-only mode
1766 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1768 searchpath_t *search;
1771 search = FS_FindFile (filename, &pack_ind, quiet);
1777 // Found in the filesystem?
1780 char path [MAX_OSPATH];
1781 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1782 return FS_SysOpen (path, "rb", nonblocking);
1785 // So, we found it in a package...
1786 return FS_OpenPackedFile (search->pack, pack_ind);
1791 =============================================================================
1793 MAIN PUBLIC FUNCTIONS
1795 =============================================================================
1799 ====================
1802 Open a file. The syntax is the same as fopen
1803 ====================
1805 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1807 if (FS_CheckNastyPath(filepath, false))
1809 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1813 // If the file is opened in "write", "append", or "read/write" mode
1814 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1816 char real_path [MAX_OSPATH];
1818 // Open the file on disk directly
1819 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1821 // Create directories up to the file
1822 FS_CreatePath (real_path);
1824 return FS_SysOpen (real_path, mode, nonblocking);
1826 // Else, we look at the various search paths and open the file in read-only mode
1828 return FS_OpenReadFile (filepath, quiet, nonblocking);
1833 ====================
1837 ====================
1839 int FS_Close (qfile_t* file)
1841 if (close (file->handle))
1846 qz_inflateEnd (&file->ztk->zstream);
1847 Mem_Free (file->ztk);
1856 ====================
1859 Write "datasize" bytes into a file
1860 ====================
1862 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1866 // If necessary, seek to the exact file position we're supposed to be
1867 if (file->buff_ind != file->buff_len)
1868 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1870 // Purge cached data
1873 // Write the buffer and update the position
1874 result = write (file->handle, data, (fs_offset_t)datasize);
1875 file->position = lseek (file->handle, 0, SEEK_CUR);
1876 if (file->real_length < file->position)
1877 file->real_length = file->position;
1887 ====================
1890 Read up to "buffersize" bytes from a file
1891 ====================
1893 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1895 fs_offset_t count, done;
1897 if (buffersize == 0)
1900 // Get rid of the ungetc character
1901 if (file->ungetc != EOF)
1903 ((char*)buffer)[0] = file->ungetc;
1911 // First, we copy as many bytes as we can from "buff"
1912 if (file->buff_ind < file->buff_len)
1914 count = file->buff_len - file->buff_ind;
1915 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1917 memcpy (buffer, &file->buff[file->buff_ind], count);
1918 file->buff_ind += count;
1920 buffersize -= count;
1921 if (buffersize == 0)
1925 // NOTE: at this point, the read buffer is always empty
1927 // If the file isn't compressed
1928 if (! (file->flags & QFILE_FLAG_DEFLATED))
1932 // We must take care to not read after the end of the file
1933 count = file->real_length - file->position;
1935 // If we have a lot of data to get, put them directly into "buffer"
1936 if (buffersize > sizeof (file->buff) / 2)
1938 if (count > (fs_offset_t)buffersize)
1939 count = (fs_offset_t)buffersize;
1940 lseek (file->handle, file->offset + file->position, SEEK_SET);
1941 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1945 file->position += nb;
1947 // Purge cached data
1953 if (count > (fs_offset_t)sizeof (file->buff))
1954 count = (fs_offset_t)sizeof (file->buff);
1955 lseek (file->handle, file->offset + file->position, SEEK_SET);
1956 nb = read (file->handle, file->buff, count);
1959 file->buff_len = nb;
1960 file->position += nb;
1962 // Copy the requested data in "buffer" (as much as we can)
1963 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1964 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1965 file->buff_ind = count;
1973 // If the file is compressed, it's more complicated...
1974 // We cycle through a few operations until we have read enough data
1975 while (buffersize > 0)
1977 ztoolkit_t *ztk = file->ztk;
1980 // NOTE: at this point, the read buffer is always empty
1982 // If "input" is also empty, we need to refill it
1983 if (ztk->in_ind == ztk->in_len)
1985 // If we are at the end of the file
1986 if (file->position == file->real_length)
1989 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
1990 if (count > (fs_offset_t)sizeof (ztk->input))
1991 count = (fs_offset_t)sizeof (ztk->input);
1992 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
1993 if (read (file->handle, ztk->input, count) != count)
1995 Con_Printf ("FS_Read: unexpected end of file\n");
2000 ztk->in_len = count;
2001 ztk->in_position += count;
2004 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2005 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2007 // Now that we are sure we have compressed data available, we need to determine
2008 // if it's better to inflate it in "file->buff" or directly in "buffer"
2010 // Inflate the data in "file->buff"
2011 if (buffersize < sizeof (file->buff) / 2)
2013 ztk->zstream.next_out = file->buff;
2014 ztk->zstream.avail_out = sizeof (file->buff);
2015 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2016 if (error != Z_OK && error != Z_STREAM_END)
2018 Con_Printf ("FS_Read: Can't inflate file\n");
2021 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2023 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2024 file->position += file->buff_len;
2026 // Copy the requested data in "buffer" (as much as we can)
2027 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2028 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2029 file->buff_ind = count;
2032 // Else, we inflate directly in "buffer"
2035 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2036 ztk->zstream.avail_out = (unsigned int)buffersize;
2037 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2038 if (error != Z_OK && error != Z_STREAM_END)
2040 Con_Printf ("FS_Read: Can't inflate file\n");
2043 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2045 // How much data did it inflate?
2046 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2047 file->position += count;
2049 // Purge cached data
2054 buffersize -= count;
2062 ====================
2065 Print a string into a file
2066 ====================
2068 int FS_Print (qfile_t* file, const char *msg)
2070 return (int)FS_Write (file, msg, strlen (msg));
2074 ====================
2077 Print a string into a file
2078 ====================
2080 int FS_Printf(qfile_t* file, const char* format, ...)
2085 va_start (args, format);
2086 result = FS_VPrintf (file, format, args);
2094 ====================
2097 Print a string into a file
2098 ====================
2100 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2103 fs_offset_t buff_size = MAX_INPUTLINE;
2108 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2109 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2110 if (len >= 0 && len < buff_size)
2112 Mem_Free (tempbuff);
2116 len = write (file->handle, tempbuff, len);
2117 Mem_Free (tempbuff);
2124 ====================
2127 Get the next character of a file
2128 ====================
2130 int FS_Getc (qfile_t* file)
2134 if (FS_Read (file, &c, 1) != 1)
2142 ====================
2145 Put a character back into the read buffer (only supports one character!)
2146 ====================
2148 int FS_UnGetc (qfile_t* file, unsigned char c)
2150 // If there's already a character waiting to be read
2151 if (file->ungetc != EOF)
2160 ====================
2163 Move the position index in a file
2164 ====================
2166 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2169 unsigned char* buffer;
2170 fs_offset_t buffersize;
2172 // Compute the file offset
2176 offset += file->position - file->buff_len + file->buff_ind;
2183 offset += file->real_length;
2189 if (offset < 0 || offset > file->real_length)
2192 // If we have the data in our read buffer, we don't need to actually seek
2193 if (file->position - file->buff_len <= offset && offset <= file->position)
2195 file->buff_ind = offset + file->buff_len - file->position;
2199 // Purge cached data
2202 // Unpacked or uncompressed files can seek directly
2203 if (! (file->flags & QFILE_FLAG_DEFLATED))
2205 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2207 file->position = offset;
2211 // Seeking in compressed files is more a hack than anything else,
2212 // but we need to support it, so here we go.
2215 // If we have to go back in the file, we need to restart from the beginning
2216 if (offset <= file->position)
2220 ztk->in_position = 0;
2222 lseek (file->handle, file->offset, SEEK_SET);
2224 // Reset the Zlib stream
2225 ztk->zstream.next_in = ztk->input;
2226 ztk->zstream.avail_in = 0;
2227 qz_inflateReset (&ztk->zstream);
2230 // We need a big buffer to force inflating into it directly
2231 buffersize = 2 * sizeof (file->buff);
2232 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2234 // Skip all data until we reach the requested offset
2235 while (offset > file->position)
2237 fs_offset_t diff = offset - file->position;
2238 fs_offset_t count, len;
2240 count = (diff > buffersize) ? buffersize : diff;
2241 len = FS_Read (file, buffer, count);
2255 ====================
2258 Give the current position in a file
2259 ====================
2261 fs_offset_t FS_Tell (qfile_t* file)
2263 return file->position - file->buff_len + file->buff_ind;
2268 ====================
2271 Give the total size of a file
2272 ====================
2274 fs_offset_t FS_FileSize (qfile_t* file)
2276 return file->real_length;
2281 ====================
2284 Erases any buffered input or output data
2285 ====================
2287 void FS_Purge (qfile_t* file)
2299 Filename are relative to the quake directory.
2300 Always appends a 0 byte.
2303 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2306 unsigned char *buf = NULL;
2307 fs_offset_t filesize = 0;
2309 file = FS_Open (path, "rb", quiet, false);
2312 filesize = file->real_length;
2313 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2314 buf[filesize] = '\0';
2315 FS_Read (file, buf, filesize);
2319 if (filesizepointer)
2320 *filesizepointer = filesize;
2329 The filename will be prefixed by the current game directory
2332 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2336 file = FS_Open (filename, "wb", false, false);
2339 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2343 Con_DPrintf("FS_WriteFile: %s\n", filename);
2344 FS_Write (file, data, len);
2351 =============================================================================
2353 OTHERS PUBLIC FUNCTIONS
2355 =============================================================================
2363 void FS_StripExtension (const char *in, char *out, size_t size_out)
2371 while ((currentchar = *in) && size_out > 1)
2373 if (currentchar == '.')
2375 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2377 *out++ = currentchar;
2393 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2397 // if path doesn't have a .EXT, append extension
2398 // (extension should include the .)
2399 src = path + strlen(path) - 1;
2401 while (*src != '/' && src != path)
2404 return; // it has an extension
2408 strlcat (path, extension, size_path);
2416 Look for a file in the packages and in the filesystem
2419 qboolean FS_FileExists (const char *filename)
2421 return (FS_FindFile (filename, NULL, true) != NULL);
2429 Look for a file in the filesystem only
2432 qboolean FS_SysFileExists (const char *path)
2437 // TODO: use another function instead, to avoid opening the file
2438 desc = open (path, O_RDONLY | O_BINARY);
2447 if (stat (path,&buf) == -1)
2454 void FS_mkdir (const char *path)
2467 Allocate and fill a search structure with information on matching filenames.
2470 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2473 searchpath_t *searchpath;
2475 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2476 stringlist_t resultlist;
2477 stringlist_t dirlist;
2478 const char *slash, *backslash, *colon, *separator;
2480 char netpath[MAX_OSPATH];
2481 char temp[MAX_OSPATH];
2483 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2488 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2492 stringlistinit(&resultlist);
2493 stringlistinit(&dirlist);
2495 slash = strrchr(pattern, '/');
2496 backslash = strrchr(pattern, '\\');
2497 colon = strrchr(pattern, ':');
2498 separator = max(slash, backslash);
2499 separator = max(separator, colon);
2500 basepathlength = separator ? (separator + 1 - pattern) : 0;
2501 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2503 memcpy(basepath, pattern, basepathlength);
2504 basepath[basepathlength] = 0;
2506 // search through the path, one element at a time
2507 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2509 // is the element a pak file?
2510 if (searchpath->pack)
2512 // look through all the pak file elements
2513 pak = searchpath->pack;
2514 for (i = 0;i < pak->numfiles;i++)
2516 strlcpy(temp, pak->files[i].name, sizeof(temp));
2519 if (matchpattern(temp, (char *)pattern, true))
2521 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2522 if (!strcmp(resultlist.strings[resultlistindex], temp))
2524 if (resultlistindex == resultlist.numstrings)
2526 stringlistappend(&resultlist, temp);
2528 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2531 // strip off one path element at a time until empty
2532 // this way directories are added to the listing if they match the pattern
2533 slash = strrchr(temp, '/');
2534 backslash = strrchr(temp, '\\');
2535 colon = strrchr(temp, ':');
2537 if (separator < slash)
2539 if (separator < backslash)
2540 separator = backslash;
2541 if (separator < colon)
2543 *((char *)separator) = 0;
2549 // get a directory listing and look at each name
2550 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2551 stringlistinit(&dirlist);
2552 listdirectory(&dirlist, netpath);
2553 for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
2555 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
2556 if (matchpattern(temp, (char *)pattern, true))
2558 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2559 if (!strcmp(resultlist.strings[resultlistindex], temp))
2561 if (resultlistindex == resultlist.numstrings)
2563 stringlistappend(&resultlist, temp);
2565 Con_DPrintf("SearchDirFile: %s\n", temp);
2569 stringlistfreecontents(&dirlist);
2573 if (resultlist.numstrings)
2575 stringlistsort(&resultlist);
2576 numfiles = resultlist.numstrings;
2578 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2579 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2580 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2581 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2582 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2583 search->numfilenames = (int)numfiles;
2586 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2589 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2590 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2591 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2593 numchars += (int)textlen;
2596 stringlistfreecontents(&resultlist);
2602 void FS_FreeSearch(fssearch_t *search)
2607 extern int con_linewidth;
2608 int FS_ListDirectory(const char *pattern, int oneperline)
2617 char linebuf[MAX_INPUTLINE];
2619 search = FS_Search(pattern, true, true);
2622 numfiles = search->numfilenames;
2625 // FIXME: the names could be added to one column list and then
2626 // gradually shifted into the next column if they fit, and then the
2627 // next to make a compact variable width listing but it's a lot more
2629 // find width for columns
2631 for (i = 0;i < numfiles;i++)
2633 l = (int)strlen(search->filenames[i]);
2634 if (columnwidth < l)
2637 // count the spacing character
2639 // calculate number of columns
2640 numcolumns = con_linewidth / columnwidth;
2641 // don't bother with the column printing if it's only one column
2642 if (numcolumns >= 2)
2644 numlines = (numfiles + numcolumns - 1) / numcolumns;
2645 for (i = 0;i < numlines;i++)
2648 for (k = 0;k < numcolumns;k++)
2650 l = i * numcolumns + k;
2653 name = search->filenames[l];
2654 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2655 linebuf[linebufpos++] = name[j];
2656 // space out name unless it's the last on the line
2657 if (k + 1 < numcolumns && l + 1 < numfiles)
2658 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2659 linebuf[linebufpos++] = ' ';
2662 linebuf[linebufpos] = 0;
2663 Con_Printf("%s\n", linebuf);
2670 for (i = 0;i < numfiles;i++)
2671 Con_Printf("%s\n", search->filenames[i]);
2672 FS_FreeSearch(search);
2673 return (int)numfiles;
2676 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2678 const char *pattern;
2681 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2684 if (Cmd_Argc() == 2)
2685 pattern = Cmd_Argv(1);
2688 if (!FS_ListDirectory(pattern, oneperline))
2689 Con_Print("No files found.\n");
2694 FS_ListDirectoryCmd("dir", true);
2699 FS_ListDirectoryCmd("ls", false);
2702 const char *FS_WhichPack(const char *filename)
2705 searchpath_t *sp = FS_FindFile(filename, &index, true);
2707 return sp->pack->filename;
2713 ====================
2714 FS_IsRegisteredQuakePack
2716 Look for a proof of purchase file file in the requested package
2718 If it is found, this file should NOT be downloaded.
2719 ====================
2721 qboolean FS_IsRegisteredQuakePack(const char *name)
2723 searchpath_t *search;
2726 // search through the path, one element at a time
2727 for (search = fs_searchpaths;search;search = search->next)
2729 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
2731 int (*strcmp_funct) (const char* str1, const char* str2);
2732 int left, right, middle;
2735 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
2737 // Look for the file (binary search)
2739 right = pak->numfiles - 1;
2740 while (left <= right)
2744 middle = (left + right) / 2;
2745 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
2751 // If we're too far in the list
2758 // we found the requested pack but it is not registered quake
2766 int FS_CRCFile(const char *filename, size_t *filesizepointer)
2769 unsigned char *filedata;
2770 fs_offset_t filesize;
2771 if (filesizepointer)
2772 *filesizepointer = 0;
2773 if (!filename || !*filename)
2775 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
2778 if (filesizepointer)
2779 *filesizepointer = filesize;
2780 crc = CRC_Block(filedata, filesize);