mirror of
https://github.com/Q3Rally-Team/rallyunlimited-engine.git
synced 2024-11-26 06:01:34 +00:00
815 lines
17 KiB
C
815 lines
17 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena 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 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// win_main.c
|
|
|
|
#include "../qcommon/q_shared.h"
|
|
#include "../qcommon/qcommon.h"
|
|
#ifndef DEDICATED
|
|
#include "../client/client.h"
|
|
#include "glw_win.h"
|
|
#endif
|
|
#include "win_local.h"
|
|
#include "resource.h"
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
|
|
|
|
#define MEM_THRESHOLD (96*1024*1024)
|
|
|
|
WinVars_t g_wv;
|
|
|
|
/*
|
|
==================
|
|
Sys_LowPhysicalMemory
|
|
==================
|
|
*/
|
|
qboolean Sys_LowPhysicalMemory( void ) {
|
|
MEMORYSTATUS stat;
|
|
GlobalMemoryStatus( &stat );
|
|
return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Sys_BeginProfiling
|
|
==================
|
|
*/
|
|
void Sys_BeginProfiling( void ) {
|
|
// this is just used on the mac build
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
Sys_Error
|
|
|
|
Show the early console as an error dialog
|
|
=============
|
|
*/
|
|
void QDECL Sys_Error( const char *error, ... ) {
|
|
va_list argptr;
|
|
char text[4096];
|
|
MSG msg;
|
|
|
|
va_start( argptr, error );
|
|
Q_vsnprintf( text, sizeof( text ), error, argptr );
|
|
va_end( argptr );
|
|
|
|
#ifndef DEDICATED
|
|
CL_Shutdown( text, qtrue );
|
|
#endif
|
|
|
|
Conbuf_AppendText( text );
|
|
Conbuf_AppendText( "\n" );
|
|
|
|
Sys_SetErrorText( text );
|
|
Sys_ShowConsole( 1, qtrue );
|
|
|
|
timeEndPeriod( 1 );
|
|
|
|
// wait for the user to quit
|
|
while ( 1 ) {
|
|
if ( GetMessage( &msg, NULL, 0, 0 ) <= 0 ) {
|
|
Cmd_Clear();
|
|
Com_Quit_f();
|
|
}
|
|
TranslateMessage( &msg );
|
|
DispatchMessage( &msg );
|
|
}
|
|
|
|
Sys_DestroyConsole();
|
|
|
|
exit( 1 );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Sys_Quit
|
|
==============
|
|
*/
|
|
void Sys_Quit( void ) {
|
|
|
|
timeEndPeriod( 1 );
|
|
|
|
Sys_DestroyConsole();
|
|
exit( 0 );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Sys_Print
|
|
==============
|
|
*/
|
|
void Sys_Print( const char *msg )
|
|
{
|
|
Conbuf_AppendText( msg );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Sys_Mkdir
|
|
==============
|
|
*/
|
|
void Sys_Mkdir( const char *path )
|
|
{
|
|
_mkdir( path );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Sys_FOpen
|
|
==============
|
|
*/
|
|
FILE *Sys_FOpen( const char *ospath, const char *mode )
|
|
{
|
|
size_t length;
|
|
|
|
// Windows API ignores all trailing spaces and periods which can get around Quake 3 file system restrictions.
|
|
length = strlen( ospath );
|
|
if ( length == 0 || ospath[length-1] == ' ' || ospath[length-1] == '.' ) {
|
|
return NULL;
|
|
}
|
|
|
|
return fopen( ospath, mode );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Sys_ResetReadOnlyAttribute
|
|
==============
|
|
*/
|
|
qboolean Sys_ResetReadOnlyAttribute( const char *ospath ) {
|
|
DWORD dwAttr;
|
|
|
|
dwAttr = GetFileAttributesA( ospath );
|
|
if ( dwAttr & FILE_ATTRIBUTE_READONLY ) {
|
|
dwAttr &= ~FILE_ATTRIBUTE_READONLY;
|
|
if ( SetFileAttributesA( ospath, dwAttr ) ) {
|
|
return qtrue;
|
|
} else {
|
|
return qfalse;
|
|
}
|
|
} else {
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Sys_Pwd
|
|
==============
|
|
*/
|
|
const char *Sys_Pwd( void )
|
|
{
|
|
static char pwd[ MAX_OSPATH ];
|
|
TCHAR buffer[ MAX_OSPATH ];
|
|
char *s;
|
|
|
|
if ( pwd[0] )
|
|
return pwd;
|
|
|
|
GetModuleFileName( NULL, buffer, ARRAY_LEN( buffer ) );
|
|
buffer[ ARRAY_LEN( buffer ) - 1 ] = '\0';
|
|
|
|
Q_strncpyz( pwd, WtoA( buffer ), sizeof( pwd ) );
|
|
|
|
s = strrchr( pwd, PATH_SEP );
|
|
if ( s )
|
|
*s = '\0';
|
|
else // bogus case?
|
|
{
|
|
_getcwd( pwd, sizeof( pwd ) - 1 );
|
|
pwd[ sizeof( pwd ) - 1 ] = '\0';
|
|
}
|
|
|
|
return pwd;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Sys_DefaultBasePath
|
|
==============
|
|
*/
|
|
const char *Sys_DefaultBasePath( void )
|
|
{
|
|
return Sys_Pwd();
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
DIRECTORY SCANNING
|
|
|
|
==============================================================
|
|
*/
|
|
|
|
void Sys_ListFilteredFiles( const char *basedir, const char *subdirs, const char *filter, char **list, int *numfiles ) {
|
|
char search[MAX_OSPATH*2+1];
|
|
char newsubdirs[MAX_OSPATH*2];
|
|
char filename[MAX_OSPATH*2];
|
|
intptr_t findhandle;
|
|
struct _finddata_t findinfo;
|
|
|
|
if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
|
|
return;
|
|
}
|
|
|
|
if ( *subdirs ) {
|
|
Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs );
|
|
}
|
|
else {
|
|
Com_sprintf( search, sizeof(search), "%s\\*", basedir );
|
|
}
|
|
|
|
findhandle = _findfirst (search, &findinfo);
|
|
if (findhandle == -1) {
|
|
return;
|
|
}
|
|
|
|
do {
|
|
if (findinfo.attrib & _A_SUBDIR) {
|
|
if ( !Q_streq( findinfo.name, "." ) && !Q_streq( findinfo.name, ".." ) ) {
|
|
if ( *subdirs ) {
|
|
Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name );
|
|
} else {
|
|
Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name );
|
|
}
|
|
Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles );
|
|
}
|
|
}
|
|
if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
|
|
break;
|
|
}
|
|
Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name );
|
|
if ( !Com_FilterPath( filter, filename ) )
|
|
continue;
|
|
list[ *numfiles ] = FS_CopyString( filename );
|
|
(*numfiles)++;
|
|
} while ( _findnext (findhandle, &findinfo) != -1 );
|
|
|
|
_findclose (findhandle);
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
Sys_Sleep
|
|
=============
|
|
*/
|
|
void Sys_Sleep( int msec ) {
|
|
|
|
if ( msec < 0 ) {
|
|
// special case: wait for event or network packet
|
|
DWORD dwResult;
|
|
msec = 300;
|
|
do {
|
|
dwResult = MsgWaitForMultipleObjects( 0, NULL, FALSE, msec, QS_ALLEVENTS );
|
|
} while ( dwResult == WAIT_TIMEOUT && NET_Sleep( 10 * 1000 ) );
|
|
//WaitMessage();
|
|
return;
|
|
}
|
|
|
|
// busy wait there because Sleep(0) will relinquish CPU - which is not what we want
|
|
//if ( msec == 0 )
|
|
// return;
|
|
|
|
Sleep ( msec );
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
Sys_ListFiles
|
|
=============
|
|
*/
|
|
char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs ) {
|
|
char search[MAX_OSPATH*2+MAX_QPATH+1];
|
|
int nfiles;
|
|
char **listCopy;
|
|
char *list[MAX_FOUND_FILES];
|
|
struct _finddata_t findinfo;
|
|
intptr_t findhandle;
|
|
int flag;
|
|
int extLen;
|
|
int length;
|
|
int i;
|
|
const char *x;
|
|
qboolean hasPatterns;
|
|
|
|
if ( filter ) {
|
|
|
|
nfiles = 0;
|
|
Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
|
|
|
|
list[ nfiles ] = NULL;
|
|
*numfiles = nfiles;
|
|
|
|
if (!nfiles)
|
|
return NULL;
|
|
|
|
listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( listCopy[0] ) );
|
|
for ( i = 0 ; i < nfiles ; i++ ) {
|
|
listCopy[i] = list[i];
|
|
}
|
|
listCopy[i] = NULL;
|
|
|
|
return listCopy;
|
|
}
|
|
|
|
if ( !extension ) {
|
|
extension = "";
|
|
}
|
|
|
|
// passing a slash as extension will find directories
|
|
if ( extension[0] == '/' && extension[1] == 0 ) {
|
|
extension = "";
|
|
flag = 0;
|
|
} else {
|
|
flag = _A_SUBDIR;
|
|
}
|
|
|
|
Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension );
|
|
|
|
findhandle = _findfirst( search, &findinfo );
|
|
if ( findhandle == -1 ) {
|
|
*numfiles = 0;
|
|
return NULL;
|
|
}
|
|
|
|
extLen = (int)strlen( extension );
|
|
hasPatterns = Com_HasPatterns( extension );
|
|
if ( hasPatterns && extension[0] == '.' && extension[1] != '\0' ) {
|
|
extension++;
|
|
}
|
|
|
|
// search
|
|
nfiles = 0;
|
|
|
|
do {
|
|
if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) {
|
|
if ( nfiles == MAX_FOUND_FILES - 1 ) {
|
|
break;
|
|
}
|
|
if ( *extension ) {
|
|
if ( hasPatterns ) {
|
|
x = strrchr( findinfo.name, '.' );
|
|
if ( !x || !Com_FilterExt( extension, x+1 ) ) {
|
|
continue;
|
|
}
|
|
} else {
|
|
length = strlen( findinfo.name );
|
|
if ( length < extLen || Q_stricmp( findinfo.name + length - extLen, extension ) ) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
list[ nfiles ] = FS_CopyString( findinfo.name );
|
|
nfiles++;
|
|
}
|
|
} while ( _findnext (findhandle, &findinfo) != -1 );
|
|
|
|
list[ nfiles ] = NULL;
|
|
|
|
_findclose (findhandle);
|
|
|
|
// return a copy of the list
|
|
*numfiles = nfiles;
|
|
|
|
if ( !nfiles ) {
|
|
return NULL;
|
|
}
|
|
|
|
listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( listCopy[0] ) );
|
|
for ( i = 0 ; i < nfiles ; i++ ) {
|
|
listCopy[i] = list[i];
|
|
}
|
|
listCopy[i] = NULL;
|
|
|
|
Com_SortFileList( listCopy, nfiles, extension[0] != '\0' );
|
|
|
|
return listCopy;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
Sys_FreeFileList
|
|
=============
|
|
*/
|
|
void Sys_FreeFileList( char **list ) {
|
|
int i;
|
|
|
|
if ( !list ) {
|
|
return;
|
|
}
|
|
|
|
for ( i = 0 ; list[i] ; i++ ) {
|
|
Z_Free( list[i] );
|
|
}
|
|
|
|
Z_Free( list );
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
Sys_GetFileStats
|
|
=============
|
|
*/
|
|
qboolean Sys_GetFileStats( const char *filename, fileOffset_t *size, fileTime_t *mtime, fileTime_t *ctime ) {
|
|
struct _stat s;
|
|
|
|
if ( _stat( filename, &s ) == 0 ) {
|
|
*size = (fileOffset_t)s.st_size;
|
|
*mtime = (fileTime_t)s.st_mtime;
|
|
*ctime = (fileTime_t)s.st_ctime;
|
|
return qtrue;
|
|
} else {
|
|
*size = 0;
|
|
*mtime = *ctime = 0;
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
|
|
//========================================================
|
|
|
|
/*
|
|
========================================================================
|
|
|
|
LOAD/UNLOAD DLL
|
|
|
|
========================================================================
|
|
*/
|
|
|
|
static int dll_err_count = 0;
|
|
|
|
/*
|
|
=================
|
|
Sys_LoadLibrary
|
|
=================
|
|
*/
|
|
void *Sys_LoadLibrary( const char *name )
|
|
{
|
|
const char *ext;
|
|
|
|
if ( !name || !*name )
|
|
return NULL;
|
|
|
|
if ( FS_AllowedExtension( name, qfalse, &ext ) )
|
|
{
|
|
Com_Error( ERR_FATAL, "Sys_LoadLibrary: Unable to load library with '%s' extension", ext );
|
|
}
|
|
|
|
return (void *)LoadLibrary( AtoW( name ) );
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Sys_LoadFunction
|
|
=================
|
|
*/
|
|
void *Sys_LoadFunction( void *handle, const char *name )
|
|
{
|
|
void *symbol;
|
|
|
|
if ( handle == NULL || name == NULL || *name == '\0' )
|
|
{
|
|
dll_err_count++;
|
|
return NULL;
|
|
}
|
|
|
|
symbol = GetProcAddress( handle, name );
|
|
if ( !symbol )
|
|
dll_err_count++;
|
|
|
|
return symbol;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Sys_LoadFunctionErrors
|
|
=================
|
|
*/
|
|
int Sys_LoadFunctionErrors( void )
|
|
{
|
|
int result = dll_err_count;
|
|
dll_err_count = 0;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Sys_UnloadLibrary
|
|
=================
|
|
*/
|
|
void Sys_UnloadLibrary( void *handle )
|
|
{
|
|
if ( handle )
|
|
FreeLibrary( handle );
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Sys_SendKeyEvents
|
|
|
|
Platform-dependent event handling
|
|
=================
|
|
*/
|
|
void Sys_SendKeyEvents( void )
|
|
{
|
|
#ifndef DEDICATED
|
|
if ( !com_dedicated->integer )
|
|
HandleEvents();
|
|
else
|
|
#endif
|
|
HandleConsoleEvents();
|
|
}
|
|
|
|
|
|
//================================================================
|
|
|
|
|
|
/*
|
|
==================
|
|
SetTimerResolution
|
|
|
|
Try to set lower timer period
|
|
==================
|
|
*/
|
|
static void SetTimerResolution( void )
|
|
{
|
|
typedef HRESULT (WINAPI *pfnNtQueryTimerResolution)( PULONG MinRes, PULONG MaxRes, PULONG CurRes );
|
|
typedef HRESULT (WINAPI *pfnNtSetTimerResolution)( ULONG NewRes, BOOLEAN SetRes, PULONG CurRes );
|
|
pfnNtQueryTimerResolution pNtQueryTimerResolution;
|
|
pfnNtSetTimerResolution pNtSetTimerResolution;
|
|
ULONG curr, minr, maxr;
|
|
HMODULE dll;
|
|
|
|
dll = LoadLibrary( T( "ntdll" ) );
|
|
if ( dll )
|
|
{
|
|
pNtQueryTimerResolution = (pfnNtQueryTimerResolution) GetProcAddress( dll, "NtQueryTimerResolution" );
|
|
pNtSetTimerResolution = (pfnNtSetTimerResolution) GetProcAddress( dll, "NtSetTimerResolution" );
|
|
if ( pNtQueryTimerResolution && pNtSetTimerResolution )
|
|
{
|
|
pNtQueryTimerResolution( &minr, &maxr, &curr );
|
|
if ( maxr < 5000 ) // well, we don't need less than 0.5ms periods for select()
|
|
maxr = 5000;
|
|
pNtSetTimerResolution( maxr, TRUE, &curr );
|
|
}
|
|
FreeLibrary( dll );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Sys_Init
|
|
|
|
Called after the common systems (cvars, files, etc)
|
|
are initialized
|
|
================
|
|
*/
|
|
void Sys_Init( void ) {
|
|
|
|
// make sure the timer is high precision, otherwise
|
|
// NT gets 18ms resolution
|
|
timeBeginPeriod( 1 );
|
|
|
|
SetTimerResolution();
|
|
|
|
Cvar_Set( "arch", "winnt" );
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
|
|
/*
|
|
==================
|
|
SetDPIAwareness
|
|
==================
|
|
*/
|
|
#if 0
|
|
static void SetDPIAwareness( void )
|
|
{
|
|
typedef HANDLE (WINAPI *pfnSetThreadDpiAwarenessContext)( HANDLE dpiContext );
|
|
typedef HRESULT (WINAPI *pfnSetProcessDpiAwareness)( int value );
|
|
|
|
pfnSetThreadDpiAwarenessContext pSetThreadDpiAwarenessContext;
|
|
pfnSetProcessDpiAwareness pSetProcessDpiAwareness;
|
|
HMODULE dll;
|
|
|
|
dll = GetModuleHandle( T("user32") );
|
|
if ( dll )
|
|
{
|
|
pSetThreadDpiAwarenessContext = (pfnSetThreadDpiAwarenessContext) GetProcAddress( dll, "SetThreadDpiAwarenessContext" );
|
|
if ( pSetThreadDpiAwarenessContext )
|
|
{
|
|
pSetThreadDpiAwarenessContext( (HANDLE)(intptr_t)-2 ); // DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
|
|
}
|
|
|
|
}
|
|
|
|
dll = LoadLibrary( T("shcore") );
|
|
if ( dll )
|
|
{
|
|
pSetProcessDpiAwareness = (pfnSetProcessDpiAwareness) GetProcAddress( dll, "SetProcessDpiAwareness" );
|
|
if ( pSetProcessDpiAwareness )
|
|
{
|
|
pSetProcessDpiAwareness( 2 ); // PROCESS_PER_MONITOR_DPI_AWARE
|
|
}
|
|
FreeLibrary( dll );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
static const char *GetExceptionName( DWORD code )
|
|
{
|
|
static char buf[ 32 ];
|
|
|
|
switch ( code )
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION: return "ACCESS_VIOLATION";
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT: return "DATATYPE_MISALIGNMENT";
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "ARRAY_BOUNDS_EXCEEDED";
|
|
case EXCEPTION_PRIV_INSTRUCTION: return "PRIV_INSTRUCTION";
|
|
case EXCEPTION_IN_PAGE_ERROR: return "IN_PAGE_ERROR";
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL_INSTRUCTION";
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "NONCONTINUABLE_EXCEPTION";
|
|
case EXCEPTION_STACK_OVERFLOW: return "STACK_OVERFLOW";
|
|
case EXCEPTION_INVALID_DISPOSITION: return "INVALID_DISPOSITION";
|
|
case EXCEPTION_GUARD_PAGE: return "GUARD_PAGE";
|
|
case EXCEPTION_INVALID_HANDLE: return "INVALID_HANDLE";
|
|
default: break;
|
|
}
|
|
|
|
sprintf( buf, "0x%08X", (unsigned int)code );
|
|
return buf;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
ExceptionFilter
|
|
|
|
Restore gamma and hide fullscreen window in case of crash
|
|
==================
|
|
*/
|
|
static LONG WINAPI ExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
|
|
{
|
|
#ifndef DEDICATED
|
|
if ( com_dedicated->integer == 0 ) {
|
|
extern cvar_t *com_cl_running;
|
|
if ( com_cl_running && com_cl_running->integer ) {
|
|
// assume we can restart client module
|
|
} else {
|
|
GLW_RestoreGamma();
|
|
|
|
if ( g_wv.hWnd && glw_state.cdsFullscreen )
|
|
ShowWindow( g_wv.hWnd, SW_HIDE );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( ExceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT )
|
|
{
|
|
char msg[128];
|
|
byte *addr, *base;
|
|
qboolean vma;
|
|
|
|
addr = (byte*)ExceptionInfo->ExceptionRecord->ExceptionAddress;
|
|
base = (byte*)GetModuleHandle( NULL );
|
|
|
|
if ( addr >= base )
|
|
{
|
|
addr = (byte*)(addr - base);
|
|
vma = qtrue;
|
|
}
|
|
else
|
|
{
|
|
vma = qfalse;
|
|
}
|
|
|
|
sprintf( msg, "Exception Code: %s\nException Address: %p%s",
|
|
GetExceptionName( ExceptionInfo->ExceptionRecord->ExceptionCode ),
|
|
addr, vma ? " (VMA)" : "" );
|
|
|
|
Com_Error( ERR_DROP, "Unhandled exception caught\n%s", msg );
|
|
}
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
WinMain
|
|
==================
|
|
*/
|
|
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
|
|
{
|
|
static char sys_cmdline[ MAX_STRING_CHARS ];
|
|
char con_title[ MAX_CVAR_VALUE_STRING ];
|
|
int xpos, ypos;
|
|
qboolean useXYpos;
|
|
HANDLE hProcess;
|
|
DWORD dwPriority;
|
|
|
|
// should never get a previous instance in Win32
|
|
if ( hPrevInstance ) {
|
|
return 0;
|
|
}
|
|
|
|
// slightly boost process priority if it set to default
|
|
hProcess = GetCurrentProcess();
|
|
dwPriority = GetPriorityClass( hProcess );
|
|
if ( dwPriority == NORMAL_PRIORITY_CLASS || dwPriority == ABOVE_NORMAL_PRIORITY_CLASS ) {
|
|
SetPriorityClass( hProcess, HIGH_PRIORITY_CLASS );
|
|
}
|
|
|
|
//SetDPIAwareness();
|
|
|
|
g_wv.hInstance = hInstance;
|
|
Q_strncpyz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
|
|
|
|
useXYpos = Com_EarlyParseCmdLine( sys_cmdline, con_title, sizeof( con_title ), &xpos, &ypos );
|
|
|
|
// done before Com/Sys_Init since we need this for error output
|
|
Sys_CreateConsole( con_title, xpos, ypos, useXYpos );
|
|
|
|
// no abort/retry/fail errors
|
|
SetErrorMode( SEM_FAILCRITICALERRORS );
|
|
|
|
SetUnhandledExceptionFilter( ExceptionFilter );
|
|
|
|
// get the initial time base
|
|
Sys_Milliseconds();
|
|
|
|
Com_Init( sys_cmdline );
|
|
NET_Init();
|
|
|
|
Com_Printf( "Working directory: %s\n", Sys_Pwd() );
|
|
|
|
// hide the early console since we've reached the point where we
|
|
// have a working graphics subsystems
|
|
if ( !com_dedicated->integer && !com_viewlog->integer ) {
|
|
Sys_ShowConsole( 0, qfalse );
|
|
}
|
|
|
|
// main game loop
|
|
while( 1 ) {
|
|
// set low precision every frame, because some system calls
|
|
// reset it arbitrarily
|
|
// _controlfp( _PC_24, _MCW_PC );
|
|
// _controlfp( -1, _MCW_EM ); // no exceptions, even if some crappy syscall turns them back on!
|
|
|
|
#ifdef DEDICATED
|
|
// run the game
|
|
Com_Frame( qfalse );
|
|
#else
|
|
// make sure mouse and joystick are only called once a frame
|
|
IN_Frame();
|
|
// run the game
|
|
Com_Frame( CL_NoDelay() );
|
|
#endif
|
|
}
|
|
|
|
// never gets here
|
|
return 0;
|
|
}
|