1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 // sys_win.h
21
22 #define _WINNT_VER 0x5
23
24 #include "../qcommon/qcommon.h"
25 #include "winquake.h"
26 #include "resource.h"
27 #include <errno.h>
28 #include <float.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <direct.h>
32 #include <io.h>
33 #include <conio.h>
34 #include <process.h>
35 #include <dbghelp.h>
36 #include <Richedit.h>
37
38 #ifdef USE_OPENSSL
39 #define OPENSSLEXPORT __cdecl
40 #include <openssl/sha.h>
41 #endif
42
43 #ifdef USE_CURL
44 #define CURL_STATICLIB
45 #define CURL_HIDDEN_SYMBOLS
46 #define CURL_EXTERN_SYMBOL
47 #define CURL_CALLING_CONVENTION __cdecl
48 #include <curl/curl.h>
49 #endif
50
51 #include "../win32/conproc.h"
52
53 int SV_CountPlayers (void);
54
55 //#define DEMO
56
57 #ifdef DEDICATED_ONLY
58 qboolean global_Service = false;
59 #endif
60
61 HMODULE hSh32 = NULL;
62 FARPROC procShell_NotifyIcon = NULL;
63 NOTIFYICONDATA pNdata;
64
65 cvar_t *win_priority;
66 //cvar_t *win_disablewinkey;
67
68 qboolean s_win95;
69
70 int starttime;
71 int ActiveApp;
72 qboolean Minimized;
73
74 HWND hwnd_Server;
75
76 #ifndef NO_SERVER
77 #ifdef USE_PYROADMIN
78 extern netadr_t netaddress_pyroadmin;
79 #endif
80 static HANDLE hinput, houtput;
81 BOOL oldStyleConsole = FALSE;
82 #endif
83
84 uint32 sys_msg_time;
85 uint32 sys_frame_time;
86
87 #ifdef USE_PYROADMIN
88 extern cvar_t *pyroadminport;
89 #endif
90
91 cvar_t *win_disableexceptionhandler = &uninitialized_cvar;
92 cvar_t *win_silentexceptionhandler = &uninitialized_cvar;
93 cvar_t *sys_fpu_bits = &uninitialized_cvar;
94
95 //static HANDLE qwclsemaphore;
96
97 #define MAX_NUM_ARGVS 128
98 int argc;
99 char *argv[MAX_NUM_ARGVS];
100
101 //cvar_t *win_priority;
102
103 sizebuf_t console_buffer;
104 byte console_buff[16384];
105
106 //r1: service support
107 #ifdef DEDICATED_ONLY
108 SERVICE_STATUS MyServiceStatus;
109 SERVICE_STATUS_HANDLE MyServiceStatusHandle;
110 #endif
111
112 //original game command line
113 char cmdline[4096];
114 char bname[MAX_QPATH];
115
116 /*
117 ===============================================================================
118
119 SYSTEM IO
120
121 ===============================================================================
122 */
123
Sys_FileLength(const char * path)124 int Sys_FileLength (const char *path)
125 {
126 WIN32_FILE_ATTRIBUTE_DATA fileData;
127
128 if (GetFileAttributesEx (path, GetFileExInfoStandard, &fileData))
129 return (int)fileData.nFileSizeLow;
130 else
131 return -1;
132 }
133
134 void VID_Restart_f (void);
135 void S_Init (int fullInit);
Sys_Error(const char * error,...)136 NORETURN void Sys_Error (const char *error, ...)
137 {
138 va_list argptr;
139 char text[1024];
140 int ret;
141
142 #ifndef DEDICATED_ONLY
143 if (cl_hwnd && IsWindow (cl_hwnd))
144 DestroyWindow (cl_hwnd);
145 #endif
146
147 Qcommon_Shutdown ();
148
149 va_start (argptr, error);
150 vsnprintf (text, sizeof(text)-1, error, argptr);
151 va_end (argptr);
152
153 text[sizeof(text)-1] = 0;
154
155 if (strlen(text) < 900)
156 strcat (text, "\r\n\r\nWould you like to debug? (DEVELOPERS ONLY!)\n");
157
158 rebox:;
159
160 ret = MessageBox(NULL, text, "Quake II Fatal Error", MB_ICONEXCLAMATION | MB_YESNO);
161
162 if (ret == IDYES)
163 {
164 ret = MessageBox(NULL, "Please attach your debugger now to prevent the built in exception handler from catching the breakpoint. When ready, press Yes to cause a breakpoint or No to cancel.", "Quake II Fatal Error", MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2);
165 if (ret == IDYES)
166 {
167 #ifndef _DEBUG
168 if (!IsDebuggerPresent ())
169 {
170 ExitProcess (0x1d107);
171 }
172 #endif
173 Sys_DebugBreak ();
174 }
175 else
176 {
177 goto rebox;
178 }
179 }
180
181 ExitProcess (0xDEAD);
182 }
183
Sys_Quit(void)184 void Sys_Quit (void)
185 {
186 timeEndPeriod( 1 );
187
188 #ifndef DEDICATED_ONLY
189 CL_Shutdown();
190 #endif
191
192 Qcommon_Shutdown ();
193
194 // Cleanup before shutdown
195 //UnhookWindowsHookEx( g_hKeyboardHook );
196
197 if (procShell_NotifyIcon)
198 procShell_NotifyIcon (NIM_DELETE, &pNdata);
199
200 if (hSh32)
201 FreeLibrary (hSh32);
202
203 #ifdef DEDICATED_ONLY
204 if (!global_Service)
205 #endif
206 //exit (0);
207 ExitProcess (0);
208 }
209
WinError(void)210 void WinError (void)
211 {
212 LPVOID lpMsgBuf;
213
214 FormatMessage(
215 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
216 NULL,
217 GetLastError(),
218 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
219 (LPTSTR) &lpMsgBuf,
220 0,
221 NULL
222 );
223
224 // Display the string.
225 MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );
226
227 // Free the buffer.
228 LocalFree( lpMsgBuf );
229 }
230
231 #ifdef DEDICATED_ONLY
232
Sys_ServiceCtrlHandler(DWORD Opcode)233 void EXPORT Sys_ServiceCtrlHandler (DWORD Opcode)
234 {
235 switch(Opcode)
236 {
237 case SERVICE_CONTROL_STOP:
238 // Do whatever it takes to stop here.
239 MyServiceStatus.dwWin32ExitCode = 0;
240 MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
241 MyServiceStatus.dwCheckPoint = 0;
242 MyServiceStatus.dwWaitHint = 0;
243
244 SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus);
245
246 Com_Quit();
247 return;
248 }
249
250 // Send current status.
251 SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus);
252 }
253
Sys_ServiceStart(DWORD argc,LPTSTR * argv)254 void EXPORT Sys_ServiceStart (DWORD argc, LPTSTR *argv)
255 {
256 MyServiceStatus.dwServiceType = SERVICE_WIN32;
257 MyServiceStatus.dwCurrentState = SERVICE_START_PENDING;
258 MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
259 MyServiceStatus.dwWin32ExitCode = 0;
260 MyServiceStatus.dwServiceSpecificExitCode = 0;
261 MyServiceStatus.dwCheckPoint = 0;
262 MyServiceStatus.dwWaitHint = 0;
263
264 MyServiceStatusHandle = RegisterServiceCtrlHandler(
265 argv[0],
266 (LPHANDLER_FUNCTION)Sys_ServiceCtrlHandler);
267
268 if (MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
269 return;
270
271 // Initialization complete - report running status.
272 MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
273 MyServiceStatus.dwCheckPoint = 0;
274 MyServiceStatus.dwWaitHint = 0;
275
276 SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus);
277
278 WinMain (0, NULL, cmdline, 0);
279
280 return;
281 }
282
main(void)283 int EXPORT main(void)
284 {
285 SERVICE_TABLE_ENTRY DispatchTable[] =
286 {
287 { "R1Q2", (LPSERVICE_MAIN_FUNCTION)Sys_ServiceStart },
288 { NULL, NULL }
289 };
290
291 if (!StartServiceCtrlDispatcher( DispatchTable))
292 return 1;
293
294 return 0;
295 }
296
297 //================================================================
298
Sys_InstallService(char * servername,char * cmdline)299 void Sys_InstallService(char *servername, char *cmdline)
300 {
301 SC_HANDLE schService, schSCManager;
302
303 char srvName[MAX_OSPATH];
304 char srvDispName[MAX_OSPATH];
305 char lpszBinaryPathName[2048];
306
307 GetModuleFileName(NULL, lpszBinaryPathName, sizeof(lpszBinaryPathName));
308
309 while (*cmdline != ' ') {
310 cmdline++;
311 }
312
313 cmdline++;
314
315 strcat (lpszBinaryPathName, " -service ");
316 strcat (lpszBinaryPathName, cmdline);
317
318 Com_sprintf (srvDispName, sizeof(srvDispName), "Quake II - %s", servername);
319 Com_sprintf (srvName, sizeof(srvName), "R1Q2(%s)", servername);
320
321 schSCManager = OpenSCManager(
322 NULL, // local machine
323 NULL, // ServicesActive database
324 SC_MANAGER_ALL_ACCESS); // full access rights
325
326 if (schSCManager == NULL) {
327 Com_Printf ("OpenSCManager FAILED. GetLastError = %d\n", LOG_GENERAL, GetLastError());
328 return;
329 }
330
331 schService = CreateService(
332 schSCManager, // SCManager database
333 srvName, // name of service
334 srvDispName, // service name to display
335 SERVICE_ALL_ACCESS, // desired access
336 SERVICE_WIN32_OWN_PROCESS, // service type
337 SERVICE_AUTO_START, // start type
338 SERVICE_ERROR_NORMAL, // error control type
339 lpszBinaryPathName, // service's binary
340 NULL, // no load ordering group
341 NULL, // no tag identifier
342 NULL, // no dependencies
343 NULL, // LocalSystem account
344 NULL); // no password
345
346 if (schService == NULL)
347 {
348 Com_Printf ("CreateService FAILED. GetLastError = %d\n", LOG_GENERAL, GetLastError());
349 }
350 else
351 {
352 Com_Printf ("CreateService SUCCESS.\n", LOG_GENERAL);
353 CloseServiceHandle(schService);
354 }
355
356 CloseServiceHandle (schSCManager);
357 }
358
Sys_DeleteService(char * servername)359 void Sys_DeleteService (char *servername)
360 {
361 SC_HANDLE schService, schSCManager;
362 char srvName[MAX_OSPATH];
363
364 snprintf (srvName, sizeof(srvName)-1, "R1Q2(%s)", servername);
365 //strcpy (srvName, servername);
366
367 schSCManager = OpenSCManager(
368 NULL, // local machine
369 NULL, // ServicesActive database
370 SC_MANAGER_ALL_ACCESS); // full access rights
371
372 if (schSCManager == NULL) {
373 Com_Printf ("OpenSCManager FAILED. GetLastError = %d\n", LOG_GENERAL, GetLastError());
374 return;
375 }
376
377 schService = OpenService(
378 schSCManager, // SCManager database
379 srvName, // name of service
380 DELETE); // only need DELETE access
381
382 if (schService == NULL) {
383 Com_Printf ("OpenService FAILED. GetLastError = %d\n", LOG_GENERAL, GetLastError());
384 } else {
385 DeleteService(schService);
386 CloseServiceHandle(schService);
387 Com_Printf ("DeleteService SUCCESS.\n", LOG_GENERAL);
388 }
389
390 CloseServiceHandle (schSCManager);
391 }
392 #endif
393
Sys_EnableTray(void)394 void Sys_EnableTray (void)
395 {
396 memset (&pNdata, 0, sizeof(pNdata));
397
398 pNdata.cbSize = sizeof(NOTIFYICONDATA);
399 pNdata.hWnd = hwnd_Server;
400 pNdata.uID = 0;
401 pNdata.uCallbackMessage = WM_USER + 4;
402 GetWindowText (hwnd_Server, pNdata.szTip, sizeof(pNdata.szTip)-1);
403 pNdata.hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE(IDI_ICON2));
404 pNdata.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
405
406 hSh32 = LoadLibrary ("shell32.dll");
407 procShell_NotifyIcon = GetProcAddress (hSh32, "Shell_NotifyIcon");
408
409 procShell_NotifyIcon (NIM_ADD, &pNdata);
410
411 Com_Printf ("Minimize to tray enabled.\n", LOG_GENERAL);
412 }
413
Sys_DisableTray(void)414 void Sys_DisableTray (void)
415 {
416 ShowWindow (hwnd_Server, SW_RESTORE);
417 procShell_NotifyIcon (NIM_DELETE, &pNdata);
418
419 if (hSh32)
420 FreeLibrary (hSh32);
421
422 procShell_NotifyIcon = NULL;
423
424 Com_Printf ("Minimize to tray disabled.\n", LOG_GENERAL);
425 }
426
Sys_Minimize(void)427 void Sys_Minimize (void)
428 {
429 SendMessage (hwnd_Server, WM_ACTIVATE, MAKELONG(WA_INACTIVE,1), 0);
430 }
431
432 #ifndef NO_SERVER
Sys_SetWindowText(char * buff)433 void Sys_SetWindowText (char *buff)
434 {
435 #ifdef DEDICATED_ONLY
436 if (!global_Service)
437 #endif
438 SetWindowText (hwnd_Server, buff);
439 }
440
ServerWindowProcCommandExecute(void)441 void ServerWindowProcCommandExecute (void)
442 {
443 int ret;
444 char buff[1024];
445
446 *(DWORD *)&buff = sizeof(buff)-2;
447
448 ret = (int)SendDlgItemMessage (hwnd_Server, IDC_COMMAND, EM_GETLINE, 1, (LPARAM)buff);
449 if (!ret)
450 return;
451
452 buff[ret] = '\n';
453 buff[ret+1] = '\0';
454 Sys_ConsoleOutput (buff);
455 //Cmd_ExecuteString (buff);
456 Cbuf_AddText (buff);
457 SendDlgItemMessage (hwnd_Server, IDC_COMMAND, WM_SETTEXT, 0, (LPARAM)"");
458 }
459
Sys_UpdateConsoleBuffer(void)460 void Sys_UpdateConsoleBuffer (void)
461 {
462 #ifdef DEDICATED_ONLY
463 if (global_Service)
464 return;
465 #endif
466
467 if (console_buffer.cursize)
468 {
469 int len;
470
471 /*buflen = console_buffer.cursize + 1024;
472
473 if (consoleBufferPointer + buflen >= sizeof(consoleFullBuffer))
474 {
475 int moved;
476 char *p = consoleFullBuffer + buflen;
477 char *q;
478
479 while (p[0] && p[0] != '\n')
480 p++;
481 p++;
482 q = (consoleFullBuffer + buflen);
483 moved = (buflen + (int)(p - q));
484 memmove (consoleFullBuffer, consoleFullBuffer + moved, consoleBufferPointer - moved);
485 consoleBufferPointer -= moved;
486 consoleFullBuffer[consoleBufferPointer] = '\0';
487 }
488
489 memcpy (consoleFullBuffer+consoleBufferPointer, console_buffer.data, console_buffer.cursize);
490 consoleBufferPointer += (console_buffer.cursize - 1);*/
491
492 SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_SETSEL, (WPARAM)-1, (LPARAM)-1);
493 SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_REPLACESEL, 0, (LPARAM)console_buffer.data);
494
495 while ((len = (int)SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_GETLINECOUNT, 0, 0)) > 1000)
496 {
497 int line_length;
498 line_length = (int)SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_LINELENGTH, 0, 0);
499
500 SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_SETSEL, 0, line_length + 1);
501 SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_REPLACESEL, 0, (LPARAM)"");
502 }
503
504 SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_SETSEL, (WPARAM)-1, (LPARAM)-1);
505 SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, WM_VSCROLL, SB_BOTTOM, 0);
506
507 SZ_Clear (&console_buffer);
508 }
509 }
510
511 //================================================================
512
ServerWindowProcCommand(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)513 LRESULT ServerWindowProcCommand(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
514 {
515 UINT idItem = LOWORD(wParam);
516 UINT wNotifyCode = HIWORD(wParam);
517 //HWND hwndCtl = (HWND) lParam;
518
519 switch (idItem) {
520 case IDOK:
521 switch (wNotifyCode) {
522 case BN_CLICKED:
523 ServerWindowProcCommandExecute();
524 break;
525 }
526 }
527
528 return FALSE;
529 }
530
ServerWindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)531 LRESULT CALLBACK ServerWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
532 {
533 //UINT idItem = LOWORD(wParam);
534
535 switch (message) {
536 case WM_COMMAND:
537 return ServerWindowProcCommand(hwnd, message, wParam, lParam);
538 case WM_ENDSESSION:
539 Cbuf_AddText ("quit exiting due to Windows shutdown.\n");
540 return TRUE;
541 case WM_CLOSE:
542 #ifdef DEDICATED_ONLY
543 if (!global_Service)
544 #endif
545 {
546 if (SV_CountPlayers()) {
547 int ays = MessageBox (hwnd_Server, "There are still players on the server! Really shut it down?", "WARNING!", MB_YESNO + MB_ICONEXCLAMATION);
548 if (ays == IDNO)
549 return TRUE;
550 }
551 }
552 Cbuf_AddText ("quit terminated by local request.\n");
553 break;
554 case WM_ACTIVATE:
555 {
556 int minimized = (BOOL)HIWORD(wParam);
557
558 Minimized = minimized;
559 if (procShell_NotifyIcon)
560 {
561 if (minimized && LOWORD(wParam) == WA_INACTIVE)
562 {
563 Minimized = true;
564 ShowWindow (hwnd_Server, SW_HIDE);
565 return FALSE;
566 }
567 }
568 return DefWindowProc (hwnd, message, wParam, lParam);
569 }
570 case WM_USER + 4:
571 if (lParam == WM_LBUTTONDBLCLK) {
572 ShowWindow (hwnd_Server, SW_RESTORE);
573 SetForegroundWindow (hwnd_Server);
574 SetFocus (GetDlgItem (hwnd_Server, IDC_COMMAND));
575 }
576 return FALSE;
577 }
578
579 return FALSE;
580 }
581 #endif
582
_priority_changed(cvar_t * cvar,char * ov,char * nv)583 void _priority_changed (cvar_t *cvar, char *ov, char *nv)
584 {
585 //r1: let dedicated servers eat the cpu if needed
586 switch (cvar->intvalue)
587 {
588 case -2:
589 SetPriorityClass (GetCurrentProcess (), IDLE_PRIORITY_CLASS);
590 break;
591 #ifdef BELOW_NORMAL_PRIORITY_CLASS
592 case -1:
593 SetPriorityClass (GetCurrentProcess (), BELOW_NORMAL_PRIORITY_CLASS);
594 break;
595 #endif
596 case 0:
597 SetPriorityClass (GetCurrentProcess (), NORMAL_PRIORITY_CLASS);
598 break;
599 #ifdef ABOVE_NORMAL_PRIORITY_CLASS
600 case 1:
601 SetPriorityClass (GetCurrentProcess (), ABOVE_NORMAL_PRIORITY_CLASS);
602 break;
603 #endif
604 case 2:
605 SetPriorityClass (GetCurrentProcess (), HIGH_PRIORITY_CLASS);
606 break;
607 default:
608 Com_Printf ("Unknown priority class %d.\n", LOG_GENERAL, cvar->intvalue);
609 Cvar_Set (cvar->name, ov);
610 break;
611 }
612 }
613
614 //CRITICAL_SECTION consoleCrit;
615
Sys_AcquireConsoleMutex(void)616 void Sys_AcquireConsoleMutex (void)
617 {
618 //EnterCriticalSection (&consoleCrit);
619 }
620
Sys_ReleaseConsoleMutex(void)621 void Sys_ReleaseConsoleMutex (void)
622 {
623 //LeaveCriticalSection (&consoleCrit);
624 }
625
Sys_InitConsoleMutex(void)626 void Sys_InitConsoleMutex (void)
627 {
628 //InitializeCriticalSection (&consoleCrit);
629 }
630
Sys_FreeConsoleMutex(void)631 void Sys_FreeConsoleMutex (void)
632 {
633 //DeleteCriticalSection (&consoleCrit);
634 }
635
636 qboolean os_winxp = false;
637
638 /*
639 ================
640 Sys_Init
641 ================
642 */
Sys_Init(void)643 void Sys_Init (void)
644 {
645 OSVERSIONINFO vinfo;
646
647 timeBeginPeriod( 1 );
648
649 //initializes base time
650 Sys_Milliseconds ();
651
652 vinfo.dwOSVersionInfoSize = sizeof(vinfo);
653
654 if (!GetVersionEx (&vinfo))
655 Sys_Error ("Couldn't get OS info");
656
657 if (vinfo.dwMajorVersion < 4)
658 Sys_Error ("Quake2 requires windows version 4 or greater");
659 if (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)
660 Sys_Error ("Quake2 doesn't run on Win32s");
661 else if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
662 s_win95 = true;
663
664 #ifndef DEDICATED_ONLY
665 if (vinfo.dwMajorVersion >= 5)
666 os_winxp = true;
667 #endif
668
669 #ifndef NO_SERVER
670
671 if (dedicated->intvalue)
672 {
673 HICON hIcon;
674 BOOL hide = FALSE;
675
676 int i;
677
678 for (i = 1; i < argc; i++)
679 {
680 if (!strcmp (argv[i], "-hideconsole"))
681 {
682 hide = TRUE;
683 }
684 else if (!strcmp (argv[i], "-oldconsole"))
685 {
686 #ifdef DEDICATED_ONLY
687 if (global_Service)
688 Sys_Error ("-oldconsole and service mode are incompatible");
689 #endif
690 oldStyleConsole = TRUE;
691 break;
692 }
693 else if (!Q_stricmp (argv[i], "-HCHILD") || !Q_stricmp (argv[i], "-HPARENT") || !Q_stricmp (argv[i], "-HFILE"))
694 {
695 //for gamehost compatibility
696 oldStyleConsole = TRUE;
697 }
698 }
699
700 if (oldStyleConsole)
701 {
702 if (!AllocConsole ())
703 Sys_Error ("Couldn't create dedicated server console");
704 hinput = GetStdHandle (STD_INPUT_HANDLE);
705 houtput = GetStdHandle (STD_OUTPUT_HANDLE);
706
707 // let QHOST hook in
708 InitConProc (argc, argv);
709 }
710 else
711 {
712 #ifdef DEDICATED_ONLY
713 if (!global_Service)
714 #endif
715 {
716 if (!LoadLibrary ("Riched20"))
717 Sys_Error ("Couldn't load RICHED20.DLL. GetLastError() = %d", GetLastError());
718
719 hwnd_Server = CreateDialog (global_hInstance, MAKEINTRESOURCE(IDD_SERVER_GUI), NULL, (DLGPROC)ServerWindowProc);
720
721 if (!hwnd_Server)
722 Sys_Error ("Couldn't create dedicated server window. GetLastError() = %d", GetLastError());
723
724 if (hide)
725 ShowWindow (hwnd_Server, SW_HIDE);
726
727 SendDlgItemMessage (hwnd_Server, IDC_CONSOLE, EM_SETREADONLY, TRUE, 0);
728
729 SZ_Init (&console_buffer, console_buff, sizeof(console_buff));
730 console_buffer.allowoverflow = true;
731
732 //memset (consoleFullBuffer, 0, sizeof(consoleFullBuffer));
733
734 hIcon = (HICON)LoadImage( global_hInstance,
735 MAKEINTRESOURCE(IDI_ICON2),
736 IMAGE_ICON,
737 GetSystemMetrics(SM_CXSMICON),
738 GetSystemMetrics(SM_CYSMICON),
739 0);
740
741 //FIXME: if compiled with ICL, this icon turns into the win32 'info' icon (???)
742 if(hIcon)
743 SendMessage(hwnd_Server, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
744
745 SetFocus (GetDlgItem (hwnd_Server, IDC_COMMAND));
746 }
747 }
748 }
749 else
750 {
751 // Initialization
752 }
753
754 Sys_InitConsoleMutex ();
755
756 #ifndef _DEBUG
757 sys_fpu_bits = Cvar_Get ("sys_fpu_bits", "2", CVAR_NOSET);
758 #else
759 sys_fpu_bits = Cvar_Get ("sys_fpu_bits", "2", 0);
760 #endif
761
762 //Cmd_AddCommand ("win_priority", Sys_SetQ2Priority);
763 win_priority = Cvar_Get ("win_priority", "0", 0);
764 win_priority->changed = _priority_changed;
765 win_priority->changed (win_priority, "0", win_priority->string);
766 win_disableexceptionhandler = Cvar_Get ("win_disableexceptionhandler", "0", 0);
767 win_silentexceptionhandler = Cvar_Get ("win_silentexceptionhandler", "0", 0);
768 #endif
769 }
770
771 #ifndef NO_SERVER
772 static char console_text[1024];
773 static int console_textlen;
774
775 /*
776 ================
777 Sys_ConsoleInput
778 ================
779 */
Sys_ConsoleInput(void)780 char *Sys_ConsoleInput (void)
781 {
782 Sys_UpdateConsoleBuffer ();
783
784 if (!oldStyleConsole)
785 {
786 return NULL;
787 }
788 else
789 {
790 INPUT_RECORD recs[1024];
791 int dummy;
792 int ch, numread, numevents;
793
794 if (!dedicated || !dedicated->intvalue)
795 return NULL;
796
797 for ( ;; )
798 {
799 if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
800 Sys_Error ("Error getting # of console events");
801
802 if (numevents <= 0)
803 break;
804
805 if (!ReadConsoleInput(hinput, recs, 1, &numread))
806 Sys_Error ("Error reading console input");
807
808 if (numread != 1)
809 Sys_Error ("Couldn't read console input");
810
811 if (recs[0].EventType == KEY_EVENT)
812 {
813 if (!recs[0].Event.KeyEvent.bKeyDown)
814 {
815 ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
816
817 switch (ch)
818 {
819 case '\r':
820 WriteFile(houtput, "\r\n", 2, &dummy, NULL);
821
822 if (console_textlen)
823 {
824 if (console_textlen >= sizeof(console_text)-1)
825 {
826 Com_Printf ("Sys_ConsoleInput: Line too long, discarded.\n", LOG_SERVER);
827 return NULL;
828 }
829 console_text[console_textlen] = 0;
830 console_textlen = 0;
831 return console_text;
832 }
833 break;
834
835 case '\b':
836 if (console_textlen)
837 {
838 console_textlen--;
839 WriteFile(houtput, "\b \b", 3, &dummy, NULL);
840 }
841 break;
842
843 default:
844 if (ch >= ' ')
845 {
846 if (console_textlen < sizeof(console_text)-2)
847 {
848 WriteFile(houtput, &ch, 1, &dummy, NULL);
849 console_text[console_textlen] = ch;
850 console_textlen++;
851 }
852 }
853
854 break;
855
856 }
857 }
858 }
859 }
860 return NULL;
861 }
862 }
863
Sys_ConsoleOutputOld(const char * string)864 void Sys_ConsoleOutputOld (const char *string)
865 {
866 int dummy;
867 char text[256];
868 char cleanstring[2048];
869 const char *p;
870 char *s;
871
872 p = string;
873 s = cleanstring;
874
875 while (p[0])
876 {
877 if (p[0] == '\n')
878 {
879 *s++ = '\r';
880 }
881
882 //r1: strip high bits here
883 *s = (p[0]) & 127;
884
885 if (s[0] >= 32 || s[0] == '\n' || s[0] == '\t')
886 s++;
887
888 p++;
889
890 if ((s - cleanstring) >= sizeof(cleanstring)-2)
891 {
892 *s++ = '\n';
893 break;
894 }
895 }
896 s[0] = '\0';
897
898 if (console_textlen)
899 {
900 text[0] = '\r';
901 memset(&text[1], ' ', console_textlen);
902 text[console_textlen+1] = '\r';
903 text[console_textlen+2] = 0;
904 WriteFile(houtput, text, console_textlen+2, &dummy, NULL);
905 }
906
907 WriteFile(houtput, cleanstring, (DWORD)strlen(cleanstring), &dummy, NULL);
908
909 if (console_textlen)
910 WriteFile(houtput, console_text, console_textlen, &dummy, NULL);
911 }
912
913 /*
914 ================
915 Sys_ConsoleOutput
916
917 Print text to the dedicated console
918 ================
919 */
Sys_ConsoleOutput(const char * string)920 void Sys_ConsoleOutput (const char *string)
921 {
922 char text[2048];
923 const char *p;
924 char *s;
925 //int n = 0;
926
927 if (!dedicated->intvalue)
928 return;
929
930 if (oldStyleConsole)
931 {
932 Sys_ConsoleOutputOld (string);
933 return;
934 }
935
936 #ifdef DEDICATED_ONLY
937 if (global_Service)
938 return;
939 #endif
940
941 //r1: no output for services, non dedicated and not before buffer is initialized.
942 if (!console_buffer.maxsize)
943 return;
944
945 Sys_AcquireConsoleMutex();
946
947 #ifdef USE_PYROADMIN
948 if (pyroadminport->intvalue)
949 {
950 int len;
951 char buff[1152];
952 len = Com_sprintf (buff, sizeof(buff), "line\n%s", string);
953 Netchan_OutOfBand (NS_SERVER, &netaddress_pyroadmin, len, (byte *)buff);
954 }
955 #endif
956
957 p = string;
958 s = text;
959
960 while (p[0])
961 {
962 if (p[0] == '\n')
963 {
964 *s++ = '\r';
965 }
966
967 //r1: strip high bits here
968 *s = (p[0]) & 127;
969
970 if (s[0] >= 32 || s[0] == '\n' || s[0] == '\t')
971 s++;
972
973 p++;
974
975 if ((s - text) >= sizeof(text)-2)
976 {
977 *s++ = '\n';
978 break;
979 }
980 }
981 s[0] = '\0';
982
983 //MessageBox (NULL, text, "hi", MB_OK);
984 //if (console_buffer.cursize + strlen(text)+2 > console_buffer.maxsize)
985 SZ_Print (&console_buffer, text);
986 Sys_ReleaseConsoleMutex();
987 }
988
989 #endif
990
Sys_Sleep(int msec)991 void Sys_Sleep (int msec)
992 {
993 Sleep (msec);
994 }
995
996 /*
997 ================
998 Sys_SendKeyEvents
999
1000 Send Key_Event calls
1001 ================
1002 */
1003 #ifndef DEDICATED_ONLY
Sys_SendKeyEvents(void)1004 void Sys_SendKeyEvents (void)
1005 {
1006 if (g_pKeyboard)
1007 {
1008 IN_ReadKeyboard ();
1009 }
1010 else
1011 {
1012 MSG msg;
1013
1014 //Com_Printf ("sske\n", LOG_GENERAL);
1015
1016 //while (GetInputState())
1017 while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1018 {
1019 //if (GetMessage (&msg, NULL, 0, 0) == -1)
1020 //Com_Quit ();
1021 sys_msg_time = msg.time;
1022 TranslateMessage (&msg);
1023 DispatchMessage (&msg);
1024 }
1025 }
1026
1027 // grab frame time
1028 sys_frame_time = timeGetTime(); // FIXME: should this be at start?
1029 }
1030 #endif
1031
1032 /*
1033 ================
1034 Sys_GetClipboardData
1035
1036 ================
1037 */
Sys_GetClipboardData(void)1038 char *Sys_GetClipboardData( void )
1039 {
1040 char *data = NULL;
1041 char *cliptext;
1042
1043 if ( OpenClipboard( NULL ) != 0 )
1044 {
1045 HANDLE hClipboardData;
1046
1047 if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 )
1048 {
1049 if ( ( cliptext = GlobalLock( hClipboardData ) ) != 0 )
1050 {
1051 data = malloc( GlobalSize( hClipboardData ) + 1 );
1052 strcpy( data, cliptext );
1053 GlobalUnlock( hClipboardData );
1054 }
1055 }
1056 CloseClipboard();
1057 }
1058 return data;
1059 }
1060
1061 /*
1062 ==============================================================================
1063
1064 WINDOWS CRAP
1065
1066 ==============================================================================
1067 */
1068
1069 /*
1070 =================
1071 Sys_AppActivate
1072 =================
1073 */
Sys_AppActivate(void)1074 void Sys_AppActivate (void)
1075 {
1076 #ifndef DEDICATED_ONLY
1077 ShowWindow ( cl_hwnd, SW_RESTORE);
1078 SetForegroundWindow ( cl_hwnd );
1079 #endif
1080 }
1081
1082 /*
1083 ========================================================================
1084
1085 GAME DLL
1086
1087 ========================================================================
1088 */
1089 #ifndef NO_SERVER
1090 static HINSTANCE game_library;
1091
1092 /*
1093 =================
1094 Sys_UnloadGame
1095 =================
1096 */
Sys_UnloadGame(void)1097 void Sys_UnloadGame (void)
1098 {
1099 if (!FreeLibrary (game_library))
1100 Sys_Error ("FreeLibrary failed for game library (%d)", GetLastError());
1101 game_library = NULL;
1102 }
1103
1104 /*
1105 =================
1106 Sys_GetGameAPI
1107
1108 Loads the game dll
1109 =================
1110 */
Sys_GetGameAPI(void * parms,int baseq2DLL)1111 void *Sys_GetGameAPI (void *parms, int baseq2DLL)
1112 {
1113 void *(IMPORT *GetGameAPI) (void *);
1114 char newname[MAX_OSPATH];
1115 char name[MAX_OSPATH];
1116 char *path;
1117 char cwd[MAX_OSPATH];
1118 FILE *newExists;
1119
1120 #if defined _M_IX86
1121 const char gamename[] = "gamex86.dll";
1122
1123 #ifdef NDEBUG
1124 const char debugdir[] = "release";
1125 #else
1126 const char debugdir[] = "debug";
1127 #endif
1128
1129 #elif defined _M_ALPHA
1130 const char gamename[] = "gameaxp.dll";
1131
1132 #ifdef NDEBUG
1133 const char debugdir[] = "releaseaxp";
1134 #else
1135 const char debugdir[] = "debugaxp";
1136 #endif
1137
1138 #elif defined _WIN64
1139
1140 const char gamename[] = "gamex86_64.dll";
1141
1142 #ifdef NDEBUG
1143 const char debugdir[] = "release";
1144 #else
1145 const char debugdir[] = "debug";
1146 #endif
1147
1148 #else
1149 #error Don't know what kind of dynamic objects to use for this architecture.
1150 #endif
1151
1152 if (game_library)
1153 Com_Error (ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame");
1154
1155 // check the current debug directory first for development purposes
1156 _getcwd (cwd, sizeof(cwd));
1157 Com_sprintf (newname, sizeof(newname), "%s/%s/%s.new", cwd, debugdir, gamename);
1158 Com_sprintf (name, sizeof(name), "%s/%s/%s", cwd, debugdir, gamename);
1159 newExists = fopen (newname, "rb");
1160 if (newExists)
1161 {
1162 Com_DPrintf ("%s.new found, moving to %s...\n", gamename, gamename);
1163 fclose (newExists);
1164 remove (name);
1165 rename (newname, name);
1166 }
1167 game_library = LoadLibrary ( name );
1168 if (game_library)
1169 {
1170 Com_DPrintf ("LoadLibrary (%s)\n", name);
1171 }
1172 else
1173 {
1174 #ifdef _DEBUG
1175 // check the current directory for other development purposes
1176 Com_sprintf (name, sizeof(name), "%s/%s", cwd, gamename);
1177 Com_sprintf (newname, sizeof(newname), "%s/%s.new", cwd, gamename);
1178 newExists = fopen (newname, "rb");
1179 if (newExists)
1180 {
1181 Com_DPrintf ("%s.new found, moving to %s...\n", gamename, gamename);
1182 fclose (newExists);
1183 remove (name);
1184 rename (newname, name);
1185 }
1186 game_library = LoadLibrary ( name );
1187 if (game_library)
1188 {
1189 Com_DPrintf ("LoadLibrary (%s)\n", name);
1190 }
1191 else
1192 #endif
1193 {
1194 // now run through the search paths
1195 if (baseq2DLL)
1196 {
1197 Com_sprintf (name, sizeof(name), "./" BASEDIRNAME "/%s", gamename);
1198 game_library = LoadLibrary (name);
1199 }
1200 else
1201 {
1202 path = NULL;
1203 for (;;)
1204 {
1205 path = FS_NextPath (path);
1206 if (!path)
1207 return NULL; // couldn't find one anywhere
1208 Com_sprintf (name, sizeof(name), "%s/%s", path, gamename);
1209 Com_sprintf (newname, sizeof(newname), "%s/%s.new", path, gamename);
1210 newExists = fopen (newname, "rb");
1211 if (newExists)
1212 {
1213 Com_DPrintf ("%s.new found, moving to %s...\n", gamename, gamename);
1214 fclose (newExists);
1215 remove (name);
1216 rename (newname, name);
1217 }
1218 game_library = LoadLibrary (name);
1219 if (game_library)
1220 {
1221 Com_DPrintf ("LoadLibrary (%s)\n",name);
1222 break;
1223 }
1224 else
1225 Com_DPrintf ("LoadLibrary (%s) = %d\n", name, GetLastError());
1226 }
1227 }
1228 }
1229 }
1230
1231 if (!game_library)
1232 return NULL;
1233
1234 //if (!Sys_CheckFPUStatus())
1235 // Com_Printf ("\2WARNING: The FPU control word was changed after loading %s!\n", LOG_GENERAL, name);
1236
1237 GetGameAPI = (void *(IMPORT *)(void *))GetProcAddress (game_library, "GetGameAPI");
1238 if (!GetGameAPI)
1239 {
1240 Sys_UnloadGame ();
1241 return NULL;
1242 }
1243
1244 return GetGameAPI (parms);
1245 }
1246 #endif
1247
1248 //=======================================================================
1249
1250
1251 /*
1252 ==================
1253 ParseCommandLine
1254
1255 ==================
1256 */
ParseCommandLine(LPSTR lpCmdLine)1257 void ParseCommandLine (LPSTR lpCmdLine)
1258 {
1259 char *p;
1260
1261 argc = 1;
1262 argv[0] = "exe";
1263
1264 GetModuleFileName (NULL, bname, sizeof(bname)-1);
1265 p = strrchr (bname, '\\');
1266 if (p)
1267 binary_name = p + 1;
1268 else
1269 binary_name = bname;
1270
1271 while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
1272 {
1273 while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
1274 lpCmdLine++;
1275
1276 if (*lpCmdLine)
1277 {
1278 argv[argc] = lpCmdLine;
1279 argc++;
1280
1281 while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
1282 lpCmdLine++;
1283
1284 if (*lpCmdLine)
1285 {
1286 *lpCmdLine = 0;
1287 lpCmdLine++;
1288 }
1289
1290 }
1291 }
1292 }
1293
1294 /*#ifndef NO_SERVER
1295 void QuakeMain (void)
1296 {
1297 int time, oldtime, newtime;
1298
1299 oldtime = Sys_Milliseconds ();
1300
1301 _controlfp( _PC_24, _MCW_PC );
1302
1303 for (;;)
1304 {
1305 if (Minimized || dedicated->intvalue)
1306 Sleep (1);
1307
1308 do
1309 {
1310 newtime = Sys_Milliseconds ();
1311 time = newtime - oldtime;
1312 } while (time < 1);
1313
1314 Qcommon_Frame (time);
1315
1316 oldtime = newtime;
1317 }
1318 }
1319 #endif*/
1320
FixWorkingDirectory(void)1321 void FixWorkingDirectory (void)
1322 {
1323 int i;
1324 char *p;
1325 char curDir[MAX_PATH];
1326
1327 GetModuleFileName (NULL, curDir, sizeof(curDir)-1);
1328
1329 p = strrchr (curDir, '\\');
1330 p[0] = 0;
1331
1332 for (i = 1; i < argc; i++)
1333 {
1334 if (!strcmp (argv[i], "-nopathcheck"))
1335 goto skipPathCheck;
1336
1337 if (!strcmp (argv[i], "-nocwdcheck"))
1338 return;
1339 }
1340
1341 if (strlen(curDir) > (MAX_OSPATH - MAX_QPATH))
1342 Sys_Error ("Current path is too long. Please move your Quake II installation to a shorter path.");
1343
1344 skipPathCheck:
1345
1346 SetCurrentDirectory (curDir);
1347 }
1348
1349 #ifdef ANTICHEAT
1350 #ifndef DEDICATED_ONLY
1351 /*
1352 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
1353
1354 BOOL IsWow64()
1355 {
1356 BOOL bIsWow64 = FALSE;
1357 LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("KERNEL32"),"IsWow64Process");
1358
1359 if (NULL != fnIsWow64Process)
1360 {
1361 fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
1362 }
1363 return bIsWow64;
1364 }*/
1365
1366 typedef struct
1367 {
1368 void (*Check) (void);
1369 } anticheat_export_t;
1370
1371 anticheat_export_t *anticheat;
1372
1373 typedef VOID * (*FNINIT) (VOID);
1374
Sys_GetAntiCheatAPI(void)1375 int Sys_GetAntiCheatAPI (void)
1376 {
1377 qboolean updated = false;
1378 HMODULE hAC;
1379 static FNINIT init = NULL;
1380
1381 //windows version check
1382 if (s_win95)
1383 {
1384 Com_Printf ("ERROR: Anticheat requires Windows 2000/XP/2003/Vista.\n", LOG_GENERAL);
1385 return 0;
1386 }
1387
1388 /*if (IsWow64())
1389 {
1390 Com_Printf ("ERROR: Anticheat is currently incompatible with 64 bit Windows.\n", LOG_GENERAL);
1391 return 0;
1392 }*/
1393
1394 //already loaded, just reinit
1395 if (anticheat)
1396 {
1397 anticheat = (anticheat_export_t *)init ();
1398 if (!anticheat)
1399 return 0;
1400 return 1;
1401 }
1402
1403 reInit:
1404
1405 #if defined(_DEBUG)
1406 hAC = LoadLibrary ("anticheatd");
1407 #else
1408 hAC = LoadLibrary ("anticheat");
1409 #endif
1410 if (!hAC)
1411 return 0;
1412
1413 //this should never fail unless the anticheat.dll is bad
1414 if (!init)
1415 {
1416 init = (FNINIT)GetProcAddress (hAC, "Initialize");
1417 if (!init)
1418 Sys_Error ("Couldn't GetProcAddress Initialize on anticheat.dll!\r\n\r\nPlease check you are using a valid anticheat.dll from http://antiche.at/");
1419 }
1420
1421 anticheat = (anticheat_export_t *)init ();
1422
1423 if (!updated && !anticheat)
1424 {
1425 updated = true;
1426 FreeLibrary (hAC);
1427 hAC = NULL;
1428 init = NULL;
1429 goto reInit;
1430 }
1431
1432 if (!anticheat)
1433 return 0;
1434
1435 //if (!Sys_CheckFPUStatus ())
1436 // Com_Printf ("\2WARNING: The FPU control word has changed after loading anticheat!\n", LOG_GENERAL);
1437
1438 return 1;
1439 }
1440 #endif
1441 #endif
1442
1443 typedef BOOL (WINAPI *ENUMERATELOADEDMODULES64) (HANDLE hProcess, PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, PVOID UserContext);
1444 typedef DWORD (WINAPI *SYMSETOPTIONS) (DWORD SymOptions);
1445 typedef BOOL (WINAPI *SYMINITIALIZE) (HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess);
1446 typedef BOOL (WINAPI *SYMCLEANUP) (HANDLE hProcess);
1447 typedef BOOL (WINAPI *STACKWALK64) (
1448 DWORD MachineType,
1449 HANDLE hProcess,
1450 HANDLE hThread,
1451 LPSTACKFRAME64 StackFrame,
1452 PVOID ContextRecord,
1453 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
1454 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
1455 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
1456 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
1457 );
1458
1459 typedef PVOID (WINAPI *SYMFUNCTIONTABLEACCESS64) (HANDLE hProcess, DWORD64 AddrBase);
1460 typedef DWORD64 (WINAPI *SYMGETMODULEBASE64) (HANDLE hProcess, DWORD64 dwAddr);
1461 typedef BOOL (WINAPI *SYMFROMADDR) (HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);
1462 typedef BOOL (WINAPI *SYMGETMODULEINFO64) (HANDLE hProcess, DWORD64 dwAddr, PIMAGEHLP_MODULE64 ModuleInfo);
1463
1464 typedef DWORD64 (WINAPI *SYMLOADMODULE64) (HANDLE hProcess, HANDLE hFile, PSTR ImageName, PSTR ModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll);
1465
1466 typedef BOOL (WINAPI *MINIDUMPWRITEDUMP) (
1467 HANDLE hProcess,
1468 DWORD ProcessId,
1469 HANDLE hFile,
1470 MINIDUMP_TYPE DumpType,
1471 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
1472 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
1473 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
1474 );
1475
1476 typedef HINSTANCE (WINAPI *SHELLEXECUTEA) (HWND hwnd, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd);
1477
1478 SYMGETMODULEINFO64 fnSymGetModuleInfo64;
1479 SYMLOADMODULE64 fnSymLoadModule64;
1480
1481 typedef BOOL (WINAPI *VERQUERYVALUE) (const LPVOID pBlock, LPTSTR lpSubBlock, PUINT lplpBuffer, PUINT puLen);
1482 typedef DWORD (WINAPI *GETFILEVERSIONINFOSIZE) (LPTSTR lptstrFilename, LPDWORD lpdwHandle);
1483 typedef BOOL (WINAPI *GETFILEVERSIONINFO) (LPTSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData);
1484
1485 VERQUERYVALUE fnVerQueryValue;
1486 GETFILEVERSIONINFOSIZE fnGetFileVersionInfoSize;
1487 GETFILEVERSIONINFO fnGetFileVersionInfo;
1488 BOOL versionedGL = FALSE;
1489
EnumerateLoadedModulesProcDump(PSTR ModuleName,DWORD64 ModuleBase,ULONG ModuleSize,PVOID UserContext)1490 BOOL CALLBACK EnumerateLoadedModulesProcDump (PSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext)
1491 {
1492 VS_FIXEDFILEINFO *fileVersion;
1493 BYTE *verInfo;
1494 DWORD dummy, len;
1495 FILE *fhReport = (FILE *)UserContext;
1496 CHAR verString[32];
1497 CHAR lowered[MAX_PATH];
1498
1499 Q_strncpy (lowered, ModuleName, sizeof(lowered)-1);
1500 strlwr (lowered);
1501
1502 if (fnGetFileVersionInfo && fnVerQueryValue && fnGetFileVersionInfoSize)
1503 {
1504 if (len = (fnGetFileVersionInfoSize (ModuleName, &dummy)))
1505 {
1506 verInfo = LocalAlloc (LPTR, len);
1507 if (fnGetFileVersionInfo (ModuleName, dummy, len, verInfo))
1508 {
1509 if (fnVerQueryValue (verInfo, "\\", (LPVOID)&fileVersion, &dummy))
1510 {
1511 if (strstr (lowered, "ref_gl"))
1512 versionedGL = TRUE;
1513 sprintf (verString, "%d.%d.%d.%d", HIWORD(fileVersion->dwFileVersionMS), LOWORD(fileVersion->dwFileVersionMS), HIWORD(fileVersion->dwFileVersionLS), LOWORD(fileVersion->dwFileVersionLS));
1514 }
1515 else
1516 {
1517 strcpy (verString, "unknown");
1518 }
1519 }
1520 else
1521 {
1522 strcpy (verString, "unknown");
1523 }
1524
1525 LocalFree (verInfo);
1526 }
1527 else
1528 {
1529 strcpy (verString, "unknown");
1530 }
1531 }
1532 else
1533 {
1534 strcpy (verString, "unknown");
1535 }
1536
1537 #ifdef _M_AMD64
1538 fprintf (fhReport, "[0x%16I64X - 0x%16I64X] %s (%lu bytes, version %s)\r\n", ModuleBase, ModuleBase + (DWORD64)ModuleSize, ModuleName, ModuleSize, verString);
1539 #else
1540 fprintf (fhReport, "[0x%08I64X - 0x%08I64X] %s (%lu bytes, version %s)\r\n", ModuleBase, ModuleBase + (DWORD64)ModuleSize, ModuleName, ModuleSize, verString);
1541 #endif
1542 return TRUE;
1543 }
1544
EnumerateLoadedModulesProcSymInfo(PSTR ModuleName,DWORD64 ModuleBase,ULONG ModuleSize,PVOID UserContext)1545 BOOL CALLBACK EnumerateLoadedModulesProcSymInfo (PSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext)
1546 {
1547 IMAGEHLP_MODULE64 symInfo = {0};
1548 FILE *fhReport = (FILE *)UserContext;
1549 //CHAR szImageName[512];
1550 PCHAR symType;
1551
1552 symInfo.SizeOfStruct = sizeof(symInfo);
1553
1554 if (fnSymGetModuleInfo64 (GetCurrentProcess(), ModuleBase, &symInfo))
1555 {
1556 //WideCharToMultiByte (CP_UTF8, 0, symInfo.LoadedImageName, -1, szImageName, sizeof(szImageName), 0, NULL);
1557
1558 switch (symInfo.SymType)
1559 {
1560 case SymCoff:
1561 symType = "COFF";
1562 break;
1563 case SymCv:
1564 symType = "CV";
1565 break;
1566 case SymExport:
1567 symType = "Export";
1568 break;
1569 case SymPdb:
1570 symType = "PDB";
1571 break;
1572 case SymNone:
1573 symType = "No";
1574 break;
1575 default:
1576 symType = "Unknown";
1577 break;
1578 }
1579
1580 fprintf (fhReport, "%s, %s symbols loaded.\r\n", symInfo.LoadedImageName, symType);
1581 }
1582 else
1583 {
1584 int i = GetLastError ();
1585 fprintf (fhReport, "%s, couldn't check symbols (error %d, DBGHELP.DLL too old?)\r\n", ModuleName, i);
1586 }
1587
1588 return TRUE;
1589 }
1590
1591 CHAR szModuleName[MAX_PATH];
1592
EnumerateLoadedModulesProcInfo(PSTR ModuleName,DWORD64 ModuleBase,ULONG ModuleSize,PVOID UserContext)1593 BOOL CALLBACK EnumerateLoadedModulesProcInfo (PSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext)
1594 {
1595 DWORD64 addr = (DWORD64)UserContext;
1596 if (addr > ModuleBase && addr < ModuleBase + ModuleSize)
1597 {
1598 strncpy (szModuleName, ModuleName, sizeof(szModuleName)-1);
1599 return FALSE;
1600 }
1601 return TRUE;
1602 }
1603
1604 #ifdef USE_CURL
R1Q2UploadProgress(void * clientp,double dltotal,double dlnow,double ultotal,double ulnow)1605 static int EXPORT R1Q2UploadProgress (void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
1606 {
1607 char progressBuff[512];
1608 DWORD len, ret;
1609 double speed;
1610 int percent;
1611
1612 if (ultotal <= 0)
1613 return 0;
1614
1615 curl_easy_getinfo ((CURL *)clientp, CURLINFO_SPEED_UPLOAD, &speed);
1616
1617 percent = (int)((ulnow / ultotal)*100.0f);
1618
1619 len = snprintf (progressBuff, sizeof(progressBuff)-1, "[%d%%] %g / %g bytes, %g bytes/sec.\n", percent, ulnow, ultotal, speed);
1620 WriteConsole(GetStdHandle (STD_OUTPUT_HANDLE), progressBuff, len, &ret, NULL);
1621
1622 snprintf (progressBuff, sizeof(progressBuff)-1, "[%d%%] R1Q2 Crash Dump Uploader", percent);
1623 SetConsoleTitle (progressBuff);
1624
1625 return 0;
1626 }
1627
1628 #ifndef DEDICATED_ONLY
1629 extern cvar_t *cl_http_proxy;
1630 #endif
R1Q2UploadCrashDump(LPCSTR crashDump,LPCSTR crashText)1631 VOID R1Q2UploadCrashDump (LPCSTR crashDump, LPCSTR crashText)
1632 {
1633 struct curl_httppost* post = NULL;
1634 struct curl_httppost* last = NULL;
1635
1636 CURL *curl;
1637
1638 DWORD lenDmp;
1639 DWORD lenTxt;
1640 DWORD ret;
1641
1642 BOOL console = FALSE;
1643
1644 __try
1645 {
1646 lenDmp = Sys_FileLength (crashDump);
1647 lenTxt = Sys_FileLength (crashText);
1648
1649 if (lenTxt == -1)
1650 return;
1651
1652 if (AllocConsole ())
1653 console = TRUE;
1654
1655 SetConsoleTitle ("R1Q2 Crash Dump Uploader");
1656
1657 if (console)
1658 WriteConsole(GetStdHandle (STD_OUTPUT_HANDLE), "Connecting...\n", 14, &ret, NULL);
1659
1660 curl = curl_easy_init ();
1661
1662 /* Add simple file section */
1663 if (lenDmp > 0)
1664 curl_formadd (&post, &last, CURLFORM_PTRNAME, "minidump", CURLFORM_FILE, crashDump, CURLFORM_END);
1665
1666 curl_formadd (&post, &last, CURLFORM_PTRNAME, "report", CURLFORM_FILE, crashText, CURLFORM_END);
1667
1668 /* Set the form info */
1669 curl_easy_setopt (curl, CURLOPT_HTTPPOST, post);
1670
1671 #ifndef DEDICATED_ONLY
1672 if (cl_http_proxy)
1673 curl_easy_setopt (curl, CURLOPT_PROXY, cl_http_proxy->string);
1674 #endif
1675
1676 //curl_easy_setopt (curl, CURLOPT_UPLOAD, 1);
1677 if (console)
1678 {
1679 curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, R1Q2UploadProgress);
1680 curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, curl);
1681 curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0);
1682 }
1683 else
1684 {
1685 curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1);
1686 }
1687
1688 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
1689 curl_easy_setopt (curl, CURLOPT_USERAGENT, R1Q2_VERSION_STRING);
1690 curl_easy_setopt (curl, CURLOPT_URL, "http://www.r1ch.net/stuff/r1q2/receiveCrashDump.php");
1691
1692 if (curl_easy_perform (curl) != CURLE_OK)
1693 {
1694 if (!win_silentexceptionhandler->intvalue)
1695 MessageBox (NULL, "An error occured while trying to upload the crash dump. Please post it manually on the R1Q2 forums.", "Upload Error", MB_ICONEXCLAMATION | MB_OK);
1696 }
1697 else
1698 {
1699 if (!win_silentexceptionhandler->intvalue)
1700 {
1701 long response;
1702
1703 if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response) == CURLE_OK)
1704 {
1705 if (response == 202)
1706 {
1707 MessageBox (NULL, "Upload completed, however the server reports that you are using an out of date R1Q2 version. Crash reports from old versions are not as likely to be investigated as the problem may already have been fixed. Please update your R1Q2 using the R1Q2Updater.", "Upload Complete", MB_ICONEXCLAMATION | MB_OK);
1708 }
1709 else if (response == 200)
1710 {
1711 MessageBox (NULL, "Upload completed. Thanks for submitting your crash report!\n\nIf you would like feedback on the cause of this crash, please post a brief note on the R1Q2 forums describing what you were doing at the time the exception occured. If possible, please also attach the R1Q2CrashLog.txt file.", "Upload Complete", MB_ICONINFORMATION | MB_OK);
1712 }
1713 else
1714 {
1715 MessageBox (NULL, "Upload failed, HTTP error.", "Upload Failed", MB_ICONEXCLAMATION | MB_OK);
1716 }
1717 }
1718 }
1719 }
1720 }
1721 __except (EXCEPTION_EXECUTE_HANDLER)
1722 {
1723 if (!win_silentexceptionhandler->intvalue)
1724 MessageBox (NULL, "An exception occured while trying to upload the crash dump. Please post it manually on the R1Q2 forums.", "Upload Error", MB_ICONEXCLAMATION | MB_OK);
1725 }
1726
1727 if (console)
1728 FreeConsole ();
1729 }
1730 #endif
1731
IsOpenGLValid(VOID)1732 BOOL IsOpenGLValid (VOID)
1733 {
1734 HMODULE hOpenGL;
1735
1736 hOpenGL = GetModuleHandle ("OPENGL32");
1737 if (hOpenGL)
1738 {
1739 CHAR openglPath[MAX_PATH];
1740 CHAR systemPath[MAX_PATH];
1741
1742 GetModuleFileName (hOpenGL, openglPath, sizeof(openglPath)-1);
1743 GetSystemDirectory (systemPath, sizeof(systemPath)-1);
1744
1745 strlwr (openglPath);
1746 strlwr (systemPath);
1747
1748 if (strstr (openglPath, systemPath))
1749 return TRUE;
1750 }
1751
1752 return FALSE;
1753 }
1754
R1Q2ExceptionHandler(DWORD exceptionCode,LPEXCEPTION_POINTERS exceptionInfo)1755 DWORD R1Q2ExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo)
1756 {
1757 FILE *fhReport;
1758
1759 HANDLE hProcess;
1760
1761 HMODULE hDbgHelp, hVersion;
1762
1763 MINIDUMP_EXCEPTION_INFORMATION miniInfo;
1764 STACKFRAME64 frame = {0};
1765 CONTEXT context = *exceptionInfo->ContextRecord;
1766 SYMBOL_INFO *symInfo;
1767 DWORD64 fnOffset;
1768 CHAR tempPath[MAX_PATH];
1769 CHAR dumpPath[MAX_PATH];
1770 OSVERSIONINFOEX osInfo;
1771 SYSTEMTIME timeInfo;
1772
1773 ENUMERATELOADEDMODULES64 fnEnumerateLoadedModules64;
1774 SYMSETOPTIONS fnSymSetOptions;
1775 SYMINITIALIZE fnSymInitialize;
1776 STACKWALK64 fnStackWalk64;
1777 SYMFUNCTIONTABLEACCESS64 fnSymFunctionTableAccess64;
1778 SYMGETMODULEBASE64 fnSymGetModuleBase64;
1779 SYMFROMADDR fnSymFromAddr;
1780 SYMCLEANUP fnSymCleanup;
1781 MINIDUMPWRITEDUMP fnMiniDumpWriteDump;
1782
1783 DWORD ret, i;
1784 DWORD64 InstructionPtr;
1785
1786 BOOL wantUpload = TRUE;
1787
1788 CHAR searchPath[MAX_PATH], *p, *gameMsg;
1789
1790 if (win_disableexceptionhandler->intvalue == 2)
1791 return EXCEPTION_EXECUTE_HANDLER;
1792 else if (win_disableexceptionhandler->intvalue)
1793 return EXCEPTION_CONTINUE_SEARCH;
1794
1795 #ifndef DEDICATED_ONLY
1796 ShowCursor (TRUE);
1797 if (cl_hwnd)
1798 DestroyWindow (cl_hwnd);
1799 #else
1800 if (hwnd_Server)
1801 EnableWindow (hwnd_Server, FALSE);
1802 #endif
1803
1804 #ifdef _DEBUG
1805 ret = MessageBox (NULL, "EXCEPTION_CONTINUE_SEARCH?", "Unhandled Exception", MB_ICONERROR | MB_YESNO);
1806 if (ret == IDYES)
1807 return EXCEPTION_CONTINUE_SEARCH;
1808 #endif
1809
1810 #ifndef _DEBUG
1811 if (IsDebuggerPresent ())
1812 return EXCEPTION_CONTINUE_SEARCH;
1813 #endif
1814
1815 hDbgHelp = LoadLibrary ("DBGHELP");
1816 hVersion = LoadLibrary ("VERSION");
1817
1818 if (!hDbgHelp)
1819 {
1820 if (!win_silentexceptionhandler->intvalue)
1821 MessageBox (NULL, PRODUCTNAME " has encountered an unhandled exception and must be terminated. No crash report could be generated since R1Q2 failed to load DBGHELP.DLL. Please obtain DBGHELP.DLL and place it in your R1Q2 directory to enable crash dump generation.", "Unhandled Exception", MB_OK | MB_ICONEXCLAMATION);
1822 return EXCEPTION_CONTINUE_SEARCH;
1823 }
1824
1825 if (hVersion)
1826 {
1827 fnVerQueryValue = (VERQUERYVALUE)GetProcAddress (hVersion, "VerQueryValueA");
1828 fnGetFileVersionInfo = (GETFILEVERSIONINFO)GetProcAddress (hVersion, "GetFileVersionInfoA");
1829 fnGetFileVersionInfoSize = (GETFILEVERSIONINFOSIZE)GetProcAddress (hVersion, "GetFileVersionInfoSizeA");
1830 }
1831
1832 fnEnumerateLoadedModules64 = (ENUMERATELOADEDMODULES64)GetProcAddress (hDbgHelp, "EnumerateLoadedModules64");
1833 fnSymSetOptions = (SYMSETOPTIONS)GetProcAddress (hDbgHelp, "SymSetOptions");
1834 fnSymInitialize = (SYMINITIALIZE)GetProcAddress (hDbgHelp, "SymInitialize");
1835 fnSymFunctionTableAccess64 = (SYMFUNCTIONTABLEACCESS64)GetProcAddress (hDbgHelp, "SymFunctionTableAccess64");
1836 fnSymGetModuleBase64 = (SYMGETMODULEBASE64)GetProcAddress (hDbgHelp, "SymGetModuleBase64");
1837 fnStackWalk64 = (STACKWALK64)GetProcAddress (hDbgHelp, "StackWalk64");
1838 fnSymFromAddr = (SYMFROMADDR)GetProcAddress (hDbgHelp, "SymFromAddr");
1839 fnSymCleanup = (SYMCLEANUP)GetProcAddress (hDbgHelp, "SymCleanup");
1840 fnSymGetModuleInfo64 = (SYMGETMODULEINFO64)GetProcAddress (hDbgHelp, "SymGetModuleInfo64");
1841 //fnSymLoadModule64 = (SYMLOADMODULE64)GetProcAddress (hDbgHelp, "SymLoadModule64");
1842 fnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress (hDbgHelp, "MiniDumpWriteDump");
1843
1844 if (!fnEnumerateLoadedModules64 || !fnSymSetOptions || !fnSymInitialize || !fnSymFunctionTableAccess64 ||
1845 !fnSymGetModuleBase64 || !fnStackWalk64 || !fnSymFromAddr || !fnSymCleanup || !fnSymGetModuleInfo64)// ||
1846 //!fnSymLoadModule64)
1847 {
1848 FreeLibrary (hDbgHelp);
1849 if (hVersion)
1850 FreeLibrary (hVersion);
1851 MessageBox (NULL, PRODUCTNAME " has encountered an unhandled exception and must be terminated. No crash report could be generated since the version of DBGHELP.DLL in use is too old. Please obtain an up-to-date DBGHELP.DLL and place it in your R1Q2 directory to enable crash dump generation.", "Unhandled Exception", MB_OK | MB_ICONEXCLAMATION);
1852 return EXCEPTION_CONTINUE_SEARCH;
1853 }
1854
1855 if (!win_silentexceptionhandler->intvalue)
1856 ret = MessageBox (NULL, PRODUCTNAME " has encountered an unhandled exception and must be terminated. Would you like to generate a crash report?", "Unhandled Exception", MB_ICONEXCLAMATION | MB_YESNO);
1857 else
1858 ret = IDYES;
1859
1860 if (ret == IDNO)
1861 {
1862 FreeLibrary (hDbgHelp);
1863 if (hVersion)
1864 FreeLibrary (hVersion);
1865 return EXCEPTION_CONTINUE_SEARCH;
1866 }
1867
1868 hProcess = GetCurrentProcess();
1869
1870 fnSymSetOptions (SYMOPT_UNDNAME | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_ANYTHING);
1871
1872 GetModuleFileName (NULL, searchPath, sizeof(searchPath));
1873 p = strrchr (searchPath, '\\');
1874 if (p) p[0] = 0;
1875
1876 GetSystemTime (&timeInfo);
1877
1878 i = 1;
1879
1880 for (;;)
1881 {
1882 snprintf (tempPath, sizeof(tempPath)-1, "%s\\R1Q2CrashLog%.4d-%.2d-%.2d%_%d.txt", searchPath, timeInfo.wYear, timeInfo.wMonth, timeInfo.wDay, i);
1883 if (Sys_FileLength (tempPath) == -1)
1884 break;
1885 i++;
1886 }
1887
1888 fhReport = fopen (tempPath, "wb");
1889
1890 if (!fhReport)
1891 {
1892 FreeLibrary (hDbgHelp);
1893 if (hVersion)
1894 FreeLibrary (hVersion);
1895 return EXCEPTION_CONTINUE_SEARCH;
1896 }
1897
1898 #ifdef _DEBUG
1899 //strcat (searchPath, ";c:\\websyms");
1900 #endif
1901
1902 fnSymInitialize (hProcess, searchPath, TRUE);
1903
1904 #ifdef _DEBUG
1905 GetModuleFileName (NULL, searchPath, sizeof(searchPath));
1906 p = strrchr (searchPath, '\\');
1907 if (p) p[0] = 0;
1908 #endif
1909
1910
1911 #ifdef _M_AMD64
1912 InstructionPtr = context.Rip;
1913 frame.AddrPC.Offset = InstructionPtr;
1914 frame.AddrFrame.Offset = context.Rbp;
1915 frame.AddrStack.Offset = context.Rsp;
1916 #else
1917 InstructionPtr = context.Eip;
1918 frame.AddrPC.Offset = InstructionPtr;
1919 frame.AddrFrame.Offset = context.Ebp;
1920 frame.AddrStack.Offset = context.Esp;
1921 #endif
1922
1923 frame.AddrFrame.Mode = AddrModeFlat;
1924 frame.AddrPC.Mode = AddrModeFlat;
1925 frame.AddrStack.Mode = AddrModeFlat;
1926
1927 symInfo = LocalAlloc (LPTR, sizeof(*symInfo) + 128);
1928 symInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
1929 symInfo->MaxNameLen = 128;
1930 fnOffset = 0;
1931
1932 memset (&osInfo, 0, sizeof(osInfo));
1933 osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1934
1935 if (!GetVersionEx ((OSVERSIONINFO *)&osInfo))
1936 {
1937 osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1938 GetVersionEx ((OSVERSIONINFO *)&osInfo);
1939 }
1940
1941 strcpy (szModuleName, "<unknown>");
1942 fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcInfo, (VOID *)InstructionPtr);
1943
1944 strlwr (szModuleName);
1945
1946 if (strstr (szModuleName, "gamex86.dll"))
1947 {
1948 gameMsg =
1949 "It is very likely that the Game DLL you are using for the mod you run is at fault.\r\n"
1950 "Please send this crash report to the author(s) of the mod you are running.";
1951 wantUpload = FALSE;
1952 }
1953 else if (strstr (szModuleName, "ref_soft.dll"))
1954 {
1955 gameMsg =
1956 "It is very likely that the software renderer (ref_soft) is at fault. Software\r\n"
1957 "rendering in Q2 has always been unreliable. If possible, please use OpenGL to avoid\r\n"
1958 "crashes. Consider using the modified ref_soft.dll available on the r1ch.net forums to\r\n"
1959 "fix some small bugs if you are not using it already.";
1960 wantUpload = FALSE;
1961 }
1962 else if (strstr (szModuleName, "opengl32.dll"))
1963 {
1964 if (!IsOpenGLValid ())
1965 {
1966 gameMsg =
1967 "You have an OPENGL32.DLL file in your Quake II folder. This is overriding the correct\r\n"
1968 "DLL from your Windows system folder. Delete the OPENGL32.DLL in your Quake II folder to\r\n"
1969 "ensure the correct version of OPENGL is loaded.";
1970 wantUpload = FALSE;
1971 }
1972 else
1973 {
1974 gameMsg =
1975 "This crash occured in OpenGL. Make sure you are using the latest video drivers and\r\n"
1976 "the latest version of R1GL. If you are using software mode, consider switching to\r\n"
1977 "OpenGL for better performance.";
1978 }
1979 }
1980 else if (strstr (szModuleName, "ref_gl.dll") && !versionedGL)
1981 {
1982 gameMsg =
1983 "This crash occured in ref_gl and you aren't using R1GL. It is unlikely that\r\n"
1984 "this crash can be fixed. Consider switching to R1GL for improved crash\r\n"
1985 "reporting, speed and stability. The original ref_gl has known crash bugs that\r\n"
1986 "cannot be fixed so you will continue to crash unless you use R1GL.";
1987 wantUpload = FALSE;
1988 }
1989 else if (strstr (szModuleName, "r1q2.exe") || strstr (szModuleName, "ref_r1gl.dll") || strstr (szModuleName, "dedicated.exe"))
1990 {
1991 #ifdef USE_CURL
1992 gameMsg =
1993 "Since this crash appears to be inside R1Q2 or R1GL, it would be very helpful\r\n"
1994 "if when prompted, you submitted the crash report to r1ch.net. This will aid in\r\n"
1995 "finding the fault that caused this exception.";
1996 #else
1997 gameMsg =
1998 "\r\nSince this crash appears to be inside R1Q2 or R1GL, it would be very helpful\r\n"
1999 "if you submitted the crash report to r1ch.net forums. This will aid in finding\r\n"
2000 "the fault that caused this exception.";
2001 #endif
2002 }
2003 else
2004 {
2005 gameMsg =
2006 "Please note, unless you are using both R1Q2 and R1GL, any crashes will be much\r\n"
2007 "harder to diagnose. If you are still using ref_gl, please consider using R1GL for\r\n"
2008 "an accurate crash report.";
2009 }
2010
2011 #ifdef USE_CURL
2012 fprintf (fhReport,
2013 PRODUCTNAME " encountered an unhandled exception and has terminated. If you are able to\r\n"
2014 "reproduce this crash, please submit the crash report to r1ch.net when prompted or\r\n"
2015 "post this file and the crash dump .dmp file (if available) on the R1Q2 forums at\r\n"
2016 "http://www.r1ch.net/forum/index.php?board=8.0\r\n"
2017 "\r\n"
2018 " PLEASE MAKE SURE YOU ARE USING THE LATEST VERSIONS OF R1Q2/R1GL/ETC!\r\n"
2019 "\r\n"
2020 "This crash appears to have occured in the '%s' module.\r\n%s\r\n\r\n", szModuleName, gameMsg);
2021 #else
2022 fprintf (fhReport,
2023 PRODUCTNAME " encountered an unhandled exception and has terminated. If you are able to\r\n"
2024 "reproduce this crash, please submit the crash report on the R1Q2 forums at\r\n"
2025 "http://www.r1ch.net/forum/index.php?board=8.0 - include this .txt file and the\r\n"
2026 ".dmp file (if available)\r\n"
2027 "\r\n"
2028 "This crash appears to have occured in the '%s' module.%s\r\n\r\n", szModuleName, gameMsg);
2029 #endif
2030
2031 fprintf (fhReport, "**** UNHANDLED EXCEPTION: %x\r\nFault address: %I64p (%s)\r\n", exceptionCode, InstructionPtr, szModuleName);
2032
2033 fprintf (fhReport, PRODUCTNAME " module: %s(%s) (Version: %s)\r\n", binary_name, R1BINARY, R1Q2_VERSION_STRING);
2034 fprintf (fhReport, "Windows version: %d.%d (Build %d) %s\r\n\r\n", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.szCSDVersion);
2035
2036 fprintf (fhReport, "Symbol information:\r\n");
2037 fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcSymInfo, (VOID *)fhReport);
2038
2039 fprintf (fhReport, "\r\nEnumerate loaded modules:\r\n");
2040 fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcDump, (VOID *)fhReport);
2041
2042 fprintf (fhReport, "\r\nStack trace:\r\n");
2043 fprintf (fhReport, "Stack EIP Arg0 Arg1 Arg2 Arg3 Address\r\n");
2044 while (fnStackWalk64 (IMAGE_FILE_MACHINE_I386, hProcess, GetCurrentThread(), &frame, &context, NULL, (PFUNCTION_TABLE_ACCESS_ROUTINE64)fnSymFunctionTableAccess64, (PGET_MODULE_BASE_ROUTINE64)fnSymGetModuleBase64, NULL))
2045 {
2046 strcpy (szModuleName, "<unknown>");
2047 fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcInfo, (VOID *)frame.AddrPC.Offset);
2048 strlwr (szModuleName);
2049
2050 p = strrchr (szModuleName, '\\');
2051 if (p)
2052 {
2053 p++;
2054 }
2055 else
2056 {
2057 p = szModuleName;
2058 }
2059
2060 if (strstr (p, "ref_gl.dll") && !versionedGL)
2061 {
2062 gameMsg =
2063 "This crash occured in ref_gl and you aren't using R1GL. It is unlikely that "
2064 "this crash can be fixed. Consider switching to R1GL for improved stability, speed "
2065 "and crash reporting capabilities. The original ref_gl has known crash bugs that "
2066 "cannot be fixed so you will continue to crash unless you use R1GL.";
2067 wantUpload = FALSE;
2068 }
2069 else if (strstr (p, "ref_ncgl.dll"))
2070 {
2071 gameMsg =
2072 "This crash occured in ref_ncgl.dll. It is unlikely that this crash can be fixed. "
2073 "Consider switching to R1GL for improved stability, speed and crash reporting capabilities. "
2074 "NoCheat GL has known crash bugs that are not fixed so you will continue to crash unless you "
2075 "use R1GL.";
2076 wantUpload = FALSE;
2077 }
2078 else if (strstr (p, "atioglxx.dll") || strstr (p, "atioglx2.dll"))
2079 {
2080 gameMsg =
2081 "This crash occured in the ATI GL drivers. The ATI drivers are optimized in such "
2082 "a way that makes debugging very difficult. Please make sure you are using the latest "
2083 "ATI drivers and that you do not have any OPENGL32.DLL or ATIOGLXX.DLL files in your Q2 "
2084 "folder.";
2085 wantUpload = FALSE;
2086 }
2087 else if (strstr (p, "opengl32.dll"))
2088 {
2089 if (!IsOpenGLValid ())
2090 {
2091 gameMsg =
2092 "You have an OPENGL32.DLL file in your Quake II folder. This is overriding the correct "
2093 "DLL from your Windows system folder. Delete the OPENGL32.DLL in your Quake II folder to "
2094 "ensure the correct version of OPENGL is loaded.";
2095 wantUpload = FALSE;
2096 }
2097 }
2098
2099 #ifdef _M_AMD64
2100 if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
2101 {
2102 fprintf (fhReport, "%16I64X %16I64X %16I64X %16I64X %16I64X %16I64X %s!%s+0x%I64x\r\n", frame.AddrStack.Offset, frame.AddrPC.Offset, frame.Params[0], frame.Params[1], frame.Params[2], frame.Params[3], p, symInfo->Name, fnOffset);
2103 }
2104 else
2105 {
2106 fprintf (fhReport, "%16I64X %16I64X %16I64X %16I64X %16I64X %16I64X %s!0x%I64x\r\n", frame.AddrStack.Offset, frame.AddrPC.Offset, frame.Params[0], frame.Params[1], frame.Params[2], frame.Params[3], p, frame.AddrPC.Offset);
2107 }
2108 #else
2109 if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
2110 {
2111 fprintf (fhReport, "%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!%s+0x%I64x\r\n", frame.AddrStack.Offset, frame.AddrPC.Offset, (DWORD)frame.Params[0], (DWORD)frame.Params[1], (DWORD)frame.Params[2], (DWORD)frame.Params[3], p, symInfo->Name, fnOffset);
2112 }
2113 else
2114 {
2115 fprintf (fhReport, "%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!0x%I64x\r\n", frame.AddrStack.Offset, frame.AddrPC.Offset, (DWORD)frame.Params[0], (DWORD)frame.Params[1], (DWORD)frame.Params[2], (DWORD)frame.Params[3], p, frame.AddrPC.Offset);
2116 }
2117 #endif
2118 }
2119
2120 if (fnMiniDumpWriteDump)
2121 {
2122 HANDLE hFile;
2123
2124 GetTempPath (sizeof(dumpPath)-16, dumpPath);
2125 strcat (dumpPath, "R1Q2CrashDump.dmp");
2126
2127 hFile = CreateFile (dumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2128 if (hFile != INVALID_HANDLE_VALUE)
2129 {
2130 miniInfo.ClientPointers = TRUE;
2131 miniInfo.ExceptionPointers = exceptionInfo;
2132 miniInfo.ThreadId = GetCurrentThreadId ();
2133 if (fnMiniDumpWriteDump (hProcess, GetCurrentProcessId(), hFile, MiniDumpWithIndirectlyReferencedMemory|MiniDumpWithDataSegs, &miniInfo, NULL, NULL))
2134 {
2135 #ifndef NO_ZLIB
2136 FILE *fh;
2137 #endif
2138 CHAR zPath[MAX_PATH];
2139
2140 CloseHandle (hFile);
2141 #ifndef NO_ZLIB
2142 fh = fopen (dumpPath, "rb");
2143 if (fh)
2144 {
2145
2146 BYTE buff[0xFFFF];
2147 size_t len;
2148 gzFile gz;
2149
2150 snprintf (zPath, sizeof(zPath)-1, "%s\\R1Q2CrashLog%.4d-%.2d-%.2d_%d.dmp.gz", searchPath, timeInfo.wYear, timeInfo.wMonth, timeInfo.wDay, i);
2151 gz = gzopen (zPath, "wb");
2152 if (gz)
2153 {
2154 while ((len = fread (buff, 1, sizeof(buff), fh)) > 0)
2155 {
2156 gzwrite (gz, buff, (unsigned int)len);
2157 }
2158 gzclose (gz);
2159 fclose (fh);
2160 }
2161 }
2162 #else
2163 snprintf (zPath, sizeof(zPath)-1, "%s\\R1Q2CrashLog%.4d-%.2d-%.2d_%d.dmp", searchPath, timeInfo.wYear, timeInfo.wMonth, timeInfo.wDay, i);
2164 CopyFile (dumpPath, zPath, FALSE);
2165 #endif
2166 DeleteFile (dumpPath);
2167 strcpy (dumpPath, zPath);
2168 fprintf (fhReport, "\r\nA minidump was saved to %s.\r\nPlease include this file when posting a crash report.\r\n", dumpPath);
2169 }
2170 else
2171 {
2172 CloseHandle (hFile);
2173 DeleteFile (dumpPath);
2174 }
2175 }
2176 }
2177 else
2178 {
2179 fprintf (fhReport, "\r\nA minidump could not be created. Minidumps are only available on Windows XP or later.\r\n");
2180 }
2181
2182 fclose (fhReport);
2183
2184 LocalFree (symInfo);
2185
2186 fnSymCleanup (hProcess);
2187
2188 if (!win_silentexceptionhandler->intvalue)
2189 {
2190 HMODULE shell;
2191 shell = LoadLibrary ("SHELL32");
2192 if (shell)
2193 {
2194 SHELLEXECUTEA fncOpen = (SHELLEXECUTEA)GetProcAddress (shell, "ShellExecuteA");
2195 if (fncOpen)
2196 fncOpen (NULL, NULL, tempPath, NULL, searchPath, SW_SHOWDEFAULT);
2197
2198 FreeLibrary (shell);
2199 }
2200 }
2201
2202 #ifdef USE_CURL
2203 if (wantUpload)
2204 {
2205 if (!win_silentexceptionhandler->intvalue)
2206 ret = MessageBox (NULL, "Would you like to upload this crash report to r1ch.net to help improve R1Q2? If you are able to reproduce this crash, please do not submit multiple reports as this will only delay processing.\r\n\r\nIf you would like feedback on this crash, please post the crash report and .dmp file on the r1ch.net forums.", "Unhandled Exception", MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2);
2207 else
2208 ret = IDYES;
2209
2210 if (ret == IDYES)
2211 R1Q2UploadCrashDump (dumpPath, tempPath);
2212 }
2213 else
2214 {
2215 CHAR message[1024], *s;
2216 strcpy (message, "Crash analysis:\r\n\r\n");
2217 strcat (message, gameMsg);
2218 s = message + sizeof("Crash analysis:\r\n\r\n");
2219 while (s[0])
2220 {
2221 if (s[0] == '\n')
2222 s[0] = ' ';
2223 else if (s[0] == '\r')
2224 s[0] = ' ';
2225 s++;
2226 }
2227
2228 MessageBox (NULL, message, "Unhandled Exception", MB_OK | MB_ICONEXCLAMATION);
2229 }
2230 #endif
2231
2232 FreeLibrary (hDbgHelp);
2233 if (hVersion)
2234 FreeLibrary (hVersion);
2235
2236 return EXCEPTION_EXECUTE_HANDLER;
2237 }
2238
2239 #ifndef DEDICATED_ONLY
2240
2241 char sys_url_location[1024];
2242
Sys_ShellExec(const char * cmd)2243 void Sys_ShellExec (const char *cmd)
2244 {
2245 HMODULE shell;
2246 shell = LoadLibrary ("SHELL32");
2247 if (shell)
2248 {
2249 SHELLEXECUTEA fncOpen = (SHELLEXECUTEA)GetProcAddress (shell, "ShellExecuteA");
2250 if (fncOpen)
2251 fncOpen (NULL, NULL, cmd, NULL, NULL, SW_SHOWDEFAULT);
2252
2253 FreeLibrary (shell);
2254 }
2255 }
2256
Sys_OpenURL(void)2257 void Sys_OpenURL (void)
2258 {
2259 Sys_ShellExec (sys_url_location);
2260 }
2261
Sys_UpdateURLMenu(const char * s)2262 void Sys_UpdateURLMenu (const char *s)
2263 {
2264 HMENU menu;
2265 CHAR title[80];
2266 CHAR *dots;
2267
2268 GetSystemMenu (cl_hwnd, TRUE);
2269
2270 if (strlen (s) > 64)
2271 dots = "...";
2272 else
2273 dots = "";
2274
2275 strncpy (sys_url_location, s, sizeof(sys_url_location)-1);
2276
2277 Com_sprintf (title, sizeof(title), "Open \"%.64s%s\"", s, dots);
2278
2279 menu = GetSystemMenu (cl_hwnd, FALSE);
2280 InsertMenu (menu, 0, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
2281 InsertMenu (menu, 0, MF_BYPOSITION, 1234, title);
2282 }
2283 #endif
2284
2285 const __int64 nano100SecInWeek= (__int64)10000000*60*60*24*7;
2286 const __int64 nano100SecInDay = (__int64)10000000*60*60*24;
2287 const __int64 nano100SecInHour= (__int64)10000000*60*60;
2288 const __int64 nano100SecInMin = (__int64)10000000*60;
2289 const __int64 nano100SecInSec = (__int64)10000000;
2290
Sys_ProcessTimes_f(void)2291 void Sys_ProcessTimes_f (void)
2292 {
2293 FILETIME createTime, exitTime, kernelTime, userTime;
2294 __int64 total, tmp;
2295 DWORD days, hours, mins;
2296 double seconds;
2297
2298 GetProcessTimes (GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
2299
2300 total = *(__int64 *)&kernelTime;
2301
2302 tmp = total / nano100SecInDay;
2303 days = (DWORD)tmp;
2304 total -= tmp * nano100SecInDay;
2305
2306 tmp = total / nano100SecInHour;
2307 hours = (DWORD)tmp;
2308 total -= tmp * nano100SecInHour;
2309
2310 tmp = total / nano100SecInMin;
2311 mins = (DWORD)tmp;
2312 total -= tmp * nano100SecInMin;
2313
2314 seconds = (double)total / (double)nano100SecInSec;
2315
2316 Com_Printf ("%ud %uh %um %gs kernel\n", LOG_GENERAL, days, hours, mins, seconds);
2317
2318 total = *(__int64 *)&userTime;
2319
2320 tmp = total / nano100SecInDay;
2321 days = (DWORD)tmp;
2322 total -= tmp * nano100SecInDay;
2323
2324 tmp = total / nano100SecInHour;
2325 hours = (DWORD)tmp;
2326 total -= tmp * nano100SecInHour;
2327
2328 tmp = total / nano100SecInMin;
2329 mins = (DWORD)tmp;
2330 total -= tmp * nano100SecInMin;
2331
2332 seconds = (double)total / (double)nano100SecInSec;
2333
2334 Com_Printf ("%ud %uh %um %gs user\n", LOG_GENERAL, days, hours, mins, seconds);
2335 }
2336
2337 static unsigned int badspins, goodspins;
2338
Sys_Spinstats_f(void)2339 void Sys_Spinstats_f (void)
2340 {
2341 Com_Printf ("%u fast spins, %u slow spins, %.2f%% slow.\n", LOG_GENERAL, goodspins, badspins, ((float)badspins / (float)(goodspins+badspins)) * 100.0f);
2342 }
2343
2344 #ifdef _M_IX86
2345
Sys_GetFPUStatus(void)2346 __declspec(naked) unsigned short Sys_GetFPUStatus (void)
2347 {
2348 __asm
2349 {
2350 xor eax, eax
2351 push eax
2352 mov eax, esp
2353 fnstcw dword ptr [eax]
2354 pop eax
2355 ret
2356 }
2357 }
2358
Sys_SetFPU(byte bits)2359 void Sys_SetFPU (byte bits)
2360 {
2361 __asm
2362 {
2363 xor eax, eax
2364 push eax
2365 mov eax, esp
2366 mov ecx, eax
2367 fnstcw word ptr [eax]
2368 mov eax, [eax]
2369 and ah, 0f0h
2370 or ah, bits ; RTZ/truncate/chop mode, 24 bit precision
2371 mov [ecx], eax
2372 fldcw word ptr [ecx]
2373 pop eax
2374 }
2375 }
2376
2377 //FPU should be round to nearest, 24 bit precision.
2378 //3.20 = 0x007f
Sys_CheckFPUStatus(void)2379 qboolean Sys_CheckFPUStatus (void)
2380 {
2381 static unsigned short last_word = 0;
2382 unsigned short fpu_control_word;
2383
2384 fpu_control_word = Sys_GetFPUStatus ();
2385
2386 Com_DPrintf ("Sys_CheckFPUStatus: rounding %d, precision %d\n", (fpu_control_word >> 10) & 3, (fpu_control_word >> 8) & 3);
2387
2388 //check rounding (10) and precision (8) are set properly
2389 /*if (((fpu_control_word >> 10) & 3) != 3 ||
2390 ((fpu_control_word >> 8) & 3) != 0)
2391 {
2392 if (fpu_control_word != last_word)
2393 {
2394 Com_Printf ("\2WARNING: The FPU control word was modified by some external force to rounding %d, precision %d. Resetting.\n", LOG_GENERAL, (fpu_control_word >> 10) & 3, (fpu_control_word >> 8) & 3);
2395 Sys_SetFPU (sys_fpu_bits->intvalue);
2396 fpu_control_word = Sys_GetFPUStatus ();
2397 last_word = fpu_control_word;
2398 return false;
2399 }
2400 }*/
2401
2402 last_word = fpu_control_word;
2403 return true;
2404 }
2405 #endif
2406
2407 /*
2408 ==================
2409 WinMain
2410
2411 ==================
2412 */
2413 HINSTANCE global_hInstance;
2414
2415 //#define FLOAT2INTCAST(f)(*((int *)(&f)))
2416 //#define FLOAT_GT_ZERO(f) (FLOAT2INTCAST(f) > 0)
2417
2418 extern cvar_t *sys_loopstyle;
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)2419 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
2420 {
2421 #ifndef NO_SERVER
2422 // unsigned int handle;
2423 #endif
2424 MSG msg;
2425 unsigned int time, oldtime, newtime;
2426 int spins;
2427
2428 badspins = goodspins = 0;
2429
2430 /* previous instances do not exist in Win32 */
2431 if (hPrevInstance)
2432 return 0;
2433
2434 if (hInstance)
2435 strncpy (cmdline, lpCmdLine, sizeof(cmdline)-1);
2436
2437 global_hInstance = hInstance;
2438
2439 ParseCommandLine (lpCmdLine);
2440
2441 //r1ch: always change to our directory (ugh)
2442 FixWorkingDirectory ();
2443
2444 //hInstance is empty when we are back here with service code
2445 #ifdef DEDICATED_ONLY
2446 if (hInstance && argc > 1)
2447 {
2448 if (!strcmp(argv[1], "-service"))
2449 {
2450 global_Service = true;
2451 return main ();
2452 }
2453 }
2454 #endif
2455
2456
2457 __try
2458 {
2459 Sys_SetFPU (sys_fpu_bits->intvalue);
2460 Sys_CheckFPUStatus ();
2461
2462 Qcommon_Init (argc, argv);
2463
2464 #ifndef _M_AMD64
2465 //_controlfp( _PC_24, _MCW_PC );
2466 #endif
2467
2468 oldtime = Sys_Milliseconds ();
2469 /* main window message loop */
2470 for (;;)
2471 {
2472 // if at a full screen console, don't update unless needed
2473 //if (Minimized
2474 /*#ifndef NO_SERVER
2475 || (dedicated->intvalue)
2476 #endif*/
2477 //)
2478 //{
2479 // Sleep (1);
2480 //}
2481
2482 /*while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
2483 {
2484 //if (GetMessage (&msg, NULL, 0, 0) == -1)
2485 // Com_Quit ();
2486 sys_msg_time = msg.time;
2487
2488 #ifndef NO_SERVER
2489 if (!hwnd_Server || !IsDialogMessage(hwnd_Server, &msg))
2490 {
2491 #endif
2492 TranslateMessage (&msg);
2493 DispatchMessage (&msg);
2494 #ifndef NO_SERVER
2495 }
2496 #endif
2497 }*/
2498
2499 if (dedicated->intvalue && sys_loopstyle->intvalue)
2500 {
2501 while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
2502 {
2503 //if (GetMessage (&msg, NULL, 0, 0) == -1)
2504 // Com_Quit ();
2505 sys_msg_time = msg.time;
2506
2507 #ifndef NO_SERVER
2508 if (!hwnd_Server || !IsDialogMessage(hwnd_Server, &msg))
2509 {
2510 #endif
2511 TranslateMessage (&msg);
2512 DispatchMessage (&msg);
2513 #ifndef NO_SERVER
2514 }
2515 #endif
2516 }
2517 newtime = Sys_Milliseconds ();
2518 time = newtime - oldtime;
2519 spins = 0;
2520 }
2521 else
2522 {
2523 spins = 0;
2524 do
2525 {
2526 while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
2527 {
2528 //if (GetMessage (&msg, NULL, 0, 0) == -1)
2529 // Com_Quit ();
2530 sys_msg_time = msg.time;
2531
2532 #ifndef NO_SERVER
2533 if (!hwnd_Server || !IsDialogMessage(hwnd_Server, &msg))
2534 {
2535 #endif
2536 TranslateMessage (&msg);
2537 DispatchMessage (&msg);
2538 #ifndef NO_SERVER
2539 }
2540 #endif
2541 }
2542 newtime = Sys_Milliseconds ();
2543 time = newtime - oldtime;
2544 if (!time)
2545 Sleep (0);
2546 spins ++;
2547 } while (0 && time < 1);
2548 }
2549
2550 if (spins > 500)
2551 badspins++;
2552 else
2553 goodspins++;
2554
2555 Sys_SetFPU (sys_fpu_bits->intvalue);
2556 //Sys_CheckFPUStatus ();
2557 Qcommon_Frame (time);
2558
2559 oldtime = newtime;
2560 }
2561 }
2562 __except (R1Q2ExceptionHandler(GetExceptionCode(), GetExceptionInformation()))
2563 {
2564 return 1;
2565 }
2566
2567 return 0;
2568 }
2569