/*
===========================================================================
Return to Castle Wolfenstein multiplayer GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
RTCW MP 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 3 of the License, or
(at your option) any later version.
RTCW MP 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 RTCW MP Source Code. If not, see .
In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __linux__ // rb010123
#include
#endif
#include
#ifdef __linux__
#include // bk001213 - force dumps on divide by zero
#endif
// FIXME TTimo should we gard this? most *nix system should comply?
#include
#include "../game/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../renderer/tr_public.h"
#include "linux_local.h" // bk001204
// Structure containing functions exported from refresh DLL
refexport_t re;
unsigned sys_frame_time;
uid_t saved_euid;
qboolean stdin_active = qtrue;
// =============================================================
// tty console variables
// =============================================================
// enable/disabled tty input mode
// NOTE TTimo this is used during startup, cannot be changed during run
static cvar_t *ttycon = NULL;
// general flag to tell about tty console mode
static qboolean ttycon_on = qfalse;
// when printing general stuff to stdout stderr (Sys_Printf)
// we need to disable the tty console stuff
// this increments to we can recursively disable
static int ttycon_hide = 0;
// some key codes that the terminal may be using
// TTimo NOTE: I'm not sure how relevant this is
static int tty_erase;
static int tty_eof;
static struct termios tty_tc;
static field_t tty_con;
// history
// NOTE TTimo this is a bit duplicate of the graphical console history
// but it's safer and faster to write our own here
#define TTY_HISTORY 32
static field_t ttyEditLines[TTY_HISTORY];
static int hist_current = -1, hist_count = 0;
// =======================================================================
// General routines
// =======================================================================
// bk001207
#define MEM_THRESHOLD 96 * 1024 * 1024
/*
==================
Sys_LowPhysicalMemory()
==================
*/
qboolean Sys_LowPhysicalMemory() {
//MEMORYSTATUS stat;
//GlobalMemoryStatus (&stat);
//return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
return qfalse; // bk001207 - FIXME
}
void Sys_BeginProfiling( void ) {
}
/*
=================
Sys_In_Restart_f
Restart the input subsystem
=================
*/
void Sys_In_Restart_f( void ) {
IN_Shutdown();
IN_Init();
}
// =============================================================
// tty console routines
// NOTE: if the user is editing a line when something gets printed to the early console then it won't look good
// so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output
// =============================================================
// flush stdin, I suspect some terminals are sending a LOT of shit
// FIXME TTimo relevant?
void tty_FlushIn() {
char key;
while ( read( 0, &key, 1 ) != -1 ) ;
}
// do a backspace
// TTimo NOTE: it seems on some terminals just sending '\b' is not enough
// so for now, in any case we send "\b \b" .. yeah well ..
// (there may be a way to find out if '\b' alone would work though)
void tty_Back() {
char key;
key = '\b';
write( 1, &key, 1 );
key = ' ';
write( 1, &key, 1 );
key = '\b';
write( 1, &key, 1 );
}
// clear the display of the line currently edited
// bring cursor back to beginning of line
void tty_Hide() {
int i;
assert( ttycon_on );
if ( ttycon_hide ) {
ttycon_hide++;
return;
}
if ( tty_con.cursor > 0 ) {
for ( i = 0; i < tty_con.cursor; i++ )
{
tty_Back();
}
}
ttycon_hide++;
}
// show the current line
// FIXME TTimo need to position the cursor if needed??
void tty_Show() {
int i;
assert( ttycon_on );
assert( ttycon_hide > 0 );
ttycon_hide--;
if ( ttycon_hide == 0 ) {
if ( tty_con.cursor ) {
for ( i = 0; i < tty_con.cursor; i++ )
{
write( 1, tty_con.buffer + i, 1 );
}
}
}
}
// never exit without calling this, or your terminal will be left in a pretty bad state
void Sys_ConsoleInputShutdown() {
if ( ttycon_on ) {
Com_Printf( "Shutdown tty console\n" );
tcsetattr( 0, TCSADRAIN, &tty_tc );
}
}
void Hist_Add( field_t *field ) {
int i;
assert( hist_count <= TTY_HISTORY );
assert( hist_count >= 0 );
assert( hist_current >= -1 );
assert( hist_current <= hist_count );
// make some room
for ( i = TTY_HISTORY - 1; i > 0; i-- )
{
ttyEditLines[i] = ttyEditLines[i - 1];
}
ttyEditLines[0] = *field;
if ( hist_count < TTY_HISTORY ) {
hist_count++;
}
hist_current = -1; // re-init
}
field_t *Hist_Prev() {
int hist_prev;
assert( hist_count <= TTY_HISTORY );
assert( hist_count >= 0 );
assert( hist_current >= -1 );
assert( hist_current <= hist_count );
hist_prev = hist_current + 1;
if ( hist_prev >= hist_count ) {
return NULL;
}
hist_current++;
return &( ttyEditLines[hist_current] );
}
field_t *Hist_Next() {
assert( hist_count <= TTY_HISTORY );
assert( hist_count >= 0 );
assert( hist_current >= -1 );
assert( hist_current <= hist_count );
if ( hist_current >= 0 ) {
hist_current--;
}
if ( hist_current == -1 ) {
return NULL;
}
return &( ttyEditLines[hist_current] );
}
// =============================================================
// general sys routines
// =============================================================
#define MAX_CMD 1024
static char exit_cmdline[MAX_CMD] = "";
void Sys_DoStartProcess( char *cmdline );
// single exit point (regular exit or in case of signal fault)
void Sys_Exit( int ex ) {
Sys_ConsoleInputShutdown();
// we may be exiting to spawn another process
if ( exit_cmdline[0] != '\0' ) {
// possible race conditions?
// buggy kernels / buggy GL driver, I don't know for sure
// but it's safer to wait an eternity before and after the fork
sleep( 1 );
Sys_DoStartProcess( exit_cmdline );
sleep( 1 );
}
#ifdef NDEBUG // regular behavior
// We can't do this
// as long as GL DLL's keep installing with atexit...
//exit(ex);
_exit( ex );
#else
// Give me a backtrace on error exits.
assert( ex == 0 );
exit( ex );
#endif
}
void Sys_Quit( void ) {
CL_Shutdown();
fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) & ~FNDELAY );
Sys_Exit( 0 );
}
void Sys_Init( void ) {
Cmd_AddCommand( "in_restart", Sys_In_Restart_f );
#if defined __linux__
#if defined __i386__
Cvar_Set( "arch", "linux i386" );
#elif defined __alpha__
Cvar_Set( "arch", "linux alpha" );
#elif defined __sparc__
Cvar_Set( "arch", "linux sparc" );
#elif defined __FreeBSD__
#if defined __i386__ // FreeBSD
Cvar_Set( "arch", "freebsd i386" );
#elif defined __alpha__
Cvar_Set( "arch", "freebsd alpha" );
#else
Cvar_Set( "arch", "freebsd unknown" );
#endif // FreeBSD
#else
Cvar_Set( "arch", "linux unknown" );
#endif
#elif defined __sun__
#if defined __i386__
Cvar_Set( "arch", "solaris x86" );
#elif defined __sparc__
Cvar_Set( "arch", "solaris sparc" );
#else
Cvar_Set( "arch", "solaris unknown" );
#endif
#elif defined __sgi__
#if defined __mips__
Cvar_Set( "arch", "sgi mips" );
#else
Cvar_Set( "arch", "sgi unknown" );
#endif
#else
Cvar_Set( "arch", "unknown" );
#endif
Cvar_Set( "username", Sys_GetCurrentUser() );
IN_Init();
}
void Sys_Error( const char *error, ... ) {
va_list argptr;
char string[1024];
// change stdin to non blocking
// NOTE TTimo not sure how well that goes with tty console mode
fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) & ~FNDELAY );
// don't bother do a show on this one heh
if ( ttycon_on ) {
tty_Hide();
}
CL_Shutdown();
va_start( argptr,error );
vsprintf( string,error,argptr );
va_end( argptr );
fprintf( stderr, "Sys_Error: %s\n", string );
Sys_Exit( 1 ); // bk010104 - use single exit point.
}
void Sys_Warn( char *warning, ... ) {
va_list argptr;
char string[1024];
va_start( argptr,warning );
vsprintf( string,warning,argptr );
va_end( argptr );
if ( ttycon_on ) {
tty_Hide();
}
fprintf( stderr, "Warning: %s", string );
if ( ttycon_on ) {
tty_Show();
}
}
/*
============
Sys_FileTime
returns -1 if not present
============
*/
int Sys_FileTime( char *path ) {
struct stat buf;
if ( stat( path,&buf ) == -1 ) {
return -1;
}
return buf.st_mtime;
}
// NOTE TTimo: huh?
void floating_point_exception_handler( int whatever ) {
signal( SIGFPE, floating_point_exception_handler );
}
// initialize the console input (tty mode if wanted and possible)
void Sys_ConsoleInputInit() {
struct termios tc;
// TTimo
// show_bug.cgi?id=390
// ttycon 0 or 1, if the process is backgrounded (running non interactively)
// then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
signal( SIGTTIN, SIG_IGN );
signal( SIGTTOU, SIG_IGN );
// FIXME TTimo initialize this in Sys_Init or something?
ttycon = Cvar_Get( "ttycon", "1", 0 );
if ( ttycon && ttycon->value ) {
if ( isatty( STDIN_FILENO ) != 1 ) {
Com_Printf( "stdin is not a tty, tty console mode failed\n" );
Cvar_Set( "ttycon", "0" );
ttycon_on = qfalse;
return;
}
Com_Printf( "Started tty console (use +set ttycon 0 to disable)\n" );
Field_Clear( &tty_con );
tcgetattr( 0, &tty_tc );
tty_erase = tc.c_cc[VERASE];
tty_eof = tc.c_cc[VEOF];
tc = tty_tc;
/*
ECHO: don't echo input characters
ICANON: enable canonical mode. This enables the special
characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
STATUS, and WERASE, and buffers by lines.
ISIG: when any of the characters INTR, QUIT, SUSP, or
DSUSP are received, generate the corresponding sig
nal
*/
tc.c_lflag &= ~( ECHO | ICANON );
/*
ISTRIP strip off bit 8
INPCK enable input parity checking
*/
tc.c_iflag &= ~( ISTRIP | INPCK );
tc.c_cc[VMIN] = 1;
tc.c_cc[VTIME] = 0;
tcsetattr( 0, TCSADRAIN, &tc );
ttycon_on = qtrue;
} else {
ttycon_on = qfalse;
}
}
char *Sys_ConsoleInput( void ) {
// we use this when sending back commands
static char text[256];
int i;
int avail;
char key;
field_t *history;
if ( ttycon && ttycon->value ) {
avail = read( 0, &key, 1 );
if ( avail != -1 ) {
// we have something
// backspace?
// NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
if ( ( key == tty_erase ) || ( key == 127 ) || ( key == 8 ) ) {
if ( tty_con.cursor > 0 ) {
tty_con.cursor--;
tty_con.buffer[tty_con.cursor] = '\0';
tty_Back();
}
return NULL;
}
// check if this is a control char
if ( ( key ) && ( key ) < ' ' ) {
if ( key == '\n' ) {
// push it in history
Hist_Add( &tty_con );
strcpy( text, tty_con.buffer );
Field_Clear( &tty_con );
key = '\n';
write( 1, &key, 1 );
return text;
}
if ( key == '\t' ) {
tty_Hide();
Field_CompleteCommand( &tty_con );
// Field_CompleteCommand does weird things to the string, do a cleanup
// it adds a '\' at the beginning of the string
// cursor doesn't reflect actual length of the string that's sent back
tty_con.cursor = strlen( tty_con.buffer );
if ( tty_con.cursor > 0 ) {
if ( tty_con.buffer[0] == '\\' ) {
for ( i = 0; i <= tty_con.cursor; i++ )
{
tty_con.buffer[i] = tty_con.buffer[i + 1];
}
tty_con.cursor--;
}
}
tty_Show();
return NULL;
}
avail = read( 0, &key, 1 );
if ( avail != -1 ) {
// VT 100 keys
if ( key == '[' || key == 'O' ) {
avail = read( 0, &key, 1 );
if ( avail != -1 ) {
switch ( key )
{
case 'A':
history = Hist_Prev();
if ( history ) {
tty_Hide();
tty_con = *history;
tty_Show();
}
tty_FlushIn();
return NULL;
break;
case 'B':
history = Hist_Next();
tty_Hide();
if ( history ) {
tty_con = *history;
} else
{
Field_Clear( &tty_con );
}
tty_Show();
tty_FlushIn();
return NULL;
break;
case 'C':
return NULL;
case 'D':
return NULL;
}
}
}
}
Com_DPrintf( "droping ISCTL sequence: %d, tty_erase: %d\n", key, tty_erase );
tty_FlushIn();
return NULL;
}
// push regular character
tty_con.buffer[tty_con.cursor] = key;
tty_con.cursor++;
// print the current line (this is differential)
write( 1, &key, 1 );
}
return NULL;
} else
{
int len;
fd_set fdset;
struct timeval timeout;
if ( !com_dedicated || !com_dedicated->value ) {
return NULL;
}
if ( !stdin_active ) {
return NULL;
}
FD_ZERO( &fdset );
FD_SET( 0, &fdset ); // stdin
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if ( select( 1, &fdset, NULL, NULL, &timeout ) == -1 || !FD_ISSET( 0, &fdset ) ) {
return NULL;
}
len = read( 0, text, sizeof( text ) );
if ( len == 0 ) { // eof!
stdin_active = qfalse;
return NULL;
}
if ( len < 1 ) {
return NULL;
}
text[len - 1] = 0; // rip off the /n and terminate
return text;
}
}
/*****************************************************************************/
/*
=================
Sys_UnloadDll
=================
*/
void Sys_UnloadDll( void *dllHandle ) {
// bk001206 - verbose error reporting
const char* err; // rb010123 - now const
if ( !dllHandle ) {
Com_Printf( "Sys_UnloadDll(NULL)\n" );
return;
}
dlclose( dllHandle );
err = dlerror();
if ( err != NULL ) {
Com_Printf( "Sys_UnloadGame failed on dlclose: \"%s\"!\n", err );
}
}
/*
=================
Sys_LoadDll
Used to load a development dll instead of a virtual machine
NOTE: DLL checksuming / DLL pk3 and search paths
if we are connecting to an sv_pure server, the .so will be extracted
from a pk3 and placed in the homepath (i.e. the write area)
since DLLs are looked for in several places, it is important that
we are sure we are going to load the DLL that was just extracted
for convenience, debug builds are searching in current directory
prior to scanning homepath and basepath. this allows tu run stuff
'out of the box' with a +set fs_basepath after compile.
in release we start searching directly in fs_homepath then fs_basepath
(that makes sure we are going to load the DLL we extracted from pk3)
this should not be a problem for mod makers, since they always copy their
DLL to the main installation prior to run (sv_pure 1 would have a tendency
to kill their compiled DLL with extracted ones though :-( )
NOTE T.Ray:
fqpath param added 2/15/02 by T.Ray - Sys_LoadDll is only called in vm.c at this time
fqpath will be empty if dll not loaded, otherwise will hold fully qualified path of dll module loaded
fqpath buffersize must be at least MAX_QPATH+1 bytes long
=================
*/
/*
=================
Try_CopyDLLForMod
little utility function for media-only mods
tries to copy a reference DLL to the mod's fs_homepath
return false if failed, or if we are not in a mod situation
returns true if successful, *p_fn is set to the correct path
this is used when we are loading a mod and realize we don't have the DLL in the standard path
=================
*/
qboolean CopyDLLForMod( char **p_fn, const char* gamedir, const char *pwdpath, const char *homepath, const char *basepath, const char *fname ) {
char *fn = *p_fn;
// this may be a media only mod, so next we need to search in the basegame
if ( strlen( gamedir ) && Q_stricmp( gamedir, BASEGAME ) ) {
// walk for a base file
// NOTE TTimo: we don't attempt to validate version-wise, it could be a problem
// (acceptable tradeoff I say, should not cause major issues)
#ifndef NDEBUG
fn = FS_BuildOSPath( pwdpath, BASEGAME, fname );
if ( access( fn, R_OK ) == -1 ) {
#endif
fn = FS_BuildOSPath( homepath, BASEGAME, fname );
if ( access( fn, R_OK ) == -1 ) {
fn = FS_BuildOSPath( basepath, BASEGAME, fname );
if ( access( fn, R_OK ) == -1 ) {
return qfalse; // this one is hopeless
}
}
#ifndef NDEBUG
}
#endif
// basefile found, we copy to homepath in all cases
// fortunately FS_BuildOSPath does a flip flop, we have 'from path' in fn and 'to path' in *p_fn
*p_fn = FS_BuildOSPath( homepath, gamedir, fname );
// copy to destination
FS_CopyFile( fn, *p_fn );
if ( access( *p_fn, R_OK ) == -1 ) { // could do with FS_CopyFile returning a boolean actually
Com_DPrintf( "Copy operation failed\n" );
return qfalse;
}
return qtrue;
} else
{
return qfalse;
}
}
// TTimo - Wolf MP specific, adding .mp. to shared objects
char* Sys_GetDLLName( const char *name ) {
#if defined __i386__
return va( "%s.mp.i386.so", name );
#elif defined __powerpc__
return va( "%s.mp.ppc.so", name );
#elif defined __axp__
return va( "%s.mp.axp.so", name );
#elif defined __mips__
return va( "%s.mp.mips.so", name );
#else
#error Unknown arch
#endif
}
void *Sys_LoadDll( const char *name, char *fqpath,
int( **entryPoint ) ( int, ... ),
int ( *systemcalls )( int, ... ) ) {
void *libHandle;
void ( *dllEntry )( int ( *syscallptr )( int, ... ) );
char fname[MAX_OSPATH];
char *pwdpath;
char *homepath;
char *basepath;
char *gamedir;
char *fn;
const char* err = NULL; // bk001206 // rb0101023 - now const
#if !defined( DEDICATED )
char *cvar_name = NULL;
#endif
*fqpath = 0 ; // added 2/15/02 by T.Ray
// bk001206 - let's have some paranoia
assert( name );
Q_strncpyz( fname, Sys_GetDLLName( name ), sizeof( fname ) );
// bk001129 - was RTLD_LAZY
#define Q_RTLD RTLD_NOW
pwdpath = Sys_Cwd();
homepath = Cvar_VariableString( "fs_homepath" );
basepath = Cvar_VariableString( "fs_basepath" );
gamedir = Cvar_VariableString( "fs_game" );
// this is relevant to client only
// this code is in for full client hosting a game, but it's not affected by it
#if !defined( DEDICATED )
// do a first scan to identify what we are going to dlopen
// we need to pass this to FS_ExtractFromPakFile so that it checksums the right file
// NOTE: if something fails (not found, or file operation failed), we will ERR_FATAL (in the checksum itself, we only ERR_DROP)
#ifndef NDEBUG
fn = FS_BuildOSPath( pwdpath, gamedir, fname );
if ( access( fn, R_OK ) == -1 ) {
#endif
fn = FS_BuildOSPath( homepath, gamedir, fname );
if ( access( fn, R_OK ) == 0 ) {
// there is a .so in fs_homepath, but is it a valid one version-wise?
// we use a persistent variable in config.cfg to make sure
// this is set in FS_CL_ExtractFromPakFile when the file is extracted
cvar_t *lastVersion;
cvar_name = va( "cl_lastVersion%s", name );
lastVersion = Cvar_Get( cvar_name, "(uninitialized)", CVAR_ARCHIVE );
if ( Q_stricmp( Cvar_VariableString( "version" ), lastVersion->string ) ) {
Com_DPrintf( "clearing non matching version of %s .so: %s\n", name, fn );
if ( remove( fn ) == -1 ) {
Com_Error( ERR_FATAL, "failed to remove outdated '%s' file:\n\"%s\"\n", fn, strerror( errno ) );
}
// we cancelled fs_homepath, go work on basepath now
fn = FS_BuildOSPath( basepath, gamedir, fname );
if ( access( fn, R_OK ) == -1 ) {
// we may be dealing with a media-only mod, check wether we can find 'reference' DLLs and copy them over
if ( !CopyDLLForMod( &fn, gamedir, pwdpath, homepath, basepath, fname ) ) {
Com_Error( ERR_FATAL, "Sys_LoadDll(%s) failed, no corresponding .so file found in fs_homepath or fs_basepath\n", name );
}
}
}
// the .so in fs_homepath is valid version-wise .. FS_CL_ExtractFromPakFile will have to decide wether it's valid pk3-wise later
} else {
fn = FS_BuildOSPath( basepath, gamedir, fname );
if ( access( fn, R_OK ) == -1 ) {
// we may be dealing with a media-only mod, check wether we can find 'reference' DLLs and copy them over
if ( !CopyDLLForMod( &fn, gamedir, pwdpath, homepath, basepath, fname ) ) {
Com_Error( ERR_FATAL, "Sys_LoadDll(%s) failed, no corresponding .so file found in fs_homepath or fs_basepath\n", name );
}
}
}
#ifndef NDEBUG
}
#endif
// NERVE - SMF - extract dlls from pak file for security
// we have to handle the game dll a little differently
// NOTE #2: we may have found a file in fs_basepath, and if the checksum is wrong, FS_Extract will write in fs_homepath
// won't be a problem since we start a brand new scan next
if ( cl_connectedToPureServer && Q_strncmp( name, "qagame", 6 ) ) {
if ( !FS_CL_ExtractFromPakFile( fn, gamedir, fname, cvar_name ) ) {
Com_Error( ERR_DROP, "Game code(%s) failed Pure Server check", fname );
}
}
#endif
#ifndef NDEBUG
// current directory
// NOTE: only for debug build, see Sys_LoadDll discussion
fn = FS_BuildOSPath( pwdpath, gamedir, fname );
Com_Printf( "Sys_LoadDll(%s)... ", fn );
libHandle = dlopen( fn, Q_RTLD );
if ( !libHandle ) {
Com_Printf( "\nSys_LoadDll(%s) failed:\n\"%s\"\n", fn, dlerror() );
#endif
// homepath
fn = FS_BuildOSPath( homepath, gamedir, fname );
Com_Printf( "Sys_LoadDll(%s)... ", fn );
libHandle = dlopen( fn, Q_RTLD );
if ( !libHandle ) {
Com_Printf( "\nSys_LoadDll(%s) failed:\n\"%s\"\n", fn, dlerror() );
// basepath
fn = FS_BuildOSPath( basepath, gamedir, fname );
Com_Printf( "Sys_LoadDll(%s)... ", fn );
libHandle = dlopen( fn, Q_RTLD );
if ( !libHandle ) {
// report any problem
Com_Printf( "\nSys_LoadDll(%s) failed:\n\"%s\"\n", fn, dlerror() );
} else {
Com_Printf( "ok\n" );
}
// not found, bail
if ( !libHandle ) {
#ifndef NDEBUG // in debug abort on failure
Com_Error( ERR_FATAL, "Sys_LoadDll(%s) failed dlopen() completely!\n", name );
#else
Com_Printf( "Sys_LoadDll(%s) failed dlopen() completely!\n", name );
#endif
return NULL;
}
} else {
Com_Printf( "ok\n" );
}
#ifndef NDEBUG
} else {
Com_Printf( "ok\n" );
}
#endif
Q_strncpyz( fqpath, fn, MAX_QPATH ) ; // added 2/15/02 by T.Ray
dllEntry = dlsym( libHandle, "dllEntry" );
*entryPoint = dlsym( libHandle, "vmMain" );
if ( !*entryPoint || !dllEntry ) {
err = dlerror();
#ifndef NDEBUG // in debug abort on failure
Com_Error( ERR_FATAL, "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
#else
Com_Printf( "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
#endif
dlclose( libHandle );
err = dlerror();
if ( err != NULL ) {
Com_Printf( "Sys_LoadDll(%s) failed dlcose:\n\"%s\"\n", name, err );
}
return NULL;
}
Com_Printf( "Sys_LoadDll(%s) found **vmMain** at %p \n", name, *entryPoint );
dllEntry( systemcalls );
Com_Printf( "Sys_LoadDll(%s) succeeded!\n", name );
return libHandle;
}
/*
========================================================================
BACKGROUND FILE STREAMING
========================================================================
*/
#if 1
void Sys_InitStreamThread( void ) {
}
void Sys_ShutdownStreamThread( void ) {
}
void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
}
void Sys_EndStreamedFile( fileHandle_t f ) {
}
int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
return FS_Read( buffer, size * count, f );
}
void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
FS_Seek( f, offset, origin );
}
#else
typedef struct
{
fileHandle_t file;
byte *buffer;
qboolean eof;
int bufferSize;
int streamPosition; // next byte to be returned by Sys_StreamRead
int threadPosition; // next byte to be read from file
} streamState_t;
streamState_t stream;
/*
===============
Sys_StreamThread
A thread will be sitting in this loop forever
================
*/
void Sys_StreamThread( void ) {
int buffer;
int count;
int readCount;
int bufferPoint;
int r;
// if there is any space left in the buffer, fill it up
if ( !stream.eof ) {
count = stream.bufferSize - ( stream.threadPosition - stream.streamPosition );
if ( count ) {
bufferPoint = stream.threadPosition % stream.bufferSize;
buffer = stream.bufferSize - bufferPoint;
readCount = buffer < count ? buffer : count;
r = FS_Read( stream.buffer + bufferPoint, readCount, stream.file );
stream.threadPosition += r;
if ( r != readCount ) {
stream.eof = qtrue;
}
}
}
}
/*
===============
Sys_InitStreamThread
================
*/
void Sys_InitStreamThread( void ) {
}
/*
===============
Sys_ShutdownStreamThread
================
*/
void Sys_ShutdownStreamThread( void ) {
}
/*
===============
Sys_BeginStreamedFile
================
*/
void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
if ( stream.file ) {
Com_Error( ERR_FATAL, "Sys_BeginStreamedFile: unclosed stream" );
}
stream.file = f;
stream.buffer = Z_Malloc( readAhead );
stream.bufferSize = readAhead;
stream.streamPosition = 0;
stream.threadPosition = 0;
stream.eof = qfalse;
}
/*
===============
Sys_EndStreamedFile
================
*/
void Sys_EndStreamedFile( fileHandle_t f ) {
if ( f != stream.file ) {
Com_Error( ERR_FATAL, "Sys_EndStreamedFile: wrong file" );
}
stream.file = 0;
Z_Free( stream.buffer );
}
/*
===============
Sys_StreamedRead
================
*/
int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
int available;
int remaining;
int sleepCount;
int copy;
int bufferCount;
int bufferPoint;
byte *dest;
dest = (byte *)buffer;
remaining = size * count;
if ( remaining <= 0 ) {
Com_Error( ERR_FATAL, "Streamed read with non-positive size" );
}
sleepCount = 0;
while ( remaining > 0 )
{
available = stream.threadPosition - stream.streamPosition;
if ( !available ) {
if ( stream.eof ) {
break;
}
Sys_StreamThread();
continue;
}
bufferPoint = stream.streamPosition % stream.bufferSize;
bufferCount = stream.bufferSize - bufferPoint;
copy = available < bufferCount ? available : bufferCount;
if ( copy > remaining ) {
copy = remaining;
}
memcpy( dest, stream.buffer + bufferPoint, copy );
stream.streamPosition += copy;
dest += copy;
remaining -= copy;
}
return ( count * size - remaining ) / size;
}
/*
===============
Sys_StreamSeek
================
*/
void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
// clear to that point
FS_Seek( f, offset, origin );
stream.streamPosition = 0;
stream.threadPosition = 0;
stream.eof = qfalse;
}
#endif
/*
========================================================================
EVENT LOOP
========================================================================
*/
// bk000306: upped this from 64
#define MAX_QUED_EVENTS 256
#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 )
sysEvent_t eventQue[MAX_QUED_EVENTS];
// bk000306: initialize
int eventHead = 0;
int eventTail = 0;
byte sys_packetReceived[MAX_MSGLEN];
/*
================
Sys_QueEvent
A time of 0 will get the current time
Ptr should either be null, or point to a block of data that can
be freed by the game later.
================
*/
void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
sysEvent_t *ev;
ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
// bk000305 - was missing
if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
Com_Printf( "Sys_QueEvent: overflow\n" );
// we are discarding an event, but don't leak memory
if ( ev->evPtr ) {
Z_Free( ev->evPtr );
}
eventTail++;
}
eventHead++;
if ( time == 0 ) {
time = Sys_Milliseconds();
}
ev->evTime = time;
ev->evType = type;
ev->evValue = value;
ev->evValue2 = value2;
ev->evPtrLength = ptrLength;
ev->evPtr = ptr;
}
/*
================
Sys_GetEvent
================
*/
sysEvent_t Sys_GetEvent( void ) {
sysEvent_t ev;
char *s;
msg_t netmsg;
netadr_t adr;
// return if we have data
if ( eventHead > eventTail ) {
eventTail++;
return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
}
// pump the message loop
// in vga this calls KBD_Update, under X, it calls GetEvent
Sys_SendKeyEvents();
// check for console commands
s = Sys_ConsoleInput();
if ( s ) {
char *b;
int len;
len = strlen( s ) + 1;
b = Z_Malloc( len );
strcpy( b, s );
Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
}
// check for other input devices
IN_Frame();
// check for network packets
MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) );
if ( Sys_GetPacket( &adr, &netmsg ) ) {
netadr_t *buf;
int len;
// copy out to a seperate buffer for qeueing
len = sizeof( netadr_t ) + netmsg.cursize;
buf = Z_Malloc( len );
*buf = adr;
memcpy( buf + 1, netmsg.data, netmsg.cursize );
Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf );
}
// return if we have data
if ( eventHead > eventTail ) {
eventTail++;
return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
}
// create an empty event to return
memset( &ev, 0, sizeof( ev ) );
ev.evTime = Sys_Milliseconds();
return ev;
}
/*****************************************************************************/
qboolean Sys_CheckCD( void ) {
return qtrue;
}
void Sys_AppActivate( void ) {
}
char *Sys_GetClipboardData( void ) {
return NULL;
}
void Sys_Print( const char *msg ) {
if ( ttycon_on ) {
tty_Hide();
}
fputs( msg, stderr );
if ( ttycon_on ) {
tty_Show();
}
}
void Sys_ConfigureFPU() { // bk001213 - divide by zero
#ifdef __linux__
#ifdef __i386
#ifndef NDEBUG
// bk0101022 - enable FPE's in debug mode
static int fpu_word = _FPU_DEFAULT & ~( _FPU_MASK_ZM | _FPU_MASK_IM );
int current = 0;
_FPU_GETCW( current );
if ( current != fpu_word ) {
#if 0
Com_Printf( "FPU Control 0x%x (was 0x%x)\n", fpu_word, current );
_FPU_SETCW( fpu_word );
_FPU_GETCW( current );
assert( fpu_word == current );
#endif
}
#else // NDEBUG
static int fpu_word = _FPU_DEFAULT;
_FPU_SETCW( fpu_word );
#endif // NDEBUG
#endif // __i386
#endif // __linux
}
void Sys_PrintBinVersion( const char* name ) {
char* date = __DATE__;
char* time = __TIME__;
char* sep = "==============================================================";
fprintf( stdout, "\n\n%s\n", sep );
#ifdef DEDICATED
fprintf( stdout, "Linux Quake3 Dedicated Server [%s %s]\n", date, time );
#else
fprintf( stdout, "Linux Quake3 Full Executable [%s %s]\n", date, time );
#endif
fprintf( stdout, " local install: %s\n", name );
fprintf( stdout, "%s\n\n", sep );
}
/*
==================
chmod OR on a file
==================
*/
void Sys_Chmod( char *file, int mode ) {
struct stat s_buf;
int perm;
if ( stat( file, &s_buf ) != 0 ) {
Com_Printf( "stat('%s') failed: errno %d\n", file, errno );
return;
}
perm = s_buf.st_mode | mode;
if ( chmod( file, perm ) != 0 ) {
Com_Printf( "chmod('%s', %d) failed: errno %d\n", file, perm, errno );
}
Com_DPrintf( "chmod +%d '%s'\n", mode, file );
}
/*
==================
Sys_DoStartProcess
actually forks and starts a process
UGLY HACK:
Sys_StartProcess works with a command line only
if this command line is actually a binary with command line parameters,
the only way to do this on unix is to use a system() call
but system doesn't replace the current process, which leads to a situation like:
wolf.x86--spawned_process.x86
in the case of auto-update for instance, this will cause write access denied on wolf.x86:
wolf-beta/2002-March/000079.html
we hack the implementation here, if there are no space in the command line, assume it's a straight process and use execl
otherwise, use system ..
The clean solution would be Sys_StartProcess and Sys_StartProcess_Args..
==================
*/
void Sys_DoStartProcess( char *cmdline ) {
switch ( fork() )
{
case - 1:
// main thread
break;
case 0:
if ( strchr( cmdline, ' ' ) ) {
system( cmdline );
} else
{
execl( cmdline, cmdline, NULL );
printf( "execl failed: %s\n", strerror( errno ) );
}
_exit( 0 );
break;
}
}
/*
==================
Sys_StartProcess
if !doexit, start the process asap
otherwise, push it for execution at exit
(i.e. let complete shutdown of the game and freeing of resources happen)
NOTE: might even want to add a small delay?
==================
*/
void Sys_StartProcess( char *cmdline, qboolean doexit ) {
if ( doexit ) {
Com_DPrintf( "Sys_StartProcess %s (delaying to final exit)\n", cmdline );
Q_strncpyz( exit_cmdline, cmdline, MAX_CMD );
Cbuf_ExecuteText( EXEC_APPEND, "quit\n" );
return;
}
Com_DPrintf( "Sys_StartProcess %s\n", cmdline );
Sys_DoStartProcess( cmdline );
}
/*
=================
Sys_OpenURL
=================
*/
void Sys_OpenURL( const char *url, qboolean doexit ) {
char *basepath, *homepath, *pwdpath;
char fname[20];
char fn[MAX_OSPATH];
char cmdline[MAX_CMD];
Com_Printf( "Sys_OpenURL %s\n", url );
// opening an URL on *nix can mean a lot of things ..
// just spawn a script instead of deciding for the user :-)
// do the setup before we fork
// search for an openurl.sh script
// search procedure taken from Sys_LoadDll
Q_strncpyz( fname, "openurl.sh", 20 );
pwdpath = Sys_Cwd();
Com_sprintf( fn, MAX_OSPATH, "%s/%s", pwdpath, fname );
if ( access( fn, X_OK ) == -1 ) {
Com_DPrintf( "%s not found\n", fn );
// try in home path
homepath = Cvar_VariableString( "fs_homepath" );
Com_sprintf( fn, MAX_OSPATH, "%s/%s", homepath, fname );
if ( access( fn, X_OK ) == -1 ) {
Com_DPrintf( "%s not found\n", fn );
// basepath, last resort
basepath = Cvar_VariableString( "fs_basepath" );
Com_sprintf( fn, MAX_OSPATH, "%s/%s", basepath, fname );
if ( access( fn, X_OK ) == -1 ) {
Com_DPrintf( "%s not found\n", fn );
Com_Printf( "Can't find script '%s' to open requested URL (use +set developer 1 for more verbosity)\n", fname );
// we won't quit
return;
}
}
}
Com_DPrintf( "URL script: %s\n", fn );
// build the command line
Com_sprintf( cmdline, MAX_CMD, "%s '%s' &", fn, url );
Sys_StartProcess( cmdline, doexit );
}
void Sys_ParseArgs( int argc, char* argv[] ) {
if ( argc == 2 ) {
if ( ( !strcmp( argv[1], "--version" ) )
|| ( !strcmp( argv[1], "-v" ) ) ) {
Sys_PrintBinVersion( argv[0] );
Sys_Exit( 0 );
}
}
}
#include "../client/client.h"
extern clientStatic_t cls;
int main( int argc, char* argv[] ) {
// int oldtime, newtime; // bk001204 - unused
int len, i;
char *cmdline;
void Sys_SetDefaultCDPath( const char *path );
// go back to real user for config loads
saved_euid = geteuid();
seteuid( getuid() );
Sys_ParseArgs( argc, argv ); // bk010104 - added this for support
// TTimo: no CD path
Sys_SetDefaultCDPath( "" );
// merge the command line, this is kinda silly
for ( len = 1, i = 1; i < argc; i++ )
len += strlen( argv[i] ) + 1;
cmdline = malloc( len );
*cmdline = 0;
for ( i = 1; i < argc; i++ )
{
if ( i > 1 ) {
strcat( cmdline, " " );
}
strcat( cmdline, argv[i] );
}
// bk000306 - clear queues
memset( &eventQue[0], 0, MAX_QUED_EVENTS * sizeof( sysEvent_t ) );
memset( &sys_packetReceived[0], 0, MAX_MSGLEN * sizeof( byte ) );
Com_Init( cmdline );
NET_Init();
Sys_ConsoleInputInit();
fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) | FNDELAY );
#ifdef DEDICATED
// init here for dedicated, as we don't have GLimp_Init
InitSig();
#endif
while ( 1 )
{
#ifdef __linux__
Sys_ConfigureFPU();
#endif
Com_Frame();
}
}