/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../qcommon/q_shared.h" #include "../qcommon/qcommon.h" #include "sys_local.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef KEY_WOW64_32KEY #define KEY_WOW64_32KEY 0x0200 #endif // Used to determine where to store user-specific files static char homePath[ MAX_OSPATH ] = { 0 }; // Used to store the Steam Quake 3 installation path static char steamPath[ MAX_OSPATH ] = { 0 }; // Used to store the GOG Quake 3 installation path static char gogPath[ MAX_OSPATH ] = { 0 }; // Used to store the Microsoft Store Quake 3 installation path static char microsoftStorePath[MAX_OSPATH] = { 0 }; #ifndef DEDICATED static UINT timerResolution = 0; #endif /* ================ Sys_SetFPUCW Set FPU control word to default value ================ */ #ifndef _RC_CHOP // mingw doesn't seem to have these defined :( #define _MCW_EM 0x0008001fU #define _MCW_RC 0x00000300U #define _MCW_PC 0x00030000U #define _RC_NEAR 0x00000000U #define _PC_53 0x00010000U unsigned int _controlfp(unsigned int new, unsigned int mask); #endif #define FPUCWMASK1 (_MCW_RC | _MCW_EM) #define FPUCW (_RC_NEAR | _MCW_EM | _PC_53) #if idx64 #define FPUCWMASK (FPUCWMASK1) #else #define FPUCWMASK (FPUCWMASK1 | _MCW_PC) #endif void Sys_SetFloatEnv(void) { _controlfp(FPUCW, FPUCWMASK); } /* ================ Sys_DefaultHomePath ================ */ char *Sys_DefaultHomePath( void ) { TCHAR szPath[MAX_PATH]; FARPROC qSHGetFolderPath; HMODULE shfolder = LoadLibrary("shfolder.dll"); if(shfolder == NULL) { Com_Printf("Unable to load SHFolder.dll\n"); return NULL; } if(!*homePath && com_homepath) { qSHGetFolderPath = GetProcAddress(shfolder, "SHGetFolderPathA"); if(qSHGetFolderPath == NULL) { Com_Printf("Unable to find SHGetFolderPath in SHFolder.dll\n"); FreeLibrary(shfolder); return NULL; } if( !SUCCEEDED( qSHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, szPath ) ) ) { Com_Printf("Unable to detect CSIDL_APPDATA\n"); FreeLibrary(shfolder); return NULL; } Com_sprintf(homePath, sizeof(homePath), "%s%c", szPath, PATH_SEP); if(com_homepath->string[0]) Q_strcat(homePath, sizeof(homePath), com_homepath->string); else Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_WIN); } FreeLibrary(shfolder); return homePath; } /* ================ Sys_SteamPath ================ */ char *Sys_SteamPath( void ) { #if defined(STEAMPATH_NAME) || defined(STEAMPATH_APPID) HKEY steamRegKey; DWORD pathLen = MAX_OSPATH; qboolean finishPath = qfalse; #ifdef STEAMPATH_APPID // Assuming Steam is a 32-bit app if (!steamPath[0] && !RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App " STEAMPATH_APPID, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &steamRegKey)) { pathLen = MAX_OSPATH; if (RegQueryValueEx(steamRegKey, "InstallLocation", NULL, NULL, (LPBYTE)steamPath, &pathLen)) steamPath[0] = '\0'; RegCloseKey(steamRegKey); } #endif #ifdef STEAMPATH_NAME if (!steamPath[0] && !RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Valve\\Steam", 0, KEY_QUERY_VALUE, &steamRegKey)) { pathLen = MAX_OSPATH; if (RegQueryValueEx(steamRegKey, "SteamPath", NULL, NULL, (LPBYTE)steamPath, &pathLen)) if (RegQueryValueEx(steamRegKey, "InstallPath", NULL, NULL, (LPBYTE)steamPath, &pathLen)) steamPath[0] = '\0'; if (steamPath[0]) finishPath = qtrue; RegCloseKey(steamRegKey); } #endif if (steamPath[0]) { if (pathLen == MAX_OSPATH) pathLen--; steamPath[pathLen] = '\0'; if (finishPath) Q_strcat(steamPath, MAX_OSPATH, "\\SteamApps\\common\\" STEAMPATH_NAME ); } #endif return steamPath; } /* ================ Sys_GogPath ================ */ char *Sys_GogPath( void ) { #ifdef GOGPATH_ID HKEY gogRegKey; DWORD pathLen = MAX_OSPATH; if (!gogPath[0] && !RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\GOG.com\\Games\\" GOGPATH_ID, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &gogRegKey)) { pathLen = MAX_OSPATH; if (RegQueryValueEx(gogRegKey, "PATH", NULL, NULL, (LPBYTE)gogPath, &pathLen)) gogPath[0] = '\0'; RegCloseKey(gogRegKey); } if (gogPath[0]) { if (pathLen == MAX_OSPATH) pathLen--; gogPath[pathLen] = '\0'; } #endif return gogPath; } /* ================ Sys_MicrosoftStorePath ================ */ char* Sys_MicrosoftStorePath(void) { #ifdef MSSTORE_PATH if (!microsoftStorePath[0]) { TCHAR szPath[MAX_PATH]; FARPROC qSHGetFolderPath; HMODULE shfolder = LoadLibrary("shfolder.dll"); if(shfolder == NULL) { Com_Printf("Unable to load SHFolder.dll\n"); return microsoftStorePath; } qSHGetFolderPath = GetProcAddress(shfolder, "SHGetFolderPathA"); if(qSHGetFolderPath == NULL) { Com_Printf("Unable to find SHGetFolderPath in SHFolder.dll\n"); FreeLibrary(shfolder); return microsoftStorePath; } if( !SUCCEEDED( qSHGetFolderPath( NULL, CSIDL_PROGRAM_FILES, NULL, 0, szPath ) ) ) { Com_Printf("Unable to detect CSIDL_PROGRAM_FILES\n"); FreeLibrary(shfolder); return microsoftStorePath; } FreeLibrary(shfolder); // default: C:\Program Files\ModifiableWindowsApps\Quake 3\EN Com_sprintf(microsoftStorePath, sizeof(microsoftStorePath), "%s%cModifiableWindowsApps%c%s%cEN", szPath, PATH_SEP, PATH_SEP, MSSTORE_PATH, PATH_SEP); } #endif return microsoftStorePath; } /* ================ Sys_Milliseconds ================ */ int sys_timeBase; int Sys_Milliseconds (void) { int sys_curtime; static qboolean initialized = qfalse; if (!initialized) { sys_timeBase = timeGetTime(); initialized = qtrue; } sys_curtime = timeGetTime() - sys_timeBase; return sys_curtime; } /* ================ Sys_RandomBytes ================ */ qboolean Sys_RandomBytes( byte *string, int len ) { HCRYPTPROV prov; if( !CryptAcquireContext( &prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) ) { return qfalse; } if( !CryptGenRandom( prov, len, (BYTE *)string ) ) { CryptReleaseContext( prov, 0 ); return qfalse; } CryptReleaseContext( prov, 0 ); return qtrue; } /* ================ Sys_GetCurrentUser ================ */ char *Sys_GetCurrentUser( void ) { static char s_userName[1024]; unsigned long size = sizeof( s_userName ); if( !GetUserName( s_userName, &size ) ) strcpy( s_userName, "player" ); if( !s_userName[0] ) { strcpy( s_userName, "player" ); } return s_userName; } #define MEM_THRESHOLD 96*1024*1024 /* ================== Sys_LowPhysicalMemory ================== */ qboolean Sys_LowPhysicalMemory( void ) { MEMORYSTATUS stat; GlobalMemoryStatus (&stat); return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse; } /* ============== Sys_Basename ============== */ const char *Sys_Basename( char *path ) { static char base[ MAX_OSPATH ] = { 0 }; int length; length = strlen( path ) - 1; // Skip trailing slashes while( length > 0 && path[ length ] == '\\' ) length--; while( length > 0 && path[ length - 1 ] != '\\' ) length--; Q_strncpyz( base, &path[ length ], sizeof( base ) ); length = strlen( base ) - 1; // Strip trailing slashes while( length > 0 && base[ length ] == '\\' ) base[ length-- ] = '\0'; return base; } /* ============== Sys_Dirname ============== */ const char *Sys_Dirname( char *path ) { static char dir[ MAX_OSPATH ] = { 0 }; int length; Q_strncpyz( dir, path, sizeof( dir ) ); length = strlen( dir ) - 1; while( length > 0 && dir[ length ] != '\\' ) length--; dir[ length ] = '\0'; return dir; } /* ============== Sys_FOpen ============== */ FILE *Sys_FOpen( const char *ospath, const char *mode ) { size_t length; // Windows API ignores all trailing spaces and periods which can get around Quake 3 file system restrictions. length = strlen( ospath ); if ( length == 0 || ospath[length-1] == ' ' || ospath[length-1] == '.' ) { return NULL; } return fopen( ospath, mode ); } /* ============== Sys_Mkdir ============== */ qboolean Sys_Mkdir( const char *path ) { if( !CreateDirectory( path, NULL ) ) { if( GetLastError( ) != ERROR_ALREADY_EXISTS ) return qfalse; } return qtrue; } /* ================== Sys_Mkfifo Noop on windows because named pipes do not function the same way ================== */ FILE *Sys_Mkfifo( const char *ospath ) { return NULL; } /* ============== Sys_Cwd ============== */ char *Sys_Cwd( void ) { static char cwd[MAX_OSPATH]; _getcwd( cwd, sizeof( cwd ) - 1 ); cwd[MAX_OSPATH-1] = 0; return cwd; } /* ============================================================== DIRECTORY SCANNING ============================================================== */ #define MAX_FOUND_FILES 0x1000 /* ============== Sys_ListFilteredFiles ============== */ void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) { char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; char filename[MAX_OSPATH]; intptr_t findhandle; struct _finddata_t findinfo; if ( *numfiles >= MAX_FOUND_FILES - 1 ) { return; } if ( basedir[0] == '\0' ) { return; } if (strlen(subdirs)) { Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs ); } else { Com_sprintf( search, sizeof(search), "%s\\*", basedir ); } findhandle = _findfirst (search, &findinfo); if (findhandle == -1) { return; } do { if (findinfo.attrib & _A_SUBDIR) { if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) { if (strlen(subdirs)) { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name); } else { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name); } Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); } } if ( *numfiles >= MAX_FOUND_FILES - 1 ) { break; } Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name ); if (!Com_FilterPath( filter, filename, qfalse )) continue; list[ *numfiles ] = CopyString( filename ); (*numfiles)++; } while ( _findnext (findhandle, &findinfo) != -1 ); _findclose (findhandle); } /* ============== strgtr ============== */ static qboolean strgtr(const char *s0, const char *s1) { int l0, l1, i; l0 = strlen(s0); l1 = strlen(s1); if (l1 s0[i]) { return qtrue; } if (s1[i] < s0[i]) { return qfalse; } } return qfalse; } /* ============== Sys_ListFiles ============== */ char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) { char search[MAX_OSPATH]; int nfiles; char **listCopy; char *list[MAX_FOUND_FILES]; struct _finddata_t findinfo; intptr_t findhandle; int flag; int i; int extLen; if (filter) { nfiles = 0; Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); list[ nfiles ] = 0; *numfiles = nfiles; if (!nfiles) return NULL; listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } if ( directory[0] == '\0' ) { *numfiles = 0; return NULL; } if ( !extension) { extension = ""; } // passing a slash as extension will find directories if ( extension[0] == '/' && extension[1] == 0 ) { extension = ""; flag = 0; } else { flag = _A_SUBDIR; } extLen = strlen( extension ); Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension ); // search nfiles = 0; findhandle = _findfirst (search, &findinfo); if (findhandle == -1) { *numfiles = 0; return NULL; } do { if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) { if (*extension) { if ( strlen( findinfo.name ) < extLen || Q_stricmp( findinfo.name + strlen( findinfo.name ) - extLen, extension ) ) { continue; // didn't match } } if ( nfiles == MAX_FOUND_FILES - 1 ) { break; } list[ nfiles ] = CopyString( findinfo.name ); nfiles++; } } while ( _findnext (findhandle, &findinfo) != -1 ); list[ nfiles ] = 0; _findclose (findhandle); // return a copy of the list *numfiles = nfiles; if ( !nfiles ) { return NULL; } listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; do { flag = 0; for(i=1; i 0 ) { Com_Memcpy( p, buffer, size ); p += size; } *p = '\0'; if( OpenClipboard( NULL ) && EmptyClipboard( ) ) SetClipboardData( CF_TEXT, memoryHandle ); GlobalUnlock( clipMemory ); CloseClipboard( ); } } } /* ============== Sys_Dialog Display a win32 dialog box ============== */ dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ) { UINT uType; switch( type ) { default: case DT_INFO: uType = MB_ICONINFORMATION|MB_OK; break; case DT_WARNING: uType = MB_ICONWARNING|MB_OK; break; case DT_ERROR: uType = MB_ICONERROR|MB_OK; break; case DT_YES_NO: uType = MB_ICONQUESTION|MB_YESNO; break; case DT_OK_CANCEL: uType = MB_ICONWARNING|MB_OKCANCEL; break; } switch( MessageBox( NULL, message, title, uType ) ) { default: case IDOK: return DR_OK; case IDCANCEL: return DR_CANCEL; case IDYES: return DR_YES; case IDNO: return DR_NO; } } /* ============== Sys_GLimpSafeInit Windows specific "safe" GL implementation initialisation ============== */ void Sys_GLimpSafeInit( void ) { } /* ============== Sys_GLimpInit Windows specific GL implementation initialisation ============== */ void Sys_GLimpInit( void ) { } /* ============== Sys_PlatformInit Windows specific initialisation ============== */ void Sys_PlatformInit( void ) { #ifndef DEDICATED TIMECAPS ptc; #endif Sys_SetFloatEnv(); #ifndef DEDICATED if(timeGetDevCaps(&ptc, sizeof(ptc)) == MMSYSERR_NOERROR) { timerResolution = ptc.wPeriodMin; if(timerResolution > 1) { Com_Printf("Warning: Minimum supported timer resolution is %ums " "on this system, recommended resolution 1ms\n", timerResolution); } timeBeginPeriod(timerResolution); } else timerResolution = 0; #endif } /* ============== Sys_PlatformExit Windows specific initialisation ============== */ void Sys_PlatformExit( void ) { #ifndef DEDICATED if(timerResolution) timeEndPeriod(timerResolution); #endif } /* ============== Sys_SetEnv set/unset environment variables (empty value removes it) ============== */ void Sys_SetEnv(const char *name, const char *value) { if(value) _putenv(va("%s=%s", name, value)); else _putenv(va("%s=", name)); } /* ============== Sys_PID ============== */ int Sys_PID( void ) { return GetCurrentProcessId( ); } /* ============== Sys_PIDIsRunning ============== */ qboolean Sys_PIDIsRunning( int pid ) { DWORD processes[ 1024 ]; DWORD numBytes, numProcesses; int i; if( !EnumProcesses( processes, sizeof( processes ), &numBytes ) ) return qfalse; // Assume it's not running numProcesses = numBytes / sizeof( DWORD ); // Search for the pid for( i = 0; i < numProcesses; i++ ) { if( processes[ i ] == pid ) return qtrue; } return qfalse; } /* ================= Sys_DllExtension Check if filename should be allowed to be loaded as a DLL. ================= */ qboolean Sys_DllExtension( const char *name ) { return COM_CompareExtension( name, DLL_EXT ); }