/* 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 #include #ifdef SERVERONLY #include #include #ifdef MULTITHREAD #include #endif #ifndef MINIMAL //#define USESERVICE #endif #define SERVICENAME DISTRIBUTION"SV" static HANDLE hconsoleout; 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 (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; #ifdef GLQUAKE 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 #include #include #include #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 ================ */ int _mkdir(const char *path);; void Sys_mkdir (char *path) { _mkdir(path); } qboolean Sys_remove (char *path) { remove(path); return true; } qboolean Sys_Rename (char *oldfname, 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 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(); 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 Rank_Flush(); #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], *nl; 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 // 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); } } void Sys_PrintColouredChar(unsigned int chr) { DWORD dummy; wchar_t wc; if (chr & CON_HIDDEN) return; ApplyColour(chr); wc = chr & CON_CHARMASK; WriteConsoleW(hconsoleout, &wc, 1, &dummy, NULL); } /* ================ 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], *c, *end; end = COM_ParseFunString(CON_WHITEMASK, msg, out, sizeof(out), false); for (c = out; c < end; c++) Sys_PrintColouredChar (*c); ApplyColour(CON_WHITEMASK); } 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); 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]; memset(&parms, 0, sizeof(parms)); TL_InitLanguages(); parms.argc = com_argc; parms.argv = com_argv; GetModuleFileName(NULL, bindir, sizeof(bindir)-1); *COM_SkipPath(bindir) = 0; parms.binarydir = bindir; 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; float delay = 0.001; // // main loop // oldtime = Sys_DoubleTime () - 0.1; while (1) { NET_Sleep(delay, false); // find time passed since last cycle newtime = Sys_DoubleTime (); time = newtime - oldtime; oldtime = newtime; 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 |= 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 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(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 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 #endif