1 /*
2 ===========================================================================
3 Copyright (C) 2005 - 2015, ioquake3 contributors
4 Copyright (C) 2013 - 2015, OpenJK contributors
5 
6 This file is part of the OpenJK source code.
7 
8 OpenJK is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License version 2 as
10 published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 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 this program; if not, see <http://www.gnu.org/licenses/>.
19 ===========================================================================
20 */
21 
22 #include <csignal>
23 #include <cstdlib>
24 #include <cstdarg>
25 #include <cstdio>
26 #include <sys/stat.h>
27 #define __STDC_FORMAT_MACROS
28 #include <inttypes.h>
29 #include "qcommon/qcommon.h"
30 #include "sys_local.h"
31 #include "sys_loadlib.h"
32 #include "sys_public.h"
33 #include "con_local.h"
34 
35 static char binaryPath[ MAX_OSPATH ] = { 0 };
36 static char installPath[ MAX_OSPATH ] = { 0 };
37 
38 cvar_t *com_minimized;
39 cvar_t *com_unfocused;
40 cvar_t *com_maxfps;
41 cvar_t *com_maxfpsMinimized;
42 cvar_t *com_maxfpsUnfocused;
43 
44 /*
45 =================
46 Sys_SetBinaryPath
47 =================
48 */
Sys_SetBinaryPath(const char * path)49 void Sys_SetBinaryPath(const char *path)
50 {
51 	Q_strncpyz(binaryPath, path, sizeof(binaryPath));
52 }
53 
54 /*
55 =================
56 Sys_BinaryPath
57 =================
58 */
Sys_BinaryPath(void)59 char *Sys_BinaryPath(void)
60 {
61 	return binaryPath;
62 }
63 
64 /*
65 =================
66 Sys_SetDefaultInstallPath
67 =================
68 */
Sys_SetDefaultInstallPath(const char * path)69 void Sys_SetDefaultInstallPath(const char *path)
70 {
71 	Q_strncpyz(installPath, path, sizeof(installPath));
72 }
73 
74 /*
75 =================
76 Sys_DefaultInstallPath
77 =================
78 */
Sys_DefaultInstallPath(void)79 char *Sys_DefaultInstallPath(void)
80 {
81 	if (*installPath)
82 		return installPath;
83 	else
84 		return Sys_Cwd();
85 }
86 
87 /*
88 =================
89 Sys_DefaultAppPath
90 =================
91 */
Sys_DefaultAppPath(void)92 char *Sys_DefaultAppPath(void)
93 {
94 	return Sys_BinaryPath();
95 }
96 
97 /*
98 ==================
99 Sys_GetClipboardData
100 ==================
101 */
Sys_GetClipboardData(void)102 char *Sys_GetClipboardData( void ) {
103 #ifdef DEDICATED
104 	return NULL;
105 #else
106 	if ( !SDL_HasClipboardText() )
107 		return NULL;
108 
109 	char *cbText = SDL_GetClipboardText();
110 	size_t len = strlen( cbText ) + 1;
111 
112 	char *buf = (char *)Z_Malloc( len, TAG_CLIPBOARD );
113 	Q_strncpyz( buf, cbText, len );
114 
115 	SDL_free( cbText );
116 	return buf;
117 #endif
118 }
119 
120 /*
121 =================
122 Sys_ConsoleInput
123 
124 Handle new console input
125 =================
126 */
Sys_ConsoleInput(void)127 char *Sys_ConsoleInput(void)
128 {
129 	return CON_Input( );
130 }
131 
Sys_Print(const char * msg)132 void Sys_Print( const char *msg ) {
133 	// TTimo - prefix for text that shows up in console but not in notify
134 	// backported from RTCW
135 	if ( !Q_strncmp( msg, "[skipnotify]", 12 ) ) {
136 		msg += 12;
137 	}
138 	if ( msg[0] == '*' ) {
139 		msg += 1;
140 	}
141 	ConsoleLogAppend( msg );
142 	CON_Print( msg );
143 }
144 
145 /*
146 ================
147 Sys_Init
148 
149 Called after the common systems (cvars, files, etc)
150 are initialized
151 ================
152 */
Sys_Init(void)153 void Sys_Init( void ) {
154 	Cmd_AddCommand ("in_restart", IN_Restart);
155 	Cvar_Get( "arch", OS_STRING " " ARCH_STRING, CVAR_ROM );
156 	Cvar_Get( "username", Sys_GetCurrentUser(), CVAR_ROM );
157 
158 	com_unfocused = Cvar_Get( "com_unfocused", "0", CVAR_ROM );
159 	com_minimized = Cvar_Get( "com_minimized", "0", CVAR_ROM );
160 #ifdef _JK2EXE
161 	com_maxfps = Cvar_Get ("com_maxfps", "125", CVAR_ARCHIVE );
162 #else
163 	com_maxfps = Cvar_Get( "com_maxfps", "125", CVAR_ARCHIVE, "Maximum frames per second" );
164 #endif
165 	com_maxfpsUnfocused = Cvar_Get( "com_maxfpsUnfocused", "0", CVAR_ARCHIVE_ND );
166 	com_maxfpsMinimized = Cvar_Get( "com_maxfpsMinimized", "50", CVAR_ARCHIVE_ND );
167 }
168 
Sys_Exit(int ex)169 static void NORETURN Sys_Exit( int ex ) {
170 	IN_Shutdown();
171 #ifndef DEDICATED
172 	SDL_Quit();
173 #endif
174 
175 	NET_Shutdown();
176 
177 	Sys_PlatformExit();
178 
179 	Com_ShutdownHunkMemory();
180 	Com_ShutdownZoneMemory();
181 
182 	CON_Shutdown();
183 
184     exit( ex );
185 }
186 
187 #if !defined(DEDICATED)
Sys_ErrorDialog(const char * error)188 static void Sys_ErrorDialog( const char *error )
189 {
190 	time_t rawtime;
191 	char timeStr[32] = {}; // should really only reach ~19 chars
192 	char crashLogPath[MAX_OSPATH];
193 
194 	time( &rawtime );
195 	strftime( timeStr, sizeof( timeStr ), "%Y-%m-%d_%H-%M-%S", localtime( &rawtime ) ); // or gmtime
196 	Com_sprintf( crashLogPath, sizeof( crashLogPath ),
197 					"%s%ccrashlog-%s.txt",
198 					Sys_DefaultHomePath(), PATH_SEP, timeStr );
199 
200 	Sys_Mkdir( Sys_DefaultHomePath() );
201 
202 	FILE *fp = fopen( crashLogPath, "w" );
203 	if ( fp )
204 	{
205 		ConsoleLogWriteOut( fp );
206 		fclose( fp );
207 
208 		const char *errorMessage = va( "%s\n\nThe crash log was written to %s", error, crashLogPath );
209 		if ( SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Error", errorMessage, NULL ) < 0 )
210 		{
211 			fprintf( stderr, "%s", errorMessage );
212 		}
213 	}
214 	else
215 	{
216 		// Getting pretty desperate now
217 		ConsoleLogWriteOut( stderr );
218 		fflush( stderr );
219 
220 		const char *errorMessage = va( "%s\nCould not write the crash log file, but we printed it to stderr.\n"
221 										"Try running the game using a command line interface.", error );
222 		if ( SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Error", errorMessage, NULL ) < 0 )
223 		{
224 			// We really have hit rock bottom here :(
225 			fprintf( stderr, "%s", errorMessage );
226 		}
227 	}
228 }
229 #endif
230 
Sys_Error(const char * error,...)231 void NORETURN QDECL Sys_Error( const char *error, ... )
232 {
233 	va_list argptr;
234 	char    string[1024];
235 
236 	va_start (argptr,error);
237 	Q_vsnprintf (string, sizeof(string), error, argptr);
238 	va_end (argptr);
239 
240 	Sys_Print( string );
241 
242 	// Only print Sys_ErrorDialog for client binary. The dedicated
243 	// server binary is meant to be a command line program so you would
244 	// expect to see the error printed.
245 #if !defined(DEDICATED)
246 	Sys_ErrorDialog( string );
247 #endif
248 
249 	Sys_Exit( 3 );
250 }
251 
Sys_Quit(void)252 void NORETURN Sys_Quit (void) {
253     Sys_Exit(0);
254 }
255 
256 /*
257 ============
258 Sys_FileTime
259 
260 returns -1 if not present
261 ============
262 */
Sys_FileTime(const char * path)263 time_t Sys_FileTime( const char *path )
264 {
265 	struct stat buf;
266 
267 	if ( stat( path, &buf ) == -1 )
268 		return -1;
269 
270 	return buf.st_mtime;
271 }
272 
273 /*
274 =================
275 Sys_UnloadDll
276 =================
277 */
Sys_UnloadDll(void * dllHandle)278 void Sys_UnloadDll( void *dllHandle )
279 {
280 	if( !dllHandle )
281 	{
282 		Com_Printf("Sys_UnloadDll(NULL)\n");
283 		return;
284 	}
285 
286 	Sys_UnloadLibrary(dllHandle);
287 }
288 
289 /*
290 =================
291 Sys_LoadDll
292 
293 First try to load library name from system library path,
294 from executable path, then fs_basepath.
295 =================
296 */
Sys_LoadDll(const char * name,qboolean useSystemLib)297 void *Sys_LoadDll( const char *name, qboolean useSystemLib )
298 {
299 	void *dllhandle = NULL;
300 
301 	// Don't load any DLLs that end with the pk3 extension
302 	if ( COM_CompareExtension( name, ".pk3" ) )
303 	{
304 		Com_Printf( S_COLOR_YELLOW "WARNING: Rejecting DLL named \"%s\"", name );
305 		return NULL;
306 	}
307 
308 	if ( useSystemLib )
309 	{
310 		Com_Printf( "Trying to load \"%s\"...\n", name );
311 
312 		dllhandle = Sys_LoadLibrary( name );
313 		if ( dllhandle )
314 			return dllhandle;
315 
316 		Com_Printf( "%s(%s) failed: \"%s\"\n", __FUNCTION__, name, Sys_LibraryError() );
317 	}
318 
319 	const char *binarypath = Sys_BinaryPath();
320 	const char *basepath = Cvar_VariableString( "fs_basepath" );
321 
322 	if ( !*binarypath )
323 		binarypath = ".";
324 
325 	const char *searchPaths[] = {
326 		binarypath,
327 		basepath,
328 	};
329 	const size_t numPaths = ARRAY_LEN( searchPaths );
330 
331 	for ( size_t i = 0; i < numPaths; i++ )
332 	{
333 		const char *libDir = searchPaths[i];
334 		if ( !libDir[0] )
335 			continue;
336 
337 		Com_Printf( "Trying to load \"%s\" from \"%s\"...\n", name, libDir );
338 		char *fn = va( "%s%c%s", libDir, PATH_SEP, name );
339 		dllhandle = Sys_LoadLibrary( fn );
340 		if ( dllhandle )
341 			return dllhandle;
342 
343 		Com_Printf( "%s(%s) failed: \"%s\"\n", __FUNCTION__, fn, Sys_LibraryError() );
344 	}
345 	return NULL;
346 }
347 
348 #if defined(MACOS_X) && !defined(_JK2EXE)
Sys_LoadMachOBundle(const char * name)349 void *Sys_LoadMachOBundle( const char *name )
350 {
351 	if ( !FS_LoadMachOBundle(name) )
352 		return NULL;
353 
354 	char *homepath = Cvar_VariableString( "fs_homepath" );
355 	char *gamedir = Cvar_VariableString( "fs_game" );
356 	char dllName[MAX_QPATH];
357 
358 	Com_sprintf( dllName, sizeof(dllName), "%s_pk3" DLL_EXT, name );
359 
360 	//load the unzipped library
361 	char *fn = FS_BuildOSPath( homepath, gamedir, dllName );
362 
363 	void    *libHandle = Sys_LoadLibrary( fn );
364 
365 	if ( libHandle != NULL ) {
366 		Com_Printf( "Loaded pk3 bundle %s.\n", name );
367 	}
368 
369 	return libHandle;
370 }
371 #endif
372 
373 enum SearchPathFlag
374 {
375 	SEARCH_PATH_MOD		= 1 << 0,
376 	SEARCH_PATH_BASE	= 1 << 1,
377 	SEARCH_PATH_OPENJK	= 1 << 2,
378 	SEARCH_PATH_ROOT	= 1 << 3
379 };
380 
Sys_LoadDllFromPaths(const char * filename,const char * gamedir,const char ** searchPaths,size_t numPaths,uint32_t searchFlags,const char * callerName)381 static void *Sys_LoadDllFromPaths( const char *filename, const char *gamedir, const char **searchPaths,
382 									size_t numPaths, uint32_t searchFlags, const char *callerName )
383 {
384 	char *fn;
385 	void *libHandle;
386 
387 	if ( searchFlags & SEARCH_PATH_MOD )
388 	{
389 		for ( size_t i = 0; i < numPaths; i++ )
390 		{
391 			const char *libDir = searchPaths[i];
392 			if ( !libDir[0] )
393 				continue;
394 
395 			fn = FS_BuildOSPath( libDir, gamedir, filename );
396 			libHandle = Sys_LoadLibrary( fn );
397 			if ( libHandle )
398 				return libHandle;
399 
400 			Com_Printf( "%s(%s) failed: \"%s\"\n", callerName, fn, Sys_LibraryError() );
401 		}
402 	}
403 
404 	if ( searchFlags & SEARCH_PATH_BASE )
405 	{
406 		for ( size_t i = 0; i < numPaths; i++ )
407 		{
408 			const char *libDir = searchPaths[i];
409 			if ( !libDir[0] )
410 				continue;
411 
412 			fn = FS_BuildOSPath( libDir, BASEGAME, filename );
413 			libHandle = Sys_LoadLibrary( fn );
414 			if ( libHandle )
415 				return libHandle;
416 
417 			Com_Printf( "%s(%s) failed: \"%s\"\n", callerName, fn, Sys_LibraryError() );
418 		}
419 	}
420 
421 	if ( searchFlags & SEARCH_PATH_OPENJK )
422 	{
423 		for ( size_t i = 0; i < numPaths; i++ )
424 		{
425 			const char *libDir = searchPaths[i];
426 			if ( !libDir[0] )
427 				continue;
428 
429 			fn = FS_BuildOSPath( libDir, OPENJKGAME, filename );
430 			libHandle = Sys_LoadLibrary( fn );
431 			if ( libHandle )
432 				return libHandle;
433 
434 			Com_Printf( "%s(%s) failed: \"%s\"\n", callerName, fn, Sys_LibraryError() );
435 		}
436 	}
437 
438 	if ( searchFlags & SEARCH_PATH_ROOT )
439 	{
440 		for ( size_t i = 0; i < numPaths; i++ )
441 		{
442 			const char *libDir = searchPaths[i];
443 			if ( !libDir[0] )
444 				continue;
445 
446 			fn = va( "%s%c%s", libDir, PATH_SEP, filename );
447 			libHandle = Sys_LoadLibrary( fn );
448 			if ( libHandle )
449 				return libHandle;
450 
451 			Com_Printf( "%s(%s) failed: \"%s\"\n", callerName, fn, Sys_LibraryError() );
452 		}
453 	}
454 
455 	return NULL;
456 }
457 
FreeUnpackDLLResult(UnpackDLLResult * result)458 static void FreeUnpackDLLResult(UnpackDLLResult *result)
459 {
460 	if ( result->tempDLLPath )
461 		Z_Free((void *)result->tempDLLPath);
462 }
463 
Sys_LoadLegacyGameDll(const char * name,VMMainProc ** vmMain,SystemCallProc * systemcalls)464 void *Sys_LoadLegacyGameDll( const char *name, VMMainProc **vmMain, SystemCallProc *systemcalls )
465 {
466 	void	*libHandle = NULL;
467 	char	filename[MAX_OSPATH];
468 
469 	Com_sprintf (filename, sizeof(filename), "%s" ARCH_STRING DLL_EXT, name);
470 
471 #if defined(_DEBUG)
472 	libHandle = Sys_LoadLibrary( filename );
473 	if ( !libHandle )
474 #endif
475 	{
476 		UnpackDLLResult unpackResult = Sys_UnpackDLL(filename);
477 		if ( !unpackResult.succeeded )
478 		{
479 			if ( Sys_DLLNeedsUnpacking() )
480 			{
481 				FreeUnpackDLLResult(&unpackResult);
482 				Com_DPrintf( "Sys_LoadLegacyGameDll: Failed to unpack %s from PK3.\n", filename );
483 				return NULL;
484 			}
485 		}
486 		else
487 		{
488 			libHandle = Sys_LoadLibrary(unpackResult.tempDLLPath);
489 		}
490 
491 		FreeUnpackDLLResult(&unpackResult);
492 
493 		if ( !libHandle )
494 		{
495 #if defined(MACOS_X) && !defined(_JK2EXE)
496 			//First, look for the old-style mac .bundle that's inside a pk3
497 			//It's actually zipped, and the zipfile has the same name as 'name'
498 			libHandle = Sys_LoadMachOBundle( name );
499 #endif
500 
501 			if (!libHandle) {
502 				char *basepath = Cvar_VariableString( "fs_basepath" );
503 				char *homepath = Cvar_VariableString( "fs_homepath" );
504 				char *cdpath = Cvar_VariableString( "fs_cdpath" );
505 				char *gamedir = Cvar_VariableString( "fs_game" );
506 		#ifdef MACOS_X
507 				char *apppath = Cvar_VariableString( "fs_apppath" );
508 		#endif
509 
510 				const char *searchPaths[] = {
511 					homepath,
512 		#ifdef MACOS_X
513 					apppath,
514 		#endif
515 					basepath,
516 					cdpath,
517 				};
518 				size_t numPaths = ARRAY_LEN( searchPaths );
519 
520 				libHandle = Sys_LoadDllFromPaths( filename, gamedir, searchPaths, numPaths, SEARCH_PATH_BASE | SEARCH_PATH_MOD, __FUNCTION__ );
521 				if ( !libHandle )
522 					return NULL;
523 			}
524 		}
525 	}
526 
527 	typedef void QDECL DllEntryProc( SystemCallProc *syscallptr );
528 
529 	DllEntryProc *dllEntry = (DllEntryProc *)Sys_LoadFunction( libHandle, "dllEntry" );
530 	*vmMain = (VMMainProc *)Sys_LoadFunction( libHandle, "vmMain" );
531 
532 	if ( !*vmMain || !dllEntry ) {
533 		Com_DPrintf ( "Sys_LoadLegacyGameDll(%s) failed to find vmMain function:\n...%s!\n", name, Sys_LibraryError() );
534 		Sys_UnloadLibrary( libHandle );
535 		return NULL;
536 	}
537 
538 	Com_DPrintf ( "Sys_LoadLegacyGameDll(%s) found vmMain function at 0x%" PRIxPTR "\n", name, *vmMain );
539 	dllEntry( systemcalls );
540 
541 	return libHandle;
542 }
543 
Sys_LoadSPGameDll(const char * name,GetGameAPIProc ** GetGameAPI)544 void *Sys_LoadSPGameDll( const char *name, GetGameAPIProc **GetGameAPI )
545 {
546 	void	*libHandle = NULL;
547 	char	filename[MAX_OSPATH];
548 
549 	assert( GetGameAPI );
550 
551 	Com_sprintf (filename, sizeof(filename), "%s" ARCH_STRING DLL_EXT, name);
552 
553 #if defined(MACOS_X) && !defined(_JK2EXE)
554     //First, look for the old-style mac .bundle that's inside a pk3
555     //It's actually zipped, and the zipfile has the same name as 'name'
556     libHandle = Sys_LoadMachOBundle( filename );
557 #endif
558 
559 	if (!libHandle) {
560 		char *basepath = Cvar_VariableString( "fs_basepath" );
561 		char *homepath = Cvar_VariableString( "fs_homepath" );
562 		char *cdpath = Cvar_VariableString( "fs_cdpath" );
563 		char *gamedir = Cvar_VariableString( "fs_game" );
564 #ifdef MACOS_X
565         char *apppath = Cvar_VariableString( "fs_apppath" );
566 #endif
567 
568 		const char *searchPaths[] = {
569 			homepath,
570 #ifdef MACOS_X
571 			apppath,
572 #endif
573 			basepath,
574 			cdpath,
575 		};
576 		size_t numPaths = ARRAY_LEN( searchPaths );
577 
578 		libHandle = Sys_LoadDllFromPaths( filename, gamedir, searchPaths, numPaths,
579 											SEARCH_PATH_BASE | SEARCH_PATH_MOD | SEARCH_PATH_OPENJK | SEARCH_PATH_ROOT,
580 											__FUNCTION__ );
581 		if ( !libHandle )
582 			return NULL;
583 	}
584 
585 	*GetGameAPI = (GetGameAPIProc *)Sys_LoadFunction( libHandle, "GetGameAPI" );
586 	if ( !*GetGameAPI ) {
587 		Com_DPrintf ( "%s(%s) failed to find GetGameAPI function:\n...%s!\n", __FUNCTION__, name, Sys_LibraryError() );
588 		Sys_UnloadLibrary( libHandle );
589 		return NULL;
590 	}
591 
592 	return libHandle;
593 }
594 
Sys_LoadGameDll(const char * name,GetModuleAPIProc ** moduleAPI)595 void *Sys_LoadGameDll( const char *name, GetModuleAPIProc **moduleAPI )
596 {
597 	void	*libHandle = NULL;
598 	char	filename[MAX_OSPATH];
599 
600 	Com_sprintf (filename, sizeof(filename), "%s" ARCH_STRING DLL_EXT, name);
601 
602 #if defined(_DEBUG)
603 	libHandle = Sys_LoadLibrary( filename );
604 	if ( !libHandle )
605 #endif
606 	{
607 		UnpackDLLResult unpackResult = Sys_UnpackDLL(filename);
608 		if ( !unpackResult.succeeded )
609 		{
610 			if ( Sys_DLLNeedsUnpacking() )
611 			{
612 				FreeUnpackDLLResult(&unpackResult);
613 				Com_DPrintf( "Sys_LoadLegacyGameDll: Failed to unpack %s from PK3.\n", filename );
614 				return NULL;
615 			}
616 		}
617 		else
618 		{
619 			libHandle = Sys_LoadLibrary(unpackResult.tempDLLPath);
620 		}
621 
622 		FreeUnpackDLLResult(&unpackResult);
623 
624 		if ( !libHandle )
625 		{
626 #if defined(MACOS_X) && !defined(_JK2EXE)
627 			//First, look for the old-style mac .bundle that's inside a pk3
628 			//It's actually zipped, and the zipfile has the same name as 'name'
629 			libHandle = Sys_LoadMachOBundle( name );
630 #endif
631 
632 			if (!libHandle) {
633 				char *basepath = Cvar_VariableString( "fs_basepath" );
634 				char *homepath = Cvar_VariableString( "fs_homepath" );
635 				char *cdpath = Cvar_VariableString( "fs_cdpath" );
636 				char *gamedir = Cvar_VariableString( "fs_game" );
637 #ifdef MACOS_X
638 				char *apppath = Cvar_VariableString( "fs_apppath" );
639 #endif
640 
641 				const char *searchPaths[] = {
642 					homepath,
643 #ifdef MACOS_X
644 					apppath,
645 #endif
646 					basepath,
647 					cdpath,
648 				};
649 				size_t numPaths = ARRAY_LEN( searchPaths );
650 
651 				libHandle = Sys_LoadDllFromPaths( filename, gamedir, searchPaths, numPaths, SEARCH_PATH_BASE | SEARCH_PATH_MOD, __FUNCTION__ );
652 				if ( !libHandle )
653 					return NULL;
654 			}
655 		}
656 	}
657 
658 	*moduleAPI = (GetModuleAPIProc *)Sys_LoadFunction( libHandle, "GetModuleAPI" );
659 	if ( !*moduleAPI ) {
660 		Com_DPrintf ( "Sys_LoadGameDll(%s) failed to find GetModuleAPI function:\n...%s!\n", name, Sys_LibraryError() );
661 		Sys_UnloadLibrary( libHandle );
662 		return NULL;
663 	}
664 
665 	return libHandle;
666 }
667 
668 /*
669 =================
670 Sys_SigHandler
671 =================
672 */
Sys_SigHandler(int signal)673 void Sys_SigHandler( int signal )
674 {
675 	static qboolean signalcaught = qfalse;
676 
677 	if( signalcaught )
678 	{
679 		fprintf( stderr, "DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n",
680 			signal );
681 	}
682 	else
683 	{
684 		signalcaught = qtrue;
685 		//VM_Forced_Unload_Start();
686 #ifndef DEDICATED
687 		CL_Shutdown();
688 		//CL_Shutdown(va("Received signal %d", signal), qtrue, qtrue);
689 #endif
690 		SV_Shutdown(va("Received signal %d", signal) );
691 		//VM_Forced_Unload_Done();
692 	}
693 
694 	if( signal == SIGTERM || signal == SIGINT )
695 		Sys_Exit( 1 );
696 	else
697 		Sys_Exit( 2 );
698 }
699 
700 #ifdef MACOS_X
701 /*
702  =================
703  Sys_StripAppBundle
704 
705  Discovers if passed dir is suffixed with the directory structure of a Mac OS X
706  .app bundle. If it is, the .app directory structure is stripped off the end and
707  the result is returned. If not, dir is returned untouched.
708  =================
709  */
Sys_StripAppBundle(char * dir)710 char *Sys_StripAppBundle( char *dir )
711 {
712 	static char cwd[MAX_OSPATH];
713 
714 	Q_strncpyz(cwd, dir, sizeof(cwd));
715 	if(strcmp(Sys_Basename(cwd), "MacOS"))
716 		return dir;
717 	Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
718 	if(strcmp(Sys_Basename(cwd), "Contents"))
719 		return dir;
720 	Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
721 	if(!strstr(Sys_Basename(cwd), ".app"))
722 		return dir;
723 	Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
724 	return cwd;
725 }
726 #endif
727 
728 #ifndef DEFAULT_BASEDIR
729 #	ifdef MACOS_X
730 #		define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath())
731 #	else
732 #		define DEFAULT_BASEDIR Sys_BinaryPath()
733 #	endif
734 #endif
735 
main(int argc,char * argv[])736 int main ( int argc, char* argv[] )
737 {
738 	int		i;
739 	char	commandLine[ MAX_STRING_CHARS ] = { 0 };
740 
741 	Sys_PlatformInit();
742 	CON_Init();
743 
744 	// get the initial time base
745 	Sys_Milliseconds();
746 
747 #ifdef MACOS_X
748 	// This is passed if we are launched by double-clicking
749 	if ( argc >= 2 && Q_strncmp ( argv[1], "-psn", 4 ) == 0 )
750 		argc = 1;
751 #endif
752 
753 	Sys_SetBinaryPath( Sys_Dirname( argv[ 0 ] ) );
754 	Sys_SetDefaultInstallPath( DEFAULT_BASEDIR );
755 
756 	// Concatenate the command line for passing to Com_Init
757 	for( i = 1; i < argc; i++ )
758 	{
759 		const bool containsSpaces = (strchr(argv[i], ' ') != NULL);
760 		if (containsSpaces)
761 			Q_strcat( commandLine, sizeof( commandLine ), "\"" );
762 
763 		Q_strcat( commandLine, sizeof( commandLine ), argv[ i ] );
764 
765 		if (containsSpaces)
766 			Q_strcat( commandLine, sizeof( commandLine ), "\"" );
767 
768 		Q_strcat( commandLine, sizeof( commandLine ), " " );
769 	}
770 
771 	Com_Init (commandLine);
772 
773 #ifndef DEDICATED
774 	SDL_version compiled;
775 	SDL_version linked;
776 
777 	SDL_VERSION( &compiled );
778 	SDL_GetVersion( &linked );
779 
780 	Com_Printf( "SDL Version Compiled: %d.%d.%d\n", compiled.major, compiled.minor, compiled.patch );
781 	Com_Printf( "SDL Version Linked: %d.%d.%d\n", linked.major, linked.minor, linked.patch );
782 #endif
783 
784 	NET_Init();
785 
786 	// main game loop
787 	while (1)
788 	{
789 		if ( com_busyWait->integer )
790 		{
791 			bool shouldSleep = false;
792 
793 #if !defined(_JK2EXE)
794 			if ( com_dedicated->integer )
795 			{
796 				shouldSleep = true;
797 			}
798 #endif
799 
800 			if ( com_minimized->integer )
801 			{
802 				shouldSleep = true;
803 			}
804 
805 			if ( shouldSleep )
806 			{
807 				Sys_Sleep( 5 );
808 			}
809 		}
810 
811 		// run the game
812 		Com_Frame();
813 	}
814 
815 	// never gets here
816 	return 0;
817 }
818