]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - fs.c
changed "sample frames missing" and similar warnings to require developer 1000 or...
[xonotic/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 5a849e1466548dc2fbb9392390f6bb09b367f2b9..0c6901b3823836002769d861e6bab239c75a3bc2 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -249,7 +249,6 @@ FUNCTION PROTOTYPES
 void FS_Dir_f(void);
 void FS_Ls_f(void);
 
-static const char *FS_FileExtension (const char *in);
 static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet);
 static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
                                                                        fs_offset_t offset, fs_offset_t packsize,
@@ -962,34 +961,38 @@ then loads and adds pak1.pak pak2.pak ...
 */
 void FS_AddGameDirectory (const char *dir)
 {
-       stringlist_t *list, *current;
+       int i;
+       stringlist_t list;
        searchpath_t *search;
        char pakfile[MAX_OSPATH];
 
        strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
 
-       list = listdirectory(dir);
+       stringlistinit(&list);
+       listdirectory(&list, dir);
+       stringlistsort(&list);
 
        // add any PAK package in the directory
-       for (current = list;current;current = current->next)
+       for (i = 0;i < list.numstrings;i++)
        {
-               if (!strcasecmp(FS_FileExtension(current->text), "pak"))
+               if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
                {
-                       dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text);
+                       dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
                        FS_AddPack_Fullpath(pakfile, NULL, false);
                }
        }
 
-       // add any PK3 package in the director
-       for (current = list;current;current = current->next)
+       // add any PK3 package in the directory
+       for (i = 0;i < list.numstrings;i++)
        {
-               if (!strcasecmp(FS_FileExtension(current->text), "pk3"))
+               if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
                {
-                       dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text);
+                       dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
                        FS_AddPack_Fullpath(pakfile, NULL, false);
                }
        }
-       freedirectory(list);
+
+       stringlistfreecontents(&list);
 
        // Add the directory to the search path
        // (unpacked files have the priority over packed files)
@@ -1028,7 +1031,7 @@ void FS_AddGameHierarchy (const char *dir)
 FS_FileExtension
 ============
 */
-static const char *FS_FileExtension (const char *in)
+const char *FS_FileExtension (const char *in)
 {
        const char *separator, *backslash, *colon, *dot;
 
@@ -1048,6 +1051,26 @@ static const char *FS_FileExtension (const char *in)
 }
 
 
+/*
+============
+FS_FileWithoutPath
+============
+*/
+const char *FS_FileWithoutPath (const char *in)
+{
+       const char *separator, *backslash, *colon;
+
+       separator = strrchr(in, '/');
+       backslash = strrchr(in, '\\');
+       if (!separator || separator < backslash)
+               separator = backslash;
+       colon = strrchr(in, ':');
+       if (!separator || separator < colon)
+               separator = colon;
+       return separator ? separator + 1 : in;
+}
+
+
 /*
 ================
 FS_ClearSearchPath
@@ -1072,10 +1095,10 @@ void FS_ClearSearchPath (void)
 
 /*
 ================
-FS_Rescan_f
+FS_Rescan
 ================
 */
-void FS_Rescan_f (void)
+void FS_Rescan (void)
 {
        int i;
        qboolean fs_modified = false;
@@ -1105,7 +1128,7 @@ void FS_Rescan_f (void)
                fs_modified = true;
                FS_AddGameHierarchy (fs_gamedirs[i]);
                // update the com_modname (used server info)
-               strlcpy (com_modname, com_argv[i], sizeof (com_modname));
+               strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
        }
 
        // set the default screenshot name to either the mod name or the
@@ -1135,29 +1158,67 @@ void FS_Rescan_f (void)
        }
 }
 
+void FS_Rescan_f(void)
+{
+       FS_Rescan();
+}
 
 /*
 ================
-FS_ChangeGameDir
+FS_ChangeGameDirs
 ================
 */
