/*
===========================================================================
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 );
}