+/*
+#ifdef WIN32
+#pragma comment(lib, "shell32.lib")
+#include <ShlObj.h>
+#endif
+*/
+
+/*
+================
+FS_Init_SelfPack
+================
+*/
+void FS_Init_SelfPack (void)
+{
+ PK3_OpenLibrary ();
+ fs_mempool = Mem_AllocPool("file management", 0, NULL);
+ if(com_selffd >= 0)
+ {
+ fs_selfpack = FS_LoadPackPK3FromFD(com_argv[0], com_selffd, true);
+ if(fs_selfpack)
+ {
+ char *buf, *q;
+ const char *p;
+ FS_AddSelfPack();
+ buf = (char *) FS_LoadFile("darkplaces.opt", tempmempool, true, NULL);
+ if(buf)
+ {
+ const char **new_argv;
+ int i = 0;
+ int args_left = 256;
+ new_argv = (const char **)Mem_Alloc(fs_mempool, sizeof(*com_argv) * (com_argc + args_left + 2));
+ if(com_argc == 0)
+ {
+ new_argv[0] = "dummy";
+ com_argc = 1;
+ }
+ else
+ {
+ memcpy((char *)(&new_argv[0]), &com_argv[0], sizeof(*com_argv) * com_argc);
+ }
+ 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, sz);
+ strlcpy(q, com_token, sz);
+ new_argv[com_argc + i] = q;
+ ++i;
+ }
+ new_argv[i+com_argc] = NULL;
+ com_argv = new_argv;
+ com_argc = com_argc + i;
+ }
+ Mem_Free(buf);
+ }
+ }
+}
+
+static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsize)
+{
+#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, maxlength, "../Documents/");
+ return 1;
+ }
+ return -1;
+
+#elif defined(WIN32)
+ char *homedir;
+#if _MSC_VER >= 1400
+ size_t homedirlen;
+#endif
+ TCHAR mydocsdir[MAX_PATH + 1];
+ wchar_t *savedgamesdirw;
+ char savedgamesdir[MAX_OSPATH];
+ int fd;
+ char vabuf[1024];
+
+ userdir[0] = 0;
+ switch(userdirmode)
+ {
+ 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(userdir, userdirsize, "%s/My Games/%s/", mydocsdir, gameuserdirname);
+ break;
+ }
+#if _MSC_VER >= 1400
+ _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
+ if (SHGetKnownFolderPath(&FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_NO_ALIAS, NULL, &savedgamesdirw) == S_OK)
+#endif
+*/
+ 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
+ qCoTaskMemFree(savedgamesdirw);
+ }
+ qCoUninitialize();
+ if (savedgamesdir[0])
+ {
+ dpsnprintf(userdir, userdirsize, "%s/%s/", savedgamesdir, gameuserdirname);
+ break;
+ }
+ }
+ return -1;
+ }
+#else
+ 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(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
+ // 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
+ break;
+ }
+ return -1;
+ }
+#endif
+
+
+#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
+
+ // 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 = 0;
+#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
+ }
+}