DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

win_main.cpp (42672B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../../idlib/precompiled.h"
     31 
     32 #include <errno.h>
     33 #include <float.h>
     34 #include <fcntl.h>
     35 #include <direct.h>
     36 #include <io.h>
     37 #include <conio.h>
     38 #include <mapi.h>
     39 #include <ShellAPI.h>
     40 #include <Shlobj.h>
     41 
     42 #ifndef __MRC__
     43 #include <sys/types.h>
     44 #include <sys/stat.h>
     45 #endif
     46 
     47 #include "../sys_local.h"
     48 #include "win_local.h"
     49 #include "../../renderer/tr_local.h"
     50 
     51 idCVar Win32Vars_t::sys_arch( "sys_arch", "", CVAR_SYSTEM | CVAR_INIT, "" );
     52 idCVar Win32Vars_t::sys_cpustring( "sys_cpustring", "detect", CVAR_SYSTEM | CVAR_INIT, "" );
     53 idCVar Win32Vars_t::in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_BOOL, "enable mouse input" );
     54 idCVar Win32Vars_t::win_allowAltTab( "win_allowAltTab", "0", CVAR_SYSTEM | CVAR_BOOL, "allow Alt-Tab when fullscreen" );
     55 idCVar Win32Vars_t::win_notaskkeys( "win_notaskkeys", "0", CVAR_SYSTEM | CVAR_INTEGER, "disable windows task keys" );
     56 idCVar Win32Vars_t::win_username( "win_username", "", CVAR_SYSTEM | CVAR_INIT, "windows user name" );
     57 idCVar Win32Vars_t::win_outputEditString( "win_outputEditString", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
     58 idCVar Win32Vars_t::win_viewlog( "win_viewlog", "0", CVAR_SYSTEM | CVAR_INTEGER, "" );
     59 idCVar Win32Vars_t::win_timerUpdate( "win_timerUpdate", "0", CVAR_SYSTEM | CVAR_BOOL, "allows the game to be updated while dragging the window" );
     60 idCVar Win32Vars_t::win_allowMultipleInstances( "win_allowMultipleInstances", "0", CVAR_SYSTEM | CVAR_BOOL, "allow multiple instances running concurrently" );
     61 
     62 Win32Vars_t	win32;
     63 
     64 static char		sys_cmdline[MAX_STRING_CHARS];
     65 
     66 static sysMemoryStats_t exeLaunchMemoryStats;
     67 
     68 static HANDLE hProcessMutex;
     69 
     70 /*
     71 ================
     72 Sys_GetExeLaunchMemoryStatus
     73 ================
     74 */
     75 void Sys_GetExeLaunchMemoryStatus( sysMemoryStats_t &stats ) {
     76 	stats = exeLaunchMemoryStats;
     77 }
     78 
     79 /*
     80 ==================
     81 Sys_Sentry
     82 ==================
     83 */
     84 void Sys_Sentry() {
     85 }
     86 
     87 
     88 #pragma optimize( "", on )
     89 
     90 #ifdef DEBUG
     91 
     92 
     93 static unsigned int debug_total_alloc = 0;
     94 static unsigned int debug_total_alloc_count = 0;
     95 static unsigned int debug_current_alloc = 0;
     96 static unsigned int debug_current_alloc_count = 0;
     97 static unsigned int debug_frame_alloc = 0;
     98 static unsigned int debug_frame_alloc_count = 0;
     99 
    100 idCVar sys_showMallocs( "sys_showMallocs", "0", CVAR_SYSTEM, "" );
    101 
    102 // _HOOK_ALLOC, _HOOK_REALLOC, _HOOK_FREE
    103 
    104 typedef struct CrtMemBlockHeader
    105 {
    106 	struct _CrtMemBlockHeader *pBlockHeaderNext;	// Pointer to the block allocated just before this one:
    107 	struct _CrtMemBlockHeader *pBlockHeaderPrev;	// Pointer to the block allocated just after this one
    108    char *szFileName;    // File name
    109    int nLine;           // Line number
    110    size_t nDataSize;    // Size of user block
    111    int nBlockUse;       // Type of block
    112    long lRequest;       // Allocation number
    113 	byte		gap[4];								// Buffer just before (lower than) the user's memory:
    114 } CrtMemBlockHeader;
    115 
    116 #include <crtdbg.h>
    117 
    118 /*
    119 ==================
    120 Sys_AllocHook
    121 
    122 	called for every malloc/new/free/delete
    123 ==================
    124 */
    125 int Sys_AllocHook( int nAllocType, void *pvData, size_t nSize, int nBlockUse, long lRequest, const unsigned char * szFileName, int nLine ) 
    126 {
    127 	CrtMemBlockHeader	*pHead;
    128 	byte				*temp;
    129 
    130 	if ( nBlockUse == _CRT_BLOCK )
    131 	{
    132       return( TRUE );
    133 	}
    134 
    135 	// get a pointer to memory block header
    136 	temp = ( byte * )pvData;
    137 	temp -= 32;
    138 	pHead = ( CrtMemBlockHeader * )temp;
    139 
    140 	switch( nAllocType ) {
    141 		case	_HOOK_ALLOC:
    142 			debug_total_alloc += nSize;
    143 			debug_current_alloc += nSize;
    144 			debug_frame_alloc += nSize;
    145 			debug_total_alloc_count++;
    146 			debug_current_alloc_count++;
    147 			debug_frame_alloc_count++;
    148 			break;
    149 
    150 		case	_HOOK_FREE:
    151 			assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
    152 
    153 			debug_current_alloc -= pHead->nDataSize;
    154 			debug_current_alloc_count--;
    155 			debug_total_alloc_count++;
    156 			debug_frame_alloc_count++;
    157 			break;
    158 
    159 		case	_HOOK_REALLOC:
    160 			assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
    161 
    162 			debug_current_alloc -= pHead->nDataSize;
    163 			debug_total_alloc += nSize;
    164 			debug_current_alloc += nSize;
    165 			debug_frame_alloc += nSize;
    166 			debug_total_alloc_count++;
    167 			debug_current_alloc_count--;
    168 			debug_frame_alloc_count++;
    169 			break;
    170 	}
    171 	return( TRUE );
    172 }
    173 
    174 /*
    175 ==================
    176 Sys_DebugMemory_f
    177 ==================
    178 */
    179 void Sys_DebugMemory_f() {
    180   	common->Printf( "Total allocation %8dk in %d blocks\n", debug_total_alloc / 1024, debug_total_alloc_count );
    181   	common->Printf( "Current allocation %8dk in %d blocks\n", debug_current_alloc / 1024, debug_current_alloc_count );
    182 }
    183 
    184 /*
    185 ==================
    186 Sys_MemFrame
    187 ==================
    188 */
    189 void Sys_MemFrame() {
    190 	if( sys_showMallocs.GetInteger() ) {
    191 		common->Printf("Frame: %8dk in %5d blocks\n", debug_frame_alloc / 1024, debug_frame_alloc_count );
    192 	}
    193 
    194 	debug_frame_alloc = 0;
    195 	debug_frame_alloc_count = 0;
    196 }
    197 
    198 #endif
    199 
    200 /*
    201 ==================
    202 Sys_FlushCacheMemory
    203 
    204 On windows, the vertex buffers are write combined, so they
    205 don't need to be flushed from the cache
    206 ==================
    207 */
    208 void Sys_FlushCacheMemory( void *base, int bytes ) {
    209 }
    210 
    211 /*
    212 =============
    213 Sys_Error
    214 
    215 Show the early console as an error dialog
    216 =============
    217 */
    218 void Sys_Error( const char *error, ... ) {
    219 	va_list		argptr;
    220 	char		text[4096];
    221     MSG        msg;
    222 
    223 	va_start( argptr, error );
    224 	vsprintf( text, error, argptr );
    225 	va_end( argptr);
    226 
    227 	Conbuf_AppendText( text );
    228 	Conbuf_AppendText( "\n" );
    229 
    230 	Win_SetErrorText( text );
    231 	Sys_ShowConsole( 1, true );
    232 
    233 	timeEndPeriod( 1 );
    234 
    235 	Sys_ShutdownInput();
    236 
    237 	GLimp_Shutdown();
    238 
    239 	extern idCVar com_productionMode;
    240 	if ( com_productionMode.GetInteger() == 0 ) {
    241 		// wait for the user to quit
    242 		while ( 1 ) {
    243 			if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
    244 				common->Quit();
    245 			}
    246 			TranslateMessage( &msg );
    247 			DispatchMessage( &msg );
    248 		}
    249 	}
    250 	Sys_DestroyConsole();
    251 
    252 	exit (1);
    253 }
    254 
    255 /*
    256 ========================
    257 Sys_Launch
    258 ========================
    259 */
    260 void Sys_Launch( const char * path, idCmdArgs & args,  void * data, unsigned int dataSize ) {
    261 
    262 	TCHAR				szPathOrig[_MAX_PATH];
    263 	STARTUPINFO			si;
    264 	PROCESS_INFORMATION	pi;
    265 
    266 	ZeroMemory( &si, sizeof(si) );
    267 	si.cb = sizeof(si);
    268 
    269 	strcpy( szPathOrig, va( "\"%s\" %s", Sys_EXEPath(), (const char *)data ) );
    270 
    271 	if ( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
    272 		idLib::Error( "Could not start process: '%s' ", szPathOrig );
    273 		return;
    274 	}
    275 	cmdSystem->AppendCommandText( "quit\n" );
    276 }
    277 
    278 /*
    279 ========================
    280 Sys_GetCmdLine
    281 ========================
    282 */
    283 const char * Sys_GetCmdLine() {
    284 	return sys_cmdline;
    285 }
    286 
    287 /*
    288 ========================
    289 Sys_ReLaunch
    290 ========================
    291 */
    292 void Sys_ReLaunch( void * data, const unsigned int dataSize ) {
    293 	TCHAR				szPathOrig[MAX_PRINT_MSG];
    294 	STARTUPINFO			si;
    295 	PROCESS_INFORMATION	pi;
    296 
    297 	ZeroMemory( &si, sizeof(si) );
    298 	si.cb = sizeof(si);
    299 
    300 	strcpy( szPathOrig, va( "\"%s\" %s", Sys_EXEPath(), (const char *)data ) );
    301 
    302 	CloseHandle( hProcessMutex );
    303 
    304 	if ( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
    305 		idLib::Error( "Could not start process: '%s' ", szPathOrig );
    306 		return;
    307 	}
    308 	cmdSystem->AppendCommandText( "quit\n" );
    309 }
    310 
    311 /*
    312 ==============
    313 Sys_Quit
    314 ==============
    315 */
    316 void Sys_Quit() {
    317 	timeEndPeriod( 1 );
    318 	Sys_ShutdownInput();
    319 	Sys_DestroyConsole();
    320 	ExitProcess( 0 );
    321 }
    322 
    323 
    324 /*
    325 ==============
    326 Sys_Printf
    327 ==============
    328 */
    329 #define MAXPRINTMSG 4096
    330 void Sys_Printf( const char *fmt, ... ) {
    331 	char		msg[MAXPRINTMSG];
    332 
    333 	va_list argptr;
    334 	va_start(argptr, fmt);
    335 	idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
    336 	va_end(argptr);
    337 	msg[sizeof(msg)-1] = '\0';
    338 
    339 	OutputDebugString( msg );
    340 
    341 	if ( win32.win_outputEditString.GetBool() && idLib::IsMainThread() ) {
    342 		Conbuf_AppendText( msg );
    343 	}
    344 }
    345 
    346 /*
    347 ==============
    348 Sys_DebugPrintf
    349 ==============
    350 */
    351 #define MAXPRINTMSG 4096
    352 void Sys_DebugPrintf( const char *fmt, ... ) {
    353 	char msg[MAXPRINTMSG];
    354 
    355 	va_list argptr;
    356 	va_start( argptr, fmt );
    357 	idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
    358 	msg[ sizeof(msg)-1 ] = '\0';
    359 	va_end( argptr );
    360 
    361 	OutputDebugString( msg );
    362 }
    363 
    364 /*
    365 ==============
    366 Sys_DebugVPrintf
    367 ==============
    368 */
    369 void Sys_DebugVPrintf( const char *fmt, va_list arg ) {
    370 	char msg[MAXPRINTMSG];
    371 
    372 	idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, arg );
    373 	msg[ sizeof(msg)-1 ] = '\0';
    374 
    375 	OutputDebugString( msg );
    376 }
    377 
    378 /*
    379 ==============
    380 Sys_Sleep
    381 ==============
    382 */
    383 void Sys_Sleep( int msec ) {
    384 	Sleep( msec );
    385 }
    386 
    387 /*
    388 ==============
    389 Sys_ShowWindow
    390 ==============
    391 */
    392 void Sys_ShowWindow( bool show ) {
    393 	::ShowWindow( win32.hWnd, show ? SW_SHOW : SW_HIDE );
    394 }
    395 
    396 /*
    397 ==============
    398 Sys_IsWindowVisible
    399 ==============
    400 */
    401 bool Sys_IsWindowVisible() {
    402 	return ( ::IsWindowVisible( win32.hWnd ) != 0 );
    403 }
    404 
    405 /*
    406 ==============
    407 Sys_Mkdir
    408 ==============
    409 */
    410 void Sys_Mkdir( const char *path ) {
    411 	_mkdir (path);
    412 }
    413 
    414 /*
    415 =================
    416 Sys_FileTimeStamp
    417 =================
    418 */
    419 ID_TIME_T Sys_FileTimeStamp( idFileHandle fp ) {
    420 	FILETIME writeTime;
    421 	GetFileTime( fp, NULL, NULL, &writeTime );
    422 
    423 	/*
    424 		FILETIME = number of 100-nanosecond ticks since midnight 
    425 		1 Jan 1601 UTC. time_t = number of 1-second ticks since 
    426 		midnight 1 Jan 1970 UTC. To translate, we subtract a
    427 		FILETIME representation of midnight, 1 Jan 1970 from the
    428 		time in question and divide by the number of 100-ns ticks
    429 		in one second.
    430 	*/
    431 
    432 	SYSTEMTIME base_st = {
    433 		1970,   // wYear
    434 		1,      // wMonth
    435 		0,      // wDayOfWeek
    436 		1,      // wDay
    437 		0,      // wHour
    438 		0,      // wMinute
    439 		0,      // wSecond
    440 		0       // wMilliseconds
    441 	};
    442 
    443 	FILETIME base_ft;
    444 	SystemTimeToFileTime( &base_st, &base_ft );
    445 
    446 	LARGE_INTEGER itime;
    447 	itime.QuadPart = reinterpret_cast<LARGE_INTEGER&>( writeTime ).QuadPart;
    448 	itime.QuadPart -= reinterpret_cast<LARGE_INTEGER&>( base_ft ).QuadPart;
    449 	itime.QuadPart /= 10000000LL;
    450 	return itime.QuadPart;
    451 }
    452 
    453 /*
    454 ========================
    455 Sys_Rmdir
    456 ========================
    457 */
    458 bool Sys_Rmdir( const char *path ) {
    459 	return _rmdir( path ) == 0;
    460 }
    461 
    462 /*
    463 ========================
    464 Sys_IsFileWritable
    465 ========================
    466 */
    467 bool Sys_IsFileWritable( const char *path ) {
    468 	struct _stat st;
    469 	if ( _stat( path, &st ) == -1 ) {
    470 		return true;
    471 	}
    472 	return ( st.st_mode & S_IWRITE ) != 0;
    473 }
    474 
    475 /*
    476 ========================
    477 Sys_IsFolder
    478 ========================
    479 */
    480 sysFolder_t Sys_IsFolder( const char *path ) {
    481 	struct _stat buffer;
    482 	if ( _stat( path, &buffer ) < 0 ) {
    483 		return FOLDER_ERROR;
    484 	}
    485 	return ( buffer.st_mode & _S_IFDIR ) != 0 ? FOLDER_YES : FOLDER_NO;
    486 }
    487 
    488 /*
    489 ==============
    490 Sys_Cwd
    491 ==============
    492 */
    493 const char *Sys_Cwd() {
    494 	static char cwd[MAX_OSPATH];
    495 
    496 	_getcwd( cwd, sizeof( cwd ) - 1 );
    497 	cwd[MAX_OSPATH-1] = 0;
    498 
    499 	return cwd;
    500 }
    501 
    502 /*
    503 ==============
    504 Sys_DefaultBasePath
    505 ==============
    506 */
    507 const char *Sys_DefaultBasePath() {
    508 	return Sys_Cwd();
    509 }
    510 
    511 // Vista shit
    512 typedef HRESULT (WINAPI * SHGetKnownFolderPath_t)( const GUID & rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath );
    513 // NOTE: FOLIDERID_SavedGames is already exported from in shell32.dll in Windows 7.  We can only detect
    514 // the compiler version, but that doesn't doesn't tell us which version of the OS we're linking against.
    515 // This GUID value should never change, so we name it something other than FOLDERID_SavedGames to get
    516 // around this problem.
    517 const GUID FOLDERID_SavedGames_IdTech5 = { 0x4c5c32ff, 0xbb9d, 0x43b0, { 0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4 } };
    518 
    519 /*
    520 ==============
    521 Sys_DefaultSavePath
    522 ==============
    523 */
    524 const char *Sys_DefaultSavePath() {
    525 	static char savePath[ MAX_PATH ];
    526 	memset( savePath, 0, MAX_PATH );
    527 
    528 	HMODULE hShell = LoadLibrary( "shell32.dll" );
    529 	if ( hShell ) {
    530 		SHGetKnownFolderPath_t SHGetKnownFolderPath = (SHGetKnownFolderPath_t)GetProcAddress( hShell, "SHGetKnownFolderPath" );
    531 		if ( SHGetKnownFolderPath ) {
    532 			wchar_t * path;
    533 			if ( SUCCEEDED( SHGetKnownFolderPath( FOLDERID_SavedGames_IdTech5, CSIDL_FLAG_CREATE | CSIDL_FLAG_PER_USER_INIT, 0, &path ) ) ) {
    534 				if ( wcstombs( savePath, path, MAX_PATH ) > MAX_PATH ) {
    535 					savePath[0] = 0;
    536 				}
    537 				CoTaskMemFree( path );
    538 			}
    539 		}
    540 		FreeLibrary( hShell );
    541 	}
    542 
    543 	if ( savePath[0] == 0 ) {
    544 		SHGetFolderPath( NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, savePath );
    545 		strcat( savePath, "\\My Games" );
    546 	}
    547 
    548 	strcat( savePath, SAVE_PATH );
    549 
    550 	return savePath;
    551 }
    552 
    553 /*
    554 ==============
    555 Sys_EXEPath
    556 ==============
    557 */
    558 const char *Sys_EXEPath() {
    559 	static char exe[ MAX_OSPATH ];
    560 	GetModuleFileName( NULL, exe, sizeof( exe ) - 1 );
    561 	return exe;
    562 }
    563 
    564 /*
    565 ==============
    566 Sys_ListFiles
    567 ==============
    568 */
    569 int Sys_ListFiles( const char *directory, const char *extension, idStrList &list ) {
    570 	idStr		search;
    571 	struct _finddata_t findinfo;
    572 	int			findhandle;
    573 	int			flag;
    574 
    575 	if ( !extension) {
    576 		extension = "";
    577 	}
    578 
    579 	// passing a slash as extension will find directories
    580 	if ( extension[0] == '/' && extension[1] == 0 ) {
    581 		extension = "";
    582 		flag = 0;
    583 	} else {
    584 		flag = _A_SUBDIR;
    585 	}
    586 
    587 	sprintf( search, "%s\\*%s", directory, extension );
    588 
    589 	// search
    590 	list.Clear();
    591 
    592 	findhandle = _findfirst( search, &findinfo );
    593 	if ( findhandle == -1 ) {
    594 		return -1;
    595 	}
    596 
    597 	do {
    598 		if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) {
    599 			list.Append( findinfo.name );
    600 		}
    601 	} while ( _findnext( findhandle, &findinfo ) != -1 );
    602 
    603 	_findclose( findhandle );
    604 
    605 	return list.Num();
    606 }
    607 
    608 
    609 /*
    610 ================
    611 Sys_GetClipboardData
    612 ================
    613 */
    614 char *Sys_GetClipboardData() {
    615 	char *data = NULL;
    616 	char *cliptext;
    617 
    618 	if ( OpenClipboard( NULL ) != 0 ) {
    619 		HANDLE hClipboardData;
    620 
    621 		if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) {
    622 			if ( ( cliptext = (char *)GlobalLock( hClipboardData ) ) != 0 ) {
    623 				data = (char *)Mem_Alloc( GlobalSize( hClipboardData ) + 1, TAG_CRAP );
    624 				strcpy( data, cliptext );
    625 				GlobalUnlock( hClipboardData );
    626 				
    627 				strtok( data, "\n\r\b" );
    628 			}
    629 		}
    630 		CloseClipboard();
    631 	}
    632 	return data;
    633 }
    634 
    635 /*
    636 ================
    637 Sys_SetClipboardData
    638 ================
    639 */
    640 void Sys_SetClipboardData( const char *string ) {
    641 	HGLOBAL HMem;
    642 	char *PMem;
    643 
    644 	// allocate memory block
    645 	HMem = (char *)::GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, strlen( string ) + 1 );
    646 	if ( HMem == NULL ) {
    647 		return;
    648 	}
    649 	// lock allocated memory and obtain a pointer
    650 	PMem = (char *)::GlobalLock( HMem );
    651 	if ( PMem == NULL ) {
    652 		return;
    653 	}
    654 	// copy text into allocated memory block
    655 	lstrcpy( PMem, string );
    656 	// unlock allocated memory
    657 	::GlobalUnlock( HMem );
    658 	// open Clipboard
    659 	if ( !OpenClipboard( 0 ) ) {
    660 		::GlobalFree( HMem );
    661 		return;
    662 	}
    663 	// remove current Clipboard contents
    664 	EmptyClipboard();
    665 	// supply the memory handle to the Clipboard
    666 	SetClipboardData( CF_TEXT, HMem );
    667 	HMem = 0;
    668 	// close Clipboard
    669 	CloseClipboard();
    670 }
    671 
    672 /*
    673 ========================
    674 ExecOutputFn
    675 ========================
    676 */
    677 static void ExecOutputFn( const char * text ) {
    678 	idLib::Printf( text );
    679 }
    680 
    681 
    682 /*
    683 ========================
    684 Sys_Exec
    685 
    686 if waitMsec is INFINITE, completely block until the process exits
    687 If waitMsec is -1, don't wait for the process to exit
    688 Other waitMsec values will allow the workFn to be called at those intervals.
    689 ========================
    690 */
    691 bool Sys_Exec(	const char * appPath, const char * workingPath, const char * args, 
    692 	execProcessWorkFunction_t workFn, execOutputFunction_t outputFn, const int waitMS,
    693 	unsigned int & exitCode ) {
    694 		exitCode = 0;
    695 		SECURITY_ATTRIBUTES secAttr;
    696 		secAttr.nLength = sizeof( SECURITY_ATTRIBUTES );
    697 		secAttr.bInheritHandle = TRUE;
    698 		secAttr.lpSecurityDescriptor = NULL;
    699 
    700 		HANDLE hStdOutRead;
    701 		HANDLE hStdOutWrite;
    702 		CreatePipe( &hStdOutRead, &hStdOutWrite, &secAttr, 0 );
    703 		SetHandleInformation( hStdOutRead, HANDLE_FLAG_INHERIT, 0 );
    704 
    705 		HANDLE hStdInRead;
    706 		HANDLE hStdInWrite;
    707 		CreatePipe( &hStdInRead, &hStdInWrite, &secAttr, 0 );
    708 		SetHandleInformation( hStdInWrite, HANDLE_FLAG_INHERIT, 0 );										
    709 
    710 		STARTUPINFO si;
    711 		memset( &si, 0, sizeof( si ) );
    712 		si.cb = sizeof( si );
    713 		si.hStdError = hStdOutWrite;
    714 		si.hStdOutput = hStdOutWrite;
    715 		si.hStdInput = hStdInRead;
    716 		si.wShowWindow = FALSE;
    717 		si.dwFlags |= STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    718 
    719 		PROCESS_INFORMATION pi;
    720 		memset ( &pi, 0, sizeof( pi ) );
    721 
    722 		if ( outputFn != NULL ) {
    723 			outputFn( va( "^2Executing Process: ^7%s\n^2working path: ^7%s\n^2args: ^7%s\n", appPath, workingPath, args ) );
    724 		} else {
    725 			outputFn = ExecOutputFn;
    726 		}
    727 
    728 		// we duplicate args here so we can concatenate the exe name and args into a single command line
    729 		const char * imageName = appPath;
    730 		char * cmdLine = NULL;
    731 		{
    732 			// if we have any args, we need to copy them to a new buffer because CreateProcess modifies
    733 			// the command line buffer.
    734 			if ( args != NULL ) {
    735 				if ( appPath != NULL ) {
    736 					int len = idStr::Length( args ) + idStr::Length( appPath ) + 1 /* for space */ + 1 /* for NULL terminator */ + 2 /* app quotes */;
    737 					cmdLine = (char*)Mem_Alloc( len, TAG_TEMP );
    738 					// note that we're putting quotes around the appPath here because when AAS2.exe gets an app path with spaces
    739 					// in the path "w:/zion/build/win32/Debug with Inlines/AAS2.exe" it gets more than one arg for the app name,
    740 					// which it most certainly should not, so I am assuming this is a side effect of using CreateProcess.
    741 					idStr::snPrintf( cmdLine, len, "\"%s\" %s", appPath, args );
    742 				} else {
    743 					int len = idStr::Length( args ) + 1;
    744 					cmdLine = (char*)Mem_Alloc( len, TAG_TEMP );
    745 					idStr::Copynz( cmdLine, args, len );
    746 				}
    747 				// the image name should always be NULL if we have command line arguments because it is already
    748 				// prefixed to the command line.
    749 				imageName = NULL;
    750 			}
    751 		}
    752 
    753 		BOOL result = CreateProcess( imageName, (LPSTR)cmdLine, NULL, NULL, TRUE, 0, NULL, workingPath, &si, &pi );
    754 
    755 		if ( result == FALSE ) {
    756 			TCHAR szBuf[1024]; 
    757 			LPVOID lpMsgBuf;
    758 			DWORD dw = GetLastError(); 
    759 
    760 			FormatMessage(
    761 				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
    762 				FORMAT_MESSAGE_FROM_SYSTEM,
    763 				NULL,
    764 				dw,
    765 				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    766 				(LPTSTR) &lpMsgBuf,
    767 				0, NULL );
    768 
    769 			wsprintf( szBuf, "%d: %s", dw, lpMsgBuf );
    770 			if ( outputFn != NULL ) {
    771 				outputFn( szBuf );
    772 			}
    773 			LocalFree( lpMsgBuf );
    774 			if ( cmdLine != NULL ) {
    775 				Mem_Free( cmdLine );
    776 			}
    777 			return false;
    778 		} else if ( waitMS >= 0 ) {	// if waitMS == -1, don't wait for process to exit
    779 			DWORD ec = 0;
    780 			DWORD wait = 0;
    781 			char buffer[ 4096 ];
    782 			for ( ; ; ) {
    783 				wait = WaitForSingleObject( pi.hProcess, waitMS );
    784 				GetExitCodeProcess( pi.hProcess, &ec );
    785 
    786 				DWORD bytesRead = 0;
    787 				DWORD bytesAvail = 0;
    788 				DWORD bytesLeft = 0;
    789 				BOOL ok = PeekNamedPipe( hStdOutRead, NULL, 0, NULL, &bytesAvail, &bytesLeft );
    790 				if ( ok && bytesAvail != 0 ) {
    791 					ok = ReadFile( hStdOutRead, buffer, sizeof( buffer ) - 3, &bytesRead, NULL );
    792 					if ( ok && bytesRead > 0 ) {
    793 						buffer[ bytesRead ] = '\0';
    794 						if ( outputFn != NULL ) {
    795 							int length = 0;
    796 							for ( int i = 0; buffer[i] != '\0'; i++ ) {
    797 								if ( buffer[i] != '\r' ) {
    798 									buffer[length++] = buffer[i];
    799 								}
    800 							}
    801 							buffer[length++] = '\0';
    802 							outputFn( buffer );
    803 						}
    804 					}
    805 				}
    806 
    807 				if ( ec != STILL_ACTIVE ) {
    808 					exitCode = ec;
    809 					break;
    810 				}
    811 
    812 				if ( workFn != NULL ) {
    813 					if ( !workFn() ) {
    814 						TerminateProcess( pi.hProcess, 0 );
    815 						break;
    816 					}
    817 				}
    818 			}
    819 		}
    820 
    821 		// this assumes that windows duplicates the command line string into the created process's
    822 		// environment space.
    823 		if ( cmdLine != NULL ) {
    824 			Mem_Free( cmdLine );
    825 		}
    826 
    827 		CloseHandle( pi.hProcess );
    828 		CloseHandle( pi.hThread );
    829 		CloseHandle( hStdOutRead );
    830 		CloseHandle( hStdOutWrite );
    831 		CloseHandle( hStdInRead );
    832 		CloseHandle( hStdInWrite );
    833 		return true;
    834 }
    835 
    836 /*
    837 ========================================================================
    838 
    839 DLL Loading
    840 
    841 ========================================================================
    842 */
    843 
    844 /*
    845 =====================
    846 Sys_DLL_Load
    847 =====================
    848 */
    849 int Sys_DLL_Load( const char *dllName ) {
    850 	HINSTANCE libHandle = LoadLibrary( dllName );
    851 	return (int)libHandle;
    852 }
    853 
    854 /*
    855 =====================
    856 Sys_DLL_GetProcAddress
    857 =====================
    858 */
    859 void *Sys_DLL_GetProcAddress( int dllHandle, const char *procName ) {
    860 	return GetProcAddress( (HINSTANCE)dllHandle, procName ); 
    861 }
    862 
    863 /*
    864 =====================
    865 Sys_DLL_Unload
    866 =====================
    867 */
    868 void Sys_DLL_Unload( int dllHandle ) {
    869 	if ( !dllHandle ) {
    870 		return;
    871 	}
    872 	if ( FreeLibrary( (HINSTANCE)dllHandle ) == 0 ) {
    873 		int lastError = GetLastError();
    874 		LPVOID lpMsgBuf;
    875 		FormatMessage(
    876 			FORMAT_MESSAGE_ALLOCATE_BUFFER,
    877 		    NULL,
    878 			lastError,
    879 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    880 			(LPTSTR) &lpMsgBuf,
    881 			0,
    882 			NULL 
    883 		);
    884 		Sys_Error( "Sys_DLL_Unload: FreeLibrary failed - %s (%d)", lpMsgBuf, lastError );
    885 	}
    886 }
    887 
    888 /*
    889 ========================================================================
    890 
    891 EVENT LOOP
    892 
    893 ========================================================================
    894 */
    895 
    896 #define	MAX_QUED_EVENTS		256
    897 #define	MASK_QUED_EVENTS	( MAX_QUED_EVENTS - 1 )
    898 
    899 sysEvent_t	eventQue[MAX_QUED_EVENTS];
    900 int			eventHead = 0;
    901 int			eventTail = 0;
    902 
    903 /*
    904 ================
    905 Sys_QueEvent
    906 
    907 Ptr should either be null, or point to a block of data that can
    908 be freed by the game later.
    909 ================
    910 */
    911 void Sys_QueEvent( sysEventType_t type, int value, int value2, int ptrLength, void *ptr, int inputDeviceNum ) {
    912 	sysEvent_t * ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
    913 
    914 	if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
    915 		common->Printf("Sys_QueEvent: overflow\n");
    916 		// we are discarding an event, but don't leak memory
    917 		if ( ev->evPtr ) {
    918 			Mem_Free( ev->evPtr );
    919 		}
    920 		eventTail++;
    921 	}
    922 
    923 	eventHead++;
    924 
    925 	ev->evType = type;
    926 	ev->evValue = value;
    927 	ev->evValue2 = value2;
    928 	ev->evPtrLength = ptrLength;
    929 	ev->evPtr = ptr;
    930 	ev->inputDevice = inputDeviceNum;
    931 }
    932 
    933 /*
    934 =============
    935 Sys_PumpEvents
    936 
    937 This allows windows to be moved during renderbump
    938 =============
    939 */
    940 void Sys_PumpEvents() {
    941     MSG msg;
    942 
    943 	// pump the message loop
    944 	while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {
    945 		if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
    946 			common->Quit();
    947 		}
    948 
    949 		// save the msg time, because wndprocs don't have access to the timestamp
    950 		if ( win32.sysMsgTime && win32.sysMsgTime > (int)msg.time ) {
    951 			// don't ever let the event times run backwards	
    952 //			common->Printf( "Sys_PumpEvents: win32.sysMsgTime (%i) > msg.time (%i)\n", win32.sysMsgTime, msg.time );
    953 		} else {
    954 			win32.sysMsgTime = msg.time;
    955 		}
    956  
    957 		TranslateMessage (&msg);
    958       	DispatchMessage (&msg);
    959 	}
    960 }
    961 
    962 /*
    963 ================
    964 Sys_GenerateEvents
    965 ================
    966 */
    967 void Sys_GenerateEvents() {
    968 	static int entered = false;
    969 	char *s;
    970 
    971 	if ( entered ) {
    972 		return;
    973 	}
    974 	entered = true;
    975 
    976 	// pump the message loop
    977 	Sys_PumpEvents();
    978 
    979 	// grab or release the mouse cursor if necessary
    980 	IN_Frame();
    981 
    982 	// check for console commands
    983 	s = Sys_ConsoleInput();
    984 	if ( s ) {
    985 		char	*b;
    986 		int		len;
    987 
    988 		len = strlen( s ) + 1;
    989 		b = (char *)Mem_Alloc( len, TAG_EVENTS );
    990 		strcpy( b, s );
    991 		Sys_QueEvent( SE_CONSOLE, 0, 0, len, b, 0 );
    992 	}
    993 
    994 	entered = false;
    995 }
    996 
    997 /*
    998 ================
    999 Sys_ClearEvents
   1000 ================
   1001 */
   1002 void Sys_ClearEvents() {
   1003 	eventHead = eventTail = 0;
   1004 }
   1005 
   1006 /*
   1007 ================
   1008 Sys_GetEvent
   1009 ================
   1010 */
   1011 sysEvent_t Sys_GetEvent() {
   1012 	sysEvent_t	ev;
   1013 
   1014 	// return if we have data
   1015 	if ( eventHead > eventTail ) {
   1016 		eventTail++;
   1017 		return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
   1018 	}
   1019 
   1020 	// return the empty event 
   1021 	memset( &ev, 0, sizeof( ev ) );
   1022 
   1023 	return ev;
   1024 }
   1025 
   1026 //================================================================
   1027 
   1028 /*
   1029 =================
   1030 Sys_In_Restart_f
   1031 
   1032 Restart the input subsystem
   1033 =================
   1034 */
   1035 void Sys_In_Restart_f( const idCmdArgs &args ) {
   1036 	Sys_ShutdownInput();
   1037 	Sys_InitInput();
   1038 }
   1039 
   1040 /*
   1041 ================
   1042 Sys_AlreadyRunning
   1043 
   1044 returns true if there is a copy of D3 running already
   1045 ================
   1046 */
   1047 bool Sys_AlreadyRunning() {
   1048 #ifndef DEBUG
   1049 	if ( !win32.win_allowMultipleInstances.GetBool() ) {
   1050 		hProcessMutex = ::CreateMutex( NULL, FALSE, "DOOM3" );
   1051 		if ( ::GetLastError() == ERROR_ALREADY_EXISTS || ::GetLastError() == ERROR_ACCESS_DENIED ) {
   1052 			return true;
   1053 		}
   1054 	}
   1055 #endif
   1056 	return false;
   1057 }
   1058 
   1059 /*
   1060 ================
   1061 Sys_Init
   1062 
   1063 The cvar system must already be setup
   1064 ================
   1065 */
   1066 #define OSR2_BUILD_NUMBER 1111
   1067 #define WIN98_BUILD_NUMBER 1998
   1068 
   1069 void Sys_Init() {
   1070 
   1071 	CoInitialize( NULL );
   1072 
   1073 	// get WM_TIMER messages pumped every millisecond
   1074 //	SetTimer( NULL, 0, 100, NULL );
   1075 
   1076 	cmdSystem->AddCommand( "in_restart", Sys_In_Restart_f, CMD_FL_SYSTEM, "restarts the input system" );
   1077 
   1078 	//
   1079 	// Windows user name
   1080 	//
   1081 	win32.win_username.SetString( Sys_GetCurrentUser() );
   1082 
   1083 	//
   1084 	// Windows version
   1085 	//
   1086 	win32.osversion.dwOSVersionInfoSize = sizeof( win32.osversion );
   1087 
   1088 	if ( !GetVersionEx( (LPOSVERSIONINFO)&win32.osversion ) )
   1089 		Sys_Error( "Couldn't get OS info" );
   1090 
   1091 	if ( win32.osversion.dwMajorVersion < 4 ) {
   1092 		Sys_Error( GAME_NAME " requires Windows version 4 (NT) or greater" );
   1093 	}
   1094 	if ( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32s ) {
   1095 		Sys_Error( GAME_NAME " doesn't run on Win32s" );
   1096 	}
   1097 
   1098 	if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
   1099 		if( win32.osversion.dwMajorVersion <= 4 ) {
   1100 			win32.sys_arch.SetString( "WinNT (NT)" );
   1101 		} else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 0 ) {
   1102 			win32.sys_arch.SetString( "Win2K (NT)" );
   1103 		} else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 1 ) {
   1104 			win32.sys_arch.SetString( "WinXP (NT)" );
   1105 		} else if ( win32.osversion.dwMajorVersion == 6 ) {
   1106 			win32.sys_arch.SetString( "Vista" );
   1107 		} else {
   1108 			win32.sys_arch.SetString( "Unknown NT variant" );
   1109 		}
   1110 	} else if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
   1111 		if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 0 ) {
   1112 			// Win95
   1113 			if( win32.osversion.szCSDVersion[1] == 'C' ) {
   1114 				win32.sys_arch.SetString( "Win95 OSR2 (95)" );
   1115 			} else {
   1116 				win32.sys_arch.SetString( "Win95 (95)" );
   1117 			}
   1118 		} else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 10 ) {
   1119 			// Win98
   1120 			if( win32.osversion.szCSDVersion[1] == 'A' ) {
   1121 				win32.sys_arch.SetString( "Win98SE (95)" );
   1122 			} else {
   1123 				win32.sys_arch.SetString( "Win98 (95)" );
   1124 			}
   1125 		} else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 90 ) {
   1126 			// WinMe
   1127 		  	win32.sys_arch.SetString( "WinMe (95)" );
   1128 		} else {
   1129 		  	win32.sys_arch.SetString( "Unknown 95 variant" );
   1130 		}
   1131 	} else {
   1132 		win32.sys_arch.SetString( "unknown Windows variant" );
   1133 	}
   1134 
   1135 	//
   1136 	// CPU type
   1137 	//
   1138 	if ( !idStr::Icmp( win32.sys_cpustring.GetString(), "detect" ) ) {
   1139 		idStr string;
   1140 
   1141 		common->Printf( "%1.0f MHz ", Sys_ClockTicksPerSecond() / 1000000.0f );
   1142 
   1143 		win32.cpuid = Sys_GetCPUId();
   1144 
   1145 		string.Clear();
   1146 
   1147 		if ( win32.cpuid & CPUID_AMD ) {
   1148 			string += "AMD CPU";
   1149 		} else if ( win32.cpuid & CPUID_INTEL ) {
   1150 			string += "Intel CPU";
   1151 		} else if ( win32.cpuid & CPUID_UNSUPPORTED ) {
   1152 			string += "unsupported CPU";
   1153 		} else {
   1154 			string += "generic CPU";
   1155 		}
   1156 
   1157 		string += " with ";
   1158 		if ( win32.cpuid & CPUID_MMX ) {
   1159 			string += "MMX & ";
   1160 		}
   1161 		if ( win32.cpuid & CPUID_3DNOW ) {
   1162 			string += "3DNow! & ";
   1163 		}
   1164 		if ( win32.cpuid & CPUID_SSE ) {
   1165 			string += "SSE & ";
   1166 		}
   1167 		if ( win32.cpuid & CPUID_SSE2 ) {
   1168             string += "SSE2 & ";
   1169 		}
   1170 		if ( win32.cpuid & CPUID_SSE3 ) {
   1171 			string += "SSE3 & ";
   1172 		}
   1173 		if ( win32.cpuid & CPUID_HTT ) {
   1174 			string += "HTT & ";
   1175 		}
   1176 		string.StripTrailing( " & " );
   1177 		string.StripTrailing( " with " );
   1178 		win32.sys_cpustring.SetString( string );
   1179 	} else {
   1180 		common->Printf( "forcing CPU type to " );
   1181 		idLexer src( win32.sys_cpustring.GetString(), idStr::Length( win32.sys_cpustring.GetString() ), "sys_cpustring" );
   1182 		idToken token;
   1183 
   1184 		int id = CPUID_NONE;
   1185 		while( src.ReadToken( &token ) ) {
   1186 			if ( token.Icmp( "generic" ) == 0 ) {
   1187 				id |= CPUID_GENERIC;
   1188 			} else if ( token.Icmp( "intel" ) == 0 ) {
   1189 				id |= CPUID_INTEL;
   1190 			} else if ( token.Icmp( "amd" ) == 0 ) {
   1191 				id |= CPUID_AMD;
   1192 			} else if ( token.Icmp( "mmx" ) == 0 ) {
   1193 				id |= CPUID_MMX;
   1194 			} else if ( token.Icmp( "3dnow" ) == 0 ) {
   1195 				id |= CPUID_3DNOW;
   1196 			} else if ( token.Icmp( "sse" ) == 0 ) {
   1197 				id |= CPUID_SSE;
   1198 			} else if ( token.Icmp( "sse2" ) == 0 ) {
   1199 				id |= CPUID_SSE2;
   1200 			} else if ( token.Icmp( "sse3" ) == 0 ) {
   1201 				id |= CPUID_SSE3;
   1202 			} else if ( token.Icmp( "htt" ) == 0 ) {
   1203 				id |= CPUID_HTT;
   1204 			}
   1205 		}
   1206 		if ( id == CPUID_NONE ) {
   1207 			common->Printf( "WARNING: unknown sys_cpustring '%s'\n", win32.sys_cpustring.GetString() );
   1208 			id = CPUID_GENERIC;
   1209 		}
   1210 		win32.cpuid = (cpuid_t) id;
   1211 	}
   1212 
   1213 	common->Printf( "%s\n", win32.sys_cpustring.GetString() );
   1214 	common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() );
   1215 	common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
   1216 	if ( ( win32.cpuid & CPUID_SSE2 ) == 0 ) {
   1217 		common->Error( "SSE2 not supported!" );
   1218 	}
   1219 
   1220 	win32.g_Joystick.Init();
   1221 }
   1222 
   1223 /*
   1224 ================
   1225 Sys_Shutdown
   1226 ================
   1227 */
   1228 void Sys_Shutdown() {
   1229 	CoUninitialize();
   1230 }
   1231 
   1232 /*
   1233 ================
   1234 Sys_GetProcessorId
   1235 ================
   1236 */
   1237 cpuid_t Sys_GetProcessorId() {
   1238     return win32.cpuid;
   1239 }
   1240 
   1241 /*
   1242 ================
   1243 Sys_GetProcessorString
   1244 ================
   1245 */
   1246 const char *Sys_GetProcessorString() {
   1247 	return win32.sys_cpustring.GetString();
   1248 }
   1249 
   1250 //=======================================================================
   1251 
   1252 //#define SET_THREAD_AFFINITY
   1253 
   1254 
   1255 /*
   1256 ====================
   1257 Win_Frame
   1258 ====================
   1259 */
   1260 void Win_Frame() {
   1261 	// if "viewlog" has been modified, show or hide the log console
   1262 	if ( win32.win_viewlog.IsModified() ) {
   1263 		win32.win_viewlog.ClearModified();
   1264 	}
   1265 }
   1266 
   1267 extern "C" { void _chkstk( int size ); };
   1268 void clrstk();
   1269 
   1270 /*
   1271 ====================
   1272 TestChkStk
   1273 ====================
   1274 */
   1275 void TestChkStk() {
   1276 	int		buffer[0x1000];
   1277 
   1278 	buffer[0] = 1;
   1279 }
   1280 
   1281 /*
   1282 ====================
   1283 HackChkStk
   1284 ====================
   1285 */
   1286 void HackChkStk() {
   1287 	DWORD	old;
   1288 	VirtualProtect( _chkstk, 6, PAGE_EXECUTE_READWRITE, &old );
   1289 	*(byte *)_chkstk = 0xe9;
   1290 	*(int *)((int)_chkstk+1) = (int)clrstk - (int)_chkstk - 5;
   1291 
   1292 	TestChkStk();
   1293 }
   1294 
   1295 /*
   1296 ====================
   1297 GetExceptionCodeInfo
   1298 ====================
   1299 */
   1300 const char *GetExceptionCodeInfo( UINT code ) {
   1301 	switch( code ) {
   1302 		case EXCEPTION_ACCESS_VIOLATION: return "The thread tried to read from or write to a virtual address for which it does not have the appropriate access.";
   1303 		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.";
   1304 		case EXCEPTION_BREAKPOINT: return "A breakpoint was encountered.";
   1305 		case EXCEPTION_DATATYPE_MISALIGNMENT: return "The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.";
   1306 		case EXCEPTION_FLT_DENORMAL_OPERAND: return "One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.";
   1307 		case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "The thread tried to divide a floating-point value by a floating-point divisor of zero.";
   1308 		case EXCEPTION_FLT_INEXACT_RESULT: return "The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
   1309 		case EXCEPTION_FLT_INVALID_OPERATION: return "This exception represents any floating-point exception not included in this list.";
   1310 		case EXCEPTION_FLT_OVERFLOW: return "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
   1311 		case EXCEPTION_FLT_STACK_CHECK: return "The stack overflowed or underflowed as the result of a floating-point operation.";
   1312 		case EXCEPTION_FLT_UNDERFLOW: return "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
   1313 		case EXCEPTION_ILLEGAL_INSTRUCTION: return "The thread tried to execute an invalid instruction.";
   1314 		case EXCEPTION_IN_PAGE_ERROR: return "The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.";
   1315 		case EXCEPTION_INT_DIVIDE_BY_ZERO: return "The thread tried to divide an integer value by an integer divisor of zero.";
   1316 		case EXCEPTION_INT_OVERFLOW: return "The result of an integer operation caused a carry out of the most significant bit of the result.";
   1317 		case EXCEPTION_INVALID_DISPOSITION: return "An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.";
   1318 		case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "The thread tried to continue execution after a noncontinuable exception occurred.";
   1319 		case EXCEPTION_PRIV_INSTRUCTION: return "The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
   1320 		case EXCEPTION_SINGLE_STEP: return "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
   1321 		case EXCEPTION_STACK_OVERFLOW: return "The thread used up its stack.";
   1322 		default: return "Unknown exception";
   1323 	}
   1324 }
   1325 
   1326 /*
   1327 ====================
   1328 EmailCrashReport
   1329 
   1330   emailer originally from Raven/Quake 4
   1331 ====================
   1332 */
   1333 void EmailCrashReport( LPSTR messageText ) {
   1334 	static int lastEmailTime = 0;
   1335 
   1336 	if ( Sys_Milliseconds() < lastEmailTime + 10000 ) {
   1337 		return;
   1338 	}
   1339 
   1340 	lastEmailTime = Sys_Milliseconds();
   1341 
   1342 	HINSTANCE mapi = LoadLibrary( "MAPI32.DLL" ); 
   1343 	if( mapi ) {
   1344 		LPMAPISENDMAIL	MAPISendMail = ( LPMAPISENDMAIL )GetProcAddress( mapi, "MAPISendMail" );
   1345 		if( MAPISendMail ) {
   1346 			MapiRecipDesc toProgrammers =
   1347 			{
   1348 				0,										// ulReserved
   1349 					MAPI_TO,							// ulRecipClass
   1350 					"DOOM 3 Crash",						// lpszName
   1351 					"SMTP:programmers@idsoftware.com",	// lpszAddress
   1352 					0,									// ulEIDSize
   1353 					0									// lpEntry
   1354 			};
   1355 
   1356 			MapiMessage		message = {};
   1357 			message.lpszSubject = "DOOM 3 Fatal Error";
   1358 			message.lpszNoteText = messageText;
   1359 			message.nRecipCount = 1;
   1360 			message.lpRecips = &toProgrammers;
   1361 
   1362 			MAPISendMail(
   1363 				0,									// LHANDLE lhSession
   1364 				0,									// ULONG ulUIParam
   1365 				&message,							// lpMapiMessage lpMessage
   1366 				MAPI_DIALOG,						// FLAGS flFlags
   1367 				0									// ULONG ulReserved
   1368 				);
   1369 		}
   1370 		FreeLibrary( mapi );
   1371 	}
   1372 }
   1373 
   1374 int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse );
   1375 
   1376 /*
   1377 ====================
   1378 _except_handler
   1379 ====================
   1380 */
   1381 EXCEPTION_DISPOSITION __cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame,
   1382 												struct _CONTEXT *ContextRecord, void * DispatcherContext ) {
   1383 
   1384 	static char msg[ 8192 ];
   1385 	char FPUFlags[2048];
   1386 
   1387 	Sys_FPU_PrintStateFlags( FPUFlags, ContextRecord->FloatSave.ControlWord,
   1388 										ContextRecord->FloatSave.StatusWord,
   1389 										ContextRecord->FloatSave.TagWord,
   1390 										ContextRecord->FloatSave.ErrorOffset,
   1391 										ContextRecord->FloatSave.ErrorSelector,
   1392 										ContextRecord->FloatSave.DataOffset,
   1393 										ContextRecord->FloatSave.DataSelector );
   1394 
   1395 
   1396 	sprintf( msg, 
   1397 		"Please describe what you were doing when DOOM 3 crashed!\n"
   1398 		"If this text did not pop into your email client please copy and email it to programmers@idsoftware.com\n"
   1399 			"\n"
   1400 			"-= FATAL EXCEPTION =-\n"
   1401 			"\n"
   1402 			"%s\n"
   1403 			"\n"
   1404 			"0x%x at address 0x%08p\n"
   1405 			"\n"
   1406 			"%s\n"
   1407 			"\n"
   1408 			"EAX = 0x%08x EBX = 0x%08x\n"
   1409 			"ECX = 0x%08x EDX = 0x%08x\n"
   1410 			"ESI = 0x%08x EDI = 0x%08x\n"
   1411 			"EIP = 0x%08x ESP = 0x%08x\n"
   1412 			"EBP = 0x%08x EFL = 0x%08x\n"
   1413 			"\n"
   1414 			"CS = 0x%04x\n"
   1415 			"SS = 0x%04x\n"
   1416 			"DS = 0x%04x\n"
   1417 			"ES = 0x%04x\n"
   1418 			"FS = 0x%04x\n"
   1419 			"GS = 0x%04x\n"
   1420 			"\n"
   1421 			"%s\n",
   1422 			com_version.GetString(),
   1423 			ExceptionRecord->ExceptionCode,
   1424 			ExceptionRecord->ExceptionAddress,
   1425 			GetExceptionCodeInfo( ExceptionRecord->ExceptionCode ),
   1426 			ContextRecord->Eax, ContextRecord->Ebx,
   1427 			ContextRecord->Ecx, ContextRecord->Edx,
   1428 			ContextRecord->Esi, ContextRecord->Edi,
   1429 			ContextRecord->Eip, ContextRecord->Esp,
   1430 			ContextRecord->Ebp, ContextRecord->EFlags,
   1431 			ContextRecord->SegCs,
   1432 			ContextRecord->SegSs,
   1433 			ContextRecord->SegDs,
   1434 			ContextRecord->SegEs,
   1435 			ContextRecord->SegFs,
   1436 			ContextRecord->SegGs,
   1437 			FPUFlags
   1438 		);
   1439 
   1440 	EmailCrashReport( msg );
   1441 	common->FatalError( msg );
   1442 
   1443     // Tell the OS to restart the faulting instruction
   1444     return ExceptionContinueExecution;
   1445 }
   1446 
   1447 #define TEST_FPU_EXCEPTIONS	/*	FPU_EXCEPTION_INVALID_OPERATION |		*/	\
   1448 							/*	FPU_EXCEPTION_DENORMALIZED_OPERAND |	*/	\
   1449 							/*	FPU_EXCEPTION_DIVIDE_BY_ZERO |			*/	\
   1450 							/*	FPU_EXCEPTION_NUMERIC_OVERFLOW |		*/	\
   1451 							/*	FPU_EXCEPTION_NUMERIC_UNDERFLOW |		*/	\
   1452 							/*	FPU_EXCEPTION_INEXACT_RESULT |			*/	\
   1453 								0
   1454 
   1455 /*
   1456 ==================
   1457 WinMain
   1458 ==================
   1459 */
   1460 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
   1461 
   1462 	const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) );
   1463 
   1464 	Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 );
   1465 
   1466 	Sys_GetCurrentMemoryStatus( exeLaunchMemoryStats );
   1467 
   1468 #if 0
   1469     DWORD handler = (DWORD)_except_handler;
   1470     __asm
   1471     {                           // Build EXCEPTION_REGISTRATION record:
   1472         push    handler         // Address of handler function
   1473         push    FS:[0]          // Address of previous handler
   1474         mov     FS:[0],ESP      // Install new EXECEPTION_REGISTRATION
   1475     }
   1476 #endif
   1477 
   1478 	win32.hInstance = hInstance;
   1479 	idStr::Copynz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
   1480 
   1481 	// done before Com/Sys_Init since we need this for error output
   1482 	Sys_CreateConsole();
   1483 
   1484 	// no abort/retry/fail errors
   1485 	SetErrorMode( SEM_FAILCRITICALERRORS );
   1486 
   1487 	for ( int i = 0; i < MAX_CRITICAL_SECTIONS; i++ ) {
   1488 		InitializeCriticalSection( &win32.criticalSections[i] );
   1489 	}
   1490 
   1491 	// make sure the timer is high precision, otherwise
   1492 	// NT gets 18ms resolution
   1493 	timeBeginPeriod( 1 );
   1494 
   1495 	// get the initial time base
   1496 	Sys_Milliseconds();
   1497 
   1498 #ifdef DEBUG
   1499 	// disable the painfully slow MS heap check every 1024 allocs
   1500 	_CrtSetDbgFlag( 0 );
   1501 #endif
   1502 
   1503 //	Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
   1504 	Sys_FPU_SetPrecision( FPU_PRECISION_DOUBLE_EXTENDED );
   1505 
   1506 	common->Init( 0, NULL, lpCmdLine );
   1507 
   1508 #if TEST_FPU_EXCEPTIONS != 0
   1509 	common->Printf( Sys_FPU_GetState() );
   1510 #endif
   1511 
   1512 	if ( win32.win_notaskkeys.GetInteger() ) {
   1513 		DisableTaskKeys( TRUE, FALSE, /*( win32.win_notaskkeys.GetInteger() == 2 )*/ FALSE );
   1514 	}
   1515 
   1516 	// hide or show the early console as necessary
   1517 	if ( win32.win_viewlog.GetInteger() ) {
   1518 		Sys_ShowConsole( 1, true );
   1519 	} else {
   1520 		Sys_ShowConsole( 0, false );
   1521 	}
   1522 
   1523 #ifdef SET_THREAD_AFFINITY 
   1524 	// give the main thread an affinity for the first cpu
   1525 	SetThreadAffinityMask( GetCurrentThread(), 1 );
   1526 #endif
   1527 
   1528 	::SetCursor( hcurSave );
   1529 
   1530 	::SetFocus( win32.hWnd );
   1531 
   1532     // main game loop
   1533 	while( 1 ) {
   1534 
   1535 		Win_Frame();
   1536 
   1537 #ifdef DEBUG
   1538 		Sys_MemFrame();
   1539 #endif
   1540 
   1541 		// set exceptions, even if some crappy syscall changes them!
   1542 		Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
   1543 
   1544 		// run the game
   1545 		common->Frame();
   1546 	}
   1547 
   1548 	// never gets here
   1549 	return 0;
   1550 }
   1551 
   1552 /*
   1553 ====================
   1554 clrstk
   1555 
   1556 I tried to get the run time to call this at every function entry, but
   1557 ====================
   1558 */
   1559 static int	parmBytes;
   1560 __declspec( naked ) void clrstk() {
   1561 	// eax = bytes to add to stack
   1562 	__asm {
   1563 		mov		[parmBytes],eax
   1564         neg     eax                     ; compute new stack pointer in eax
   1565         add     eax,esp
   1566         add     eax,4
   1567         xchg    eax,esp
   1568         mov     eax,dword ptr [eax]		; copy the return address
   1569         push    eax
   1570         
   1571         ; clear to zero
   1572         push	edi
   1573         push	ecx
   1574         mov		edi,esp
   1575         add		edi,12
   1576         mov		ecx,[parmBytes]
   1577 		shr		ecx,2
   1578         xor		eax,eax
   1579 		cld
   1580         rep	stosd
   1581         pop		ecx
   1582         pop		edi
   1583         
   1584         ret
   1585 	}
   1586 }
   1587 
   1588 /*
   1589 ==================
   1590 idSysLocal::OpenURL
   1591 ==================
   1592 */
   1593 void idSysLocal::OpenURL( const char *url, bool doexit ) {
   1594 	static bool doexit_spamguard = false;
   1595 	HWND wnd;
   1596 
   1597 	if (doexit_spamguard) {
   1598 		common->DPrintf( "OpenURL: already in an exit sequence, ignoring %s\n", url );
   1599 		return;
   1600 	}
   1601 
   1602 	common->Printf("Open URL: %s\n", url);
   1603 
   1604 	if ( !ShellExecute( NULL, "open", url, NULL, NULL, SW_RESTORE ) ) {
   1605 		common->Error( "Could not open url: '%s' ", url );
   1606 		return;
   1607 	}
   1608 
   1609 	wnd = GetForegroundWindow();
   1610 	if ( wnd ) {
   1611 		ShowWindow( wnd, SW_MAXIMIZE );
   1612 	}
   1613 
   1614 	if ( doexit ) {
   1615 		doexit_spamguard = true;
   1616 		cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
   1617 	}
   1618 }
   1619 
   1620 /*
   1621 ==================
   1622 idSysLocal::StartProcess
   1623 ==================
   1624 */
   1625 void idSysLocal::StartProcess( const char *exePath, bool doexit ) {
   1626 	TCHAR				szPathOrig[_MAX_PATH];
   1627 	STARTUPINFO			si;
   1628 	PROCESS_INFORMATION	pi;
   1629 
   1630 	ZeroMemory( &si, sizeof(si) );
   1631 	si.cb = sizeof(si);
   1632 
   1633 	strncpy( szPathOrig, exePath, _MAX_PATH );
   1634 
   1635 	if( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
   1636         common->Error( "Could not start process: '%s' ", szPathOrig );
   1637 	    return;
   1638 	}
   1639 
   1640 	if ( doexit ) {
   1641 		cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
   1642 	}
   1643 }
   1644 
   1645 /*
   1646 ==================
   1647 Sys_SetFatalError
   1648 ==================
   1649 */
   1650 void Sys_SetFatalError( const char *error ) {
   1651 }
   1652 
   1653 /*
   1654 ================
   1655 Sys_SetLanguageFromSystem
   1656 ================
   1657 */
   1658 extern idCVar sys_lang;
   1659 void Sys_SetLanguageFromSystem() {
   1660 	sys_lang.SetString( Sys_DefaultLanguage() );
   1661 }