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)
156 size_t buff_ind, buff_len; // buffer current index and length
157 qbyte buff [FILE_BUFF_SIZE];
164 // ------ PK3 files on disk ------ //
166 // You can get the complete ZIP format description from PKWARE website
170 unsigned int signature;
171 unsigned short disknum;
172 unsigned short cdir_disknum; // number of the disk with the start of the central directory
173 unsigned short localentries; // number of entries in the central directory on this disk
174 unsigned short nbentries; // total number of entries in the central directory on this disk
175 unsigned int cdir_size; // size of the central directory
176 unsigned int cdir_offset; // with respect to the starting disk number
177 unsigned short comment_size;
178 } pk3_endOfCentralDir_t;
181 // ------ PAK files on disk ------ //
185 int filepos, filelen;
196 // Packages in memory
199 PACKFILE_FLAG_NONE = 0,
200 PACKFILE_FLAG_TRUEOFFS = (1 << 0), // the offset in packfile_t is the true contents offset
201 PACKFILE_FLAG_DEFLATED = (1 << 1) // file compressed using the deflate algorithm
206 char name [MAX_QPATH];
207 packfile_flags_t flags;
209 size_t packsize; // size in the package
210 size_t realsize; // real file size (uncompressed)
213 typedef struct pack_s
215 char filename [MAX_OSPATH];
217 int ignorecase; // PK3 ignores case
225 // Search paths for files (including packages)
226 typedef struct searchpath_s
228 // only one of filename / pack will be used
229 char filename[MAX_OSPATH];
231 struct searchpath_s *next;
236 =============================================================================
240 =============================================================================
246 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
247 size_t offset, size_t packsize,
248 size_t realsize, packfile_flags_t flags);
252 =============================================================================
256 =============================================================================
259 mempool_t *fs_mempool;
260 mempool_t *pak_mempool;
264 pack_t *packlist = NULL;
266 searchpath_t *fs_searchpaths = NULL;
268 #define MAX_FILES_IN_PACK 65536
270 char fs_gamedir[MAX_OSPATH];
271 char fs_basedir[MAX_OSPATH];
273 qboolean fs_modified; // set true if using non-id files
277 =============================================================================
279 PRIVATE FUNCTIONS - PK3 HANDLING
281 =============================================================================
284 // Functions exported from zlib
286 # define ZEXPORT WINAPI
291 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
292 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
293 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
294 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
296 #define qz_inflateInit2(strm, windowBits) \
297 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
299 static dllfunction_t zlibfuncs[] =
301 {"inflate", (void **) &qz_inflate},
302 {"inflateEnd", (void **) &qz_inflateEnd},
303 {"inflateInit2_", (void **) &qz_inflateInit2_},
304 {"inflateReset", (void **) &qz_inflateReset},
308 // Handle for Zlib DLL
309 static dllhandle_t zlib_dll = NULL;
319 void PK3_CloseLibrary (void)
321 Sys_UnloadLibrary (&zlib_dll);
329 Try to load the Zlib DLL
332 qboolean PK3_OpenLibrary (void)
334 const char* dllnames [] =
338 #elif defined(MACOSX)
352 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
354 Con_Printf ("Compressed files support disabled\n");
358 Con_Printf ("Compressed files support enabled\n");
365 PK3_GetEndOfCentralDir
367 Extract the end of the central directory from a PK3 package
370 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
372 long filesize, maxsize;
376 // Get the package size
377 filesize = lseek (packhandle, 0, SEEK_END);
378 if (filesize < ZIP_END_CDIR_SIZE)
381 // Load the end of the file in memory
382 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
385 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
386 buffer = Mem_Alloc (tempmempool, maxsize);
387 lseek (packhandle, filesize - maxsize, SEEK_SET);
388 if (read (packhandle, buffer, maxsize) != (ssize_t) maxsize)
394 // Look for the end of central dir signature around the end of the file
395 maxsize -= ZIP_END_CDIR_SIZE;
396 ptr = &buffer[maxsize];
398 while (BuffBigLong (ptr) != ZIP_END_HEADER)
410 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
411 eocd->signature = LittleLong (eocd->signature);
412 eocd->disknum = LittleShort (eocd->disknum);
413 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
414 eocd->localentries = LittleShort (eocd->localentries);
415 eocd->nbentries = LittleShort (eocd->nbentries);
416 eocd->cdir_size = LittleLong (eocd->cdir_size);
417 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
418 eocd->comment_size = LittleShort (eocd->comment_size);
430 Extract the file list from a PK3 file
433 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
435 qbyte *central_dir, *ptr;
439 // Load the central directory in memory
440 central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
441 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
442 read (pack->handle, central_dir, eocd->cdir_size);
444 // Extract the files properties
445 // The parsing is done "by hand" because some fields have variable sizes and
446 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
447 remaining = eocd->cdir_size;
450 for (ind = 0; ind < eocd->nbentries; ind++)
452 size_t namesize, count;
454 // Checking the remaining size
455 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
457 Mem_Free (central_dir);
460 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
463 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
465 Mem_Free (central_dir);
469 namesize = BuffLittleShort (&ptr[28]); // filename length
471 // Check encryption, compression, and attributes
472 // 1st uint8 : general purpose bit flag
473 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
474 // 2nd uint8 : external file attributes
475 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
476 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
478 // Still enough bytes for the name?
479 if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
481 Mem_Free (central_dir);
485 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
486 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
488 char filename [sizeof (pack->files[0].name)];
489 size_t offset, packsize, realsize;
490 packfile_flags_t flags;
492 // Extract the name (strip it if necessary)
493 if (namesize >= sizeof (filename))
494 namesize = sizeof (filename) - 1;
495 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
496 filename[namesize] = '\0';
498 if (BuffLittleShort (&ptr[10]))
499 flags = PACKFILE_FLAG_DEFLATED;
502 offset = BuffLittleLong (&ptr[42]);
503 packsize = BuffLittleLong (&ptr[20]);
504 realsize = BuffLittleLong (&ptr[24]);
505 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
509 // Skip the name, additionnal field, and comment
510 // 1er uint16 : extra field length
511 // 2eme uint16 : file comment length
512 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
513 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
517 // If the package is empty, central_dir is NULL here
518 if (central_dir != NULL)
519 Mem_Free (central_dir);
520 return pack->numfiles;
528 Create a package entry associated with a PK3 file
531 pack_t *FS_LoadPackPK3 (const char *packfile)
534 pk3_endOfCentralDir_t eocd;
538 packhandle = open (packfile, O_RDONLY | O_BINARY);
542 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
543 Sys_Error ("%s is not a PK3 file", packfile);
545 // Multi-volume ZIP archives are NOT allowed
546 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
547 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
549 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
550 // since eocd.nbentries is an unsigned 16 bits integer
551 #if MAX_FILES_IN_PACK < 65535
552 if (eocd.nbentries > MAX_FILES_IN_PACK)
553 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
556 // Create a package structure in memory
557 pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
558 pack->ignorecase = true; // PK3 ignores case
559 strlcpy (pack->filename, packfile, sizeof (pack->filename));
560 pack->handle = packhandle;
561 pack->numfiles = eocd.nbentries;
562 pack->mempool = Mem_AllocPool (packfile, 0, NULL);
563 pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
564 pack->next = packlist;
567 real_nb_files = PK3_BuildFileList (pack, &eocd);
568 if (real_nb_files < 0)
569 Sys_Error ("%s is not a valid PK3 file", packfile);
571 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
578 PK3_GetTrueFileOffset
580 Find where the true file data offset is
583 void PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
585 qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
589 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
592 // Load the local file description
593 lseek (pack->handle, pfile->offset, SEEK_SET);
594 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
595 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
596 Sys_Error ("Can't retrieve file %s in package %s", pfile->name, pack->filename);
598 // Skip name and extra field
599 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
601 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
606 =============================================================================
608 OTHER PRIVATE FUNCTIONS
610 =============================================================================
618 Add a file to the list of files contained into a package
621 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
622 size_t offset, size_t packsize,
623 size_t realsize, packfile_flags_t flags)
625 int (*strcmp_funct) (const char* str1, const char* str2);
626 int left, right, middle;
629 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
631 // Look for the slot we should put that file into (binary search)
633 right = pack->numfiles - 1;
634 while (left <= right)
638 middle = (left + right) / 2;
639 diff = strcmp_funct (pack->files[middle].name, name);
641 // If we found the file, there's a problem
643 Sys_Error ("Package %s contains the file %s several times\n",
644 pack->filename, name);
646 // If we're too far in the list
653 // We have to move the right of the list by one slot to free the one we need
654 pfile = &pack->files[left];
655 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
658 strlcpy (pfile->name, name, sizeof (pfile->name));
659 pfile->offset = offset;
660 pfile->packsize = packsize;
661 pfile->realsize = realsize;
662 pfile->flags = flags;
672 Only used for FS_Open.
675 void FS_CreatePath (char *path)
679 for (ofs = path+1 ; *ofs ; ofs++)
681 if (*ofs == '/' || *ofs == '\\')
683 // create the directory
699 void FS_Path_f (void)
703 Con_Print("Current search path:\n");
704 for (s=fs_searchpaths ; s ; s=s->next)
708 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
711 Con_Printf("%s\n", s->filename);
720 Takes an explicit (not game tree related) path to a pak file.
722 Loads the header and directory, adding the files at the beginning
723 of the list so they override previous pack files.
726 pack_t *FS_LoadPackPAK (const char *packfile)
728 dpackheader_t header;
734 packhandle = open (packfile, O_RDONLY | O_BINARY);
737 read (packhandle, (void *)&header, sizeof(header));
738 if (memcmp(header.id, "PACK", 4))
739 Sys_Error ("%s is not a packfile", packfile);
740 header.dirofs = LittleLong (header.dirofs);
741 header.dirlen = LittleLong (header.dirlen);
743 if (header.dirlen % sizeof(dpackfile_t))
744 Sys_Error ("%s has an invalid directory size", packfile);
746 numpackfiles = header.dirlen / sizeof(dpackfile_t);
748 if (numpackfiles > MAX_FILES_IN_PACK)
749 Sys_Error ("%s has %i files", packfile, numpackfiles);
751 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
752 pack->ignorecase = false; // PAK is case sensitive
753 strlcpy (pack->filename, packfile, sizeof (pack->filename));
754 pack->handle = packhandle;
756 pack->mempool = Mem_AllocPool(packfile, 0, NULL);
757 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
758 pack->next = packlist;
761 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
762 lseek (packhandle, header.dirofs, SEEK_SET);
763 read (packhandle, (void *)info, header.dirlen);
765 // parse the directory
766 for (i = 0;i < numpackfiles;i++)
768 size_t offset = LittleLong (info[i].filepos);
769 size_t size = LittleLong (info[i].filelen);
771 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
776 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
785 Sets fs_gamedir, adds the directory to the head of the path,
786 then loads and adds pak1.pak pak2.pak ...
789 void FS_AddGameDirectory (const char *dir)
791 stringlist_t *list, *current;
792 searchpath_t *search;
794 char pakfile[MAX_OSPATH];
796 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
798 list = listdirectory(dir);
800 // add any PAK package in the directory
801 for (current = list;current;current = current->next)
803 if (matchpattern(current->text, "*.pak", true))
805 snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
806 pak = FS_LoadPackPAK (pakfile);
809 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
811 search->next = fs_searchpaths;
812 fs_searchpaths = search;
815 Con_Printf("unable to load pak \"%s\"\n", pakfile);
819 // add any PK3 package in the director
820 for (current = list;current;current = current->next)
822 if (matchpattern(current->text, "*.pk3", true))
824 snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
825 pak = FS_LoadPackPK3 (pakfile);
828 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
830 search->next = fs_searchpaths;
831 fs_searchpaths = search;
834 Con_Printf("unable to load pak \"%s\"\n", pakfile);
839 // Add the directory to the search path
840 // (unpacked files have the priority over packed files)
841 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
842 strlcpy (search->filename, dir, sizeof (search->filename));
843 search->next = fs_searchpaths;
844 fs_searchpaths = search;
853 void FS_AddGameHierarchy (const char *dir)
857 strlcpy (com_modname, dir, sizeof (com_modname));
859 // Add the common game directory
860 FS_AddGameDirectory (va("%s/%s", fs_basedir, dir));
862 // Add the personal game directory
863 homedir = getenv ("HOME");
864 if (homedir != NULL && homedir[0] != '\0')
865 FS_AddGameDirectory (va("%s/.%s/%s", homedir, gameuserdirname, dir));
874 static const char *FS_FileExtension (const char *in)
876 const char *separator, *backslash, *colon, *dot;
878 separator = strrchr(in, '/');
879 backslash = strrchr(in, '\\');
880 if (separator < backslash)
881 separator = backslash;
882 colon = strrchr(in, ':');
883 if (separator < colon)
886 dot = strrchr(in, '.');
887 if (dot == NULL || dot < separator)
902 searchpath_t *search;
904 fs_mempool = Mem_AllocPool("file management", 0, NULL);
905 pak_mempool = Mem_AllocPool("paks", 0, NULL);
907 Cvar_RegisterVariable (&scr_screenshot_name);
909 Cmd_AddCommand ("path", FS_Path_f);
910 Cmd_AddCommand ("dir", FS_Dir_f);
911 Cmd_AddCommand ("ls", FS_Ls_f);
913 strcpy(fs_basedir, ".");
914 strcpy(fs_gamedir, ".");
919 // Overrides the system supplied base directory (under GAMENAME)
920 // 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)
921 i = COM_CheckParm ("-basedir");
922 if (i && i < com_argc-1)
924 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
925 i = strlen (fs_basedir);
926 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
930 // -path <dir or packfile> [<dir or packfile>] ...
931 // Fully specifies the exact search path, overriding the generated one
932 // 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)
933 i = COM_CheckParm ("-path");
937 while (++i < com_argc)
939 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
942 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
943 if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
945 search->pack = FS_LoadPackPAK (com_argv[i]);
947 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
949 else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
951 search->pack = FS_LoadPackPK3 (com_argv[i]);
953 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
956 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
957 search->next = fs_searchpaths;
958 fs_searchpaths = search;
963 // add the game-specific paths
964 // gamedirname1 (typically id1)
965 FS_AddGameHierarchy (gamedirname1);
966 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
968 // add the game-specific path, if any
972 FS_AddGameHierarchy (gamedirname2);
976 // Adds basedir/gamedir as an override game
977 // LordHavoc: now supports multiple -game directories
978 for (i = 1;i < com_argc;i++)
982 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
986 FS_AddGameHierarchy (com_argv[i]);
987 Cvar_SetQuick (&scr_screenshot_name, com_modname);
991 // If "-condebug" is in the command line, remove the previous log file
992 if (COM_CheckParm ("-condebug") != 0)
993 unlink (va("%s/qconsole.log", fs_gamedir));
1001 void FS_Shutdown (void)
1003 Mem_FreePool (&pak_mempool);
1004 Mem_FreePool (&fs_mempool);
1008 ====================
1011 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1012 ====================
1014 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
1020 // Parse the mode string
1029 opt = O_CREAT | O_TRUNC;
1033 opt = O_CREAT | O_APPEND;
1036 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1039 for (ind = 1; mode[ind] != '\0'; ind++)
1050 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1051 filepath, mode, mode[ind]);
1055 file = Mem_Alloc (fs_mempool, sizeof (*file));
1056 memset (file, 0, sizeof (*file));
1058 file->handle = open (filepath, mod | opt, 0666);
1059 if (file->handle < 0)
1065 // For files opened in read mode, we now need to get the length
1066 if (mod == O_RDONLY)
1068 file->real_length = lseek (file->handle, 0, SEEK_END);
1069 lseek (file->handle, 0, SEEK_SET);
1080 Open a packed file using its package file descriptor
1083 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1089 pfile = &pack->files[pack_ind];
1091 // If we don't have the true offset, get it now
1092 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1093 PK3_GetTrueFileOffset (pfile, pack);
1095 // No Zlib DLL = no compressed files
1096 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1098 Con_Printf("WARNING: can't open the compressed file %s\n"
1099 "You need the Zlib DLL to use compressed files\n",
1105 dup_handle = dup (pack->handle);
1107 Sys_Error ("FS_OpenPackedFile: can't dup package's handle (pack: %s)", pack->filename);
1109 file = Mem_Alloc (fs_mempool, sizeof (*file));
1110 memset (file, 0, sizeof (*file));
1111 file->handle = dup_handle;
1112 file->flags = QFILE_FLAG_PACKED;
1113 file->real_length = pfile->realsize;
1114 file->offset = pfile->offset;
1117 if (lseek (file->handle, file->offset, SEEK_SET) == -1)
1118 Sys_Error ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)",
1119 pfile->name, pack->filename, file->offset);
1121 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1125 file->flags |= QFILE_FLAG_DEFLATED;
1127 // We need some more variables
1128 ztk = Mem_Alloc (fs_mempool, sizeof (*ztk));
1130 ztk->comp_length = pfile->packsize;
1132 // Initialize zlib stream
1133 ztk->zstream.next_in = ztk->input;
1134 ztk->zstream.avail_in = 0;
1136 /* From Zlib's "unzip.c":
1138 * windowBits is passed < 0 to tell that there is no zlib header.
1139 * Note that in this case inflate *requires* an extra "dummy" byte
1140 * after the compressed stream in order to complete decompression and
1141 * return Z_STREAM_END.
1142 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1143 * size of both compressed and uncompressed data
1145 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1146 Sys_Error ("FS_OpenPackedFile: inflate init error (file: %s)", pfile->name);
1148 ztk->zstream.next_out = file->buff;
1149 ztk->zstream.avail_out = sizeof (file->buff);
1154 fs_filesize = pfile->realsize;
1160 ====================
1163 Return true if the path should be rejected due to one of the following:
1164 1: path elements that are non-portable
1165 2: path elements that would allow access to files outside the game directory,
1166 or are just not a good idea for a mod to be using.
1167 ====================
1169 int FS_CheckNastyPath (const char *path)
1171 // Windows: don't allow \ in filenames (windows-only), period.
1172 // (on Windows \ is a directory separator, but / is also supported)
1173 if (strstr(path, "\\"))
1174 return 1; // non-portable
1176 // Mac: don't allow Mac-only filenames - : is a directory separator
1177 // instead of /, but we rely on / working already, so there's no reason to
1178 // support a Mac-only path
1179 // Amiga and Windows: : tries to go to root of drive
1180 if (strstr(path, ":"))
1181 return 1; // non-portable attempt to go to root of drive
1183 // Amiga: // is parent directory
1184 if (strstr(path, "//"))
1185 return 1; // non-portable attempt to go to parent directory
1187 // all: don't allow going to current directory (./) or parent directory (../ or /../)
1188 if (strstr(path, "./"))
1189 return 2; // attempt to go outside the game directory
1191 // Windows and UNIXes: don't allow absolute paths
1193 return 2; // attempt to go outside the game directory
1195 // after all these checks we're pretty sure it's a / separated filename
1196 // and won't do much if any harm
1202 ====================
1205 Look for a file in the packages and in the filesystem
1207 Return the searchpath where the file was found (or NULL)
1208 and the file index in the package if relevant
1209 ====================
1211 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1213 searchpath_t *search;
1216 // search through the path, one element at a time
1217 for (search = fs_searchpaths;search;search = search->next)
1219 // is the element a pak file?
1222 int (*strcmp_funct) (const char* str1, const char* str2);
1223 int left, right, middle;
1226 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1228 // Look for the file (binary search)
1230 right = pak->numfiles - 1;
1231 while (left <= right)
1235 middle = (left + right) / 2;
1236 diff = strcmp_funct (pak->files[middle].name, name);
1242 Con_DPrintf("FS_FindFile: %s in %s\n",
1243 pak->files[middle].name, pak->filename);
1250 // If we're too far in the list
1259 char netpath[MAX_OSPATH];
1260 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1261 if (FS_SysFileExists (netpath))
1264 Con_DPrintf("FS_FindFile: %s\n", netpath);
1274 Con_DPrintf("FS_FindFile: can't find %s\n", name);
1286 Look for a file in the search paths and open it in read-only mode
1291 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet)
1293 searchpath_t *search;
1296 search = FS_FindFile (filename, &pack_ind, quiet);
1305 // Found in the filesystem?
1308 char path [MAX_OSPATH];
1309 snprintf (path, sizeof (path), "%s/%s", search->filename, filename);
1310 return FS_SysOpen (path, "rb");
1313 // So, we found it in a package...
1314 return FS_OpenPackedFile (search->pack, pack_ind);
1319 =============================================================================
1321 MAIN PUBLIC FUNCTIONS
1323 =============================================================================
1327 ====================
1330 Open a file. The syntax is the same as fopen
1331 ====================
1333 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1337 if (FS_CheckNastyPath(filepath))
1339 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1343 // If the file is opened in "write", "append", or "read/write" mode
1344 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1346 char real_path [MAX_OSPATH];
1348 // Open the file on disk directly
1349 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1351 // Create directories up to the file
1352 FS_CreatePath (real_path);
1354 return FS_SysOpen (real_path, mode);
1357 // Else, we look at the various search paths and open the file in read-only mode
1358 file = FS_OpenReadFile (filepath, quiet);
1360 fs_filesize = file->real_length;
1367 ====================
1371 ====================
1373 int FS_Close (qfile_t* file)
1375 if (close (file->handle))
1380 qz_inflateEnd (&file->ztk->zstream);
1381 Mem_Free (file->ztk);
1390 ====================
1393 Write "datasize" bytes into a file
1394 ====================
1396 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1398 ssize_t result = write (file->handle, data, datasize);
1407 ====================
1410 Read up to "buffersize" bytes from a file
1411 ====================
1413 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1417 // First, we copy as many bytes as we can from "buff"
1418 if (file->buff_ind < file->buff_len)
1420 count = file->buff_len - file->buff_ind;
1422 done = (buffersize > count) ? count : buffersize;
1423 memcpy (buffer, &file->buff[file->buff_ind], done);
1424 file->buff_ind += done;
1427 if (buffersize == 0)
1433 // NOTE: at this point, the read buffer is always empty
1435 // If the file isn't compressed
1436 if (! (file->flags & QFILE_FLAG_DEFLATED))
1440 // We must take care to not read after the end of the file
1441 count = file->real_length - file->position;
1443 // If we have a lot of data to get, put them directly into "buffer"
1444 if (buffersize > sizeof (file->buff) / 2)
1446 if (count > buffersize)
1448 lseek (file->handle, file->offset + file->position, SEEK_SET);
1449 nb = read (file->handle, &((qbyte*)buffer)[done], count);
1453 file->position += nb;
1455 // Invalidate the output data (for FS_Seek)
1462 if (count > sizeof (file->buff))
1463 count = sizeof (file->buff);
1464 lseek (file->handle, file->offset + file->position, SEEK_SET);
1465 nb = read (file->handle, file->buff, count);
1468 file->buff_len = nb;
1469 file->position += nb;
1471 // Copy the requested data in "buffer" (as much as we can)
1472 count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1473 memcpy (&((qbyte*)buffer)[done], file->buff, count);
1474 file->buff_ind = count;
1482 // If the file is compressed, it's more complicated...
1483 // We cycle through a few operations until we have read enough data
1484 while (buffersize > 0)
1486 ztoolkit_t *ztk = file->ztk;
1489 // NOTE: at this point, the read buffer is always empty
1491 // If "input" is also empty, we need to refill it
1492 if (ztk->in_ind == ztk->in_len)
1494 // If we are at the end of the file
1495 if (file->position == file->real_length)
1498 count = ztk->comp_length - ztk->in_position;
1499 if (count > sizeof (ztk->input))
1500 count = sizeof (ztk->input);
1501 lseek (file->handle, file->offset + ztk->in_position, SEEK_SET);
1502 if (read (file->handle, ztk->input, count) != (ssize_t)count)
1503 Sys_Error ("FS_Read: unexpected end of file");
1506 ztk->in_len = count;
1507 ztk->in_position += count;
1510 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1511 ztk->zstream.avail_in = ztk->in_len - ztk->in_ind;
1513 // Now that we are sure we have compressed data available, we need to determine
1514 // if it's better to inflate it in "file->buff" or directly in "buffer"
1516 // Inflate the data in "file->buff"
1517 if (buffersize < sizeof (file->buff) / 2)
1519 ztk->zstream.next_out = file->buff;
1520 ztk->zstream.avail_out = sizeof (file->buff);
1521 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1522 if (error != Z_OK && error != Z_STREAM_END)
1523 Sys_Error ("Can't inflate file");
1524 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1526 file->buff_len = sizeof (file->buff) - ztk->zstream.avail_out;
1527 file->position += file->buff_len;
1529 // Copy the requested data in "buffer" (as much as we can)
1530 count = (buffersize > file->buff_len) ? file->buff_len : buffersize;
1531 memcpy (&((qbyte*)buffer)[done], file->buff, count);
1532 file->buff_ind = count;
1535 // Else, we inflate directly in "buffer"
1538 ztk->zstream.next_out = &((qbyte*)buffer)[done];
1539 ztk->zstream.avail_out = buffersize;
1540 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1541 if (error != Z_OK && error != Z_STREAM_END)
1542 Sys_Error ("Can't inflate file");
1543 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1545 // How much data did it inflate?
1546 count = buffersize - ztk->zstream.avail_out;
1547 file->position += count;
1549 // Invalidate the output data (for FS_Seek)
1555 buffersize -= count;
1563 ====================
1566 Print a string into a file
1567 ====================
1569 int FS_Print (qfile_t* file, const char *msg)
1571 return FS_Write (file, msg, strlen (msg));
1575 ====================
1578 Print a string into a file
1579 ====================
1581 int FS_Printf(qfile_t* file, const char* format, ...)
1586 va_start (args, format);
1587 result = FS_VPrintf (file, format, args);
1595 ====================
1598 Print a string into a file
1599 ====================
1601 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
1604 char tempstring [1024];
1606 len = vsnprintf (tempstring, sizeof(tempstring), format, ap);
1607 if (len >= sizeof (tempstring))
1609 char *temp = Mem_Alloc (tempmempool, len + 1);
1610 len = vsnprintf (temp, len + 1, format, ap);
1611 len = write (file->handle, temp, len);
1616 return write (file->handle, tempstring, len);
1621 ====================
1624 Get the next character of a file
1625 ====================
1627 int FS_Getc (qfile_t* file)
1631 if (FS_Read (file, &c, 1) != 1)
1639 ====================
1642 Move the position index in a file
1643 ====================
1645 int FS_Seek (qfile_t* file, long offset, int whence)
1651 // Compute the file offset
1655 offset += file->position - file->buff_len + file->buff_ind;
1662 offset += file->real_length;
1668 if (offset < 0 || offset > (long) file->real_length)
1671 // If we have the data in our read buffer, we don't need to actually seek
1672 if (file->position - file->buff_len <= (size_t)offset &&
1673 (size_t)offset <= file->position)
1675 file->buff_ind = offset + file->buff_len - file->position;
1679 // Invalidate the read buffer contents
1683 // Unpacked or uncompressed files can seek directly
1684 if (! (file->flags & QFILE_FLAG_DEFLATED))
1686 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
1688 file->position = offset;
1692 // Seeking in compressed files is more a hack than anything else,
1693 // but we need to support it, so here we go.
1696 // If we have to go back in the file, we need to restart from the beginning
1697 if ((size_t)offset <= file->position)
1701 ztk->in_position = 0;
1703 lseek (file->handle, file->offset, SEEK_SET);
1705 // Reset the Zlib stream
1706 ztk->zstream.next_in = ztk->input;
1707 ztk->zstream.avail_in = 0;
1708 qz_inflateReset (&ztk->zstream);
1711 // We need a big buffer to force inflating into it directly
1712 buffersize = 2 * sizeof (file->buff);
1713 buffer = Mem_Alloc (tempmempool, buffersize);
1715 // Skip all data until we reach the requested offset
1716 while ((size_t)offset > file->position)
1718 size_t diff = offset - file->position;
1721 count = (diff > buffersize) ? buffersize : diff;
1722 len = FS_Read (file, buffer, count);
1736 ====================
1739 Give the current position in a file
1740 ====================
1742 long FS_Tell (qfile_t* file)
1744 return file->position - file->buff_len + file->buff_ind;
1749 ====================
1752 Extract a line from a file
1753 ====================
1755 char* FS_Gets (qfile_t* file, char* buffer, size_t buffersize)
1759 for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1761 int c = FS_Getc (file);
1776 buffer[ind + 1] = '\0';
1785 buffer[buffersize - 1] = '\0';
1794 Dynamic length version of fgets. DO NOT free the buffer.
1797 char *FS_Getline (qfile_t *file)
1799 static int size = 256;
1800 static char *buf = 0;
1805 buf = Mem_Alloc (fs_mempool, size);
1807 if (!FS_Gets (file, buf, size))
1811 while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1813 t = Mem_Alloc (fs_mempool, size + 256);
1814 memcpy(t, buf, size);
1818 if (!FS_Gets (file, buf + len, size - len))
1822 while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1832 Filename are relative to the quake directory.
1833 Always appends a 0 byte.
1836 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1841 file = FS_Open (path, "rb", quiet);
1845 buf = Mem_Alloc (pool, fs_filesize + 1);
1846 buf[fs_filesize] = '\0';
1848 FS_Read (file, buf, fs_filesize);
1859 The filename will be prefixed by the current game directory
1862 qboolean FS_WriteFile (const char *filename, void *data, int len)
1866 file = FS_Open (filename, "wb", false);
1869 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1873 Con_DPrintf("FS_WriteFile: %s\n", filename);
1874 FS_Write (file, data, len);
1881 =============================================================================
1883 OTHERS PUBLIC FUNCTIONS
1885 =============================================================================
1893 void FS_StripExtension (const char *in, char *out, size_t size_out)
1900 while (*in && size_out > 1)
1904 else if (*in == '/' || *in == '\\' || *in == ':')
1921 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1925 // if path doesn't have a .EXT, append extension
1926 // (extension should include the .)
1927 src = path + strlen(path) - 1;
1929 while (*src != '/' && src != path)
1932 return; // it has an extension
1936 strlcat (path, extension, size_path);
1944 Look for a file in the packages and in the filesystem
1947 qboolean FS_FileExists (const char *filename)
1949 return (FS_FindFile (filename, NULL, true) != NULL);
1957 Look for a file in the filesystem only
1960 qboolean FS_SysFileExists (const char *path)
1965 // TODO: use another function instead, to avoid opening the file
1966 desc = open (path, O_RDONLY | O_BINARY);
1975 if (stat (path,&buf) == -1)
1982 void FS_mkdir (const char *path)
1995 Allocate and fill a search structure with information on matching filenames.
1998 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2001 searchpath_t *searchpath;
2003 int i, basepathlength, numfiles, numchars;
2004 stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
2005 const char *slash, *backslash, *colon, *separator;
2007 char netpath[MAX_OSPATH];
2008 char temp[MAX_OSPATH];
2010 while(!strncmp(pattern, "./", 2))
2012 while(!strncmp(pattern, ".\\", 2))
2019 slash = strrchr(pattern, '/');
2020 backslash = strrchr(pattern, '\\');
2021 colon = strrchr(pattern, ':');
2022 separator = pattern;
2023 if (separator < slash)
2025 if (separator < backslash)
2026 separator = backslash;
2027 if (separator < colon)
2029 basepathlength = separator - pattern;
2030 basepath = Mem_Alloc (tempmempool, basepathlength + 1);
2032 memcpy(basepath, pattern, basepathlength);
2033 basepath[basepathlength] = 0;
2035 // search through the path, one element at a time
2036 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2038 // is the element a pak file?
2039 if (searchpath->pack)
2041 // look through all the pak file elements
2042 pak = searchpath->pack;
2043 for (i = 0;i < pak->numfiles;i++)
2045 strcpy(temp, pak->files[i].name);
2048 if (matchpattern(temp, (char *)pattern, true))
2050 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2051 if (!strcmp(listtemp->text, temp))
2053 if (listtemp == NULL)
2055 listcurrent = stringlistappend(listcurrent, temp);
2056 if (liststart == NULL)
2057 liststart = listcurrent;
2059 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2062 // strip off one path element at a time until empty
2063 // this way directories are added to the listing if they match the pattern
2064 slash = strrchr(temp, '/');
2065 backslash = strrchr(temp, '\\');
2066 colon = strrchr(temp, ':');
2068 if (separator < slash)
2070 if (separator < backslash)
2071 separator = backslash;
2072 if (separator < colon)
2074 *((char *)separator) = 0;
2080 // get a directory listing and look at each name
2081 snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
2082 if ((dir = listdirectory(netpath)))
2084 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2086 snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
2087 if (matchpattern(temp, (char *)pattern, true))
2089 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2090 if (!strcmp(listtemp->text, temp))
2092 if (listtemp == NULL)
2094 listcurrent = stringlistappend(listcurrent, temp);
2095 if (liststart == NULL)
2096 liststart = listcurrent;
2098 Con_DPrintf("SearchDirFile: %s\n", temp);
2109 liststart = stringlistsort(liststart);
2112 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2115 numchars += strlen(listtemp->text) + 1;
2117 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2118 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2119 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2120 search->numfilenames = numfiles;
2123 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2125 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2126 strcpy(search->filenames[numfiles], listtemp->text);
2128 numchars += strlen(listtemp->text) + 1;
2131 stringlistfree(liststart);
2138 void FS_FreeSearch(fssearch_t *search)
2143 extern int con_linewidth;
2144 int FS_ListDirectory(const char *pattern, int oneperline)
2155 search = FS_Search(pattern, true, true);
2158 numfiles = search->numfilenames;
2161 // FIXME: the names could be added to one column list and then
2162 // gradually shifted into the next column if they fit, and then the
2163 // next to make a compact variable width listing but it's a lot more
2165 // find width for columns
2167 for (i = 0;i < numfiles;i++)
2169 l = strlen(search->filenames[i]);
2170 if (columnwidth < l)
2173 // count the spacing character
2175 // calculate number of columns
2176 numcolumns = con_linewidth / columnwidth;
2177 // don't bother with the column printing if it's only one column
2178 if (numcolumns >= 2)
2180 numlines = (numfiles + numcolumns - 1) / numcolumns;
2181 for (i = 0;i < numlines;i++)
2184 for (k = 0;k < numcolumns;k++)
2186 l = i * numcolumns + k;
2189 name = search->filenames[l];
2190 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2191 linebuf[linebufpos++] = name[j];
2192 // space out name unless it's the last on the line
2193 if (k < (numcolumns - 1) && l < (numfiles - 1))
2194 for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2195 linebuf[linebufpos++] = ' ';
2198 linebuf[linebufpos] = 0;
2199 Con_Printf("%s\n", linebuf);
2206 for (i = 0;i < numfiles;i++)
2207 Con_Printf("%s\n", search->filenames[i]);
2208 FS_FreeSearch(search);
2212 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2214 const char *pattern;
2217 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2220 if (Cmd_Argc() == 2)
2221 pattern = Cmd_Argv(1);
2224 if (!FS_ListDirectory(pattern, oneperline))
2225 Con_Print("No files found.\n");
2230 FS_ListDirectoryCmd("dir", true);
2235 FS_ListDirectoryCmd("ls", false);