/* =========================================================================== 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 =========================================================================== */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "../qcommon/q_shared.h" #include "../qcommon/qcommon.h" //============================================================================= /* ================ Sys_Milliseconds ================ */ /* base time in seconds, that's our origin timeval:tv_sec is an int: assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038 using unsigned long data type to work right with Sys_XTimeToSysTime */ unsigned long sys_timeBase = 0; /* current time in ms, using sys_timeBase as origin NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch 0x7fffffff ms - ~24 days although timeval:tv_usec is an int, I'm not sure whether it is actually used as an unsigned int (which would affect the wrap period) */ int Sys_Milliseconds( void ) { struct timeval tp; int curtime; gettimeofday( &tp, NULL ); if ( !sys_timeBase ) { sys_timeBase = tp.tv_sec; return tp.tv_usec/1000; } curtime = (tp.tv_sec - sys_timeBase) * 1000 + tp.tv_usec / 1000; return curtime; } char *strlwr( char *s ) { if ( s==NULL ) { // bk001204 - paranoia assert(0); return s; } while (*s) { *s = tolower(*s); s++; } return s; // bk001204 - duh } /* ================== Sys_RandomBytes ================== */ qboolean Sys_RandomBytes( byte *string, int len ) { FILE *fp; fp = fopen( "/dev/urandom", "r" ); if( !fp ) return qfalse; setvbuf( fp, NULL, _IONBF, 0 ); // don't buffer reads from /dev/urandom if ( fread( string, sizeof( byte ), len, fp ) != len ) { fclose( fp ); return qfalse; } fclose( fp ); return qtrue; } //============================================ // bk001129 - new in 1.26 void Sys_ListFilteredFiles( const char *basedir, const char *subdirs, const char *filter, char **list, int *numfiles ) { char search[MAX_OSPATH*2+1]; char newsubdirs[MAX_OSPATH*2]; char filename[MAX_OSPATH*2]; DIR *fdir; struct dirent *d; struct stat st; if ( *numfiles >= MAX_FOUND_FILES - 1 ) { return; } if ( *subdirs ) { Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs ); } else { Com_sprintf( search, sizeof(search), "%s", basedir ); } if ((fdir = opendir(search)) == NULL) { return; } while ((d = readdir(fdir)) != NULL) { Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name); if (stat(filename, &st) == -1) continue; if (st.st_mode & S_IFDIR) { if ( !Q_streq( d->d_name, "." ) && !Q_streq( d->d_name, ".." ) ) { if ( *subdirs) { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name); } else { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name); } Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); } } if ( *numfiles >= MAX_FOUND_FILES - 1 ) { break; } Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name ); if ( !Com_FilterPath( filter, filename ) ) continue; list[ *numfiles ] = FS_CopyString( filename ); (*numfiles)++; } closedir(fdir); } // bk001129 - in 1.17 this used to be // char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles, qboolean wantsubs ) char **Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qboolean wantsubs ) { struct dirent *d; DIR *fdir; qboolean dironly = wantsubs; char search[MAX_OSPATH*2+MAX_QPATH+1]; int nfiles; int extLen; int length; char **listCopy; char *list[MAX_FOUND_FILES]; int i; struct stat st; qboolean hasPatterns; const char *x; if ( filter ) { nfiles = 0; Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); list[ nfiles ] = NULL; *numfiles = nfiles; if ( !nfiles ) return NULL; listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( listCopy[0] ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } if ( !extension) extension = ""; if ( extension[0] == '/' && extension[1] == 0 ) { extension = ""; dironly = qtrue; } if ((fdir = opendir(directory)) == NULL) { *numfiles = 0; return NULL; } extLen = (int)strlen( extension ); hasPatterns = Com_HasPatterns( extension ); if ( hasPatterns && extension[0] == '.' && extension[1] != '\0' ) { extension++; } // search nfiles = 0; while ((d = readdir(fdir)) != NULL) { if ( nfiles == MAX_FOUND_FILES - 1 ) break; Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name); if (stat(search, &st) == -1) continue; if ((dironly && !(st.st_mode & S_IFDIR)) || (!dironly && (st.st_mode & S_IFDIR))) continue; if ( *extension ) { if ( hasPatterns ) { x = strrchr( d->d_name, '.' ); if ( !x || !Com_FilterExt( extension, x+1 ) ) { continue; } } else { length = (int) strlen( d->d_name ); if ( length < extLen || Q_stricmp( d->d_name + length - extLen, extension ) ) { continue; } } } list[ nfiles ] = FS_CopyString( d->d_name ); nfiles++; } list[ nfiles ] = NULL; closedir( fdir ); // return a copy of the list *numfiles = nfiles; if ( !nfiles ) { return NULL; } listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( listCopy[0] ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; Com_SortFileList( listCopy, nfiles, extension[0] != '\0' ); return listCopy; } /* ================= Sys_FreeFileList ================= */ void Sys_FreeFileList( char **list ) { int i; if ( !list ) { return; } for ( i = 0 ; list[i] ; i++ ) { Z_Free( list[i] ); } Z_Free( list ); } /* ============= Sys_GetFileStats ============= */ qboolean Sys_GetFileStats( const char *filename, fileOffset_t *size, fileTime_t *mtime, fileTime_t *ctime ) { struct stat s; if ( stat( filename, &s ) == 0 ) { *size = (fileOffset_t)s.st_size; *mtime = (fileTime_t)s.st_mtime; *ctime = (fileTime_t)s.st_ctime; return qtrue; } else { *size = 0; *mtime = *ctime = 0; return qfalse; } } /* ================= Sys_Mkdir ================= */ void Sys_Mkdir( const char *path ) { mkdir( path, 0750 ); } /* ================= Sys_FOpen ================= */ FILE *Sys_FOpen( const char *ospath, const char *mode ) { struct stat buf; // check if path exists and it is not a directory if ( stat( ospath, &buf ) == 0 && S_ISDIR( buf.st_mode ) ) return NULL; return fopen( ospath, mode ); } /* ============== Sys_ResetReadOnlyAttribute ============== */ qboolean Sys_ResetReadOnlyAttribute( const char *ospath ) { return qfalse; } /* ================= Sys_Pwd ================= */ const char *Sys_Pwd( void ) { static char pwd[ MAX_OSPATH ]; if ( pwd[0] ) return pwd; // more reliable, linux-specific if ( readlink( "/proc/self/exe", pwd, sizeof( pwd ) - 1 ) != -1 ) { pwd[ sizeof( pwd ) - 1 ] = '\0'; dirname( pwd ); return pwd; } if ( !getcwd( pwd, sizeof( pwd ) ) ) { pwd[0] = '\0'; } return pwd; } /* ================= Sys_DefaultBasePath ================= */ const char *Sys_DefaultBasePath( void ) { return Sys_Pwd(); } /* ================= Sys_DefaultHomePath ================= */ const char *Sys_DefaultHomePath( void ) { // Used to determine where to store user-specific files static char homePath[ MAX_OSPATH ]; const char *p; if ( *homePath ) return homePath; if ( (p = getenv("HOME")) != NULL ) { Q_strncpyz( homePath, p, sizeof( homePath ) ); #ifdef MACOS_X Q_strcat( homePath, sizeof(homePath), "/Library/Application Support/Quake3" ); #else Q_strcat( homePath, sizeof( homePath ), "/.q3a" ); #endif if ( mkdir( homePath, 0750 ) ) { if ( errno != EEXIST ) Sys_Error( "Unable to create directory \"%s\", error is %s(%d)\n", homePath, strerror( errno ), errno ); } return homePath; } return ""; // assume current dir } /* ================ Sys_SteamPath ================ */ const char *Sys_SteamPath( void ) { static char steamPath[ MAX_OSPATH ]; // Disabled since Steam doesn't let you install Quake 3 on Mac/Linux #if 0 const char *p; if( ( p = getenv( "HOME" ) ) != NULL ) { #ifdef MACOS_X char *steamPathEnd = "/Library/Application Support/Steam/SteamApps/common/" STEAMPATH_NAME; #else char *steamPathEnd = "/.steam/steam/SteamApps/common/" STEAMPATH_NAME; #endif Com_sprintf(steamPath, sizeof(steamPath), "%s%s", p, steamPathEnd); } #endif return steamPath; } /* ================= Sys_ShowConsole ================= */ void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) { // not implemented } /* ======================================================================== LOAD/UNLOAD DLL ======================================================================== */ static int dll_err_count = 0; /* ================= Sys_LoadLibrary ================= */ void *Sys_LoadLibrary( const char *name ) { const char *ext; void *handle; if ( FS_AllowedExtension( name, qfalse, &ext ) ) { Com_Error( ERR_FATAL, "Sys_LoadLibrary: Unable to load library with '%s' extension", ext ); } handle = dlopen( name, RTLD_NOW ); return handle; } /* ================= Sys_UnloadLibrary ================= */ void Sys_UnloadLibrary( void *handle ) { if ( handle != NULL ) dlclose( handle ); } /* ================= Sys_LoadFunction ================= */ void *Sys_LoadFunction( void *handle, const char *name ) { const char *error; char buf[1024]; void *symbol; size_t nlen; if ( handle == NULL || name == NULL || *name == '\0' ) { dll_err_count++; return NULL; } dlerror(); /* clear old error state */ symbol = dlsym( handle, name ); error = dlerror(); if ( error != NULL ) { nlen = strlen( name ) + 1; if ( nlen >= sizeof( buf ) ) return NULL; buf[0] = '_'; strcpy( buf+1, name ); dlerror(); /* clear old error state */ symbol = dlsym( handle, buf ); } if ( !symbol ) dll_err_count++; return symbol; } /* ================= Sys_LoadFunctionErrors ================= */ int Sys_LoadFunctionErrors( void ) { int result = dll_err_count; dll_err_count = 0; return result; } /* ================= Sys_SetAffinityMask ================= */ #ifdef USE_AFFINITY_MASK void Sys_SetAffinityMask( int mask ) { static qboolean inited = qfalse; static cpu_set_t old_set; cpu_set_t set; int cpu; if ( !inited ) { if ( sched_getaffinity( getpid(), sizeof( old_set ), &old_set ) == 0 ) { inited = qtrue; } else { Com_Printf( S_COLOR_YELLOW "sched_getaffinity() error.\n" ); return; } } if ( mask == 0 ) // restore default set { memcpy( &set, &old_set, sizeof( set ) ); for ( cpu = 0; cpu < sizeof( mask ) * 8; cpu++ ) { if ( CPU_ISSET( cpu, &set ) ) mask |= (1 << cpu); } } else { CPU_ZERO( &set ); for ( cpu = 0; cpu < sizeof( mask ) * 8; cpu++ ) { if ( mask & (1 << cpu) ) CPU_SET( cpu, &set ); } } if ( sched_setaffinity( getpid(), sizeof( set ), &set ) == 0 ) { Com_Printf( "setting CPU affinity mask to %i\n", mask ); } else { Com_Printf( S_COLOR_YELLOW "error setting CPU affinity mask %i\n", mask ); } } #endif // USE_AFFINITY_MASK