]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - fs.c
added BoxInsideBox macro
[xonotic/darkplaces.git] / fs.c
diff --git a/fs.c b/fs.c
index 50490bfba1c0c11bc16b84629459968b01940942..9219f4e1afc417c7feeedcb0736b38d60a434ff0 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,
@@ -980,7 +979,7 @@ void FS_AddGameDirectory (const char *dir)
                }
        }
 
-       // add any PK3 package in the director
+       // add any PK3 package in the directory
        for (current = list;current;current = current->next)
        {
                if (!strcasecmp(FS_FileExtension(current->text), "pk3"))
@@ -989,6 +988,7 @@ void FS_AddGameDirectory (const char *dir)
                        FS_AddPack_Fullpath(pakfile, NULL, false);
                }
        }
+
        freedirectory(list);
 
        // Add the directory to the search path
@@ -1028,7 +1028,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 +1048,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 +1092,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;
@@ -1135,29 +1155,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 +1237,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 +1249,42 @@ 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_disconnected || 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)
+{
+       stringlist_t *list = listdirectory(va("%s%s/", fs_basedir, gamedir));
+       if (list)
+               freedirectory(list);
+       return list != NULL;
 }
 
 
@@ -1270,6 +1340,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 +1356,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 +1499,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 +1570,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 +1592,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 +1755,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 +2215,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
@@ -2551,3 +2668,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;
+}
+