/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2000-2013 Darklegion Development Copyright (C) 2015-2019 GrangerHub This file is part of Tremulous. Tremulous 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 3 of the License, or (at your option) any later version. Tremulous 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 Tremulous; if not, see Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "qcommon/q_shared.h" #include "qcommon/qcommon.h" #include "dialog.h" #include "sys_local.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 extern "C" 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_Milliseconds ================ */ int sys_timeBase; int Sys_Milliseconds (void) { int sys_curtime; static bool initialized = false; if (!initialized) { sys_timeBase = timeGetTime(); initialized = true; } sys_curtime = timeGetTime() - sys_timeBase; return sys_curtime; } /* ================ Sys_RandomBytes ================ */ bool Sys_RandomBytes( byte *string, int len ) { HCRYPTPROV prov; if( !CryptAcquireContext( &prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) ) { return false; } if( !CryptGenRandom( prov, len, (BYTE *)string ) ) { CryptReleaseContext( prov, 0 ); return false; } CryptReleaseContext( prov, 0 ); return true; } /* ================ 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; } /* ================== Sys_CryptoRandomBytes ================== */ void Sys_CryptoRandomBytes( byte *string, int len ) { if ( !Sys_RandomBytes( string, len ) ) Com_Error( ERR_FATAL, "Sys_CryptoRandomBytes: error generating random data" ); } #define MEM_THRESHOLD 96*1024*1024 /* ================== Sys_LowPhysicalMemory ================== */ bool Sys_LowPhysicalMemory( void ) { MEMORYSTATUS stat; GlobalMemoryStatus (&stat); return (stat.dwTotalPhys <= MEM_THRESHOLD) ? true : false; } /* ============== 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 ) { return fopen( ospath, mode ); } /* ============== Sys_Mkdir ============== */ bool Sys_Mkdir( const char *path ) { if( !CreateDirectory( path, NULL ) ) { if( GetLastError( ) != ERROR_ALREADY_EXISTS ) return false; } return true; } /* ================== Sys_Mkfifo Noop on windows because named pipes do not function the same way ================== */ FILE *Sys_Mkfifo( const char *ospath ) { return NULL; } /* ============== Sys_OpenWithDefault Opens a path with the default application ============== */ bool Sys_OpenWithDefault( const char *path ) { HINSTANCE hInst; uint64_t err; Com_Printf( S_COLOR_WHITE "Sys_OpenWithDefault: opening %s .....\n", path ); hInst = ShellExecute(0, "open", path, 0, 0 , SW_SHOWNORMAL ); err = (uint64_t)hInst; if( err > 32 ) { //success return true; } // failure switch ( err ) { case 0: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The operating system is out of memory or resources.\n", "warning" ); break; case ERROR_FILE_NOT_FOUND: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The specified file was not found.\n", "warning" ); break; case ERROR_PATH_NOT_FOUND: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The specified path was not found.\n", "warning" ); break; case ERROR_BAD_FORMAT: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The .exe file is invalid (non-Win32 .exe or error in .exe image).\n", "warning" ); break; case SE_ERR_ACCESSDENIED: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The operating system denied access to the specified file.\n", "warning" ); break; case SE_ERR_ASSOCINCOMPLETE: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The file name association is incomplete or invalid.\n", "warning" ); break; case SE_ERR_DDEBUSY: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The DDE transaction could not be completed because other DDE transactions were being processed.\n", "warning" ); break; case SE_ERR_DDEFAIL: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The DDE transaction failed.\n", "warning" ); break; case SE_ERR_DDETIMEOUT: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The DDE transaction could not be completed because the request timed out.\n", "warning" ); break; case SE_ERR_DLLNOTFOUND: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: The specified DLL was not found.\n", "warning" ); break; case SE_ERR_NOASSOC: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable.\n", "warning" ); break; case SE_ERR_OOM: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: There was not enough memory to complete the operation.\n", "warning" ); break; case SE_ERR_SHARE: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: A sharing violation occurred.\n", "warning" ); break; default: Sys_Dialog( DT_WARNING, "Sys_OpenWithDefault: Failed to open path.\n", "warning" ); break; } return false; } /* ============== 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, const char *subdirs, const 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 (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, false )) continue; list[ *numfiles ] = CopyString( filename ); (*numfiles)++; } while ( _findnext (findhandle, &findinfo) != -1 ); _findclose (findhandle); } /* ============== strgtr ============== */ static bool strgtr(const char *s0, const char *s1) { int l0, l1, i; l0 = strlen(s0); l1 = strlen(s1); if (l1 s0[i]) { return true; } if (s1[i] < s0[i]) { return false; } } return false; } /* ============== Sys_ListFiles ============== */ char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, bool 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 = (char**)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } 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 = (char**)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 ) { 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 ============== */ bool Sys_PIDIsRunning( int pid ) { DWORD processes[ 1024 ]; DWORD numBytes, numProcesses; int i; if( !EnumProcesses( processes, sizeof( processes ), &numBytes ) ) return false; // Assume it's not running numProcesses = numBytes / sizeof( DWORD ); // Search for the pid for( i = 0; i < numProcesses; i++ ) { if( processes[ i ] == pid ) return true; } return false; } /* ================= Sys_DllExtension Check if filename should be allowed to be loaded as a DLL. ================= */ bool Sys_DllExtension( const char *name ) { return COM_CompareExtension( name, DLL_EXT ); }