-void Host_SaveConfig_f (void);
-void Host_LoadConfig_f (void);
-qboolean FS_ChangeGameDir(const char *string)
+extern void Host_SaveConfig_f (void);
+extern void Host_LoadConfig_f (void);
+qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
 {
-       // if already using the requested gamedir, do nothing
-       if (fs_numgamedirs == 1 && !strcmp(fs_gamedirs[0], string))
-               return false;
+       int i;
+
+       if (fs_numgamedirs == numgamedirs)
+       {
+               for (i = 0;i < numgamedirs;i++)
+                       if (strcasecmp(fs_gamedirs[i], gamedirs[i]))
+                               break;
+               if (i == numgamedirs)
+                       return true; // already using this set of gamedirs, do nothing
+       }
+
+       if (numgamedirs > MAX_GAMEDIRS)
+       {
+               if (complain)
+                       Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
+               return false; // too many gamedirs
+       }
+
+       for (i = 0;i < numgamedirs;i++)
+       {
+               // if string is nasty, reject it
+               if(FS_CheckNastyPath(gamedirs[i], true))
+               {
+                       if (complain)
+                               Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]);
+                       return false; // nasty gamedirs
+               }
+       }
+
+       for (i = 0;i < numgamedirs;i++)
+       {
+               if (!FS_CheckGameDir(gamedirs[i]) && failmissing)
+               {
+                       if (complain)
+                               Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]);
+                       return false; // missing gamedirs
+               }
+       }
 
-       // save the current config
        Host_SaveConfig_f();
 
-       // change to the new gamedir
-       fs_numgamedirs = 1;
-       strlcpy(fs_gamedirs[0], string, sizeof(fs_gamedirs[0]));
+       fs_numgamedirs = numgamedirs;
+       for (i = 0;i < fs_numgamedirs;i++)
+               strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
 
        // reinitialize filesystem to detect the new paks
-       FS_Rescan_f();
+       FS_Rescan();
 
        // exec the new config
        Host_LoadConfig_f();
@@ -1179,6 +1240,8 @@ FS_GameDir_f
 void FS_GameDir_f (void)
 {
        int i;
+       int numgamedirs;
+       char gamedirs[MAX_GAMEDIRS][MAX_QPATH];
 
        if (Cmd_Argc() < 2)
        {
@@ -1189,32 +1252,45 @@ void FS_GameDir_f (void)
                return;
        }
 
-       if (cls.state != ca_disconnected || sv.active)
+       numgamedirs = Cmd_Argc() - 1;
+       if (numgamedirs > MAX_GAMEDIRS)
        {
-               Con_Printf("Can not change gamedir while client is connected or server is running!\n");
+               Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS);
                return;
        }
 
-       Host_SaveConfig_f();
+       for (i = 0;i < numgamedirs;i++)
+               strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
 
-       fs_numgamedirs = 0;
-       for (i = 1;i < Cmd_Argc() && fs_numgamedirs < MAX_GAMEDIRS;i++)
+       // allow gamedir change during demo loop
+       if (cls.demoplayback)
+               CL_Disconnect();
+
+       if (cls.state == ca_connected || sv.active)
        {
-               strlcpy(fs_gamedirs[fs_numgamedirs], Cmd_Argv(i), sizeof(fs_gamedirs[fs_numgamedirs]));
-               fs_numgamedirs++;
+               // actually, changing during game would work fine, but would be stupid
+               Con_Printf("Can not change gamedir while client is connected or server is running!\n");
+               return;
        }
 
-       // reinitialize filesystem to detect the new paks
-       FS_Rescan_f();
-
-       // exec the new config
-       Host_LoadConfig_f();
+       FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
+}
 
-       // reinitialize the loaded sounds
-       S_Reload_f();
 
-       // reinitialize renderer (this reloads hud/console background/etc)
-       R_Modules_Restart();
+/*
+================
+FS_CheckGameDir
+================
+*/
+qboolean FS_CheckGameDir(const char *gamedir)
+{
+       qboolean success;
+       stringlist_t list;
+       stringlistinit(&list);
+       listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
+       success = list.numstrings > 0;
+       stringlistfreecontents(&list);
+       return success;
 }
 
 
@@ -1270,6 +1346,12 @@ void FS_Init (void)
        if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
                strlcat(fs_basedir, "/", sizeof(fs_basedir));
 
+       if (!FS_CheckGameDir(gamedirname1))
+               Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1);
+
+       if (gamedirname2 && !FS_CheckGameDir(gamedirname2))
+               Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2);
+
        // -game <gamedir>
        // Adds basedir/gamedir as an override game
        // LordHavoc: now supports multiple -game directories
@@ -1280,14 +1362,18 @@ void FS_Init (void)
                if (!strcmp (com_argv[i], "-game") && i < com_argc-1)
                {
                        i++;
+                       if (FS_CheckNastyPath(com_argv[i], true))
+                               Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]);
+                       if (!FS_CheckGameDir(com_argv[i]))
+                               Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]);
                        // add the gamedir to the list of active gamedirs
                        strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
                        fs_numgamedirs++;
                }
        }
 
