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 }