# include <direct.h>
# include <io.h>
# include <shlobj.h>
+# include <sys/stat.h>
+# include <share.h>
#else
# include <pwd.h>
# include <sys/stat.h>
#include "quakedef.h"
+#if TARGET_OS_IPHONE
+// include SDL for IPHONEOS code
+# include <SDL.h>
+#endif
+
+#include "thread.h"
+
#include "fs.h"
#include "wad.h"
// largefile support for Win32
#ifdef WIN32
+#undef lseek
# define lseek _lseeki64
#endif
-#if _MSC_VER >= 1400
// suppress deprecated warnings
-# include <sys/stat.h>
-# include <share.h>
+#if _MSC_VER >= 1400
# define read _read
# define write _write
# define close _close
*/
mempool_t *fs_mempool;
+void *fs_mutex = NULL;
searchpath_t *fs_searchpaths = NULL;
const char *const fs_checkgamedir_missing = "missing";
{"SHGetFolderPathA", (void **) &qSHGetFolderPath},
{NULL, NULL}
};
+static const char* shfolderdllnames [] =
+{
+ "shfolder.dll", // IE 4, or Win NT and higher
+ NULL
+};
static dllhandle_t shfolder_dll = NULL;
+
+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 HRESULT (WINAPI *qSHGetKnownFolderPath) (qREFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
+static dllfunction_t shell32funcs[] =
+{
+ {"SHGetKnownFolderPath", (void **) &qSHGetKnownFolderPath},
+ {NULL, NULL}
+};
+static const char* shell32dllnames [] =
+{
+ "shell32.dll", // Vista and higher
+ NULL
+};
+static dllhandle_t shell32_dll = NULL;
+
+static HRESULT (WINAPI *qCoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
+static void (WINAPI *qCoUninitialize)(void);
+static void (WINAPI *qCoTaskMemFree)(LPVOID pv);
+static dllfunction_t ole32funcs[] =
+{
+ {"CoInitializeEx", (void **) &qCoInitializeEx},
+ {"CoUninitialize", (void **) &qCoUninitialize},
+ {"CoTaskMemFree", (void **) &qCoTaskMemFree},
+ {NULL, NULL}
+};
+static const char* ole32dllnames [] =
+{
+ "ole32.dll", // 2000 and higher
+ NULL
+};
+static dllhandle_t ole32_dll = NULL;
#endif
/*
Unload the Zlib DLL
====================
*/
-void PK3_CloseLibrary (void)
+static void PK3_CloseLibrary (void)
{
#ifndef LINK_TO_ZLIB
Sys_UnloadLibrary (&zlib_dll);
Try to load the Zlib DLL
====================
*/
-qboolean PK3_OpenLibrary (void)
+static qboolean PK3_OpenLibrary (void)
{
#ifdef LINK_TO_ZLIB
return true;
Extract the end of the central directory from a PK3 package
====================
*/
-qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
+static qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd)
{
fs_offset_t filesize, maxsize;
unsigned char *buffer, *ptr;
Mem_Free (buffer);
+ if (
+ eocd->cdir_size > filesize ||
+ eocd->cdir_offset >= filesize ||
+ eocd->cdir_offset + eocd->cdir_size > filesize
+ )
+ {
+ // Obviously invalid central directory.
+ return false;
+ }
+
return true;
}
Extract the file list from a PK3 file
====================
*/
-int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
+static int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd)
{
unsigned char *central_dir, *ptr;
unsigned int ind;
// Load the central directory in memory
central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size);
- lseek (pack->handle, eocd->cdir_offset, SEEK_SET);
+ if (lseek (pack->handle, eocd->cdir_offset, SEEK_SET) == -1)
+ {
+ Mem_Free (central_dir);
+ return -1;
+ }
if(read (pack->handle, central_dir, eocd->cdir_size) != (fs_offset_t) eocd->cdir_size)
{
Mem_Free (central_dir);
if ((ptr[8] & 0x21) == 0 && (ptr[38] & 0x18) == 0)
{
// Still enough bytes for the name?
- if (remaining < namesize || namesize >= (int)sizeof (*pack->files))
+ if (namesize < 0 || remaining < namesize || namesize >= (int)sizeof (*pack->files))
{
Mem_Free (central_dir);
return -1;
flags = PACKFILE_FLAG_DEFLATED;
else
flags = 0;
- offset = BuffLittleLong (&ptr[42]) + eocd->prepended_garbage;
- packsize = BuffLittleLong (&ptr[20]);
- realsize = BuffLittleLong (&ptr[24]);
+ offset = (unsigned int)(BuffLittleLong (&ptr[42]) + eocd->prepended_garbage);
+ packsize = (unsigned int)BuffLittleLong (&ptr[20]);
+ realsize = (unsigned int)BuffLittleLong (&ptr[24]);
switch(ptr[5]) // C_VERSION_MADE_BY_1
{
Create a package entry associated with a PK3 file
====================
*/
-pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle)
+static pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean silent)
{
pk3_endOfCentralDir_t eocd;
pack_t *pack;
if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd))
{
- Con_Printf ("%s is not a PK3 file\n", packfile);
+ if(!silent)
+ Con_Printf ("%s is not a PK3 file\n", packfile);
close(packhandle);
return NULL;
}
Con_DPrintf("Added packfile %s (%i files)\n", packfile, real_nb_files);
return pack;
}
-pack_t *FS_LoadPackPK3 (const char *packfile)
+static pack_t *FS_LoadPackPK3 (const char *packfile)
{
int packhandle;
-#if _MSC_VER >= 1400
- _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
-#else
- packhandle = open (packfile, O_RDONLY | O_BINARY);
-#endif
+ packhandle = FS_SysOpenFD (packfile, "rb", false);
if (packhandle < 0)
return NULL;
- return FS_LoadPackPK3FromFD(packfile, packhandle);
+ return FS_LoadPackPK3FromFD(packfile, packhandle, false);
}
Find where the true file data offset is
====================
*/
-qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
+static qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack)
{
unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE];
fs_offset_t count;
return true;
// Load the local file description
- lseek (pack->handle, pfile->offset, SEEK_SET);
+ if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
+ {
+ Con_Printf ("Can't seek in package %s\n", pack->filename);
+ return false;
+ }
count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE);
if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER)
{
}
+static void FS_mkdir (const char *path)
+{
+ if(COM_CheckParm("-readonly"))
+ return;
+
+#if WIN32
+ if (_mkdir (path) == -1)
+#else
+ if (mkdir (path, 0777) == -1)
+#endif
+ {
+ // No logging for this. The only caller is FS_CreatePath (which
+ // calls it in ways that will intentionally produce EEXIST),
+ // and its own callers always use the directory afterwards and
+ // thus will detect failure that way.
+ }
+}
+
+
/*
============
FS_CreatePath
============
*/
-void FS_Path_f (void)
+static void FS_Path_f (void)
{
searchpath_t *s;
*Loads the header and directory, adding the files at the beginning
*of the list so they override previous pack files.
*/
-pack_t *FS_LoadPackPAK (const char *packfile)
+static pack_t *FS_LoadPackPAK (const char *packfile)
{
dpackheader_t header;
int i, numpackfiles;
pack_t *pack;
dpackfile_t *info;
-#if _MSC_VER >= 1400
- _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
-#else
- packhandle = open (packfile, O_RDONLY | O_BINARY);
-#endif
+ packhandle = FS_SysOpenFD(packfile, "rb", false);
if (packhandle < 0)
return NULL;
if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
numpackfiles = header.dirlen / sizeof(dpackfile_t);
- if (numpackfiles > MAX_FILES_IN_PACK)
+ if (numpackfiles < 0 || numpackfiles > MAX_FILES_IN_PACK)
{
Con_Printf ("%s has %i files\n", packfile, numpackfiles);
close(packhandle);
}
pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
- pack->ignorecase = false; // PAK is case sensitive
+ pack->ignorecase = true; // PAK is sensitive in Quake1 but insensitive in Quake2
strlcpy (pack->filename, packfile, sizeof (pack->filename));
pack->handle = packhandle;
pack->numfiles = 0;
// parse the directory
for (i = 0;i < numpackfiles;i++)
{
- fs_offset_t offset = LittleLong (info[i].filepos);
- fs_offset_t size = LittleLong (info[i].filelen);
+ fs_offset_t offset = (unsigned int)LittleLong (info[i].filepos);
+ fs_offset_t size = (unsigned int)LittleLong (info[i].filelen);
+
+ // Ensure a zero terminated file name (required by format).
+ info[i].name[sizeof(info[i].name) - 1] = 0;
FS_AddFileToPack (info[i].name, pack, offset, size, size, PACKFILE_FLAG_TRUEOFFS);
}
Create a package entry associated with a directory file
====================
*/
-pack_t *FS_LoadPackVirtual (const char *dirname)
+static pack_t *FS_LoadPackVirtual (const char *dirname)
{
pack_t *pack;
pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t));
pak = FS_LoadPackPAK (pakfile);
else if(!strcasecmp(ext, "pk3"))
pak = FS_LoadPackPK3 (pakfile);
+ else if(!strcasecmp(ext, "obb")) // android apk expansion
+ pak = FS_LoadPackPK3 (pakfile);
else
Con_Printf("\"%s\" does not have a pack extension\n", pakfile);
then loads and adds pak1.pak pak2.pak ...
================
*/
-void FS_AddGameDirectory (const char *dir)
+static void FS_AddGameDirectory (const char *dir)
{
int i;
stringlist_t list;
stringlistinit(&list);
listdirectory(&list, "", dir);
- stringlistsort(&list);
+ stringlistsort(&list, false);
// add any PAK package in the directory
for (i = 0;i < list.numstrings;i++)
// 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]), "pk3dir"))
+ if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3") || !strcasecmp(FS_FileExtension(list.strings[i]), "obb") || !strcasecmp(FS_FileExtension(list.strings[i]), "pk3dir"))
{
FS_AddPack_Fullpath(list.strings[i], list.strings[i] + strlen(dir), NULL, false);
}
FS_AddGameHierarchy
================
*/
-void FS_AddGameHierarchy (const char *dir)
+static void FS_AddGameHierarchy (const char *dir)
{
+ char vabuf[1024];
// Add the common game directory
- FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
+ FS_AddGameDirectory (va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, dir));
if (*fs_userdir)
- FS_AddGameDirectory(va("%s%s/", fs_userdir, dir));
+ FS_AddGameDirectory(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, dir));
}
FS_ClearSearchPath
================
*/
-void FS_ClearSearchPath (void)
+static void FS_ClearSearchPath (void)
{
// unload all packs and directory information, close all pack files
// (if a qfile is still reading a pack it won't be harmed because it used
{
int i;
qboolean fs_modified = false;
+ qboolean reset = false;
char gamedirbuf[MAX_INPUTLINE];
+ char vabuf[1024];
+ if (fs_searchpaths)
+ reset = true;
FS_ClearSearchPath();
+ // automatically activate gamemode for the gamedirs specified
+ if (reset)
+ COM_ChangeGameTypeForGameDirs();
+
// add the game-specific paths
// gamedirname1 (typically id1)
FS_AddGameHierarchy (gamedirname1);
// update the com_modname (used for server info)
- strlcpy(com_modname, gamedirname1, sizeof(com_modname));
+ if (gamedirname2 && gamedirname2[0])
+ strlcpy(com_modname, gamedirname2, sizeof(com_modname));
+ else
+ 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)
- if (gamedirname2)
+ if (gamedirname2 && gamedirname2[0])
{
fs_modified = true;
FS_AddGameHierarchy (gamedirname2);
// update the com_modname (used server info)
strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname));
if(i)
- strlcat(gamedirbuf, va(" %s", fs_gamedirs[i]), sizeof(gamedirbuf));
+ strlcat(gamedirbuf, va(vabuf, sizeof(vabuf), " %s", fs_gamedirs[i]), sizeof(gamedirbuf));
else
strlcpy(gamedirbuf, fs_gamedirs[i], sizeof(gamedirbuf));
}
// If "-condebug" is in the command line, remove the previous log file
if (COM_CheckParm ("-condebug") != 0)
- unlink (va("%s/qconsole.log", fs_gamedir));
+ 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
if (FS_FileExists("gfx/pop.lmp"))
else
Con_Print("Playing shareware version.\n");
break;
+ default:
+ break;
}
// unload all wads so that future queries will return the new data
W_UnloadAll();
}
-void FS_Rescan_f(void)
+static void FS_Rescan_f(void)
{
FS_Rescan();
}
FS_ChangeGameDirs
================
*/
-extern void Host_SaveConfig (void);
-extern void Host_LoadConfig_f (void);
+extern qboolean vid_opened;
qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
{
int i;
// reinitialize filesystem to detect the new paks
FS_Rescan();
- // exec the new config
- Host_LoadConfig_f();
+ if (cls.demoplayback)
+ {
+ CL_Disconnect_f();
+ cls.demonum = 0;
+ }
// unload all sounds so they will be reloaded from the new files as needed
S_UnloadAllSounds_f();
- // reinitialize renderer (this reloads hud/console background/etc)
- R_Modules_Restart();
+ // close down the video subsystem, it will start up again when the config finishes...
+ VID_Stop();
+ vid_opened = false;
+
+ // restart the video subsystem after the config is executed
+ Cbuf_InsertText("\nloadconfig\nvid_restart\n\n");
return true;
}
FS_GameDir_f
================
*/
-void FS_GameDir_f (void)
+static void FS_GameDir_f (void)
{
int i;
int numgamedirs;
FS_ChangeGameDirs(numgamedirs, gamedirs, true, true);
}
-static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking);
-static const char *FS_SysCheckGameDir(const char *gamedir)
+static const char *FS_SysCheckGameDir(const char *gamedir, char *buf, size_t buflength)
{
- static char buf[8192];
qboolean success;
qfile_t *f;
stringlist_t list;
fs_offset_t n;
+ char vabuf[1024];
stringlistinit(&list);
listdirectory(&list, gamedir, "");
if(success)
{
- f = FS_SysOpen(va("%smodinfo.txt", gamedir), "r", false);
+ f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%smodinfo.txt", gamedir), "r", false);
if(f)
{
- n = FS_Read (f, buf, sizeof(buf) - 1);
+ n = FS_Read (f, buf, buflength - 1);
if(n >= 0)
buf[n] = 0;
else
const char *FS_CheckGameDir(const char *gamedir)
{
const char *ret;
+ static char buf[8192];
+ char vabuf[1024];
if (FS_CheckNastyPath(gamedir, true))
return NULL;
- ret = FS_SysCheckGameDir(va("%s%s/", fs_userdir, gamedir));
+ ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_userdir, gamedir), buf, sizeof(buf));
if(ret)
{
if(!*ret)
{
// get description from basedir
- ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
+ ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
if(ret)
return ret;
return "";
return ret;
}
- ret = FS_SysCheckGameDir(va("%s%s/", fs_basedir, gamedir));
+ ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
if(ret)
return ret;
static void FS_ListGameDirs(void)
{
stringlist_t list, list2;
- int i, j;
+ int i;
const char *info;
+ char vabuf[1024];
fs_all_gamedirs_count = 0;
if(fs_all_gamedirs)
Mem_Free(fs_all_gamedirs);
stringlistinit(&list);
- listdirectory(&list, va("%s/", fs_basedir), "");
- listdirectory(&list, va("%s/", fs_userdir), "");
- stringlistsort(&list);
+ listdirectory(&list, va(vabuf, sizeof(vabuf), "%s/", fs_basedir), "");
+ listdirectory(&list, va(vabuf, sizeof(vabuf), "%s/", fs_userdir), "");
+ stringlistsort(&list, false);
stringlistinit(&list2);
for(i = 0; i < list.numstrings; ++i)
continue;
if(!*info)
continue;
- strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].name, list2.strings[i], sizeof(fs_all_gamedirs[j].name));
- strlcpy(fs_all_gamedirs[fs_all_gamedirs_count].description, info, sizeof(fs_all_gamedirs[j].description));
+ 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));
++fs_all_gamedirs_count;
}
}
+/*
+#ifdef WIN32
+#pragma comment(lib, "shell32.lib")
+#include <ShlObj.h>
+#endif
+*/
+
/*
================
FS_Init_SelfPack
fs_mempool = Mem_AllocPool("file management", 0, NULL);
if(com_selffd >= 0)
{
- fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd);
+ fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
if(fs_selfpack)
{
char *buf, *q;
p = buf;
while(COM_ParseToken_Console(&p))
{
+ size_t sz = strlen(com_token) + 1; // shut up clang
if(i >= args_left)
break;
- q = (char *)Mem_Alloc(fs_mempool, strlen(com_token) + 1);
- strlcpy(q, com_token, strlen(com_token) + 1);
+ q = (char *)Mem_Alloc(fs_mempool, sz);
+ strlcpy(q, com_token, sz);
new_argv[com_argc + i] = q;
++i;
}
}
}
-/*
-================
-FS_Init
-================
-*/
-void FS_Init (void)
+static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsize)
{
- const char *p;
- int i;
-#ifdef WIN32
- TCHAR mydocsdir[MAX_PATH + 1];
+#if defined(__IPHONEOS__)
+ if (userdirmode == USERDIRMODE_HOME)
+ {
+ // 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);
+ return 1;
+ }
+ return -1;
+
+#elif defined(WIN32)
+ char *homedir;
#if _MSC_VER >= 1400
size_t homedirlen;
#endif
-#endif
- char *homedir;
-
-#ifdef WIN32
- const char* dllnames [] =
- {
- "shfolder.dll", // IE 4, or Win NT and higher
- NULL
- };
- Sys_LoadLibrary(dllnames, &shfolder_dll, shfolderfuncs);
- // don't care for the result; if it fails, %USERPROFILE% will be used instead
-#endif
+ TCHAR mydocsdir[MAX_PATH + 1];
+ wchar_t *savedgamesdirw;
+ char savedgamesdir[MAX_OSPATH];
+ int fd;
+ char vabuf[1024];
- // Add the personal game directory
- if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
- {
- dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
- }
- else if(COM_CheckParm("-nohome"))
- {
- *fs_userdir = 0;
- }
- else
+ userdir[0] = 0;
+ switch(userdirmode)
{
-#ifdef WIN32
- if(qSHGetFolderPath && (qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK))
+ default:
+ return -1;
+ case USERDIRMODE_NOHOME:
+ strlcpy(userdir, fs_basedir, userdirsize);
+ break;
+ case USERDIRMODE_MYGAMES:
+ if (!shfolder_dll)
+ Sys_LoadLibrary(shfolderdllnames, &shfolder_dll, shfolderfuncs);
+ mydocsdir[0] = 0;
+ if (qSHGetFolderPath && qSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
{
- dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
- Con_DPrintf("Obtained personal directory %s from SHGetFolderPath\n", fs_userdir);
+ dpsnprintf(userdir, userdirsize, "%s/My Games/%s/", mydocsdir, gameuserdirname);
+ break;
}
- else
- {
- // use the environment
#if _MSC_VER >= 1400
- _dupenv_s (&homedir, &homedirlen, "USERPROFILE");
+ _dupenv_s(&homedir, &homedirlen, "USERPROFILE");
+ if(homedir)
+ {
+ dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
+ free(homedir);
+ break;
+ }
+#else
+ homedir = getenv("USERPROFILE");
+ if(homedir)
+ {
+ dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
+ break;
+ }
+#endif
+ return -1;
+ case USERDIRMODE_SAVEDGAMES:
+ if (!shell32_dll)
+ Sys_LoadLibrary(shell32dllnames, &shell32_dll, shell32funcs);
+ if (!ole32_dll)
+ Sys_LoadLibrary(ole32dllnames, &ole32_dll, ole32funcs);
+ if (qSHGetKnownFolderPath && qCoInitializeEx && qCoTaskMemFree && qCoUninitialize)
+ {
+ savedgamesdir[0] = 0;
+ qCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+/*
+#ifdef __cplusplus
+ if (SHGetKnownFolderPath(FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
#else
- homedir = getenv("USERPROFILE");
+ if (SHGetKnownFolderPath(&FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
#endif
-
- if(homedir)
+*/
+ if (qSHGetKnownFolderPath(&qFOLDERID_SavedGames, qKF_FLAG_CREATE | qKF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
{
- dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/My Documents/My Games/%s/", homedir, gameuserdirname);
+ memset(savedgamesdir, 0, sizeof(savedgamesdir));
#if _MSC_VER >= 1400
- free(homedir);
+ wcstombs_s(NULL, savedgamesdir, sizeof(savedgamesdir), savedgamesdirw, sizeof(savedgamesdir)-1);
+#else
+ wcstombs(savedgamesdir, savedgamesdirw, sizeof(savedgamesdir)-1);
#endif
- Con_DPrintf("Obtained personal directory %s from environment\n", fs_userdir);
+ qCoTaskMemFree(savedgamesdirw);
+ }
+ qCoUninitialize();
+ if (savedgamesdir[0])
+ {
+ dpsnprintf(userdir, userdirsize, "%s/%s/", savedgamesdir, gameuserdirname);
+ break;
}
}
-
- if(!*fs_userdir)
- Con_DPrintf("Could not obtain home directory; not supporting -mygames\n");
+ return -1;
+ }
#else
- homedir = getenv ("HOME");
+ int fd;
+ char *homedir;
+ char vabuf[1024];
+ userdir[0] = 0;
+ switch(userdirmode)
+ {
+ default:
+ return -1;
+ case USERDIRMODE_NOHOME:
+ strlcpy(userdir, fs_basedir, userdirsize);
+ break;
+ case USERDIRMODE_HOME:
+ homedir = getenv("HOME");
if(homedir)
- dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/.%s/", homedir, gameuserdirname);
-
- if(!*fs_userdir)
- Con_DPrintf("Could not obtain home directory; assuming -nohome\n");
-#endif
-
-#ifdef WIN32
- if(!COM_CheckParm("-mygames"))
{
-#if _MSC_VER >= 1400
- int fd;
- _sopen_s(&fd, va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
+ dpsnprintf(userdir, userdirsize, "%s/.%s/", homedir, gameuserdirname);
+ break;
+ }
+ return -1;
+ case USERDIRMODE_SAVEDGAMES:
+ homedir = getenv("HOME");
+ if(homedir)
+ {
+#ifdef MACOSX
+ dpsnprintf(userdir, userdirsize, "%s/Library/Application Support/%s/", homedir, gameuserdirname);
#else
- int fd = open (va("%s%s/config.cfg", fs_basedir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
+ // the XDG say some files would need to go in:
+ // XDG_CONFIG_HOME (or ~/.config/%s/)
+ // XDG_DATA_HOME (or ~/.local/share/%s/)
+ // XDG_CACHE_HOME (or ~/.cache/%s/)
+ // and also search the following global locations if defined:
+ // XDG_CONFIG_DIRS (normally /etc/xdg/%s/)
+ // XDG_DATA_DIRS (normally /usr/share/%s/)
+ // this would be too complicated...
+ return -1;
#endif
- if(fd >= 0)
- {
- close(fd);
- *fs_userdir = 0; // we have write access to the game dir, so let's use it
- }
+ break;
}
-#endif
+ return -1;
}
+#endif
- strlcpy(fs_gamedir, "", sizeof(fs_gamedir));
-// If the base directory is explicitly defined by the compilation process
-#ifdef DP_FS_BASEDIR
- strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
-#else
- *fs_basedir = 0;
+#if !defined(__IPHONEOS__)
-#ifdef MACOSX
- // FIXME: is there a better way to find the directory outside the .app?
- if (strstr(com_argv[0], ".app/"))
- {
- char *split;
+#ifdef WIN32
+ // historical behavior...
+ if (userdirmode == USERDIRMODE_NOHOME && strcmp(gamedirname1, "id1"))
+ return 0; // don't bother checking if the basedir folder is writable, it's annoying... unless it is Quake on Windows where NOHOME is the default preferred and we have to check for an error case
+#endif
- split = strstr(com_argv[0], ".app/");
- while (split > com_argv[0] && *split != '/')
- split--;
- strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
- fs_basedir[split - com_argv[0]] = 0;
- }
+ // see if we can write to this path (note: won't create path)
+#ifdef WIN32
+ // no access() here, we must try to open the file for appending
+ fd = FS_SysOpenFD(va(vabuf, sizeof(vabuf), "%s%s/config.cfg", userdir, gamedirname1), "a", false);
+ if(fd >= 0)
+ close(fd);
+#else
+ // on Unix, we don't need to ACTUALLY attempt to open the file
+ if(access(va(vabuf, sizeof(vabuf), "%s%s/", userdir, gamedirname1), W_OK | X_OK) >= 0)
+ fd = 1;
+ else
+ fd = -1;
#endif
+ if(fd >= 0)
+ {
+ return 1; // good choice - the path exists and is writable
+ }
+ else
+ {
+ if (userdirmode == USERDIRMODE_NOHOME)
+ return -1; // path usually already exists, we lack permissions
+ else
+ return 0; // probably good - failed to write but maybe we need to create path
+ }
#endif
+}
+
+/*
+================
+FS_Init
+================
+*/
+void FS_Init (void)
+{
+ const char *p;
+ int i;
+
+ *fs_basedir = 0;
+ *fs_userdir = 0;
+ *fs_gamedir = 0;
// -basedir <path>
// Overrides the system supplied base directory (under GAMENAME)
if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/'))
fs_basedir[i-1] = 0;
}
+ else
+ {
+// If the base directory is explicitly defined by the compilation process
+#ifdef DP_FS_BASEDIR
+ strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir));
+#elif defined(__ANDROID__)
+ dpsnprintf(fs_basedir, sizeof(fs_basedir), "/sdcard/%s/", gameuserdirname);
+#elif defined(MACOSX)
+ // FIXME: is there a better way to find the directory outside the .app, without using Objective-C?
+ if (strstr(com_argv[0], ".app/"))
+ {
+ char *split;
+ strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir));
+ split = strstr(fs_basedir, ".app/");
+ if (split)
+ {
+ struct stat statresult;
+ char vabuf[1024];
+ // truncate to just after the .app/
+ split[5] = 0;
+ // see if gamedir exists in Resources
+ 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));
+ }
+ else
+ {
+ // no gamedir found in Resources, gamedir is probably
+ // outside the .app, remove .app part of path
+ while (split > fs_basedir && *split != '/')
+ split--;
+ *split = 0;
+ }
+ }
+ }
+#endif
+ }
+ // make sure the appending of a path separator won't create an unterminated string
+ 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));
+ // Add the personal game directory
+ if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
+ dpsnprintf(fs_userdir, sizeof(fs_userdir), "%s/", com_argv[i+1]);
+ else if (COM_CheckParm("-nohome"))
+ *fs_userdir = 0; // user wants roaming installation, no userdir
+ else
+ {
+ int dirmode;
+ int highestuserdirmode = USERDIRMODE_COUNT - 1;
+ int preferreduserdirmode = USERDIRMODE_COUNT - 1;
+ int userdirstatus[USERDIRMODE_COUNT];
+#ifdef WIN32
+ // historical behavior...
+ if (!strcmp(gamedirname1, "id1"))
+ 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;
+ // gather the status of the possible userdirs
+ for (dirmode = 0;dirmode < USERDIRMODE_COUNT;dirmode++)
+ {
+ userdirstatus[dirmode] = FS_ChooseUserDir((userdirmode_t)dirmode, fs_userdir, sizeof(fs_userdir));
+ if (userdirstatus[dirmode] == 1)
+ Con_DPrintf("userdir %i = %s (writable)\n", dirmode, fs_userdir);
+ else if (userdirstatus[dirmode] == 0)
+ Con_DPrintf("userdir %i = %s (not writable or does not exist)\n", dirmode, fs_userdir);
+ else
+ Con_DPrintf("userdir %i (not applicable)\n", dirmode);
+ }
+ // some games may prefer writing to basedir, but if write fails we
+ // have to search for a real userdir...
+ if (preferreduserdirmode == 0 && userdirstatus[0] < 1)
+ preferreduserdirmode = highestuserdirmode;
+ // check for an existing userdir and continue using it if possible...
+ for (dirmode = USERDIRMODE_COUNT - 1;dirmode > 0;dirmode--)
+ if (userdirstatus[dirmode] == 1)
+ break;
+ // if no existing userdir found, make a new one...
+ if (dirmode == 0 && preferreduserdirmode > 0)
+ for (dirmode = preferreduserdirmode;dirmode > 0;dirmode--)
+ if (userdirstatus[dirmode] >= 0)
+ break;
+ // and finally, we picked one...
+ FS_ChooseUserDir((userdirmode_t)dirmode, fs_userdir, sizeof(fs_userdir));
+ Con_DPrintf("userdir %i is the winner\n", dirmode);
+ }
+
+ // if userdir equal to basedir, clear it to avoid confusion later
+ if (!strcmp(fs_basedir, fs_userdir))
+ fs_userdir[0] = 0;
+
FS_ListGameDirs();
p = FS_CheckGameDir(gamedirname1);
// generate the searchpath
FS_Rescan();
+
+ if (Thread_HasThreads())
+ fs_mutex = Thread_CreateMutex();
}
void FS_Init_Commands(void)
// by the OS anyway)
FS_ClearSearchPath();
Mem_FreePool (&fs_mempool);
+ PK3_CloseLibrary ();
#ifdef WIN32
Sys_UnloadLibrary (&shfolder_dll);
+ Sys_UnloadLibrary (&shell32_dll);
+ Sys_UnloadLibrary (&ole32_dll);
#endif
+
+ if (fs_mutex)
+ Thread_DestroyMutex(fs_mutex);
}
int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
{
- int handle;
+ int handle = -1;
int mod, opt;
unsigned int ind;
+ qboolean dolock = false;
// Parse the mode string
switch (mode[0])
case 'b':
opt |= O_BINARY;
break;
+ case 'l':
+ dolock = true;
+ break;
default:
Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
filepath, mode, mode[ind]);
if (nonblocking)
opt |= O_NONBLOCK;
-#if _MSC_VER >= 1400
- _sopen_s(&handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+ if(COM_CheckParm("-readonly") && mod != O_RDONLY)
+ return -1;
+
+#ifdef WIN32
+# if _MSC_VER >= 1400
+ _sopen_s(&handle, filepath, 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);
+# endif
#else
handle = open (filepath, mod | opt, 0666);
+ if(handle >= 0 && dolock)
+ {
+ struct flock l;
+ l.l_type = ((mod == O_RDONLY) ? F_RDLCK : F_WRLCK);
+ l.l_whence = SEEK_SET;
+ l.l_start = 0;
+ l.l_len = 0;
+ if(fcntl(handle, F_SETLK, &l) == -1)
+ {
+ close(handle);
+ handle = -1;
+ }
+ }
#endif
+
return handle;
}
Internal function used to create a qfile_t and open the relevant non-packed file on disk
====================
*/
-static qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
+qfile_t* FS_SysOpen (const char* filepath, const char* mode, qboolean nonblocking)
{
qfile_t* file;
Open a packed file using its package file descriptor
===========
*/
-qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
+static qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind)
{
packfile_t *pfile;
int dup_handle;
// the dup() call to avoid having to close the dup_handle on error here
if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1)
{
- Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n",
- pfile->name, pack->filename, (int) pfile->offset);
+ Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %08x%08x)\n",
+ pfile->name, pack->filename, (unsigned int)(pfile->offset >> 32), (unsigned int)(pfile->offset));
return NULL;
}
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: don't allow . character immediately before a slash, this catches all imaginable cases of ./, ../, .../, etc
+ if (strstr(path, "./"))
+ return 2; // possible attempt to go outside the game directory
// all: forbid trailing slash on gamedir
if (isgamedir && path[strlen(path)-1] == '/')
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, int symlinkLevels)
+static qfile_t *FS_OpenReadFile (const char *filename, qboolean quiet, qboolean nonblocking, int symlinkLevels)
{
searchpath_t *search;
int pack_ind;
*/
qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet)
{
+ qfile_t *result = NULL;
if (FS_CheckNastyPath(filepath, false))
{
Con_Printf("FS_OpenVirtualFile(\"%s\", %s): nasty filename rejected\n", filepath, quiet ? "true" : "false");
return NULL;
}
- return FS_OpenReadFile (filepath, quiet, false, 16);
+ if (fs_mutex) Thread_LockMutex(fs_mutex);
+ result = FS_OpenReadFile (filepath, quiet, false, 16);
+ if (fs_mutex) Thread_UnlockMutex(fs_mutex);
+ return result;
}
if (file->filename)
{
if (file->flags & QFILE_FLAG_REMOVE)
- remove(file->filename);
+ {
+ if (remove(file->filename) == -1)
+ {
+ // No need to report this. If removing a just
+ // written file failed, this most likely means
+ // someone else deleted it first - which we
+ // like.
+ }
+ }
Mem_Free((void *) file->filename);
}
*/
fs_offset_t FS_Write (qfile_t* file, const void* data, size_t datasize)
{
- fs_offset_t result;
+ fs_offset_t written = 0;
// If necessary, seek to the exact file position we're supposed to be
if (file->buff_ind != file->buff_len)
- lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR);
+ {
+ if (lseek (file->handle, file->buff_ind - file->buff_len, SEEK_CUR) == -1)
+ {
+ Con_Printf("WARNING: could not seek in %s.\n", file->filename);
+ }
+ }
// Purge cached data
FS_Purge (file);
// Write the buffer and update the position
- result = write (file->handle, data, (fs_offset_t)datasize);
+ // LordHavoc: to hush a warning about passing size_t to an unsigned int parameter on Win64 we do this as multiple writes if the size would be too big for an integer (we never write that big in one go, but it's a theory)
+ while (written < (fs_offset_t)datasize)
+ {
+ // figure out how much to write in one chunk
+ fs_offset_t maxchunk = 1<<30; // 1 GiB
+ int chunk = (int)min((fs_offset_t)datasize - written, maxchunk);
+ int result = (int)write (file->handle, (const unsigned char *)data + written, chunk);
+ // if at least some was written, add it to our accumulator
+ if (result > 0)
+ written += result;
+ // if the result is not what we expected, consider the write to be incomplete
+ if (result != chunk)
+ break;
+ }
file->position = lseek (file->handle, 0, SEEK_CUR);
if (file->real_length < file->position)
file->real_length = file->position;
- if (result < 0)
- return 0;
-
- return result;
+ // note that this will never be less than 0 even if the write failed
+ return written;
}
{
if (count > (fs_offset_t)buffersize)
count = (fs_offset_t)buffersize;
- lseek (file->handle, file->offset + file->position, SEEK_SET);
+ if (lseek (file->handle, file->offset + file->position, SEEK_SET) == -1)
+ {
+ // Seek failed. When reading from a pipe, and
+ // the caller never called FS_Seek, this still
+ // works fine. So no reporting this error.
+ }
nb = read (file->handle, &((unsigned char*)buffer)[done], count);
if (nb > 0)
{
{
if (count > (fs_offset_t)sizeof (file->buff))
count = (fs_offset_t)sizeof (file->buff);
- lseek (file->handle, file->offset + file->position, SEEK_SET);
+ if (lseek (file->handle, file->offset + file->position, SEEK_SET) == -1)
+ {
+ // Seek failed. When reading from a pipe, and
+ // the caller never called FS_Seek, this still
+ // works fine. So no reporting this error.
+ }
nb = read (file->handle, file->buff, count);
if (nb > 0)
{
ztk->in_len = 0;
ztk->in_position = 0;
file->position = 0;
- lseek (file->handle, file->offset, SEEK_SET);
+ if (lseek (file->handle, file->offset, SEEK_SET) == -1)
+ Con_Printf("IMPOSSIBLE: couldn't seek in already opened pk3 file.\n");
// Reset the Zlib stream
ztk->zstream.next_in = ztk->input;
// if path doesn't have a .EXT, append extension
// (extension should include the .)
- src = path + strlen(path) - 1;
+ src = path + strlen(path);
while (*src != '/' && src != path)
{
return FS_SysFileType (path) != FS_FILETYPE_NONE;
}
-void FS_mkdir (const char *path)
-{
-#if WIN32
- _mkdir (path);
-#else
- mkdir (path, 0777);
-#endif
-}
-
/*
===========
FS_Search
if (resultlist.numstrings)
{
- stringlistsort(&resultlist);
+ stringlistsort(&resultlist, true);
numfiles = resultlist.numstrings;
numchars = 0;
for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
}
extern int con_linewidth;
-int FS_ListDirectory(const char *pattern, int oneperline)
+static int FS_ListDirectory(const char *pattern, int oneperline)
{
int numfiles;
int numcolumns;
static void FS_ListDirectoryCmd (const char* cmdname, int oneperline)
{
const char *pattern;
- if (Cmd_Argc() > 3)
+ if (Cmd_Argc() >= 3)
{
Con_Printf("usage:\n%s [path/pattern]\n", cmdname);
return;
searchpath_t *sp = FS_FindFile(filename, &index, true);
if(sp && sp->pack)
return sp->pack->shortname;
+ else if(sp)
+ return "";
else
return 0;
}
int diff;
middle = (left + right) / 2;
- diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
+ diff = strcmp_funct (pak->files[middle].name, "gfx/pop.lmp");
// Found it
if (!diff)
}
strm.next_in = (unsigned char*)data;
- strm.avail_in = size;
+ strm.avail_in = (unsigned int)size;
tmp = (unsigned char *) Mem_Alloc(tempmempool, size);
if(!tmp)
}
strm.next_out = tmp;
- strm.avail_out = size;
+ strm.avail_out = (unsigned int)size;
if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END)
{
return NULL;
}
- if(deflated_size)
- *deflated_size = (size_t)strm.total_out;
+ *deflated_size = (size_t)strm.total_out;
memcpy(out, tmp, strm.total_out);
Mem_Free(tmp);
}
strm.next_in = (unsigned char*)data;
- strm.avail_in = size;
+ strm.avail_in = (unsigned int)size;
do
{
memcpy(out, outbuf.data, outbuf.cursize);
Mem_Free(outbuf.data);
- if(inflated_size)
- *inflated_size = (size_t)outbuf.cursize;
+ *inflated_size = (size_t)outbuf.cursize;
return out;
}