/* =========================================================================== 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 =========================================================================== */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // dirname #include #ifdef __linux__ // rb010123 #include #endif #include #ifdef __linux__ #include // bk001213 - force dumps on divide by zero #endif #if defined(__sun) #include #endif // FIXME TTimo should we gard this? most *nix system should comply? #include #include "../client/client.h" #include "../qcommon/qcommon.h" #include "linux_local.h" // bk001204 // Structure containing functions exported from refresh DLL refexport_t re; unsigned sys_frame_time; 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 so 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; static cvar_t *ttycon_ansicolor = NULL; static qboolean ttycon_color_on = qfalse; // 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; cvar_t *in_subframe; cvar_t *in_nograb; // this is strictly for developers qboolean endof_frame = qfalse; static jmp_buf sys_exitframe; static int sys_retcode; static char sys_cmdline[MAX_STRING_CHARS]; #ifndef DEDICATED WinVars_t g_wv; #endif // ======================================================================= // General routines // ======================================================================= // bk001207 #define MEM_THRESHOLD 96*1024*1024 #ifndef DEDICATED void* Q_EXTERNAL_CALL Sys_PlatformGetVars( void ) { return &g_wv; } /* We translate axes movement into keypresses. */ static int joy_keys[16] = { K_LEFTARROW, K_RIGHTARROW, K_UPARROW, K_DOWNARROW, K_JOY16, K_JOY17, K_JOY18, K_JOY19, K_JOY20, K_JOY21, K_JOY22, K_JOY23, K_JOY24, K_JOY25, K_JOY26, K_JOY27 }; // bk001130 - from linux_glimp.c extern cvar_t * in_joystick; extern cvar_t * in_joystickDebug; extern cvar_t * joy_threshold; #define ARRAYLEN(x) (sizeof (x) / sizeof (x[0])) struct { qboolean buttons[16]; // !!! FIXME: these might be too many. unsigned int oldaxes; } stick_state; #endif //end ifdef DEDICATED check /* ================== Sys_LowPhysicalMemory() ================== */ qboolean Sys_LowPhysicalMemory() { //MEMORYSTATUS stat; //GlobalMemoryStatus (&stat); //return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse; return qfalse; // bk001207 - FIXME } void Sys_BeginProfiling( void ) { } void Sys_Sleep( int msec ) { if (msec > 2 ) { struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = msec * 1000; nanosleep(&ts, NULL); } } extern qboolean app_active; qboolean Sys_IsForeground( void ) { return app_active; } qboolean Sys_OpenUrl( const char *url ) { char *browser = getenv("BROWSER"); char *kde_session = getenv("KDE_FULL_SESSION"); char *gnome_session = getenv("GNOME_DESKTOP_SESSION_ID"); //Try to use xdg-open, if not, try default, then kde, gnome if ( browser ) { Sys_Fork( browser, url); return qtrue; } else if ( kde_session && Q_stricmp("true", kde_session) == 0 ) { Sys_Fork( "konqueror", url); return qtrue; } else if( gnome_session ) { Sys_Fork( "gnome-open", url); return qtrue; } else { Sys_Fork( "/usr/bin/firefox", url); } // open url somehow return qtrue; } void Sys_WriteDump( const char *fmt, ... ) { } qboolean Sys_Fork( const char *path, char *cmdLine ) { int pid; pid = fork(); if (pid == 0) { struct stat filestat; char *argv[3]; //Try to set the executable bit if ( stat(path, &filestat) == 0 ) { chmod(path, filestat.st_mode | S_IXUSR); } argv[0] = path; argv[1] = cmdLine; argv[2] = NULL; execvp(path, argv); printf("Exec Failed for: %s\n", path); _exit(255); } else if (pid == -1) { return qfalse; } return qtrue; } /* =============== Sys_SwapVersion - check to see if we are running the highest numbered exe, and switch to it if we are not =============== */ extern cvar_t *fs_homepath; extern char *fs_gamedir; void Sys_SwapVersion( ) { int i; char ospath[MAX_OSPATH]; char *homedir, *gamedir, *cdpath; FILE *f; homedir = Cvar_VariableString("fs_homepath"); gamedir = Cvar_VariableString("fs_game"); cdpath = Cvar_VariableString("fs_cdpath"); for (i=99; i >= 0; i--) { FS_BuildOSPath( ospath, sizeof(ospath), homedir, gamedir, va(PLATFORM_EXE_NAME, i) ); f = fopen( ospath, "rb" ); if (f) { fclose( f ); Q_strcat(sys_cmdline, sizeof(sys_cmdline), va("+set fs_cdpath %s", cdpath) ); if (Sys_Fork( ospath, sys_cmdline ) ) { Com_Quit_f (); } } } } #ifndef DEDICATED /* ================= Sys_In_Restart_f Restart the input subsystem ================= */ void Sys_In_Restart_f( void ) { IN_Shutdown(); IN_Init(); } #endif // ============================================================= // 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( void ) { 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( void ) { 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( void ) { int i; assert(ttycon_on); if (ttycon_hide) { ttycon_hide++; return; } if (tty_con.cursor>0) { for (i=0; i0); ttycon_hide--; if (ttycon_hide == 0) { if (tty_con.cursor) { for (i=0; i= 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= 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( void ) { 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 // ============================================================= #if 0 // NOTE TTimo this is not used .. looks interesting though? protection against buffer overflow kind of stuff? void Sys_Printf (char *fmt, ...) { va_list argptr; char text[1024]; unsigned char *p; va_start (argptr,fmt); vsprintf (text,fmt,argptr); va_end (argptr); if (strlen(text) > sizeof(text)) Sys_Error("memory overwrite in Sys_Printf"); for (p = (unsigned char *)text; *p; p++) { *p &= 0x7f; if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9) printf("[%02x]", *p); else putc(*p, stdout); } } #endif // single exit point (regular exit or in case of signal fault) void Sys_Exit( int ex ) { Sys_ConsoleInputShutdown(); sys_retcode = ex; longjmp( sys_exitframe, -1); } void Sys_Quit (void) { CL_Shutdown (); fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); Sys_Exit(0); } #if idppc_altivec && !MACOS_X /* This is the brute force way of detecting instruction sets... the code is borrowed from SDL, which got the idea from the libmpeg2 library - thanks! */ #include #include static jmp_buf jmpbuf; static void illegal_instruction(int sig) { longjmp(jmpbuf, 1); } #endif qboolean Sys_DetectAltivec( void ) { qboolean altivec = qfalse; #if idppc_altivec #ifdef MACOS_X long feat = 0; OSErr err = Gestalt(gestaltPowerPCProcessorFeatures, &feat); if ((err==noErr) && ((1 << gestaltPowerPCHasVectorInstructions) & feat)) { altivec = qtrue; } #else void (*handler)(int sig); handler = signal(SIGILL, illegal_instruction); if ( setjmp(jmpbuf) == 0 ) { asm volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0" : : "r" (-1)); altivec = qtrue; } signal(SIGILL, handler); #endif #endif return altivec; } void Sys_Init(void) { char tmp[ MAX_NAME_LENGTH ]; #ifndef DEDICATED Cmd_AddCommand ("in_restart", Sys_In_Restart_f); #endif #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(tmp, sizeof(tmp) ) ); IN_Init(); // rcg08312005 moved into glimp. } 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; } 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( void ) { struct termios tc; // TTimo // https://zerowing.idsoftware.com/bugzilla/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 = tty_tc.c_cc[VERASE]; tty_eof = tty_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; ttycon_ansicolor = Cvar_Get( "ttycon_ansicolor", "0", CVAR_ARCHIVE ); if( ttycon_ansicolor && ttycon_ansicolor->value ) { ttycon_color_on = qtrue; } } else ttycon_on = qfalse; } char *Sys_ConsoleInput(void) { // we use this when sending back commands static char text[256]; 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_AutoComplete( &tty_con ); 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; } } /*****************************************************************************/ char *do_dlerror(void) { return dlerror(); } /* ================= Sys_UnloadDll ================= */ void Sys_UnloadDll( void *dllHandle ) { // bk001206 - verbose error reporting if ( !dllHandle ) { Com_Printf("Sys_UnloadDll(NULL)\n"); return; } dlclose( dllHandle ); { const char* err; // rb010123 - now const 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 TTimo: changed the load procedure to match VFS logic, and allow developer use #1 look down current path #2 look in fs_homepath #3 look in fs_basepath ================= */ static void* try_dlopen(const char* base, const char* gamedir, const char* fname, char* fqpath ) { void* libHandle; char fn[MAX_OSPATH]; *fqpath = 0; // bk001129 - was RTLD_LAZY #define Q_RTLD RTLD_NOW FS_BuildOSPath( fn, sizeof(fn), base, gamedir, fname ); Com_Printf( "Sys_LoadDll(%s)... \n", fn ); libHandle = dlopen( fn, Q_RTLD ); if(!libHandle) { Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, do_dlerror() ); return NULL; } Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn ); Q_strncpyz ( fqpath , fn , MAX_QPATH ) ; // added 7/20/02 by T.Ray return libHandle; } void *Sys_LoadDll( const char *name, char *fqpath , intptr_t (**entryPoint)(int, ...), intptr_t (*systemcalls)(intptr_t, ...) ) { void *libHandle; void (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) ); char curpath[MAX_OSPATH]; char fname[MAX_OSPATH]; char *basepath; char *homepath; char pwdpath[MAX_OSPATH]; char *gamedir; const char* err = NULL; // bk001206 - let's have some paranoia assert( name ); getcwd(curpath, sizeof(curpath)); snprintf (fname, sizeof(fname), "%s" ARCH_STRING DLL_EXT, name); // TODO: use fs_searchpaths from files.c Sys_Cwd(pwdpath, sizeof(pwdpath) ); basepath = Cvar_VariableString( "fs_basepath" ); homepath = Cvar_VariableString( "fs_homepath" ); gamedir = Cvar_VariableString( "fs_game" ); libHandle = try_dlopen(pwdpath, gamedir, fname, fqpath); if(!libHandle && homepath) libHandle = try_dlopen(homepath, gamedir, fname, fqpath); if(!libHandle && basepath) libHandle = try_dlopen(basepath, gamedir, fname, fqpath); if(!libHandle) { #if 0 // don't abort -- ln //#ifndef NDEBUG // bk001206 - 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; } dllEntry = dlsym( libHandle, "dllEntry" ); *entryPoint = dlsym( libHandle, "vmMain" ); if ( !*entryPoint || !dllEntry ) { err = do_dlerror(); #ifndef NDEBUG // bk001206 - 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 = do_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 ); // bk001212 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 network packets MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); adr.type = NA_IP; 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; } /* ============================================================== DIRECTORY SCANNING ============================================================== */ void Sys_SplitPath( const char * fullname, char * path, int path_size, char * name, int name_size, char * ext, int ext_size ) { char _dir [ MAX_OSPATH ]; char _name [ MAX_OSPATH ]; char *extloc; Q_strncpyz( _dir, fullname, sizeof(_dir) ); Q_strncpyz( _name, fullname, sizeof(_name) ); if ( path ) { char *dirpath = dirname(_dir); if (dirpath && dirpath[0] != '.') { Q_strncpyz( path, dirpath, path_size ); Q_strcat(path, path_size, "/"); } else { Q_strncpyz( path, "", path_size); } } if ( name ) COM_StripExtension( COM_SkipPath(_name), name, name_size ); if ( ext ) { if ( (extloc = rindex(fullname, '.')) != NULL ) { if (rindex(extloc, '/') == NULL){ Q_strncpyz( ext, extloc, ext_size); } else { ext[0] = NULL; } } else { ext[0] = NULL; } } } void Sys_AppActivate (void) { } char *Sys_GetClipboardData(void) { return NULL; } static struct Q3ToAnsiColorTable_s { char Q3color; char *ANSIcolor; } tty_colorTable[ ] = { { COLOR_BLACK, "30" }, { COLOR_RED, "31" }, { COLOR_GREEN, "32" }, { COLOR_YELLOW, "33" }, { COLOR_BLUE, "34" }, { COLOR_CYAN, "36" }, { COLOR_MAGENTA, "35" }, { COLOR_WHITE, "0" } }; static int tty_colorTableSize = sizeof( tty_colorTable ) / sizeof( tty_colorTable[ 0 ] ); void Sys_ANSIColorify( const char *msg, char *buffer, int bufferSize ) { int msgLength, pos; int i, j; char *escapeCode; char tempBuffer[ 7 ]; if( !msg || !buffer ) return; msgLength = strlen( msg ); pos = 0; i = 0; buffer[ 0 ] = '\0'; while( i < msgLength ) { if( msg[ i ] == '\n' ) { Com_sprintf( tempBuffer, 7, "%c[0m\n", 0x1B ); strncat( buffer, tempBuffer, bufferSize ); i++; } else if( msg[ i ] == Q_COLOR_ESCAPE ) { i++; if( i < msgLength ) { escapeCode = NULL; for( j = 0; j < tty_colorTableSize; j++ ) { if( msg[ i ] == tty_colorTable[ j ].Q3color ) { escapeCode = tty_colorTable[ j ].ANSIcolor; break; } } if( escapeCode ) { Com_sprintf( tempBuffer, 7, "%c[%sm", 0x1B, escapeCode ); strncat( buffer, tempBuffer, bufferSize ); } i++; } } else { Com_sprintf( tempBuffer, 7, "%c", msg[ i++ ] ); strncat( buffer, tempBuffer, bufferSize ); } } } void Sys_Print( const char *msg ) { if (ttycon_on) { tty_Hide(); } if( ttycon_on && ttycon_color_on ) { char ansiColorString[ MAXPRINTMSG ]; Sys_ANSIColorify( msg, ansiColorString, MAXPRINTMSG ); fputs( ansiColorString, stderr ); } else fputs(msg, stderr); if (ttycon_on) { tty_Show(); } } void Sys_ConfigureFPU( void ) { // 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 ); } 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); } } } #ifndef DEFAULT_BASEDIR # define DEFAULT_BASEDIR Sys_DefaultCDPath() #endif extern clientStatic_t cls; int TheRealMain(char *cmdline, void * hWnd) { // bk000306 - clear queues struct timeval tp; memset( &eventQue[0], 0, MAX_QUED_EVENTS*sizeof(sysEvent_t) ); memset( &sys_packetReceived[0], 0, MAX_MSGLEN*sizeof(byte) ); Q_strncpyz(sys_cmdline, cmdline, sizeof(sys_cmdline)); //Seed our random number generator gettimeofday(&tp, NULL); Sys_QueEvent( 0, SE_RANDSEED, ((tp.tv_sec*1000) + (tp.tv_usec/1000)), 0, 0, 0 ); #if !defined( DEDICATED ) g_wv.hWnd = hWnd; #endif Com_Init(cmdline); NET_Init(); #ifndef DEDICATED #ifdef USE_IRC Net_IRC_Init(); #endif #endif Sys_ConsoleInputInit(); fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); InitSig(); while (1) { if (setjmp( sys_exitframe ) ) { #if !defined( DEDICATED ) S_Shutdown(); CL_ShutdownRef(); #endif return sys_retcode; } #ifdef __linux__ Sys_ConfigureFPU(); #endif //Check for input IN_Frame(); Com_Frame (); } return 0; } int main ( int argc, char* argv[] ) { // int oldtime, newtime; // bk001204 - unused int len, i, lnk; char *cmdline; char cdpath[MAX_OSPATH] = {0}; char tmp[MAX_OSPATH]; Sys_ParseArgs( argc, argv ); // bk010104 - added this for support lnk = readlink(argv[0], cdpath, sizeof(cdpath)-1); if (lnk > 0 ) { cdpath[lnk] = 0; } else { strncat(cdpath, argv[0], sizeof(cdpath)-1); } dirname(cdpath); if (cdpath[0] == '.' && strlen(cdpath) == 1) { getcwd(cdpath, sizeof(cdpath)); } Sys_SetDefaultCDPath(cdpath); Sys_SetDefaultInstallPath(Sys_DefaultCDPath(tmp, sizeof(tmp))); // 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]); } return TheRealMain(cmdline, 0); }