/* =========================================================================== Copyright (C) 1997-2001 Id Software, Inc. This file is part of Quake 2 source code. Quake 2 source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake 2 source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake 2 source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // sys_win.h #include "../qcommon/qcommon.h" #include "winquake.h" #include "resource.h" #include #include #include #include #include #include #include #include #include "../win32/conproc.h" // [Slipyx] mingw support for _controlfp. from float.h. these would be defined // if __STRICT_ANSI__ was undefined before including float.h, but i just copied // out these specific definitions instead since they're the only ones used. #if defined(__MINGW32__) #define _MCW_PC 0x00030000 #define _PC_24 0x00020000 _CRTIMP unsigned int __cdecl __MINGW_NOTHROW _controlfp (unsigned int unNew, unsigned int unMask); #endif // __MINGW32__ #define MINIMUM_WIN_MEMORY 0x0a00000 #define MAXIMUM_WIN_MEMORY 0x1000000 qboolean s_win95; qboolean s_win9X; qboolean s_winNT; qboolean s_winNT6; int starttime; int ActiveApp; qboolean Minimized; static HANDLE hinput, houtput; unsigned sys_msg_time; unsigned sys_frame_time; static HANDLE qwclsemaphore; #define MAX_NUM_ARGVS 128 int argc; char *argv[MAX_NUM_ARGVS]; #define NT5_SAVEDIR "My Games/KMQuake2" #define NT6_SAVEDIR "KMQuake2" #define NT5_DLDIR "My Downloads/KMQuake2" #define NT6_DLDIR "KMQuake2" static char exe_dir[MAX_OSPATH]; static char pref_dir[MAX_OSPATH]; static char download_dir[MAX_OSPATH]; qboolean Detect_WinNT5orLater (void); qboolean Detect_WinNT6orLater (void); typedef HRESULT (WINAPI *SHGETFOLDERPATHW) (HWND hwnd, int CSIDL, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath); SHGETFOLDERPATHW fnSHGetFolderPathW = NULL; // from DarkPlaces const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}}; const GUID qFOLDERID_Downloads = {0x374DE290, 0x123F, 0x4565, {0x91, 0x64, 0x39, 0xC4, 0x92, 0x5E, 0x46, 0x7B}}; #define qREFKNOWNFOLDERID const GUID * #define qKF_FLAG_CREATE 0x8000 #define qKF_FLAG_NO_ALIAS 0x1000 static HRESULT (WINAPI *fnSHGetKnownFolderPath) (qREFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath); static HRESULT (WINAPI *fnCoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); static void (WINAPI *fnCoUninitialize)(void); static void (WINAPI *fnCoTaskMemFree)(LPVOID pv); // end DarkPlaces code #ifndef NEW_DED_CONSOLE /* =============================================================================== DEDICATED CONSOLE =============================================================================== */ static char console_text[256]; static int console_textlen; /* ================ Sys_InitConsole ================ */ void Sys_InitConsole (void) { // if (!dedicated->value) if (!dedicated->integer) return; if (!AllocConsole ()) Sys_Error ("Couldn't create dedicated server console"); hinput = GetStdHandle (STD_INPUT_HANDLE); houtput = GetStdHandle (STD_OUTPUT_HANDLE); // let QHOST hook in InitConProc (argc, argv); } /* ================ Sys_ConsoleInput ================ */ char *Sys_ConsoleInput (void) { INPUT_RECORD recs[1024]; int dummy; int ch, numread, numevents; // if (!dedicated || !dedicated->value) if (!dedicated || !dedicated->integer) return NULL; for ( ;; ) { if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) Sys_Error ("Error getting # of console events"); if (numevents <= 0) break; if (!ReadConsoleInput(hinput, recs, 1, &numread)) Sys_Error ("Error reading console input"); if (numread != 1) Sys_Error ("Couldn't read console input"); if (recs[0].EventType == KEY_EVENT) { if (!recs[0].Event.KeyEvent.bKeyDown) { ch = recs[0].Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (console_textlen) { console_text[console_textlen] = 0; console_textlen = 0; return console_text; } break; case '\b': if (console_textlen) { console_textlen--; WriteFile(houtput, "\b \b", 3, &dummy, NULL); } break; default: if (ch >= ' ') { if (console_textlen < sizeof(console_text)-2) { WriteFile(houtput, &ch, 1, &dummy, NULL); console_text[console_textlen] = ch; console_textlen++; } } break; } } } } return NULL; } /* ================ Sys_ConsoleOutput Print text to the dedicated console ================ */ void Sys_ConsoleOutput (char *string) { int dummy; char text[256]; // if (!dedicated || !dedicated->value) if (!dedicated || !dedicated->integer) return; if (console_textlen) { text[0] = '\r'; memset(&text[1], ' ', console_textlen); text[console_textlen+1] = '\r'; text[console_textlen+2] = 0; WriteFile(houtput, text, console_textlen+2, &dummy, NULL); } WriteFile(houtput, string, strlen(string), &dummy, NULL); if (console_textlen) WriteFile(houtput, console_text, console_textlen, &dummy, NULL); } void Sys_ShowConsole (qboolean show) { if (!show) Sys_RemoveStartupLogo (); } //================================================================ #endif // NEW_DED_CONSOLE /* ================ Sys_Sleep ================ */ void Sys_Sleep (int msec) { Sleep (msec); } /* ================ Sys_TickCount ================ */ unsigned Sys_TickCount (void) { return GetTickCount(); } /* ================ Sys_SendKeyEvents Send Key_Event calls ================ */ void Sys_SendKeyEvents (void) { MSG msg; while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessage (&msg, NULL, 0, 0)) Sys_Quit (); sys_msg_time = msg.time; TranslateMessage (&msg); DispatchMessage (&msg); } // grab frame time sys_frame_time = timeGetTime(); // FIXME: should this be at start? } /* ================ Sys_GetClipboardData ================ */ char *Sys_GetClipboardData( void ) { char *data = NULL; char *cliptext; if ( OpenClipboard( NULL ) != 0 ) { HANDLE hClipboardData; if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) { if ( ( cliptext = GlobalLock( hClipboardData ) ) != 0 ) { data = malloc( GlobalSize( hClipboardData ) + 1 ); // strncpy( data, cliptext ); Q_strncpyz (data, GlobalSize( hClipboardData ) + 1, cliptext); GlobalUnlock( hClipboardData ); } } CloseClipboard(); } return data; } /* =============================================================================== SYSTEM IO =============================================================================== */ #ifndef NEW_DED_CONSOLE void Sys_Error (char *error, ...) { va_list argptr; char text[1024]; CL_Shutdown (); Qcommon_Shutdown (); va_start (argptr, error); // vsprintf (text, error, argptr); Q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); if (qwclsemaphore) CloseHandle (qwclsemaphore); // shut down QHOST hooks if necessary DeinitConProc (); exit (1); } #endif // NEW_DED_CONSOLE void Sys_Quit (void) { timeEndPeriod( 1 ); CL_Shutdown(); Qcommon_Shutdown (); CloseHandle (qwclsemaphore); // if (dedicated && dedicated->value) if (dedicated && dedicated->integer) FreeConsole (); #ifdef NEW_DED_CONSOLE Sys_ShutdownConsole(); #else // shut down QHOST hooks if necessary DeinitConProc (); #endif exit (0); } void WinError (void) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL ); // Display the string. MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION ); // Free the buffer. LocalFree( lpMsgBuf ); } //================================================================ /* ================ Sys_ScanForCD ================ */ char *Sys_ScanForCD (void) { static char cddir[MAX_OSPATH]; static qboolean done; char drive[4]; FILE *f; char test[MAX_QPATH]; qboolean missionpack = false; // Knightmare added int i; // Knightmare added if (done) // don't re-check return cddir; // no abort/retry/fail errors SetErrorMode (SEM_FAILCRITICALERRORS); drive[0] = 'c'; drive[1] = ':'; drive[2] = '\\'; drive[3] = 0; Com_Printf("\nScanning for game CD data path..."); done = true; // Knightmare- check if mission pack gamedir is set for (i=0; i> 16) & 255; // Check if extended functions are present __cpuid(registers, 0x80000000); maxExtFunc = registers[0]; // Get extended features if (maxExtFunc >= 0x80000001) { __cpuid(registers, 0x80000001); extFeatures = registers[3]; } /* // Get non-threaded core count if (!Q_stricmp(vendor, "AuthenticAMD")) { if (maxExtFunc >= 0x80000008) { __cpuid(registers, 0x80000008); numCores = 1 + (registers[2] & 255); } } else if (!Q_stricmp(vendor, "GenuineIntel")) { __cpuid(registers, 4); numCores = 1 + ( (registers[0] >> 26) & 63 ); } */ #elif defined (_M_IX86) // int tempThreads, tempCores; // Check if CPUID instruction is supported __try { __asm { mov eax, 0 cpuid } } __except (EXCEPTION_EXECUTE_HANDLER) { return false; } // Get CPU info __asm { ; // Get vendor identifier mov eax, 0 cpuid mov dword ptr[vendor+0], ebx mov dword ptr[vendor+4], edx mov dword ptr[vendor+8], ecx mov dword ptr[vendor+12], 0 ; // Get standard bits and features mov eax, 1 cpuid mov stdBits, eax mov moreFeatures, ecx ; // Knightmare added mov features, edx // mov tempThreads, ebx ; // Knightmare added ; // Check if extended functions are present mov extFeatures, 0 mov eax, 80000000h cpuid mov maxExtFunc, eax ; // Knightmare added cmp eax, 80000000h jbe NoExtFunction ; // Get extended features mov eax, 80000001h cpuid mov extFeatures, edx NoExtFunction: } /* numLogicalCores = (tempThreads >> 16) & 255; // Get non-threaded core count if (!Q_stricmp(vendor, "AuthenticAMD")) { if (maxExtFunc >= 0x80000008) { __asm { mov eax, 80000008h cpuid mov tempCores, ecx } numCores = 1 + (tempCores & 255); } } else if (!Q_stricmp(vendor, "GenuineIntel")) { __asm { mov eax, 4 cpuid mov tempCores, eax } numCores = 1 + ( (tempCores >> 26) & 63 ); } */ #endif // Get CPU name family = (stdBits >> 8) & 15; model = (stdBits >> 4) & 15; if ( (family == 15) || (family == 6) ) { // extFamily = ( ((stdBits >> 20) & 15) << 4 ) + family; extFamily = ((stdBits >> 20) & 255) + family; extModel = ( ((stdBits >> 16) & 15) << 4 ) + model; } else { extFamily = (stdBits >> 20) & 255; extModel = (stdBits >> 16) & 15; } if (!Q_stricmp(vendor, "AuthenticAMD")) { Q_strncpyz(cpuString, maxSize, "AMD"); // Com_sprintf(cpuString, maxSize, "AMD Family %i ExtFamily %i Model %i ExtModel %i", family, extFamily, model, extModel); switch (family) { case 4: Q_strncatz(cpuString, maxSize, " 5x86"); break; case 5: switch (model) { case 0: case 1: case 2: case 3: Q_strncatz(cpuString, maxSize, " K5"); break; case 6: case 7: Q_strncatz(cpuString, maxSize, " K6"); break; case 8: Q_strncatz(cpuString, maxSize, " K6-2"); break; case 9: case 10: case 11: case 12: case 13: case 14: case 15: Q_strncatz(cpuString, maxSize, " K6-III"); break; } break; case 6: // K7 family switch (model) { case 1: // 250nm core case 2: // 180nm core case 4: // Thunderbird core case 6: // Palomino core case 8: // Thoroughbred core case 10: // Barton core Q_strncatz(cpuString, maxSize, " Athlon"); break; case 3: // Spitfire core case 7: // Morgan core Q_strncatz(cpuString, maxSize, " Duron"); break; default: Q_strncatz(cpuString, maxSize, " K7"); break; } break; case 15: // refer to extended family if (extFamily == 0x0F) // K8 family { switch (model) { // case 0: // case 2: // case 6: case 4: // Clawhammer/Newark case 7: // San Diego/Newcastle case 12: // Newcastle/Albany Q_strncatz(cpuString, maxSize, " Athlon 64"); break; case 3: // Toledo case 11: // Manchester/Brisbane case 15: // Winchester/Venice Q_strncatz(cpuString, maxSize, " Athlon 64 X2"); break; case 1: case 5: Q_strncatz(cpuString, maxSize, " Athlon 64 FX / Opteron"); break; default: Q_strncatz(cpuString, maxSize, " K8"); break; } } else if (extFamily == 0x10) // K10 family { switch (model) { case 0: // Barcelona A0-A2 case 2: // Barcelona B0-B3 Q_strncatz(cpuString, maxSize, " Phenom / Opteron"); break; case 4: // Deneb / Shanghai case 10: // Thuban Q_strncatz(cpuString, maxSize, " Phenom II / Opteron"); break; case 5: // Propus case 6: // Regor Q_strncatz(cpuString, maxSize, " Athlon II"); break; default: Q_strncatz(cpuString, maxSize, " K10"); break; } } else if (extFamily == 0x12) // Stars Fusion family { switch (model) { case 1: // Llano Q_strncatz(cpuString, maxSize, " A8 APU"); break; default: Q_strncatz(cpuString, maxSize, " A series APU"); break; } } else if (extFamily == 0x14) // Bobcat family { switch (model) { case 1: // Zacate Q_strncatz(cpuString, maxSize, " E-350 APU"); break; default: Q_strncatz(cpuString, maxSize, " E series APU"); break; } } else if (extFamily == 0x15) // Bulldozer family { switch (model) { case 0: if (extModel == 0x10) // Trinity Q_strncatz(cpuString, maxSize, " A series APU"); else if (extModel == 0x30) // Kaveri Q_strncatz(cpuString, maxSize, " A series APU"); else // Zambezi Q_strncatz(cpuString, maxSize, " FX series"); break; case 1: if (extModel == 1) // Zambezi Q_strncatz(cpuString, maxSize, " FX series"); else if (extModel == 0x60) // Carrizo Q_strncatz(cpuString, maxSize, " A series APU"); break; case 2: if (extModel == 2) // Zambezi / Vishera Q_strncatz(cpuString, maxSize, " FX series"); break; case 3: if (extModel == 0x13) // Richland Q_strncatz(cpuString, maxSize, " A series APU"); break; case 5: if (extModel == 0x65) // Bristol Ridge Q_strncatz(cpuString, maxSize, " A series APU"); break; default: Q_strncatz(cpuString, maxSize, " FX series"); break; } } else if (extFamily == 0x17) // Ryzen Family { switch (model) { case 0: if (extModel == 0x60) // Renoir Q_strncatz(cpuString, maxSize, " Ryzen 7/5/3 4x00G"); break; case 1: if (extModel == 1) // Summit Ridge Q_strncatz(cpuString, maxSize, " Ryzen 7/5/3 1x00"); else if (extModel == 0x11) // Raven Ridge Q_strncatz(cpuString, maxSize, " Ryzen 5/3 2x00G"); else if (extModel == 0x71) // Matisse (Zen 2) Q_strncatz(cpuString, maxSize, " Ryzen 9/7/5/3 3x00"); break; case 8: if (extModel == 8) // Pinnacle Ridge Q_strncatz(cpuString, maxSize, " Ryzen 7/5/3 2x00"); else if (extModel == 0x18) // Picasso Q_strncatz(cpuString, maxSize, " Ryzen 5/3 3x00G"); break; default: Q_strncatz(cpuString, maxSize, " Zen/Zen+/Zen2"); break; } } else if (extFamily == 0x19) // Ryzen Zen3 Family { switch (model) { case 0: if (extModel == 0x50) // Cezanne Q_strncatz(cpuString, maxSize, " Ryzen 7/5/3 5x00G"); break; case 1: if (extModel == 0x21) // Vermeer (Zen3) Q_strncatz(cpuString, maxSize, " Ryzen 9/7/5/3 5x00"); break; default: Q_strncatz(cpuString, maxSize, " Zen3"); break; } } break; default: // unknown family break; } } else if (!Q_stricmp(vendor, "CyrixInstead")) { Q_strncpyz(cpuString, maxSize, "Cyrix"); switch (family) { case 4: Q_strncatz(cpuString, maxSize, " 5x86"); break; case 5: switch (model) { case 2: Q_strncatz(cpuString, maxSize, " 6x86"); break; case 4: Q_strncatz(cpuString, maxSize, " MediaGX"); break; default: Q_strncatz(cpuString, maxSize, " 6x86 / MediaGX"); break; } break; case 6: Q_strncatz(cpuString, maxSize, " 6x86MX"); break; default: // unknown family break; } } else if (!Q_stricmp(vendor, "CentaurHauls")) { Q_strncpyz(cpuString, maxSize, "Centaur"); switch (family) { case 5: switch (model) { case 4: Q_strncatz(cpuString, maxSize, " C6"); break; case 8: Q_strncatz(cpuString, maxSize, " C2"); break; case 9: Q_strncatz(cpuString, maxSize, " C3"); break; default: // unknown model break; } break; default: // unknown family break; } } else if (!Q_stricmp(vendor, "NexGenDriven")) { Q_strncpyz(cpuString, maxSize, "NexGen"); switch (family) { case 5: switch (model) { case 0: Q_strncatz(cpuString, maxSize, " Nx586 / Nx586FPU"); break; default: // unknown model break; } break; default: // unknown family break; } } else if (!Q_stricmp(vendor, "GenuineIntel")) { Q_strncpyz(cpuString, maxSize, "Intel"); // Com_sprintf(cpuString, maxSize, "Intel Family %i ExtFamily %i Model %i ExtModel %i", family, extFamily, model, extModel); switch (family) { case 5: // Pentium family switch (model) { case 0: case 1: case 2: case 3: case 4: case 7: case 8: default: Q_strncatz(cpuString, maxSize, " Pentium"); break; } break; case 6: if (model == extModel) // P6 family, before extModel was used { switch (model) { case 0: case 1: Q_strncatz(cpuString, maxSize, " Pentium Pro"); break; // Actual differentiation depends on cache settings case 3: // Klamath case 5: // Deschutes Q_strncatz(cpuString, maxSize, " Pentium II"); break; case 6: Q_strncatz(cpuString, maxSize, " Celeron"); break; // Actual differentiation depends on cache settings case 7: // Katmai case 8: // Coppermine case 10: // Coppermine case 11: // Tualatin Q_strncatz(cpuString, maxSize, " Pentium III"); break; case 12: // Silverthorne Q_strncatz(cpuString, maxSize, " Atom"); break; case 9: // Banias case 13: // Dothan Q_strncatz(cpuString, maxSize, " Pentium M"); break; case 14: // Yonah Q_strncatz(cpuString, maxSize, " Core"); break; case 15: // Conroe / Kentsfield Q_strncatz(cpuString, maxSize, " Core 2"); break; default: Q_strncatz(cpuString, maxSize, " P6"); break; } } else // Newer CPUs { switch (model) { case 5: if (extModel == 0x25) // Clarkdale / Arrandale Q_strncatz(cpuString, maxSize, " Core i5/i3 6xx / Core i3 5xx"); else if (extModel == 0x45) // Haswell ULT Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 4xxxU"); else if (extModel == 0x55) // Skylake-X, Cascade Lake-X Q_strncatz(cpuString, maxSize, " Core i9/i7 79xx/78xx or Core i9 10xxx"); else if (extModel == 0xA5) // Comet Lake Q_strncatz(cpuString, maxSize, " Core i9/i7/i5/i3 10xxx"); break; case 7: if (extModel == 0x17) // Wolfdale / Yorkfield (Penryn) Q_strncatz(cpuString, maxSize, " Core 2"); else if (extModel == 0x47) // Broadwell Q_strncatz(cpuString, maxSize, " Core i7/i5 5xxx"); else if (extModel == 0xA7) // Rocket Lake Q_strncatz(cpuString, maxSize, " Core i9/i7/i5/i3 11xxx"); break; case 10: if (extModel == 0x2A) // Sandy Bridge Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 2xxx"); else if (extModel == 0x3A) // Ivy Bridge Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 3xxx"); else if (extModel == 0x1A) // Bloomfield Q_strncatz(cpuString, maxSize, " Core i7 9xx"); else if (extModel == 0x7A) // Gemini Lake Q_strncatz(cpuString, maxSize, " Pentium J4xxx"); break; case 12: if (extModel == 0x2C) // Gulftown Q_strncatz(cpuString, maxSize, " Core i7 9xx"); else if (extModel == 0x3C) // Haswell Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 4xxx"); else if (extModel == 0x8C) // Tiger Lake Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 11xxG7"); else // Silverthorne Q_strncatz(cpuString, maxSize, " Atom"); break; case 13: if (extModel == 0x2D) // Sandy Bridge-E Q_strncatz(cpuString, maxSize, " Core i7 39xx / 38xx"); break; case 14: if (extModel == 0x1E) // Lynnfield Q_strncatz(cpuString, maxSize, " Core i7 8xx / Core i5 7xx"); else if (extModel == 0x3E) // Ivy Bridge-E Q_strncatz(cpuString, maxSize, " Core i7 49xx / 48xx"); else if (extModel == 0x4E) // Skylake U/Y Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 6xxxU"); else if (extModel == 0x5E) // Skylake Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 6xxx"); else if (extModel == 0x7E) // Ice lake Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 10xxG7"); else if (extModel == 0x8E) // Kaby Lake U/Y Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 7xxxU"); else if (extModel == 0x9E) // Coffee Lake Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 8xxx"); break; case 15: if (extModel == 0x0F) // Conroe / Kentsfield (Merom) Q_strncatz(cpuString, maxSize, " Core 2"); else if (extModel == 0x3F) // Haswell-E Q_strncatz(cpuString, maxSize, " Core i7 59xx / 58xx"); else if (extModel == 0x4F) // Broadwell-E Q_strncatz(cpuString, maxSize, " Core i7 69xx / 68xx"); else if (extModel == 0x5E) // Kaby Lake Q_strncatz(cpuString, maxSize, " Core i7/i5/i3 7xxx"); else if (extModel == 0x9E) // Kaby Lake-X Q_strncatz(cpuString, maxSize, " Core i7/i5 7xxx"); break; default: break; } } break; case 7: // Itanium family switch (model) { default: Q_strncatz(cpuString, maxSize, " Itanium"); break; } break; case 15: // refer to extended family if (extFamily == 0x0F) // NetBurst family { switch (model) { case 0: // Williamette case 1: // Williamette case 2: // Northwood case 3: // Prescott case 4: // Smithfield case 6: // Cedar Mill / Presler Q_strncatz(cpuString, maxSize, " Pentium 4"); break; // case 4: // Smithfield // case 6: // Cedar Mill / Presler // Q_strncatz(cpuString, " Pentium D", maxSize); // break; default: Q_strncatz(cpuString, maxSize, " NetBurst"); break; } } else if (extFamily == 0x1F || extFamily == 0x2F) // Itanium 2 family { switch (model) { default: Q_strncatz(cpuString, maxSize, " Itanium 2"); break; } } break; default: // unknown family break; } } else return false; // Check if RDTSC instruction is supported if ((features >> 4) & 1) { // Measure CPU speed QueryPerformanceFrequency((LARGE_INTEGER *)&frequency); #if defined (_M_X64) || defined (_M_AMD64) || defined (__x86_64__) start = __rdtsc(); #elif defined (_M_IX86) __asm { rdtsc mov dword ptr[start+0], eax mov dword ptr[start+4], edx } #endif QueryPerformanceCounter((LARGE_INTEGER *)&stop); stop += frequency; do { QueryPerformanceCounter((LARGE_INTEGER *)&counter); } while (counter < stop); #if defined (_M_X64) || defined (_M_AMD64) || defined (__x86_64__) end = __rdtsc(); #elif defined (_M_IX86) __asm { rdtsc mov dword ptr[end+0], eax mov dword ptr[end+4], edx } #endif speed = (unsigned)((end - start) / 1000000); if (speed > 1000) Q_strncatz(cpuString, maxSize, va(" %4.2f GHz", ((float)speed/1000.0f))); else Q_strncatz(cpuString, maxSize, va(" %u MHz", speed)); } // get number of logical processors GetSystemInfo(&sysInfo); if (sysInfo.dwNumberOfProcessors > 1) Q_strncatz(cpuString, maxSize, va(" (%u logical CPUs)", sysInfo.dwNumberOfProcessors)); /* if (numLogicalCores >= 2 || numCores >= 2) { if (numLogicalCores > numCores) // Hyperthreading or SMT Q_strncatz(cpuString, maxSize, va(" (%u cores, %u threads)", numCores, numLogicalCores)); else Q_strncatz(cpuString, maxSize, va(" (%u cores)", numCores)); } */ // Get extended instruction sets supported hasMMX = (features >> 23) & 1; hasMMXExt = (extFeatures >> 22) & 1; has3DNow = (extFeatures >> 31) & 1; has3DNowExt = (extFeatures >> 30) & 1; hasSSE = (features >> 25) & 1; hasSSE2 = (features >> 26) & 1; hasSSE3 = (moreFeatures >> 0) & 1; hasSSE41 = (moreFeatures >> 19) & 1; hasSSE42 = (moreFeatures >> 20) & 1; hasSSE4a = (moreFeatures >> 6) & 1; hasAVX = (moreFeatures >> 28) & 1; if (hasMMX || has3DNow || hasSSE) { Q_strncatz(cpuString, maxSize, " w/"); if (hasMMX){ Q_strncatz(cpuString, maxSize, " MMX"); if (hasMMXExt) Q_strncatz(cpuString, maxSize, "+"); } if (has3DNow){ Q_strncatz(cpuString, maxSize, " 3DNow!"); if (has3DNowExt) Q_strncatz(cpuString, maxSize, "+"); } if (hasSSE){ Q_strncatz(cpuString, maxSize, " SSE"); if (hasSSE42) Q_strncatz(cpuString, maxSize, "4.2"); else if (hasSSE41) Q_strncatz(cpuString, maxSize, "4.1"); else if (hasSSE3) Q_strncatz(cpuString, maxSize, "3"); else if (hasSSE2) Q_strncatz(cpuString, maxSize, "2"); } if (hasSSE4a){ Q_strncatz(cpuString, maxSize, " SSE4a"); } if (hasAVX){ Q_strncatz(cpuString, maxSize, " AVX"); } } return true; #else // Q_strncpyz(cpuString, maxSize, "Alpha AXP"); Q_strncpyz(cpuString, maxSize, CPUSTRING); // [Slipyx] mingw support return true; #endif } /* ================ Sys_Init ================ */ void Sys_Init (void) { OSVERSIONINFOEX osInfo; SYSTEM_INFO sysInfo; MEMORYSTATUS memStatus; // Knightmare added char string[128]; // Knightmare added #if 0 // allocate a named semaphore on the client so the // front end can tell if it is alive // mutex will fail if semephore already exists qwclsemaphore = CreateMutex( NULL, /* Security attributes */ 0, /* owner */ "qwcl"); /* Semaphore name */ if (!qwclsemaphore) Sys_Error ("QWCL is already running on this system"); CloseHandle (qwclsemaphore); qwclsemaphore = CreateSemaphore( NULL, /* Security attributes */ 0, /* Initial count */ 1, /* Maximum count */ "qwcl"); /* Semaphore name */ #endif timeBeginPeriod( 1 ); osInfo.dwOSVersionInfoSize = sizeof(osInfo); if (!GetVersionEx ((OSVERSIONINFO*) &osInfo)) Sys_Error ("Couldn't get OS info"); GetSystemInfo(&sysInfo); if (osInfo.dwMajorVersion < 4) Sys_Error ("KMQuake2 requires windows version 4 or greater"); if (osInfo.dwPlatformId == VER_PLATFORM_WIN32s) Sys_Error ("KMQuake2 doesn't run on Win32s"); else if ( osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) s_win95 = true; // from Q2E - OS & CPU detection if (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { // wProductType field not supported in MSVC6 #if (_MSC_VER < 1300) if (osInfo.dwMajorVersion == 4) { Q_strncpyz (string, sizeof(string), "Windows NT"); } else if (osInfo.dwMajorVersion == 5 && osInfo.dwMinorVersion == 0) { Q_strncpyz (string, sizeof(string), "Windows 2000"); } else if (osInfo.dwMajorVersion == 5 && osInfo.dwMinorVersion == 1) { Q_strncpyz (string, sizeof(string), "Windows XP"); } else if (osInfo.dwMajorVersion == 5 && osInfo.dwMinorVersion == 2) { Q_strncpyz (string, sizeof(string), "Windows XP"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 0) { Q_strncpyz (string, sizeof(string), "Windows Vista"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 1) { Q_strncpyz (string, sizeof(string), "Windows 7"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 2) { Q_strncpyz (string, sizeof(string), "Windows 8"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 3) { Q_strncpyz (string, sizeof(string), "Windows 8.1"); } else if (osInfo.dwMajorVersion == 10 && osInfo.dwMinorVersion == 0) { Q_strncpyz (string, sizeof(string), "Windows 10"); } #else // (_MSC_VER < 1300) if (osInfo.dwMajorVersion == 4) { Q_strncpyz (string, sizeof(string), "Windows NT"); } else if (osInfo.dwMajorVersion == 5 && osInfo.dwMinorVersion == 0) { if (osInfo.wProductType == VER_NT_WORKSTATION) Q_strncpyz (string, sizeof(string), "Windows 2000"); else Q_strncpyz (string, sizeof(string), "Windows 2000 Server"); } else if (osInfo.dwMajorVersion == 5 && osInfo.dwMinorVersion == 1) { Q_strncpyz (string, sizeof(string), "Windows XP"); } else if (osInfo.dwMajorVersion == 5 && osInfo.dwMinorVersion == 2) { // if ( (osInfo.wProductType == VER_NT_WORKSTATION) && (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) ) // Q_strncpyz (string, "Windows XP x64 Edition", sizeof(string)); if (osInfo.wProductType == VER_NT_WORKSTATION) Q_strncpyz (string, sizeof(string), "Windows XP"); else Q_strncpyz (string, sizeof(string), "Windows Server 2003"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 0) { if (osInfo.wProductType == VER_NT_WORKSTATION) Q_strncpyz (string, sizeof(string), "Windows Vista"); else Q_strncpyz (string, sizeof(string), "Windows Server 2008"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 1) { if (osInfo.wProductType == VER_NT_WORKSTATION) Q_strncpyz (string, sizeof(string), "Windows 7"); else Q_strncpyz (string, sizeof(string), "Windows Server 2008 R2"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 2) { if (osInfo.wProductType == VER_NT_WORKSTATION) Q_strncpyz (string, sizeof(string), "Windows 8"); else Q_strncpyz (string, sizeof(string), "Windows Server 2012"); } else if (osInfo.dwMajorVersion == 6 && osInfo.dwMinorVersion == 3) { if (osInfo.wProductType == VER_NT_WORKSTATION) Q_strncpyz (string, sizeof(string), "Windows 8.1"); else Q_strncpyz (string, sizeof(string), "Windows Server 2012 R2"); } else if (osInfo.dwMajorVersion == 10 && osInfo.dwMinorVersion == 0) { if (osInfo.wProductType == VER_NT_WORKSTATION) Q_strncpyz (string, sizeof(string), "Windows 10"); else Q_strncpyz (string, sizeof(string), "Windows Server 2016"); } #endif // (_MSC_VER < 1300) else { Q_strncpyz (string, sizeof(string), va("Windows %i.%i", osInfo.dwMajorVersion, osInfo.dwMinorVersion)); } if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) Q_strncatz (string, sizeof(string), " x64"); if (strlen(osInfo.szCSDVersion) > 0) Q_strncatz (string, sizeof(string), va(" %s", osInfo.szCSDVersion)); } else if (osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { if (osInfo.dwMajorVersion == 4 && osInfo.dwMinorVersion == 0) { if (osInfo.szCSDVersion[1] == 'C' || osInfo.szCSDVersion[1] == 'B') Q_strncpyz (string, sizeof(string), "Windows 95 OSR2"); else Q_strncpyz (string, sizeof(string), "Windows 95"); } else if (osInfo.dwMajorVersion == 4 && osInfo.dwMinorVersion == 10) { if (osInfo.szCSDVersion[1] == 'A') Q_strncpyz (string, sizeof(string), "Windows 98 SE"); else Q_strncpyz (string, sizeof(string), "Windows 98"); } else if (osInfo.dwMajorVersion == 4 && osInfo.dwMinorVersion == 90) Q_strncpyz (string, sizeof(string), "Windows ME"); else Q_strncpyz (string, sizeof(string), va("Windows %i.%i", osInfo.dwMajorVersion, osInfo.dwMinorVersion)); } else Q_strncpyz (string, sizeof(string), va("Windows %i.%i", osInfo.dwMajorVersion, osInfo.dwMinorVersion)); if (osInfo.dwBuildNumber > 0) Q_strncatz (string, sizeof(string), va(", build %i", osInfo.dwBuildNumber)); Com_Printf ("OS: %s\n", string); Cvar_Get ("sys_osVersion", string, CVAR_NOSET|CVAR_LATCH|CVAR_SAVE_IGNORE); // Detect CPU Com_Printf ("Detecting CPU... "); if (Sys_DetectCPU (string, sizeof(string))) { Com_Printf ("Found %s\n", string); Cvar_Get ("sys_cpuString", string, CVAR_NOSET|CVAR_LATCH|CVAR_SAVE_IGNORE); } else { Com_Printf ("Unknown CPU found\n"); Cvar_Get ("sys_cpuString", "Unknown", CVAR_NOSET|CVAR_LATCH|CVAR_SAVE_IGNORE); } // Get physical memory GlobalMemoryStatus (&memStatus); Q_strncpyz (string, sizeof(string), va("%u", (memStatus.dwTotalPhys >> 20))); Com_Printf ("Memory: %s MB\n", string); Cvar_Get ("sys_ramMegs", string, CVAR_NOSET|CVAR_LATCH|CVAR_SAVE_IGNORE); // end Q2E detection #ifndef NEW_DED_CONSOLE Sys_InitConsole (); // show dedicated console, moved to function #endif } /* ============================================================================== WINDOWS CRAP ============================================================================== */ /* ================= Sys_AppActivate ================= */ void Sys_AppActivate (void) { ShowWindow ( cl_hwnd, SW_RESTORE); SetForegroundWindow ( cl_hwnd ); } /* ======================================================================== GAME DLL ======================================================================== */ static HINSTANCE game_library; /* ================= Sys_UnloadGame ================= */ void Sys_UnloadGame (void) { if (!FreeLibrary (game_library)) Com_Error (ERR_FATAL, "FreeLibrary failed for game library"); game_library = NULL; } /* ================= Sys_GetGameAPI Loads the game dll ================= */ void *Sys_GetGameAPI (void *parms) { void *(*GetGameAPI) (void *); char name[MAX_OSPATH]; char *path; char cwd[MAX_OSPATH]; #if defined (_M_X64) || defined (_M_AMD64) || defined (__x86_64__) const char *gamename = "kmq2gamex64.dll"; #ifdef NDEBUG const char *debugdir = "release"; #else const char *debugdir = "debug"; #endif #elif defined (_M_IX86) // Knightmare- changed DLL name for better cohabitation const char *gamename = "kmq2gamex86.dll"; #ifdef NDEBUG const char *debugdir = "release"; #else const char *debugdir = "debug"; #endif #elif defined _M_ALPHA const char *gamename = "kmq2gameaxp.dll"; #ifdef NDEBUG const char *debugdir = "releaseaxp"; #else const char *debugdir = "debugaxp"; #endif #endif if (game_library) Com_Error (ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame"); // check the current debug directory first for development purposes _getcwd (cwd, sizeof(cwd)); Com_sprintf (name, sizeof(name), "%s/%s/%s", cwd, debugdir, gamename); game_library = LoadLibrary ( name ); if (game_library) { Com_DPrintf ("LoadLibrary (%s)\n", name); } else { #ifdef DEBUG // check the current directory for other development purposes Com_sprintf (name, sizeof(name), "%s/%s", cwd, gamename); game_library = LoadLibrary ( name ); if (game_library) { Com_DPrintf ("LoadLibrary (%s)\n", name); } else #endif { // now run through the search paths path = NULL; while (1) { // path = FS_NextPath (path); path = FS_NextGamePath (path); if (!path) return NULL; // couldn't find one anywhere Com_sprintf (name, sizeof(name), "%s/%s", path, gamename); game_library = LoadLibrary (name); if (game_library) { Com_DPrintf ("LoadLibrary (%s)\n",name); break; } } } } GetGameAPI = (void *)GetProcAddress (game_library, "GetGameAPI"); if (!GetGameAPI) { Sys_UnloadGame (); return NULL; } return GetGameAPI (parms); } //======================================================================= /* ================== ParseCommandLine ================== */ void ParseCommandLine (LPSTR lpCmdLine) { argc = 1; argv[0] = "exe"; while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) { while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) lpCmdLine++; if (*lpCmdLine) { argv[argc] = lpCmdLine; argc++; while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) lpCmdLine++; if (*lpCmdLine) { *lpCmdLine = 0; lpCmdLine++; } } } } // Knightmare- startup logo, code from TomazQuake #ifndef NEW_DED_CONSOLE HWND hwnd_dialog; // Knightmare added qboolean logoRemoved = false; void Sys_CreateStartupLogo (void) { // if (!(dedicated && dedicated->integer)) { hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL); RECT rect; // Knightmare added if (hwnd_dialog) { if (GetWindowRect (hwnd_dialog, &rect)) { if (rect.left > (rect.top * 2)) { SetWindowPos (hwnd_dialog, 0, (rect.left/2) - ((rect.right - rect.left)/2), rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } } ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); UpdateWindow (hwnd_dialog); SetForegroundWindow (hwnd_dialog); } } // end Knightmare } void Sys_RemoveStartupLogo (void) { if (!logoRemoved) { DestroyWindow (hwnd_dialog); logoRemoved = true; } } #endif // NEW_DED_CONSOLE /* ================== ReplaceBackSlashes Replaces backslashes in a path with slashes. ================== */ static void ReplaceBackSlashes (char *path) { char *cur, *old; cur = old = path; if (strstr(cur, "\\") != NULL) { while (cur != NULL) { if ((cur - old) > 1) { *cur = '/'; } old = cur; cur = strchr(old + 1, '\\'); } } } /* ================== Sys_ExeDir ================== */ const char *Sys_ExeDir (void) { return exe_dir; } /* ================== Sys_PrefDir ================== */ const char *Sys_PrefDir (void) { return pref_dir; } /* ================== Sys_DownloadDir ================== */ const char *Sys_DownloadDir (void) { return download_dir; } /* ================== Sys_InitPrefDir Adapted from DK 1.3 source ================== */ void Sys_InitPrefDir (void) { if ( win_use_profile_dir && win_use_profile_dir->integer ) { char profile[MAX_PATH], dlPath[MAX_PATH]; const char *reason = "No error!"; const char *reason_dl = "No error!"; int len, len_dl; qboolean bGotNT6SavedGames=false, bGotNT6Downloads=false; // Use Saved Games/KMQuake2 on Win Vista and later, unless "mygames" parameter is set if ( Detect_WinNT6orLater() && !COM_CheckParm ("-mygames") && !COM_CheckParm ("+mygames") ) { WCHAR *wprofile, *wDLPath; HMODULE hShell32 = LoadLibrary("shell32"); HMODULE hOle32 = LoadLibrary("ole32"); if ( !hShell32 || !hOle32 ) { reason = reason_dl = "shell32.dll or ole32.dll couldn't be loaded"; } else { fnSHGetKnownFolderPath = (void *)GetProcAddress (hShell32, "SHGetKnownFolderPath"); fnCoInitializeEx = (void *)GetProcAddress (hOle32, "CoInitializeEx"); fnCoUninitialize = (void *)GetProcAddress (hOle32, "CoUninitialize"); fnCoTaskMemFree = (void *)GetProcAddress (hOle32, "CoTaskMemFree"); if ( !fnSHGetKnownFolderPath || !fnCoInitializeEx || !fnCoUninitialize || !fnCoTaskMemFree ) { reason = reason_dl = "functions SHGetKnownFolderPath / CoInitializeEx / CoUninitialize / CoTaskMemFree couldn't be mapped"; } else { // from DarkPlaces memset (profile, 0, sizeof(profile)); fnCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (fnSHGetKnownFolderPath(&qFOLDERID_SavedGames, qKF_FLAG_CREATE | qKF_FLAG_NO_ALIAS, NULL, &wprofile) == S_OK) { memset(profile, 0, sizeof(profile)); #if _MSC_VER >= 1400 wcstombs_s(NULL, profile, sizeof(profile), wprofile, sizeof(profile)-1); #else wcstombs(profile, wprofile, sizeof(profile)-1); #endif fnCoTaskMemFree(wprofile); } // Also get Downloads Folder from qFOLDERID_Downloads if (fnSHGetKnownFolderPath(&qFOLDERID_Downloads, qKF_FLAG_CREATE | qKF_FLAG_NO_ALIAS, NULL, &wDLPath) == S_OK) { memset(dlPath, 0, sizeof(dlPath)); #if _MSC_VER >= 1400 wcstombs_s(NULL, dlPath, sizeof(dlPath), wDLPath, sizeof(dlPath)-1); #else wcstombs(dlPath, wDLPath, sizeof(dlPath)-1); #endif fnCoTaskMemFree(wDLPath); } fnCoUninitialize(); // end DarkPlaces code len = (int)strlen(profile); if (len == 0) { reason = "SHGetKnownFolderPath()/wcstombs() returned 0 length string"; } else { // Check if path is too long if ( (len + strlen(NT6_SAVEDIR) + 3) >= 256 ) { reason = "the resulting path would be too long (>= 256 chars)"; } else { // Replace backslashes with slashes ReplaceBackSlashes (profile); Com_sprintf (pref_dir, sizeof(pref_dir), "%s/%s", profile, NT6_SAVEDIR); bGotNT6SavedGames = true; // return; } } len_dl = (int)strlen(dlPath); if (len_dl == 0) { reason_dl = "SHGetKnownFolderPath()/wcstombs() returned 0 length string"; } else { // Check if path is too long if ( (len_dl + strlen(NT6_DLDIR) + 3) >= 256 ) { reason_dl = "the resulting path would be too long (>= 256 chars)"; } else { // Replace backslashes with slashes ReplaceBackSlashes (dlPath); Com_sprintf (download_dir, sizeof(download_dir), "%s/%s", dlPath, NT6_DLDIR); bGotNT6Downloads = true; // return; } } } } if (bGotNT6SavedGames && bGotNT6Downloads) // successfully got both dirs, return now return; // Output reason for why one or both dirs couldn't be found, then fall back on NT5 path if (!bGotNT6SavedGames) Com_Printf("Couldn't get PrefDir (Saved Games/KMQuake2), because %s.\n", reason); if (!bGotNT6Downloads) Com_Printf("Couldn't get DownloadDir (Downloads/KMQuake2), because %s.\n", reason_dl); } // Use My Documents/My Games/KMQuake2 on Win2K / XP if ( Detect_WinNT5orLater() ) { WCHAR sprofile[MAX_PATH]; WCHAR uprofile[MAX_PATH]; HMODULE hShell32 = LoadLibrary("shell32"); if (!hShell32) { reason = "shell32.dll couldn't be loaded"; } else { fnSHGetFolderPathW = (SHGETFOLDERPATHW)GetProcAddress (hShell32, "SHGetFolderPathW"); if (!fnSHGetFolderPathW) { reason = "function SHGetFolderPathW couldn't be mapped"; } else { memset (pref_dir, 0, sizeof(pref_dir)); /* The following lines implement a horrible hack to connect the UTF-16 WinAPI to the ASCII Quake II. While this should work in most cases, it'll fail if the "Windows to DOS filename translation" is switched off. In that case the function will return NULL and no homedir is used. */ // Get path to "My Documents" folder fnSHGetFolderPathW (NULL, CSIDL_PERSONAL, NULL, 8, uprofile); // Create a UTF-16 DOS path len = GetShortPathNameW (uprofile, sprofile, sizeof(sprofile)); if (len == 0) { reason = "GetShortPathNameW() returned 0"; } else { // Since the DOS path contains no UTF-16 characters, just convert it to ASCII len = WideCharToMultiByte (CP_ACP, 0, sprofile, -1, profile, sizeof(profile), NULL, NULL); if (len == 0) { reason = "WideCharToMultiByte() returned 0"; } else { // Check if path is too long if ( ((len + strlen(NT5_SAVEDIR) + 3) >= 256) || ((len + strlen(NT5_DLDIR) + 3) >= 256) ) { reason = "The resulting path would be too long (>= 256 chars)"; } else { // Replace backslashes with slashes ReplaceBackSlashes (profile); // Allow splitting of dirs from above NT6 section if only one failed if (!bGotNT6SavedGames) Com_sprintf (pref_dir, sizeof(pref_dir), "%s/%s", profile, NT5_SAVEDIR); if (!bGotNT6Downloads) Com_sprintf (download_dir, sizeof(download_dir), "%s/%s", profile, NT5_DLDIR); return; } } } } } Com_Printf("Couldn't get PrefDir (My Documents/My Games/KMQuake2), because %s.\n", reason); } } Com_sprintf (pref_dir, sizeof(pref_dir), exe_dir); Com_sprintf (download_dir, sizeof(download_dir), exe_dir); } /* ================== Init_ExeDir ================== */ static void Init_ExeDir (void) { #if 1 memset(exe_dir, 0, sizeof(exe_dir)); Q_snprintfz (exe_dir, sizeof(exe_dir), "."); #else char buf[MAX_PATH]; const char *lastSlash; int dirLen; memset(buf, 0, sizeof(buf)); memset(exe_dir, 0, sizeof(exe_dir)); GetModuleFileName (NULL, buf, sizeof(buf)-1); // get path up to last backslash lastSlash = strrchr(buf, '\\'); if (lastSlash == NULL) lastSlash = strrchr(buf, '/'); dirLen = lastSlash ? (lastSlash - buf) : 0; if ( lastSlash == NULL || dirLen == 0 || dirLen >= sizeof(exe_dir) ) { Q_snprintfz (exe_dir, sizeof(exe_dir), "."); } else { memcpy(exe_dir, buf, dirLen); } #endif } /* ================== FixWorkingDirectory ================== */ void FixWorkingDirectory (void) { int i; char *p; char curDir[MAX_PATH]; GetModuleFileName (NULL, curDir, sizeof(curDir)-1); p = strrchr (curDir, '\\'); p[0] = 0; for (i = 1; i < argc; i++) { if (!strcmp (argv[i], "-nopathcheck")) goto skipPathCheck; if (!strcmp (argv[i], "-nocwdcheck")) return; } if (strlen(curDir) > (MAX_OSPATH - MAX_QPATH)) Sys_Error ("Current path is too long. Please move your Quake II installation to a shorter path."); skipPathCheck: SetCurrentDirectory (curDir); } /* ================== Detect_WinNT5orLater ================== */ qboolean Detect_WinNT5orLater (void) { DWORD WinVersion; DWORD WinLowByte, WinHighByte; WinVersion = GetVersion(); WinLowByte = (DWORD)(LOBYTE(LOWORD(WinVersion))); WinHighByte = (DWORD)(HIBYTE(HIWORD(WinVersion))); if (WinLowByte <= 4) { Com_DPrintf("Windows 9x or NT 4 detected.\n"); return false; } if (WinLowByte >= 5) { Com_DPrintf("Windows 5.x or later detected.\n"); return true; } return false; } /* ================== Detect_WinNT6orLater ================== */ qboolean Detect_WinNT6orLater (void) { DWORD WinVersion; DWORD WinLowByte, WinHighByte; WinVersion = GetVersion(); WinLowByte = (DWORD)(LOBYTE(LOWORD(WinVersion))); WinHighByte = (DWORD)(HIBYTE(HIWORD(WinVersion))); if (WinLowByte <= 4) { Com_DPrintf("Windows 9x or NT 4 detected.\n"); return false; } if (WinLowByte == 5) { Com_DPrintf("Windows 5.x (Win2K / XP / Server 2003) detected.\n"); return false; } if (WinLowByte >= 6) { Com_DPrintf("Windows 6.x (WinVista / 7 / 8) detected.\n"); return true; } return false; } /* ================== Sys_SetHighDPIMode From Yamagi Quake2 ================== */ typedef enum KMQ2_PROCESS_DPI_AWARENESS { KMQ2_PROCESS_DPI_UNAWARE = 0, KMQ2_PROCESS_SYSTEM_DPI_AWARE = 1, KMQ2_PROCESS_PER_MONITOR_DPI_AWARE = 2 } KMQ2_PROCESS_DPI_AWARENESS; void Sys_SetHighDPIMode (void) { HINSTANCE userDLL, shcoreDLL; /* For Vista, Win7 and Win8 */ BOOL(WINAPI *SetProcessDPIAware)(void) = NULL; /* Win8.1 and later */ HRESULT(WINAPI *SetProcessDpiAwareness)(KMQ2_PROCESS_DPI_AWARENESS dpiAwareness) = NULL; userDLL = LoadLibrary("USER32.DLL"); if (userDLL) { SetProcessDPIAware = (BOOL(WINAPI *)(void)) GetProcAddress(userDLL, "SetProcessDPIAware"); } shcoreDLL = LoadLibrary("SHCORE.DLL"); if (shcoreDLL) { SetProcessDpiAwareness = (HRESULT(WINAPI *)(KMQ2_PROCESS_DPI_AWARENESS)) GetProcAddress(shcoreDLL, "SetProcessDpiAwareness"); } if (SetProcessDpiAwareness) { SetProcessDpiAwareness(KMQ2_PROCESS_PER_MONITOR_DPI_AWARE); } else if (SetProcessDPIAware) { SetProcessDPIAware(); } } /* ================== WinMain ================== */ HINSTANCE global_hInstance; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; int time, oldtime, newtime; char *cddir; int i; // Knightmare added qboolean cdscan = false; // Knightmare added /* previous instances do not exist in Win32 */ if (hPrevInstance) return 0; global_hInstance = hInstance; ParseCommandLine (lpCmdLine); Sys_SetHighDPIMode (); // setup DPI awareness // r1ch: always change to our directory (ugh) FixWorkingDirectory (); Init_ExeDir (); // Knightmare added #ifdef NEW_DED_CONSOLE // init debug console Sys_InitDedConsole (); Com_Printf("KMQ2 %4.2fu%d %s %s %s %s\n", VERSION, VERSION_UPDATE, CPUSTRING, OS_STRING, COMPILETYPE_STRING, __DATE__); #else Sys_CreateStartupLogo (); #endif // NEW_DED_CONSOLE // Knightmare- scan for cd command line option for (i=0; ivalue) ) if (Minimized || (dedicated && dedicated->integer) ) { Sleep (1); } while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessage (&msg, NULL, 0, 0)) Com_Quit (); sys_msg_time = msg.time; TranslateMessage (&msg); DispatchMessage (&msg); } // DarkOne's CPU usage fix while (1) { newtime = Sys_Milliseconds(); time = newtime - oldtime; if (time > 0) break; Sleep(0); // may also use Sleep(1); to free more CPU, but it can lower your fps } /*do { newtime = Sys_Milliseconds (); time = newtime - oldtime; } while (time < 1);*/ // Con_Printf ("time:%5.2f - %5.2f = %5.2f\n", newtime, oldtime, time); #if defined(_M_IX86) || defined(__i386__) // _controlfp( ~( _EM_ZERODIVIDE /*| _EM_INVALID*/ ), _MCW_EM ); _controlfp( _PC_24, _MCW_PC ); #endif Qcommon_Frame (time); oldtime = newtime; } // never gets here return TRUE; }