1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "idlib/CmdArgs.h"
31 #include "framework/async/AsyncNetwork.h"
32 #include "framework/Licensee.h"
33 #include "framework/UsercmdGen.h"
34 #include "renderer/tr_local.h"
35 #include "sys/win32/rc/CreateResourceIDs.h"
36 #include "sys/sys_local.h"
37 
38 #include "sys/win32/win_local.h"
39 
40 #include <errno.h>
41 #include <float.h>
42 #include <fcntl.h>
43 #include <direct.h>
44 #include <io.h>
45 #include <conio.h>
46 #include <shellapi.h>
47 #include <shlobj.h>
48 
49 #ifndef __MRC__
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #endif
53 
54 #include "tools/edit_public.h"
55 
56 #include <SDL_main.h>
57 
58 idCVar Win32Vars_t::win_outputDebugString( "win_outputDebugString", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
59 idCVar Win32Vars_t::win_outputEditString( "win_outputEditString", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
60 idCVar Win32Vars_t::win_viewlog( "win_viewlog", "0", CVAR_SYSTEM | CVAR_INTEGER, "" );
61 
62 Win32Vars_t	win32;
63 
64 #ifdef ID_ALLOW_TOOLS
65 /* These are required for tools (DG: taken from SteelStorm2) */
66 
67 static HMODULE hOpenGL_DLL;
68 
69 typedef int  (WINAPI * PWGLCHOOSEPIXELFORMAT) (HDC, CONST PIXELFORMATDESCRIPTOR *);
70 typedef int   (WINAPI * PWGLDESCRIBEPIXELFORMAT) (HDC, int, UINT, LPPIXELFORMATDESCRIPTOR);
71 typedef int   (WINAPI * PWGLGETPIXELFORMAT)(HDC);
72 typedef BOOL(WINAPI * PWGLSETPIXELFORMAT)(HDC, int, CONST PIXELFORMATDESCRIPTOR *);
73 typedef BOOL(WINAPI * PWGLSWAPBUFFERS)(HDC);
74 
75 PWGLCHOOSEPIXELFORMAT	qwglChoosePixelFormat;
76 PWGLDESCRIBEPIXELFORMAT	qwglDescribePixelFormat;
77 PWGLGETPIXELFORMAT		qwglGetPixelFormat;
78 PWGLSETPIXELFORMAT		qwglSetPixelFormat;
79 PWGLSWAPBUFFERS			qwglSwapBuffers;
80 
81 typedef BOOL(WINAPI * PWGLCOPYCONTEXT)(HGLRC, HGLRC, UINT);
82 typedef HGLRC(WINAPI * PWGLCREATECONTEXT)(HDC);
83 typedef HGLRC(WINAPI * PWGLCREATELAYERCONTEXT)(HDC, int);
84 typedef BOOL(WINAPI * PWGLDELETECONTEXT)(HGLRC);
85 typedef HGLRC(WINAPI * PWGLGETCURRENTCONTEXT)(VOID);
86 typedef HDC(WINAPI * PWGLGETCURRENTDC)(VOID);
87 typedef PROC(WINAPI * PWGLGETPROCADDRESS)(LPCSTR);
88 typedef BOOL(WINAPI * PWGLMAKECURRENT)(HDC, HGLRC);
89 typedef BOOL(WINAPI * PWGLSHARELISTS)(HGLRC, HGLRC);
90 typedef BOOL(WINAPI * PWGLUSEFONTBITMAPS)(HDC, DWORD, DWORD, DWORD);
91 
92 
93 PWGLCOPYCONTEXT			qwglCopyContext;
94 PWGLCREATECONTEXT		qwglCreateContext;
95 PWGLCREATELAYERCONTEXT	qwglCreateLayerContext;
96 PWGLDELETECONTEXT		qwglDeleteContext;
97 PWGLGETCURRENTCONTEXT	qwglGetCurrentContext;
98 PWGLGETCURRENTDC		qwglGetCurrentDC;
99 PWGLGETPROCADDRESS		qwglGetProcAddress;
100 PWGLMAKECURRENT			qwglMakeCurrent;
101 PWGLSHARELISTS			qwglShareLists;
102 PWGLUSEFONTBITMAPS		qwglUseFontBitmaps;
103 
104 typedef BOOL(WINAPI * PWGLUSEFONTOUTLINES)(HDC, DWORD, DWORD, DWORD, FLOAT,
105 	FLOAT, int, LPGLYPHMETRICSFLOAT);
106 typedef BOOL(WINAPI * PWGLDESCRIBELAYERPLANE)(HDC, int, int, UINT,
107 	LPLAYERPLANEDESCRIPTOR);
108 typedef int  (WINAPI * PWGLSETLAYERPALETTEENTRIES)(HDC, int, int, int,
109 	CONST COLORREF *);
110 typedef int  (WINAPI * PWGLGETLAYERPALETTEENTRIES)(HDC, int, int, int,
111 	COLORREF *);
112 typedef BOOL(WINAPI * PWGLREALIZELAYERPALETTE)(HDC, int, BOOL);
113 typedef BOOL(WINAPI * PWGLSWAPLAYERBUFFERS)(HDC, UINT);
114 
115 PWGLUSEFONTOUTLINES			qwglUseFontOutlines;
116 PWGLDESCRIBELAYERPLANE		qwglDescribeLayerPlane;
117 PWGLSETLAYERPALETTEENTRIES	qwglSetLayerPaletteEntries;
118 PWGLGETLAYERPALETTEENTRIES	qwglGetLayerPaletteEntries;
119 PWGLREALIZELAYERPALETTE		qwglRealizeLayerPalette;
120 PWGLSWAPLAYERBUFFERS		qwglSwapLayerBuffers;
121 
122 #endif /* End stuff required for tools */
123 
124 /*
125 =============
126 Sys_Error
127 
128 Show the early console as an error dialog
129 =============
130 */
Sys_Error(const char * error,...)131 void Sys_Error( const char *error, ... ) {
132 	va_list		argptr;
133 	char		text[4096];
134 	MSG        msg;
135 
136 	va_start( argptr, error );
137 	vsprintf( text, error, argptr );
138 	va_end( argptr);
139 
140 	printf("%s", text);
141 
142 	Conbuf_AppendText( text );
143 	Conbuf_AppendText( "\n" );
144 
145 	Win_SetErrorText( text );
146 	Sys_ShowConsole( 1, true );
147 
148 	timeEndPeriod( 1 );
149 
150 	Sys_ShutdownInput();
151 
152 	GLimp_Shutdown();
153 
154 	// wait for the user to quit
155 	while ( 1 ) {
156 		if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
157 			common->Quit();
158 		}
159 		TranslateMessage( &msg );
160 		DispatchMessage( &msg );
161 	}
162 
163 	Sys_DestroyConsole();
164 
165 	exit (1);
166 }
167 
168 /*
169 ==============
170 Sys_Quit
171 ==============
172 */
Sys_Quit(void)173 void Sys_Quit( void ) {
174 #ifdef ID_ALLOW_TOOLS
175 	// Free OpenGL DLL.
176 	if (hOpenGL_DLL)
177 	{
178 		FreeLibrary(hOpenGL_DLL);
179 	}
180 #endif
181 
182 	timeEndPeriod( 1 );
183 	Sys_ShutdownInput();
184 	Sys_DestroyConsole();
185 	ExitProcess( 0 );
186 }
187 
188 
189 /*
190 ==============
191 Sys_Printf
192 ==============
193 */
194 #define MAXPRINTMSG 4096
Sys_Printf(const char * fmt,...)195 void Sys_Printf( const char *fmt, ... ) {
196 	char		msg[MAXPRINTMSG];
197 
198 	va_list argptr;
199 	va_start(argptr, fmt);
200 	idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
201 	va_end(argptr);
202 	msg[sizeof(msg)-1] = '\0';
203 
204 	printf("%s", msg);
205 
206 	if ( win32.win_outputDebugString.GetBool() ) {
207 		OutputDebugString( msg );
208 	}
209 	if ( win32.win_outputEditString.GetBool() ) {
210 		Conbuf_AppendText( msg );
211 	}
212 }
213 
214 /*
215 ==============
216 Sys_DebugPrintf
217 ==============
218 */
219 #define MAXPRINTMSG 4096
Sys_DebugPrintf(const char * fmt,...)220 void Sys_DebugPrintf( const char *fmt, ... ) {
221 	char msg[MAXPRINTMSG];
222 
223 	va_list argptr;
224 	va_start( argptr, fmt );
225 	idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
226 	msg[ sizeof(msg)-1 ] = '\0';
227 	va_end( argptr );
228 
229 	printf("%s", msg);
230 
231 	OutputDebugString( msg );
232 }
233 
234 /*
235 ==============
236 Sys_DebugVPrintf
237 ==============
238 */
Sys_DebugVPrintf(const char * fmt,va_list arg)239 void Sys_DebugVPrintf( const char *fmt, va_list arg ) {
240 	char msg[MAXPRINTMSG];
241 
242 	idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, arg );
243 	msg[ sizeof(msg)-1 ] = '\0';
244 
245 	printf("%s", msg);
246 
247 	OutputDebugString( msg );
248 }
249 
250 /*
251 ==============
252 Sys_ShowWindow
253 ==============
254 */
Sys_ShowWindow(bool show)255 void Sys_ShowWindow( bool show ) {
256 	::ShowWindow( win32.hWnd, show ? SW_SHOW : SW_HIDE );
257 }
258 
259 /*
260 ==============
261 Sys_IsWindowVisible
262 ==============
263 */
Sys_IsWindowVisible(void)264 bool Sys_IsWindowVisible( void ) {
265 	return ( ::IsWindowVisible( win32.hWnd ) != 0 );
266 }
267 
268 /*
269 ==============
270 Sys_Mkdir
271 ==============
272 */
Sys_Mkdir(const char * path)273 void Sys_Mkdir( const char *path ) {
274 	_mkdir (path);
275 }
276 
277 /*
278 =================
279 Sys_FileTimeStamp
280 =================
281 */
Sys_FileTimeStamp(FILE * fp)282 ID_TIME_T Sys_FileTimeStamp( FILE *fp ) {
283 	struct _stat st;
284 	_fstat( _fileno( fp ), &st );
285 	return (long) st.st_mtime;
286 }
287 
288 /*
289 ==============
290 Sys_Cwd
291 ==============
292 */
Sys_Cwd(void)293 const char *Sys_Cwd( void ) {
294 	static char cwd[MAX_OSPATH];
295 
296 	_getcwd( cwd, sizeof( cwd ) - 1 );
297 	cwd[MAX_OSPATH-1] = 0;
298 
299 	return cwd;
300 }
301 
WPath2A(char * dst,size_t size,const WCHAR * src)302 static int WPath2A(char *dst, size_t size, const WCHAR *src) {
303 	int len;
304 	BOOL default_char = FALSE;
305 
306 	// test if we can convert lossless
307 	len = WideCharToMultiByte(CP_ACP, 0, src, -1, dst, size, NULL, &default_char);
308 
309 	if (default_char) {
310 		/* The following lines implement a horrible
311 		   hack to connect the UTF-16 WinAPI to the
312 		   ASCII doom3 strings. While this should work in
313 		   most cases, it'll fail if the "Windows to
314 		   DOS filename translation" is switched off.
315 		   In that case the function will return NULL
316 		   and no homedir is used. */
317 		WCHAR w[MAX_OSPATH];
318 		len = GetShortPathNameW(src, w, sizeof(w));
319 
320 		if (len == 0)
321 			return 0;
322 
323 		/* Since the DOS path contains no UTF-16 characters, convert it to the system's default code page */
324 		len = WideCharToMultiByte(CP_ACP, 0, w, len, dst, size - 1, NULL, NULL);
325 	}
326 
327 	if (len == 0)
328 		return 0;
329 
330 	dst[len] = 0;
331 	/* Replace backslashes by slashes */
332 	for (int i = 0; i < len; ++i)
333 		if (dst[i] == '\\')
334 			dst[i] = '/';
335 
336 	// cut trailing slash
337 	if (dst[len - 1] == '/') {
338 		dst[len - 1] = 0;
339 		len--;
340 	}
341 
342 	return len;
343 }
344 
345 /*
346 ==============
347 Returns "My Documents"/My Games/dhewm3 directory (or equivalent - "CSIDL_PERSONAL").
348 To be used with Sys_DefaultSavePath(), so savegames, screenshots etc will be
349 saved to the users files instead of systemwide.
350 
351 Based on (with kind permission) Yamagi Quake II's Sys_GetHomeDir()
352 
353 Returns the number of characters written to dst
354 ==============
355  */
356 extern "C" { // DG: I need this in SDL_win32_main.c
Sys_GetHomeDir(char * dst,size_t size)357 	int Sys_GetHomeDir(char *dst, size_t size)
358 	{
359 		int len;
360 		WCHAR profile[MAX_OSPATH];
361 
362 		/* Get the path to "My Documents" directory */
363 		SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, profile);
364 
365 		len = WPath2A(dst, size, profile);
366 		if (len == 0)
367 			return 0;
368 
369 		idStr::Append(dst, size, "/My Games/dhewm3");
370 
371 		return len;
372 	}
373 }
374 
GetRegistryPath(char * dst,size_t size,const WCHAR * subkey,const WCHAR * name)375 static int GetRegistryPath(char *dst, size_t size, const WCHAR *subkey, const WCHAR *name) {
376 	WCHAR w[MAX_OSPATH];
377 	DWORD len = sizeof(w);
378 	HKEY res;
379 	DWORD sam = KEY_QUERY_VALUE
380 #ifdef _WIN64
381 		| KEY_WOW64_32KEY
382 #endif
383 		;
384 	DWORD type;
385 
386 	if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, subkey, 0, sam, &res) != ERROR_SUCCESS)
387 		return 0;
388 
389 	if (RegQueryValueExW(res, name, NULL, &type, (LPBYTE)w, &len) != ERROR_SUCCESS) {
390 		RegCloseKey(res);
391 		return 0;
392 	}
393 
394 	RegCloseKey(res);
395 
396 	if (type != REG_SZ)
397 		return 0;
398 
399 	return WPath2A(dst, size, w);
400 }
401 
Sys_GetPath(sysPath_t type,idStr & path)402 bool Sys_GetPath(sysPath_t type, idStr &path) {
403 	char buf[MAX_OSPATH];
404 	struct _stat st;
405 	idStr s;
406 
407 	switch(type) {
408 	case PATH_BASE:
409 		// try <path to exe>/base first
410 		if (Sys_GetPath(PATH_EXE, path)) {
411 			path.StripFilename();
412 
413 			s = path;
414 			s.AppendPath(BASE_GAMEDIR);
415 			if (_stat(s.c_str(), &st) != -1 && (st.st_mode & _S_IFDIR)) {
416 				common->Warning("using path of executable: %s", path.c_str());
417 				return true;
418 			} else {
419 				s = path + "/demo/demo00.pk4";
420 				if (_stat(s.c_str(), &st) != -1 && (st.st_mode & _S_IFREG)) {
421 					common->Warning("using path of executable (seems to contain demo game data): %s ", path.c_str());
422 					return true;
423 				}
424 			}
425 
426 			common->Warning("base path '%s' does not exist", s.c_str());
427 		}
428 
429 		// Note: apparently there is no registry entry for the Doom 3 Demo
430 
431 		// fallback to vanilla doom3 cd install
432 		if (GetRegistryPath(buf, sizeof(buf), L"SOFTWARE\\id\\Doom 3", L"InstallPath") > 0) {
433 			path = buf;
434 			return true;
435 		}
436 
437 		// fallback to steam doom3 install
438 		if (GetRegistryPath(buf, sizeof(buf), L"SOFTWARE\\Valve\\Steam", L"InstallPath") > 0) {
439 			path = buf;
440 			path.AppendPath("steamapps\\common\\doom 3");
441 
442 			if (_stat(path.c_str(), &st) != -1 && st.st_mode & _S_IFDIR)
443 				return true;
444 		}
445 
446 		common->Warning("vanilla doom3 path not found either");
447 
448 		return false;
449 
450 	case PATH_CONFIG:
451 	case PATH_SAVE:
452 		if (Sys_GetHomeDir(buf, sizeof(buf)) < 1) {
453 			Sys_Error("ERROR: Couldn't get dir to home path");
454 			return false;
455 		}
456 
457 		path = buf;
458 		return true;
459 
460 	case PATH_EXE:
461 		GetModuleFileName(NULL, buf, sizeof(buf) - 1);
462 		path = buf;
463 		path.BackSlashesToSlashes();
464 		return true;
465 	}
466 
467 	return false;
468 }
469 
470 /*
471 ==============
472 Sys_ListFiles
473 ==============
474 */
Sys_ListFiles(const char * directory,const char * extension,idStrList & list)475 int Sys_ListFiles( const char *directory, const char *extension, idStrList &list ) {
476 	idStr		search;
477 	struct _finddata_t findinfo;
478 	intptr_t	findhandle;
479 	int			flag;
480 
481 	if ( !extension) {
482 		extension = "";
483 	}
484 
485 	// passing a slash as extension will find directories
486 	if ( extension[0] == '/' && extension[1] == 0 ) {
487 		extension = "";
488 		flag = 0;
489 	} else {
490 		flag = _A_SUBDIR;
491 	}
492 
493 	sprintf( search, "%s\\*%s", directory, extension );
494 
495 	// search
496 	list.Clear();
497 
498 	findhandle = _findfirst( search, &findinfo );
499 	if ( findhandle == -1 ) {
500 		return -1;
501 	}
502 
503 	do {
504 		if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) {
505 			list.Append( findinfo.name );
506 		}
507 	} while ( _findnext( findhandle, &findinfo ) != -1 );
508 
509 	_findclose( findhandle );
510 
511 	return list.Num();
512 }
513 
514 
515 /*
516 ================
517 Sys_GetClipboardData
518 ================
519 */
Sys_GetClipboardData(void)520 char *Sys_GetClipboardData( void ) {
521 	char *data = NULL;
522 	char *cliptext;
523 
524 	if ( OpenClipboard( NULL ) != 0 ) {
525 		HANDLE hClipboardData;
526 
527 		if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) {
528 			if ( ( cliptext = (char *)GlobalLock( hClipboardData ) ) != 0 ) {
529 				data = (char *)Mem_Alloc( GlobalSize( hClipboardData ) + 1 );
530 				strcpy( data, cliptext );
531 				GlobalUnlock( hClipboardData );
532 
533 				strtok( data, "\n\r\b" );
534 			}
535 		}
536 		CloseClipboard();
537 	}
538 	return data;
539 }
540 
541 /*
542 ================
543 Sys_SetClipboardData
544 ================
545 */
Sys_SetClipboardData(const char * string)546 void Sys_SetClipboardData( const char *string ) {
547 	HGLOBAL HMem;
548 	char *PMem;
549 
550 	// allocate memory block
551 	HMem = (char *)::GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, strlen( string ) + 1 );
552 	if ( HMem == NULL ) {
553 		return;
554 	}
555 	// lock allocated memory and obtain a pointer
556 	PMem = (char *)::GlobalLock( HMem );
557 	if ( PMem == NULL ) {
558 		return;
559 	}
560 	// copy text into allocated memory block
561 	lstrcpy( PMem, string );
562 	// unlock allocated memory
563 	::GlobalUnlock( HMem );
564 	// open Clipboard
565 	if ( !OpenClipboard( 0 ) ) {
566 		::GlobalFree( HMem );
567 		return;
568 	}
569 	// remove current Clipboard contents
570 	EmptyClipboard();
571 	// supply the memory handle to the Clipboard
572 	SetClipboardData( CF_TEXT, HMem );
573 	HMem = 0;
574 	// close Clipboard
575 	CloseClipboard();
576 }
577 
578 /*
579 ========================================================================
580 
581 DLL Loading
582 
583 ========================================================================
584 */
585 
586 /*
587 =====================
588 Sys_DLL_Load
589 =====================
590 */
Sys_DLL_Load(const char * dllName)591 uintptr_t Sys_DLL_Load( const char *dllName ) {
592 	HINSTANCE	libHandle;
593 	libHandle = LoadLibrary( dllName );
594 	if ( libHandle ) {
595 		// since we can't have LoadLibrary load only from the specified path, check it did the right thing
596 		char loadedPath[ MAX_OSPATH ];
597 		GetModuleFileName( libHandle, loadedPath, sizeof( loadedPath ) - 1 );
598 		if ( idStr::IcmpPath( dllName, loadedPath ) ) {
599 			Sys_Printf( "ERROR: LoadLibrary '%s' wants to load '%s'\n", dllName, loadedPath );
600 			Sys_DLL_Unload( (uintptr_t)libHandle );
601 			return 0;
602 		}
603 	} else {
604 		DWORD e = GetLastError();
605 		LPVOID msgBuf = nullptr;
606 
607 		FormatMessage(
608 			FORMAT_MESSAGE_ALLOCATE_BUFFER |
609 			FORMAT_MESSAGE_FROM_SYSTEM |
610 			FORMAT_MESSAGE_IGNORE_INSERTS,
611 			NULL,
612 			e,
613 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
614 			(LPTSTR)&msgBuf,
615 			0, NULL);
616 
617 		idStr errorStr = va( "[%i (0x%X)]\t%s", e, e, msgBuf );
618 
619 		// common, skipped.
620 		if ( e == 0x7E ) // [126 (0x7E)] The specified module could not be found.
621 			errorStr = "";
622 		// probably going to be common. Lets try to be less cryptic.
623 		else if ( e == 0xC1 ) // [193 (0xC1)] is not a valid Win32 application.
624 			errorStr = va( "[%i (0x%X)]\t%s", e, e, "probably the DLL is of the wrong architecture, like x64 instead of x86" );
625 
626 		if ( errorStr.Length() )
627 			common->Warning( "LoadLibrary(%s) Failed ! %s", dllName, errorStr.c_str() );
628 
629 		::LocalFree( msgBuf );
630 	}
631 	return (uintptr_t)libHandle;
632 }
633 
634 /*
635 =====================
636 Sys_DLL_GetProcAddress
637 =====================
638 */
Sys_DLL_GetProcAddress(uintptr_t dllHandle,const char * procName)639 void *Sys_DLL_GetProcAddress( uintptr_t dllHandle, const char *procName ) {
640 	return (void *)GetProcAddress( (HINSTANCE)dllHandle, procName );
641 }
642 
643 /*
644 =====================
645 Sys_DLL_Unload
646 =====================
647 */
Sys_DLL_Unload(uintptr_t dllHandle)648 void Sys_DLL_Unload( uintptr_t dllHandle ) {
649 	if ( !dllHandle ) {
650 		return;
651 	}
652 	if ( FreeLibrary( (HINSTANCE)dllHandle ) == 0 ) {
653 		int lastError = GetLastError();
654 		LPVOID lpMsgBuf;
655 		FormatMessage(
656 			FORMAT_MESSAGE_ALLOCATE_BUFFER,
657 		    NULL,
658 			lastError,
659 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
660 			(LPTSTR) &lpMsgBuf,
661 			0,
662 			NULL
663 		);
664 		Sys_Error( "Sys_DLL_Unload: FreeLibrary failed - %s (%d)", lpMsgBuf, lastError );
665 	}
666 }
667 
668 /*
669 ================
670 Sys_Init
671 
672 The cvar system must already be setup
673 ================
674 */
Sys_Init(void)675 void Sys_Init( void ) {
676 
677 	CoInitialize( NULL );
678 
679 	// make sure the timer is high precision, otherwise
680 	// NT gets 18ms resolution
681 	timeBeginPeriod( 1 );
682 
683 	// get WM_TIMER messages pumped every millisecond
684 //	SetTimer( NULL, 0, 100, NULL );
685 
686 #ifdef DEBUG
687 	cmdSystem->AddCommand( "createResourceIDs", CreateResourceIDs_f, CMD_FL_TOOL, "assigns resource IDs in _resouce.h files" );
688 #endif
689 #if 0
690 	cmdSystem->AddCommand( "setAsyncSound", Sys_SetAsyncSound_f, CMD_FL_SYSTEM, "set the async sound option" );
691 #endif
692 
693 	//
694 	// Windows version
695 	//
696 	win32.osversion.dwOSVersionInfoSize = sizeof( win32.osversion );
697 
698 	if ( !GetVersionEx( (LPOSVERSIONINFO)&win32.osversion ) )
699 		Sys_Error( "Couldn't get OS info" );
700 
701 	if ( win32.osversion.dwMajorVersion < 4 ) {
702 		Sys_Error( GAME_NAME " requires Windows version 4 (NT) or greater" );
703 	}
704 	if ( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32s ) {
705 		Sys_Error( GAME_NAME " doesn't run on Win32s" );
706 	}
707 
708 	common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() );
709 }
710 
711 /*
712 ================
713 Sys_Shutdown
714 ================
715 */
Sys_Shutdown(void)716 void Sys_Shutdown( void ) {
717 #ifdef ID_ALLOW_TOOLS
718 	qwglCopyContext = NULL;
719 	qwglCreateContext = NULL;
720 	qwglCreateLayerContext = NULL;
721 	qwglDeleteContext = NULL;
722 	qwglDescribeLayerPlane = NULL;
723 	qwglGetCurrentContext = NULL;
724 	qwglGetCurrentDC = NULL;
725 	qwglGetLayerPaletteEntries = NULL;
726 	qwglGetProcAddress = NULL;
727 	qwglMakeCurrent = NULL;
728 	qwglRealizeLayerPalette = NULL;
729 	qwglSetLayerPaletteEntries = NULL;
730 	qwglShareLists = NULL;
731 	qwglSwapLayerBuffers = NULL;
732 	qwglUseFontBitmaps = NULL;
733 	qwglUseFontOutlines = NULL;
734 	qwglChoosePixelFormat = NULL;
735 	qwglDescribePixelFormat = NULL;
736 	qwglGetPixelFormat = NULL;
737 	qwglSetPixelFormat = NULL;
738 	qwglSwapBuffers = NULL;
739 #endif // ID_ALLOW_TOOLS
740 
741 	CoUninitialize();
742 }
743 
744 //=======================================================================
745 
746 //#define SET_THREAD_AFFINITY
747 
748 
749 /*
750 ====================
751 Win_Frame
752 ====================
753 */
Win_Frame(void)754 void Win_Frame( void ) {
755 	// if "viewlog" has been modified, show or hide the log console
756 	if ( win32.win_viewlog.IsModified() ) {
757 		if ( !com_skipRenderer.GetBool() && idAsyncNetwork::serverDedicated.GetInteger() != 1 ) {
758 			Sys_ShowConsole( win32.win_viewlog.GetInteger(), false );
759 		}
760 		win32.win_viewlog.ClearModified();
761 	}
762 }
763 
764 // code that tells windows we're High DPI aware so it doesn't scale our windows
765 // taken from Yamagi Quake II
766 
767 typedef enum D3_PROCESS_DPI_AWARENESS {
768 	D3_PROCESS_DPI_UNAWARE = 0,
769 	D3_PROCESS_SYSTEM_DPI_AWARE = 1,
770 	D3_PROCESS_PER_MONITOR_DPI_AWARE = 2
771 } YQ2_PROCESS_DPI_AWARENESS;
772 
setHighDPIMode(void)773 static void setHighDPIMode(void)
774 {
775 	/* For Vista, Win7 and Win8 */
776 	BOOL(WINAPI *SetProcessDPIAware)(void) = NULL;
777 
778 	/* Win8.1 and later */
779 	HRESULT(WINAPI *SetProcessDpiAwareness)(D3_PROCESS_DPI_AWARENESS dpiAwareness) = NULL;
780 
781 
782 	HINSTANCE userDLL = LoadLibrary("USER32.DLL");
783 
784 	if (userDLL)
785 	{
786 		SetProcessDPIAware = (BOOL(WINAPI *)(void)) GetProcAddress(userDLL, "SetProcessDPIAware");
787 	}
788 
789 
790 	HINSTANCE shcoreDLL = LoadLibrary("SHCORE.DLL");
791 
792 	if (shcoreDLL)
793 	{
794 		SetProcessDpiAwareness = (HRESULT(WINAPI *)(YQ2_PROCESS_DPI_AWARENESS))
795 									GetProcAddress(shcoreDLL, "SetProcessDpiAwareness");
796 	}
797 
798 
799 	if (SetProcessDpiAwareness) {
800 		SetProcessDpiAwareness(D3_PROCESS_PER_MONITOR_DPI_AWARE);
801 	}
802 	else if (SetProcessDPIAware) {
803 		SetProcessDPIAware();
804 	}
805 }
806 
807 #ifdef ID_ALLOW_TOOLS
loadWGLpointers()808 static void loadWGLpointers() {
809 	if (hOpenGL_DLL == NULL)
810 	{
811 		// Load OpenGL DLL.
812 		hOpenGL_DLL = LoadLibrary("opengl32.dll");
813 		if (hOpenGL_DLL == NULL) {
814 			Sys_Error(GAME_NAME " Cannot Load opengl32.dll - Disabling TOOLS");
815 			return;
816 		}
817 	}
818 	// opengl32.dll found... grab the addresses.
819 
820 	qwglGetProcAddress = (PWGLGETPROCADDRESS)GetProcAddress(hOpenGL_DLL, "wglGetProcAddress");
821 
822 	// Context controls
823 	qwglCopyContext = (PWGLCOPYCONTEXT)GetProcAddress(hOpenGL_DLL, "wglCopyContext");
824 	qwglCreateContext = (PWGLCREATECONTEXT)GetProcAddress(hOpenGL_DLL, "wglCreateContext");
825 	qwglCreateLayerContext = (PWGLCREATELAYERCONTEXT)GetProcAddress(hOpenGL_DLL, "wglCreateLayerContext");
826 	qwglDeleteContext = (PWGLDELETECONTEXT)GetProcAddress(hOpenGL_DLL, "wglDeleteContext");
827 	qwglGetCurrentContext = (PWGLGETCURRENTCONTEXT)GetProcAddress(hOpenGL_DLL, "wglGetCurrentContext");
828 	qwglGetCurrentDC = (PWGLGETCURRENTDC)GetProcAddress(hOpenGL_DLL, "wglGetCurrentDC");
829 	qwglMakeCurrent = (PWGLMAKECURRENT)GetProcAddress(hOpenGL_DLL, "wglMakeCurrent");
830 	qwglShareLists = (PWGLSHARELISTS)GetProcAddress(hOpenGL_DLL, "wglShareLists");
831 
832 	// Fonts
833 	qwglUseFontBitmaps = (PWGLUSEFONTBITMAPS)GetProcAddress(hOpenGL_DLL, "wglUseFontBitmapsA");
834 	qwglUseFontOutlines = (PWGLUSEFONTOUTLINES)GetProcAddress(hOpenGL_DLL, "wglUseFontOutlinesA");
835 
836 	// Layers.
837 	qwglDescribeLayerPlane = (PWGLDESCRIBELAYERPLANE)GetProcAddress(hOpenGL_DLL, "wglDescribeLayerPlane");
838 	qwglSwapLayerBuffers = (PWGLSWAPLAYERBUFFERS)GetProcAddress(hOpenGL_DLL, "wglSwapLayerBuffers");
839 
840 	// Palette controls
841 	qwglGetLayerPaletteEntries = (PWGLGETLAYERPALETTEENTRIES)GetProcAddress(hOpenGL_DLL, "wglGetLayerPaletteEntries");
842 	qwglRealizeLayerPalette = (PWGLREALIZELAYERPALETTE)GetProcAddress(hOpenGL_DLL, "wglRealizeLayerPalette");
843 	qwglSetLayerPaletteEntries = (PWGLSETLAYERPALETTEENTRIES)GetProcAddress(hOpenGL_DLL, "wglSetLayerPaletteEntries");
844 
845 
846 	// These by default exist in windows
847 	qwglChoosePixelFormat = ChoosePixelFormat;
848 	qwglDescribePixelFormat = DescribePixelFormat;
849 	qwglGetPixelFormat = GetPixelFormat;
850 	qwglSetPixelFormat = SetPixelFormat;
851 	qwglSwapBuffers = SwapBuffers;
852 }
853 #endif
854 
855 /*
856 ==================
857 WinMain
858 ==================
859 */
main(int argc,char * argv[])860 int main(int argc, char *argv[]) {
861 	const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) );
862 
863 #ifdef ID_DEDICATED
864 	MSG msg;
865 #else
866 	// tell windows we're high dpi aware, otherwise display scaling screws up the game
867 	setHighDPIMode();
868 #endif
869 
870 	Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 );
871 
872 	win32.hInstance = GetModuleHandle(NULL);
873 
874 	// done before Com/Sys_Init since we need this for error output
875 	Sys_CreateConsole();
876 
877 	// no abort/retry/fail errors
878 	SetErrorMode( SEM_FAILCRITICALERRORS );
879 
880 #ifdef DEBUG
881 	// disable the painfully slow MS heap check every 1024 allocs
882 	_CrtSetDbgFlag( 0 );
883 #endif
884 
885 #ifdef ID_ALLOW_TOOLS
886 	loadWGLpointers();
887 #endif
888 
889 	if ( argc > 1 ) {
890 		common->Init( argc-1, &argv[1] );
891 	} else {
892 		common->Init( 0, NULL );
893 	}
894 
895 	// hide or show the early console as necessary
896 	if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) {
897 		Sys_ShowConsole( 1, true );
898 	} else {
899 		Sys_ShowConsole( 0, false );
900 	}
901 
902 #ifdef SET_THREAD_AFFINITY
903 	// give the main thread an affinity for the first cpu
904 	SetThreadAffinityMask( GetCurrentThread(), 1 );
905 #endif
906 
907 	// ::SetCursor( hcurSave ); // DG: I think SDL handles the cursor fine..
908 
909 	// Launch the script debugger
910 	if ( strstr( GetCommandLine(), "+debugger" ) ) {
911 		// DebuggerClientInit( lpCmdLine );
912 		return 0;
913 	}
914 
915 	// ::SetFocus( win32.hWnd ); // DG: let SDL handle focus, otherwise input is fucked up! (#100)
916 
917 	// main game loop
918 	while( 1 ) {
919 #if ID_DEDICATED
920 		// Since this is a Dedicated Server, process all Windowing Messages
921 		// Now.
922 		while(PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)){
923 			TranslateMessage(&msg);
924 			DispatchMessage(&msg);
925 		}
926 
927 		// Give the OS a little time to recuperate.
928 		Sleep(10);
929 #endif
930 
931 		Win_Frame();
932 
933 #ifdef ID_ALLOW_TOOLS
934 		if ( com_editors ) {
935 			if ( com_editors & EDITOR_GUI ) {
936 				// GUI editor
937 				GUIEditorRun();
938 			} else if ( com_editors & EDITOR_RADIANT ) {
939 				// Level Editor
940 				RadiantRun();
941 			}
942 			else if (com_editors & EDITOR_MATERIAL ) {
943 				//BSM Nerve: Add support for the material editor
944 				MaterialEditorRun();
945 			}
946 			else {
947 				if ( com_editors & EDITOR_LIGHT ) {
948 					// in-game Light Editor
949 					LightEditorRun();
950 				}
951 				if ( com_editors & EDITOR_SOUND ) {
952 					// in-game Sound Editor
953 					SoundEditorRun();
954 				}
955 				if ( com_editors & EDITOR_DECL ) {
956 					// in-game Declaration Browser
957 					DeclBrowserRun();
958 				}
959 				if ( com_editors & EDITOR_AF ) {
960 					// in-game Articulated Figure Editor
961 					AFEditorRun();
962 				}
963 				if ( com_editors & EDITOR_PARTICLE ) {
964 					// in-game Particle Editor
965 					ParticleEditorRun();
966 				}
967 				if ( com_editors & EDITOR_SCRIPT ) {
968 					// in-game Script Editor
969 					ScriptEditorRun();
970 				}
971 				if ( com_editors & EDITOR_PDA ) {
972 					// in-game PDA Editor
973 					PDAEditorRun();
974 				}
975 			}
976 		}
977 #endif
978 		// run the game
979 		common->Frame();
980 	}
981 
982 	// never gets here
983 	return 0;
984 }
985 
986 /*
987 ==================
988 idSysLocal::OpenURL
989 ==================
990 */
OpenURL(const char * url,bool doexit)991 void idSysLocal::OpenURL( const char *url, bool doexit ) {
992 	static bool doexit_spamguard = false;
993 	HWND wnd;
994 
995 	if (doexit_spamguard) {
996 		common->DPrintf( "OpenURL: already in an exit sequence, ignoring %s\n", url );
997 		return;
998 	}
999 
1000 	common->Printf("Open URL: %s\n", url);
1001 
1002 	if ( !ShellExecute( NULL, "open", url, NULL, NULL, SW_RESTORE ) ) {
1003 		common->Error( "Could not open url: '%s' ", url );
1004 		return;
1005 	}
1006 
1007 	wnd = GetForegroundWindow();
1008 	if ( wnd ) {
1009 		ShowWindow( wnd, SW_MAXIMIZE );
1010 	}
1011 
1012 	if ( doexit ) {
1013 		doexit_spamguard = true;
1014 		cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
1015 	}
1016 }
1017 
1018 /*
1019 ==================
1020 idSysLocal::StartProcess
1021 ==================
1022 */
StartProcess(const char * exePath,bool doexit)1023 void idSysLocal::StartProcess( const char *exePath, bool doexit ) {
1024 	TCHAR				szPathOrig[_MAX_PATH];
1025 	STARTUPINFO			si;
1026 	PROCESS_INFORMATION	pi;
1027 
1028 	ZeroMemory( &si, sizeof(si) );
1029 	si.cb = sizeof(si);
1030 
1031 	strncpy( szPathOrig, exePath, _MAX_PATH );
1032 
1033 	if( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
1034 		common->Error( "Could not start process: '%s' ", szPathOrig );
1035 	    return;
1036 	}
1037 
1038 	if ( doexit ) {
1039 		cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
1040 	}
1041 }
1042