1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5 
6 This file is part of Tremulous.
7 
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 #include <unistd.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <limits.h>
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <sys/ipc.h>
34 #include <sys/shm.h>
35 #include <sys/stat.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <sys/wait.h>
39 #include <sys/mman.h>
40 #include <errno.h>
41 #include <libgen.h> // dirname
42 #ifdef __linux__ // rb010123
43   #include <mntent.h>
44 #endif
45 
46 #if (defined(DEDICATED) && defined(USE_SDL_VIDEO))
47 #undef USE_SDL_VIDEO
48 #endif
49 
50 #if USE_SDL_VIDEO
51 #include "SDL.h"
52 #include "SDL_loadso.h"
53 #else
54 #include <dlfcn.h>
55 #endif
56 
57 #ifdef __linux__
58   #include <fpu_control.h> // bk001213 - force dumps on divide by zero
59 #endif
60 
61 #if defined(__sun)
62   #include <sys/file.h>
63 #endif
64 
65 // FIXME TTimo should we gard this? most *nix system should comply?
66 #include <termios.h>
67 
68 #include "../qcommon/q_shared.h"
69 #include "../qcommon/qcommon.h"
70 #include "../renderer/tr_public.h"
71 
72 #include "linux_local.h" // bk001204
73 
74 #if idppc_altivec
75   #ifdef MACOS_X
76     #include <Carbon/Carbon.h>
77   #endif
78 #endif
79 
80 unsigned  sys_frame_time;
81 
82 qboolean stdin_active = qtrue;
83 
84 // =============================================================
85 // tty console variables
86 // =============================================================
87 
88 // enable/disabled tty input mode
89 // NOTE TTimo this is used during startup, cannot be changed during run
90 static cvar_t *ttycon = NULL;
91 // general flag to tell about tty console mode
92 static qboolean ttycon_on = qfalse;
93 // when printing general stuff to stdout stderr (Sys_Printf)
94 //   we need to disable the tty console stuff
95 // this increments so we can recursively disable
96 static int ttycon_hide = 0;
97 // some key codes that the terminal may be using
98 // TTimo NOTE: I'm not sure how relevant this is
99 static int tty_erase;
100 static int tty_eof;
101 
102 static struct termios tty_tc;
103 
104 static field_t tty_con;
105 
106 static cvar_t *ttycon_ansicolor = NULL;
107 static qboolean ttycon_color_on = qfalse;
108 
109 // history
110 // NOTE TTimo this is a bit duplicate of the graphical console history
111 //   but it's safer and faster to write our own here
112 #define TTY_HISTORY 32
113 static field_t ttyEditLines[TTY_HISTORY];
114 static int hist_current = -1, hist_count = 0;
115 
116 // =======================================================================
117 // General routines
118 // =======================================================================
119 
120 // bk001207
121 #define MEM_THRESHOLD 96*1024*1024
122 
123 /*
124 ==================
125 Sys_LowPhysicalMemory()
126 ==================
127 */
Sys_LowPhysicalMemory()128 qboolean Sys_LowPhysicalMemory() {
129   //MEMORYSTATUS stat;
130   //GlobalMemoryStatus (&stat);
131   //return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
132   return qfalse; // bk001207 - FIXME
133 }
134 
135 /*
136 ==================
137 Sys_FunctionCmp
138 ==================
139 */
Sys_FunctionCmp(void * f1,void * f2)140 int Sys_FunctionCmp(void *f1, void *f2) {
141   return qtrue;
142 }
143 
144 /*
145 ==================
146 Sys_FunctionCheckSum
147 ==================
148 */
Sys_FunctionCheckSum(void * f1)149 int Sys_FunctionCheckSum(void *f1) {
150   return 0;
151 }
152 
153 /*
154 ==================
155 Sys_MonkeyShouldBeSpanked
156 ==================
157 */
Sys_MonkeyShouldBeSpanked(void)158 int Sys_MonkeyShouldBeSpanked( void ) {
159   return 0;
160 }
161 
Sys_BeginProfiling(void)162 void Sys_BeginProfiling( void ) {
163 }
164 
165 /*
166 =================
167 Sys_In_Restart_f
168 
169 Restart the input subsystem
170 =================
171 */
Sys_In_Restart_f(void)172 void Sys_In_Restart_f( void )
173 {
174   IN_Shutdown();
175   IN_Init();
176 }
177 
178 // =============================================================
179 // tty console routines
180 // NOTE: if the user is editing a line when something gets printed to the early console then it won't look good
181 //   so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output
182 // =============================================================
183 
184 // flush stdin, I suspect some terminals are sending a LOT of shit
185 // FIXME TTimo relevant?
tty_FlushIn(void)186 void tty_FlushIn( void )
187 {
188   char key;
189   while (read(0, &key, 1)!=-1);
190 }
191 
192 // do a backspace
193 // TTimo NOTE: it seems on some terminals just sending '\b' is not enough
194 //   so for now, in any case we send "\b \b" .. yeah well ..
195 //   (there may be a way to find out if '\b' alone would work though)
tty_Back(void)196 void tty_Back( void )
197 {
198   char key;
199   key = '\b';
200   write(1, &key, 1);
201   key = ' ';
202   write(1, &key, 1);
203   key = '\b';
204   write(1, &key, 1);
205 }
206 
207 // clear the display of the line currently edited
208 // bring cursor back to beginning of line
tty_Hide(void)209 void tty_Hide( void )
210 {
211   int i;
212   assert(ttycon_on);
213   if (ttycon_hide)
214   {
215     ttycon_hide++;
216     return;
217   }
218   if (tty_con.cursor>0)
219   {
220     for (i=0; i<tty_con.cursor; i++)
221     {
222       tty_Back();
223     }
224   }
225   ttycon_hide++;
226 }
227 
228 // show the current line
229 // FIXME TTimo need to position the cursor if needed??
tty_Show(void)230 void tty_Show( void )
231 {
232   int i;
233   assert(ttycon_on);
234   assert(ttycon_hide>0);
235   ttycon_hide--;
236   if (ttycon_hide == 0)
237   {
238     if (tty_con.cursor)
239     {
240       for (i=0; i<tty_con.cursor; i++)
241       {
242         write(1, tty_con.buffer+i, 1);
243       }
244     }
245   }
246 }
247 
248 // never exit without calling this, or your terminal will be left in a pretty bad state
Sys_ConsoleInputShutdown(void)249 void Sys_ConsoleInputShutdown( void )
250 {
251   if (ttycon_on)
252   {
253     Com_Printf("Shutdown tty console\n");
254     tcsetattr (0, TCSADRAIN, &tty_tc);
255   }
256 }
257 
Hist_Add(field_t * field)258 void Hist_Add(field_t *field)
259 {
260   int i;
261   assert(hist_count <= TTY_HISTORY);
262   assert(hist_count >= 0);
263   assert(hist_current >= -1);
264   assert(hist_current <= hist_count);
265   // make some room
266   for (i=TTY_HISTORY-1; i>0; i--)
267   {
268     ttyEditLines[i] = ttyEditLines[i-1];
269   }
270   ttyEditLines[0] = *field;
271   if (hist_count<TTY_HISTORY)
272   {
273     hist_count++;
274   }
275   hist_current = -1; // re-init
276 }
277 
Hist_Prev(void)278 field_t *Hist_Prev( void )
279 {
280   int hist_prev;
281   assert(hist_count <= TTY_HISTORY);
282   assert(hist_count >= 0);
283   assert(hist_current >= -1);
284   assert(hist_current <= hist_count);
285   hist_prev = hist_current + 1;
286   if (hist_prev >= hist_count)
287   {
288     return NULL;
289   }
290   hist_current++;
291   return &(ttyEditLines[hist_current]);
292 }
293 
Hist_Next(void)294 field_t *Hist_Next( void )
295 {
296   assert(hist_count <= TTY_HISTORY);
297   assert(hist_count >= 0);
298   assert(hist_current >= -1);
299   assert(hist_current <= hist_count);
300   if (hist_current >= 0)
301   {
302     hist_current--;
303   }
304   if (hist_current == -1)
305   {
306     return NULL;
307   }
308   return &(ttyEditLines[hist_current]);
309 }
310 
311 // =============================================================
312 // general sys routines
313 // =============================================================
314 
315 #if 0
316 // NOTE TTimo this is not used .. looks interesting though? protection against buffer overflow kind of stuff?
317 void Sys_Printf (char *fmt, ...)
318 {
319   va_list   argptr;
320   char    text[1024];
321   unsigned char   *p;
322 
323   va_start (argptr,fmt);
324   vsprintf (text,fmt,argptr);
325   va_end (argptr);
326 
327   if (strlen(text) > sizeof(text))
328     Sys_Error("memory overwrite in Sys_Printf");
329 
330   for (p = (unsigned char *)text; *p; p++)
331   {
332     *p &= 0x7f;
333     if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
334       printf("[%02x]", *p);
335     else
336       putc(*p, stdout);
337   }
338 }
339 #endif
340 
341 // single exit point (regular exit or in case of signal fault)
Sys_Exit(int ex)342 void Sys_Exit( int ex ) {
343   Sys_ConsoleInputShutdown();
344 
345 #ifdef NDEBUG // regular behavior
346 
347   // We can't do this
348   //  as long as GL DLL's keep installing with atexit...
349   //exit(ex);
350   _exit(ex);
351 #else
352 
353   // Give me a backtrace on error exits.
354   assert( ex == 0 );
355   exit(ex);
356 #endif
357 }
358 
359 
Sys_Quit(void)360 void Sys_Quit (void) {
361   CL_Shutdown ();
362   fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
363   Sys_Exit(0);
364 }
365 
366 
367 #if idppc_altivec && !MACOS_X
368 /* This is the brute force way of detecting instruction sets...
369    the code is borrowed from SDL, which got the idea from the libmpeg2
370    library - thanks!
371  */
372 #include <signal.h>
373 #include <setjmp.h>
374 static jmp_buf jmpbuf;
illegal_instruction(int sig)375 static void illegal_instruction(int sig)
376 {
377     longjmp(jmpbuf, 1);
378 }
379 #endif
380 
Sys_DetectAltivec(void)381 qboolean Sys_DetectAltivec( void )
382 {
383     qboolean altivec = qfalse;
384 
385 #if idppc_altivec
386     #ifdef MACOS_X
387     long feat = 0;
388     OSErr err = Gestalt(gestaltPowerPCProcessorFeatures, &feat);
389     if ((err==noErr) && ((1 << gestaltPowerPCHasVectorInstructions) & feat)) {
390         altivec = qtrue;
391     }
392     #else
393     void (*handler)(int sig);
394     handler = signal(SIGILL, illegal_instruction);
395     if ( setjmp(jmpbuf) == 0 ) {
396         asm volatile ("mtspr 256, %0\n\t"
397                       "vand %%v0, %%v0, %%v0"
398                         :
399                         : "r" (-1));
400         altivec = qtrue;
401     }
402     signal(SIGILL, handler);
403     #endif
404 #endif
405 
406     return altivec;
407 }
408 
Sys_Init(void)409 void Sys_Init(void)
410 {
411 
412   Cmd_AddCommand ("in_restart", Sys_In_Restart_f);
413 
414   Cvar_Set( "arch", OS_STRING " " ARCH_STRING );
415 
416   Cvar_Set( "username", Sys_GetCurrentUser() );
417 
418   //IN_Init();   // rcg08312005 moved into glimp.
419 
420 }
421 
Sys_Error(const char * error,...)422 void  Sys_Error( const char *error, ...)
423 {
424   va_list     argptr;
425   char        string[1024];
426 
427   // change stdin to non blocking
428   // NOTE TTimo not sure how well that goes with tty console mode
429   fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
430 
431   // don't bother do a show on this one heh
432   if (ttycon_on)
433   {
434     tty_Hide();
435   }
436 
437   CL_Shutdown ();
438 
439   va_start (argptr,error);
440   vsprintf (string,error,argptr);
441   va_end (argptr);
442   fprintf(stderr, "Sys_Error: %s\n", string);
443 
444   Sys_Exit( 1 ); // bk010104 - use single exit point.
445 }
446 
Sys_Warn(char * warning,...)447 void Sys_Warn (char *warning, ...)
448 {
449   va_list     argptr;
450   char        string[1024];
451 
452   va_start (argptr,warning);
453   vsprintf (string,warning,argptr);
454   va_end (argptr);
455 
456   if (ttycon_on)
457   {
458     tty_Hide();
459   }
460 
461   fprintf(stderr, "Warning: %s", string);
462 
463   if (ttycon_on)
464   {
465     tty_Show();
466   }
467 }
468 
469 /*
470 ============
471 Sys_FileTime
472 
473 returns -1 if not present
474 ============
475 */
Sys_FileTime(char * path)476 int Sys_FileTime (char *path)
477 {
478   struct  stat  buf;
479 
480   if (stat (path,&buf) == -1)
481     return -1;
482 
483   return buf.st_mtime;
484 }
485 
floating_point_exception_handler(int whatever)486 void floating_point_exception_handler(int whatever)
487 {
488   signal(SIGFPE, floating_point_exception_handler);
489 }
490 
491 // initialize the console input (tty mode if wanted and possible)
Sys_ConsoleInputInit(void)492 void Sys_ConsoleInputInit( void )
493 {
494   struct termios tc;
495 
496   // TTimo
497   // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390
498   // ttycon 0 or 1, if the process is backgrounded (running non interactively)
499   // then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
500   signal(SIGTTIN, SIG_IGN);
501   signal(SIGTTOU, SIG_IGN);
502 
503   // FIXME TTimo initialize this in Sys_Init or something?
504   ttycon = Cvar_Get("ttycon", "1", 0);
505   if (ttycon && ttycon->value)
506   {
507     if (isatty(STDIN_FILENO)!=1)
508     {
509       Com_Printf("stdin is not a tty, tty console mode failed\n");
510       Cvar_Set("ttycon", "0");
511       ttycon_on = qfalse;
512       return;
513     }
514     Com_Printf("Started tty console (use +set ttycon 0 to disable)\n");
515     Field_Clear(&tty_con);
516     tcgetattr (0, &tty_tc);
517     tty_erase = tty_tc.c_cc[VERASE];
518     tty_eof = tty_tc.c_cc[VEOF];
519     tc = tty_tc;
520     /*
521      ECHO: don't echo input characters
522      ICANON: enable canonical mode.  This  enables  the  special
523               characters  EOF,  EOL,  EOL2, ERASE, KILL, REPRINT,
524               STATUS, and WERASE, and buffers by lines.
525      ISIG: when any of the characters  INTR,  QUIT,  SUSP,  or
526               DSUSP are received, generate the corresponding sig�
527               nal
528     */
529     tc.c_lflag &= ~(ECHO | ICANON);
530     /*
531      ISTRIP strip off bit 8
532      INPCK enable input parity checking
533      */
534     tc.c_iflag &= ~(ISTRIP | INPCK);
535     tc.c_cc[VMIN] = 1;
536     tc.c_cc[VTIME] = 0;
537     tcsetattr (0, TCSADRAIN, &tc);
538     ttycon_on = qtrue;
539 
540     ttycon_ansicolor = Cvar_Get( "ttycon_ansicolor", "0", CVAR_ARCHIVE );
541     if( ttycon_ansicolor && ttycon_ansicolor->value )
542     {
543       ttycon_color_on = qtrue;
544     }
545   } else
546     ttycon_on = qfalse;
547 }
548 
Sys_ConsoleInput(void)549 char *Sys_ConsoleInput(void)
550 {
551   // we use this when sending back commands
552   static char text[256];
553   int avail;
554   char key;
555   field_t *history;
556 
557   if (ttycon && ttycon->value)
558   {
559     avail = read(0, &key, 1);
560     if (avail != -1)
561     {
562       // we have something
563       // backspace?
564       // NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
565       if ((key == tty_erase) || (key == 127) || (key == 8))
566       {
567         if (tty_con.cursor > 0)
568         {
569           tty_con.cursor--;
570           tty_con.buffer[tty_con.cursor] = '\0';
571           tty_Back();
572         }
573         return NULL;
574       }
575       // check if this is a control char
576       if ((key) && (key) < ' ')
577       {
578         if (key == '\n')
579         {
580           // push it in history
581           Hist_Add(&tty_con);
582           strcpy(text, tty_con.buffer);
583           Field_Clear(&tty_con);
584           key = '\n';
585           write(1, &key, 1);
586           return text;
587         }
588         if (key == '\t')
589         {
590           tty_Hide();
591           Field_AutoComplete( &tty_con );
592           tty_Show();
593           return NULL;
594         }
595         avail = read(0, &key, 1);
596         if (avail != -1)
597         {
598           // VT 100 keys
599           if (key == '[' || key == 'O')
600           {
601             avail = read(0, &key, 1);
602             if (avail != -1)
603             {
604               switch (key)
605               {
606               case 'A':
607                 history = Hist_Prev();
608                 if (history)
609                 {
610                   tty_Hide();
611                   tty_con = *history;
612                   tty_Show();
613                 }
614                 tty_FlushIn();
615                 return NULL;
616                 break;
617               case 'B':
618                 history = Hist_Next();
619                 tty_Hide();
620                 if (history)
621                 {
622                   tty_con = *history;
623                 } else
624                 {
625                   Field_Clear(&tty_con);
626                 }
627                 tty_Show();
628                 tty_FlushIn();
629                 return NULL;
630                 break;
631               case 'C':
632                 return NULL;
633               case 'D':
634                 return NULL;
635               }
636             }
637           }
638         }
639         Com_DPrintf("droping ISCTL sequence: %d, tty_erase: %d\n", key, tty_erase);
640         tty_FlushIn();
641         return NULL;
642       }
643       // push regular character
644       tty_con.buffer[tty_con.cursor] = key;
645       tty_con.cursor++;
646       // print the current line (this is differential)
647       write(1, &key, 1);
648     }
649     return NULL;
650   } else
651   {
652     int     len;
653     fd_set  fdset;
654     struct timeval timeout;
655 
656     if (!com_dedicated || !com_dedicated->value)
657       return NULL;
658 
659     if (!stdin_active)
660       return NULL;
661 
662     FD_ZERO(&fdset);
663     FD_SET(0, &fdset); // stdin
664     timeout.tv_sec = 0;
665     timeout.tv_usec = 0;
666     if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
667     {
668       return NULL;
669     }
670 
671     len = read (0, text, sizeof(text));
672     if (len == 0)
673     { // eof!
674       stdin_active = qfalse;
675       return NULL;
676     }
677 
678     if (len < 1)
679       return NULL;
680     text[len-1] = 0;    // rip off the /n and terminate
681 
682     return text;
683   }
684 }
685 
686 /*****************************************************************************/
687 
do_dlerror(void)688 char *do_dlerror(void)
689 {
690 #if USE_SDL_VIDEO
691     return SDL_GetError();
692 #else
693     return dlerror();
694 #endif
695 }
696 
697 
698 /*
699 =================
700 Sys_UnloadDll
701 
702 =================
703 */
Sys_UnloadDll(void * dllHandle)704 void Sys_UnloadDll( void *dllHandle ) {
705   // bk001206 - verbose error reporting
706   if ( !dllHandle )
707   {
708     Com_Printf("Sys_UnloadDll(NULL)\n");
709     return;
710   }
711 
712 #if USE_SDL_VIDEO
713   SDL_UnloadObject(dllHandle);
714 #else
715   dlclose( dllHandle );
716   {
717     const char* err; // rb010123 - now const
718     err = dlerror();
719     if ( err != NULL )
720       Com_Printf ( "Sys_UnloadGame failed on dlclose: \"%s\"!\n", err );
721   }
722 #endif
723 }
724 
725 
726 /*
727 =================
728 Sys_LoadDll
729 
730 Used to load a development dll instead of a virtual machine
731 TTimo:
732 changed the load procedure to match VFS logic, and allow developer use
733 #1 look down current path
734 #2 look in fs_homepath
735 #3 look in fs_basepath
736 =================
737 */
738 
try_dlopen(const char * base,const char * gamedir,const char * fname,char * fqpath)739 static void* try_dlopen(const char* base, const char* gamedir, const char* fname, char* fqpath )
740 {
741   void* libHandle;
742   char* fn;
743 
744   *fqpath = 0;
745 
746 // bk001129 - was RTLD_LAZY
747 #define Q_RTLD    RTLD_NOW
748 
749   fn = FS_BuildOSPath( base, gamedir, fname );
750   Com_Printf( "Sys_LoadDll(%s)... \n", fn );
751 
752 #if USE_SDL_VIDEO
753   libHandle = SDL_LoadObject(fn);
754 #else
755   libHandle = dlopen( fn, Q_RTLD );
756 #endif
757 
758   if(!libHandle) {
759     Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, do_dlerror() );
760     return NULL;
761   }
762 
763   Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn );
764   Q_strncpyz ( fqpath , fn , MAX_QPATH ) ;		// added 7/20/02 by T.Ray
765 
766   return libHandle;
767 }
768 
Sys_LoadDll(const char * name,char * fqpath,intptr_t (** entryPoint)(int,...),intptr_t (* systemcalls)(intptr_t,...))769 void *Sys_LoadDll( const char *name, char *fqpath ,
770                    intptr_t (**entryPoint)(int, ...),
771                    intptr_t (*systemcalls)(intptr_t, ...) )
772 {
773   void *libHandle;
774   void  (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) );
775   char  curpath[MAX_OSPATH];
776   char  fname[MAX_OSPATH];
777   char  *basepath;
778   char  *homepath;
779   char  *pwdpath;
780   char  *cdpath;
781   char  *gamedir;
782   const char*  err = NULL;
783 
784   // bk001206 - let's have some paranoia
785   assert( name );
786 
787   getcwd(curpath, sizeof(curpath));
788   snprintf (fname, sizeof(fname), "%s" ARCH_STRING DLL_EXT, name);
789 
790   // TODO: use fs_searchpaths from files.c
791   pwdpath = Sys_Cwd();
792   basepath = Cvar_VariableString( "fs_basepath" );
793   homepath = Cvar_VariableString( "fs_homepath" );
794   cdpath = Cvar_VariableString( "fs_cdpath" );
795   gamedir = Cvar_VariableString( "fs_game" );
796 
797   libHandle = try_dlopen(pwdpath, gamedir, fname, fqpath);
798 
799   if(!libHandle && homepath)
800     libHandle = try_dlopen(homepath, gamedir, fname, fqpath);
801 
802   if(!libHandle && basepath)
803     libHandle = try_dlopen(basepath, gamedir, fname, fqpath);
804 
805   if(!libHandle && cdpath)
806     libHandle = try_dlopen(cdpath, gamedir, fname, fqpath);
807 
808   if(!libHandle) {
809 #if 0 // don't abort -- ln
810 //#ifndef NDEBUG // bk001206 - in debug abort on failure
811     Com_Error ( ERR_FATAL, "Sys_LoadDll(%s) failed dlopen() completely!\n", name  );
812 #else
813     Com_Printf ( "Sys_LoadDll(%s) failed dlopen() completely!\n", name );
814 #endif
815     return NULL;
816   }
817 
818 #if USE_SDL_VIDEO
819   dllEntry = SDL_LoadFunction( libHandle, "dllEntry" );
820   *entryPoint = SDL_LoadFunction( libHandle, "vmMain" );
821 #else
822   dllEntry = dlsym( libHandle, "dllEntry" );
823   *entryPoint = dlsym( libHandle, "vmMain" );
824 #endif
825 
826   if ( !*entryPoint || !dllEntry )
827   {
828     err = do_dlerror();
829 #ifndef NDEBUG // bk001206 - in debug abort on failure
830     Com_Error ( ERR_FATAL, "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
831 #else
832     Com_Printf ( "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
833 #endif
834 #if USE_SDL_VIDEO
835     SDL_UnloadObject(libHandle);
836 #else
837     dlclose( libHandle );
838     err = do_dlerror();
839     if ( err != NULL ) {
840       Com_Printf ( "Sys_LoadDll(%s) failed dlcose:\n\"%s\"\n", name, err );
841     }
842 #endif
843 
844     return NULL;
845   }
846   Com_Printf ( "Sys_LoadDll(%s) found **vmMain** at  %p  \n", name, *entryPoint ); // bk001212
847   dllEntry( systemcalls );
848   Com_Printf ( "Sys_LoadDll(%s) succeeded!\n", name );
849   return libHandle;
850 }
851 
852 /*
853 ========================================================================
854 
855 BACKGROUND FILE STREAMING
856 
857 ========================================================================
858 */
859 
860 #if 1
861 
Sys_InitStreamThread(void)862 void Sys_InitStreamThread( void ) {
863 }
864 
Sys_ShutdownStreamThread(void)865 void Sys_ShutdownStreamThread( void ) {
866 }
867 
Sys_BeginStreamedFile(fileHandle_t f,int readAhead)868 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
869 }
870 
Sys_EndStreamedFile(fileHandle_t f)871 void Sys_EndStreamedFile( fileHandle_t f ) {
872 }
873 
Sys_StreamedRead(void * buffer,int size,int count,fileHandle_t f)874 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
875   return FS_Read( buffer, size * count, f );
876 }
877 
Sys_StreamSeek(fileHandle_t f,int offset,int origin)878 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
879   FS_Seek( f, offset, origin );
880 }
881 
882 #else
883 
884 typedef struct
885 {
886   fileHandle_t file;
887   byte  *buffer;
888   qboolean  eof;
889   int   bufferSize;
890   int   streamPosition; // next byte to be returned by Sys_StreamRead
891   int   threadPosition; // next byte to be read from file
892 } streamState_t;
893 
894 streamState_t stream;
895 
896 /*
897 ===============
898 Sys_StreamThread
899 
900 A thread will be sitting in this loop forever
901 ================
902 */
Sys_StreamThread(void)903 void Sys_StreamThread( void )
904 {
905   int   buffer;
906   int   count;
907   int   readCount;
908   int   bufferPoint;
909   int   r;
910 
911   // if there is any space left in the buffer, fill it up
912   if ( !stream.eof )
913   {
914     count = stream.bufferSize - (stream.threadPosition - stream.streamPosition);
915     if ( count )
916     {
917       bufferPoint = stream.threadPosition % stream.bufferSize;
918       buffer = stream.bufferSize - bufferPoint;
919       readCount = buffer < count ? buffer : count;
920       r = FS_Read ( stream.buffer + bufferPoint, readCount, stream.file );
921       stream.threadPosition += r;
922 
923       if ( r != readCount )
924         stream.eof = qtrue;
925     }
926   }
927 }
928 
929 /*
930 ===============
931 Sys_InitStreamThread
932 
933 ================
934 */
Sys_InitStreamThread(void)935 void Sys_InitStreamThread( void )
936 {
937 }
938 
939 /*
940 ===============
941 Sys_ShutdownStreamThread
942 
943 ================
944 */
Sys_ShutdownStreamThread(void)945 void Sys_ShutdownStreamThread( void )
946 {
947 }
948 
949 
950 /*
951 ===============
952 Sys_BeginStreamedFile
953 
954 ================
955 */
Sys_BeginStreamedFile(fileHandle_t f,int readAhead)956 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead )
957 {
958   if ( stream.file )
959   {
960     Com_Error( ERR_FATAL, "Sys_BeginStreamedFile: unclosed stream");
961   }
962 
963   stream.file = f;
964   stream.buffer = Z_Malloc( readAhead );
965   stream.bufferSize = readAhead;
966   stream.streamPosition = 0;
967   stream.threadPosition = 0;
968   stream.eof = qfalse;
969 }
970 
971 /*
972 ===============
973 Sys_EndStreamedFile
974 
975 ================
976 */
Sys_EndStreamedFile(fileHandle_t f)977 void Sys_EndStreamedFile( fileHandle_t f )
978 {
979   if ( f != stream.file )
980   {
981     Com_Error( ERR_FATAL, "Sys_EndStreamedFile: wrong file");
982   }
983 
984   stream.file = 0;
985   Z_Free( stream.buffer );
986 }
987 
988 
989 /*
990 ===============
991 Sys_StreamedRead
992 
993 ================
994 */
Sys_StreamedRead(void * buffer,int size,int count,fileHandle_t f)995 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f )
996 {
997   int   available;
998   int   remaining;
999   int   sleepCount;
1000   int   copy;
1001   int   bufferCount;
1002   int   bufferPoint;
1003   byte  *dest;
1004 
1005   dest = (byte *)buffer;
1006   remaining = size * count;
1007 
1008   if ( remaining <= 0 )
1009   {
1010     Com_Error( ERR_FATAL, "Streamed read with non-positive size" );
1011   }
1012 
1013   sleepCount = 0;
1014   while ( remaining > 0 )
1015   {
1016     available = stream.threadPosition - stream.streamPosition;
1017     if ( !available )
1018     {
1019       if (stream.eof)
1020         break;
1021       Sys_StreamThread();
1022       continue;
1023     }
1024 
1025     bufferPoint = stream.streamPosition % stream.bufferSize;
1026     bufferCount = stream.bufferSize - bufferPoint;
1027 
1028     copy = available < bufferCount ? available : bufferCount;
1029     if ( copy > remaining )
1030     {
1031       copy = remaining;
1032     }
1033     memcpy( dest, stream.buffer + bufferPoint, copy );
1034     stream.streamPosition += copy;
1035     dest += copy;
1036     remaining -= copy;
1037   }
1038 
1039   return(count * size - remaining) / size;
1040 }
1041 
1042 /*
1043 ===============
1044 Sys_StreamSeek
1045 
1046 ================
1047 */
Sys_StreamSeek(fileHandle_t f,int offset,int origin)1048 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
1049   // clear to that point
1050   FS_Seek( f, offset, origin );
1051   stream.streamPosition = 0;
1052   stream.threadPosition = 0;
1053   stream.eof = qfalse;
1054 }
1055 
1056 #endif
1057 
1058 /*
1059 ========================================================================
1060 
1061 EVENT LOOP
1062 
1063 ========================================================================
1064 */
1065 
1066 // bk000306: upped this from 64
1067 #define	MAX_QUED_EVENTS		256
1068 #define	MASK_QUED_EVENTS	( MAX_QUED_EVENTS - 1 )
1069 
1070 sysEvent_t  eventQue[MAX_QUED_EVENTS];
1071 // bk000306: initialize
1072 int   eventHead = 0;
1073 int             eventTail = 0;
1074 byte    sys_packetReceived[MAX_MSGLEN];
1075 
1076 /*
1077 ================
1078 Sys_QueEvent
1079 
1080 A time of 0 will get the current time
1081 Ptr should either be null, or point to a block of data that can
1082 be freed by the game later.
1083 ================
1084 */
Sys_QueEvent(int time,sysEventType_t type,int value,int value2,int ptrLength,void * ptr)1085 void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
1086   sysEvent_t  *ev;
1087 
1088   ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
1089 
1090   // bk000305 - was missing
1091   if ( eventHead - eventTail >= MAX_QUED_EVENTS )
1092   {
1093     Com_Printf("Sys_QueEvent: overflow\n");
1094     // we are discarding an event, but don't leak memory
1095     if ( ev->evPtr )
1096     {
1097       Z_Free( ev->evPtr );
1098     }
1099     eventTail++;
1100   }
1101 
1102   eventHead++;
1103 
1104   if ( time == 0 )
1105   {
1106     time = Sys_Milliseconds();
1107   }
1108 
1109   ev->evTime = time;
1110   ev->evType = type;
1111   ev->evValue = value;
1112   ev->evValue2 = value2;
1113   ev->evPtrLength = ptrLength;
1114   ev->evPtr = ptr;
1115 }
1116 
1117 /*
1118 ================
1119 Sys_GetEvent
1120 
1121 ================
1122 */
Sys_GetEvent(void)1123 sysEvent_t Sys_GetEvent( void ) {
1124   sysEvent_t  ev;
1125   char    *s;
1126   msg_t   netmsg;
1127   netadr_t  adr;
1128 
1129   // return if we have data
1130   if ( eventHead > eventTail )
1131   {
1132     eventTail++;
1133     return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
1134   }
1135 
1136   // pump the message loop
1137   // in vga this calls KBD_Update, under X, it calls GetEvent
1138   Sys_SendKeyEvents ();
1139 
1140   // check for console commands
1141   s = Sys_ConsoleInput();
1142   if ( s )
1143   {
1144     char  *b;
1145     int   len;
1146 
1147     len = strlen( s ) + 1;
1148     b = Z_Malloc( len );
1149     strcpy( b, s );
1150     Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
1151   }
1152 
1153   // check for other input devices
1154   IN_Frame();
1155 
1156   // check for network packets
1157   MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) );
1158   if ( Sys_GetPacket ( &adr, &netmsg ) )
1159   {
1160     netadr_t    *buf;
1161     int       len;
1162 
1163     // copy out to a seperate buffer for qeueing
1164     len = sizeof( netadr_t ) + netmsg.cursize;
1165     buf = Z_Malloc( len );
1166     *buf = adr;
1167     memcpy( buf+1, netmsg.data, netmsg.cursize );
1168     Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf );
1169   }
1170 
1171   // return if we have data
1172   if ( eventHead > eventTail )
1173   {
1174     eventTail++;
1175     return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
1176   }
1177 
1178   // create an empty event to return
1179 
1180   memset( &ev, 0, sizeof( ev ) );
1181   ev.evTime = Sys_Milliseconds();
1182 
1183   return ev;
1184 }
1185 
1186 /*****************************************************************************/
1187 
Sys_CheckCD(void)1188 qboolean Sys_CheckCD( void ) {
1189   return qtrue;
1190 }
1191 
Sys_AppActivate(void)1192 void Sys_AppActivate (void)
1193 {
1194 }
1195 
Sys_GetClipboardData(void)1196 char *Sys_GetClipboardData(void)
1197 {
1198   return NULL;
1199 }
1200 
1201 static struct Q3ToAnsiColorTable_s
1202 {
1203   char Q3color;
1204   char *ANSIcolor;
1205 } tty_colorTable[ ] =
1206 {
1207   { COLOR_BLACK,    "30" },
1208   { COLOR_RED,      "31" },
1209   { COLOR_GREEN,    "32" },
1210   { COLOR_YELLOW,   "33" },
1211   { COLOR_BLUE,     "34" },
1212   { COLOR_CYAN,     "36" },
1213   { COLOR_MAGENTA,  "35" },
1214   { COLOR_WHITE,    "0" }
1215 };
1216 
1217 static int tty_colorTableSize =
1218   sizeof( tty_colorTable ) / sizeof( tty_colorTable[ 0 ] );
1219 
Sys_ANSIColorify(const char * msg,char * buffer,int bufferSize)1220 void Sys_ANSIColorify( const char *msg, char *buffer, int bufferSize )
1221 {
1222   int   msgLength, pos;
1223   int   i, j;
1224   char  *escapeCode;
1225   char  tempBuffer[ 7 ];
1226 
1227   if( !msg || !buffer )
1228     return;
1229 
1230   msgLength = strlen( msg );
1231   pos = 0;
1232   i = 0;
1233   buffer[ 0 ] = '\0';
1234 
1235   while( i < msgLength )
1236   {
1237     if( msg[ i ] == '\n' )
1238     {
1239       Com_sprintf( tempBuffer, 7, "%c[0m\n", 0x1B );
1240       strncat( buffer, tempBuffer, bufferSize );
1241       i++;
1242     }
1243     else if( msg[ i ] == Q_COLOR_ESCAPE )
1244     {
1245       i++;
1246 
1247       if( i < msgLength )
1248       {
1249         escapeCode = NULL;
1250         for( j = 0; j < tty_colorTableSize; j++ )
1251         {
1252           if( msg[ i ] == tty_colorTable[ j ].Q3color )
1253           {
1254             escapeCode = tty_colorTable[ j ].ANSIcolor;
1255             break;
1256           }
1257         }
1258 
1259         if( escapeCode )
1260         {
1261           Com_sprintf( tempBuffer, 7, "%c[%sm", 0x1B, escapeCode );
1262           strncat( buffer, tempBuffer, bufferSize );
1263         }
1264 
1265         i++;
1266       }
1267     }
1268     else
1269     {
1270       Com_sprintf( tempBuffer, 7, "%c", msg[ i++ ] );
1271       strncat( buffer, tempBuffer, bufferSize );
1272     }
1273   }
1274 }
1275 
Sys_Print(const char * msg)1276 void  Sys_Print( const char *msg )
1277 {
1278   if (ttycon_on)
1279   {
1280     tty_Hide();
1281   }
1282 
1283   if( ttycon_on && ttycon_color_on )
1284   {
1285     char ansiColorString[ MAXPRINTMSG ];
1286     Sys_ANSIColorify( msg, ansiColorString, MAXPRINTMSG );
1287     fputs( ansiColorString, stderr );
1288   }
1289   else
1290     fputs(msg, stderr);
1291 
1292   if (ttycon_on)
1293   {
1294     tty_Show();
1295   }
1296 }
1297 
1298 
Sys_ConfigureFPU(void)1299 void    Sys_ConfigureFPU( void ) { // bk001213 - divide by zero
1300 #ifdef __linux__
1301 #ifdef __i386
1302 #ifndef NDEBUG
1303 
1304   // bk0101022 - enable FPE's in debug mode
1305   static int fpu_word = _FPU_DEFAULT & ~(_FPU_MASK_ZM | _FPU_MASK_IM);
1306   int current = 0;
1307   _FPU_GETCW(current);
1308   if ( current!=fpu_word)
1309   {
1310 #if 0
1311     Com_Printf("FPU Control 0x%x (was 0x%x)\n", fpu_word, current );
1312     _FPU_SETCW( fpu_word );
1313     _FPU_GETCW( current );
1314     assert(fpu_word==current);
1315 #endif
1316   }
1317 #else // NDEBUG
1318   static int fpu_word = _FPU_DEFAULT;
1319   _FPU_SETCW( fpu_word );
1320 #endif // NDEBUG
1321 #endif // __i386
1322 #endif // __linux
1323 }
1324 
Sys_PrintBinVersion(const char * name)1325 void Sys_PrintBinVersion( const char* name ) {
1326   char* date = __DATE__;
1327   char* time = __TIME__;
1328   char* sep = "==============================================================";
1329   fprintf( stdout, "\n\n%s\n", sep );
1330 #ifdef DEDICATED
1331   fprintf( stdout, "Linux Tremulous Dedicated Server [%s %s]\n", date, time );
1332 #else
1333   fprintf( stdout, "Linux Tremulous Full Executable  [%s %s]\n", date, time );
1334 #endif
1335   fprintf( stdout, " local install: %s\n", name );
1336   fprintf( stdout, "%s\n\n", sep );
1337 }
1338 
1339 /*
1340 =================
1341 Sys_BinName
1342 
1343 This resolves any symlinks to the binary. It's disabled for debug
1344 builds because there are situations where you are likely to want
1345 to symlink to binaries and /not/ have the links resolved.
1346 =================
1347 */
Sys_BinName(const char * arg0)1348 char *Sys_BinName( const char *arg0 )
1349 {
1350   static char   dst[ PATH_MAX ];
1351 
1352 #ifdef NDEBUG
1353 
1354 #ifdef __linux__
1355   int n = readlink( "/proc/self/exe", dst, PATH_MAX - 1 );
1356 
1357   if( n >= 0 && n < PATH_MAX )
1358     dst[ n ] = '\0';
1359   else
1360     Q_strncpyz( dst, arg0, PATH_MAX );
1361 #else
1362 #warning Sys_BinName not implemented
1363   Q_strncpyz( dst, arg0, PATH_MAX );
1364 #endif
1365 
1366 #else
1367   Q_strncpyz( dst, arg0, PATH_MAX );
1368 #endif
1369 
1370   return dst;
1371 }
1372 
Sys_ParseArgs(int argc,char * argv[])1373 void Sys_ParseArgs( int argc, char* argv[] ) {
1374 
1375   if ( argc==2 )
1376   {
1377     if ( (!strcmp( argv[1], "--version" ))
1378          || ( !strcmp( argv[1], "-v" )) )
1379     {
1380       Sys_PrintBinVersion( Sys_BinName( argv[0] ) );
1381       Sys_Exit(0);
1382     }
1383   }
1384 }
1385 
1386 #ifndef DEFAULT_BASEDIR
1387 # define DEFAULT_BASEDIR Sys_DefaultCDPath()
1388 #endif
1389 
1390 #include "../client/client.h"
1391 extern clientStatic_t cls;
1392 
main(int argc,char * argv[])1393 int main ( int argc, char* argv[] )
1394 {
1395   // int 	oldtime, newtime; // bk001204 - unused
1396   int   len, i;
1397   char  *cmdline;
1398   char cdpath[PATH_MAX] = {0};
1399   void Sys_SetDefaultCDPath(const char *path);
1400 
1401   Sys_ParseArgs( argc, argv );  // bk010104 - added this for support
1402 
1403   strncat(cdpath, Sys_BinName( argv[0] ), sizeof(cdpath)-1);
1404   Sys_SetDefaultCDPath(dirname(cdpath));
1405 
1406   Sys_SetDefaultInstallPath(DEFAULT_BASEDIR);
1407 
1408   // merge the command line, this is kinda silly
1409   for (len = 1, i = 1; i < argc; i++)
1410     len += strlen(argv[i]) + 1;
1411   cmdline = malloc(len);
1412   *cmdline = 0;
1413   for (i = 1; i < argc; i++)
1414   {
1415     if (i > 1)
1416       strcat(cmdline, " ");
1417     strcat(cmdline, argv[i]);
1418   }
1419 
1420   // bk000306 - clear queues
1421   memset( &eventQue[0], 0, MAX_QUED_EVENTS*sizeof(sysEvent_t) );
1422   memset( &sys_packetReceived[0], 0, MAX_MSGLEN*sizeof(byte) );
1423 
1424   Com_Init(cmdline);
1425   NET_Init();
1426 
1427   Sys_ConsoleInputInit();
1428 
1429   fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
1430 
1431 #ifdef DEDICATED
1432 	// init here for dedicated, as we don't have GLimp_Init
1433 	InitSig();
1434 #endif
1435 
1436   while (1)
1437   {
1438 #if !defined( DEDICATED ) && USE_SDL_VIDEO
1439     int appState = SDL_GetAppState( );
1440 
1441     // If we have no input focus at all, sleep a bit
1442     if( !( appState & ( SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS ) ) )
1443       usleep( 16000 );
1444 
1445     // If we're minimised, sleep a bit more
1446     if( !( appState & SDL_APPACTIVE ) )
1447       usleep( 32000 );
1448 #endif
1449 
1450 #ifdef __linux__
1451     Sys_ConfigureFPU();
1452 #endif
1453     Com_Frame ();
1454   }
1455 
1456   return 0;
1457 }
1458 
1459