1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <limits.h>
26 #include <sys/types.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <errno.h>
33
34 #ifndef DEDICATED
35 #ifdef USE_LOCAL_HEADERS
36 # include "SDL.h"
37 # include "SDL_cpuinfo.h"
38 #else
39 # include <SDL.h>
40 # include <SDL_cpuinfo.h>
41 #endif
42 #endif
43
44 #include "sys_local.h"
45 #include "sys_loadlib.h"
46
47 #include "../qcommon/q_shared.h"
48 #include "../qcommon/qcommon.h"
49
50 static char binaryPath[ MAX_OSPATH ] = { 0 };
51 static char installPath[ MAX_OSPATH ] = { 0 };
52
53 /*
54 =================
55 Sys_SetBinaryPath
56 =================
57 */
Sys_SetBinaryPath(const char * path)58 void Sys_SetBinaryPath(const char *path)
59 {
60 Q_strncpyz(binaryPath, path, sizeof(binaryPath));
61 }
62
63 /*
64 =================
65 Sys_BinaryPath
66 =================
67 */
Sys_BinaryPath(void)68 char *Sys_BinaryPath(void)
69 {
70 return binaryPath;
71 }
72
73 /*
74 =================
75 Sys_SetDefaultInstallPath
76 =================
77 */
Sys_SetDefaultInstallPath(const char * path)78 void Sys_SetDefaultInstallPath(const char *path)
79 {
80 Q_strncpyz(installPath, path, sizeof(installPath));
81 }
82
83 /*
84 =================
85 Sys_DefaultInstallPath
86 =================
87 */
Sys_DefaultInstallPath(void)88 char *Sys_DefaultInstallPath(void)
89 {
90 if (*installPath)
91 return installPath;
92 else
93 return Sys_Cwd();
94 }
95
96 /*
97 =================
98 Sys_DefaultAppPath
99 =================
100 */
Sys_DefaultAppPath(void)101 char *Sys_DefaultAppPath(void)
102 {
103 return Sys_BinaryPath();
104 }
105
106 /*
107 =================
108 Sys_In_Restart_f
109
110 Restart the input subsystem
111 =================
112 */
Sys_In_Restart_f(void)113 void Sys_In_Restart_f( void )
114 {
115 #ifndef DEDICATED
116 if( !SDL_WasInit( SDL_INIT_VIDEO ) )
117 {
118 Com_Printf( "in_restart: Cannot restart input while video is shutdown\n" );
119 return;
120 }
121 #endif
122
123 IN_Restart( );
124 }
125
126 /*
127 =================
128 Sys_ConsoleInput
129
130 Handle new console input
131 =================
132 */
Sys_ConsoleInput(void)133 char *Sys_ConsoleInput(void)
134 {
135 return CON_Input( );
136 }
137
138 /*
139 ==================
140 Sys_GetClipboardData
141 ==================
142 */
Sys_GetClipboardData(void)143 char *Sys_GetClipboardData(void)
144 {
145 #ifdef DEDICATED
146 return NULL;
147 #else
148 char *data = NULL;
149 char *cliptext;
150
151 if ( ( cliptext = SDL_GetClipboardText() ) != NULL ) {
152 if ( cliptext[0] != '\0' ) {
153 size_t bufsize = strlen( cliptext ) + 1;
154
155 data = Z_Malloc( bufsize );
156 Q_strncpyz( data, cliptext, bufsize );
157
158 // find first listed char and set to '\0'
159 strtok( data, "\n\r\b" );
160 }
161 SDL_free( cliptext );
162 }
163 return data;
164 #endif
165 }
166
167 #ifdef DEDICATED
168 # define PID_FILENAME "iowolfsp_server.pid"
169 #else
170 # define PID_FILENAME "iowolfsp.pid"
171 #endif
172
173 /*
174 =================
175 Sys_PIDFileName
176 =================
177 */
Sys_PIDFileName(const char * gamedir)178 static char *Sys_PIDFileName( const char *gamedir )
179 {
180 const char *homePath = Cvar_VariableString( "fs_homepath" );
181
182 if( *homePath != '\0' )
183 return va( "%s/%s/%s", homePath, gamedir, PID_FILENAME );
184
185 return NULL;
186 }
187
188 /*
189 =================
190 Sys_RemovePIDFile
191 =================
192 */
Sys_RemovePIDFile(const char * gamedir)193 void Sys_RemovePIDFile( const char *gamedir )
194 {
195 char *pidFile = Sys_PIDFileName( gamedir );
196
197 if( pidFile != NULL )
198 remove( pidFile );
199 }
200
201 /*
202 =================
203 Sys_WritePIDFile
204
205 Return qtrue if there is an existing stale PID file
206 =================
207 */
Sys_WritePIDFile(const char * gamedir)208 static qboolean Sys_WritePIDFile( const char *gamedir )
209 {
210 char *pidFile = Sys_PIDFileName( gamedir );
211 FILE *f;
212 qboolean stale = qfalse;
213
214 if( pidFile == NULL )
215 return qfalse;
216
217 // First, check if the pid file is already there
218 if( ( f = fopen( pidFile, "r" ) ) != NULL )
219 {
220 char pidBuffer[ 64 ] = { 0 };
221 int pid;
222
223 pid = fread( pidBuffer, sizeof( char ), sizeof( pidBuffer ) - 1, f );
224 fclose( f );
225
226 if(pid > 0)
227 {
228 pid = atoi( pidBuffer );
229 if( !Sys_PIDIsRunning( pid ) )
230 stale = qtrue;
231 }
232 else
233 stale = qtrue;
234 }
235
236 if( FS_CreatePath( pidFile ) ) {
237 return 0;
238 }
239
240 if( ( f = fopen( pidFile, "w" ) ) != NULL )
241 {
242 fprintf( f, "%d", Sys_PID( ) );
243 fclose( f );
244 }
245 else
246 Com_Printf( S_COLOR_YELLOW "Couldn't write %s.\n", pidFile );
247
248 return stale;
249 }
250
251 /*
252 =================
253 Sys_InitPIDFile
254 =================
255 */
Sys_InitPIDFile(const char * gamedir)256 void Sys_InitPIDFile( const char *gamedir ) {
257 if( Sys_WritePIDFile( gamedir ) ) {
258 #ifndef DEDICATED
259 char message[1024];
260 char modName[MAX_OSPATH];
261
262 FS_GetModDescription( gamedir, modName, sizeof ( modName ) );
263 Q_CleanStr( modName );
264
265 Com_sprintf( message, sizeof (message), "The last time %s ran, "
266 "it didn't exit properly. This may be due to inappropriate video "
267 "settings. Would you like to start with \"safe\" video settings?", modName );
268
269 if( Sys_Dialog( DT_YES_NO, message, "Abnormal Exit" ) == DR_YES ) {
270 Cvar_Set( "com_abnormalExit", "1" );
271 }
272 #endif
273 }
274 }
275
276 /*
277 =================
278 Sys_Exit
279
280 Single exit point (regular exit or in case of error)
281 =================
282 */
Sys_Exit(int exitCode)283 static __attribute__ ((noreturn)) void Sys_Exit( int exitCode )
284 {
285 CON_Shutdown( );
286
287 #ifndef DEDICATED
288 SDL_Quit( );
289 #endif
290
291 if( exitCode < 2 && com_fullyInitialized )
292 {
293 // Normal exit
294 Sys_RemovePIDFile( FS_GetCurrentGameDir() );
295 }
296
297 NET_Shutdown( );
298
299 Sys_PlatformExit( );
300
301 exit( exitCode );
302 }
303
304 /*
305 =================
306 Sys_Quit
307 =================
308 */
Sys_Quit(void)309 void Sys_Quit( void )
310 {
311 Sys_Exit( 0 );
312 }
313
314 /*
315 =================
316 Sys_GetProcessorFeatures
317 =================
318 */
Sys_GetProcessorFeatures(void)319 cpuFeatures_t Sys_GetProcessorFeatures( void )
320 {
321 cpuFeatures_t features = 0;
322
323 #ifndef DEDICATED
324 if( SDL_HasRDTSC( ) ) features |= CF_RDTSC;
325 if( SDL_Has3DNow( ) ) features |= CF_3DNOW;
326 if( SDL_HasMMX( ) ) features |= CF_MMX;
327 if( SDL_HasSSE( ) ) features |= CF_SSE;
328 if( SDL_HasSSE2( ) ) features |= CF_SSE2;
329 if( SDL_HasAltiVec( ) ) features |= CF_ALTIVEC;
330 #endif
331
332 return features;
333 }
334
335 /*
336 =================
337 Sys_Init
338 =================
339 */
Sys_Init(void)340 void Sys_Init(void)
341 {
342 Cmd_AddCommand( "in_restart", Sys_In_Restart_f );
343 Cvar_Set( "arch", OS_STRING " " ARCH_STRING );
344 Cvar_Set( "username", Sys_GetCurrentUser( ) );
345 }
346
347 /*
348 =================
349 Sys_AnsiColorPrint
350
351 Transform Q3 colour codes to ANSI escape sequences
352 =================
353 */
Sys_AnsiColorPrint(const char * msg)354 void Sys_AnsiColorPrint( const char *msg )
355 {
356 static char buffer[ MAXPRINTMSG ];
357 int length = 0;
358 static int q3ToAnsi[ 8 ] =
359 {
360 30, // COLOR_BLACK
361 31, // COLOR_RED
362 32, // COLOR_GREEN
363 33, // COLOR_YELLOW
364 34, // COLOR_BLUE
365 36, // COLOR_CYAN
366 35, // COLOR_MAGENTA
367 0 // COLOR_WHITE
368 };
369
370 while( *msg )
371 {
372 if( Q_IsColorString( msg ) || *msg == '\n' )
373 {
374 // First empty the buffer
375 if( length > 0 )
376 {
377 buffer[ length ] = '\0';
378 fputs( buffer, stderr );
379 length = 0;
380 }
381
382 if( *msg == '\n' )
383 {
384 // Issue a reset and then the newline
385 fputs( "\033[0m\n", stderr );
386 msg++;
387 }
388 else
389 {
390 // Print the color code
391 Com_sprintf( buffer, sizeof( buffer ), "\033[%dm",
392 q3ToAnsi[ ColorIndex( *( msg + 1 ) ) ] );
393 fputs( buffer, stderr );
394 msg += 2;
395 }
396 }
397 else
398 {
399 if( length >= MAXPRINTMSG - 1 )
400 break;
401
402 buffer[ length ] = *msg;
403 length++;
404 msg++;
405 }
406 }
407
408 // Empty anything still left in the buffer
409 if( length > 0 )
410 {
411 buffer[ length ] = '\0';
412 fputs( buffer, stderr );
413 }
414 }
415
416 /*
417 =================
418 Sys_Print
419 =================
420 */
Sys_Print(const char * msg)421 void Sys_Print( const char *msg )
422 {
423 CON_LogWrite( msg );
424 CON_Print( msg );
425 }
426
427 /*
428 =================
429 Sys_Error
430 =================
431 */
Sys_Error(const char * error,...)432 void Sys_Error( const char *error, ... )
433 {
434 va_list argptr;
435 char string[1024];
436
437 va_start (argptr,error);
438 Q_vsnprintf (string, sizeof(string), error, argptr);
439 va_end (argptr);
440
441 Sys_ErrorDialog( string );
442
443 Sys_Exit( 3 );
444 }
445
446 #if 0
447 /*
448 =================
449 Sys_Warn
450 =================
451 */
452 static __attribute__ ((format (printf, 1, 2))) void Sys_Warn( char *warning, ... )
453 {
454 va_list argptr;
455 char string[1024];
456
457 va_start (argptr,warning);
458 Q_vsnprintf (string, sizeof(string), warning, argptr);
459 va_end (argptr);
460
461 CON_Print( va( "Warning: %s", string ) );
462 }
463 #endif
464
465 /*
466 ============
467 Sys_FileTime
468
469 returns -1 if not present
470 ============
471 */
Sys_FileTime(char * path)472 int Sys_FileTime( char *path )
473 {
474 struct stat buf;
475
476 if (stat (path,&buf) == -1)
477 return -1;
478
479 return buf.st_mtime;
480 }
481
482 /*
483 =================
484 Sys_UnloadDll
485 =================
486 */
Sys_UnloadDll(void * dllHandle)487 void Sys_UnloadDll( void *dllHandle )
488 {
489 if( !dllHandle )
490 {
491 Com_Printf("Sys_UnloadDll(NULL)\n");
492 return;
493 }
494
495 Sys_UnloadLibrary(dllHandle);
496 }
497
498 /*
499 =================
500 Sys_LoadDll
501
502 First try to load library name from system library path,
503 from executable path, then fs_basepath.
504 =================
505 */
506
Sys_LoadDll(const char * name,qboolean useSystemLib)507 void *Sys_LoadDll(const char *name, qboolean useSystemLib)
508 {
509 void *dllhandle = NULL;
510
511 if(!Sys_DllExtension(name))
512 {
513 Com_Printf("Refusing to attempt to load library \"%s\": Extension not allowed.\n", name);
514 return NULL;
515 }
516
517 if(useSystemLib)
518 {
519 Com_Printf("Trying to load \"%s\"...\n", name);
520 dllhandle = Sys_LoadLibrary(name);
521 }
522
523 if(!dllhandle)
524 {
525 const char *topDir;
526 char libPath[MAX_OSPATH];
527 int len;
528
529 topDir = Sys_BinaryPath();
530
531 if(!*topDir)
532 topDir = ".";
533
534 len = Com_sprintf(libPath, sizeof(libPath), "%s%c%s", topDir, PATH_SEP, name);
535 if(len < sizeof(libPath))
536 {
537 Com_Printf("Trying to load \"%s\" from \"%s\"...\n", name, topDir);
538 dllhandle = Sys_LoadLibrary(libPath);
539 }
540 else
541 {
542 Com_Printf("Skipping trying to load \"%s\" from \"%s\", file name is too long.\n", name, topDir);
543 }
544
545 if(!dllhandle)
546 {
547 const char *basePath = Cvar_VariableString("fs_basepath");
548
549 if(!basePath || !*basePath)
550 basePath = ".";
551
552 if(FS_FilenameCompare(topDir, basePath))
553 {
554 len = Com_sprintf(libPath, sizeof(libPath), "%s%c%s", basePath, PATH_SEP, name);
555 if(len < sizeof(libPath))
556 {
557 Com_Printf("Trying to load \"%s\" from \"%s\"...\n", name, basePath);
558 dllhandle = Sys_LoadLibrary(libPath);
559 }
560 else
561 {
562 Com_Printf("Skipping trying to load \"%s\" from \"%s\", file name is too long.\n", name, basePath);
563 }
564 }
565
566 if(!dllhandle)
567 Com_Printf("Loading \"%s\" failed\n", name);
568 }
569 }
570
571 return dllhandle;
572 }
573
574 /*
575 =================
576 Sys_LoadGameDll
577
578 Used to load a development dll instead of a virtual machine
579 =================
580 */
Sys_LoadGameDll(const char * name,intptr_t (QDECL ** entryPoint)(intptr_t,...),intptr_t (* systemcalls)(intptr_t,...))581 void *Sys_LoadGameDll(const char *name,
582 intptr_t (QDECL **entryPoint)(intptr_t, ...),
583 intptr_t (*systemcalls)(intptr_t, ...))
584 {
585 void *libHandle;
586 void (*dllEntry)(intptr_t (*syscallptr)(intptr_t, ...));
587
588 assert(name);
589
590 if(!Sys_DllExtension(name))
591 {
592 Com_Printf("Refusing to attempt to load library \"%s\": Extension not allowed.\n", name);
593 return NULL;
594 }
595
596 Com_DPrintf( "Loading DLL file: %s\n", name);
597 libHandle = Sys_LoadLibrary(name);
598
599 if(!libHandle)
600 {
601 Com_Printf("Sys_LoadGameDll(%s) failed:\n\"%s\"\n", name, Sys_LibraryError());
602 return NULL;
603 }
604
605 dllEntry = Sys_LoadFunction( libHandle, "dllEntry" );
606 *entryPoint = Sys_LoadFunction( libHandle, "vmMain" );
607
608 if ( !*entryPoint || !dllEntry )
609 {
610 Com_Printf ( "Sys_LoadGameDll(%s) failed to find vmMain function:\n\"%s\" !\n", name, Sys_LibraryError( ) );
611 Sys_UnloadLibrary(libHandle);
612
613 return NULL;
614 }
615
616 Com_DPrintf ( "Sys_LoadGameDll(%s) found vmMain function at %p\n", name, *entryPoint );
617 dllEntry( systemcalls );
618
619 return libHandle;
620 }
621
622 /*
623 =================
624 Sys_ParseArgs
625 =================
626 */
Sys_ParseArgs(int argc,char ** argv)627 void Sys_ParseArgs( int argc, char **argv )
628 {
629 if( argc == 2 )
630 {
631 if( !strcmp( argv[1], "--version" ) ||
632 !strcmp( argv[1], "-v" ) )
633 {
634 const char* date = PRODUCT_DATE;
635 #ifdef DEDICATED
636 fprintf( stdout, Q3_VERSION " dedicated server (%s)\n", date );
637 #else
638 fprintf( stdout, Q3_VERSION " client (%s)\n", date );
639 #endif
640 Sys_Exit( 0 );
641 }
642 }
643 }
644
645 #ifndef DEFAULT_BASEDIR
646 # ifdef __APPLE__
647 # define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath())
648 # else
649 # define DEFAULT_BASEDIR Sys_BinaryPath()
650 # endif
651 #endif
652
653 /*
654 =================
655 Sys_SigHandler
656 =================
657 */
Sys_SigHandler(int signal)658 void Sys_SigHandler( int signal )
659 {
660 static qboolean signalcaught = qfalse;
661
662 if( signalcaught )
663 {
664 fprintf( stderr, "DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n",
665 signal );
666 }
667 else
668 {
669 signalcaught = qtrue;
670 VM_Forced_Unload_Start();
671 #ifndef DEDICATED
672 CL_Shutdown(va("Received signal %d", signal), qtrue, qtrue);
673 #endif
674 SV_Shutdown(va("Received signal %d", signal) );
675 VM_Forced_Unload_Done();
676 }
677
678 if( signal == SIGTERM || signal == SIGINT )
679 Sys_Exit( 1 );
680 else
681 Sys_Exit( 2 );
682 }
683
684 /*
685 =================
686 main
687 =================
688 */
main(int argc,char ** argv)689 int main( int argc, char **argv )
690 {
691 int i;
692 char commandLine[ MAX_STRING_CHARS ] = { 0 };
693
694 #ifndef DEDICATED
695 // SDL version check
696
697 // Compile time
698 # if !SDL_VERSION_ATLEAST(MINSDL_MAJOR,MINSDL_MINOR,MINSDL_PATCH)
699 # error A more recent version of SDL is required
700 # endif
701
702 // Run time
703 SDL_version ver;
704 SDL_GetVersion( &ver );
705
706 #define MINSDL_VERSION \
707 XSTRING(MINSDL_MAJOR) "." \
708 XSTRING(MINSDL_MINOR) "." \
709 XSTRING(MINSDL_PATCH)
710
711 if( SDL_VERSIONNUM( ver.major, ver.minor, ver.patch ) <
712 SDL_VERSIONNUM( MINSDL_MAJOR, MINSDL_MINOR, MINSDL_PATCH ) )
713 {
714 Sys_Dialog( DT_ERROR, va( "SDL version " MINSDL_VERSION " or greater is required, "
715 "but only version %d.%d.%d was found. You may be able to obtain a more recent copy "
716 "from http://www.libsdl.org/.", ver.major, ver.minor, ver.patch ), "SDL Library Too Old" );
717
718 Sys_Exit( 1 );
719 }
720 #endif
721
722 Sys_PlatformInit( );
723
724 // Set the initial time base
725 Sys_Milliseconds( );
726
727 #ifdef __APPLE__
728 // This is passed if we are launched by double-clicking
729 if ( argc >= 2 && Q_strncmp ( argv[1], "-psn", 4 ) == 0 )
730 argc = 1;
731 #endif
732
733 Sys_ParseArgs( argc, argv );
734 Sys_SetBinaryPath( Sys_Dirname( argv[ 0 ] ) );
735 Sys_SetDefaultInstallPath( DEFAULT_BASEDIR );
736
737 // Concatenate the command line for passing to Com_Init
738 for( i = 1; i < argc; i++ )
739 {
740 const qboolean containsSpaces = strchr(argv[i], ' ') != NULL;
741 if (containsSpaces)
742 Q_strcat( commandLine, sizeof( commandLine ), "\"" );
743
744 Q_strcat( commandLine, sizeof( commandLine ), argv[ i ] );
745
746 if (containsSpaces)
747 Q_strcat( commandLine, sizeof( commandLine ), "\"" );
748
749 Q_strcat( commandLine, sizeof( commandLine ), " " );
750 }
751
752 CON_Init( );
753 Com_Init( commandLine );
754 NET_Init( );
755
756 signal( SIGILL, Sys_SigHandler );
757 signal( SIGFPE, Sys_SigHandler );
758 signal( SIGSEGV, Sys_SigHandler );
759 signal( SIGTERM, Sys_SigHandler );
760 signal( SIGINT, Sys_SigHandler );
761
762 while( 1 )
763 {
764 Com_Frame( );
765 }
766
767 return 0;
768 }
769
770