]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/win32/win_main.cpp
hello world
[icculus/iodoom3.git] / neo / sys / win32 / win_main.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include <errno.h>
33 #include <float.h>
34 #include <fcntl.h>
35 #include <direct.h>
36 #include <io.h>
37 #include <conio.h>
38 #include <mapi.h>
39 #include <ShellAPI.h>
40
41 #ifndef __MRC__
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #endif
45
46 #include "../sys_local.h"
47 #include "win_local.h"
48 #include "rc/CreateResourceIDs.h"
49 #include "../../renderer/tr_local.h"
50
51 idCVar Win32Vars_t::sys_arch( "sys_arch", "", CVAR_SYSTEM | CVAR_INIT, "" );
52 idCVar Win32Vars_t::sys_cpustring( "sys_cpustring", "detect", CVAR_SYSTEM | CVAR_INIT, "" );
53 idCVar Win32Vars_t::in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_BOOL, "enable mouse input" );
54 idCVar Win32Vars_t::win_allowAltTab( "win_allowAltTab", "0", CVAR_SYSTEM | CVAR_BOOL, "allow Alt-Tab when fullscreen" );
55 idCVar Win32Vars_t::win_notaskkeys( "win_notaskkeys", "0", CVAR_SYSTEM | CVAR_INTEGER, "disable windows task keys" );
56 idCVar Win32Vars_t::win_username( "win_username", "", CVAR_SYSTEM | CVAR_INIT, "windows user name" );
57 idCVar Win32Vars_t::win_xpos( "win_xpos", "3", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "horizontal position of window" );
58 idCVar Win32Vars_t::win_ypos( "win_ypos", "22", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "vertical position of window" );
59 idCVar Win32Vars_t::win_outputDebugString( "win_outputDebugString", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
60 idCVar Win32Vars_t::win_outputEditString( "win_outputEditString", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
61 idCVar Win32Vars_t::win_viewlog( "win_viewlog", "0", CVAR_SYSTEM | CVAR_INTEGER, "" );
62 idCVar Win32Vars_t::win_timerUpdate( "win_timerUpdate", "0", CVAR_SYSTEM | CVAR_BOOL, "allows the game to be updated while dragging the window" );
63 idCVar Win32Vars_t::win_allowMultipleInstances( "win_allowMultipleInstances", "0", CVAR_SYSTEM | CVAR_BOOL, "allow multiple instances running concurrently" );
64
65 Win32Vars_t     win32;
66
67 static char             sys_cmdline[MAX_STRING_CHARS];
68
69 // not a hard limit, just what we keep track of for debugging
70 xthreadInfo *g_threads[MAX_THREADS];
71
72 int g_thread_count = 0;
73
74 static sysMemoryStats_t exeLaunchMemoryStats;
75
76 static  xthreadInfo     threadInfo;
77 static  HANDLE          hTimer;
78
79 /*
80 ================
81 Sys_GetExeLaunchMemoryStatus
82 ================
83 */
84 void Sys_GetExeLaunchMemoryStatus( sysMemoryStats_t &stats ) {
85         stats = exeLaunchMemoryStats;
86 }
87
88 /*
89 ==================
90 Sys_Createthread
91 ==================
92 */
93 void Sys_CreateThread(  xthread_t function, void *parms, xthreadPriority priority, xthreadInfo &info, const char *name, xthreadInfo *threads[MAX_THREADS], int *thread_count ) {
94         HANDLE temp = CreateThread(     NULL,   // LPSECURITY_ATTRIBUTES lpsa,
95                                                                         0,              // DWORD cbStack,
96                                                                         (LPTHREAD_START_ROUTINE)function,       // LPTHREAD_START_ROUTINE lpStartAddr,
97                                                                         parms,  // LPVOID lpvThreadParm,
98                                                                         0,              //   DWORD fdwCreate,
99                                                                         &info.threadId);
100         info.threadHandle = (int) temp;
101         if (priority == THREAD_HIGHEST) {
102                 SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_HIGHEST );                //  we better sleep enough to do this
103         } else if (priority == THREAD_ABOVE_NORMAL ) {
104                 SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_ABOVE_NORMAL );
105         }
106         info.name = name;
107         if ( *thread_count < MAX_THREADS ) {
108                 threads[(*thread_count)++] = &info;
109         } else {
110                 common->DPrintf("WARNING: MAX_THREADS reached\n");
111         }
112 }
113
114 /*
115 ==================
116 Sys_DestroyThread
117 ==================
118 */
119 void Sys_DestroyThread( xthreadInfo& info ) {
120         WaitForSingleObject( (HANDLE)info.threadHandle, INFINITE);
121         CloseHandle( (HANDLE)info.threadHandle );
122         info.threadHandle = 0;
123 }
124
125 /*
126 ==================
127 Sys_Sentry
128 ==================
129 */
130 void Sys_Sentry() {
131         int j = 0;
132 }
133
134 /*
135 ==================
136 Sys_GetThreadName
137 ==================
138 */
139 const char* Sys_GetThreadName(int *index) {
140         int id = GetCurrentThreadId();
141         for( int i = 0; i < g_thread_count; i++ ) {
142                 if ( id == g_threads[i]->threadId ) {
143                         if ( index ) {
144                                 *index = i;
145                         }
146                         return g_threads[i]->name;
147                 }
148         }
149         if ( index ) {
150                 *index = -1;
151         }
152         return "main";
153 }
154
155
156 /*
157 ==================
158 Sys_EnterCriticalSection
159 ==================
160 */
161 void Sys_EnterCriticalSection( int index ) {
162         assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
163         if ( TryEnterCriticalSection( &win32.criticalSections[index] ) == 0 ) {
164                 EnterCriticalSection( &win32.criticalSections[index] );
165 //              Sys_DebugPrintf( "busy lock '%s' in thread '%s'\n", lock->name, Sys_GetThreadName() );
166         }
167 }
168
169 /*
170 ==================
171 Sys_LeaveCriticalSection
172 ==================
173 */
174 void Sys_LeaveCriticalSection( int index ) {
175         assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
176         LeaveCriticalSection( &win32.criticalSections[index] );
177 }
178
179 /*
180 ==================
181 Sys_WaitForEvent
182 ==================
183 */
184 void Sys_WaitForEvent( int index ) {
185         assert( index == 0 );
186         if ( !win32.backgroundDownloadSemaphore ) {
187                 win32.backgroundDownloadSemaphore = CreateEvent( NULL, TRUE, FALSE, NULL );
188         }
189         WaitForSingleObject( win32.backgroundDownloadSemaphore, INFINITE );
190         ResetEvent( win32.backgroundDownloadSemaphore );
191 }
192
193 /*
194 ==================
195 Sys_TriggerEvent
196 ==================
197 */
198 void Sys_TriggerEvent( int index ) {
199         assert( index == 0 );
200         SetEvent( win32.backgroundDownloadSemaphore );
201 }
202
203
204
205 #pragma optimize( "", on )
206
207 #ifdef DEBUG
208
209
210 static unsigned int debug_total_alloc = 0;
211 static unsigned int debug_total_alloc_count = 0;
212 static unsigned int debug_current_alloc = 0;
213 static unsigned int debug_current_alloc_count = 0;
214 static unsigned int debug_frame_alloc = 0;
215 static unsigned int debug_frame_alloc_count = 0;
216
217 idCVar sys_showMallocs( "sys_showMallocs", "0", CVAR_SYSTEM, "" );
218
219 // _HOOK_ALLOC, _HOOK_REALLOC, _HOOK_FREE
220
221 typedef struct CrtMemBlockHeader
222 {
223         struct _CrtMemBlockHeader *pBlockHeaderNext;    // Pointer to the block allocated just before this one:
224         struct _CrtMemBlockHeader *pBlockHeaderPrev;    // Pointer to the block allocated just after this one
225    char *szFileName;    // File name
226    int nLine;           // Line number
227    size_t nDataSize;    // Size of user block
228    int nBlockUse;       // Type of block
229    long lRequest;       // Allocation number
230         byte            gap[4];                                                         // Buffer just before (lower than) the user's memory:
231 } CrtMemBlockHeader;
232
233 #include <crtdbg.h>
234
235 /*
236 ==================
237 Sys_AllocHook
238
239         called for every malloc/new/free/delete
240 ==================
241 */
242 int Sys_AllocHook( int nAllocType, void *pvData, size_t nSize, int nBlockUse, long lRequest, const unsigned char * szFileName, int nLine ) 
243 {
244         CrtMemBlockHeader       *pHead;
245         byte                            *temp;
246
247         if ( nBlockUse == _CRT_BLOCK )
248         {
249       return( TRUE );
250         }
251
252         // get a pointer to memory block header
253         temp = ( byte * )pvData;
254         temp -= 32;
255         pHead = ( CrtMemBlockHeader * )temp;
256
257         switch( nAllocType ) {
258                 case    _HOOK_ALLOC:
259                         debug_total_alloc += nSize;
260                         debug_current_alloc += nSize;
261                         debug_frame_alloc += nSize;
262                         debug_total_alloc_count++;
263                         debug_current_alloc_count++;
264                         debug_frame_alloc_count++;
265                         break;
266
267                 case    _HOOK_FREE:
268                         assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
269
270                         debug_current_alloc -= pHead->nDataSize;
271                         debug_current_alloc_count--;
272                         debug_total_alloc_count++;
273                         debug_frame_alloc_count++;
274                         break;
275
276                 case    _HOOK_REALLOC:
277                         assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
278
279                         debug_current_alloc -= pHead->nDataSize;
280                         debug_total_alloc += nSize;
281                         debug_current_alloc += nSize;
282                         debug_frame_alloc += nSize;
283                         debug_total_alloc_count++;
284                         debug_current_alloc_count--;
285                         debug_frame_alloc_count++;
286                         break;
287         }
288         return( TRUE );
289 }
290
291 /*
292 ==================
293 Sys_DebugMemory_f
294 ==================
295 */
296 void Sys_DebugMemory_f( void ) {
297         common->Printf( "Total allocation %8dk in %d blocks\n", debug_total_alloc / 1024, debug_total_alloc_count );
298         common->Printf( "Current allocation %8dk in %d blocks\n", debug_current_alloc / 1024, debug_current_alloc_count );
299 }
300
301 /*
302 ==================
303 Sys_MemFrame
304 ==================
305 */
306 void Sys_MemFrame( void ) {
307         if( sys_showMallocs.GetInteger() ) {
308                 common->Printf("Frame: %8dk in %5d blocks\n", debug_frame_alloc / 1024, debug_frame_alloc_count );
309         }
310
311         debug_frame_alloc = 0;
312         debug_frame_alloc_count = 0;
313 }
314
315 #endif
316
317 /*
318 ==================
319 Sys_FlushCacheMemory
320
321 On windows, the vertex buffers are write combined, so they
322 don't need to be flushed from the cache
323 ==================
324 */
325 void Sys_FlushCacheMemory( void *base, int bytes ) {
326 }
327
328 /*
329 =============
330 Sys_Error
331
332 Show the early console as an error dialog
333 =============
334 */
335 void Sys_Error( const char *error, ... ) {
336         va_list         argptr;
337         char            text[4096];
338     MSG        msg;
339
340         va_start( argptr, error );
341         vsprintf( text, error, argptr );
342         va_end( argptr);
343
344         Conbuf_AppendText( text );
345         Conbuf_AppendText( "\n" );
346
347         Win_SetErrorText( text );
348         Sys_ShowConsole( 1, true );
349
350         timeEndPeriod( 1 );
351
352         Sys_ShutdownInput();
353
354         GLimp_Shutdown();
355
356         // wait for the user to quit
357         while ( 1 ) {
358                 if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
359                         common->Quit();
360                 }
361                 TranslateMessage( &msg );
362         DispatchMessage( &msg );
363         }
364
365         Sys_DestroyConsole();
366
367         exit (1);
368 }
369
370 /*
371 ==============
372 Sys_Quit
373 ==============
374 */
375 void Sys_Quit( void ) {
376         timeEndPeriod( 1 );
377         Sys_ShutdownInput();
378         Sys_DestroyConsole();
379         ExitProcess( 0 );
380 }
381
382
383 /*
384 ==============
385 Sys_Printf
386 ==============
387 */
388 #define MAXPRINTMSG 4096
389 void Sys_Printf( const char *fmt, ... ) {
390         char            msg[MAXPRINTMSG];
391
392         va_list argptr;
393         va_start(argptr, fmt);
394         idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
395         va_end(argptr);
396         msg[sizeof(msg)-1] = '\0';
397
398         if ( win32.win_outputDebugString.GetBool() ) {
399                 OutputDebugString( msg );
400         }
401         if ( win32.win_outputEditString.GetBool() ) {
402                 Conbuf_AppendText( msg );
403         }
404 }
405
406 /*
407 ==============
408 Sys_DebugPrintf
409 ==============
410 */
411 #define MAXPRINTMSG 4096
412 void Sys_DebugPrintf( const char *fmt, ... ) {
413         char msg[MAXPRINTMSG];
414
415         va_list argptr;
416         va_start( argptr, fmt );
417         idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
418         msg[ sizeof(msg)-1 ] = '\0';
419         va_end( argptr );
420
421         OutputDebugString( msg );
422 }
423
424 /*
425 ==============
426 Sys_DebugVPrintf
427 ==============
428 */
429 void Sys_DebugVPrintf( const char *fmt, va_list arg ) {
430         char msg[MAXPRINTMSG];
431
432         idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, arg );
433         msg[ sizeof(msg)-1 ] = '\0';
434
435         OutputDebugString( msg );
436 }
437
438 /*
439 ==============
440 Sys_Sleep
441 ==============
442 */
443 void Sys_Sleep( int msec ) {
444         Sleep( msec );
445 }
446
447 /*
448 ==============
449 Sys_ShowWindow
450 ==============
451 */
452 void Sys_ShowWindow( bool show ) {
453         ::ShowWindow( win32.hWnd, show ? SW_SHOW : SW_HIDE );
454 }
455
456 /*
457 ==============
458 Sys_IsWindowVisible
459 ==============
460 */
461 bool Sys_IsWindowVisible( void ) {
462         return ( ::IsWindowVisible( win32.hWnd ) != 0 );
463 }
464
465 /*
466 ==============
467 Sys_Mkdir
468 ==============
469 */
470 void Sys_Mkdir( const char *path ) {
471         _mkdir (path);
472 }
473
474 /*
475 =================
476 Sys_FileTimeStamp
477 =================
478 */
479 ID_TIME_T Sys_FileTimeStamp( FILE *fp ) {
480         struct _stat st;
481         _fstat( _fileno( fp ), &st );
482         return (long) st.st_mtime;
483 }
484
485 /*
486 ==============
487 Sys_Cwd
488 ==============
489 */
490 const char *Sys_Cwd( void ) {
491         static char cwd[MAX_OSPATH];
492
493         _getcwd( cwd, sizeof( cwd ) - 1 );
494         cwd[MAX_OSPATH-1] = 0;
495
496         return cwd;
497 }
498
499 /*
500 ==============
501 Sys_DefaultCDPath
502 ==============
503 */
504 const char *Sys_DefaultCDPath( void ) {
505         return "";
506 }
507
508 /*
509 ==============
510 Sys_DefaultBasePath
511 ==============
512 */
513 const char *Sys_DefaultBasePath( void ) {
514         return Sys_Cwd();
515 }
516
517 /*
518 ==============
519 Sys_DefaultSavePath
520 ==============
521 */
522 const char *Sys_DefaultSavePath( void ) {
523         return cvarSystem->GetCVarString( "fs_basepath" );
524 }
525
526 /*
527 ==============
528 Sys_EXEPath
529 ==============
530 */
531 const char *Sys_EXEPath( void ) {
532         static char exe[ MAX_OSPATH ];
533         GetModuleFileName( NULL, exe, sizeof( exe ) - 1 );
534         return exe;
535 }
536
537 /*
538 ==============
539 Sys_ListFiles
540 ==============
541 */
542 int Sys_ListFiles( const char *directory, const char *extension, idStrList &list ) {
543         idStr           search;
544         struct _finddata_t findinfo;
545         int                     findhandle;
546         int                     flag;
547
548         if ( !extension) {
549                 extension = "";
550         }
551
552         // passing a slash as extension will find directories
553         if ( extension[0] == '/' && extension[1] == 0 ) {
554                 extension = "";
555                 flag = 0;
556         } else {
557                 flag = _A_SUBDIR;
558         }
559
560         sprintf( search, "%s\\*%s", directory, extension );
561
562         // search
563         list.Clear();
564
565         findhandle = _findfirst( search, &findinfo );
566         if ( findhandle == -1 ) {
567                 return -1;
568         }
569
570         do {
571                 if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) {
572                         list.Append( findinfo.name );
573                 }
574         } while ( _findnext( findhandle, &findinfo ) != -1 );
575
576         _findclose( findhandle );
577
578         return list.Num();
579 }
580
581
582 /*
583 ================
584 Sys_GetClipboardData
585 ================
586 */
587 char *Sys_GetClipboardData( void ) {
588         char *data = NULL;
589         char *cliptext;
590
591         if ( OpenClipboard( NULL ) != 0 ) {
592                 HANDLE hClipboardData;
593
594                 if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) {
595                         if ( ( cliptext = (char *)GlobalLock( hClipboardData ) ) != 0 ) {
596                                 data = (char *)Mem_Alloc( GlobalSize( hClipboardData ) + 1 );
597                                 strcpy( data, cliptext );
598                                 GlobalUnlock( hClipboardData );
599                                 
600                                 strtok( data, "\n\r\b" );
601                         }
602                 }
603                 CloseClipboard();
604         }
605         return data;
606 }
607
608 /*
609 ================
610 Sys_SetClipboardData
611 ================
612 */
613 void Sys_SetClipboardData( const char *string ) {
614         HGLOBAL HMem;
615         char *PMem;
616
617         // allocate memory block
618         HMem = (char *)::GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, strlen( string ) + 1 );
619         if ( HMem == NULL ) {
620                 return;
621         }
622         // lock allocated memory and obtain a pointer
623         PMem = (char *)::GlobalLock( HMem );
624         if ( PMem == NULL ) {
625                 return;
626         }
627         // copy text into allocated memory block
628         lstrcpy( PMem, string );
629         // unlock allocated memory
630         ::GlobalUnlock( HMem );
631         // open Clipboard
632         if ( !OpenClipboard( 0 ) ) {
633                 ::GlobalFree( HMem );
634                 return;
635         }
636         // remove current Clipboard contents
637         EmptyClipboard();
638         // supply the memory handle to the Clipboard
639         SetClipboardData( CF_TEXT, HMem );
640         HMem = 0;
641         // close Clipboard
642         CloseClipboard();
643 }
644
645 /*
646 ========================================================================
647
648 DLL Loading
649
650 ========================================================================
651 */
652
653 /*
654 =====================
655 Sys_DLL_Load
656 =====================
657 */
658 int Sys_DLL_Load( const char *dllName ) {
659         HINSTANCE       libHandle;
660         libHandle = LoadLibrary( dllName );
661         if ( libHandle ) {
662                 // since we can't have LoadLibrary load only from the specified path, check it did the right thing
663                 char loadedPath[ MAX_OSPATH ];
664                 GetModuleFileName( libHandle, loadedPath, sizeof( loadedPath ) - 1 );
665                 if ( idStr::IcmpPath( dllName, loadedPath ) ) {
666                         Sys_Printf( "ERROR: LoadLibrary '%s' wants to load '%s'\n", dllName, loadedPath );
667                         Sys_DLL_Unload( (int)libHandle );
668                         return 0;
669                 }
670         }
671         return (int)libHandle;
672 }
673
674 /*
675 =====================
676 Sys_DLL_GetProcAddress
677 =====================
678 */
679 void *Sys_DLL_GetProcAddress( int dllHandle, const char *procName ) {
680         return GetProcAddress( (HINSTANCE)dllHandle, procName ); 
681 }
682
683 /*
684 =====================
685 Sys_DLL_Unload
686 =====================
687 */
688 void Sys_DLL_Unload( int dllHandle ) {
689         if ( !dllHandle ) {
690                 return;
691         }
692         if ( FreeLibrary( (HINSTANCE)dllHandle ) == 0 ) {
693                 int lastError = GetLastError();
694                 LPVOID lpMsgBuf;
695                 FormatMessage(
696                         FORMAT_MESSAGE_ALLOCATE_BUFFER,
697                     NULL,
698                         lastError,
699                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
700                         (LPTSTR) &lpMsgBuf,
701                         0,
702                         NULL 
703                 );
704                 Sys_Error( "Sys_DLL_Unload: FreeLibrary failed - %s (%d)", lpMsgBuf, lastError );
705         }
706 }
707
708 /*
709 ========================================================================
710
711 EVENT LOOP
712
713 ========================================================================
714 */
715
716 #define MAX_QUED_EVENTS         256
717 #define MASK_QUED_EVENTS        ( MAX_QUED_EVENTS - 1 )
718
719 sysEvent_t      eventQue[MAX_QUED_EVENTS];
720 int                     eventHead = 0;
721 int                     eventTail = 0;
722
723 /*
724 ================
725 Sys_QueEvent
726
727 Ptr should either be null, or point to a block of data that can
728 be freed by the game later.
729 ================
730 */
731 void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
732         sysEvent_t      *ev;
733
734         ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
735
736         if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
737                 common->Printf("Sys_QueEvent: overflow\n");
738                 // we are discarding an event, but don't leak memory
739                 if ( ev->evPtr ) {
740                         Mem_Free( ev->evPtr );
741                 }
742                 eventTail++;
743         }
744
745         eventHead++;
746
747         ev->evType = type;
748         ev->evValue = value;
749         ev->evValue2 = value2;
750         ev->evPtrLength = ptrLength;
751         ev->evPtr = ptr;
752 }
753
754 /*
755 =============
756 Sys_PumpEvents
757
758 This allows windows to be moved during renderbump
759 =============
760 */
761 void Sys_PumpEvents( void ) {
762     MSG msg;
763
764         // pump the message loop
765         while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {
766                 if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
767                         common->Quit();
768                 }
769
770                 // save the msg time, because wndprocs don't have access to the timestamp
771                 if ( win32.sysMsgTime && win32.sysMsgTime > (int)msg.time ) {
772                         // don't ever let the event times run backwards 
773 //                      common->Printf( "Sys_PumpEvents: win32.sysMsgTime (%i) > msg.time (%i)\n", win32.sysMsgTime, msg.time );
774                 } else {
775                         win32.sysMsgTime = msg.time;
776                 }
777
778 #ifdef ID_ALLOW_TOOLS
779                 if ( GUIEditorHandleMessage ( &msg ) ) {        
780                         continue;
781                 }
782 #endif
783  
784                 TranslateMessage (&msg);
785         DispatchMessage (&msg);
786         }
787 }
788
789 /*
790 ================
791 Sys_GenerateEvents
792 ================
793 */
794 void Sys_GenerateEvents( void ) {
795         static int entered = false;
796         char *s;
797
798         if ( entered ) {
799                 return;
800         }
801         entered = true;
802
803         // pump the message loop
804         Sys_PumpEvents();
805
806         // make sure mouse and joystick are only called once a frame
807         IN_Frame();
808
809         // check for console commands
810         s = Sys_ConsoleInput();
811         if ( s ) {
812                 char    *b;
813                 int             len;
814
815                 len = strlen( s ) + 1;
816                 b = (char *)Mem_Alloc( len );
817                 strcpy( b, s );
818                 Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
819         }
820
821         entered = false;
822 }
823
824 /*
825 ================
826 Sys_ClearEvents
827 ================
828 */
829 void Sys_ClearEvents( void ) {
830         eventHead = eventTail = 0;
831 }
832
833 /*
834 ================
835 Sys_GetEvent
836 ================
837 */
838 sysEvent_t Sys_GetEvent( void ) {
839         sysEvent_t      ev;
840
841         // return if we have data
842         if ( eventHead > eventTail ) {
843                 eventTail++;
844                 return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
845         }
846
847         // return the empty event 
848         memset( &ev, 0, sizeof( ev ) );
849
850         return ev;
851 }
852
853 //================================================================
854
855 /*
856 =================
857 Sys_In_Restart_f
858
859 Restart the input subsystem
860 =================
861 */
862 void Sys_In_Restart_f( const idCmdArgs &args ) {
863         Sys_ShutdownInput();
864         Sys_InitInput();
865 }
866
867
868 /*
869 ==================
870 Sys_AsyncThread
871 ==================
872 */
873 static void Sys_AsyncThread( void *parm ) {
874         int             wakeNumber;
875         int             startTime;
876
877         startTime = Sys_Milliseconds();
878         wakeNumber = 0;
879
880         while ( 1 ) {
881 #ifdef WIN32    
882                 // this will trigger 60 times a second
883                 int r = WaitForSingleObject( hTimer, 100 );
884                 if ( r != WAIT_OBJECT_0 ) {
885                         OutputDebugString( "idPacketServer::PacketServerInterrupt: bad wait return" );
886                 }
887 #endif
888
889 #if 0
890                 wakeNumber++;
891                 int             msec = Sys_Milliseconds();
892                 int             deltaTime = msec - startTime;
893                 startTime = msec;
894
895                 char    str[1024];
896                 sprintf( str, "%i ", deltaTime );
897                 OutputDebugString( str );
898 #endif
899
900
901                 common->Async();
902         }
903 }
904
905 /*
906 ==============
907 Sys_StartAsyncThread
908
909 Start the thread that will call idCommon::Async()
910 ==============
911 */
912 void Sys_StartAsyncThread( void ) {
913         // create an auto-reset event that happens 60 times a second
914         hTimer = CreateWaitableTimer( NULL, false, NULL );
915         if ( !hTimer ) {
916                 common->Error( "idPacketServer::Spawn: CreateWaitableTimer failed" );
917         }
918
919         LARGE_INTEGER   t;
920         t.HighPart = t.LowPart = 0;
921         SetWaitableTimer( hTimer, &t, USERCMD_MSEC, NULL, NULL, TRUE );
922
923         Sys_CreateThread( (xthread_t)Sys_AsyncThread, NULL, THREAD_ABOVE_NORMAL, threadInfo, "Async", g_threads,  &g_thread_count );
924
925 #ifdef SET_THREAD_AFFINITY 
926         // give the async thread an affinity for the second cpu
927         SetThreadAffinityMask( (HANDLE)threadInfo.threadHandle, 2 );
928 #endif
929
930         if ( !threadInfo.threadHandle ) {
931                 common->Error( "Sys_StartAsyncThread: failed" );
932         }
933 }
934
935 /*
936 ================
937 Sys_AlreadyRunning
938
939 returns true if there is a copy of D3 running already
940 ================
941 */
942 bool Sys_AlreadyRunning( void ) {
943 #ifndef DEBUG
944         if ( !win32.win_allowMultipleInstances.GetBool() ) {
945                 HANDLE hMutexOneInstance = ::CreateMutex( NULL, FALSE, "DOOM3" );
946                 if ( ::GetLastError() == ERROR_ALREADY_EXISTS || ::GetLastError() == ERROR_ACCESS_DENIED ) {
947                         return true;
948                 }
949         }
950 #endif
951         return false;
952 }
953
954 /*
955 ================
956 Sys_Init
957
958 The cvar system must already be setup
959 ================
960 */
961 #define OSR2_BUILD_NUMBER 1111
962 #define WIN98_BUILD_NUMBER 1998
963
964 void Sys_Init( void ) {
965
966         CoInitialize( NULL );
967
968         // make sure the timer is high precision, otherwise
969         // NT gets 18ms resolution
970         timeBeginPeriod( 1 );
971
972         // get WM_TIMER messages pumped every millisecond
973 //      SetTimer( NULL, 0, 100, NULL );
974
975         cmdSystem->AddCommand( "in_restart", Sys_In_Restart_f, CMD_FL_SYSTEM, "restarts the input system" );
976 #ifdef DEBUG
977         cmdSystem->AddCommand( "createResourceIDs", CreateResourceIDs_f, CMD_FL_TOOL, "assigns resource IDs in _resouce.h files" );
978 #endif
979 #if 0
980         cmdSystem->AddCommand( "setAsyncSound", Sys_SetAsyncSound_f, CMD_FL_SYSTEM, "set the async sound option" );
981 #endif
982
983         //
984         // Windows user name
985         //
986         win32.win_username.SetString( Sys_GetCurrentUser() );
987
988         //
989         // Windows version
990         //
991         win32.osversion.dwOSVersionInfoSize = sizeof( win32.osversion );
992
993         if ( !GetVersionEx( (LPOSVERSIONINFO)&win32.osversion ) )
994                 Sys_Error( "Couldn't get OS info" );
995
996         if ( win32.osversion.dwMajorVersion < 4 ) {
997                 Sys_Error( GAME_NAME " requires Windows version 4 (NT) or greater" );
998         }
999         if ( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32s ) {
1000                 Sys_Error( GAME_NAME " doesn't run on Win32s" );
1001         }
1002
1003         if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
1004                 if( win32.osversion.dwMajorVersion <= 4 ) {
1005                         win32.sys_arch.SetString( "WinNT (NT)" );
1006                 } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 0 ) {
1007                         win32.sys_arch.SetString( "Win2K (NT)" );
1008                 } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 1 ) {
1009                         win32.sys_arch.SetString( "WinXP (NT)" );
1010                 } else if ( win32.osversion.dwMajorVersion == 6 ) {
1011                         win32.sys_arch.SetString( "Vista" );
1012                 } else {
1013                         win32.sys_arch.SetString( "Unknown NT variant" );
1014                 }
1015         } else if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
1016                 if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 0 ) {
1017                         // Win95
1018                         if( win32.osversion.szCSDVersion[1] == 'C' ) {
1019                                 win32.sys_arch.SetString( "Win95 OSR2 (95)" );
1020                         } else {
1021                                 win32.sys_arch.SetString( "Win95 (95)" );
1022                         }
1023                 } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 10 ) {
1024                         // Win98
1025                         if( win32.osversion.szCSDVersion[1] == 'A' ) {
1026                                 win32.sys_arch.SetString( "Win98SE (95)" );
1027                         } else {
1028                                 win32.sys_arch.SetString( "Win98 (95)" );
1029                         }
1030                 } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 90 ) {
1031                         // WinMe
1032                         win32.sys_arch.SetString( "WinMe (95)" );
1033                 } else {
1034                         win32.sys_arch.SetString( "Unknown 95 variant" );
1035                 }
1036         } else {
1037                 win32.sys_arch.SetString( "unknown Windows variant" );
1038         }
1039
1040         //
1041         // CPU type
1042         //
1043         if ( !idStr::Icmp( win32.sys_cpustring.GetString(), "detect" ) ) {
1044                 idStr string;
1045
1046                 common->Printf( "%1.0f MHz ", Sys_ClockTicksPerSecond() / 1000000.0f );
1047
1048                 win32.cpuid = Sys_GetCPUId();
1049
1050                 string.Clear();
1051
1052                 if ( win32.cpuid & CPUID_AMD ) {
1053                         string += "AMD CPU";
1054                 } else if ( win32.cpuid & CPUID_INTEL ) {
1055                         string += "Intel CPU";
1056                 } else if ( win32.cpuid & CPUID_UNSUPPORTED ) {
1057                         string += "unsupported CPU";
1058                 } else {
1059                         string += "generic CPU";
1060                 }
1061
1062                 string += " with ";
1063                 if ( win32.cpuid & CPUID_MMX ) {
1064                         string += "MMX & ";
1065                 }
1066                 if ( win32.cpuid & CPUID_3DNOW ) {
1067                         string += "3DNow! & ";
1068                 }
1069                 if ( win32.cpuid & CPUID_SSE ) {
1070                         string += "SSE & ";
1071                 }
1072                 if ( win32.cpuid & CPUID_SSE2 ) {
1073             string += "SSE2 & ";
1074                 }
1075                 if ( win32.cpuid & CPUID_SSE3 ) {
1076                         string += "SSE3 & ";
1077                 }
1078                 if ( win32.cpuid & CPUID_HTT ) {
1079                         string += "HTT & ";
1080                 }
1081                 string.StripTrailing( " & " );
1082                 string.StripTrailing( " with " );
1083                 win32.sys_cpustring.SetString( string );
1084         } else {
1085                 common->Printf( "forcing CPU type to " );
1086                 idLexer src( win32.sys_cpustring.GetString(), idStr::Length( win32.sys_cpustring.GetString() ), "sys_cpustring" );
1087                 idToken token;
1088
1089                 int id = CPUID_NONE;
1090                 while( src.ReadToken( &token ) ) {
1091                         if ( token.Icmp( "generic" ) == 0 ) {
1092                                 id |= CPUID_GENERIC;
1093                         } else if ( token.Icmp( "intel" ) == 0 ) {
1094                                 id |= CPUID_INTEL;
1095                         } else if ( token.Icmp( "amd" ) == 0 ) {
1096                                 id |= CPUID_AMD;
1097                         } else if ( token.Icmp( "mmx" ) == 0 ) {
1098                                 id |= CPUID_MMX;
1099                         } else if ( token.Icmp( "3dnow" ) == 0 ) {
1100                                 id |= CPUID_3DNOW;
1101                         } else if ( token.Icmp( "sse" ) == 0 ) {
1102                                 id |= CPUID_SSE;
1103                         } else if ( token.Icmp( "sse2" ) == 0 ) {
1104                                 id |= CPUID_SSE2;
1105                         } else if ( token.Icmp( "sse3" ) == 0 ) {
1106                                 id |= CPUID_SSE3;
1107                         } else if ( token.Icmp( "htt" ) == 0 ) {
1108                                 id |= CPUID_HTT;
1109                         }
1110                 }
1111                 if ( id == CPUID_NONE ) {
1112                         common->Printf( "WARNING: unknown sys_cpustring '%s'\n", win32.sys_cpustring.GetString() );
1113                         id = CPUID_GENERIC;
1114                 }
1115                 win32.cpuid = (cpuid_t) id;
1116         }
1117
1118         common->Printf( "%s\n", win32.sys_cpustring.GetString() );
1119         common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() );
1120         common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
1121 }
1122
1123 /*
1124 ================
1125 Sys_Shutdown
1126 ================
1127 */
1128 void Sys_Shutdown( void ) {
1129         CoUninitialize();
1130 }
1131
1132 /*
1133 ================
1134 Sys_GetProcessorId
1135 ================
1136 */
1137 cpuid_t Sys_GetProcessorId( void ) {
1138     return win32.cpuid;
1139 }
1140
1141 /*
1142 ================
1143 Sys_GetProcessorString
1144 ================
1145 */
1146 const char *Sys_GetProcessorString( void ) {
1147         return win32.sys_cpustring.GetString();
1148 }
1149
1150 //=======================================================================
1151
1152 //#define SET_THREAD_AFFINITY
1153
1154
1155 /*
1156 ====================
1157 Win_Frame
1158 ====================
1159 */
1160 void Win_Frame( void ) {
1161         // if "viewlog" has been modified, show or hide the log console
1162         if ( win32.win_viewlog.IsModified() ) {
1163                 if ( !com_skipRenderer.GetBool() && idAsyncNetwork::serverDedicated.GetInteger() != 1 ) {
1164                         Sys_ShowConsole( win32.win_viewlog.GetInteger(), false );
1165                 }
1166                 win32.win_viewlog.ClearModified();
1167         }
1168 }
1169
1170 extern "C" { void _chkstk( int size ); };
1171 void clrstk( void );
1172
1173 /*
1174 ====================
1175 TestChkStk
1176 ====================
1177 */
1178 void TestChkStk( void ) {
1179         int             buffer[0x1000];
1180
1181         buffer[0] = 1;
1182 }
1183
1184 /*
1185 ====================
1186 HackChkStk
1187 ====================
1188 */
1189 void HackChkStk( void ) {
1190         DWORD   old;
1191         VirtualProtect( _chkstk, 6, PAGE_EXECUTE_READWRITE, &old );
1192         *(byte *)_chkstk = 0xe9;
1193         *(int *)((int)_chkstk+1) = (int)clrstk - (int)_chkstk - 5;
1194
1195         TestChkStk();
1196 }
1197
1198 /*
1199 ====================
1200 GetExceptionCodeInfo
1201 ====================
1202 */
1203 const char *GetExceptionCodeInfo( UINT code ) {
1204         switch( code ) {
1205                 case EXCEPTION_ACCESS_VIOLATION: return "The thread tried to read from or write to a virtual address for which it does not have the appropriate access.";
1206                 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.";
1207                 case EXCEPTION_BREAKPOINT: return "A breakpoint was encountered.";
1208                 case EXCEPTION_DATATYPE_MISALIGNMENT: return "The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.";
1209                 case EXCEPTION_FLT_DENORMAL_OPERAND: return "One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.";
1210                 case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "The thread tried to divide a floating-point value by a floating-point divisor of zero.";
1211                 case EXCEPTION_FLT_INEXACT_RESULT: return "The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
1212                 case EXCEPTION_FLT_INVALID_OPERATION: return "This exception represents any floating-point exception not included in this list.";
1213                 case EXCEPTION_FLT_OVERFLOW: return "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
1214                 case EXCEPTION_FLT_STACK_CHECK: return "The stack overflowed or underflowed as the result of a floating-point operation.";
1215                 case EXCEPTION_FLT_UNDERFLOW: return "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
1216                 case EXCEPTION_ILLEGAL_INSTRUCTION: return "The thread tried to execute an invalid instruction.";
1217                 case EXCEPTION_IN_PAGE_ERROR: return "The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.";
1218                 case EXCEPTION_INT_DIVIDE_BY_ZERO: return "The thread tried to divide an integer value by an integer divisor of zero.";
1219                 case EXCEPTION_INT_OVERFLOW: return "The result of an integer operation caused a carry out of the most significant bit of the result.";
1220                 case EXCEPTION_INVALID_DISPOSITION: return "An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.";
1221                 case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "The thread tried to continue execution after a noncontinuable exception occurred.";
1222                 case EXCEPTION_PRIV_INSTRUCTION: return "The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
1223                 case EXCEPTION_SINGLE_STEP: return "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
1224                 case EXCEPTION_STACK_OVERFLOW: return "The thread used up its stack.";
1225                 default: return "Unknown exception";
1226         }
1227 }
1228
1229 /*
1230 ====================
1231 EmailCrashReport
1232
1233   emailer originally from Raven/Quake 4
1234 ====================
1235 */
1236 void EmailCrashReport( LPSTR messageText ) {
1237         LPMAPISENDMAIL  MAPISendMail;
1238         MapiMessage             message;
1239         static int lastEmailTime = 0;
1240
1241         if ( Sys_Milliseconds() < lastEmailTime + 10000 ) {
1242                 return;
1243         }
1244
1245         lastEmailTime = Sys_Milliseconds();
1246
1247         HINSTANCE mapi = LoadLibrary( "MAPI32.DLL" ); 
1248         if( mapi ) {
1249                 MAPISendMail = ( LPMAPISENDMAIL )GetProcAddress( mapi, "MAPISendMail" );
1250                 if( MAPISendMail ) {
1251                         MapiRecipDesc toProgrammers =
1252                         {
1253                                 0,                                                                              // ulReserved
1254                                         MAPI_TO,                                                        // ulRecipClass
1255                                         "DOOM 3 Crash",                                         // lpszName
1256                                         "SMTP:programmers@idsoftware.com",      // lpszAddress
1257                                         0,                                                                      // ulEIDSize
1258                                         0                                                                       // lpEntry
1259                         };
1260
1261                         memset( &message, 0, sizeof( message ) );
1262                         message.lpszSubject = "DOOM 3 Fatal Error";
1263                         message.lpszNoteText = messageText;
1264                         message.nRecipCount = 1;
1265                         message.lpRecips = &toProgrammers;
1266
1267                         MAPISendMail(
1268                                 0,                                                                      // LHANDLE lhSession
1269                                 0,                                                                      // ULONG ulUIParam
1270                                 &message,                                                       // lpMapiMessage lpMessage
1271                                 MAPI_DIALOG,                                            // FLAGS flFlags
1272                                 0                                                                       // ULONG ulReserved
1273                                 );
1274                 }
1275                 FreeLibrary( mapi );
1276         }
1277 }
1278
1279 int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse );
1280
1281 /*
1282 ====================
1283 _except_handler
1284 ====================
1285 */
1286 EXCEPTION_DISPOSITION __cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame,
1287                                                                                                 struct _CONTEXT *ContextRecord, void * DispatcherContext ) {
1288
1289         static char msg[ 8192 ];
1290         char FPUFlags[2048];
1291
1292         Sys_FPU_PrintStateFlags( FPUFlags, ContextRecord->FloatSave.ControlWord,
1293                                                                                 ContextRecord->FloatSave.StatusWord,
1294                                                                                 ContextRecord->FloatSave.TagWord,
1295                                                                                 ContextRecord->FloatSave.ErrorOffset,
1296                                                                                 ContextRecord->FloatSave.ErrorSelector,
1297                                                                                 ContextRecord->FloatSave.DataOffset,
1298                                                                                 ContextRecord->FloatSave.DataSelector );
1299
1300
1301         sprintf( msg, 
1302                 "Please describe what you were doing when DOOM 3 crashed!\n"
1303                 "If this text did not pop into your email client please copy and email it to programmers@idsoftware.com\n"
1304                         "\n"
1305                         "-= FATAL EXCEPTION =-\n"
1306                         "\n"
1307                         "%s\n"
1308                         "\n"
1309                         "0x%x at address 0x%08x\n"
1310                         "\n"
1311                         "%s\n"
1312                         "\n"
1313                         "EAX = 0x%08x EBX = 0x%08x\n"
1314                         "ECX = 0x%08x EDX = 0x%08x\n"
1315                         "ESI = 0x%08x EDI = 0x%08x\n"
1316                         "EIP = 0x%08x ESP = 0x%08x\n"
1317                         "EBP = 0x%08x EFL = 0x%08x\n"
1318                         "\n"
1319                         "CS = 0x%04x\n"
1320                         "SS = 0x%04x\n"
1321                         "DS = 0x%04x\n"
1322                         "ES = 0x%04x\n"
1323                         "FS = 0x%04x\n"
1324                         "GS = 0x%04x\n"
1325                         "\n"
1326                         "%s\n",
1327                         com_version.GetString(),
1328                         ExceptionRecord->ExceptionCode,
1329                         ExceptionRecord->ExceptionAddress,
1330                         GetExceptionCodeInfo( ExceptionRecord->ExceptionCode ),
1331                         ContextRecord->Eax, ContextRecord->Ebx,
1332                         ContextRecord->Ecx, ContextRecord->Edx,
1333                         ContextRecord->Esi, ContextRecord->Edi,
1334                         ContextRecord->Eip, ContextRecord->Esp,
1335                         ContextRecord->Ebp, ContextRecord->EFlags,
1336                         ContextRecord->SegCs,
1337                         ContextRecord->SegSs,
1338                         ContextRecord->SegDs,
1339                         ContextRecord->SegEs,
1340                         ContextRecord->SegFs,
1341                         ContextRecord->SegGs,
1342                         FPUFlags
1343                 );
1344
1345         EmailCrashReport( msg );
1346         common->FatalError( msg );
1347
1348     // Tell the OS to restart the faulting instruction
1349     return ExceptionContinueExecution;
1350 }
1351
1352 #define TEST_FPU_EXCEPTIONS     /*      FPU_EXCEPTION_INVALID_OPERATION |               */      \
1353                                                         /*      FPU_EXCEPTION_DENORMALIZED_OPERAND |    */      \
1354                                                         /*      FPU_EXCEPTION_DIVIDE_BY_ZERO |                  */      \
1355                                                         /*      FPU_EXCEPTION_NUMERIC_OVERFLOW |                */      \
1356                                                         /*      FPU_EXCEPTION_NUMERIC_UNDERFLOW |               */      \
1357                                                         /*      FPU_EXCEPTION_INEXACT_RESULT |                  */      \
1358                                                                 0
1359
1360 /*
1361 ==================
1362 WinMain
1363 ==================
1364 */
1365 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
1366
1367         const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) );
1368
1369         Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 );
1370
1371         Sys_GetCurrentMemoryStatus( exeLaunchMemoryStats );
1372
1373 #if 0
1374     DWORD handler = (DWORD)_except_handler;
1375     __asm
1376     {                           // Build EXCEPTION_REGISTRATION record:
1377         push    handler         // Address of handler function
1378         push    FS:[0]          // Address of previous handler
1379         mov     FS:[0],ESP      // Install new EXECEPTION_REGISTRATION
1380     }
1381 #endif
1382
1383         win32.hInstance = hInstance;
1384         idStr::Copynz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
1385
1386         // done before Com/Sys_Init since we need this for error output
1387         Sys_CreateConsole();
1388
1389         // no abort/retry/fail errors
1390         SetErrorMode( SEM_FAILCRITICALERRORS );
1391
1392         for ( int i = 0; i < MAX_CRITICAL_SECTIONS; i++ ) {
1393                 InitializeCriticalSection( &win32.criticalSections[i] );
1394         }
1395
1396         // get the initial time base
1397         Sys_Milliseconds();
1398
1399 #ifdef DEBUG
1400         // disable the painfully slow MS heap check every 1024 allocs
1401         _CrtSetDbgFlag( 0 );
1402 #endif
1403
1404 //      Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
1405         Sys_FPU_SetPrecision( FPU_PRECISION_DOUBLE_EXTENDED );
1406
1407         common->Init( 0, NULL, lpCmdLine );
1408
1409 #if TEST_FPU_EXCEPTIONS != 0
1410         common->Printf( Sys_FPU_GetState() );
1411 #endif
1412
1413 #ifndef ID_DEDICATED
1414         if ( win32.win_notaskkeys.GetInteger() ) {
1415                 DisableTaskKeys( TRUE, FALSE, /*( win32.win_notaskkeys.GetInteger() == 2 )*/ FALSE );
1416         }
1417 #endif
1418
1419         Sys_StartAsyncThread();
1420
1421         // hide or show the early console as necessary
1422         if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) {
1423                 Sys_ShowConsole( 1, true );
1424         } else {
1425                 Sys_ShowConsole( 0, false );
1426         }
1427
1428 #ifdef SET_THREAD_AFFINITY 
1429         // give the main thread an affinity for the first cpu
1430         SetThreadAffinityMask( GetCurrentThread(), 1 );
1431 #endif
1432
1433         ::SetCursor( hcurSave );
1434
1435         // Launch the script debugger
1436         if ( strstr( lpCmdLine, "+debugger" ) ) {
1437                 // DebuggerClientInit( lpCmdLine );
1438                 return 0;
1439         }
1440
1441         ::SetFocus( win32.hWnd );
1442
1443     // main game loop
1444         while( 1 ) {
1445
1446                 Win_Frame();
1447
1448 #ifdef DEBUG
1449                 Sys_MemFrame();
1450 #endif
1451
1452                 // set exceptions, even if some crappy syscall changes them!
1453                 Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
1454
1455 #ifdef ID_ALLOW_TOOLS
1456                 if ( com_editors ) {
1457                         if ( com_editors & EDITOR_GUI ) {
1458                                 // GUI editor
1459                                 GUIEditorRun();
1460                         } else if ( com_editors & EDITOR_RADIANT ) {
1461                                 // Level Editor
1462                                 RadiantRun();
1463                         }
1464                         else if (com_editors & EDITOR_MATERIAL ) {
1465                                 //BSM Nerve: Add support for the material editor
1466                                 MaterialEditorRun();
1467                         }
1468                         else {
1469                                 if ( com_editors & EDITOR_LIGHT ) {
1470                                         // in-game Light Editor
1471                                         LightEditorRun();
1472                                 }
1473                                 if ( com_editors & EDITOR_SOUND ) {
1474                                         // in-game Sound Editor
1475                                         SoundEditorRun();
1476                                 }
1477                                 if ( com_editors & EDITOR_DECL ) {
1478                                         // in-game Declaration Browser
1479                                         DeclBrowserRun();
1480                                 }
1481                                 if ( com_editors & EDITOR_AF ) {
1482                                         // in-game Articulated Figure Editor
1483                                         AFEditorRun();
1484                                 }
1485                                 if ( com_editors & EDITOR_PARTICLE ) {
1486                                         // in-game Particle Editor
1487                                         ParticleEditorRun();
1488                                 }
1489                                 if ( com_editors & EDITOR_SCRIPT ) {
1490                                         // in-game Script Editor
1491                                         ScriptEditorRun();
1492                                 }
1493                                 if ( com_editors & EDITOR_PDA ) {
1494                                         // in-game PDA Editor
1495                                         PDAEditorRun();
1496                                 }
1497                         }
1498                 }
1499 #endif
1500                 // run the game
1501                 common->Frame();
1502         }
1503
1504         // never gets here
1505         return 0;
1506 }
1507
1508 /*
1509 ====================
1510 clrstk
1511
1512 I tried to get the run time to call this at every function entry, but
1513 ====================
1514 */
1515 static int      parmBytes;
1516 __declspec( naked ) void clrstk( void ) {
1517         // eax = bytes to add to stack
1518         __asm {
1519                 mov             [parmBytes],eax
1520         neg     eax                     ; compute new stack pointer in eax
1521         add     eax,esp
1522         add     eax,4
1523         xchg    eax,esp
1524         mov     eax,dword ptr [eax]             ; copy the return address
1525         push    eax
1526         
1527         ; clear to zero
1528         push    edi
1529         push    ecx
1530         mov             edi,esp
1531         add             edi,12
1532         mov             ecx,[parmBytes]
1533                 shr             ecx,2
1534         xor             eax,eax
1535                 cld
1536         rep     stosd
1537         pop             ecx
1538         pop             edi
1539         
1540         ret
1541         }
1542 }
1543
1544 /*
1545 ==================
1546 idSysLocal::OpenURL
1547 ==================
1548 */
1549 void idSysLocal::OpenURL( const char *url, bool doexit ) {
1550         static bool doexit_spamguard = false;
1551         HWND wnd;
1552
1553         if (doexit_spamguard) {
1554                 common->DPrintf( "OpenURL: already in an exit sequence, ignoring %s\n", url );
1555                 return;
1556         }
1557
1558         common->Printf("Open URL: %s\n", url);
1559
1560         if ( !ShellExecute( NULL, "open", url, NULL, NULL, SW_RESTORE ) ) {
1561                 common->Error( "Could not open url: '%s' ", url );
1562                 return;
1563         }
1564
1565         wnd = GetForegroundWindow();
1566         if ( wnd ) {
1567                 ShowWindow( wnd, SW_MAXIMIZE );
1568         }
1569
1570         if ( doexit ) {
1571                 doexit_spamguard = true;
1572                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
1573         }
1574 }
1575
1576 /*
1577 ==================
1578 idSysLocal::StartProcess
1579 ==================
1580 */
1581 void idSysLocal::StartProcess( const char *exePath, bool doexit ) {
1582         TCHAR                           szPathOrig[_MAX_PATH];
1583         STARTUPINFO                     si;
1584         PROCESS_INFORMATION     pi;
1585
1586         ZeroMemory( &si, sizeof(si) );
1587         si.cb = sizeof(si);
1588
1589         strncpy( szPathOrig, exePath, _MAX_PATH );
1590
1591         if( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
1592         common->Error( "Could not start process: '%s' ", szPathOrig );
1593             return;
1594         }
1595
1596         if ( doexit ) {
1597                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
1598         }
1599 }
1600
1601 /*
1602 ==================
1603 Sys_SetFatalError
1604 ==================
1605 */
1606 void Sys_SetFatalError( const char *error ) {
1607 }
1608
1609 /*
1610 ==================
1611 Sys_DoPreferences
1612 ==================
1613 */
1614 void Sys_DoPreferences( void ) {
1615 }