Add 'schemes' value to fmf files, for games to list (multiple) uri schemes with which to easily start the engine and connect to a specified server. Also add cl_verify_urischeme, a bit like ezquake has.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6021 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
f0aea54e5d
commit
cf31dcccad
12 changed files with 488 additions and 147 deletions
|
@ -123,6 +123,7 @@ cvar_t cl_demospeed = CVARF("cl_demospeed", "1", 0);
|
||||||
cvar_t cl_demoreel = CVARFD("cl_demoreel", "0", CVAR_SAVE, "When enabled, the engine will begin playing a demo loop on startup.");
|
cvar_t cl_demoreel = CVARFD("cl_demoreel", "0", CVAR_SAVE, "When enabled, the engine will begin playing a demo loop on startup.");
|
||||||
|
|
||||||
cvar_t cl_loopbackprotocol = CVARD("cl_loopbackprotocol", "qw", "Which protocol to use for single-player/the internal client. Should be one of: qw, qwid, nqid, nq, fitz, bjp3, dp6, dp7, auto. If 'auto', will use qw protocols for qw mods, and nq protocols for nq mods.");
|
cvar_t cl_loopbackprotocol = CVARD("cl_loopbackprotocol", "qw", "Which protocol to use for single-player/the internal client. Should be one of: qw, qwid, nqid, nq, fitz, bjp3, dp6, dp7, auto. If 'auto', will use qw protocols for qw mods, and nq protocols for nq mods.");
|
||||||
|
static cvar_t cl_verify_urischeme = CVARAFD("cl_verify_urischeme", "0", "cl_verify_qwprotocol"/*ezquake, inappropriate for misc schemes*/, CVAR_NOSAVE/*checked at startup, so its only really default.cfg that sets it*/, "0: Do nothing.\n1: Check whether our protocol scheme is registered and prompt the user to register associations.\n2: Always re-register on every startup, without prompting. Sledgehammer style.");
|
||||||
|
|
||||||
|
|
||||||
cvar_t cl_threadedphysics = CVARD("cl_threadedphysics", "0", "When set, client input frames are generated and sent on a worker thread");
|
cvar_t cl_threadedphysics = CVARD("cl_threadedphysics", "0", "When set, client input frames are generated and sent on a worker thread");
|
||||||
|
@ -5006,6 +5007,8 @@ void CL_Init (void)
|
||||||
#ifndef SERVERONLY
|
#ifndef SERVERONLY
|
||||||
Cvar_Register (&cl_loopbackprotocol, cl_controlgroup);
|
Cvar_Register (&cl_loopbackprotocol, cl_controlgroup);
|
||||||
#endif
|
#endif
|
||||||
|
Cvar_Register (&cl_verify_urischeme, cl_controlgroup);
|
||||||
|
|
||||||
Cvar_Register (&cl_countpendingpl, cl_controlgroup);
|
Cvar_Register (&cl_countpendingpl, cl_controlgroup);
|
||||||
Cvar_Register (&cl_threadedphysics, cl_controlgroup);
|
Cvar_Register (&cl_threadedphysics, cl_controlgroup);
|
||||||
hud_tracking_show = Cvar_Get("hud_tracking_show", "1", 0, "statusbar");
|
hud_tracking_show = Cvar_Get("hud_tracking_show", "1", 0, "statusbar");
|
||||||
|
@ -6003,18 +6006,62 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (nlen >= 5 && !strncmp(fname, "qw://", 5))
|
const char *schemeend = strstr(fname, "://");
|
||||||
|
|
||||||
|
if (schemeend)
|
||||||
{ //this is also implemented by ezquake, so be careful here...
|
{ //this is also implemented by ezquake, so be careful here...
|
||||||
//"qw://[stream@]host[:port]/COMMAND" join, spectate, qtvplay
|
//examples:
|
||||||
|
// "quake2://broker:port"
|
||||||
|
// "quake2:rtc://broker:port/game"
|
||||||
|
// "qw://[stream@]host[:port]/COMMAND" join, spectate, qtvplay
|
||||||
|
//we'll chop off any non-auth prefix, its just so we can handle multiple protocols via a single uri scheme.
|
||||||
char *t, *cmd;
|
char *t, *cmd;
|
||||||
const char *url;
|
const char *url;
|
||||||
char buffer[8192];
|
char buffer[8192];
|
||||||
t = Z_Malloc(nlen+1);
|
const char *schemestart = strchr(fname, ':');
|
||||||
memcpy(t, fname, nlen);
|
int schemelen, urilen;
|
||||||
t[nlen] = 0;
|
|
||||||
url = t+5;
|
|
||||||
|
|
||||||
for (cmd = t+5; *cmd; cmd++)
|
//if its one of our explicit protocols then use the url as-is
|
||||||
|
const char *netschemes[] = {"udp", "udp4", "udp6", "ipx", "tcp", "tcp4", "tcp6", "spx", "ws", "wss", "tls", "dtls", "ice", "rtc", "ices", "rtcs", "irc", "udg", "unix"};
|
||||||
|
int i;
|
||||||
|
size_t slen;
|
||||||
|
|
||||||
|
if (!schemestart || schemestart==schemeend)
|
||||||
|
schemestart = fname;
|
||||||
|
else
|
||||||
|
schemestart++;
|
||||||
|
schemelen = schemeend-schemestart;
|
||||||
|
urilen = nlen-(schemestart-fname);
|
||||||
|
|
||||||
|
for (i = 0; i < countof(netschemes); i++)
|
||||||
|
{
|
||||||
|
slen = strlen(netschemes[i]);
|
||||||
|
if (schemelen == slen && !strncmp(schemestart, netschemes[i], slen))
|
||||||
|
{
|
||||||
|
char quoted[8192];
|
||||||
|
char *t = Z_Malloc(urilen+1);
|
||||||
|
memcpy(t, schemestart, urilen);
|
||||||
|
t[urilen] = 0;
|
||||||
|
|
||||||
|
Cbuf_AddText(va("connect %s\n", COM_QuotedString(t, quoted, sizeof(quoted), false)), RESTRICT_LOCAL);
|
||||||
|
|
||||||
|
if(file)
|
||||||
|
VFS_CLOSE(file);
|
||||||
|
Z_Free(t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
schemelen++;
|
||||||
|
if (!strncmp(schemestart+schemelen, "//", schemelen))
|
||||||
|
schemelen+=2;
|
||||||
|
|
||||||
|
t = Z_Malloc(urilen+1);
|
||||||
|
memcpy(t, schemestart, urilen);
|
||||||
|
t[urilen] = 0;
|
||||||
|
url = t+schemelen;
|
||||||
|
|
||||||
|
for (cmd = t+schemelen; *cmd; cmd++)
|
||||||
{
|
{
|
||||||
if (*cmd == '/')
|
if (*cmd == '/')
|
||||||
{
|
{
|
||||||
|
@ -6043,25 +6090,6 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
|
||||||
Z_Free(t);
|
Z_Free(t);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
const char *netschemes[] = {"udp://", "udp4//", "udp6//", "ipx://", "tcp://", "tcp4//", "tcp6//", "spx://", "ws://", "wss://", "tls://", "dtls://", "ice://", "rtc://", "ices://", "rtcs://", "irc://", "udg://", "unix://"};
|
|
||||||
int i;
|
|
||||||
size_t slen;
|
|
||||||
for (i = 0; i < countof(netschemes); i++)
|
|
||||||
{
|
|
||||||
slen = strlen(netschemes[i]);
|
|
||||||
if (nlen >= slen && !strncmp(fname, netschemes[i], slen))
|
|
||||||
{
|
|
||||||
char quoted[8192];
|
|
||||||
char *t = Z_Malloc(nlen+1);
|
|
||||||
memcpy(t, fname, nlen);
|
|
||||||
t[nlen] = 0;
|
|
||||||
Cbuf_AddText(va("connect %s\n", COM_QuotedString(t, quoted, sizeof(quoted), false)), RESTRICT_LOCAL);
|
|
||||||
Z_Free(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f = Z_Malloc(sizeof(*f) + nlen);
|
f = Z_Malloc(sizeof(*f) + nlen);
|
||||||
|
@ -6762,7 +6790,11 @@ void CL_ExecInitialConfigs(char *resetcommand)
|
||||||
Ruleset_Scan();
|
Ruleset_Scan();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Host_URIPrompt(void *ctx, promptbutton_t btn)
|
||||||
|
{
|
||||||
|
if (btn == PROMPT_YES)
|
||||||
|
Cbuf_AddText ("\nsys_register_file_associations\n", RESTRICT_LOCAL);
|
||||||
|
}
|
||||||
|
|
||||||
void Host_FinishLoading(void)
|
void Host_FinishLoading(void)
|
||||||
{
|
{
|
||||||
|
@ -6846,6 +6878,22 @@ void Host_FinishLoading(void)
|
||||||
}
|
}
|
||||||
else //3 flags for a renderer restart
|
else //3 flags for a renderer restart
|
||||||
Renderer_Start();
|
Renderer_Start();
|
||||||
|
|
||||||
|
|
||||||
|
if (fs_manifest->schemes && Cmd_IsCommand("sys_register_file_associations"))
|
||||||
|
{
|
||||||
|
if (cl_verify_urischeme.ival >= 2)
|
||||||
|
Cbuf_AddText ("\nsys_register_file_associations\n", RESTRICT_LOCAL);
|
||||||
|
else if (cl_verify_urischeme.ival)
|
||||||
|
{
|
||||||
|
char *scheme = Sys_URIScheme_NeedsRegistering();
|
||||||
|
if (scheme)
|
||||||
|
{
|
||||||
|
Menu_Prompt(Host_URIPrompt, NULL, va("The URI scheme %s:// is not configured.\nRegister now?", scheme), "Register", NULL, "No");
|
||||||
|
Z_Free(scheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1018,6 +1018,10 @@ qboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)
|
||||||
void Sys_SendKeyEvents(void)
|
void Sys_SendKeyEvents(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
char *Sys_URIScheme_NeedsRegistering(void)
|
||||||
|
{ //android does its mime/etc registrations via android xml junk. dynamically registering stuff isn't supported, so pretend that its already registered to avoid annoying prompts.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
void Sys_Init(void)
|
void Sys_Init(void)
|
||||||
{
|
{
|
||||||
Cvar_Register(&sys_keepscreenon, "android stuff");
|
Cvar_Register(&sys_keepscreenon, "android stuff");
|
||||||
|
|
|
@ -289,11 +289,85 @@ void Sys_Quit (void)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *Sys_URIScheme_NeedsRegistering(void)
|
||||||
|
{
|
||||||
|
qboolean found;
|
||||||
|
qofs_t insize;
|
||||||
|
char *in;
|
||||||
|
char confbase[MAX_OSPATH];
|
||||||
|
|
||||||
|
char scheme[64];
|
||||||
|
const char *schemes = fs_manifest->schemes;
|
||||||
|
|
||||||
|
schemes=COM_ParseOut(schemes, scheme, sizeof(scheme));
|
||||||
|
if (!schemes)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
{ //user
|
||||||
|
const char *config = getenv("XDG_CONFIG_HOME");
|
||||||
|
const char *home = getenv("HOME");
|
||||||
|
if (config && *config)
|
||||||
|
Q_strncpyz(confbase, config, sizeof(confbase));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (home && *home)
|
||||||
|
Q_snprintfz(confbase, sizeof(confbase), "%s/.config", home);
|
||||||
|
else
|
||||||
|
return NULL; //can't register anyway, just pretend its registered.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if the scheme is registered or not.
|
||||||
|
in = FS_MallocFile(va("%s/mimeapps.list", confbase), FS_SYSTEM, &insize);
|
||||||
|
if (in)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
qboolean inadded = false;
|
||||||
|
char *l = in;
|
||||||
|
const char *schemeline = va("x-scheme-handler/%s=fte-%s.desktop;", scheme, fs_manifest->installation);
|
||||||
|
size_t schemelinelen = strlen(schemeline);
|
||||||
|
found = false;
|
||||||
|
while(*l)
|
||||||
|
{
|
||||||
|
char *le;
|
||||||
|
while(*l == ' ' || *l == '\n')
|
||||||
|
l++;
|
||||||
|
le = strchr(l, '\n');
|
||||||
|
if (le)
|
||||||
|
le = le+1;
|
||||||
|
else
|
||||||
|
le = l + strlen(l);
|
||||||
|
if (!strncmp(l, "[Added Associations]", 20))
|
||||||
|
inadded = true;
|
||||||
|
else if (!strncmp(l, "[", 1))
|
||||||
|
inadded = false;
|
||||||
|
else if (inadded && !strncmp(l, schemeline, schemelinelen))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
l = le;
|
||||||
|
}
|
||||||
|
} while(found && (schemes=COM_ParseOut(schemes, scheme, sizeof(scheme))));
|
||||||
|
Z_Free(in);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
found = false;
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
return NULL;
|
||||||
|
return Z_StrDup(scheme);
|
||||||
|
}
|
||||||
static void Sys_Register_File_Associations_f(void)
|
static void Sys_Register_File_Associations_f(void)
|
||||||
{
|
{
|
||||||
|
const char *s;
|
||||||
char xdgbase[MAX_OSPATH];
|
char xdgbase[MAX_OSPATH];
|
||||||
char confbase[MAX_OSPATH];
|
char confbase[MAX_OSPATH];
|
||||||
|
|
||||||
|
char scheme[MAX_OSPATH];
|
||||||
|
const char *schemes = fs_manifest->schemes;
|
||||||
|
|
||||||
if (1)
|
if (1)
|
||||||
{ //user
|
{ //user
|
||||||
const char *data = getenv("XDG_DATA_HOME");
|
const char *data = getenv("XDG_DATA_HOME");
|
||||||
|
@ -357,41 +431,66 @@ static void Sys_Register_File_Associations_f(void)
|
||||||
|
|
||||||
//we need to create some .desktop file first, so stuff knows how to start us up.
|
//we need to create some .desktop file first, so stuff knows how to start us up.
|
||||||
{
|
{
|
||||||
|
vfsfile_t *f;
|
||||||
char iconsyspath[MAX_OSPATH];
|
char iconsyspath[MAX_OSPATH];
|
||||||
char *exe = realpath(host_parms.argv[0], NULL);
|
char *exe = realpath(host_parms.argv[0], NULL);
|
||||||
char *basedir = realpath(com_gamepath, NULL);
|
char *basedir = realpath(com_gamepath, NULL);
|
||||||
const char *iconname = fs_manifest->installation;
|
const char *iconname = fs_manifest->installation;
|
||||||
const char *desktopfile =
|
|
||||||
"[Desktop Entry]\n"
|
if (!exe)
|
||||||
"Type=Application\n"
|
{
|
||||||
"Encoding=UTF-8\n"
|
int i;
|
||||||
"Name=%s\n"
|
if (strchr(host_parms.argv[0], '/') && (i=readlink("/proc/self/exe", iconsyspath, sizeof(iconsyspath)-1))>0)
|
||||||
"Comment=Awesome First Person Shooter\n" //again should be a manicfest item
|
{ //if they used a relative path to invoke the binary, replace it with an absolute one.
|
||||||
"Exec=\"%s\" %%u\n" //FIXME: FS_GetManifestArgs! etc!
|
iconsyspath[i] = 0;
|
||||||
"Path=%s\n"
|
exe = strdup(iconsyspath);
|
||||||
"Icon=%s\n"
|
}
|
||||||
"Terminal=false\n"
|
else //no absolute path. assume it was loaded from the (default) path.
|
||||||
"Categories=Game;\n"
|
exe = strdup(host_parms.argv[0]);
|
||||||
"MimeType=" "application/x-quakeworlddemo;" "x-scheme-handler/qw;\n"
|
}
|
||||||
;
|
|
||||||
if (!strcmp(iconname, "afterquake") || !strcmp(iconname, "nq")) //hacks so that we don't need to create icons.
|
if (!strcmp(iconname, "afterquake") || !strcmp(iconname, "nq")) //hacks so that we don't need to create icons.
|
||||||
iconname = "quake";
|
iconname = "quake";
|
||||||
|
|
||||||
if (FS_NativePath("icon.png", FS_PUBBASEGAMEONLY, iconsyspath, sizeof(iconsyspath)))
|
if (FS_NativePath("icon.png", FS_PUBBASEGAMEONLY, iconsyspath, sizeof(iconsyspath)))
|
||||||
iconname = iconsyspath;
|
iconname = iconsyspath;
|
||||||
|
|
||||||
desktopfile = va(desktopfile,
|
s = va("%s/applications/fte-%s.desktop", xdgbase, fs_manifest->installation);
|
||||||
|
FS_CreatePath(s, FS_SYSTEM);
|
||||||
|
f = FS_OpenVFS(s, "wb", FS_SYSTEM);
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
VFS_PRINTF(f,
|
||||||
|
"[Desktop Entry]\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
"Encoding=UTF-8\n"
|
||||||
|
"Name=%s\n"
|
||||||
|
"Comment=Awesome First Person Shooter\n" //again should be a manicfest item
|
||||||
|
"Exec=\"%s\" %%u %s\n"
|
||||||
|
"Path=%s\n"
|
||||||
|
"Icon=%s\n"
|
||||||
|
"Terminal=false\n"
|
||||||
|
"Categories=Game;\n"
|
||||||
|
"MimeType=" "application/x-quakeworlddemo;",
|
||||||
fs_manifest->formalname?fs_manifest->formalname:fs_manifest->installation,
|
fs_manifest->formalname?fs_manifest->formalname:fs_manifest->installation,
|
||||||
exe, basedir, iconname);
|
exe, FS_GetManifestArgs(), basedir, iconname);
|
||||||
|
|
||||||
|
for (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));)
|
||||||
|
VFS_PRINTF(f, "x-scheme-handler/%s;", scheme);
|
||||||
|
VFS_PRINTF(f, "\n");
|
||||||
|
|
||||||
|
VFS_CLOSE(f);
|
||||||
|
}
|
||||||
|
|
||||||
free(exe);
|
free(exe);
|
||||||
free(basedir);
|
free(basedir);
|
||||||
FS_WriteFile(va("%s/applications/fte-%s.desktop", xdgbase, fs_manifest->installation), desktopfile, strlen(desktopfile), FS_SYSTEM);
|
|
||||||
|
|
||||||
//FIXME: read icon.png and write it to ~/.local/share/icons/hicolor/WxH/apps/foo.png
|
//FIXME: read icon.png and write it to ~/.local/share/icons/hicolor/WxH/apps/foo.png
|
||||||
}
|
}
|
||||||
|
|
||||||
//we need to set some default applications.
|
//we need to set some default applications.
|
||||||
//write out a new file and rename the new over the top of the old
|
//write out a new file and rename the new over the top of the old
|
||||||
|
for (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));)
|
||||||
{
|
{
|
||||||
char *foundassoc = NULL;
|
char *foundassoc = NULL;
|
||||||
vfsfile_t *out = FS_OpenVFS(va("%s/.mimeapps.list.new", confbase), "wb", FS_SYSTEM);
|
vfsfile_t *out = FS_OpenVFS(va("%s/.mimeapps.list.new", confbase), "wb", FS_SYSTEM);
|
||||||
|
@ -403,6 +502,8 @@ static void Sys_Register_File_Associations_f(void)
|
||||||
{
|
{
|
||||||
qboolean inadded = false;
|
qboolean inadded = false;
|
||||||
char *l = in;
|
char *l = in;
|
||||||
|
const char *schemeline = va("x-scheme-handler/%s=", scheme);
|
||||||
|
size_t schemelinelen = strlen(schemeline);
|
||||||
while(*l)
|
while(*l)
|
||||||
{
|
{
|
||||||
char *le;
|
char *le;
|
||||||
|
@ -421,10 +522,10 @@ static void Sys_Register_File_Associations_f(void)
|
||||||
}
|
}
|
||||||
else if (!strncmp(l, "[", 1))
|
else if (!strncmp(l, "[", 1))
|
||||||
inadded = false;
|
inadded = false;
|
||||||
else if (inadded && !strncmp(l, "x-scheme-handler/qw=", 20))
|
else if (inadded && !strncmp(l, schemeline, schemelinelen))
|
||||||
{
|
{
|
||||||
foundassoc = l;
|
foundassoc = l;
|
||||||
insize -= strlen(le);
|
insize -= le-l;
|
||||||
memmove(l, le, strlen(le)); //remove the line
|
memmove(l, le, strlen(le)); //remove the line
|
||||||
}
|
}
|
||||||
l = le;
|
l = le;
|
||||||
|
@ -432,7 +533,7 @@ static void Sys_Register_File_Associations_f(void)
|
||||||
if (foundassoc)
|
if (foundassoc)
|
||||||
{ //if we found it, or somewhere to insert it, then insert it.
|
{ //if we found it, or somewhere to insert it, then insert it.
|
||||||
VFS_WRITE(out, in, foundassoc-in);
|
VFS_WRITE(out, in, foundassoc-in);
|
||||||
VFS_PRINTF(out, "x-scheme-handler/qw=fte-%s.desktop;\n", fs_manifest->installation);
|
VFS_PRINTF(out, "x-scheme-handler/%s=fte-%s.desktop;\n", scheme, fs_manifest->installation);
|
||||||
VFS_WRITE(out, foundassoc, insize - (foundassoc-in));
|
VFS_WRITE(out, foundassoc, insize - (foundassoc-in));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -442,7 +543,7 @@ static void Sys_Register_File_Associations_f(void)
|
||||||
if (!foundassoc)
|
if (!foundassoc)
|
||||||
{ //if file not found, or no appropriate section, just concat it on the end.
|
{ //if file not found, or no appropriate section, just concat it on the end.
|
||||||
VFS_PRINTF(out, "[Added Associations]\n");
|
VFS_PRINTF(out, "[Added Associations]\n");
|
||||||
VFS_PRINTF(out, "x-scheme-handler/qw=fte-%s.desktop;\n", fs_manifest->installation);
|
VFS_PRINTF(out, "x-scheme-handler/%s=fte-%s.desktop;\n", scheme, fs_manifest->installation);
|
||||||
}
|
}
|
||||||
VFS_FLUSH(out);
|
VFS_FLUSH(out);
|
||||||
VFS_CLOSE(out);
|
VFS_CLOSE(out);
|
||||||
|
|
|
@ -371,6 +371,10 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *Sys_URIScheme_NeedsRegistering(void)
|
||||||
|
{ //no support, report something that'll disable annoying prompts.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
void Sys_Init()
|
void Sys_Init()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -725,6 +725,11 @@ int Sys_FileTime (char *path)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *Sys_URIScheme_NeedsRegistering(void)
|
||||||
|
{ //no support.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void Sys_Init(void)
|
void Sys_Init(void)
|
||||||
{
|
{
|
||||||
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
|
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
|
||||||
|
|
|
@ -1511,10 +1511,13 @@ void Sys_MakeCodeWriteable (void *startaddr, unsigned long length)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Sys_DoFileAssociations(int elevated);
|
void Sys_DoFileAssociations(int elevated, const char *scheme);
|
||||||
void Sys_Register_File_Associations_f(void)
|
void Sys_Register_File_Associations_f(void)
|
||||||
{
|
{
|
||||||
Sys_DoFileAssociations(0);
|
if (!Q_strcasecmp(Cmd_Argv(1), "quiet"))
|
||||||
|
Sys_DoFileAssociations(2, fs_manifest->schemes); //current user only.
|
||||||
|
else
|
||||||
|
Sys_DoFileAssociations(0, fs_manifest->schemes); //user+machine(with elevation on failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void QDECL Sys_Priority_Changed(cvar_t *var, char *oldval)
|
static void QDECL Sys_Priority_Changed(cvar_t *var, char *oldval)
|
||||||
|
@ -1559,7 +1562,7 @@ void Sys_Init (void)
|
||||||
#ifndef SERVERONLY
|
#ifndef SERVERONLY
|
||||||
Cvar_Register(&sys_disableWinKeys, "System vars");
|
Cvar_Register(&sys_disableWinKeys, "System vars");
|
||||||
Cvar_Register(&sys_disableTaskSwitch, "System vars");
|
Cvar_Register(&sys_disableTaskSwitch, "System vars");
|
||||||
Cmd_AddCommandD("sys_register_file_associations", Sys_Register_File_Associations_f, "Register FTE as the system handler for .bsp .mvd .qwd .dem files. Also register the qw:// URL protocol. This command will probably trigger a UAC prompt in Windows Vista and up. Deny it for current-user-only asociations (will also prevent listing in windows' 'default programs' ui due to microsoft bugs/limitations).");
|
Cmd_AddCommandD("sys_register_file_associations", Sys_Register_File_Associations_f, "Register FTE as the system handler for .bsp .mvd .qwd .dem files. Also register the URL protocol. This command will probably trigger a UAC prompt in Windows Vista and up. Deny it for current-user-only asociations (will also prevent listing in windows' 'default programs' ui due to microsoft bugs/limitations).");
|
||||||
|
|
||||||
#ifdef QUAKESPYAPI
|
#ifdef QUAKESPYAPI
|
||||||
#ifndef CLIENTONLY
|
#ifndef CLIENTONLY
|
||||||
|
@ -2884,6 +2887,47 @@ void Win7_TaskListInit(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//using this like posix' access function, but with much more code, microsoftisms, and no errno codes/info
|
||||||
|
//no, I don't really have a clue why it needs to be so long.
|
||||||
|
//#include <svrapi.h>
|
||||||
|
#ifndef ACCESS_READ
|
||||||
|
#define ACCESS_READ 0x1
|
||||||
|
#define ACCESS_WRITE 0x2
|
||||||
|
#endif
|
||||||
|
static BOOL microsoft_accessW(LPWSTR pszFolder, DWORD dwAccessDesired)
|
||||||
|
{
|
||||||
|
HANDLE hToken;
|
||||||
|
PRIVILEGE_SET PrivilegeSet;
|
||||||
|
DWORD dwPrivSetSize;
|
||||||
|
DWORD dwAccessGranted;
|
||||||
|
BOOL fAccessGranted = FALSE;
|
||||||
|
GENERIC_MAPPING GenericMapping;
|
||||||
|
SECURITY_INFORMATION si = (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION);
|
||||||
|
PSECURITY_DESCRIPTOR psdSD = NULL;
|
||||||
|
DWORD dwNeeded;
|
||||||
|
GetFileSecurityW(pszFolder,si,NULL,0,&dwNeeded);
|
||||||
|
psdSD = malloc(dwNeeded);
|
||||||
|
GetFileSecurityW(pszFolder,si,psdSD,dwNeeded,&dwNeeded);
|
||||||
|
ImpersonateSelf(SecurityImpersonation);
|
||||||
|
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken);
|
||||||
|
memset(&GenericMapping, 0xff, sizeof(GENERIC_MAPPING));
|
||||||
|
GenericMapping.GenericRead = ACCESS_READ;
|
||||||
|
GenericMapping.GenericWrite = ACCESS_WRITE;
|
||||||
|
GenericMapping.GenericExecute = 0;
|
||||||
|
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
|
||||||
|
MapGenericMask(&dwAccessDesired, &GenericMapping);
|
||||||
|
dwPrivSetSize = sizeof(PRIVILEGE_SET);
|
||||||
|
AccessCheck(psdSD, hToken, dwAccessDesired, &GenericMapping, &PrivilegeSet, &dwPrivSetSize, &dwAccessGranted, &fAccessGranted);
|
||||||
|
free(psdSD);
|
||||||
|
return fAccessGranted;
|
||||||
|
}
|
||||||
|
static BOOL microsoft_accessU(LPCSTR pszFolder, DWORD dwAccessDesired)
|
||||||
|
{
|
||||||
|
wchar_t wpath[MAX_OSPATH];
|
||||||
|
return microsoft_accessW(widen(wpath, sizeof(wpath), pszFolder), dwAccessDesired);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef SVNREVISION
|
#ifndef SVNREVISION
|
||||||
#if 0 //1 to debug engine update in msvc.
|
#if 0 //1 to debug engine update in msvc.
|
||||||
#define SVNREVISION 1
|
#define SVNREVISION 1
|
||||||
|
@ -3085,10 +3129,68 @@ typedef struct qIApplicationAssociationRegistrationUI
|
||||||
} *lpVtbl;
|
} *lpVtbl;
|
||||||
} qIApplicationAssociationRegistrationUI;
|
} qIApplicationAssociationRegistrationUI;
|
||||||
|
|
||||||
void Sys_DoFileAssociations(int elevated)
|
char *Sys_URIScheme_NeedsRegistering(void)
|
||||||
|
{ //just disables the prompts.
|
||||||
|
HKEY root;
|
||||||
|
char buffer[2048];
|
||||||
|
char scheme[64];
|
||||||
|
const char *s, *schemes = fs_manifest->schemes;
|
||||||
|
char *exec, *me;
|
||||||
|
size_t i;
|
||||||
|
wchar_t enginebinaryw[MAX_OSPATH];
|
||||||
|
char enginebinary[MAX_OSPATH*4];
|
||||||
|
|
||||||
|
for (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )
|
||||||
|
{
|
||||||
|
root = HKEY_CURRENT_USER;
|
||||||
|
if (!MyRegGetStringValue(root, va("Software\\Classes\\%s", scheme), "", buffer, sizeof(buffer)))
|
||||||
|
{
|
||||||
|
root = HKEY_LOCAL_MACHINE;
|
||||||
|
if (!MyRegGetStringValue(root, va("Software\\Classes\\%s", scheme), "", buffer, sizeof(buffer)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//the scheme exists at least...
|
||||||
|
if (!MyRegGetStringValue(root, va("Software\\Classes\\%s\\shell\\open\\command", scheme), "", buffer, sizeof(buffer)))
|
||||||
|
break; //erk, missing.
|
||||||
|
COM_Parse(buffer);
|
||||||
|
if (!microsoft_accessU(com_token, ACCESS_READ))
|
||||||
|
break; //can't read it? doesn't exist?
|
||||||
|
exec = COM_SkipPath(com_token);
|
||||||
|
for (i = 0; exec[i]; i++)
|
||||||
|
if (exec[i] == '_' || exec[i] == '.' || (exec[i] >= '0' && exec[i] <= '9'))
|
||||||
|
{ //anything that looks like a revision number
|
||||||
|
exec[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetModuleFileNameW(NULL, enginebinaryw, countof(enginebinaryw)-1);
|
||||||
|
narrowen(enginebinary, sizeof(enginebinary), enginebinaryw);
|
||||||
|
me = COM_SkipPath(enginebinary);
|
||||||
|
for (i = 0; me[i]; i++)
|
||||||
|
if (me[i] == '_' || me[i] == '.' || (me[i] >= '0' && me[i] <= '9'))
|
||||||
|
{ //anything that looks like a revision number
|
||||||
|
me[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Q_strcasecmp(exec, me))
|
||||||
|
break; //looks like its set to something else.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s)
|
||||||
|
return Z_StrDup(scheme);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
void Sys_DoFileAssociations(int elevated, const char *schemes)
|
||||||
{
|
{
|
||||||
|
//elevated:
|
||||||
|
// 0: console command
|
||||||
|
// 1: running as an elevated/admin process
|
||||||
|
// 2: register as current user only (do not show associations prompt).
|
||||||
char command[1024];
|
char command[1024];
|
||||||
qboolean ok = true;
|
char scheme[64];
|
||||||
|
const char *s;
|
||||||
|
qboolean ok = true;
|
||||||
HKEY root;
|
HKEY root;
|
||||||
|
|
||||||
//I'd do everything in current_user if I could, but windows sucks too much for that.
|
//I'd do everything in current_user if I could, but windows sucks too much for that.
|
||||||
|
@ -3101,12 +3203,13 @@ void Sys_DoFileAssociations(int elevated)
|
||||||
//on xp, we use ONLY current user. no 'registered applications' means no 'registered applications bug', which means no need to use hklm at all.
|
//on xp, we use ONLY current user. no 'registered applications' means no 'registered applications bug', which means no need to use hklm at all.
|
||||||
//in vista/7, we have to create stuff in local_machine. in which case we might as well put ALL associations in there. the ui stuff will allow user-specific settings, so this is not an issue other than the fact that it triggers uac.
|
//in vista/7, we have to create stuff in local_machine. in which case we might as well put ALL associations in there. the ui stuff will allow user-specific settings, so this is not an issue other than the fact that it triggers uac.
|
||||||
//in 8, we cannot programatically force ownership of our associations, so we might as well just use the ui method even for vista+7 instead of the ruder version.
|
//in 8, we cannot programatically force ownership of our associations, so we might as well just use the ui method even for vista+7 instead of the ruder version.
|
||||||
|
//in win10, the 'ui' stuff is just a quick popup to tell the user to configure defaults themselves. hopefully we can fall back on the regular associations for when the user didn't override anyting.
|
||||||
if (qwinvermaj < 6)
|
if (qwinvermaj < 6)
|
||||||
elevated = 2;
|
elevated = 2;
|
||||||
|
|
||||||
root = elevated == 2?HKEY_CURRENT_USER:HKEY_LOCAL_MACHINE;
|
root = (elevated>=2)?HKEY_CURRENT_USER:HKEY_LOCAL_MACHINE;
|
||||||
|
|
||||||
#define ASSOC_VERSION 2
|
// #define ASSOC_VERSION 2
|
||||||
#define ASSOCV "1"
|
#define ASSOCV "1"
|
||||||
|
|
||||||
//register the basic demo class
|
//register the basic demo class
|
||||||
|
@ -3116,6 +3219,13 @@ void Sys_DoFileAssociations(int elevated)
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_DemoFile."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_DemoFile."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
||||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_DemoFile."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_DemoFile."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
||||||
|
if (ok)
|
||||||
|
{ //and now the extensions themselves...
|
||||||
|
MyRegSetValue(root, "Software\\Classes\\.qtv", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||||
|
MyRegSetValue(root, "Software\\Classes\\.mvd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||||
|
MyRegSetValue(root, "Software\\Classes\\.qwd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||||
|
MyRegSetValue(root, "Software\\Classes\\.dem", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||||
|
}
|
||||||
|
|
||||||
//register the basic map class. yeah, the command is the same as for demos. but the description is different!
|
//register the basic map class. yeah, the command is the same as for demos. but the description is different!
|
||||||
Q_snprintfz(command, sizeof(command), "Quake Map");
|
Q_snprintfz(command, sizeof(command), "Quake Map");
|
||||||
|
@ -3124,8 +3234,13 @@ void Sys_DoFileAssociations(int elevated)
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_BSPFile."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_BSPFile."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
||||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_BSPFile."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_BSPFile."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
||||||
|
if (ok)
|
||||||
|
{ //and now the extensions themselves...
|
||||||
|
MyRegSetValue(root, "Software\\Classes\\.bsp", "", REG_SZ, DISTRIBUTION"_BSPFile."ASSOCV, strlen(DISTRIBUTION"_BSPFile."ASSOCV));
|
||||||
|
MyRegSetValue(root, "Software\\Classes\\.map", "", REG_SZ, DISTRIBUTION"_BSPFile."ASSOCV, strlen(DISTRIBUTION"_BSPFile."ASSOCV));
|
||||||
|
}
|
||||||
|
|
||||||
//register the basic protocol class
|
//register the basic uri scheme class
|
||||||
Q_snprintfz(command, sizeof(command), "QuakeWorld Server");
|
Q_snprintfz(command, sizeof(command), "QuakeWorld Server");
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"", "", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"", "", REG_SZ, command, strlen(command));
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"", "URL Protocol", REG_SZ, "", strlen(""));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"", "URL Protocol", REG_SZ, "", strlen(""));
|
||||||
|
@ -3133,8 +3248,22 @@ void Sys_DoFileAssociations(int elevated)
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
||||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
||||||
|
if (ok)
|
||||||
|
{ //and now the schemes themselves... (doesn't really use the same scheme stuff)
|
||||||
|
for (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )
|
||||||
|
{
|
||||||
|
Q_snprintfz(command, sizeof(command), "QuakeWorld Server");
|
||||||
|
MyRegSetValue(root, va("Software\\Classes\\%s", scheme), "", REG_SZ, command, strlen(command));
|
||||||
|
MyRegSetValue(root, va("Software\\Classes\\%s", scheme), "URL Protocol", REG_SZ, "", strlen(""));
|
||||||
|
Q_snprintfz(command, sizeof(command), "\"%s\",0", com_argv[0]);
|
||||||
|
MyRegSetValue(root, va("Software\\Classes\\%s\\DefaultIcon", scheme), "", REG_SZ, command, strlen(command));
|
||||||
|
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||||
|
MyRegSetValue(root, va("Software\\Classes\\%s\\shell\\open\\command", scheme), "", REG_SZ, command, strlen(command));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//try to get ourselves listed in windows' 'default programs' ui.
|
|
||||||
|
//now try to get ourselves listed in windows' 'default programs' ui.
|
||||||
Q_snprintfz(command, sizeof(command), "%s", FULLENGINENAME);
|
Q_snprintfz(command, sizeof(command), "%s", FULLENGINENAME);
|
||||||
ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities", "ApplicationName", REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities", "ApplicationName", REG_SZ, command, strlen(command));
|
||||||
Q_snprintfz(command, sizeof(command), "%s", FULLENGINENAME" is an awesome hybrid game engine able to run multiple Quake-compatible/derived games.");
|
Q_snprintfz(command, sizeof(command), "%s", FULLENGINENAME" is an awesome hybrid game engine able to run multiple Quake-compatible/derived games.");
|
||||||
|
@ -3154,53 +3283,34 @@ void Sys_DoFileAssociations(int elevated)
|
||||||
// ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\FileAssociations", ".fmf", REG_SZ, DISTRIBUTION"_ManifestFile", strlen(DISTRIBUTION"_ManifestFile"));
|
// ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\FileAssociations", ".fmf", REG_SZ, DISTRIBUTION"_ManifestFile", strlen(DISTRIBUTION"_ManifestFile"));
|
||||||
// ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\MIMEAssociations", "application/x-ftemanifest", REG_SZ, DISTRIBUTION"_ManifestFile", strlen(DISTRIBUTION"_ManifestFile"));
|
// ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\MIMEAssociations", "application/x-ftemanifest", REG_SZ, DISTRIBUTION"_ManifestFile", strlen(DISTRIBUTION"_ManifestFile"));
|
||||||
|
|
||||||
Q_snprintfz(command, sizeof(command), DISTRIBUTION"_Server.1");
|
Q_snprintfz(command, sizeof(command), DISTRIBUTION"_Server."ASSOCV);
|
||||||
ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\UrlAssociations", "qw", REG_SZ, command, strlen(command));
|
for (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )
|
||||||
|
{
|
||||||
|
ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME "\\Capabilities\\UrlAssociations", scheme, REG_SZ, command, strlen(command));
|
||||||
|
}
|
||||||
|
|
||||||
Q_snprintfz(command, sizeof(command), "Software\\"FULLENGINENAME"\\Capabilities");
|
Q_snprintfz(command, sizeof(command), "Software\\"FULLENGINENAME"\\Capabilities");
|
||||||
ok = ok & MyRegSetValue(root, "Software\\RegisteredApplications", FULLENGINENAME, REG_SZ, command, strlen(command));
|
ok = ok & MyRegSetValue(root, "Software\\RegisteredApplications", FULLENGINENAME, REG_SZ, command, strlen(command));
|
||||||
|
|
||||||
|
//also try to add it to current user.
|
||||||
|
if (root==HKEY_LOCAL_MACHINE)
|
||||||
|
Sys_DoFileAssociations(2, schemes);
|
||||||
|
|
||||||
|
//let the shell know that file associations changed (otherwise we might have to wait for a reboot)
|
||||||
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
||||||
|
|
||||||
if (!ok && elevated < 2)
|
if (!ok && root==HKEY_LOCAL_MACHINE)
|
||||||
{
|
{
|
||||||
HINSTANCE ch = ShellExecute(mainwindow, "runas", com_argv[0], va("-register_types %i", elevated+1), NULL, SW_SHOWNORMAL);
|
ShellExecute(mainwindow, "runas", com_argv[0], va("-register_types \"%s\"", schemes), NULL, SW_SHOWNORMAL);
|
||||||
if ((qintptr_t)ch <= 32)
|
|
||||||
Sys_DoFileAssociations(2);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok)
|
if (ok && root==HKEY_LOCAL_MACHINE)
|
||||||
{
|
{
|
||||||
// char buf[1];
|
|
||||||
//attempt to display the vista+ prompt (only way possible in win8, apparently)
|
//attempt to display the vista+ prompt (only way possible in win8, apparently)
|
||||||
|
//note that in win10 this will supposedly just show a notification popup with the user required to configure it manually via control panel.
|
||||||
qIApplicationAssociationRegistrationUI *aarui = NULL;
|
qIApplicationAssociationRegistrationUI *aarui = NULL;
|
||||||
|
|
||||||
//needs to be done anyway to ensure that its listed, and so that we get the association if nothing else has it.
|
|
||||||
//however, the popup for when you start new programs is very annoying, so lets try to avoid that. our file associations are somewhat explicit anyway.
|
|
||||||
//note that you'll probably still get the clumsy prompt if you try to run fte as a different user. really depends if you gave it local machine write access.
|
|
||||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.qtv", "", buf, sizeof(buf)))
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\.qtv", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
|
||||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.mvd", "", buf, sizeof(buf)))
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\.mvd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
|
||||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.qwd", "", buf, sizeof(buf)))
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\.qwd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
|
||||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.dem", "", buf, sizeof(buf)))
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\.dem", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
|
||||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.bsp", "", buf, sizeof(buf)))
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\.bsp", "", REG_SZ, DISTRIBUTION"_BSPFile."ASSOCV, strlen(DISTRIBUTION"_BSPFile.1"));
|
|
||||||
//legacy url associations are a bit more explicit
|
|
||||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\Classes\\qw", "", buf, sizeof(buf)))
|
|
||||||
{
|
|
||||||
Q_snprintfz(command, sizeof(command), "QuakeWorld Server");
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\qw", "", REG_SZ, command, strlen(command));
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\qw", "URL Protocol", REG_SZ, "", strlen(""));
|
|
||||||
Q_snprintfz(command, sizeof(command), "\"%s\",0", com_argv[0]);
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\qw\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
|
||||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
|
||||||
MyRegSetValue(root, "Software\\Classes\\qw\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
|
||||||
}
|
|
||||||
|
|
||||||
CoInitialize(NULL);
|
CoInitialize(NULL);
|
||||||
if (FAILED(CoCreateInstance(&qCLSID_ApplicationAssociationRegistrationUI, 0, CLSCTX_INPROC_SERVER, &qIID_IApplicationAssociationRegistrationUI, (LPVOID*)&aarui)))
|
if (FAILED(CoCreateInstance(&qCLSID_ApplicationAssociationRegistrationUI, 0, CLSCTX_INPROC_SERVER, &qIID_IApplicationAssociationRegistrationUI, (LPVOID*)&aarui)))
|
||||||
aarui = NULL;
|
aarui = NULL;
|
||||||
|
@ -3308,46 +3418,6 @@ int MessageBoxU(HWND hWnd, char *lpText, char *lpCaption, UINT uType)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WEBCLIENT
|
#ifdef WEBCLIENT
|
||||||
//using this like posix' access function, but with much more code, microsoftisms, and no errno codes/info
|
|
||||||
//no, I don't really have a clue why it needs to be so long.
|
|
||||||
//#include <svrapi.h>
|
|
||||||
#ifndef ACCESS_READ
|
|
||||||
#define ACCESS_READ 0x1
|
|
||||||
#define ACCESS_WRITE 0x2
|
|
||||||
#endif
|
|
||||||
static BOOL microsoft_accessW(LPWSTR pszFolder, DWORD dwAccessDesired)
|
|
||||||
{
|
|
||||||
HANDLE hToken;
|
|
||||||
PRIVILEGE_SET PrivilegeSet;
|
|
||||||
DWORD dwPrivSetSize;
|
|
||||||
DWORD dwAccessGranted;
|
|
||||||
BOOL fAccessGranted = FALSE;
|
|
||||||
GENERIC_MAPPING GenericMapping;
|
|
||||||
SECURITY_INFORMATION si = (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION);
|
|
||||||
PSECURITY_DESCRIPTOR psdSD = NULL;
|
|
||||||
DWORD dwNeeded;
|
|
||||||
GetFileSecurityW(pszFolder,si,NULL,0,&dwNeeded);
|
|
||||||
psdSD = malloc(dwNeeded);
|
|
||||||
GetFileSecurityW(pszFolder,si,psdSD,dwNeeded,&dwNeeded);
|
|
||||||
ImpersonateSelf(SecurityImpersonation);
|
|
||||||
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken);
|
|
||||||
memset(&GenericMapping, 0xff, sizeof(GENERIC_MAPPING));
|
|
||||||
GenericMapping.GenericRead = ACCESS_READ;
|
|
||||||
GenericMapping.GenericWrite = ACCESS_WRITE;
|
|
||||||
GenericMapping.GenericExecute = 0;
|
|
||||||
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
|
|
||||||
MapGenericMask(&dwAccessDesired, &GenericMapping);
|
|
||||||
dwPrivSetSize = sizeof(PRIVILEGE_SET);
|
|
||||||
AccessCheck(psdSD, hToken, dwAccessDesired, &GenericMapping, &PrivilegeSet, &dwPrivSetSize, &dwAccessGranted, &fAccessGranted);
|
|
||||||
free(psdSD);
|
|
||||||
return fAccessGranted;
|
|
||||||
}
|
|
||||||
static BOOL microsoft_accessU(LPCSTR pszFolder, DWORD dwAccessDesired)
|
|
||||||
{
|
|
||||||
wchar_t wpath[MAX_OSPATH];
|
|
||||||
return microsoft_accessW(widen(wpath, sizeof(wpath), pszFolder), dwAccessDesired);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef GWLP_WNDPROC
|
#ifndef GWLP_WNDPROC
|
||||||
#define GWLP_WNDPROC GWL_WNDPROC
|
#define GWLP_WNDPROC GWL_WNDPROC
|
||||||
#define SetWindowLongPtr SetWindowLong
|
#define SetWindowLongPtr SetWindowLong
|
||||||
|
@ -4112,9 +4182,10 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
|
||||||
isPlugin = 0;
|
isPlugin = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (COM_CheckParm("-register_types"))
|
c = COM_CheckParm("-register_types");
|
||||||
|
if (c)
|
||||||
{
|
{
|
||||||
Sys_DoFileAssociations(1);
|
Sys_DoFileAssociations(1, (c+1 < com_argc)?com_argv[c+1]:NULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7746,7 +7746,7 @@ void Info_SetValueForKey (char *s, const char *key, const char *value, int maxsi
|
||||||
Info_SetValueForStarKey (s, key, value, maxsize);
|
Info_SetValueForStarKey (s, key, value, maxsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value))
|
void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value))
|
||||||
{
|
{
|
||||||
char key[1024];
|
char key[1024];
|
||||||
char value[1024];
|
char value[1024];
|
||||||
|
|
|
@ -764,6 +764,8 @@ typedef struct
|
||||||
char *rtcbroker; //the broker to use for webrtc connections.
|
char *rtcbroker; //the broker to use for webrtc connections.
|
||||||
char *basedir; //this is where we expect to find the data.
|
char *basedir; //this is where we expect to find the data.
|
||||||
char *iconname; //path we can find the icon (relative to the fmf's location)
|
char *iconname; //path we can find the icon (relative to the fmf's location)
|
||||||
|
|
||||||
|
char *schemes; //protocol scheme used to connect to a server running this game, use com_parse.
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
|
@ -854,9 +856,9 @@ void Info_RemovePrefixedKeys (char *start, char prefix);
|
||||||
void Info_RemoveKey (char *s, const char *key);
|
void Info_RemoveKey (char *s, const char *key);
|
||||||
char *Info_KeyForNumber (const char *s, int num);
|
char *Info_KeyForNumber (const char *s, int num);
|
||||||
void Info_Print (const char *s, const char *lineprefix);
|
void Info_Print (const char *s, const char *lineprefix);
|
||||||
|
void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));
|
||||||
/*
|
/*
|
||||||
void Info_RemoveNonStarKeys (char *start);
|
void Info_RemoveNonStarKeys (char *start);
|
||||||
void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));
|
|
||||||
void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags);
|
void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,7 @@ void FS_Manifest_Free(ftemanifest_t *man)
|
||||||
Z_Free(man->downloadsurl);
|
Z_Free(man->downloadsurl);
|
||||||
Z_Free(man->installupd);
|
Z_Free(man->installupd);
|
||||||
#endif
|
#endif
|
||||||
|
Z_Free(man->schemes);
|
||||||
Z_Free(man->protocolname);
|
Z_Free(man->protocolname);
|
||||||
Z_Free(man->eula);
|
Z_Free(man->eula);
|
||||||
Z_Free(man->defaultexec);
|
Z_Free(man->defaultexec);
|
||||||
|
@ -259,6 +260,8 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm)
|
||||||
if (oldm->installupd)
|
if (oldm->installupd)
|
||||||
newm->installupd = Z_StrDup(oldm->installupd);
|
newm->installupd = Z_StrDup(oldm->installupd);
|
||||||
#endif
|
#endif
|
||||||
|
if (oldm->schemes)
|
||||||
|
newm->schemes = Z_StrDup(oldm->schemes);
|
||||||
if (oldm->protocolname)
|
if (oldm->protocolname)
|
||||||
newm->protocolname = Z_StrDup(oldm->protocolname);
|
newm->protocolname = Z_StrDup(oldm->protocolname);
|
||||||
if (oldm->eula)
|
if (oldm->eula)
|
||||||
|
@ -319,6 +322,8 @@ static void FS_Manifest_Print(ftemanifest_t *man)
|
||||||
if (man->installupd)
|
if (man->installupd)
|
||||||
Con_Printf("install %s\n", COM_QuotedString(man->installupd, buffer, sizeof(buffer), false));
|
Con_Printf("install %s\n", COM_QuotedString(man->installupd, buffer, sizeof(buffer), false));
|
||||||
#endif
|
#endif
|
||||||
|
if (man->schemes)
|
||||||
|
Con_Printf("schemes %s\n", COM_QuotedString(man->schemes, buffer, sizeof(buffer), false));
|
||||||
if (man->protocolname)
|
if (man->protocolname)
|
||||||
Con_Printf("protocolname %s\n", COM_QuotedString(man->protocolname, buffer, sizeof(buffer), false));
|
Con_Printf("protocolname %s\n", COM_QuotedString(man->protocolname, buffer, sizeof(buffer), false));
|
||||||
if (man->defaultexec)
|
if (man->defaultexec)
|
||||||
|
@ -660,6 +665,14 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
|
||||||
man->installupd = Z_StrDup(Cmd_Argv(1));
|
man->installupd = Z_StrDup(Cmd_Argv(1));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
else if (!Q_strcasecmp(cmd, "schemes"))
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Z_Free(man->schemes);
|
||||||
|
man->schemes = Z_StrDup(Cmd_Argv(1));
|
||||||
|
for (i = 2; i < Cmd_Argc(); i++)
|
||||||
|
Z_StrCat(&man->schemes, va(" %s", Cmd_Argv(i)));
|
||||||
|
}
|
||||||
else if (!Q_strcasecmp(cmd, "protocolname"))
|
else if (!Q_strcasecmp(cmd, "protocolname"))
|
||||||
{
|
{
|
||||||
Z_Free(man->protocolname);
|
Z_Free(man->protocolname);
|
||||||
|
@ -3807,9 +3820,9 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
|
||||||
/*quake requires a few settings for compatibility*/
|
/*quake requires a few settings for compatibility*/
|
||||||
#define QRPCOMPAT "set cl_cursor_scale 0.2\nset cl_cursor_bias_x 7.5\nset cl_cursor_bias_y 0.8\n"
|
#define QRPCOMPAT "set cl_cursor_scale 0.2\nset cl_cursor_bias_x 7.5\nset cl_cursor_bias_y 0.8\n"
|
||||||
#define QUAKESPASMSUCKS "set mod_h2holey_bugged 1\n"
|
#define QUAKESPASMSUCKS "set mod_h2holey_bugged 1\n"
|
||||||
#define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset r_meshpitch -1\nr_sprite_backfacing 1\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
|
#define QCFG "//schemes quake qw\n" "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset r_meshpitch -1\nr_sprite_backfacing 1\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
|
||||||
/*NetQuake reconfiguration, to make certain people feel more at home...*/
|
/*NetQuake reconfiguration, to make certain people feel more at home...*/
|
||||||
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\ncfg_save_auto 1\n" QCFG "set sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n"
|
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\n" QCFG "cfg_save_auto 1\nset sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n"
|
||||||
#define SPASMCFG NQCFG "fps_preset builtin_spasm\nset cl_demoreel 0\ncl_sbar 2\nset gl_load24bit 1\n"
|
#define SPASMCFG NQCFG "fps_preset builtin_spasm\nset cl_demoreel 0\ncl_sbar 2\nset gl_load24bit 1\n"
|
||||||
#define FITZCFG NQCFG "fps_preset builtin_spasm\ncl_sbar 2\nset gl_load24bit 1\n"
|
#define FITZCFG NQCFG "fps_preset builtin_spasm\ncl_sbar 2\nset gl_load24bit 1\n"
|
||||||
#define TENEBRAECFG NQCFG "fps_preset builtin_tenebrae\n"
|
#define TENEBRAECFG NQCFG "fps_preset builtin_tenebrae\n"
|
||||||
|
@ -3823,11 +3836,11 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
|
||||||
/*some modern non-compat settings*/
|
/*some modern non-compat settings*/
|
||||||
#define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n"
|
#define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n"
|
||||||
/*set some stuff so our regular qw client appears more like hexen2. sv_mintic is required to 'fix' the ravenstaff so that its projectiles don't impact upon each other*/
|
/*set some stuff so our regular qw client appears more like hexen2. sv_mintic is required to 'fix' the ravenstaff so that its projectiles don't impact upon each other*/
|
||||||
#define HEX2CFG "set v_gammainverted 1\nset com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset r_meshpitch -1\nset r_meshroll -1\nr_sprite_backfacing 1\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
|
#define HEX2CFG "//schemes hexen2\n" "set v_gammainverted 1\nset com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset r_meshpitch -1\nset r_meshroll -1\nr_sprite_backfacing 1\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
|
||||||
/*yay q2!*/
|
/*yay q2!*/
|
||||||
#define Q2CFG "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n"
|
#define Q2CFG "//schemes quake2\n" "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n"
|
||||||
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
|
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
|
||||||
#define Q3CFG "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 8\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n"
|
#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 8\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n"
|
||||||
//#define RMQCFG "sv_bigcoords 1\n"
|
//#define RMQCFG "sv_bigcoords 1\n"
|
||||||
|
|
||||||
#ifdef HAVE_SSL
|
#ifdef HAVE_SSL
|
||||||
|
@ -5900,6 +5913,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!man->schemes)
|
||||||
|
{
|
||||||
|
Cmd_TokenizeString(va("schemes \"%s\"", gamemode_info[i].argname+1), false, false);
|
||||||
|
FS_Manifest_ParseTokens(man);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef PACKAGEMANAGER
|
#ifdef PACKAGEMANAGER
|
||||||
if (!man->downloadsurl && gamemode_info[i].downloadsurl)
|
if (!man->downloadsurl && gamemode_info[i].downloadsurl)
|
||||||
{
|
{
|
||||||
|
@ -5932,7 +5951,17 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
|
||||||
}
|
}
|
||||||
if (!man->defaultexec && gamemode_info[i].customexec)
|
if (!man->defaultexec && gamemode_info[i].customexec)
|
||||||
{
|
{
|
||||||
man->defaultexec = Z_StrDup(gamemode_info[i].customexec);
|
const char *e = gamemode_info[i].customexec;
|
||||||
|
while (e[0] == '/' && e[1] == '/')
|
||||||
|
{
|
||||||
|
e+=2;
|
||||||
|
while(*e)
|
||||||
|
{
|
||||||
|
if (*e++ == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
man->defaultexec = Z_StrDup(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
builtingame = true;
|
builtingame = true;
|
||||||
|
|
|
@ -49,6 +49,7 @@ void VARGS Sys_Printf (char *fmt, ...) LIKEPRINTF(1);
|
||||||
void Sys_Warn (char *fmt, ...) LIKEPRINTF(1);
|
void Sys_Warn (char *fmt, ...) LIKEPRINTF(1);
|
||||||
//like Sys_Printf. dunno why there needs to be two of em.
|
//like Sys_Printf. dunno why there needs to be two of em.
|
||||||
|
|
||||||
|
char *Sys_URIScheme_NeedsRegistering(void); //returns the name of one of the current manifests uri schemes that isn't registered (but should be registerable).
|
||||||
void Sys_Quit (void);
|
void Sys_Quit (void);
|
||||||
void Sys_RecentServer(char *command, char *target, char *title, char *desc);
|
void Sys_RecentServer(char *command, char *target, char *title, char *desc);
|
||||||
qboolean Sys_RunInstaller(void);
|
qboolean Sys_RunInstaller(void);
|
||||||
|
|
|
@ -57,6 +57,7 @@ typedef struct svm_server_s {
|
||||||
bucket_t bucket; //for faster address lookups.
|
bucket_t bucket; //for faster address lookups.
|
||||||
struct svm_game_s *game;
|
struct svm_game_s *game;
|
||||||
struct svm_server_s *next;
|
struct svm_server_s *next;
|
||||||
|
char rules[1024];
|
||||||
} svm_server_t;
|
} svm_server_t;
|
||||||
|
|
||||||
typedef struct svm_game_s {
|
typedef struct svm_game_s {
|
||||||
|
@ -66,6 +67,7 @@ typedef struct svm_game_s {
|
||||||
size_t numservers;
|
size_t numservers;
|
||||||
qboolean persistent;
|
qboolean persistent;
|
||||||
char *aliases; //list of terminated names, terminated with a double-null
|
char *aliases; //list of terminated names, terminated with a double-null
|
||||||
|
char *scheme;
|
||||||
char name[1]; //eg: Quake
|
char name[1]; //eg: Quake
|
||||||
} svm_game_t;
|
} svm_game_t;
|
||||||
|
|
||||||
|
@ -604,6 +606,31 @@ vfsfile_t *SVM_Generate_Gamelist(const char **mimetype, const char *query)
|
||||||
*mimetype = "text/html";
|
*mimetype = "text/html";
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
struct rulelist_s
|
||||||
|
{
|
||||||
|
unsigned int lines;
|
||||||
|
unsigned int blobofs;
|
||||||
|
char *line[64];
|
||||||
|
char blob[8192];
|
||||||
|
};
|
||||||
|
static void SVM_GatherServerRule(void *ctx, const char *key, const char *val)
|
||||||
|
{
|
||||||
|
struct rulelist_s *rules = ctx;
|
||||||
|
char niceval[256];
|
||||||
|
if (rules->lines == countof(rules->line))
|
||||||
|
return; //overflow
|
||||||
|
QuakeCharsToHTML(niceval, sizeof(niceval), val, false);
|
||||||
|
if (!Q_snprintfz(rules->blob+rules->blobofs, sizeof(rules->blob)-rules->blobofs, "<tr><td>%s</td><td>%s</td></tr>\n", key, niceval))
|
||||||
|
{
|
||||||
|
rules->line[rules->lines++] = rules->blob+rules->blobofs;
|
||||||
|
rules->blobofs += strlen(rules->blob+rules->blobofs)+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static int QDECL SVM_SortServerRule(const void *r1, const void *r2)
|
||||||
|
{
|
||||||
|
return Q_strcasecmp(*(char*const*const)r1, *(char*const*const)r2);
|
||||||
|
}
|
||||||
|
|
||||||
vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr, const char *query)
|
vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr, const char *query)
|
||||||
{
|
{
|
||||||
vfsfile_t *f = VFSPIPE_Open(1, false);
|
vfsfile_t *f = VFSPIPE_Open(1, false);
|
||||||
|
@ -611,13 +638,12 @@ vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr
|
||||||
char hostname[1024];
|
char hostname[1024];
|
||||||
svm_server_t *server;
|
svm_server_t *server;
|
||||||
netadr_t adr[64];
|
netadr_t adr[64];
|
||||||
int count;
|
size_t count, u;
|
||||||
|
const char *url;
|
||||||
|
|
||||||
VFS_PRINTF(f, "%s", master_css);
|
VFS_PRINTF(f, "%s", master_css);
|
||||||
VFS_PRINTF(f, "<h1>Single Server Info</h1>\n");
|
VFS_PRINTF(f, "<h1>Single Server Info</h1>\n");
|
||||||
|
|
||||||
VFS_PRINTF(f, "<table border=1>\n");
|
|
||||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
|
||||||
//FIXME: block dns lookups here?
|
//FIXME: block dns lookups here?
|
||||||
count = NET_StringToAdr2(serveraddr, 0, adr, countof(adr), NULL);
|
count = NET_StringToAdr2(serveraddr, 0, adr, countof(adr), NULL);
|
||||||
while(count-->0)
|
while(count-->0)
|
||||||
|
@ -625,13 +651,45 @@ vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr
|
||||||
server = SVM_GetServer(&adr[count]);
|
server = SVM_GetServer(&adr[count]);
|
||||||
if (server)
|
if (server)
|
||||||
{
|
{
|
||||||
|
VFS_PRINTF(f, "<table border=1>\n");
|
||||||
|
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", server->game?server->game->name:"Unknown", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr), (server->needpass&1)?"🔒":"", hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
|
||||||
|
url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||||
|
if (server->game->scheme && !server->brokerid)
|
||||||
|
url = va("<a href=\"%s://%s\">%s</a>", server->game->scheme, url, url);
|
||||||
|
|
||||||
|
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", server->game?server->game->name:"Unknown", url, (server->needpass&1)?"🔒":"", hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||||
|
VFS_PRINTF(f, "</table>\n");
|
||||||
|
VFS_PRINTF(f, "<br/>\n");
|
||||||
|
|
||||||
|
if (*server->rules)
|
||||||
|
{
|
||||||
|
struct rulelist_s rules;
|
||||||
|
rules.lines = rules.blobofs = 0;
|
||||||
|
Info_Enumerate(server->rules, &rules, SVM_GatherServerRule);
|
||||||
|
qsort(rules.line, rules.lines, sizeof(rules.line[0]), SVM_SortServerRule);
|
||||||
|
|
||||||
|
//VFS_PRINTF(f, "<table border=0>\n");
|
||||||
|
// VFS_PRINTF(f, "<td></td><td>");
|
||||||
|
VFS_PRINTF(f, "<table border=1>\n");
|
||||||
|
VFS_PRINTF(f, "</th><th>Rule</th><th>Value</th></tr>\n");
|
||||||
|
for (u = 0; u < rules.lines; u++)
|
||||||
|
VFS_PUTS(f, rules.line[u]);
|
||||||
|
VFS_PRINTF(f, "</table>");
|
||||||
|
// VFS_PRINTF(f, "</td>");
|
||||||
|
//VFS_PRINTF(f, "</table>\n");
|
||||||
|
VFS_PRINTF(f, "<br/>\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
VFS_PRINTF(f, "<table border=1>\n");
|
||||||
|
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||||
VFS_PRINTF(f, "<tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr>\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &adr[count]));
|
VFS_PRINTF(f, "<tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr>\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &adr[count]));
|
||||||
|
VFS_PRINTF(f, "</table>\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
VFS_PRINTF(f, "</table>\n");
|
|
||||||
|
|
||||||
*mimetype = "text/html";
|
*mimetype = "text/html";
|
||||||
return f;
|
return f;
|
||||||
|
@ -642,7 +700,7 @@ vfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr
|
||||||
vfsfile_t *f = VFSPIPE_Open(1, false);
|
vfsfile_t *f = VFSPIPE_Open(1, false);
|
||||||
char tmpbuf[256];
|
char tmpbuf[256];
|
||||||
char hostname[1024];
|
char hostname[1024];
|
||||||
const char *url;
|
const char *url, *infourl;
|
||||||
svm_game_t *game;
|
svm_game_t *game;
|
||||||
svm_server_t *server;
|
svm_server_t *server;
|
||||||
unsigned clients=0,bots=0,specs=0;
|
unsigned clients=0,bots=0,specs=0;
|
||||||
|
@ -684,11 +742,14 @@ vfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr
|
||||||
{
|
{
|
||||||
url = tmpbuf;
|
url = tmpbuf;
|
||||||
Q_snprintfz(tmpbuf, sizeof(tmpbuf), "rtc://%s/%s", masteraddr, server->brokerid);
|
Q_snprintfz(tmpbuf, sizeof(tmpbuf), "rtc://%s/%s", masteraddr, server->brokerid);
|
||||||
|
infourl = tmpbuf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
{
|
||||||
|
infourl = url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||||
|
}
|
||||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s%s%s</td><td>%s</td><td>%s</td><td>%u", url, (server->needpass&1)?"🔒":"", (server->coop&1)?"🚸":"", hostname, server->gamedir, server->mapname, server->clients);
|
VFS_PRINTF(f, "<tr><td><a href=\"/server/%s\">%s</a></td><td>%s%s%s</td><td>%s</td><td>%s</td><td>%u", infourl, url, (server->needpass&1)?"🔒":"", (server->coop&1)?"🚸":"", hostname, server->gamedir, server->mapname, server->clients);
|
||||||
if (server->bots)
|
if (server->bots)
|
||||||
VFS_PRINTF(f, "+%ub", server->bots);
|
VFS_PRINTF(f, "+%ub", server->bots);
|
||||||
VFS_PRINTF(f, "/%u", server->maxclients);
|
VFS_PRINTF(f, "/%u", server->maxclients);
|
||||||
|
@ -1192,6 +1253,7 @@ static void SVM_ProcessUDPPacket(void)
|
||||||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||||
if (!strcmp(chal, ourchallenge))
|
if (!strcmp(chal, ourchallenge))
|
||||||
{
|
{
|
||||||
|
|
||||||
bots = atoi(Info_ValueForKey(s, "bots"));
|
bots = atoi(Info_ValueForKey(s, "bots"));
|
||||||
clients = atoi(Info_ValueForKey(s, "clients"));
|
clients = atoi(Info_ValueForKey(s, "clients"));
|
||||||
clients = max(0, clients-bots);
|
clients = max(0, clients-bots);
|
||||||
|
@ -1204,6 +1266,7 @@ static void SVM_ProcessUDPPacket(void)
|
||||||
srv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);
|
srv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);
|
||||||
if (srv)
|
if (srv)
|
||||||
{
|
{
|
||||||
|
Q_strncpyz(srv->rules, s, sizeof(srv->rules));
|
||||||
if (developer.ival)
|
if (developer.ival)
|
||||||
Info_Print(s, "\t");
|
Info_Print(s, "\t");
|
||||||
if (game)
|
if (game)
|
||||||
|
@ -1380,6 +1443,7 @@ static void SVM_ProcessUDPPacket(void)
|
||||||
srv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);
|
srv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);
|
||||||
if (srv)
|
if (srv)
|
||||||
{
|
{
|
||||||
|
Q_strncpyz(srv->rules, s, sizeof(srv->rules));
|
||||||
if (developer.ival)
|
if (developer.ival)
|
||||||
Info_Print(s, "\t");
|
Info_Print(s, "\t");
|
||||||
srv->protover = 3;//atoi(Info_ValueForKey(s, "protocol"));
|
srv->protover = 3;//atoi(Info_ValueForKey(s, "protocol"));
|
||||||
|
@ -1606,6 +1670,13 @@ static qboolean SVM_FoundManifest(void *usr, ftemanifest_t *man)
|
||||||
g = COM_Parse(g);
|
g = COM_Parse(g);
|
||||||
game = SVM_FindGame(com_token, 2);
|
game = SVM_FindGame(com_token, 2);
|
||||||
#endif
|
#endif
|
||||||
|
if (!game)
|
||||||
|
return false;
|
||||||
|
if (man->schemes && !game->scheme)
|
||||||
|
{
|
||||||
|
COM_Parse(man->schemes);
|
||||||
|
game->scheme = Z_StrDup(com_token);
|
||||||
|
}
|
||||||
while (*g)
|
while (*g)
|
||||||
{
|
{
|
||||||
g = COM_Parse(g);
|
g = COM_Parse(g);
|
||||||
|
|
|
@ -155,6 +155,11 @@ void Sys_BrowserRedirect_f(void)
|
||||||
emscriptenfte_window_location(Cmd_Argv(1));
|
emscriptenfte_window_location(Cmd_Argv(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *Sys_URIScheme_NeedsRegistering(void)
|
||||||
|
{ //just disables the prompts that we can't honour anyway.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void Sys_Init(void)
|
void Sys_Init(void)
|
||||||
{
|
{
|
||||||
extern cvar_t vid_width, vid_height, vid_fullscreen;
|
extern cvar_t vid_width, vid_height, vid_fullscreen;
|
||||||
|
|
Loading…
Reference in a new issue