4 Copyright (C) 2003-2006 Mathieu Olivier
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to:
20 Free Software Foundation, Inc.
21 59 Temple Place - Suite 330
22 Boston, MA 02111-1307, USA
25 // on UNIX platforms we need to define this so that video saving does not cause a SIGFSZ (file size) signal when a video clip exceeds 2GB
26 #define _FILE_OFFSET_BITS 64
38 # include <sys/stat.h>
44 // Win32 requires us to add O_BINARY, but the other OSes don't have it
49 // In case the system doesn't support the O_NONBLOCK flag
57 All of Quake's data access is through a hierchal file system, but the contents
58 of the file system can be transparently merged from several sources.
60 The "base directory" is the path to the directory holding the quake.exe and
61 all game directories. The sys_* files pass this to host_init in
62 quakeparms_t->basedir. This can be overridden with the "-basedir" command
63 line parm to allow code debugging in a different directory. The base
64 directory is only used during filesystem initialization.
66 The "game directory" is the first tree on the search path and directory that
67 all generated files (savegames, screenshots, demos, config files) will be
68 saved to. This can be overridden with the "-game" command line parameter.
69 The game directory can never be changed while quake is executing. This is a
70 precaution against having a malicious server instruct clients to write files
71 over areas they shouldn't.
77 =============================================================================
81 =============================================================================
84 // Magic numbers of a ZIP file (big-endian format)
85 #define ZIP_DATA_HEADER 0x504B0304 // "PK\3\4"
86 #define ZIP_CDIR_HEADER 0x504B0102 // "PK\1\2"
87 #define ZIP_END_HEADER 0x504B0506 // "PK\5\6"
89 // Other constants for ZIP files
90 #define ZIP_MAX_COMMENTS_SIZE ((unsigned short)0xFFFF)
91 #define ZIP_END_CDIR_SIZE 22
92 #define ZIP_CDIR_CHUNK_BASE_SIZE 46
93 #define ZIP_LOCAL_CHUNK_BASE_SIZE 30
95 // Zlib constants (from zlib.h)
96 #define Z_SYNC_FLUSH 2
99 #define Z_STREAM_END 1
100 #define ZLIB_VERSION "1.2.3"
102 // Uncomment the following line if the zlib DLL you have still uses
103 // the 1.1.x series calling convention on Win32 (WINAPI)
104 //#define ZLIB_USES_WINAPI
108 =============================================================================
112 =============================================================================
115 // Zlib stream (from zlib.h)
116 // Warning: some pointers we don't use directly have
117 // been cast to "void*" for a matter of simplicity
120 unsigned char *next_in; // next input byte
121 unsigned int avail_in; // number of bytes available at next_in
122 unsigned long total_in; // total nb of input bytes read so far
124 unsigned char *next_out; // next output byte should be put there
125 unsigned int avail_out; // remaining free space at next_out
126 unsigned long total_out; // total nb of bytes output so far
128 char *msg; // last error message, NULL if no error
129 void *state; // not visible by applications
131 void *zalloc; // used to allocate the internal state
132 void *zfree; // used to free the internal state
133 void *opaque; // private data object passed to zalloc and zfree
135 int data_type; // best guess about the data type: ascii or binary
136 unsigned long adler; // adler32 value of the uncompressed data
137 unsigned long reserved; // reserved for future use
141 // inside a package (PAK or PK3)
142 #define QFILE_FLAG_PACKED (1 << 0)
143 // file is compressed using the deflate algorithm (PK3 only)
144 #define QFILE_FLAG_DEFLATED (1 << 1)
146 #define FILE_BUFF_SIZE 2048
150 size_t comp_length; // length of the compressed file
151 size_t in_ind, in_len; // input buffer current index and length
152 size_t in_position; // position in the compressed file
153 unsigned char input [FILE_BUFF_SIZE];
159 int handle; // file descriptor
160 fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
161 fs_offset_t position; // current position in the file
162 fs_offset_t offset; // offset into the package (0 if external file)
163 int ungetc; // single stored character from ungetc, cleared to EOF when read
166 fs_offset_t buff_ind, buff_len; // buffer current index and length
167 unsigned char buff [FILE_BUFF_SIZE];
174 // ------ PK3 files on disk ------ //
176 // You can get the complete ZIP format description from PKWARE website
178 typedef struct pk3_endOfCentralDir_s
180 unsigned int signature;
181 unsigned short disknum;
182 unsigned short cdir_disknum; // number of the disk with the start of the central directory
183 unsigned short localentries; // number of entries in the central directory on this disk
184 unsigned short nbentries; // total number of entries in the central directory on this disk
185 unsigned int cdir_size; // size of the central directory
186 unsigned int cdir_offset; // with respect to the starting disk number
187 unsigned short comment_size;
188 } pk3_endOfCentralDir_t;
191 // ------ PAK files on disk ------ //
192 typedef struct dpackfile_s
195 int filepos, filelen;
198 typedef struct dpackheader_s
206 // Packages in memory
207 // the offset in packfile_t is the true contents offset
208 #define PACKFILE_FLAG_TRUEOFFS (1 << 0)
209 // file compressed using the deflate algorithm
210 #define PACKFILE_FLAG_DEFLATED (1 << 1)
212 typedef struct packfile_s
214 char name [MAX_QPATH];
217 fs_offset_t packsize; // size in the package
218 fs_offset_t realsize; // real file size (uncompressed)
221 typedef struct pack_s
223 char filename [MAX_OSPATH];
225 int ignorecase; // PK3 ignores case
232 // Search paths for files (including packages)
233 typedef struct searchpath_s
235 // only one of filename / pack will be used
236 char filename[MAX_OSPATH];
238 struct searchpath_s *next;
243 =============================================================================
247 =============================================================================
253 static const char *FS_FileExtension (const char *in);
254 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
255 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
256 fs_offset_t offset, fs_offset_t packsize,
257 fs_offset_t realsize, int flags);
261 =============================================================================
265 =============================================================================
268 mempool_t *fs_mempool;
270 pack_t *packlist = NULL;
272 searchpath_t *fs_searchpaths = NULL;
274 #define MAX_FILES_IN_PACK 65536
276 char fs_gamedir[MAX_OSPATH];
277 char fs_basedir[MAX_OSPATH];
279 qboolean fs_modified; // set true if using non-id files
281 cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running)"};
285 =============================================================================
287 PRIVATE FUNCTIONS - PK3 HANDLING
289 =============================================================================
292 // Functions exported from zlib
293 #if defined(WIN32) && defined(ZLIB_USES_WINAPI)
294 # define ZEXPORT WINAPI
299 static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush);
300 static int (ZEXPORT *qz_inflateEnd) (z_stream* strm);
301 static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size);
302 static int (ZEXPORT *qz_inflateReset) (z_stream* strm);
304 #define qz_inflateInit2(strm, windowBits) \
305 qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
307 static dllfunction_t zlibfuncs[] =
309 {"inflate", (void **) &qz_inflate},
310 {"inflateEnd", (void **) &qz_inflateEnd},
311 {"inflateInit2_", (void **) &qz_inflateInit2_},
312 {"inflateReset", (void **) &qz_inflateReset},
316 // Handle for Zlib DLL
317 static dllhandle_t zlib_dll = NULL;
327 void PK3_CloseLibrary (void)
329 Sys_UnloadLibrary (&zlib_dll);
337 Try to load the Zlib DLL
340 qboolean PK3_OpenLibrary (void)
342 const char* dllnames [] =
347 # ifdef ZLIB_USES_WINAPI
353 #elif defined(MACOSX)
367 if (! Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs))
369 Con_Printf ("Compressed files support disabled\n");
373 Con_Printf ("Compressed files support enabled\n");
380 PK3_GetEndOfCentralDir
382 Extract the end of the central directory from a PK3 package
385 qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
387 fs_offset_t filesize, maxsize;
388 unsigned char *buffer, *ptr;
391 // Get the package size
392 filesize = lseek (packhandle, 0, SEEK_END);
393 if (filesize < ZIP_END_CDIR_SIZE)
396 // Load the end of the file in memory
397 if (filesize < ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE)
400 maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE;
401 buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize);
402 lseek (packhandle, filesize - maxsize, SEEK_SET);
403 if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize)
409 // Look for the end of central dir signature around the end of the file
410 maxsize -= ZIP_END_CDIR_SIZE;
411 ptr = &buffer[maxsize];
413 while (BuffBigLong (ptr) != ZIP_END_HEADER)
425 memcpy (eocd, ptr, ZIP_END_CDIR_SIZE);
426 eocd->signature = LittleLong (eocd->signature);
427 eocd->disknum = LittleShort (eocd->disknum);
428 eocd->cdir_disknum = LittleShort (eocd->cdir_disknum);
429 eocd->localentries = LittleShort (eocd->localentries);
430 eocd->nbentries = LittleShort (eocd->nbentries);
431 eocd->cdir_size = LittleLong (eocd->cdir_size);
432 eocd->cdir_offset = LittleLong (eocd->cdir_offset);
433 eocd->comment_size = LittleShort (eocd->comment_size);
445 Extract the file list from a PK3 file
448 int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
450 unsigned char *central_dir, *ptr;
452 fs_offset_t remaining;
454 // Load the central directory in memory
455 central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
456 lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
457 read (pack->handle, central_dir, eocd->cdir_size);
459 // Extract the files properties
460 // The parsing is done "by hand" because some fields have variable sizes and
461 // the constant part isn't 4-bytes aligned, which makes the use of structs difficult
462 remaining = eocd->cdir_size;
465 for (ind = 0; ind < eocd->nbentries; ind++)
467 fs_offset_t namesize, count;
469 // Checking the remaining size
470 if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE)
472 Mem_Free (central_dir);
475 remaining -= ZIP_CDIR_CHUNK_BASE_SIZE;
478 if (BuffBigLong (ptr) != ZIP_CDIR_HEADER)
480 Mem_Free (central_dir);
484 namesize = BuffLittleShort (&ptr[28]); // filename length
486 // Check encryption, compression, and attributes
487 // 1st uint8 : general purpose bit flag
488 // Check bits 0 (encryption), 3 (data descriptor after the file), and 5 (compressed patched data (?))
489 // 2nd uint8 : external file attributes
490 // Check bits 3 (file is a directory) and 5 (file is a volume (?))
491 if ((ptr[8] & 0x29) == 0 && (ptr[38] & 0x18) == 0)
493 // Still enough bytes for the name?
494 if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
496 Mem_Free (central_dir);
500 // WinZip doesn't use the "directory" attribute, so we need to check the name directly
501 if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/')
503 char filename [sizeof (pack->files[0].name)];
504 fs_offset_t offset, packsize, realsize;
507 // Extract the name (strip it if necessary)
508 namesize = min(namesize, (int)sizeof (filename) - 1);
509 memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize);
510 filename[namesize] = '\0';
512 if (BuffLittleShort (&ptr[10]))
513 flags = PACKFILE_FLAG_DEFLATED;
516 offset = BuffLittleLong (&ptr[42]);
517 packsize = BuffLittleLong (&ptr[20]);
518 realsize = BuffLittleLong (&ptr[24]);
519 FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags);
523 // Skip the name, additionnal field, and comment
524 // 1er uint16 : extra field length
525 // 2eme uint16 : file comment length
526 count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
527 ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
531 // If the package is empty, central_dir is NULL here
532 if (central_dir != NULL)
533 Mem_Free (central_dir);
534 return pack->numfiles;
542 Create a package entry associated with a PK3 file
545 pack_t *FS_LoadPackPK3 (const char *packfile)
548 pk3_endOfCentralDir_t eocd;
552 packhandle = open (packfile, O_RDONLY | O_BINARY);
556 if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
558 Con_Printf ("%s is not a PK3 file\n", packfile);
563 // Multi-volume ZIP archives are NOT allowed
564 if (eocd.disknum != 0 || eocd.cdir_disknum != 0)
566 Con_Printf ("%s is a multi-volume ZIP archive\n", packfile);
571 // We only need to do this test if MAX_FILES_IN_PACK is lesser than 65535
572 // since eocd.nbentries is an unsigned 16 bits integer
573 #if MAX_FILES_IN_PACK < 65535
574 if (eocd.nbentries > MAX_FILES_IN_PACK)
576 Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries);
582 // Create a package structure in memory
583 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
584 pack->ignorecase = true; // PK3 ignores case
585 strlcpy (pack->filename, packfile, sizeof (pack->filename));
586 pack->handle = packhandle;
587 pack->numfiles = eocd.nbentries;
588 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
589 pack->next = packlist;
592 real_nb_files = PK3_BuildFileList (pack, &eocd);
593 if (real_nb_files < 0)
595 Con_Printf ("%s is not a valid PK3 file\n", packfile);
601 Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files);
608 PK3_GetTrueFileOffset
610 Find where the true file data offset is
613 qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
615 unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
619 if (pfile->flags & PACKFILE_FLAG_TRUEOFFS)
622 // Load the local file description
623 lseek (pack->handle, pfile->offset, SEEK_SET);
624 count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
625 if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
627 Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename);
631 // Skip name and extra field
632 pfile->offset += BuffLittleShort (&buffer[26]) + BuffLittleShort (&buffer[28]) + ZIP_LOCAL_CHUNK_BASE_SIZE;
634 pfile->flags |= PACKFILE_FLAG_TRUEOFFS;
640 =============================================================================
642 OTHER PRIVATE FUNCTIONS
644 =============================================================================
652 Add a file to the list of files contained into a package
655 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
656 fs_offset_t offset, fs_offset_t packsize,
657 fs_offset_t realsize, int flags)
659 int (*strcmp_funct) (const char* str1, const char* str2);
660 int left, right, middle;
663 strcmp_funct = pack->ignorecase ? strcasecmp : strcmp;
665 // Look for the slot we should put that file into (binary search)
667 right = pack->numfiles - 1;
668 while (left <= right)
672 middle = (left + right) / 2;
673 diff = strcmp_funct (pack->files[middle].name, name);
675 // If we found the file, there's a problem
677 Con_Printf ("Package %s contains the file %s several times\n", pack->filename, name);
679 // If we're too far in the list
686 // We have to move the right of the list by one slot to free the one we need
687 pfile = &pack->files[left];
688 memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
691 strlcpy (pfile->name, name, sizeof (pfile->name));
692 pfile->offset = offset;
693 pfile->packsize = packsize;
694 pfile->realsize = realsize;
695 pfile->flags = flags;
705 Only used for FS_Open.
708 void FS_CreatePath (char *path)
712 for (ofs = path+1 ; *ofs ; ofs++)
714 if (*ofs == '/' || *ofs == '\\')
716 // create the directory
732 void FS_Path_f (void)
736 Con_Print("Current search path:\n");
737 for (s=fs_searchpaths ; s ; s=s->next)
740 Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
742 Con_Printf("%s\n", s->filename);
751 Takes an explicit (not game tree related) path to a pak file.
753 Loads the header and directory, adding the files at the beginning
754 of the list so they override previous pack files.
757 pack_t *FS_LoadPackPAK (const char *packfile)
759 dpackheader_t header;
765 packhandle = open (packfile, O_RDONLY | O_BINARY);
768 read (packhandle, (void *)&header, sizeof(header));
769 if (memcmp(header.id, "PACK", 4))
771 Con_Printf ("%s is not a packfile\n", packfile);
775 header.dirofs = LittleLong (header.dirofs);
776 header.dirlen = LittleLong (header.dirlen);
778 if (header.dirlen % sizeof(dpackfile_t))
780 Con_Printf ("%s has an invalid directory size\n", packfile);
785 numpackfiles = header.dirlen / sizeof(dpackfile_t);
787 if (numpackfiles > MAX_FILES_IN_PACK)
789 Con_Printf ("%s has %i files\n", packfile, numpackfiles);
794 info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
795 lseek (packhandle, header.dirofs, SEEK_SET);
796 if(header.dirlen != read (packhandle, (void *)info, header.dirlen))
798 Con_Printf("%s is an incomplete PAK, not loading\n", packfile);
804 pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
805 pack->ignorecase = false; // PAK is case sensitive
806 strlcpy (pack->filename, packfile, sizeof (pack->filename));
807 pack->handle = packhandle;
809 pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
810 pack->next = packlist;
813 // parse the directory
814 for (i = 0;i < numpackfiles;i++)
816 fs_offset_t offset = LittleLong (info[i].filepos);
817 fs_offset_t size = LittleLong (info[i].filelen);
819 FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
824 Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
832 Adds the given pack to the search path.
833 The pack type is autodetected by the file extension.
835 Returns true if the file was successfully added to the
836 search path or if it was already included.
838 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
842 static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
844 searchpath_t *search;
846 const char *ext = FS_FileExtension(pakfile);
848 for(search = fs_searchpaths; search; search = search->next)
850 if(search->pack && !strcasecmp(search->pack->filename, pakfile))
853 *already_loaded = true;
854 return true; // already loaded
859 *already_loaded = false;
861 if(!strcasecmp(ext, "pak"))
862 pak = FS_LoadPackPAK (pakfile);
863 else if(!strcasecmp(ext, "pk3"))
864 pak = FS_LoadPackPK3 (pakfile);
866 Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
872 // find the first item whose next one is a pack or NULL
873 searchpath_t *insertion_point = 0;
874 if(fs_searchpaths && !fs_searchpaths->pack)
876 insertion_point = fs_searchpaths;
879 if(!insertion_point->next)
881 if(insertion_point->next->pack)
883 insertion_point = insertion_point->next;
886 // If insertion_point is NULL, this means that either there is no
887 // item in the list yet, or that the very first item is a pack. In
888 // that case, we want to insert at the beginning...
891 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
893 search->next = fs_searchpaths;
894 fs_searchpaths = search;
897 // otherwise we want to append directly after insertion_point.
899 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
901 search->next = insertion_point->next;
902 insertion_point->next = search;
907 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
909 search->next = fs_searchpaths;
910 fs_searchpaths = search;
916 Con_Printf("unable to load pak \"%s\"\n", pakfile);
926 Adds the given pack to the search path and searches for it in the game path.
927 The pack type is autodetected by the file extension.
929 Returns true if the file was successfully added to the
930 search path or if it was already included.
932 If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
936 qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
938 char fullpath[MAX_QPATH];
940 searchpath_t *search;
943 *already_loaded = false;
945 // then find the real name...
946 search = FS_FindFile(pakfile, &index, true);
947 if(!search || search->pack)
949 Con_Printf("could not find pak \"%s\"\n", pakfile);
953 dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
955 return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs);
963 Sets fs_gamedir, adds the directory to the head of the path,
964 then loads and adds pak1.pak pak2.pak ...
967 void FS_AddGameDirectory (const char *dir)
969 stringlist_t *list, *current;
970 searchpath_t *search;
971 char pakfile[MAX_OSPATH];
973 strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
975 list = listdirectory(dir);
977 // add any PAK package in the directory
978 for (current = list;current;current = current->next)
980 if (!strcasecmp(FS_FileExtension(current->text), "pak"))
982 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text);
983 FS_AddPack_Fullpath(pakfile, NULL, false);
987 // add any PK3 package in the director
988 for (current = list;current;current = current->next)
990 if (!strcasecmp(FS_FileExtension(current->text), "pk3"))
992 dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text);
993 FS_AddPack_Fullpath(pakfile, NULL, false);
998 // Add the directory to the search path
999 // (unpacked files have the priority over packed files)
1000 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1001 strlcpy (search->filename, dir, sizeof (search->filename));
1002 search->next = fs_searchpaths;
1003 fs_searchpaths = search;
1012 void FS_AddGameHierarchy (const char *dir)
1015 const char *homedir;
1018 // Add the common game directory
1019 FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
1022 // Add the personal game directory
1023 homedir = getenv ("HOME");
1024 if (homedir != NULL && homedir[0] != '\0')
1025 FS_AddGameDirectory (va("%s/.%s/%s/", homedir, gameuserdirname, dir));
1035 static const char *FS_FileExtension (const char *in)
1037 const char *separator, *backslash, *colon, *dot;
1039 separator = strrchr(in, '/');
1040 backslash = strrchr(in, '\\');
1041 if (!separator || separator < backslash)
1042 separator = backslash;
1043 colon = strrchr(in, ':');
1044 if (!separator || separator < colon)
1047 dot = strrchr(in, '.');
1048 if (dot == NULL || (separator && (dot < separator)))
1063 searchpath_t *search;
1065 fs_mempool = Mem_AllocPool("file management", 0, NULL);
1067 strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
1069 // If the base directory is explicitly defined by the compilation process
1070 #ifdef DP_FS_BASEDIR
1071 strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
1073 strlcpy(fs_basedir, "", sizeof(fs_basedir));
1076 // FIXME: is there a better way to find the directory outside the .app?
1077 if (strstr(com_argv[0], ".app/"))
1081 split = strstr(com_argv[0], ".app/");
1082 while (split > com_argv[0] && *split != '/')
1084 strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
1085 fs_basedir[split - com_argv[0]] = 0;
1093 // Overrides the system supplied base directory (under GAMENAME)
1094 // 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)
1095 i = COM_CheckParm ("-basedir");
1096 if (i && i < com_argc-1)
1098 strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir));
1099 i = (int)strlen (fs_basedir);
1100 if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
1101 fs_basedir[i-1] = 0;
1104 // add a path separator to the end of the basedir if it lacks one
1105 if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
1106 strlcat(fs_basedir, "/", sizeof(fs_basedir));
1108 // -path <dir or packfile> [<dir or packfile>] ...
1109 // Fully specifies the exact search path, overriding the generated one
1110 // 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)
1111 i = COM_CheckParm ("-path");
1115 while (++i < com_argc)
1117 if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
1120 if(!FS_AddPack_Fullpath(com_argv[i], NULL, false))
1122 search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
1123 strlcpy (search->filename, com_argv[i], sizeof (search->filename));
1124 search->next = fs_searchpaths;
1125 fs_searchpaths = search;
1131 // add the game-specific paths
1132 // gamedirname1 (typically id1)
1133 FS_AddGameHierarchy (gamedirname1);
1135 // add the game-specific path, if any
1139 FS_AddGameHierarchy (gamedirname2);
1142 // set the com_modname (reported in server info)
1143 strlcpy(com_modname, gamedirname1, sizeof(com_modname));
1146 // Adds basedir/gamedir as an override game
1147 // LordHavoc: now supports multiple -game directories
1148 for (i = 1;i < com_argc;i++)
1152 if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
1156 FS_AddGameHierarchy (com_argv[i]);
1157 // update the com_modname
1158 strlcpy (com_modname, com_argv[i], sizeof (com_modname));
1162 // If "-condebug" is in the command line, remove the previous log file
1163 if (COM_CheckParm ("-condebug") != 0)
1164 unlink (va("%s/qconsole.log", fs_gamedir));
1167 // look for the pop.lmp file and set registered to true if it is found
1168 if (gamemode == GAME_NORMAL && !FS_FileExists("gfx/pop.lmp"))
1171 Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
1173 Con_Print("Playing shareware version.\n");
1177 Cvar_Set ("registered", "1");
1178 if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
1179 Con_Print("Playing registered version.\n");
1182 // set the default screenshot name to either the mod name or the
1183 // gamemode screenshot name
1185 Cvar_SetQuick (&scr_screenshot_name, com_modname);
1187 Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
1190 void FS_Init_Commands(void)
1192 Cvar_RegisterVariable (&scr_screenshot_name);
1194 Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)");
1195 Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
1196 Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
1204 void FS_Shutdown (void)
1206 Mem_FreePool (&fs_mempool);
1210 ====================
1213 Internal function used to create a qfile_t and open the relevant non-packed file on disk
1214 ====================
1216 static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
1222 // Parse the mode string
1231 opt = O_CREAT | O_TRUNC;
1235 opt = O_CREAT | O_APPEND;
1238 Con_Printf ("FS_SysOpen(%s, %s): invalid mode\n", filepath, mode);
1241 for (ind = 1; mode[ind] != '\0'; ind++)
1252 Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
1253 filepath, mode, mode[ind]);
1260 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1261 memset (file, 0, sizeof (*file));
1264 file->handle = open (filepath, mod | opt, 0666);
1265 if (file->handle < 0)
1271 file->real_length = lseek (file->handle, 0, SEEK_END);
1273 // For files opened in append mode, we start at the end of the file
1275 file->position = file->real_length;
1277 lseek (file->handle, 0, SEEK_SET);
1287 Open a packed file using its package file descriptor
1290 qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
1296 pfile = &pack->files[pack_ind];
1298 // If we don't have the true offset, get it now
1299 if (! (pfile->flags & PACKFILE_FLAG_TRUEOFFS))
1300 if (!PK3_GetTrueFileOffset (pfile, pack))
1303 // No Zlib DLL = no compressed files
1304 if (!zlib_dll && (pfile->flags & PACKFILE_FLAG_DEFLATED))
1306 Con_Printf("WARNING: can't open the compressed file %s\n"
1307 "You need the Zlib DLL to use compressed files\n",
1312 // LordHavoc: lseek affects all duplicates of a handle so we do it before
1313 // the dup() call to avoid having to close the dup_handle on error here
1314 if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
1316 Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
1317 pfile->name, pack->filename, pfile->offset);
1321 dup_handle = dup (pack->handle);
1324 Con_Printf ("FS_OpenPackedFile: can't dup package's handle (pack: %s)\n", pack->filename);
1328 file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
1329 memset (file, 0, sizeof (*file));
1330 file->handle = dup_handle;
1331 file->flags = QFILE_FLAG_PACKED;
1332 file->real_length = pfile->realsize;
1333 file->offset = pfile->offset;
1337 if (pfile->flags & PACKFILE_FLAG_DEFLATED)
1341 file->flags |= QFILE_FLAG_DEFLATED;
1343 // We need some more variables
1344 ztk = (ztoolkit_t *)Mem_Alloc (fs_mempool, sizeof (*ztk));
1346 ztk->comp_length = pfile->packsize;
1348 // Initialize zlib stream
1349 ztk->zstream.next_in = ztk->input;
1350 ztk->zstream.avail_in = 0;
1352 /* From Zlib's "unzip.c":
1354 * windowBits is passed < 0 to tell that there is no zlib header.
1355 * Note that in this case inflate *requires* an extra "dummy" byte
1356 * after the compressed stream in order to complete decompression and
1357 * return Z_STREAM_END.
1358 * In unzip, i don't wait absolutely Z_STREAM_END because I known the
1359 * size of both compressed and uncompressed data
1361 if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK)
1363 Con_Printf ("FS_OpenPackedFile: inflate init error (file: %s)\n", pfile->name);
1369 ztk->zstream.next_out = file->buff;
1370 ztk->zstream.avail_out = sizeof (file->buff);
1379 ====================
1382 Return true if the path should be rejected due to one of the following:
1383 1: path elements that are non-portable
1384 2: path elements that would allow access to files outside the game directory,
1385 or are just not a good idea for a mod to be using.
1386 ====================
1388 int FS_CheckNastyPath (const char *path)
1390 // Windows: don't allow \ in filenames (windows-only), period.
1391 // (on Windows \ is a directory separator, but / is also supported)
1392 if (strstr(path, "\\"))
1393 return 1; // non-portable
1395 // Mac: don't allow Mac-only filenames - : is a directory separator
1396 // instead of /, but we rely on / working already, so there's no reason to
1397 // support a Mac-only path
1398 // Amiga and Windows: : tries to go to root of drive
1399 if (strstr(path, ":"))
1400 return 1; // non-portable attempt to go to root of drive
1402 // Amiga: // is parent directory
1403 if (strstr(path, "//"))
1404 return 1; // non-portable attempt to go to parent directory
1406 // all: don't allow going to current directory (./) or parent directory (../ or /../)
1407 if (strstr(path, "./"))
1408 return 2; // attempt to go outside the game directory
1410 // Windows and UNIXes: don't allow absolute paths
1412 return 2; // attempt to go outside the game directory
1414 // after all these checks we're pretty sure it's a / separated filename
1415 // and won't do much if any harm
1421 ====================
1424 Look for a file in the packages and in the filesystem
1426 Return the searchpath where the file was found (or NULL)
1427 and the file index in the package if relevant
1428 ====================
1430 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
1432 searchpath_t *search;
1435 // search through the path, one element at a time
1436 for (search = fs_searchpaths;search;search = search->next)
1438 // is the element a pak file?
1441 int (*strcmp_funct) (const char* str1, const char* str2);
1442 int left, right, middle;
1445 strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
1447 // Look for the file (binary search)
1449 right = pak->numfiles - 1;
1450 while (left <= right)
1454 middle = (left + right) / 2;
1455 diff = strcmp_funct (pak->files[middle].name, name);
1460 if (!quiet && developer.integer >= 10)
1461 Con_Printf("FS_FindFile: %s in %s\n",
1462 pak->files[middle].name, pak->filename);
1469 // If we're too far in the list
1478 char netpath[MAX_OSPATH];
1479 dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
1480 if (FS_SysFileExists (netpath))
1482 if (!quiet && developer.integer >= 10)
1483 Con_Printf("FS_FindFile: %s\n", netpath);
1492 if (!quiet && developer.integer >= 10)
1493 Con_Printf("FS_FindFile: can't find %s\n", name);
1505 Look for a file in the search paths and open it in read-only mode
1508 qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
1510 searchpath_t *search;
1513 search = FS_FindFile (filename, &pack_ind, quiet);
1519 // Found in the filesystem?
1522 char path [MAX_OSPATH];
1523 dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
1524 return FS_SysOpen (path, "rb", nonblocking);
1527 // So, we found it in a package...
1528 return FS_OpenPackedFile (search->pack, pack_ind);
1533 =============================================================================
1535 MAIN PUBLIC FUNCTIONS
1537 =============================================================================
1541 ====================
1544 Open a file. The syntax is the same as fopen
1545 ====================
1547 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
1549 if (FS_CheckNastyPath(filepath))
1551 Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
1555 // If the file is opened in "write", "append", or "read/write" mode
1556 if (mode[0] == 'w' || mode[0] == 'a' || strchr (mode, '+'))
1558 char real_path [MAX_OSPATH];
1560 // Open the file on disk directly
1561 dpsnprintf (real_path, sizeof (real_path), "%s/%s", fs_gamedir, filepath);
1563 // Create directories up to the file
1564 FS_CreatePath (real_path);
1566 return FS_SysOpen (real_path, mode, nonblocking);
1568 // Else, we look at the various search paths and open the file in read-only mode
1570 return FS_OpenReadFile (filepath, quiet, nonblocking);
1575 ====================
1579 ====================
1581 int FS_Close (qfile_t* file)
1583 if (close (file->handle))
1588 qz_inflateEnd (&file->ztk->zstream);
1589 Mem_Free (file->ztk);
1598 ====================
1601 Write "datasize" bytes into a file
1602 ====================
1604 fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
1608 // If necessary, seek to the exact file position we're supposed to be
1609 if (file->buff_ind != file->buff_len)
1610 lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
1612 // Purge cached data
1615 // Write the buffer and update the position
1616 result = write (file->handle, data, (fs_offset_t)datasize);
1617 file->position = lseek (file->handle, 0, SEEK_CUR);
1618 if (file->real_length < file->position)
1619 file->real_length = file->position;
1629 ====================
1632 Read up to "buffersize" bytes from a file
1633 ====================
1635 fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize)
1637 fs_offset_t count, done;
1639 if (buffersize == 0)
1642 // Get rid of the ungetc character
1643 if (file->ungetc != EOF)
1645 ((char*)buffer)[0] = file->ungetc;
1653 // First, we copy as many bytes as we can from "buff"
1654 if (file->buff_ind < file->buff_len)
1656 count = file->buff_len - file->buff_ind;
1658 done += ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
1659 memcpy (buffer, &file->buff[file->buff_ind], done);
1660 file->buff_ind += done;
1663 if (buffersize == 0)
1667 // NOTE: at this point, the read buffer is always empty
1669 // If the file isn't compressed
1670 if (! (file->flags & QFILE_FLAG_DEFLATED))
1674 // We must take care to not read after the end of the file
1675 count = file->real_length - file->position;
1677 // If we have a lot of data to get, put them directly into "buffer"
1678 if (buffersize > sizeof (file->buff) / 2)
1680 if (count > (fs_offset_t)buffersize)
1681 count = (fs_offset_t)buffersize;
1682 lseek (file->handle, file->offset + file->position, SEEK_SET);
1683 nb = read (file->handle, &((unsigned char*)buffer)[done], count);
1687 file->position += nb;
1689 // Purge cached data
1695 if (count > (fs_offset_t)sizeof (file->buff))
1696 count = (fs_offset_t)sizeof (file->buff);
1697 lseek (file->handle, file->offset + file->position, SEEK_SET);
1698 nb = read (file->handle, file->buff, count);
1701 file->buff_len = nb;
1702 file->position += nb;
1704 // Copy the requested data in "buffer" (as much as we can)
1705 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1706 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1707 file->buff_ind = count;
1715 // If the file is compressed, it's more complicated...
1716 // We cycle through a few operations until we have read enough data
1717 while (buffersize > 0)
1719 ztoolkit_t *ztk = file->ztk;
1722 // NOTE: at this point, the read buffer is always empty
1724 // If "input" is also empty, we need to refill it
1725 if (ztk->in_ind == ztk->in_len)
1727 // If we are at the end of the file
1728 if (file->position == file->real_length)
1731 count = (fs_offset_t)(ztk->comp_length - ztk->in_position);
1732 if (count > (fs_offset_t)sizeof (ztk->input))
1733 count = (fs_offset_t)sizeof (ztk->input);
1734 lseek (file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET);
1735 if (read (file->handle, ztk->input, count) != count)
1737 Con_Printf ("FS_Read: unexpected end of file\n");
1742 ztk->in_len = count;
1743 ztk->in_position += count;
1746 ztk->zstream.next_in = &ztk->input[ztk->in_ind];
1747 ztk->zstream.avail_in = (unsigned int)(ztk->in_len - ztk->in_ind);
1749 // Now that we are sure we have compressed data available, we need to determine
1750 // if it's better to inflate it in "file->buff" or directly in "buffer"
1752 // Inflate the data in "file->buff"
1753 if (buffersize < sizeof (file->buff) / 2)
1755 ztk->zstream.next_out = file->buff;
1756 ztk->zstream.avail_out = sizeof (file->buff);
1757 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1758 if (error != Z_OK && error != Z_STREAM_END)
1760 Con_Printf ("FS_Read: Can't inflate file\n");
1763 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1765 file->buff_len = (fs_offset_t)sizeof (file->buff) - ztk->zstream.avail_out;
1766 file->position += file->buff_len;
1768 // Copy the requested data in "buffer" (as much as we can)
1769 count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
1770 memcpy (&((unsigned char*)buffer)[done], file->buff, count);
1771 file->buff_ind = count;
1774 // Else, we inflate directly in "buffer"
1777 ztk->zstream.next_out = &((unsigned char*)buffer)[done];
1778 ztk->zstream.avail_out = (unsigned int)buffersize;
1779 error = qz_inflate (&ztk->zstream, Z_SYNC_FLUSH);
1780 if (error != Z_OK && error != Z_STREAM_END)
1782 Con_Printf ("FS_Read: Can't inflate file\n");
1785 ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
1787 // How much data did it inflate?
1788 count = (fs_offset_t)(buffersize - ztk->zstream.avail_out);
1789 file->position += count;
1791 // Purge cached data
1796 buffersize -= count;
1804 ====================
1807 Print a string into a file
1808 ====================
1810 int FS_Print (qfile_t* file, const char *msg)
1812 return (int)FS_Write (file, msg, strlen (msg));
1816 ====================
1819 Print a string into a file
1820 ====================
1822 int FS_Printf(qfile_t* file, const char* format, ...)
1827 va_start (args, format);
1828 result = FS_VPrintf (file, format, args);
1836 ====================
1839 Print a string into a file
1840 ====================
1842 int FS_VPrintf (qfile_t* file, const char* format, va_list ap)
1845 fs_offset_t buff_size = MAX_INPUTLINE;
1850 tempbuff = (char *)Mem_Alloc (tempmempool, buff_size);
1851 len = dpvsnprintf (tempbuff, buff_size, format, ap);
1852 if (len >= 0 && len < buff_size)
1854 Mem_Free (tempbuff);
1858 len = write (file->handle, tempbuff, len);
1859 Mem_Free (tempbuff);
1866 ====================
1869 Get the next character of a file
1870 ====================
1872 int FS_Getc (qfile_t* file)
1876 if (FS_Read (file, &c, 1) != 1)
1884 ====================
1887 Put a character back into the read buffer (only supports one character!)
1888 ====================
1890 int FS_UnGetc (qfile_t* file, unsigned char c)
1892 // If there's already a character waiting to be read
1893 if (file->ungetc != EOF)
1902 ====================
1905 Move the position index in a file
1906 ====================
1908 int FS_Seek (qfile_t* file, fs_offset_t offset, int whence)
1911 unsigned char* buffer;
1912 fs_offset_t buffersize;
1914 // Compute the file offset
1918 offset += file->position - file->buff_len + file->buff_ind;
1925 offset += file->real_length;
1931 if (offset < 0 || offset > (long) file->real_length)
1934 // If we have the data in our read buffer, we don't need to actually seek
1935 if (file->position - file->buff_len <= offset && offset <= file->position)
1937 file->buff_ind = offset + file->buff_len - file->position;
1941 // Purge cached data
1944 // Unpacked or uncompressed files can seek directly
1945 if (! (file->flags & QFILE_FLAG_DEFLATED))
1947 if (lseek (file->handle, file->offset + offset, SEEK_SET) == -1)
1949 file->position = offset;
1953 // Seeking in compressed files is more a hack than anything else,
1954 // but we need to support it, so here we go.
1957 // If we have to go back in the file, we need to restart from the beginning
1958 if (offset <= file->position)
1962 ztk->in_position = 0;
1964 lseek (file->handle, file->offset, SEEK_SET);
1966 // Reset the Zlib stream
1967 ztk->zstream.next_in = ztk->input;
1968 ztk->zstream.avail_in = 0;
1969 qz_inflateReset (&ztk->zstream);
1972 // We need a big buffer to force inflating into it directly
1973 buffersize = 2 * sizeof (file->buff);
1974 buffer = (unsigned char *)Mem_Alloc (tempmempool, buffersize);
1976 // Skip all data until we reach the requested offset
1977 while (offset > file->position)
1979 fs_offset_t diff = offset - file->position;
1980 fs_offset_t count, len;
1982 count = (diff > buffersize) ? buffersize : diff;
1983 len = FS_Read (file, buffer, count);
1997 ====================
2000 Give the current position in a file
2001 ====================
2003 fs_offset_t FS_Tell (qfile_t* file)
2005 return file->position - file->buff_len + file->buff_ind;
2010 ====================
2013 Erases any buffered input or output data
2014 ====================
2016 void FS_Purge (qfile_t* file)
2028 Filename are relative to the quake directory.
2029 Always appends a 0 byte.
2032 unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
2035 unsigned char *buf = NULL;
2036 fs_offset_t filesize = 0;
2038 file = FS_Open (path, "rb", quiet, false);
2041 filesize = file->real_length;
2042 buf = (unsigned char *)Mem_Alloc (pool, filesize + 1);
2043 buf[filesize] = '\0';
2044 FS_Read (file, buf, filesize);
2048 if (filesizepointer)
2049 *filesizepointer = filesize;
2058 The filename will be prefixed by the current game directory
2061 qboolean FS_WriteFile (const char *filename, void *data, fs_offset_t len)
2065 file = FS_Open (filename, "wb", false, false);
2068 Con_Printf("FS_WriteFile: failed on %s\n", filename);
2072 Con_DPrintf("FS_WriteFile: %s\n", filename);
2073 FS_Write (file, data, len);
2080 =============================================================================
2082 OTHERS PUBLIC FUNCTIONS
2084 =============================================================================
2092 void FS_StripExtension (const char *in, char *out, size_t size_out)
2100 while ((currentchar = *in) && size_out > 1)
2102 if (currentchar == '.')
2104 else if (currentchar == '/' || currentchar == '\\' || currentchar == ':')
2106 *out++ = currentchar;
2122 void FS_DefaultExtension (char *path, const char *extension, size_t size_path)
2126 // if path doesn't have a .EXT, append extension
2127 // (extension should include the .)
2128 src = path + strlen(path) - 1;
2130 while (*src != '/' && src != path)
2133 return; // it has an extension
2137 strlcat (path, extension, size_path);
2145 Look for a file in the packages and in the filesystem
2148 qboolean FS_FileExists (const char *filename)
2150 return (FS_FindFile (filename, NULL, true) != NULL);
2158 Look for a file in the filesystem only
2161 qboolean FS_SysFileExists (const char *path)
2166 // TODO: use another function instead, to avoid opening the file
2167 desc = open (path, O_RDONLY | O_BINARY);
2176 if (stat (path,&buf) == -1)
2183 void FS_mkdir (const char *path)
2196 Allocate and fill a search structure with information on matching filenames.
2199 fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
2202 searchpath_t *searchpath;
2204 int i, basepathlength, numfiles, numchars;
2205 stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
2206 const char *slash, *backslash, *colon, *separator;
2208 char netpath[MAX_OSPATH];
2209 char temp[MAX_OSPATH];
2211 for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
2216 Con_Printf("Don't use punctuation at the beginning of a search pattern!\n");
2224 slash = strrchr(pattern, '/');
2225 backslash = strrchr(pattern, '\\');
2226 colon = strrchr(pattern, ':');
2227 separator = max(slash, backslash);
2228 separator = max(separator, colon);
2229 basepathlength = separator ? (separator + 1 - pattern) : 0;
2230 basepath = (char *)Mem_Alloc (tempmempool, basepathlength + 1);
2232 memcpy(basepath, pattern, basepathlength);
2233 basepath[basepathlength] = 0;
2235 // search through the path, one element at a time
2236 for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next)
2238 // is the element a pak file?
2239 if (searchpath->pack)
2241 // look through all the pak file elements
2242 pak = searchpath->pack;
2243 for (i = 0;i < pak->numfiles;i++)
2245 strlcpy(temp, pak->files[i].name, sizeof(temp));
2248 if (matchpattern(temp, (char *)pattern, true))
2250 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2251 if (!strcmp(listtemp->text, temp))
2253 if (listtemp == NULL)
2255 listcurrent = stringlistappend(listcurrent, temp);
2256 if (liststart == NULL)
2257 liststart = listcurrent;
2259 Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
2262 // strip off one path element at a time until empty
2263 // this way directories are added to the listing if they match the pattern
2264 slash = strrchr(temp, '/');
2265 backslash = strrchr(temp, '\\');
2266 colon = strrchr(temp, ':');
2268 if (separator < slash)
2270 if (separator < backslash)
2271 separator = backslash;
2272 if (separator < colon)
2274 *((char *)separator) = 0;
2280 // get a directory listing and look at each name
2281 dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
2282 if ((dir = listdirectory(netpath)))
2284 for (dirfile = dir;dirfile;dirfile = dirfile->next)
2286 dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirfile->text);
2287 if (matchpattern(temp, (char *)pattern, true))
2289 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2290 if (!strcmp(listtemp->text, temp))
2292 if (listtemp == NULL)
2294 listcurrent = stringlistappend(listcurrent, temp);
2295 if (liststart == NULL)
2296 liststart = listcurrent;
2298 Con_DPrintf("SearchDirFile: %s\n", temp);
2309 liststart = stringlistsort(liststart);
2312 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2315 numchars += (int)strlen(listtemp->text) + 1;
2317 search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
2318 search->filenames = (char **)((char *)search + sizeof(fssearch_t));
2319 search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
2320 search->numfilenames = (int)numfiles;
2323 for (listtemp = liststart;listtemp;listtemp = listtemp->next)
2326 search->filenames[numfiles] = search->filenamesbuffer + numchars;
2327 textlen = strlen(listtemp->text) + 1;
2328 memcpy(search->filenames[numfiles], listtemp->text, textlen);
2330 numchars += (int)textlen;
2333 stringlistfree(liststart);
2340 void FS_FreeSearch(fssearch_t *search)
2345 extern int con_linewidth;
2346 int FS_ListDirectory(const char *pattern, int oneperline)
2355 char linebuf[MAX_INPUTLINE];
2357 search = FS_Search(pattern, true, true);
2360 numfiles = search->numfilenames;
2363 // FIXME: the names could be added to one column list and then
2364 // gradually shifted into the next column if they fit, and then the
2365 // next to make a compact variable width listing but it's a lot more
2367 // find width for columns
2369 for (i = 0;i < numfiles;i++)
2371 l = (int)strlen(search->filenames[i]);
2372 if (columnwidth < l)
2375 // count the spacing character
2377 // calculate number of columns
2378 numcolumns = con_linewidth / columnwidth;
2379 // don't bother with the column printing if it's only one column
2380 if (numcolumns >= 2)
2382 numlines = (numfiles + numcolumns - 1) / numcolumns;
2383 for (i = 0;i < numlines;i++)
2386 for (k = 0;k < numcolumns;k++)
2388 l = i * numcolumns + k;
2391 name = search->filenames[l];
2392 for (j = 0;name[j] && linebufpos + 1 < (int)sizeof(linebuf);j++)
2393 linebuf[linebufpos++] = name[j];
2394 // space out name unless it's the last on the line
2395 if (k + 1 < numcolumns && l + 1 < numfiles)
2396 for (;j < columnwidth && linebufpos + 1 < (int)sizeof(linebuf);j++)
2397 linebuf[linebufpos++] = ' ';
2400 linebuf[linebufpos] = 0;
2401 Con_Printf("%s\n", linebuf);
2408 for (i = 0;i < numfiles;i++)
2409 Con_Printf("%s\n", search->filenames[i]);
2410 FS_FreeSearch(search);
2411 return (int)numfiles;
2414 static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
2416 const char *pattern;
2419 Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
2422 if (Cmd_Argc() == 2)
2423 pattern = Cmd_Argv(1);
2426 if (!FS_ListDirectory(pattern, oneperline))
2427 Con_Print("No files found.\n");
2432 FS_ListDirectoryCmd("dir", true);
2437 FS_ListDirectoryCmd("ls", false);
2440 const char *FS_WhichPack(const char *filename)
2443 searchpath_t *sp = FS_FindFile(filename, &index, true);
2445 return sp->pack->filename;