Normalize pathes passed to FS_FOpenFile().

There're some maps and maybe models or even mods in the wild which have
hardcoded paths with self references (`/./`) and / or empty diretories
(`//`). These assets works when read from the filesystem, but not when
read from PAK or ZIP files. Work around that by removing self references
and empty directories from the path right before opening the file.

Closes #767.
This commit is contained in:
Yamagi 2021-11-13 10:58:12 +01:00
parent 42d61449b1
commit 09001d3bfb

View file

@ -369,7 +369,7 @@ FS_FCloseFile(fileHandle_t f)
* for streaming data out of either a pak file or a seperate file.
*/
int
FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
FS_FOpenFile(const char *rawname, fileHandle_t *f, qboolean gamedir_only)
{
char path[MAX_OSPATH], lwrName[MAX_OSPATH];
fsHandle_t *handle;
@ -377,6 +377,58 @@ FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
fsSearchPath_t *search;
int i;
// Remove self references and empty dirs from the requested path.
// ZIPs and PAKs don't support them, but they may be hardcoded in
// some custom maps or models.
char name[MAX_QPATH] = {};
size_t namelen = strlen(rawname);
for (int input = 0, output = 0; input < namelen; input++)
{
// Remove self reference.
if (rawname[input] == '.')
{
if (output > 0)
{
// Inside the path.
if (name[output - 1] == '/' && rawname[input + 1] == '/')
{
input++;
continue;
}
}
else
{
// At the beginning. Note: This is save because the Quake II
// VFS doesn't have a current working dir. Paths are always
// absolute.
if (rawname[input + 1] == '/')
{
continue;
}
}
}
// Empty dir.
if (rawname[input] == '/')
{
if (rawname[input + 1] == '/')
{
continue;
}
}
// Pathes starting with a /. I'm not sure if this is
// a problem. It shouldn't hurt to remove the leading
// slash, though.
if (rawname[input] == '/' && output == 0)
{
continue;
}
name[output] = rawname[input];
output++;
}
file_from_protected_pak = false;
handle = FS_HandleForFile(name, f);
Q_strlcpy(handle->name, name, sizeof(handle->name));