mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-28 15:12:00 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
1427 lines
35 KiB
C++
1427 lines
35 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
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 <errno.h>
|
|
#include <float.h>
|
|
#include <fcntl.h>
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
#include <conio.h>
|
|
#include <shellapi.h>
|
|
|
|
#ifndef __MRC__
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
idCVar Win32Vars_t::sys_arch( "sys_arch", "", CVAR_SYSTEM | CVAR_INIT, "" );
|
|
idCVar Win32Vars_t::sys_cpustring( "sys_cpustring", "detect", 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 char sys_cmdline[MAX_STRING_CHARS];
|
|
|
|
// not a hard limit, just what we keep track of for debugging
|
|
xthreadInfo *g_threads[MAX_THREADS];
|
|
|
|
int g_thread_count = 0;
|
|
|
|
static sysMemoryStats_t exeLaunchMemoryStats;
|
|
|
|
static xthreadInfo threadInfo;
|
|
static HANDLE hTimer;
|
|
|
|
/*
|
|
================
|
|
Sys_GetExeLaunchMemoryStatus
|
|
================
|
|
*/
|
|
void Sys_GetExeLaunchMemoryStatus( sysMemoryStats_t &stats ) {
|
|
stats = exeLaunchMemoryStats;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Sys_Createthread
|
|
==================
|
|
*/
|
|
void Sys_CreateThread( xthread_t function, void *parms, xthreadPriority priority, xthreadInfo &info, const char *name, xthreadInfo *threads[MAX_THREADS], int *thread_count ) {
|
|
DWORD id;
|
|
HANDLE temp = CreateThread( NULL, // LPSECURITY_ATTRIBUTES lpsa,
|
|
0, // DWORD cbStack,
|
|
(LPTHREAD_START_ROUTINE)function, // LPTHREAD_START_ROUTINE lpStartAddr,
|
|
parms, // LPVOID lpvThreadParm,
|
|
0, // DWORD fdwCreate,
|
|
&id);
|
|
|
|
info.threadId = id;
|
|
info.threadHandle = (intptr_t) temp;
|
|
if (priority == THREAD_HIGHEST) {
|
|
SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_HIGHEST ); // we better sleep enough to do this
|
|
} else if (priority == THREAD_ABOVE_NORMAL ) {
|
|
SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_ABOVE_NORMAL );
|
|
}
|
|
info.name = name;
|
|
if ( *thread_count < MAX_THREADS ) {
|
|
threads[(*thread_count)++] = &info;
|
|
} else {
|
|
common->DPrintf("WARNING: MAX_THREADS reached\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Sys_DestroyThread
|
|
==================
|
|
*/
|
|
void Sys_DestroyThread( xthreadInfo& info ) {
|
|
WaitForSingleObject( (HANDLE)info.threadHandle, INFINITE);
|
|
CloseHandle( (HANDLE)info.threadHandle );
|
|
info.threadHandle = 0;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Sys_Sentry
|
|
==================
|
|
*/
|
|
void Sys_Sentry() {
|
|
int j = 0;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Sys_GetThreadName
|
|
==================
|
|
*/
|
|
const char* Sys_GetThreadName(int *index) {
|
|
size_t id = GetCurrentThreadId();
|
|
for( int i = 0; i < g_thread_count; i++ ) {
|
|
if ( id == g_threads[i]->threadId ) {
|
|
if ( index ) {
|
|
*index = i;
|
|
}
|
|
return g_threads[i]->name;
|
|
}
|
|
}
|
|
if ( index ) {
|
|
*index = -1;
|
|
}
|
|
return "main";
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Sys_EnterCriticalSection
|
|
==================
|
|
*/
|
|
void Sys_EnterCriticalSection( int index ) {
|
|
assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
|
|
if ( TryEnterCriticalSection( &win32.criticalSections[index] ) == 0 ) {
|
|
EnterCriticalSection( &win32.criticalSections[index] );
|
|
// Sys_DebugPrintf( "busy lock '%s' in thread '%s'\n", lock->name, Sys_GetThreadName() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Sys_LeaveCriticalSection
|
|
==================
|
|
*/
|
|
void Sys_LeaveCriticalSection( int index ) {
|
|
assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
|
|
LeaveCriticalSection( &win32.criticalSections[index] );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Sys_WaitForEvent
|
|
==================
|
|
*/
|
|
void Sys_WaitForEvent( int index ) {
|
|
assert( index == 0 );
|
|
if ( !win32.backgroundDownloadSemaphore ) {
|
|
win32.backgroundDownloadSemaphore = CreateEvent( NULL, TRUE, FALSE, NULL );
|
|
}
|
|
WaitForSingleObject( win32.backgroundDownloadSemaphore, INFINITE );
|
|
ResetEvent( win32.backgroundDownloadSemaphore );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Sys_TriggerEvent
|
|
==================
|
|
*/
|
|
void Sys_TriggerEvent( int index ) {
|
|
assert( index == 0 );
|
|
SetEvent( win32.backgroundDownloadSemaphore );
|
|
}
|
|
|
|
|
|
|
|
#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 <crtdbg.h>
|
|
|
|
/*
|
|
==================
|
|
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_Sleep
|
|
==============
|
|
*/
|
|
void Sys_Sleep( int msec ) {
|
|
Sleep( msec );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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_AsyncThread
|
|
==================
|
|
*/
|
|
static THREAD_RETURN_TYPE Sys_AsyncThread( void *parm ) {
|
|
int wakeNumber;
|
|
int startTime;
|
|
|
|
startTime = Sys_Milliseconds();
|
|
wakeNumber = 0;
|
|
|
|
while ( 1 ) {
|
|
#ifdef WIN32
|
|
// this will trigger 60 times a second
|
|
int r = WaitForSingleObject( hTimer, 100 );
|
|
if ( r != WAIT_OBJECT_0 ) {
|
|
OutputDebugString( "idPacketServer::PacketServerInterrupt: bad wait return" );
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
wakeNumber++;
|
|
int msec = Sys_Milliseconds();
|
|
int deltaTime = msec - startTime;
|
|
startTime = msec;
|
|
|
|
char str[1024];
|
|
sprintf( str, "%i ", deltaTime );
|
|
OutputDebugString( str );
|
|
#endif
|
|
|
|
|
|
common->Async();
|
|
}
|
|
|
|
return (THREAD_RETURN_TYPE) 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Sys_StartAsyncThread
|
|
|
|
Start the thread that will call idCommon::Async()
|
|
==============
|
|
*/
|
|
void Sys_StartAsyncThread( void ) {
|
|
// create an auto-reset event that happens 60 times a second
|
|
hTimer = CreateWaitableTimer( NULL, false, NULL );
|
|
if ( !hTimer ) {
|
|
common->Error( "idPacketServer::Spawn: CreateWaitableTimer failed" );
|
|
}
|
|
|
|
LARGE_INTEGER t;
|
|
t.HighPart = t.LowPart = 0;
|
|
SetWaitableTimer( hTimer, &t, USERCMD_MSEC, NULL, NULL, TRUE );
|
|
|
|
Sys_CreateThread( Sys_AsyncThread, NULL, THREAD_ABOVE_NORMAL, threadInfo, "Async", g_threads, &g_thread_count );
|
|
|
|
#ifdef SET_THREAD_AFFINITY
|
|
// give the async thread an affinity for the second cpu
|
|
SetThreadAffinityMask( (HANDLE)threadInfo.threadHandle, 2 );
|
|
#endif
|
|
|
|
if ( !threadInfo.threadHandle ) {
|
|
common->Error( "Sys_StartAsyncThread: failed" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
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" );
|
|
}
|
|
|
|
//
|
|
// CPU type
|
|
//
|
|
if ( !idStr::Icmp( win32.sys_cpustring.GetString(), "detect" ) ) {
|
|
idStr string;
|
|
|
|
common->Printf( "%1.0f MHz ", Sys_ClockTicksPerSecond() / 1000000.0f );
|
|
|
|
win32.cpuid = Sys_GetCPUId();
|
|
|
|
string.Clear();
|
|
|
|
if ( win32.cpuid & CPUID_AMD ) {
|
|
string += "AMD CPU";
|
|
} else if ( win32.cpuid & CPUID_INTEL ) {
|
|
string += "Intel CPU";
|
|
} else if ( win32.cpuid & CPUID_UNSUPPORTED ) {
|
|
string += "unsupported CPU";
|
|
} else {
|
|
string += "generic CPU";
|
|
}
|
|
|
|
string += " with ";
|
|
if ( win32.cpuid & CPUID_MMX ) {
|
|
string += "MMX & ";
|
|
}
|
|
if ( win32.cpuid & CPUID_3DNOW ) {
|
|
string += "3DNow! & ";
|
|
}
|
|
if ( win32.cpuid & CPUID_SSE ) {
|
|
string += "SSE & ";
|
|
}
|
|
if ( win32.cpuid & CPUID_SSE2 ) {
|
|
string += "SSE2 & ";
|
|
}
|
|
if ( win32.cpuid & CPUID_SSE3 ) {
|
|
string += "SSE3 & ";
|
|
}
|
|
if ( win32.cpuid & CPUID_HTT ) {
|
|
string += "HTT & ";
|
|
}
|
|
string.StripTrailing( " & " );
|
|
string.StripTrailing( " with " );
|
|
win32.sys_cpustring.SetString( string );
|
|
} else {
|
|
common->Printf( "forcing CPU type to " );
|
|
idLexer src( win32.sys_cpustring.GetString(), idStr::Length( win32.sys_cpustring.GetString() ), "sys_cpustring" );
|
|
idToken token;
|
|
|
|
int id = CPUID_NONE;
|
|
while( src.ReadToken( &token ) ) {
|
|
if ( token.Icmp( "generic" ) == 0 ) {
|
|
id |= CPUID_GENERIC;
|
|
} else if ( token.Icmp( "intel" ) == 0 ) {
|
|
id |= CPUID_INTEL;
|
|
} else if ( token.Icmp( "amd" ) == 0 ) {
|
|
id |= CPUID_AMD;
|
|
} else if ( token.Icmp( "mmx" ) == 0 ) {
|
|
id |= CPUID_MMX;
|
|
} else if ( token.Icmp( "3dnow" ) == 0 ) {
|
|
id |= CPUID_3DNOW;
|
|
} else if ( token.Icmp( "sse" ) == 0 ) {
|
|
id |= CPUID_SSE;
|
|
} else if ( token.Icmp( "sse2" ) == 0 ) {
|
|
id |= CPUID_SSE2;
|
|
} else if ( token.Icmp( "sse3" ) == 0 ) {
|
|
id |= CPUID_SSE3;
|
|
} else if ( token.Icmp( "htt" ) == 0 ) {
|
|
id |= CPUID_HTT;
|
|
}
|
|
}
|
|
if ( id == CPUID_NONE ) {
|
|
common->Printf( "WARNING: unknown sys_cpustring '%s'\n", win32.sys_cpustring.GetString() );
|
|
id = CPUID_GENERIC;
|
|
}
|
|
win32.cpuid = id;
|
|
}
|
|
|
|
common->Printf( "%s\n", win32.sys_cpustring.GetString() );
|
|
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();
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_GetProcessorId
|
|
================
|
|
*/
|
|
int Sys_GetProcessorId( void ) {
|
|
return win32.cpuid;
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_GetProcessorString
|
|
================
|
|
*/
|
|
const char *Sys_GetProcessorString( void ) {
|
|
return win32.sys_cpustring.GetString();
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
//#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 WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
|
|
|
|
const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) );
|
|
|
|
Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 );
|
|
|
|
Sys_GetCurrentMemoryStatus( exeLaunchMemoryStats );
|
|
|
|
win32.hInstance = hInstance;
|
|
idStr::Copynz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
|
|
|
|
// done before Com/Sys_Init since we need this for error output
|
|
Sys_CreateConsole();
|
|
|
|
// no abort/retry/fail errors
|
|
SetErrorMode( SEM_FAILCRITICALERRORS );
|
|
|
|
for ( int i = 0; i < MAX_CRITICAL_SECTIONS; i++ ) {
|
|
InitializeCriticalSection( &win32.criticalSections[i] );
|
|
}
|
|
|
|
// get the initial time base
|
|
Sys_Milliseconds();
|
|
|
|
#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 );
|
|
|
|
common->Init( 0, NULL, lpCmdLine );
|
|
|
|
#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
|
|
|
|
Sys_StartAsyncThread();
|
|
|
|
// 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( lpCmdLine, "+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 ) {
|
|
}
|