244c08ad04
Also tweeked video code to remove 6 dead sw-only functions. git-svn-id: https://svn.code.sf.net/p/fteqw/code/branches/wip@3556 fc73d0e0-1445-4013-8a0c-d673dee63da5
1630 lines
34 KiB
C
1630 lines
34 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.
|
|
|
|
*/
|
|
#include "qwsvdef.h"
|
|
#include <sys/types.h>
|
|
#include <sys/timeb.h>
|
|
|
|
#ifdef SERVERONLY
|
|
|
|
#include <winsock.h>
|
|
#include <conio.h>
|
|
|
|
#ifdef MULTITHREAD
|
|
#include <process.h>
|
|
#endif
|
|
|
|
#ifndef MINIMAL
|
|
//#define USESERVICE
|
|
#endif
|
|
#define SERVICENAME DISTRIBUTION"SV"
|
|
|
|
|
|
static HANDLE hconsoleout;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#if _MSC_VER >= 1300
|
|
#define CATCHCRASH
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#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
|
|
);
|
|
|
|
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);
|
|
|
|
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*/
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
return NULL;
|
|
|
|
for (i = 0; funcs[i].name; i++)
|
|
{
|
|
*funcs[i].funcptr = GetProcAddress(lib, funcs[i].name);
|
|
if (!*funcs[i].funcptr)
|
|
break;
|
|
}
|
|
if (funcs[i].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 *(*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__
|
|
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
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
#include <shellapi.h>
|
|
|
|
#ifdef USESERVICE
|
|
qboolean asservice;
|
|
SERVICE_STATUS_HANDLE ServerServiceStatusHandle;
|
|
SERVICE_STATUS MyServiceStatus;
|
|
void CreateSampleService(qboolean create);
|
|
#endif
|
|
|
|
void PR_Deinit(void);
|
|
|
|
cvar_t sys_nostdout = {"sys_nostdout","0"};
|
|
cvar_t sys_maxtic = {"sys_maxtic", "100"};
|
|
cvar_t sys_colorconsole = {"sys_colorconsole", "1"};
|
|
|
|
HWND consolewindowhandle;
|
|
HWND hiddenwindowhandler;
|
|
|
|
int Sys_DebugLog(char *file, char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
static char data[1024];
|
|
int fd;
|
|
|
|
va_start(argptr, fmt);
|
|
vsnprintf(data, sizeof(data)-1, fmt, argptr);
|
|
va_end(argptr);
|
|
fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
|
|
if (fd)
|
|
{
|
|
write(fd, data, strlen(data));
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
return 1; // error
|
|
};
|
|
|
|
/*
|
|
================
|
|
Sys_FileTime
|
|
================
|
|
*/
|
|
int Sys_FileTime (char *path)
|
|
{
|
|
FILE *f;
|
|
|
|
f = fopen(path, "rb");
|
|
if (f)
|
|
{
|
|
fclose(f);
|
|
return 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_mkdir
|
|
================
|
|
*/
|
|
int _mkdir(const char *path);;
|
|
void Sys_mkdir (char *path)
|
|
{
|
|
_mkdir(path);
|
|
}
|
|
|
|
qboolean Sys_remove (char *path)
|
|
{
|
|
remove(path);
|
|
|
|
return true;
|
|
}
|
|
|
|
int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, int, void *), void *parm)
|
|
{
|
|
HANDLE r;
|
|
WIN32_FIND_DATA fd;
|
|
char apath[MAX_OSPATH];
|
|
char file[MAX_OSPATH];
|
|
char *s;
|
|
int go;
|
|
Q_strncpyz(apath, match, sizeof(apath));
|
|
// sprintf(apath, "%s%s", gpath, match);
|
|
for (s = apath+strlen(apath)-1; s>= apath; s--)
|
|
{
|
|
if (*s == '/')
|
|
break;
|
|
}
|
|
s++;
|
|
*s = '\0';
|
|
|
|
|
|
|
|
Q_snprintfz(file, sizeof(file), "%s/%s", gpath, match);
|
|
r = FindFirstFile(file, &fd);
|
|
if (r==(HANDLE)-1)
|
|
return 1;
|
|
go = true;
|
|
do
|
|
{
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory
|
|
{
|
|
if (*fd.cFileName != '.')
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s/", apath, fd.cFileName);
|
|
go = func(file, fd.nFileSizeLow, parm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s", apath, fd.cFileName);
|
|
go = func(file, fd.nFileSizeLow, parm);
|
|
}
|
|
}
|
|
while(FindNextFile(r, &fd) && go);
|
|
FindClose(r);
|
|
|
|
return go;
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_Error
|
|
================
|
|
*/
|
|
#include <process.h>
|
|
void Sys_Error (const char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char text[1024];
|
|
double end;
|
|
STARTUPINFO startupinfo;
|
|
PROCESS_INFORMATION processinfo;
|
|
|
|
va_start (argptr,error);
|
|
vsnprintf (text,sizeof(text)-1, error,argptr);
|
|
va_end (argptr);
|
|
|
|
|
|
// MessageBox(NULL, text, "Error", 0 /* MB_OK */ );
|
|
Sys_Printf ("ERROR: %s\n", text);
|
|
|
|
Con_Log(text);
|
|
|
|
NET_Shutdown(); //free sockets and stuff.
|
|
|
|
#ifdef USESERVICE
|
|
if (asservice)
|
|
Sys_Quit();
|
|
#endif
|
|
|
|
if (COM_CheckParm("-noreset"))
|
|
Sys_Quit();
|
|
|
|
Sys_Printf ("A new server will be started in 10 seconds unless you press a key\n");
|
|
|
|
|
|
//check for a key press, quitting if we get one in 10 secs
|
|
end = Sys_DoubleTime() + 10;
|
|
while(Sys_DoubleTime() < end)
|
|
{
|
|
Sleep(500); // don't burn up CPU with polling
|
|
if (_kbhit())
|
|
Sys_Quit();
|
|
}
|
|
|
|
Sys_Printf("\nLoading new instance of FTE...\n\n\n");
|
|
PR_Deinit(); //this takes a bit more mem
|
|
Rank_Flush();
|
|
#ifndef MINGW
|
|
fcloseall(); //make sure all files are written.
|
|
#endif
|
|
VirtualFree (host_parms.membase, 0, MEM_RELEASE);
|
|
// free(host_parms.membase); //get rid of the mem. We don't need it now.
|
|
// system("dqwsv.exe"); //spawn a new server to take over. This way, if debugging, then any key will quit, otherwise the server will just spawn a new one.
|
|
|
|
memset(&startupinfo, 0, sizeof(startupinfo));
|
|
memset(&processinfo, 0, sizeof(processinfo));
|
|
|
|
CreateProcess(NULL,
|
|
GetCommandLine(),
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&startupinfo,
|
|
&processinfo);
|
|
|
|
CloseHandle(processinfo.hProcess);
|
|
CloseHandle(processinfo.hThread);
|
|
|
|
Sys_Quit ();
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_Milliseconds
|
|
================
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_DoubleTime
|
|
================
|
|
*/
|
|
double Sys_DoubleTime (void)
|
|
{
|
|
double t;
|
|
struct _timeb tstruct;
|
|
static int starttime;
|
|
|
|
_ftime( &tstruct );
|
|
|
|
if (!starttime)
|
|
starttime = tstruct.time;
|
|
t = (tstruct.time-starttime) + tstruct.millitm*0.001;
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Sys_ConsoleInput
|
|
================
|
|
*/
|
|
|
|
char coninput_text[256];
|
|
int coninput_len;
|
|
char *Sys_ConsoleInput (void)
|
|
{
|
|
int c;
|
|
|
|
if (consolewindowhandle)
|
|
{
|
|
MSG msg;
|
|
while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
|
|
{
|
|
if (!GetMessage (&msg, NULL, 0, 0))
|
|
return NULL;
|
|
TranslateMessage (&msg);
|
|
DispatchMessage (&msg);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// read a line out
|
|
while (_kbhit())
|
|
{
|
|
c = _getch();
|
|
if (c == '\r')
|
|
{
|
|
coninput_text[coninput_len] = 0;
|
|
putch ('\n');
|
|
putch (']');
|
|
coninput_len = 0;
|
|
return coninput_text;
|
|
}
|
|
if (c == 8)
|
|
{
|
|
if (coninput_len)
|
|
{
|
|
putch (c);
|
|
putch (' ');
|
|
putch (c);
|
|
coninput_len--;
|
|
coninput_text[coninput_len] = 0;
|
|
}
|
|
continue;
|
|
}
|
|
if (c == '\t')
|
|
{
|
|
int i;
|
|
char *s = Cmd_CompleteCommand(coninput_text, true, true, 0);
|
|
if(s)
|
|
{
|
|
for (i = 0; i < coninput_len; i++)
|
|
putch('\b');
|
|
for (i = 0; i < coninput_len; i++)
|
|
putch(' ');
|
|
for (i = 0; i < coninput_len; i++)
|
|
putch('\b');
|
|
|
|
strcpy(coninput_text, s);
|
|
coninput_len = strlen(coninput_text);
|
|
printf("%s", coninput_text);
|
|
}
|
|
continue;
|
|
}
|
|
putch (c);
|
|
coninput_text[coninput_len] = c;
|
|
coninput_len++;
|
|
coninput_text[coninput_len] = 0;
|
|
if (coninput_len == sizeof(coninput_text))
|
|
coninput_len = 0;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ApplyColour(unsigned int chr)
|
|
{
|
|
static int oldchar = CON_WHITEMASK;
|
|
chr &= CON_FLAGSMASK;
|
|
|
|
if (oldchar == chr)
|
|
return;
|
|
oldchar = chr;
|
|
|
|
if (hconsoleout)
|
|
{
|
|
unsigned short val = 0;
|
|
|
|
// bits 28-31 of the console chars match up to the attributes for
|
|
// the CHAR_INFO struct exactly
|
|
if (chr & CON_NONCLEARBG)
|
|
val = ((chr & (CON_FGMASK|CON_BGMASK)) >> CON_FGSHIFT);
|
|
else
|
|
{
|
|
int fg = (chr & CON_FGMASK) >> CON_FGSHIFT;
|
|
|
|
switch (fg)
|
|
{
|
|
case COLOR_BLACK: // reverse ^0 like the Linux version
|
|
val = BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE;
|
|
break;
|
|
case COLOR_WHITE: // reset to defaults?
|
|
val = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; // use grey
|
|
break;
|
|
case COLOR_GREY:
|
|
val = FOREGROUND_INTENSITY; // color light grey as dark grey
|
|
break;
|
|
default:
|
|
val = fg; // send RGBI value as is
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((chr & CON_HALFALPHA) && (val & ~FOREGROUND_INTENSITY))
|
|
val &= ~FOREGROUND_INTENSITY; // strip intensity to fake alpha
|
|
|
|
SetConsoleTextAttribute(hconsoleout, val);
|
|
}
|
|
}
|
|
|
|
void Sys_PrintColouredChar(unsigned int chr)
|
|
{
|
|
ApplyColour(chr);
|
|
|
|
printf("%c", chr & CON_CHARMASK);
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_Printf
|
|
================
|
|
*/
|
|
#define MAXPRINTMSG 4096
|
|
void Sys_Printf (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
|
|
if (sys_nostdout.value)
|
|
return;
|
|
|
|
if (1)
|
|
{
|
|
char msg[MAXPRINTMSG];
|
|
unsigned char *t;
|
|
|
|
va_start (argptr,fmt);
|
|
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < coninput_len; i++)
|
|
putch('\b');
|
|
putch('\b');
|
|
for (i = 0; i < coninput_len; i++)
|
|
putch(' ');
|
|
putch(' ');
|
|
for (i = 0; i < coninput_len; i++)
|
|
putch('\b');
|
|
putch('\b');
|
|
}
|
|
|
|
|
|
for (t = (unsigned char*)msg; *t; t++)
|
|
{
|
|
if (*t >= 146 && *t < 156)
|
|
*t = *t - 146 + '0';
|
|
if (*t >= 0x12 && *t <= 0x1b)
|
|
*t = *t - 0x12 + '0';
|
|
if (*t == 143)
|
|
*t = '.';
|
|
if (*t == 157 || *t == 158 || *t == 159)
|
|
*t = '-';
|
|
if (*t >= 128)
|
|
*t -= 128;
|
|
if (*t == 16)
|
|
*t = '[';
|
|
if (*t == 17)
|
|
*t = ']';
|
|
if (*t == 0x1c)
|
|
*t = 249;
|
|
}
|
|
|
|
if (sys_colorconsole.value && hconsoleout)
|
|
{
|
|
int ext = CON_WHITEMASK;
|
|
int extstack[4];
|
|
int extstackdepth = 0;
|
|
unsigned char *str = (unsigned char*)msg;
|
|
|
|
|
|
while(*str)
|
|
{
|
|
if (*str == '^')
|
|
{
|
|
str++;
|
|
if (*str >= '0' && *str <= '9')
|
|
{
|
|
ext = q3codemasks[*str++-'0'] | (ext&~CON_Q3MASK); //change colour only.
|
|
continue;
|
|
}
|
|
else if (*str == '&') // extended code
|
|
{
|
|
if (isextendedcode(str[1]) && isextendedcode(str[2]))
|
|
{
|
|
str++; // foreground char
|
|
if (*str == '-') // default for FG
|
|
ext = (COLOR_WHITE << CON_FGSHIFT) | (ext&~CON_FGMASK);
|
|
else if (*str >= 'A')
|
|
ext = ((*str - ('A' - 10)) << CON_FGSHIFT) | (ext&~CON_FGMASK);
|
|
else
|
|
ext = ((*str - '0') << CON_FGSHIFT) | (ext&~CON_FGMASK);
|
|
str++; // background char
|
|
if (*str == '-') // default (clear) for BG
|
|
ext &= ~CON_BGMASK & ~CON_NONCLEARBG;
|
|
else if (*str >= 'A')
|
|
ext = ((*str - ('A' - 10)) << CON_BGSHIFT) | (ext&~CON_BGMASK) | CON_NONCLEARBG;
|
|
else
|
|
ext = ((*str - '0') << CON_BGSHIFT) | (ext&~CON_BGMASK) | CON_NONCLEARBG;
|
|
str++;
|
|
continue;
|
|
}
|
|
Sys_PrintColouredChar('^' | ext);
|
|
// else invalid code
|
|
}
|
|
else if (*str == 'a')
|
|
{
|
|
str++;
|
|
ext ^= CON_2NDCHARSETTEXT;
|
|
continue;
|
|
}
|
|
else if (*str == 'b')
|
|
{
|
|
str++;
|
|
ext ^= CON_BLINKTEXT;
|
|
continue;
|
|
}
|
|
else if (*str == 'h')
|
|
{
|
|
str++;
|
|
ext ^= CON_HALFALPHA;
|
|
continue;
|
|
}
|
|
else if (*str == 's') //store on stack (it's great for names)
|
|
{
|
|
str++;
|
|
if (extstackdepth < sizeof(extstack)/sizeof(extstack[0]))
|
|
{
|
|
extstack[extstackdepth] = ext;
|
|
extstackdepth++;
|
|
}
|
|
continue;
|
|
}
|
|
else if (*str == 'r') //restore from stack (it's great for names)
|
|
{
|
|
str++;
|
|
if (extstackdepth)
|
|
{
|
|
extstackdepth--;
|
|
ext = extstack[extstackdepth];
|
|
}
|
|
continue;
|
|
}
|
|
else if (*str == '^')
|
|
{
|
|
Sys_PrintColouredChar('^' | ext);
|
|
str++;
|
|
}
|
|
else
|
|
{
|
|
Sys_PrintColouredChar('^' | ext);
|
|
Sys_PrintColouredChar ((*str++) | ext);
|
|
}
|
|
continue;
|
|
}
|
|
Sys_PrintColouredChar ((*str++) | ext);
|
|
}
|
|
|
|
ApplyColour(CON_WHITEMASK);
|
|
}
|
|
else
|
|
printf("%s", msg);
|
|
|
|
|
|
if (coninput_len)
|
|
printf("]%s", coninput_text);
|
|
else
|
|
putch(']');
|
|
}
|
|
else
|
|
{
|
|
va_start (argptr,fmt);
|
|
vprintf (fmt,argptr);
|
|
va_end (argptr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_Quit
|
|
================
|
|
*/
|
|
|
|
void Sys_Quit (void)
|
|
{
|
|
#ifdef USESERVICE
|
|
if (asservice)
|
|
{
|
|
MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
MyServiceStatus.dwCheckPoint = 0;
|
|
MyServiceStatus.dwWaitHint = 0;
|
|
MyServiceStatus.dwWin32ExitCode = 0;
|
|
MyServiceStatus.dwServiceSpecificExitCode = 0;
|
|
|
|
SetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);
|
|
}
|
|
#endif
|
|
exit (0);
|
|
}
|
|
|
|
int restorecode;
|
|
|
|
LRESULT (CALLBACK Sys_WindowHandler)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (uMsg == WM_USER)
|
|
{
|
|
if (lParam & 1)
|
|
{
|
|
}
|
|
else if ((lParam & 2 && restorecode == 0) ||
|
|
(lParam & 4 && restorecode == 1) ||
|
|
(lParam & 4 && restorecode == 2) )
|
|
{
|
|
// MessageBox(NULL, "Hello", "", 0);
|
|
restorecode++;
|
|
}
|
|
else if (lParam & 2 && restorecode == 3)
|
|
{
|
|
DestroyWindow(hWnd);
|
|
ShowWindow(consolewindowhandle, SW_SHOWNORMAL);
|
|
consolewindowhandle = NULL;
|
|
|
|
Cbuf_AddText("status\n", RESTRICT_LOCAL);
|
|
}
|
|
else if (lParam & 6)
|
|
{
|
|
restorecode = (lParam & 2)>0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
return DefWindowProc (hWnd, uMsg, wParam, lParam);
|
|
}
|
|
void Sys_HideConsole(void)
|
|
{
|
|
HMODULE kernel32dll;
|
|
HWND (WINAPI *GetConsoleWindow)(void);
|
|
|
|
if (consolewindowhandle)
|
|
return; //err... already hidden... ?
|
|
|
|
restorecode = 0;
|
|
|
|
GetConsoleWindow = NULL;
|
|
kernel32dll = LoadLibrary("kernel32.dll");
|
|
consolewindowhandle = NULL;
|
|
if (kernel32dll)
|
|
{
|
|
GetConsoleWindow = (void*)GetProcAddress(kernel32dll, "GetConsoleWindow");
|
|
if (GetConsoleWindow)
|
|
consolewindowhandle = GetConsoleWindow();
|
|
|
|
FreeModule(kernel32dll); //works because the underlying code uses kernel32, so this decreases the reference count rather than closing it.
|
|
}
|
|
|
|
if (!consolewindowhandle)
|
|
{
|
|
char old[512];
|
|
#define STRINGH "Trying to hide" //msvc sucks
|
|
GetConsoleTitle(old, sizeof(old));
|
|
SetConsoleTitle(STRINGH);
|
|
consolewindowhandle = FindWindow(NULL, STRINGH);
|
|
SetConsoleTitle(old);
|
|
#undef STRINGH
|
|
}
|
|
|
|
if (consolewindowhandle)
|
|
{
|
|
WNDCLASS wc;
|
|
NOTIFYICONDATA d;
|
|
|
|
/* Register the frame class */
|
|
memset(&wc, 0, sizeof(wc));
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = Sys_WindowHandler;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = GetModuleHandle(NULL);
|
|
wc.hIcon = 0;
|
|
wc.hCursor = LoadCursor (NULL,IDC_ARROW);
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = 0;
|
|
wc.lpszClassName = "DeadQuake";
|
|
|
|
RegisterClass (&wc);
|
|
|
|
hiddenwindowhandler = CreateWindow(wc.lpszClassName, "DeadQuake", 0, 0, 0, 16, 16, NULL, NULL, GetModuleHandle(NULL), NULL);
|
|
if (!hiddenwindowhandler)
|
|
{
|
|
Con_Printf("Failed to create window\n");
|
|
return;
|
|
}
|
|
ShowWindow(consolewindowhandle, SW_HIDE);
|
|
|
|
d.cbSize = sizeof(NOTIFYICONDATA);
|
|
d.hWnd = hiddenwindowhandler;
|
|
d.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
|
|
d.hIcon = NULL;
|
|
d.uCallbackMessage = WM_USER;
|
|
d.uID = 0;
|
|
strcpy(d.szTip, "");
|
|
Shell_NotifyIcon(NIM_ADD, &d);
|
|
}
|
|
else
|
|
Con_Printf("Your OS doesn't seem to properly support the way this was implemented\n");
|
|
}
|
|
|
|
void Sys_ServerActivity(void)
|
|
{
|
|
HMODULE kernel32dll;
|
|
HWND (WINAPI *GetConsoleWindow)(void);
|
|
HWND wnd;
|
|
|
|
restorecode = 0;
|
|
|
|
GetConsoleWindow = NULL;
|
|
kernel32dll = LoadLibrary("kernel32.dll");
|
|
wnd = NULL;
|
|
if (kernel32dll)
|
|
{
|
|
GetConsoleWindow = (void*)GetProcAddress(kernel32dll, "GetConsoleWindow");
|
|
if (GetConsoleWindow)
|
|
wnd = GetConsoleWindow();
|
|
|
|
FreeModule(kernel32dll); //works because the underlying code uses kernel32, so this decreases the reference count rather than closing it.
|
|
}
|
|
|
|
if (!wnd)
|
|
{
|
|
char old[512];
|
|
#define STRINGF "About To Flash" //msvc sucks
|
|
GetConsoleTitle(old, sizeof(old));
|
|
SetConsoleTitle(STRINGF);
|
|
wnd = FindWindow(NULL, STRINGF);
|
|
SetConsoleTitle(old);
|
|
#undef STRINGF
|
|
}
|
|
|
|
if (wnd && GetActiveWindow() != wnd)
|
|
FlashWindow(wnd, true);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Sys_Init
|
|
|
|
Quake calls this so the system can register variables before host_hunklevel
|
|
is marked
|
|
=============
|
|
*/
|
|
void Sys_Init (void)
|
|
{
|
|
Cvar_Register (&sys_nostdout, "System controls");
|
|
Cvar_Register (&sys_maxtic, "System controls");
|
|
Cvar_Register (&sys_colorconsole, "System controls");
|
|
|
|
Cmd_AddCommand("hide", Sys_HideConsole);
|
|
|
|
hconsoleout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
main
|
|
|
|
==================
|
|
*/
|
|
char *newargv[256];
|
|
|
|
void Signal_Error_Handler (int sig)
|
|
{
|
|
Sys_Error("Illegal error occured");
|
|
}
|
|
|
|
|
|
void StartQuakeServer(void)
|
|
{
|
|
quakeparms_t parms;
|
|
static char cwd[1024];
|
|
int t;
|
|
|
|
TL_InitLanguages();
|
|
|
|
parms.argc = com_argc;
|
|
parms.argv = com_argv;
|
|
|
|
parms.memsize = 32*1024*1024;
|
|
|
|
if ((t = COM_CheckParm ("-heapsize")) != 0 &&
|
|
t + 1 < com_argc)
|
|
parms.memsize = Q_atoi (com_argv[t + 1]) * 1024;
|
|
|
|
if ((t = COM_CheckParm ("-mem")) != 0 &&
|
|
t + 1 < com_argc)
|
|
parms.memsize = Q_atoi (com_argv[t + 1]) * 1024 * 1024;
|
|
|
|
parms.membase = VirtualAlloc(NULL, parms.memsize, MEM_RESERVE, PAGE_NOACCESS);
|
|
// parms.membase = malloc (parms.memsize);
|
|
|
|
if (!parms.membase)
|
|
Sys_Error("Insufficient memory.\n");
|
|
|
|
parms.basedir = ".";
|
|
|
|
SV_Init (&parms);
|
|
|
|
// run one frame immediately for first heartbeat
|
|
SV_Frame ();
|
|
}
|
|
|
|
|
|
#ifdef USESERVICE
|
|
int servicecontrol;
|
|
#endif
|
|
void ServerMainLoop(void)
|
|
{
|
|
double newtime, time, oldtime;
|
|
//
|
|
// main loop
|
|
//
|
|
oldtime = Sys_DoubleTime () - 0.1;
|
|
while (1)
|
|
{
|
|
NET_Sleep(sys_maxtic.value, false);
|
|
|
|
// find time passed since last cycle
|
|
newtime = Sys_DoubleTime ();
|
|
time = newtime - oldtime;
|
|
oldtime = newtime;
|
|
SV_Frame ();
|
|
|
|
|
|
#ifdef USESERVICE
|
|
switch(servicecontrol)
|
|
{
|
|
case SERVICE_CONTROL_PAUSE:
|
|
// Initialization complete - report running status.
|
|
MyServiceStatus.dwCurrentState = SERVICE_PAUSED;
|
|
MyServiceStatus.dwCheckPoint = 0;
|
|
MyServiceStatus.dwWaitHint = 0;
|
|
|
|
SetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);
|
|
sv.paused |= 2;
|
|
break;
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
// Initialization complete - report running status.
|
|
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
MyServiceStatus.dwCheckPoint = 0;
|
|
MyServiceStatus.dwWaitHint = 0;
|
|
|
|
SetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);
|
|
|
|
sv.paused &= ~2;
|
|
break;
|
|
case SERVICE_CONTROL_STOP: //leave the loop
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef USESERVICE
|
|
|
|
VOID WINAPI MyServiceCtrlHandler(DWORD dwControl)
|
|
{
|
|
servicecontrol = dwControl;
|
|
}
|
|
|
|
void WINAPI StartQuakeServerService (DWORD argc, LPTSTR *argv)
|
|
{
|
|
HKEY hk;
|
|
char path[MAX_OSPATH];
|
|
DWORD pathlen;
|
|
DWORD type;
|
|
|
|
asservice = true;
|
|
|
|
MyServiceStatus.dwServiceType = SERVICE_WIN32|SERVICE_INTERACTIVE_PROCESS;
|
|
MyServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE;
|
|
MyServiceStatus.dwWin32ExitCode = 0;
|
|
MyServiceStatus.dwServiceSpecificExitCode = 0;
|
|
MyServiceStatus.dwCheckPoint = 0;
|
|
MyServiceStatus.dwWaitHint = 0;
|
|
|
|
ServerServiceStatusHandle = RegisterServiceCtrlHandler(
|
|
SERVICENAME,
|
|
MyServiceCtrlHandler);
|
|
|
|
if (ServerServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
|
|
{
|
|
printf(" [MY_SERVICE] RegisterServiceCtrlHandler failed %d\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
|
|
RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\"DISTRIBUTIONLONG"\\"FULLENGINENAME, &hk);
|
|
RegQueryValueEx(hk, "servicepath", 0, &type, NULL, &pathlen);
|
|
if (type == REG_SZ && pathlen < sizeof(path))
|
|
RegQueryValueEx(hk, "servicepath", 0, NULL, path, &pathlen);
|
|
RegCloseKey(hk);
|
|
|
|
SetCurrentDirectory(path);
|
|
|
|
|
|
COM_InitArgv (argc, argv);
|
|
StartQuakeServer();
|
|
|
|
|
|
// Handle error condition
|
|
if (!sv.state)
|
|
{
|
|
MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
MyServiceStatus.dwCheckPoint = 0;
|
|
MyServiceStatus.dwWaitHint = 0;
|
|
MyServiceStatus.dwWin32ExitCode = 0;
|
|
MyServiceStatus.dwServiceSpecificExitCode = 0;
|
|
|
|
SetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);
|
|
return;
|
|
}
|
|
|
|
// Initialization complete - report running status.
|
|
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
MyServiceStatus.dwCheckPoint = 0;
|
|
MyServiceStatus.dwWaitHint = 0;
|
|
|
|
if (!SetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus))
|
|
{
|
|
printf(" [MY_SERVICE] SetServiceStatus error %ld\n",GetLastError());
|
|
}
|
|
|
|
ServerMainLoop();
|
|
|
|
MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
MyServiceStatus.dwCheckPoint = 0;
|
|
MyServiceStatus.dwWaitHint = 0;
|
|
MyServiceStatus.dwWin32ExitCode = 0;
|
|
MyServiceStatus.dwServiceSpecificExitCode = 0;
|
|
|
|
SetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);
|
|
|
|
return;
|
|
}
|
|
|
|
SERVICE_TABLE_ENTRY DispatchTable[] =
|
|
{
|
|
{ SERVICENAME, StartQuakeServerService },
|
|
{ NULL, NULL }
|
|
};
|
|
#endif
|
|
|
|
qboolean NET_Sleep(int msec, qboolean stdinissocket);
|
|
int main (int argc, char **argv)
|
|
{
|
|
#ifdef USESERVICE
|
|
if (StartServiceCtrlDispatcher( DispatchTable))
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CATCHCRASH
|
|
__try
|
|
#endif
|
|
{
|
|
COM_InitArgv (argc, argv);
|
|
#ifdef USESERVICE
|
|
if (COM_CheckParm("-register"))
|
|
{
|
|
CreateSampleService(1);
|
|
return true;
|
|
}
|
|
if (COM_CheckParm("-unregister"))
|
|
{
|
|
CreateSampleService(0);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifndef _DEBUG
|
|
if (COM_CheckParm("-noreset"))
|
|
{
|
|
signal (SIGFPE, Signal_Error_Handler);
|
|
signal (SIGILL, Signal_Error_Handler);
|
|
signal (SIGSEGV, Signal_Error_Handler);
|
|
}
|
|
#endif
|
|
StartQuakeServer();
|
|
|
|
ServerMainLoop();
|
|
}
|
|
#ifdef CATCHCRASH
|
|
__except (CrashExceptionHandler(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef USESERVICE
|
|
void CreateSampleService(qboolean create)
|
|
{
|
|
BOOL deleted;
|
|
char path[MAX_OSPATH];
|
|
char exe[MAX_OSPATH];
|
|
SC_HANDLE schService;
|
|
|
|
SC_HANDLE schSCManager;
|
|
|
|
// Open a handle to the SC Manager database.
|
|
schSCManager = OpenSCManager(
|
|
NULL, // local machine
|
|
NULL, // ServicesActive database
|
|
SC_MANAGER_ALL_ACCESS); // full access rights
|
|
|
|
if (NULL == schSCManager)
|
|
{
|
|
Con_Printf("Failed to open SCManager (%d)\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
if (!GetModuleFileName(NULL, exe+1, sizeof(exe)-2))
|
|
{
|
|
Con_Printf("Path too long\n");
|
|
return;
|
|
}
|
|
GetCurrentDirectory(sizeof(path), path);
|
|
exe[0] = '\"';
|
|
exe[strlen(path)+1] = '\0';
|
|
exe[strlen(path)] = '\"';
|
|
|
|
if (!create)
|
|
{
|
|
schService = OpenServiceA(schSCManager, SERVICENAME, SERVICE_ALL_ACCESS);
|
|
if (schService)
|
|
{
|
|
deleted = DeleteService(schService);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HKEY hk;
|
|
RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\"DISTRIBUTIONLONG"\\"FULLENGINENAME, &hk);
|
|
if (!hk)RegCreateKey(HKEY_LOCAL_MACHINE, "Software\\"DISTRIBUTIONLONG"\\"FULLENGINENAME, &hk);
|
|
RegSetValueEx(hk, "servicepath", 0, REG_SZ, path, strlen(path));
|
|
RegCloseKey(hk);
|
|
|
|
schService = CreateService(
|
|
schSCManager, // SCManager database
|
|
SERVICENAME, // name of service
|
|
FULLENGINENAME" Server", // service name to display
|
|
SERVICE_ALL_ACCESS, // desired access
|
|
SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, // service type
|
|
SERVICE_AUTO_START, // start type
|
|
SERVICE_ERROR_NORMAL, // error control type
|
|
exe, // service's binary
|
|
NULL, // no load ordering group
|
|
NULL, // no tag identifier
|
|
NULL, // no dependencies
|
|
NULL, // LocalSystem account
|
|
NULL); // no password
|
|
}
|
|
|
|
if (schService == NULL)
|
|
{
|
|
Con_Printf("CreateService failed.\n");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
CloseServiceHandle(schService);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD
|
|
/* Thread creation calls */
|
|
typedef struct threadwrap_s
|
|
{
|
|
void (*func)(void *);
|
|
void *args;
|
|
} threadwrap_t;
|
|
|
|
// the thread call is wrapped so we don't need WINAPI everywhere
|
|
DWORD WINAPI threadwrapper(void *args)
|
|
{
|
|
threadwrap_t tw;
|
|
tw.func = ((threadwrap_t *)args)->func;
|
|
tw.args = ((threadwrap_t *)args)->args;
|
|
|
|
free(args);
|
|
tw.func(tw.args);
|
|
|
|
#ifndef WIN32CRTDLL
|
|
_endthreadex(0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void *Sys_CreateThread(int (*func)(void *), void *args, int stacksize)
|
|
{
|
|
threadwrap_t *tw = (threadwrap_t *)malloc(sizeof(threadwrap_t));
|
|
HANDLE handle;
|
|
|
|
if (!tw)
|
|
return NULL;
|
|
|
|
stacksize += 128; // wrapper overhead, also prevent default stack size
|
|
tw->func = func;
|
|
tw->args = args;
|
|
#ifdef WIN32CRTDLL
|
|
handle = (HANDLE)CreateThread(NULL, stacksize, &threadwrapper, (void *)tw, 0, NULL);
|
|
#else
|
|
handle = (HANDLE)_beginthreadex(NULL, stacksize, &threadwrapper, (void *)tw, 0, NULL);
|
|
#endif
|
|
if (!handle)
|
|
{
|
|
free(tw);
|
|
return NULL;
|
|
}
|
|
|
|
return (void *)handle;
|
|
}
|
|
|
|
void Sys_WaitOnThread(void *thread)
|
|
{
|
|
WaitForSingleObject((HANDLE)thread, INFINITE);
|
|
CloseHandle((HANDLE)thread);
|
|
}
|
|
|
|
/* Mutex calls */
|
|
void *Sys_CreateMutex(void)
|
|
{
|
|
return (void *)CreateMutex(NULL, 0, NULL);
|
|
}
|
|
|
|
qboolean Sys_TryLockMutex(void *mutex)
|
|
{
|
|
return WaitForSingleObject(mutex, 0) == WAIT_OBJECT_0;
|
|
}
|
|
|
|
qboolean Sys_LockMutex(void *mutex)
|
|
{
|
|
return WaitForSingleObject(mutex, INFINITE) == WAIT_OBJECT_0;
|
|
}
|
|
|
|
qboolean Sys_UnlockMutex(void *mutex)
|
|
{
|
|
return !!ReleaseMutex(mutex);
|
|
}
|
|
|
|
void Sys_DestroyMutex(void *mutex)
|
|
{
|
|
CloseHandle(mutex);
|
|
}
|
|
|
|
/* Conditional wait calls */
|
|
/*
|
|
TODO: Windows Vista has condition variables as documented here:
|
|
http://msdn.microsoft.com/en-us/library/ms682052(VS.85).aspx
|
|
Note this uses Slim Reader/Writer locks (Vista+ exclusive)
|
|
or critical sections.
|
|
|
|
The condition variable implementation is based on the libSDL implementation.
|
|
This code could probably be made more efficient with the use of events or
|
|
different mechanisms but for now the main concern is a correct and
|
|
complete solution.
|
|
*/
|
|
typedef struct condvar_s
|
|
{
|
|
int waiting;
|
|
int signals;
|
|
CRITICAL_SECTION countlock;
|
|
CRITICAL_SECTION mainlock;
|
|
HANDLE wait_sem;
|
|
HANDLE wait_done;
|
|
} condvar_t;
|
|
|
|
void *Sys_CreateConditional(void)
|
|
{
|
|
condvar_t *cv;
|
|
|
|
cv = (condvar_t *)malloc(sizeof(condvar_t));
|
|
if (!cv)
|
|
return NULL;
|
|
|
|
cv->waiting = 0;
|
|
cv->signals = 0;
|
|
InitializeCriticalSection (&cv->mainlock);
|
|
InitializeCriticalSection (&cv->countlock);
|
|
cv->wait_sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
|
|
cv->wait_done = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
|
|
|
|
if (cv->wait_sem && cv->wait_done)
|
|
return (void *)cv;
|
|
|
|
// something failed so deallocate everything
|
|
if (cv->wait_done)
|
|
CloseHandle(cv->wait_done);
|
|
if (cv->wait_sem)
|
|
CloseHandle(cv->wait_sem);
|
|
DeleteCriticalSection(&cv->countlock);
|
|
DeleteCriticalSection(&cv->mainlock);
|
|
free(cv);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
qboolean Sys_LockConditional(void *condv)
|
|
{
|
|
EnterCriticalSection(&((condvar_t *)condv)->mainlock);
|
|
return true;
|
|
}
|
|
|
|
qboolean Sys_UnlockConditional(void *condv)
|
|
{
|
|
LeaveCriticalSection(&((condvar_t *)condv)->mainlock);
|
|
return true;
|
|
}
|
|
|
|
qboolean Sys_ConditionWait(void *condv)
|
|
{
|
|
condvar_t *cv = (condvar_t *)condv;
|
|
qboolean success;
|
|
|
|
// increase count for non-signaled waiting threads
|
|
EnterCriticalSection(&cv->countlock);
|
|
cv->waiting++;
|
|
LeaveCriticalSection(&cv->countlock);
|
|
|
|
LeaveCriticalSection(&cv->mainlock); // unlock as per condition variable definition
|
|
|
|
// wait on a signal
|
|
success = (WaitForSingleObject(cv->wait_sem, INFINITE) != WAIT_FAILED);
|
|
|
|
// update waiting count and alert signaling thread that we're done to avoid the deadlock condition
|
|
EnterCriticalSection(&cv->countlock);
|
|
if (cv->signals > 0)
|
|
{
|
|
ReleaseSemaphore(cv->wait_done, cv->signals, NULL);
|
|
cv->signals = 0;
|
|
}
|
|
cv->waiting--;
|
|
LeaveCriticalSection(&cv->countlock);
|
|
|
|
EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition
|
|
|
|
return success;
|
|
}
|
|
|
|
qboolean Sys_ConditionSignal(void *condv)
|
|
{
|
|
condvar_t *cv = (condvar_t *)condv;
|
|
|
|
// if there are non-signaled waiting threads, we signal one and wait on the response
|
|
EnterCriticalSection(&cv->countlock);
|
|
if (cv->waiting > cv->signals)
|
|
{
|
|
cv->signals++;
|
|
ReleaseSemaphore(cv->wait_sem, 1, NULL);
|
|
LeaveCriticalSection(&cv->countlock);
|
|
WaitForSingleObject(cv->wait_done, INFINITE);
|
|
}
|
|
else
|
|
LeaveCriticalSection(&cv->countlock);
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean Sys_ConditionBroadcast(void *condv)
|
|
{
|
|
condvar_t *cv = (condvar_t *)condv;
|
|
|
|
// if there are non-signaled waiting threads, we signal all of them and wait on all the responses back
|
|
EnterCriticalSection(&cv->countlock);
|
|
if (cv->waiting > cv->signals)
|
|
{
|
|
int i, num_waiting;
|
|
|
|
num_waiting = (cv->waiting - cv->signals);
|
|
cv->signals = cv->waiting;
|
|
|
|
ReleaseSemaphore(cv->wait_sem, num_waiting, NULL);
|
|
LeaveCriticalSection(&cv->countlock);
|
|
// there's no call to wait for the same object multiple times so we need to loop through
|
|
// and burn up the semaphore count
|
|
for (i = 0; i < num_waiting; i++)
|
|
WaitForSingleObject(cv->wait_done, INFINITE);
|
|
}
|
|
else
|
|
LeaveCriticalSection(&cv->countlock);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Sys_DestroyConditional(void *condv)
|
|
{
|
|
condvar_t *cv = (condvar_t *)condv;
|
|
|
|
CloseHandle(cv->wait_done);
|
|
CloseHandle(cv->wait_sem);
|
|
DeleteCriticalSection(&cv->countlock);
|
|
DeleteCriticalSection(&cv->mainlock);
|
|
free(cv);
|
|
}
|
|
#endif
|
|
|
|
#endif
|