6a831e2f99
enabled ssl support in windows for plugins. updated jabber plugin. tweaked downloading logic in an effort to end the madness. Updated sql code to support sleeping the qc thread until query completion, as a more readable alternative to callbacks. Client updates should be more explicit now, removing a potential bug. networked terrain editing should now be in a better state. updated the dir command to show paths too (so when the same file is listed twice, the pak its in will at least be different). Many files changed because of this. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4273 fc73d0e0-1445-4013-8a0c-d673dee63da5
2681 lines
62 KiB
C
2681 lines
62 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// sys_win.h
|
|
|
|
#include "quakedef.h"
|
|
|
|
#include "winquake.h"
|
|
#include "resource.h"
|
|
#include "errno.h"
|
|
#include "fcntl.h"
|
|
#include <limits.h>
|
|
#include <conio.h>
|
|
#include <io.h>
|
|
#include <direct.h>
|
|
|
|
//#define RESTARTTEST
|
|
|
|
#ifdef MULTITHREAD
|
|
#include <process.h>
|
|
#endif
|
|
|
|
#ifdef GLQUAKE
|
|
#define PRINTGLARRAYS
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#if _MSC_VER >= 1300
|
|
#define CATCHCRASH
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(CLIENTONLY) && !defined(SERVERONLY)
|
|
qboolean isDedicated = false;
|
|
#endif
|
|
extern qboolean isPlugin;
|
|
qboolean debugout;
|
|
float gammapending; //to cope with ATI. When it times out, v_gamma is reforced in order to correct/update gamma now the drivers think that they have won.
|
|
|
|
HWND sys_parentwindow;
|
|
unsigned int sys_parentleft; //valid if sys_parentwindow is set
|
|
unsigned int sys_parenttop;
|
|
unsigned int sys_parentwidth; //valid if sys_parentwindow is set
|
|
unsigned int sys_parentheight;
|
|
|
|
extern int fs_switchgame;
|
|
|
|
|
|
#ifdef RESTARTTEST
|
|
jmp_buf restart_jmpbuf;
|
|
#endif
|
|
/*
|
|
================
|
|
Sys_RandomBytes
|
|
================
|
|
*/
|
|
#include <wincrypt.h>
|
|
qboolean Sys_RandomBytes(qbyte *string, int len)
|
|
{
|
|
HCRYPTPROV prov;
|
|
|
|
if(!CryptAcquireContext( &prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!CryptGenRandom(prov, len, (BYTE *)string))
|
|
{
|
|
CryptReleaseContext( prov, 0);
|
|
return false;
|
|
}
|
|
CryptReleaseContext(prov, 0);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Library loading
|
|
=================
|
|
*/
|
|
void Sys_CloseLibrary(dllhandle_t *lib)
|
|
{
|
|
FreeLibrary((HMODULE)lib);
|
|
}
|
|
dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
|
|
{
|
|
int i;
|
|
HMODULE lib;
|
|
|
|
lib = LoadLibrary(name);
|
|
if (!lib)
|
|
{
|
|
#ifdef _WIN64
|
|
lib = LoadLibrary(va("%s_64", name));
|
|
#elif defined(_WIN32)
|
|
lib = LoadLibrary(va("%s_32", name));
|
|
#endif
|
|
if (!lib)
|
|
return NULL;
|
|
}
|
|
|
|
if (funcs)
|
|
{
|
|
for (i = 0; funcs[i].name; i++)
|
|
{
|
|
*funcs[i].funcptr = GetProcAddress(lib, funcs[i].name);
|
|
if (!*funcs[i].funcptr)
|
|
break;
|
|
}
|
|
if (funcs[i].name)
|
|
{
|
|
Con_DPrintf("Missing export \"%s\" in \"%s\"\n", funcs[i].name, name);
|
|
Sys_CloseLibrary((dllhandle_t*)lib);
|
|
lib = NULL;
|
|
}
|
|
}
|
|
|
|
return (dllhandle_t*)lib;
|
|
}
|
|
|
|
void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)
|
|
{
|
|
if (!module)
|
|
return NULL;
|
|
return GetProcAddress((HINSTANCE)module, exportname);
|
|
}
|
|
#ifdef HLSERVER
|
|
char *Sys_GetNameForAddress(dllhandle_t *module, void *address)
|
|
{
|
|
//windows doesn't provide a function to do this, so we have to do it ourselves.
|
|
//this isn't the fastest way...
|
|
//halflife needs this function.
|
|
char *base = (char *)module;
|
|
|
|
IMAGE_DATA_DIRECTORY *datadir;
|
|
IMAGE_EXPORT_DIRECTORY *block;
|
|
IMAGE_NT_HEADERS *ntheader;
|
|
IMAGE_DOS_HEADER *dosheader = (void*)base;
|
|
|
|
int i, j;
|
|
DWORD *funclist;
|
|
DWORD *namelist;
|
|
SHORT *ordilist;
|
|
|
|
if (!dosheader || dosheader->e_magic != IMAGE_DOS_SIGNATURE)
|
|
return NULL; //yeah, that wasn't an exe
|
|
|
|
ntheader = (void*)(base + dosheader->e_lfanew);
|
|
if (!dosheader->e_lfanew || ntheader->Signature != IMAGE_NT_SIGNATURE)
|
|
return NULL; //urm, wait, a 16bit dos exe?
|
|
|
|
|
|
datadir = &ntheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
|
|
|
block = (IMAGE_EXPORT_DIRECTORY *)(base + datadir->VirtualAddress);
|
|
funclist = (DWORD*)(base+block->AddressOfFunctions);
|
|
namelist = (DWORD*)(base+block->AddressOfNames);
|
|
ordilist = (SHORT*)(base+block->AddressOfNameOrdinals);
|
|
for (i = 0; i < block->NumberOfFunctions; i++)
|
|
{
|
|
if (base+funclist[i] == address)
|
|
{
|
|
for (j = 0; j < block->NumberOfNames; j++)
|
|
{
|
|
if (ordilist[j] == i)
|
|
{
|
|
return base+namelist[i];
|
|
}
|
|
}
|
|
//it has no name. huh?
|
|
return NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef Q2SERVER
|
|
static HINSTANCE game_library;
|
|
|
|
/*
|
|
=================
|
|
Sys_UnloadGame
|
|
=================
|
|
*/
|
|
void Sys_UnloadGame (void)
|
|
{
|
|
if (!FreeLibrary (game_library))
|
|
Sys_Error ("FreeLibrary failed for game library");
|
|
game_library = NULL;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Sys_GetGameAPI
|
|
|
|
Loads the game dll
|
|
=================
|
|
*/
|
|
void *Sys_GetGameAPI (void *parms)
|
|
{
|
|
void *(VARGS *GetGameAPI) (void *);
|
|
char name[MAX_OSPATH];
|
|
char *path;
|
|
char cwd[MAX_OSPATH];
|
|
#if defined _M_IX86
|
|
const char *gamename = "gamex86.dll";
|
|
|
|
#ifdef NDEBUG
|
|
const char *debugdir = "release";
|
|
#else
|
|
const char *debugdir = "debug";
|
|
#endif
|
|
|
|
#elif defined(__amd64__) || defined(__AMD64__) || defined(_AMD64_)
|
|
const char *gamename = "gameamd.dll";
|
|
|
|
#ifdef NDEBUG
|
|
const char *debugdir = "release";
|
|
#else
|
|
const char *debugdir = "debug";
|
|
#endif
|
|
|
|
#elif defined _M_ALPHA
|
|
const char *gamename = "gameaxp.dll";
|
|
|
|
#ifdef NDEBUG
|
|
const char *debugdir = "releaseaxp";
|
|
#else
|
|
const char *debugdir = "debugaxp";
|
|
#endif
|
|
|
|
#endif
|
|
|
|
if (game_library)
|
|
Sys_Error ("Sys_GetGameAPI without Sys_UnloadingGame");
|
|
|
|
// check the current debug directory first for development purposes
|
|
#ifdef _WIN32
|
|
GetCurrentDirectory(sizeof(cwd), cwd);
|
|
#else
|
|
_getcwd (cwd, sizeof(cwd));
|
|
#endif
|
|
snprintf (name, sizeof(name), "%s/%s/%s", cwd, debugdir, gamename);
|
|
game_library = LoadLibrary ( name );
|
|
if (game_library)
|
|
{
|
|
Con_DPrintf ("LoadLibrary (%s)\n", name);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
// check the current directory for other development purposes
|
|
_snprintf (name, sizeof(name), "%s/%s", cwd, gamename);
|
|
game_library = LoadLibrary ( name );
|
|
if (game_library)
|
|
{
|
|
Con_DPrintf ("LoadLibrary (%s)\n", name);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// now run through the search paths
|
|
path = NULL;
|
|
while (1)
|
|
{
|
|
path = COM_NextPath (path);
|
|
if (!path)
|
|
return NULL; // couldn't find one anywhere
|
|
snprintf (name, sizeof(name), "%s/%s", path, gamename);
|
|
game_library = LoadLibrary (name);
|
|
if (game_library)
|
|
{
|
|
Con_DPrintf ("LoadLibrary (%s)\n",name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GetGameAPI = (void *)GetProcAddress (game_library, "GetGameAPI");
|
|
if (!GetGameAPI)
|
|
{
|
|
Sys_UnloadGame ();
|
|
return NULL;
|
|
}
|
|
|
|
return GetGameAPI (parms);
|
|
}
|
|
#endif
|
|
|
|
|
|
#define MINIMUM_WIN_MEMORY MINIMUM_MEMORY
|
|
#define MAXIMUM_WIN_MEMORY 0x8000000
|
|
|
|
int starttime;
|
|
qboolean ActiveApp, Minimized;
|
|
qboolean WinNT;
|
|
|
|
static HANDLE hinput, houtput;
|
|
|
|
HANDLE qwclsemaphore;
|
|
|
|
static HANDLE tevent;
|
|
|
|
void Sys_InitFloatTime (void);
|
|
|
|
int VARGS Sys_DebugLog(char *file, char *fmt, ...)
|
|
{
|
|
FILE *fd;
|
|
va_list argptr;
|
|
static char data[1024];
|
|
|
|
va_start(argptr, fmt);
|
|
vsnprintf(data, sizeof(data)-1, fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
#if defined(CRAZYDEBUGGING) && CRAZYDEBUGGING > 1
|
|
{
|
|
static int sock;
|
|
if (!sock)
|
|
{
|
|
struct sockaddr_in sa;
|
|
netadr_t na;
|
|
int _true = true;
|
|
int listip;
|
|
listip = COM_CheckParm("-debugip");
|
|
NET_StringToAdr(listip?com_argv[listip+1]:"127.0.0.1", &na);
|
|
NetadrToSockadr(&na, (struct sockaddr_qstorage*)&sa);
|
|
sa.sin_port = htons(10000);
|
|
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (-1==connect(sock, (struct sockaddr*)&sa, sizeof(sa)))
|
|
Sys_Error("Couldn't send debug log lines\n");
|
|
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&_true, sizeof(_true));
|
|
}
|
|
send(sock, data, strlen(data), 0);
|
|
}
|
|
#endif
|
|
fd = fopen(file, "ab");
|
|
if (fd)
|
|
{
|
|
fprintf(fd, "%s", data);
|
|
fclose(fd);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
};
|
|
|
|
#ifdef CATCHCRASH
|
|
#include "dbghelp.h"
|
|
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP) (
|
|
HANDLE hProcess,
|
|
DWORD ProcessId,
|
|
HANDLE hFile,
|
|
MINIDUMP_TYPE DumpType,
|
|
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
|
);
|
|
|
|
#ifdef PRINTGLARRAYS
|
|
#include "glquake.h"
|
|
#define GL_VERTEX_ARRAY_BINDING 0x85B5
|
|
#define GL_ARRAY_BUFFER 0x8892
|
|
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
|
|
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
|
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
|
|
#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896
|
|
#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897
|
|
#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898
|
|
#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A
|
|
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
|
|
#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
|
|
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
|
|
#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
|
|
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
|
|
#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
|
|
#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
|
|
#define GL_CURRENT_PROGRAM 0x8B8D
|
|
|
|
char *DecodeGLEnum(GLenum num)
|
|
{
|
|
switch(num)
|
|
{
|
|
case GL_CW: return "GL_CW";
|
|
case GL_CCW: return "GL_CCW";
|
|
case GL_NEVER: return "GL_NEVER";
|
|
case GL_LESS: return "GL_LESS";
|
|
case GL_EQUAL: return "GL_EQUAL";
|
|
case GL_LEQUAL: return "GL_LEQUAL";
|
|
case GL_GREATER: return "GL_GREATER";
|
|
case GL_NOTEQUAL: return "GL_NOTEQUAL";
|
|
case GL_GEQUAL: return "GL_GEQUAL";
|
|
case GL_ALWAYS: return "GL_ALWAYS";
|
|
case GL_FRONT: return "GL_FRONT";
|
|
case GL_BACK: return "GL_BACK";
|
|
case GL_FRONT_AND_BACK: return "GL_FRONT_AND_BACK";
|
|
case GL_COMBINE_ARB: return "GL_COMBINE";
|
|
case GL_MODULATE: return "GL_MODULATE";
|
|
case GL_REPLACE: return "GL_REPLACE";
|
|
case GL_ZERO: return "GL_ZERO";
|
|
case GL_ONE: return "GL_ONE";
|
|
case GL_SRC_COLOR: return "GL_SRC_COLOR";
|
|
case GL_ONE_MINUS_SRC_COLOR: return "GL_ONE_MINUS_SRC_COLOR";
|
|
case GL_SRC_ALPHA: return "GL_SRC_ALPHA";
|
|
case GL_ONE_MINUS_SRC_ALPHA: return "GL_ONE_MINUS_SRC_ALPHA";
|
|
case GL_DST_ALPHA: return "GL_DST_ALPHA";
|
|
case GL_ONE_MINUS_DST_ALPHA: return "GL_ONE_MINUS_DST_ALPHA";
|
|
case GL_DST_COLOR: return "GL_DST_COLOR";
|
|
case GL_ONE_MINUS_DST_COLOR: return "GL_ONE_MINUS_DST_COLOR";
|
|
case GL_SRC_ALPHA_SATURATE: return "GL_SRC_ALPHA_SATURATE";
|
|
default: return va("0x%x", num);
|
|
}
|
|
}
|
|
|
|
void DumpGLState(void)
|
|
{
|
|
int rval;
|
|
void *ptr;
|
|
int i;
|
|
GLint glint;
|
|
GLint glint4[4];
|
|
void (APIENTRY *qglGetVertexAttribiv) (GLuint index, GLenum pname, GLint* params);
|
|
void (APIENTRY *qglGetVertexAttribPointerv) (GLuint index, GLenum pname, GLvoid** pointer);
|
|
qglGetVertexAttribiv = (void*)wglGetProcAddress("glGetVertexAttribiv");
|
|
qglGetVertexAttribPointerv = (void*)wglGetProcAddress("glGetVertexAttribPointerv");
|
|
#pragma comment(lib,"opengl32.lib")
|
|
|
|
if (qglGetVertexAttribiv)
|
|
{
|
|
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &rval);
|
|
Sys_Printf("VERTEX_ARRAY_BINDING: %i\n", rval);
|
|
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &rval);
|
|
Sys_Printf("GL_ARRAY_BUFFER_BINDING: %i\n", rval);
|
|
if (glIsEnabled(GL_COLOR_ARRAY))
|
|
{
|
|
glGetIntegerv(GL_COLOR_ARRAY_BUFFER_BINDING, &rval);
|
|
glGetPointerv(GL_COLOR_ARRAY_POINTER, &ptr);
|
|
Sys_Printf("GL_COLOR_ARRAY: %s %i:%p\n", glIsEnabled(GL_COLOR_ARRAY)?"en":"dis", rval, ptr);
|
|
}
|
|
// if (glIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT))
|
|
// {
|
|
// glGetPointerv(GL_FOG_COORD_ARRAY_POINTER, &ptr);
|
|
// Sys_Printf("GL_FOG_COORDINATE_ARRAY_EXT: %i (%lx)\n", (int) glIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT), (int) ptr);
|
|
// }
|
|
// if (glIsEnabled(GL_INDEX_ARRAY))
|
|
{
|
|
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &rval);
|
|
glGetPointerv(GL_INDEX_ARRAY_POINTER, &ptr);
|
|
Sys_Printf("GL_INDEX_ARRAY: %s %i:%p\n", glIsEnabled(GL_INDEX_ARRAY)?"en":"dis", rval, ptr);
|
|
}
|
|
if (glIsEnabled(GL_NORMAL_ARRAY))
|
|
{
|
|
glGetIntegerv(GL_NORMAL_ARRAY_BUFFER_BINDING, &rval);
|
|
glGetPointerv(GL_NORMAL_ARRAY_POINTER, &ptr);
|
|
Sys_Printf("GL_NORMAL_ARRAY: %s %i:%p\n", glIsEnabled(GL_NORMAL_ARRAY)?"en":"dis", rval, ptr);
|
|
}
|
|
// glGetPointerv(GL_SECONDARY_COLOR_ARRAY_POINTER, &ptr);
|
|
// Sys_Printf("GL_SECONDARY_COLOR_ARRAY: %i (%lx)\n", (int) glIsEnabled(GL_SECONDARY_COLOR_ARRAY), (int) ptr);
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
qglClientActiveTextureARB(mtexid0 + i);
|
|
if (glIsEnabled(GL_TEXTURE_COORD_ARRAY))
|
|
{
|
|
glGetIntegerv(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &rval);
|
|
glGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER, &ptr);
|
|
Sys_Printf("GL_TEXTURE_COORD_ARRAY %i: %s %i:%p\n", i, glIsEnabled(GL_TEXTURE_COORD_ARRAY)?"en":"dis", rval, ptr);
|
|
}
|
|
}
|
|
if (glIsEnabled(GL_VERTEX_ARRAY))
|
|
{
|
|
glGetIntegerv(GL_VERTEX_ARRAY_BUFFER_BINDING, &rval);
|
|
glGetPointerv(GL_VERTEX_ARRAY_POINTER, &ptr);
|
|
Sys_Printf("GL_VERTEX_ARRAY: %s %i:%p\n", glIsEnabled(GL_VERTEX_ARRAY)?"en":"dis", rval, ptr);
|
|
}
|
|
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
int en, bo, as, st, ty, no;
|
|
|
|
qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &en);
|
|
if (!en)
|
|
continue;
|
|
qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &bo);
|
|
qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &as);
|
|
qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &st);
|
|
qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &ty);
|
|
qglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &no);
|
|
qglGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr);
|
|
|
|
Sys_Printf("attrib%i: %s as:%i st:%i ty:%0x %s%i:%p\n", i, en?"en":"dis", as, st,ty,no?"norm ":"", bo, ptr);
|
|
}
|
|
|
|
glGetIntegerv(GL_CURRENT_PROGRAM, &glint);
|
|
Sys_Printf("GL_CURRENT_PROGRAM: %i\n", glint);
|
|
|
|
glGetIntegerv(GL_BLEND, &glint);
|
|
Sys_Printf("GL_BLEND: %i\n", glint);
|
|
glGetIntegerv(GL_BLEND_SRC, &glint);
|
|
Sys_Printf("GL_BLEND_SRC: %i\n", DecodeGLEnum(glint));
|
|
glGetIntegerv(GL_BLEND_DST, &glint);
|
|
Sys_Printf("GL_BLEND_DST: %i\n", DecodeGLEnum(glint));
|
|
|
|
glGetIntegerv(GL_DEPTH_WRITEMASK, &glint);
|
|
Sys_Printf("GL_DEPTH_WRITEMASK: %i\n", glint);
|
|
glGetIntegerv(GL_DEPTH_TEST, &glint);
|
|
Sys_Printf("GL_DEPTH_TEST: %i\n", glint);
|
|
glGetIntegerv(GL_DEPTH_FUNC, &glint);
|
|
Sys_Printf("GL_DEPTH_FUNC: %s\n", DecodeGLEnum(glint));
|
|
glGetIntegerv(GL_CULL_FACE, &glint);
|
|
Sys_Printf("GL_CULL_FACE: %i\n", glint);
|
|
glGetIntegerv(GL_CULL_FACE_MODE, &glint);
|
|
Sys_Printf("GL_CULL_FACE_MODE: %s\n", DecodeGLEnum(glint));
|
|
glGetIntegerv(GL_FRONT_FACE, &glint);
|
|
Sys_Printf("GL_FRONT_FACE: %s\n", DecodeGLEnum(glint));
|
|
glGetIntegerv(GL_SCISSOR_TEST, &glint);
|
|
Sys_Printf("GL_SCISSOR_TEST: %i\n", glint);
|
|
glGetIntegerv(GL_STENCIL_TEST, &glint);
|
|
Sys_Printf("GL_STENCIL_TEST: %i\n", glint);
|
|
glGetIntegerv(GL_COLOR_WRITEMASK, glint4);
|
|
Sys_Printf("GL_COLOR_WRITEMASK: %i %i %i %i\n", glint4[0], glint4[1], glint4[2], glint4[3]);
|
|
|
|
GL_SelectTexture(0);
|
|
glGetIntegerv(GL_TEXTURE_2D, &glint);
|
|
Sys_Printf("GL_TEXTURE_2D: %i\n", glint);
|
|
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint);
|
|
Sys_Printf("GL_TEXTURE_ENV_MODE: %s\n", DecodeGLEnum(glint));
|
|
GL_SelectTexture(1);
|
|
glGetIntegerv(GL_TEXTURE_2D, &glint);
|
|
Sys_Printf("GL_TEXTURE_2D: %i\n", glint);
|
|
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint);
|
|
Sys_Printf("GL_TEXTURE_ENV_MODE: %s\n", DecodeGLEnum(glint));
|
|
GL_SelectTexture(2);
|
|
glGetIntegerv(GL_TEXTURE_2D, &glint);
|
|
Sys_Printf("GL_TEXTURE_2D: %i\n", glint);
|
|
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint);
|
|
Sys_Printf("GL_TEXTURE_ENV_MODE: %s\n", DecodeGLEnum(glint));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo)
|
|
{
|
|
char dumpPath[1024];
|
|
HANDLE hProc = GetCurrentProcess();
|
|
DWORD procid = GetCurrentProcessId();
|
|
HANDLE dumpfile;
|
|
HMODULE hDbgHelp;
|
|
MINIDUMPWRITEDUMP fnMiniDumpWriteDump;
|
|
HMODULE hKernel;
|
|
BOOL (WINAPI *pIsDebuggerPresent)(void);
|
|
|
|
#ifdef PRINTGLARRAYS
|
|
if (qrenderer == QR_OPENGL)
|
|
DumpGLState();
|
|
#endif
|
|
|
|
hKernel = LoadLibrary ("kernel32");
|
|
pIsDebuggerPresent = (void*)GetProcAddress(hKernel, "IsDebuggerPresent");
|
|
|
|
#ifdef GLQUAKE
|
|
GLVID_Crashed();
|
|
#endif
|
|
|
|
if (pIsDebuggerPresent ())
|
|
{
|
|
/*if we have a current window, minimize it to bring us out of fullscreen*/
|
|
extern qboolean vid_initializing;
|
|
qboolean oldval = vid_initializing;
|
|
vid_initializing = true;
|
|
// ShowWindow(mainwindow, SW_MINIMIZE);
|
|
vid_initializing = oldval;
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
/*if we have a current window, kill it, so it can't steal input of handle window messages or anything risky like that*/
|
|
DestroyWindow(mainwindow);
|
|
|
|
hDbgHelp = LoadLibrary ("DBGHELP");
|
|
if (hDbgHelp)
|
|
fnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress (hDbgHelp, "MiniDumpWriteDump");
|
|
else
|
|
fnMiniDumpWriteDump = NULL;
|
|
|
|
if (fnMiniDumpWriteDump)
|
|
{
|
|
if (MessageBox(NULL, "KABOOM! We crashed!\nBlame the monkey in the corner.\nI hope you saved your work.\nWould you like to take a dump now?", DISTRIBUTION " Sucks", MB_ICONSTOP|MB_YESNO) != IDYES)
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
|
|
/*take a dump*/
|
|
GetTempPath (sizeof(dumpPath)-16, dumpPath);
|
|
Q_strncatz(dumpPath, DISTRIBUTION"CrashDump.dmp", sizeof(dumpPath));
|
|
dumpfile = CreateFile (dumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (dumpfile)
|
|
{
|
|
MINIDUMP_EXCEPTION_INFORMATION crashinfo;
|
|
crashinfo.ClientPointers = TRUE;
|
|
crashinfo.ExceptionPointers = exceptionInfo;
|
|
crashinfo.ThreadId = GetCurrentThreadId ();
|
|
if (fnMiniDumpWriteDump(hProc, procid, dumpfile, MiniDumpWithIndirectlyReferencedMemory|MiniDumpWithDataSegs, &crashinfo, NULL, NULL))
|
|
{
|
|
CloseHandle(dumpfile);
|
|
MessageBox(NULL, va("You can find the crashdump at\n%s\nPlease send this file to someone.\n\nWarning: sensitive information (like your current user name) might be present in the dump.\nYou will probably want to compress it.", dumpPath), DISTRIBUTION " Sucks", 0);
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
MessageBox(NULL, "Kaboom! Sorry. No MiniDumpWriteDump function.", DISTRIBUTION " Sucks", 0);
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
#endif
|
|
|
|
int *debug;
|
|
|
|
|
|
#ifndef SERVERONLY
|
|
|
|
#if (_WIN32_WINNT < 0x0400)
|
|
#define LLKHF_ALTDOWN 0x00000020
|
|
#define LLKHF_UP 0x00000080
|
|
#define WH_KEYBOARD_LL 13
|
|
typedef struct {
|
|
DWORD vkCode;
|
|
DWORD scanCode;
|
|
DWORD flags;
|
|
DWORD time;
|
|
DWORD dwExtraInfo;
|
|
} KBDLLHOOKSTRUCT;
|
|
#elif defined(MINGW)
|
|
#ifndef LLKHF_UP
|
|
#define LLKHF_UP 0x00000080
|
|
#endif
|
|
#endif
|
|
|
|
HHOOK llkeyboardhook;
|
|
|
|
cvar_t sys_disableWinKeys = SCVAR("sys_disableWinKeys", "0");
|
|
cvar_t sys_disableTaskSwitch = SCVARF("sys_disableTaskSwitch", "0", CVAR_NOTFROMSERVER); // please don't encourage people to use this...
|
|
|
|
LRESULT CALLBACK LowLevelKeyboardProc (INT nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
KBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT *) lParam;
|
|
if (ActiveApp)
|
|
switch (nCode)
|
|
{
|
|
case HC_ACTION:
|
|
{
|
|
//Trap the Left Windowskey
|
|
if (pkbhs->vkCode == VK_LWIN)
|
|
{
|
|
Key_Event (0, K_LWIN, 0, !(pkbhs->flags & LLKHF_UP));
|
|
return 1;
|
|
}
|
|
//Trap the Right Windowskey
|
|
if (pkbhs->vkCode == VK_RWIN)
|
|
{
|
|
Key_Event (0, K_RWIN, 0, !(pkbhs->flags & LLKHF_UP));
|
|
return 1;
|
|
}
|
|
//Trap the Application Key (what a pointless key)
|
|
if (pkbhs->vkCode == VK_APPS)
|
|
{
|
|
Key_Event (0, K_APP, 0, !(pkbhs->flags & LLKHF_UP));
|
|
return 1;
|
|
}
|
|
|
|
// Disable CTRL+ESC
|
|
//this works, but we've got to give some way to tab out...
|
|
if (sys_disableTaskSwitch.ival)
|
|
{
|
|
if (pkbhs->vkCode == VK_ESCAPE && GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1))
|
|
return 1;
|
|
// Disable ATL+TAB
|
|
if (pkbhs->vkCode == VK_TAB && pkbhs->flags & LLKHF_ALTDOWN)
|
|
return 1;
|
|
// Disable ALT+ESC
|
|
if (pkbhs->vkCode == VK_ESCAPE && pkbhs->flags & LLKHF_ALTDOWN)
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return CallNextHookEx (llkeyboardhook, nCode, wParam, lParam);
|
|
}
|
|
|
|
void SetHookState(qboolean state)
|
|
{
|
|
if (!state == !llkeyboardhook) //not so types are comparable
|
|
return;
|
|
|
|
if (llkeyboardhook)
|
|
{
|
|
UnhookWindowsHookEx(llkeyboardhook);
|
|
llkeyboardhook = NULL;
|
|
}
|
|
if (state)
|
|
llkeyboardhook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
FILE IO
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
void Sys_mkdir (char *path)
|
|
{
|
|
_mkdir (path);
|
|
}
|
|
|
|
qboolean Sys_remove (char *path)
|
|
{
|
|
if (remove (path) != 0)
|
|
{
|
|
int e = errno;
|
|
if (e == ENOENT)
|
|
return true; //return success if it doesn't already exist.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean Sys_Rename (char *oldfname, char *newfname)
|
|
{
|
|
return !rename(oldfname, newfname);
|
|
}
|
|
|
|
static int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (*func)(const char *fname, int fsize, void *parm, void *spath), void *parm, void *spath)
|
|
{
|
|
HANDLE r;
|
|
WIN32_FIND_DATA fd;
|
|
int nest = neststart; //neststart refers to just after a /
|
|
qboolean go;
|
|
qboolean wild = false;
|
|
|
|
while(match[nest] && match[nest] != '/')
|
|
{
|
|
if (match[nest] == '?' || match[nest] == '*')
|
|
wild = true;
|
|
nest++;
|
|
}
|
|
if (match[nest] == '/')
|
|
{
|
|
char submatch[MAX_OSPATH];
|
|
char tmproot[MAX_OSPATH];
|
|
char file[MAX_OSPATH];
|
|
|
|
if (!wild)
|
|
return Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath);
|
|
|
|
if (nest-neststart+1> MAX_OSPATH)
|
|
return 1;
|
|
memcpy(submatch, match+neststart, nest - neststart);
|
|
submatch[nest - neststart] = 0;
|
|
nest++;
|
|
|
|
if (neststart+4 > MAX_OSPATH)
|
|
return 1;
|
|
memcpy(tmproot, match, neststart);
|
|
strcpy(tmproot+neststart, "*.*");
|
|
|
|
r = FindFirstFile(tmproot, &fd);
|
|
strcpy(tmproot+neststart, "");
|
|
if (r==(HANDLE)-1)
|
|
return 1;
|
|
go = true;
|
|
do
|
|
{
|
|
if (*fd.cFileName == '.'); //don't ever find files with a name starting with '.'
|
|
else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory
|
|
{
|
|
if (wildcmp(submatch, fd.cFileName))
|
|
{
|
|
int newnest;
|
|
if (strlen(tmproot) + strlen(fd.cFileName) + strlen(match+nest) + 2 < MAX_OSPATH)
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s/", tmproot, fd.cFileName);
|
|
newnest = strlen(file);
|
|
strcpy(file+newnest, match+nest);
|
|
go = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath);
|
|
}
|
|
}
|
|
}
|
|
} while(FindNextFile(r, &fd) && go);
|
|
FindClose(r);
|
|
}
|
|
else
|
|
{
|
|
const char *submatch = match + neststart;
|
|
char tmproot[MAX_OSPATH];
|
|
char file[MAX_OSPATH];
|
|
|
|
if (neststart+4 > MAX_OSPATH)
|
|
return 1;
|
|
memcpy(tmproot, match, neststart);
|
|
strcpy(tmproot+neststart, "*.*");
|
|
|
|
r = FindFirstFile(tmproot, &fd);
|
|
strcpy(tmproot+neststart, "");
|
|
if (r==(HANDLE)-1)
|
|
return 1;
|
|
go = true;
|
|
do
|
|
{
|
|
if (*fd.cFileName == '.'); //don't ever find files with a name starting with '.'
|
|
else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory
|
|
{
|
|
if (wildcmp(submatch, fd.cFileName))
|
|
{
|
|
if (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 2 < MAX_OSPATH)
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s/", tmproot+matchstart, fd.cFileName);
|
|
go = func(file, fd.nFileSizeLow, parm, spath);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wildcmp(submatch, fd.cFileName))
|
|
{
|
|
if (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 1 < MAX_OSPATH)
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s", tmproot+matchstart, fd.cFileName);
|
|
go = func(file, fd.nFileSizeLow, parm, spath);
|
|
}
|
|
}
|
|
}
|
|
} while(FindNextFile(r, &fd) && go);
|
|
FindClose(r);
|
|
}
|
|
|
|
return go;
|
|
}
|
|
int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *fname, int fsize, void *parm, void *spath), void *parm, void *spath)
|
|
{
|
|
char fullmatch[MAX_OSPATH];
|
|
int start;
|
|
if (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH)
|
|
return 1;
|
|
|
|
strcpy(fullmatch, gpath);
|
|
start = strlen(fullmatch);
|
|
if (start && fullmatch[start-1] != '/')
|
|
fullmatch[start++] = '/';
|
|
fullmatch[start] = 0;
|
|
strcat(fullmatch, match);
|
|
return Sys_EnumerateFiles2(fullmatch, start, start, func, parm, spath);
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
SYSTEM IO
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
Sys_MakeCodeWriteable
|
|
================
|
|
*/
|
|
void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
|
|
{
|
|
DWORD flOldProtect;
|
|
|
|
//@@@ copy on write or just read-write?
|
|
if (!VirtualProtect((LPVOID)startaddr, length, PAGE_EXECUTE_READWRITE, &flOldProtect))
|
|
{
|
|
char str[1024];
|
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
str,
|
|
sizeof(str),
|
|
NULL);
|
|
Sys_Error("Protection change failed!\nError %d: %s\n", (int)GetLastError(), str);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Sys_Init
|
|
================
|
|
*/
|
|
void Sys_Init (void)
|
|
{
|
|
// LARGE_INTEGER PerformanceFreq;
|
|
// unsigned int lowpart, highpart;
|
|
OSVERSIONINFO vinfo;
|
|
|
|
#ifndef SERVERONLY
|
|
Cvar_Register(&sys_disableWinKeys, "System vars");
|
|
Cvar_Register(&sys_disableTaskSwitch, "System vars");
|
|
|
|
#ifndef CLIENTONLY
|
|
if (!isDedicated && !COM_CheckParm("-nomutex"))
|
|
#else
|
|
if (!COM_CheckParm("-nomutex")) //we need to create a mutex to allow gamespy to realise that we're running, but it might not be desired as it prevents other clients from running too.
|
|
#endif
|
|
{
|
|
// allocate a named semaphore on the client so the
|
|
// front end can tell if it is alive
|
|
|
|
// mutex will fail if semephore already exists
|
|
qwclsemaphore = CreateMutex(
|
|
NULL, // Security attributes
|
|
0, // owner
|
|
"qwcl"); // Semaphore name
|
|
// if (!qwclsemaphore)
|
|
// Sys_Error ("QWCL is already running on this system");
|
|
CloseHandle (qwclsemaphore);
|
|
|
|
qwclsemaphore = CreateSemaphore(
|
|
NULL, // Security attributes
|
|
0, // Initial count
|
|
1, // Maximum count
|
|
"qwcl"); // Semaphore name
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
if (!QueryPerformanceFrequency (&PerformanceFreq))
|
|
Sys_Error ("No hardware timer available");
|
|
|
|
// get 32 out of the 64 time bits such that we have around
|
|
// 1 microsecond resolution
|
|
lowpart = (unsigned int)PerformanceFreq.LowPart;
|
|
highpart = (unsigned int)PerformanceFreq.HighPart;
|
|
lowshift = 0;
|
|
|
|
while (highpart || (lowpart > 2000000.0))
|
|
{
|
|
lowshift++;
|
|
lowpart >>= 1;
|
|
lowpart |= (highpart & 1) << 31;
|
|
highpart >>= 1;
|
|
}
|
|
|
|
pfreq = 1.0 / (double)lowpart;
|
|
|
|
Sys_InitFloatTime ();
|
|
#endif
|
|
|
|
// make sure the timer is high precision, otherwise
|
|
// NT gets 18ms resolution
|
|
timeBeginPeriod( 1 );
|
|
|
|
vinfo.dwOSVersionInfoSize = sizeof(vinfo);
|
|
|
|
if (!GetVersionEx (&vinfo))
|
|
Sys_Error ("Couldn't get OS info");
|
|
|
|
if ((vinfo.dwMajorVersion < 4) ||
|
|
(vinfo.dwPlatformId == VER_PLATFORM_WIN32s))
|
|
{
|
|
Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0");
|
|
}
|
|
|
|
if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
|
WinNT = true;
|
|
else
|
|
WinNT = false;
|
|
}
|
|
|
|
|
|
void Sys_Shutdown(void)
|
|
{
|
|
if (host_parms.membase)
|
|
{
|
|
VirtualFree(host_parms.membase, 0, MEM_RELEASE);
|
|
host_parms.membase = 0;
|
|
}
|
|
|
|
if (tevent)
|
|
CloseHandle (tevent);
|
|
tevent = NULL;
|
|
|
|
if (qwclsemaphore)
|
|
CloseHandle (qwclsemaphore);
|
|
qwclsemaphore = NULL;
|
|
}
|
|
|
|
|
|
void VARGS Sys_Error (const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char text[1024];
|
|
//, text2[1024];
|
|
// DWORD dummy;
|
|
|
|
va_start (argptr, error);
|
|
vsnprintf (text, sizeof(text), error, argptr);
|
|
va_end (argptr);
|
|
|
|
#ifndef SERVERONLY
|
|
SetHookState(false);
|
|
Host_Shutdown ();
|
|
#else
|
|
SV_Shutdown();
|
|
#endif
|
|
|
|
MessageBox(NULL, text, "Error", 0);
|
|
|
|
#ifndef SERVERONLY
|
|
CloseHandle (qwclsemaphore);
|
|
SetHookState(false);
|
|
#endif
|
|
|
|
exit (1);
|
|
}
|
|
|
|
static wchar_t dequake(conchar_t chr)
|
|
{
|
|
chr &= CON_CHARMASK;
|
|
|
|
/*only this range are quake chars*/
|
|
if (chr >= 0xe000 && chr < 0xe100)
|
|
{
|
|
chr &= 0xff;
|
|
if (chr >= 146 && chr < 156)
|
|
chr = chr - 146 + '0';
|
|
if (chr >= 0x12 && chr <= 0x1b)
|
|
chr = chr - 0x12 + '0';
|
|
if (chr == 143)
|
|
chr = '.';
|
|
if (chr == 128 || chr == 129 || chr == 130 || chr == 157 || chr == 158 || chr == 159)
|
|
chr = '-';
|
|
if (chr >= 128)
|
|
chr -= 128;
|
|
if (chr == 16)
|
|
chr = '[';
|
|
if (chr == 17)
|
|
chr = ']';
|
|
if (chr == 0x1c)
|
|
chr = 249;
|
|
}
|
|
/*this range contains pictograms*/
|
|
if (chr >= 0xe100 && chr < 0xe200)
|
|
{
|
|
chr = '?';
|
|
}
|
|
return chr;
|
|
}
|
|
|
|
void VARGS Sys_Printf (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char text[1024];
|
|
DWORD dummy;
|
|
|
|
conchar_t msg[1024], *end, *in;
|
|
wchar_t wide[1024], *out;
|
|
int wlen;
|
|
|
|
if (!houtput && !debugout)
|
|
return;
|
|
|
|
va_start (argptr,fmt);
|
|
vsnprintf (text, sizeof(text), fmt, argptr);
|
|
va_end (argptr);
|
|
|
|
end = COM_ParseFunString(CON_WHITEMASK, text, msg, sizeof(msg), false);
|
|
out = wide;
|
|
in = msg;
|
|
wlen = 0;
|
|
for (in = msg; in < end; in++)
|
|
{
|
|
if (!(*in & CON_HIDDEN))
|
|
{
|
|
*out++ = dequake(*in & CON_CHARMASK);
|
|
wlen++;
|
|
}
|
|
}
|
|
*out = 0;
|
|
|
|
if (debugout)
|
|
OutputDebugStringW(wide);
|
|
if (houtput)
|
|
WriteConsoleW(houtput, wide, wlen, &dummy, NULL);
|
|
}
|
|
|
|
void Sys_Quit (void)
|
|
{
|
|
#ifndef SERVERONLY
|
|
SetHookState(false);
|
|
|
|
Host_Shutdown ();
|
|
|
|
SetHookState(false);
|
|
#else
|
|
SV_Shutdown();
|
|
#endif
|
|
|
|
TL_Shutdown();
|
|
|
|
#ifdef RESTARTTEST
|
|
longjmp(restart_jmpbuf, 1);
|
|
#endif
|
|
|
|
#ifdef USE_MSVCRT_DEBUG
|
|
if (_CrtDumpMemoryLeaks())
|
|
OutputDebugStringA("Leaks detected\n");
|
|
#endif
|
|
|
|
exit(1);
|
|
}
|
|
|
|
|
|
#if 1
|
|
/*
|
|
================
|
|
Sys_DoubleTime
|
|
================
|
|
*/
|
|
double Sys_DoubleTime (void)
|
|
{
|
|
static int first = 1;
|
|
static LARGE_INTEGER qpcfreq;
|
|
LARGE_INTEGER PerformanceCount;
|
|
static LONGLONG oldcall;
|
|
static LONGLONG firsttime;
|
|
LONGLONG diff;
|
|
|
|
QueryPerformanceCounter (&PerformanceCount);
|
|
if (first)
|
|
{
|
|
first = 0;
|
|
QueryPerformanceFrequency(&qpcfreq);
|
|
firsttime = PerformanceCount.QuadPart;
|
|
diff = 0;
|
|
}
|
|
else
|
|
diff = PerformanceCount.QuadPart - oldcall;
|
|
if (diff >= 0)
|
|
oldcall = PerformanceCount.QuadPart;
|
|
return (oldcall - firsttime) / (double)qpcfreq.QuadPart;
|
|
}
|
|
unsigned int Sys_Milliseconds (void)
|
|
{
|
|
return Sys_DoubleTime()*1000;
|
|
}
|
|
#else
|
|
unsigned int Sys_Milliseconds (void)
|
|
{
|
|
static DWORD starttime;
|
|
static qboolean first = true;
|
|
DWORD now;
|
|
// double t;
|
|
|
|
now = timeGetTime();
|
|
|
|
if (first) {
|
|
first = false;
|
|
starttime = now;
|
|
return 0.0;
|
|
}
|
|
/*
|
|
if (now < starttime) // wrapped?
|
|
{
|
|
double r;
|
|
r = (now) + (LONG_MAX - starttime);
|
|
starttime = now;
|
|
return r;
|
|
}
|
|
|
|
if (now - starttime == 0)
|
|
return 0.0;
|
|
*/
|
|
return (now - starttime);
|
|
}
|
|
|
|
double Sys_DoubleTime (void)
|
|
{
|
|
return Sys_Milliseconds()/1000.f;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
//clipboard
|
|
HANDLE clipboardhandle;
|
|
char *cliputf8;
|
|
char *Sys_GetClipboard(void)
|
|
{
|
|
char *clipText;
|
|
unsigned short *clipWText;
|
|
if (OpenClipboard(NULL))
|
|
{
|
|
extern cvar_t com_parseutf8;
|
|
//windows programs interpret CF_TEXT as ansi (aka: gibberish)
|
|
//so grab utf-16 text and convert it to utf-8 if our console parsing is set to accept that.
|
|
if (com_parseutf8.ival > 0)
|
|
{
|
|
clipboardhandle = GetClipboardData(CF_UNICODETEXT);
|
|
if (clipboardhandle)
|
|
{
|
|
clipWText = GlobalLock(clipboardhandle);
|
|
if (clipWText)
|
|
{
|
|
unsigned int l, c;
|
|
for (l = 0; clipWText[l]; l++)
|
|
;
|
|
l = l*4 + 1;
|
|
clipText = cliputf8 = malloc(l);
|
|
while(*clipWText)
|
|
{
|
|
c = utf8_encode(clipText, *clipWText++, l);
|
|
if (!c)
|
|
break;
|
|
l -= c;
|
|
clipText += c;
|
|
}
|
|
*clipText = 0;
|
|
return cliputf8;
|
|
}
|
|
|
|
//failed at the last hurdle
|
|
|
|
GlobalUnlock(clipboardhandle);
|
|
}
|
|
}
|
|
|
|
clipboardhandle = GetClipboardData(CF_TEXT);
|
|
if (clipboardhandle)
|
|
{
|
|
clipText = GlobalLock(clipboardhandle);
|
|
if (clipText)
|
|
return clipText;
|
|
|
|
//failed at the last hurdle
|
|
|
|
GlobalUnlock(clipboardhandle);
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
|
|
clipboardhandle = NULL;
|
|
|
|
return NULL;
|
|
}
|
|
void Sys_CloseClipboard(char *bf)
|
|
{
|
|
if (clipboardhandle)
|
|
{
|
|
free(cliputf8);
|
|
cliputf8 = NULL;
|
|
GlobalUnlock(clipboardhandle);
|
|
CloseClipboard();
|
|
clipboardhandle = NULL;
|
|
}
|
|
}
|
|
void Sys_SaveClipboard(char *text)
|
|
{
|
|
HANDLE glob;
|
|
char *temp;
|
|
unsigned short *tempw;
|
|
extern cvar_t com_parseutf8;
|
|
if (!OpenClipboard(NULL))
|
|
return;
|
|
EmptyClipboard();
|
|
|
|
glob = GlobalAlloc(GMEM_MOVEABLE, strlen(text) + 1);
|
|
if (glob == NULL)
|
|
{
|
|
CloseClipboard();
|
|
return;
|
|
}
|
|
|
|
if (com_parseutf8.ival > 0)
|
|
{
|
|
glob = GlobalAlloc(GMEM_MOVEABLE, (strlen(text) + 1)*2);
|
|
if (glob)
|
|
{
|
|
tempw = GlobalLock(glob);
|
|
if (tempw != NULL)
|
|
{
|
|
int error;
|
|
while(*text)
|
|
{
|
|
*tempw++ = utf8_decode(&error, text, &text);
|
|
}
|
|
*tempw = 0;
|
|
GlobalUnlock(glob);
|
|
SetClipboardData(CF_UNICODETEXT, glob);
|
|
}
|
|
else
|
|
GlobalFree(glob);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//yes, quake chars will get mangled horribly.
|
|
temp = GlobalLock(glob);
|
|
if (temp != NULL)
|
|
{
|
|
strcpy(temp, text);
|
|
GlobalUnlock(glob);
|
|
SetClipboardData(CF_TEXT, glob);
|
|
}
|
|
else
|
|
GlobalFree(glob);
|
|
}
|
|
|
|
CloseClipboard();
|
|
}
|
|
|
|
|
|
//end of clipboard
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////
|
|
//the system console stuff
|
|
|
|
char *Sys_ConsoleInput (void)
|
|
{
|
|
static char text[256];
|
|
static int len;
|
|
INPUT_RECORD recs[1024];
|
|
// int count;
|
|
int i;
|
|
int ch;
|
|
DWORD numevents, numread, dummy=0;
|
|
HANDLE th;
|
|
char *clipText, *textCopied;
|
|
|
|
if (!hinput)
|
|
return NULL;
|
|
|
|
for ( ;; )
|
|
{
|
|
if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
|
|
Sys_Error ("Error getting # of console events");
|
|
|
|
if (numevents <= 0)
|
|
break;
|
|
|
|
if (WinNT)
|
|
{
|
|
if (!ReadConsoleInputW(hinput, recs, 1, &numread))
|
|
Sys_Error ("Error reading console input");
|
|
}
|
|
else
|
|
{
|
|
if (!ReadConsoleInputA(hinput, recs, 1, &numread))
|
|
Sys_Error ("Error reading console input");
|
|
}
|
|
|
|
if (numread != 1)
|
|
Sys_Error ("Couldn't read console input");
|
|
|
|
if (recs[0].EventType == KEY_EVENT)
|
|
{
|
|
if (recs[0].Event.KeyEvent.bKeyDown)
|
|
{
|
|
ch = recs[0].Event.KeyEvent.uChar.UnicodeChar;
|
|
|
|
switch (ch)
|
|
{
|
|
case '\r':
|
|
WriteFile(houtput, "\r\n", 2, &dummy, NULL);
|
|
|
|
if (len)
|
|
{
|
|
text[len] = 0;
|
|
len = 0;
|
|
return text;
|
|
}
|
|
break;
|
|
|
|
case '\b':
|
|
if (len)
|
|
{
|
|
len--;
|
|
WriteFile(houtput, "\b \b", 3, &dummy, NULL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (((ch=='V' || ch=='v') && (recs[0].Event.KeyEvent.dwControlKeyState &
|
|
(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))) || ((recs[0].Event.KeyEvent.dwControlKeyState
|
|
& SHIFT_PRESSED) && (recs[0].Event.KeyEvent.wVirtualKeyCode
|
|
==VK_INSERT))) {
|
|
if (OpenClipboard(NULL)) {
|
|
th = GetClipboardData(CF_TEXT);
|
|
if (th) {
|
|
clipText = GlobalLock(th);
|
|
if (clipText) {
|
|
textCopied = BZ_Malloc(GlobalSize(th)+1);
|
|
strcpy(textCopied, clipText);
|
|
/* Substitutes a NULL for every token */strtok(textCopied, "\n\r\b");
|
|
i = strlen(textCopied);
|
|
if (i+len>=256)
|
|
i=256-len;
|
|
if (i>0) {
|
|
textCopied[i]=0;
|
|
text[len]=0;
|
|
strcat(text, textCopied);
|
|
len+=dummy;
|
|
WriteFile(houtput, textCopied, i, &dummy, NULL);
|
|
}
|
|
BZ_Free(textCopied);
|
|
}
|
|
GlobalUnlock(th);
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
} else if (ch >= ' ')
|
|
{
|
|
wchar_t wch = ch;
|
|
WriteConsoleW(houtput, &wch, 1, &dummy, NULL);
|
|
len += i;
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL WINAPI HandlerRoutine (DWORD dwCtrlType)
|
|
{
|
|
switch (dwCtrlType)
|
|
{
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
case CTRL_CLOSE_EVENT:
|
|
case CTRL_LOGOFF_EVENT:
|
|
case CTRL_SHUTDOWN_EVENT:
|
|
Cbuf_AddText ("quit\n", RESTRICT_LOCAL);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifndef CP_UTF8
|
|
#define CP_UTF8 65001
|
|
#endif
|
|
qboolean Sys_InitTerminal (void)
|
|
{
|
|
DWORD m;
|
|
if (!AllocConsole())
|
|
return false;
|
|
|
|
#ifndef SERVERONLY
|
|
if (qwclsemaphore)
|
|
{
|
|
CloseHandle(qwclsemaphore);
|
|
qwclsemaphore = NULL;
|
|
}
|
|
#endif
|
|
|
|
SetConsoleCtrlHandler (HandlerRoutine, TRUE);
|
|
SetConsoleCP(CP_UTF8);
|
|
SetConsoleOutputCP(CP_UTF8);
|
|
SetConsoleTitle (FULLENGINENAME " dedicated server");
|
|
hinput = GetStdHandle (STD_INPUT_HANDLE);
|
|
houtput = GetStdHandle (STD_OUTPUT_HANDLE);
|
|
|
|
GetConsoleMode(hinput, &m);
|
|
SetConsoleMode(hinput, m | 0x40 | 0x80);
|
|
|
|
return true;
|
|
}
|
|
void Sys_CloseTerminal (void)
|
|
{
|
|
FreeConsole();
|
|
|
|
hinput = NULL;
|
|
houtput = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
////////////////////////////
|
|
|
|
void Sys_SendKeyEvents (void)
|
|
{
|
|
MSG msg;
|
|
|
|
if (isPlugin)
|
|
{
|
|
DWORD avail;
|
|
static char text[256], *nl;
|
|
static int textpos = 0;
|
|
|
|
HANDLE input = GetStdHandle(STD_INPUT_HANDLE);
|
|
if (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL))
|
|
{
|
|
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
|
|
}
|
|
else if (avail)
|
|
{
|
|
if (avail > sizeof(text)-1-avail)
|
|
avail = sizeof(text)-1-avail;
|
|
if (ReadFile(input, text+textpos, avail, &avail, NULL))
|
|
{
|
|
textpos += avail;
|
|
while(1)
|
|
{
|
|
text[textpos] = 0;
|
|
nl = strchr(text, '\n');
|
|
if (nl)
|
|
{
|
|
*nl++ = 0;
|
|
if (!qrenderer && !strncmp(text, "vid_recenter ", 13))
|
|
{
|
|
Cmd_TokenizeString(text, false, false);
|
|
sys_parentleft = strtoul(Cmd_Argv(1), NULL, 0);
|
|
sys_parenttop = strtoul(Cmd_Argv(2), NULL, 0);
|
|
sys_parentwidth = strtoul(Cmd_Argv(3), NULL, 0);
|
|
sys_parentheight = strtoul(Cmd_Argv(4), NULL, 0);
|
|
sys_parentwindow = (HWND)strtoul(Cmd_Argv(5), NULL, 16);
|
|
}
|
|
Cmd_ExecuteString(text, RESTRICT_LOCAL);
|
|
memmove(text, nl, textpos - (nl - text));
|
|
textpos -= (nl - text);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
else if (isDedicated)
|
|
{
|
|
#ifndef CLIENTONLY
|
|
SV_GetConsoleCommands ();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (gammapending)
|
|
{
|
|
gammapending -= host_frametime;
|
|
if (gammapending < host_frametime)
|
|
{
|
|
gammapending = 0;
|
|
Cvar_ForceCallback(&v_gamma);
|
|
}
|
|
}
|
|
|
|
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
// we always update if there are any event, even if we're paused
|
|
//if (!GetMessage (&msg, NULL, 0, 0))
|
|
// break;
|
|
// Sys_Quit ();
|
|
// if (TranslateMessage (&msg))
|
|
// continue;
|
|
DispatchMessage (&msg);
|
|
}
|
|
}
|
|
|
|
|
|
void Sys_ServerActivity(void)
|
|
{
|
|
#ifndef SERVERONLY
|
|
if (GetActiveWindow() != mainwindow)
|
|
FlashWindow(mainwindow, true);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
WINDOWS CRAP
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
WinMain
|
|
==================
|
|
*/
|
|
void SleepUntilInput (int time)
|
|
{
|
|
|
|
MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qboolean Sys_Startup_CheckMem(quakeparms_t *parms)
|
|
{
|
|
int t;
|
|
MEMORYSTATUS lpBuffer;
|
|
lpBuffer.dwLength = sizeof(MEMORYSTATUS);
|
|
GlobalMemoryStatus (&lpBuffer);
|
|
|
|
// take the greater of all the available memory or half the total memory,
|
|
// but at least 8 Mb and no more than 16 Mb, unless they explicitly
|
|
// request otherwise
|
|
parms->memsize = lpBuffer.dwAvailPhys;
|
|
|
|
if (parms->memsize < MINIMUM_WIN_MEMORY)
|
|
parms->memsize = MINIMUM_WIN_MEMORY;
|
|
|
|
if (parms->memsize < (lpBuffer.dwTotalPhys >> 1))
|
|
parms->memsize = lpBuffer.dwTotalPhys >> 1;
|
|
|
|
if (parms->memsize > MAXIMUM_WIN_MEMORY)
|
|
parms->memsize = MAXIMUM_WIN_MEMORY;
|
|
|
|
if (COM_CheckParm ("-heapsize"))
|
|
{
|
|
t = COM_CheckParm("-heapsize") + 1;
|
|
|
|
if (t < com_argc)
|
|
parms->memsize = Q_atoi (com_argv[t]) * 1024;
|
|
}
|
|
else if (COM_CheckParm ("-mem"))
|
|
{
|
|
t = COM_CheckParm("-mem") + 1;
|
|
|
|
if (t < com_argc)
|
|
parms->memsize = Q_atoi (com_argv[t]) * 1024*1024;
|
|
}
|
|
|
|
parms->membase = VirtualAlloc (NULL, parms->memsize, MEM_RESERVE, PAGE_NOACCESS);
|
|
// parms->membase = malloc (parms.memsize);
|
|
|
|
if (!parms->membase)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
WinMain
|
|
==================
|
|
*/
|
|
HINSTANCE global_hInstance;
|
|
int global_nCmdShow;
|
|
char *argv[MAX_NUM_ARGVS];
|
|
static char exename[256];
|
|
HWND hwnd_dialog;
|
|
|
|
|
|
|
|
#define COBJMACROS
|
|
|
|
#ifndef MINGW
|
|
#if _MSC_VER > 1200
|
|
#include <shobjidl.h>
|
|
#endif
|
|
#endif
|
|
#if _MSC_VER > 1200
|
|
#include <shlguid.h>
|
|
#endif
|
|
#include <shlobj.h>
|
|
//#include <Propsys.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#include "ntverp.h"
|
|
#endif
|
|
|
|
#if defined(__MINGW64_VERSION_MAJOR) && (__MINGW64_VERSION_MAJOR >= 3)
|
|
#define SHARD_APPIDINFOLINK SHARD_APPIDINFOLINK
|
|
#endif
|
|
|
|
#ifndef SHARD_APPIDINFOLINK
|
|
|
|
// SDK version 7600 = v7.0a & v7.1
|
|
#if !defined(VER_PRODUCTBUILD) || VER_PRODUCTBUILD < 7600
|
|
typedef struct SHARDAPPIDINFOLINK {
|
|
IShellLinkW *psl;
|
|
PCWSTR pszAppID;
|
|
} SHARDAPPIDINFOLINK;
|
|
#endif
|
|
|
|
#define SHARD_APPIDINFOLINK 0x00000007
|
|
|
|
#if !defined(VER_PRODUCTBUILD) || VER_PRODUCTBUILD < 7600
|
|
typedef struct {
|
|
GUID fmtid;
|
|
DWORD pid;
|
|
} PROPERTYKEY;
|
|
#endif
|
|
typedef struct IPropertyStore IPropertyStore;
|
|
;
|
|
#ifndef MINGW
|
|
#if !defined(VER_PRODUCTBUILD) || VER_PRODUCTBUILD < 7600
|
|
typedef struct IPropertyStore
|
|
{
|
|
CONST_VTBL struct
|
|
{
|
|
/*IUnknown*/
|
|
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
|
IPropertyStore * This,
|
|
REFIID riid,
|
|
void **ppvObject);
|
|
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
|
IPropertyStore * This);
|
|
ULONG ( STDMETHODCALLTYPE *Release )(
|
|
IPropertyStore * This);
|
|
|
|
/*property store stuff*/
|
|
HRESULT ( STDMETHODCALLTYPE *GetCount)(
|
|
IPropertyStore * This,
|
|
ULONG *count);
|
|
|
|
HRESULT ( STDMETHODCALLTYPE *GetAt)(
|
|
IPropertyStore * This,
|
|
DWORD prop,
|
|
PROPERTYKEY * key);
|
|
|
|
HRESULT ( STDMETHODCALLTYPE *GetValue)(
|
|
IPropertyStore * This,
|
|
PROPERTYKEY * key,
|
|
PROPVARIANT * val);
|
|
|
|
HRESULT ( STDMETHODCALLTYPE *SetValue)(
|
|
IPropertyStore * This,
|
|
PROPERTYKEY * key,
|
|
PROPVARIANT * val);
|
|
|
|
HRESULT ( STDMETHODCALLTYPE *Commit)(
|
|
IPropertyStore * This);
|
|
} *lpVtbl;
|
|
} IPropertyStore;
|
|
|
|
#endif
|
|
#endif
|
|
static const IID qIID_IPropertyStore = {0x886d8eeb, 0x8cf2, 0x4446, {0x8d, 0x02, 0xcd, 0xba, 0x1d, 0xbd, 0xcf, 0x99}};
|
|
|
|
#ifndef MINGW
|
|
#if !defined(VER_PRODUCTBUILD) || VER_PRODUCTBUILD < 7600
|
|
#define IObjectArray IUnknown
|
|
#endif
|
|
#endif
|
|
static const IID qIID_IObjectArray = {0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}};
|
|
|
|
#ifndef MINGW
|
|
#if !defined(VER_PRODUCTBUILD) || VER_PRODUCTBUILD < 7600
|
|
typedef struct IObjectCollection
|
|
{
|
|
struct IObjectCollectionVtbl
|
|
{
|
|
HRESULT ( __stdcall *QueryInterface )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppvObject);
|
|
|
|
ULONG ( __stdcall *AddRef )(
|
|
/* [in] IShellLink*/ void *This);
|
|
|
|
ULONG ( __stdcall *Release )(
|
|
/* [in] IShellLink*/ void *This);
|
|
|
|
HRESULT ( __stdcall *GetCount )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [out] */ UINT *pcObjects);
|
|
|
|
HRESULT ( __stdcall *GetAt )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ UINT uiIndex,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [iid_is][out] */ void **ppv);
|
|
|
|
HRESULT ( __stdcall *AddObject )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ void *punk);
|
|
|
|
HRESULT ( __stdcall *AddFromArray )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ IObjectArray *poaSource);
|
|
|
|
HRESULT ( __stdcall *RemoveObjectAt )(
|
|
/* [in] IShellLink*/ void *This,
|
|
/* [in] */ UINT uiIndex);
|
|
|
|
HRESULT ( __stdcall *Clear )(
|
|
/* [in] IShellLink*/ void *This);
|
|
} *lpVtbl;
|
|
} IObjectCollection;
|
|
#endif
|
|
#endif
|
|
static const IID qIID_IObjectCollection = {0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}};
|
|
static const CLSID qCLSID_EnumerableObjectCollection = {0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}};
|
|
|
|
|
|
#ifndef MINGW
|
|
#if !defined(VER_PRODUCTBUILD) || VER_PRODUCTBUILD < 7600
|
|
typedef struct ICustomDestinationList
|
|
{
|
|
struct ICustomDestinationListVtbl
|
|
{
|
|
HRESULT ( __stdcall *QueryInterface ) (
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppvObject);
|
|
|
|
ULONG ( __stdcall *AddRef )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
ULONG ( __stdcall *Release )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
HRESULT ( __stdcall *SetAppID )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [string][in] */ LPCWSTR pszAppID);
|
|
|
|
HRESULT ( __stdcall *BeginList )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [out] */ UINT *pcMinSlots,
|
|
/* [in] */ const GUID * const riid,
|
|
/* [out] */ void **ppv);
|
|
|
|
HRESULT ( __stdcall *AppendCategory )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [string][in] */ LPCWSTR pszCategory,
|
|
/* [in] IObjectArray*/ void *poa);
|
|
|
|
HRESULT ( __stdcall *AppendKnownCategory )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] KNOWNDESTCATEGORY*/ int category);
|
|
|
|
HRESULT ( __stdcall *AddUserTasks )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] IObjectArray*/ void *poa);
|
|
|
|
HRESULT ( __stdcall *CommitList )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
HRESULT ( __stdcall *GetRemovedDestinations )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [in] */ const IID * const riid,
|
|
/* [out] */ void **ppv);
|
|
|
|
HRESULT ( __stdcall *DeleteList )(
|
|
/* [in] ICustomDestinationList*/ void *This,
|
|
/* [string][unique][in] */ LPCWSTR pszAppID);
|
|
|
|
HRESULT ( __stdcall *AbortList )(
|
|
/* [in] ICustomDestinationList*/ void *This);
|
|
|
|
} *lpVtbl;
|
|
} ICustomDestinationList;
|
|
#endif
|
|
#endif
|
|
static const IID qIID_ICustomDestinationList = {0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}};
|
|
static const CLSID qCLSID_DestinationList = {0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
#if _MSC_VER > 1200
|
|
#define WIN7_APPNAME L"FTEQuake"
|
|
|
|
static IShellLinkW *CreateShellLink(char *command, char *target, char *title, char *desc)
|
|
{
|
|
HRESULT hr;
|
|
IShellLinkW *link;
|
|
IPropertyStore *prop_store;
|
|
|
|
WCHAR buf[1024];
|
|
char tmp[1024], *s;
|
|
|
|
// Get a pointer to the IShellLink interface.
|
|
hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, &link);
|
|
if (FAILED(hr))
|
|
return NULL;
|
|
|
|
GetModuleFileNameW(NULL, buf, sizeof(buf)/sizeof(wchar_t)-1);
|
|
IShellLinkW_SetIconLocation(link, buf, 0); /*grab the first icon from our exe*/
|
|
IShellLinkW_SetPath(link, buf); /*program to run*/
|
|
|
|
Q_strncpyz(tmp, com_quakedir, sizeof(tmp));
|
|
/*normalize the gamedir, so we don't end up with the same thing multiple times*/
|
|
for(s = tmp; *s; s++)
|
|
{
|
|
if (*s == '\\')
|
|
*s = '/';
|
|
else
|
|
*s = tolower(*s);
|
|
}
|
|
swprintf(buf, sizeof(buf), L"%S \"%S\" -basedir \"%S\"", command, target, tmp);
|
|
IShellLinkW_SetArguments(link, buf); /*args*/
|
|
swprintf(buf, sizeof(buf), L"%S", desc);
|
|
IShellLinkW_SetDescription(link, buf); /*tooltip*/
|
|
|
|
|
|
hr = IShellLinkW_QueryInterface(link, &qIID_IPropertyStore, &prop_store);
|
|
|
|
#ifndef MINGW
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
PROPVARIANT pv;
|
|
PROPERTYKEY PKEY_Title;
|
|
pv.vt=VT_LPSTR;
|
|
pv.pszVal=title; /*item text*/
|
|
CLSIDFromString(L"{F29F85E0-4FF9-1068-AB91-08002B27B3D9}", &(PKEY_Title.fmtid));
|
|
PKEY_Title.pid=2;
|
|
hr = prop_store->lpVtbl->SetValue(prop_store, &PKEY_Title, &pv);
|
|
hr = prop_store->lpVtbl->Commit(prop_store);
|
|
prop_store->lpVtbl->Release(prop_store);
|
|
}
|
|
#endif
|
|
|
|
return link;
|
|
}
|
|
|
|
void Sys_RecentServer(char *command, char *target, char *title, char *desc)
|
|
{
|
|
SHARDAPPIDINFOLINK appinfo;
|
|
IShellLinkW *link;
|
|
|
|
link = CreateShellLink(command, target, title, desc);
|
|
if (!link)
|
|
return;
|
|
|
|
appinfo.pszAppID=WIN7_APPNAME;
|
|
appinfo.psl=link;
|
|
SHAddToRecentDocs(SHARD_APPIDINFOLINK, &appinfo);
|
|
IShellLinkW_Release(link);
|
|
}
|
|
void Win7_Init(void)
|
|
{
|
|
HANDLE h;
|
|
HRESULT (WINAPI *pSetCurrentProcessExplicitAppUserModelID)(PCWSTR AppID);
|
|
|
|
|
|
h = LoadLibrary("shell32.dll");
|
|
if (h)
|
|
{
|
|
pSetCurrentProcessExplicitAppUserModelID = (void*)GetProcAddress(h, "SetCurrentProcessExplicitAppUserModelID");
|
|
if (pSetCurrentProcessExplicitAppUserModelID)
|
|
pSetCurrentProcessExplicitAppUserModelID(WIN7_APPNAME);
|
|
}
|
|
}
|
|
|
|
void Win7_TaskListInit(void)
|
|
{
|
|
ICustomDestinationList *cdl;
|
|
IObjectCollection *col;
|
|
IObjectArray *arr;
|
|
IShellLinkW *link;
|
|
CoInitialize(NULL);
|
|
if (SUCCEEDED(CoCreateInstance(&qCLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, &qIID_ICustomDestinationList, &cdl)))
|
|
{
|
|
UINT minslots;
|
|
IUnknown *removed;
|
|
cdl->lpVtbl->BeginList(cdl, &minslots, &qIID_IObjectArray, &removed);
|
|
|
|
if (SUCCEEDED(CoCreateInstance(&qCLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC_SERVER, &qIID_IObjectCollection, &col)))
|
|
{
|
|
|
|
switch(M_GameType())
|
|
{
|
|
case MGT_QUAKE1:
|
|
link = CreateShellLink("+menu_servers", "", "Server List", "Pick a multiplayer server to join");
|
|
if (link)
|
|
{
|
|
col->lpVtbl->AddObject(col, (IUnknown*)link);
|
|
link->lpVtbl->Release(link);
|
|
}
|
|
link = CreateShellLink("+map start", "", "Start New Game (Quake)", "Begin a new single-player game");
|
|
if (link)
|
|
{
|
|
col->lpVtbl->AddObject(col, (IUnknown*)link);
|
|
link->lpVtbl->Release(link);
|
|
}
|
|
break;
|
|
case MGT_QUAKE2:
|
|
link = CreateShellLink("+menu_servers", "", "Quake2 Server List", "Pick a multiplayer server to join");
|
|
if (link)
|
|
{
|
|
col->lpVtbl->AddObject(col, (IUnknown*)link);
|
|
link->lpVtbl->Release(link);
|
|
}
|
|
link = CreateShellLink("+map unit1", "", "Start New Game (Quake2)", "Begin a new game");
|
|
if (link)
|
|
{
|
|
col->lpVtbl->AddObject(col, (IUnknown*)link);
|
|
link->lpVtbl->Release(link);
|
|
}
|
|
break;
|
|
case MGT_HEXEN2:
|
|
link = CreateShellLink("+menu_servers", "", "Hexen2 Server List", "Pick a multiplayer server to join");
|
|
if (link)
|
|
{
|
|
col->lpVtbl->AddObject(col, (IUnknown*)link);
|
|
link->lpVtbl->Release(link);
|
|
}
|
|
link = CreateShellLink("+map demo1", "", "Start New Game (Hexen2)", "Begin a new game");
|
|
if (link)
|
|
{
|
|
col->lpVtbl->AddObject(col, (IUnknown*)link);
|
|
link->lpVtbl->Release(link);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(col->lpVtbl->QueryInterface(col, &qIID_IObjectArray, &arr)))
|
|
{
|
|
cdl->lpVtbl->AddUserTasks(cdl, arr);
|
|
arr->lpVtbl->Release(arr);
|
|
}
|
|
col->lpVtbl->Release(col);
|
|
}
|
|
cdl->lpVtbl->AppendKnownCategory(cdl, 1);
|
|
cdl->lpVtbl->CommitList(cdl);
|
|
cdl->lpVtbl->Release(cdl);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(SVNREVISION) && !defined(MINIMAL)
|
|
#define SVNREVISIONSTR STRINGIFY(SVNREVISION)
|
|
#if defined(OFFICIAL_RELEASE)
|
|
#define BUILDTYPE "rel"
|
|
#else
|
|
#define BUILDTYPE "test"
|
|
#define UPDATE_URL "http://triptohell.info/moodles/"
|
|
#define UPDATE_URL_VERSION UPDATE_URL "version.txt"
|
|
#ifdef _WIN64
|
|
#define UPDATE_URL_BUILD UPDATE_URL "win64/fte" EXETYPE "64.exe"
|
|
#else
|
|
#define UPDATE_URL_BUILD UPDATE_URL "win32/fte" EXETYPE ".exe"
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(SERVERONLY)
|
|
#define EXETYPE "qwsv" //not gonna happen, but whatever.
|
|
#elif defined(GLQUAKE) && defined(D3DQUAKE)
|
|
#define EXETYPE "qw"
|
|
#elif defined(GLQUAKE)
|
|
#ifdef MINIMAL
|
|
#define EXETYPE "minglqw"
|
|
#else
|
|
#define EXETYPE "glqw"
|
|
#endif
|
|
#elif defined(D3DQUAKE)
|
|
#define EXETYPE "d3dqw"
|
|
#elif defined(SWQUAKE)
|
|
#define EXETYPE "swqw"
|
|
#else
|
|
//erm...
|
|
#define EXETYPE "qw"
|
|
#endif
|
|
|
|
|
|
qboolean MyRegGetStringValue(HKEY base, char *keyname, char *valuename, void *data, int datalen)
|
|
{
|
|
qboolean result = false;
|
|
DWORD resultlen = datalen - 1;
|
|
HKEY subkey;
|
|
DWORD type = REG_NONE;
|
|
if (RegOpenKeyEx(base, keyname, 0, KEY_READ, &subkey) == ERROR_SUCCESS)
|
|
{
|
|
result = ERROR_SUCCESS == RegQueryValueEx(subkey, valuename, NULL, &type, data, &datalen);
|
|
RegCloseKey (subkey);
|
|
}
|
|
|
|
if (type == REG_SZ || type == REG_EXPAND_SZ)
|
|
((char*)data)[datalen] = 0;
|
|
else
|
|
((char*)data)[0] = 0;
|
|
return result;
|
|
}
|
|
|
|
#ifdef UPDATE_URL
|
|
|
|
void MyRegSetValue(HKEY base, char *keyname, char *valuename, int type, void *data, int datalen)
|
|
{
|
|
HKEY subkey;
|
|
if (RegCreateKeyEx(base, keyname, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &subkey, NULL) == ERROR_SUCCESS)
|
|
{
|
|
RegSetValueEx(subkey, valuename, 0, type, data, datalen);
|
|
RegCloseKey (subkey);
|
|
}
|
|
}
|
|
void MyRegDeleteKeyValue(HKEY base, char *keyname, char *valuename)
|
|
{
|
|
HKEY subkey;
|
|
if (RegOpenKeyEx(base, keyname, 0, KEY_WRITE, &subkey) == ERROR_SUCCESS)
|
|
{
|
|
RegDeleteValue(subkey, valuename);
|
|
RegCloseKey (subkey);
|
|
}
|
|
}
|
|
|
|
qboolean Update_GetHomeDirectory(char *homedir, int homedirsize)
|
|
{
|
|
HMODULE shfolder = LoadLibrary("shfolder.dll");
|
|
|
|
if (shfolder)
|
|
{
|
|
HRESULT (WINAPI *dSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);
|
|
dSHGetFolderPath = (void *)GetProcAddress(shfolder, "SHGetFolderPathA");
|
|
if (dSHGetFolderPath)
|
|
{
|
|
char folder[MAX_PATH];
|
|
// 0x5 == CSIDL_PERSONAL
|
|
if (dSHGetFolderPath(NULL, 0x5, NULL, 0, folder) == S_OK)
|
|
{
|
|
Q_snprintfz(homedir, homedirsize, "%s/My Games/%s/", folder, FULLENGINENAME);
|
|
return true;
|
|
}
|
|
}
|
|
// FreeLibrary(shfolder);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#include "fs.h"
|
|
void Update_Version_Updated(struct dl_download *dl)
|
|
{
|
|
//happens in a thread, avoid va
|
|
if (dl->file)
|
|
{
|
|
if (dl->status == DL_FINISHED)
|
|
{
|
|
char buf[8192];
|
|
unsigned int size = 0, chunk;
|
|
char pendingname[MAX_OSPATH];
|
|
vfsfile_t *pending;
|
|
Update_GetHomeDirectory(pendingname, sizeof(pendingname));
|
|
Q_strncatz(pendingname, DISTRIBUTION BUILDTYPE EXETYPE".tmp", sizeof(pendingname));
|
|
pending = VFSOS_Open(pendingname, "wb");
|
|
if (pending)
|
|
{
|
|
while(1)
|
|
{
|
|
chunk = VFS_READ(dl->file, buf, sizeof(buf));
|
|
if (!chunk)
|
|
break;
|
|
size += VFS_WRITE(pending, buf, chunk);
|
|
}
|
|
VFS_CLOSE(pending);
|
|
if (VFS_GETLEN(dl->file) == size)
|
|
{
|
|
MyRegSetValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" BUILDTYPE EXETYPE, REG_SZ, pendingname, strlen(pendingname)+1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void Update_Versioninfo_Available(struct dl_download *dl)
|
|
{
|
|
if (dl->file)
|
|
{
|
|
if (dl->status == DL_FINISHED)
|
|
{
|
|
char linebuf[1024];
|
|
while(VFS_GETS(dl->file, linebuf, sizeof(linebuf)))
|
|
{
|
|
if (!strnicmp(linebuf, "Revision: ", 10))
|
|
{
|
|
if (atoi(linebuf+10) > atoi(SVNREVISIONSTR))
|
|
{
|
|
struct dl_download *dl;
|
|
Con_Printf("Downloading update: revision %i\n", atoi(linebuf+10));
|
|
dl = HTTP_CL_Get(UPDATE_URL_BUILD, NULL, Update_Version_Updated);
|
|
dl->file = FS_OpenTemp();
|
|
#ifdef MULTITHREAD
|
|
DL_CreateThread(dl, NULL, NULL);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static qboolean doupdatecheck;
|
|
void Update_Check(void)
|
|
{
|
|
struct dl_download *dl;
|
|
if (doupdatecheck)
|
|
{
|
|
doupdatecheck = false;
|
|
dl = HTTP_CL_Get(UPDATE_URL_VERSION, NULL, Update_Versioninfo_Available);
|
|
dl->file = FS_OpenTemp();
|
|
#ifdef MULTITHREAD
|
|
DL_CreateThread(dl, NULL, NULL);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void Update_CreatePath (char *path)
|
|
{
|
|
char *ofs;
|
|
|
|
for (ofs = path+1 ; *ofs ; ofs++)
|
|
{
|
|
if (*ofs == '/')
|
|
{ // create the directory
|
|
*ofs = 0;
|
|
Sys_mkdir (path);
|
|
*ofs = '/';
|
|
}
|
|
}
|
|
}
|
|
|
|
qboolean Sys_CheckUpdated(void)
|
|
{
|
|
int ffe = COM_CheckParm("--fromfrontend");
|
|
PROCESS_INFORMATION childinfo;
|
|
STARTUPINFO startinfo = {sizeof(startinfo)};
|
|
|
|
if (!strcmp(SVNREVISIONSTR, "-"))
|
|
return false; //no revision info in this build, meaning its custom built and thus cannot check against the available updated versions.
|
|
else if (COM_CheckParm("-noupdate") || COM_CheckParm("--noupdate"))
|
|
return false;
|
|
else if (!COM_CheckParm("-autoupdate") && !COM_CheckParm("--autoupdate"))
|
|
return false;
|
|
else if (COM_CheckParm("-plugin"))
|
|
{
|
|
//download, but don't invoke. the caller is expected to start us up properly (once installed).
|
|
}
|
|
else if (!ffe)
|
|
{
|
|
//if we're not from the frontend, we should run the updated build instead
|
|
char frontendpath[MAX_OSPATH];
|
|
char pendingpath[MAX_OSPATH];
|
|
char updatedpath[MAX_OSPATH];
|
|
|
|
MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" BUILDTYPE EXETYPE, pendingpath, sizeof(pendingpath));
|
|
if (*pendingpath)
|
|
{
|
|
MyRegDeleteKeyValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, "pending" BUILDTYPE EXETYPE);
|
|
Update_GetHomeDirectory(updatedpath, sizeof(updatedpath));
|
|
Update_CreatePath(updatedpath);
|
|
Q_strncatz(updatedpath, "cur" BUILDTYPE EXETYPE".exe", sizeof(updatedpath));
|
|
DeleteFile(updatedpath);
|
|
if (MoveFile(pendingpath, updatedpath))
|
|
MyRegSetValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, BUILDTYPE EXETYPE, REG_SZ, updatedpath, strlen(updatedpath)+1);
|
|
}
|
|
|
|
MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\"FULLENGINENAME, BUILDTYPE EXETYPE, updatedpath, sizeof(updatedpath));
|
|
|
|
if (*updatedpath)
|
|
{
|
|
GetModuleFileName(NULL, frontendpath, sizeof(frontendpath)-1);
|
|
if (CreateProcess(updatedpath, va("\"%s\" %s --fromfrontend \"%s\" \"%s\" %s", frontendpath, COM_Parse(GetCommandLineA()), SVNREVISIONSTR, frontendpath), NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo))
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char frontendpath[MAX_OSPATH];
|
|
//com_argv[ffe+1] is frontend revision
|
|
//com_argv[ffe+2] is frontend location
|
|
if (atoi(com_argv[ffe+1]) > atoi(SVNREVISIONSTR))
|
|
{
|
|
//ping-pong it back, to make sure we're running the most recent version.
|
|
GetModuleFileName(NULL, frontendpath, sizeof(frontendpath)-1);
|
|
if (CreateProcess(com_argv[ffe+2], va("--fromfrontend \"%s\" \"%s\" %s", "", "", COM_Parse(GetCommandLineA())), NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo))
|
|
return true;
|
|
}
|
|
}
|
|
doupdatecheck = true;
|
|
return false;
|
|
}
|
|
#else
|
|
qboolean Sys_CheckUpdated(void)
|
|
{
|
|
return false;
|
|
}
|
|
void Update_Check(void)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
#ifdef _MSC_VER
|
|
#include <signal.h>
|
|
void VARGS Signal_Error_Handler(int i)
|
|
{
|
|
int *basepointer;
|
|
__asm {mov basepointer,ebp};
|
|
Sys_Error("Received signal, offset was 0x%8x", basepointer[73]);
|
|
}
|
|
#endif
|
|
*/
|
|
|
|
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
|
{
|
|
// MSG msg;
|
|
quakeparms_t parms;
|
|
double time, oldtime, newtime;
|
|
char cwd[1024], bindir[1024], *s;
|
|
const char *qtvfile = NULL;
|
|
int delay = 0;
|
|
|
|
/* previous instances do not exist in Win32 */
|
|
if (hPrevInstance)
|
|
return 0;
|
|
|
|
memset(&parms, 0, sizeof(parms));
|
|
|
|
#ifndef MINGW
|
|
#if _MSC_VER > 1200
|
|
Win7_Init();
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#if _M_IX86_FP >= 1
|
|
{
|
|
int idedx;
|
|
char cpuname[13];
|
|
/*I'm not going to check to make sure cpuid works.*/
|
|
__asm
|
|
{
|
|
xor eax, eax
|
|
cpuid
|
|
mov dword ptr [cpuname+0],ebx
|
|
mov dword ptr [cpuname+4],edx
|
|
mov dword ptr [cpuname+8],ecx
|
|
}
|
|
cpuname[12] = 0;
|
|
__asm
|
|
{
|
|
mov eax, 0x1
|
|
cpuid
|
|
mov idedx, edx
|
|
}
|
|
#if _M_IX86_FP >= 2
|
|
if (!(idedx&(1<<26)))
|
|
MessageBox(NULL, "This is an SSE2 optimised build, and your cpu doesn't seem to support it", DISTRIBUTION, 0);
|
|
else
|
|
#endif
|
|
if (!(idedx&(1<<25)))
|
|
MessageBox(NULL, "This is an SSE optimised build, and your cpu doesn't seem to support it", DISTRIBUTION, 0);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CATCHCRASH
|
|
__try
|
|
#endif
|
|
{
|
|
/*
|
|
#ifndef _DEBUG
|
|
#ifdef _MSC_VER
|
|
signal (SIGFPE, Signal_Error_Handler);
|
|
signal (SIGILL, Signal_Error_Handler);
|
|
signal (SIGSEGV, Signal_Error_Handler);
|
|
#endif
|
|
#endif
|
|
*/
|
|
global_hInstance = hInstance;
|
|
global_nCmdShow = nCmdShow;
|
|
|
|
parms.argc = 1;
|
|
argv[0] = exename;
|
|
|
|
while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS))
|
|
{
|
|
while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
|
|
lpCmdLine++;
|
|
|
|
if (*lpCmdLine)
|
|
{
|
|
if (*lpCmdLine == '\"')
|
|
{
|
|
lpCmdLine++;
|
|
|
|
argv[parms.argc] = lpCmdLine;
|
|
parms.argc++;
|
|
|
|
while (*lpCmdLine && *lpCmdLine != '\"')
|
|
lpCmdLine++;
|
|
}
|
|
else
|
|
{
|
|
argv[parms.argc] = lpCmdLine;
|
|
parms.argc++;
|
|
|
|
|
|
while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
|
|
lpCmdLine++;
|
|
}
|
|
|
|
if (*lpCmdLine)
|
|
{
|
|
*lpCmdLine = 0;
|
|
lpCmdLine++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef RESTARTTEST
|
|
setjmp (restart_jmpbuf);
|
|
#endif
|
|
|
|
GetModuleFileName(NULL, bindir, sizeof(bindir)-1);
|
|
s = COM_SkipPath(exename);
|
|
strcpy(exename, s);
|
|
*s = 0;
|
|
parms.argv = (const char **)argv;
|
|
|
|
COM_InitArgv (parms.argc, parms.argv);
|
|
|
|
if (Sys_CheckUpdated())
|
|
return true;
|
|
|
|
isPlugin = !!COM_CheckParm("-plugin");
|
|
if (isPlugin)
|
|
{
|
|
printf("status Starting up!\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
if (COM_CheckParm("--version") || COM_CheckParm("-v"))
|
|
{
|
|
printf("version: %s\n", version_string());
|
|
return true;
|
|
}
|
|
if (COM_CheckParm("-outputdebugstring"))
|
|
debugout = true;
|
|
|
|
if (!GetCurrentDirectory (sizeof(cwd), cwd))
|
|
Sys_Error ("Couldn't determine current directory");
|
|
if (parms.argc >= 2)
|
|
{
|
|
if (*parms.argv[1] != '-' && *parms.argv[1] != '+')
|
|
{
|
|
char *e;
|
|
|
|
qtvfile = parms.argv[1];
|
|
|
|
GetModuleFileName(NULL, cwd, sizeof(cwd)-1);
|
|
for (e = cwd+strlen(cwd)-1; e >= cwd; e--)
|
|
{
|
|
if (*e == '/' || *e == '\\')
|
|
{
|
|
*e = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
TL_InitLanguages();
|
|
//tprints are now allowed
|
|
|
|
parms.basedir = cwd;
|
|
parms.binarydir = bindir;
|
|
|
|
parms.argc = com_argc;
|
|
parms.argv = com_argv;
|
|
|
|
#if !defined(CLIENTONLY) && !defined(SERVERONLY)
|
|
if (COM_CheckParm ("-dedicated"))
|
|
isDedicated = true;
|
|
#endif
|
|
|
|
if (isDedicated)
|
|
{
|
|
#if !defined(CLIENTONLY)
|
|
if (!Sys_InitTerminal())
|
|
Sys_Error ("Couldn't allocate dedicated server console");
|
|
#endif
|
|
}
|
|
|
|
if (!Sys_Startup_CheckMem(&parms))
|
|
Sys_Error ("Not enough memory free; check disk space\n");
|
|
|
|
|
|
#ifndef CLIENTONLY
|
|
if (isDedicated) //compleate denial to switch to anything else - many of the client structures are not initialized.
|
|
{
|
|
int delay;
|
|
|
|
SV_Init (&parms);
|
|
|
|
delay = SV_Frame()*1000;
|
|
|
|
while (1)
|
|
{
|
|
if (!isDedicated)
|
|
Sys_Error("Dedicated was cleared");
|
|
NET_Sleep(delay, false);
|
|
delay = SV_Frame()*1000;
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!tevent)
|
|
Sys_Error ("Couldn't create event");
|
|
|
|
#ifdef SERVERONLY
|
|
Sys_Printf ("SV_Init\n");
|
|
SV_Init(&parms);
|
|
#else
|
|
Sys_Printf ("Host_Init\n");
|
|
Host_Init (&parms);
|
|
#endif
|
|
|
|
oldtime = Sys_DoubleTime ();
|
|
|
|
if (qtvfile)
|
|
{
|
|
char *ext = COM_FileExtension(qtvfile);
|
|
if (!strcmp(ext, "qwd") || !strcmp(ext, "dem") || !strcmp(ext, "mvd"))
|
|
Cbuf_AddText(va("playdemo \"#%s\"\n", qtvfile), RESTRICT_LOCAL);
|
|
else
|
|
Cbuf_AddText(va("qtvplay \"#%s\"\n", qtvfile), RESTRICT_LOCAL);
|
|
}
|
|
|
|
//client console should now be initialized.
|
|
|
|
#ifndef MINGW
|
|
#if _MSC_VER > 1200
|
|
Win7_TaskListInit();
|
|
#endif
|
|
#endif
|
|
|
|
if (isPlugin)
|
|
{
|
|
printf("status Running!\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
Update_Check();
|
|
|
|
/* main window message loop */
|
|
while (1)
|
|
{
|
|
if (isDedicated)
|
|
{
|
|
#ifndef CLIENTONLY
|
|
NET_Sleep(delay, false);
|
|
|
|
// find time passed since last cycle
|
|
newtime = Sys_DoubleTime ();
|
|
time = newtime - oldtime;
|
|
oldtime = newtime;
|
|
|
|
delay = 1000*SV_Frame ();
|
|
#else
|
|
Sys_Error("wut?");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifndef SERVERONLY
|
|
double sleeptime;
|
|
newtime = Sys_DoubleTime ();
|
|
time = newtime - oldtime;
|
|
sleeptime = Host_Frame (time);
|
|
oldtime = newtime;
|
|
|
|
SetHookState(sys_disableWinKeys.ival);
|
|
|
|
/*sleep if its not yet time for a frame*/
|
|
Sys_Sleep(sleeptime);
|
|
#else
|
|
Sys_Error("wut?");
|
|
#endif
|
|
}
|
|
|
|
#ifndef SERVERONLY
|
|
if (fs_switchgame != -1)
|
|
{
|
|
SetHookState(false);
|
|
|
|
Host_Shutdown ();
|
|
|
|
COM_InitArgv (parms.argc, parms.argv);
|
|
if (!Sys_Startup_CheckMem(&parms))
|
|
return 0;
|
|
Host_Init (&parms);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef CATCHCRASH
|
|
__except (CrashExceptionHandler(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/* return success of application */
|
|
return TRUE;
|
|
}
|
|
|
|
int __cdecl main(void)
|
|
{
|
|
char *cmdline;
|
|
FreeConsole();
|
|
cmdline = GetCommandLine();
|
|
while (*cmdline && *cmdline == ' ')
|
|
cmdline++;
|
|
if (*cmdline == '\"')
|
|
{
|
|
cmdline++;
|
|
while (*cmdline && *cmdline != '\"')
|
|
cmdline++;
|
|
if (*cmdline == '\"')
|
|
cmdline++;
|
|
}
|
|
else
|
|
{
|
|
while (*cmdline && *cmdline != ' ')
|
|
cmdline++;
|
|
}
|
|
return WinMain(GetModuleHandle(NULL), NULL, cmdline, SW_NORMAL);
|
|
}
|
|
|
|
qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)
|
|
{
|
|
HDC hdc;
|
|
int rate;
|
|
|
|
hdc = GetDC(NULL);
|
|
|
|
*width = GetDeviceCaps(hdc, HORZRES);
|
|
*height = GetDeviceCaps(hdc, VERTRES);
|
|
*bpp = GetDeviceCaps(hdc, BITSPIXEL);
|
|
rate = GetDeviceCaps(hdc, VREFRESH);
|
|
|
|
if (rate == 1)
|
|
rate = 0;
|
|
|
|
*refreshrate = rate;
|
|
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Sys_Sleep (double seconds)
|
|
{
|
|
Sleep(seconds * 1000);
|
|
}
|