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