#include "fs.h"
#include "wad.h"
+#ifdef WIN32
+#include "utf8lib.h"
+#endif
+
// Win32 requires us to add O_BINARY, but the other OSes don't have it
#ifndef O_BINARY
# define O_BINARY 0
}
#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
+All of Quake's data access is through a hierarchical file system, the contents
of the file system can be transparently merged from several sources.
The "base directory" is the path to the directory holding the quake.exe and
The "game directory" is the first tree on the search path and directory that
all generated files (savegames, screenshots, demos, config files) will be
saved to. This can be overridden with the "-game" command line parameter.
-The game directory can never be changed while quake is executing. This is a
-precaution against having a malicious server instruct clients to write files
-over areas they shouldn't.
+If multiple "-game <gamedir>" args are passed the last one is the "primary"
+and files will be saved there, the rest are read-only.
*/
char filename [MAX_OSPATH];
char shortname [MAX_QPATH];
filedesc_t handle;
- int ignorecase; ///< PK3 ignores case
+ qbool ignorecase; ///< PK3 ignores case
int numfiles;
qbool vpack;
+ qbool dlcache;
packfile_t *files;
} pack_t;
//@}
void FS_Ls_f(cmd_state_t *cmd);
void FS_Which_f(cmd_state_t *cmd);
-static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet);
+static searchpath_t *FS_FindFile (const char *name, int *index, const char **canonicalname, qbool quiet);
static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack,
fs_offset_t offset, fs_offset_t packsize,
fs_offset_t realsize, int flags);
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)"};
#endif
#ifdef WIN32
-static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
+static HRESULT (WINAPI *qSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
static dllfunction_t shfolderfuncs[] =
{
- {"SHGetFolderPathA", (void **) &qSHGetFolderPath},
+ {"SHGetFolderPathW", (void **) &qSHGetFolderPath},
{NULL, NULL}
};
static const char* shfolderdllnames [] =
};
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
return -1;
}
- namesize = BuffLittleShort (&ptr[28]); // filename length
+ namesize = (unsigned short)BuffLittleShort (&ptr[28]); // filename length
// Check encryption, compression, and attributes
// 1st uint8 : general purpose bit flag
if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
{
// Still enough bytes for the name?
- if (namesize < 0 || remaining < namesize || namesize >= (int)sizeof (*pack->files))
+ if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
{
Mem_Free (central_dir);
return -1;
// Skip the name, additionnal field, and comment
// 1er uint16 : extra field length
// 2eme uint16 : file comment length
- count = namesize + BuffLittleShort (&ptr[30]) + BuffLittleShort (&ptr[32]);
+ count = namesize + (unsigned short)BuffLittleShort (&ptr[30]) + (unsigned short)BuffLittleShort (&ptr[32]);
ptr += ZIP_CDIR_CHUNK_BASE_SIZE + count;
remaining -= count;
}
// 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;
return pfile;
}
+#if WIN32
+#define WSTRBUF 4096
+static inline int wstrlen(wchar *wstr)
+{
+ int len = 0;
+ while (wstr[len] != 0 && len < WSTRBUF)
+ ++len;
+ return len;
+}
+#define widen(str, wstr) fromwtf8(str, strlen(str), wstr, WSTRBUF)
+#define narrow(wstr, str) towtf8(wstr, wstrlen(wstr), str, WSTRBUF)
+#endif
static void FS_mkdir (const char *path)
{
+#if WIN32
+ wchar pathw[WSTRBUF] = {0};
+#endif
if(Sys_CheckParm("-readonly"))
return;
#if WIN32
- if (_mkdir (path) == -1)
+ widen(path, pathw);
+ if (_wmkdir (pathw) == -1)
#else
if (mkdir (path, 0777) == -1)
#endif
}
}
-
/*
============
FS_CreatePath
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;
* plain directories.
*
*/
-static qbool FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qbool *already_loaded, qbool keep_plain_dirs)
+static qbool FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache)
{
searchpath_t *search;
pack_t *pak = 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)
fs_searchpaths = search;
}
search->pack = pak;
+ search->pack->dlcache = dlcache;
if(pak->vpack)
{
dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile);
* If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
* plain directories.
*/
-qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs)
+qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache)
{
char fullpath[MAX_OSPATH];
int index;
*already_loaded = false;
// then find the real name...
- search = FS_FindFile(pakfile, &index, true);
+ search = FS_FindFile(pakfile, &index, NULL, true);
if(!search || search->pack)
{
Con_Printf("could not find pak \"%s\"\n", pakfile);
dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile);
- return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs);
+ return FS_AddPack_Fullpath(fullpath, pakfile, already_loaded, keep_plain_dirs, dlcache);
}
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);
{
if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
{
- FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
+ FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false, false);
}
}
if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "obb") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir")
|| !strcasecmp(FS_FileExtension(list.strings[i]), "dpk") || !strcasecmp(FS_FileExtension(list.strings[i]), "dpkdir"))
{
- FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
+ FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false, false);
}
}
// 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;
}
}
}
+/*
+================
+FS_UnloadPacks_dlcache
+
+Like FS_ClearSearchPath() but unloads only the packs loaded from dlcache
+so we don't need to use a full FS_Rescan() to prevent
+content from the previous server and/or map from interfering with the next
+================
+*/
+void FS_UnloadPacks_dlcache(void)
+{
+ searchpath_t *search = fs_searchpaths, *searchprev = fs_searchpaths, *searchnext;
+
+ if (!fs_unload_dlcache.integer)
+ return;
+
+ while (search)
+ {
+ searchnext = search->next;
+ if (search->pack && search->pack->dlcache)
+ {
+ Con_DPrintf("Unloading pack: %s\n", search->pack->shortname);
+
+ // remove it from the search path list
+ if (search == fs_searchpaths)
+ fs_searchpaths = search->next;
+ else
+ searchprev->next = search->next;
+
+ // close the file
+ FILEDESC_CLOSE(search->pack->handle);
+ // free any memory associated with it
+ if (search->pack->files)
+ Mem_Free(search->pack->files);
+ Mem_Free(search->pack);
+ Mem_Free(search);
+ }
+ else
+ searchprev = search;
+ search = searchnext;
+ }
+}
+
static void FS_AddSelfPack(void)
{
if(fs_selfpack)
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
// add back the selfpack as new first item
FS_AddSelfPack();
- // set the default screenshot name to either the mod name or the
- // gamemode screenshot name
- if (strcmp(com_modname, gamedirname1))
- Cvar_SetQuick (&scr_screenshot_name, com_modname);
- else
- Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
-
+ if (cls.state != ca_dedicated)
+ {
+ // set the default screenshot name to either the mod name or the
+ // gamemode screenshot name
+ if (strcmp(com_modname, gamedirname1))
+ Cvar_SetQuick (&scr_screenshot_name, com_modname);
+ else
+ Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname);
+ }
+
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();
if (cls.demoplayback)
{
- CL_Disconnect(false, NULL);
+ CL_Disconnect();
cls.demonum = 0;
}
// unload all sounds so they will be reloaded from the new files as needed
S_UnloadAllSounds_f(cmd_local);
- // restart the video subsystem after the config is executed
- Cbuf_InsertText(cmd_local, "\nloadconfig\nvid_restart\n\n");
+ // reset everything that can be and reload configs
+ Cbuf_InsertText(cmd_local, "\nloadconfig\n");
return true;
}
}
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)
{
}
// halt demo playback to close the file
- CL_Disconnect(false, NULL);
+ CL_Disconnect();
FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
}
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;
#elif defined(WIN32)
- char *homedir;
+ char homedir[WSTRBUF];
+ wchar *homedirw;
#if _MSC_VER >= 1400
- size_t homedirlen;
+ size_t homedirwlen;
#endif
- TCHAR mydocsdir[MAX_PATH + 1];
+ wchar_t mydocsdirw[WSTRBUF];
+ char mydocsdir[WSTRBUF];
wchar_t *savedgamesdirw;
- char savedgamesdir[MAX_OSPATH];
+ char savedgamesdir[WSTRBUF] = {0};
int fd;
char vabuf[1024];
default:
return -1;
case USERDIRMODE_NOHOME:
- strlcpy(userdir, fs_basedir, userdirsize);
+ dp_strlcpy(userdir, fs_basedir, userdirsize);
break;
case USERDIRMODE_MYGAMES:
if (!shfolder_dll)
Sys_LoadDependency(shfolderdllnames, &shfolder_dll, shfolderfuncs);
mydocsdir[0] = 0;
- if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
+ if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdirw) == S_OK)
{
+ narrow(mydocsdirw, mydocsdir);
dpsnprintf(userdir, userdirsize, "%s/My Games/%s/", mydocsdir, gameuserdirname);
break;
}
#if _MSC_VER >= 1400
- _dupenv_s(&homedir, &homedirlen, "USERPROFILE");
- if(homedir)
+ _wdupenv_s(&homedirw, &homedirwlen, L"USERPROFILE");
+ narrow(homedirw, homedir);
+ if(homedir[0])
{
dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
- free(homedir);
+ free(homedirw);
break;
}
#else
- homedir = getenv("USERPROFILE");
- if(homedir)
+ homedirw = _wgetenv(L"USERPROFILE");
+ narrow(homedirw, homedir);
+ if(homedir[0])
{
dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
break;
*/
if (qSHGetKnownFolderPath(&qFOLDERID_SavedGames, qKF_FLAG_CREATE | qKF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
{
- memset(savedgamesdir, 0, sizeof(savedgamesdir));
-#if _MSC_VER >= 1400
- wcstombs_s(NULL, savedgamesdir, sizeof(savedgamesdir), savedgamesdirw, sizeof(savedgamesdir)-1);
-#else
- wcstombs(savedgamesdir, savedgamesdirw, sizeof(savedgamesdir)-1);
-#endif
+ narrow(savedgamesdirw, savedgamesdir);
qCoTaskMemFree(savedgamesdirw);
}
qCoUninitialize();
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)");
+ Cmd_AddCommand(CF_SHARED, "gamedir", FS_GameDir_f, "changes active gamedir list, can take multiple arguments which shouldn't include the base directory, the last gamedir is the \"primary\" and files will be saved there (example usage: gamedir ctf id1)");
Cmd_AddCommand(CF_SHARED, "fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
Cmd_AddCommand(CF_SHARED, "path", FS_Path_f, "print searchpath (game directories and archives)");
Cmd_AddCommand(CF_SHARED, "dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
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
{
}
}
}
+#else
+ // use the working directory
+ getcwd(fs_basedir, sizeof(fs_basedir));
#endif
}
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++;
}
}
int mod, opt;
unsigned int ind;
qbool dolock = false;
+ #ifdef WIN32
+ wchar filepathw[WSTRBUF] = {0};
+ #endif
// Parse the mode string
switch (mode[0])
handle = SDL_RWFromFile(filepath, mode);
#else
# ifdef WIN32
+ widen(filepath, filepathw);
# if _MSC_VER >= 1400
- _sopen_s(&handle, filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+ _wsopen_s(&handle, filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
# else
- handle = _sopen (filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+ handle = _wsopen (filepathw, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
# endif
# else
handle = open (filepath, mod | opt, 0666);
====================
FS_SanitizePath
-Sanitize path (replace non-portable characters
+Sanitize path (replace non-portable characters
with portable ones in-place, etc)
====================
*/
and the file index in the package if relevant
====================
*/
-static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet)
+static searchpath_t *FS_FindFile (const char *name, int *index, const char **canonicalname, qbool quiet)
{
searchpath_t *search;
pack_t *pak;
if (index != NULL)
*index = -1;
+ if (canonicalname)
+ *canonicalname = NULL;
return NULL;
}
if (!quiet && developer_extra.integer)
- Con_DPrintf("FS_FindFile: %s in %s\n",
- pak->files[middle].name, pak->filename);
+ Con_DPrintf("FS_FindFile: %s in %s\n", pak->files[middle].name, pak->filename);
if (index != NULL)
*index = middle;
+ if (canonicalname)
+ *canonicalname = pak->files[middle].name;
return search;
}
if (index != NULL)
*index = -1;
+ if (canonicalname)
+ *canonicalname = name;
return search;
}
}
if (index != NULL)
*index = -1;
+ if (canonicalname)
+ *canonicalname = NULL;
return NULL;
}
searchpath_t *search;
int pack_ind;
- search = FS_FindFile (filename, &pack_ind, quiet);
+ search = FS_FindFile (filename, &pack_ind, NULL, quiet);
// Not found?
if (search == NULL)
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);
}
searchpath_t *search;
char fullpath[MAX_OSPATH];
- search = FS_FindFile (filename, NULL, true);
+ search = FS_FindFile (filename, NULL, NULL, true);
if(!search)
return FS_FILETYPE_NONE;
FS_FileExists
Look for a file in the packages and in the filesystem
+Returns its canonical name (VFS path with correct capitalisation) if found, else NULL.
+If the file is found outside a pak, this will be the same pointer as passed in.
==================
*/
-qbool FS_FileExists (const char *filename)
+const char *FS_FileExists (const char *filename)
{
- return (FS_FindFile (filename, NULL, true) != NULL);
+ const char *canonicalname;
+
+ return FS_FindFile(filename, NULL, &canonicalname, true) ? canonicalname : NULL;
}
# ifndef INVALID_FILE_ATTRIBUTES
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
# endif
-
- DWORD result = GetFileAttributes(path);
+ wchar pathw[WSTRBUF] = {0};
+ DWORD result;
+ widen(path, pathw);
+ result = GetFileAttributesW(pathw);
if(result == INVALID_FILE_ATTRIBUTES)
return FS_FILETYPE_NONE;
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);
+ sp = FS_FindFile(filename, &index, NULL, true);
if (!sp) {
Con_Printf("%s isn't anywhere\n", filename);
return;
const char *FS_WhichPack(const char *filename)
{
int index;
- searchpath_t *sp = FS_FindFile(filename, &index, true);
+ searchpath_t *sp = FS_FindFile(filename, &index, NULL, true);
if(sp && sp->pack)
return sp->pack->shortname;
else 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;
}