4 Copyright (C) 2003 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>
49 All of Quake's data access is through a hierchal file system, but the contents
50 of the file system can be transparently merged from several sources.
52 The "base directory" is the path to the directory holding the quake.exe and
53 all game directories. The sys_* files pass this to host_init in
54 quakeparms_t->basedir. This can be overridden with the "-basedir" command
55 line parm to allow code debugging in a different directory. The base
56 directory is only used during filesystem initialization.
58 The "game directory" is the first tree on the search path and directory that
59 all generated files (savegames, screenshots, demos, config files) will be
60 saved to. This can be overridden with the "-game" command line parameter.
61 The game directory can never be changed while quake is executing. This is a
62 precacution against having a malicious server instruct clients to write files
63 over areas they shouldn't.
69 =============================================================================
73 =============================================================================
76 // Magic numbers of a ZIP file (big-endian format)
77 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
78 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
79 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
81 // Other constants for ZIP files
82 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
83 #define ZIP_END_CDIR_SIZE 22
84 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
85 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
87 // Zlib constants (from zlib.h)
88 #define Z_SYNC_FLUSH 2
91 #define Z_STREAM_END 1
92 #define ZLIB_VERSION "1.1.4"
96 =============================================================================
100 =============================================================================
103 // Zlib stream (from zlib.h)
104 // Warning: some pointers we don't use directly have
105 // been cast to "void*" for a matter of simplicity
108 qbyte *next_in; // next input byte
109 unsigned int avail_in; // number of bytes available at next_in
110 unsigned long total_in; // total nb of input bytes read so far
112 qbyte *next_out; // next output byte should be put there
113 unsigned int avail_out; // remaining free space at next_out
114 unsigned long total_out; // total nb of bytes output so far
116 char *msg; // last error message, NULL if no error
117 void *state; // not visible by applications
119 void *zalloc; // used to allocate the internal state
120 void *zfree; // used to free the internal state
121 void *opaque; // private data object passed to zalloc and zfree
123 int data_type; // best guess about the data type: ascii or binary
124 unsigned long adler; // adler32 value of the uncompressed data
125 unsigned long reserved; // reserved for future use
129 // Our own file structure on top of FILE
133 FS_FLAG_PACKED = (1 << 0), // inside a package (PAK or PK3)
134 FS_FLAG_DEFLATED = (1 << 1) // file is compressed using the deflate algorithm (PK3 only)
137 #define ZBUFF_SIZE 1024
141 size_t real_length; // length of the uncompressed file
142 size_t in_ind, in_max; // input buffer index and counter
143 size_t in_position; // position in the compressed file
144 size_t out_ind, out_max; // output buffer index and counter
145 size_t out_position; // how many bytes did we uncompress until now?
146 qbyte input [ZBUFF_SIZE];
147 qbyte output [ZBUFF_SIZE];
154 size_t length; // file size on disk (PACKED only)
155 size_t offset; // offset into a package (PACKED only)
156 size_t position; // current position in the file (PACKED only)
157 ztoolkit_t* z; // used for inflating (DEFLATED only)
161 // ------ PK3 files on disk ------ //
163 // You can get the complete ZIP format description from PKWARE website
167 unsigned int signature;
168 unsigned short disknum;
169 unsigned short cdir_disknum; // number of the disk with the start of the central directory
170 unsigned short localentries; // number of entries in the central directory on this disk
171 unsigned short nbentries; // total number of entries in the central directory on this disk
172 unsigned int cdir_size; // size of the central directory
173 unsigned int cdir_offset; // with respect to the starting disk number
174 unsigned short comment_size;
175 } pk3_endOfCentralDir_t;
178 // ------ PAK files on disk ------ //
182 int filepos, filelen;
193 // Packages in memory
197 FILE_FLAG_TRUEOFFS = (1 << 0), // the offset in packfile_t is the true contents offset
198 FILE_FLAG_DEFLATED = (1 << 1) // file compressed using the deflate algorithm
203 char name [MAX_QPATH];
206 size_t packsize; // size in the package
207 size_t realsize; // real file size (uncompressed)
210 typedef struct pack_s
212 char filename [MAX_OSPATH];
214 int ignorecase; // PK3 ignores case
222 // Search paths for files (including packages)
223 typedef struct searchpath_s
225 // only one of filename / pack will be used
226 char filename[MAX_OSPATH];
228 struct searchpath_s *next;
233 =============================================================================
237 =============================================================================
243 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
244 size_t offset, size_t packsize,
245 size_t realsize, file_flags_t flags);
249 =============================================================================
253 =============================================================================
256 mempool_t *fs_mempool;
257 mempool_t *pak_mempool;
261 pack_t *packlist = NULL;
263 searchpath_t *fs_searchpaths = NULL;
265 #define MAX_FILES_IN_PACK 65536
267 char fs_gamedir[MAX_OSPATH];
268 char fs_basedir[MAX_OSPATH];
270 qboolean fs_modified; // set true if using non-id files
274 =============================================================================
276 PRIVATE FUNCTIONS - PK3 HANDLING
278 =============================================================================
281 // Functions exported from zlib
283 # define ZEXPORT WINAPI
288 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
289 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
290 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
291 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
293 #define qz_inflateInit2(strm, windowBits) \
294 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
296 static dllfunction_t zlibfuncs[] =
298 {"inflate", (void **) &qz_inflate},
299 {"inflateEnd", (void **) &qz_inflateEnd},
300 {"inflateInit2_", (void **) &qz_inflateInit2_},
301 {"inflateReset", (void **) &qz_inflateReset},
305 // Handle for Zlib DLL
306 static dllhandle_t zlib_dll = NULL;
316 void PK3_CloseLibrary (void)
318 Sys_UnloadLibrary (&zlib_dll);
326 Try to load the Zlib DLL
329 qboolean PK3_OpenLibrary (void)
338 dllname = "zlib.dll";
344 if (! Sys_LoadLibrary (dllname, &zlib_dll, zlibfuncs))
346 Con_Printf ("Compressed files support disabled\n");
350 Con_Printf ("Compressed files support enabled\n");
357 PK3_GetEndOfCentralDir
359 Extract the end of the central directory from a PK3 package
362 qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd)
364 long filesize, maxsize;
368 // Get the package size
369 fseek (packhandle, 0, SEEK_END);
370 filesize = ftell (packhandle);
371 if (filesize < ZIP_END_CDIR_SIZE)
374 // Load the end of the file in memory
375 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
378 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
379 buffer = Mem_Alloc (tempmempool, maxsize);
380 fseek (packhandle, filesize - maxsize, SEEK_SET);
381 if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) maxsize)
387 // Look for the end of central dir signature around the end of the file
388 maxsize -= ZIP_END_CDIR_SIZE;
389 ptr = &buffer[maxsize];
391 while (BuffBigLong (ptr) != ZIP_END_HEADER)
403 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
404 eocd->signature = LittleLong (eocd->signature);
405 eocd->disknum = LittleShort (eocd->disknum);
406 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
407 eocd->localentries = LittleShort (eocd->localentries);
408 eocd->nbentries = LittleShort (eocd->nbentries);
409 eocd->cdir_size = LittleLong (eocd->cdir_size);
410 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
411 eocd->comment_size = LittleShort (eocd->comment_size);
423 Extract the file list from a PK3 file
426 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
428 qbyte *central_dir, *ptr;
432 // Load the central directory in memory
433 central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
434 fseek (pack->handle, eocd->cdir_offset, SEEK_SET);
435 fread (central_dir, 1, eocd->cdir_size, pack->handle);
437 // Extract the files properties
438 // The parsing is done "by hand" because some fields have variable sizes and
439 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
440 remaining = eocd->cdir_size;
443 for (ind = 0; ind < eocd->nbentries; ind++)
445 size_t namesize, count;
447 // Checking the remaining size
448 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
450 Mem_Free (central_dir);
453 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
456 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
458 Mem_Free (central_dir);
462 namesize = BuffLittleShort (&ptr[28]); // filename length
464 // Check encryption, compression, and attributes
465 // 1st uint8 : general purpose bit flag
466 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
467 // 2nd uint8 : external file attributes
468 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
469 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
471 // Still enough bytes for the name?
472 if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
474 Mem_Free (central_dir);
478 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
479 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
481 char filename [sizeof (pack->files[0].name)];
482 size_t offset, packsize, realsize;
485 // Extract the name (strip it if necessary)
486 if (namesize >= sizeof (filename))
487 namesize = sizeof (filename) - 1;
488 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
489 filename[namesize] = '\0';
491 if (BuffLittleShort (&ptr[10]))
492 flags = FILE_FLAG_DEFLATED;
495 offset = BuffLittleLong (&ptr[42]);
496 packsize = BuffLittleLong (&ptr[20]);
497 realsize = BuffLittleLong (&ptr[24]);
498 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
502 // Skip the name, additionnal field, and comment
503 // 1er uint16 : extra field length
504 // 2eme uint16 : file comment length
505 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
506 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
510 Mem_Free (central_dir);
511 return pack->numfiles;
519 Create a package entry associated with a PK3 file
522 pack_t *FS_LoadPackPK3 (const char *packfile)
525 pk3_endOfCentralDir_t eocd;
529 packhandle = fopen (packfile, "rb");
533 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
534 Sys_Error ("%s is not a PK3 file", packfile);
536 // Multi-volume ZIP archives are NOT allowed
537 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
538 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
540 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
541 // since eocd.nbentries is an unsigned 16 bits integer
542 #if MAX_FILES_IN_PACK < 65535
543 if (eocd.nbentries > MAX_FILES_IN_PACK)
544 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
547 // Create a package structure in memory
548 pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
549 pack->ignorecase = true; // PK3 ignores case
550 strlcpy (pack->filename, packfile, sizeof (pack->filename));
551 pack->handle = packhandle;
552 pack->numfiles = eocd.nbentries;
553 pack->mempool = Mem_AllocPool (packfile, 0, NULL);
554 pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
555 pack->next = packlist;
558 real_nb_files = PK3_BuildFileList (pack, &eocd);
559 if (real_nb_files <= 0)
560 Sys_Error ("%s is not a valid PK3 file", packfile);
562 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
569 PK3_GetTrueFileOffset
571 Find where the true file data offset is
574 void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
576 qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
580 if (file->flags & FILE_FLAG_TRUEOFFS)
583 // Load the local file description
584 fseek (pack->handle, file->offset, SEEK_SET);
585 count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
586 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
587 Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
589 // Skip name and extra field
590 file->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
592 file->flags |= FILE_FLAG_TRUEOFFS;
597 =============================================================================
599 OTHER PRIVATE FUNCTIONS
601 =============================================================================
609 Add a file to the list of files contained into a package
612 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
613 size_t offset, size_t packsize,
614 size_t realsize, file_flags_t flags)
616 int (*strcmp_funct) (const char* str1, const char* str2);
617 size_t left, right, middle;
621 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
623 // Look for the slot we should put that file into (binary search)
625 right = pack->numfiles;
626 while (left != right)
628 middle = (left + right - 1) / 2;
629 diff = strcmp_funct (pack->files[middle].name, name);
631 // If we found the file, there's a problem
633 Sys_Error ("Package %s contains several time the file %s\n",
634 pack->filename, name);
636 // If we're too far in the list
643 // We have to move the right of the list by one slot to free the one we need
644 file = &pack->files[left];
645 memmove (file + 1, file, (pack->numfiles - left) * sizeof (*file));
648 strlcpy (file->name, name, sizeof (file->name));
649 file->offset = offset;
650 file->packsize = packsize;
651 file->realsize = realsize;
662 Only used for FS_Open.
665 void FS_CreatePath (char *path)
669 for (ofs = path+1 ; *ofs ; ofs++)
671 if (*ofs == '/' || *ofs == '\\')
673 // create the directory
689 void FS_Path_f (void)
693 Con_Print("Current search path:\n");
694 for (s=fs_searchpaths ; s ; s=s->next)
698 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
701 Con_Printf("%s\n", s->filename);
710 Takes an explicit (not game tree related) path to a pak file.
712 Loads the header and directory, adding the files at the beginning
713 of the list so they override previous pack files.
716 pack_t *FS_LoadPackPAK (const char *packfile)
718 dpackheader_t header;
722 dpackfile_t *info; // temporary alloc, allowing huge pack directories
724 packhandle = fopen (packfile, "rb");
728 fread ((void *)&header, 1, sizeof(header), packhandle);
729 if (memcmp(header.id, "PACK", 4))
730 Sys_Error ("%s is not a packfile", packfile);
731 header.dirofs = LittleLong (header.dirofs);
732 header.dirlen = LittleLong (header.dirlen);
734 if (header.dirlen % sizeof(dpackfile_t))
735 Sys_Error ("%s has an invalid directory size", packfile);
737 numpackfiles = header.dirlen / sizeof(dpackfile_t);
739 if (numpackfiles > MAX_FILES_IN_PACK)
740 Sys_Error ("%s has %i files", packfile, numpackfiles);
742 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
743 pack->ignorecase = false; // PAK is case sensitive
744 strlcpy (pack->filename, packfile, sizeof (pack->filename));
745 pack->handle = packhandle;
747 pack->mempool = Mem_AllocPool(packfile, 0, NULL);
748 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
749 pack->next = packlist;
752 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
753 fseek (packhandle, header.dirofs, SEEK_SET);
754 fread ((void *)info, 1, header.dirlen, packhandle);
756 // parse the directory
757 for (i = 0;i < numpackfiles;i++)
759 size_t offset = LittleLong (info[i].filepos);
760 size_t size = LittleLong (info[i].filelen);
762 FS_AddFileToPack (info[i].name, pack, offset, size, size, FILE_FLAG_TRUEOFFS);
767 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
776 Sets fs_gamedir, adds the directory to the head of the path,
777 then loads and adds pak1.pak pak2.pak ...
780 void FS_AddGameDirectory (char *dir)
782 stringlist_t *list, *current;
783 searchpath_t *search;
785 char pakfile[MAX_OSPATH];
787 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
790 // add the directory to the search path
791 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
792 strlcpy (search->filename, dir, sizeof (search->filename));
793 search->next = fs_searchpaths;
794 fs_searchpaths = search;
797 list = listdirectory(dir);
799 // add any PAK package in the directory
800 for (current = list;current;current = current->next)
802 if (matchpattern(current->text, "*.pak", true))
804 snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
805 pak = FS_LoadPackPAK (pakfile);
808 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
810 search->next = fs_searchpaths;
811 fs_searchpaths = search;
814 Con_Printf("unable to load pak \"%s\"\n", pakfile);
818 // add any PK3 package in the director
819 for (current = list;current;current = current->next)
821 if (matchpattern(current->text, "*.pk3", true))
823 snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
824 pak = FS_LoadPackPK3 (pakfile);
827 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
829 search->next = fs_searchpaths;
830 fs_searchpaths = search;
833 Con_Printf("unable to load pak \"%s\"\n", pakfile);
838 // Unpacked files have the priority over packed files if AKVERSION is defined
840 // add the directory to the search path
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;
854 char *FS_FileExtension (const char *in)
856 static char exten[8];
857 const char *slash, *backslash, *colon, *dot, *separator;
860 slash = strrchr(in, '/');
861 backslash = strrchr(in, '\\');
862 colon = strrchr(in, ':');
863 dot = strrchr(in, '.');
865 if (separator < backslash)
866 separator = backslash;
867 if (separator < colon)
869 if (dot == NULL || dot < separator)
872 for (i = 0;i < 7 && dot[i];i++)
887 searchpath_t *search;
889 fs_mempool = Mem_AllocPool("file management", 0, NULL);
890 pak_mempool = Mem_AllocPool("paks", 0, NULL);
892 Cvar_RegisterVariable (&scr_screenshot_name);
894 Cmd_AddCommand ("path", FS_Path_f);
895 Cmd_AddCommand ("dir", FS_Dir_f);
896 Cmd_AddCommand ("ls", FS_Ls_f);
898 strcpy(fs_basedir, ".");
899 strcpy(fs_gamedir, ".");
904 // Overrides the system supplied base directory (under GAMENAME)
905 // 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)
906 i = COM_CheckParm ("-basedir");
907 if (i && i < com_argc-1)
909 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
910 i = strlen (fs_basedir);
911 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
915 // -path <dir or packfile> [<dir or packfile>] ...
916 // Fully specifies the exact search path, overriding the generated one
917 // 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)
918 i = COM_CheckParm ("-path");
922 while (++i < com_argc)
924 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
927 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
928 if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
930 search->pack = FS_LoadPackPAK (com_argv[i]);
932 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
934 else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
936 search->pack = FS_LoadPackPK3 (com_argv[i]);
938 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
941 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
942 search->next = fs_searchpaths;
943 fs_searchpaths = search;
948 // start up with GAMENAME by default (id1)
949 strlcpy (com_modname, GAMENAME, sizeof (com_modname));
950 FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
951 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
953 // add the game-specific path, if any
957 strlcpy (com_modname, gamedirname, sizeof (com_modname));
958 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
962 // Adds basedir/gamedir as an override game
963 // LordHavoc: now supports multiple -game directories
964 for (i = 1;i < com_argc;i++)
968 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
972 strlcpy (com_modname, com_argv[i], sizeof (com_modname));
973 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i]));
974 Cvar_SetQuick (&scr_screenshot_name, com_modname);
984 Internal function used to create a qfile_t and open the relevant file on disk
987 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
991 file = Mem_Alloc (fs_mempool, sizeof (*file));
992 memset (file, 0, sizeof (*file));
994 file->stream = fopen (filepath, mode);
1010 qfile_t *FS_OpenRead (const char *path, int offs, int len)
1014 file = FS_SysOpen (path, "rb");
1017 Sys_Error ("Couldn't open %s", path);
1022 if (offs < 0 || len < 0)
1024 // We set fs_filesize here for normal files
1025 fseek (file->stream, 0, SEEK_END);
1026 fs_filesize = ftell (file->stream);
1027 fseek (file->stream, 0, SEEK_SET);
1032 fseek (file->stream, offs, SEEK_SET);
1034 file->flags |= FS_FLAG_PACKED;
1036 file->offset = offs;
1045 ====================
1048 Look for a file in the packages and in the filesystem
1050 Return the searchpath where the file was found (or NULL)
1051 and the file index in the package if relevant
1052 ====================
1054 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1056 searchpath_t *search;
1058 int (*strcmp_funct) (const char* str1, const char* str2);
1060 // search through the path, one element at a time
1061 for (search = fs_searchpaths;search;search = search->next)
1063 // is the element a pak file?
1066 size_t left, right, middle;
1069 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1071 // Look for the file (binary search)
1073 right = pak->numfiles;
1074 while (left != right)
1078 middle = (left + right - 1) / 2;
1079 diff = strcmp_funct (pak->files[middle].name, name);
1085 Sys_Printf("FS_FindFile: %s in %s\n",
1086 pak->files[middle].name, pak->filename);
1093 // If we're too far in the list
1102 char netpath[MAX_OSPATH];
1103 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1104 if (FS_SysFileExists (netpath))
1107 Sys_Printf("FS_FindFile: %s\n", netpath);
1117 Sys_Printf("FS_FindFile: can't find %s\n", name);
1129 If the requested file is inside a packfile, a new qfile_t* will be opened
1135 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
1137 searchpath_t *search;
1138 packfile_t *packfile;
1142 search = FS_FindFile (filename, &i, quiet);
1151 // Found in the filesystem?
1154 char netpath[MAX_OSPATH];
1155 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
1156 return FS_OpenRead(netpath, -1, -1);
1159 // So, we found it in a package...
1160 packfile = &search->pack->files[i];
1162 // If we don't have the true offset, get it now
1163 if (! (packfile->flags & FILE_FLAG_TRUEOFFS))
1164 PK3_GetTrueFileOffset (packfile, search->pack);
1166 // No Zlib DLL = no compressed files
1167 if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED))
1169 Con_Printf("WARNING: can't open the compressed file %s\n"
1170 "You need the Zlib DLL to use compressed files\n",
1176 // open a new file in the pakfile
1177 file = FS_OpenRead (search->pack->filename, packfile->offset, packfile->packsize);
1178 fs_filesize = packfile->realsize;
1180 if (packfile->flags & FILE_FLAG_DEFLATED)
1184 file->flags |= FS_FLAG_DEFLATED;
1186 // We need some more variables
1187 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1189 ztk->real_length = packfile->realsize;
1191 // Initialize zlib stream
1192 ztk->zstream.next_in = ztk->input;
1193 ztk->zstream.avail_in = 0;
1195 /* From Zlib's "unzip.c":
1197 * windowBits is passed < 0 to tell that there is no zlib header.
1198 * Note that in this case inflate *requires* an extra "dummy" byte
1199 * after the compressed stream in order to complete decompression and
1200 * return Z_STREAM_END.
1201 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1202 * size of both compressed and uncompressed data
1204 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1205 Sys_Error ("inflate init error (file: %s)", filename);
1207 ztk->zstream.next_out = ztk->output;
1208 ztk->zstream.avail_out = sizeof (ztk->output);
1218 =============================================================================
1220 MAIN PUBLIC FUNCTIONS
1222 =============================================================================
1226 ====================
1229 Open a file. The syntax is the same as fopen
1230 ====================
1232 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1234 // If the file is opened in "write" or "append" mode
1235 if (strchr (mode, 'w') || strchr (mode, 'a'))
1237 char real_path [MAX_OSPATH];
1239 // Open the file on disk directly
1240 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1242 // Create directories up to the file
1243 FS_CreatePath (real_path);
1245 return FS_SysOpen (real_path, mode);
1248 // Else, we look at the various search paths
1249 return FS_FOpenFile (filepath, quiet);
1254 ====================
1258 ====================
1260 int FS_Close (qfile_t* file)
1262 if (fclose (file->stream))
1267 qz_inflateEnd (&file->z->zstream);
1277 ====================
1280 Write "datasize" bytes into a file
1281 ====================
1283 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1285 return fwrite (data, 1, datasize, file->stream);
1290 ====================
1293 Read up to "buffersize" bytes from a file
1294 ====================
1296 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1301 // Quick path for unpacked files
1302 if (! (file->flags & FS_FLAG_PACKED))
1303 return fread (buffer, 1, buffersize, file->stream);
1305 // If the file isn't compressed
1306 if (! (file->flags & FS_FLAG_DEFLATED))
1308 // We must take care to not read after the end of the file
1309 count = file->length - file->position;
1310 if (buffersize > count)
1313 nb = fread (buffer, 1, buffersize, file->stream);
1315 file->position += nb;
1319 // If the file is compressed, it's more complicated...
1322 // First, we copy as many bytes as we can from "output"
1323 if (ztk->out_ind < ztk->out_max)
1325 count = ztk->out_max - ztk->out_ind;
1327 nb = (buffersize > count) ? count : buffersize;
1328 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1330 file->position += nb;
1335 // We cycle through a few operations until we have inflated enough data
1336 while (nb < buffersize)
1338 // NOTE: at this point, "output" should always be empty
1340 // If "input" is also empty, we need to fill it
1341 if (ztk->in_ind == ztk->in_max)
1345 // If we are at the end of the file
1346 if (ztk->out_position == ztk->real_length)
1349 remain = file->length - ztk->in_position;
1350 count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1351 fread (ztk->input, 1, count, file->stream);
1353 // Update indexes and counters
1355 ztk->in_max = count;
1356 ztk->in_position += count;
1359 // Now that we are sure we have compressed data available, we need to determine
1360 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1361 // case if we still need more bytes than "output" can contain)
1363 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1364 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1366 // If output will be able to contain at least 1 more byte than the data we need
1367 if (buffersize - nb < sizeof (ztk->output))
1371 // Inflate the data in "output"
1372 ztk->zstream.next_out = ztk->output;
1373 ztk->zstream.avail_out = sizeof (ztk->output);
1374 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1375 if (error != Z_OK && error != Z_STREAM_END)
1376 Sys_Error ("Can't inflate file");
1377 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1378 ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1379 ztk->out_position += ztk->out_max;
1381 // Copy the requested data in "buffer" (as much as we can)
1382 count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1383 memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1384 ztk->out_ind = count;
1387 // Else, we inflate directly in "buffer"
1392 // Inflate the data in "buffer"
1393 ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1394 ztk->zstream.avail_out = buffersize - nb;
1395 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1396 if (error != Z_OK && error != Z_STREAM_END)
1397 Sys_Error ("Can't inflate file");
1398 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1400 // Invalidate the output data (for FS_Seek)
1404 // How much data did it inflate?
1405 count = buffersize - nb - ztk->zstream.avail_out;
1406 ztk->out_position += count;
1410 file->position += count;
1418 ====================
1421 Flush the file output stream
1422 ====================
1424 int FS_Flush (qfile_t* file)
1426 return fflush (file->stream);
1431 ====================
1434 Print a string into a file
1435 ====================
1437 int FS_Print(qfile_t* file, const char *msg)
1439 return FS_Write(file, msg, strlen(msg));
1443 ====================
1446 Print a string into a file
1447 ====================
1449 int FS_Printf(qfile_t* file, const char* format, ...)
1454 va_start (args, format);
1455 result = vfprintf (file->stream, format, args);
1463 ====================
1466 Print a string into a file
1467 ====================
1469 int FS_VPrintf(qfile_t* file, const char* format, va_list ap)
1471 return vfprintf (file->stream, format, ap);
1476 ====================
1479 Get the next character of a file
1480 ====================
1482 int FS_Getc (qfile_t* file)
1486 if (FS_Read (file, &c, 1) != 1)
1494 ====================
1497 Move the position index in a file
1498 ====================
1500 int FS_Seek (qfile_t* file, long offset, int whence)
1502 // Quick path for unpacked files
1503 if (! (file->flags & FS_FLAG_PACKED))
1504 return fseek (file->stream, offset, whence);
1506 // Seeking in compressed files is more a hack than anything else,
1507 // but we need to support it, so here it is.
1508 if (file->flags & FS_FLAG_DEFLATED)
1510 ztoolkit_t *ztk = file->z;
1511 qbyte buffer [sizeof (ztk->output)]; // it's big to force inflating into buffer directly
1516 offset += file->position;
1523 offset += ztk->real_length;
1529 if (offset < 0 || offset > (long) ztk->real_length)
1532 // If we need to go back in the file
1533 if (offset <= (long) file->position)
1535 // If we still have the data we need in the output buffer
1536 if (file->position - offset <= ztk->out_ind)
1538 ztk->out_ind -= file->position - offset;
1539 file->position = offset;
1543 // Else, we restart from the beginning of the file
1546 ztk->in_position = 0;
1549 ztk->out_position = 0;
1551 fseek (file->stream, file->offset, SEEK_SET);
1553 // Reset the Zlib stream
1554 ztk->zstream.next_in = ztk->input;
1555 ztk->zstream.avail_in = 0;
1556 qz_inflateReset (&ztk->zstream);
1559 // Skip all data until we reach the requested offset
1560 while ((long) file->position < offset)
1562 size_t diff = offset - file->position;
1565 count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1566 len = FS_Read (file, buffer, count);
1574 // Packed files receive a special treatment too, because
1575 // we need to make sure it doesn't go outside of the file
1579 offset += file->position;
1586 offset += file->length;
1592 if (offset < 0 || offset > (long) file->length)
1595 if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1597 file->position = offset;
1603 ====================
1606 Give the current position in a file
1607 ====================
1609 long FS_Tell (qfile_t* file)
1611 if (file->flags & FS_FLAG_PACKED)
1612 return file->position;
1614 return ftell (file->stream);
1619 ====================
1622 Extract a line from a file
1623 ====================
1625 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1629 // Quick path for unpacked files
1630 if (! (file->flags & FS_FLAG_PACKED))
1631 return fgets (buffer, buffersize, file->stream);
1633 for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1635 int c = FS_Getc (file);
1650 buffer[ind + 1] = '\0';
1659 buffer[buffersize - 1] = '\0';
1668 Dynamic length version of fgets. DO NOT free the buffer.
1671 char *FS_Getline (qfile_t *file)
1673 static int size = 256;
1674 static char *buf = 0;
1679 buf = Mem_Alloc (fs_mempool, size);
1681 if (!FS_Gets (file, buf, size))
1685 while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1687 t = Mem_Alloc (fs_mempool, size + 256);
1688 memcpy(t, buf, size);
1692 if (!FS_Gets (file, buf + len, size - len))
1696 while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1703 ====================
1706 Extract a line from a file
1707 ====================
1709 int FS_Eof (qfile_t* file)
1711 if (file->flags & FS_FLAG_PACKED)
1713 if (file->flags & FS_FLAG_DEFLATED)
1714 return (file->position == file->z->real_length);
1716 return (file->position == file->length);
1719 return feof (file->stream);
1727 Filename are relative to the quake directory.
1728 Always appends a 0 byte.
1731 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1736 // look for it in the filesystem or pack files
1737 h = FS_Open (path, "rb", quiet);
1741 buf = Mem_Alloc(pool, fs_filesize+1);
1743 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1745 ((qbyte *)buf)[fs_filesize] = 0;
1747 FS_Read (h, buf, fs_filesize);
1758 The filename will be prefixed by the current game directory
1761 qboolean FS_WriteFile (const char *filename, void *data, int len)
1765 handle = FS_Open (filename, "wb", false);
1768 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1772 Con_DPrintf("FS_WriteFile: %s\n", filename);
1773 FS_Write (handle, data, len);
1780 =============================================================================
1782 OTHERS PUBLIC FUNCTIONS
1784 =============================================================================
1792 void FS_StripExtension (const char *in, char *out, size_t size_out)
1799 while (*in && size_out > 1)
1803 else if (*in == '/' || *in == '\\' || *in == ':')
1820 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1824 // if path doesn't have a .EXT, append extension
1825 // (extension should include the .)
1826 src = path + strlen(path) - 1;
1828 while (*src != '/' && src != path)
1831 return; // it has an extension
1835 strlcat (path, extension, size_path);
1843 Look for a file in the packages and in the filesystem
1846 qboolean FS_FileExists (const char *filename)
1848 return (FS_FindFile (filename, NULL, true) != NULL);
1856 Look for a file in the filesystem only
1859 qboolean FS_SysFileExists (const char *path)
1864 f = fopen (path, "rb");
1875 if (stat (path,&buf) == -1)
1882 void FS_mkdir (const char *path)
1895 Allocate and fill a search structure with information on matching filenames.
1898 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
1901 searchpath_t *searchpath;
1903 int i, basepathlength, numfiles, numchars;
1904 stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
1905 const char *slash, *backslash, *colon, *separator;
1907 char netpath[MAX_OSPATH];
1908 char temp[MAX_OSPATH];
1910 while(!strncmp(pattern, "./", 2))
1912 while(!strncmp(pattern, ".\\", 2))
1919 slash = strrchr(pattern, '/');
1920 backslash = strrchr(pattern, '\\');
1921 colon = strrchr(pattern, ':');
1922 separator = pattern;
1923 if (separator < slash)
1925 if (separator < backslash)
1926 separator = backslash;
1927 if (separator < colon)
1929 basepathlength = separator - pattern;
1930 basepath = Mem_Alloc (tempmempool, basepathlength + 1);
1932 memcpy(basepath, pattern, basepathlength);
1933 basepath[basepathlength] = 0;
1935 // search through the path, one element at a time
1936 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
1938 // is the element a pak file?
1939 if (searchpath->pack)
1941 // look through all the pak file elements
1942 pak = searchpath->pack;
1943 for (i = 0;i < pak->numfiles;i++)
1945 strcpy(temp, pak->files[i].name);
1948 if (matchpattern(temp, (char *)pattern, true))
1950 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1951 if (!strcmp(listtemp->text, temp))
1953 if (listtemp == NULL)
1955 listcurrent = stringlistappend(listcurrent, temp);
1956 if (liststart == NULL)
1957 liststart = listcurrent;
1959 Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
1962 // strip off one path element at a time until empty
1963 // this way directories are added to the listing if they match the pattern
1964 slash = strrchr(temp, '/');
1965 backslash = strrchr(temp, '\\');
1966 colon = strrchr(temp, ':');
1968 if (separator < slash)
1970 if (separator < backslash)
1971 separator = backslash;
1972 if (separator < colon)
1974 *((char *)separator) = 0;
1980 // get a directory listing and look at each name
1981 snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
1982 if ((dir = listdirectory(netpath)))
1984 for (dirfile = dir;dirfile;dirfile = dirfile->next)
1986 snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
1987 if (matchpattern(temp, (char *)pattern, true))
1989 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1990 if (!strcmp(listtemp->text, temp))
1992 if (listtemp == NULL)
1994 listcurrent = stringlistappend(listcurrent, temp);
1995 if (liststart == NULL)
1996 liststart = listcurrent;
1998 Sys_Printf("SearchDirFile: %s\n", temp);
2009 liststart = stringlistsort(liststart);
2012 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2015 numchars += strlen(listtemp->text) + 1;
2017 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2018 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2019 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2020 search->numfilenames = numfiles;
2023 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2025 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2026 strcpy(search->filenames[numfiles], listtemp->text);
2028 numchars += strlen(listtemp->text) + 1;
2031 stringlistfree(liststart);
2038 void FS_FreeSearch(fssearch_t *search)
2043 extern int con_linewidth;
2044 int FS_ListDirectory(const char *pattern, int oneperline)
2055 search = FS_Search(pattern, true, true);
2058 numfiles = search->numfilenames;
2061 // FIXME: the names could be added to one column list and then
2062 // gradually shifted into the next column if they fit, and then the
2063 // next to make a compact variable width listing but it's a lot more
2065 // find width for columns
2067 for (i = 0;i < numfiles;i++)
2069 l = strlen(search->filenames[i]);
2070 if (columnwidth < l)
2073 // count the spacing character
2075 // calculate number of columns
2076 numcolumns = con_linewidth / columnwidth;
2077 // don't bother with the column printing if it's only one column
2078 if (numcolumns >= 2)
2080 numlines = (numfiles + numcolumns - 1) / numcolumns;
2081 for (i = 0;i < numlines;i++)
2084 for (k = 0;k < numcolumns;k++)
2086 l = i * numcolumns + k;
2089 name = search->filenames[l];
2090 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2091 linebuf[linebufpos++] = name[j];
2092 // space out name unless it's the last on the line
2093 if (k < (numcolumns - 1) && l < (numfiles - 1))
2094 for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2095 linebuf[linebufpos++] = ' ';
2098 linebuf[linebufpos] = 0;
2099 Con_Printf("%s\n", linebuf);
2106 for (i = 0;i < numfiles;i++)
2107 Con_Printf("%s\n", search->filenames[i]);
2108 FS_FreeSearch(search);
2112 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2114 const char *pattern;
2117 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2120 if (Cmd_Argc() == 2)
2121 pattern = Cmd_Argv(1);
2124 if (!FS_ListDirectory(pattern, oneperline))
2125 Con_Print("No files found.\n");
2130 FS_ListDirectoryCmd("dir", true);
2135 FS_ListDirectoryCmd("ls", false);