dhewm3/neo/sys/win32/win_main.cpp
2012-01-07 19:00:43 +01:00

749 lines
16 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
#include <SDL_main.h>
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_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_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);
printf("%s", text);
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';
printf("%s", msg);
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 );
printf("%s", msg);
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';
printf("%s", msg);
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( (uintptr_t)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 );
}
}
/*
================
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
================
*/
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 );
#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 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" );
}
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();
}
}
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
// 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();
// 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 ) {
}