]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/osx/DOOMController.mm
195c18a43cf2dd83c4148bce2da5b126dc7229bd
[icculus/iodoom3.git] / neo / sys / osx / DOOMController.mm
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 // -*- mode: objc -*-
30 #import "../../idlib/precompiled.h"
31 #import "DOOMController.h"
32
33 #import <unistd.h>
34 #import <pthread.h>
35
36 #import <Foundation/Foundation.h>
37 #import <Carbon/Carbon.h>
38 #import <AppKit/AppKit.h>
39 #import <OpenGL/gl.h>
40
41 #import "macosx_common.h"
42 #import "macosx_local.h"
43 #import "macosx_sys.h"
44
45 #import <fenv.h>
46 #import <ucontext.h>
47 #import <mach/thread_status.h>
48
49 #define MAX_KEYS                256
50
51 static idStr                    savepath;
52
53 extern  bool    key_overstrikeMode;
54
55 #define TEST_FPU_EXCEPTIONS                     \
56 FPU_EXCEPTION_INVALID_OPERATION |               \
57 FPU_EXCEPTION_DENORMALIZED_OPERAND |    \
58 FPU_EXCEPTION_DIVIDE_BY_ZERO |                  \
59 /* FPU_EXCEPTION_NUMERIC_OVERFLOW |     */              \
60 /* FPU_EXCEPTION_NUMERIC_UNDERFLOW | */         \
61 /* FPU_EXCEPTION_INEXACT_RESULT | */            \
62 0
63
64 #define kRegKey @"RegCode"
65
66 static const ControlID  kRegCode1EditText =     { 'RegC', 1 };
67
68 struct RegCodeInfo
69 {
70         char                                                    prefRegCode1[256];
71         bool                                                    okPressed;              
72         WindowRef                                               window;
73         ControlRef                                              regCode1EditText;
74 };
75
76 static OSErr DoRegCodeDialog( char* ioRegCode1 );
77
78
79 @interface DOOMController (Private)
80 - (void)quakeMain;
81 - (BOOL)checkRegCodes;
82 - (BOOL)checkOS;
83 @end
84
85 @implementation DOOMController
86
87 /*
88 + (void)initialize;
89 {
90     static bool initialized = NO;
91
92     [super initialize];
93     if ( initialized ) {
94         return;
95         }
96     initialized = YES;
97 }
98 */
99
100 #define MAX_ARGC 1024
101
102 - (void)applicationDidFinishLaunching:(NSNotification *)notification;
103 {
104     NS_DURING {
105                 NSAssert(sizeof(bool) == 1, @"sizeof(bool) should equal 1 byte");
106         [self quakeMain];
107     } NS_HANDLER {
108         Sys_Error( (const char *)[ [ localException reason ] cString ] );
109     } NS_ENDHANDLER;
110     Sys_Quit();
111 }
112
113 - (void)applicationWillHide:(NSNotification *)notification;
114 {
115     Sys_ShutdownInput();
116 }
117
118 - (void)applicationWillUnhide:(NSNotification *)notification;
119 {
120     Sys_InitInput();
121 }
122
123 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
124 {
125         common->Quit();
126         return NSTerminateLater;        // we never reach this
127 }
128
129 #if 0
130 // Actions
131
132 - (IBAction)paste:(id)sender;
133 {
134     int shiftWasDown, insertWasDown;
135     unsigned int currentTime;
136
137     currentTime = Sys_Milliseconds();
138     // Save the original keyboard state
139     shiftWasDown = keys[K_SHIFT].down;
140     insertWasDown = keys[K_INS].down;
141     // Fake a Shift-Insert keyboard event
142     keys[K_SHIFT].down = true;
143     Posix_QueEvent(currentTime, SE_KEY, K_INS, true, 0, NULL);
144     Posix_QueEvent(currentTime, SE_KEY, K_INS, false, 0, NULL);
145     // Restore the original keyboard state
146     keys[K_SHIFT].down = shiftWasDown;
147     keys[K_INS].down = insertWasDown;
148 }
149
150 extern void CL_Quit_f(void);
151 //extern void SetProgramPath(const char *path);
152
153
154 - (IBAction)requestTerminate:(id)sender;
155 {
156     //osxQuit();
157         common->Quit();
158 }
159
160 - (void)showBanner;
161 {
162     static bool hasShownBanner = NO;
163
164     if (!hasShownBanner) {
165         //cvar_t *showBanner;
166
167         hasShownBanner = YES;
168         //showBanner = Cvar_Get("cl_showBanner", "1", 0);
169         //if ( showBanner->integer != 0 ) {
170                 if ( true ) {
171             NSPanel *splashPanel;
172             NSImage *bannerImage;
173             NSRect bannerRect;
174             NSImageView *bannerImageView;
175             
176             bannerImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"banner.jpg"]];
177             bannerRect = NSMakeRect(0.0, 0.0, [bannerImage size].width, [bannerImage size].height);
178             
179             splashPanel = [[NSPanel alloc] initWithContentRect:bannerRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
180             
181             bannerImageView = [[NSImageView alloc] initWithFrame:bannerRect];
182             [bannerImageView setImage:bannerImage];
183             [splashPanel setContentView:bannerImageView];
184             [bannerImageView release];
185             
186             [splashPanel center];
187             [splashPanel setHasShadow:YES];
188             [splashPanel orderFront: nil];
189             [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.5]];
190             [splashPanel close];
191             
192             [bannerImage release];
193         }
194     }
195 }
196
197 // Services
198
199 - (void)connectToServer:(NSPasteboard *)pasteboard userData:(NSString *)data error:(NSString **)error;
200 {
201     NSArray *pasteboardTypes;
202
203     pasteboardTypes = [pasteboard types];
204     if ([pasteboardTypes containsObject:NSStringPboardType]) {
205         NSString *requestedServer;
206
207         requestedServer = [pasteboard stringForType:NSStringPboardType];
208         if (requestedServer) {
209             Cbuf_AddText( va( "connect %s\n", [requestedServer cString]));
210             return;
211         }
212     }
213     *error = @"Unable to connect to server:  could not find string on pasteboard";
214 }
215
216 - (void)performCommand:(NSPasteboard *)pasteboard userData:(NSString *)data error:(NSString **)error;
217 {
218     NSArray *pasteboardTypes;
219
220     pasteboardTypes = [pasteboard types];
221     if ([pasteboardTypes containsObject:NSStringPboardType]) {
222         NSString *requestedCommand;
223
224         requestedCommand = [pasteboard stringForType:NSStringPboardType];
225         if (requestedCommand) {
226             Cbuf_AddText(va("%s\n", [requestedCommand cString]));
227             return;
228         }
229     }
230     *error = @"Unable to perform command:  could not find string on pasteboard";
231 }
232
233 #endif // commented out all the banners and actions
234
235 @end
236
237 @implementation DOOMController (Private)
238
239 - (void)quakeMain
240 {
241     NSAutoreleasePool *pool;
242     int argc = 0;
243     const char *argv[MAX_ARGC];
244     NSProcessInfo *processInfo;
245     NSArray *arguments;
246     unsigned int argumentIndex, argumentCount;
247     //const char *cddir;
248     //NSFileManager *defaultManager;
249     //bool tryAgain;
250
251     pool = [[NSAutoreleasePool alloc] init];
252
253     [NSApp setServicesProvider:self];
254
255     processInfo = [NSProcessInfo processInfo];
256     arguments = [processInfo arguments];
257     argumentCount = [arguments count];
258     for (argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) {
259         argv[argc++] = strdup([[arguments objectAtIndex:argumentIndex] cString]);
260     }
261     if (![[NSFileManager defaultManager] changeCurrentDirectoryPath:[[NSBundle mainBundle] resourcePath]]) {
262         Sys_Error("Could not access application resources");
263     }
264     //cddir = macosx_scanForLibraryDirectory();
265     /*
266     do {
267         tryAgain = NO;
268         defaultManager = [NSFileManager defaultManager];
269         if (![defaultManager fileExistsAtPath:@"./base/default.cfg"] && (!cddir || *cddir == '\0' || ![defaultManager fileExistsAtPath:[NSString stringWithFormat:@"%s/baseq3/pak0.pk3", cddir]])) {
270             NSString *message;
271
272             if (!cddir || *cddir == '\0') {
273                 message = [NSString stringWithFormat:@"Could not find DOOM levels."];
274             } else if (![defaultManager fileExistsAtPath:[NSString stringWithFormat:@"%s", cddir]]) {
275                 message = [NSString stringWithFormat:@"Could not find DOOM levels:  '%s' does not exist.", cddir];
276             } else {
277                 message = [NSString stringWithFormat:@"Could not find DOOM levels:  '%s' is not a complete DOOM installation.", cddir];
278             }
279             switch (NSRunAlertPanel(@"DOOM", @"%@", @"Quit", @"Find...", nil, message)) {
280                 case NSAlertDefaultReturn:
281                 default:
282                     Sys_Quit();
283                     break;
284                 case NSAlertAlternateReturn:
285                     tryAgain = YES;
286                     break;
287             }
288             if (tryAgain) {
289                 NSOpenPanel *openPanel;
290                 int result;
291
292                 openPanel = [NSOpenPanel openPanel];
293                 [openPanel setAllowsMultipleSelection:NO];
294                 [openPanel setCanChooseDirectories:YES];
295                 [openPanel setCanChooseFiles:NO];
296                 result = [openPanel runModalForDirectory:nil file:nil];
297                 if (result == NSOKButton) {
298                     NSArray *filenames;
299
300                     filenames = [openPanel filenames];
301                     if ([filenames count] == 1) {
302                         NSString *cdPath;
303
304                         cdPath = [filenames objectAtIndex:0];
305                         [[NSUserDefaults standardUserDefaults] setObject:cdPath forKey:@"CDPath"];
306                         cddir = strdup([cdPath cString]);
307                     }
308                 }
309             }
310         }
311     } while (tryAgain);
312     */
313 /*
314     if (cddir && *cddir != '\0') {
315         SetProgramPath([[[NSString stringWithCString:cddir] stringByAppendingPathComponent:@"/x"] cString]);
316     }
317 */
318
319         //Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
320
321         Posix_EarlyInit( );
322
323 #ifndef _DEBUG
324         if ( [self checkOS] == FALSE) {
325                 common->Quit();
326         }
327         
328         if ( [self checkDVD] == FALSE) {
329                 common->Quit();
330         }
331 #endif
332         
333         // need strncmp, can't use idlib before init
334 #undef strncmp
335         // Finder passes the process serial number as only argument after the program path
336         // nuke it if we see it
337         if ( argc > 1 && strncmp( argv[ 1 ], "-psn", 4 ) ) {
338                 common->Init( argc-1, &argv[1], NULL );
339         } else {
340                 common->Init( 0, NULL, NULL );
341         }
342
343         Posix_LateInit( );
344
345     [NSApp activateIgnoringOtherApps:YES];
346
347     while (1) {
348 #ifdef OMNI_TIMER
349         OTPeriodicTimerReset();
350         OTNodeStart(RootNode);
351 #endif
352
353                 // maintain exceptions in case system calls are turning them off (is that needed)
354                 //Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
355
356                 common->Frame();
357
358         // We should think about doing this less frequently than every frame
359         [pool release];
360         pool = [[NSAutoreleasePool alloc] init];
361 #ifdef OMNI_TIMER
362         OTNodeStop(RootNode);
363 #endif
364     }
365
366     [pool release];
367 }
368
369 - (BOOL)checkRegCodes
370 {
371         BOOL retval;
372         NSString *cdKey;
373         NSUserDefaults *userDefaults;
374         
375         userDefaults = [NSUserDefaults standardUserDefaults];
376         cdKey = [userDefaults stringForKey:kRegKey];
377         
378         retval = TRUE;
379         if ( cdKey == nil || [cdKey length] == 0 ) {
380                 char regCode[256];
381                 if ( DoRegCodeDialog( regCode ) != noErr ) {
382                         retval = FALSE;
383                 }
384                 else {
385                         [userDefaults setObject:[NSString stringWithCString: regCode] forKey:kRegKey];
386                         [userDefaults synchronize];
387                 }
388         }
389         return retval;
390 }
391
392 - (BOOL)checkOS
393 {
394         OSErr   err;
395         long gestaltOSVersion;
396         err = Gestalt(gestaltSystemVersion, &gestaltOSVersion);
397         if ( err || gestaltOSVersion < 0x1038 ) {
398                 NSBundle *thisBundle = [ NSBundle mainBundle ];
399                 NSString *messsage = [ thisBundle localizedStringForKey:@"InsufficientOS" value:@"No translation" table:nil ];
400                 NSRunAlertPanel(@GAME_NAME, messsage, nil, nil, nil);
401                 return FALSE;
402         }
403         return TRUE;
404 }
405
406 - (BOOL)checkDVD
407 {
408         return TRUE;
409 }
410
411 @end
412
413 /*
414 ==============
415 Sys_EXEPath
416 ==============
417 */
418 const char *Sys_EXEPath( void ) {
419         static char exepath[ 1024 ];
420         strncpy( exepath, [ [ [ NSBundle mainBundle ] bundlePath ] cString ], 1024 );
421         return exepath;
422 }
423
424 /*
425  ==========
426  Sys_DefaultSavePath
427  ==========
428  */
429 const char *Sys_DefaultSavePath(void) {
430 #if defined( ID_DEMO_BUILD )
431         sprintf( savepath, "%s/Library/Application Support/Doom 3 Demo", [NSHomeDirectory() cString] );
432 #else
433         sprintf( savepath, "%s/Library/Application Support/Doom 3", [NSHomeDirectory() cString] );
434 #endif
435         return savepath.c_str();
436 }
437
438 /*
439 ==========
440 Sys_DefaultBasePath
441 ==========
442 */
443 const char *Sys_DefaultBasePath(void) {
444         static char basepath[ 1024 ];
445         strncpy( basepath, [ [ [ NSBundle mainBundle ] bundlePath ] cString ], 1024 );
446         char *snap = strrchr( basepath, '/' );
447         if ( snap ) {
448                 *snap = '\0';
449         }
450         return basepath;
451 }
452
453 /*
454 ===============
455 Sys_Shutdown
456 ===============
457 */
458 void Sys_Shutdown( void ) {
459         savepath.Clear();
460         Posix_Shutdown();
461 }
462
463
464 /*
465 ===============
466 Sys_GetProcessorId
467 ===============
468 */
469 cpuid_t Sys_GetProcessorId( void ) {
470         cpuid_t cpuid = CPUID_GENERIC;
471 #if defined(__ppc__)
472         cpuid |= CPUID_ALTIVEC;
473 #elif defined(__i386__)
474         cpuid |= CPUID_INTEL | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_SSE3 | CPUID_HTT | CPUID_CMOV | CPUID_FTZ | CPUID_DAZ;
475 #endif
476         return cpuid;
477 }
478
479 /*
480 ===============
481 Sys_GetProcessorString
482 ===============
483 */
484 const char *Sys_GetProcessorString( void ) {
485 #if defined(__ppc__)
486         return "ppc CPU with AltiVec extensions";
487 #elif defined(__i386__)
488         return "x86 CPU with MMX/SSE/SSE2/SSE3 extensions";
489 #else
490         #error
491         return NULL;
492 #endif
493 }
494
495 /*
496 ===============
497 Sys_FPU_EnableExceptions
498 http://developer.apple.com/documentation/mac/PPCNumerics/PPCNumerics-154.html
499 http://developer.apple.com/documentation/Performance/Conceptual/Mac_OSX_Numerics/Mac_OSX_Numerics.pdf
500 ===============
501 */
502
503 #define fegetenvd(x) asm volatile( "mffs %0" : "=f" (x) );
504 #define fesetenvd(x) asm volatile( "mtfsf 255,%0" : : "f" (x) ); 
505 enum {
506         FE_ENABLE_INEXACT               = 0x8,
507         FE_ENABLE_DIVBYZERO             = 0x10,
508         FE_ENABLE_UNDERFLOW             = 0x20,
509         FE_ENABLE_OVERFLOW              = 0x40,
510         FE_ENABLE_INVALID               = 0x80,
511         FE_ENABLE_ALL_EXCEPT    = 0xF8
512 };
513
514 typedef union {
515         struct {
516                 unsigned long hi;
517                 unsigned long lo;
518         } i;
519         double d;
520 } hexdouble;
521
522 static int exception_mask = 0;
523
524 void Sys_FPU_EnableExceptions( int exceptions ) {
525 #if 0
526         if ( exceptions & ( FPU_EXCEPTION_INVALID_OPERATION | FPU_EXCEPTION_DENORMALIZED_OPERAND ) ) {
527                 // clear the flag before enabling the exception
528                 asm( "mtfsb0 2" );
529                 asm( "mtfsb0 7" );
530                 asm( "mtfsb0 8" );
531                 asm( "mtfsb0 9" );
532                 asm( "mtfsb0 10" );
533                 asm( "mtfsb0 11" );
534                 asm( "mtfsb0 12" );
535                 asm( "mtfsb0 21" );
536                 asm( "mtfsb0 22" );
537                 asm( "mtfsb0 23" );
538                 // enable
539                 asm( "mtfsb1 24" );
540         } else {
541                 asm( "mtfsb0 24" );
542         }
543         if ( exceptions & FPU_EXCEPTION_DIVIDE_BY_ZERO ) {
544                 asm( "mtfsb0 5" );
545                 asm( "mtfsb1 27" );
546         } else {
547                 asm( "mtfsb0 27" );
548         }
549         if ( exceptions & FPU_EXCEPTION_NUMERIC_OVERFLOW ) {
550                 asm( "mtfsb0 3" );
551                 asm( "mtfsb1 25" );
552         } else {
553                 asm( "mtfsb0 25" );
554         }
555         if ( exceptions & FPU_EXCEPTION_NUMERIC_UNDERFLOW ) {
556                 asm( "mtfsb0 4" );
557                 asm( "mtfsb1 26" );
558         } else {
559                 asm( "mtfsb0 26" );
560         }
561         if ( exceptions & FPU_EXCEPTION_INEXACT_RESULT ) {
562                 asm( "mtfsb0 6" );
563                 asm( "mtfsb0 13" );
564                 asm( "mtfsb0 14" );
565                 asm( "mtfsb1 28" );
566         } else {
567                 asm( "mtfsb0 28" );
568         }
569 #elif defined(__ppc__)
570         hexdouble t;
571         exception_mask = 0;
572         if ( exceptions & ( FPU_EXCEPTION_INVALID_OPERATION | FPU_EXCEPTION_DENORMALIZED_OPERAND ) ) {
573                 exception_mask |= FE_ENABLE_INVALID;
574         }
575         if ( exceptions & FPU_EXCEPTION_DIVIDE_BY_ZERO ) {
576                 exception_mask |= FE_ENABLE_DIVBYZERO;
577         }
578         if ( exceptions & FPU_EXCEPTION_NUMERIC_OVERFLOW ) {
579                 exception_mask |= FE_ENABLE_OVERFLOW;
580         }
581         if ( exceptions & FPU_EXCEPTION_NUMERIC_UNDERFLOW ) {
582                 exception_mask |= FE_ENABLE_UNDERFLOW;
583         }
584         if ( exceptions & FPU_EXCEPTION_INEXACT_RESULT ) {
585                 exception_mask |= FE_ENABLE_INVALID;
586         }
587         Sys_Printf( "Sys_FPUEnableExceptions: 0x%x\n", exception_mask );
588         // clear the exception flags
589         feclearexcept( FE_ALL_EXCEPT );
590         // set the enable flags on the exceptions we want
591         fegetenvd( t.d );
592         t.i.lo &= ~FE_ENABLE_ALL_EXCEPT;
593         t.i.lo |= exception_mask;
594         fesetenvd( t.d );
595         Sys_Printf( "done\n" );
596 #endif
597 }
598
599 /*
600 ===============
601 Sys_FPE_handler
602 ===============
603 */
604 void Sys_FPE_handler( int signum, siginfo_t *info, void *context ) {
605 #if defined(__ppc__)
606         int ret;
607         ppc_float_state_t *fs;
608         ppc_thread_state_t *ss;
609
610         fs = &( (struct ucontext *)context )->uc_mcontext->fs;
611         ss = &( (struct ucontext *)context )->uc_mcontext->ss;
612
613         Sys_Printf( "FPE at 0x%x:\n", info->si_addr );
614
615         ret = fetestexcept( FE_ALL_EXCEPT );
616         if ( ret & FE_INEXACT ) {
617                 Sys_Printf( "FE_INEXACT " );
618         }
619         if ( ret & FE_DIVBYZERO ) {
620                 Sys_Printf( "FE_DIVBYZERO " );
621         }
622         if ( ret & FE_UNDERFLOW ) {
623                 Sys_Printf( "FE_UNDERFLOW " );
624         }
625         if ( ret & FE_OVERFLOW ) {
626                 Sys_Printf( "FE_OVERFLOW " );
627         }
628         if ( ret & FE_INVALID ) {
629                 Sys_Printf( "FE_INVALID " );
630         }
631         Sys_Printf( "\n" );
632         // clear the exception flags
633         feclearexcept( FE_ALL_EXCEPT );
634         // re-arm
635         fs->fpscr &= exception_mask;
636         ss->srr0 += 4;
637 #endif
638 }
639
640 /*
641 ===============
642 Sys_GetClockTicks
643 ===============
644 */
645 double Sys_GetClockTicks( void ) {
646         // NOTE that this only affects idTimer atm, which is only used for performance timing during developement
647 #warning FIXME: implement Sys_GetClockTicks
648         return 0.0;
649 }
650
651 /*
652 ===============
653 Sys_ClockTicksPerSecond
654 ===============
655 */
656 double Sys_ClockTicksPerSecond(void) {
657         // Our strategy is to query both Gestalt & IOKit and then take the larger of the two values.
658         
659         long gestaltSpeed, ioKitSpeed = -1;
660         
661         // GESTALT
662         
663         // gestaltProcClkSpeedMHz available in 10.3 needs to be used because CPU speeds have now
664         // exceeded the signed long that Gestalt returns.
665         long osVers;
666         OSErr err;
667         Gestalt(gestaltSystemVersion, &osVers);
668         if (osVers >= 0x1030)
669                 err = Gestalt(gestaltProcClkSpeedMHz, &gestaltSpeed);
670         else
671         {
672                 err = Gestalt(gestaltProcClkSpeed, &gestaltSpeed);
673                 if (err == noErr)
674                         gestaltSpeed = gestaltSpeed / 1000000;                          
675         }       
676         
677         // IO KIT
678         
679     mach_port_t masterPort;
680         CFMutableDictionaryRef matchDict = nil;
681         io_iterator_t itThis;
682         io_service_t service = nil;
683         
684     if (IOMasterPort(MACH_PORT_NULL, &masterPort))
685                 goto bail;
686         
687         matchDict = IOServiceNameMatching("cpus");      
688         if (IOServiceGetMatchingServices(masterPort, matchDict, &itThis))
689                 goto bail;
690     
691         service = IOIteratorNext(itThis);
692     while(service)
693     {
694                 io_service_t ioCpu = NULL;
695                 if (IORegistryEntryGetChildEntry(service, kIODeviceTreePlane, &ioCpu))
696                         goto bail;
697                 
698                 if (ioCpu)
699                 {
700                         CFDataRef data = (CFDataRef)IORegistryEntryCreateCFProperty(ioCpu, CFSTR("clock-frequency"),kCFAllocatorDefault,0);
701                         if (data)
702                                 ioKitSpeed = *((unsigned long*)CFDataGetBytePtr(data)) / 1000000;
703                 }
704                 service = IOIteratorNext(itThis);
705         }
706         
707         // Return the larger value
708         
709 bail:
710         return ( ioKitSpeed > gestaltSpeed ? ioKitSpeed : gestaltSpeed ) * 1000000.f;
711 }
712
713 /*
714 ================
715 Sys_GetSystemRam
716 returns in megabytes
717 ================
718 */
719 int Sys_GetSystemRam( void ) {
720         long ramSize;
721         
722         if ( Gestalt( gestaltPhysicalRAMSize, &ramSize ) == noErr ) {
723                 return ramSize / (1024*1024);
724         }
725         else
726                 return 1024;
727 }
728
729 /*
730 ================
731 Sys_GetVideoRam
732 returns in megabytes
733 ================
734 */
735 int Sys_GetVideoRam( void ) {
736         unsigned int i;
737         CFTypeRef typeCode;
738         long vramStorage = 64;
739         const short MAXDISPLAYS = 8;
740         CGDisplayCount displayCount;
741         io_service_t dspPorts[MAXDISPLAYS];
742         CGDirectDisplayID displays[MAXDISPLAYS];
743
744         CGGetOnlineDisplayList( MAXDISPLAYS, displays, &displayCount );
745         
746         for ( i = 0; i < displayCount; i++ ) {
747                 if ( Sys_DisplayToUse() == displays[i] ) {
748                         dspPorts[i] = CGDisplayIOServicePort(displays[i]);
749                         typeCode = IORegistryEntryCreateCFProperty( dspPorts[i], CFSTR("IOFBMemorySize"), kCFAllocatorDefault, kNilOptions );
750                         if( typeCode && CFGetTypeID( typeCode ) == CFNumberGetTypeID() ) {
751                                 CFNumberGetValue( ( CFNumberRef )typeCode, kCFNumberSInt32Type, &vramStorage );
752                                 vramStorage /= (1024*1024);
753                         }
754                 }
755         }
756
757         return vramStorage;
758 }
759
760 bool OSX_GetCPUIdentification( int& cpuId, bool& oldArchitecture )
761 {
762         long cpu;
763         Gestalt(gestaltNativeCPUtype, &cpu);
764         
765         cpuId = cpu;
766         oldArchitecture = cpuId < gestaltCPU970;
767         return true;
768 }
769
770 void OSX_GetVideoCard( int& outVendorId, int& outDeviceId )
771 {
772     kern_return_t err;
773     mach_port_t masterPort;
774     io_iterator_t itThis;
775     io_service_t service;
776         
777         outVendorId = -1;
778         outDeviceId = -1;
779         
780         // Get a mach port for us and check for errors
781     err = IOMasterPort(MACH_PORT_NULL, &masterPort);
782     if(err)
783                 return;
784     // Grab all the PCI devices out of the registry
785     err = IOServiceGetMatchingServices(masterPort, IOServiceMatching("IOPCIDevice"), &itThis);
786     if(err)
787                 return;
788     
789     // Yank everything out of the iterator
790         // We could walk through all devices and try to determine the best card. But for now,
791         // we'll just look at the first card.
792     while(1)
793     {
794                 service = IOIteratorNext(itThis);
795                 io_name_t dName;
796                 
797                 // Make sure we have a valid service
798                 if(service)
799                 {
800                         // Get the classcode so we know what we're looking at
801                         CFDataRef classCode =  (CFDataRef)IORegistryEntryCreateCFProperty(service,CFSTR("class-code"),kCFAllocatorDefault,0);
802                         // Only accept devices that are 
803                         // PCI Spec - 0x00030000 is a display device
804                         if((*(UInt32*)CFDataGetBytePtr(classCode) & 0x00ff0000) == 0x00030000)
805                         {
806                                 // Get the name of the service (hw)
807                                 IORegistryEntryGetName(service, dName);
808                                 
809                             CFDataRef vendorID, deviceID;
810                             
811                                 // Get the information for the device we've selected from the list
812                             vendorID = (CFDataRef)IORegistryEntryCreateCFProperty(service, CFSTR("vendor-id"),kCFAllocatorDefault,0);
813                             deviceID = (CFDataRef)IORegistryEntryCreateCFProperty(service, CFSTR("device-id"),kCFAllocatorDefault,0);
814                             
815                             outVendorId = *((long*)CFDataGetBytePtr(vendorID));
816                             outDeviceId = *((long*)CFDataGetBytePtr(deviceID));
817                                 
818                                 CFRelease(vendorID);
819                                 CFRelease(deviceID);
820                         }
821                         CFRelease(classCode);
822                         
823                         // Stop after finding the first device
824                         if (outVendorId != -1)
825                                 break;
826                 }
827                 else
828                         break;
829         }
830 }
831
832 /*
833 ===============
834 main
835 ===============
836 */
837 int main( int argc, const char *argv[] ) {
838         return NSApplicationMain( argc, argv );
839 }
840
841
842 #pragma mark -
843
844
845 bool FormatRegCode(const char* inRegCode, char* outRegCode)
846 {       
847         // Clean up the reg code. Remove spaces. Accept only numbers/letters.
848         char* dst = outRegCode;
849         const char* src = inRegCode;
850         while (*src)
851         {
852                 if (isalnum(*src))
853                         *dst++ = *src;
854                 else if (*src != ' ')
855                         return false;
856                 src++;
857         }
858         *dst = 0;
859         
860         // Reg codes are 18 characters in length
861         return strlen(outRegCode) == 18;
862 }
863
864 /*
865  ===============
866  RegCodeHandler
867  ===============
868  */
869 static pascal OSStatus RegCodeHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* inUserData )
870 {
871 #pragma unused( inHandler )
872 #if 1
873         // FIXME: the CD key API has changed for startup check support and expansion pack key support
874         return noErr;
875 #else   
876         HICommand                       cmd;
877         OSStatus                        result = eventNotHandledErr;
878         RegCodeInfo*            regCodeInfo = (RegCodeInfo*)inUserData;
879         
880         GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd );
881         
882         switch ( cmd.commandID ) {
883                 case kHICommandOK:
884                         bool fValid;
885                         Size actualSize;
886                         char cntrl[256];
887                         char doomKey[256];
888                         char strippedKey[256];
889                         
890                         fValid = false;
891                         strippedKey[0] = doomKey[0] = NULL;
892                         GetControlData ( regCodeInfo->regCode1EditText, kControlEntireControl, kControlEditTextTextTag, 256, cntrl, &actualSize );
893                         cntrl[actualSize] = NULL;
894                         if ( FormatRegCode( cntrl, strippedKey ) ) {
895                                 strncat( doomKey, strippedKey, 16 );
896                                 strcat( doomKey, " " );
897                                 strncat( doomKey, strippedKey + 16, 2 );
898                                 fValid = session->CheckKey( doomKey );
899                         }
900                         if ( fValid ) {
901                                 strcpy( regCodeInfo->prefRegCode1, doomKey );
902                                 session->SetCDKey( doomKey );
903                         }
904                         else {
905                                 unsigned char theError[512]; 
906                                 unsigned char theExplanation[512];
907                                 CFStringRef theErrorStr = CFCopyLocalizedString( CFSTR("DVD_KEY_ERROR"), "" );
908                                 CFStringRef theExplanationStr = CFCopyLocalizedString( CFSTR("DVD_KEY_EXPLANATION"), "" );
909                                 c2pstrcpy( theError, CFStringGetCStringPtr( theErrorStr, kCFStringEncodingMacRoman ) );
910                                 c2pstrcpy( theExplanation, CFStringGetCStringPtr( theExplanationStr, kCFStringEncodingMacRoman )  );
911                                 
912                                 StandardAlert(kAlertStopAlert, theError, theExplanation, NULL, NULL);
913                                 
914                                 // Highlight the invalid reg code
915                                 ClearKeyboardFocus(regCodeInfo->window);
916                                 SetKeyboardFocus( regCodeInfo->window, regCodeInfo->regCode1EditText, kControlEditTextPart );
917                                 ControlEditTextSelectionRec sel = {0, 32000};
918                                 SetControlData (regCodeInfo->regCode1EditText, kControlEntireControl, kControlEditTextSelectionTag, sizeof(sel), &sel);
919                                 break;  
920                         }
921                         
922                         regCodeInfo->okPressed = true;
923                         QuitAppModalLoopForWindow( regCodeInfo->window );
924                         result = noErr;
925                         
926                         break;
927                         
928                 case kHICommandCancel:
929                         regCodeInfo->okPressed = false;
930                         QuitAppModalLoopForWindow( regCodeInfo->window );
931                         result = noErr;
932                         break;
933                         
934         }       
935         return result;
936 #endif
937 }
938
939 /*
940  ===============
941  DoRegCodeDialog
942  ===============
943  */
944 static OSErr DoRegCodeDialog( char* ioRegCode1 )
945 {
946         OSErr err;
947         RegCodeInfo regCodeInfo;
948         memset(&regCodeInfo, 0, sizeof(regCodeInfo));
949         
950         IBNibRef aslNib;
951         CFBundleRef theBundle = CFBundleGetMainBundle();
952         err = CreateNibReferenceWithCFBundle( theBundle, CFSTR("ASLCore"), &aslNib );
953         err = ::CreateWindowFromNib( aslNib, CFSTR("Reg Code Sheet"), &regCodeInfo.window );
954         if (err != noErr)
955                 return err;
956         
957         GetControlByID( regCodeInfo.window, &kRegCode1EditText, &regCodeInfo.regCode1EditText );
958         assert( regCodeInfo.regCode1EditText );
959         SetKeyboardFocus( regCodeInfo.window, regCodeInfo.regCode1EditText, kControlEditTextPart );
960         ControlEditTextSelectionRec sel = {0, 32000};
961         SetControlData (regCodeInfo.regCode1EditText, kControlEntireControl, kControlEditTextSelectionTag, sizeof(sel), &sel);
962         
963         EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess };
964         EventHandlerUPP handler = NewEventHandlerUPP( RegCodeHandler );
965         InstallWindowEventHandler( regCodeInfo.window, handler, 1, &cmdEvent, &regCodeInfo, NULL );
966         
967         RepositionWindow( regCodeInfo.window, NULL, kWindowAlertPositionOnMainScreen );
968         ShowWindow( regCodeInfo.window );
969         
970         RunAppModalLoopForWindow( regCodeInfo.window );
971         
972         DisposeWindow( regCodeInfo.window );
973         
974         if (regCodeInfo.okPressed) {    
975                 strcpy(ioRegCode1, regCodeInfo.prefRegCode1);
976         }
977         
978         return regCodeInfo.okPressed ? (OSErr)noErr : (OSErr)userCanceledErr;   
979 }
980
981 /*
982 =================
983 Sys_AsyncThread
984 =================
985 */
986 void Sys_AsyncThread( void ) {
987         while ( 1 ) {
988                 usleep( 16666 );
989                 common->Async();
990                 Sys_TriggerEvent( TRIGGER_EVENT_ONE );
991                 pthread_testcancel();
992         }
993 }
994
995
996 #if defined(__ppc__)
997
998 /*
999  ================
1000  Sys_FPU_SetDAZ
1001  ================
1002  */
1003 void Sys_FPU_SetDAZ( bool enable ) {
1004 }
1005
1006 /*
1007  ================
1008  Sys_FPU_SetFTZ
1009  ================
1010  */
1011 void Sys_FPU_SetFTZ( bool enable ) {
1012 }
1013
1014
1015 #elif defined(__i386__)
1016
1017 #include <xmmintrin.h>
1018
1019 /*
1020  ================
1021  Sys_FPU_SetDAZ
1022  ================
1023  */
1024 void Sys_FPU_SetDAZ( bool enable ) {
1025         uint32_t dwData;
1026         uint32_t enable_l = (uint32_t) enable;
1027         
1028         enable_l = enable_l & 1;
1029         enable_l = enable_l << 6;       
1030         dwData = _mm_getcsr(); // store MXCSR to dwData
1031         dwData = dwData & 0xffbf;
1032         dwData = dwData | enable_l;
1033         _mm_setcsr(dwData); // load MXCSR with dwData
1034 }
1035
1036 /*
1037  ================
1038  Sys_FPU_SetFTZ
1039  ================
1040  */
1041 void Sys_FPU_SetFTZ( bool enable ) {
1042         
1043         uint32_t dwData;
1044         uint32_t enable_l = (uint32_t) enable;
1045         
1046         enable_l = enable_l & 1;
1047         enable_l = enable_l << 15;      
1048         dwData = _mm_getcsr(); // store MXCSR to dwData
1049         dwData = dwData & 0x7fff;
1050         dwData = dwData | enable_l;
1051         _mm_setcsr(dwData); // load MXCSR with dwData
1052 }
1053
1054 #endif