4 Copyright (C) 2003-2005 Mathieu Olivier
5 Copyright (C) 1999,2000 contributors of the QuakeForge project
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 See the GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to:
21 Free Software Foundation, Inc.
22 59 Temple Place - Suite 330
23 Boston, MA 02111-1307, USA
36 # include <sys/stat.h>
42 // Win32 requires us to add O_BINARY, but the other OSes don't have it
50 All of Quake's data access is through a hierchal file system, but the contents
51 of the file system can be transparently merged from several sources.
53 The "base directory" is the path to the directory holding the quake.exe and
54 all game directories. The sys_* files pass this to host_init in
55 quakeparms_t->basedir. This can be overridden with the "-basedir" command
56 line parm to allow code debugging in a different directory. The base
57 directory is only used during filesystem initialization.
59 The "game directory" is the first tree on the search path and directory that
60 all generated files (savegames, screenshots, demos, config files) will be
61 saved to. This can be overridden with the "-game" command line parameter.
62 The game directory can never be changed while quake is executing. This is a
63 precaution against having a malicious server instruct clients to write files
64 over areas they shouldn't.
70 =============================================================================
74 =============================================================================
77 // Magic numbers of a ZIP file (big-endian format)
78 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
79 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
80 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
82 // Other constants for ZIP files
83 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
84 #define ZIP_END_CDIR_SIZE 22
85 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
86 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
88 // Zlib constants (from zlib.h)
89 #define Z_SYNC_FLUSH 2
92 #define Z_STREAM_END 1
93 #define ZLIB_VERSION "1.1.4"
97 =============================================================================
101 =============================================================================
104 // Zlib stream (from zlib.h)
105 // Warning: some pointers we don't use directly have
106 // been cast to "void*" for a matter of simplicity
109 qbyte *next_in; // next input byte
110 unsigned int avail_in; // number of bytes available at next_in
111 unsigned long total_in; // total nb of input bytes read so far
113 qbyte *next_out; // next output byte should be put there
114 unsigned int avail_out; // remaining free space at next_out
115 unsigned long total_out; // total nb of bytes output so far
117 char *msg; // last error message, NULL if no error
118 void *state; // not visible by applications
120 void *zalloc; // used to allocate the internal state
121 void *zfree; // used to free the internal state
122 void *opaque; // private data object passed to zalloc and zfree
124 int data_type; // best guess about the data type: ascii or binary
125 unsigned long adler; // adler32 value of the uncompressed data
126 unsigned long reserved; // reserved for future use
133 QFILE_FLAG_PACKED = (1 << 0), // inside a package (PAK or PK3)
134 QFILE_FLAG_DEFLATED = (1 << 1) // file is compressed using the deflate algorithm (PK3 only)
137 #define FILE_BUFF_SIZE 2048
141 size_t comp_length; // length of the compressed file
142 size_t in_ind, in_len; // input buffer current index and length
143 size_t in_position; // position in the compressed file
144 qbyte input [FILE_BUFF_SIZE];
150 int handle; // file descriptor
151 size_t real_length; // uncompressed file size (for files opened in "read" mode)
152 size_t position; // current position in the file
153 size_t offset; // offset into the package (0 if external file)
154 int ungetc; // single stored character from ungetc, cleared to EOF when read
157 size_t buff_ind, buff_len; // buffer current index and length
158 qbyte buff [FILE_BUFF_SIZE];
165 // ------ PK3 files on disk ------ //
167 // You can get the complete ZIP format description from PKWARE website
171 unsigned int signature;
172 unsigned short disknum;
173 unsigned short cdir_disknum; // number of the disk with the start of the central directory
174 unsigned short localentries; // number of entries in the central directory on this disk
175 unsigned short nbentries; // total number of entries in the central directory on this disk
176 unsigned int cdir_size; // size of the central directory
177 unsigned int cdir_offset; // with respect to the starting disk number
178 unsigned short comment_size;
179 } pk3_endOfCentralDir_t;
182 // ------ PAK files on disk ------ //
186 int filepos, filelen;
197 // Packages in memory
200 PACKFILE_FLAG_NONE = 0,
201 PACKFILE_FLAG_TRUEOFFS = (1 << 0), // the offset in packfile_t is the true contents offset
202 PACKFILE_FLAG_DEFLATED = (1 << 1) // file compressed using the deflate algorithm
207 char name [MAX_QPATH];
208 packfile_flags_t flags;
210 size_t packsize; // size in the package
211 size_t realsize; // real file size (uncompressed)
214 typedef struct pack_s
216 char filename [MAX_OSPATH];
218 int ignorecase; // PK3 ignores case
226 // Search paths for files (including packages)
227 typedef struct searchpath_s
229 // only one of filename / pack will be used
230 char filename[MAX_OSPATH];
232 struct searchpath_s *next;
237 =============================================================================
241 =============================================================================
247 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
248 size_t offset, size_t packsize,
249 size_t realsize, packfile_flags_t flags);
253 =============================================================================
257 =============================================================================
260 mempool_t *fs_mempool;
261 mempool_t *pak_mempool;
265 pack_t *packlist = NULL;
267 searchpath_t *fs_searchpaths = NULL;
269 #define MAX_FILES_IN_PACK 65536
271 char fs_gamedir[MAX_OSPATH];
272 char fs_basedir[MAX_OSPATH];
274 qboolean fs_modified; // set true if using non-id files
278 =============================================================================
280 PRIVATE FUNCTIONS - PK3 HANDLING
282 =============================================================================
285 // Functions exported from zlib
287 # define ZEXPORT WINAPI
292 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
293 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
294 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
295 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
297 #define qz_inflateInit2(strm, windowBits) \
298 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
300 static dllfunction_t zlibfuncs[] =
302 {"inflate", (void **) &qz_inflate},
303 {"inflateEnd", (void **) &qz_inflateEnd},
304 {"inflateInit2_", (void **) &qz_inflateInit2_},
305 {"inflateReset", (void **) &qz_inflateReset},
309 // Handle for Zlib DLL
310 static dllhandle_t zlib_dll = NULL;
320 void PK3_CloseLibrary (void)
322 Sys_UnloadLibrary (&zlib_dll);
330 Try to load the Zlib DLL
333 qboolean PK3_OpenLibrary (void)
335 const char* dllnames [] =
339 #elif defined(MACOSX)
353 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
355 Con_Printf ("Compressed files support disabled\n");
359 Con_Printf ("Compressed files support enabled\n");
366 PK3_GetEndOfCentralDir
368 Extract the end of the central directory from a PK3 package
371 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
373 long filesize, maxsize;
377 // Get the package size
378 filesize = lseek (packhandle, 0, SEEK_END);
379 if (filesize < ZIP_END_CDIR_SIZE)
382 // Load the end of the file in memory
383 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
386 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
387 buffer = Mem_Alloc (tempmempool, maxsize);
388 lseek (packhandle, filesize - maxsize, SEEK_SET);
389 if (read (packhandle, buffer, maxsize) != (ssize_t) maxsize)
395 // Look for the end of central dir signature around the end of the file
396 maxsize -= ZIP_END_CDIR_SIZE;
397 ptr = &buffer[maxsize];
399 while (BuffBigLong (ptr) != ZIP_END_HEADER)
411 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
412 eocd->signature = LittleLong (eocd->signature);
413 eocd->disknum = LittleShort (eocd->disknum);
414 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
415 eocd->localentries = LittleShort (eocd->localentries);
416 eocd->nbentries = LittleShort (eocd->nbentries);
417 eocd->cdir_size = LittleLong (eocd->cdir_size);
418 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
419 eocd->comment_size = LittleShort (eocd->comment_size);
431 Extract the file list from a PK3 file
434 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
436 qbyte *central_dir, *ptr;
440 // Load the central directory in memory
441 central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
442 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
443 read (pack->handle, central_dir, eocd->cdir_size);
445 // Extract the files properties
446 // The parsing is done "by hand" because some fields have variable sizes and
447 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
448 remaining = eocd->cdir_size;
451 for (ind = 0; ind < eocd->nbentries; ind++)
453 size_t namesize, count;
455 // Checking the remaining size
456 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
458 Mem_Free (central_dir);
461 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
464 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
466 Mem_Free (central_dir);
470 namesize = BuffLittleShort (&ptr[28]); // filename length
472 // Check encryption, compression, and attributes
473 // 1st uint8 : general purpose bit flag
474 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
475 // 2nd uint8 : external file attributes
476 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
477 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
479 // Still enough bytes for the name?
480 if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
482 Mem_Free (central_dir);
486 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
487 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
489 char filename [sizeof (pack->files[0].name)];
490 size_t offset, packsize, realsize;
491 packfile_flags_t flags;
493 // Extract the name (strip it if necessary)
494 if (namesize >= sizeof (filename))
495 namesize = sizeof (filename) - 1;
496 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
497 filename[namesize] = '\0';
499 if (BuffLittleShort (&ptr[10]))
500 flags = PACKFILE_FLAG_DEFLATED;
503 offset = BuffLittleLong (&ptr[42]);
504 packsize = BuffLittleLong (&ptr[20]);
505 realsize = BuffLittleLong (&ptr[24]);
506 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
510 // Skip the name, additionnal field, and comment
511 // 1er uint16 : extra field length
512 // 2eme uint16 : file comment length
513 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
514 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
518 // If the package is empty, central_dir is NULL here
519 if (central_dir != NULL)
520 Mem_Free (central_dir);
521 return pack->numfiles;
529 Create a package entry associated with a PK3 file
532 pack_t *FS_LoadPackPK3 (const char *packfile)
535 pk3_endOfCentralDir_t eocd;
539 packhandle = open (packfile, O_RDONLY | O_BINARY);
543 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
544 Sys_Error ("%s is not a PK3 file", packfile);
546 // Multi-volume ZIP archives are NOT allowed
547 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
548 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
550 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
551 // since eocd.nbentries is an unsigned 16 bits integer
552 #if MAX_FILES_IN_PACK < 65535
553 if (eocd.nbentries > MAX_FILES_IN_PACK)
554 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
557 // Create a package structure in memory
558 pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
559 pack->ignorecase = true; // PK3 ignores case
560 strlcpy (pack->filename, packfile, sizeof (pack->filename));
561 pack->handle = packhandle;
562 pack->numfiles = eocd.nbentries;
563 pack->mempool = Mem_AllocPool (packfile, 0, NULL);
564 pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
565 pack->next = packlist;
568 real_nb_files = PK3_BuildFileList (pack, &eocd);
569 if (real_nb_files < 0)
570 Sys_Error ("%s is not a valid PK3 file", packfile);
572 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
579 PK3_GetTrueFileOffset
581 Find where the true file data offset is
584 void PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
586 qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
590 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
593 // Load the local file description
594 lseek (pack->handle, pfile->offset, SEEK_SET);
595 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
596 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
597 Sys_Error ("Can't retrieve file %s in package %s", pfile->name, pack->filename);
599 // Skip name and extra field
600 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
602 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
607 =============================================================================
609 OTHER PRIVATE FUNCTIONS
611 =============================================================================
619 Add a file to the list of files contained into a package
622 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
623 size_t offset, size_t packsize,
624 size_t realsize, packfile_flags_t flags)
626 int (*strcmp_funct) (const char* str1, const char* str2);
627 int left, right, middle;
630 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
632 // Look for the slot we should put that file into (binary search)
634 right = pack->numfiles - 1;
635 while (left <= right)
639 middle = (left + right) / 2;
640 diff = strcmp_funct (pack->files[middle].name, name);
642 // If we found the file, there's a problem
644 Sys_Error ("Package %s contains the file %s several times\n",
645 pack->filename, name);
647 // If we're too far in the list
654 // We have to move the right of the list by one slot to free the one we need
655 pfile = &pack->files[left];
656 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
659 strlcpy (pfile->name, name, sizeof (pfile->name));
660 pfile->offset = offset;
661 pfile->packsize = packsize;
662 pfile->realsize = realsize;
663 pfile->flags = flags;
673 Only used for FS_Open.
676 void FS_CreatePath (char *path)
680 for (ofs = path+1 ; *ofs ; ofs++)
682 if (*ofs == '/' || *ofs == '\\')
684 // create the directory
700 void FS_Path_f (void)
704 Con_Print("Current search path:\n");
705 for (s=fs_searchpaths ; s ; s=s->next)
709 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
712 Con_Printf("%s\n", s->filename);
721 Takes an explicit (not game tree related) path to a pak file.
723 Loads the header and directory, adding the files at the beginning
724 of the list so they override previous pack files.
727 pack_t *FS_LoadPackPAK (const char *packfile)
729 dpackheader_t header;
735 packhandle = open (packfile, O_RDONLY | O_BINARY);
738 read (packhandle, (void *)&header, sizeof(header));
739 if (memcmp(header.id, "PACK", 4))
740 Sys_Error ("%s is not a packfile", packfile);
741 header.dirofs = LittleLong (header.dirofs);
742 header.dirlen = LittleLong (header.dirlen);
744 if (header.dirlen % sizeof(dpackfile_t))
745 Sys_Error ("%s has an invalid directory size", packfile);
747 numpackfiles = header.dirlen / sizeof(dpackfile_t);
749 if (numpackfiles > MAX_FILES_IN_PACK)
750 Sys_Error ("%s has %i files", packfile, numpackfiles);
752 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
753 pack->ignorecase = false; // PAK is case sensitive
754 strlcpy (pack->filename, packfile, sizeof (pack->filename));
755 pack->handle = packhandle;
757 pack->mempool = Mem_AllocPool(packfile, 0, NULL);
758 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
759 pack->next = packlist;
762 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
763 lseek (packhandle, header.dirofs, SEEK_SET);
764 read (packhandle, (void *)info, header.dirlen);
766 // parse the directory
767 for (i = 0;i < numpackfiles;i++)
769 size_t offset = LittleLong (info[i].filepos);
770 size_t size = LittleLong (info[i].filelen);
772 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
777 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
786 Sets fs_gamedir, adds the directory to the head of the path,
787 then loads and adds pak1.pak pak2.pak ...
790 void FS_AddGameDirectory (const char *dir)
792 stringlist_t *list, *current;
793 searchpath_t *search;
795 char pakfile[MAX_OSPATH];
797 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
799 list = listdirectory(dir);
801 // add any PAK package in the directory
802 for (current = list;current;current = current->next)
804 if (matchpattern(current->text, "*.pak", true))
806 dpsnprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
807 pak = FS_LoadPackPAK (pakfile);
810 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
812 search->next = fs_searchpaths;
813 fs_searchpaths = search;
816 Con_Printf("unable to load pak \"%s\"\n", pakfile);
820 // add any PK3 package in the director
821 for (current = list;current;current = current->next)
823 if (matchpattern(current->text, "*.pk3", true))
825 dpsnprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
826 pak = FS_LoadPackPK3 (pakfile);
829 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
831 search->next = fs_searchpaths;
832 fs_searchpaths = search;
835 Con_Printf("unable to load pak \"%s\"\n", pakfile);
840 // Add the directory to the search path
841 // (unpacked files have the priority over packed files)
842 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
843 strlcpy (search->filename, dir, sizeof (search->filename));
844 search->next = fs_searchpaths;
845 fs_searchpaths = search;
854 void FS_AddGameHierarchy (const char *dir)
860 strlcpy (com_modname, dir, sizeof (com_modname));
862 // Add the common game directory
863 FS_AddGameDirectory (va("%s/%s", fs_basedir, dir));
866 // Add the personal game directory
867 homedir = getenv ("HOME");
868 if (homedir != NULL && homedir[0] != '\0')
869 FS_AddGameDirectory (va("%s/.%s/%s", homedir, gameuserdirname, dir));
879 static const char *FS_FileExtension (const char *in)
881 const char *separator, *backslash, *colon, *dot;
883 separator = strrchr(in, '/');
884 backslash = strrchr(in, '\\');
885 if (separator < backslash)
886 separator = backslash;
887 colon = strrchr(in, ':');
888 if (separator < colon)
891 dot = strrchr(in, '.');
892 if (dot == NULL || dot < separator)
907 searchpath_t *search;
909 fs_mempool = Mem_AllocPool("file management", 0, NULL);
910 pak_mempool = Mem_AllocPool("paks", 0, NULL);
912 Cvar_RegisterVariable (&scr_screenshot_name);
914 Cmd_AddCommand ("path", FS_Path_f);
915 Cmd_AddCommand ("dir", FS_Dir_f);
916 Cmd_AddCommand ("ls", FS_Ls_f);
918 strcpy(fs_basedir, ".");
919 strcpy(fs_gamedir, ".");
924 // Overrides the system supplied base directory (under GAMENAME)
925 // 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)
926 i = COM_CheckParm ("-basedir");
927 if (i && i < com_argc-1)
929 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
930 i = strlen (fs_basedir);
931 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
935 // -path <dir or packfile> [<dir or packfile>] ...
936 // Fully specifies the exact search path, overriding the generated one
937 // COMMANDLINEOPTION: Filesystem: -path <path ..> specifies the full search path manually, overriding the generated one, example: -path c:\quake\id1 c:\quake\pak0.pak c:\quake\pak1.pak (not recommended)
938 i = COM_CheckParm ("-path");
942 while (++i < com_argc)
944 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
947 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
948 if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
950 search->pack = FS_LoadPackPAK (com_argv[i]);
952 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
954 else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
956 search->pack = FS_LoadPackPK3 (com_argv[i]);
958 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
961 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
962 search->next = fs_searchpaths;
963 fs_searchpaths = search;
968 // add the game-specific paths
969 // gamedirname1 (typically id1)
970 FS_AddGameHierarchy (gamedirname1);
971 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
973 // add the game-specific path, if any
977 FS_AddGameHierarchy (gamedirname2);
981 // Adds basedir/gamedir as an override game
982 // LordHavoc: now supports multiple -game directories
983 for (i = 1;i < com_argc;i++)
987 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
991 FS_AddGameHierarchy (com_argv[i]);
992 Cvar_SetQuick (&scr_screenshot_name, com_modname);
996 // If "-condebug" is in the command line, remove the previous log file
997 if (COM_CheckParm ("-condebug") != 0)
998 unlink (va("%s/qconsole.log", fs_gamedir));
1006 void FS_Shutdown (void)
1008 Mem_FreePool (&pak_mempool);
1009 Mem_FreePool (&fs_mempool);
1013 ====================
1016 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1017 ====================
1019 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1025 // Parse the mode string
1034 opt = O_CREAT | O_TRUNC;
1038 opt = O_CREAT | O_APPEND;
1041 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1044 for (ind = 1; mode[ind] != '\0'; ind++)
1055 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1056 filepath, mode, mode[ind]);
1065 file = Mem_Alloc (fs_mempool, sizeof (*file));
1066 memset (file, 0, sizeof (*file));
1069 file->handle = open (filepath, mod | opt, 0666);
1070 if (file->handle < 0)
1076 file->real_length = lseek (file->handle, 0, SEEK_END);
1078 // For files opened in append mode, we start at the end of the file
1080 file->position = file->real_length;
1082 lseek (file->handle, 0, SEEK_SET);
1092 Open a packed file using its package file descriptor
1095 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1101 pfile = &pack->files[pack_ind];
1103 // If we don't have the true offset, get it now
1104 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1105 PK3_GetTrueFileOffset (pfile, pack);
1107 // No Zlib DLL = no compressed files
1108 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1110 Con_Printf("WARNING: can't open the compressed file %s\n"
1111 "You need the Zlib DLL to use compressed files\n",
1117 dup_handle = dup (pack->handle);
1119 Sys_Error ("FS_OpenPackedFile: can't dup package's handle (pack: %s)", pack->filename);
1121 file = Mem_Alloc (fs_mempool, sizeof (*file));
1122 memset (file, 0, sizeof (*file));
1123 file->handle = dup_handle;
1124 file->flags = QFILE_FLAG_PACKED;
1125 file->real_length = pfile->realsize;
1126 file->offset = pfile->offset;
1130 if (lseek (file->handle, file->offset, SEEK_SET) == -1)
1131 Sys_Error ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)",
1132 pfile->name, pack->filename, file->offset);
1134 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1138 file->flags |= QFILE_FLAG_DEFLATED;
1140 // We need some more variables
1141 ztk = Mem_Alloc (fs_mempool, sizeof (*ztk));
1143 ztk->comp_length = pfile->packsize;
1145 // Initialize zlib stream
1146 ztk->zstream.next_in = ztk->input;
1147 ztk->zstream.avail_in = 0;
1149 /* From Zlib's "unzip.c":
1151 * windowBits is passed < 0 to tell that there is no zlib header.
1152 * Note that in this case inflate *requires* an extra "dummy" byte
1153 * after the compressed stream in order to complete decompression and
1154 * return Z_STREAM_END.
1155 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1156 * size of both compressed and uncompressed data
1158 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1159 Sys_Error ("FS_OpenPackedFile: inflate init error (file: %s)", pfile->name);
1161 ztk->zstream.next_out = file->buff;
1162 ztk->zstream.avail_out = sizeof (file->buff);
1167 fs_filesize = pfile->realsize;
1173 ====================
1176 Return true if the path should be rejected due to one of the following:
1177 1: path elements that are non-portable
1178 2: path elements that would allow access to files outside the game directory,
1179 or are just not a good idea for a mod to be using.
1180 ====================
1182 int FS_CheckNastyPath (const char *path)
1184 // Windows: don't allow \ in filenames (windows-only), period.
1185 // (on Windows \ is a directory separator, but / is also supported)
1186 if (strstr(path, "\\"))
1187 return 1; // non-portable
1189 // Mac: don't allow Mac-only filenames - : is a directory separator
1190 // instead of /, but we rely on / working already, so there's no reason to
1191 // support a Mac-only path
1192 // Amiga and Windows: : tries to go to root of drive
1193 if (strstr(path, ":"))
1194 return 1; // non-portable attempt to go to root of drive
1196 // Amiga: // is parent directory
1197 if (strstr(path, "//"))
1198 return 1; // non-portable attempt to go to parent directory
1200 // all: don't allow going to current directory (./) or parent directory (../ or /../)
1201 if (strstr(path, "./"))
1202 return 2; // attempt to go outside the game directory
1204 // Windows and UNIXes: don't allow absolute paths
1206 return 2; // attempt to go outside the game directory
1208 // after all these checks we're pretty sure it's a / separated filename
1209 // and won't do much if any harm
1215 ====================
1218 Look for a file in the packages and in the filesystem
1220 Return the searchpath where the file was found (or NULL)
1221 and the file index in the package if relevant
1222 ====================
1224 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1226 searchpath_t *search;
1229 // search through the path, one element at a time
1230 for (search = fs_searchpaths;search;search = search->next)
1232 // is the element a pak file?
1235 int (*strcmp_funct) (const char* str1, const char* str2);
1236 int left, right, middle;
1239 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1241 // Look for the file (binary search)
1243 right = pak->numfiles - 1;
1244 while (left <= right)
1248 middle = (left + right) / 2;
1249 diff = strcmp_funct (pak->files[middle].name, name);
1255 Con_DPrintf("FS_FindFile: %s in %s\n",
1256 pak->files[middle].name, pak->filename);
1263 // If we're too far in the list
1272 char netpath[MAX_OSPATH];
1273 dpsnprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1274 if (FS_SysFileExists (netpath))
1277 Con_DPrintf("FS_FindFile: %s\n", netpath);
1287 Con_DPrintf("FS_FindFile: can't find %s\n", name);
1299 Look for a file in the search paths and open it in read-only mode
1304 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1306 searchpath_t *search;
1309 search = FS_FindFile (filename, &pack_ind, quiet);
1318 // Found in the filesystem?
1321 char path [MAX_OSPATH];
1322 dpsnprintf (path, sizeof (path), "%s/%s", search->filename, filename);
1323 return FS_SysOpen (path, "rb", nonblocking);
1326 // So, we found it in a package...
1327 return FS_OpenPackedFile (search->pack, pack_ind);
1332 =============================================================================
1334 MAIN PUBLIC FUNCTIONS
1336 =============================================================================
1340 ====================
1343 Open a file. The syntax is the same as fopen
1344 ====================
1346 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1350 if (FS_CheckNastyPath(filepath))
1352 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1356 // If the file is opened in "write", "append", or "read/write" mode
1357 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1359 char real_path [MAX_OSPATH];
1361 // Open the file on disk directly
1362 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1364 // Create directories up to the file
1365 FS_CreatePath (real_path);
1367 return FS_SysOpen (real_path, mode, nonblocking);
1370 // Else, we look at the various search paths and open the file in read-only mode
1371 file = FS_OpenReadFile (filepath, quiet, nonblocking);
1373 fs_filesize = file->real_length;
1380 ====================
1384 ====================
1386 int FS_Close (qfile_t* file)
1388 if (close (file->handle))
1393 qz_inflateEnd (&file->ztk->zstream);
1394 Mem_Free (file->ztk);
1403 ====================
1406 Write "datasize" bytes into a file
1407 ====================
1409 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1413 // If necessary, seek to the exact file position we're supposed to be
1414 if (file->buff_ind != file->buff_len)
1415 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1417 // Purge cached data
1420 // Write the buffer and update the position
1421 result = write (file->handle, data, datasize);
1422 file->position = lseek (file->handle, 0, SEEK_CUR);
1423 if (file->real_length < file->position)
1424 file->real_length = file->position;
1434 ====================
1437 Read up to "buffersize" bytes from a file
1438 ====================
1440 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1444 if (buffersize == 0)
1447 // Get rid of the ungetc character
1448 if (file->ungetc != EOF)
1450 ((char*)buffer)[0] = file->ungetc;
1458 // First, we copy as many bytes as we can from "buff"
1459 if (file->buff_ind < file->buff_len)
1461 count = file->buff_len - file->buff_ind;
1463 done += (buffersize > count) ? count : buffersize;
1464 memcpy (buffer, &file->buff[file->buff_ind], done);
1465 file->buff_ind += done;
1468 if (buffersize == 0)
1472 // NOTE: at this point, the read buffer is always empty
1474 // If the file isn't compressed
1475 if (! (file->flags & QFILE_FLAG_DEFLATED))
1479 // We must take care to not read after the end of the file
1480 count = file->real_length - file->position;
1482 // If we have a lot of data to get, put them directly into "buffer"
1483 if (buffersize > sizeof (file->buff) / 2)
1485 if (count > buffersize)
1487 lseek (file->handle, file->offset + file->position, SEEK_SET);
1488 nb = read (file->handle, &((qbyte*)buffer)[done], count);
1492 file->position += nb;
1494 // Purge cached data
1500 if (count > sizeof (file->buff))
1501 count = sizeof (file->buff);
1502 lseek (file->handle, file->offset + file->position, SEEK_SET);
1503 nb = read (file->handle, file->buff, count);
1506 file->buff_len = nb;
1507 file->position += nb;
1509 // Copy the requested data in "buffer" (as much as we can)
1510 count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1511 memcpy (&((qbyte*)buffer)[done], file->buff, count);
1512 file->buff_ind = count;
1520 // If the file is compressed, it's more complicated...
1521 // We cycle through a few operations until we have read enough data
1522 while (buffersize > 0)
1524 ztoolkit_t *ztk = file->ztk;
1527 // NOTE: at this point, the read buffer is always empty
1529 // If "input" is also empty, we need to refill it
1530 if (ztk->in_ind == ztk->in_len)
1532 // If we are at the end of the file
1533 if (file->position == file->real_length)
1536 count = ztk->comp_length - ztk->in_position;
1537 if (count > sizeof (ztk->input))
1538 count = sizeof (ztk->input);
1539 lseek (file->handle, file->offset + ztk->in_position, SEEK_SET);
1540 if (read (file->handle, ztk->input, count) != (ssize_t)count)
1541 Sys_Error ("FS_Read: unexpected end of file");
1544 ztk->in_len = count;
1545 ztk->in_position += count;
1548 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1549 ztk->zstream.avail_in = ztk->in_len - ztk->in_ind;
1551 // Now that we are sure we have compressed data available, we need to determine
1552 // if it's better to inflate it in "file->buff" or directly in "buffer"
1554 // Inflate the data in "file->buff"
1555 if (buffersize < sizeof (file->buff) / 2)
1557 ztk->zstream.next_out = file->buff;
1558 ztk->zstream.avail_out = sizeof (file->buff);
1559 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1560 if (error != Z_OK && error != Z_STREAM_END)
1561 Sys_Error ("Can't inflate file");
1562 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1564 file->buff_len = sizeof (file->buff) - ztk->zstream.avail_out;
1565 file->position += file->buff_len;
1567 // Copy the requested data in "buffer" (as much as we can)
1568 count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1569 memcpy (&((qbyte*)buffer)[done], file->buff, count);
1570 file->buff_ind = count;
1573 // Else, we inflate directly in "buffer"
1576 ztk->zstream.next_out = &((qbyte*)buffer)[done];
1577 ztk->zstream.avail_out = buffersize;
1578 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1579 if (error != Z_OK && error != Z_STREAM_END)
1580 Sys_Error ("Can't inflate file");
1581 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1583 // How much data did it inflate?
1584 count = buffersize - ztk->zstream.avail_out;
1585 file->position += count;
1587 // Purge cached data
1592 buffersize -= count;
1600 ====================
1603 Print a string into a file
1604 ====================
1606 int FS_Print (qfile_t* file, const char *msg)
1608 return FS_Write (file, msg, strlen (msg));
1612 ====================
1615 Print a string into a file
1616 ====================
1618 int FS_Printf(qfile_t* file, const char* format, ...)
1623 va_start (args, format);
1624 result = FS_VPrintf (file, format, args);
1632 ====================
1635 Print a string into a file
1636 ====================
1638 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
1642 char *tempbuff = NULL;
1645 tempbuff = Mem_Alloc (tempmempool, buff_size);
1646 len = dpvsnprintf (tempbuff, buff_size, format, ap);
1649 Mem_Free (tempbuff);
1651 tempbuff = Mem_Alloc (tempmempool, buff_size);
1652 len = dpvsnprintf (tempbuff, buff_size, format, ap);
1655 len = write (file->handle, tempbuff, len);
1656 Mem_Free (tempbuff);
1663 ====================
1666 Get the next character of a file
1667 ====================
1669 int FS_Getc (qfile_t* file)
1673 if (FS_Read (file, &c, 1) != 1)
1681 ====================
1684 Put a character back into the read buffer (only supports one character!)
1685 ====================
1687 int FS_UnGetc (qfile_t* file, unsigned char c)
1689 // If there's already a character waiting to be read
1690 if (file->ungetc != EOF)
1699 ====================
1702 Move the position index in a file
1703 ====================
1705 int FS_Seek (qfile_t* file, long offset, int whence)
1711 // Compute the file offset
1715 offset += file->position - file->buff_len + file->buff_ind;
1722 offset += file->real_length;
1728 if (offset < 0 || offset > (long) file->real_length)
1731 // If we have the data in our read buffer, we don't need to actually seek
1732 if (file->position - file->buff_len <= (size_t)offset &&
1733 (size_t)offset <= file->position)
1735 file->buff_ind = offset + file->buff_len - file->position;
1739 // Purge cached data
1742 // Unpacked or uncompressed files can seek directly
1743 if (! (file->flags & QFILE_FLAG_DEFLATED))
1745 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
1747 file->position = offset;
1751 // Seeking in compressed files is more a hack than anything else,
1752 // but we need to support it, so here we go.
1755 // If we have to go back in the file, we need to restart from the beginning
1756 if ((size_t)offset <= file->position)
1760 ztk->in_position = 0;
1762 lseek (file->handle, file->offset, SEEK_SET);
1764 // Reset the Zlib stream
1765 ztk->zstream.next_in = ztk->input;
1766 ztk->zstream.avail_in = 0;
1767 qz_inflateReset (&ztk->zstream);
1770 // We need a big buffer to force inflating into it directly
1771 buffersize = 2 * sizeof (file->buff);
1772 buffer = Mem_Alloc (tempmempool, buffersize);
1774 // Skip all data until we reach the requested offset
1775 while ((size_t)offset > file->position)
1777 size_t diff = offset - file->position;
1780 count = (diff > buffersize) ? buffersize : diff;
1781 len = FS_Read (file, buffer, count);
1795 ====================
1798 Give the current position in a file
1799 ====================
1801 long FS_Tell (qfile_t* file)
1803 return file->position - file->buff_len + file->buff_ind;
1808 ====================
1811 Erases any buffered input or output data
1812 ====================
1814 void FS_Purge (qfile_t* file)
1826 Filename are relative to the quake directory.
1827 Always appends a 0 byte.
1830 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1835 file = FS_Open (path, "rb", quiet, false);
1839 buf = Mem_Alloc (pool, fs_filesize + 1);
1840 buf[fs_filesize] = '\0';
1842 FS_Read (file, buf, fs_filesize);
1853 The filename will be prefixed by the current game directory
1856 qboolean FS_WriteFile (const char *filename, void *data, int len)
1860 file = FS_Open (filename, "wb", false, false);
1863 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1867 Con_DPrintf("FS_WriteFile: %s\n", filename);
1868 FS_Write (file, data, len);
1875 =============================================================================
1877 OTHERS PUBLIC FUNCTIONS
1879 =============================================================================
1887 void FS_StripExtension (const char *in, char *out, size_t size_out)
1894 while (*in && size_out > 1)
1898 else if (*in == '/' || *in == '\\' || *in == ':')
1915 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1919 // if path doesn't have a .EXT, append extension
1920 // (extension should include the .)
1921 src = path + strlen(path) - 1;
1923 while (*src != '/' && src != path)
1926 return; // it has an extension
1930 strlcat (path, extension, size_path);
1938 Look for a file in the packages and in the filesystem
1941 qboolean FS_FileExists (const char *filename)
1943 return (FS_FindFile (filename, NULL, true) != NULL);
1951 Look for a file in the filesystem only
1954 qboolean FS_SysFileExists (const char *path)
1959 // TODO: use another function instead, to avoid opening the file
1960 desc = open (path, O_RDONLY | O_BINARY);
1969 if (stat (path,&buf) == -1)
1976 void FS_mkdir (const char *path)
1989 Allocate and fill a search structure with information on matching filenames.
1992 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
1995 searchpath_t *searchpath;
1997 int i, basepathlength, numfiles, numchars;
1998 stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
1999 const char *slash, *backslash, *colon, *separator;
2001 char netpath[MAX_OSPATH];
2002 char temp[MAX_OSPATH];
2004 while(!strncmp(pattern, "./", 2))
2006 while(!strncmp(pattern, ".\\", 2))
2013 slash = strrchr(pattern, '/');
2014 backslash = strrchr(pattern, '\\');
2015 colon = strrchr(pattern, ':');
2016 separator = pattern;
2017 if (separator < slash)
2019 if (separator < backslash)
2020 separator = backslash;
2021 if (separator < colon)
2023 basepathlength = separator - pattern;
2024 basepath = Mem_Alloc (tempmempool, basepathlength + 1);
2026 memcpy(basepath, pattern, basepathlength);
2027 basepath[basepathlength] = 0;
2029 // search through the path, one element at a time
2030 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2032 // is the element a pak file?
2033 if (searchpath->pack)
2035 // look through all the pak file elements
2036 pak = searchpath->pack;
2037 for (i = 0;i < pak->numfiles;i++)
2039 strcpy(temp, pak->files[i].name);
2042 if (matchpattern(temp, (char *)pattern, true))
2044 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2045 if (!strcmp(listtemp->text, temp))
2047 if (listtemp == NULL)
2049 listcurrent = stringlistappend(listcurrent, temp);
2050 if (liststart == NULL)
2051 liststart = listcurrent;
2053 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2056 // strip off one path element at a time until empty
2057 // this way directories are added to the listing if they match the pattern
2058 slash = strrchr(temp, '/');
2059 backslash = strrchr(temp, '\\');
2060 colon = strrchr(temp, ':');
2062 if (separator < slash)
2064 if (separator < backslash)
2065 separator = backslash;
2066 if (separator < colon)
2068 *((char *)separator) = 0;
2074 // get a directory listing and look at each name
2075 dpsnprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
2076 if ((dir = listdirectory(netpath)))
2078 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2080 dpsnprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
2081 if (matchpattern(temp, (char *)pattern, true))
2083 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2084 if (!strcmp(listtemp->text, temp))
2086 if (listtemp == NULL)
2088 listcurrent = stringlistappend(listcurrent, temp);
2089 if (liststart == NULL)
2090 liststart = listcurrent;
2092 Con_DPrintf("SearchDirFile: %s\n", temp);
2103 liststart = stringlistsort(liststart);
2106 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2109 numchars += strlen(listtemp->text) + 1;
2111 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2112 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2113 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2114 search->numfilenames = numfiles;
2117 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2119 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2120 strcpy(search->filenames[numfiles], listtemp->text);
2122 numchars += strlen(listtemp->text) + 1;
2125 stringlistfree(liststart);
2132 void FS_FreeSearch(fssearch_t *search)
2137 extern int con_linewidth;
2138 int FS_ListDirectory(const char *pattern, int oneperline)
2149 search = FS_Search(pattern, true, true);
2152 numfiles = search->numfilenames;
2155 // FIXME: the names could be added to one column list and then
2156 // gradually shifted into the next column if they fit, and then the
2157 // next to make a compact variable width listing but it's a lot more
2159 // find width for columns
2161 for (i = 0;i < numfiles;i++)
2163 l = strlen(search->filenames[i]);
2164 if (columnwidth < l)
2167 // count the spacing character
2169 // calculate number of columns
2170 numcolumns = con_linewidth / columnwidth;
2171 // don't bother with the column printing if it's only one column
2172 if (numcolumns >= 2)
2174 numlines = (numfiles + numcolumns - 1) / numcolumns;
2175 for (i = 0;i < numlines;i++)
2178 for (k = 0;k < numcolumns;k++)
2180 l = i * numcolumns + k;
2183 name = search->filenames[l];
2184 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2185 linebuf[linebufpos++] = name[j];
2186 // space out name unless it's the last on the line
2187 if (k < (numcolumns - 1) && l < (numfiles - 1))
2188 for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2189 linebuf[linebufpos++] = ' ';
2192 linebuf[linebufpos] = 0;
2193 Con_Printf("%s\n", linebuf);
2200 for (i = 0;i < numfiles;i++)
2201 Con_Printf("%s\n", search->filenames[i]);
2202 FS_FreeSearch(search);
2206 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2208 const char *pattern;
2211 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2214 if (Cmd_Argc() == 2)
2215 pattern = Cmd_Argv(1);
2218 if (!FS_ListDirectory(pattern, oneperline))
2219 Con_Print("No files found.\n");
2224 FS_ListDirectoryCmd("dir", true);
2229 FS_ListDirectoryCmd("ls", false);