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!
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 @@
|
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
|