/*
===========================================================================
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.
===========================================================================
*/
#if MAC_ALICE || MAC_STVEF_HM
#ifdef __cplusplus
extern "C" {
#endif
#endif
#include "../renderer/tr_local.h"
#include "../client/client.h"
#include "mac_local.h"
#include "g_public.h"
#include "unzip.h"
#if MAC_ALICE || MAC_STVEF_HM
#ifdef __cplusplus
}
#endif
#endif
#include
#include
#include
#define bzero( a,b ) memset( a,0,b )
cvar_t *pagememory;
long gSystemVersion;
qboolean sys_copy = qfalse;
static char sys_cmdline[MAX_STRING_CHARS];
clientStatic_t cls;
void *Sys_GetBotAIAPI( void *parms ) {
return NULL;
}
void Conbuf_AppendText( const char *pMsg ) {
char msg[4096];
strcpy( msg, pMsg );
printf( Q_CleanStr( msg ) );
printf( "\n" );
}
/*
==================
Sys_LowPhysicalMemory()
==================
*/
qboolean Sys_LowPhysicalMemory( void ) {
return qfalse;
}
/*
===============
PrintMatches
===============
*/
static char g_consoleField1[256];
static char g_consoleField2[256];
static void PrintMatches( const char *s ) {
if ( !Q_stricmpn( s, g_consoleField1, strlen( g_consoleField1 ) ) ) {
printf( " %s\n", s );
}
}
qboolean stdin_active = qtrue;
char *Sys_ConsoleInput( void ) {
#if 1
static char text[256];
int len;
fd_set fdset;
struct timeval timeout;
#ifndef DEDICATED
if ( !com_dedicated || !com_dedicated->value ) {
return NULL;
}
#endif
if ( !stdin_active ) {
return NULL;
}
FD_ZERO( &fdset );
FD_SET( 0, &fdset ); // stdin
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if ( select( 1, &fdset, NULL, NULL, &timeout ) == -1 || !FD_ISSET( 0, &fdset ) ) {
return NULL;
}
len = read( 0, text, sizeof( text ) );
if ( len == 0 ) { // eof!
stdin_active = qfalse;
return NULL;
}
if ( len < 1 ) {
return NULL;
}
text[len - 1] = 0; // rip off the /n and terminate
return text;
#else
const char ClearLine[] = "\r \r";
static int len = 0;
static bool bPendingExtended = false;
if ( !kbhit() ) {
return NULL;
}
if ( len == 0 ) {
memset( g_consoleField1,0,sizeof( g_consoleField1 ) );
}
g_consoleField1[len] = getch();
if ( bPendingExtended ) {
switch ( g_consoleField1[len] )
{
case 'H': //up
strcpy( g_consoleField1, g_consoleField2 );
printf( ClearLine );
printf( "%s",g_consoleField1 );
len = strlen( g_consoleField1 );
break;
case 'K': //left
break;
case 'M': //right
break;
case 'P': //down
break;
}
g_consoleField1[len] = 0; //erase last key hit
bPendingExtended = false;
} else {
switch ( (unsigned char) g_consoleField1[len] )
{
case 0x00: //fkey is next
case 0xe0: //extended = arrow keys
g_consoleField1[len] = 0; //erase last key hit
bPendingExtended = true;
break;
case 8: // backspace
printf( "%c %c",g_consoleField1[len],g_consoleField1[len] );
g_consoleField1[len] = 0;
if ( len > 0 ) {
len--;
}
g_consoleField1[len] = 0;
break;
case 9: //Tab
if ( len ) {
g_consoleField1[len] = 0; //erase last key hit
printf( "\n" );
// run through again, printing matches
Cmd_CommandCompletion( PrintMatches );
Cvar_CommandCompletion( PrintMatches );
printf( "\n%s", g_consoleField1 );
}
break;
case 27: // esc
// clear the line
printf( ClearLine );
len = 0;
break;
case '\r': //enter
g_consoleField1[len] = 0; //erase last key hit
printf( "\n" );
if ( len ) {
len = 0;
strcpy( g_consoleField2, g_consoleField1 );
return g_consoleField1;
}
break;
case 'v' - 'a' + 1: // ctrl-v is paste
g_consoleField1[len] = 0; //erase last key hit
char *cbd;
cbd = Sys_GetClipboardData();
if ( cbd ) {
strncpy( &g_consoleField1[len], cbd, sizeof( g_consoleField1 ) );
printf( "%s",cbd );
len += strlen( cbd );
Z_Free( cbd );
if ( len == sizeof( g_consoleField1 ) ) {
len = 0;
return g_consoleField1;
}
}
break;
default:
printf( "%c",g_consoleField1[len] );
len++;
if ( len == sizeof( g_consoleField1 ) ) {
len = 0;
return g_consoleField1;
}
break;
}
}
return NULL;
#endif
}
/*
==================
Sys_BeginProfiling
==================
*/
void Sys_BeginProfiling( void ) {
// this is just used on the mac build
}
void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) {
}
/*
=================
Sys_Shutdown
=================
*/
void Sys_Shutdown( void ) {
#if MAC_Q3_MP
Sys_ShutdownNetworking();
#endif
}
/*
=============
Sys_Error
Show the early console as an error dialog
=============
*/
void Sys_Error( const char *error, ... ) {
va_list argptr;
char text[4096];
va_start( argptr, error );
vsprintf( text, error, argptr );
va_end( argptr );
Conbuf_AppendText( text );
Conbuf_AppendText( "\n" );
// Sys_SetErrorText( text );
Sys_ShowConsole( 1, qtrue );
Sys_Shutdown();
// 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_Quit
==============
*/
void Sys_Quit( void ) {
Sys_Shutdown();
ExitToShell();
}
/*
==============
Sys_Print
==============
*/
void Sys_Print( const char *msg ) {
printf( "%s", msg );
}
void Sys_DebugPrint( const char *msg ) {
//¥¥¥ OutputDebugString( msg );
}
/*
================
Sys_CheckCD
Return true if the proper CD is in the drive
================
*/
qboolean Sys_CheckCD( void ) {
#ifdef FINAL_BUILD
// return Sys_ScanForCD();
#else
return qtrue;
#endif
}
/*
================
Sys_GetClipboardData
================
*/
char *Sys_GetClipboardData( void ) {
ScrapRef currentScrap;
OSStatus status;
char *buffer;
Size byteCount;
GetCurrentScrap( ¤tScrap );
status = GetScrapFlavorSize( currentScrap, 'TEXT', &byteCount );
if ( ( status == noErr ) && ( byteCount < 1023 ) ) {
buffer = (char *)Z_Malloc( byteCount + 1 );
if ( !buffer ) {
return NULL;
}
// Get the 'TEXT' clipping from the clipboard
status = GetScrapFlavorData( currentScrap, 'TEXT', &byteCount, (void *)buffer );
}
return buffer;
}
#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
void Sys_SnapVector( float *v ) {
v[0] = rint( v[0] );
v[1] = rint( v[1] );
v[2] = rint( v[2] );
}
#if MAC_WOLF_ET
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
#pragma mark -
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;
}
void Sys_CloseMutex( void ) {
}
/*
========================================================================
LOAD/UNLOAD DLL
========================================================================
*/
#pragma mark -
CFragConnectionID game_connID;
CFBundleRef gameBundleRef = NULL;
typedef void ( *dllEntryPtr )( int ( *syscallptr )( int, ... ) );
typedef int ( *systemCallsPtr )( int, ... );
typedef int ( *vmMainPtr )( int, ... );
typedef void *( *getGameAPIPtr )( void * );
typedef struct
{
void *procAddr;
void *TOCAddr;
} TVector;
typedef struct
{
// For CFM shared libraries
CFragConnectionID connID;
// For Mach-O bundles
CFBundleRef bundleRef;
CFURLRef bundleURL;
systemCallsPtr cfmGlue;
dllEntryPtr cfmGlue2;
vmMainPtr cfmGlue3;
TVector tvector;
} 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.
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 (out of the pk3 if pure)
return false;
}
if ( FS_FileIsInPAK( name, &ck ) == -1 ) {
FS_FreeFile( data );
return false;
}
// We create our temp copy of the DLL zip file outside the Quake3 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
}
#endif
#if MAC_WOLF2_MP
#pragma mark Sys_LoadDll
void *Sys_LoadDll( const char *name, char *fqpath, int( **entryPoint ) ( int, ... ), int ( *systemCalls )( int, ... ) )
#else
void *Sys_LoadDll( const char *name, int( **entryPoint ) ( int, ... ), int ( *systemCalls )( int, ... ) )
#endif
{
OSErr err = noErr;
FSSpec SLSpec;
char name2[255];
dllEntryPtr dllEntry = NULL;
dll_t *dllSpec;
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:
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;
Boolean ok;
// get app bundle
#if DEDICATED
char path[PATH_MAX];
CFURLRef bundleURL;
strcpy( path, kMacExeName );
bundleURL = CFURLCreateFromFileSystemRepresentation( NULL, path, strlen( path ), true );
refMainBundle = CFBundleCreate( NULL, bundleURL );
CFRelease( bundleURL );
#else
refMainBundle = CFBundleGetMainBundle();
#endif
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;
}
// 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 ) {
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 ) {
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 );
}
if ( dllSpec->cfmGlue2 ) {
DisposePtr( (Ptr)dllSpec->cfmGlue2 );
}
if ( dllSpec->cfmGlue3 ) {
DisposePtr( (Ptr)dllSpec->cfmGlue3 );
}
}
free( dllSpec );
}
void *Sys_GetGameAPI( void *parms ) {
OSErr err = noErr;
CFBundleRef refMainBundle = NULL;
CFURLRef refMainBundleURL = NULL;
CFStringRef fileName;
Boolean didLoad;
getGameAPIPtr GetGameAPI = NULL;
char name2[255];
// get app bundle (works even for a non-bundled app!)
#if DEDICATED
char path[PATH_MAX];
CFURLRef bundleURL;
strcpy( path, kMacExeName );
bundleURL = CFURLCreateFromFileSystemRepresentation( NULL, path, strlen( path ), true );
refMainBundle = CFBundleCreate( NULL, bundleURL );
CFRelease( bundleURL );
#else
refMainBundle = CFBundleGetMainBundle();
#endif
if ( !refMainBundle ) {
goto cantLoad;
}
// get a URL to our shared library from the "Resources" directory of the bundle
refMainBundleURL = CFBundleCopyResourceURL( refMainBundle, CFSTR( kMacGameLib ".bundle" ), NULL, NULL );
if ( !refMainBundleURL ) {
err = fnfErr;
goto cantLoad;
}
#if MAC_DEBUG
CFShow( refMainBundleURL );
#endif
// get the actual bundle for the library
gameBundleRef = CFBundleCreate( kCFAllocatorDefault, refMainBundleURL );
if ( !gameBundleRef ) {
goto cantLoad;
}
CFRelease( refMainBundleURL );
// Load the bundle code
didLoad = CFBundleLoadExecutable( gameBundleRef );
if ( !didLoad ) {
goto cantLoad;
}
GetGameAPI = (getGameAPIPtr)CFBundleGetFunctionPointerForName( gameBundleRef, CFSTR( "GetGameAPI" ) );
if ( GetGameAPI == NULL ) {
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;
if ( gameBundleRef ) {
CFRelease( gameBundleRef );
gameBundleRef = NULL;
}
}
/*
========================================================================
BACKGROUND FILE STREAMING
========================================================================
*/
void Sys_InitStreamThread( void ) {
}
void Sys_ShutdownStreamThread( void ) {
}
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];
static int eventHead = 0;
static int eventTail = 0;
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_GetEvent
================
*/
sysEvent_t Sys_GetEvent( void ) {
// MSG msg;
sysEvent_t ev;
char *s;
msg_t netmsg;
netadr_t adr;
// return if we have data
if ( eventHead > eventTail ) {
eventTail++;
return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
}
#if 0 // ¥¥¥
// pump the message loop
while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {
if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
Com_Quit_f();
}
// save the msg time, because wndprocs don't have access to the timestamp
// g_wv.sysMsgTime = msg.time;
TranslateMessage( &msg );
DispatchMessage( &msg );
}
#endif
// check for console commands
s = Sys_ConsoleInput();
if ( s ) {
char *b;
int len;
len = strlen( s ) + 1;
b = (char *)Z_Malloc( len );
Q_strncpyz( b, s, len );
Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
}
// 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
// the readcount stepahead is for SOCKS support
len = sizeof( netadr_t ) + netmsg.cursize - netmsg.readcount;
buf = (netadr_t *)Z_Malloc( len );
*buf = adr;
memcpy( buf + 1, &netmsg.data[netmsg.readcount], netmsg.cursize - netmsg.readcount );
Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf );
}
// 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();
return ev;
}
//================================================================
/*
=================
Sys_In_Restart_f
Restart the input subsystem
=================
*/
void Sys_In_Restart_f( void ) {
#if 0 // ¥¥¥
IN_Shutdown();
IN_Init();
#endif
}
/*
=================
Sys_Net_Restart_f
Restart the network subsystem
=================
*/
#if 0 // ¥¥¥
void Sys_Net_Restart_f( void ) {
NET_Restart();
}
#endif
enum {
CPUID_PPC_601 = 1,
CPUID_PPC_603,
CPUID_PPC_604,
CPUID_PPC_G3,
CPUID_PPC_G4,
CPUID_PPC_UNK
};
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:
return CPUID_PPC_UNK;
}
} else {
return CPUID_GENERIC;
}
}
/*
================
Sys_Init
Called after the common systems (cvars, files, etc)
are initialized
================
*/
void Sys_Init( void ) {
long response;
OSErr err;
int cpuid;
int numCPU = 1;
#if MAC_Q3_MP
Sys_InitNetworking();
#endif
// make sure the timer is high precision, otherwise
// NT gets 18ms resolution
/// timeBeginPeriod( 1 );
Cmd_AddCommand( "in_restart", Sys_In_Restart_f );
//¥¥¥ Cmd_AddCommand ("net_restart", Sys_Net_Restart_f);
//
// 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;
}
} 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 0
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 );
/// Cvar_Set( "username", Sys_GetCurrentUser() );
#if 0 // ¥¥¥ LBO
IN_Init(); // FIXME: not in dedicated?
#endif
}
//===============================================================================
// 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.
//===============================================================================
FSSpec app_spec;
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 );
}
//=======================================================================
//int totalMsec, countMsec;
/*
==================
WinMain
==================
*/
//int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
int main( int argc, char **argv ) {
char cwd[MAX_OSPATH];
char *cmdline;
int i,len;
OSErr err;
// int startTime, endTime;
// should never get a previous instance in Win32
// if ( hPrevInstance ) {
// return 0;
// }
// merge the command line, this is kinda silly
for ( len = 1, i = 1; i < argc; i++ )
len += strlen( argv[i] ) + 1;
cmdline = (char *)malloc( len );
*cmdline = 0;
for ( i = 1; i < argc; i++ ) {
if ( i > 1 ) {
strcat( cmdline, " " );
}
strcat( cmdline, argv[i] );
}
SetDefaultDirectory();
// Are we on 10?
err = Gestalt( gestaltSystemVersion, &gSystemVersion );
// g_wv.hInstance = hInstance;
// Q_strncpyz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
// done before Com/Sys_Init since we need this for error output
// Sys_CreateConsole();
// no abort/retry/fail errors
/// SetErrorMode( SEM_FAILCRITICALERRORS );
// get the initial time base
Sys_Milliseconds();
#if 0
// if we find the CD, add a +set cddir xxx command line
Sys_ScanForCD();
#endif
//¥¥¥ Sys_InitStreamThread();
Com_Init( cmdline );
#if MAC_MOHAA
Sys_InitLocalization();
#endif
//¥¥¥ NET_Init();
Com_Printf( "Working directory: %s\n", Sys_Cwd() );
// hide the early console since we've reached the point where we
// have a working graphics subsystems
if ( !com_dedicated->integer && !com_viewlog->integer ) {
Sys_ShowConsole( 0, qfalse );
}
// main game loop
while ( 1 ) {
// if not running as a game client, sleep a bit
// if ( g_wv.isMinimized || ( com_dedicated && com_dedicated->integer ) ) {
//¥¥¥ Sleep( 5 );
// }
// set low precision every frame, because some system calls
// reset it arbitrarily
// _controlfp( _PC_24, _MCW_PC );
// startTime = Sys_Milliseconds();
#if 0 // ¥¥¥
// make sure mouse and joystick are only called once a frame
IN_Frame();
#endif
// run the game
Com_Frame();
// endTime = Sys_Milliseconds();
// totalMsec += endTime - startTime;
// countMsec++;
}
// never gets here
return 0;
}