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