#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
filedesc_t handle;
int ignorecase; ///< PK3 ignores case
int numfiles;
- qboolean vpack;
+ 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, qboolean quiet);
+static searchpath_t *FS_FindFile (const char *name, int* index, 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);
gamedir_t *fs_all_gamedirs = NULL;
int fs_all_gamedirs_count = 0;
-cvar_t scr_screenshot_name = {CVAR_CLIENT | CVAR_NORESETTODEFAULTS, "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 = {CVAR_CLIENT | CVAR_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 cvar_fs_gamedir = {CVAR_CLIENT | CVAR_SERVER | CVAR_READONLY | CVAR_NORESETTODEFAULTS, "fs_gamedir", "", "the list of currently selected gamedirs (use the 'gamedir' command to change this)"};
+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
static void PK3_CloseLibrary (void)
{
#ifndef LINK_TO_ZLIB
- Sys_UnloadLibrary (&zlib_dll);
+ Sys_FreeLibrary (&zlib_dll);
#endif
}
Try to load the Zlib DLL
====================
*/
-static qboolean PK3_OpenLibrary (void)
+static qbool PK3_OpenLibrary (void)
{
#ifdef LINK_TO_ZLIB
return true;
return true;
// Load the DLL
- return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs);
+ return Sys_LoadDependency (dllnames, &zlib_dll, zlibfuncs);
#endif
}
See if zlib is available
====================
*/
-qboolean FS_HasZlib(void)
+qbool FS_HasZlib(void)
{
#ifdef LINK_TO_ZLIB
return true;
Extract the end of the central directory from a PK3 package
====================
*/
-static qboolean PK3_GetEndOfCentralDir (const char *packfile, filedesc_t packhandle, pk3_endOfCentralDir_t *eocd)
+static qbool PK3_GetEndOfCentralDir (const char *packfile, filedesc_t packhandle, pk3_endOfCentralDir_t *eocd)
{
fs_offset_t filesize, maxsize;
unsigned char *buffer, *ptr;
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 entry associated with a PK3 file
====================
*/
-static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle, qboolean silent)
+static pack_t *FS_LoadPackPK3FromFD (const char *packfile, filedesc_t packhandle, qbool silent)
{
pk3_endOfCentralDir_t eocd;
pack_t *pack;
return pack;
}
-static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking);
+static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbool nonblocking);
static pack_t *FS_LoadPackPK3 (const char *packfile)
{
filedesc_t packhandle;
Find where the true file data offset is
====================
*/
-static qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
+static qbool PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
{
unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
fs_offset_t count;
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(COM_CheckParm("-readonly"))
+#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
* plain directories.
*
*/
-static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, qboolean *already_loaded, qboolean 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(already_loaded)
*already_loaded = false;
- if(!strcasecmp(ext, "pk3dir"))
+ if(!strcasecmp(ext, "pk3dir") || !strcasecmp(ext, "dpkdir"))
pak = FS_LoadPackVirtual (pakfile);
else if(!strcasecmp(ext, "pak"))
pak = FS_LoadPackPAK (pakfile);
- else if(!strcasecmp(ext, "pk3"))
+ else if(!strcasecmp(ext, "pk3") || !strcasecmp(ext, "dpk"))
pak = FS_LoadPackPK3 (pakfile);
else if(!strcasecmp(ext, "obb")) // android apk expansion
pak = FS_LoadPackPK3 (pakfile);
fs_searchpaths = search;
}
search->pack = pak;
+ search->pack->dlcache = dlcache;
if(pak->vpack)
{
dpsnprintf(search->filename, sizeof(search->filename), "%s/", pakfile);
- // if shortname ends with "pk3dir", strip that suffix to make it just "pk3"
+ // if shortname ends with "pk3dir" or "dpkdir", strip that suffix to make it just "pk3" or "dpk"
// same goes for the name inside the pack structure
l = strlen(pak->shortname);
if(l >= 7)
- if(!strcasecmp(pak->shortname + l - 7, ".pk3dir"))
+ if(!strcasecmp(pak->shortname + l - 7, ".pk3dir") || !strcasecmp(pak->shortname + l - 7, ".dpkdir"))
pak->shortname[l - 3] = 0;
l = strlen(pak->filename);
if(l >= 7)
- if(!strcasecmp(pak->filename + l - 7, ".pk3dir"))
+ if(!strcasecmp(pak->filename + l - 7, ".pk3dir") || !strcasecmp(pak->filename + l - 7, ".dpkdir"))
pak->filename[l - 3] = 0;
}
return true;
* If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
* plain directories.
*/
-qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs)
+qbool FS_AddPack(const char *pakfile, qbool *already_loaded, qbool keep_plain_dirs, qbool dlcache)
{
char fullpath[MAX_OSPATH];
int index;
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);
}
{
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);
}
}
// add any PK3 package in the directory
for (i = 0;i < list.numstrings;i++)
{
- if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "obb") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir"))
+ 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);
}
}
{
const char *separator, *backslash, *colon, *dot;
+ dot = strrchr(in, '.');
+ if (dot == NULL)
+ return "";
+
separator = strrchr(in, '/');
backslash = strrchr(in, '\\');
if (!separator || separator < backslash)
if (!separator || separator < colon)
separator = colon;
- dot = strrchr(in, '.');
- if (dot == NULL || (separator && (dot < separator)))
+ if (separator && (dot < separator))
return "";
return dot + 1;
}
}
+/*
+================
+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)
void FS_Rescan (void)
{
int i;
- qboolean fs_modified = false;
- qboolean reset = false;
+ qbool fs_modified = false;
+ qbool reset = false;
char gamedirbuf[MAX_INPUTLINE];
char vabuf[1024];
// 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((i = COM_CheckParm("-modname")) && i < sys.argc - 1)
+ 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));
// If "-condebug" is in the command line, remove the previous log file
- if (COM_CheckParm ("-condebug") != 0)
+ if (Sys_CheckParm ("-condebug") != 0)
unlink (va(vabuf, sizeof(vabuf), "%s/qconsole.log", fs_gamedir));
// look for the pop.lmp file and set registered to true if it is found
FS_ChangeGameDirs
================
*/
-extern qboolean vid_opened;
-qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
+extern qbool vid_opened;
+qbool FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qbool complain, qbool failmissing)
{
int i;
const char *p;
}
}
- Host_SaveConfig();
+ Host_SaveConfig(CONFIGFILENAME);
fs_numgamedirs = numgamedirs;
for (i = 0;i < fs_numgamedirs;i++)
if (cls.demoplayback)
{
- CL_Disconnect_f(&cmd_client);
+ CL_Disconnect();
cls.demonum = 0;
}
// unload all sounds so they will be reloaded from the new files as needed
- S_UnloadAllSounds_f(&cmd_client);
+ S_UnloadAllSounds_f(cmd_local);
// restart the video subsystem after the config is executed
- Cbuf_InsertText(&cmd_client, "\nloadconfig\nvid_restart\n\n");
+ Cbuf_InsertText(cmd_local, "\nloadconfig\nvid_restart\n\n");
return true;
}
static const char *FS_SysCheckGameDir(const char *gamedir, char *buf, size_t buflength)
{
- qboolean success;
+ qbool success;
qfile_t *f;
stringlist_t list;
fs_offset_t n;
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);
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];
break;
case USERDIRMODE_MYGAMES:
if (!shfolder_dll)
- Sys_LoadLibrary(shfolderdllnames, &shfolder_dll, shfolderfuncs);
+ 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;
return -1;
case USERDIRMODE_SAVEDGAMES:
if (!shell32_dll)
- Sys_LoadLibrary(shell32dllnames, &shell32_dll, shell32funcs);
+ Sys_LoadDependency(shell32dllnames, &shell32_dll, shell32funcs);
if (!ole32_dll)
- Sys_LoadLibrary(ole32dllnames, &ole32_dll, ole32funcs);
+ Sys_LoadDependency(ole32dllnames, &ole32_dll, ole32funcs);
if (qSHGetKnownFolderPath && qCoInitializeEx && qCoTaskMemFree && qCoUninitialize)
{
savedgamesdir[0] = 0;
*/
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();
{
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(CMD_SHARED, "gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
- Cmd_AddCommand(CMD_SHARED, "fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
- Cmd_AddCommand(CMD_SHARED, "path", FS_Path_f, "print searchpath (game directories and archives)");
- Cmd_AddCommand(CMD_SHARED, "dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line");
- Cmd_AddCommand(CMD_SHARED, "ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
- Cmd_AddCommand(CMD_SHARED, "which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
+ 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, "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");
+ Cmd_AddCommand(CF_SHARED, "ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line");
+ Cmd_AddCommand(CF_SHARED, "which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from");
}
static void FS_Init_Dir (void)
// -basedir <path>
// Overrides the system supplied base directory (under GAMENAME)
// COMMANDLINEOPTION: Filesystem: -basedir <path> chooses what base directory the game data is in, inside this there should be a data directory for the game (for example id1)
- i = COM_CheckParm ("-basedir");
+ i = Sys_CheckParm ("-basedir");
if (i && i < sys.argc-1)
{
strlcpy (fs_basedir, sys.argv[i+1], sizeof (fs_basedir));
strlcat(fs_basedir, "/", sizeof(fs_basedir));
// Add the personal game directory
- if((i = COM_CheckParm("-userdir")) && i < sys.argc - 1)
+ if((i = Sys_CheckParm("-userdir")) && i < sys.argc - 1)
dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", sys.argv[i+1]);
- else if (COM_CheckParm("-nohome"))
+ else if (Sys_CheckParm("-nohome"))
*fs_userdir = 0; // user wants roaming installation, no userdir
else
{
preferreduserdirmode = USERDIRMODE_NOHOME;
# endif
// check what limitations the user wants to impose
- if (COM_CheckParm("-home")) preferreduserdirmode = USERDIRMODE_HOME;
- if (COM_CheckParm("-mygames")) preferreduserdirmode = USERDIRMODE_MYGAMES;
- if (COM_CheckParm("-savedgames")) preferreduserdirmode = USERDIRMODE_SAVEDGAMES;
+ if (Sys_CheckParm("-home")) preferreduserdirmode = USERDIRMODE_HOME;
+ if (Sys_CheckParm("-mygames")) preferreduserdirmode = USERDIRMODE_MYGAMES;
+ if (Sys_CheckParm("-savedgames")) preferreduserdirmode = USERDIRMODE_SAVEDGAMES;
// gather the status of the possible userdirs
for (dirmode = 0;dirmode < USERDIRMODE_COUNT;dirmode++)
{
i++;
p = FS_CheckGameDir(sys.argv[i]);
if(!p)
- Sys_Error("Nasty -game name rejected: %s", sys.argv[i]);
+ Con_Printf("WARNING: Nasty -game name rejected: %s\n", sys.argv[i]);
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
char *buf;
// Load darkplaces.opt from the FS.
- if (!COM_CheckParm("-noopt"))
+ if (!Sys_CheckParm("-noopt"))
{
buf = (char *) FS_SysLoadFile("darkplaces.opt", tempmempool, true, NULL);
if(buf)
#ifndef USE_RWOPS
// Provide the SelfPack.
- if (!COM_CheckParm("-noselfpack") && sys.selffd >= 0)
+ if (!Sys_CheckParm("-noselfpack") && sys.selffd >= 0)
{
fs_selfpack = FS_LoadPackPK3FromFD(sys.argv[0], sys.selffd, true);
if(fs_selfpack)
{
FS_AddSelfPack();
- if (!COM_CheckParm("-noopt"))
+ if (!Sys_CheckParm("-noopt"))
{
buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL);
if(buf)
PK3_CloseLibrary ();
#ifdef WIN32
- Sys_UnloadLibrary (&shfolder_dll);
- Sys_UnloadLibrary (&shell32_dll);
- Sys_UnloadLibrary (&ole32_dll);
+ Sys_FreeLibrary (&shfolder_dll);
+ Sys_FreeLibrary (&shell32_dll);
+ Sys_FreeLibrary (&ole32_dll);
#endif
if (fs_mutex)
Thread_DestroyMutex(fs_mutex);
}
-static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qboolean nonblocking)
+static filedesc_t FS_SysOpenFiledesc(const char *filepath, const char *mode, qbool nonblocking)
{
filedesc_t handle = FILEDESC_INVALID;
int mod, opt;
unsigned int ind;
- qboolean dolock = false;
+ qbool dolock = false;
+ #ifdef WIN32
+ wchar filepathw[WSTRBUF] = {0};
+ #endif
// Parse the mode string
switch (mode[0])
if (nonblocking)
opt |= O_NONBLOCK;
- if(COM_CheckParm("-readonly") && mod != O_RDONLY)
+ if(Sys_CheckParm("-readonly") && mod != O_RDONLY)
return FILEDESC_INVALID;
#if USE_RWOPS
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);
return handle;
}
-int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
+int FS_SysOpenFD(const char *filepath, const char *mode, qbool nonblocking)
{
#ifdef USE_RWOPS
return -1;
Internal function used to create a qfile_t and open the relevant non-packed file on disk
====================
*/
-qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
+qfile_t* FS_SysOpen (const char* filepath, const char* mode, qbool nonblocking)
{
qfile_t* file;
or are just not a good idea for a mod to be using.
====================
*/
-int FS_CheckNastyPath (const char *path, qboolean isgamedir)
+int FS_CheckNastyPath (const char *path, qbool 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 false;
}
+/*
+====================
+FS_SanitizePath
+
+Sanitize path (replace non-portable characters
+with portable ones in-place, etc)
+====================
+*/
+void FS_SanitizePath(char *path)
+{
+ for (; *path; path++)
+ if (*path == '\\')
+ *path = '/';
+}
/*
====================
and the file index in the package if relevant
====================
*/
-static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet)
+static searchpath_t *FS_FindFile (const char *name, int* index, qbool quiet)
{
searchpath_t *search;
pack_t *pak;
Look for a file in the search paths and open it in read-only mode
===========
*/
-static qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
+static qfile_t *FS_OpenReadFile (const char *filename, qbool quiet, qbool nonblocking, int symlinkLevels)
{
searchpath_t *search;
int pack_ind;
if(count < 0)
return NULL;
linkbuf[count] = 0;
-
+
// Now combine the paths...
mergeslash = strrchr(filename, '/');
mergestart = linkbuf;
Used for savegame scanning in menu, and all file writing.
====================
*/
-qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qboolean quiet)
+qfile_t* FS_OpenRealFile (const char* filepath, const char* mode, qbool quiet)
{
char real_path [MAX_OSPATH];
Open a file. The syntax is the same as fopen
====================
*/
-qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
+qfile_t* FS_OpenVirtualFile (const char* filepath, qbool quiet)
{
qfile_t *result = NULL;
if (FS_CheckNastyPath(filepath, false))
Open a file. The syntax is the same as fopen
====================
*/
-qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet)
+qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qbool quiet)
{
qfile_t* file;
file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file));
{
fs_offset_t count, done;
- if (buffersize == 0)
+ if (buffersize == 0 || !buffer)
return 0;
// Get rid of the ungetc character
Always appends a 0 byte.
============
*/
-static unsigned char *FS_LoadAndCloseQFile (qfile_t *file, const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
+static unsigned char *FS_LoadAndCloseQFile (qfile_t *file, const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer)
{
unsigned char *buf = NULL;
fs_offset_t filesize = 0;
Always appends a 0 byte.
============
*/
-unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
+unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer)
{
qfile_t *file = FS_OpenVirtualFile(path, quiet);
return FS_LoadAndCloseQFile(file, path, pool, quiet, filesizepointer);
Always appends a 0 byte.
============
*/
-unsigned char *FS_SysLoadFile (const char *path, mempool_t *pool, qboolean quiet, fs_offset_t *filesizepointer)
+unsigned char *FS_SysLoadFile (const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer)
{
qfile_t *file = FS_SysOpen(path, "rb", false);
return FS_LoadAndCloseQFile(file, path, pool, quiet, filesizepointer);
The filename will be prefixed by the current game directory
============
*/
-qboolean FS_WriteFileInBlocks (const char *filename, const void *const *data, const fs_offset_t *len, size_t count)
+qbool FS_WriteFileInBlocks (const char *filename, const void *const *data, const fs_offset_t *len, size_t count)
{
qfile_t *file;
size_t i;
return true;
}
-qboolean FS_WriteFile (const char *filename, const void *data, fs_offset_t len)
+qbool FS_WriteFile (const char *filename, const void *data, fs_offset_t len)
{
return FS_WriteFileInBlocks(filename, &data, &len, 1);
}
Look for a file in the packages and in the filesystem
==================
*/
-qboolean FS_FileExists (const char *filename)
+qbool FS_FileExists (const char *filename)
{
return (FS_FindFile (filename, NULL, true) != 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;
#endif
}
-qboolean FS_SysFileExists (const char *path)
+qbool FS_SysFileExists (const char *path)
{
return FS_SysFileType (path) != FS_FILETYPE_NONE;
}
Allocate and fill a search structure with information on matching filenames.
===========
*/
-fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet)
+fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet, const char *packfile)
{
fssearch_t *search;
searchpath_t *searchpath;
int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
stringlist_t resultlist;
stringlist_t dirlist;
- const char *slash, *backslash, *colon, *separator;
+ stringlist_t matchedSet, foundSet;
+ const char *start, *slash, *backslash, *colon, *separator;
char *basepath;
for (i = 0;pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\';i++)
{
// look through all the pak file elements
pak = searchpath->pack;
+ if(packfile)
+ {
+ if(strcmp(packfile, pak->shortname))
+ continue;
+ }
for (i = 0;i < pak->numfiles;i++)
{
char temp[MAX_OSPATH];
}
else
{
- stringlist_t matchedSet, foundSet;
- const char *start = pattern;
+ if(packfile)
+ continue;
+
+ start = pattern;
stringlistinit(&matchedSet);
stringlistinit(&foundSet);
const char *name;
char linebuf[MAX_INPUTLINE];
fssearch_t *search;
- search = FS_Search(pattern, true, true);
+ search = FS_Search(pattern, true, true, NULL);
if (!search)
return 0;
numfiles = search->numfilenames;
{
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) {
If it is found, this file should NOT be downloaded.
====================
*/
-qboolean FS_IsRegisteredQuakePack(const char *name)
+qbool FS_IsRegisteredQuakePack(const char *name)
{
searchpath_t *search;
pack_t *pak;
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;
}