mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
3ce18b138c
Simplified loading and saving of cvars in slider menu control. Enlarged text in Game, Multiplayer, and Options menus. Fixed repeat of failed file causing HTTP downloads to restart. Added cl_zoommode cvar to simplify Lazarus zoom command. Changed zoom command to use new cl_zoommode cvar in default Lazarus and missionpack DLLs. Removed unused "crossh" cvar in default Lazarus and missionpack DLLs. Fixed Makron not having a classname when spawned from Jorg in default Lazarus and missionpack DLLs. Made Tactician Gunner ignore small amounts of damage in missionpack DLL.
2165 lines
52 KiB
C
2165 lines
52 KiB
C
/*
|
|
===========================================================================
|
|
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 <errno.h>
|
|
#include <float.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
#include <conio.h>
|
|
#include <shlobj.h>
|
|
#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<argc; i++)
|
|
if (!strcmp(argv[i], "game") && (i+1<argc))
|
|
{
|
|
if (!strcmp(argv[i+1], "rogue") || !strcmp(argv[i+1], "xatrix"))
|
|
missionpack = true;
|
|
break; // game parameter only appears once in command line
|
|
}
|
|
|
|
// scan the drives
|
|
for (drive[0] = 'c' ; drive[0] <= 'z' ; drive[0]++)
|
|
{
|
|
// where activision put the stuff...
|
|
if (missionpack) // Knightmare- mission packs have cinematics in different path
|
|
{
|
|
sprintf (cddir, "%sdata\\max", drive);
|
|
sprintf (test, "%sdata\\patch\\quake2.exe", drive);
|
|
}
|
|
else
|
|
{
|
|
sprintf (cddir, "%sinstall\\data", drive);
|
|
sprintf (test, "%sinstall\\data\\quake2.exe", drive);
|
|
}
|
|
f = fopen(test, "r");
|
|
if (f)
|
|
{
|
|
fclose (f);
|
|
if (GetDriveType (drive) == DRIVE_CDROM) {
|
|
Com_Printf(" found %s\n", cddir);
|
|
return cddir;
|
|
}
|
|
}
|
|
}
|
|
|
|
Com_Printf(" could not find %s on any CDROM drive!\n", test);
|
|
|
|
cddir[0] = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//================================================================
|
|
|
|
/*
|
|
=================
|
|
Sys_DetectCPU
|
|
l33t CPU detection
|
|
Borrowed from Q2E
|
|
=================
|
|
*/
|
|
static qboolean Sys_DetectCPU (char *cpuString, int maxSize)
|
|
{
|
|
#if ( defined (_M_IX86) || defined (_M_X64) || defined (_M_AMD64) || defined (__x86_64__) ) && !defined(__GNUC__) // [Slipyx] mingw support
|
|
|
|
char vendor[16];
|
|
// int numLogicalCores=1, numCores=1;
|
|
int maxExtFunc, stdBits, features, moreFeatures, extFeatures;
|
|
int family, extFamily, model, extModel;
|
|
unsigned __int64 start, end, counter, stop, frequency;
|
|
unsigned speed;
|
|
qboolean hasMMX, hasMMXExt, has3DNow, has3DNowExt, hasSSE, hasSSE2, hasSSE3, hasSSE41, hasSSE42, hasSSE4a, hasAVX;
|
|
SYSTEM_INFO sysInfo;
|
|
|
|
#if defined (_M_X64) || defined (_M_AMD64) || defined (__x86_64__)
|
|
#define rdtsc __asm __emit 0fh __asm __emit 031h
|
|
int registers[4];
|
|
|
|
// Get vendor identifier
|
|
__cpuid(registers, 0);
|
|
memset(vendor, 0, sizeof(vendor));
|
|
memcpy(vendor+0, ®isters[1], sizeof(char)*4); // ebx
|
|
memcpy(vendor+4, ®isters[3], sizeof(char)*4); // edx
|
|
memcpy(vendor+8, ®isters[2], sizeof(char)*4); // ecx
|
|
|
|
// Get standard bits and features
|
|
__cpuid(registers, 1);
|
|
stdBits = registers[0];
|
|
moreFeatures = registers[2];
|
|
features = registers[3];
|
|
// numLogicalCores = (registers[1] >> 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; i<argc; i++)
|
|
if (!strcmp(argv[i], "scanforcd")) {
|
|
cdscan = true;
|
|
break;
|
|
}
|
|
|
|
// if we find the CD, add a +set cddir xxx command line
|
|
if (cdscan)
|
|
{
|
|
cddir = Sys_ScanForCD ();
|
|
if (cddir && argc < MAX_NUM_ARGVS - 3)
|
|
{
|
|
int i;
|
|
|
|
// don't override a cddir on the command line
|
|
for (i=0 ; i<argc ; i++)
|
|
if (!strcmp(argv[i], "cddir"))
|
|
break;
|
|
if (i == argc)
|
|
{
|
|
argv[argc++] = "+set";
|
|
argv[argc++] = "cddir";
|
|
argv[argc++] = cddir;
|
|
}
|
|
}
|
|
}
|
|
|
|
Qcommon_Init (argc, argv);
|
|
oldtime = Sys_Milliseconds ();
|
|
|
|
/* main window message loop */
|
|
while (1)
|
|
{
|
|
// if at a full screen console, don't update unless needed
|
|
// if (Minimized || (dedicated && dedicated->value) )
|
|
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;
|
|
}
|