34 #include "../ports/system.h"
35 #include "../shared/defines.h"
36 #include "../shared/parse.h"
45 #define MODS_DIR "mods"
49 if (fs_searchpaths ==
nullptr) {
50 Sys_Error(
"Filesystem call made without initialization");
58 if (f->
f !=
nullptr) {
70 for (
const searchpath_t* search = fs_searchpaths; search; search = search->
next) {
72 return search->filename;
94 const int pos = ftell(f->
f);
97 const int end = ftell(f->
f);
123 for (
char* ofs = pathCopy + 1; *ofs; ofs++) {
166 file->
z = file->
f =
nullptr;
185 for (
const filelink_t* link = fs_links; link; link = link->
next) {
186 if (!strncmp(filename, link->from, link->fromlength)) {
187 Com_sprintf(netpath,
sizeof(netpath),
"%s%s", link->to, filename + link->fromlength);
191 Com_Printf(
"linked file could not be opened: %s\n", netpath);
197 for (
const searchpath_t* search = fs_searchpaths; search; search = search->
next) {
201 const pack_t* pak = search->pack;
222 Com_sprintf(netpath,
sizeof(netpath),
"%s/%s", search->filename, filename);
238 #define PK3_SEEK_BUFFER_SIZE 65536
253 Sys_Error(
"Negative offsets and FS_SEEK_END not implemented "
254 "for FS_Seek on pk3 file contents\n");
288 return fseek(f->
f, offset, _origin);
311 #define MAX_READ 0x10000
334 Sys_Error(
"FS_Read (zipfile): -1 bytes read");
342 int block = remaining;
345 const int read = fread(buf, 1, block, f->
f);
348 if (read != block && feof(f->
f))
349 return (len - remaining + read);
355 else if (failOnEmptyRead)
358 return len - remaining;
373 return FS_Read2(buffer, len, f,
true);
435 Com_Printf(
"Could not load '%s'\n", packfile);
441 err =
unzGetCurrentFileInfo(uf, &file_info, filenameInZip,
sizeof(filenameInZip),
nullptr, 0,
nullptr, 0);
459 err =
unzGetCurrentFileInfo(uf, &file_info, filenameInZip,
sizeof(filenameInZip),
nullptr, 0,
nullptr, 0);
465 Q_strncpyz(newfiles[i].
name, filenameInZip,
sizeof(newfiles[i].name));
469 pack->
files = newfiles;
478 Com_Printf(
"Pack file type %s unrecognized\n", extension);
483 #define MAX_PACKFILES 1024
486 "pk3",
"zip",
nullptr
499 int pakfile_count = 0;
502 for (
searchpath_t* search = fs_searchpaths; search; search = search->
next) {
503 if (
Q_streq(search->filename, dir))
505 if (write && search->write) {
506 Com_Printf(
"change writing directory to %s\n", dir);
507 search->write =
false;
513 for (
char const*
const* extList =
pakFileExt; *extList; ++extList) {
514 Com_sprintf(pattern,
sizeof(pattern),
"%s/*.%s", dir, *extList);
516 if (dirnames !=
nullptr) {
517 for (
int i = 0;
i < ndirs - 1;
i++) {
518 if (strrchr(dirnames[
i],
'/')) {
519 Q_strncpyz(pakfile_list[pakfile_count], dirnames[i],
sizeof(pakfile_list[pakfile_count]));
535 for (
int i = 0;
i < pakfile_count;
i++) {
543 search->
write =
false;
544 fs_searchpaths = search;
551 search->
write = write;
552 fs_searchpaths = search;
570 if (s[strlen(s) - 1] !=
'.')
589 if (s[strlen(s) - 1] !=
'.') {
590 Q_strncpyz(tempList[nfiles], s,
sizeof(tempList[nfiles]));
603 for (
int i = 0;
i < nfiles;
i++) {
619 char* prev =
nullptr;
623 if (prev &&
Q_streq(prevpath, prev))
638 #elif defined (__APPLE__) || defined (MACOSX)
645 Com_Printf(
"could not find the home directory\n");
660 Q_strcat(gdir,
sizeof(gdir),
"/%s", dir);
675 char const*
const append =
"/" MODS_DIR;
676 Q_strcat(gdir,
sizeof(gdir), append);
682 char const* searchpaths[] = {
694 for (
const char** path = searchpaths; *path; path++) {
695 const char* pattern = *path;
698 if (dirnames !=
nullptr) {
699 for (
int i = 0;
i < ndirs - 1;
i++) {
713 static void FS_Mod_f (
void)
734 snprintf(name,
sizeof(name),
"%s/autoexec.cfg", s->filename);
751 static void FS_Link_f (
void)
789 static void FS_Dir_f (
void)
792 const char* path =
nullptr;
797 Com_sprintf(findname,
sizeof(findname),
"%s/%s", path, wildcard);
804 if (dirnames !=
nullptr) {
805 for (
int i = 0;
i < ndirs - 1;
i++) {
806 char const*
const slash = strrchr(dirnames[
i],
'/');
807 Com_Printf(
"%s\n", slash ? slash + 1 : dirnames[
i]);
817 static void FS_List_f (
void)
822 Com_Printf(
"Show files for '%s'\n", wildcard);
832 static void FS_Info_f (
void)
837 for (
searchpath_t* search = fs_searchpaths; search; search = search->
next) {
838 if (search->pack ==
nullptr)
839 Com_Printf(
"...path: '%s'\n", search->filename);
841 Com_Printf(
"...pakfile: '%s' (%i files)\n", search->pack->filename, search->pack->numfiles);
848 static void FS_RestartFilesystem_f (
void)
862 {
"fs_restart", FS_RestartFilesystem_f,
"Reloads the file subsystem"},
863 {
"link", FS_Link_f,
"Create file links"},
864 {
"dir", FS_Dir_f,
"Show the filesystem contents per game dir - also supports wildcarding"},
865 {
"ls", FS_List_f,
"Show the filesystem contents"},
866 {
"fs_info", FS_Info_f,
"Show information about the virtual filesystem"},
867 {
"fs_mod", FS_Mod_f,
"Show or activate mods"},
869 {
nullptr,
nullptr,
nullptr}
872 static void FS_RemoveCommands (
void)
878 static void FS_InitCommandsAndCvars (
void)
890 Com_Printf(
"\n---- filesystem initialization -----\n");
903 Com_sprintf(path,
sizeof(path),
"./%s", fsGameDir);
913 FS_InitCommandsAndCvars();
970 for (
listBlock_t** anchor = &fs_blocklist; *anchor;) {
973 *anchor = block->
next;
978 anchor = &block->
next;
985 fs_blocklist = block;
995 for (
searchpath_t* search = fs_searchpaths; search; search = search->
next) {
998 const char* ext = strrchr(files,
'.');
999 const pack_t* pak = search->pack;
1000 size_t l = strlen(files);
1003 Q_strncpyz(findname, files,
sizeof(findname));
1005 l -= (strlen(ext) + 1);
1011 const char* fileNameEntry = pak->
files[
i].
name;
1012 bool matchAlsoInSubDirs = (findname[0] ==
'*' || !strncmp(fileNameEntry, findname, l))
1013 && (ext[0] ==
'*' || strstr(fileNameEntry, ext));
1014 if (matchAlsoInSubDirs) {
1016 if (strstr(findname,
"**"))
1022 Com_FilePath(fileNameEntry, pathNameEntry,
sizeof(pathNameEntry));
1023 if (
Q_streq(pathNameEntry, pathName))
1031 }
else if (strstr(files,
"**")) {
1033 const char* wildcard = strstr(files,
"**");
1034 const size_t l = strlen(files) - strlen(wildcard);
1036 Q_strncpyz(findname, files,
sizeof(findname));
1039 if (l > 0 && findname[l - 1] ==
'/')
1040 findname[l - 1] =
'\0';
1052 Com_sprintf(findname,
sizeof(findname),
"%s/%s", search->filename, files);
1056 if (filenames !=
nullptr) {
1057 for (
int i = 0;
i < nfiles - 1;
i++) {
1085 if (files ==
nullptr) {
1091 for (block = fs_blocklist; block; block = block->
next) {
1098 for (block = fs_blocklist; block; block = block->
next) {
1104 Com_Printf(
"FS_NextFileFromFileList: Could not create filelist for %s\n", files);
1111 if (_block != block) {
1113 listEntry = block->
files;
1116 const char* file =
nullptr;
1118 file = (
const char*)listEntry->
data;
1119 listEntry = listEntry->
next;
1138 static byte* buffer =
nullptr;
1152 for (block = fs_blocklist; block; block = block->
next) {
1161 for (block = fs_blocklist; block; block = block->
next) {
1167 Com_Printf(
"FS_GetFileData: Could not create filelist for %s\n", files);
1174 fileList = block->
files;
1177 fileList = fileList->
next;
1184 strcpy(strrchr(filename,
'/') + 1, (
const char*)fileList->
data);
1187 return (
const char*)buffer;
1199 static byte* lBuffer;
1200 static char headerType[
MAX_VAR];
1201 static char headerName[512];
1215 if (!
Q_streq(files, lastList)) {
1217 Q_strncpyz(lastList, files,
sizeof(lastList));
1220 for (block = fs_blocklist; block; block = block->
next) {
1230 lFile = block->
files;
1238 if (*token ==
'{') {
1243 Q_strncpyz(headerType, token,
sizeof(headerType));
1246 Q_strncpyz(headerName, token,
sizeof(headerName));
1253 lFile = lFile->
next;
1255 while (!lFile && lBlock) {
1257 for (lBlock = lBlock->
next; lBlock; lBlock = lBlock->
next) {
1259 lFile = lBlock->
files;
1277 strcpy(strrchr(filename,
'/') + 1, (
const char*)lFile->
data);
1282 lFile = lFile->
next;
1285 *text = (
char*)lBuffer;
1289 if (
Q_strneq (*text,
"--!usr/bin/lua", 14)) {
1291 Q_strncpyz(headerName, (
const char*)lFile->
data,
sizeof(headerName));
1293 static char luaType[] =
"lua";
1296 }
else if (!lBuffer)
1320 const char* mapStr1 = *(
const char*
const*)map1;
1321 const char* mapStr2 = *(
const char*
const*)map2;
1324 if (mapStr1[0] ==
'+')
1326 if (mapStr2[0] ==
'+')
1346 Com_sprintf(name,
sizeof(name),
"maps/%s.bsp", filename);
1354 FS_Read(header,
sizeof(header), &file);
1356 for (
int i = 0;
i < 2;
i++)
1376 const char* baseMapName =
nullptr;
1380 if (!reset && fs_mapsInstalledInit)
1382 else if (fs_mapsInstalledInit) {
1387 fs_numInstalledMaps = -1;
1391 for (
searchpath_t* search = fs_searchpaths; search; search = search->
next) {
1395 pack_t* pak = search->pack;
1398 baseMapName = strchr(pak->
files[
i].
name,
'/');
1401 baseMapName = strchr(baseMapName + 1,
'/');
1409 if (fs_numInstalledMaps + 1 >=
MAX_MAPS) {
1410 Com_Printf(
"FS_GetMaps: Max maps limit hit\n");
1414 if (fs_maps[fs_numInstalledMaps + 1] ==
nullptr) {
1415 Com_Printf(
"Could not allocate memory in FS_GetMaps\n");
1422 fs_numInstalledMaps++;
1423 if (strstr(findname,
".ump"))
1430 Com_sprintf(findname,
sizeof(findname),
"%s/maps/*.bsp", search->filename);
1434 if (dirnames !=
nullptr) {
1435 for (
int i = 0;
i < ndirs - 1;
i++) {
1440 if (fs_numInstalledMaps + 1 >=
MAX_MAPS) {
1441 Com_Printf(
"FS_GetMaps: Max maps limit hit\n");
1445 if (fs_maps[fs_numInstalledMaps + 1] ==
nullptr) {
1446 Com_Printf(
"Could not allocate memory in FS_GetMaps\n");
1450 fs_numInstalledMaps++;
1453 Com_Printf(
"invalid mapstatus: %i (%s)\n", status, dirnames[i]);
1459 Com_sprintf(findname,
sizeof(findname),
"%s/maps/*.ump", search->filename);
1463 if (dirnames !=
nullptr) {
1464 for (
int i = 0;
i < ndirs - 1;
i++) {
1467 if (fs_numInstalledMaps + 1 >=
MAX_MAPS) {
1468 Com_Printf(
"FS_GetMaps: Max maps limit hit\n");
1472 if (fs_maps[fs_numInstalledMaps + 1] ==
nullptr) {
1473 Com_Printf(
"Could not allocate memory in FS_GetMaps\n");
1477 fs_numInstalledMaps++;
1486 fs_mapsInstalledInit =
true;
1488 qsort(fs_maps, fs_numInstalledMaps + 1,
sizeof(
char*),
FS_MapDefSort);
1502 const int len = fprintf(f->
f,
"%s", buf);
1518 int remaining =
len;
1521 const int block = remaining;
1522 const int written = fwrite(buf, 1, block, f->
f);
1532 if (written == -1) {
1537 remaining -= written;
1551 const int c =
FS_Write(buffer, len, &f);
1555 if (c != len || lencheck != len) {
1556 Com_Printf(
"FS_WriteFile: failed to finish writing '%s'\n", filename);
1558 Com_Printf(
"FS_WriteFile: could not remove file: %s\n", filename);
1586 va_start(ap, filename);
1622 fs_searchpaths =
nullptr;
1624 fs_mapsInstalledInit =
false;
1625 fs_numInstalledMaps = -1;
1626 fs_blocklist =
nullptr;
1629 FS_RemoveCommands();
1643 if (gamedir !=
nullptr)
1644 Com_Printf(
"restarting with gamedir set to %s\n", gamedir);
1654 if (!fs_searchpaths)
1655 Sys_Error(
"Filesystem call made without initialization");
1657 Com_Printf(
"FS_CopyFile: copy %s to %s\n", fromOSPath, toOSPath);
1664 const int len = ftell(f);
1668 if (fread(buf, 1, len, f) != len)
1680 if (fwrite(buf, 1, len, f) != len)
1681 Sys_Error(
"Short write in FS_CopyFile");
1692 if (!fs_searchpaths)
1693 Sys_Error(
"Filesystem call made without initialization");
1695 Com_Printf(
"FS_RemoveFile: remove %s\n", osPath);
1712 if (!fs_searchpaths)
1713 Sys_Error(
"Filesystem call made without initialization");
char * Sys_GetHomeDirectory(void)
Returns the home environment variable (which hold the path of the user's homedir) ...
const char * Cmd_Argv(int arg)
Returns a given argument.
char filename[MAX_OSPATH]
const char * FS_NextFileFromFileList(const char *files)
Returns the next file that is found in the virtual filesystem identified by the given file pattern...
int ZEXPORT unzGetCurrentFileInfoPosition(unzFile file, unsigned long *pos)
void FS_AddGameDirectory(const char *dir, bool write)
Adds the directory to the head of the search path.
int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap)
Safe (null terminating) vsnprintf implementation.
void Sys_Error(const char *error,...)
void Sys_Mkdir(const char *path)
int FS_CheckFile(const char *fmt,...)
Just returns the filelength and -1 if the file wasn't found.
static pack_t * FS_LoadPackFile(const char *packfile)
Takes an explicit (not game tree related) path to a pak file. Adding the files at the beginning of th...
void FS_ExecAutoexec(void)
const char * Com_SkipPath(const char *pathname)
Returns just the filename from a given path.
static char const *const pakFileExt[]
int FS_OpenFile(const char *filename, qFILE *file, filemode_t mode)
Finds and opens the file in the search path.
const char * Com_GetExtension(const char *path)
struct listBlock_s * next
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
void LIST_AddStringSorted(linkedList_t **listDest, const char *data)
int FS_Seek(qFILE *f, long offset, int origin)
Sets the file position of the given file.
static int CheckBSPFile(const char *filename)
Checks for valid BSP-file.
#define PK3_SEEK_BUFFER_SIZE
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
void Com_StripExtension(const char *in, char *out, const size_t size)
Removes the file extension from a filename.
bool FS_FileExists(const char *filename,...)
Checks whether a file exists (not in virtual filesystem)
struct searchpath_s * next
Links one file onto another - like a symlink.
int Sys_Rename(const char *oldname, const char *newname)
int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info)
void Cbuf_AddText(const char *format,...)
Adds command text at the end of the buffer.
int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity)
void FS_GetMaps(bool reset)
File the fs_maps array with valid maps.
void Com_FilePath(const char *in, char *out, size_t size)
Returns the path up to, but not including the last /.
int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)
void FS_RestartFilesystem(const char *gamedir)
Restart the filesystem (reload all pk3 files)
void FS_RemoveFile(const char *osPath)
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
void Com_Printf(const char *const fmt,...)
static void _AddToListBlock(linkedList_t **fl, const char *name, bool stripPath)
Add one name to the filelist.
int FS_BuildFileList(const char *fileList)
Build a filelist.
void LIST_Delete(linkedList_t **list)
int ZEXPORT unzGoToNextFile(unzFile file)
void FS_CopyFile(const char *fromOSPath, const char *toOSPath)
Copy a fully specified file from one place to another.
void Cmd_TableRemoveList(const cmdList_t *cmdList)
static char findname[MAX_OSPATH]
int LIST_Count(const linkedList_t *list)
#define Q_strvalid(string)
static bool FS_GetHomeDirectory(char *gdir, size_t length)
char * Sys_FindFirst(const char *path, unsigned musthave, unsigned canthave)
Opens the directory and returns the first file that matches our searchrules.
const char * FS_Gamedir(void)
Called to find where to write a file (savegames, etc)
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
struct listBlock_s listBlock_t
void FS_NormPath(char *path)
Convert operating systems path separators to ufo virtual filesystem separators (/) ...
int ZEXPORT unzGoToFirstFile(unzFile file)
void _Mem_Free(void *ptr, const char *fileName, const int fileLine)
void LIST_AddString(linkedList_t **listDest, const char *data)
Adds an string to a new or to an already existing linked list. The string is copied here...
unzFile ZEXPORT unzOpen(const char *path)
QGL_EXTERN GLuint GLsizei GLsizei * length
int ZEXPORT unzCloseCurrentFile(unzFile file)
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
int ZEXPORT unzSetCurrentFileInfoPosition(unzFile file, unsigned long pos)
int Sys_Remove(const char *filename)
int FS_GetModList(linkedList_t **mods)
Searches and builds a list of mod directories.
void FS_AddHomeAsGameDirectory(const char *dir, bool write)
void FS_InitFilesystem(bool writeToHomeDir)
void Sys_FindClose(void)
Closes the find handle.
int FS_WriteFile(const void *buffer, size_t len, const char *filename)
int FS_Printf(qFILE *f, const char *msg,...)
Can print chunks for 1024 chars into a file.
const char * FS_GetCwd(void)
Return current working dir.
static int FS_MapDefSort(const void *map1, const void *map2)
#define Q_strcasecmp(a, b)
const linkedList_t * LIST_ContainsString(const linkedList_t *list, const char *string)
Searches for the first occurrence of a given string.
Header for various formats like pak, and model formats as well as bsp format.
#define Mem_FreePool(pool)
char * Q_strlwr(char *str)
Converts a string to lowercase.
static bool fs_mapsInstalledInit
int FS_FileLength(qFILE *f)
Returns the size of a given file or -1 if no file is opened.
#define Mem_PoolAllocTypeN(type, n, pool)
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
void Sys_Mkfifo(const char *ospath, struct qFILE_s *f)
int FS_Read(void *buffer, int len, qFILE *f)
#define Q_strneq(a, b, n)
bool FS_RenameFile(const char *from, const char *to, bool relative)
Renames a file.
QGL_EXTERN GLuint GLchar GLuint * len
char * Sys_FindNext(unsigned musthave, unsigned canthave)
Returns the next file of the already opened directory (Sys_FindFirst) that matches our search mask...
const char * FS_GetFileData(const char *files)
Returns the buffer of a file.
char * FS_NextScriptHeader(const char *files, const char **name, const char **text)
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
int ZEXPORT unzOpenCurrentFile(unzFile file)
void Cmd_TableCheck(void)
Check both the functiontable and the associated hashtable for invalid entries.
char filename[MAX_OSPATH]
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
int FS_Read2(void *buffer, int len, qFILE *f, bool failOnEmptyRead)
Read a file into a given buffer in memory.
static int fs_openedFiles
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
int ZEXPORT unzClose(unzFile file)
void FS_CreateOpenPipeFile(const char *filename, qFILE *f)
definitions common between client and server, but not game lib
FILE * Sys_Fopen(const char *filename, const char *mode)
int Sys_Access(const char *filename, int mode)
static searchpath_t * fs_searchpaths
memPool_t * com_fileSysPool
int Q_StringSort(const void *string1, const void *string2)
Compare two strings.
const char * Cvar_GetString(const char *varName)
Returns the value of cvar as string.
#define Mem_PoolStrDup(in, pool, tagNum)
static filelink_t * fs_links
void FS_CloseFile(qFILE *f)
Closes a file handle.
#define Mem_PoolAllocType(type, pool)
char * Sys_Cwd(void)
Get current working dir.
void Sys_NormPath(char *path)
Normalize path (remove all \ )
void Cbuf_Execute(void)
Pulls off terminated lines of text from the command buffer and sends them through Cmd_ExecuteString...
void Sys_ListFilteredFiles(const char *basedir, const char *subdirs, const char *filter, linkedList_t **list)
void FS_CreatePath(const char *path)
Creates any directories needed to store the given filename.
static listBlock_t * fs_blocklist
void FS_Shutdown(void)
Cleanup function.
char ** FS_ListFiles(const char *findname, int *numfiles, unsigned musthave, unsigned canthave)
Builds a qsorted filelist.
void Com_SkipBlock(const char **text)
Skips a block of {} in our script files.
void Cmd_TableAddList(const cmdList_t *cmdList)
void FS_FreeFile(void *buffer)
int FS_Write(const void *buffer, int len, qFILE *f)
Properly handles partial writes.
const char * FS_NextPath(const char *prevpath)
Allows enumerating all of the directories in the search path.
int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len)