2005-08-26 04:48:05 +00:00
/ *
=== === === === === === === === === === === === === === === === === === === === === === === === ===
Copyright ( C ) 1999 -2005 Id Software , Inc .
This file is part of Quake III Arena source code .
Quake III Arena 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 2 of the License ,
or ( at your option ) any later version .
Quake III Arena 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 Foobar ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 -1301 USA
=== === === === === === === === === === === === === === === === === === === === === === === === ===
* /
# import "Q3Controller.h"
# import < Foundation / Foundation . h >
# import < AppKit / AppKit . h >
# include "client.h"
# include "macosx_local.h"
// # include "GameRanger SDK/gameranger.h"
# ifdef OMNI_TIMER
# import "macosx_timers.h"
# endif
# define MAX_ARGC 1024
static qboolean Sys_IsProcessingTerminationRequest = qfalse ;
static void Sys_CreatePathToFile ( NSString * path , NSDictionary * attributes ) ;
@ interface Q3Controller ( Private )
- ( void ) quakeMain ;
@ end
@ implementation Q3Controller
# ifndef DEDICATED
- ( void ) applicationDidFinishLaunching : ( NSNotification * ) notification ;
{
NS_DURING {
[ self quakeMain ] ;
} NS_HANDLER {
Sys_Error ( "%@" , [ localException reason ] ) ;
} NS_ENDHANDLER ;
Sys_Quit ( ) ;
}
- ( void ) applicationDidUnhide : ( NSNotification * ) notification ;
{
// Don ' t reactivate the game if we are asking whether to quit
if ( Sys_IsProcessingTerminationRequest )
return ;
if ( ! Sys_Unhide ( ) )
// Didn ' t work - - hide again so we should get another chance to unhide later
[ NSApp hide : nil ] ;
}
- ( NSApplicationTerminateReply ) applicationShouldTerminate : ( NSApplication * ) sender ;
{
int choice ;
if ( ! Sys_IsHidden ) {
// We ' re terminating via - terminate :
return NSTerminateNow ;
}
// Avoid reactivating GL when we unhide due to this panel
Sys_IsProcessingTerminationRequest = qtrue ;
choice = NSRunAlertPanel ( nil , @ "Quit without saving?" , @ "Don't Quit" , @ "Quit" , nil ) ;
Sys_IsProcessingTerminationRequest = qfalse ;
if ( choice = = NSAlertAlternateReturn )
return NSTerminateNow ;
// Make sure we get re - hidden
[ NSApp hide : nil ] ;
return NSTerminateCancel ;
}
// Actions
- ( IBAction ) paste : ( id ) sender ;
{
int shiftWasDown , insertWasDown ;
unsigned int currentTime ;
currentTime = Sys_Milliseconds ( ) ;
// Save the original keyboard state
shiftWasDown = keys [ K_SHIFT ] . down ;
insertWasDown = keys [ K_INS ] . down ;
// Fake a Shift - Insert keyboard event
keys [ K_SHIFT ] . down = qtrue ;
Sys_QueEvent ( currentTime , SE_KEY , K_INS , qtrue , 0 , NULL ) ;
Sys_QueEvent ( currentTime , SE_KEY , K_INS , qfalse , 0 , NULL ) ;
// Restore the original keyboard state
keys [ K_SHIFT ] . down = shiftWasDown ;
keys [ K_INS ] . down = insertWasDown ;
}
extern void CL_Quit _f ( void ) ;
- ( IBAction ) requestTerminate : ( id ) sender ;
{
Com_Quit _f ( ) ;
// UI_QuitMenu ( ) ;
}
- ( void ) showBanner ;
{
static BOOL hasShownBanner = NO ;
if ( ! hasShownBanner ) {
cvar_t * showBanner ;
hasShownBanner = YES ;
showBanner = Cvar_Get ( "cl_showBanner" , "1" , 0 ) ;
if ( showBanner -> integer ! = 0 ) {
NSPanel * splashPanel ;
NSImage * bannerImage ;
NSRect bannerRect ;
NSImageView * bannerImageView ;
bannerImage = [ [ NSImage alloc ] initWithContentsOfFile : [ [ NSBundle mainBundle ] pathForImageResource : @ "banner.jpg" ] ] ;
bannerRect = NSMakeRect ( 0.0 , 0.0 , [ bannerImage size ] . width , [ bannerImage size ] . height ) ;
splashPanel = [ [ NSPanel alloc ] initWithContentRect : bannerRect styleMask : NSBorderlessWindowMask backing : NSBackingStoreBuffered defer : NO ] ;
bannerImageView = [ [ NSImageView alloc ] initWithFrame : bannerRect ] ;
[ bannerImageView setImage : bannerImage ] ;
[ splashPanel setContentView : bannerImageView ] ;
[ bannerImageView release ] ;
[ splashPanel center ] ;
[ splashPanel setHasShadow : YES ] ;
[ splashPanel orderFront : nil ] ;
[ NSThread sleepUntilDate : [ NSDate dateWithTimeIntervalSinceNow : 2.5 ] ] ;
[ splashPanel close ] ;
[ bannerImage release ] ;
}
}
}
// Services
- ( void ) connectToServer : ( NSPasteboard * ) pasteboard userData : ( NSString * ) data error : ( NSString * * ) error ;
{
NSArray * pasteboardTypes ;
pasteboardTypes = [ pasteboard types ] ;
if ( [ pasteboardTypes containsObject : NSStringPboardType ] ) {
NSString * requestedServer ;
requestedServer = [ pasteboard stringForType : NSStringPboardType ] ;
if ( requestedServer ) {
Cbuf_AddText ( va ( "connect %s\n" , [ requestedServer cString ] ) ) ;
return ;
}
}
* error = @ "Unable to connect to server: could not find string on pasteboard" ;
}
- ( void ) performCommand : ( NSPasteboard * ) pasteboard userData : ( NSString * ) data error : ( NSString * * ) error ;
{
NSArray * pasteboardTypes ;
pasteboardTypes = [ pasteboard types ] ;
if ( [ pasteboardTypes containsObject : NSStringPboardType ] ) {
NSString * requestedCommand ;
requestedCommand = [ pasteboard stringForType : NSStringPboardType ] ;
if ( requestedCommand ) {
Cbuf_AddText ( va ( "%s\n" , [ requestedCommand cString ] ) ) ;
return ;
}
}
* error = @ "Unable to perform command: could not find string on pasteboard" ;
}
# endif
- ( void ) quakeMain ;
{
NSAutoreleasePool * pool ;
int argc = 0 ;
const char * argv [ MAX_ARGC ] ;
NSProcessInfo * processInfo ;
NSArray * arguments ;
unsigned int argumentIndex , argumentCount ;
NSFileManager * defaultManager ;
unsigned int commandLineLength ;
NSString * installationPathKey , * installationPath ;
char * cmdline ;
BOOL foundDirectory ;
NSString * appName , * demoAppName , * selectButton ;
int count = 0 ;
pool = [ [ NSAutoreleasePool alloc ] init ] ;
[ NSApp setServicesProvider : self ] ;
processInfo = [ NSProcessInfo processInfo ] ;
arguments = [ processInfo arguments ] ;
argumentCount = [ arguments count ] ;
for ( argumentIndex = 0 ; argumentIndex < argumentCount ; argumentIndex + + ) {
NSString * arg ;
arg = [ arguments objectAtIndex : argumentIndex ] ;
// Don ' t pass the Process Serial Number command line arg that the Window Server / Finder invokes us with
if ( [ arg hasPrefix : @ "-psn_" ] )
continue ;
argv [ argc + + ] = strdup ( [ arg cString ] ) ;
}
// Figure out where the level data is stored .
installationPathKey = @ "RetailInstallationPath" ;
installationPath = [ [ NSUserDefaults standardUserDefaults ] objectForKey : installationPathKey ] ;
if ( ! installationPath ) {
// Default to the directory containing the executable ( which is where most users will want to put it
installationPath = [ [ [ NSBundle mainBundle ] bundlePath ] stringByDeletingLastPathComponent ] ;
}
# if ! defined ( DEDICATED )
appName = [ [ [ NSBundle mainBundle ] infoDictionary ] objectForKey : @ "CFBundleName" ] ;
# else
// We are hard coding the app name here since the dedicated server is a tool , not a app bundle and does not have access to the Info . plist that the client app does . Suck .
appName = @ "Quake3" ;
# endif
demoAppName = appName ;
while ( YES ) {
NSString * dataPath ;
NSOpenPanel * openPanel ;
int result ;
foundDirectory = NO ;
defaultManager = [ NSFileManager defaultManager ] ;
// NSLog ( @ "Candidate installation path = %@" , installationPath ) ;
dataPath = [ installationPath stringByAppendingPathComponent : @ "baseq3" ] ;
if ( [ defaultManager fileExistsAtPath : dataPath ] ) {
// Check that the data directory contains at least one . pk3 file . We don ' t know what it will be named , so don ' t hard code a name ( for example it might be named ' french . pk3 ' for a French release
NSArray * files ;
unsigned int fileIndex ;
files = [ defaultManager directoryContentsAtPath : dataPath ] ;
fileIndex = [ files count ] ;
while ( fileIndex - - ) {
if ( [ [ files objectAtIndex : fileIndex ] hasSuffix : @ "pk3" ] ) {
// NSLog ( @ "Found %@." , [ files objectAtIndex : fileIndex ] ) ;
foundDirectory = YES ;
break ;
}
}
}
if ( foundDirectory )
break ;
# ifdef DEDICATED
break ;
# warning TJW : We are hard coding the app name and default domain here since the dedicated server is a tool , not a app bundle and does not have access to the Info . plist that the client app does . Suck .
NSLog ( @ "Unable to determine installation directory. Please move the executable into the '%@' installation directory or add a '%@' key in the 'Q3DedicatedServer' defaults domain." , appName , installationPathKey , [ [ NSBundle mainBundle ] bundleIdentifier ] ) ;
Sys_Quit ( ) ;
exit ( 1 ) ;
# else
selectButton = @ "Select Retail Installation..." ;
result = NSRunAlertPanel ( demoAppName , @ "You need to select the installation directory for %@ (not any directory inside of it -- the installation directory itself)." , selectButton , @ "Quit" , nil , appName ) ;
switch ( result ) {
case NSAlertDefaultReturn :
break ;
default :
Sys_Quit ( ) ;
break ;
}
openPanel = [ NSOpenPanel openPanel ] ;
[ openPanel setAllowsMultipleSelection : NO ] ;
[ openPanel setCanChooseDirectories : YES ] ;
[ openPanel setCanChooseFiles : NO ] ;
result = [ openPanel runModalForDirectory : nil file : nil ] ;
if ( result = = NSOKButton ) {
NSArray * filenames ;
filenames = [ openPanel filenames ] ;
if ( [ filenames count ] = = 1 ) {
installationPath = [ filenames objectAtIndex : 0 ] ;
[ [ NSUserDefaults standardUserDefaults ] setObject : installationPath forKey : installationPathKey ] ;
[ [ NSUserDefaults standardUserDefaults ] synchronize ] ;
}
}
# endif
}
// Create the application support directory if it doesn ' t exist already
do {
NSArray * results ;
NSString * libraryPath , * homePath , * filePath ;
NSDictionary * attributes ;
results = NSSearchPathForDirectoriesInDomains ( NSLibraryDirectory , NSUserDomainMask , YES ) ;
if ( ! [ results count ] )
break ;
libraryPath = [ results objectAtIndex : 0 ] ;
homePath = [ libraryPath stringByAppendingPathComponent : @ "Application Support" ] ;
homePath = [ homePath stringByAppendingPathComponent : appName ] ;
filePath = [ homePath stringByAppendingPathComponent : @ "foo" ] ;
attributes = [ NSDictionary dictionaryWithObjectsAndKeys : [ NSNumber numberWithUnsignedInt : 0750 ] , NSFilePosixPermissions , nil ] ;
NS_DURING {
Sys_CreatePathToFile ( filePath , attributes ) ;
Sys_SetDefaultHomePath ( [ homePath fileSystemRepresentation ] ) ;
} NS_HANDLER {
NSLog ( @ "Exception: %@" , localException ) ;
# ifndef DEDICATED
NSRunAlertPanel ( nil , @ "Unable to create '%@'. Please make sure that you have permission to write to this folder and re-run the game." , @ "OK" , nil , nil , homePath ) ;
# endif
Sys_Quit ( ) ;
} NS_ENDHANDLER ;
} while ( 0 ) ;
// Provoke the CD scanning code into looking up the CD .
Sys_CheckCD ( ) ;
// Let the filesystem know where our local install is
Sys_SetDefaultInstallPath ( [ installationPath cString ] ) ;
cmdline = NULL ;
# if 0
if ( GRCheckFileForCmd ( ) ) {
GRGetWaitingCmd ( ) ;
if ( GRHasProperty ( ' Exec ' ) ) {
NSString * cfgPath , * grCfg ;
cfgPath = [ [ [ NSBundle mainBundle ] bundlePath ] stringByDeletingLastPathComponent ] ;
cfgPath = [ cfgPath stringByAppendingPathComponent : [ NSString stringWithCString : GRGetPropertyStr ( ' Exec ' ) ] ] ;
grCfg = [ NSString stringWithContentsOfFile : cfgPath ] ;
cmdline = malloc ( strlen ( [ grCfg cString ] ) + 1 ) ;
[ grCfg getCString : cmdline ] ;
}
}
# endif
if ( ! cmdline ) {
// merge the command line , this is kinda silly
for ( commandLineLength = 1 , argumentIndex = 1 ; argumentIndex < argc ; argumentIndex + + )
commandLineLength + = strlen ( argv [ argumentIndex ] ) + 1 ;
cmdline = malloc ( commandLineLength ) ;
* cmdline = ' \ 0 ' ;
for ( argumentIndex = 1 ; argumentIndex < argc ; argumentIndex + + ) {
if ( argumentIndex > 1 )
strcat ( cmdline , " " ) ;
strcat ( cmdline , argv [ argumentIndex ] ) ;
}
}
Com_Printf ( "command line: %s\n" , cmdline ) ;
Com_Init ( cmdline ) ;
# ifndef DEDICATED
[ NSApp activateIgnoringOtherApps : YES ] ;
# endif
while ( 1 ) {
Com_Frame ( ) ;
if ( ( count & 15 ) = = 0 ) {
// We should think about doing this less frequently than every frame
[ pool release ] ;
pool = [ [ NSAutoreleasePool alloc ] init ] ;
}
}
[ pool release ] ;
}
@ end
// Creates any directories needed to be able to create a file at the specified path . Raises an exception on failure .
static void Sys_CreatePathToFile ( NSString * path , NSDictionary * attributes )
{
NSArray * pathComponents ;
unsigned int dirIndex , dirCount ;
unsigned int startingIndex ;
NSFileManager * manager ;
manager = [ NSFileManager defaultManager ] ;
pathComponents = [ path pathComponents ] ;
dirCount = [ pathComponents count ] - 1 ;
startingIndex = 0 ;
for ( dirIndex = startingIndex ; dirIndex < dirCount ; dirIndex + + ) {
NSString * partialPath ;
BOOL fileExists ;
partialPath = [ NSString pathWithComponents : [ pathComponents subarrayWithRange : NSMakeRange ( 0 , dirIndex + 1 ) ] ] ;
// Don ' t use the ' fileExistsAtPath : isDirectory : ' version since it doesn ' t traverse symlinks
fileExists = [ manager fileExistsAtPath : partialPath ] ;
if ( ! fileExists ) {
if ( ! [ manager createDirectoryAtPath : partialPath attributes : attributes ] ) {
[ NSException raise : NSGenericException format : @ "Unable to create a directory at path: %@" , partialPath ] ;
}
} else {
NSDictionary * attributes ;
attributes = [ manager fileAttributesAtPath : partialPath traverseLink : YES ] ;
if ( ! [ [ attributes objectForKey : NSFileType ] isEqualToString : NSFileTypeDirectory ] ) {
[ NSException raise : NSGenericException format : @ "Unable to write to path \" % @ \ " because \" % @ \ " is not a directory" ,
path , partialPath ] ;
}
}
}
}
# ifdef DEDICATED
void S_ClearSoundBuffer ( void ) {
}
# endif