rallyunlimited-engine/code/unix/unix_shared.c
2024-02-02 19:46:17 +03:00

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