+Return true if the path should be rejected due to one of the following:
+1: path elements that are non-portable
+2: path elements that would allow access to files outside the game directory,
+ or are just not a good idea for a mod to be using.
+====================
+*/
+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, "\\"))
+ return 1; // non-portable
+
+ // Mac: don't allow Mac-only filenames - : is a directory separator
+ // instead of /, but we rely on / working already, so there's no reason to
+ // support a Mac-only path
+ // Amiga and Windows: : tries to go to root of drive
+ if (strstr(path, ":"))
+ return 1; // non-portable attempt to go to root of drive
+
+ // Amiga: // is parent directory
+ if (strstr(path, "//"))
+ return 1; // non-portable attempt to go to parent directory
+
+ // 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;
+}
+
+
+/*
+====================
+FS_FindFile
+
+Look for a file in the packages and in the filesystem
+
+Return the searchpath where the file was found (or NULL)
+and the file index in the package if relevant
+====================
+*/
+static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
+{
+ searchpath_t *search;
+ pack_t *pak;
+
+ // search through the path, one element at a time
+ for (search = fs_searchpaths;search;search = search->next)
+ {
+ // is the element a pak file?
+ if (search->pack)
+ {
+ 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, name);
+
+ // Found it
+ if (!diff)
+ {
+ if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
+ {
+ // yes, but the first one is empty so we treat it as not being there
+ if (!quiet && developer.integer >= 10)
+ Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
+
+ if (index != NULL)
+ *index = -1;
+ return NULL;
+ }
+
+ if (!quiet && developer.integer >= 10)
+ Con_Printf("FS_FindFile: %s in %s\n",
+ pak->files[middle].name, pak->filename);
+
+ if (index != NULL)
+ *index = middle;
+ return search;
+ }
+
+ // If we're too far in the list
+ if (diff > 0)
+ right = middle - 1;
+ else
+ left = middle + 1;
+ }
+ }
+ else
+ {
+ char netpath[MAX_OSPATH];
+ dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name);
+ if (FS_SysFileExists (netpath))
+ {
+ if (!quiet && developer.integer >= 10)
+ Con_Printf("FS_FindFile: %s\n", netpath);
+
+ if (index != NULL)
+ *index = -1;
+ return search;
+ }
+ }
+ }
+
+ if (!quiet && developer.integer >= 10)
+ Con_Printf("FS_FindFile: can't find %s\n", name);
+
+ if (index != NULL)
+ *index = -1;
+ return NULL;
+}
+
+
+/*
+===========
+FS_OpenReadFile
+
+Look for a file in the search paths and open it in read-only mode
+===========
+*/
+qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking)
+{
+ searchpath_t *search;
+ int pack_ind;
+
+ search = FS_FindFile (filename, &pack_ind, quiet);
+
+ // Not found?
+ if (search == NULL)
+ return NULL;
+
+ // Found in the filesystem?
+ if (pack_ind < 0)
+ {
+ char path [MAX_OSPATH];
+ dpsnprintf (path, sizeof (path), "%s%s", search->filename, filename);
+ return FS_SysOpen (path, "rb", nonblocking);