4 Copyright (C) 2003-2006 Mathieu Olivier
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to:
20 Free Software Foundation, Inc.
21 59 Temple Place - Suite 330
22 Boston, MA 02111-1307, USA
36 # include <sys/stat.h>
43 // Win32 requires us to add O_BINARY, but the other OSes don't have it
48 // In case the system doesn't support the O_NONBLOCK flag
53 // largefile support for Win32
55 # define lseek _lseeki64
59 // suppress deprecated warnings
60 # include <sys/stat.h>
65 # define unlink _unlink
71 All of Quake's data access is through a hierchal file system, but the contents
72 of the file system can be transparently merged from several sources.
74 The "base directory" is the path to the directory holding the quake.exe and
75 all game directories. The sys_* files pass this to host_init in
76 quakeparms_t->basedir. This can be overridden with the "-basedir" command
77 line parm to allow code debugging in a different directory. The base
78 directory is only used during filesystem initialization.
80 The "game directory" is the first tree on the search path and directory that
81 all generated files (savegames, screenshots, demos, config files) will be
82 saved to. This can be overridden with the "-game" command line parameter.
83 The game directory can never be changed while quake is executing. This is a
84 precaution against having a malicious server instruct clients to write files
85 over areas they shouldn't.
91 =============================================================================
95 =============================================================================
98 // Magic numbers of a ZIP file (big-endian format)
99 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
100 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
101 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
103 // Other constants for ZIP files
104 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
105 #define ZIP_END_CDIR_SIZE 22
106 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
107 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
109 // Zlib constants (from zlib.h)
110 #define Z_SYNC_FLUSH 2
113 #define Z_STREAM_END 1
114 #define ZLIB_VERSION "1.2.3"
116 // Uncomment the following line if the zlib DLL you have still uses
117 // the 1.1.x series calling convention on Win32 (WINAPI)
118 //#define ZLIB_USES_WINAPI
122 =============================================================================
126 =============================================================================
129 // Zlib stream (from zlib.h)
130 // Warning: some pointers we don't use directly have
131 // been cast to "void*" for a matter of simplicity
134 unsigned char *next_in; // next input byte
135 unsigned int avail_in; // number of bytes available at next_in
136 unsigned long total_in; // total nb of input bytes read so far
138 unsigned char *next_out; // next output byte should be put there
139 unsigned int avail_out; // remaining free space at next_out
140 unsigned long total_out; // total nb of bytes output so far
142 char *msg; // last error message, NULL if no error
143 void *state; // not visible by applications
145 void *zalloc; // used to allocate the internal state
146 void *zfree; // used to free the internal state
147 void *opaque; // private data object passed to zalloc and zfree
149 int data_type; // best guess about the data type: ascii or binary
150 unsigned long adler; // adler32 value of the uncompressed data
151 unsigned long reserved; // reserved for future use
155 // inside a package (PAK or PK3)
156 #define QFILE_FLAG_PACKED (1 << 0)
157 // file is compressed using the deflate algorithm (PK3 only)
158 #define QFILE_FLAG_DEFLATED (1 << 1)
160 #define FILE_BUFF_SIZE 2048
164 size_t comp_length; // length of the compressed file
165 size_t in_ind, in_len; // input buffer current index and length
166 size_t in_position; // position in the compressed file
167 unsigned char input [FILE_BUFF_SIZE];
173 int handle; // file descriptor
174 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
175 fs_offset_t position; // current position in the file
176 fs_offset_t offset; // offset into the package (0 if external file)
177 int ungetc; // single stored character from ungetc, cleared to EOF when read
180 fs_offset_t buff_ind, buff_len; // buffer current index and length
181 unsigned char buff [FILE_BUFF_SIZE];
188 // ------ PK3 files on disk ------ //
190 // You can get the complete ZIP format description from PKWARE website
192 typedef struct pk3_endOfCentralDir_s
194 unsigned int signature;
195 unsigned short disknum;
196 unsigned short cdir_disknum; // number of the disk with the start of the central directory
197 unsigned short localentries; // number of entries in the central directory on this disk
198 unsigned short nbentries; // total number of entries in the central directory on this disk
199 unsigned int cdir_size; // size of the central directory
200 unsigned int cdir_offset; // with respect to the starting disk number
201 unsigned short comment_size;
202 } pk3_endOfCentralDir_t;
205 // ------ PAK files on disk ------ //
206 typedef struct dpackfile_s
209 int filepos, filelen;
212 typedef struct dpackheader_s
220 // Packages in memory
221 // the offset in packfile_t is the true contents offset
222 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
223 // file compressed using the deflate algorithm
224 #define PACKFILE_FLAG_DEFLATED (1 << 1)
225 // file is a symbolic link
226 #define PACKFILE_FLAG_SYMLINK (1 << 2)
228 typedef struct packfile_s
230 char name [MAX_QPATH];
233 fs_offset_t packsize; // size in the package
234 fs_offset_t realsize; // real file size (uncompressed)
237 typedef struct pack_s
239 char filename [MAX_OSPATH];
241 int ignorecase; // PK3 ignores case
247 // Search paths for files (including packages)
248 typedef struct searchpath_s
250 // only one of filename / pack will be used
251 char filename[MAX_OSPATH];
253 struct searchpath_s *next;
258 =============================================================================
262 =============================================================================
268 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
269 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
270 fs_offset_t offset, fs_offset_t packsize,
271 fs_offset_t realsize, int flags);
275 =============================================================================
279 =============================================================================
282 mempool_t *fs_mempool;
284 searchpath_t *fs_searchpaths = NULL;
286 #define MAX_FILES_IN_PACK 65536
288 char fs_gamedir[MAX_OSPATH];
289 char fs_basedir[MAX_OSPATH];
291 // list of active game directories (empty if not running a mod)
292 int fs_numgamedirs = 0;
293 char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
295 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)"};
296 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"};
300 =============================================================================
302 PRIVATE FUNCTIONS - PK3 HANDLING
304 =============================================================================
307 // Functions exported from zlib
308 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
309 # define ZEXPORT WINAPI
314 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
315 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
316 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
317 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
319 #define qz_inflateInit2(strm, windowBits) \
320 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
322 static dllfunction_t zlibfuncs[] =
324 {"inflate", (void **) &qz_inflate},
325 {"inflateEnd", (void **) &qz_inflateEnd},
326 {"inflateInit2_", (void **) &qz_inflateInit2_},
327 {"inflateReset", (void **) &qz_inflateReset},
331 // Handle for Zlib DLL
332 static dllhandle_t zlib_dll = NULL;
335 static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
336 static dllfunction_t shfolderfuncs[] =
338 {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
341 static dllhandle_t shfolder_dll = NULL;
351 void PK3_CloseLibrary (void)
353 Sys_UnloadLibrary (&zlib_dll);
361 Try to load the Zlib DLL
364 qboolean PK3_OpenLibrary (void)
366 const char* dllnames [] =
371 # ifdef ZLIB_USES_WINAPI
377 #elif defined(MACOSX)
391 return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
397 PK3_GetEndOfCentralDir
399 Extract the end of the central directory from a PK3 package
402 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
404 fs_offset_t filesize, maxsize;
405 unsigned char *buffer, *ptr;
408 // Get the package size
409 filesize = lseek (packhandle, 0, SEEK_END);
410 if (filesize < ZIP_END_CDIR_SIZE)
413 // Load the end of the file in memory
414 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
417 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
418 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
419 lseek (packhandle, filesize - maxsize, SEEK_SET);
420 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
426 // Look for the end of central dir signature around the end of the file
427 maxsize -= ZIP_END_CDIR_SIZE;
428 ptr = &buffer[maxsize];
430 while (BuffBigLong (ptr) != ZIP_END_HEADER)
442 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
443 eocd->signature = LittleLong (eocd->signature);
444 eocd->disknum = LittleShort (eocd->disknum);
445 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
446 eocd->localentries = LittleShort (eocd->localentries);
447 eocd->nbentries = LittleShort (eocd->nbentries);
448 eocd->cdir_size = LittleLong (eocd->cdir_size);
449 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
450 eocd->comment_size = LittleShort (eocd->comment_size);
462 Extract the file list from a PK3 file
465 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
467 unsigned char *central_dir, *ptr;
469 fs_offset_t remaining;
471 // Load the central directory in memory
472 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
473 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
474 read (pack->handle, central_dir, eocd->cdir_size);
476 // Extract the files properties
477 // The parsing is done "by hand" because some fields have variable sizes and
478 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
479 remaining = eocd->cdir_size;
482 for (ind = 0; ind < eocd->nbentries; ind++)
484 fs_offset_t namesize, count;
486 // Checking the remaining size
487 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
489 Mem_Free (central_dir);
492 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
495 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
497 Mem_Free (central_dir);
501 namesize = BuffLittleShort (&ptr[28]); // filename length
503 // Check encryption, compression, and attributes
504 // 1st uint8 : general purpose bit flag
505 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
507 // LordHavoc: bit 3 would be a problem if we were scanning the archive
508 // but is not a problem in the central directory where the values are
511 // bit 3 seems to always be set by the standard Mac OSX zip maker
513 // 2nd uint8 : external file attributes
514 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
515 if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
517 // Still enough bytes for the name?
518 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
520 Mem_Free (central_dir);
524 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
525 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
527 char filename [sizeof (pack->files[0].name)];
528 fs_offset_t offset, packsize, realsize;
531 // Extract the name (strip it if necessary)
532 namesize = min(namesize, (int)sizeof (filename) - 1);
533 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
534 filename[namesize] = '\0';
536 if (BuffLittleShort (&ptr[10]))
537 flags = PACKFILE_FLAG_DEFLATED;
540 offset = BuffLittleLong (&ptr[42]);
541 packsize = BuffLittleLong (&ptr[20]);
542 realsize = BuffLittleLong (&ptr[24]);
544 switch(ptr[5]) // C_VERSION_MADE_BY_1
549 if((BuffLittleShort(&ptr[40]) & 0120000) == 0120000)
550 // can't use S_ISLNK here, as this has to compile on non-UNIX too
551 flags |= PACKFILE_FLAG_SYMLINK;
555 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
559 // Skip the name, additionnal field, and comment
560 // 1er uint16 : extra field length
561 // 2eme uint16 : file comment length
562 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
563 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
567 // If the package is empty, central_dir is NULL here
568 if (central_dir != NULL)
569 Mem_Free (central_dir);
570 return pack->numfiles;
578 Create a package entry associated with a PK3 file
581 pack_t *FS_LoadPackPK3 (const char *packfile)
584 pk3_endOfCentralDir_t eocd;
589 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
591 packhandle = open (packfile, O_RDONLY | O_BINARY);
596 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
598 Con_Printf ("%s is not a PK3 file\n", packfile);
603 // Multi-volume ZIP archives are NOT allowed
604 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
606 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
611 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
612 // since eocd.nbentries is an unsigned 16 bits integer
613 #if MAX_FILES_IN_PACK < 65535
614 if (eocd.nbentries > MAX_FILES_IN_PACK)
616 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
622 // Create a package structure in memory
623 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
624 pack->ignorecase = true; // PK3 ignores case
625 strlcpy (pack->filename, packfile, sizeof (pack->filename));
626 pack->handle = packhandle;
627 pack->numfiles = eocd.nbentries;
628 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
630 real_nb_files = PK3_BuildFileList (pack, &eocd);
631 if (real_nb_files < 0)
633 Con_Printf ("%s is not a valid PK3 file\n", packfile);
639 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
646 PK3_GetTrueFileOffset
648 Find where the true file data offset is
651 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
653 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
657 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
660 // Load the local file description
661 lseek (pack->handle, pfile->offset, SEEK_SET);
662 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
663 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
665 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
669 // Skip name and extra field
670 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
672 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
678 =============================================================================
680 OTHER PRIVATE FUNCTIONS
682 =============================================================================
690 Add a file to the list of files contained into a package
693 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
694 fs_offset_t offset, fs_offset_t packsize,
695 fs_offset_t realsize, int flags)
697 int (*strcmp_funct) (const char* str1, const char* str2);
698 int left, right, middle;
701 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
703 // Look for the slot we should put that file into (binary search)
705 right = pack->numfiles - 1;
706 while (left <= right)
710 middle = (left + right) / 2;
711 diff = strcmp_funct (pack->files[middle].name, name);
713 // If we found the file, there's a problem
715 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
717 // If we're too far in the list
724 // We have to move the right of the list by one slot to free the one we need
725 pfile = &pack->files[left];
726 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
729 strlcpy (pfile->name, name, sizeof (pfile->name));
730 pfile->offset = offset;
731 pfile->packsize = packsize;
732 pfile->realsize = realsize;
733 pfile->flags = flags;
743 Only used for FS_Open.
746 void FS_CreatePath (char *path)
750 for (ofs = path+1 ; *ofs ; ofs++)
752 if (*ofs == '/' || *ofs == '\\')
754 // create the directory
770 void FS_Path_f (void)
774 Con_Print("Current search path:\n");
775 for (s=fs_searchpaths ; s ; s=s->next)
778 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
780 Con_Printf("%s\n", s->filename);
789 Takes an explicit (not game tree related) path to a pak file.
791 Loads the header and directory, adding the files at the beginning
792 of the list so they override previous pack files.
795 pack_t *FS_LoadPackPAK (const char *packfile)
797 dpackheader_t header;
804 _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
806 packhandle = open (packfile, O_RDONLY | O_BINARY);
810 read (packhandle, (void *)&header, sizeof(header));
811 if (memcmp(header.id, "PACK", 4))
813 Con_Printf ("%s is not a packfile\n", packfile);
817 header.dirofs = LittleLong (header.dirofs);
818 header.dirlen = LittleLong (header.dirlen);
820 if (header.dirlen % sizeof(dpackfile_t))
822 Con_Printf ("%s has an invalid directory size\n", packfile);
827 numpackfiles = header.dirlen / sizeof(dpackfile_t);
829 if (numpackfiles > MAX_FILES_IN_PACK)
831 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
836 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
837 lseek (packhandle, header.dirofs, SEEK_SET);
838 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
840 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
846 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
847 pack->ignorecase = false; // PAK is case sensitive
848 strlcpy (pack->filename, packfile, sizeof (pack->filename));
849 pack->handle = packhandle;
851 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
853 // parse the directory
854 for (i = 0;i < numpackfiles;i++)
856 fs_offset_t offset = LittleLong (info[i].filepos);
857 fs_offset_t size = LittleLong (info[i].filelen);
859 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
864 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
872 Adds the given pack to the search path.
873 The pack type is autodetected by the file extension.
875 Returns true if the file was successfully added to the
876 search path or if it was already included.
878 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
882 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
884 searchpath_t *search;
886 const char *ext = FS_FileExtension(pakfile);
888 for(search = fs_searchpaths; search; search = search->next)
890 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
893 *already_loaded = true;
894 return true; // already loaded
899 *already_loaded = false;
901 if(!strcasecmp(ext, "pak"))
902 pak = FS_LoadPackPAK (pakfile);
903 else if(!strcasecmp(ext, "pk3"))
904 pak = FS_LoadPackPK3 (pakfile);
906 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
912 // find the first item whose next one is a pack or NULL
913 searchpath_t *insertion_point = 0;
914 if(fs_searchpaths && !fs_searchpaths->pack)
916 insertion_point = fs_searchpaths;
919 if(!insertion_point->next)
921 if(insertion_point->next->pack)
923 insertion_point = insertion_point->next;
926 // If insertion_point is NULL, this means that either there is no
927 // item in the list yet, or that the very first item is a pack. In
928 // that case, we want to insert at the beginning...
931 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
933 search->next = fs_searchpaths;
934 fs_searchpaths = search;
937 // otherwise we want to append directly after insertion_point.
939 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
941 search->next = insertion_point->next;
942 insertion_point->next = search;
947 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
949 search->next = fs_searchpaths;
950 fs_searchpaths = search;
956 Con_Printf("unable to load pak \"%s\"\n", pakfile);
966 Adds the given pack to the search path and searches for it in the game path.
967 The pack type is autodetected by the file extension.
969 Returns true if the file was successfully added to the
970 search path or if it was already included.
972 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
976 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
978 char fullpath[MAX_QPATH];
980 searchpath_t *search;
983 *already_loaded = false;
985 // then find the real name...
986 search = FS_FindFile(pakfile, &index, true);
987 if(!search || search->pack)
989 Con_Printf("could not find pak \"%s\"\n", pakfile);
993 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
995 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
1003 Sets fs_gamedir, adds the directory to the head of the path,
1004 then loads and adds pak1.pak pak2.pak ...
1007 void FS_AddGameDirectory (const char *dir)
1011 searchpath_t *search;
1013 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
1015 stringlistinit(&list);
1016 listdirectory(&list, "", dir);
1017 stringlistsort(&list);
1019 // add any PAK package in the directory
1020 for (i = 0;i < list.numstrings;i++)
1022 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
1024 FS_AddPack_Fullpath(list.strings[i], NULL, false);
1028 // add any PK3 package in the directory
1029 for (i = 0;i < list.numstrings;i++)
1031 if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
1033 FS_AddPack_Fullpath(list.strings[i], NULL, false);
1037 stringlistfreecontents(&list);
1039 // Add the directory to the search path
1040 // (unpacked files have the priority over packed files)
1041 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1042 strlcpy (search->filename, dir, sizeof (search->filename));
1043 search->next = fs_searchpaths;
1044 fs_searchpaths = search;
1053 void FS_AddGameHierarchy (const char *dir)
1056 char userdir[MAX_QPATH];
1058 TCHAR mydocsdir[MAX_PATH + 1];
1059 #if _MSC_VER >= 1400
1065 // Add the common game directory
1066 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1070 // Add the personal game directory
1072 if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
1074 dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
1075 Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", userdir);
1079 // use the environment
1080 #if _MSC_VER >= 1400
1081 _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
1083 homedir = getenv("USERPROFILE");
1088 dpsnprintf(userdir, sizeof(userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
1089 #if _MSC_VER >= 1400
1092 Con_DPrintf("Obtained personal directory %s from environment\n", userdir);
1095 *userdir = 0; // just to make sure it hasn't been written to by SHGetFolderPath returning failure
1099 Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
1101 homedir = getenv ("HOME");
1103 dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
1106 Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
1111 if(!COM_CheckParm("-mygames"))
1113 #if _MSC_VER >= 1400
1115 _sopen_s(&fd, va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
1117 int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
1122 *userdir = 0; // we have write access to the game dir, so let's use it
1127 if(COM_CheckParm("-nohome"))
1130 if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
1131 dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
1134 FS_AddGameDirectory(va("%s%s/", userdir, dir));
1143 const char *FS_FileExtension (const char *in)
1145 const char *separator, *backslash, *colon, *dot;
1147 separator = strrchr(in, '/');
1148 backslash = strrchr(in, '\\');
1149 if (!separator || separator < backslash)
1150 separator = backslash;
1151 colon = strrchr(in, ':');
1152 if (!separator || separator < colon)
1155 dot = strrchr(in, '.');
1156 if (dot == NULL || (separator && (dot < separator)))
1168 const char *FS_FileWithoutPath (const char *in)
1170 const char *separator, *backslash, *colon;
1172 separator = strrchr(in, '/');
1173 backslash = strrchr(in, '\\');
1174 if (!separator || separator < backslash)
1175 separator = backslash;
1176 colon = strrchr(in, ':');
1177 if (!separator || separator < colon)
1179 return separator ? separator + 1 : in;
1188 void FS_ClearSearchPath (void)
1190 // unload all packs and directory information, close all pack files
1191 // (if a qfile is still reading a pack it won't be harmed because it used
1192 // dup() to get its own handle already)
1193 while (fs_searchpaths)
1195 searchpath_t *search = fs_searchpaths;
1196 fs_searchpaths = search->next;
1200 close(search->pack->handle);
1201 // free any memory associated with it
1202 if (search->pack->files)
1203 Mem_Free(search->pack->files);
1204 Mem_Free(search->pack);
1216 void FS_Rescan (void)
1219 qboolean fs_modified = false;
1221 FS_ClearSearchPath();
1223 // add the game-specific paths
1224 // gamedirname1 (typically id1)
1225 FS_AddGameHierarchy (gamedirname1);
1226 // update the com_modname (used for server info)
1227 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1229 // add the game-specific path, if any
1230 // (only used for mission packs and the like, which should set fs_modified)
1234 FS_AddGameHierarchy (gamedirname2);
1238 // Adds basedir/gamedir as an override game
1239 // LordHavoc: now supports multiple -game directories
1240 // set the com_modname (reported in server info)
1241 for (i = 0;i < fs_numgamedirs;i++)
1244 FS_AddGameHierarchy (fs_gamedirs[i]);
1245 // update the com_modname (used server info)
1246 strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
1249 // set the default screenshot name to either the mod name or the
1250 // gamemode screenshot name
1251 if (strcmp(com_modname, gamedirname1))
1252 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1254 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1256 // If "-condebug" is in the command line, remove the previous log file
1257 if (COM_CheckParm ("-condebug") != 0)
1258 unlink (va("%s/qconsole.log", fs_gamedir));
1260 // look for the pop.lmp file and set registered to true if it is found
1261 if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp"))
1264 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1266 Con_Print("Playing shareware version.\n");
1270 Cvar_Set ("registered", "1");
1271 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1272 Con_Print("Playing registered version.\n");
1275 // unload all wads so that future queries will return the new data
1279 void FS_Rescan_f(void)
1289 extern void Host_SaveConfig (void);
1290 extern void Host_LoadConfig_f (void);
1291 qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
1295 if (fs_numgamedirs == numgamedirs)
1297 for (i = 0;i < numgamedirs;i++)
1298 if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
1300 if (i == numgamedirs)
1301 return true; // already using this set of gamedirs, do nothing
1304 if (numgamedirs > MAX_GAMEDIRS)
1307 Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1308 return false; // too many gamedirs
1311 for (i = 0;i < numgamedirs;i++)
1313 // if string is nasty, reject it
1314 if(FS_CheckNastyPath(gamedirs[i], true))
1317 Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
1318 return false; // nasty gamedirs
1322 for (i = 0;i < numgamedirs;i++)
1324 if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
1327 Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
1328 return false; // missing gamedirs
1334 fs_numgamedirs = numgamedirs;
1335 for (i = 0;i < fs_numgamedirs;i++)
1336 strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
1338 // reinitialize filesystem to detect the new paks
1341 // exec the new config
1342 Host_LoadConfig_f();
1344 // unload all sounds so they will be reloaded from the new files as needed
1345 S_UnloadAllSounds_f();
1347 // reinitialize renderer (this reloads hud/console background/etc)
1348 R_Modules_Restart();
1358 void FS_GameDir_f (void)
1362 char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
1366 Con_Printf("gamedirs active:");
1367 for (i = 0;i < fs_numgamedirs;i++)
1368 Con_Printf(" %s", fs_gamedirs[i]);
1373 numgamedirs = Cmd_Argc() - 1;
1374 if (numgamedirs > MAX_GAMEDIRS)
1376 Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
1380 for (i = 0;i < numgamedirs;i++)
1381 strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
1383 if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
1385 // actually, changing during game would work fine, but would be stupid
1386 Con_Printf("Can not change gamedir while client is connected or server is running!\n");
1390 // halt demo playback to close the file
1393 FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
1402 qboolean FS_CheckGameDir(const char *gamedir)
1406 stringlistinit(&list);
1407 listdirectory(&list, va("%s%s/", fs_basedir, gamedir), "");
1408 success = list.numstrings > 0;
1409 stringlistfreecontents(&list);
1424 const char* dllnames [] =
1426 "shfolder.dll", // IE 4, or Win NT and higher
1429 Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
1430 // don't care for the result; if it fails, %USERPROFILE% will be used instead
1433 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1435 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1437 // If the base directory is explicitly defined by the compilation process
1438 #ifdef DP_FS_BASEDIR
1439 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1441 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1444 // FIXME: is there a better way to find the directory outside the .app?
1445 if (strstr(com_argv[0], ".app/"))
1449 split = strstr(com_argv[0], ".app/");
1450 while (split > com_argv[0] && *split != '/')
1452 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1453 fs_basedir[split - com_argv[0]] = 0;
1461 // Overrides the system supplied base directory (under GAMENAME)
1462 // 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)
1463 i = COM_CheckParm ("-basedir");
1464 if (i && i < com_argc-1)
1466 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1467 i = (int)strlen (fs_basedir);
1468 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1469 fs_basedir[i-1] = 0;
1472 // add a path separator to the end of the basedir if it lacks one
1473 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1474 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1476 if (!FS_CheckGameDir(gamedirname1))
1477 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
1479 if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
1480 Con_Printf("WARNING: base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
1483 // Adds basedir/gamedir as an override game
1484 // LordHavoc: now supports multiple -game directories
1485 for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++)
1489 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1492 if (FS_CheckNastyPath(com_argv[i], true))
1493 Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
1494 if (!FS_CheckGameDir(com_argv[i]))
1495 Con_Printf("WARNING: -game %s%s/ not found!\n", fs_basedir, com_argv[i]);
1496 // add the gamedir to the list of active gamedirs
1497 strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
1502 // generate the searchpath
1506 void FS_Init_Commands(void)
1508 Cvar_RegisterVariable (&scr_screenshot_name);
1509 Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
1511 Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
1512 Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
1513 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1514 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1515 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1523 void FS_Shutdown (void)
1525 // close all pack files and such
1526 // (hopefully there aren't any other open files, but they'll be cleaned up
1527 // by the OS anyway)
1528 FS_ClearSearchPath();
1529 Mem_FreePool (&fs_mempool);
1532 Sys_UnloadLibrary (&shfolder_dll);
1537 ====================
1540 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1541 ====================
1543 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1549 // Parse the mode string
1558 opt = O_CREAT | O_TRUNC;
1562 opt = O_CREAT | O_APPEND;
1565 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1568 for (ind = 1; mode[ind] != '\0'; ind++)
1579 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1580 filepath, mode, mode[ind]);
1587 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1588 memset (file, 0, sizeof (*file));
1591 #if _MSC_VER >= 1400
1592 _sopen_s(&file->handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
1594 file->handle = open (filepath, mod | opt, 0666);
1596 if (file->handle < 0)
1602 file->real_length = lseek (file->handle, 0, SEEK_END);
1604 // For files opened in append mode, we start at the end of the file
1606 file->position = file->real_length;
1608 lseek (file->handle, 0, SEEK_SET);
1618 Open a packed file using its package file descriptor
1621 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1627 pfile = &pack->files[pack_ind];
1629 // If we don't have the true offset, get it now
1630 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1631 if (!PK3_GetTrueFileOffset (pfile, pack))
1634 // No Zlib DLL = no compressed files
1635 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1637 Con_Printf("WARNING: can't open the compressed file %s\n"
1638 "You need the Zlib DLL to use compressed files\n",
1643 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1644 // the dup() call to avoid having to close the dup_handle on error here
1645 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1647 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1648 pfile->name, pack->filename, (int) pfile->offset);
1652 dup_handle = dup (pack->handle);
1655 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1659 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1660 memset (file, 0, sizeof (*file));
1661 file->handle = dup_handle;
1662 file->flags = QFILE_FLAG_PACKED;
1663 file->real_length = pfile->realsize;
1664 file->offset = pfile->offset;
1668 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1672 file->flags |= QFILE_FLAG_DEFLATED;
1674 // We need some more variables
1675 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1677 ztk->comp_length = pfile->packsize;
1679 // Initialize zlib stream
1680 ztk->zstream.next_in = ztk->input;
1681 ztk->zstream.avail_in = 0;
1683 /* From Zlib's "unzip.c":
1685 * windowBits is passed < 0 to tell that there is no zlib header.
1686 * Note that in this case inflate *requires* an extra "dummy" byte
1687 * after the compressed stream in order to complete decompression and
1688 * return Z_STREAM_END.
1689 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1690 * size of both compressed and uncompressed data
1692 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1694 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1700 ztk->zstream.next_out = file->buff;
1701 ztk->zstream.avail_out = sizeof (file->buff);
1710 ====================
1713 Return true if the path should be rejected due to one of the following:
1714 1: path elements that are non-portable
1715 2: path elements that would allow access to files outside the game directory,
1716 or are just not a good idea for a mod to be using.
1717 ====================
1719 int FS_CheckNastyPath (const char *path, qboolean isgamedir)
1721 // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
1725 // Windows: don't allow \ in filenames (windows-only), period.
1726 // (on Windows \ is a directory separator, but / is also supported)
1727 if (strstr(path, "\\"))
1728 return 1; // non-portable
1730 // Mac: don't allow Mac-only filenames - : is a directory separator
1731 // instead of /, but we rely on / working already, so there's no reason to
1732 // support a Mac-only path
1733 // Amiga and Windows: : tries to go to root of drive
1734 if (strstr(path, ":"))
1735 return 1; // non-portable attempt to go to root of drive
1737 // Amiga: // is parent directory
1738 if (strstr(path, "//"))
1739 return 1; // non-portable attempt to go to parent directory
1741 // all: don't allow going to parent directory (../ or /../)
1742 if (strstr(path, ".."))
1743 return 2; // attempt to go outside the game directory
1745 // Windows and UNIXes: don't allow absolute paths
1747 return 2; // attempt to go outside the game directory
1749 // 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
1750 if (strchr(path, '.'))
1754 // gamedir is entirely path elements, so simply forbid . entirely
1757 if (strchr(path, '.') < strrchr(path, '/'))
1758 return 2; // possible attempt to go outside the game directory
1761 // all: forbid trailing slash on gamedir
1762 if (isgamedir && path[strlen(path)-1] == '/')
1765 // all: forbid leading dot on any filename for any reason
1766 if (strstr(path, "/."))
1767 return 2; // attempt to go outside the game directory
1769 // after all these checks we're pretty sure it's a / separated filename
1770 // and won't do much if any harm
1776 ====================
1779 Look for a file in the packages and in the filesystem
1781 Return the searchpath where the file was found (or NULL)
1782 and the file index in the package if relevant
1783 ====================
1785 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1787 searchpath_t *search;
1790 // search through the path, one element at a time
1791 for (search = fs_searchpaths;search;search = search->next)
1793 // is the element a pak file?
1796 int (*strcmp_funct) (const char* str1, const char* str2);
1797 int left, right, middle;
1800 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1802 // Look for the file (binary search)
1804 right = pak->numfiles - 1;
1805 while (left <= right)
1809 middle = (left + right) / 2;
1810 diff = strcmp_funct (pak->files[middle].name, name);
1815 if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
1817 // yes, but the first one is empty so we treat it as not being there
1818 if (!quiet && developer.integer >= 10)
1819 Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
1826 if (!quiet && developer.integer >= 10)
1827 Con_Printf("FS_FindFile: %s in %s\n",
1828 pak->files[middle].name, pak->filename);
1835 // If we're too far in the list
1844 char netpath[MAX_OSPATH];
1845 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1846 if (FS_SysFileExists (netpath))
1848 if (!quiet && developer.integer >= 10)
1849 Con_Printf("FS_FindFile: %s\n", netpath);
1858 if (!quiet && developer.integer >= 10)
1859 Con_Printf("FS_FindFile: can't find %s\n", name);
1871 Look for a file in the search paths and open it in read-only mode
1874 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
1876 searchpath_t *search;
1879 search = FS_FindFile (filename, &pack_ind, quiet);
1885 // Found in the filesystem?
1888 char path [MAX_OSPATH];
1889 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1890 return FS_SysOpen (path, "rb", nonblocking);
1893 // So, we found it in a package...
1895 // Is it a PK3 symlink?
1896 // TODO also handle directory symlinks by parsing the whole structure...
1897 // but heck, file symlinks are good enough for now
1898 if(search->pack->files[pack_ind].flags & PACKFILE_FLAG_SYMLINK)
1900 if(symlinkLevels <= 0)
1902 Con_Printf("symlink: %s: too many levels of symbolic links\n", filename);
1907 char linkbuf[MAX_QPATH];
1909 qfile_t *linkfile = FS_OpenPackedFile (search->pack, pack_ind);
1910 const char *mergeslash;
1915 count = FS_Read(linkfile, linkbuf, sizeof(linkbuf) - 1);
1921 // Now combine the paths...
1922 mergeslash = strrchr(filename, '/');
1923 mergestart = linkbuf;
1925 mergeslash = filename;
1926 while(!strncmp(mergestart, "../", 3))
1929 while(mergeslash > filename)
1932 if(*mergeslash == '/')
1936 // Now, mergestart will point to the path to be appended, and mergeslash points to where it should be appended
1937 if(mergeslash == filename)
1939 // Either mergeslash == filename, then we just replace the name (done below)
1943 // Or, we append the name after mergeslash;
1944 // or rather, we can also shift the linkbuf so we can put everything up to and including mergeslash first
1945 int spaceNeeded = mergeslash - filename + 1;
1946 int spaceRemoved = mergestart - linkbuf;
1947 if(count - spaceRemoved + spaceNeeded >= MAX_QPATH)
1949 Con_DPrintf("symlink: too long path rejected\n");
1952 memmove(linkbuf + spaceNeeded, linkbuf + spaceRemoved, count - spaceRemoved);
1953 memcpy(linkbuf, filename, spaceNeeded);
1954 linkbuf[count - spaceRemoved + spaceNeeded] = 0;
1955 mergestart = linkbuf;
1957 if (!quiet && developer_loading.integer)
1958 Con_DPrintf("symlink: %s -> %s\n", filename, mergestart);
1959 if(FS_CheckNastyPath (mergestart, false))
1961 Con_DPrintf("symlink: nasty path %s rejected\n", mergestart);
1964 return FS_OpenReadFile(mergestart, quiet, nonblocking, symlinkLevels - 1);
1968 return FS_OpenPackedFile (search->pack, pack_ind);
1973 =============================================================================
1975 MAIN PUBLIC FUNCTIONS
1977 =============================================================================
1981 ====================
1984 Open a file. The syntax is the same as fopen
1985 ====================
1987 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1989 if (FS_CheckNastyPath(filepath, false))
1991 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1995 // If the file is opened in "write", "append", or "read/write" mode
1996 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1998 char real_path [MAX_OSPATH];
2000 // Open the file on disk directly
2001 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
2003 // Create directories up to the file
2004 FS_CreatePath (real_path);
2006 return FS_SysOpen (real_path, mode, nonblocking);
2008 // Else, we look at the various search paths and open the file in read-only mode
2010 return FS_OpenReadFile (filepath, quiet, nonblocking, 16);
2015 ====================
2019 ====================
2021 int FS_Close (qfile_t* file)
2023 if (close (file->handle))
2028 qz_inflateEnd (&file->ztk->zstream);
2029 Mem_Free (file->ztk);
2038 ====================
2041 Write "datasize" bytes into a file
2042 ====================
2044 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
2048 // If necessary, seek to the exact file position we're supposed to be
2049 if (file->buff_ind != file->buff_len)
2050 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
2052 // Purge cached data
2055 // Write the buffer and update the position
2056 result = write (file->handle, data, (fs_offset_t)datasize);
2057 file->position = lseek (file->handle, 0, SEEK_CUR);
2058 if (file->real_length < file->position)
2059 file->real_length = file->position;
2069 ====================
2072 Read up to "buffersize" bytes from a file
2073 ====================
2075 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
2077 fs_offset_t count, done;
2079 if (buffersize == 0)
2082 // Get rid of the ungetc character
2083 if (file->ungetc != EOF)
2085 ((char*)buffer)[0] = file->ungetc;
2093 // First, we copy as many bytes as we can from "buff"
2094 if (file->buff_ind < file->buff_len)
2096 count = file->buff_len - file->buff_ind;
2097 count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
2099 memcpy (buffer, &file->buff[file->buff_ind], count);
2100 file->buff_ind += count;
2102 buffersize -= count;
2103 if (buffersize == 0)
2107 // NOTE: at this point, the read buffer is always empty
2109 // If the file isn't compressed
2110 if (! (file->flags & QFILE_FLAG_DEFLATED))
2114 // We must take care to not read after the end of the file
2115 count = file->real_length - file->position;
2117 // If we have a lot of data to get, put them directly into "buffer"
2118 if (buffersize > sizeof (file->buff) / 2)
2120 if (count > (fs_offset_t)buffersize)
2121 count = (fs_offset_t)buffersize;
2122 lseek (file->handle, file->offset + file->position, SEEK_SET);
2123 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
2127 file->position += nb;
2129 // Purge cached data
2135 if (count > (fs_offset_t)sizeof (file->buff))
2136 count = (fs_offset_t)sizeof (file->buff);
2137 lseek (file->handle, file->offset + file->position, SEEK_SET);
2138 nb = read (file->handle, file->buff, count);
2141 file->buff_len = nb;
2142 file->position += nb;
2144 // Copy the requested data in "buffer" (as much as we can)
2145 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2146 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2147 file->buff_ind = count;
2155 // If the file is compressed, it's more complicated...
2156 // We cycle through a few operations until we have read enough data
2157 while (buffersize > 0)
2159 ztoolkit_t *ztk = file->ztk;
2162 // NOTE: at this point, the read buffer is always empty
2164 // If "input" is also empty, we need to refill it
2165 if (ztk->in_ind == ztk->in_len)
2167 // If we are at the end of the file
2168 if (file->position == file->real_length)
2171 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
2172 if (count > (fs_offset_t)sizeof (ztk->input))
2173 count = (fs_offset_t)sizeof (ztk->input);
2174 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
2175 if (read (file->handle, ztk->input, count) != count)
2177 Con_Printf ("FS_Read: unexpected end of file\n");
2182 ztk->in_len = count;
2183 ztk->in_position += count;
2186 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
2187 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
2189 // Now that we are sure we have compressed data available, we need to determine
2190 // if it's better to inflate it in "file->buff" or directly in "buffer"
2192 // Inflate the data in "file->buff"
2193 if (buffersize < sizeof (file->buff) / 2)
2195 ztk->zstream.next_out = file->buff;
2196 ztk->zstream.avail_out = sizeof (file->buff);
2197 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2198 if (error != Z_OK && error != Z_STREAM_END)
2200 Con_Printf ("FS_Read: Can't inflate file\n");
2203 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2205 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
2206 file->position += file->buff_len;
2208 // Copy the requested data in "buffer" (as much as we can)
2209 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2210 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
2211 file->buff_ind = count;
2214 // Else, we inflate directly in "buffer"
2217 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
2218 ztk->zstream.avail_out = (unsigned int)buffersize;
2219 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
2220 if (error != Z_OK && error != Z_STREAM_END)
2222 Con_Printf ("FS_Read: Can't inflate file\n");
2225 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
2227 // How much data did it inflate?
2228 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
2229 file->position += count;
2231 // Purge cached data
2236 buffersize -= count;
2244 ====================
2247 Print a string into a file
2248 ====================
2250 int FS_Print (qfile_t* file, const char *msg)
2252 return (int)FS_Write (file, msg, strlen (msg));
2256 ====================
2259 Print a string into a file
2260 ====================
2262 int FS_Printf(qfile_t* file, const char* format, ...)
2267 va_start (args, format);
2268 result = FS_VPrintf (file, format, args);
2276 ====================
2279 Print a string into a file
2280 ====================
2282 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
2285 fs_offset_t buff_size = MAX_INPUTLINE;
2290 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
2291 len = dpvsnprintf (tempbuff, buff_size, format, ap);
2292 if (len >= 0 && len < buff_size)
2294 Mem_Free (tempbuff);
2298 len = write (file->handle, tempbuff, len);
2299 Mem_Free (tempbuff);
2306 ====================
2309 Get the next character of a file
2310 ====================
2312 int FS_Getc (qfile_t* file)
2316 if (FS_Read (file, &c, 1) != 1)
2324 ====================
2327 Put a character back into the read buffer (only supports one character!)
2328 ====================
2330 int FS_UnGetc (qfile_t* file, unsigned char c)
2332 // If there's already a character waiting to be read
2333 if (file->ungetc != EOF)
2342 ====================
2345 Move the position index in a file
2346 ====================
2348 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
2351 unsigned char* buffer;
2352 fs_offset_t buffersize;
2354 // Compute the file offset
2358 offset += file->position - file->buff_len + file->buff_ind;
2365 offset += file->real_length;
2371 if (offset < 0 || offset > file->real_length)
2374 // If we have the data in our read buffer, we don't need to actually seek
2375 if (file->position - file->buff_len <= offset && offset <= file->position)
2377 file->buff_ind = offset + file->buff_len - file->position;
2381 // Purge cached data
2384 // Unpacked or uncompressed files can seek directly
2385 if (! (file->flags & QFILE_FLAG_DEFLATED))
2387 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
2389 file->position = offset;
2393 // Seeking in compressed files is more a hack than anything else,
2394 // but we need to support it, so here we go.
2397 // If we have to go back in the file, we need to restart from the beginning
2398 if (offset <= file->position)
2402 ztk->in_position = 0;
2404 lseek (file->handle, file->offset, SEEK_SET);
2406 // Reset the Zlib stream
2407 ztk->zstream.next_in = ztk->input;
2408 ztk->zstream.avail_in = 0;
2409 qz_inflateReset (&ztk->zstream);
2412 // We need a big buffer to force inflating into it directly
2413 buffersize = 2 * sizeof (file->buff);
2414 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
2416 // Skip all data until we reach the requested offset
2417 while (offset > file->position)
2419 fs_offset_t diff = offset - file->position;
2420 fs_offset_t count, len;
2422 count = (diff > buffersize) ? buffersize : diff;
2423 len = FS_Read (file, buffer, count);
2437 ====================
2440 Give the current position in a file
2441 ====================
2443 fs_offset_t FS_Tell (qfile_t* file)
2445 return file->position - file->buff_len + file->buff_ind;
2450 ====================
2453 Give the total size of a file
2454 ====================
2456 fs_offset_t FS_FileSize (qfile_t* file)
2458 return file->real_length;
2463 ====================
2466 Erases any buffered input or output data
2467 ====================
2469 void FS_Purge (qfile_t* file)
2481 Filename are relative to the quake directory.
2482 Always appends a 0 byte.
2485 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2488 unsigned char *buf = NULL;
2489 fs_offset_t filesize = 0;
2491 file = FS_Open (path, "rb", quiet, false);
2494 filesize = file->real_length;
2495 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2496 buf[filesize] = '\0';
2497 FS_Read (file, buf, filesize);
2499 if (developer_loadfile.integer)
2500 Con_Printf("loaded file \"%s\" (%u bytes)\n", path, (unsigned int)filesize);
2503 if (filesizepointer)
2504 *filesizepointer = filesize;
2513 The filename will be prefixed by the current game directory
2516 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2520 file = FS_Open (filename, "wb", false, false);
2523 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2527 Con_DPrintf("FS_WriteFile: %s (%u bytes)\n", filename, (unsigned int)len);
2528 FS_Write (file, data, len);
2535 =============================================================================
2537 OTHERS PUBLIC FUNCTIONS
2539 =============================================================================
2547 void FS_StripExtension (const char *in, char *out, size_t size_out)
2555 while ((currentchar = *in) && size_out > 1)
2557 if (currentchar == '.')
2559 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2561 *out++ = currentchar;
2577 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2581 // if path doesn't have a .EXT, append extension
2582 // (extension should include the .)
2583 src = path + strlen(path) - 1;
2585 while (*src != '/' && src != path)
2588 return; // it has an extension
2592 strlcat (path, extension, size_path);
2600 Look for a file in the packages and in the filesystem
2603 int FS_FileType (const char *filename)
2605 searchpath_t *search;
2606 char fullpath[MAX_QPATH];
2608 search = FS_FindFile (filename, NULL, true);
2610 return FS_FILETYPE_NONE;
2613 return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
2615 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
2616 return FS_SysFileType(fullpath);
2624 Look for a file in the packages and in the filesystem
2627 qboolean FS_FileExists (const char *filename)
2629 return (FS_FindFile (filename, NULL, true) != NULL);
2637 Look for a file in the filesystem only
2640 int FS_SysFileType (const char *path)
2643 // Sajt - some older sdks are missing this define
2644 # ifndef INVALID_FILE_ATTRIBUTES
2645 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
2648 DWORD result = GetFileAttributes(path);
2650 if(result == INVALID_FILE_ATTRIBUTES)
2651 return FS_FILETYPE_NONE;
2653 if(result & FILE_ATTRIBUTE_DIRECTORY)
2654 return FS_FILETYPE_DIRECTORY;
2656 return FS_FILETYPE_FILE;
2660 if (stat (path,&buf) == -1)
2661 return FS_FILETYPE_NONE;
2663 if(S_ISDIR(buf.st_mode))
2664 return FS_FILETYPE_DIRECTORY;
2666 return FS_FILETYPE_FILE;
2670 qboolean FS_SysFileExists (const char *path)
2672 return FS_SysFileType (path) != FS_FILETYPE_NONE;
2675 void FS_mkdir (const char *path)
2688 Allocate and fill a search structure with information on matching filenames.
2691 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2694 searchpath_t *searchpath;
2696 int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
2697 stringlist_t resultlist;
2698 stringlist_t dirlist;
2699 const char *slash, *backslash, *colon, *separator;
2701 char temp[MAX_OSPATH];
2703 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2708 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2712 stringlistinit(&resultlist);
2713 stringlistinit(&dirlist);
2715 slash = strrchr(pattern, '/');
2716 backslash = strrchr(pattern, '\\');
2717 colon = strrchr(pattern, ':');
2718 separator = max(slash, backslash);
2719 separator = max(separator, colon);
2720 basepathlength = separator ? (separator + 1 - pattern) : 0;
2721 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2723 memcpy(basepath, pattern, basepathlength);
2724 basepath[basepathlength] = 0;
2726 // search through the path, one element at a time
2727 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2729 // is the element a pak file?
2730 if (searchpath->pack)
2732 // look through all the pak file elements
2733 pak = searchpath->pack;
2734 for (i = 0;i < pak->numfiles;i++)
2736 strlcpy(temp, pak->files[i].name, sizeof(temp));
2739 if (matchpattern(temp, (char *)pattern, true))
2741 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2742 if (!strcmp(resultlist.strings[resultlistindex], temp))
2744 if (resultlistindex == resultlist.numstrings)
2746 stringlistappend(&resultlist, temp);
2747 if (!quiet && developer_loading.integer)
2748 Con_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
2751 // strip off one path element at a time until empty
2752 // this way directories are added to the listing if they match the pattern
2753 slash = strrchr(temp, '/');
2754 backslash = strrchr(temp, '\\');
2755 colon = strrchr(temp, ':');
2757 if (separator < slash)
2759 if (separator < backslash)
2760 separator = backslash;
2761 if (separator < colon)
2763 *((char *)separator) = 0;
2769 stringlist_t matchedSet, foundSet;
2770 const char *start = pattern;
2772 stringlistinit(&matchedSet);
2773 stringlistinit(&foundSet);
2774 // add a first entry to the set
2775 stringlistappend(&matchedSet, "");
2776 // iterate through pattern's path
2779 const char *asterisk, *wildcard, *nextseparator, *prevseparator;
2780 char subpath[MAX_OSPATH];
2781 char subpattern[MAX_OSPATH];
2783 // find the next wildcard
2784 wildcard = strchr(start, '?');
2785 asterisk = strchr(start, '*');
2786 if (asterisk && (!wildcard || asterisk < wildcard))
2788 wildcard = asterisk;
2793 nextseparator = strchr( wildcard, '/' );
2797 nextseparator = NULL;
2800 if( !nextseparator ) {
2801 nextseparator = start + strlen( start );
2804 // prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
2805 // copy everything up except nextseperator
2806 strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
2807 // find the last '/' before the wildcard
2808 prevseparator = strrchr( subpattern, '/' );
2810 prevseparator = subpattern;
2813 // copy everything from start to the previous including the '/' (before the wildcard)
2814 // everything up to start is already included in the path of matchedSet's entries
2815 strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
2817 // for each entry in matchedSet try to open the subdirectories specified in subpath
2818 for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
2819 strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
2820 strlcat( temp, subpath, sizeof(temp) );
2821 listdirectory( &foundSet, searchpath->filename, temp );
2823 if( dirlistindex == 0 ) {
2826 // reset the current result set
2827 stringlistfreecontents( &matchedSet );
2828 // match against the pattern
2829 for( dirlistindex = 0 ; dirlistindex < foundSet.numstrings ; dirlistindex++ ) {
2830 const char *direntry = foundSet.strings[ dirlistindex ];
2831 if (matchpattern(direntry, subpattern, true)) {
2832 stringlistappend( &matchedSet, direntry );
2835 stringlistfreecontents( &foundSet );
2837 start = nextseparator;
2840 for (dirlistindex = 0;dirlistindex < matchedSet.numstrings;dirlistindex++)
2842 const char *temp = matchedSet.strings[dirlistindex];
2843 if (matchpattern(temp, (char *)pattern, true))
2845 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2846 if (!strcmp(resultlist.strings[resultlistindex], temp))
2848 if (resultlistindex == resultlist.numstrings)
2850 stringlistappend(&resultlist, temp);
2851 if (!quiet && developer_loading.integer)
2852 Con_Printf("SearchDirFile: %s\n", temp);
2856 stringlistfreecontents( &matchedSet );
2860 if (resultlist.numstrings)
2862 stringlistsort(&resultlist);
2863 numfiles = resultlist.numstrings;
2865 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2866 numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
2867 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2868 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2869 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2870 search->numfilenames = (int)numfiles;
2873 for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
2876 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2877 textlen = strlen(resultlist.strings[resultlistindex]) + 1;
2878 memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
2880 numchars += (int)textlen;
2883 stringlistfreecontents(&resultlist);
2889 void FS_FreeSearch(fssearch_t *search)
2894 extern int con_linewidth;
2895 int FS_ListDirectory(const char *pattern, int oneperline)
2904 char linebuf[MAX_INPUTLINE];
2906 search = FS_Search(pattern, true, true);
2909 numfiles = search->numfilenames;
2912 // FIXME: the names could be added to one column list and then
2913 // gradually shifted into the next column if they fit, and then the
2914 // next to make a compact variable width listing but it's a lot more
2916 // find width for columns
2918 for (i = 0;i < numfiles;i++)
2920 l = (int)strlen(search->filenames[i]);
2921 if (columnwidth < l)
2924 // count the spacing character
2926 // calculate number of columns
2927 numcolumns = con_linewidth / columnwidth;
2928 // don't bother with the column printing if it's only one column
2929 if (numcolumns >= 2)
2931 numlines = (numfiles + numcolumns - 1) / numcolumns;
2932 for (i = 0;i < numlines;i++)
2935 for (k = 0;k < numcolumns;k++)
2937 l = i * numcolumns + k;
2940 name = search->filenames[l];
2941 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2942 linebuf[linebufpos++] = name[j];
2943 // space out name unless it's the last on the line
2944 if (k + 1 < numcolumns && l + 1 < numfiles)
2945 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2946 linebuf[linebufpos++] = ' ';
2949 linebuf[linebufpos] = 0;
2950 Con_Printf("%s\n", linebuf);
2957 for (i = 0;i < numfiles;i++)
2958 Con_Printf("%s\n", search->filenames[i]);
2959 FS_FreeSearch(search);
2960 return (int)numfiles;
2963 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2965 const char *pattern;
2968 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2971 if (Cmd_Argc() == 2)
2972 pattern = Cmd_Argv(1);
2975 if (!FS_ListDirectory(pattern, oneperline))
2976 Con_Print("No files found.\n");
2981 FS_ListDirectoryCmd("dir", true);
2986 FS_ListDirectoryCmd("ls", false);
2989 const char *FS_WhichPack(const char *filename)
2992 searchpath_t *sp = FS_FindFile(filename, &index, true);
2994 return sp->pack->filename;
3000 ====================
3001 FS_IsRegisteredQuakePack
3003 Look for a proof of purchase file file in the requested package
3005 If it is found, this file should NOT be downloaded.
3006 ====================
3008 qboolean FS_IsRegisteredQuakePack(const char *name)
3010 searchpath_t *search;
3013 // search through the path, one element at a time
3014 for (search = fs_searchpaths;search;search = search->next)
3016 if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
3018 int (*strcmp_funct) (const char* str1, const char* str2);
3019 int left, right, middle;
3022 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
3024 // Look for the file (binary search)
3026 right = pak->numfiles - 1;
3027 while (left <= right)
3031 middle = (left + right) / 2;
3032 diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
3038 // If we're too far in the list
3045 // we found the requested pack but it is not registered quake
3053 int FS_CRCFile(const char *filename, size_t *filesizepointer)
3056 unsigned char *filedata;
3057 fs_offset_t filesize;
3058 if (filesizepointer)
3059 *filesizepointer = 0;
3060 if (!filename || !*filename)
3062 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
3065 if (filesizepointer)
3066 *filesizepointer = filesize;
3067 crc = CRC_Block(filedata, filesize);