/*
===========================================================================
Wolfenstein: Enemy Territory GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
This file is part of the Wolfenstein: Enemy Territory GPL Source Code (ΒWolf ET Source CodeΒ).
Wolf ET Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wolf ET Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wolf ET Source Code. If not, see .
In addition, the Wolf: ET 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 Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below.
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.
===========================================================================
*/
#include
#include
Q3DEF_BEGIN
#include "../renderer/tr_local.h"
#include "../client/client.h"
#include "mac_local.h"
#include "../game/g_public.h"
#include "../qcommon/unzip.h"
Q3DEF_END
#include
//#include "HID Manager/HID_Utilities_CFM.h"
#include "AGLUtils.h"
#include "MacPrefs.h"
#include "CDrawSprocket.h"
#if MAC_Q3_QUICKTIME
#include "CQuickTimePlayer.h"
#endif
#include "PickMonitor/pickmonitor.h"
#if MAC_Q3_MP
#define kOMEventClass FOUR_CHAR_CODE( 'OM2!' )
#define kOMExecuteCommands FOUR_CHAR_CODE( 'exec' )
//#include "GameRanger SDK/GameRanger.h"
qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message );
#endif
void GetDialogItemTextAppearance( DialogRef inDialog, DialogItemIndex inItem, StringPtr outText );
char *ReadCommandLineParms( void );
static cvar_t *gamedll;
static cvar_t *cgamedll;
cvar_t *pagememory;
int sys_ticBase;
int sys_msecBase;
int sys_lastEventTic;
int sys_CDinDrive = false;
cvar_t *sys_profile;
static char *sCommandLine;
static char *sCommandLine_temp;
static Boolean sGameRangerHosting;
FSSpec app_spec;
long gSystemVersion;
Boolean gUseCarbonEvents;
Boolean gMoviePlaying;
Fixed gRefreshRate;
SInt32 mac_keyboardScript;
Boolean macSessionGraphics = false;
Boolean OptionDialog( void );
Q3DEF_BEGIN
extern void GLimp_pause( void );
extern void GLimp_resume( void );
//===========================================================================
qboolean Sys_CheckCD( void ) {
return qtrue;
}
#pragma mark -
#if MAC_Q3_MP
// Call this from SV_SpawnServer() in sv_init.c(pp), right after the call to SV_InitGameProgs();
void Mac_GameRanger_HostReady( void ) {
// Were we started by GameRanger?
if ( sGameRangerHosting ) {
// Yes, tell the clients we're ready to host so that they can join
//GRHostReady ();
// Clear the flag so we don't call GR inappropriately
sGameRangerHosting = false;
}
}
#endif
//===========================================================================
#pragma mark -
CFragConnectionID game_connID;
CFragConnectionID cgame_connID;
CFragConnectionID ui_connID;
typedef void ( *dllEntryPtr )( int ( *syscallptr )( int, ... ) );
typedef int ( *systemCallsPtr )( int, ... );
typedef int ( *vmMainPtr )( int, ... );
typedef struct
{
// For CFM shared libraries
CFragConnectionID connID;
// For Mach-O bundles
CFBundleRef bundleRef;
CFURLRef bundleURL;
systemCallsPtr cfmGlue;
} dll_t;
//
// This function allocates a block of CFM glue code which contains the instructions to call CFM routines
//
UInt32 sGlueCode[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420};
void *MachOFunctionPointerForCFMFunctionPointer( void *cfmfp ) {
UInt32 *mfp = (UInt32*) NewPtr( sizeof( sGlueCode ) ); // Must later dispose of allocated memory
mfp[0] = sGlueCode[0] | ( (UInt32)cfmfp >> 16 );
mfp[1] = sGlueCode[1] | ( (UInt32)cfmfp & 0xFFFF );
mfp[2] = sGlueCode[2];
mfp[3] = sGlueCode[3];
mfp[4] = sGlueCode[4];
mfp[5] = sGlueCode[5];
MakeDataExecutable( mfp, sizeof( sGlueCode ) );
return( mfp );
}
/*
===============
Sys_UnpackDLL
Tries to find a named DLL in the pk3 hierarchy. If found, it copies it, and then
attempts to treat the copied file as a zip file, unzipping it to the temp directory.
This should only be called when looking for a Mach-O .bundle DLL.
===============
*/
extern qboolean legacy_mp_bin;
Boolean Sys_UnpackDLL( const char *name ) {
void *data;
FILE *macFile;
int len = FS_ReadFile( name, &data );
int ck;
char zipPath[PATH_MAX];
char macFolderPath[PATH_MAX];
if ( len < 1 ) {
// failed to read the file (if pure, may exist out of the pk3s)
return false;
}
// qagame can be loaded outside of pk3 hierarchy
// for everything else, pure rules apply
if ( strcmp( name, "qagame_mac" ) ) {
if ( FS_FileIsInPAK( name, &ck ) == -1 ) {
FS_FreeFile( data );
return false;
}
}
if ( legacy_mp_bin ) {
// don't load from legacy 2.60 mp_bin.pk3, get our updated version
return false;
}
// We create our temp copy of the DLL zip file outside the folder hierarchy
// so that it's not found and used the next time through (which might mark it as unpure).
Mac_GetOSPath( kUserDomain, kTemporaryFolderType, macFolderPath );
sprintf( zipPath, "%s%s%s", macFolderPath, kPathSep, kMacGameDir );
Sys_Mkdir( zipPath );
sprintf( zipPath, "%s%s%s%s%s", macFolderPath, kPathSep, kMacGameDir, kPathSep, name );
macFile = fopen( zipPath, "wb" );
if ( !macFile ) {
//can't open for writing? Might be in use.
//This is possibly a malicious user attempt to circumvent dll
//replacement so we won't allow it.
FS_FreeFile( data );
return false;
}
if ( fwrite( data, 1, len, macFile ) < len ) {
//Failed to write the full length. Full disk maybe?
FS_FreeFile( data );
fclose( macFile );
return false;
}
fclose( macFile );
// LBO - now try and unzip the file
{
unzFile zipFile;
unz_global_info zipInfo;
int err;
int i;
void *buf = NULL;
zipFile = unzOpen( zipPath );
if ( !zipFile ) {
return false;
}
err = unzGetGlobalInfo( zipFile, &zipInfo );
if ( err != UNZ_OK ) {
return false;
}
err = unzGoToFirstFile( zipFile );
for ( i = 0; i < zipInfo.number_entry; i++ )
{
unz_file_info file_info;
char newFileName[256];
char newFilePath[PATH_MAX];
FILE *newFile;
int j;
err = unzGetCurrentFileInfo( zipFile, &file_info, newFileName, sizeof( newFileName ), NULL, 0, NULL, 0 );
for ( j = 0; j < strlen( newFileName ); j++ )
{
if ( newFileName[j] == '/' ) {
newFileName[j] = kPathSep[0];
}
}
sprintf( newFilePath, "%s%s%s%s%s", macFolderPath, kPathSep, kMacGameDir, kPathSep, newFileName );
if ( newFilePath[strlen( newFilePath ) - 1] == kPathSep[0] ) {
newFilePath[strlen( newFilePath ) - 1] = 0;
Sys_Mkdir( newFilePath );
} else
{
newFile = fopen( newFilePath,"wb" );
if ( !newFile ) {
break;
}
if ( buf ) {
free( buf );
}
buf = malloc( file_info.uncompressed_size );
if ( !buf ) {
break;
}
err = unzOpenCurrentFile( zipFile );
if ( err ) {
break;
}
err = unzReadCurrentFile( zipFile, buf, file_info.uncompressed_size );
if ( ( err < 0 ) || ( err != file_info.uncompressed_size ) ) {
break;
}
err = fwrite( buf, file_info.uncompressed_size, 1, newFile );
if ( err != 1 ) {
break;
}
fclose( newFile );
err = unzCloseCurrentFile( zipFile );
}
err = unzGoToNextFile( zipFile );
if ( err ) {
break;
}
}
if ( buf ) {
free( buf );
}
}
FS_FreeFile( data );
remove( zipPath );
return true;
}
#if MAC_WOLF2_MP
char* Sys_GetDLLName( const char *name ) {
#if MAC_DEBUG
return va( "%s_d_mac", name );
#else
return va( "%s_mac", name );
#endif
/*
return va("%s.mp.osx.so", name);
*/
}
#endif
#pragma mark Sys_LoadDll
void *Sys_LoadDll( const char *name, char *fqpath, int( **entryPoint ) ( int, ... ), int ( *systemCalls )( int, ... ) ) {
OSErr err = noErr;
FSSpec SLSpec;
char name2[255];
dllEntryPtr dllEntry = NULL;
dll_t *dllSpec;
legacy_mp_bin = qfalse;
dllSpec = (dll_t *) calloc( sizeof( dll_t ), 1 );
if ( dllSpec == NULL ) {
goto cantLoad;
}
err = noErr;
Com_sprintf( name2, sizeof( name2 ), "%s", Sys_GetDLLName( name ) );
Boolean didLoad;
// Try Mach-O first.
tryMachO:
{
char macFolderPath[PATH_MAX];
char temp[PATH_MAX];
if ( !Sys_UnpackDLL( name2 ) ) {
goto tryAppPackage;
}
Mac_GetOSPath( kUserDomain, kTemporaryFolderType, macFolderPath );
// create a URL to the bundle
sprintf( temp, "%s%s%s%s%s.bundle", macFolderPath, kPathSep, kMacGameDir, kPathSep, name2 );
dllSpec->bundleURL = CFURLCreateWithBytes( kCFAllocatorDefault, (UInt8 *)temp, strlen( temp ), kCFStringEncodingUTF8, NULL );
// did we actaully get a bundle URL
if ( !dllSpec->bundleURL ) {
goto tryAppPackage;
}
// get the actual bundle for the library
dllSpec->bundleRef = CFBundleCreate( kCFAllocatorDefault, dllSpec->bundleURL );
if ( !dllSpec->bundleRef ) {
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto tryAppPackage;
}
// Load the bundle code
didLoad = CFBundleLoadExecutable( dllSpec->bundleRef );
if ( !didLoad ) {
if ( dllSpec->bundleRef ) {
CFRelease( dllSpec->bundleRef );
}
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto tryAppPackage;
}
dllEntry = (dllEntryPtr)CFBundleGetFunctionPointerForName( dllSpec->bundleRef, CFSTR( "dllEntry" ) );
if ( dllEntry == NULL ) {
if ( dllSpec->bundleRef ) {
CFRelease( dllSpec->bundleRef );
}
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto tryAppPackage;
}
dllEntry( systemCalls );
// Bind "vmMain" so we can call into it later as well
*entryPoint = (vmMainPtr)CFBundleGetFunctionPointerForName( dllSpec->bundleRef, CFSTR( "vmMain" ) );
if ( *entryPoint == NULL ) {
if ( dllSpec->bundleRef ) {
CFRelease( dllSpec->bundleRef );
}
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto tryAppPackage;
}
Com_Printf( "Mach-O bundle %s loaded (pk3)\n", name );
return (void *)dllSpec;
}
tryAppPackage:
// TTimo - never load out of tree if we are in pure mode
// (I suppose all the Mac ports of Q3-tech Aspyr has made are flawed that way)
// when we found in the legacy_mp_bin, keep going. We'll make sure the checksum matches the hardcoded value
if ( !legacy_mp_bin && FS_IsPure() ) {
goto cantLoad;
}
Com_sprintf( name2, sizeof( name2 ), "%s", Sys_GetDLLName( name ) );
strcat( name2, ".bundle" );
// Look for the bundle inside the app package
{
FSRef myBundleRef;
CFBundleRef refMainBundle = NULL;
CFStringRef libString;
CFStringRef cfpath;
char path[ PATH_MAX ];
Boolean ok;
unsigned int checksum;
// get app bundle
refMainBundle = CFBundleGetMainBundle();
if ( !refMainBundle ) {
err = fnfErr;
goto cantLoad;
}
libString = CFStringCreateWithCString( kCFAllocatorDefault, name2, kCFStringEncodingUTF8 );
// get a URL to our shared library from the "Resources" directory of the bundle
dllSpec->bundleURL = CFBundleCopyResourceURL( refMainBundle, libString, NULL, NULL );
if ( !dllSpec->bundleURL ) {
err = fnfErr;
goto cantLoad;
}
if ( legacy_mp_bin ) {
// make sure the override is what we expect
cfpath = CFURLCopyFileSystemPath( dllSpec->bundleURL, kCFURLPOSIXPathStyle );
if ( !CFStringGetCString( cfpath, path, sizeof( path ), kCFStringEncodingASCII ) ) {
Com_Printf( "failed to get the OS path to the bundle\n" );
goto cantLoad;
}
strncat( path, va( "/Contents/MacOS/%s_mac", name ), sizeof( path ) - strlen( path ) - 64 );
checksum = FS_ChecksumOSPath( path );
//Com_Printf( "%s %X\n", path, checksum );
// it doesn't hurt to always do the load and checksum, makes the setup easier
// we only enforce the check when pureness is required however
if ( FS_IsPure() ) {
if ( checksum != 0x50A800B3 && checksum != 0x81A6FB10 ) {
goto cantLoad;
}
}
}
// get the actual bundle for the library
dllSpec->bundleRef = CFBundleCreate( kCFAllocatorDefault, dllSpec->bundleURL );
if ( !dllSpec->bundleRef ) {
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto cantLoad;
}
// Load the bundle code
didLoad = CFBundleLoadExecutable( dllSpec->bundleRef );
if ( !didLoad ) {
if ( dllSpec->bundleRef ) {
CFRelease( dllSpec->bundleRef );
}
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto cantLoad;
}
dllEntry = (dllEntryPtr)CFBundleGetFunctionPointerForName( dllSpec->bundleRef, CFSTR( "dllEntry" ) );
if ( dllEntry == NULL ) {
Com_Printf( "dllEntry not found\n" );
if ( dllSpec->bundleRef ) {
CFRelease( dllSpec->bundleRef );
}
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto cantLoad;
}
dllEntry( systemCalls );
// Bind "vmMain" so we can call into it later as well
*entryPoint = (vmMainPtr)CFBundleGetFunctionPointerForName( dllSpec->bundleRef, CFSTR( "vmMain" ) );
if ( *entryPoint == NULL ) {
Com_Printf( "vmMain not found\n" );
if ( dllSpec->bundleRef ) {
CFRelease( dllSpec->bundleRef );
}
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
goto cantLoad;
}
Com_Printf( "Mach-O bundle %s loaded\n", name );
return (void *)dllSpec;
}
cantLoad:
return NULL;
}
void Sys_UnloadDll( void *dllHandle ) {
OSErr err = noErr;
dll_t *dllSpec = (dll_t *) dllHandle;
if ( dllSpec->connID ) {
err = CloseConnection( &( dllSpec->connID ) );
} else
{
if ( dllSpec->bundleRef ) {
CFRelease( dllSpec->bundleRef );
}
if ( dllSpec->bundleURL ) {
CFRelease( dllSpec->bundleURL );
}
if ( dllSpec->cfmGlue ) {
DisposePtr( (Ptr)dllSpec->cfmGlue );
}
}
free( dllSpec );
}
void *Sys_GetGameAPI( void *parms ) {
void *( *GetGameAPI )(void *);
OSErr err = noErr;
FSSpec SLSpec;
Str255 symName;
CFragSymbolClass symClass;
// If the library is still hanging around for some freakish reason, dump it
if ( game_connID ) {
Sys_UnloadGame();
}
#if MAC_TIME_LIMIT
CheckTimeLimit();
#endif
c2pstrcpy( symName, kMacGameLib );
if ( AmIBundled() ) {
FSRef myBundleRef;
CFBundleRef refMainBundle = NULL;
CFURLRef refMainBundleURL = NULL;
Boolean ok;
// get app bundle
refMainBundle = CFBundleGetMainBundle();
if ( !refMainBundle ) {
err = fnfErr;
goto cantLoad;
}
// get a URL to our shared library from the "Resources" directory of the bundle
refMainBundleURL = CFBundleCopyResourceURL( refMainBundle, CFSTR( kMacGameLib ), NULL, NULL );
if ( !refMainBundleURL ) {
err = fnfErr;
goto cantLoad;
}
// Turn that URL into an FSRef
ok = CFURLGetFSRef( refMainBundleURL, &myBundleRef );
CFRelease( refMainBundleURL );
if ( !ok ) {
err = fnfErr;
goto cantLoad;
}
// Now get an FSSpec for it
err = FSGetCatalogInfo( &myBundleRef, kFSCatInfoNone, NULL, NULL, &SLSpec, NULL );
} else
{
char gameDir2[1024];
err = FSMakeFSSpec( 0, 0L, symName, &SLSpec );
if ( err == fnfErr ) {
sprintf( gameDir2, ":%s:%s", kMacGameDir, kMacGameLib );
c2pstrcpy( symName, gameDir2 );
err = FSMakeFSSpec( 0, 0L, symName, &SLSpec );
}
}
if ( err ) {
goto cantLoad;
}
err = GetDiskFragment( &SLSpec, 0, kCFragGoesToEOF, NULL, kPrivateCFragCopy, &game_connID, (Ptr*)NULL, symName );
// If no memory, try again in the system heap
if ( err == cfragNoClientMemErr ) {
THz savedZone = LMGetApplZone();
LMSetApplZone( LMGetSysZone() );
err = GetDiskFragment( &SLSpec, 0, kCFragGoesToEOF, NULL, kPrivateCFragCopy, &game_connID, (Ptr*)NULL, symName );
LMSetApplZone( savedZone );
}
if ( err ) {
goto cantLoad;
}
strcpy( (char*) symName, (char*)"\pGetGameAPI" );
err = FindSymbol( game_connID, symName, (Ptr *)&GetGameAPI, &symClass );
if ( err ) {
goto cantLoad;
}
return GetGameAPI( parms );
cantLoad:
if ( err == cfragNoClientMemErr ) {
Sys_Error( "Can't load %s. Give the game additional memory and try again.", kMacGameLib );
} else {
Sys_Error( "Can't load %s, error %d", kMacGameLib, err );
}
// Keep the compiler happy
return NULL;
}
void Sys_UnloadGame( void ) {
OSErr err = noErr;
if ( game_connID ) {
err = CloseConnection( &game_connID );
}
game_connID = 0;
}
void *Sys_GetUIAPI( void ) {
void *( *GetUIAPI )(void);
OSErr err = noErr;
FSSpec SLSpec;
Str255 symName;
CFragSymbolClass symClass;
// If the library is still hanging around for some freakish reason, dump it
if ( ui_connID ) {
Sys_UnloadCGame();
}
#if MAC_TIME_LIMIT
CheckTimeLimit();
#endif
c2pstrcpy( symName, kMacUILib );
if ( AmIBundled() ) {
FSRef myBundleRef;
CFBundleRef refMainBundle = NULL;
CFURLRef refMainBundleURL = NULL;
Boolean ok;
// get app bundle
refMainBundle = CFBundleGetMainBundle();
if ( !refMainBundle ) {
err = fnfErr;
goto cantLoad;
}
// get a URL to our shared library from the "Resources" directory of the bundle
refMainBundleURL = CFBundleCopyResourceURL( refMainBundle, CFSTR( kMacUILib ), NULL, NULL );
if ( !refMainBundleURL ) {
err = fnfErr;
goto cantLoad;
}
// Turn that URL into an FSRef
ok = CFURLGetFSRef( refMainBundleURL, &myBundleRef );
CFRelease( refMainBundleURL );
if ( !ok ) {
err = fnfErr;
goto cantLoad;
}
// Now get an FSSpec for it
err = FSGetCatalogInfo( &myBundleRef, kFSCatInfoNone, NULL, NULL, &SLSpec, NULL );
} else
{
char gameDir2[1024];
err = FSMakeFSSpec( 0, 0L, symName, &SLSpec );
if ( err == fnfErr ) {
sprintf( gameDir2, ":%s:%s", kMacGameDir, kMacUILib );
c2pstrcpy( symName, gameDir2 );
err = FSMakeFSSpec( 0, 0L, symName, &SLSpec );
}
if ( err == fnfErr ) {
Sys_Error( "Can't find %s at location %s", kMacUILib, gameDir2 );
}
}
if ( err ) {
goto cantLoad;
}
err = GetDiskFragment( &SLSpec, 0, kCFragGoesToEOF, NULL, kPrivateCFragCopy, &ui_connID, (Ptr*)NULL, symName );
// If no memory, try again in the system heap
if ( err == cfragNoClientMemErr ) {
THz savedZone = LMGetApplZone();
LMSetApplZone( LMGetSysZone() );
err = GetDiskFragment( &SLSpec, 0, kCFragGoesToEOF, NULL, kPrivateCFragCopy, &ui_connID, (Ptr*)NULL, symName );
LMSetApplZone( savedZone );
}
if ( err ) {
goto cantLoad;
}
strcpy( (char*) symName, (char*)"\pGetUIAPI" );
err = FindSymbol( ui_connID, symName, (Ptr *)&GetUIAPI, &symClass );
if ( err ) {
goto cantLoad;
}
return GetUIAPI();
cantLoad:
if ( err == cfragNoClientMemErr ) {
Sys_Error( "Can't load %s. Give the game additional memory and try again.", kMacUILib );
} else {
Sys_Error( "Can't load %s, error %d", kMacUILib, err );
}
// Keep the compiler happy
return NULL;
}
void Sys_UnloadUI( void ) {
OSErr err = noErr;
if ( ui_connID ) {
err = CloseConnection( &ui_connID );
ui_connID = 0;
}
}
void *Sys_GetCGameAPI( void ) {
void *( *GetCGameAPI )(void);
OSErr err = noErr;
FSSpec SLSpec;
Str255 symName;
CFragSymbolClass symClass;
// If the library is still hanging around for some freakish reason, dump it
if ( cgame_connID ) {
Sys_UnloadCGame();
}
#if MAC_TIME_LIMIT
CheckTimeLimit();
#endif
c2pstrcpy( symName, kMacCGameLib );
if ( AmIBundled() ) {
FSRef myBundleRef;
CFBundleRef refMainBundle = NULL;
CFURLRef refMainBundleURL = NULL;
Boolean ok;
// get app bundle
refMainBundle = CFBundleGetMainBundle();
if ( !refMainBundle ) {
err = fnfErr;
goto cantLoad;
}
// get a URL to our shared library from the "Resources" directory of the bundle
refMainBundleURL = CFBundleCopyResourceURL( refMainBundle, CFSTR( kMacCGameLib ), NULL, NULL );
if ( !refMainBundleURL ) {
err = fnfErr;
goto cantLoad;
}
// Turn that URL into an FSRef
ok = CFURLGetFSRef( refMainBundleURL, &myBundleRef );
CFRelease( refMainBundleURL );
if ( !ok ) {
err = fnfErr;
goto cantLoad;
}
// Now get an FSSpec for it
err = FSGetCatalogInfo( &myBundleRef, kFSCatInfoNone, NULL, NULL, &SLSpec, NULL );
} else
{
char gameDir2[1024];
err = FSMakeFSSpec( 0, 0L, symName, &SLSpec );
if ( err == fnfErr ) {
sprintf( gameDir2, ":%s:%s", kMacGameDir, kMacCGameLib );
c2pstrcpy( symName, gameDir2 );
err = FSMakeFSSpec( 0, 0L, symName, &SLSpec );
}
if ( err == fnfErr ) {
Sys_Error( "Can't find %s at location %s", kMacCGameLib, gameDir2 );
}
}
if ( err ) {
goto cantLoad;
}
err = GetDiskFragment( &SLSpec, 0, kCFragGoesToEOF, NULL, kPrivateCFragCopy, &cgame_connID, (Ptr*)NULL, symName );
// If no memory, try again in the system heap
if ( err == cfragNoClientMemErr ) {
THz savedZone = LMGetApplZone();
LMSetApplZone( LMGetSysZone() );
err = GetDiskFragment( &SLSpec, 0, kCFragGoesToEOF, NULL, kPrivateCFragCopy, &cgame_connID, (Ptr*)NULL, symName );
LMSetApplZone( savedZone );
}
if ( err ) {
goto cantLoad;
}
strcpy( (char*) symName, (char*)"\pGetCGameAPI" );
err = FindSymbol( cgame_connID, symName, (Ptr *)&GetCGameAPI, &symClass );
if ( err ) {
goto cantLoad;
}
return GetCGameAPI();
cantLoad:
if ( err == cfragNoClientMemErr ) {
Sys_Error( "Can't load %s. Give the game additional memory and try again.", kMacCGameLib );
} else {
Sys_Error( "Can't load %s, error %d", kMacCGameLib, err );
}
// Keep the compiler happy
return NULL;
}
void Sys_UnloadCGame( void ) {
OSErr err = noErr;
if ( cgame_connID ) {
err = CloseConnection( &cgame_connID );
cgame_connID = 0;
}
}
//===========================================================================
char *Sys_GetClipboardData( void ) {
ScrapRef currentScrap;
OSStatus status;
char *buffer;
Size byteCount;
GetCurrentScrap( ¤tScrap );
status = GetScrapFlavorSize( currentScrap, 'TEXT', &byteCount );
if ( ( status == noErr ) && ( byteCount < 1023 ) ) {
#if MAC_Q3_OLDSTUFF
buffer = (char *)Z_Malloc( byteCount + 1 );
#else
buffer = (char *)Z_Malloc( byteCount + 1, TAG_GENERAL, qfalse );
#endif
if ( !buffer ) {
return NULL;
}
// Get the 'TEXT' clipping from the clipboard
status = GetScrapFlavorData( currentScrap, 'TEXT', &byteCount, (void *)buffer );
}
return buffer;
}
char *Sys_GetWholeClipboard( void ) {
// LBO - not sure what this does differently from Sys_GetClipboardData
return Sys_GetClipboardData();
}
void Sys_SetClipboard( const char *contents ) {
ScrapRef currentScrap;
OSStatus status;
Size byteCount;
ClearCurrentScrap();
GetCurrentScrap( ¤tScrap );
byteCount = strlen( contents );
// Put the 'TEXT' clipping on the clipboard
status = PutScrapFlavor( currentScrap, 'TEXT', kScrapFlavorMaskNone, byteCount, (void *)contents );
}
enum {
CPUID_PPC_601 = 1,
CPUID_PPC_603,
CPUID_PPC_604,
CPUID_PPC_G3,
CPUID_PPC_G4,
CPUID_PPC_UNK,
CPUID_INTEL
};
int Sys_GetProcessorId( void ) {
OSErr err;
long response;
err = Gestalt( gestaltNativeCPUfamily, &response );
if ( err == noErr ) {
switch ( response )
{
case gestaltCPU601:
return CPUID_PPC_601;
case gestaltCPU603:
return CPUID_PPC_603;
case gestaltCPU604:
return CPUID_PPC_604;
case gestaltCPU750:
return CPUID_PPC_G3;
case gestaltCPUG4:
return CPUID_PPC_G4;
default:
err = Gestalt( gestaltSysArchitecture, &response );
if ( err == noErr ) {
if ( response == gestaltPowerPC ) {
return CPUID_PPC_UNK;
} else {
return CPUID_INTEL;
}
} else {
return CPUID_GENERIC;
}
}
} else {
return CPUID_GENERIC;
}
}
void *Sys_InitializeCriticalSection() {
return (void *)-1;
}
void Sys_EnterCriticalSection( void *ptr ) {
}
void Sys_LeaveCriticalSection( void *ptr ) {
}
int Sys_GetHighQualityCPU() {
// FIXME TTimo see win_shared.c
return 0;
}
void Sys_StartProcess( char *exeName, qboolean doexit ) {
OSErr launchErr;
FSSpec spec;
Str255 macName;
LaunchParamBlockRec myLaunchParams;
ProcessSerialNumber launchedProcessSN;
launchErr = FSMakeFSSpec( 0, 0L, "\pMedal of Honor", &spec );
if ( launchErr != noErr ) {
Com_Error( ERR_DROP, "Unable to find Medal of Honor" );
}
myLaunchParams.launchBlockID = extendedBlock;
myLaunchParams.launchEPBLength = extendedBlockLen;
myLaunchParams.launchFileFlags = 0;
if ( !doexit ) {
myLaunchParams.launchControlFlags = launchContinue + launchNoFileFlags;
} else {
myLaunchParams.launchControlFlags = launchNoFileFlags;
}
myLaunchParams.launchAppSpec = &spec;
myLaunchParams.launchAppParameters = nil;
launchErr = LaunchApplication( &myLaunchParams );
if ( launchErr != noErr ) {
Com_Error( ERR_DROP, "Unable to launch Medal of Honor" );
}
// this is only used for WolfSP / WolfMP spawning for now
if ( doexit ) {
exit( 0 );
}
}
/*
==================
Sys_OpenURL
==================
*/
void Sys_OpenURL( const char *url, qboolean doexit ) {
OSStatus err;
ICInstance inst;
long startSel;
long endSel;
#ifdef WOLF_SP_DEMO
strcpy( url, "www.aspyr.com/mini-sites/rtcw" ); //DAJ HACK
#endif
err = ICStart( &inst, 'WlfS' );
if ( err == noErr ) {
#if !TARGET_API_MAC_CARBON
err = ICFindConfigFile( inst, 0, NULL );
if ( err == noErr )
#endif
{
startSel = 0;
endSel = strlen( url );
err = ICLaunchURL( inst, "\p", (char*)url, strlen( url ), &startSel, &endSel );
}
ICStop( inst );
}
if ( doexit ) {
ExitToShell();
}
}
void Sys_SnapVector( float *v ) {
v[0] = rint( v[0] );
v[1] = rint( v[1] );
v[2] = rint( v[2] );
}
/*
==================
Sys_CloseMutex
==================
*/
void Sys_CloseMutex( void ) {
}
//===================================================================
/*
=================
Sys_In_Restart_f
Restart the input subsystem
=================
*/
void Sys_In_Restart_f( void ) {
Sys_ShutdownInput();
Sys_InitInput();
}
/*
================
Sys_Init
The cvar and file system has been setup, so configurations are loaded
================
*/
void Sys_Init( void ) {
long response;
OSErr err;
int cpuid;
int numCPU;
#if MAC_Q3_MP
Sys_InitNetworking();
#endif
Sys_InitInput();
Cmd_AddCommand( "in_restart", Sys_In_Restart_f );
#if MAC_Q3_MP
// Cmd_AddCommand ("net_restart", Sys_Net_Restart_f);
#endif
numCPU = 1;
if ( MPLibraryIsLoaded() ) {
numCPU = MPProcessors();
}
//
// figure out our CPU
//
Cvar_Get( "sys_cpustring", "detect", 0 );
if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "detect" ) ) {
Com_Printf( "...detecting CPU, found " );
cpuid = Sys_GetProcessorId();
switch ( cpuid )
{
case CPUID_PPC_601:
Cvar_Set( "sys_cpustring", "PowerPC 601" );
break;
case CPUID_PPC_603:
Cvar_Set( "sys_cpustring", "PowerPC 603" );
break;
case CPUID_PPC_604:
Cvar_Set( "sys_cpustring", "PowerPC 604" );
break;
case CPUID_PPC_G3:
Cvar_Set( "sys_cpustring", "PowerPC G3" );
break;
case CPUID_PPC_G4:
if ( numCPU == 2 ) {
Cvar_Set( "sys_cpustring", "PowerPC G4 x 2" );
} else if ( numCPU == 1 ) {
Cvar_Set( "sys_cpustring", "PowerPC G4" );
} else {
Cvar_Set( "sys_cpustring", "PowerPC G4 (multiple)" );
}
break;
case CPUID_PPC_UNK:
if ( numCPU == 2 ) {
Cvar_Set( "sys_cpustring", "PowerPC (unknown) x 2" );
} else if ( numCPU == 1 ) {
Cvar_Set( "sys_cpustring", "PowerPC (unknown)" );
} else {
Cvar_Set( "sys_cpustring", "PowerPC (unknown - multiple)" );
}
break;
case CPUID_INTEL:
Cvar_Set( "sys_cpustring", "Intel" );
break;
}
} else
{
Com_Printf( "...forcing CPU type to " );
if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "generic" ) ) {
cpuid = CPUID_GENERIC;
} else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "x87" ) ) {
cpuid = CPUID_INTEL_PENTIUM;
} else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "mmx" ) ) {
cpuid = CPUID_INTEL_MMX;
} else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "3dnow" ) ) {
cpuid = CPUID_AMD_3DNOW;
} else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "PentiumIII" ) ) {
cpuid = CPUID_INTEL_KATMAI;
}
#if !MAC_Q3_OLDSTUFF
else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "PentiumIV" ) ) {
cpuid = CPUID_INTEL_WILLIAMETTE;
}
#endif
else if ( !Q_stricmp( Cvar_VariableString( "sys_cpustring" ), "axp" ) ) {
cpuid = CPUID_AXP;
} else
{
Com_Printf( "WARNING: unknown sys_cpustring '%s'\n", Cvar_VariableString( "sys_cpustring" ) );
cpuid = CPUID_GENERIC;
}
}
Cvar_SetValue( "sys_cpuid", cpuid );
Com_Printf( "%s\n", Cvar_VariableString( "sys_cpustring" ) );
err = Gestalt( gestaltProcClkSpeed, &response );
Cvar_SetValue( "sys_cpuspeed", response );
err = Gestalt( gestaltPhysicalRAMSize, &response );
Cvar_SetValue( "sys_memory", response );
}
/*
=================
Sys_Shutdown
=================
*/
void Sys_Shutdown( void ) {
Sys_EndProfiling();
Sys_ShutdownInput();
#if MAC_Q3_MP
Sys_ShutdownNetworking();
#endif
#if TARGET_API_MAC_CARBON
if ( gUseCarbonEvents ) {
QuitApplicationEventLoop();
}
#endif
#if MAC_Q3_QUICKTIME
ExitMovies();
#endif
SavePrefs( &macPrefs );
}
/*
==================
Sys_PumpMessageLoop
==================
*/
void Sys_PumpMessageLoop( void ) {
}
/*
=================
Sys_BeginProfiling
=================
*/
static qboolean sys_profiling;
void Sys_BeginProfiling( void ) {
#if __profile__
if ( !sys_profile->integer ) {
return;
}
ProfilerInit( collectDetailed, bestTimeBase, 16384, 64 );
sys_profiling = qtrue;
#endif
}
/*
=================
Sys_EndProfiling
=================
*/
void Sys_EndProfiling( void ) {
#if __profile__
unsigned char pstring[1024];
if ( !sys_profiling ) {
return;
}
sys_profiling = qfalse;
sprintf( (char *)pstring + 1, "%s:profile.txt", Cvar_VariableString( "fs_basepath" ) );
pstring[0] = strlen( (char *)pstring + 1 );
ProfilerDump( pstring );
//DAJ ProfilerTerm();
#endif
}
qboolean Sys_LowPhysicalMemory( void ) {
return qfalse;
}
//================================================================================
/*
================
Sys_Milliseconds
================
*/
#if MAC_JKJA && MAC_Q3_MP
inline int Old_Sys_Milliseconds( void ) {
AbsoluteTime t;
Nanoseconds nano;
double doub;
#define kTwoPower32 ( 4294967296.0 ) /* 2^32 */
t = UpTime();
nano = AbsoluteToNanoseconds( t );
doub = ( ( (double) nano.hi ) * kTwoPower32 ) + nano.lo;
return doub * 0.000001;
}
int Sys_Milliseconds( bool baseTime ) {
static int sys_timeBase = Old_Sys_Milliseconds();
int sys_curtime;
sys_curtime = Old_Sys_Milliseconds();
if ( !baseTime ) {
sys_curtime -= sys_timeBase;
}
return sys_curtime;
}
#else
int Sys_Milliseconds( void ) {
AbsoluteTime t;
Nanoseconds nano;
double doub;
#define kTwoPower32 ( 4294967296.0 ) /* 2^32 */
t = UpTime();
nano = AbsoluteToNanoseconds( t );
doub = ( ( (double) nano.hi ) * kTwoPower32 ) + nano.lo;
return doub * 0.000001;
}
#endif
/*
================
Sys_Error
================
*/
void Sys_Error( const char *format, ... ) {
va_list argptr;
char errString[1024];
AlertStdAlertParamRec param;
short itemHit;
Str255 briefMsg;
int length;
Sys_Shutdown();
// If we're using DSp on OSX, we need to pause the context so we can see dialogs
if ( r_fullscreen ) {
GLimp_pause();
if ( ( r_fullscreen->integer ) && ( gSystemVersion >= 0x01000 ) ) {
if ( gDrawSprocket->GetState() == kDSpContextState_Active ) {
gDrawSprocket->SetState( kDSpContextState_Paused );
}
}
}
// aglSetFullscreen on 10 will not pause correctly, so the dialog
// will not be seen. We just quit the app instead.
//if (gSystemVersion >= 0x1000) goto bail;
va_start( argptr, format );
length = vsprintf( errString + 1, format, argptr );
va_end( argptr );
errString[0] = length;
// set the dialog box strings
param.movable = 0;
param.filterProc = NULL;
param.defaultText = "\pOK";
param.cancelText = NULL;
param.otherText = NULL;
param.helpButton = false;
param.defaultButton = kAlertStdAlertOKButton;
param.cancelButton = 0;
param.position = kWindowDefaultPosition;
GetIndString( briefMsg, rErrStrings, kErrStringError );
StandardAlert( kAlertStopAlert, briefMsg, (StringPtr) errString, ¶m, &itemHit );
bail:
// LBO - we do an ExitToShell here because exit can call some destructors that
// are in rough shape if the app hasn't started up properly.
ExitToShell();
}
/*
================
Sys_ErrorString
================
*/
void Sys_ErrorString( int inStringNum ) {
va_list argptr;
Str255 errString;
AlertStdAlertParamRec param;
short itemHit;
Str255 briefMsg;
int length;
Sys_Shutdown();
// If we're using DSp on OSX, we need to pause the context so we can see dialogs
if ( r_fullscreen ) {
GLimp_pause();
if ( ( r_fullscreen->integer ) && ( gSystemVersion >= 0x01000 ) ) {
if ( gDrawSprocket->GetState() == kDSpContextState_Active ) {
gDrawSprocket->SetState( kDSpContextState_Paused );
}
}
}
// aglSetFullscreen on 10 will not pause correctly, so the dialog
// will not be seen. We just quit the app instead.
//if (gSystemVersion >= 0x1000) goto bail;
GetIndString( errString, rErrStrings, inStringNum );
// set the dialog box strings
param.movable = 0;
param.filterProc = NULL;
param.defaultText = "\pOK";
param.cancelText = NULL;
param.otherText = NULL;
param.helpButton = false;
param.defaultButton = kAlertStdAlertOKButton;
param.cancelButton = 0;
param.position = kWindowDefaultPosition;
GetIndString( briefMsg, rErrStrings, kErrStringError );
StandardAlert( kAlertStopAlert, briefMsg, (StringPtr) errString, ¶m, &itemHit );
bail:
// LBO - we do an ExitToShell here because exit can call some destructors that
// are in rough shape if the app hasn't started up properly.
ExitToShell();
// exit(0);
}
void Sys_ErrorLite( int inStringNum ) {
va_list argptr;
Str255 errString;
AlertStdAlertParamRec param;
short itemHit;
Str255 briefMsg;
int length;
GetIndString( errString, rErrStrings, inStringNum );
// set the dialog box strings
param.movable = 0;
param.filterProc = NULL;
param.defaultText = "\pOK";
param.cancelText = NULL;
param.otherText = NULL;
param.helpButton = false;
param.defaultButton = kAlertStdAlertOKButton;
param.cancelButton = 0;
param.position = kWindowDefaultPosition;
GetIndString( briefMsg, rErrStrings, kErrStringError );
StandardAlert( kAlertStopAlert, briefMsg, (StringPtr) errString, ¶m, &itemHit );
bail:
// LBO - we do an ExitToShell here because exit can call some destructors that
// are in rough shape if the app hasn't started up properly.
ExitToShell();
// exit(0);
}
/*
================
Sys_ErrorString
================
*/
void Sys_ErrorMisc( OSStatus inErr ) {
char errString[1024];
AlertStdAlertParamRec param;
short itemHit;
Str255 briefMsg;
int length;
Sys_Shutdown();
// If we're using DSp on OSX, we need to pause the context so we can see dialogs
if ( r_fullscreen ) {
GLimp_pause();
if ( ( r_fullscreen->integer ) && ( gSystemVersion >= 0x01000 ) ) {
if ( gDrawSprocket->GetState() == kDSpContextState_Active ) {
gDrawSprocket->SetState( kDSpContextState_Paused );
}
}
}
// aglSetFullscreen on 10 will not pause correctly, so the dialog
// will not be seen. We just quit the app instead.
//if (gSystemVersion >= 0x1000) goto bail;
GetIndString( (StringPtr)errString, rErrStrings, kErrStringMisc );
// set the dialog box strings
param.movable = 0;
param.filterProc = NULL;
param.defaultText = "\pOK";
param.cancelText = NULL;
param.otherText = NULL;
param.helpButton = false;
param.defaultButton = kAlertStdAlertOKButton;
param.cancelButton = 0;
param.position = kWindowDefaultPosition;
GetIndString( briefMsg, rErrStrings, kErrStringError );
StandardAlert( kAlertStopAlert, briefMsg, (StringPtr)errString, ¶m, &itemHit );
// LBO - we do an ExitToShell here because exit can call some destructors that
// are in rough shape if the app hasn't started up properly.
ExitToShell();
}
/*
================
Sys_Warning
================
*/
void Sys_Warning( const char *format, ... ) {
va_list argptr;
char errString[1024];
AlertStdAlertParamRec param;
short itemHit;
Str255 briefMsg;
int length;
GLimp_pause();
// If we're using DSp on OSX, we need to pause the context so we can see dialogs
if ( ( r_fullscreen->integer ) && ( gSystemVersion >= 0x01000 ) ) {
if ( gDrawSprocket->GetState() == kDSpContextState_Active ) {
gDrawSprocket->SetState( kDSpContextState_Paused );
}
}
va_start( argptr, format );
length = vsprintf( errString + 1, format, argptr );
va_end( argptr );
errString[0] = length;
// set the dialog box strings
param.movable = 0;
param.filterProc = NULL;
param.defaultText = "\pOK";
param.cancelText = NULL;
param.otherText = NULL;
param.helpButton = false;
param.defaultButton = kAlertStdAlertOKButton;
param.cancelButton = 0;
param.position = kWindowDefaultPosition;
GetIndString( briefMsg, rErrStrings, kErrStringWarning );
StandardAlert( kAlertStopAlert, briefMsg, (StringPtr) errString, ¶m, &itemHit );
// If we're using DSp on OSX, we need to pause the context so we can see dialogs
if ( ( r_fullscreen->integer ) && ( gSystemVersion >= 0x01000 ) ) {
if ( gDrawSprocket->GetState() == kDSpContextState_Paused ) {
gDrawSprocket->SetState( kDSpContextState_Active );
}
}
GLimp_resume();
GLimp_SetGameGamma();
}
/*
================
Sys_Quit
================
*/
void Sys_Quit( void ) {
Sys_Shutdown();
GLimp_pause();
ExitToShell();
}
//===================================================================
void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
}
void Sys_EndStreamedFile( fileHandle_t f ) {
}
int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
return FS_Read( buffer, size * count, f );
}
void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
FS_Seek( f, offset, origin );
}
//=================================================================================
/*
========================================================================
EVENT LOOP
========================================================================
*/
#define MAX_QUED_EVENTS 256
#define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 )
sysEvent_t eventQue[MAX_QUED_EVENTS];
int eventHead, eventTail;
byte sys_packetReceived[MAX_MSGLEN];
/*
================
Sys_QueEvent
A time of 0 will get the current time
Ptr should either be null, or point to a block of data that can
be freed by the game later.
================
*/
void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
sysEvent_t *ev;
ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
Com_Printf( "Sys_QueEvent: overflow\n" );
// we are discarding an event, but don't leak memory
if ( ev->evPtr ) {
Z_Free( ev->evPtr );
}
eventTail++;
}
eventHead++;
if ( time == 0 ) {
time = Sys_Milliseconds();
}
ev->evTime = time;
ev->evType = type;
ev->evValue = value;
ev->evValue2 = value2;
ev->evPtrLength = ptrLength;
ev->evPtr = ptr;
}
/*
=================
Sys_PumpEvents
=================
*/
void Sys_PumpEvents( void ) {
char *s;
msg_t netmsg;
netadr_t adr;
//Com_Printf ("Sys_PumpEvents\n");
// pump the message loop
Sys_SendKeyEvents();
// check for console commands
s = Sys_ConsoleInput();
if ( s ) {
char *b;
int len;
len = strlen( s ) + 1;
b = (char *)Z_Malloc( len );
if ( !b ) {
Com_Error( ERR_FATAL, "malloc failed in Sys_PumpEvents" );
}
strcpy( b, s );
Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
}
// check for other input devices
Sys_Input();
#if MAC_Q3_MP
// check for network packets
MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) );
if ( Sys_GetPacket( &adr, &netmsg ) ) {
netadr_t *buf;
int len;
// copy out to a seperate buffer for qeueing
len = sizeof( netadr_t ) + netmsg.cursize;
buf = (netadr_t *)Z_Malloc( len );
if ( !buf ) {
Com_Error( ERR_FATAL, "malloc failed in Sys_PumpEvents" );
}
*buf = adr;
memcpy( buf + 1, netmsg.data, netmsg.cursize );
Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf );
}
#endif
}
/*
================
Sys_GetEvent
================
*/
sysEvent_t Sys_GetEvent( void ) {
sysEvent_t ev;
//Com_Printf ("Sys_GetEvent\n");
#if MAC_TIME_LIMIT
CheckTimeLimit();
#endif
if ( eventHead == eventTail ) {
Sys_PumpEvents();
}
// return if we have data
if ( eventHead > eventTail ) {
eventTail++;
return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
}
// create an empty event to return
memset( &ev, 0, sizeof( ev ) );
ev.evTime = Sys_Milliseconds();
// track the mac event "when" to milliseconds rate
sys_ticBase = sys_lastEventTic;
sys_msecBase = ev.evTime;
return ev;
}
#if MAC_WOLF_ET
qboolean Sys_IsNumLockDown( void ) {
return qfalse;
}
float Sys_GetCPUSpeed( void ) {
float cpuSpeed;
OSErr err;
long response;
err = Gestalt( gestaltProcClkSpeed, &response );
if ( !err ) {
cpuSpeed = (float)response / 1000.0f;
} else {
cpuSpeed = 500.0f;
}
return cpuSpeed;
}
#endif
Q3DEF_END
//------------------------------------------------------------------------------------
// ₯ IsPressed
//------------------------------------------------------------------------------------
short IsPressed( unsigned short k ) {
// FIXME: don't think that ever worked
#if 0
unsigned char km[16];
KeyMap *keymap = (KeyMap*) &km;
#if TARGET_API_MAC_CARBON
GetKeys( (SInt32 *) *keymap );
#else
GetKeys( (UInt32 *) *keymap );
#endif
return ( ( km[k >> 3] >> ( k & 7 ) ) & 1 );
#else
return 0;
#endif
}
#pragma mark -
extern "C"
{
void DisableAutodial() {
//NOT IMPLMENTED
}
void RestoreAutodial() {
//NOT IMPLMENTED
}
void RecoverLostAutodialData() {
//NOT IMPLMENTED
}
void SetNormalThreadPriority() {
//NOT IMPLMENTED
}
void SetBelowNormalThreadPriority() {
//NOT IMPLMENTED
}
qboolean LoadRegistryInfo( qboolean user, const char *pszName, void *pvBuf, long *plSize ) {
//NOT IMPLMENTED
return qfalse;
}
qboolean SaveRegistryInfo( qboolean user, const char *pszName, void *pvBuf, long lSize, qboolean bString ) {
//NOT IMPLMENTED
return qfalse;
}
}
#pragma mark -
#if TARGET_API_MAC_CARBON
//===============================================================================
// SetDefaultDirectory
//
// Under native OS X, the default directory for apps is some bizarro Unix
// location. Here we redefine it to the classic MacOS location, which is the
// same folder that the app is running from.
//===============================================================================
void SetDefaultDirectory( void ) {
ProcessSerialNumber serial;
ProcessInfoRec info;
WDPBRec wpb;
OSErr err;
serial.highLongOfPSN = 0;
serial.lowLongOfPSN = kCurrentProcess;
info.processInfoLength = sizeof( ProcessInfoRec );
info.processName = NULL;
info.processAppSpec = &app_spec;
err = GetProcessInformation( &serial, &info );
wpb.ioVRefNum = app_spec.vRefNum;
wpb.ioWDDirID = app_spec.parID;
wpb.ioNamePtr = NULL;
err = PBHSetVolSync( &wpb );
}
#endif
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
// GetButtonValue
//
// A helper routine to read the state of a dialog control without all the crap
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
short GetButtonValue( DialogRef theDialog, short item ) {
short theType;
Handle theHndl;
Rect theRect;
GetDialogItem( theDialog, item, &theType, &theHndl, &theRect );
return ( GetControlValue( (ControlRef) theHndl ) );
}
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
// SetButtonValue
//
// A helper routine to set the state of a dialog control without all the crap
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
void SetButtonValue( DialogRef theDialog, short item, short value ) {
short theType;
Handle theHndl;
Rect theRect;
GetDialogItem( theDialog, item, &theType, &theHndl, &theRect );
SetControlValue( (ControlRef) theHndl, value );
}
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
// DisableDialogControl
//
// A helper routine to disable a dialog button without all the crap
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
void DisableDialogControl( DialogRef theDialog, short item ) {
short theType;
Handle theHndl;
Rect theRect;
GetDialogItem( theDialog, item, &theType, &theHndl, &theRect );
HiliteControl( (ControlRef) theHndl, kControlInactivePart );
}
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
// EnableDialogControl
//
// A helper routine to disable a dialog button without all the crap
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
void EnableDialogControl( DialogRef theDialog, short item ) {
short theType;
Handle theHndl;
Rect theRect;
GetDialogItem( theDialog, item, &theType, &theHndl, &theRect );
HiliteControl( (ControlRef) theHndl, kControlNoPart );
}
//===============================================================================
// GetDialogItemType
//
// Returns the type of a dialog item.
//===============================================================================
DialogItemType GetDialogItemType( DialogRef inDialog, DialogItemIndex inItem ) {
DialogItemType itemType;
Handle itemHandle;
Rect itemBounds;
GetDialogItem( inDialog, inItem, &itemType, &itemHandle, &itemBounds );
return itemType;
}
//===============================================================================
// GetDialogControlValue
//
// Returns the value of the control attached to a dialog item
//===============================================================================
SInt16 GetDialogControlValue( DialogRef inDialog, DialogItemIndex inItem ) {
ControlRef controlRef;
// fetch the control handle and return the value
if ( GetDialogItemAsControl( inDialog, inItem, &controlRef ) == noErr ) {
return GetControlValue( controlRef );
} else {
return 0;
}
}
//===============================================================================
// SetDialogControlValue
//
// Sets the value of the control attached to a dialog item
//===============================================================================
void SetDialogControlValue( DialogRef inDialog, DialogItemIndex inItem, SInt16 inValue ) {
ControlRef controlRef;
// fetch the control handle and set the value
if ( GetDialogItemAsControl( inDialog, inItem, &controlRef ) == noErr ) {
if ( GetControlValue( controlRef ) != inValue ) {
SetControlValue( controlRef, inValue );
}
}
}
//===============================================================================
// GetDialogItemTextAppearance
//
// Returns the text of a static/edit text item assuming the Appearance Manager.
//===============================================================================
void GetDialogItemTextAppearance( DialogRef inDialog, DialogItemIndex inItem, StringPtr outText ) {
ControlRef controlRef;
OSStatus status = noErr;
// fetch the control handle and set the value
status = GetDialogItemAsControl( inDialog, inItem, &controlRef );
if ( status == noErr ) {
GetDialogItemText( (Handle)controlRef, outText );
}
}
#pragma mark -
#pragma mark ₯ Apple Event Handlers / Only Mortal Events
static char *GetCommandLineParameters( const AppleEvent *appleEvt, AEKeyword keyWord ) {
OSErr theError;
DescType typeCode;
Size dataSize;
Size actualSize;
char *theCommandLine = NULL;
if ( ( theError = AESizeOfParam( appleEvt, keyWord, &typeCode, &dataSize ) ) == noErr && dataSize > 0 ) {
if ( ( theCommandLine = (char*)malloc( dataSize + 1 ) ) == NULL ) {
return NULL;
}
if ( ( theError = AEGetParamPtr( appleEvt, keyWord, typeChar, &typeCode, theCommandLine, dataSize, &actualSize ) ) != noErr || dataSize != actualSize ) {
free( (void*)theCommandLine );
theCommandLine = NULL;
return NULL;
}
theCommandLine[ (int)actualSize ] = '\0';
}
return theCommandLine;
}
static pascal OSErr RunAppleEventHandler( const AppleEvent *appleEvt, AppleEvent *reply, long refcon ) {
#pragma unused( reply, refcon )
// Parameters pass in with an 'orun' event. 'CLin' seems to be the defacto standard, as seen in Unreal Tournament, so we use it too.
// The normal behavior of 'orun' is to open an empty document, but that doesn't make much sense here!
if ( ( sCommandLine = GetCommandLineParameters( appleEvt, 'CLin' ) ) != NULL ) {
// char theDebugMessage[ 1024 ];
}
return noErr;
}
static pascal OSErr QuitAppleEventHandler( const AppleEvent *appleEvt, AppleEvent* reply, long refcon ) {
#pragma unused (appleEvt, reply, refcon)
if ( gUseCarbonEvents ) {
QuitApplicationEventLoop();
} else {
Com_Quit_f();
}
return noErr;
//#pragma noreturn (QuitAppleEventHandler)
}
#if MAC_Q3_MP
static pascal OSErr OMExecuteAppleEventHandler( const AppleEvent *appleEvt, AppleEvent *reply, long refcon ) {
#pragma unused( reply, refcon )
/* Ensure we dump any previous command line string */
if ( sCommandLine != NULL ) {
free( (void*)sCommandLine );
}
sCommandLine = NULL;
#if !DEDICATED // ₯₯₯
if ( ( sCommandLine = GetCommandLineParameters( appleEvt, 'CLin' ) ) != NULL ) {
Sys_SendStringToConsole( sCommandLine );
Com_Printf( "]%s (via AppleEvent)\n", sCommandLine );
}
#endif
return noErr;
}
#endif
static OSErr InitAppleEvents( void ) {
OSErr err = noErr;
err = AEInstallEventHandler( kCoreEventClass, kAEOpenApplication, NewAEEventHandlerUPP( RunAppleEventHandler ), 0, false );
if ( err != noErr ) {
goto bail;
}
err = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP( QuitAppleEventHandler ), 0, false );
if ( err != noErr ) {
goto bail;
}
#if MAC_Q3_MP
err = AEInstallEventHandler( kOMEventClass, kOMExecuteCommands, NewAEEventHandlerUPP( OMExecuteAppleEventHandler ), 0, false );
if ( err != noErr ) {
goto bail;
}
#endif
bail:
return err;
}
#pragma mark -
/*
=============
InitMacStuff
=============
*/
void InitMacStuff( void ) {
Handle menuBar;
char dir[MAX_OSPATH];
OSErr err;
long qtVer;
long response;
Boolean bail = false;
OSStatus error;
SecuritySessionId mySession;
SessionAttributeBits sessionInfo;
error = SessionGetInfo( callerSecuritySession, &mySession, &sessionInfo );
if ( sessionInfo & sessionHasGraphicAccess ) {
macSessionGraphics = true;
}
MoreMasterPointers( 64UL * 10 );
if ( macSessionGraphics ) {
InitCursor();
}
// Install some Apple Events handlers
err = InitAppleEvents();
if ( err != noErr ) {
ExitToShell();
}
// We do this early because it relies on grabbing the first apple event from the Finder.
sCommandLine_temp = ReadCommandLineParms();
// Preflight some stuff
#if MAC_Q3_QUICKTIME
// Check for QuickTime 4.0 so we can play .mp3 files
err = Gestalt( gestaltQuickTime, (long *) &qtVer );
if ( err ) {
qtVer = 0L;
}
// If QT4 isn't present, tell user he won't have mp3 audio
if ( qtVer < 0x04000000 ) {
Sys_ErrorLite( kErrStringQuickTime );
}
#endif
// Are we on 10?
err = Gestalt( gestaltSystemVersion, &gSystemVersion );
// We bail on < MacOS 10.2.8
if ( gSystemVersion < 0x1028 ) {
Sys_ErrorLite( kErrStringWrongOSX );
}
// No DrawSprocket? Then freak out! We could run in a window, but...eh.
if ( (Ptr) DSpStartup == (Ptr) kUnresolvedCFragSymbolAddress ) {
Sys_ErrorLite( kErrStringDrawSprocket );
}
if ( !MPLibraryIsLoaded() ) {
Sys_ErrorLite( kErrStringMisc );
}
#if MAC_TIME_LIMIT
CheckTimeLimit();
#endif
#if TARGET_API_MAC_CARBON
// Tell OSX to set the local directory to the one the app lives in
SetDefaultDirectory();
#endif
// see if we should modify quit in accordance with the Aqua HI guidelines
err = Gestalt( gestaltMenuMgrAttr, &response );
if ( ( err == noErr ) && ( response & gestaltMenuMgrAquaLayoutMask ) ) {
menuBar = GetNewMBar( rMenuBarX );
} else
{
menuBar = GetNewMBar( rMenuBar );
}
// init menu
if ( !menuBar ) {
Com_Error( ERR_FATAL, "MenuBar not found." );
}
SetMenuBar( menuBar );
DisposeHandle( menuBar );
DrawMenuBar();
#if MAC_Q3_QUICKTIME
EnterMovies();
#endif
SetEventMask( -1 );
}
//==================================================================================
// OptionDialog
//
// Configures Mac-specific options at startup. Returns true if we're to quit
Boolean OptionDialog( void ) {
short itemHit;
DialogRef theDialog;
GWorldPtr savedPort;
GDHandle savedDevice;
Boolean done;
Cursor arrow;
DisplayIDType theMonitor = 0;
OSStatus status = noErr;
// Dialog item constants
enum {
rOptionsDialog = 130,
dMenuFrequency = 3,
dButtonQuit,
dButtonMonitors,
dButtonConfigJoysticks
};
status = DSpStartup();
if ( status != noErr ) {
Sys_ErrorMisc( status );
}
// attempt to fetch the alert dialog
theDialog = GetNewDialog( rOptionsDialog, NULL, ( WindowRef ) - 1 );
if ( theDialog == NULL ) {
return false;
}
// point to the port
GetGWorld( &savedPort, &savedDevice );
SetPortDialogPort( theDialog );
// set the default items
SetDialogDefaultItem( theDialog, ok );
SetDialogCancelItem( theDialog, cancel );
// Set up the controls
SetButtonValue( theDialog, dMenuFrequency, macPrefs.monitorFrequency );
theMonitor = macPrefs.displayID;
// Disable the "choose monitor" button if we've only got one to pick from
{
GDHandle currentMonitor;
UInt32 totalMonitors = 0;
// Walk the device list
for ( currentMonitor = GetDeviceList(); currentMonitor != NULL; currentMonitor = GetNextDevice( currentMonitor ) )
{
totalMonitors++;
}
// Only 1 monitor found
if ( totalMonitors == 1 ) {
DisableDialogControl( theDialog, dButtonMonitors );
theMonitor = 0;
}
}
// Disable the joystick config on OS 9
if ( gSystemVersion < 0x1000 ) {
DisableDialogControl( theDialog, dButtonConfigJoysticks );
}
// display the alert and make sure the cursor is an arrow
ShowWindow( GetDialogWindow( theDialog ) );
SetCursor( GetQDGlobalsArrow( &arrow ) );
// loop on ModalDialog until we're done
done = false;
while ( !done )
{
ModalDialog( NULL, &itemHit );
switch ( itemHit )
{
case ok:
case cancel:
case dButtonQuit:
done = true;
break;
case dButtonConfigJoysticks:
//ConfigJoystick_HID();
break;
case dButtonMonitors:
{
status = PickMonitorDialog( &theMonitor );
break;
}
}
}
// Read back the controls
if ( itemHit == ok ) {
macPrefs.monitorFrequency = GetButtonValue( theDialog, dMenuFrequency );
macPrefs.displayID = theMonitor;
}
bail:
// tear it down
DisposeDialog( theDialog );
SetGWorld( savedPort, savedDevice );
// If the user wants to quit, return true
if ( itemHit == dButtonQuit ) {
return true;
} else {
return false;
}
}
/*
=============
ReadCommandLineParms
Read startup options from a 'CLin' apple event, a text file or dialog box
=============
*/
char *ReadCommandLineParms( void ) {
FILE *f;
int len;
char *buf;
EventRecord event;
UInt32 timeoutTicks;
sGameRangerHosting = false;
memset( &event, 0, sizeof( EventRecord ) );
// wait for the first AppleEvent to come through, but time out after 1/2 second
timeoutTicks = TickCount() + 30;
while ( !WaitNextEvent( highLevelEventMask, &event, 0x7fffffff, NULL ) && TickCount() < timeoutTicks ) {};
// Attempt to handle the first AppleEvent (usually oapp or odoc)
AEProcessAppleEvent( &event );
// If the 'oapp' event set up the command line, return it
if ( sCommandLine != NULL ) {
return sCommandLine;
}
/*
#if MAC_Q3_MP
// Finally, use GameRanger to fill out the command line if nothing else
if (GRCheckFileForCmd())
{
GRGetWaitingCmd();
if (GRHasProperty( 'Exec' ))
{
char *grName = GRGetPropertyStr( 'Exec' );
// otherwise check for a parms file
f = fopen( grName, "r" );
if ( !f )
{
return "";
}
fseek( f, 0, SEEK_END );
len = ftell( f );
fseek( f, 0, SEEK_SET );
buf = (char *) malloc( len + 1 );
if ( !buf )
{
Sys_Error ("Could not launch via GameRanger, %s length %d", grName, len);
}
buf[len] = 0;
fread( buf, len, 1, f );
fclose( f );
sGameRangerHosting = true;
return buf;
}
}
#endif // MAC_Q3_MP
*/
return NULL;
}
/*
=============
main
=============
*/
int main( int argc, const char *argv[] ) {
char *commandLine;
char realCommandLine[1024];
int i;
Boolean bail = false;
InitMacStuff();
LoadPrefs( &macPrefs );
// If option or command is held down, throw up a config dialog
if ( IsPressed( kOptionKey ) || IsPressed( kCommandKey ) ) {
bail = OptionDialog();
}
if ( bail ) {
ExitToShell();
}
switch ( macPrefs.monitorFrequency )
{
case kMenuRefreshAuto: gRefreshRate = 0; break;
case kMenuRefresh60: gRefreshRate = kFrequency60; break;
case kMenuRefresh67: gRefreshRate = kFrequency67; break;
case kMenuRefresh70: gRefreshRate = kFrequency70; break;
case kMenuRefresh75: gRefreshRate = kFrequency75; break;
case kMenuRefresh80: gRefreshRate = kFrequency80; break;
case kMenuRefresh85: gRefreshRate = kFrequency85; break;
case kMenuRefresh90: gRefreshRate = kFrequency90; break;
case kMenuRefresh99: gRefreshRate = kFrequency99; break;
case kMenuRefresh100: gRefreshRate = kFrequency100; break;
case kMenuRefresh120: gRefreshRate = kFrequency120; break;
case kMenuRefresh124: gRefreshRate = kFrequency124; break;
default: gRefreshRate = 0; break;
}
// Write the changes back out now, in case we bail before the app quits properly
SavePrefs( &macPrefs );
// Default to 1 for carbon events if we're on 10
// Otherwise, default to old-school events on 8/9
if ( gSystemVersion >= 0x1000 ) {
gUseCarbonEvents = true;
} else {
gUseCarbonEvents = false;
}
if ( gUseCarbonEvents ) {
InstallStandardEventHandler( GetApplicationEventTarget() );
}
Sys_CreateConsole();
// get the initial time base
Sys_Milliseconds();
if ( sCommandLine_temp ) {
strcpy( realCommandLine, sCommandLine_temp );
} else {
realCommandLine[ 0 ] = '\0';
for ( i = 1; i < argc; i++ ) {
if ( i > 1 ) {
strcat( realCommandLine, " " );
}
strcat( realCommandLine, argv[ i ] );
}
}
Com_Init( realCommandLine );
sys_profile = Cvar_Get( "sys_profile", "0", 0 );
sys_profile->modified = qfalse;
#ifndef DEDICATED
// LBO - 1.11a patch. Use carbon events when running as a dedicated server. This prevents all the
// hitching warnings when resizing the windows, etc.
if ( com_dedicated->integer && gSystemVersion >= 0x1000 ) {
gUseCarbonEvents = true;
}
#endif
#if 0 && MAC_DEBUG
Com_Printf( "SYS_DLLNAME_QAGAME + %d: '%s'\n", SYS_DLLNAME_QAGAME_SHIFT, FS_ShiftStr( "qagame_mac", SYS_DLLNAME_QAGAME_SHIFT ) );
Com_Printf( "SYS_DLLNAME_CGAME + %d: '%s'\n", SYS_DLLNAME_CGAME_SHIFT, FS_ShiftStr( "cgame_mac", SYS_DLLNAME_CGAME_SHIFT ) );
Com_Printf( "SYS_DLLNAME_UI + %d: '%s'\n", SYS_DLLNAME_UI_SHIFT, FS_ShiftStr( "ui_mac", SYS_DLLNAME_UI_SHIFT ) );
#endif
if ( gUseCarbonEvents ) {
Carbon_InstallTimer();
Carbon_InstallEvents();
RunApplicationEventLoop();
Com_Quit_f();
} else
{
while ( 1 )
{
// run the frame
Com_Frame();
}
}
return 0;
}