mirror of
https://github.com/Q3Rally-Team/rallyunlimited-engine.git
synced 2024-11-29 07:22:15 +00:00
616 lines
12 KiB
C
616 lines
12 KiB
C
/*
|
|
===========================================================================
|
|
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 <sched.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/time.h>
|
|
#include <pwd.h>
|
|
#include <dlfcn.h>
|
|
#include <libgen.h>
|
|
|
|
#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
|