-       // update the searchpath
-       FS_Rescan_f();
+       // generate the searchpath
+       FS_Rescan();
 }
 
 void FS_Init_Commands(void)
@@ -1419,7 +1505,7 @@ qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
        if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
        {
                Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
-                                       pfile->name, pack->filename, pfile->offset);
+                                       pfile->name, pack->filename, (int) pfile->offset);
                return NULL;
        }
 
@@ -1490,8 +1576,12 @@ Return true if the path should be rejected due to one of the following:
    or are just not a good idea for a mod to be using.
 ====================
 */
-int FS_CheckNastyPath (const char *path)
+int FS_CheckNastyPath (const char *path, qboolean isgamedir)
 {
+       // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
+       if (!path[0])
+               return 2;
+
        // Windows: don't allow \ in filenames (windows-only), period.
        // (on Windows \ is a directory separator, but / is also supported)
        if (strstr(path, "\\"))
@@ -1508,14 +1598,34 @@ int FS_CheckNastyPath (const char *path)
        if (strstr(path, "//"))
                return 1; // non-portable attempt to go to parent directory
 
-       // all: don't allow going to current directory (./) or parent directory (../ or /../)
-       if (strstr(path, "./"))
+       // all: don't allow going to parent directory (../ or /../)
+       if (strstr(path, ".."))
                return 2; // attempt to go outside the game directory
 
        // Windows and UNIXes: don't allow absolute paths
        if (path[0] == '/')
                return 2; // attempt to go outside the game directory
 
+       // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements), this catches all imaginable cases of ./, ../, .../, etc
+       if (strchr(path, '.'))
+       {
+               if (isgamedir)
+               {
+                       // gamedir is entirely path elements, so simply forbid . entirely
+                       return 2;
+               }
+               if (strchr(path, '.') < strrchr(path, '/'))
+                       return 2; // possible attempt to go outside the game directory
+       }
+
+       // all: forbid trailing slash on gamedir
+       if (isgamedir && path[strlen(path)-1] == '/')
+               return 2;
+
+       // all: forbid leading dot on any filename for any reason
+       if (strstr(path, "/."))
+               return 2; // attempt to go outside the game directory
+
        // after all these checks we're pretty sure it's a / separated filename
        // and won't do much if any harm
        return false;
@@ -1651,7 +1761,7 @@ Open a file. The syntax is the same as fopen
 */
 qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking)
 {
-       if (FS_CheckNastyPath(filepath))
+       if (FS_CheckNastyPath(filepath, false))
        {
                Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false");
                return NULL;
@@ -2111,6 +2221,19 @@ fs_offset_t FS_Tell (qfile_t* file)
 }
 
 
+/*
+====================
+FS_FileSize
+
+Give the total size of a file
+====================
+*/
+fs_offset_t FS_FileSize (qfile_t* file)
+{
+       return file->real_length;
+}
+
+
 /*
 ====================
 FS_Purge
@@ -2306,8 +2429,9 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
        fssearch_t *search;
        searchpath_t *searchpath;
        pack_t *pak;
-       int i, basepathlength, numfiles, numchars;
-       stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
+       int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
+       stringlist_t resultlist;
+       stringlist_t dirlist;
        const char *slash, *backslash, *colon, *separator;
        char *basepath;
        char netpath[MAX_OSPATH];
@@ -2322,10 +2446,9 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
                return NULL;
        }
 
+       stringlistinit(&resultlist);
+       stringlistinit(&dirlist);
        search = NULL;
-       liststart = NULL;
-       listcurrent = NULL;
-       listtemp = NULL;
        slash = strrchr(pattern, '/');
        backslash = strrchr(pattern, '\\');
        colon = strrchr(pattern, ':');
@@ -2352,14 +2475,12 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
                                {
                                        if (matchpattern(temp, (char *)pattern, true))
                                        {
-                                               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
-                                                       if (!strcmp(listtemp->text, temp))
+                                               for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
+                                                       if (!strcmp(resultlist.strings[resultlistindex], temp))
                                                                break;
-                                               if (listtemp == NULL)
+                                               if (resultlistindex == resultlist.numstrings)
                                                {
-                                                       listcurrent = stringlistappend(listcurrent, temp);
-                                                       if (liststart == NULL)
-                                                               liststart = listcurrent;
+                                                       stringlistappend(&resultlist, temp);
                                                        if (!quiet)
                                                                Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
                                                }
@@ -2384,59 +2505,52 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
                {
                        // get a directory listing and look at each name
                        dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
-                       if ((dir = listdirectory(netpath)))
+                       stringlistinit(&dirlist);
+                       listdirectory(&dirlist, netpath);
+                       for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
                        {
-                               for (dirfile = dir;dirfile;dirfile = dirfile->next)
+                               dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
+                               if (matchpattern(temp, (char *)pattern, true))
                                {
-                                       dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirfile->text);
-                                       if (matchpattern(temp, (char *)pattern, true))
+                                       for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
+                                               if (!strcmp(resultlist.strings[resultlistindex], temp))
+                                                       break;
+                                       if (resultlistindex == resultlist.numstrings)
                                        {
-                                               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
-                                                       if (!strcmp(listtemp->text, temp))
-                                                               break;
-                                               if (listtemp == NULL)
-                                               {
-                                                       listcurrent = stringlistappend(listcurrent, temp);
-                                                       if (liststart == NULL)
-                                                               liststart = listcurrent;
-                                                       if (!quiet)
-                                                               Con_DPrintf("SearchDirFile: %s\n", temp);
-                                               }
+                                               stringlistappend(&resultlist, temp);
+                                               if (!quiet)
+                                                       Con_DPrintf("SearchDirFile: %s\n", temp);
                                        }
                                }
-                               freedirectory(dir);
                        }
+                       stringlistfreecontents(&dirlist);
                }
        }
 
-       if (liststart)
+       if (resultlist.numstrings)
        {
-               liststart = stringlistsort(liststart);
-               numfiles = 0;
+               stringlistsort(&resultlist);
+               numfiles = resultlist.numstrings;
                numchars = 0;
-               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
-               {
-                       numfiles++;
-                       numchars += (int)strlen(listtemp->text) + 1;
-               }
+               for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
+                       numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
                search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
                search->filenames = (char **)((char *)search + sizeof(fssearch_t));
                search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
                search->numfilenames = (int)numfiles;
                numfiles = 0;
                numchars = 0;
-               for (listtemp = liststart;listtemp;listtemp = listtemp->next)
+               for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
                {
                        size_t textlen;
                        search->filenames[numfiles] = search->filenamesbuffer + numchars;
-                       textlen = strlen(listtemp->text) + 1;
-                       memcpy(search->filenames[numfiles], listtemp->text, textlen);
+                       textlen = strlen(resultlist.strings[resultlistindex]) + 1;
+                       memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
                        numfiles++;
                        numchars += (int)textlen;
                }
-               if (liststart)
-                       stringlistfree(liststart);
        }
+       stringlistfreecontents(&resultlist);
 
        Mem_Free(basepath);
        return search;
@@ -2551,3 +2665,78 @@ const char *FS_WhichPack(const char *filename)
        else
                return 0;
 }
+
+/*
+====================
+FS_IsRegisteredQuakePack
+
+Look for a proof of purchase file file in the requested package
+
+If it is found, this file should NOT be downloaded.
+====================
+*/
+qboolean FS_IsRegisteredQuakePack(const char *name)
+{
+       searchpath_t *search;
+       pack_t *pak;
+
+       // search through the path, one element at a time
+       for (search = fs_searchpaths;search;search = search->next)
+       {
+               if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name))
+               {
+                       int (*strcmp_funct) (const char* str1, const char* str2);
+                       int left, right, middle;
+
+                       pak = search->pack;
+                       strcmp_funct = pak->ignorecase ? strcasecmp : strcmp;
+
+                       // Look for the file (binary search)
+                       left = 0;
+                       right = pak->numfiles - 1;
+                       while (left <= right)
+                       {
+                               int diff;
+
+                               middle = (left + right) / 2;
+                               diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
+
+                               // Found it
+                               if (!diff)
+                                       return true;
+
+                               // If we're too far in the list
+                               if (diff > 0)
+                                       right = middle - 1;
+                               else
+                                       left = middle + 1;
+                       }
+
+                       // we found the requested pack but it is not registered quake
+                       return false;
+               }
+       }
+
+       return false;
+}
+
+int FS_CRCFile(const char *filename, size_t *filesizepointer)
+{
+       int crc = -1;
+       unsigned char *filedata;
+       fs_offset_t filesize;
+       if (filesizepointer)
+               *filesizepointer = 0;
+       if (!filename || !*filename)
+               return crc;
+       filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
+       if (filedata)
+       {
+               if (filesizepointer)
+                       *filesizepointer = filesize;
+               crc = CRC_Block(filedata, filesize);
+               Mem_Free(filedata);
+       }
+       return crc;
+}
+