/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . 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. 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. =========================================================================== */ #include "sys/platform.h" #include "idlib/CmdArgs.h" #include "framework/async/AsyncNetwork.h" #include "framework/Licensee.h" #include "framework/UsercmdGen.h" #include "renderer/tr_local.h" #include "sys/win32/rc/CreateResourceIDs.h" #include "sys/sys_local.h" #include "sys/win32/win_local.h" #include #include #include #include #include #include #include #ifndef __MRC__ #include #include #endif #include idCVar Win32Vars_t::sys_arch( "sys_arch", "", CVAR_SYSTEM | CVAR_INIT, "" ); idCVar Win32Vars_t::in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_BOOL, "enable mouse input" ); idCVar Win32Vars_t::win_allowAltTab( "win_allowAltTab", "0", CVAR_SYSTEM | CVAR_BOOL, "allow Alt-Tab when fullscreen" ); idCVar Win32Vars_t::win_notaskkeys( "win_notaskkeys", "0", CVAR_SYSTEM | CVAR_INTEGER, "disable windows task keys" ); idCVar Win32Vars_t::win_username( "win_username", "", CVAR_SYSTEM | CVAR_INIT, "windows user name" ); idCVar Win32Vars_t::win_xpos( "win_xpos", "3", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "horizontal position of window" ); idCVar Win32Vars_t::win_ypos( "win_ypos", "22", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "vertical position of window" ); idCVar Win32Vars_t::win_outputDebugString( "win_outputDebugString", "0", CVAR_SYSTEM | CVAR_BOOL, "" ); idCVar Win32Vars_t::win_outputEditString( "win_outputEditString", "1", CVAR_SYSTEM | CVAR_BOOL, "" ); idCVar Win32Vars_t::win_viewlog( "win_viewlog", "0", CVAR_SYSTEM | CVAR_INTEGER, "" ); idCVar Win32Vars_t::win_timerUpdate( "win_timerUpdate", "0", CVAR_SYSTEM | CVAR_BOOL, "allows the game to be updated while dragging the window" ); idCVar Win32Vars_t::win_allowMultipleInstances( "win_allowMultipleInstances", "0", CVAR_SYSTEM | CVAR_BOOL, "allow multiple instances running concurrently" ); Win32Vars_t win32; static sysMemoryStats_t exeLaunchMemoryStats; /* ================ Sys_GetExeLaunchMemoryStatus ================ */ void Sys_GetExeLaunchMemoryStatus( sysMemoryStats_t &stats ) { stats = exeLaunchMemoryStats; } /* ================== Sys_Sentry ================== */ void Sys_Sentry() { int j = 0; } #pragma optimize( "", on ) #ifdef DEBUG static unsigned int debug_total_alloc = 0; static unsigned int debug_total_alloc_count = 0; static unsigned int debug_current_alloc = 0; static unsigned int debug_current_alloc_count = 0; static unsigned int debug_frame_alloc = 0; static unsigned int debug_frame_alloc_count = 0; idCVar sys_showMallocs( "sys_showMallocs", "0", CVAR_SYSTEM, "" ); // _HOOK_ALLOC, _HOOK_REALLOC, _HOOK_FREE typedef struct CrtMemBlockHeader { struct _CrtMemBlockHeader *pBlockHeaderNext; // Pointer to the block allocated just before this one: struct _CrtMemBlockHeader *pBlockHeaderPrev; // Pointer to the block allocated just after this one char *szFileName; // File name int nLine; // Line number size_t nDataSize; // Size of user block int nBlockUse; // Type of block long lRequest; // Allocation number byte gap[4]; // Buffer just before (lower than) the user's memory: } CrtMemBlockHeader; #include /* ================== Sys_AllocHook called for every malloc/new/free/delete ================== */ int Sys_AllocHook( int nAllocType, void *pvData, size_t nSize, int nBlockUse, long lRequest, const unsigned char * szFileName, int nLine ) { CrtMemBlockHeader *pHead; byte *temp; if ( nBlockUse == _CRT_BLOCK ) { return( TRUE ); } // get a pointer to memory block header temp = ( byte * )pvData; temp -= 32; pHead = ( CrtMemBlockHeader * )temp; switch( nAllocType ) { case _HOOK_ALLOC: debug_total_alloc += nSize; debug_current_alloc += nSize; debug_frame_alloc += nSize; debug_total_alloc_count++; debug_current_alloc_count++; debug_frame_alloc_count++; break; case _HOOK_FREE: assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd ); debug_current_alloc -= pHead->nDataSize; debug_current_alloc_count--; debug_total_alloc_count++; debug_frame_alloc_count++; break; case _HOOK_REALLOC: assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd ); debug_current_alloc -= pHead->nDataSize; debug_total_alloc += nSize; debug_current_alloc += nSize; debug_frame_alloc += nSize; debug_total_alloc_count++; debug_current_alloc_count--; debug_frame_alloc_count++; break; } return( TRUE ); } /* ================== Sys_DebugMemory_f ================== */ void Sys_DebugMemory_f( void ) { common->Printf( "Total allocation %8dk in %d blocks\n", debug_total_alloc / 1024, debug_total_alloc_count ); common->Printf( "Current allocation %8dk in %d blocks\n", debug_current_alloc / 1024, debug_current_alloc_count ); } /* ================== Sys_MemFrame ================== */ void Sys_MemFrame( void ) { if( sys_showMallocs.GetInteger() ) { common->Printf("Frame: %8dk in %5d blocks\n", debug_frame_alloc / 1024, debug_frame_alloc_count ); } debug_frame_alloc = 0; debug_frame_alloc_count = 0; } #endif /* ================== Sys_FlushCacheMemory On windows, the vertex buffers are write combined, so they don't need to be flushed from the cache ================== */ void Sys_FlushCacheMemory( void *base, int bytes ) { } /* ============= Sys_Error Show the early console as an error dialog ============= */ void Sys_Error( const char *error, ... ) { va_list argptr; char text[4096]; MSG msg; va_start( argptr, error ); vsprintf( text, error, argptr ); va_end( argptr); Conbuf_AppendText( text ); Conbuf_AppendText( "\n" ); Win_SetErrorText( text ); Sys_ShowConsole( 1, true ); timeEndPeriod( 1 ); Sys_ShutdownInput(); GLimp_Shutdown(); // wait for the user to quit while ( 1 ) { if ( !GetMessage( &msg, NULL, 0, 0 ) ) { common->Quit(); } TranslateMessage( &msg ); DispatchMessage( &msg ); } Sys_DestroyConsole(); exit (1); } /* ============== Sys_Quit ============== */ void Sys_Quit( void ) { timeEndPeriod( 1 ); Sys_ShutdownInput(); Sys_DestroyConsole(); ExitProcess( 0 ); } /* ============== Sys_Printf ============== */ #define MAXPRINTMSG 4096 void Sys_Printf( const char *fmt, ... ) { char msg[MAXPRINTMSG]; va_list argptr; va_start(argptr, fmt); idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr ); va_end(argptr); msg[sizeof(msg)-1] = '\0'; if ( win32.win_outputDebugString.GetBool() ) { OutputDebugString( msg ); } if ( win32.win_outputEditString.GetBool() ) { Conbuf_AppendText( msg ); } } /* ============== Sys_DebugPrintf ============== */ #define MAXPRINTMSG 4096 void Sys_DebugPrintf( const char *fmt, ... ) { char msg[MAXPRINTMSG]; va_list argptr; va_start( argptr, fmt ); idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr ); msg[ sizeof(msg)-1 ] = '\0'; va_end( argptr ); OutputDebugString( msg ); } /* ============== Sys_DebugVPrintf ============== */ void Sys_DebugVPrintf( const char *fmt, va_list arg ) { char msg[MAXPRINTMSG]; idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, arg ); msg[ sizeof(msg)-1 ] = '\0'; OutputDebugString( msg ); } /* ============== Sys_ShowWindow ============== */ void Sys_ShowWindow( bool show ) { ::ShowWindow( win32.hWnd, show ? SW_SHOW : SW_HIDE ); } /* ============== Sys_IsWindowVisible ============== */ bool Sys_IsWindowVisible( void ) { return ( ::IsWindowVisible( win32.hWnd ) != 0 ); } /* ============== Sys_Mkdir ============== */ void Sys_Mkdir( const char *path ) { _mkdir (path); } /* ================= Sys_FileTimeStamp ================= */ ID_TIME_T Sys_FileTimeStamp( FILE *fp ) { struct _stat st; _fstat( _fileno( fp ), &st ); return (long) st.st_mtime; } /* ============== Sys_Cwd ============== */ const char *Sys_Cwd( void ) { static char cwd[MAX_OSPATH]; _getcwd( cwd, sizeof( cwd ) - 1 ); cwd[MAX_OSPATH-1] = 0; return cwd; } /* ============== Sys_DefaultCDPath ============== */ const char *Sys_DefaultCDPath( void ) { return ""; } /* ============== Sys_DefaultBasePath ============== */ const char *Sys_DefaultBasePath( void ) { return Sys_Cwd(); } /* ============== Sys_DefaultSavePath ============== */ const char *Sys_DefaultSavePath( void ) { return cvarSystem->GetCVarString( "fs_basepath" ); } /* ============== Sys_EXEPath ============== */ const char *Sys_EXEPath( void ) { static char exe[ MAX_OSPATH ]; GetModuleFileName( NULL, exe, sizeof( exe ) - 1 ); return exe; } /* ============== Sys_ListFiles ============== */ int Sys_ListFiles( const char *directory, const char *extension, idStrList &list ) { idStr search; struct _finddata_t findinfo; int findhandle; int flag; if ( !extension) { extension = ""; } // passing a slash as extension will find directories if ( extension[0] == '/' && extension[1] == 0 ) { extension = ""; flag = 0; } else { flag = _A_SUBDIR; } sprintf( search, "%s\\*%s", directory, extension ); // search list.Clear(); findhandle = _findfirst( search, &findinfo ); if ( findhandle == -1 ) { return -1; } do { if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) { list.Append( findinfo.name ); } } while ( _findnext( findhandle, &findinfo ) != -1 ); _findclose( findhandle ); return list.Num(); } /* ================ Sys_GetClipboardData ================ */ char *Sys_GetClipboardData( void ) { char *data = NULL; char *cliptext; if ( OpenClipboard( NULL ) != 0 ) { HANDLE hClipboardData; if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) { if ( ( cliptext = (char *)GlobalLock( hClipboardData ) ) != 0 ) { data = (char *)Mem_Alloc( GlobalSize( hClipboardData ) + 1 ); strcpy( data, cliptext ); GlobalUnlock( hClipboardData ); strtok( data, "\n\r\b" ); } } CloseClipboard(); } return data; } /* ================ Sys_SetClipboardData ================ */ void Sys_SetClipboardData( const char *string ) { HGLOBAL HMem; char *PMem; // allocate memory block HMem = (char *)::GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, strlen( string ) + 1 ); if ( HMem == NULL ) { return; } // lock allocated memory and obtain a pointer PMem = (char *)::GlobalLock( HMem ); if ( PMem == NULL ) { return; } // copy text into allocated memory block lstrcpy( PMem, string ); // unlock allocated memory ::GlobalUnlock( HMem ); // open Clipboard if ( !OpenClipboard( 0 ) ) { ::GlobalFree( HMem ); return; } // remove current Clipboard contents EmptyClipboard(); // supply the memory handle to the Clipboard SetClipboardData( CF_TEXT, HMem ); HMem = 0; // close Clipboard CloseClipboard(); } /* ======================================================================== DLL Loading ======================================================================== */ /* ===================== Sys_DLL_Load ===================== */ uintptr_t Sys_DLL_Load( const char *dllName ) { HINSTANCE libHandle; libHandle = LoadLibrary( dllName ); if ( libHandle ) { // since we can't have LoadLibrary load only from the specified path, check it did the right thing char loadedPath[ MAX_OSPATH ]; GetModuleFileName( libHandle, loadedPath, sizeof( loadedPath ) - 1 ); if ( idStr::IcmpPath( dllName, loadedPath ) ) { Sys_Printf( "ERROR: LoadLibrary '%s' wants to load '%s'\n", dllName, loadedPath ); Sys_DLL_Unload( (int)libHandle ); return 0; } } return (uintptr_t)libHandle; } /* ===================== Sys_DLL_GetProcAddress ===================== */ void *Sys_DLL_GetProcAddress( uintptr_t dllHandle, const char *procName ) { return (void *)GetProcAddress( (HINSTANCE)dllHandle, procName ); } /* ===================== Sys_DLL_Unload ===================== */ void Sys_DLL_Unload( uintptr_t dllHandle ) { if ( !dllHandle ) { return; } if ( FreeLibrary( (HINSTANCE)dllHandle ) == 0 ) { int lastError = GetLastError(); LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL ); Sys_Error( "Sys_DLL_Unload: FreeLibrary failed - %s (%d)", lpMsgBuf, lastError ); } } /* ======================================================================== EVENT LOOP ======================================================================== */ #define MAX_QUED_EVENTS 256 #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) sysEvent_t eventQue[MAX_QUED_EVENTS]; int eventHead = 0; int eventTail = 0; /* ================ Sys_QueEvent Ptr should either be null, or point to a block of data that can be freed by the game later. ================ */ void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) { sysEvent_t *ev; ev = &eventQue[ eventHead & MASK_QUED_EVENTS ]; if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { common->Printf("Sys_QueEvent: overflow\n"); // we are discarding an event, but don't leak memory if ( ev->evPtr ) { Mem_Free( ev->evPtr ); } eventTail++; } eventHead++; ev->evType = type; ev->evValue = value; ev->evValue2 = value2; ev->evPtrLength = ptrLength; ev->evPtr = ptr; } /* ============= Sys_PumpEvents This allows windows to be moved during renderbump ============= */ void Sys_PumpEvents( void ) { MSG msg; // pump the message loop while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) { if ( !GetMessage( &msg, NULL, 0, 0 ) ) { common->Quit(); } // save the msg time, because wndprocs don't have access to the timestamp if ( win32.sysMsgTime && win32.sysMsgTime > (int)msg.time ) { // don't ever let the event times run backwards // common->Printf( "Sys_PumpEvents: win32.sysMsgTime (%i) > msg.time (%i)\n", win32.sysMsgTime, msg.time ); } else { win32.sysMsgTime = msg.time; } #ifdef ID_ALLOW_TOOLS if ( GUIEditorHandleMessage ( &msg ) ) { continue; } #endif TranslateMessage (&msg); DispatchMessage (&msg); } } /* ================ Sys_GenerateEvents ================ */ void Sys_GenerateEvents( void ) { static int entered = false; char *s; if ( entered ) { return; } entered = true; // pump the message loop Sys_PumpEvents(); // make sure mouse and joystick are only called once a frame IN_Frame(); // check for console commands s = Sys_ConsoleInput(); if ( s ) { char *b; int len; len = strlen( s ) + 1; b = (char *)Mem_Alloc( len ); strcpy( b, s ); Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b ); } entered = false; } /* ================ Sys_ClearEvents ================ */ void Sys_ClearEvents( void ) { eventHead = eventTail = 0; } /* ================ Sys_GetEvent ================ */ sysEvent_t Sys_GetEvent( void ) { sysEvent_t ev; // return if we have data if ( eventHead > eventTail ) { eventTail++; return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; } // return the empty event memset( &ev, 0, sizeof( ev ) ); return ev; } //================================================================ /* ================= Sys_In_Restart_f Restart the input subsystem ================= */ void Sys_In_Restart_f( const idCmdArgs &args ) { Sys_ShutdownInput(); Sys_InitInput(); } /* ================ Sys_AlreadyRunning returns true if there is a copy of D3 running already ================ */ bool Sys_AlreadyRunning( void ) { #ifndef DEBUG if ( !win32.win_allowMultipleInstances.GetBool() ) { HANDLE hMutexOneInstance = ::CreateMutex( NULL, FALSE, "DOOM3" ); if ( ::GetLastError() == ERROR_ALREADY_EXISTS || ::GetLastError() == ERROR_ACCESS_DENIED ) { return true; } } #endif return false; } /* ================ Sys_Init The cvar system must already be setup ================ */ #define OSR2_BUILD_NUMBER 1111 #define WIN98_BUILD_NUMBER 1998 void Sys_Init( void ) { CoInitialize( NULL ); // make sure the timer is high precision, otherwise // NT gets 18ms resolution timeBeginPeriod( 1 ); // get WM_TIMER messages pumped every millisecond // SetTimer( NULL, 0, 100, NULL ); cmdSystem->AddCommand( "in_restart", Sys_In_Restart_f, CMD_FL_SYSTEM, "restarts the input system" ); #ifdef DEBUG cmdSystem->AddCommand( "createResourceIDs", CreateResourceIDs_f, CMD_FL_TOOL, "assigns resource IDs in _resouce.h files" ); #endif #if 0 cmdSystem->AddCommand( "setAsyncSound", Sys_SetAsyncSound_f, CMD_FL_SYSTEM, "set the async sound option" ); #endif // // Windows user name // win32.win_username.SetString( Sys_GetCurrentUser() ); // // Windows version // win32.osversion.dwOSVersionInfoSize = sizeof( win32.osversion ); if ( !GetVersionEx( (LPOSVERSIONINFO)&win32.osversion ) ) Sys_Error( "Couldn't get OS info" ); if ( win32.osversion.dwMajorVersion < 4 ) { Sys_Error( GAME_NAME " requires Windows version 4 (NT) or greater" ); } if ( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32s ) { Sys_Error( GAME_NAME " doesn't run on Win32s" ); } if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) { if( win32.osversion.dwMajorVersion <= 4 ) { win32.sys_arch.SetString( "WinNT (NT)" ); } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 0 ) { win32.sys_arch.SetString( "Win2K (NT)" ); } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 1 ) { win32.sys_arch.SetString( "WinXP (NT)" ); } else if ( win32.osversion.dwMajorVersion == 6 ) { win32.sys_arch.SetString( "Vista" ); } else { win32.sys_arch.SetString( "Unknown NT variant" ); } } else if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 0 ) { // Win95 if( win32.osversion.szCSDVersion[1] == 'C' ) { win32.sys_arch.SetString( "Win95 OSR2 (95)" ); } else { win32.sys_arch.SetString( "Win95 (95)" ); } } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 10 ) { // Win98 if( win32.osversion.szCSDVersion[1] == 'A' ) { win32.sys_arch.SetString( "Win98SE (95)" ); } else { win32.sys_arch.SetString( "Win98 (95)" ); } } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 90 ) { // WinMe win32.sys_arch.SetString( "WinMe (95)" ); } else { win32.sys_arch.SetString( "Unknown 95 variant" ); } } else { win32.sys_arch.SetString( "unknown Windows variant" ); } common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() ); common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() ); } /* ================ Sys_Shutdown ================ */ void Sys_Shutdown( void ) { CoUninitialize(); } //======================================================================= //#define SET_THREAD_AFFINITY /* ==================== Win_Frame ==================== */ void Win_Frame( void ) { // if "viewlog" has been modified, show or hide the log console if ( win32.win_viewlog.IsModified() ) { if ( !com_skipRenderer.GetBool() && idAsyncNetwork::serverDedicated.GetInteger() != 1 ) { Sys_ShowConsole( win32.win_viewlog.GetInteger(), false ); } win32.win_viewlog.ClearModified(); } } /* ==================== GetExceptionCodeInfo ==================== */ const char *GetExceptionCodeInfo( UINT code ) { switch( code ) { 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."; 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."; case EXCEPTION_BREAKPOINT: return "A breakpoint was encountered."; 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."; 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."; case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "The thread tried to divide a floating-point value by a floating-point divisor of zero."; case EXCEPTION_FLT_INEXACT_RESULT: return "The result of a floating-point operation cannot be represented exactly as a decimal fraction."; case EXCEPTION_FLT_INVALID_OPERATION: return "This exception represents any floating-point exception not included in this list."; case EXCEPTION_FLT_OVERFLOW: return "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type."; case EXCEPTION_FLT_STACK_CHECK: return "The stack overflowed or underflowed as the result of a floating-point operation."; case EXCEPTION_FLT_UNDERFLOW: return "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type."; case EXCEPTION_ILLEGAL_INSTRUCTION: return "The thread tried to execute an invalid instruction."; 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."; case EXCEPTION_INT_DIVIDE_BY_ZERO: return "The thread tried to divide an integer value by an integer divisor of zero."; case EXCEPTION_INT_OVERFLOW: return "The result of an integer operation caused a carry out of the most significant bit of the result."; 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."; case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "The thread tried to continue execution after a noncontinuable exception occurred."; case EXCEPTION_PRIV_INSTRUCTION: return "The thread tried to execute an instruction whose operation is not allowed in the current machine mode."; case EXCEPTION_SINGLE_STEP: return "A trace trap or other single-instruction mechanism signaled that one instruction has been executed."; case EXCEPTION_STACK_OVERFLOW: return "The thread used up its stack."; default: return "Unknown exception"; } } int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse ); #define TEST_FPU_EXCEPTIONS /* FPU_EXCEPTION_INVALID_OPERATION | */ \ /* FPU_EXCEPTION_DENORMALIZED_OPERAND | */ \ /* FPU_EXCEPTION_DIVIDE_BY_ZERO | */ \ /* FPU_EXCEPTION_NUMERIC_OVERFLOW | */ \ /* FPU_EXCEPTION_NUMERIC_UNDERFLOW | */ \ /* FPU_EXCEPTION_INEXACT_RESULT | */ \ 0 /* ================== WinMain ================== */ int main(int argc, char *argv[]) { const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) ); Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 ); Sys_GetCurrentMemoryStatus( exeLaunchMemoryStats ); win32.hInstance = GetModuleHandle(NULL); // done before Com/Sys_Init since we need this for error output Sys_CreateConsole(); // no abort/retry/fail errors SetErrorMode( SEM_FAILCRITICALERRORS ); #ifdef DEBUG // disable the painfully slow MS heap check every 1024 allocs _CrtSetDbgFlag( 0 ); #endif // Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS ); Sys_FPU_SetPrecision( FPU_PRECISION_DOUBLE_EXTENDED ); if ( argc > 1 ) { common->Init( argc-1, &argv[1] ); } else { common->Init( 0, NULL ); } #if TEST_FPU_EXCEPTIONS != 0 common->Printf( Sys_FPU_GetState() ); #endif #ifndef ID_DEDICATED if ( win32.win_notaskkeys.GetInteger() ) { DisableTaskKeys( TRUE, FALSE, /*( win32.win_notaskkeys.GetInteger() == 2 )*/ FALSE ); } #endif // hide or show the early console as necessary if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) { Sys_ShowConsole( 1, true ); } else { Sys_ShowConsole( 0, false ); } #ifdef SET_THREAD_AFFINITY // give the main thread an affinity for the first cpu SetThreadAffinityMask( GetCurrentThread(), 1 ); #endif ::SetCursor( hcurSave ); // Launch the script debugger if ( strstr( GetCommandLine(), "+debugger" ) ) { // DebuggerClientInit( lpCmdLine ); return 0; } ::SetFocus( win32.hWnd ); // main game loop while( 1 ) { Win_Frame(); #ifdef DEBUG Sys_MemFrame(); #endif // set exceptions, even if some crappy syscall changes them! Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS ); #ifdef ID_ALLOW_TOOLS if ( com_editors ) { if ( com_editors & EDITOR_GUI ) { // GUI editor GUIEditorRun(); } else if ( com_editors & EDITOR_RADIANT ) { // Level Editor RadiantRun(); } else if (com_editors & EDITOR_MATERIAL ) { //BSM Nerve: Add support for the material editor MaterialEditorRun(); } else { if ( com_editors & EDITOR_LIGHT ) { // in-game Light Editor LightEditorRun(); } if ( com_editors & EDITOR_SOUND ) { // in-game Sound Editor SoundEditorRun(); } if ( com_editors & EDITOR_DECL ) { // in-game Declaration Browser DeclBrowserRun(); } if ( com_editors & EDITOR_AF ) { // in-game Articulated Figure Editor AFEditorRun(); } if ( com_editors & EDITOR_PARTICLE ) { // in-game Particle Editor ParticleEditorRun(); } if ( com_editors & EDITOR_SCRIPT ) { // in-game Script Editor ScriptEditorRun(); } if ( com_editors & EDITOR_PDA ) { // in-game PDA Editor PDAEditorRun(); } } } #endif // run the game common->Frame(); } // never gets here return 0; } /* ================== idSysLocal::OpenURL ================== */ void idSysLocal::OpenURL( const char *url, bool doexit ) { static bool doexit_spamguard = false; HWND wnd; if (doexit_spamguard) { common->DPrintf( "OpenURL: already in an exit sequence, ignoring %s\n", url ); return; } common->Printf("Open URL: %s\n", url); if ( !ShellExecute( NULL, "open", url, NULL, NULL, SW_RESTORE ) ) { common->Error( "Could not open url: '%s' ", url ); return; } wnd = GetForegroundWindow(); if ( wnd ) { ShowWindow( wnd, SW_MAXIMIZE ); } if ( doexit ) { doexit_spamguard = true; cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" ); } } /* ================== idSysLocal::StartProcess ================== */ void idSysLocal::StartProcess( const char *exePath, bool doexit ) { TCHAR szPathOrig[_MAX_PATH]; STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); strncpy( szPathOrig, exePath, _MAX_PATH ); if( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) { common->Error( "Could not start process: '%s' ", szPathOrig ); return; } if ( doexit ) { cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" ); } } /* ================== Sys_SetFatalError ================== */ void Sys_SetFatalError( const char *error ) { } /* ================== Sys_DoPreferences ================== */ void Sys_DoPreferences( void ) { }