diff --git a/docs/README.html b/docs/README.html index 4cdb27cb..d2a481de 100644 --- a/docs/README.html +++ b/docs/README.html @@ -23,7 +23,7 @@ shape="rect">Wouter van Oortmerssen´s FPS called Cube. AssaultCube encourages players from all platforms to play by being multi-platform (Available on: Windows, Linux and - OSX), partially open-source and low on system + Mac OS X), partially open-source and low on system and networking requirements. It is set in a realistic looking environment (as best that's possible with this engine) while gameplay stays fast and arcade. This game is all about team oriented, multi-player fun. diff --git a/docs/commandline.html b/docs/commandline.html index 84853634..b33405eb 100644 --- a/docs/commandline.html +++ b/docs/commandline.html @@ -72,7 +72,7 @@ Remember to save once you're finished!

- Apple OSX + Apple Mac OS X

After starting the AssaultCube launcher, under the "main" tab of the AssaultCube diff --git a/docs/getstarted.html b/docs/getstarted.html index 74c619f0..53fe1ba7 100644 --- a/docs/getstarted.html +++ b/docs/getstarted.html @@ -78,8 +78,10 @@ Apple - Mac OS X - + Mac OS X 10.4 +
+ Version 10.4 or higher. + diff --git a/source/xcode/English.lproj/MainMenu.nib/classes.nib b/source/xcode/English.lproj/MainMenu.nib/classes.nib index 5f544aa5..14f4e2fe 100644 --- a/source/xcode/English.lproj/MainMenu.nib/classes.nib +++ b/source/xcode/English.lproj/MainMenu.nib/classes.nib @@ -1,30 +1,88 @@ -{ - IBClasses = ( - {CLASS = ConsoleView; LANGUAGE = ObjC; SUPERCLASS = NSView; }, - {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - { - ACTIONS = { - helpAction = id; - multiplayerAction = id; - playAction = id; - playMap = id; - playRpg = id; - }; - CLASS = Launcher; - LANGUAGE = ObjC; - OUTLETS = { - console = ConsoleView; - keys = NSArrayController; - maps = NSArrayController; - multiplayer = NSButton; - resolutions = NSPopUpButton; - view1 = NSView; - view2 = NSView; - view3 = NSView; - window = NSWindow; - }; - SUPERCLASS = NSObject; - } - ); - IBVersion = 1; -} \ No newline at end of file + + + + + IBClasses + + + ACTIONS + + helpAction + id + multiplayerAction + id + openUserdir + id + playAction + id + playMap + id + playRpg + id + showForum + id + + CLASS + Launcher + LANGUAGE + ObjC + OUTLETS + + console + ConsoleView + keys + NSArrayController + maps + NSArrayController + multiplayer + NSButton + prog + NSProgressIndicator + resolutions + NSPopUpButton + stencil + NSButton + tabs + NSTabView + view1 + NSView + view2 + NSView + view3 + NSView + view4 + NSView + view5 + NSView + window + NSWindow + + SUPERCLASS + NSObject + + + ACTIONS + + + id + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + ConsoleView + LANGUAGE + ObjC + SUPERCLASS + NSView + + + IBVersion + 1 + + diff --git a/source/xcode/English.lproj/MainMenu.nib/info.nib b/source/xcode/English.lproj/MainMenu.nib/info.nib index 7839a25e..f729718a 100644 --- a/source/xcode/English.lproj/MainMenu.nib/info.nib +++ b/source/xcode/English.lproj/MainMenu.nib/info.nib @@ -1,32 +1,24 @@ - + - IBDocumentLocation - 363 148 569 391 0 0 1920 1178 - IBEditorPositions - - 208 - 709 615 502 340 0 0 1920 1178 - 213 - 709 615 502 340 0 0 1920 1178 - 264 - 709 615 502 340 0 0 1920 1178 - 29 - 149 405 230 44 0 0 1920 1178 - IBFramework Version - 446.1 - IBLockedObjects - + 672 + IBLastKnownRelativeProjectPath + ../actioncube.xcodeproj + IBOldestOS + 5 IBOpenObjects 264 - 213 - 208 + 336 + 240 21 + 213 IBSystem Version - 8P2137 + 9F33 + targetFramework + IBCocoaFramework diff --git a/source/xcode/English.lproj/MainMenu.nib/keyedobjects.nib b/source/xcode/English.lproj/MainMenu.nib/keyedobjects.nib index d5d6573c..0b3fa84d 100644 Binary files a/source/xcode/English.lproj/MainMenu.nib/keyedobjects.nib and b/source/xcode/English.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/source/xcode/Launcher.h b/source/xcode/Launcher.h index fedcc289..b43bdd17 100644 --- a/source/xcode/Launcher.h +++ b/source/xcode/Launcher.h @@ -3,24 +3,38 @@ @class ConsoleView; @interface Launcher : NSObject { - IBOutlet NSWindow *window; - IBOutlet NSView *view1; - IBOutlet NSView *view2; - IBOutlet NSView *view3; + IBOutlet NSWindow *window; - IBOutlet NSArrayController *maps; - IBOutlet NSArrayController *keys; + //able to leave these disconnected + IBOutlet NSView *view1; //Main + IBOutlet NSView *view2; //Maps + IBOutlet NSView *view3; //Keys + IBOutlet NSView *view4; //Server + + + IBOutlet NSProgressIndicator *prog; //while scanning maps - it's there if you want to wire it up + IBOutlet NSArrayController *maps; + IBOutlet NSArrayController *keys; IBOutlet NSPopUpButton *resolutions; - IBOutlet NSButton *multiplayer; - IBOutlet ConsoleView *console; + IBOutlet NSButton *stencil; + + IBOutlet NSButton *multiplayer; + IBOutlet ConsoleView *console; @private - NSMutableDictionary *toolBarItems; - pid_t server; + NSMutableDictionary *toolBarItems; + pid_t server; + NSMutableDictionary *fileRoles; + BOOL forcename; } - (IBAction)playAction:(id)sender; - (IBAction)multiplayerAction:(id)sender; -- (IBAction)helpAction:(id)sender; -@end \ No newline at end of file +- (IBAction)playRpg:(id)sender; + +- (IBAction)playMap:(id)sender; + +- (IBAction)openUserdir:(id)sender; + +@end diff --git a/source/xcode/Launcher.m b/source/xcode/Launcher.m index f0fe30a1..13c9265b 100644 --- a/source/xcode/Launcher.m +++ b/source/xcode/Launcher.m @@ -1,35 +1,70 @@ #import "Launcher.h" #import "ConsoleView.h" #include -#include /* unlink */ -#include /* forkpty */ +#include /* _exit() */ +#include /* forkpty() */ + +// User default keys +#define dkVERSION @"version" +#define dkFULLSCREEN @"fullscreen" +#define dkFSAA @"fsaa" + +#define dkRESOLUTION @"resolution" +#define dkADVANCEDOPTS @"advancedOptions" +#define dkSERVEROPTS @"server_options" +#define dkDESCRIPTION @"server_description" +#define dkPASSWORD @"server_password" +#define dkMAXCLIENTS @"server_maxclients" #define kMaxDisplays 16 -// unless you want strings with "(null)" in them :-/ -@interface NSUserDefaults(Extras) -- (NSString*)nonNullStringForKey:(NSString*)key; -@end +//If you make a MOD then please change this, the bundle indentifier, the file extensions (.ogz, .dmo), and the url registration. +#define kSAUERBRATEN @"assaultcube" -@implementation NSUserDefaults(Extras) -- (NSString*)nonNullStringForKey:(NSString*)key { - NSString *result = [self stringForKey:key]; - return (result?result:@""); +//tab names, i.e. image names (text is localised) +#define tkMAIN @"Main" +#define tkMAPS @"Maps" +#define tkKEYS @"Keys" +#define tkSERVER @"Server" + + +@interface NSString(Extras) +@end +@implementation NSString(Extras) +- (NSString*)expand { + NSMutableString *str = [NSMutableString string]; + [str setString:self]; + [str replaceOccurrencesOfString:@":s" withString:kSAUERBRATEN options:0 range:NSMakeRange(0, [str length])]; + return str; } @end +@interface NSUserDefaults(Extras) // unless you want strings with "(null)" in them :-/ +- (NSString*)nonNullStringForKey:(NSString*)key; +@end +@implementation NSUserDefaults(Extras) +- (NSString*)nonNullStringForKey:(NSString*)key { + NSString *result = [self stringForKey:key]; + return (result ? result : @""); +} +@end + @interface Map : NSObject { NSString *path; + BOOL demo, user; } @end - @implementation Map -- (id)initWithPath:(NSString*)aPath +- (id)initWithPath:(NSString*)aPath user:(BOOL)aUser demo:(BOOL)aDemo { if((self = [super init])) + { path = [[aPath stringByDeletingPathExtension] retain]; + user = aUser; + demo = aDemo; + } return self; } - (void)dealloc @@ -37,26 +72,42 @@ [path release]; [super dealloc]; } -- (NSString*)path { return path; } +- (NSString*)path { return (demo ? [NSString stringWithFormat:@"-xdemo \"%@\"", path] : path); } // minor hack - (NSString*)name { return [path lastPathComponent]; } -- (NSImage*)image { return [[NSImage alloc] initWithContentsOfFile:[path stringByAppendingString:@".jpg"]]; } +- (NSImage*)image +{ + NSImage *image = [[NSImage alloc] initWithContentsOfFile:[path stringByAppendingString:@".jpg"]]; + if(!image && demo) image = [NSImage imageNamed:tkMAIN]; + if(!image) image = [NSImage imageNamed:@"Nomap"]; + return image; +} - (NSString*)text { + NSString *text = [NSString alloc]; NSError *error; - return [[NSString alloc] initWithContentsOfFile:[path stringByAppendingString:@".txt"] encoding:NSASCIIStringEncoding error:&error]; + if([text respondsToSelector:@selector(initWithContentsOfFile:encoding:error:)]) + text = [text initWithContentsOfFile:[path stringByAppendingString:@".txt"] encoding:NSASCIIStringEncoding error:&error]; + else + text = [text initWithContentsOfFile:[path stringByAppendingString:@".txt"]]; //deprecated in 10.4 + if(!text) text = (demo)?@"Recorded demo data":@""; + return text; } +- (void)setText:(NSString*)text { } // wtf? - damn textfield believes it's editable - (NSString*)tickIfExists:(NSString*)ext { unichar tickCh = 0x2713; - return [[NSFileManager defaultManager] fileExistsAtPath:[path stringByAppendingString:ext]] ? [NSString stringWithCharacters:&tickCh length:1] : @""; + return ([[NSFileManager defaultManager] fileExistsAtPath:[path stringByAppendingString:ext]] ? [NSString stringWithCharacters:&tickCh length:1] : @""); } - (NSString*)hasImage { return [self tickIfExists:@".jpg"]; } - (NSString*)hasText { return [self tickIfExists:@".txt"]; } - (NSString*)hasCfg { return [self tickIfExists:@".cfg"]; } +- (NSString*)user { + unichar tickCh = 0x2713; + return (user ? [NSString stringWithCharacters:&tickCh length:1] : @""); +} @end - static int numberForKey(CFDictionaryRef desc, CFStringRef key) { CFNumberRef value; @@ -67,19 +118,15 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) return num; } -@implementation Launcher + +@interface Launcher(ToolBar) +@end +@implementation Launcher(ToolBar) - (void)switchViews:(NSToolbarItem *)item { - NSView *prefsView; - switch([item tag]) - { - case 1: prefsView = view1; break; - case 2: prefsView = view2; break; - case 3: prefsView = view3; break; - //extend as see fit... - default: return; - } + NSView *views[] = {view1, view2, view3, view4}; + NSView *prefsView = views[[item tag]-1]; //to stop flicker, we make a temp blank view. NSView *tempView = [[NSView alloc] initWithFrame:[[window contentView] frame]]; @@ -94,39 +141,38 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) //set the frame to newFrame and animate it. [window setFrame:newFrame display:YES animate:YES]; - //set the main content view to the new view we have picked through the if structure above. + //set the main content view to the new view we have picked through. [window setContentView:prefsView]; [window setContentMinSize:[prefsView bounds].size]; } -- (NSToolbarItem*)addToolBarItem:(NSString*)name -{ - int n = [toolBarItems count] + 1; - NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:[NSString stringWithFormat:@"%0d", n]]; - [item setTag:n]; - [item setTarget:self]; - [item setAction:@selector(switchViews:)]; - [toolBarItems setObject:item forKey:[item itemIdentifier]]; - [item setLabel:NSLocalizedString(name, @"")]; - [item setImage:[NSImage imageNamed:name]]; - [item release]; - return item; -} - -- (void)initViews +- (void)initToolBar { toolBarItems = [[NSMutableDictionary alloc] init]; - NSToolbarItem *first = [self addToolBarItem:@"Main"]; - [self addToolBarItem:@"Keys"]; - [self addToolBarItem:@"Server"]; - - NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:NSToolbarFlexibleSpaceItemIdentifier]; - [toolBarItems setObject:item forKey:[item itemIdentifier]]; - [item release]; - - [[self addToolBarItem:@"Help"] setAction:@selector(helpAction:)]; - - NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"PreferencePanes"]; + NSEnumerator *e = [[self toolbarDefaultItemIdentifiers:nil] objectEnumerator]; + NSString *identifier; + while(identifier = [e nextObject]) + { + NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:identifier]; + int tag = [identifier intValue]; + NSString *name = identifier; + SEL action = @selector(displayHelp:); + id target = self; + if(tag) { + NSString *names[] = {tkMAIN, tkMAPS, tkKEYS, tkSERVER}; + name = names[tag-1]; + action = @selector(switchViews:); + target = self; + } + [item setTag:tag]; + [item setTarget:target]; + [item setAction:action]; + [item setLabel:NSLocalizedString(name, @"")]; + [item setImage:[NSImage imageNamed:name]]; + [toolBarItems setObject:item forKey:identifier]; + [item release]; + } + NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@""]; [toolbar setDelegate:self]; [toolbar setAllowsUserCustomization:NO]; [toolbar setAutosavesConfiguration:NO]; @@ -134,14 +180,14 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) [toolbar release]; if([window respondsToSelector:@selector(setShowsToolbarButton:)]) [window setShowsToolbarButton:NO]; //10.4+ - //Make it select the first by default + //select the first by default + NSToolbarItem *first = [toolBarItems objectForKey:[[self toolbarDefaultItemIdentifiers:nil] objectAtIndex:0]]; [toolbar setSelectedItemIdentifier:[first itemIdentifier]]; [self switchViews:first]; } -/* - * toolbar delegate methods - */ +#pragma mark toolbar delegate methods + - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag { return [toolBarItems objectForKey:itemIdentifier]; @@ -152,19 +198,58 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) return [self toolbarDefaultItemIdentifiers:theToolbar]; } -- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)theToolbar +- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar { - return [NSArray arrayWithObjects:@"1", @"2", @"3", NSToolbarFlexibleSpaceItemIdentifier, @"5", nil]; + NSMutableArray *array = (NSMutableArray *)[self toolbarSelectableItemIdentifiers:toolbar]; + [array addObject:NSToolbarFlexibleSpaceItemIdentifier]; + [array addObject:@"Help"]; + return array; } -- (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar { - return [NSArray arrayWithObjects:@"1", @"2", @"3", nil]; +- (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar +{ + NSMutableArray *array = [NSMutableArray array]; + NSView *views[] = {view1, view2, view3, view4}; + int i; + for(i = 0; i < sizeof(views)/sizeof(NSView*); i++) if(views[i]) [array addObject:[NSString stringWithFormat:@"%d", i+1]]; + return array; +} + +- (void)displayHelp:(id)sender +{ + NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/help/README.html"]; + + if (![[NSWorkspace sharedWorkspace] openFile:path]) + NSLog(@"Warning: [[NSWorkspace sharedWorkspace] openFile:path] failed"); + +} +@end + + +@implementation Launcher + +/* directory where the executable lives */ ++ (NSString *)cwd +{ + return [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Contents/gamedata"]; } - - - +/* directory where user files are kept - typically /Users//Application Support/sauerbraten */ ++ (NSString*)userdir +{ + FSRef folder; + NSString *path = nil; + if(FSFindFolder(kUserDomain, kApplicationSupportFolderType, NO, &folder) == noErr) { + CFURLRef url = CFURLCreateFromFSRef(kCFAllocatorDefault, &folder); + path = [(NSURL *)url path]; + CFRelease(url); + path = [path stringByAppendingPathComponent:kSAUERBRATEN]; + NSFileManager *fm = [NSFileManager defaultManager]; + if(![fm fileExistsAtPath:path]) [fm createDirectoryAtPath:path attributes:nil]; //ensure it exists + } + return path; +} - (void)addResolutionsForDisplay:(CGDirectDisplayID)dspy { @@ -190,63 +275,51 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) for (i = 0; i < numDisplays; i++) [self addResolutionsForDisplay:display[i]]; } - [resolutions selectItemAtIndex: [[NSUserDefaults standardUserDefaults] integerForKey:@"resolution"]]; -} - - - - -/* directory where the executable lives */ --(NSString *)cwd -{ - return [[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"assaultcube"]; + [resolutions selectItemAtIndex: [[NSUserDefaults standardUserDefaults] integerForKey:dkRESOLUTION]]; } /* build key array from config data */ -(NSArray *)getKeys:(NSDictionary *)dict { - NSMutableArray *arr = [[NSMutableArray alloc] init]; + NSMutableArray *arr = [NSMutableArray array]; NSEnumerator *e = [dict keyEnumerator]; NSString *key; while ((key = [e nextObject])) { - NSString *trig; - if([key hasPrefix:@"editbind"]) - trig = [key substringFromIndex:9]; - else if([key hasPrefix:@"bind"]) - trig = [key substringFromIndex:5]; - else - continue; + int pos = [key rangeOfString:@"bind."].location; + if(pos == NSNotFound || pos > 5) continue; [arr addObject:[NSDictionary dictionaryWithObjectsAndKeys: //keys used in nib - trig, @"key", - [key hasPrefix:@"editbind"]?@"edit":@"", @"mode", + [key substringFromIndex:pos+5], @"key", + [key substringToIndex:pos], @"mode", [dict objectForKey:key], @"action", nil]]; } return arr; } - /* * extract a dictionary from the config files containing: - * - name, gamma strings + * - name, team, gamma strings * - bind/editbind '.' key strings */ -(NSDictionary *)readConfigFiles { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; [dict setObject:@"" forKey:@"name"]; //ensure these entries are never nil + [dict setObject:@"" forKey:@"team"]; NSString *files[] = {@"config.cfg", @"autoexec.cfg"}; int i; for(i = 0; i < sizeof(files)/sizeof(NSString*); i++) { - NSString *file = [[self cwd] stringByAppendingPathComponent:files[i]]; + NSString *file = [Launcher userdir]; + file = [file stringByAppendingPathComponent:files[i]]; + NSArray *lines = [[NSString stringWithContentsOfFile:file] componentsSeparatedByString:@"\n"]; if(i==0 && !lines) // ugh - special case when first run... { - file = [[self cwd] stringByAppendingPathComponent:@"config/defaults.cfg"]; + file = [[Launcher cwd] stringByAppendingPathComponent:@"config/defaults.cfg"]; lines = [[NSString stringWithContentsOfFile:file] componentsSeparatedByString:@"\n"]; } @@ -257,6 +330,7 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) NSRange r; // more flexible to do this manually rather than via NSScanner... int j = 0; while(j < [line length] && [line characterAtIndex:j] <= ' ') j++; //skip white + if(j != 0) continue; // shouldn't be indented r.location = j; while(j < [line length] && [line characterAtIndex:j] > ' ') j++; //until white r.length = j - r.location; @@ -273,139 +347,28 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) while(j < [line length] && [line characterAtIndex:j] > ' ') j++; //until white r.length = j - r.location; } + if(r.location+r.length >= [line length]) continue; //missing value NSString *value = [line substringWithRange:r]; while(j < [line length] && [line characterAtIndex:j] <= ' ') j++; //skip white NSString *remainder = [line substringFromIndex:j]; - if([type isEqual:@"name"] || [type isEqual:@"gamma"]) + if([type isEqual:@"name"] || [type isEqual:@"team"] || [type isEqual:@"gamma"]) [dict setObject:value forKey:type]; - else if([type isEqual:@"bind"] || [type isEqual:@"editbind"]) + else if([type isEqual:@"bind"] || [type isEqual:@"editbind"] || [type isEqual:@"specbind"]) [dict setObject:remainder forKey:[NSString stringWithFormat:@"%@.%@", type,value]]; } } return dict; } --(void)updateAutoexecFile:(NSDictionary *)updates -{ - NSString *file = [[self cwd] stringByAppendingPathComponent:@"config/autoexec.cfg"]; - //build the data - NSString *result = nil; - NSArray *lines = [[NSString stringWithContentsOfFile:file] componentsSeparatedByString:@"\n"]; - if(lines) - { - NSString *line; - NSEnumerator *e = [lines objectEnumerator]; - while(line = [e nextObject]) - { - NSScanner *scanner = [NSScanner scannerWithString:line]; - NSString *type; - if([scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet] intoString:&type]) - if([updates objectForKey:type]) continue; //skip things declared in updates - result = (result) ? [NSString stringWithFormat:@"%@\n%@", result, line] : line; - } - } - NSEnumerator *e = [updates keyEnumerator]; - NSString *type; - while(type = [e nextObject]) - { - id value = [updates objectForKey:type]; - if([type isEqual:@"name"]) value = [NSString stringWithFormat:@"\"%@\"", value]; - NSString *line = [NSString stringWithFormat:@"%@ %@", type, value]; - result = (result) ? [NSString stringWithFormat:@"%@\n%@", result, line] : line; - } - //backup - NSFileManager *fm = [NSFileManager defaultManager]; - NSString *backupfile = nil; - if([fm fileExistsAtPath:file]) - { - backupfile = [file stringByAppendingString:@".bak"]; - if(![fm movePath:file toPath:backupfile handler:nil]) return; //can't create backup - } - //write the new file - if(![fm createFileAtPath:file contents:[result dataUsingEncoding:NSASCIIStringEncoding] attributes:nil]) return; //can't create new file - //remove the backup - if(backupfile) [fm removeFileAtPath:backupfile handler:nil]; -} - -- (void)serverTerminated -{ - if(server==-1) return; +- (void)killServer { + if(server > 0) kill(server, SIGKILL); //@WARNING - you do NOT want a 0 or -1 to be accidentally sent a kill! server = -1; - [multiplayer setTitle:@"Start"]; + [multiplayer setTitle:NSLocalizedString(@"Start", @"")]; [console appendText:@"\n \n"]; } -- (void)setServerActive:(BOOL)start -{ - if((server==-1) != start) return; - - if(!start) - { //STOP - - //damn server, terminate isn't good enough for you - die, die, die... - if((server!=-1) && (server!=0)) kill(server, SIGKILL); //@WARNING - you do not want a 0 or -1 to be accidentally sent a kill! - [self serverTerminated]; - } - else - { //START - NSString *cwd = [self cwd]; - NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; - - NSArray *opts = [[defs nonNullStringForKey:@"server_options"] componentsSeparatedByString:@" "]; - - const char *childCwd = [cwd fileSystemRepresentation]; - const char *childPath = [[cwd stringByAppendingPathComponent:@"actioncube.app/Contents/MacOS/actioncube"] fileSystemRepresentation]; - const char **args = (const char**)malloc(sizeof(char*)*([opts count] + 3 + 3)); //3 = {path, -d, NULL}, and +3 again for optional settings... - int i, fdm, argc = 0; - - args[argc++] = childPath; - args[argc++] = "-d"; - - for(i = 0; i < [opts count]; i++) - { - NSString *opt = [opts objectAtIndex:i]; - if([opt length] == 0) continue; //skip empty - args[argc++] = [opt UTF8String]; - } - - NSString *desc = [[NSUserDefaults standardUserDefaults] nonNullStringForKey:@"server_description"]; - if (![desc isEqualToString:@""]) args[argc++] = [[NSString stringWithFormat:@"-n%@", desc] UTF8String]; - - NSString *pass = [[NSUserDefaults standardUserDefaults] nonNullStringForKey:@"server_password"]; - if (![pass isEqualToString:@""]) args[argc++] = [[NSString stringWithFormat:@"-x%@", pass] UTF8String]; - - int clients = [[NSUserDefaults standardUserDefaults] integerForKey:@"server_maxclients"]; - if (clients > 0) args[argc++] = [[NSString stringWithFormat:@"-c%d", clients] UTF8String]; - - args[argc++] = NULL; - - switch ( (server = forkpty(&fdm, NULL, NULL, NULL)) ) // forkpty so we can reliably grab SDL console - { - case -1: - [console appendLine:@"Error - can't launch server"]; - [self serverTerminated]; - break; - case 0: // child - chdir(childCwd); - execv(childPath, (char*const*)args); - fprintf(stderr, "Error - can't launch server\n"); - _exit(0); - default: // parent - free(args); - //fprintf(stderr, "fdm=%d\n", slave_name, fdm); - [multiplayer setTitle:@"Stop"]; - - NSFileHandle *taskOutput = [[NSFileHandle alloc] initWithFileDescriptor:fdm]; - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc addObserver:self selector:@selector(serverDataAvailable:) name:NSFileHandleReadCompletionNotification object:taskOutput]; - [taskOutput readInBackgroundAndNotify]; - break; - } - } -} - - (void)serverDataAvailable:(NSNotification *)note { NSFileHandle *taskOutput = [note object]; @@ -413,7 +376,7 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) if (data && [data length]) { - NSString *text = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; + NSString *text = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; [console appendText:text]; [text release]; [taskOutput readInBackgroundAndNotify]; //wait for more data @@ -423,105 +386,324 @@ static int numberForKey(CFDictionaryRef desc, CFStringRef key) NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self name:NSFileHandleReadCompletionNotification object:taskOutput]; close([taskOutput fileDescriptor]); - [self setServerActive:NO]; + [self killServer]; } } -- (BOOL)playFile +- (BOOL)launchGame:(NSArray *)args { + NSString *cwd = [Launcher cwd]; + NSString *exe = [[NSBundle bundleWithPath:[cwd stringByAppendingPathComponent:[@":s.app" expand]]] executablePath]; + + BOOL okay = YES; + + if([args containsObject:@"-d"]) + { + if(server != -1) return NO; // server is already running + + const char **argv = (const char**)malloc(sizeof(char*)*([args count] + 2)); //{path, , NULL}; + argv[0] = [exe fileSystemRepresentation]; + argv[[args count]+1] = NULL; + int i; + for(i = 0; i < [args count]; i++) argv[i+1] = [[args objectAtIndex:i] UTF8String]; + + int fdm; + NSString *fail = [NSLocalizedString(@"ServerAlertMesg", nil) expand]; + switch ( (server = forkpty(&fdm, NULL, NULL, NULL)) ) // forkpty so we can reliably grab SDL console + { + case -1: + [console appendLine:fail]; + [self killServer]; + okay = NO; + break; + case 0: // child + chdir([cwd fileSystemRepresentation]); + if(execv([exe fileSystemRepresentation], (char*const*)argv) == -1) fprintf(stderr, "%s\n", [fail UTF8String]); + _exit(0); + default: // parent + [multiplayer setTitle:NSLocalizedString(@"Stop", @"")]; + NSFileHandle *taskOutput = [[NSFileHandle alloc] initWithFileDescriptor:fdm]; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(serverDataAvailable:) name:NSFileHandleReadCompletionNotification object:taskOutput]; + [taskOutput readInBackgroundAndNotify]; + break; + } + free(argv); + } + else + { + NS_DURING + NSTask *task = [[NSTask alloc] init]; + [task setCurrentDirectoryPath:cwd]; + [task setLaunchPath:exe]; + [task setArguments:args]; + [task setEnvironment:[NSDictionary dictionaryWithObjectsAndKeys: + @"1", @"SDL_SINGLEDISPLAY", + @"1", @"SDL_ENABLEAPPEVENTS", nil + ]]; // makes Command-H, Command-M and Command-Q work at least when not in fullscreen + + [task launch]; + if(server == -1) [NSApp terminate:self]; //if there is a server then don't exit! + NS_HANDLER + //NSLog(@"%@", localException); + NSBeginCriticalAlertSheet( + [NSLocalizedString(@"ClientAlertTitle", @"") expand] , nil, nil, nil, + window, nil, nil, nil, nil, + [NSLocalizedString(@"ClientAlertMesg", @"") expand]); + okay = NO; + NS_ENDHANDLER + } + + return okay; +} + +/* + * nil will just launch the fps game + * "-rpg" will launch the rpg demo + * "-x.." will launch and run commands + * otherwise we are specifying a map to play + */ +- (BOOL)playFile:(id)filename { NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; NSArray *res = [[resolutions titleOfSelectedItem] componentsSeparatedByString:@" x "]; - NSMutableArray *args = [[NSMutableArray alloc] init]; - NSString *cwd = [self cwd]; + NSMutableArray *args = [NSMutableArray array]; - //suppose could use this to update gamma and keys too, but can't be bothered... - [self updateAutoexecFile:[NSDictionary dictionaryWithObjectsAndKeys: - [defs nonNullStringForKey:@"name"], @"name", - nil]]; - [args addObject:[NSString stringWithFormat:@"-w%@", [res objectAtIndex:0]]]; [args addObject:[NSString stringWithFormat:@"-h%@", [res objectAtIndex:1]]]; - [args addObject:@"-z32"]; + [args addObject:@"-z32"]; //otherwise seems to have a fondness to use -z16 which looks crap - if([defs integerForKey:@"fullscreen"] == 0) [args addObject:@"-t"]; - [args addObject:[NSString stringWithFormat:@"-a%d", [defs integerForKey:@"fsaa"]]]; - [args addObject:[NSString stringWithFormat:@"-f%d", [defs integerForKey:@"shader"]]]; + if([defs integerForKey:dkFULLSCREEN] == 0) [args addObject:@"-t"]; + [args addObject:[NSString stringWithFormat:@"-a%d", [defs integerForKey:dkFSAA]]]; + + if ([stencil state] == NSOnState) [args addObject:@"-z8"]; - NSString *adv = [defs nonNullStringForKey:@"advancedOptions"]; - if(![adv isEqual:@""]) [args addObjectsFromArray:[adv componentsSeparatedByString:@" "]]; + [args addObject:[NSString stringWithFormat:@"--home=%@", [Launcher userdir]]]; + + NSMutableArray *cmds = [NSMutableArray array]; + if(forcename) [cmds addObject:[NSString stringWithFormat:@"name \"%@\"", NSUserName()]]; - NSTask *task = [[NSTask alloc] init]; - [task setCurrentDirectoryPath:cwd]; - [task setLaunchPath:[cwd stringByAppendingPathComponent:@"actioncube.app/Contents/MacOS/actioncube"]]; - [task setArguments:args]; - [task setEnvironment:[NSDictionary dictionaryWithObjectsAndKeys: - @"1", @"SDL_SINGLEDISPLAY", - @"1", @"SDL_ENABLEAPPEVENTS", nil - ]]; // makes Command-H, Command-M and Command-Q work at least when not in fullscreen - [args release]; - - BOOL okay = YES; - - NS_DURING - [task launch]; - if(server==-1) [NSApp terminate:self]; //if there is a server then don't exit! - NS_HANDLER - //NSLog(@"%@", localException); - NSBeginCriticalAlertSheet( - @"Can't start AssaultCube", nil, nil, nil, - window, nil, nil, nil, nil, - @"Please move the directory containing AssaultCube to a path that doesn't contain weird characters or start AssaultCube manually."); - okay = NO; - NS_ENDHANDLER - - return okay; + if(filename) + { + if([filename isEqual:@"-rpg"]) { + [cmds removeAllObjects]; // rpg current doesn't require name/team + [args addObject:@"-grpg"]; //demo the rpg game + } else if([filename hasPrefix:@"-x"]) + [cmds addObject:[filename substringFromIndex:2]]; + else + [args addObject:[NSString stringWithFormat:@"-l%@", filename]]; + } + + if([cmds count] > 0) + { + NSString *script = [cmds objectAtIndex:0]; + int i; + for(i = 1; i < [cmds count]; i++) script = [NSString stringWithFormat:@"%@;%@", script, [cmds objectAtIndex:i]]; + [args addObject:[NSString stringWithFormat:@"-x%@", script]]; + } + + NSEnumerator *e = [[[defs nonNullStringForKey:dkADVANCEDOPTS] componentsSeparatedByString:@" "] objectEnumerator]; + NSString *opt; + while(opt = [e nextObject]) if([opt length] != 0) [args addObject:opt]; //skip empty ones + + return [self launchGame:args]; +} + +- (void)scanMaps:(id)obj //@note threaded! +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int i; + for(i = 0; i < 2; i++) + { + NSString *dir = (i==0) ? [Launcher cwd] : [Launcher userdir]; + NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:dir]; + NSString *file; + while(file = [enumerator nextObject]) + { + NSString *role = [fileRoles objectForKey:[file pathExtension]]; + if(role) + { + Map *map = [[Map alloc] initWithPath:[dir stringByAppendingPathComponent:file] user:(i==1) demo:[role isEqual:@"Viewer"]]; + [maps performSelectorOnMainThread:@selector(addObject:) withObject:map waitUntilDone:NO]; + } + } + } + [prog performSelectorOnMainThread:@selector(stopAnimation:) withObject:nil waitUntilDone:NO]; + [pool release]; +} + +- (void)initMaps +{ + [prog startAnimation:nil]; + [maps removeObjects:[maps arrangedObjects]]; + [NSThread detachNewThreadSelector: @selector(scanMaps:) toTarget:self withObject:nil]; } - (void)awakeFromNib { - [self initViews]; + //generate some pretty icons if they are missing + NSRect region = NSMakeRect(0, 0, 64, 64); + NSImage *image = [NSImage imageNamed:tkMAIN]; + if(!image) { + image = [[NSImage imageNamed:@"NSApplicationIcon"] copy]; + [image setSize:region.size]; + [image setName:tkMAIN]; //one less image to include + } + + [self initToolBar]; + [window setBackgroundColor:[NSColor colorWithDeviceRed:0.90 green:0.90 blue:0.90 alpha:1.0]]; //Apples 'mercury' crayon color + + //from the plist we determine that dmo->Viewer, and ogz->Editor + fileRoles = [[NSMutableDictionary dictionary] retain]; + NSEnumerator *types = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDocumentTypes"] objectEnumerator]; + NSDictionary *type; + while((type = [types nextObject])) { + NSString *role = [type objectForKey:@"CFBundleTypeRole"]; + NSEnumerator *exts = [[type objectForKey:@"CFBundleTypeExtensions"] objectEnumerator]; + NSString *ext; + while((ext = [exts nextObject])) [fileRoles setObject:role forKey:ext]; + } + NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; + NSFileManager *fm = [NSFileManager defaultManager]; + + NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; + NSString *version = [defs stringForKey:dkVERSION]; + if(!version || ![version isEqual:appVersion]) + { + NSLog(@"Upgraded Version..."); + //need to flush lurking config files - they're automatically generated, so no big deal... + NSString *dir = [Launcher userdir]; + [fm removeFileAtPath:[dir stringByAppendingPathComponent:@"init.cfg"] handler:nil]; + [fm removeFileAtPath:[dir stringByAppendingPathComponent:@"config.cfg"] handler:nil]; + } + [defs setObject:appVersion forKey:dkVERSION]; + NSDictionary *dict = [self readConfigFiles]; [keys addObjects:[self getKeys:dict]]; - NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; - if([[defs nonNullStringForKey:@"name"] isEqual:@""]) - { - NSString *name = [dict objectForKey:@"name"]; - if([name isEqual:@""] || [name isEqual:@"unnamed"]) name = NSUserName(); - [defs setValue:name forKey:@"name"]; - } - + + //encourage people not to remain unnamed + NSString *name = [dict objectForKey:@"name"]; + forcename = (!name || [name isEqual:@""] || [name isEqual:@"unnamed"]); + + [self initMaps]; [self initResolutions]; server = -1; - [window setDelegate:self]; // so can catch the window close - [NSApp setDelegate:self]; //so can catch the double-click & dropped files + [NSApp setDelegate:self]; //so can catch the double-click, dropped files, termination + [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; } --(void)windowWillClose:(NSNotification *)notification + +#pragma mark - +#pragma mark application delegate + +- (void)applicationDidFinishLaunching:(NSNotification *)note { + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *dir = [Launcher cwd]; + if(![fm fileExistsAtPath:dir]) + NSBeginCriticalAlertSheet( + [NSLocalizedString(@"InitAlertTitle", @"") expand], nil, nil, nil, + window, self, nil, nil, nil, + [NSLocalizedString(@"InitAlertMesg", @"") expand]); +} + +-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + return YES; +} + +- (void)applicationWillTerminate: (NSNotification *)note { + [self killServer]; +} + +//we register 'ogz' and 'dmo' as doc types +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { - [self setServerActive:NO]; - [NSApp terminate:self]; + NSString *role = [fileRoles objectForKey:[filename pathExtension]]; + if(!role) return NO; + BOOL demo = [role isEqual:@"Viewer"]; + filename = [filename stringByDeletingPathExtension]; //chop off extension + int i; + for(i = 0; i < 2; i++) { + NSString *pkg = (i == 0) ? [Launcher cwd] : [Launcher userdir]; + if(!demo) pkg = [pkg stringByAppendingPathComponent:@"packages"]; + if([filename hasPrefix:pkg]) + return [self playFile:(demo ? [NSString stringWithFormat:@"-xdemo \"%@\"", filename] : filename)]; + } + NSBeginCriticalAlertSheet( + [NSLocalizedString(@"FileAlertMesg", @"") expand], NSLocalizedString(@"Ok", @""), NSLocalizedString(@"Cancel", @""), nil, + window, self, @selector(openPackageFolder:returnCode:contextInfo:), nil, nil, + [NSLocalizedString(@"FileAlertMesg", @"") expand]); + return NO; } -/* - * Interface actions - */ +- (void)openPackageFolder:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + if(returnCode == 0) return; + [self openUserdir:nil]; +} + +//we register 'sauerbraten' as a url scheme +- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent +{ + NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]]; + if(!url) return; + [self playFile:[NSString stringWithFormat:@"-xconnect %@", [url host]]]; +} + +#pragma mark interface actions + - (IBAction)multiplayerAction:(id)sender { [window makeFirstResponder:window]; //ensure fields are exited and committed - [self setServerActive:(server==-1)]; + if(server != -1) + { + [self killServer]; + } + else + { + NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; + + NSMutableArray *args = [NSMutableArray arrayWithObject:@"-d"]; + + NSEnumerator *e = [[[defs nonNullStringForKey:dkSERVEROPTS] componentsSeparatedByString:@" "] objectEnumerator]; + NSString *opt; + while(opt = [e nextObject]) if([opt length] != 0) [args addObject:opt]; //skip empty ones + + NSString *desc = [defs nonNullStringForKey:dkDESCRIPTION]; + if (![desc isEqual:@""]) [args addObject:[NSString stringWithFormat:@"-n%@", desc]]; + + NSString *pass = [defs nonNullStringForKey:dkPASSWORD]; + if (![pass isEqual:@""]) [args addObject:[NSString stringWithFormat:@"-p%@", pass]]; + + int clients = [defs integerForKey:dkMAXCLIENTS]; + if (clients > 0) [args addObject:[NSString stringWithFormat:@"-c%d", clients]]; + + [args addObject:[NSString stringWithFormat:@"-q%@", [Launcher userdir]]]; + + [self launchGame:args]; + } } - (IBAction)playAction:(id)sender { [window makeFirstResponder:window]; //ensure fields are exited and committed - [self playFile]; + [self playFile:nil]; } -- (IBAction)helpAction:(id)sender -{ - NSString *file = [[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"README.html"]; - [[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:file]]; +- (IBAction)playRpg:(id)sender +{ + [self playFile:@"-rpg"]; } -@end \ No newline at end of file + +- (IBAction)playMap:(id)sender +{ + NSArray *sel = [maps selectedObjects]; + if(sel && [sel count] > 0) [self playFile:[[sel objectAtIndex:0] path]]; +} + +- (IBAction)openUserdir:(id)sender +{ + [[NSWorkspace sharedWorkspace] openFile:[Launcher userdir]]; +} + +@end diff --git a/source/xcode/actioncube-Info.plist b/source/xcode/actioncube-Info.plist index f30a0c08..0f31084d 100644 --- a/source/xcode/actioncube-Info.plist +++ b/source/xcode/actioncube-Info.plist @@ -1,11 +1,11 @@ - + CFBundleDevelopmentRegion English CFBundleExecutable - actioncube + assaultcube CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/source/xcode/actioncube.xcodeproj/project.pbxproj b/source/xcode/actioncube.xcodeproj/project.pbxproj index b14bd976..176d88bc 100644 --- a/source/xcode/actioncube.xcodeproj/project.pbxproj +++ b/source/xcode/actioncube.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ B96D0D590920970C00B6C936 /* Launcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B96D0D580920970C00B6C936 /* Launcher.m */; }; B96D0D630920971300B6C936 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = B96D0D5A0920971300B6C936 /* MainMenu.nib */; }; B96D0D6D0920976E00B6C936 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B96D0D6C0920976E00B6C936 /* main.m */; }; + B975E7F40EBCC3A200880D5D /* map.icns in Resources */ = {isa = PBXBuildFile; fileRef = B975E7F30EBCC3A200880D5D /* map.icns */; }; B98944640EA68F70003CC623 /* shadow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B98944630EA68F70003CC623 /* shadow.cpp */; }; B9FA3B420DD9E6C900581F8D /* worldrender.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B9FA3B090DD9E6C900581F8D /* worldrender.cpp */; }; B9FA3B430DD9E6C900581F8D /* editing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B9FA3B0A0DD9E6C900581F8D /* editing.cpp */; }; @@ -154,12 +155,13 @@ B2F67ED704C74A3F00A80002 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; B92DC0F909D08CF9008219BD /* SDL_image.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL_image.framework; path = /Library/Frameworks/SDL_image.framework; sourceTree = ""; }; B92DC0FB09D08CF9008219BD /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = /Library/Frameworks/SDL.framework; sourceTree = ""; }; - B94198CB09207E810029DAD1 /* actioncube.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = actioncube.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B94198CB09207E810029DAD1 /* assaultcube.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = assaultcube.app; sourceTree = BUILT_PRODUCTS_DIR; }; B96D0D4C092096F200B6C936 /* launcher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = launcher.app; sourceTree = BUILT_PRODUCTS_DIR; }; B96D0D570920970C00B6C936 /* Launcher.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Launcher.h; sourceTree = ""; }; B96D0D580920970C00B6C936 /* Launcher.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Launcher.m; sourceTree = ""; }; B96D0D5B0920971300B6C936 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; B96D0D6C0920976E00B6C936 /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + B975E7F30EBCC3A200880D5D /* map.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = map.icns; sourceTree = ""; }; B98944630EA68F70003CC623 /* shadow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shadow.cpp; sourceTree = ""; }; B9CACA12092099DF00A13F04 /* launcher-Info.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; path = "launcher-Info.plist"; sourceTree = ""; }; B9FA3B090DD9E6C900581F8D /* worldrender.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = worldrender.cpp; sourceTree = ""; }; @@ -292,7 +294,7 @@ 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( - B94198CB09207E810029DAD1 /* actioncube.app */, + B94198CB09207E810029DAD1 /* assaultcube.app */, B96D0D4C092096F200B6C936 /* launcher.app */, ); name = Products; @@ -327,6 +329,7 @@ B96D0D5A0920971300B6C936 /* MainMenu.nib */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 665CDD4A0AFE7FBD0030D605 /* actioncube.icns */, + B975E7F30EBCC3A200880D5D /* map.icns */, 66899EA80C14887F00927C34 /* Help.tiff */, 66899EA90C14887F00927C34 /* Keys.gif */, 66899EAA0C14887F00927C34 /* Main.gif */, @@ -485,9 +488,9 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - B941988A09207E810029DAD1 /* actioncube */ = { + B941988A09207E810029DAD1 /* assaultcube */ = { isa = PBXNativeTarget; - buildConfigurationList = B94198C609207E810029DAD1 /* Build configuration list for PBXNativeTarget "actioncube" */; + buildConfigurationList = B94198C609207E810029DAD1 /* Build configuration list for PBXNativeTarget "assaultcube" */; buildPhases = ( B941988B09207E810029DAD1 /* Headers */, B94198A409207E810029DAD1 /* Resources */, @@ -500,10 +503,10 @@ dependencies = ( B96D0D790920980E00B6C936 /* PBXTargetDependency */, ); - name = actioncube; + name = assaultcube; productInstallPath = "$(HOME)/Applications"; productName = sauerbraten; - productReference = B94198CB09207E810029DAD1 /* actioncube.app */; + productReference = B94198CB09207E810029DAD1 /* assaultcube.app */; productType = "com.apple.product-type.application"; }; B96D0D4B092096F200B6C936 /* launcher */ = { @@ -533,9 +536,9 @@ hasScannedForEncodings = 1; mainGroup = 29B97314FDCFA39411CA2CEA /* sauerbraten */; projectDirPath = ""; - projectRoot = ..; + projectRoot = ""; targets = ( - B941988A09207E810029DAD1 /* actioncube */, + B941988A09207E810029DAD1 /* assaultcube */, B96D0D4B092096F200B6C936 /* launcher */, ); }; @@ -562,6 +565,7 @@ 66899EB90C14898500927C34 /* Server.gif in Resources */, B90357B509D09B9D002C9DC7 /* InfoPlist.strings in Resources */, 665CDFD20AFE90F30030D605 /* actioncube.icns in Resources */, + B975E7F40EBCC3A200880D5D /* map.icns in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -579,7 +583,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "echo $TARGET_BUILD_DIR\ncd $TARGET_BUILD_DIR\n\n#make the directory which our disk image will be made of\nmkdir AssaultCube\nmkdir AssaultCube/assaultcube\n\n#copy sauerbraten there\n/Developer/Tools/CpMac -r $TARGET_BUILD_DIR/actioncube.app AssaultCube/assaultcube\n/Developer/Tools/CpMac -r $TARGET_BUILD_DIR/launcher.app AssaultCube/\nmv AssaultCube/launcher.app AssaultCube/AssaultCube.app\nstrip -u -r $TARGET_BUILD_DIR/AssaultCube/assaultcube/actioncube.app/Contents/MacOS/actioncube -o $TARGET_BUILD_DIR/AssaultCube/assaultcube/actioncube.app/Contents/MacOS/actioncube_stripped\nmv $TARGET_BUILD_DIR/AssaultCube/assaultcube/actioncube.app/Contents/MacOS/actioncube $TARGET_BUILD_DIR/actioncube_nonstripped\nmv $TARGET_BUILD_DIR/AssaultCube/assaultcube/actioncube.app/Contents/MacOS/actioncube_stripped $TARGET_BUILD_DIR/AssaultCube/assaultcube/actioncube.app/Contents/MacOS/actioncube\ncp $SRCROOT/../../README.html $TARGET_BUILD_DIR/AssaultCube/\n\n#install all frameworks into app bundle\nmkdir AssaultCube/assaultcube/actioncube.app/Contents/Frameworks\nfor framework in SDL.framework SDL_image.framework Ogg.framework Vorbis.framework\ndo\n\tif [ -d $LOCAL_LIBRARY_DIR/Frameworks/$framework/ ] \n\tthen\t\n\t\t/Developer/Tools/CpMac -r $LOCAL_LIBRARY_DIR/Frameworks/$framework/ AssaultCube/assaultcube/actioncube.app/Contents/Frameworks\n\t\trm -rf AssaultCube/assaultcube/actioncube.app/Contents/Frameworks/$framework/Versions/A/Headers\n\telse\n\t\texit 1\n\tfi\ndone\n\n#copy readme and data and remove unneccesary stuff\n/Developer/Tools/CpMac -r $SRCROOT/../../docs AssaultCube/\n/Developer/Tools/CpMac -r $SRCROOT/../../bot AssaultCube/assaultcube/\n/Developer/Tools/CpMac -r $SRCROOT/../../config AssaultCube/assaultcube/\n/Developer/Tools/CpMac -r $SRCROOT/../../packages AssaultCube/assaultcube/\nrm -rf AssaultCube/docs/CVS/\nrm -rf AssaultCube/docs/*/CVS/\nrm -rf AssaultCube/docs/*/*/CVS/\nrm -f AssaultCube/docs/.DS_Store\nrm -f AssaultCube/docs/*/.DS_Store\nrm -f AssaultCube/docs/*/*/.DS_Store\nrm -rf AssaultCube/assaultcube/packages/CVS/\nrm -rf AssaultCube/assaultcube/packages/*/CVS/\nrm -rf AssaultCube/assaultcube/packages/*/*/CVS/\nrm -f AssaultCube/assaultcube/packages/.DS_Store\nrm -f AssaultCube/assaultcube/packages/*/.DS_Store\nrm -f AssaultCube/assaultcube/packages/*/*/.DS_Store\nrm -rf AssaultCube/assaultcube/config/CVS/\nrm -f AssaultCube/assaultcube/config/.DS_Store\nrm -rf AssaultCube/assaultcube/bot/CVS/\nrm -rf AssaultCube/assaultcube/bot/*/CVS/\nrm -f AssaultCube/assaultcube/bot/.DS_Store\nrm -f AssaultCube/assaultcube/bot/*/.DS_Store\nrm -rf AssaultCube/assaultcube/data/CVS/\nrm -rf AssaultCube/assaultcube/data/*/CVS/\nrm -f AssaultCube/assaultcube/data/.DS_Store\nrm -f AssaultCube/assaultcube/data/*/.DS_Store\n\n#finally make a disk image out of the stuff\nhdiutil create -srcfolder AssaultCube AssaultCube.dmg\nhdiutil internet-enable -yes AssaultCube.dmg\n\n#cleanup\nrm -r AssaultCube"; + shellScript = "echo $TARGET_BUILD_DIR\ncd $TARGET_BUILD_DIR\n\n#ensure old package is gone\nrm -f assaultcube.dmg\n\n#make the directory which our disk image will be made of\n#use /tmp as a destination because copying ourself (xcode folder) whilst compling causes the dog to chase its tail\nACPKG=`mktemp -d /tmp/acpkg.XXXXXX` || exit 1\n\n#leave indicator of where temp directory is in case things break\nln -sf $ACPKG $TARGET_BUILD_DIR/acpkg\n\n#copy launcher\n/Developer/Tools/CpMac -r $TARGET_BUILD_DIR/launcher.app $ACPKG/AssaultCube.app\n\nHELPDIR=$ACPKG/AssaultCube.app/Contents/Resources/help/\nGAMEDIR=$ACPKG/AssaultCube.app/Contents/gamedata\nmkdir $GAMEDIR\nmkdir $HELPDIR\n\n#copy assaultcube\n/Developer/Tools/CpMac -r $TARGET_BUILD_DIR/assaultcube.app $GAMEDIR/\nstrip -u -r $GAMEDIR/assaultcube.app/Contents/MacOS/assaultcube\n\n#install all frameworks into app bundle\nmkdir $GAMEDIR/assaultcube.app/Contents/Frameworks\nfor framework in SDL.framework SDL_image.framework Ogg.framework Vorbis.framework\ndo\n\tif [ -d $LOCAL_LIBRARY_DIR/Frameworks/$framework/ ] \n\tthen\t\n\t\t/Developer/Tools/CpMac -r $LOCAL_LIBRARY_DIR/Frameworks/$framework/ $GAMEDIR/assaultcube.app/Contents/Frameworks\n\t\trm -rf $GAMEDIR/assaultcube.app/Contents/Frameworks/$framework/Versions/A/Headers\n\telse\n\t\texit 1\n\tfi\ndone\n\n\n#copy readme and data and remove unneccesary stuff\n/Developer/Tools/CpMac -r $SRCROOT/../../README.html $HELPDIR/\n/Developer/Tools/CpMac -r $SRCROOT/../../docs $HELPDIR/\n/Developer/Tools/CpMac -r $SRCROOT/../../bot $GAMEDIR/\n/Developer/Tools/CpMac -r $SRCROOT/../../config $GAMEDIR/\n/Developer/Tools/CpMac -r $SRCROOT/../../screenshots $GAMEDIR/\n/Developer/Tools/CpMac -r $SRCROOT/../../mods $GAMEDIR/\n/Developer/Tools/CpMac -r $SRCROOT/../../packages $GAMEDIR/\n/Developer/Tools/CpMac -r $SRCROOT/../../source $ACPKG/sourcecode\nfind -d $ACPKG -name \"CVS\" -exec rm -rf {} \\;\nfind $ACPKG -name \".DS_Store\" -exec rm -f {} \\;\nrm -rf $ACPKG/sourcecode/xcode/build\n\n#add alias to readme\n/usr/bin/osascript < - + + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + dmo + + CFBundleTypeIconFile + map.icns + CFBundleTypeName + AssaultCube demo + CFBundleTypeRole + Viewer + LSTypeIsPackage + + NSPersistentStoreTypeKey + Binary + + + CFBundleTypeExtensions + + cgz + + CFBundleTypeIconFile + map.icns + CFBundleTypeName + AssaultCube map + CFBundleTypeRole + Editor + LSTypeIsPackage + + NSPersistentStoreTypeKey + Binary + + CFBundleDevelopmentRegion English CFBundleExecutable diff --git a/source/xcode/macutils.mm b/source/xcode/macutils.mm index 75a10b0d..d8b81a8f 100644 --- a/source/xcode/macutils.mm +++ b/source/xcode/macutils.mm @@ -1,8 +1,8 @@ #import -#define _MAXDEFSTR 260 +#define MAXSTRLEN 260 inline char *s_strncpy(char *d, const char *s, size_t m) { strncpy(d,s,m); d[m-1] = 0; return d; }; -inline char *s_strcat(char *d, const char *s) { size_t n = strlen(d); return s_strncpy(d+n,s,_MAXDEFSTR-n); }; +inline char *s_strcat(char *d, const char *s) { size_t n = strlen(d); return s_strncpy(d+n,s,MAXSTRLEN-n); }; void mac_pasteconsole(char *commandbuf) { @@ -13,4 +13,16 @@ void mac_pasteconsole(char *commandbuf) if (contents != nil) s_strcat(commandbuf, [contents lossyCString]); } -} \ No newline at end of file +} + +/* + * 0x1030 = 10.3 + * 0x1040 = 10.4 + * 0x1050 = 10.5 + */ +int mac_osversion() +{ + SInt32 MacVersion; + Gestalt(gestaltSystemVersion, &MacVersion); + return MacVersion; +} diff --git a/source/xcode/map.icns b/source/xcode/map.icns new file mode 100644 index 00000000..e6cf72c4 Binary files /dev/null and b/source/xcode/map.icns differ