mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-29 23:22:01 +00:00
ee9c9025a0
Added -install arg to linux dedicated servers, to automatically install dependancies and updates. Fix edge friction (at least when pm_edgefriction is 2 and not empty - can't break compat). Added getchannellevel csqc builtin. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5438 fc73d0e0-1445-4013-8a0c-d673dee63da5
1842 lines
43 KiB
C
1842 lines
43 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 "quakedef.h"
|
|
#include <sys/types.h>
|
|
#include <sys/timeb.h>
|
|
|
|
#ifndef HAVE_CLIENT
|
|
|
|
#include <winsock.h>
|
|
#include <conio.h>
|
|
#include <direct.h>
|
|
|
|
#ifdef MULTITHREAD
|
|
#include <process.h>
|
|
#endif
|
|
|
|
#ifndef MINIMAL
|
|
//#define USESERVICE
|
|
#endif
|
|
#define SERVICENAME DISTRIBUTION"SV"
|
|
|
|
|
|
static HANDLE hconsoleout;
|
|
extern int isPlugin; //if 2, we qcdebug to external program
|
|
|
|
|
|
qboolean WinNT; //if true, use utf-16 file paths. if false, hope that paths are in ascii.
|
|
|
|
#if defined(_DEBUG) || defined(DEBUG)
|
|
#define CATCHCRASH
|
|
#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 (qboolean iswatchdog, 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);
|
|
|
|
DWORD (WINAPI *pSymSetOptions)(DWORD SymOptions);
|
|
BOOL (WINAPI *pSymInitialize)(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess);
|
|
BOOL (WINAPI *pSymFromAddr)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);
|
|
|
|
#ifdef _WIN64
|
|
#define DBGHELP_POSTFIX "64"
|
|
BOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
|
|
PVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD64 AddrBase);
|
|
DWORD64 (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD64 qwAddr);
|
|
BOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);
|
|
BOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULE64 ModuleInfo);
|
|
#define STACKFRAMEX STACKFRAME64
|
|
#define IMAGEHLP_LINEX IMAGEHLP_LINE64
|
|
#define IMAGEHLP_MODULEX IMAGEHLP_MODULE64
|
|
#else
|
|
#define DBGHELP_POSTFIX ""
|
|
BOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress);
|
|
PVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD AddrBase);
|
|
DWORD (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD dwAddr);
|
|
BOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line);
|
|
BOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD dwAddr, PIMAGEHLP_MODULE ModuleInfo);
|
|
#define STACKFRAMEX STACKFRAME
|
|
#define IMAGEHLP_LINEX IMAGEHLP_LINE
|
|
#define IMAGEHLP_MODULEX IMAGEHLP_MODULE
|
|
#endif
|
|
dllfunction_t debughelpfuncs[] =
|
|
{
|
|
{(void*)&pSymFromAddr, "SymFromAddr"},
|
|
{(void*)&pSymSetOptions, "SymSetOptions"},
|
|
{(void*)&pSymInitialize, "SymInitialize"},
|
|
{(void*)&pStackWalkX, "StackWalk"DBGHELP_POSTFIX},
|
|
{(void*)&pSymFunctionTableAccessX, "SymFunctionTableAccess"DBGHELP_POSTFIX},
|
|
{(void*)&pSymGetModuleBaseX, "SymGetModuleBase"DBGHELP_POSTFIX},
|
|
{(void*)&pSymGetLineFromAddrX, "SymGetLineFromAddr"DBGHELP_POSTFIX},
|
|
{(void*)&pSymGetModuleInfoX, "SymGetModuleInfo"DBGHELP_POSTFIX},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
switch(exceptionCode)
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
case EXCEPTION_BREAKPOINT:
|
|
case EXCEPTION_SINGLE_STEP:
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
case EXCEPTION_FLT_OVERFLOW:
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
case EXCEPTION_INT_OVERFLOW:
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
case EXCEPTION_INVALID_DISPOSITION:
|
|
case EXCEPTION_GUARD_PAGE:
|
|
case EXCEPTION_INVALID_HANDLE:
|
|
// case EXCEPTION_POSSIBLE_DEADLOCK:
|
|
break;
|
|
default:
|
|
//because windows is a steaming pile of shite, we have to ignore any software-generated exceptions, because most of them are not in fact fatal, *EVEN IF THEY CLAIM TO BE NON-CONTINUABLE*
|
|
return exceptionCode;
|
|
}
|
|
|
|
hKernel = LoadLibrary ("kernel32");
|
|
pIsDebuggerPresent = (void*)GetProcAddress(hKernel, "IsDebuggerPresent");
|
|
|
|
if (pIsDebuggerPresent && pIsDebuggerPresent())
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
#if defined(HAVE_CLIENT) && defined(GLQUAKE)
|
|
if (qrenderer == QR_OPENGL)
|
|
GLVID_Crashed();
|
|
#endif
|
|
|
|
#if 1//ndef _MSC_VER
|
|
{
|
|
if (Sys_LoadLibrary("DBGHELP", debughelpfuncs))
|
|
{
|
|
STACKFRAMEX stack;
|
|
CONTEXT *pcontext = exceptionInfo->ContextRecord;
|
|
IMAGEHLP_LINEX line;
|
|
IMAGEHLP_MODULEX module;
|
|
struct
|
|
{
|
|
SYMBOL_INFO sym;
|
|
char name[1024];
|
|
} sym;
|
|
int frameno;
|
|
char stacklog[8192];
|
|
int logpos, logstart;
|
|
char *logline;
|
|
|
|
stacklog[logpos=0] = 0;
|
|
|
|
pSymInitialize(hProc, NULL, TRUE);
|
|
pSymSetOptions(SYMOPT_LOAD_LINES);
|
|
|
|
memset(&stack, 0, sizeof(stack));
|
|
#ifdef _WIN64
|
|
#define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_AMD64
|
|
stack.AddrPC.Mode = AddrModeFlat;
|
|
stack.AddrPC.Offset = pcontext->Rip;
|
|
stack.AddrFrame.Mode = AddrModeFlat;
|
|
stack.AddrFrame.Offset = pcontext->Rbp;
|
|
stack.AddrStack.Mode = AddrModeFlat;
|
|
stack.AddrStack.Offset = pcontext->Rsp;
|
|
#else
|
|
#define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_I386
|
|
stack.AddrPC.Mode = AddrModeFlat;
|
|
stack.AddrPC.Offset = pcontext->Eip;
|
|
stack.AddrFrame.Mode = AddrModeFlat;
|
|
stack.AddrFrame.Offset = pcontext->Ebp;
|
|
stack.AddrStack.Mode = AddrModeFlat;
|
|
stack.AddrStack.Offset = pcontext->Esp;
|
|
#endif
|
|
|
|
Q_strncpyz(stacklog+logpos, FULLENGINENAME " or dependancy has crashed. The following stack dump been copied to your windows clipboard.\n"
|
|
#ifdef _MSC_VER
|
|
"Would you like to generate a core dump too?\n"
|
|
#endif
|
|
"\n", sizeof(stacklog)-logpos);
|
|
logstart = logpos += strlen(stacklog+logpos);
|
|
|
|
//so I know which one it is
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
#define BUILDDEBUGREL "Debug"
|
|
#else
|
|
#define BUILDDEBUGREL "Optimised"
|
|
#endif
|
|
#ifdef MINIMAL
|
|
#define BUILDMINIMAL "Min"
|
|
#else
|
|
#define BUILDMINIMAL ""
|
|
#endif
|
|
#if defined(GLQUAKE) && !defined(D3DQUAKE)
|
|
#define BUILDTYPE "GL"
|
|
#elif !defined(GLQUAKE) && defined(D3DQUAKE)
|
|
#define BUILDTYPE "D3D"
|
|
#else
|
|
#define BUILDTYPE "Merged"
|
|
#endif
|
|
|
|
Q_snprintfz(stacklog+logpos, sizeof(stacklog)-logpos, "Build: %s %s %s: %s\r\n", BUILDDEBUGREL, PLATFORM, BUILDMINIMAL BUILDTYPE, version_string());
|
|
logpos += strlen(stacklog+logpos);
|
|
|
|
for(frameno = 0; ; frameno++)
|
|
{
|
|
DWORD64 symdisp;
|
|
DWORD linedisp;
|
|
DWORD_PTR symaddr;
|
|
if (!pStackWalkX(IMAGE_FILE_MACHINE_THIS, hProc, GetCurrentThread(), &stack, pcontext, NULL, pSymFunctionTableAccessX, pSymGetModuleBaseX, NULL))
|
|
break;
|
|
memset(&module, 0, sizeof(module));
|
|
module.SizeOfStruct = sizeof(module);
|
|
pSymGetModuleInfoX(hProc, stack.AddrPC.Offset, &module);
|
|
memset(&line, 0, sizeof(line));
|
|
line.SizeOfStruct = sizeof(line);
|
|
symdisp = 0;
|
|
memset(&sym, 0, sizeof(sym));
|
|
sym.sym.MaxNameLen = sizeof(sym.name);
|
|
symaddr = stack.AddrPC.Offset;
|
|
sym.sym.SizeOfStruct = sizeof(sym.sym);
|
|
if (pSymFromAddr(hProc, symaddr, &symdisp, &sym.sym))
|
|
{
|
|
if (pSymGetLineFromAddrX(hProc, stack.AddrPC.Offset, &linedisp, &line))
|
|
logline = va("%-20s - %s:%i (%s)\r\n", sym.sym.Name, line.FileName, (int)line.LineNumber, module.LoadedImageName);
|
|
else
|
|
logline = va("%-20s+%#x (%s)\r\n", sym.sym.Name, (unsigned int)symdisp, module.LoadedImageName);
|
|
}
|
|
else
|
|
logline = va("0x%p (%s)\r\n", (void*)(DWORD_PTR)stack.AddrPC.Offset, module.LoadedImageName);
|
|
Q_strncpyz(stacklog+logpos, logline, sizeof(stacklog)-logpos);
|
|
logpos += strlen(stacklog+logpos);
|
|
if (logpos+1 >= sizeof(stacklog))
|
|
break;
|
|
}
|
|
Sys_Printf("%s", stacklog+logstart);
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
else
|
|
{
|
|
Sys_Printf("We crashed.\nUnable to load dbghelp library. Stack info is not available\n");
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
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)
|
|
{
|
|
if (pIsDebuggerPresent ())
|
|
{
|
|
//its possible someone attached a debugger while we were showing that message
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
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.", FULLENGINENAME " Sucks", 0);
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
LONG CALLBACK nonmsvc_CrashExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
|
|
{
|
|
DWORD foo = EXCEPTION_CONTINUE_SEARCH;
|
|
foo = CrashExceptionHandler(false, ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo);
|
|
//we have no handler. thus we handle it by exiting.
|
|
if (foo == EXCEPTION_EXECUTE_HANDLER)
|
|
exit(1);
|
|
return foo;
|
|
}
|
|
#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;
|
|
|
|
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)
|
|
{
|
|
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
|
|
|
|
|
|
|
|
|
|
#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_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;
|
|
}
|
|
|
|
|
|
wchar_t *widen(wchar_t *out, size_t outlen, const char *utf8);
|
|
char *narrowen(char *out, size_t outlen, wchar_t *wide);
|
|
|
|
/*
|
|
================
|
|
Sys_mkdir
|
|
================
|
|
*/
|
|
void Sys_mkdir (const char *path)
|
|
{
|
|
_mkdir(path);
|
|
}
|
|
qboolean Sys_rmdir (const char *path)
|
|
{
|
|
return 0==_rmdir(path);
|
|
}
|
|
|
|
qboolean Sys_remove (const char *path)
|
|
{
|
|
remove(path);
|
|
|
|
return true;
|
|
}
|
|
qboolean Sys_Rename (const char *oldfname, const char *newfname)
|
|
{
|
|
return !rename(oldfname, newfname);
|
|
}
|
|
|
|
static time_t Sys_FileTimeToTime(FILETIME ft)
|
|
{
|
|
ULARGE_INTEGER ull;
|
|
ull.LowPart = ft.dwLowDateTime;
|
|
ull.HighPart = ft.dwHighDateTime;
|
|
return ull.QuadPart / 10000000ULL - 11644473600ULL;
|
|
}
|
|
|
|
static int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)
|
|
{
|
|
qboolean go;
|
|
if (!WinNT)
|
|
{
|
|
HANDLE r;
|
|
WIN32_FIND_DATAA fd;
|
|
int nest = neststart; //neststart refers to just after a /
|
|
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 '.' (includes .. and . directories, and unix hidden files)
|
|
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, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), 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, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);
|
|
}
|
|
}
|
|
}
|
|
} while(FindNextFile(r, &fd) && go);
|
|
FindClose(r);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HANDLE r;
|
|
WIN32_FIND_DATAW fd;
|
|
int nest = neststart; //neststart refers to just after a /
|
|
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];
|
|
|
|
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, "*.*");
|
|
|
|
{
|
|
wchar_t wroot[MAX_OSPATH];
|
|
r = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd);
|
|
}
|
|
strcpy(tmproot+neststart, "");
|
|
if (r==(HANDLE)-1)
|
|
return 1;
|
|
go = true;
|
|
do
|
|
{
|
|
char utf8[MAX_OSPATH];
|
|
char file[MAX_OSPATH];
|
|
narrowen(utf8, sizeof(utf8), fd.cFileName);
|
|
if (*utf8 == '.'); //don't ever find files with a name starting with '.'
|
|
else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory
|
|
{
|
|
if (wildcmp(submatch, utf8))
|
|
{
|
|
int newnest;
|
|
if (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH)
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s/", tmproot, utf8);
|
|
newnest = strlen(file);
|
|
strcpy(file+newnest, match+nest);
|
|
go = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath);
|
|
}
|
|
}
|
|
}
|
|
} while(FindNextFileW(r, &fd) && go);
|
|
FindClose(r);
|
|
}
|
|
else
|
|
{
|
|
const char *submatch = match + neststart;
|
|
char tmproot[MAX_OSPATH];
|
|
|
|
if (neststart+4 > MAX_OSPATH)
|
|
return 1;
|
|
memcpy(tmproot, match, neststart);
|
|
strcpy(tmproot+neststart, "*.*");
|
|
|
|
{
|
|
wchar_t wroot[MAX_OSPATH];
|
|
r = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd);
|
|
}
|
|
strcpy(tmproot+neststart, "");
|
|
if (r==(HANDLE)-1)
|
|
return 1;
|
|
go = true;
|
|
do
|
|
{
|
|
char utf8[MAX_OSPATH];
|
|
char file[MAX_OSPATH];
|
|
|
|
narrowen(utf8, sizeof(utf8), fd.cFileName);
|
|
if (*utf8 == '.')
|
|
; //don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files)
|
|
else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //is a directory
|
|
{
|
|
if (wildcmp(submatch, utf8))
|
|
{
|
|
if (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH)
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s/", tmproot+matchstart, utf8);
|
|
go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wildcmp(submatch, utf8))
|
|
{
|
|
if (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH)
|
|
{
|
|
Q_snprintfz(file, sizeof(file), "%s%s", tmproot+matchstart, utf8);
|
|
go = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);
|
|
}
|
|
}
|
|
}
|
|
} while(FindNextFileW(r, &fd) && go);
|
|
FindClose(r);
|
|
}
|
|
}
|
|
return go;
|
|
}
|
|
int Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *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);
|
|
}
|
|
|
|
/*
|
|
================
|
|
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);
|
|
COM_WorkerAbort(text);
|
|
|
|
|
|
// 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();
|
|
exit(1);
|
|
}
|
|
|
|
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();
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
Sys_Printf("\nLoading new instance of FTE...\n\n\n");
|
|
PR_Deinit(); //this takes a bit more mem
|
|
#ifdef SVRANKING
|
|
Rank_Flush();
|
|
#endif
|
|
#ifndef MINGW
|
|
fcloseall(); //make sure all files are written.
|
|
#endif
|
|
|
|
// 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 ();
|
|
|
|
exit (1); // this function is NORETURN type, complains without this
|
|
}
|
|
|
|
/*
|
|
================
|
|
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
|
|
================
|
|
*/
|
|
void SV_GetNewSpawnParms(client_t *cl);
|
|
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;
|
|
}
|
|
|
|
#ifdef SUBSERVERS
|
|
if (SSV_IsSubServer())
|
|
{
|
|
DWORD avail;
|
|
static char text[1024];
|
|
static int textpos = 0;
|
|
|
|
HANDLE input = GetStdHandle(STD_INPUT_HANDLE);
|
|
if (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL))
|
|
{
|
|
SV_FinalMessage("Cluster shut down\n");
|
|
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
|
|
}
|
|
else if (avail)
|
|
{
|
|
if (avail > sizeof(text)-1-textpos)
|
|
avail = sizeof(text)-1-textpos;
|
|
if (ReadFile(input, text+textpos, avail, &avail, NULL))
|
|
{
|
|
textpos += avail;
|
|
while(textpos >= 2)
|
|
{
|
|
unsigned short len = text[0] | (text[1]<<8);
|
|
if (textpos >= len && len >= 2)
|
|
{
|
|
memcpy(net_message.data, text+2, len-2);
|
|
net_message.cursize = len-2;
|
|
MSG_BeginReading (msg_nullnetprim);
|
|
|
|
SSV_ReadFromControlServer();
|
|
|
|
memmove(text, text+len, textpos - len);
|
|
textpos -= len;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
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))
|
|
{
|
|
wantquit = true;
|
|
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
|
|
}
|
|
else if (avail)
|
|
{
|
|
if (avail > sizeof(text)-1-textpos)
|
|
avail = sizeof(text)-1-textpos;
|
|
if (ReadFile(input, text+textpos, avail, &avail, NULL))
|
|
{
|
|
textpos += avail;
|
|
if (textpos > sizeof(text)-1)
|
|
Sys_Error("No.");
|
|
}
|
|
}
|
|
while (textpos)
|
|
{
|
|
text[textpos] = 0;
|
|
nl = strchr(text, '\n');
|
|
if (nl)
|
|
{
|
|
*nl++ = 0;
|
|
if (coninput_len)
|
|
{
|
|
putch ('\r');
|
|
putch (']');
|
|
}
|
|
coninput_len = 0;
|
|
Q_strncpyz(coninput_text, text, sizeof(coninput_text));
|
|
memmove(text, nl, textpos - (nl - text));
|
|
textpos -= (nl - text);
|
|
return coninput_text;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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, NULL);
|
|
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);
|
|
}
|
|
}
|
|
|
|
//this could be much more efficient.
|
|
static void Sys_PrintColouredChars(conchar_t *start, conchar_t *end)
|
|
{
|
|
wchar_t wc[256];
|
|
int l;
|
|
DWORD dummy;
|
|
unsigned int cp, flags, m;
|
|
|
|
m = CON_WHITEMASK;
|
|
l = 0;
|
|
|
|
while(start < end)
|
|
{
|
|
start = Font_Decode(start, &flags, &cp);
|
|
|
|
if (l+2 >= countof(wc) || flags != m)
|
|
{
|
|
ApplyColour(m);
|
|
if (WinNT)
|
|
WriteConsoleW(hconsoleout, wc, l, &dummy, NULL);
|
|
else
|
|
{
|
|
//win95 doesn't support wide chars *sigh*. blank consoles suck.
|
|
char ac[256];
|
|
l = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL);
|
|
WriteConsole(hconsoleout, ac, l, &dummy, NULL);
|
|
}
|
|
l = 0;
|
|
}
|
|
|
|
if (!(flags & CON_HIDDEN))
|
|
{
|
|
if (cp >= 0xe000 && cp < 0xe100)
|
|
{
|
|
cp -= 0xe000;
|
|
if (cp >= 0x80)
|
|
{
|
|
char c1[32] = "---..........> " "[]0123456789.---";
|
|
cp -= 0x80;
|
|
if (cp <= countof(c1))
|
|
cp = c1[cp];
|
|
}
|
|
}
|
|
if (cp > 0xffff)
|
|
cp = '?'; //too lazy for utf-16 crap when its mostly smilies anyway.
|
|
wc[l++] = cp;
|
|
}
|
|
}
|
|
|
|
//and flush it.
|
|
if (l)
|
|
{
|
|
ApplyColour(m);
|
|
if (WinNT)
|
|
WriteConsoleW(hconsoleout, wc, l, &dummy, NULL);
|
|
else
|
|
{
|
|
//win95 doesn't support wide chars *sigh*. blank consoles suck.
|
|
char ac[256];
|
|
l = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL);
|
|
WriteConsole(hconsoleout, ac, l, &dummy, NULL);
|
|
}
|
|
}
|
|
|
|
ApplyColour(CON_WHITEMASK);
|
|
}
|
|
|
|
/*
|
|
================
|
|
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);
|
|
|
|
#ifdef SUBSERVERS
|
|
if (SSV_IsSubServer())
|
|
{
|
|
SSV_PrintToMaster(msg);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
{
|
|
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');
|
|
}
|
|
|
|
if (sys_colorconsole.value && hconsoleout)
|
|
{
|
|
conchar_t out[MAXPRINTMSG], *end;
|
|
end = COM_ParseFunString(CON_WHITEMASK, msg, out, sizeof(out), false);
|
|
Sys_PrintColouredChars (out, end);
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
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_colorconsole, "System controls");
|
|
|
|
Cmd_AddCommand("hide", Sys_HideConsole);
|
|
|
|
if (isPlugin)
|
|
hconsoleout = CreateFileA("CONOUT$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
|
|
else
|
|
hconsoleout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
// SetConsoleCP(CP_UTF8);
|
|
// SetConsoleOutputCP(CP_UTF8);
|
|
}
|
|
|
|
void Sys_Shutdown (void)
|
|
{
|
|
}
|
|
|
|
/*
|
|
==================
|
|
main
|
|
|
|
==================
|
|
*/
|
|
char *newargv[256];
|
|
|
|
void Signal_Error_Handler (int sig)
|
|
{
|
|
Sys_Error("Illegal error occured");
|
|
}
|
|
|
|
|
|
void StartQuakeServer(void)
|
|
{
|
|
quakeparms_t parms;
|
|
static char bindir[MAX_OSPATH];
|
|
int c;
|
|
|
|
memset(&parms, 0, sizeof(parms));
|
|
|
|
parms.argc = com_argc;
|
|
parms.argv = com_argv;
|
|
|
|
GetModuleFileName(NULL, bindir, sizeof(bindir)-1);
|
|
*COM_SkipPath(bindir) = 0;
|
|
parms.binarydir = bindir;
|
|
|
|
parms.basedir = "./";
|
|
|
|
TL_InitLanguages(parms.basedir);
|
|
|
|
c = COM_CheckParm("-qcdebug");
|
|
if (c)
|
|
isPlugin = 3;
|
|
else
|
|
{
|
|
c = COM_CheckParm("-plugin");
|
|
if (c)
|
|
{
|
|
if (c < com_argc && !strcmp(com_argv[c+1], "qcdebug"))
|
|
isPlugin = 2;
|
|
else
|
|
isPlugin = 1;
|
|
}
|
|
else
|
|
isPlugin = 0;
|
|
}
|
|
|
|
SV_Init (&parms);
|
|
|
|
// run one frame immediately for first heartbeat
|
|
SV_Frame ();
|
|
}
|
|
|
|
|
|
#ifdef USESERVICE
|
|
int servicecontrol;
|
|
#endif
|
|
void ServerMainLoop(void)
|
|
{
|
|
float delay = 0.001;
|
|
//
|
|
// main loop
|
|
//
|
|
while (1)
|
|
{
|
|
NET_Sleep(delay, false);
|
|
|
|
// find time passed since last cycle
|
|
delay = 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 |= PAUSE_SERVICE;
|
|
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 &= ~PAUSE_SERVICE;
|
|
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
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
#ifdef CATCHCRASH
|
|
LoadLibrary ("DBGHELP"); //heap corruption can prevent loadlibrary from working properly, so do this in advance.
|
|
#ifdef _MSC_VER
|
|
__try
|
|
#else
|
|
AddVectoredExceptionHandler(true, nonmsvc_CrashExceptionHandler);
|
|
#endif
|
|
#endif
|
|
{
|
|
COM_InitArgv (argc, (const char **)argv);
|
|
|
|
#ifdef SUBSERVERS
|
|
isClusterSlave = COM_CheckParm("-clusterslave");
|
|
#endif
|
|
#ifdef USESERVICE
|
|
if (!SSV_IsSubServer() && StartServiceCtrlDispatcher( DispatchTable))
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#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
|
|
#ifdef _MSC_VER
|
|
__except (CrashExceptionHandler(false, GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
#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
|
|
|
|
void Sys_Sleep (double seconds)
|
|
{
|
|
Sleep(seconds * 1000);
|
|
}
|
|
|
|
/*
|
|
================
|
|
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;
|
|
}
|
|
|
|
|
|
#ifdef HAVEAUTOUPDATE
|
|
int Sys_GetAutoUpdateSetting(void)
|
|
{
|
|
return -1;
|
|
}
|
|
void Sys_SetAutoUpdateSetting(int newval)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
#ifdef WEBCLIENT
|
|
qboolean Sys_RunInstaller(void)
|
|
{ //not implemented
|
|
return false;
|
|
}
|
|
#endif
|
|
#endif
|