Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

macosx_sys.m (14297B)


      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 Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 #import "../client/client.h"
     23 #import "macosx_local.h"
     24 
     25 #import "dlfcn.h"
     26 #import "Q3Controller.h"
     27 
     28 #import <AppKit/AppKit.h>
     29 #import <IOKit/IOKitLib.h>
     30 #import <IOKit/IOBSD.h>
     31 #import <IOKit/storage/IOCDMedia.h>
     32 #import <mach/mach_error.h>
     33 
     34 #import <sys/types.h>
     35 #import <unistd.h>
     36 #import <sys/param.h>
     37 #import <sys/mount.h>
     38 #import <sys/sysctl.h>
     39 
     40 #ifdef OMNI_TIMER
     41 #import "macosx_timers.h"
     42 #endif
     43 
     44 qboolean stdin_active = qfalse;
     45 
     46 //===========================================================================
     47 
     48 int main(int argc, const char *argv[]) {
     49 #ifdef DEDICATED
     50     Q3Controller *controller;
     51     
     52     stdin_active = qtrue;
     53     controller = [[Q3Controller alloc] init];
     54     [controller quakeMain];
     55     return 0;
     56 #else
     57     return NSApplicationMain(argc, argv);
     58 #endif
     59 }
     60 
     61 //===========================================================================
     62 
     63 /*
     64 =================
     65 Sys_UnloadDll
     66 
     67 =================
     68 */
     69 void Sys_UnloadDll( void *dllHandle ) {
     70 	if ( !dllHandle ) {
     71 		return;
     72 	}
     73 	dlclose( dllHandle );
     74 }
     75 
     76 /*
     77 =================
     78 Sys_LoadDll
     79 
     80 Used to load a development dll instead of a virtual machine
     81 =================
     82 */
     83 extern char		*FS_BuildOSPath( const char *base, const char *game, const char *qpath );
     84 
     85 void	* QDECL Sys_LoadDll( const char *name, char *fqpath , int (QDECL **entryPoint)(int, ...),
     86 				  int (QDECL *systemcalls)(int, ...) ) {
     87     void *libHandle;
     88     void	(*dllEntry)( int (*syscallptr)(int, ...) );
     89     NSString *bundlePath, *libraryPath;
     90     const char *path;
     91     
     92 	// TTimo
     93 	// I don't understand the search strategy here. How can the Quake3 bundle know about the location
     94 	// of the other bundles? is that configured somewhere in XCode?
     95 	/*
     96     bundlePath = [[NSBundle mainBundle] pathForResource: [NSString stringWithCString: name] ofType: @"bundle"];
     97     libraryPath = [NSString stringWithFormat: @"%@/Contents/MacOS/%s", bundlePath, name];
     98 	*/	
     99 	libraryPath = [NSString stringWithFormat: @"%s.bundle/Contents/MacOS/%s", name, name];
    100     if (!libraryPath)
    101         return NULL;
    102     
    103     path = [libraryPath cString];
    104     Com_Printf("Loading '%s'.\n", path);
    105     libHandle = dlopen( [libraryPath cString], RTLD_LAZY );
    106     if (!libHandle) {
    107         libHandle = dlopen( name, RTLD_LAZY );
    108         if (!libHandle) {
    109             Com_Printf("Error loading dll: %s\n", dlerror());
    110             return NULL;
    111         }
    112     }
    113 
    114     dllEntry = dlsym( libHandle, "_dllEntry" );
    115     if (!dllEntry) {
    116         Com_Printf("Error loading dll:  No dllEntry symbol.\n");
    117         dlclose(libHandle);
    118         return NULL;
    119     }
    120     
    121     *entryPoint = dlsym( libHandle, "_vmMain" );
    122     if (!*entryPoint) {
    123         Com_Printf("Error loading dll:  No vmMain symbol.\n");
    124         dlclose(libHandle);
    125         return NULL;
    126     }
    127     
    128     dllEntry(systemcalls);
    129     return libHandle;
    130 }
    131 
    132 //===========================================================================
    133 
    134 char *Sys_GetClipboardData(void) // FIXME
    135 {
    136     NSPasteboard *pasteboard;
    137     NSArray *pasteboardTypes;
    138 
    139     pasteboard = [NSPasteboard generalPasteboard];
    140     pasteboardTypes = [pasteboard types];
    141     if ([pasteboardTypes containsObject:NSStringPboardType]) {
    142         NSString *clipboardString;
    143 
    144         clipboardString = [pasteboard stringForType:NSStringPboardType];
    145         if (clipboardString && [clipboardString length] > 0) {
    146             return strdup([clipboardString cString]);
    147         }
    148     }
    149     return NULL;
    150 }
    151 
    152 char *Sys_GetWholeClipboard ( void )
    153 {
    154     return NULL;
    155 }
    156 
    157 void Sys_SetClipboard (const char *contents)
    158 {
    159 }
    160 
    161 
    162 /*
    163 ==================
    164 Sys_FunctionCheckSum
    165 ==================
    166 */
    167 int Sys_FunctionCheckSum(void *f1) {
    168 	return 0;
    169 }
    170 
    171 /*
    172 ==================
    173 Sys_MonkeyShouldBeSpanked
    174 ==================
    175 */
    176 int Sys_MonkeyShouldBeSpanked( void ) {
    177 	return 0;
    178 }
    179 
    180 //===========================================================================
    181 
    182 void Sys_BeginProfiling(void)
    183 {
    184 }
    185 
    186 void Sys_EndProfiling(void)
    187 {
    188 }
    189 
    190 //===========================================================================
    191 
    192 /*
    193 ================
    194 Sys_Init
    195 
    196 The cvar and file system has been setup, so configurations are loaded
    197 ================
    198 */
    199 void Sys_Init(void)
    200 {
    201 #ifdef OMNI_TIMER
    202     InitializeTimers();
    203     OTStackPushRoot(rootNode);
    204 #endif
    205 
    206     NET_Init();
    207     Sys_InitInput();	
    208 }
    209 
    210 /*
    211 =================
    212 Sys_Shutdown
    213 =================
    214 */
    215 void Sys_Shutdown(void)
    216 {
    217     Com_Printf( "----- Sys_Shutdown -----\n" );
    218     Sys_EndProfiling();
    219     Sys_ShutdownInput();	
    220     Com_Printf( "------------------------\n" );
    221 }
    222 
    223 void Sys_Error(const char *error, ...)
    224 {
    225     va_list argptr;
    226     NSString *formattedString;
    227 
    228     Sys_Shutdown();
    229 
    230     va_start(argptr,error);
    231     formattedString = [[NSString alloc] initWithFormat:[NSString stringWithCString:error] arguments:argptr];
    232     va_end(argptr);
    233 
    234     NSLog(@"Sys_Error: %@", formattedString);
    235     NSRunAlertPanel(@"Quake 3 Error", formattedString, nil, nil, nil);
    236 
    237     Sys_Quit();
    238 }
    239 
    240 void Sys_Quit(void)
    241 {
    242     Sys_Shutdown();
    243     [NSApp terminate:nil];
    244 }
    245 
    246 /*
    247 ================
    248 Sys_Print
    249 
    250 This is called for all console output, even if the game is running
    251 full screen and the dedicated console window is hidden.
    252 ================
    253 */
    254 
    255 char *ansiColors[8] =
    256 	{ "\033[30m" ,	/* ANSI Black */
    257 	  "\033[31m" ,	/* ANSI Red */
    258 	  "\033[32m" ,	/* ANSI Green */
    259 	  "\033[33m" ,  /* ANSI Yellow */
    260 	  "\033[34m" ,	/* ANSI Blue */
    261 	  "\033[36m" ,  /* ANSI Cyan */
    262 	  "\033[35m" ,	/* ANSI Magenta */
    263 	  "\033[37m" }; /* ANSI White */
    264 	  
    265 void Sys_Print(const char *text)
    266 {
    267 #if 0
    268 	/* Okay, this is a stupid hack, but what the hell, I was bored. ;) */
    269 	const char *scan = text;
    270 	int index;
    271 	
    272 	/* Make sure terminal mode is reset at the start of the line... */
    273 	fputs("\033[0m", stdout);
    274 	
    275 	while(*scan) {
    276 		/* See if we have a color control code.  If so, snarf the character, 
    277 		print what we have so far, print the ANSI Terminal color code,
    278 		skip over the color control code and continue */
    279 		if(Q_IsColorString(scan)) {
    280 			index = ColorIndex(scan[1]);
    281 			
    282 			/* Flush current message */
    283 			if(scan != text) {
    284 				fwrite(text, scan - text, 1, stdout);
    285 			}
    286 			
    287 			/* Write ANSI color code */
    288 			fputs(ansiColors[index], stdout);
    289 			
    290 			/* Reset search */
    291 			text = scan+2;
    292 			scan = text;
    293 			continue;			
    294 		}
    295 		scan++;
    296 	}
    297 
    298 	/* Flush whatever's left */
    299 	fputs(text, stdout);
    300 
    301 	/* Make sure terminal mode is reset at the end of the line too... */
    302 	fputs("\033[0m", stdout);
    303 
    304 #else
    305     fputs(text, stdout);
    306 #endif	
    307 }
    308 
    309 
    310 
    311 /*
    312 ================
    313 Sys_CheckCD
    314 
    315 Return true if the proper CD is in the drive
    316 ================
    317 */
    318 
    319 qboolean Sys_ObjectIsCDRomDevice(io_object_t object)
    320 {
    321     CFStringRef value;
    322     kern_return_t krc;
    323     CFDictionaryRef properties;
    324     qboolean isCDROM = qfalse;
    325     io_iterator_t parentIterator;
    326     io_object_t parent;
    327     
    328     krc = IORegistryEntryCreateCFProperties(object, &properties, kCFAllocatorDefault, (IOOptionBits)0);
    329     if (krc != KERN_SUCCESS) {
    330         fprintf(stderr, "IORegistryEntryCreateCFProperties returned 0x%08x -- %s\n", krc, mach_error_string(krc));
    331         return qfalse;
    332     }
    333 
    334     //NSLog(@"properties = %@", properties);
    335     
    336     // See if this is a CD-ROM
    337     value = CFDictionaryGetValue(properties, CFSTR(kIOCDMediaTypeKey));
    338     if (value && CFStringCompare(value, CFSTR("CD-ROM"), 0) == kCFCompareEqualTo)
    339         isCDROM = qtrue;
    340     CFRelease(properties);
    341 
    342     // If it isn't check each of its parents.  It seems that the parent enumerator only returns the immediate parent.  Maybe the plural indicates that an object can have multiple direct parents.  So, we'll call ourselves recursively for each parent.
    343     if (!isCDROM) {
    344         krc = IORegistryEntryGetParentIterator(object, kIOServicePlane, &parentIterator);
    345         if (krc != KERN_SUCCESS) {
    346             fprintf(stderr, "IOServiceGetMatchingServices returned 0x%08x -- %s\n",
    347                     krc, mach_error_string(krc));
    348         } else {
    349             while (!isCDROM && (parent = IOIteratorNext(parentIterator))) {
    350                 if (Sys_ObjectIsCDRomDevice(parent))
    351                     isCDROM = qtrue;
    352                 IOObjectRelease(parent);
    353             }
    354     
    355             IOObjectRelease(parentIterator);
    356         }
    357     }
    358     
    359     //NSLog(@"Sys_ObjectIsCDRomDevice -> %d", isCDROM);
    360     return isCDROM;
    361 }
    362 
    363 qboolean Sys_IsCDROMDevice(const char *deviceName)
    364 {
    365     kern_return_t krc;
    366     io_iterator_t deviceIterator;
    367     mach_port_t masterPort;
    368     io_object_t object;
    369     qboolean isCDROM = qfalse;
    370     
    371     krc = IOMasterPort(bootstrap_port, &masterPort);
    372     if (krc != KERN_SUCCESS) {
    373         fprintf(stderr, "IOMasterPort returned 0x%08x -- %s\n", krc, mach_error_string(krc));
    374         return qfalse;
    375     }
    376 
    377     // Get an iterator for this BSD device.  If it is a CD, it will likely only be one partition of the larger CD-ROM device.
    378     krc = IOServiceGetMatchingServices(masterPort,
    379                                        IOBSDNameMatching(masterPort, 0, deviceName),
    380                                        &deviceIterator);
    381     if (krc != KERN_SUCCESS) {
    382         fprintf(stderr, "IOServiceGetMatchingServices returned 0x%08x -- %s\n",
    383                 krc, mach_error_string(krc));
    384         return qfalse;
    385     }
    386 
    387     while (!isCDROM && (object = IOIteratorNext(deviceIterator))) {
    388         if (Sys_ObjectIsCDRomDevice(object)) {
    389             isCDROM = qtrue;
    390         }
    391         IOObjectRelease(object);
    392     }
    393     
    394     IOObjectRelease(deviceIterator);
    395 
    396     //NSLog(@"Sys_IsCDROMDevice -> %d", isCDROM);
    397     return isCDROM;
    398 }
    399 
    400 qboolean        Sys_CheckCD( void )
    401 {
    402     // DO NOT just return success here if we have a library directory.
    403     // Actually look for the CD.
    404 
    405     // We'll look through the actual mount points rather than just looking
    406     // for a particular directory since (a) the mount point may change
    407     // between OS version (/foo in Public Beta, /Volumes/foo after Public Beta)
    408     // and (b) this way someone can't just create a directory and warez the files.
    409     
    410     unsigned int mountCount;
    411     struct statfs  *mounts;
    412     
    413     mountCount = getmntinfo(&mounts, MNT_NOWAIT);
    414     if (mountCount <= 0) {
    415         perror("getmntinfo");
    416 #if 1 // Q3:TA doesn't need a CD, but we still need to locate it to allow for partial installs
    417         return qtrue;
    418 #else
    419         return qfalse;
    420 #endif
    421     }
    422     
    423     while (mountCount--) {
    424         const char *lastComponent;
    425         
    426         if ((mounts[mountCount].f_flags & MNT_RDONLY) != MNT_RDONLY) {
    427             // Should have been a read only CD... this isn't it
    428             continue;
    429         }
    430         
    431         if ((mounts[mountCount].f_flags & MNT_LOCAL) != MNT_LOCAL) {
    432             // Should have been a local filesystem
    433             continue;
    434         }
    435         
    436         lastComponent = strrchr(mounts[mountCount].f_mntonname, '/');
    437         if (!lastComponent) {
    438             // No slash in the mount point!  How is that possible?
    439             continue;
    440         }
    441         
    442         // Skip the slash and look for the game name
    443         lastComponent++;
    444         if ((strcasecmp(lastComponent, "Quake3") != 0)) {
    445             continue;
    446         }
    447 
    448             
    449 #if 0
    450         fprintf(stderr, "f_bsize: %d\n", mounts[mountCount].f_bsize);
    451         fprintf(stderr, "f_blocks: %d\n", mounts[mountCount].f_blocks);
    452         fprintf(stderr, "type: %d\n", mounts[mountCount].f_type);
    453         fprintf(stderr, "flags: %d\n", mounts[mountCount].f_flags);
    454         fprintf(stderr, "fstype: %s\n", mounts[mountCount].f_fstypename);
    455         fprintf(stderr, "f_mntonname: %s\n", mounts[mountCount].f_mntonname);
    456         fprintf(stderr, "f_mntfromname: %s\n", mounts[mountCount].f_mntfromname);
    457         fprintf(stderr, "\n\n");
    458 #endif
    459 
    460         lastComponent = strrchr(mounts[mountCount].f_mntfromname, '/');
    461         if (!lastComponent) {
    462             // No slash in the device name!  How is that possible?
    463             continue;
    464         }
    465         lastComponent++;
    466         if (!Sys_IsCDROMDevice(lastComponent))
    467             continue;
    468 
    469         // This looks good
    470         Sys_SetDefaultCDPath(mounts[mountCount].f_mntonname);
    471         return qtrue;
    472     }
    473     
    474 #if 1 // Q3:TA doesn't need a CD, but we still need to locate it to allow for partial installs
    475     return qtrue;
    476 #else
    477     return qfalse;
    478 #endif
    479 }
    480 
    481 
    482 //===================================================================
    483 
    484 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
    485 }
    486 
    487 void Sys_EndStreamedFile( fileHandle_t f ) {
    488 }
    489 
    490 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
    491 	return FS_Read( buffer, size * count, f );
    492 }
    493 
    494 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
    495 	FS_Seek( f, offset, origin );
    496 }
    497 
    498 
    499 void OutputDebugString(char * s)
    500 {
    501 #ifdef DEBUG
    502     fprintf(stderr, "%s", s);
    503 #endif
    504 }
    505 
    506 /*
    507 ==================
    508 Sys_LowPhysicalMemory()
    509 ==================
    510 */
    511 #define MEM_THRESHOLD 96*1024*1024
    512 
    513 qboolean Sys_LowPhysicalMemory()
    514 {
    515     return NSRealMemoryAvailable() <= MEM_THRESHOLD;
    516 }
    517 
    518 static unsigned int _Sys_ProcessorCount = 0;
    519 
    520 unsigned int Sys_ProcessorCount()
    521 {
    522     if (!_Sys_ProcessorCount) {
    523         int name[] = {CTL_HW, HW_NCPU};
    524         size_t size;
    525     
    526         size = sizeof(_Sys_ProcessorCount);
    527         if (sysctl(name, 2, &_Sys_ProcessorCount, &size, NULL, 0) < 0) {
    528             perror("sysctl");
    529             _Sys_ProcessorCount = 1;
    530         } else {
    531             Com_Printf("System processor count is %d\n", _Sys_ProcessorCount);
    532         }
    533     }
    534     
    535     return _Sys_ProcessorCount;
    536 }
    537