1 /*
2 ** i_main.cpp
3 ** System-specific startup code. Eventually calls D_DoomMain.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2009 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 // HEADER FILES ------------------------------------------------------------
36 
37 #define WIN32_LEAN_AND_MEAN
38 #define _WIN32_WINNT 0x0501
39 #include <windows.h>
40 #include <mmsystem.h>
41 #include <objbase.h>
42 #include <commctrl.h>
43 #include <richedit.h>
44 
45 #ifdef _MSC_VER
46 #pragma warning(disable:4244)
47 #endif
48 
49 //#include <wtsapi32.h>
50 #define NOTIFY_FOR_THIS_SESSION 0
51 
52 #include <stdlib.h>
53 #ifdef _MSC_VER
54 #include <eh.h>
55 #include <new.h>
56 #include <crtdbg.h>
57 #endif
58 #include "resource.h"
59 
60 #include <stdio.h>
61 #include <stdarg.h>
62 #include <math.h>
63 
64 #define USE_WINDOWS_DWORD
65 #include "doomerrors.h"
66 #include "hardware.h"
67 
68 #include "doomtype.h"
69 #include "m_argv.h"
70 #include "d_main.h"
71 #include "i_system.h"
72 #include "c_console.h"
73 #include "version.h"
74 #include "i_video.h"
75 #include "i_sound.h"
76 #include "i_input.h"
77 #include "w_wad.h"
78 #include "templates.h"
79 #include "cmdlib.h"
80 #include "g_level.h"
81 #include "doomstat.h"
82 #include "r_utility.h"
83 
84 #include "stats.h"
85 #include "st_start.h"
86 
87 #include <assert.h>
88 
89 // MACROS ------------------------------------------------------------------
90 
91 // The main window's title.
92 #ifdef _M_X64
93 #define X64 " 64-bit"
94 #else
95 #define X64 ""
96 #endif
97 
98 // The maximum number of functions that can be registered with atterm.
99 #define MAX_TERMS	64
100 
101 // TYPES -------------------------------------------------------------------
102 
103 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
104 
105 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
106 void CreateCrashLog (char *custominfo, DWORD customsize, HWND richedit);
107 void DisplayCrashLog ();
108 extern BYTE *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info);
109 void I_FlushBufferedConsoleStuff();
110 
111 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
112 
113 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
114 
115 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
116 
117 extern EXCEPTION_POINTERS CrashPointers;
118 extern BITMAPINFO *StartupBitmap;
119 extern UINT TimerPeriod;
120 
121 // PUBLIC DATA DEFINITIONS -------------------------------------------------
122 
123 // The command line arguments.
124 DArgs *Args;
125 
126 HINSTANCE		g_hInst;
127 DWORD			SessionID;
128 HANDLE			MainThread;
129 DWORD			MainThreadID;
130 HANDLE			StdOut;
131 bool			FancyStdOut, AttachedStdOut;
132 bool			ConWindowHidden;
133 
134 // The main window
135 HWND			Window;
136 
137 // The subwindows used for startup and error output
138 HWND			ConWindow, GameTitleWindow;
139 HWND			ErrorPane, ProgressBar, NetStartPane, StartupScreen, ErrorIcon;
140 
141 HFONT			GameTitleFont;
142 LONG			GameTitleFontHeight;
143 LONG			DefaultGUIFontHeight;
144 LONG			ErrorIconChar;
145 
146 // PRIVATE DATA DEFINITIONS ------------------------------------------------
147 
148 static const char WinClassName[] = GAMENAME "MainWindow";
149 static HMODULE hwtsapi32;		// handle to wtsapi32.dll
150 static void (*TermFuncs[MAX_TERMS])(void);
151 static int NumTerms;
152 
153 // CODE --------------------------------------------------------------------
154 
155 //==========================================================================
156 //
157 // atterm
158 //
159 // Our own atexit because atexit can be problematic under Linux, though I
160 // forget the circumstances that cause trouble.
161 //
162 //==========================================================================
163 
atterm(void (* func)(void))164 void atterm (void (*func)(void))
165 {
166 	// Make sure this function wasn't already registered.
167 	for (int i = 0; i < NumTerms; ++i)
168 	{
169 		if (TermFuncs[i] == func)
170 		{
171 			return;
172 		}
173 	}
174 	if (NumTerms == MAX_TERMS)
175 	{
176 		func ();
177 		I_FatalError ("Too many exit functions registered.\nIncrease MAX_TERMS in i_main.cpp");
178 	}
179 	TermFuncs[NumTerms++] = func;
180 }
181 
182 //==========================================================================
183 //
184 // popterm
185 //
186 // Removes the most recently register atterm function.
187 //
188 //==========================================================================
189 
popterm()190 void popterm ()
191 {
192 	if (NumTerms)
193 		NumTerms--;
194 }
195 
196 //==========================================================================
197 //
198 // call_terms
199 //
200 //==========================================================================
201 
call_terms(void)202 static void STACK_ARGS call_terms (void)
203 {
204 	while (NumTerms > 0)
205 	{
206 		TermFuncs[--NumTerms]();
207 	}
208 }
209 
210 #ifdef _MSC_VER
NewFailure(size_t size)211 static int STACK_ARGS NewFailure (size_t size)
212 {
213 	I_FatalError ("Failed to allocate %d bytes from process heap", size);
214 	return 0;
215 }
216 #endif
217 
218 //==========================================================================
219 //
220 // UnCOM
221 //
222 // Called by atterm if CoInitialize() succeeded.
223 //
224 //==========================================================================
225 
UnCOM(void)226 static void UnCOM (void)
227 {
228 	CoUninitialize ();
229 }
230 
231 //==========================================================================
232 //
233 // UnWTS
234 //
235 // Called by atterm if RegisterSessionNotification() succeeded.
236 //
237 //==========================================================================
238 
UnWTS(void)239 static void UnWTS (void)
240 {
241 	if (hwtsapi32 != 0)
242 	{
243 		typedef BOOL (WINAPI *ursn)(HWND);
244 		ursn unreg = (ursn)GetProcAddress (hwtsapi32, "WTSUnRegisterSessionNotification");
245 		if (unreg != 0)
246 		{
247 			unreg (Window);
248 		}
249 		FreeLibrary (hwtsapi32);
250 		hwtsapi32 = 0;
251 	}
252 }
253 
254 //==========================================================================
255 //
256 // LayoutErrorPane
257 //
258 // Lays out the error pane to the desired width, returning the required
259 // height.
260 //
261 //==========================================================================
262 
LayoutErrorPane(HWND pane,int w)263 static int LayoutErrorPane (HWND pane, int w)
264 {
265 	HWND ctl;
266 	RECT rectc;
267 
268 	// Right-align the Quit button.
269 	ctl = GetDlgItem (pane, IDOK);
270 	GetClientRect (ctl, &rectc);	// Find out how big it is.
271 	MoveWindow (ctl, w - rectc.right - 1, 1, rectc.right, rectc.bottom, TRUE);
272 	InvalidateRect (ctl, NULL, TRUE);
273 
274 	// Return the needed height for this layout
275 	return rectc.bottom + 2;
276 }
277 
278 //==========================================================================
279 //
280 // LayoutNetStartPane
281 //
282 // Lays out the network startup pane to the specified width, returning
283 // its required height.
284 //
285 //==========================================================================
286 
LayoutNetStartPane(HWND pane,int w)287 int LayoutNetStartPane (HWND pane, int w)
288 {
289 	HWND ctl;
290 	RECT margin, rectc;
291 	int staticheight, barheight;
292 
293 	// Determine margin sizes.
294 	SetRect (&margin, 7, 7, 0, 0);
295 	MapDialogRect (pane, &margin);
296 
297 	// Stick the message text in the upper left corner.
298 	ctl = GetDlgItem (pane, IDC_NETSTARTMESSAGE);
299 	GetClientRect (ctl, &rectc);
300 	MoveWindow (ctl, margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
301 
302 	// Stick the count text in the upper right corner.
303 	ctl = GetDlgItem (pane, IDC_NETSTARTCOUNT);
304 	GetClientRect (ctl, &rectc);
305 	MoveWindow (ctl, w - rectc.right - margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
306 	staticheight = rectc.bottom;
307 
308 	// Stretch the progress bar to fill the entire width.
309 	ctl = GetDlgItem (pane, IDC_NETSTARTPROGRESS);
310 	barheight = GetSystemMetrics (SM_CYVSCROLL);
311 	MoveWindow (ctl, margin.left, margin.top*2 + staticheight, w - margin.left*2, barheight, TRUE);
312 
313 	// Center the abort button underneath the progress bar.
314 	ctl = GetDlgItem (pane, IDCANCEL);
315 	GetClientRect (ctl, &rectc);
316 	MoveWindow (ctl, (w - rectc.right) / 2, margin.top*3 + staticheight + barheight, rectc.right, rectc.bottom, TRUE);
317 
318 	return margin.top*4 + staticheight + barheight + rectc.bottom;
319 }
320 
321 //==========================================================================
322 //
323 // LayoutMainWindow
324 //
325 // Lays out the main window with the game title and log controls and
326 // possibly the error pane and progress bar.
327 //
328 //==========================================================================
329 
LayoutMainWindow(HWND hWnd,HWND pane)330 void LayoutMainWindow (HWND hWnd, HWND pane)
331 {
332 	RECT rect;
333 	int errorpaneheight = 0;
334 	int bannerheight = 0;
335 	int progressheight = 0;
336 	int netpaneheight = 0;
337 	int leftside = 0;
338 	int w, h;
339 
340 	GetClientRect (hWnd, &rect);
341 	w = rect.right;
342 	h = rect.bottom;
343 
344 	if (DoomStartupInfo.Name.IsNotEmpty() && GameTitleWindow != NULL)
345 	{
346 		bannerheight = GameTitleFontHeight + 5;
347 		MoveWindow (GameTitleWindow, 0, 0, w, bannerheight, TRUE);
348 		InvalidateRect (GameTitleWindow, NULL, FALSE);
349 	}
350 	if (ProgressBar != NULL)
351 	{
352 		// Base the height of the progress bar on the height of a scroll bar
353 		// arrow, just as in the progress bar example.
354 		progressheight = GetSystemMetrics (SM_CYVSCROLL);
355 		MoveWindow (ProgressBar, 0, h - progressheight, w, progressheight, TRUE);
356 	}
357 	if (NetStartPane != NULL)
358 	{
359 		netpaneheight = LayoutNetStartPane (NetStartPane, w);
360 		SetWindowPos (NetStartPane, HWND_TOP, 0, h - progressheight - netpaneheight, w, netpaneheight, SWP_SHOWWINDOW);
361 	}
362 	if (pane != NULL)
363 	{
364 		errorpaneheight = LayoutErrorPane (pane, w);
365 		SetWindowPos (pane, HWND_TOP, 0, h - progressheight - netpaneheight - errorpaneheight, w, errorpaneheight, 0);
366 	}
367 	if (ErrorIcon != NULL)
368 	{
369 		leftside = GetSystemMetrics (SM_CXICON) + 6;
370 		MoveWindow (ErrorIcon, 0, bannerheight, leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
371 	}
372 	// If there is a startup screen, it covers the log window
373 	if (StartupScreen != NULL)
374 	{
375 		SetWindowPos (StartupScreen, HWND_TOP, leftside, bannerheight, w - leftside,
376 			h - bannerheight - errorpaneheight - progressheight - netpaneheight, SWP_SHOWWINDOW);
377 		InvalidateRect (StartupScreen, NULL, FALSE);
378 		MoveWindow (ConWindow, 0, 0, 0, 0, TRUE);
379 	}
380 	else
381 	{
382 		// The log window uses whatever space is left.
383 		MoveWindow (ConWindow, leftside, bannerheight, w - leftside,
384 			h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
385 	}
386 }
387 
388 
389 //==========================================================================
390 //
391 // I_SetIWADInfo
392 //
393 //==========================================================================
394 
I_SetIWADInfo()395 void I_SetIWADInfo()
396 {
397 	// Make the startup banner show itself
398 	LayoutMainWindow(Window, NULL);
399 }
400 
401 //==========================================================================
402 //
403 // LConProc
404 //
405 // The main window's WndProc during startup. During gameplay, the WndProc
406 // in i_input.cpp is used instead.
407 //
408 //==========================================================================
409 
LConProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)410 LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
411 {
412 	HWND view;
413 	HDC hdc;
414 	HBRUSH hbr;
415 	HGDIOBJ oldfont;
416 	RECT rect;
417 	int titlelen;
418 	SIZE size;
419 	LOGFONT lf;
420 	TEXTMETRIC tm;
421 	HINSTANCE inst = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
422 	DRAWITEMSTRUCT *drawitem;
423 	CHARFORMAT2W format;
424 
425 	switch (msg)
426 	{
427 	case WM_CREATE:
428 		// Create game title static control
429 		memset (&lf, 0, sizeof(lf));
430 		hdc = GetDC (hWnd);
431 		lf.lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
432 		lf.lfCharSet = ANSI_CHARSET;
433 		lf.lfWeight = FW_BOLD;
434 		lf.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
435 		strcpy (lf.lfFaceName, "Trebuchet MS");
436 		GameTitleFont = CreateFontIndirect (&lf);
437 
438 		oldfont = SelectObject (hdc, GetStockObject (DEFAULT_GUI_FONT));
439 		GetTextMetrics (hdc, &tm);
440 		DefaultGUIFontHeight = tm.tmHeight;
441 		if (GameTitleFont == NULL)
442 		{
443 			GameTitleFontHeight = DefaultGUIFontHeight;
444 		}
445 		else
446 		{
447 			SelectObject (hdc, GameTitleFont);
448 			GetTextMetrics (hdc, &tm);
449 			GameTitleFontHeight = tm.tmHeight;
450 		}
451 		SelectObject (hdc, oldfont);
452 
453 		// Create log read-only edit control
454 		view = CreateWindowEx (WS_EX_NOPARENTNOTIFY, "RichEdit20W", NULL,
455 			WS_CHILD | WS_VISIBLE | WS_VSCROLL |
456 			ES_LEFT | ES_MULTILINE | WS_CLIPSIBLINGS,
457 			0, 0, 0, 0,
458 			hWnd, NULL, inst, NULL);
459 		HRESULT hr;
460 		hr = GetLastError();
461 		if (view == NULL)
462 		{
463 			ReleaseDC (hWnd, hdc);
464 			return -1;
465 		}
466 		SendMessage (view, EM_SETREADONLY, TRUE, 0);
467 		SendMessage (view, EM_EXLIMITTEXT, 0, 0x7FFFFFFE);
468 		SendMessage (view, EM_SETBKGNDCOLOR, 0, RGB(70,70,70));
469 		// Setup default font for the log.
470 		//SendMessage (view, WM_SETFONT, (WPARAM)GetStockObject (DEFAULT_GUI_FONT), FALSE);
471 		format.cbSize = sizeof(format);
472 		format.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_CHARSET;
473 		format.dwEffects = 0;
474 		format.yHeight = 200;
475 		format.crTextColor = RGB(223,223,223);
476 		format.bCharSet = ANSI_CHARSET;
477 		format.bPitchAndFamily = FF_SWISS | VARIABLE_PITCH;
478 		wcscpy(format.szFaceName, L"DejaVu Sans");	// At least I have it. :p
479 		SendMessageW(view, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&format);
480 
481 		ConWindow = view;
482 		ReleaseDC (hWnd, hdc);
483 
484 		view = CreateWindowEx (WS_EX_NOPARENTNOTIFY, "STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, hWnd, NULL, inst, NULL);
485 		if (view == NULL)
486 		{
487 			return -1;
488 		}
489 		SetWindowLong (view, GWL_ID, IDC_STATIC_TITLE);
490 		GameTitleWindow = view;
491 
492 		return 0;
493 
494 	case WM_SIZE:
495 		if (wParam != SIZE_MAXHIDE && wParam != SIZE_MAXSHOW)
496 		{
497 			LayoutMainWindow (hWnd, ErrorPane);
498 		}
499 		return 0;
500 
501 	case WM_DRAWITEM:
502 		// Draw title banner.
503 		if (wParam == IDC_STATIC_TITLE && DoomStartupInfo.Name.IsNotEmpty())
504 		{
505 			const PalEntry *c;
506 
507 			// Draw the game title strip at the top of the window.
508 			drawitem = (LPDRAWITEMSTRUCT)lParam;
509 
510 			// Draw the background.
511 			rect = drawitem->rcItem;
512 			rect.bottom -= 1;
513 			c = (const PalEntry *)&DoomStartupInfo.BkColor;
514 			hbr = CreateSolidBrush (RGB(c->r,c->g,c->b));
515 			FillRect (drawitem->hDC, &drawitem->rcItem, hbr);
516 			DeleteObject (hbr);
517 
518 			// Calculate width of the title string.
519 			SetTextAlign (drawitem->hDC, TA_TOP);
520 			oldfont = SelectObject (drawitem->hDC, GameTitleFont != NULL ? GameTitleFont : (HFONT)GetStockObject (DEFAULT_GUI_FONT));
521 			titlelen = (int)DoomStartupInfo.Name.Len();
522 			GetTextExtentPoint32 (drawitem->hDC, DoomStartupInfo.Name, titlelen, &size);
523 
524 			// Draw the title.
525 			c = (const PalEntry *)&DoomStartupInfo.FgColor;
526 			SetTextColor (drawitem->hDC, RGB(c->r,c->g,c->b));
527 			SetBkMode (drawitem->hDC, TRANSPARENT);
528 			TextOut (drawitem->hDC, rect.left + (rect.right - rect.left - size.cx) / 2, 2, DoomStartupInfo.Name, titlelen);
529 			SelectObject (drawitem->hDC, oldfont);
530 			return TRUE;
531 		}
532 		// Draw startup screen
533 		else if (wParam == IDC_STATIC_STARTUP)
534 		{
535 			if (StartupScreen != NULL)
536 			{
537 				drawitem = (LPDRAWITEMSTRUCT)lParam;
538 
539 				rect = drawitem->rcItem;
540 				// Windows expects DIBs to be bottom-up but ours is top-down,
541 				// so flip it vertically while drawing it.
542 				StretchDIBits (drawitem->hDC, rect.left, rect.bottom - 1, rect.right - rect.left, rect.top - rect.bottom,
543 					0, 0, StartupBitmap->bmiHeader.biWidth, StartupBitmap->bmiHeader.biHeight,
544 					ST_Util_BitsForBitmap(StartupBitmap), StartupBitmap, DIB_RGB_COLORS, SRCCOPY);
545 
546 				// If the title banner is gone, then this is an ENDOOM screen, so draw a short prompt
547 				// where the command prompt would have been in DOS.
548 				if (GameTitleWindow == NULL)
549 				{
550 					static const char QuitText[] = "Press any key or click anywhere in the window to quit.";
551 
552 					SetTextColor (drawitem->hDC, RGB(240,240,240));
553 					SetBkMode (drawitem->hDC, TRANSPARENT);
554 					oldfont = SelectObject (drawitem->hDC, (HFONT)GetStockObject (DEFAULT_GUI_FONT));
555 					TextOut (drawitem->hDC, 3, drawitem->rcItem.bottom - DefaultGUIFontHeight - 3, QuitText, countof(QuitText)-1);
556 					SelectObject (drawitem->hDC, oldfont);
557 				}
558 				return TRUE;
559 			}
560 		}
561 		// Draw stop icon.
562 		else if (wParam == IDC_ICONPIC)
563 		{
564 			HICON icon;
565 			POINTL char_pos;
566 			drawitem = (LPDRAWITEMSTRUCT)lParam;
567 
568 			// This background color should match the edit control's.
569 			hbr = CreateSolidBrush (RGB(70,70,70));
570 			FillRect (drawitem->hDC, &drawitem->rcItem, hbr);
571 			DeleteObject (hbr);
572 
573 			// Draw the icon aligned with the first line of error text.
574 			SendMessage (ConWindow, EM_POSFROMCHAR, (WPARAM)&char_pos, ErrorIconChar);
575 			icon = (HICON)LoadImage (0, IDI_ERROR, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
576 			DrawIcon (drawitem->hDC, 6, char_pos.y, icon);
577 			return TRUE;
578 		}
579 		return FALSE;
580 
581 	case WM_COMMAND:
582 		if (ErrorIcon != NULL && (HWND)lParam == ConWindow && HIWORD(wParam) == EN_UPDATE)
583 		{
584 			// Be sure to redraw the error icon if the edit control changes.
585 			InvalidateRect (ErrorIcon, NULL, TRUE);
586 			return 0;
587 		}
588 		break;
589 
590 	case WM_CLOSE:
591 		PostQuitMessage (0);
592 		break;
593 
594 	case WM_DESTROY:
595 		if (GameTitleFont != NULL)
596 		{
597 			DeleteObject (GameTitleFont);
598 		}
599 		break;
600 	}
601 	return DefWindowProc (hWnd, msg, wParam, lParam);
602 }
603 
604 //==========================================================================
605 //
606 // ErrorPaneProc
607 //
608 // DialogProc for the error pane.
609 //
610 //==========================================================================
611 
ErrorPaneProc(HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam)612 INT_PTR CALLBACK ErrorPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
613 {
614 	switch (msg)
615 	{
616 	case WM_INITDIALOG:
617 		// Appear in the main window.
618 		LayoutMainWindow (GetParent (hDlg), hDlg);
619 		return TRUE;
620 
621 	case WM_COMMAND:
622 		// There is only one button, and it's "Ok" and makes us quit.
623 		if (HIWORD(wParam) == BN_CLICKED)
624 		{
625 			PostQuitMessage (0);
626 			return TRUE;
627 		}
628 		break;
629 	}
630 	return FALSE;
631 }
632 
633 //==========================================================================
634 //
635 // I_SetWndProc
636 //
637 // Sets the main WndProc, hides all the child windows, and starts up
638 // in-game input.
639 //
640 //==========================================================================
641 
I_SetWndProc()642 void I_SetWndProc()
643 {
644 	if (GetWindowLongPtr (Window, GWLP_USERDATA) == 0)
645 	{
646 		SetWindowLongPtr (Window, GWLP_USERDATA, 1);
647 		SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)WndProc);
648 		ShowWindow (ConWindow, SW_HIDE);
649 		ConWindowHidden = true;
650 		ShowWindow (GameTitleWindow, SW_HIDE);
651 		I_InitInput (Window);
652 	}
653 }
654 
655 //==========================================================================
656 //
657 // RestoreConView
658 //
659 // Returns the main window to its startup state.
660 //
661 //==========================================================================
662 
RestoreConView()663 void RestoreConView()
664 {
665 	// Make sure the window has a frame in case it was fullscreened.
666 	SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_OVERLAPPEDWINDOW);
667 	if (GetWindowLong (Window, GWL_EXSTYLE) & WS_EX_TOPMOST)
668 	{
669 		SetWindowPos (Window, HWND_BOTTOM, 0, 0, 512, 384,
670 			SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE);
671 		SetWindowPos (Window, HWND_TOP, 0, 0, 0, 0, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE);
672 	}
673 	else
674 	{
675 		SetWindowPos (Window, NULL, 0, 0, 512, 384,
676 			SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
677 	}
678 
679 	SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)LConProc);
680 	ShowWindow (ConWindow, SW_SHOW);
681 	ConWindowHidden = false;
682 	ShowWindow (GameTitleWindow, SW_SHOW);
683 	I_ShutdownInput ();		// Make sure the mouse pointer is available.
684 	I_FlushBufferedConsoleStuff();
685 	// Make sure the progress bar isn't visible.
686 	if (StartScreen != NULL)
687 	{
688 		delete StartScreen;
689 		StartScreen = NULL;
690 	}
691 }
692 
693 //==========================================================================
694 //
695 // ShowErrorPane
696 //
697 // Shows an error message, preferably in the main window, but it can
698 // use a normal message box too.
699 //
700 //==========================================================================
701 
ShowErrorPane(const char * text)702 void ShowErrorPane(const char *text)
703 {
704 	if (Window == NULL || ConWindow == NULL)
705 	{
706 		if (text != NULL)
707 		{
708 			MessageBox (Window, text,
709 				GAMESIG " Fatal Error", MB_OK|MB_ICONSTOP|MB_TASKMODAL);
710 		}
711 		return;
712 	}
713 
714 	if (StartScreen != NULL)	// Ensure that the network pane is hidden.
715 	{
716 		StartScreen->NetDone();
717 	}
718 	if (text != NULL)
719 	{
720 		char caption[100];
721 		mysnprintf(caption, countof(caption), "Fatal Error - " GAMESIG " %s " X64 " (%s)", GetVersionString(), GetGitTime());
722 		SetWindowText (Window, caption);
723 		ErrorIcon = CreateWindowEx (WS_EX_NOPARENTNOTIFY, "STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, Window, NULL, g_hInst, NULL);
724 		if (ErrorIcon != NULL)
725 		{
726 			SetWindowLong (ErrorIcon, GWL_ID, IDC_ICONPIC);
727 		}
728 	}
729 	ErrorPane = CreateDialogParam (g_hInst, MAKEINTRESOURCE(IDD_ERRORPANE), Window, ErrorPaneProc, (LONG_PTR)NULL);
730 
731 	if (text != NULL)
732 	{
733 		CHARRANGE end;
734 		CHARFORMAT2 oldformat, newformat;
735 		PARAFORMAT2 paraformat;
736 
737 		// Append the error message to the log.
738 		end.cpMax = end.cpMin = GetWindowTextLength (ConWindow);
739 		SendMessage (ConWindow, EM_EXSETSEL, 0, (LPARAM)&end);
740 
741 		// Remember current charformat.
742 		oldformat.cbSize = sizeof(oldformat);
743 		SendMessage (ConWindow, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
744 
745 		// Use bigger font and standout colors.
746 		newformat.cbSize = sizeof(newformat);
747 		newformat.dwMask = CFM_BOLD | CFM_COLOR | CFM_SIZE;
748 		newformat.dwEffects = CFE_BOLD;
749 		newformat.yHeight = oldformat.yHeight * 5 / 4;
750 		newformat.crTextColor = RGB(255,170,170);
751 		SendMessage (ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&newformat);
752 
753 		// Indent the rest of the text to make the error message stand out a little more.
754 		paraformat.cbSize = sizeof(paraformat);
755 		paraformat.dwMask = PFM_STARTINDENT | PFM_OFFSETINDENT | PFM_RIGHTINDENT;
756 		paraformat.dxStartIndent = paraformat.dxOffset = paraformat.dxRightIndent = 120;
757 		SendMessage (ConWindow, EM_SETPARAFORMAT, 0, (LPARAM)&paraformat);
758 		SendMessage (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)"\n");
759 
760 		// Find out where the error lines start for the error icon display control.
761 		SendMessage (ConWindow, EM_EXGETSEL, 0, (LPARAM)&end);
762 		ErrorIconChar = end.cpMax;
763 
764 		// Now start adding the actual error message.
765 		SendMessage (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)"Execution could not continue.\n\n");
766 
767 		// Restore old charformat but with light yellow text.
768 		oldformat.crTextColor = RGB(255,255,170);
769 		SendMessage (ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
770 
771 		// Add the error text.
772 		SendMessage (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)text);
773 
774 		// Make sure the error text is not scrolled below the window.
775 		SendMessage (ConWindow, EM_LINESCROLL, 0, SendMessage (ConWindow, EM_GETLINECOUNT, 0, 0));
776 		// The above line scrolled everything off the screen, so pretend to move the scroll
777 		// bar thumb, which clamps to not show any extra lines if it doesn't need to.
778 		SendMessage (ConWindow, EM_SCROLL, SB_PAGEDOWN, 0);
779 	}
780 
781 	BOOL bRet;
782 	MSG msg;
783 
784 	while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
785 	{
786 		if (bRet == -1)
787 		{
788 			MessageBox (Window, text,
789 				GAMESIG " Fatal Error", MB_OK|MB_ICONSTOP|MB_TASKMODAL);
790 			return;
791 		}
792 		else if (!IsDialogMessage (ErrorPane, &msg))
793 		{
794 			TranslateMessage (&msg);
795 			DispatchMessage (&msg);
796 		}
797 	}
798 }
799 
800 //==========================================================================
801 //
802 // DoMain
803 //
804 //==========================================================================
805 
DoMain(HINSTANCE hInstance)806 void DoMain (HINSTANCE hInstance)
807 {
808 	LONG WinWidth, WinHeight;
809 	int height, width, x, y;
810 	RECT cRect;
811 	TIMECAPS tc;
812 	DEVMODE displaysettings;
813 
814 	try
815 	{
816 #ifdef _MSC_VER
817 		_set_new_handler (NewFailure);
818 #endif
819 
820 		Args = new DArgs(__argc, __argv);
821 
822 		// Under XP, get our session ID so we can know when the user changes/locks sessions.
823 		// Since we need to remain binary compatible with older versions of Windows, we
824 		// need to extract the ProcessIdToSessionId function from kernel32.dll manually.
825 		HMODULE kernel = GetModuleHandle ("kernel32.dll");
826 
827 		if (Args->CheckParm("-stdout"))
828 		{
829 			// As a GUI application, we don't normally get a console when we start.
830 			// If we were run from the shell and are on XP+, we can attach to its
831 			// console. Otherwise, we can create a new one. If we already have a
832 			// stdout handle, then we have been redirected and should just use that
833 			// handle instead of creating a console window.
834 
835 			StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
836 			if (StdOut != NULL)
837 			{
838 				// It seems that running from a shell always creates a std output
839 				// for us, even if it doesn't go anywhere. (Running from Explorer
840 				// does not.) If we can get file information for this handle, it's
841 				// a file or pipe, so use it. Otherwise, pretend it wasn't there
842 				// and find a console to use instead.
843 				BY_HANDLE_FILE_INFORMATION info;
844 				if (!GetFileInformationByHandle(StdOut, &info))
845 				{
846 					StdOut = NULL;
847 				}
848 			}
849 			if (StdOut == NULL)
850 			{
851 				// AttachConsole was introduced with Windows XP. (OTOH, since we
852 				// have to share the console with the shell, I'm not sure if it's
853 				// a good idea to actually attach to it.)
854 				typedef BOOL (WINAPI *ac)(DWORD);
855 				ac attach_console = kernel != NULL ? (ac)GetProcAddress(kernel, "AttachConsole") : NULL;
856 				if (attach_console != NULL && attach_console(ATTACH_PARENT_PROCESS))
857 				{
858 					StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
859 					DWORD foo; WriteFile(StdOut, "\n", 1, &foo, NULL);
860 					AttachedStdOut = true;
861 				}
862 				if (StdOut == NULL && AllocConsole())
863 				{
864 					StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
865 				}
866 				FancyStdOut = true;
867 			}
868 		}
869 
870 		// Set the timer to be as accurate as possible
871 		if (timeGetDevCaps (&tc, sizeof(tc)) != TIMERR_NOERROR)
872 			TimerPeriod = 1;	// Assume minimum resolution of 1 ms
873 		else
874 			TimerPeriod = tc.wPeriodMin;
875 
876 		timeBeginPeriod (TimerPeriod);
877 
878 		/*
879 		killough 1/98:
880 
881 		This fixes some problems with exit handling
882 		during abnormal situations.
883 
884 		The old code called I_Quit() to end program,
885 		while now I_Quit() is installed as an exit
886 		handler and exit() is called to exit, either
887 		normally or abnormally.
888 		*/
889 
890 		atexit (call_terms);
891 
892 		atterm (I_Quit);
893 
894 		// Figure out what directory the program resides in.
895 		char *program;
896 
897 #ifdef _MSC_VER
898 		if (_get_pgmptr(&program) != 0)
899 		{
900 			I_FatalError("Could not determine program location.");
901 		}
902 #else
903 		char progbuff[1024];
904 		GetModuleFileName(0, progbuff, sizeof(progbuff));
905 		progbuff[1023] = '\0';
906 		program = progbuff;
907 #endif
908 
909 		progdir = program;
910 		program = progdir.LockBuffer();
911 		*(strrchr(program, '\\') + 1) = '\0';
912 		FixPathSeperator(program);
913 		progdir.Truncate((long)strlen(program));
914 		progdir.UnlockBuffer();
915 
916 		width = 512;
917 		height = 384;
918 
919 		// Many Windows structures that specify their size do so with the first
920 		// element. DEVMODE is not one of those structures.
921 		memset (&displaysettings, 0, sizeof(displaysettings));
922 		displaysettings.dmSize = sizeof(displaysettings);
923 		EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &displaysettings);
924 		x = (displaysettings.dmPelsWidth - width) / 2;
925 		y = (displaysettings.dmPelsHeight - height) / 2;
926 
927 		if (Args->CheckParm ("-0"))
928 		{
929 			x = y = 0;
930 		}
931 
932 		WNDCLASS WndClass;
933 		WndClass.style			= 0;
934 		WndClass.lpfnWndProc	= LConProc;
935 		WndClass.cbClsExtra		= 0;
936 		WndClass.cbWndExtra		= 0;
937 		WndClass.hInstance		= hInstance;
938 		WndClass.hIcon			= LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON1));
939 		WndClass.hCursor		= LoadCursor (NULL, IDC_ARROW);
940 		WndClass.hbrBackground	= NULL;
941 		WndClass.lpszMenuName	= NULL;
942 		WndClass.lpszClassName	= (LPCTSTR)WinClassName;
943 
944 		/* register this new class with Windows */
945 		if (!RegisterClass((LPWNDCLASS)&WndClass))
946 			I_FatalError ("Could not register window class");
947 
948 		/* create window */
949 		char caption[100];
950 		mysnprintf(caption, countof(caption), "" GAMESIG " %s " X64 " (%s)", GetVersionString(), GetGitTime());
951 		Window = CreateWindowEx(
952 				WS_EX_APPWINDOW,
953 				(LPCTSTR)WinClassName,
954 				(LPCTSTR)caption,
955 				WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
956 				x, y, width, height,
957 				(HWND)   NULL,
958 				(HMENU)  NULL,
959 						hInstance,
960 				NULL);
961 
962 		if (!Window)
963 			I_FatalError ("Could not open window");
964 
965 		if (kernel != NULL)
966 		{
967 			typedef BOOL (WINAPI *pts)(DWORD, DWORD *);
968 			pts pidsid = (pts)GetProcAddress (kernel, "ProcessIdToSessionId");
969 			if (pidsid != 0)
970 			{
971 				if (!pidsid (GetCurrentProcessId(), &SessionID))
972 				{
973 					SessionID = 0;
974 				}
975 				hwtsapi32 = LoadLibraryA ("wtsapi32.dll");
976 				if (hwtsapi32 != 0)
977 				{
978 					FARPROC reg = GetProcAddress (hwtsapi32, "WTSRegisterSessionNotification");
979 					if (reg == 0 || !((BOOL(WINAPI *)(HWND, DWORD))reg) (Window, NOTIFY_FOR_THIS_SESSION))
980 					{
981 						FreeLibrary (hwtsapi32);
982 						hwtsapi32 = 0;
983 					}
984 					else
985 					{
986 						atterm (UnWTS);
987 					}
988 				}
989 			}
990 		}
991 
992 		GetClientRect (Window, &cRect);
993 
994 		WinWidth = cRect.right;
995 		WinHeight = cRect.bottom;
996 
997 		CoInitialize (NULL);
998 		atterm (UnCOM);
999 
1000 		C_InitConsole (((WinWidth / 8) + 2) * 8, (WinHeight / 12) * 8, false);
1001 
1002 		I_DetectOS ();
1003 		D_DoomMain ();
1004 	}
1005 	catch (class CNoRunExit &)
1006 	{
1007 		I_ShutdownGraphics();
1008 		if (FancyStdOut && !AttachedStdOut)
1009 		{ // Outputting to a new console window: Wait for a keypress before quitting.
1010 			DWORD bytes;
1011 			HANDLE stdinput = GetStdHandle(STD_INPUT_HANDLE);
1012 
1013 			ShowWindow (Window, SW_HIDE);
1014 			WriteFile(StdOut, "Press any key to exit...", 24, &bytes, NULL);
1015 			FlushConsoleInputBuffer(stdinput);
1016 			SetConsoleMode(stdinput, 0);
1017 			ReadConsole(stdinput, &bytes, 1, &bytes, NULL);
1018 		}
1019 		else if (StdOut == NULL)
1020 		{
1021 			ShowErrorPane(NULL);
1022 		}
1023 		exit(0);
1024 	}
1025 	catch (class CDoomError &error)
1026 	{
1027 		I_ShutdownGraphics ();
1028 		RestoreConView ();
1029 		if (error.GetMessage ())
1030 		{
1031 			ShowErrorPane (error.GetMessage());
1032 		}
1033 		exit (-1);
1034 	}
1035 }
1036 
1037 //==========================================================================
1038 //
1039 // DoomSpecificInfo
1040 //
1041 // Called by the crash logger to get application-specific information.
1042 //
1043 //==========================================================================
1044 
DoomSpecificInfo(char * buffer,size_t bufflen)1045 void DoomSpecificInfo (char *buffer, size_t bufflen)
1046 {
1047 	const char *arg;
1048 	char *const buffend = buffer + bufflen - 2;	// -2 for CRLF at end
1049 	int i;
1050 
1051 	buffer += mysnprintf (buffer, buffend - buffer, GAMENAME " version %s (%s)", GetVersionString(), GetGitHash());
1052 	buffer += mysnprintf (buffer, buffend - buffer, "\r\nCommand line: %s\r\n", GetCommandLine());
1053 
1054 	for (i = 0; (arg = Wads.GetWadName (i)) != NULL; ++i)
1055 	{
1056 		buffer += mysnprintf (buffer, buffend - buffer, "\r\nWad %d: %s", i, arg);
1057 	}
1058 
1059 	if (gamestate != GS_LEVEL && gamestate != GS_TITLELEVEL)
1060 	{
1061 		buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nNot in a level.");
1062 	}
1063 	else
1064 	{
1065 		buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nCurrent map: %s", level.MapName.GetChars());
1066 
1067 		if (!viewactive)
1068 		{
1069 			buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nView not active.");
1070 		}
1071 		else
1072 		{
1073 			buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nviewx = %d", viewx);
1074 			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewy = %d", viewy);
1075 			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewz = %d", viewz);
1076 			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewangle = %x", viewangle);
1077 		}
1078 	}
1079 	*buffer++ = '\r';
1080 	*buffer++ = '\n';
1081 	*buffer++ = '\0';
1082 }
1083 
1084 // Here is how the error logging system works.
1085 //
1086 // To catch exceptions that occur in secondary threads, CatchAllExceptions is
1087 // set as the UnhandledExceptionFilter for this process. It records the state
1088 // of the thread at the time of the crash using CreateCrashLog and then queues
1089 // an APC on the primary thread. When the APC executes, it raises a software
1090 // exception that gets caught by the __try/__except block in WinMain.
1091 // I_GetEvent calls SleepEx to put the primary thread in a waitable state
1092 // periodically so that the APC has a chance to execute.
1093 //
1094 // Exceptions on the primary thread are caught by the __try/__except block in
1095 // WinMain. Not only does it record the crash information, it also shuts
1096 // everything down and displays a dialog with the information present. If a
1097 // console log is being produced, the information will also be appended to it.
1098 //
1099 // If a debugger is running, CatchAllExceptions never executes, so secondary
1100 // thread exceptions will always be caught by the debugger. For the primary
1101 // thread, IsDebuggerPresent is called to determine if a debugger is present.
1102 // Note that this function is not present on Windows 95, so we cannot
1103 // statically link to it.
1104 //
1105 // To make this work with MinGW, you will need to use inline assembly
1106 // because GCC offers no native support for Windows' SEH.
1107 
1108 //==========================================================================
1109 //
1110 // SleepForever
1111 //
1112 //==========================================================================
1113 
SleepForever()1114 void SleepForever ()
1115 {
1116 	Sleep (INFINITE);
1117 }
1118 
1119 //==========================================================================
1120 //
1121 // ExitMessedUp
1122 //
1123 // An exception occurred while exiting, so don't do any standard processing.
1124 // Just die.
1125 //
1126 //==========================================================================
1127 
ExitMessedUp(LPEXCEPTION_POINTERS foo)1128 LONG WINAPI ExitMessedUp (LPEXCEPTION_POINTERS foo)
1129 {
1130 	ExitProcess (1000);
1131 }
1132 
1133 //==========================================================================
1134 //
1135 // ExitFatally
1136 //
1137 //==========================================================================
1138 
ExitFatally(ULONG_PTR dummy)1139 void CALLBACK ExitFatally (ULONG_PTR dummy)
1140 {
1141 	SetUnhandledExceptionFilter (ExitMessedUp);
1142 	I_ShutdownGraphics ();
1143 	RestoreConView ();
1144 	DisplayCrashLog ();
1145 	exit (-1);
1146 }
1147 
1148 //==========================================================================
1149 //
1150 // CatchAllExceptions
1151 //
1152 //==========================================================================
1153 
CatchAllExceptions(LPEXCEPTION_POINTERS info)1154 LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info)
1155 {
1156 #ifdef _DEBUG
1157 	if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
1158 	{
1159 		return EXCEPTION_CONTINUE_SEARCH;
1160 	}
1161 #endif
1162 
1163 	static bool caughtsomething = false;
1164 
1165 	if (caughtsomething) return EXCEPTION_EXECUTE_HANDLER;
1166 	caughtsomething = true;
1167 
1168 	char *custominfo = (char *)HeapAlloc (GetProcessHeap(), 0, 16384);
1169 
1170 	CrashPointers = *info;
1171 	DoomSpecificInfo (custominfo, 16384);
1172 	CreateCrashLog (custominfo, (DWORD)strlen(custominfo), ConWindow);
1173 
1174 	// If the main thread crashed, then make it clean up after itself.
1175 	// Otherwise, put the crashing thread to sleep and signal the main thread to clean up.
1176 	if (GetCurrentThreadId() == MainThreadID)
1177 	{
1178 #ifndef _M_X64
1179 		info->ContextRecord->Eip = (DWORD_PTR)ExitFatally;
1180 #else
1181 		info->ContextRecord->Rip = (DWORD_PTR)ExitFatally;
1182 #endif
1183 	}
1184 	else
1185 	{
1186 #ifndef _M_X64
1187 		info->ContextRecord->Eip = (DWORD_PTR)SleepForever;
1188 #else
1189 		info->ContextRecord->Rip = (DWORD_PTR)SleepForever;
1190 #endif
1191 		QueueUserAPC (ExitFatally, MainThread, 0);
1192 	}
1193 	return EXCEPTION_CONTINUE_EXECUTION;
1194 }
1195 
1196 //==========================================================================
1197 //
1198 // infiniterecursion
1199 //
1200 // Debugging routine for testing the crash logger.
1201 //
1202 //==========================================================================
1203 
1204 #ifdef _DEBUG
infiniterecursion(int foo)1205 static void infiniterecursion(int foo)
1206 {
1207 	if (foo)
1208 	{
1209 		infiniterecursion(foo);
1210 	}
1211 }
1212 #endif
1213 
1214 //==========================================================================
1215 //
1216 // WinMain
1217 //
1218 //==========================================================================
1219 
WinMain(HINSTANCE hInstance,HINSTANCE nothing,LPSTR cmdline,int nCmdShow)1220 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE nothing, LPSTR cmdline, int nCmdShow)
1221 {
1222 	g_hInst = hInstance;
1223 
1224 	InitCommonControls ();			// Load some needed controls and be pretty under XP
1225 
1226 	// We need to load riched20.dll so that we can create the control.
1227 	if (NULL == LoadLibrary ("riched20.dll"))
1228 	{
1229 		// This should only happen on basic Windows 95 installations, but since we
1230 		// don't support Windows 95, we have no obligation to provide assistance in
1231 		// getting it installed.
1232 		MessageBoxA(NULL, "Could not load riched20.dll", GAMENAME " Error", MB_OK | MB_ICONSTOP);
1233 		exit(0);
1234 	}
1235 
1236 #if !defined(__GNUC__) && defined(_DEBUG)
1237 	if (__argc == 2 && strcmp (__argv[1], "TestCrash") == 0)
1238 	{
1239 		__try
1240 		{
1241 			*(int *)0 = 0;
1242 		}
1243 		__except(CrashPointers = *GetExceptionInformation(),
1244 			CreateCrashLog (__argv[1], 9, NULL), EXCEPTION_EXECUTE_HANDLER)
1245 		{
1246 		}
1247 		DisplayCrashLog ();
1248 		exit (0);
1249 	}
1250 	if (__argc == 2 && strcmp (__argv[1], "TestStackCrash") == 0)
1251 	{
1252 		__try
1253 		{
1254 			infiniterecursion(1);
1255 		}
1256 		__except(CrashPointers = *GetExceptionInformation(),
1257 			CreateCrashLog (__argv[1], 14, NULL), EXCEPTION_EXECUTE_HANDLER)
1258 		{
1259 		}
1260 		DisplayCrashLog ();
1261 		exit (0);
1262 	}
1263 #endif
1264 
1265 	MainThread = INVALID_HANDLE_VALUE;
1266 	DuplicateHandle (GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &MainThread,
1267 		0, FALSE, DUPLICATE_SAME_ACCESS);
1268 	MainThreadID = GetCurrentThreadId();
1269 
1270 #ifndef _DEBUG
1271 	if (MainThread != INVALID_HANDLE_VALUE)
1272 	{
1273 		SetUnhandledExceptionFilter (CatchAllExceptions);
1274 	}
1275 #endif
1276 
1277 #if defined(_DEBUG) && defined(_MSC_VER)
1278 	// Uncomment this line to make the Visual C++ CRT check the heap before
1279 	// every allocation and deallocation. This will be slow, but it can be a
1280 	// great help in finding problem areas.
1281 	//_CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF);
1282 
1283 	// Enable leak checking at exit.
1284 	_CrtSetDbgFlag (_CrtSetDbgFlag(0) | _CRTDBG_LEAK_CHECK_DF);
1285 
1286 	// Use this to break at a specific allocation number.
1287 	//_crtBreakAlloc = 77624;
1288 #endif
1289 
1290 	DoMain (hInstance);
1291 
1292 	CloseHandle (MainThread);
1293 	MainThread = INVALID_HANDLE_VALUE;
1294 	return 0;
1295 }
1296