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;
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)
323 Sys_UnloadLibrary (zlib_dll);
332 Try to load the Zlib DLL
335 qboolean PK3_OpenLibrary (void)
338 const dllfunction_t *func;
345 dllname = "zlib.dll";
351 for (func = zlibfuncs; func && func->name != NULL; func++)
352 *func->funcvariable = NULL;
355 if (! (zlib_dll = Sys_LoadLibrary (dllname)))
357 Con_Printf("Can't find %s. Compressed files support disabled\n", dllname);
361 // Get the function adresses
362 for (func = zlibfuncs; func && func->name != NULL; func++)
363 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (zlib_dll, func->name)))
365 Con_Printf("missing function \"%s\" - broken Zlib library!\n", func->name);
370 Con_Printf("%s loaded. Compressed files support enabled\n", dllname);
377 PK3_GetEndOfCentralDir
379 Extract the end of the central directory from a PK3 package
382 qboolean PK3_GetEndOfCentralDir (const char *packfile, FILE *packhandle, pk3_endOfCentralDir_t *eocd)
384 long filesize, maxsize;
388 // Get the package size
389 fseek (packhandle, 0, SEEK_END);
390 filesize = ftell (packhandle);
391 if (filesize < ZIP_END_CDIR_SIZE)
394 // Load the end of the file in memory
395 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
398 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
399 buffer = Mem_Alloc (tempmempool, maxsize);
400 fseek (packhandle, filesize - maxsize, SEEK_SET);
401 if (fread (buffer, 1, maxsize, packhandle) != (unsigned long) maxsize)
407 // Look for the end of central dir signature around the end of the file
408 maxsize -= ZIP_END_CDIR_SIZE;
409 ptr = &buffer[maxsize];
411 while (BuffBigLong (ptr) != ZIP_END_HEADER)
423 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
424 eocd->signature = LittleLong (eocd->signature);
425 eocd->disknum = LittleShort (eocd->disknum);
426 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
427 eocd->localentries = LittleShort (eocd->localentries);
428 eocd->nbentries = LittleShort (eocd->nbentries);
429 eocd->cdir_size = LittleLong (eocd->cdir_size);
430 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
431 eocd->comment_size = LittleShort (eocd->comment_size);
443 Extract the file list from a PK3 file
446 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
448 qbyte *central_dir, *ptr;
452 // Load the central directory in memory
453 central_dir = Mem_Alloc (tempmempool, eocd->cdir_size);
454 fseek (pack->handle, eocd->cdir_offset, SEEK_SET);
455 fread (central_dir, 1, eocd->cdir_size, pack->handle);
457 // Extract the files properties
458 // The parsing is done "by hand" because some fields have variable sizes and
459 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
460 remaining = eocd->cdir_size;
463 for (ind = 0; ind < eocd->nbentries; ind++)
465 size_t namesize, count;
467 // Checking the remaining size
468 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
470 Mem_Free (central_dir);
473 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
476 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
478 Mem_Free (central_dir);
482 namesize = BuffLittleShort (&ptr[28]); // filename length
484 // Check encryption, compression, and attributes
485 // 1st uint8 : general purpose bit flag
486 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
487 // 2nd uint8 : external file attributes
488 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
489 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
491 // Still enough bytes for the name?
492 if ((size_t) remaining < namesize || namesize >= sizeof (*pack->files))
494 Mem_Free (central_dir);
498 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
499 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
501 char filename [sizeof (pack->files[0].name)];
502 size_t offset, packsize, realsize;
505 // Extract the name (strip it if necessary)
506 if (namesize >= sizeof (filename))
507 namesize = sizeof (filename) - 1;
508 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
509 filename[namesize] = '\0';
511 if (BuffLittleShort (&ptr[10]))
512 flags = FILE_FLAG_DEFLATED;
515 offset = BuffLittleLong (&ptr[42]);
516 packsize = BuffLittleLong (&ptr[20]);
517 realsize = BuffLittleLong (&ptr[24]);
518 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
522 // Skip the name, additionnal field, and comment
523 // 1er uint16 : extra field length
524 // 2eme uint16 : file comment length
525 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
526 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
530 Mem_Free (central_dir);
531 return pack->numfiles;
539 Create a package entry associated with a PK3 file
542 pack_t *FS_LoadPackPK3 (const char *packfile)
545 pk3_endOfCentralDir_t eocd;
549 packhandle = fopen (packfile, "rb");
553 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
554 Sys_Error ("%s is not a PK3 file", packfile);
556 // Multi-volume ZIP archives are NOT allowed
557 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
558 Sys_Error ("%s is a multi-volume ZIP archive", packfile);
560 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
561 // since eocd.nbentries is an unsigned 16 bits integer
562 #if MAX_FILES_IN_PACK < 65535
563 if (eocd.nbentries > MAX_FILES_IN_PACK)
564 Sys_Error ("%s contains too many files (%hu)", packfile, eocd.nbentries);
567 // Create a package structure in memory
568 pack = Mem_Alloc (pak_mempool, sizeof (pack_t));
569 pack->ignorecase = true; // PK3 ignores case
570 strlcpy (pack->filename, packfile, sizeof (pack->filename));
571 pack->handle = packhandle;
572 pack->numfiles = eocd.nbentries;
573 pack->mempool = Mem_AllocPool (packfile);
574 pack->files = Mem_Alloc (pack->mempool, eocd.nbentries * sizeof(packfile_t));
575 pack->next = packlist;
578 real_nb_files = PK3_BuildFileList (pack, &eocd);
579 if (real_nb_files <= 0)
580 Sys_Error ("%s is not a valid PK3 file", packfile);
582 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
589 PK3_GetTrueFileOffset
591 Find where the true file data offset is
594 void PK3_GetTrueFileOffset (packfile_t *file, pack_t *pack)
596 qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
600 if (file->flags & FILE_FLAG_TRUEOFFS)
603 // Load the local file description
604 fseek (pack->handle, file->offset, SEEK_SET);
605 count = fread (buffer, 1, ZIP_LOCAL_CHUNK_BASE_SIZE, pack->handle);
606 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
607 Sys_Error ("Can't retrieve file %s in package %s", file->name, pack->filename);
609 // Skip name and extra field
610 file->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
612 file->flags |= FILE_FLAG_TRUEOFFS;
617 =============================================================================
619 OTHER PRIVATE FUNCTIONS
621 =============================================================================
629 Add a file to the list of files contained into a package
632 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
633 size_t offset, size_t packsize,
634 size_t realsize, file_flags_t flags)
636 int (*strcmp_funct) (const char* str1, const char* str2);
637 size_t left, right, middle;
641 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
643 // Look for the slot we should put that file into (binary search)
645 right = pack->numfiles;
646 while (left != right)
648 middle = (left + right - 1) / 2;
649 diff = strcmp_funct (pack->files[middle].name, name);
651 // If we found the file, there's a problem
653 Sys_Error ("Package %s contains several time the file %s\n",
654 pack->filename, name);
656 // If we're too far in the list
663 // We have to move the right of the list by one slot to free the one we need
664 file = &pack->files[left];
665 memmove (file + 1, file, (pack->numfiles - left) * sizeof (*file));
668 strlcpy (file->name, name, sizeof (file->name));
669 file->offset = offset;
670 file->packsize = packsize;
671 file->realsize = realsize;
682 Only used for FS_Open.
685 void FS_CreatePath (char *path)
689 for (ofs = path+1 ; *ofs ; ofs++)
691 if (*ofs == '/' || *ofs == '\\')
693 // create the directory
709 void FS_Path_f (void)
713 Con_Print("Current search path:\n");
714 for (s=fs_searchpaths ; s ; s=s->next)
718 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
721 Con_Printf("%s\n", s->filename);
730 Takes an explicit (not game tree related) path to a pak file.
732 Loads the header and directory, adding the files at the beginning
733 of the list so they override previous pack files.
736 pack_t *FS_LoadPackPAK (const char *packfile)
738 dpackheader_t header;
742 dpackfile_t *info; // temporary alloc, allowing huge pack directories
744 packhandle = fopen (packfile, "rb");
748 fread ((void *)&header, 1, sizeof(header), packhandle);
749 if (memcmp(header.id, "PACK", 4))
750 Sys_Error ("%s is not a packfile", packfile);
751 header.dirofs = LittleLong (header.dirofs);
752 header.dirlen = LittleLong (header.dirlen);
754 if (header.dirlen % sizeof(dpackfile_t))
755 Sys_Error ("%s has an invalid directory size", packfile);
757 numpackfiles = header.dirlen / sizeof(dpackfile_t);
759 if (numpackfiles > MAX_FILES_IN_PACK)
760 Sys_Error ("%s has %i files", packfile, numpackfiles);
762 pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
763 pack->ignorecase = false; // PAK is case sensitive
764 strlcpy (pack->filename, packfile, sizeof (pack->filename));
765 pack->handle = packhandle;
767 pack->mempool = Mem_AllocPool(packfile);
768 pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
769 pack->next = packlist;
772 info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
773 fseek (packhandle, header.dirofs, SEEK_SET);
774 fread ((void *)info, 1, header.dirlen, packhandle);
776 // parse the directory
777 for (i = 0;i < numpackfiles;i++)
779 size_t offset = LittleLong (info[i].filepos);
780 size_t size = LittleLong (info[i].filelen);
782 FS_AddFileToPack (info[i].name, pack, offset, size, size, FILE_FLAG_TRUEOFFS);
787 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
796 Sets fs_gamedir, adds the directory to the head of the path,
797 then loads and adds pak1.pak pak2.pak ...
800 void FS_AddGameDirectory (char *dir)
802 stringlist_t *list, *current;
803 searchpath_t *search;
805 char pakfile[MAX_OSPATH];
807 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
810 // add the directory to the search path
811 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
812 strlcpy (search->filename, dir, sizeof (search->filename));
813 search->next = fs_searchpaths;
814 fs_searchpaths = search;
817 list = listdirectory(dir);
819 // add any PAK package in the directory
820 for (current = list;current;current = current->next)
822 if (matchpattern(current->text, "*.pak", true))
824 snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
825 pak = FS_LoadPackPAK (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);
838 // add any PK3 package in the director
839 for (current = list;current;current = current->next)
841 if (matchpattern(current->text, "*.pk3", true))
843 snprintf (pakfile, sizeof (pakfile), "%s/%s", dir, current->text);
844 pak = FS_LoadPackPK3 (pakfile);
847 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
849 search->next = fs_searchpaths;
850 fs_searchpaths = search;
853 Con_Printf("unable to load pak \"%s\"\n", pakfile);
858 // Unpacked files have the priority over packed files if AKVERSION is defined
860 // add the directory to the search path
861 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
862 strlcpy (search->filename, dir, sizeof (search->filename));
863 search->next = fs_searchpaths;
864 fs_searchpaths = search;
874 char *FS_FileExtension (const char *in)
876 static char exten[8];
877 const char *slash, *backslash, *colon, *dot, *separator;
880 slash = strrchr(in, '/');
881 backslash = strrchr(in, '\\');
882 colon = strrchr(in, ':');
883 dot = strrchr(in, '.');
885 if (separator < backslash)
886 separator = backslash;
887 if (separator < colon)
892 for (i = 0;i < 7 && dot[i];i++)
907 searchpath_t *search;
909 fs_mempool = Mem_AllocPool("file management");
910 pak_mempool = Mem_AllocPool("paks");
912 Cmd_AddCommand ("path", FS_Path_f);
913 Cmd_AddCommand ("dir", FS_Dir_f);
914 Cmd_AddCommand ("ls", FS_Ls_f);
916 strcpy(fs_basedir, ".");
921 // Overrides the system supplied base directory (under GAMENAME)
922 i = COM_CheckParm ("-basedir");
923 if (i && i < com_argc-1)
924 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
926 i = strlen (fs_basedir);
927 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
930 // start up with GAMENAME by default (id1)
931 strlcpy (com_modname, GAMENAME, sizeof (com_modname));
932 FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir));
936 strlcpy (com_modname, gamedirname, sizeof (com_modname));
937 FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname));
941 // Adds basedir/gamedir as an override game
942 i = COM_CheckParm ("-game");
943 if (i && i < com_argc-1)
946 strlcpy (com_modname, com_argv[i+1], sizeof (com_modname));
947 FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1]));
950 // -path <dir or packfile> [<dir or packfile>] ...
951 // Fully specifies the exact search path, overriding the generated one
952 i = COM_CheckParm ("-path");
956 fs_searchpaths = NULL;
957 while (++i < com_argc)
959 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
962 search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
963 if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak"))
965 search->pack = FS_LoadPackPAK (com_argv[i]);
967 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
969 else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3"))
971 search->pack = FS_LoadPackPK3 (com_argv[i]);
973 Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
976 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
977 search->next = fs_searchpaths;
978 fs_searchpaths = search;
988 Internal function used to create a qfile_t and open the relevant file on disk
991 static qfile_t* FS_SysOpen (const char* filepath, const char* mode)
995 file = Mem_Alloc (fs_mempool, sizeof (*file));
996 memset (file, 0, sizeof (*file));
998 file->stream = fopen (filepath, mode);
1014 qfile_t *FS_OpenRead (const char *path, int offs, int len)
1018 file = FS_SysOpen (path, "rb");
1021 Sys_Error ("Couldn't open %s", path);
1026 if (offs < 0 || len < 0)
1028 // We set fs_filesize here for normal files
1029 fseek (file->stream, 0, SEEK_END);
1030 fs_filesize = ftell (file->stream);
1031 fseek (file->stream, 0, SEEK_SET);
1036 fseek (file->stream, offs, SEEK_SET);
1038 file->flags |= FS_FLAG_PACKED;
1040 file->offset = offs;
1049 ====================
1052 Look for a file in the packages and in the filesystem
1054 Return the searchpath where the file was found (or NULL)
1055 and the file index in the package if relevant
1056 ====================
1058 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1060 searchpath_t *search;
1062 int (*strcmp_funct) (const char* str1, const char* str2);
1064 // search through the path, one element at a time
1065 for (search = fs_searchpaths;search;search = search->next)
1067 // is the element a pak file?
1070 size_t left, right, middle;
1073 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1075 // Look for the file (binary search)
1077 right = pak->numfiles;
1078 while (left != right)
1082 middle = (left + right - 1) / 2;
1083 diff = strcmp_funct (pak->files[middle].name, name);
1089 Sys_Printf("FS_FindFile: %s in %s\n",
1090 pak->files[middle].name, pak->filename);
1097 // If we're too far in the list
1106 char netpath[MAX_OSPATH];
1107 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name);
1108 if (FS_SysFileExists (netpath))
1111 Sys_Printf("FS_FindFile: %s\n", netpath);
1121 Sys_Printf("FS_FindFile: can't find %s\n", name);
1133 If the requested file is inside a packfile, a new qfile_t* will be opened
1139 qfile_t *FS_FOpenFile (const char *filename, qboolean quiet)
1141 searchpath_t *search;
1142 packfile_t *packfile;
1146 search = FS_FindFile (filename, &i, quiet);
1155 // Found in the filesystem?
1158 char netpath[MAX_OSPATH];
1159 snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
1160 return FS_OpenRead(netpath, -1, -1);
1163 // So, we found it in a package...
1164 packfile = &search->pack->files[i];
1166 // If we don't have the true offset, get it now
1167 if (! (packfile->flags & FILE_FLAG_TRUEOFFS))
1168 PK3_GetTrueFileOffset (packfile, search->pack);
1170 // No Zlib DLL = no compressed files
1171 if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED))
1173 Con_Printf("WARNING: can't open the compressed file %s\n"
1174 "You need the Zlib DLL to use compressed files\n",
1180 // open a new file in the pakfile
1181 file = FS_OpenRead (search->pack->filename, packfile->offset, packfile->packsize);
1182 fs_filesize = packfile->realsize;
1184 if (packfile->flags & FILE_FLAG_DEFLATED)
1188 file->flags |= FS_FLAG_DEFLATED;
1190 // We need some more variables
1191 ztk = Mem_Alloc (fs_mempool, sizeof (*file->z));
1193 ztk->real_length = packfile->realsize;
1195 // Initialize zlib stream
1196 ztk->zstream.next_in = ztk->input;
1197 ztk->zstream.avail_in = 0;
1199 /* From Zlib's "unzip.c":
1201 * windowBits is passed < 0 to tell that there is no zlib header.
1202 * Note that in this case inflate *requires* an extra "dummy" byte
1203 * after the compressed stream in order to complete decompression and
1204 * return Z_STREAM_END.
1205 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1206 * size of both compressed and uncompressed data
1208 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1209 Sys_Error ("inflate init error (file: %s)", filename);
1211 ztk->zstream.next_out = ztk->output;
1212 ztk->zstream.avail_out = sizeof (ztk->output);
1222 =============================================================================
1224 MAIN PUBLIC FUNCTIONS
1226 =============================================================================
1230 ====================
1233 Open a file. The syntax is the same as fopen
1234 ====================
1236 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet)
1238 // If the file is opened in "write" or "append" mode
1239 if (strchr (mode, 'w') || strchr (mode, 'a'))
1241 char real_path [MAX_OSPATH];
1243 // Open the file on disk directly
1244 snprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1246 // Create directories up to the file
1247 FS_CreatePath (real_path);
1249 return FS_SysOpen (real_path, mode);
1252 // Else, we look at the various search paths
1253 return FS_FOpenFile (filepath, quiet);
1258 ====================
1262 ====================
1264 int FS_Close (qfile_t* file)
1266 if (fclose (file->stream))
1271 qz_inflateEnd (&file->z->zstream);
1281 ====================
1284 Write "datasize" bytes into a file
1285 ====================
1287 size_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1289 return fwrite (data, 1, datasize, file->stream);
1294 ====================
1297 Read up to "buffersize" bytes from a file
1298 ====================
1300 size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1305 // Quick path for unpacked files
1306 if (! (file->flags & FS_FLAG_PACKED))
1307 return fread (buffer, 1, buffersize, file->stream);
1309 // If the file isn't compressed
1310 if (! (file->flags & FS_FLAG_DEFLATED))
1312 // We must take care to not read after the end of the file
1313 count = file->length - file->position;
1314 if (buffersize > count)
1317 nb = fread (buffer, 1, buffersize, file->stream);
1319 file->position += nb;
1323 // If the file is compressed, it's more complicated...
1326 // First, we copy as many bytes as we can from "output"
1327 if (ztk->out_ind < ztk->out_max)
1329 count = ztk->out_max - ztk->out_ind;
1331 nb = (buffersize > count) ? count : buffersize;
1332 memcpy (buffer, &ztk->output[ztk->out_ind], nb);
1334 file->position += nb;
1339 // We cycle through a few operations until we have inflated enough data
1340 while (nb < buffersize)
1342 // NOTE: at this point, "output" should always be empty
1344 // If "input" is also empty, we need to fill it
1345 if (ztk->in_ind == ztk->in_max)
1349 // If we are at the end of the file
1350 if (ztk->out_position == ztk->real_length)
1353 remain = file->length - ztk->in_position;
1354 count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain;
1355 fread (ztk->input, 1, count, file->stream);
1357 // Update indexes and counters
1359 ztk->in_max = count;
1360 ztk->in_position += count;
1363 // Now that we are sure we have compressed data available, we need to determine
1364 // if it's better to inflate it in "output" or directly in "buffer" (we are in this
1365 // case if we still need more bytes than "output" can contain)
1367 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1368 ztk->zstream.avail_in = ztk->in_max - ztk->in_ind;
1370 // If output will be able to contain at least 1 more byte than the data we need
1371 if (buffersize - nb < sizeof (ztk->output))
1375 // Inflate the data in "output"
1376 ztk->zstream.next_out = ztk->output;
1377 ztk->zstream.avail_out = sizeof (ztk->output);
1378 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1379 if (error != Z_OK && error != Z_STREAM_END)
1380 Sys_Error ("Can't inflate file");
1381 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1382 ztk->out_max = sizeof (ztk->output) - ztk->zstream.avail_out;
1383 ztk->out_position += ztk->out_max;
1385 // Copy the requested data in "buffer" (as much as we can)
1386 count = (buffersize - nb > ztk->out_max) ? ztk->out_max : buffersize - nb;
1387 memcpy (&((qbyte*)buffer)[nb], ztk->output, count);
1388 ztk->out_ind = count;
1391 // Else, we inflate directly in "buffer"
1396 // Inflate the data in "buffer"
1397 ztk->zstream.next_out = &((qbyte*)buffer)[nb];
1398 ztk->zstream.avail_out = buffersize - nb;
1399 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1400 if (error != Z_OK && error != Z_STREAM_END)
1401 Sys_Error ("Can't inflate file");
1402 ztk->in_ind = ztk->in_max - ztk->zstream.avail_in;
1404 // Invalidate the output data (for FS_Seek)
1408 // How much data did it inflate?
1409 count = buffersize - nb - ztk->zstream.avail_out;
1410 ztk->out_position += count;
1414 file->position += count;
1422 ====================
1425 Flush the file output stream
1426 ====================
1428 int FS_Flush (qfile_t* file)
1430 return fflush (file->stream);
1435 ====================
1438 Print a string into a file
1439 ====================
1441 int FS_Print(qfile_t* file, const char *msg)
1443 return FS_Write(file, msg, strlen(msg));
1447 ====================
1450 Print a string into a file
1451 ====================
1453 int FS_Printf(qfile_t* file, const char* format, ...)
1458 va_start (args, format);
1459 result = vfprintf (file->stream, format, args);
1467 ====================
1470 Get the next character of a file
1471 ====================
1473 int FS_Getc (qfile_t* file)
1477 if (FS_Read (file, &c, 1) != 1)
1485 ====================
1488 Move the position index in a file
1489 ====================
1491 int FS_Seek (qfile_t* file, long offset, int whence)
1493 // Quick path for unpacked files
1494 if (! (file->flags & FS_FLAG_PACKED))
1495 return fseek (file->stream, offset, whence);
1497 // Seeking in compressed files is more a hack than anything else,
1498 // but we need to support it, so here it is.
1499 if (file->flags & FS_FLAG_DEFLATED)
1501 ztoolkit_t *ztk = file->z;
1502 qbyte buffer [sizeof (ztk->output)]; // it's big to force inflating into buffer directly
1507 offset += file->position;
1514 offset += ztk->real_length;
1520 if (offset < 0 || offset > (long) ztk->real_length)
1523 // If we need to go back in the file
1524 if (offset <= (long) file->position)
1526 // If we still have the data we need in the output buffer
1527 if (file->position - offset <= ztk->out_ind)
1529 ztk->out_ind -= file->position - offset;
1530 file->position = offset;
1534 // Else, we restart from the beginning of the file
1537 ztk->in_position = 0;
1540 ztk->out_position = 0;
1542 fseek (file->stream, file->offset, SEEK_SET);
1544 // Reset the Zlib stream
1545 ztk->zstream.next_in = ztk->input;
1546 ztk->zstream.avail_in = 0;
1547 qz_inflateReset (&ztk->zstream);
1550 // Skip all data until we reach the requested offset
1551 while ((long) file->position < offset)
1553 size_t diff = offset - file->position;
1556 count = (diff > sizeof (buffer)) ? sizeof (buffer) : diff;
1557 len = FS_Read (file, buffer, count);
1565 // Packed files receive a special treatment too, because
1566 // we need to make sure it doesn't go outside of the file
1570 offset += file->position;
1577 offset += file->length;
1583 if (offset < 0 || offset > (long) file->length)
1586 if (fseek (file->stream, file->offset + offset, SEEK_SET) == -1)
1588 file->position = offset;
1594 ====================
1597 Give the current position in a file
1598 ====================
1600 long FS_Tell (qfile_t* file)
1602 if (file->flags & FS_FLAG_PACKED)
1603 return file->position;
1605 return ftell (file->stream);
1610 ====================
1613 Extract a line from a file
1614 ====================
1616 char* FS_Gets (qfile_t* file, char* buffer, int buffersize)
1620 // Quick path for unpacked files
1621 if (! (file->flags & FS_FLAG_PACKED))
1622 return fgets (buffer, buffersize, file->stream);
1624 for (ind = 0; ind < (size_t) buffersize - 1; ind++)
1626 int c = FS_Getc (file);
1641 buffer[ind + 1] = '\0';
1650 buffer[buffersize - 1] = '\0';
1659 Dynamic length version of fgets. DO NOT free the buffer.
1662 char *FS_Getline (qfile_t *file)
1664 static int size = 256;
1665 static char *buf = 0;
1670 buf = Mem_Alloc (fs_mempool, size);
1672 if (!FS_Gets (file, buf, size))
1676 while (buf[len - 1] != '\n' && buf[len - 1] != '\r')
1678 t = Mem_Alloc (fs_mempool, size + 256);
1679 memcpy(t, buf, size);
1683 if (!FS_Gets (file, buf + len, size - len))
1687 while ((len = strlen(buf)) && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1694 ====================
1697 Extract a line from a file
1698 ====================
1700 int FS_Eof (qfile_t* file)
1702 if (file->flags & FS_FLAG_PACKED)
1704 if (file->flags & FS_FLAG_DEFLATED)
1705 return (file->position == file->z->real_length);
1707 return (file->position == file->length);
1710 return feof (file->stream);
1718 Filename are relative to the quake directory.
1719 Always appends a 0 byte.
1722 qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet)
1727 // look for it in the filesystem or pack files
1728 h = FS_Open (path, "rb", quiet);
1732 buf = Mem_Alloc(pool, fs_filesize+1);
1734 Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize);
1736 ((qbyte *)buf)[fs_filesize] = 0;
1738 FS_Read (h, buf, fs_filesize);
1749 The filename will be prefixed by the current game directory
1752 qboolean FS_WriteFile (const char *filename, void *data, int len)
1756 handle = FS_Open (filename, "wb", false);
1759 Con_Printf("FS_WriteFile: failed on %s\n", filename);
1763 Con_DPrintf("FS_WriteFile: %s\n", filename);
1764 FS_Write (handle, data, len);
1771 =============================================================================
1773 OTHERS PUBLIC FUNCTIONS
1775 =============================================================================
1783 void FS_StripExtension (const char *in, char *out, size_t size_out)
1790 while (*in && size_out > 1)
1794 else if (*in == '/' || *in == '\\' || *in == ':')
1811 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
1815 // if path doesn't have a .EXT, append extension
1816 // (extension should include the .)
1817 src = path + strlen(path) - 1;
1819 while (*src != '/' && src != path)
1822 return; // it has an extension
1826 strlcat (path, extension, size_path);
1834 Look for a file in the packages and in the filesystem
1837 qboolean FS_FileExists (const char *filename)
1839 return (FS_FindFile (filename, NULL, true) != NULL);
1847 Look for a file in the filesystem only
1850 qboolean FS_SysFileExists (const char *path)
1855 f = fopen (path, "rb");
1866 if (stat (path,&buf) == -1)
1873 void FS_mkdir (const char *path)
1886 Allocate and fill a search structure with information on matching filenames.
1889 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
1892 searchpath_t *searchpath;
1894 int i, basepathlength, numfiles, numchars;
1895 stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
1896 const char *slash, *backslash, *colon, *separator;
1898 char netpath[MAX_OSPATH];
1899 char temp[MAX_OSPATH];
1901 while(!strncmp(pattern, "./", 2))
1903 while(!strncmp(pattern, ".\\", 2))
1910 slash = strrchr(pattern, '/');
1911 backslash = strrchr(pattern, '\\');
1912 colon = strrchr(pattern, ':');
1913 separator = pattern;
1914 if (separator < slash)
1916 if (separator < backslash)
1917 separator = backslash;
1918 if (separator < colon)
1920 basepathlength = separator - pattern;
1921 basepath = Z_Malloc(basepathlength + 1);
1923 memcpy(basepath, pattern, basepathlength);
1924 basepath[basepathlength] = 0;
1926 // search through the path, one element at a time
1927 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
1929 // is the element a pak file?
1930 if (searchpath->pack)
1932 // look through all the pak file elements
1933 pak = searchpath->pack;
1934 for (i = 0;i < pak->numfiles;i++)
1936 strcpy(temp, pak->files[i].name);
1939 if (matchpattern(temp, (char *)pattern, true))
1941 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1942 if (!strcmp(listtemp->text, temp))
1944 if (listtemp == NULL)
1946 listcurrent = stringlistappend(listcurrent, temp);
1947 if (liststart == NULL)
1948 liststart = listcurrent;
1950 Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp);
1953 // strip off one path element at a time until empty
1954 // this way directories are added to the listing if they match the pattern
1955 slash = strrchr(temp, '/');
1956 backslash = strrchr(temp, '\\');
1957 colon = strrchr(temp, ':');
1959 if (separator < slash)
1961 if (separator < backslash)
1962 separator = backslash;
1963 if (separator < colon)
1965 *((char *)separator) = 0;
1971 // get a directory listing and look at each name
1972 snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath);
1973 if ((dir = listdirectory(netpath)))
1975 for (dirfile = dir;dirfile;dirfile = dirfile->next)
1977 snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text);
1978 if (matchpattern(temp, (char *)pattern, true))
1980 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
1981 if (!strcmp(listtemp->text, temp))
1983 if (listtemp == NULL)
1985 listcurrent = stringlistappend(listcurrent, temp);
1986 if (liststart == NULL)
1987 liststart = listcurrent;
1989 Sys_Printf("SearchDirFile: %s\n", temp);
2000 liststart = stringlistsort(liststart);
2003 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2006 numchars += strlen(listtemp->text) + 1;
2008 search = Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2009 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2010 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2011 search->numfilenames = numfiles;
2014 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2016 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2017 strcpy(search->filenames[numfiles], listtemp->text);
2019 numchars += strlen(listtemp->text) + 1;
2022 stringlistfree(liststart);
2029 void FS_FreeSearch(fssearch_t *search)
2034 extern int con_linewidth;
2035 int FS_ListDirectory(const char *pattern, int oneperline)
2046 search = FS_Search(pattern, true, false);
2049 numfiles = search->numfilenames;
2052 // FIXME: the names could be added to one column list and then
2053 // gradually shifted into the next column if they fit, and then the
2054 // next to make a compact variable width listing but it's a lot more
2056 // find width for columns
2058 for (i = 0;i < numfiles;i++)
2060 l = strlen(search->filenames[i]);
2061 if (columnwidth < l)
2064 // count the spacing character
2066 // calculate number of columns
2067 numcolumns = con_linewidth / columnwidth;
2068 // don't bother with the column printing if it's only one column
2069 if (numcolumns >= 2)
2071 numlines = (numfiles + numcolumns - 1) / numcolumns;
2072 for (i = 0;i < numlines;i++)
2075 for (k = 0;k < numcolumns;k++)
2077 l = i * numcolumns + k;
2080 name = search->filenames[l];
2081 for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++)
2082 linebuf[linebufpos++] = name[j];
2083 // space out name unless it's the last on the line
2084 if (k < (numcolumns - 1) && l < (numfiles - 1))
2085 for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++)
2086 linebuf[linebufpos++] = ' ';
2089 linebuf[linebufpos] = 0;
2090 Con_Printf("%s\n", linebuf);
2097 for (i = 0;i < numfiles;i++)
2098 Con_Printf("%s\n", search->filenames[i]);
2099 FS_FreeSearch(search);
2103 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2105 const char *pattern;
2108 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2111 if (Cmd_Argc() == 2)
2112 pattern = Cmd_Argv(1);
2115 if (!FS_ListDirectory(pattern, oneperline))
2116 Con_Print("No files found.\n");
2121 FS_ListDirectoryCmd("dir", true);
2126 FS_ListDirectoryCmd("ls", false);