2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
42 #include <sys/types.h>
46 #include "../sys_local.h"
47 #include "win_local.h"
48 #include "rc/CreateResourceIDs.h"
49 #include "../../renderer/tr_local.h"
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_xpos( "win_xpos", "3", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "horizontal position of window" );
58 idCVar Win32Vars_t::win_ypos( "win_ypos", "22", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "vertical position of window" );
59 idCVar Win32Vars_t::win_outputDebugString( "win_outputDebugString", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
60 idCVar Win32Vars_t::win_outputEditString( "win_outputEditString", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
61 idCVar Win32Vars_t::win_viewlog( "win_viewlog", "0", CVAR_SYSTEM | CVAR_INTEGER, "" );
62 idCVar Win32Vars_t::win_timerUpdate( "win_timerUpdate", "0", CVAR_SYSTEM | CVAR_BOOL, "allows the game to be updated while dragging the window" );
63 idCVar Win32Vars_t::win_allowMultipleInstances( "win_allowMultipleInstances", "0", CVAR_SYSTEM | CVAR_BOOL, "allow multiple instances running concurrently" );
67 static char sys_cmdline[MAX_STRING_CHARS];
69 // not a hard limit, just what we keep track of for debugging
70 xthreadInfo *g_threads[MAX_THREADS];
72 int g_thread_count = 0;
74 static sysMemoryStats_t exeLaunchMemoryStats;
76 static xthreadInfo threadInfo;
81 Sys_GetExeLaunchMemoryStatus
84 void Sys_GetExeLaunchMemoryStatus( sysMemoryStats_t &stats ) {
85 stats = exeLaunchMemoryStats;
93 void Sys_CreateThread( xthread_t function, void *parms, xthreadPriority priority, xthreadInfo &info, const char *name, xthreadInfo *threads[MAX_THREADS], int *thread_count ) {
94 HANDLE temp = CreateThread( NULL, // LPSECURITY_ATTRIBUTES lpsa,
96 (LPTHREAD_START_ROUTINE)function, // LPTHREAD_START_ROUTINE lpStartAddr,
97 parms, // LPVOID lpvThreadParm,
98 0, // DWORD fdwCreate,
100 info.threadHandle = (int) temp;
101 if (priority == THREAD_HIGHEST) {
102 SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_HIGHEST ); // we better sleep enough to do this
103 } else if (priority == THREAD_ABOVE_NORMAL ) {
104 SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_ABOVE_NORMAL );
107 if ( *thread_count < MAX_THREADS ) {
108 threads[(*thread_count)++] = &info;
110 common->DPrintf("WARNING: MAX_THREADS reached\n");
119 void Sys_DestroyThread( xthreadInfo& info ) {
120 WaitForSingleObject( (HANDLE)info.threadHandle, INFINITE);
121 CloseHandle( (HANDLE)info.threadHandle );
122 info.threadHandle = 0;
139 const char* Sys_GetThreadName(int *index) {
140 int id = GetCurrentThreadId();
141 for( int i = 0; i < g_thread_count; i++ ) {
142 if ( id == g_threads[i]->threadId ) {
146 return g_threads[i]->name;
158 Sys_EnterCriticalSection
161 void Sys_EnterCriticalSection( int index ) {
162 assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
163 if ( TryEnterCriticalSection( &win32.criticalSections[index] ) == 0 ) {
164 EnterCriticalSection( &win32.criticalSections[index] );
165 // Sys_DebugPrintf( "busy lock '%s' in thread '%s'\n", lock->name, Sys_GetThreadName() );
171 Sys_LeaveCriticalSection
174 void Sys_LeaveCriticalSection( int index ) {
175 assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
176 LeaveCriticalSection( &win32.criticalSections[index] );
184 void Sys_WaitForEvent( int index ) {
185 assert( index == 0 );
186 if ( !win32.backgroundDownloadSemaphore ) {
187 win32.backgroundDownloadSemaphore = CreateEvent( NULL, TRUE, FALSE, NULL );
189 WaitForSingleObject( win32.backgroundDownloadSemaphore, INFINITE );
190 ResetEvent( win32.backgroundDownloadSemaphore );
198 void Sys_TriggerEvent( int index ) {
199 assert( index == 0 );
200 SetEvent( win32.backgroundDownloadSemaphore );
205 #pragma optimize( "", on )
210 static unsigned int debug_total_alloc = 0;
211 static unsigned int debug_total_alloc_count = 0;
212 static unsigned int debug_current_alloc = 0;
213 static unsigned int debug_current_alloc_count = 0;
214 static unsigned int debug_frame_alloc = 0;
215 static unsigned int debug_frame_alloc_count = 0;
217 idCVar sys_showMallocs( "sys_showMallocs", "0", CVAR_SYSTEM, "" );
219 // _HOOK_ALLOC, _HOOK_REALLOC, _HOOK_FREE
221 typedef struct CrtMemBlockHeader
223 struct _CrtMemBlockHeader *pBlockHeaderNext; // Pointer to the block allocated just before this one:
224 struct _CrtMemBlockHeader *pBlockHeaderPrev; // Pointer to the block allocated just after this one
225 char *szFileName; // File name
226 int nLine; // Line number
227 size_t nDataSize; // Size of user block
228 int nBlockUse; // Type of block
229 long lRequest; // Allocation number
230 byte gap[4]; // Buffer just before (lower than) the user's memory:
239 called for every malloc/new/free/delete
242 int Sys_AllocHook( int nAllocType, void *pvData, size_t nSize, int nBlockUse, long lRequest, const unsigned char * szFileName, int nLine )
244 CrtMemBlockHeader *pHead;
247 if ( nBlockUse == _CRT_BLOCK )
252 // get a pointer to memory block header
253 temp = ( byte * )pvData;
255 pHead = ( CrtMemBlockHeader * )temp;
257 switch( nAllocType ) {
259 debug_total_alloc += nSize;
260 debug_current_alloc += nSize;
261 debug_frame_alloc += nSize;
262 debug_total_alloc_count++;
263 debug_current_alloc_count++;
264 debug_frame_alloc_count++;
268 assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
270 debug_current_alloc -= pHead->nDataSize;
271 debug_current_alloc_count--;
272 debug_total_alloc_count++;
273 debug_frame_alloc_count++;
277 assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
279 debug_current_alloc -= pHead->nDataSize;
280 debug_total_alloc += nSize;
281 debug_current_alloc += nSize;
282 debug_frame_alloc += nSize;
283 debug_total_alloc_count++;
284 debug_current_alloc_count--;
285 debug_frame_alloc_count++;
296 void Sys_DebugMemory_f( void ) {
297 common->Printf( "Total allocation %8dk in %d blocks\n", debug_total_alloc / 1024, debug_total_alloc_count );
298 common->Printf( "Current allocation %8dk in %d blocks\n", debug_current_alloc / 1024, debug_current_alloc_count );
306 void Sys_MemFrame( void ) {
307 if( sys_showMallocs.GetInteger() ) {
308 common->Printf("Frame: %8dk in %5d blocks\n", debug_frame_alloc / 1024, debug_frame_alloc_count );
311 debug_frame_alloc = 0;
312 debug_frame_alloc_count = 0;
321 On windows, the vertex buffers are write combined, so they
322 don't need to be flushed from the cache
325 void Sys_FlushCacheMemory( void *base, int bytes ) {
332 Show the early console as an error dialog
335 void Sys_Error( const char *error, ... ) {
340 va_start( argptr, error );
341 vsprintf( text, error, argptr );
344 Conbuf_AppendText( text );
345 Conbuf_AppendText( "\n" );
347 Win_SetErrorText( text );
348 Sys_ShowConsole( 1, true );
356 // wait for the user to quit
358 if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
361 TranslateMessage( &msg );
362 DispatchMessage( &msg );
365 Sys_DestroyConsole();
375 void Sys_Quit( void ) {
378 Sys_DestroyConsole();
388 #define MAXPRINTMSG 4096
389 void Sys_Printf( const char *fmt, ... ) {
390 char msg[MAXPRINTMSG];
393 va_start(argptr, fmt);
394 idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
396 msg[sizeof(msg)-1] = '\0';
398 if ( win32.win_outputDebugString.GetBool() ) {
399 OutputDebugString( msg );
401 if ( win32.win_outputEditString.GetBool() ) {
402 Conbuf_AppendText( msg );
411 #define MAXPRINTMSG 4096
412 void Sys_DebugPrintf( const char *fmt, ... ) {
413 char msg[MAXPRINTMSG];
416 va_start( argptr, fmt );
417 idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
418 msg[ sizeof(msg)-1 ] = '\0';
421 OutputDebugString( msg );
429 void Sys_DebugVPrintf( const char *fmt, va_list arg ) {
430 char msg[MAXPRINTMSG];
432 idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, arg );
433 msg[ sizeof(msg)-1 ] = '\0';
435 OutputDebugString( msg );
443 void Sys_Sleep( int msec ) {
452 void Sys_ShowWindow( bool show ) {
453 ::ShowWindow( win32.hWnd, show ? SW_SHOW : SW_HIDE );
461 bool Sys_IsWindowVisible( void ) {
462 return ( ::IsWindowVisible( win32.hWnd ) != 0 );
470 void Sys_Mkdir( const char *path ) {
479 ID_TIME_T Sys_FileTimeStamp( FILE *fp ) {
481 _fstat( _fileno( fp ), &st );
482 return (long) st.st_mtime;
490 const char *Sys_Cwd( void ) {
491 static char cwd[MAX_OSPATH];
493 _getcwd( cwd, sizeof( cwd ) - 1 );
494 cwd[MAX_OSPATH-1] = 0;
504 const char *Sys_DefaultCDPath( void ) {
513 const char *Sys_DefaultBasePath( void ) {
522 const char *Sys_DefaultSavePath( void ) {
523 return cvarSystem->GetCVarString( "fs_basepath" );
531 const char *Sys_EXEPath( void ) {
532 static char exe[ MAX_OSPATH ];
533 GetModuleFileName( NULL, exe, sizeof( exe ) - 1 );
542 int Sys_ListFiles( const char *directory, const char *extension, idStrList &list ) {
544 struct _finddata_t findinfo;
552 // passing a slash as extension will find directories
553 if ( extension[0] == '/' && extension[1] == 0 ) {
560 sprintf( search, "%s\\*%s", directory, extension );
565 findhandle = _findfirst( search, &findinfo );
566 if ( findhandle == -1 ) {
571 if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) {
572 list.Append( findinfo.name );
574 } while ( _findnext( findhandle, &findinfo ) != -1 );
576 _findclose( findhandle );
587 char *Sys_GetClipboardData( void ) {
591 if ( OpenClipboard( NULL ) != 0 ) {
592 HANDLE hClipboardData;
594 if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) {
595 if ( ( cliptext = (char *)GlobalLock( hClipboardData ) ) != 0 ) {
596 data = (char *)Mem_Alloc( GlobalSize( hClipboardData ) + 1 );
597 strcpy( data, cliptext );
598 GlobalUnlock( hClipboardData );
600 strtok( data, "\n\r\b" );
613 void Sys_SetClipboardData( const char *string ) {
617 // allocate memory block
618 HMem = (char *)::GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, strlen( string ) + 1 );
619 if ( HMem == NULL ) {
622 // lock allocated memory and obtain a pointer
623 PMem = (char *)::GlobalLock( HMem );
624 if ( PMem == NULL ) {
627 // copy text into allocated memory block
628 lstrcpy( PMem, string );
629 // unlock allocated memory
630 ::GlobalUnlock( HMem );
632 if ( !OpenClipboard( 0 ) ) {
633 ::GlobalFree( HMem );
636 // remove current Clipboard contents
638 // supply the memory handle to the Clipboard
639 SetClipboardData( CF_TEXT, HMem );
646 ========================================================================
650 ========================================================================
654 =====================
656 =====================
658 int Sys_DLL_Load( const char *dllName ) {
660 libHandle = LoadLibrary( dllName );
662 // since we can't have LoadLibrary load only from the specified path, check it did the right thing
663 char loadedPath[ MAX_OSPATH ];
664 GetModuleFileName( libHandle, loadedPath, sizeof( loadedPath ) - 1 );
665 if ( idStr::IcmpPath( dllName, loadedPath ) ) {
666 Sys_Printf( "ERROR: LoadLibrary '%s' wants to load '%s'\n", dllName, loadedPath );
667 Sys_DLL_Unload( (int)libHandle );
671 return (int)libHandle;
675 =====================
676 Sys_DLL_GetProcAddress
677 =====================
679 void *Sys_DLL_GetProcAddress( int dllHandle, const char *procName ) {
680 return GetProcAddress( (HINSTANCE)dllHandle, procName );
684 =====================
686 =====================
688 void Sys_DLL_Unload( int dllHandle ) {
692 if ( FreeLibrary( (HINSTANCE)dllHandle ) == 0 ) {
693 int lastError = GetLastError();
696 FORMAT_MESSAGE_ALLOCATE_BUFFER,
699 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
704 Sys_Error( "Sys_DLL_Unload: FreeLibrary failed - %s (%d)", lpMsgBuf, lastError );
709 ========================================================================
713 ========================================================================
716 #define MAX_QUED_EVENTS 256
717 #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 )
719 sysEvent_t eventQue[MAX_QUED_EVENTS];
727 Ptr should either be null, or point to a block of data that can
728 be freed by the game later.
731 void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
734 ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
736 if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
737 common->Printf("Sys_QueEvent: overflow\n");
738 // we are discarding an event, but don't leak memory
740 Mem_Free( ev->evPtr );
749 ev->evValue2 = value2;
750 ev->evPtrLength = ptrLength;
758 This allows windows to be moved during renderbump
761 void Sys_PumpEvents( void ) {
764 // pump the message loop
765 while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {
766 if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
770 // save the msg time, because wndprocs don't have access to the timestamp
771 if ( win32.sysMsgTime && win32.sysMsgTime > (int)msg.time ) {
772 // don't ever let the event times run backwards
773 // common->Printf( "Sys_PumpEvents: win32.sysMsgTime (%i) > msg.time (%i)\n", win32.sysMsgTime, msg.time );
775 win32.sysMsgTime = msg.time;
778 #ifdef ID_ALLOW_TOOLS
779 if ( GUIEditorHandleMessage ( &msg ) ) {
784 TranslateMessage (&msg);
785 DispatchMessage (&msg);
794 void Sys_GenerateEvents( void ) {
795 static int entered = false;
803 // pump the message loop
806 // make sure mouse and joystick are only called once a frame
809 // check for console commands
810 s = Sys_ConsoleInput();
815 len = strlen( s ) + 1;
816 b = (char *)Mem_Alloc( len );
818 Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
829 void Sys_ClearEvents( void ) {
830 eventHead = eventTail = 0;
838 sysEvent_t Sys_GetEvent( void ) {
841 // return if we have data
842 if ( eventHead > eventTail ) {
844 return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
847 // return the empty event
848 memset( &ev, 0, sizeof( ev ) );
853 //================================================================
859 Restart the input subsystem
862 void Sys_In_Restart_f( const idCmdArgs &args ) {
873 static void Sys_AsyncThread( void *parm ) {
877 startTime = Sys_Milliseconds();
882 // this will trigger 60 times a second
883 int r = WaitForSingleObject( hTimer, 100 );
884 if ( r != WAIT_OBJECT_0 ) {
885 OutputDebugString( "idPacketServer::PacketServerInterrupt: bad wait return" );
891 int msec = Sys_Milliseconds();
892 int deltaTime = msec - startTime;
896 sprintf( str, "%i ", deltaTime );
897 OutputDebugString( str );
909 Start the thread that will call idCommon::Async()
912 void Sys_StartAsyncThread( void ) {
913 // create an auto-reset event that happens 60 times a second
914 hTimer = CreateWaitableTimer( NULL, false, NULL );
916 common->Error( "idPacketServer::Spawn: CreateWaitableTimer failed" );
920 t.HighPart = t.LowPart = 0;
921 SetWaitableTimer( hTimer, &t, USERCMD_MSEC, NULL, NULL, TRUE );
923 Sys_CreateThread( (xthread_t)Sys_AsyncThread, NULL, THREAD_ABOVE_NORMAL, threadInfo, "Async", g_threads, &g_thread_count );
925 #ifdef SET_THREAD_AFFINITY
926 // give the async thread an affinity for the second cpu
927 SetThreadAffinityMask( (HANDLE)threadInfo.threadHandle, 2 );
930 if ( !threadInfo.threadHandle ) {
931 common->Error( "Sys_StartAsyncThread: failed" );
939 returns true if there is a copy of D3 running already
942 bool Sys_AlreadyRunning( void ) {
944 if ( !win32.win_allowMultipleInstances.GetBool() ) {
945 HANDLE hMutexOneInstance = ::CreateMutex( NULL, FALSE, "DOOM3" );
946 if ( ::GetLastError() == ERROR_ALREADY_EXISTS || ::GetLastError() == ERROR_ACCESS_DENIED ) {
958 The cvar system must already be setup
961 #define OSR2_BUILD_NUMBER 1111
962 #define WIN98_BUILD_NUMBER 1998
964 void Sys_Init( void ) {
966 CoInitialize( NULL );
968 // make sure the timer is high precision, otherwise
969 // NT gets 18ms resolution
970 timeBeginPeriod( 1 );
972 // get WM_TIMER messages pumped every millisecond
973 // SetTimer( NULL, 0, 100, NULL );
975 cmdSystem->AddCommand( "in_restart", Sys_In_Restart_f, CMD_FL_SYSTEM, "restarts the input system" );
977 cmdSystem->AddCommand( "createResourceIDs", CreateResourceIDs_f, CMD_FL_TOOL, "assigns resource IDs in _resouce.h files" );
980 cmdSystem->AddCommand( "setAsyncSound", Sys_SetAsyncSound_f, CMD_FL_SYSTEM, "set the async sound option" );
986 win32.win_username.SetString( Sys_GetCurrentUser() );
991 win32.osversion.dwOSVersionInfoSize = sizeof( win32.osversion );
993 if ( !GetVersionEx( (LPOSVERSIONINFO)&win32.osversion ) )
994 Sys_Error( "Couldn't get OS info" );
996 if ( win32.osversion.dwMajorVersion < 4 ) {
997 Sys_Error( GAME_NAME " requires Windows version 4 (NT) or greater" );
999 if ( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32s ) {
1000 Sys_Error( GAME_NAME " doesn't run on Win32s" );
1003 if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
1004 if( win32.osversion.dwMajorVersion <= 4 ) {
1005 win32.sys_arch.SetString( "WinNT (NT)" );
1006 } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 0 ) {
1007 win32.sys_arch.SetString( "Win2K (NT)" );
1008 } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 1 ) {
1009 win32.sys_arch.SetString( "WinXP (NT)" );
1010 } else if ( win32.osversion.dwMajorVersion == 6 ) {
1011 win32.sys_arch.SetString( "Vista" );
1013 win32.sys_arch.SetString( "Unknown NT variant" );
1015 } else if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
1016 if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 0 ) {
1018 if( win32.osversion.szCSDVersion[1] == 'C' ) {
1019 win32.sys_arch.SetString( "Win95 OSR2 (95)" );
1021 win32.sys_arch.SetString( "Win95 (95)" );
1023 } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 10 ) {
1025 if( win32.osversion.szCSDVersion[1] == 'A' ) {
1026 win32.sys_arch.SetString( "Win98SE (95)" );
1028 win32.sys_arch.SetString( "Win98 (95)" );
1030 } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 90 ) {
1032 win32.sys_arch.SetString( "WinMe (95)" );
1034 win32.sys_arch.SetString( "Unknown 95 variant" );
1037 win32.sys_arch.SetString( "unknown Windows variant" );
1043 if ( !idStr::Icmp( win32.sys_cpustring.GetString(), "detect" ) ) {
1046 common->Printf( "%1.0f MHz ", Sys_ClockTicksPerSecond() / 1000000.0f );
1048 win32.cpuid = Sys_GetCPUId();
1052 if ( win32.cpuid & CPUID_AMD ) {
1053 string += "AMD CPU";
1054 } else if ( win32.cpuid & CPUID_INTEL ) {
1055 string += "Intel CPU";
1056 } else if ( win32.cpuid & CPUID_UNSUPPORTED ) {
1057 string += "unsupported CPU";
1059 string += "generic CPU";
1063 if ( win32.cpuid & CPUID_MMX ) {
1066 if ( win32.cpuid & CPUID_3DNOW ) {
1067 string += "3DNow! & ";
1069 if ( win32.cpuid & CPUID_SSE ) {
1072 if ( win32.cpuid & CPUID_SSE2 ) {
1073 string += "SSE2 & ";
1075 if ( win32.cpuid & CPUID_SSE3 ) {
1076 string += "SSE3 & ";
1078 if ( win32.cpuid & CPUID_HTT ) {
1081 string.StripTrailing( " & " );
1082 string.StripTrailing( " with " );
1083 win32.sys_cpustring.SetString( string );
1085 common->Printf( "forcing CPU type to " );
1086 idLexer src( win32.sys_cpustring.GetString(), idStr::Length( win32.sys_cpustring.GetString() ), "sys_cpustring" );
1089 int id = CPUID_NONE;
1090 while( src.ReadToken( &token ) ) {
1091 if ( token.Icmp( "generic" ) == 0 ) {
1092 id |= CPUID_GENERIC;
1093 } else if ( token.Icmp( "intel" ) == 0 ) {
1095 } else if ( token.Icmp( "amd" ) == 0 ) {
1097 } else if ( token.Icmp( "mmx" ) == 0 ) {
1099 } else if ( token.Icmp( "3dnow" ) == 0 ) {
1101 } else if ( token.Icmp( "sse" ) == 0 ) {
1103 } else if ( token.Icmp( "sse2" ) == 0 ) {
1105 } else if ( token.Icmp( "sse3" ) == 0 ) {
1107 } else if ( token.Icmp( "htt" ) == 0 ) {
1111 if ( id == CPUID_NONE ) {
1112 common->Printf( "WARNING: unknown sys_cpustring '%s'\n", win32.sys_cpustring.GetString() );
1115 win32.cpuid = (cpuid_t) id;
1118 common->Printf( "%s\n", win32.sys_cpustring.GetString() );
1119 common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() );
1120 common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
1128 void Sys_Shutdown( void ) {
1137 cpuid_t Sys_GetProcessorId( void ) {
1143 Sys_GetProcessorString
1146 const char *Sys_GetProcessorString( void ) {
1147 return win32.sys_cpustring.GetString();
1150 //=======================================================================
1152 //#define SET_THREAD_AFFINITY
1156 ====================
1158 ====================
1160 void Win_Frame( void ) {
1161 // if "viewlog" has been modified, show or hide the log console
1162 if ( win32.win_viewlog.IsModified() ) {
1163 if ( !com_skipRenderer.GetBool() && idAsyncNetwork::serverDedicated.GetInteger() != 1 ) {
1164 Sys_ShowConsole( win32.win_viewlog.GetInteger(), false );
1166 win32.win_viewlog.ClearModified();
1170 extern "C" { void _chkstk( int size ); };
1171 void clrstk( void );
1174 ====================
1176 ====================
1178 void TestChkStk( void ) {
1185 ====================
1187 ====================
1189 void HackChkStk( void ) {
1191 VirtualProtect( _chkstk, 6, PAGE_EXECUTE_READWRITE, &old );
1192 *(byte *)_chkstk = 0xe9;
1193 *(int *)((int)_chkstk+1) = (int)clrstk - (int)_chkstk - 5;
1199 ====================
1200 GetExceptionCodeInfo
1201 ====================
1203 const char *GetExceptionCodeInfo( UINT code ) {
1205 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.";
1206 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.";
1207 case EXCEPTION_BREAKPOINT: return "A breakpoint was encountered.";
1208 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.";
1209 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.";
1210 case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "The thread tried to divide a floating-point value by a floating-point divisor of zero.";
1211 case EXCEPTION_FLT_INEXACT_RESULT: return "The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
1212 case EXCEPTION_FLT_INVALID_OPERATION: return "This exception represents any floating-point exception not included in this list.";
1213 case EXCEPTION_FLT_OVERFLOW: return "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
1214 case EXCEPTION_FLT_STACK_CHECK: return "The stack overflowed or underflowed as the result of a floating-point operation.";
1215 case EXCEPTION_FLT_UNDERFLOW: return "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
1216 case EXCEPTION_ILLEGAL_INSTRUCTION: return "The thread tried to execute an invalid instruction.";
1217 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.";
1218 case EXCEPTION_INT_DIVIDE_BY_ZERO: return "The thread tried to divide an integer value by an integer divisor of zero.";
1219 case EXCEPTION_INT_OVERFLOW: return "The result of an integer operation caused a carry out of the most significant bit of the result.";
1220 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.";
1221 case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "The thread tried to continue execution after a noncontinuable exception occurred.";
1222 case EXCEPTION_PRIV_INSTRUCTION: return "The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
1223 case EXCEPTION_SINGLE_STEP: return "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
1224 case EXCEPTION_STACK_OVERFLOW: return "The thread used up its stack.";
1225 default: return "Unknown exception";
1230 ====================
1233 emailer originally from Raven/Quake 4
1234 ====================
1236 void EmailCrashReport( LPSTR messageText ) {
1237 LPMAPISENDMAIL MAPISendMail;
1238 MapiMessage message;
1239 static int lastEmailTime = 0;
1241 if ( Sys_Milliseconds() < lastEmailTime + 10000 ) {
1245 lastEmailTime = Sys_Milliseconds();
1247 HINSTANCE mapi = LoadLibrary( "MAPI32.DLL" );
1249 MAPISendMail = ( LPMAPISENDMAIL )GetProcAddress( mapi, "MAPISendMail" );
1250 if( MAPISendMail ) {
1251 MapiRecipDesc toProgrammers =
1254 MAPI_TO, // ulRecipClass
1255 "DOOM 3 Crash", // lpszName
1256 "SMTP:programmers@idsoftware.com", // lpszAddress
1261 memset( &message, 0, sizeof( message ) );
1262 message.lpszSubject = "DOOM 3 Fatal Error";
1263 message.lpszNoteText = messageText;
1264 message.nRecipCount = 1;
1265 message.lpRecips = &toProgrammers;
1268 0, // LHANDLE lhSession
1269 0, // ULONG ulUIParam
1270 &message, // lpMapiMessage lpMessage
1271 MAPI_DIALOG, // FLAGS flFlags
1272 0 // ULONG ulReserved
1275 FreeLibrary( mapi );
1279 int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse );
1282 ====================
1284 ====================
1286 EXCEPTION_DISPOSITION __cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame,
1287 struct _CONTEXT *ContextRecord, void * DispatcherContext ) {
1289 static char msg[ 8192 ];
1290 char FPUFlags[2048];
1292 Sys_FPU_PrintStateFlags( FPUFlags, ContextRecord->FloatSave.ControlWord,
1293 ContextRecord->FloatSave.StatusWord,
1294 ContextRecord->FloatSave.TagWord,
1295 ContextRecord->FloatSave.ErrorOffset,
1296 ContextRecord->FloatSave.ErrorSelector,
1297 ContextRecord->FloatSave.DataOffset,
1298 ContextRecord->FloatSave.DataSelector );
1302 "Please describe what you were doing when DOOM 3 crashed!\n"
1303 "If this text did not pop into your email client please copy and email it to programmers@idsoftware.com\n"
1305 "-= FATAL EXCEPTION =-\n"
1309 "0x%x at address 0x%08x\n"
1313 "EAX = 0x%08x EBX = 0x%08x\n"
1314 "ECX = 0x%08x EDX = 0x%08x\n"
1315 "ESI = 0x%08x EDI = 0x%08x\n"
1316 "EIP = 0x%08x ESP = 0x%08x\n"
1317 "EBP = 0x%08x EFL = 0x%08x\n"
1327 com_version.GetString(),
1328 ExceptionRecord->ExceptionCode,
1329 ExceptionRecord->ExceptionAddress,
1330 GetExceptionCodeInfo( ExceptionRecord->ExceptionCode ),
1331 ContextRecord->Eax, ContextRecord->Ebx,
1332 ContextRecord->Ecx, ContextRecord->Edx,
1333 ContextRecord->Esi, ContextRecord->Edi,
1334 ContextRecord->Eip, ContextRecord->Esp,
1335 ContextRecord->Ebp, ContextRecord->EFlags,
1336 ContextRecord->SegCs,
1337 ContextRecord->SegSs,
1338 ContextRecord->SegDs,
1339 ContextRecord->SegEs,
1340 ContextRecord->SegFs,
1341 ContextRecord->SegGs,
1345 EmailCrashReport( msg );
1346 common->FatalError( msg );
1348 // Tell the OS to restart the faulting instruction
1349 return ExceptionContinueExecution;
1352 #define TEST_FPU_EXCEPTIONS /* FPU_EXCEPTION_INVALID_OPERATION | */ \
1353 /* FPU_EXCEPTION_DENORMALIZED_OPERAND | */ \
1354 /* FPU_EXCEPTION_DIVIDE_BY_ZERO | */ \
1355 /* FPU_EXCEPTION_NUMERIC_OVERFLOW | */ \
1356 /* FPU_EXCEPTION_NUMERIC_UNDERFLOW | */ \
1357 /* FPU_EXCEPTION_INEXACT_RESULT | */ \
1365 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
1367 const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) );
1369 Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 );
1371 Sys_GetCurrentMemoryStatus( exeLaunchMemoryStats );
1374 DWORD handler = (DWORD)_except_handler;
1376 { // Build EXCEPTION_REGISTRATION record:
1377 push handler // Address of handler function
1378 push FS:[0] // Address of previous handler
1379 mov FS:[0],ESP // Install new EXECEPTION_REGISTRATION
1383 win32.hInstance = hInstance;
1384 idStr::Copynz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
1386 // done before Com/Sys_Init since we need this for error output
1387 Sys_CreateConsole();
1389 // no abort/retry/fail errors
1390 SetErrorMode( SEM_FAILCRITICALERRORS );
1392 for ( int i = 0; i < MAX_CRITICAL_SECTIONS; i++ ) {
1393 InitializeCriticalSection( &win32.criticalSections[i] );
1396 // get the initial time base
1400 // disable the painfully slow MS heap check every 1024 allocs
1401 _CrtSetDbgFlag( 0 );
1404 // Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
1405 Sys_FPU_SetPrecision( FPU_PRECISION_DOUBLE_EXTENDED );
1407 common->Init( 0, NULL, lpCmdLine );
1409 #if TEST_FPU_EXCEPTIONS != 0
1410 common->Printf( Sys_FPU_GetState() );
1413 #ifndef ID_DEDICATED
1414 if ( win32.win_notaskkeys.GetInteger() ) {
1415 DisableTaskKeys( TRUE, FALSE, /*( win32.win_notaskkeys.GetInteger() == 2 )*/ FALSE );
1419 Sys_StartAsyncThread();
1421 // hide or show the early console as necessary
1422 if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) {
1423 Sys_ShowConsole( 1, true );
1425 Sys_ShowConsole( 0, false );
1428 #ifdef SET_THREAD_AFFINITY
1429 // give the main thread an affinity for the first cpu
1430 SetThreadAffinityMask( GetCurrentThread(), 1 );
1433 ::SetCursor( hcurSave );
1435 // Launch the script debugger
1436 if ( strstr( lpCmdLine, "+debugger" ) ) {
1437 // DebuggerClientInit( lpCmdLine );
1441 ::SetFocus( win32.hWnd );
1452 // set exceptions, even if some crappy syscall changes them!
1453 Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
1455 #ifdef ID_ALLOW_TOOLS
1456 if ( com_editors ) {
1457 if ( com_editors & EDITOR_GUI ) {
1460 } else if ( com_editors & EDITOR_RADIANT ) {
1464 else if (com_editors & EDITOR_MATERIAL ) {
1465 //BSM Nerve: Add support for the material editor
1466 MaterialEditorRun();
1469 if ( com_editors & EDITOR_LIGHT ) {
1470 // in-game Light Editor
1473 if ( com_editors & EDITOR_SOUND ) {
1474 // in-game Sound Editor
1477 if ( com_editors & EDITOR_DECL ) {
1478 // in-game Declaration Browser
1481 if ( com_editors & EDITOR_AF ) {
1482 // in-game Articulated Figure Editor
1485 if ( com_editors & EDITOR_PARTICLE ) {
1486 // in-game Particle Editor
1487 ParticleEditorRun();
1489 if ( com_editors & EDITOR_SCRIPT ) {
1490 // in-game Script Editor
1493 if ( com_editors & EDITOR_PDA ) {
1494 // in-game PDA Editor
1509 ====================
1512 I tried to get the run time to call this at every function entry, but
1513 ====================
1515 static int parmBytes;
1516 __declspec( naked ) void clrstk( void ) {
1517 // eax = bytes to add to stack
1520 neg eax ; compute new stack pointer in eax
1524 mov eax,dword ptr [eax] ; copy the return address
1549 void idSysLocal::OpenURL( const char *url, bool doexit ) {
1550 static bool doexit_spamguard = false;
1553 if (doexit_spamguard) {
1554 common->DPrintf( "OpenURL: already in an exit sequence, ignoring %s\n", url );
1558 common->Printf("Open URL: %s\n", url);
1560 if ( !ShellExecute( NULL, "open", url, NULL, NULL, SW_RESTORE ) ) {
1561 common->Error( "Could not open url: '%s' ", url );
1565 wnd = GetForegroundWindow();
1567 ShowWindow( wnd, SW_MAXIMIZE );
1571 doexit_spamguard = true;
1572 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
1578 idSysLocal::StartProcess
1581 void idSysLocal::StartProcess( const char *exePath, bool doexit ) {
1582 TCHAR szPathOrig[_MAX_PATH];
1584 PROCESS_INFORMATION pi;
1586 ZeroMemory( &si, sizeof(si) );
1589 strncpy( szPathOrig, exePath, _MAX_PATH );
1591 if( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
1592 common->Error( "Could not start process: '%s' ", szPathOrig );
1597 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
1606 void Sys_SetFatalError( const char *error ) {
1614 void Sys_DoPreferences( void ) {