}
#endif
+
+/* This code seems to have originally been written with the assumption that
+ * read(..., n) returns n on success. This is not the case (refer to
+ * <https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html>).
+ * Ditto for write.
+ */
+
+/*
+====================
+ReadAll
+
+Read exactly length bytes from fd into buf. If end of file is reached,
+the number of bytes read is returned. If an error occurred, that error
+is returned. Note that if an error is returned, any previously read
+data is lost.
+====================
+*/
+static fs_offset_t ReadAll(const filedesc_t fd, void *const buf, const size_t length)
+{
+ char *const p = (char *)buf;
+ size_t cursor = 0;
+ do
+ {
+ const fs_offset_t result = FILEDESC_READ(fd, p + cursor, length - cursor);
+ if (result < 0) // Error
+ return result;
+ if (result == 0) // EOF
+ break;
+ cursor += result;
+ } while (cursor < length);
+ return cursor;
+}
+
+/*
+====================
+WriteAll
+
+Write exactly length bytes to fd from buf.
+If an error occurred, that error is returned.
+====================
+*/
+static fs_offset_t WriteAll(const filedesc_t fd, const void *const buf, const size_t length)
+{
+ const char *const p = (const char *)buf;
+ size_t cursor = 0;
+ do
+ {
+ const fs_offset_t result = FILEDESC_WRITE(fd, p + cursor, length - cursor);
+ if (result < 0) // Error
+ return result;
+ cursor += result;
+ } while (cursor < length);
+ return cursor;
+}
+
+#undef FILEDESC_READ
+#define FILEDESC_READ ReadAll
+#undef FILEDESC_WRITE
+#define FILEDESC_WRITE WriteAll
+
/** \page fs File System
All of Quake's data access is through a hierchal file system, but the contents
cvar_t scr_screenshot_name = {CF_CLIENT | CF_PERSISTENT, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running; the date is encoded using strftime escapes)"};
cvar_t fs_empty_files_in_pack_mark_deletions = {CF_CLIENT | CF_SERVER, "fs_empty_files_in_pack_mark_deletions", "0", "if enabled, empty files in a pak/pk3 count as not existing but cancel the search in further packs, effectively allowing patch pak/pk3 files to 'delete' files"};
+cvar_t fs_unload_dlcache = {CF_CLIENT, "fs_unload_dlcache", "1", "if enabled, unload dlcache's loaded pak/pk3 files when changing server and/or map WARNING: disabling unloading can cause servers to override assets of other servers, \"memory leaking\" by dlcache assets never unloading and many more issues"};
cvar_t cvar_fs_gamedir = {CF_CLIENT | CF_SERVER | CF_READONLY | CF_PERSISTENT, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"};
};
static dllhandle_t shfolder_dll = NULL;
-const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}};
+const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}};
#define qREFKNOWNFOLDERID const GUID *
#define qKF_FLAG_CREATE 0x8000
#define qKF_FLAG_NO_ALIAS 0x1000
// Create a package structure in memory
pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
pack->ignorecase = true; // PK3 ignores case
- strlcpy (pack->filename, packfile, sizeof (pack->filename));
+ dp_strlcpy (pack->filename, packfile, sizeof (pack->filename));
pack->handle = packhandle;
pack->numfiles = eocd.nbentries;
pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t));
memmove (pfile + 1, pfile, (pack->numfiles - left) * sizeof (*pfile));
pack->numfiles++;
- strlcpy (pfile->name, name, sizeof (pfile->name));
+ dp_strlcpy (pfile->name, name, sizeof (pfile->name));
pfile->offset = offset;
pfile->packsize = packsize;
pfile->realsize = realsize;
pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
pack->ignorecase = true; // PAK is sensitive in Quake1 but insensitive in Quake2
- strlcpy (pack->filename, packfile, sizeof (pack->filename));
+ dp_strlcpy (pack->filename, packfile, sizeof (pack->filename));
pack->handle = packhandle;
pack->numfiles = 0;
pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t));
pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
pack->vpack = true;
pack->ignorecase = false;
- strlcpy (pack->filename, dirname, sizeof(pack->filename));
+ dp_strlcpy (pack->filename, dirname, sizeof(pack->filename));
pack->handle = FILEDESC_INVALID;
pack->numfiles = -1;
pack->files = NULL;
if(pak)
{
- strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
+ dp_strlcpy(pak->shortname, shortname, sizeof(pak->shortname));
//Con_DPrintf(" Registered pack with short name %s\n", shortname);
if(keep_plain_dirs)
stringlist_t list;
searchpath_t *search;
- strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
+ dp_strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
stringlistinit(&list);
listdirectory(&list, "", dir);
// Add the directory to the search path
// (unpacked files have the priority over packed files)
search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t));
- strlcpy (search->filename, dir, sizeof (search->filename));
+ dp_strlcpy (search->filename, dir, sizeof (search->filename));
search->next = fs_searchpaths;
fs_searchpaths = search;
}
{
searchpath_t *search = fs_searchpaths, *searchprev = fs_searchpaths, *searchnext;
+ if (!fs_unload_dlcache.integer)
+ return;
+
while (search)
{
searchnext = search->next;
FS_AddGameHierarchy (gamedirname1);
// update the com_modname (used for server info)
if (gamedirname2 && gamedirname2[0])
- strlcpy(com_modname, gamedirname2, sizeof(com_modname));
+ dp_strlcpy(com_modname, gamedirname2, sizeof(com_modname));
else
- strlcpy(com_modname, gamedirname1, sizeof(com_modname));
+ dp_strlcpy(com_modname, gamedirname1, sizeof(com_modname));
// add the game-specific path, if any
// (only used for mission packs and the like, which should set fs_modified)
fs_modified = true;
FS_AddGameHierarchy (fs_gamedirs[i]);
// update the com_modname (used server info)
- strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
+ dp_strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
if(i)
- strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf));
+ dp_strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf));
else
- strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
+ dp_strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
}
Cvar_SetQuick(&cvar_fs_gamedir, gamedirbuf); // so QC or console code can query it
}
if((i = Sys_CheckParm("-modname")) && i < sys.argc - 1)
- strlcpy(com_modname, sys.argv[i+1], sizeof(com_modname));
+ dp_strlcpy(com_modname, sys.argv[i+1], sizeof(com_modname));
// If "-condebug" is in the command line, remove the previous log file
if (Sys_CheckParm ("-condebug") != 0)
fs_numgamedirs = numgamedirs;
for (i = 0;i < fs_numgamedirs;i++)
- strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
+ dp_strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i]));
// reinitialize filesystem to detect the new paks
FS_Rescan();
}
for (i = 0;i < numgamedirs;i++)
- strlcpy(gamedirs[i], Cmd_Argv(cmd, i+1), sizeof(gamedirs[i]));
+ dp_strlcpy(gamedirs[i], Cmd_Argv(cmd, i+1), sizeof(gamedirs[i]));
if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
{
ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
if(ret)
return ret;
-
+
return fs_checkgamedir_missing;
}
continue;
if(!*info)
continue;
- stringlistappend(&list2, list.strings[i]);
+ stringlistappend(&list2, list.strings[i]);
}
stringlistfreecontents(&list);
continue;
if(!*info)
continue;
- strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[fs_all_gamedirs_count].name));
- strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[fs_all_gamedirs_count].description));
+ dp_strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[fs_all_gamedirs_count].name));
+ dp_strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[fs_all_gamedirs_count].description));
++fs_all_gamedirs_count;
}
}
if(i > args_left)
break;
q = (char *)Mem_Alloc(fs_mempool, sz);
- strlcpy(q, com_token, sz);
+ dp_strlcpy(q, com_token, sz);
new_argv[i] = q;
++i;
}
{
// fs_basedir is "" by default, to utilize this you can simply add your gamedir to the Resources in xcode
// fs_userdir stores configurations to the Documents folder of the app
- strlcpy(userdir, "../Documents/", MAX_OSPATH);
+ dp_strlcpy(userdir, "../Documents/", MAX_OSPATH);
return 1;
}
return -1;
default:
return -1;
case USERDIRMODE_NOHOME:
- strlcpy(userdir, fs_basedir, userdirsize);
+ dp_strlcpy(userdir, fs_basedir, userdirsize);
break;
case USERDIRMODE_MYGAMES:
if (!shfolder_dll)
default:
return -1;
case USERDIRMODE_NOHOME:
- strlcpy(userdir, fs_basedir, userdirsize);
+ dp_strlcpy(userdir, fs_basedir, userdirsize);
break;
case USERDIRMODE_HOME:
homedir = getenv("HOME");
{
Cvar_RegisterVariable (&scr_screenshot_name);
Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
+ Cvar_RegisterVariable (&fs_unload_dlcache);
Cvar_RegisterVariable (&cvar_fs_gamedir);
Cmd_AddCommand(CF_SHARED, "gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
i = Sys_CheckParm ("-basedir");
if (i && i < sys.argc-1)
{
- strlcpy (fs_basedir, sys.argv[i+1], sizeof (fs_basedir));
+ dp_strlcpy (fs_basedir, sys.argv[i+1], sizeof (fs_basedir));
i = (int)strlen (fs_basedir);
if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
fs_basedir[i-1] = 0;
{
// If the base directory is explicitly defined by the compilation process
#ifdef DP_FS_BASEDIR
- strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
+ dp_strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
#elif defined(__ANDROID__)
dpsnprintf(fs_basedir, sizeof(fs_basedir), "/sdcard/%s/", gameuserdirname);
#elif defined(MACOSX)
if (strstr(sys.argv[0], ".app/"))
{
char *split;
- strlcpy(fs_basedir, sys.argv[0], sizeof(fs_basedir));
+ dp_strlcpy(fs_basedir, sys.argv[0], sizeof(fs_basedir));
split = strstr(fs_basedir, ".app/");
if (split)
{
if (stat(va(vabuf, sizeof(vabuf), "%s/Contents/Resources/%s", fs_basedir, gamedirname1), &statresult) == 0)
{
// found gamedir inside Resources, use it
- strlcat(fs_basedir, "Contents/Resources/", sizeof(fs_basedir));
+ dp_strlcat(fs_basedir, "Contents/Resources/", sizeof(fs_basedir));
}
else
{
memset(fs_basedir + sizeof(fs_basedir) - 2, 0, 2);
// add a path separator to the end of the basedir if it lacks one
if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\')
- strlcat(fs_basedir, "/", sizeof(fs_basedir));
+ dp_strlcat(fs_basedir, "/", sizeof(fs_basedir));
// Add the personal game directory
if((i = Sys_CheckParm("-userdir")) && i < sys.argc - 1)
else
{
#ifdef DP_FS_USERDIR
- strlcpy(fs_userdir, DP_FS_USERDIR, sizeof(fs_userdir));
+ dp_strlcpy(fs_userdir, DP_FS_USERDIR, sizeof(fs_userdir));
#else
int dirmode;
int highestuserdirmode = USERDIRMODE_COUNT - 1;
if(p == fs_checkgamedir_missing)
Con_Printf(CON_WARN "WARNING: -game %s%s/ not found!\n", fs_basedir, sys.argv[i]);
// add the gamedir to the list of active gamedirs
- strlcpy (fs_gamedirs[fs_numgamedirs], sys.argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
+ dp_strlcpy (fs_gamedirs[fs_numgamedirs], sys.argv[i], sizeof(fs_gamedirs[fs_numgamedirs]));
fs_numgamedirs++;
}
}
====================
FS_SanitizePath
-Sanitize path (replace non-portable characters
+Sanitize path (replace non-portable characters
with portable ones in-place, etc)
====================
*/
if(count < 0)
return NULL;
linkbuf[count] = 0;
-
+
// Now combine the paths...
mergeslash = strrchr(filename, '/');
mergestart = linkbuf;
src--;
}
- strlcat (path, extension, size_path);
+ dp_strlcat (path, extension, size_path);
}
for (i = 0;i < pak->numfiles;i++)
{
char temp[MAX_OSPATH];
- strlcpy(temp, pak->files[i].name, sizeof(temp));
+ dp_strlcpy(temp, pak->files[i].name, sizeof(temp));
while (temp[0])
{
if (matchpattern(temp, (char *)pattern, true))
// prevseparator points past the '/' right before the wildcard and nextseparator at the one following it (or at the end of the string)
// copy everything up except nextseperator
- strlcpy(subpattern, pattern, min(sizeof(subpattern), (size_t) (nextseparator - pattern + 1)));
+ dp_ustr2stp(subpattern, sizeof(subpattern), pattern, nextseparator - pattern);
// find the last '/' before the wildcard
prevseparator = strrchr( subpattern, '/' );
if (!prevseparator)
prevseparator++;
// copy everything from start to the previous including the '/' (before the wildcard)
// everything up to start is already included in the path of matchedSet's entries
- strlcpy(subpath, start, min(sizeof(subpath), (size_t) ((prevseparator - subpattern) - (start - pattern) + 1)));
+ dp_ustr2stp(subpath, sizeof(subpath), start, (prevseparator - subpattern) - (start - pattern));
// for each entry in matchedSet try to open the subdirectories specified in subpath
for( dirlistindex = 0 ; dirlistindex < matchedSet.numstrings ; dirlistindex++ ) {
char temp[MAX_OSPATH];
- strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
- strlcat( temp, subpath, sizeof(temp) );
+ dp_strlcpy( temp, matchedSet.strings[ dirlistindex ], sizeof(temp) );
+ dp_strlcat( temp, subpath, sizeof(temp) );
listdirectory( &foundSet, searchpath->filename, temp );
}
if( dirlistindex == 0 ) {
{
Con_Printf("usage:\n%s <file>\n", Cmd_Argv(cmd, 0));
return;
- }
+ }
filename = Cmd_Argv(cmd, 1);
sp = FS_FindFile(filename, &index, true);
if (!sp) {
Mem_Free(tmp);
return NULL;
}
-
+
if(qz_deflateEnd(&strm) != Z_OK)
{
Con_Printf("FS_Deflate: deflateEnd failed\n");
memcpy(out, tmp, strm.total_out);
Mem_Free(tmp);
-
+
return out;
}
case Z_STREAM_END:
case Z_OK:
break;
-
+
case Z_STREAM_ERROR:
Con_Print("FS_Inflate: stream error!\n");
break;
default:
Con_Print("FS_Inflate: unknown error!\n");
break;
-
+
}
if(ret != Z_OK && ret != Z_STREAM_END)
{
Mem_Free(outbuf.data);
*inflated_size = (size_t)outbuf.cursize;
-
+
return out;
}