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