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
38 # include <sys/stat.h>
51 All of Quake's data access is through a hierchal file system, but the contents
52 of the file system can be transparently merged from several sources.
54 The "base directory" is the path to the directory holding the quake.exe and
55 all game directories. The sys_* files pass this to host_init in
56 quakeparms_t->basedir. This can be overridden with the "-basedir" command
57 line parm to allow code debugging in a different directory. The base
58 directory is only used during filesystem initialization.
60 The "game directory" is the first tree on the search path and directory that
61 all generated files (savegames, screenshots, demos, config files) will be
62 saved to. This can be overridden with the "-game" command line parameter.
63 The game directory can never be changed while quake is executing. This is a
64 precacution against having a malicious server instruct clients to write files
65 over areas they shouldn't.
71 =============================================================================
75 =============================================================================
78 // Magic numbers of a ZIP file (big-endian format)
79 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
80 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
81 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
83 // Other constants for ZIP files
84 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
85 #define ZIP_END_CDIR_SIZE 22
86 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
87 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
89 // Zlib constants (from zlib.h)
90 #define Z_SYNC_FLUSH 2
93 #define Z_STREAM_END 1
94 #define ZLIB_VERSION "1.1.4"
98 =============================================================================
102 =============================================================================
105 // Zlib stream (from zlib.h)
106 // Warning: some pointers we don't use directly have
107 // been cast to "void*" for a matter of simplicity
110 qbyte *next_in; // next input byte
111 unsigned int avail_in; // number of bytes available at next_in
112 unsigned long total_in; // total nb of input bytes read so far
114 qbyte *next_out; // next output byte should be put there
115 unsigned int avail_out; // remaining free space at next_out
116 unsigned long total_out; // total nb of bytes output so far
118 char *msg; // last error message, NULL if no error
119 void *state; // not visible by applications
121 void *zalloc; // used to allocate the internal state
122 void *zfree; // used to free the internal state
123 void *opaque; // private data object passed to zalloc and zfree
125 int data_type; // best guess about the data type: ascii or binary
126 unsigned long adler; // adler32 value of the uncompressed data
127 unsigned long reserved; // reserved for future use
131 // Our own file structure on top of FILE
135 FS_FLAG_PACKED = (1 << 0), // inside a package (PAK or PK3)
136 FS_FLAG_DEFLATED = (1 << 1) // file is compressed using the deflate algorithm (PK3 only)
139 #define ZBUFF_SIZE 1024
143 size_t real_length; // length of the uncompressed file
144 size_t in_ind, in_max; // input buffer index and counter
145 size_t in_position; // position in the compressed file
146 size_t out_ind, out_max; // output buffer index and counter
147 size_t out_position; // how many bytes did we uncompress until now?
148 qbyte input [ZBUFF_SIZE];
149 qbyte output [ZBUFF_SIZE];
156 size_t length; // file size on disk (PACKED only)
157 size_t offset; // offset into a package (PACKED only)
158 size_t position; // current position in the file (PACKED only)
159 ztoolkit_t* z; // used for inflating (DEFLATED only)
163 // ------ PK3 files on disk ------ //
165 // You can get the complete ZIP format description from PKWARE website
169 unsigned int signature;
170 unsigned short disknum;
171 unsigned short cdir_disknum; // number of the disk with the start of the central directory
172 unsigned short localentries; // number of entries in the central directory on this disk
173 unsigned short nbentries; // total number of entries in the central directory on this disk
174 unsigned int cdir_size; // size of the central directory
175 unsigned int cdir_offset; // with respect to the starting disk number
176 unsigned short comment_size;
177 } pk3_endOfCentralDir_t;
180 // ------ PAK files on disk ------ //
184 int filepos, filelen;
195 // Packages in memory
199 FILE_FLAG_TRUEOFFS = (1 << 0), // the offset in packfile_t is the true contents offset
200 FILE_FLAG_DEFLATED = (1 << 1) // file compressed using the deflate algorithm
205 char name [MAX_QPATH];
208 size_t packsize; // size in the package
209 size_t realsize; // real file size (uncompressed)
212 typedef struct pack_s
214 char filename [MAX_OSPATH];
216 int ignorecase; // PK3 ignores case
224 // Search paths for files (including packages)
225 typedef struct searchpath_s
227 // only one of filename / pack will be used
228 char filename[MAX_OSPATH];
230 struct searchpath_s *next;
235 =============================================================================
239 =============================================================================
245 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
246 size_t offset, size_t packsize,
247 size_t realsize, file_flags_t flags);
251 =============================================================================
255 =============================================================================
258 mempool_t *fs_mempool;
259 mempool_t *pak_mempool;
263 pack_t *packlist = NULL;
265 searchpath_t *fs_searchpaths = NULL;
267 #define MAX_FILES_IN_PACK 65536
269 char fs_gamedir[MAX_OSPATH];
270 char fs_basedir[MAX_OSPATH];
272 qboolean fs_modified; // set true if using non-id files
276 =============================================================================
278 PRIVATE FUNCTIONS - PK3 HANDLING
280 =============================================================================
283 // Functions exported from zlib
285 # define ZEXPORT WINAPI
290 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
291 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
292 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
293 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
295 #define qz_inflateInit2(strm, windowBits) \
296 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
298 static dllfunction_t zlibfuncs[] =
300 {"inflate", (void **) &qz_inflate},
301 {"inflateEnd", (void **) &qz_inflateEnd},
302 {"inflateInit2_", (void **) &qz_inflateInit2_},
303 {"inflateReset", (void **) &qz_inflateReset},
307 // Handle for Zlib DLL
308 static dllhandle_t zlib_dll = NULL;
318 void PK3_CloseLibrary (void)
320 Sys_UnloadLibrary (&zlib_dll);
328 Try to load the Zlib DLL
331 qboolean PK3_OpenLibrary (void)
340 dllname = "zlib.dll";
346 if (! Sys_LoadLibrary (dllname, &zlib_dll, zlibfuncs))
348 Con_Printf ("Compressed files support disabled\n");
352 Con_Printf ("Compressed files support enabled\n");
359 PK3_GetEndOfCentralDir
361 Extract the end of the central directory from a PK3 package
364 qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd)
366 long filesize, maxsize;
370 // Get the package size
371 fseek (packhandle, 0, SEEK_END);
372 filesize = ftell (packhandle);
373 if (filesize < ZIP_END_CDIR_SIZE)
376 // Load the end of the file in memory
377 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
380 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
381 buffer = Mem_Alloc (tempmempool, maxsize);
382 fseek (packhandle, filesize - maxsize, SEEK_SET);
383 if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) maxsize)
389 // Look for the end of central dir signature around the end of the file
390 maxsize -= ZIP_END_CDIR_SIZE;
391 ptr = &buffer[maxsize];
393 while (BuffBigLong (ptr) != ZIP_END_HEADER)
405 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
406 eocd->signature = LittleLong (eocd->signature);
407 eocd->disknum = LittleShort (eocd->disknum);
408 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
409 eocd->localentries = LittleShort (eocd->localentries);
410 eocd->nbentries = LittleShort (eocd->nbentries);
411 eocd->cdir_size = LittleLong (eocd->cdir_size);
412 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
413 eocd->comment_size = LittleShort (eocd->comment_size);
425 Extract the file list from a PK3 file
428 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
430 qbyte *central_dir, *ptr;
434 // Load the central directory in memory
435 central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
436 fseek (pack->handle, eocd->cdir_offset, SEEK_SET);
437 fread (central_dir, 1, eocd->cdir_size, pack->handle);
439 // Extract the files properties
440 // The parsing is done "by hand" because some fields have variable sizes and
441 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
442 remaining = eocd->cdir_size;
445 for (ind = 0; ind < eocd->nbentries; ind++)
447 size_t namesize, count;
449 // Checking the remaining size
450 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
452 Mem_Free (central_dir);
455 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
458 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
460 Mem_Free (central_dir);
464 namesize = BuffLittleShort (&ptr[28]); // filename length
466 // Check encryption, compression, and attributes
467 // 1st uint8 : general purpose bit flag
468 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
469 // 2nd uint8 : external file attributes
470 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
471 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
473 // Still enough bytes for the name?
474 if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
476 Mem_Free (central_dir);
480 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
481 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
483 char filename [sizeof (pack->files[0].name)];
484 size_t offset, packsize, realsize;
487 // Extract the name (strip it if necessary)
488 if (namesize >= sizeof (filename))
489 namesize = sizeof (filename) - 1;
490 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
491 filename[namesize] = '\0';
493 if (BuffLittleShort (&ptr[10]))
494 flags = FILE_FLAG_DEFLATED;
497 offset = BuffLittleLong (&ptr[42]);
498 packsize = BuffLittleLong (&ptr[20]);
499 realsize = BuffLittleLong (&ptr[24]);
500 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
504 // Skip the name, additionnal field, and comment
505 // 1er uint16 : extra field length
506 // 2eme uint16 : file comment length
507 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
508 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
512 Mem_Free (central_dir);
513 return pack->numfiles;
521 Create a package entry associated with a PK3 file
524 pack_t *FS_LoadPackPK3 (const char *packfile)
527 pk3_endOfCentralDir_t eocd;
531 packhandle = fopen (packfile, "rb");
535 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
536 Sys_Error ("%s is not a PK3 file", packfile);
538 // Multi-volume ZIP archives are NOT allowed
539 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
540 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
542 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
543 // since eocd.nbentries is an unsigned 16 bits integer
544 #if MAX_FILES_IN_PACK < 65535
545 if (eocd.nbentries > MAX_FILES_IN_PACK)
546 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
549 // Create a package structure in memory
550 pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
551 pack->ignorecase = true; // PK3 ignores case
552 strlcpy (pack->filename, packfile, sizeof (pack->filename));
553 pack->handle = packhandle;
554 pack->numfiles = eocd.nbentries;
555 pack->mempool = Mem_AllocPool (packfile);
556 pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
557 pack->next = packlist;
560 real_nb_files = PK3_BuildFileList (pack, &eocd);
561 if (real_nb_files <= 0)
562 Sys_Error ("%s is not a valid PK3 file", packfile);
564 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
571 PK3_GetTrueFileOffset
573 Find where the true file data offset is
576 void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
578 qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
582 if (file->flags & FILE_FLAG_TRUEOFFS)
585 // Load the local file description
586 fseek (pack->handle, file->offset, SEEK_SET);
587 count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
588 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
589 Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
591 // Skip name and extra field
592 file->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
594 file->flags |= FILE_FLAG_TRUEOFFS;
599 =============================================================================
601 OTHER PRIVATE FUNCTIONS
603 =============================================================================
611 Add a file to the list of files contained into a package
614 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
615 size_t offset, size_t packsize,
616 size_t realsize, file_flags_t flags)
618 int (*strcmp_funct) (const char* str1, const char* str2);
619 size_t left, right, middle;
623 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
625 // Look for the slot we should put that file into (binary search)
627 right = pack->numfiles;
628 while (left != right)
630 middle = (left + right - 1) / 2;
631 diff = strcmp_funct (pack->files[middle].name, name);
633 // If we found the file, there's a problem
635 Sys_Error ("Package %s contains several time the file %s\n",
636 pack->filename, name);
638 // If we're too far in the list
645 // We have to move the right of the list by one slot to free the one we need
646 file = &pack->files[left];
647 memmove (file + 1, file, (pack->numfiles - left) * sizeof (*file));
650 strlcpy (file->name, name, sizeof (file->name));
651 file->offset = offset;
652 file->packsize = packsize;
653 file->realsize = realsize;
664 Only used for FS_Open.
667 void FS_CreatePath (char *path)
671 for (ofs = path+1 ; *ofs ; ofs++)
673 if (*ofs == '/' || *ofs == '\\')
675 // create the directory
691 void FS_Path_f (void)
695 Con_Print("Current search path:\n");
696 for (s=fs_searchpaths ; s ; s=s->next)
700 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
703 Con_Printf("%s\n", s->filename);
712 Takes an explicit (not game tree related) path to a pak file.
714 Loads the header and directory, adding the files at the beginning
715 of the list so they override previous pack files.
718 pack_t *FS_LoadPackPAK (const char *packfile)
720 dpackheader_t header;
724 dpackfile_t *info; // temporary alloc, allowing huge pack directories
726 packhandle = fopen (packfile, "rb");
730 fread ((void *)&header, 1, sizeof(header), packhandle);
731 if (memcmp(header.id, "PACK", 4))
732 Sys_Error ("%s is not a packfile", packfile);
733 header.dirofs = LittleLong (header.dirofs);
734 header.dirlen = LittleLong (header.dirlen);
736 if (header.dirlen % sizeof(dpackfile_t))
737 Sys_Error ("%s has an invalid directory size", packfile);
739 numpackfiles = header.dirlen / sizeof(dpackfile_t);
741 if (numpackfiles > MAX_FILES_IN_PACK)
742 Sys_Error ("%s has %i files", packfile, numpackfiles);
744 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
745 pack->ignorecase = false; // PAK is case sensitive
746 strlcpy (pack->filename, packfile, sizeof (pack->filename));
747 pack->handle = packhandle;
749 pack->mempool = Mem_AllocPool(packfile);
750 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
751 pack->next = packlist;
754 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
755 fseek (packhandle, header.dirofs, SEEK_SET);
756 fread ((void *)info, 1, header.dirlen, packhandle);
758 // parse the directory
759 for (i = 0;i < numpackfiles;i++)
761 size_t offset = LittleLong (info[i].filepos);
762 size_t size = LittleLong (info[i].filelen);
764 FS_AddFileToPack (info[i].name, pack, offset, size, size, FILE_FLAG_TRUEOFFS);
769 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
778 Sets fs_gamedir, adds the directory to the head of the path,
779 then loads and adds pak1.pak pak2.pak ...
782 void FS_AddGameDirectory (char *dir)
784 stringlist_t *list, *current;
785 searchpath_t *search;
787 char pakfile[MAX_OSPATH];
789 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
792 // add the directory to the search path
793 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
794 strlcpy (search->filename, dir, sizeof (search->filename));
795 search->next = fs_searchpaths;
796 fs_searchpaths = search;
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 snprintf (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 snprintf (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 // Unpacked files have the priority over packed files if AKVERSION is defined
842 // add the directory to the search path
843 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
844 strlcpy (search->filename, dir, sizeof (search->filename));
845 search->next = fs_searchpaths;
846 fs_searchpaths = search;
856 char *FS_FileExtension (const char *in)
858 static char exten[8];
859 const char *slash, *backslash, *colon, *dot, *separator;
862 slash = strrchr(in, '/');
863 backslash = strrchr(in, '\\');
864 colon = strrchr(in, ':');
865 dot = strrchr(in, '.');
867 if (separator < backslash)
868 separator = backslash;
869 if (separator < colon)
871 if (dot == NULL || dot < separator)
874 for (i = 0;i < 7 && dot[i];i++)
889 searchpath_t *search;
891 fs_mempool = Mem_AllocPool("file management");
892 pak_mempool = Mem_AllocPool("paks");
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 i = COM_CheckParm ("-basedir");
906 if (i && i < com_argc-1)
908 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
909 i = strlen (fs_basedir);
910 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
914 // -path <dir or packfile> [<dir or packfile>] ...
915 // Fully specifies the exact search path, overriding the generated one
916 i = COM_CheckParm ("-path");
920 while (++i < com_argc)
922 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
925 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
926 if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
928 search->pack = FS_LoadPackPAK (com_argv[i]);
930 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
932 else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
934 search->pack = FS_LoadPackPK3 (com_argv[i]);
936 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
939 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
940 search->next = fs_searchpaths;
941 fs_searchpaths = search;
946 // start up with GAMENAME by default (id1)
947 strlcpy (com_modname, GAMENAME, sizeof (com_modname));
948 FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
950 // add the game-specific path, if any
954 strlcpy (com_modname, gamedirname, sizeof (com_modname));
955 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
959 // Adds basedir/gamedir as an override game
960 i = COM_CheckParm ("-game");
961 if (i && i < com_argc-1)
964 strlcpy (com_modname, com_argv[i+1], sizeof (com_modname));
965 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
974 Internal function used to create a qfile_t and open the relevant file on disk
977 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
981 file = Mem_Alloc (fs_mempool, sizeof (*file));
982 memset (file, 0, sizeof (*file));
984 file->stream = fopen (filepath, mode);
1000 qfile_t *FS_OpenRead (const char *path, int offs, int len)
1004 file = FS_SysOpen (path, "rb");
1007 Sys_Error ("Couldn't open %s", path);
1012 if (offs < 0 || len < 0)
1014 // We set fs_filesize here for normal files
1015 fseek (file->stream, 0, SEEK_END);
1016 fs_filesize = ftell (file->stream);
1017 fseek (file->stream, 0, SEEK_SET);
1022 fseek (file->stream, offs, SEEK_SET);
1024 file->flags |= FS_FLAG_PACKED;
1026 file->offset = offs;
1035 ====================
1038 Look for a file in the packages and in the filesystem
1040 Return the searchpath where the file was found (or NULL)
1041 and the file index in the package if relevant
1042 ====================
1044 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1046 searchpath_t *search;
1048 int (*strcmp_funct) (const char* str1, const char* str2);
1050 // search through the path, one element at a time
1051 for (search = fs_searchpaths;search;search = search->next)
1053 // is the element a pak file?
1056 size_t left, right, middle;
1059 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1061 // Look for the file (binary search)
1063 right = pak->numfiles;
1064 while (left != right)
1068 middle = (left + right - 1) / 2;
1069 diff = strcmp_funct (pak->files[middle].name, name);
1075 Sys_Printf("FS_FindFile: %s in %s\n",
1076 pak->files[middle].name, pak->filename);
1083 // If we're too far in the list
1092 char netpath[MAX_OSPATH];
1093 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1094 if (FS_SysFileExists (netpath))
1097 Sys_Printf("FS_FindFile: %s\n", netpath);
1107 Sys_Printf("FS_FindFile: can't find %s\n", name);
1119 If the requested file is inside a packfile, a new qfile_t* will be opened
1125 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
1127 searchpath_t *search;
1128 packfile_t *packfile;
1132 search = FS_FindFile (filename, &i, quiet);
1141 // Found in the filesystem?
1144 char netpath[MAX_OSPATH];
1145 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
1146 return FS_OpenRead(netpath, -1, -1);
1149 // So, we found it in a package...
1150 packfile = &search->pack->files[i];
1152 // If we don't have the true offset, get it now
1153 if (! (packfile->flags & FILE_FLAG_TRUEOFFS))
1154 PK3_GetTrueFileOffset (packfile, search->pack);
1156 // No Zlib DLL = no compressed files
1157 if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED))
1159 Con_Printf("WARNING: can't open the compressed file %s\n"
1160 "You need the Zlib DLL to use compressed files\n",
1166 // open a new file in the pakfile
1167 file = FS_OpenRead (search->pack->filename, packfile->offset, packfile->packsize);
1168 fs_filesize = packfile->realsize;
1170 if (packfile->flags & FILE_FLAG_DEFLATED)
1174 file->flags |= FS_FLAG_DEFLATED;
1176 // We need some more variables
1177 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1179 ztk->real_length = packfile->realsize;
1181 // Initialize zlib stream
1182 ztk->zstream.next_in = ztk->input;
1183 ztk->zstream.avail_in = 0;
1185 /* From Zlib's "unzip.c":
1187 * windowBits is passed < 0 to tell that there is no zlib header.
1188 * Note that in this case inflate *requires* an extra "dummy" byte
1189 * after the compressed stream in order to complete decompression and
1190 * return Z_STREAM_END.
1191 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1192 * size of both compressed and uncompressed data
1194 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1195 Sys_Error ("inflate init error (file: %s)", filename);
1197 ztk->zstream.next_out = ztk->output;
1198 ztk->zstream.avail_out = sizeof (ztk->output);
1208 =============================================================================
1210 MAIN PUBLIC FUNCTIONS
1212 =============================================================================
1216 ====================
1219 Open a file. The syntax is the same as fopen
1220 ====================
1222 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1224 // If the file is opened in "write" or "append" mode
1225 if (strchr (mode, 'w') || strchr (mode, 'a'))
1227 char real_path [MAX_OSPATH];
1229 // Open the file on disk directly
1230 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1232 // Create directories up to the file
1233 FS_CreatePath (real_path);
1235 return FS_SysOpen (real_path, mode);
1238 // Else, we look at the various search paths
1239 return FS_FOpenFile (filepath, quiet);
1244 ====================
1248 ====================
1250 int FS_Close (qfile_t* file)
1252 if (fclose (file->stream))
1257 qz_inflateEnd (&file->z->zstream);
1267 ====================
1270 Write "datasize" bytes into a file
1271 ====================
1273 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1275 return fwrite (data, 1, datasize, file->stream);
1280 ====================
1283 Read up to "buffersize" bytes from a file
1284 ====================
1286 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1291 // Quick path for unpacked files
1292 if (! (file->flags & FS_FLAG_PACKED))
1293 return fread (buffer, 1, buffersize, file->stream);
1295 // If the file isn't compressed
1296 if (! (file->flags & FS_FLAG_DEFLATED))
1298 // We must take care to not read after the end of the file
1299 count = file->length - file->position;
1300 if (buffersize > count)
1303 nb = fread (buffer, 1, buffersize, file->stream);
1305 file->position += nb;
1309 // If the file is compressed, it's more complicated...
1312 // First, we copy as many bytes as we can from "output"
1313 if (ztk->out_ind < ztk->out_max)
1315 count = ztk->out_max - ztk->out_ind;
1317 nb = (buffersize > count) ? count : buffersize;
1318 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1320 file->position += nb;
1325 // We cycle through a few operations until we have inflated enough data
1326 while (nb < buffersize)
1328 // NOTE: at this point, "output" should always be empty
1330 // If "input" is also empty, we need to fill it
1331 if (ztk->in_ind == ztk->in_max)
1335 // If we are at the end of the file
1336 if (ztk->out_position == ztk->real_length)
1339 remain = file->length - ztk->in_position;
1340 count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1341 fread (ztk->input, 1, count, file->stream);
1343 // Update indexes and counters
1345 ztk->in_max = count;
1346 ztk->in_position += count;
1349 // Now that we are sure we have compressed data available, we need to determine
1350 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1351 // case if we still need more bytes than "output" can contain)
1353 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1354 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1356 // If output will be able to contain at least 1 more byte than the data we need
1357 if (buffersize - nb < sizeof (ztk->output))
1361 // Inflate the data in "output"
1362 ztk->zstream.next_out = ztk->output;
1363 ztk->zstream.avail_out = sizeof (ztk->output);
1364 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1365 if (error != Z_OK && error != Z_STREAM_END)
1366 Sys_Error ("Can't inflate file");
1367 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1368 ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1369 ztk->out_position += ztk->out_max;
1371 // Copy the requested data in "buffer" (as much as we can)
1372 count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1373 memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1374 ztk->out_ind = count;
1377 // Else, we inflate directly in "buffer"
1382 // Inflate the data in "buffer"
1383 ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1384 ztk->zstream.avail_out = buffersize - nb;
1385 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1386 if (error != Z_OK && error != Z_STREAM_END)
1387 Sys_Error ("Can't inflate file");
1388 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1390 // Invalidate the output data (for FS_Seek)
1394 // How much data did it inflate?
1395 count = buffersize - nb - ztk->zstream.avail_out;
1396 ztk->out_position += count;
1400 file->position += count;
1408 ====================
1411 Flush the file output stream
1412 ====================
1414 int FS_Flush (qfile_t* file)
1416 return fflush (file->stream);
1421 ====================
1424 Print a string into a file
1425 ====================
1427 int FS_Print(qfile_t* file, const char *msg)
1429 return FS_Write(file, msg, strlen(msg));
1433 ====================
1436 Print a string into a file
1437 ====================
1439 int FS_Printf(qfile_t* file, const char* format, ...)
1444 va_start (args, format);
1445 result = vfprintf (file->stream, format, args);
1453 ====================
1456 Get the next character of a file
1457 ====================
1459 int FS_Getc (qfile_t* file)
1463 if (FS_Read (file, &c, 1) != 1)
1471 ====================
1474 Move the position index in a file
1475 ====================
1477 int FS_Seek (qfile_t* file, long offset, int whence)
1479 // Quick path for unpacked files
1480 if (! (file->flags & FS_FLAG_PACKED))
1481 return fseek (file->stream, offset, whence);
1483 // Seeking in compressed files is more a hack than anything else,
1484 // but we need to support it, so here it is.
1485 if (file->flags & FS_FLAG_DEFLATED)
1487 ztoolkit_t *ztk = file->z;
1488 qbyte buffer [sizeof (ztk->output)]; // it's big to force inflating into buffer directly
1493 offset += file->position;
1500 offset += ztk->real_length;
1506 if (offset < 0 || offset > (long) ztk->real_length)
1509 // If we need to go back in the file
1510 if (offset <= (long) file->position)
1512 // If we still have the data we need in the output buffer
1513 if (file->position - offset <= ztk->out_ind)
1515 ztk->out_ind -= file->position - offset;
1516 file->position = offset;
1520 // Else, we restart from the beginning of the file
1523 ztk->in_position = 0;
1526 ztk->out_position = 0;
1528 fseek (file->stream, file->offset, SEEK_SET);
1530 // Reset the Zlib stream
1531 ztk->zstream.next_in = ztk->input;
1532 ztk->zstream.avail_in = 0;
1533 qz_inflateReset (&ztk->zstream);
1536 // Skip all data until we reach the requested offset
1537 while ((long) file->position < offset)
1539 size_t diff = offset - file->position;
1542 count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1543 len = FS_Read (file, buffer, count);
1551 // Packed files receive a special treatment too, because
1552 // we need to make sure it doesn't go outside of the file
1556 offset += file->position;
1563 offset += file->length;
1569 if (offset < 0 || offset > (long) file->length)
1572 if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1574 file->position = offset;
1580 ====================
1583 Give the current position in a file
1584 ====================
1586 long FS_Tell (qfile_t* file)
1588 if (file->flags & FS_FLAG_PACKED)
1589 return file->position;
1591 return ftell (file->stream);
1596 ====================
1599 Extract a line from a file
1600 ====================
1602 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1606 // Quick path for unpacked files
1607 if (! (file->flags & FS_FLAG_PACKED))
1608 return fgets (buffer, buffersize, file->stream);
1610 for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1612 int c = FS_Getc (file);
1627 buffer[ind + 1] = '\0';
1636 buffer[buffersize - 1] = '\0';
1645 Dynamic length version of fgets. DO NOT free the buffer.
1648 char *FS_Getline (qfile_t *file)
1650 static int size = 256;
1651 static char *buf = 0;
1656 buf = Mem_Alloc (fs_mempool, size);
1658 if (!FS_Gets (file, buf, size))
1662 while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1664 t = Mem_Alloc (fs_mempool, size + 256);
1665 memcpy(t, buf, size);
1669 if (!FS_Gets (file, buf + len, size - len))
1673 while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1680 ====================
1683 Extract a line from a file
1684 ====================
1686 int FS_Eof (qfile_t* file)
1688 if (file->flags & FS_FLAG_PACKED)
1690 if (file->flags & FS_FLAG_DEFLATED)
1691 return (file->position == file->z->real_length);
1693 return (file->position == file->length);
1696 return feof (file->stream);
1704 Filename are relative to the quake directory.
1705 Always appends a 0 byte.
1708 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1713 // look for it in the filesystem or pack files
1714 h = FS_Open (path, "rb", quiet);
1718 buf = Mem_Alloc(pool, fs_filesize+1);
1720 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1722 ((qbyte *)buf)[fs_filesize] = 0;
1724 FS_Read (h, buf, fs_filesize);
1735 The filename will be prefixed by the current game directory
1738 qboolean FS_WriteFile (const char *filename, void *data, int len)
1742 handle = FS_Open (filename, "wb", false);
1745 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1749 Con_DPrintf("FS_WriteFile: %s\n", filename);
1750 FS_Write (handle, data, len);
1757 =============================================================================
1759 OTHERS PUBLIC FUNCTIONS
1761 =============================================================================
1769 void FS_StripExtension (const char *in, char *out, size_t size_out)
1776 while (*in && size_out > 1)
1780 else if (*in == '/' || *in == '\\' || *in == ':')
1797 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1801 // if path doesn't have a .EXT, append extension
1802 // (extension should include the .)
1803 src = path + strlen(path) - 1;
1805 while (*src != '/' && src != path)
1808 return; // it has an extension
1812 strlcat (path, extension, size_path);
1820 Look for a file in the packages and in the filesystem
1823 qboolean FS_FileExists (const char *filename)
1825 return (FS_FindFile (filename, NULL, true) != NULL);
1833 Look for a file in the filesystem only
1836 qboolean FS_SysFileExists (const char *path)
1841 f = fopen (path, "rb");
1852 if (stat (path,&buf) == -1)
1859 void FS_mkdir (const char *path)
1872 Allocate and fill a search structure with information on matching filenames.
1875 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
1878 searchpath_t *searchpath;
1880 int i, basepathlength, numfiles, numchars;
1881 stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
1882 const char *slash, *backslash, *colon, *separator;
1884 char netpath[MAX_OSPATH];
1885 char temp[MAX_OSPATH];
1887 while(!strncmp(pattern, "./", 2))
1889 while(!strncmp(pattern, ".\\", 2))
1896 slash = strrchr(pattern, '/');
1897 backslash = strrchr(pattern, '\\');
1898 colon = strrchr(pattern, ':');
1899 separator = pattern;
1900 if (separator < slash)
1902 if (separator < backslash)
1903 separator = backslash;
1904 if (separator < colon)
1906 basepathlength = separator - pattern;
1907 basepath = Mem_Alloc (tempmempool, basepathlength + 1);
1909 memcpy(basepath, pattern, basepathlength);
1910 basepath[basepathlength] = 0;
1912 // search through the path, one element at a time
1913 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
1915 // is the element a pak file?
1916 if (searchpath->pack)
1918 // look through all the pak file elements
1919 pak = searchpath->pack;
1920 for (i = 0;i < pak->numfiles;i++)
1922 strcpy(temp, pak->files[i].name);
1925 if (matchpattern(temp, (char *)pattern, true))
1927 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1928 if (!strcmp(listtemp->text, temp))
1930 if (listtemp == NULL)
1932 listcurrent = stringlistappend(listcurrent, temp);
1933 if (liststart == NULL)
1934 liststart = listcurrent;
1936 Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
1939 // strip off one path element at a time until empty
1940 // this way directories are added to the listing if they match the pattern
1941 slash = strrchr(temp, '/');
1942 backslash = strrchr(temp, '\\');
1943 colon = strrchr(temp, ':');
1945 if (separator < slash)
1947 if (separator < backslash)
1948 separator = backslash;
1949 if (separator < colon)
1951 *((char *)separator) = 0;
1957 // get a directory listing and look at each name
1958 snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
1959 if ((dir = listdirectory(netpath)))
1961 for (dirfile = dir;dirfile;dirfile = dirfile->next)
1963 snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
1964 if (matchpattern(temp, (char *)pattern, true))
1966 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1967 if (!strcmp(listtemp->text, temp))
1969 if (listtemp == NULL)
1971 listcurrent = stringlistappend(listcurrent, temp);
1972 if (liststart == NULL)
1973 liststart = listcurrent;
1975 Sys_Printf("SearchDirFile: %s\n", temp);
1986 liststart = stringlistsort(liststart);
1989 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1992 numchars += strlen(listtemp->text) + 1;
1994 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
1995 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
1996 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
1997 search->numfilenames = numfiles;
2000 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2002 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2003 strcpy(search->filenames[numfiles], listtemp->text);
2005 numchars += strlen(listtemp->text) + 1;
2008 stringlistfree(liststart);
2015 void FS_FreeSearch(fssearch_t *search)
2020 extern int con_linewidth;
2021 int FS_ListDirectory(const char *pattern, int oneperline)
2032 search = FS_Search(pattern, true, true);
2035 numfiles = search->numfilenames;
2038 // FIXME: the names could be added to one column list and then
2039 // gradually shifted into the next column if they fit, and then the
2040 // next to make a compact variable width listing but it's a lot more
2042 // find width for columns
2044 for (i = 0;i < numfiles;i++)
2046 l = strlen(search->filenames[i]);
2047 if (columnwidth < l)
2050 // count the spacing character
2052 // calculate number of columns
2053 numcolumns = con_linewidth / columnwidth;
2054 // don't bother with the column printing if it's only one column
2055 if (numcolumns >= 2)
2057 numlines = (numfiles + numcolumns - 1) / numcolumns;
2058 for (i = 0;i < numlines;i++)
2061 for (k = 0;k < numcolumns;k++)
2063 l = i * numcolumns + k;
2066 name = search->filenames[l];
2067 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2068 linebuf[linebufpos++] = name[j];
2069 // space out name unless it's the last on the line
2070 if (k < (numcolumns - 1) && l < (numfiles - 1))
2071 for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2072 linebuf[linebufpos++] = ' ';
2075 linebuf[linebufpos] = 0;
2076 Con_Printf("%s\n", linebuf);
2083 for (i = 0;i < numfiles;i++)
2084 Con_Printf("%s\n", search->filenames[i]);
2085 FS_FreeSearch(search);
2089 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2091 const char *pattern;
2094 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2097 if (Cmd_Argc() == 2)
2098 pattern = Cmd_Argv(1);
2101 if (!FS_ListDirectory(pattern, oneperline))
2102 Con_Print("No files found.\n");
2107 FS_ListDirectoryCmd("dir", true);
2112 FS_ListDirectoryCmd("ls", false);