1/* GemRB - Infinity Engine Emulator 2 * Copyright (C) 2011 The GemRB Project 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 * 18 * 19 */ 20 21#import "CocoaWrapper.h" 22 23#import "AppleLogger.h" 24#import "Interface.h" 25#import "System/FileStream.h" 26 27using namespace GemRB; 28 29/* The main class of the application, the application's delegate */ 30@implementation CocoaWrapper 31@synthesize configWindow=_configWindow; 32 33- (id)init 34{ 35 self = [super init]; 36 if (self) { 37 _configWindow = nil; 38 _showConfigWindow = NO; 39 } 40 return self; 41} 42 43- (BOOL)application:(NSApplication *) __unused theApplication openFile:(NSString *) filename 44{ 45 NSFileManager* fm = [NSFileManager defaultManager]; 46 // only open if passed a directory 47 BOOL isDir = NO; 48 if ([fm fileExistsAtPath:filename isDirectory:&isDir] && isDir) { 49 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 50 [defaults setObject:filename forKey:@"GamePath"]; 51 52 if (_showConfigWindow == NO) { 53 // opened via means other than config window 54 [self launchGame:self]; 55 } 56 57 return YES; 58 } 59 return NO; 60} 61 62- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *) __unused sender 63{ 64 //override this method using plugin categories. 65 NSLog(@"Application preparing for termination..."); 66 return NSTerminateNow; 67} 68 69- (void)applicationWillTerminate:(NSNotification *) __unused aNotification 70{ 71 72} 73 74// always called before openFile when launched via CLI/DragDrop 75- (void)applicationWillFinishLaunching:(NSNotification *) __unused aNotification 76{ 77 AddLogWriter(Logger::WriterPtr(new AppleLogger())); 78 79 // Load default defaults 80 NSString* defaultsPath = [[NSBundle mainBundle] pathForResource:@"defaults" ofType:@"plist"]; 81 NSMutableDictionary* defaultDict = [NSMutableDictionary dictionaryWithContentsOfFile:defaultsPath]; 82 83 NSString* path = [[NSBundle mainBundle].resourcePath stringByAppendingFormat:@"/GUIScripts"]; 84 NSFileManager* fm = [NSFileManager defaultManager]; 85 NSError* error = nil; 86 NSArray* items = [fm contentsOfDirectoryAtPath:path error:&error]; 87 88 NSMutableArray* gameTypes = [NSMutableArray arrayWithObject:@"auto"]; 89 for (NSString* subPath in items) { 90 BOOL isDir = NO; 91 if ([fm fileExistsAtPath:[NSString stringWithFormat:@"%@/%@", path, subPath] isDirectory:&isDir] && isDir) { 92 [gameTypes addObject:[subPath lastPathComponent]]; 93 } 94 } 95 [defaultDict setValue:gameTypes forKey:@"gameTypes"]; 96 97 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 98 [defaults registerDefaults:defaultDict]; 99 100 if (![defaults stringForKey:@"CachePath"]) { 101 NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, NO); 102 NSString* cachePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"gemrb"]; 103 [defaults setValue:cachePath forKey:@"CachePath"]; 104 } 105 106 NSMutableDictionary* additionalPaths = [[defaults dictionaryForKey:@"AdditionalPaths"] mutableCopy]; 107 if ([additionalPaths valueForKey:@"CustomFontPath"] == nil) { 108 NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, NO); 109 NSString* fontPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Fonts"]; 110 [additionalPaths setValue:fontPath forKey:@"CustomFontPath"]; 111 } 112 if ([additionalPaths valueForKey:@"SavePath"] == nil) { 113 [additionalPaths setValue:[defaults valueForKey:@"GamePath"] forKey:@"SavePath"]; 114 } 115 [defaults setObject:additionalPaths forKey:@"AdditionalPaths"]; 116 [additionalPaths release]; 117} 118 119/* Called when the internal event loop has just started running */ 120- (void) applicationDidFinishLaunching: (NSNotification *) __unused aNotification 121{ 122 // we configure this here so that when GemRB is launched though means such as Drag/Drop we dont show the config window 123 124 _showConfigWindow = YES; //still need to set this to YES in case an error occurs 125 [[NSBundle mainBundle] loadNibNamed:@"GemRB" owner:self topLevelObjects:nil]; 126 127 if (core == NULL) { 128 [_configWindow makeKeyAndOrderFront:nil]; 129 } 130} 131 132- (IBAction)openGame:(id) __unused sender 133{ 134 // be careful to use only methods available in 10.5! 135 NSOpenPanel* op = [NSOpenPanel openPanel]; 136 [op setCanChooseDirectories:YES]; 137 [op setCanChooseFiles:NO]; 138 [op setAllowsMultipleSelection:NO]; 139 [op setMessage:@"Select a folder containing an IE game (has a chitin.key)."]; 140 [op setPrompt:@"Select Game"]; 141 if ([op runModal] == NSModalResponseOK) { //blocks till user selection 142 [self application:NSApp openFile:op.URL.path]; 143 } 144} 145 146- (IBAction)launchGame:(id) sender 147{ 148 if (core) { 149 Log(FATAL, "Launch Game", "GemRB game is currently running. Please close it before trying to open another."); 150 return; 151 } 152 if (sender) { 153 // Note: use NSRunLoop over NSObject performSelector! 154 NSArray* modes = [NSArray arrayWithObject:NSDefaultRunLoopMode]; 155 [[NSRunLoop mainRunLoop] performSelector:@selector(launchGame:) target:self argument:nil order:0 modes:modes]; 156 return; 157 } 158 159 ToggleLogging(true); 160 161 core = new Interface(); 162 InterfaceConfig* config = new InterfaceConfig(0, NULL); 163 164 // load NSUserDefaults into config 165 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 166 NSDictionary* userValues = [defaults persistentDomainForName:@"net.sourceforge.gemrb"]; 167 NSDictionary* defaultValues = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"defaults" ofType:@"plist"]]; 168 169 NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:defaultValues]; 170 [dict addEntriesFromDictionary:userValues]; 171 172 for ( NSString* key in dict.allKeys ) { 173 id obj = [dict objectForKey:key]; 174 if ( [obj isKindOfClass:[NSDictionary class]] ) { 175 // move it to the root level 176 [dict addEntriesFromDictionary:obj]; 177 [dict removeObjectForKey:key]; 178 } 179 } 180 181 for ( NSString* key in dict ) { 182 NSString* value = nil; 183 id obj = [dict objectForKey:key]; 184 if ([obj isKindOfClass:[NSNumber class]]) { 185 value = [(NSNumber*)obj stringValue]; 186 } else if ([obj isKindOfClass:[NSString class]]) { 187 value = (NSString*)obj; 188 } 189 if (value && ![value isEqualToString:@""]) { 190 config->SetKeyValuePair([key cStringUsingEncoding:NSASCIIStringEncoding], 191 [value cStringUsingEncoding:NSASCIIStringEncoding]); 192 } 193 } 194 195 int status; 196 if ((status = core->Init(config)) == GEM_ERROR) { 197 delete config; 198 delete( core ); 199 core = NULL; 200 Log(MESSAGE, "Cocoa Wrapper", "Unable to initialize core. Terminating."); 201 } else { 202 [_configWindow close]; 203 // pass control to GemRB 204 delete config; 205 core->Main(); 206 delete( core ); 207 core = NULL; 208 209 if ([defaults boolForKey:@"TerminateOnClose"]) { 210 [NSApp terminate:self]; 211 } 212 } 213} 214 215- (id)validRequestorForSendType:(NSString *) __unused sendType returnType:(NSString *) __unused returnType 216{ 217 return nil; 218} 219 220- (BOOL)respondsToSelector:(SEL)aSelector 221{ 222 if ([NSApp respondsToSelector:aSelector]) { 223 return YES; 224 } 225 return [super respondsToSelector:aSelector]; 226} 227 228- (NSMethodSignature*) methodSignatureForSelector:(SEL)selector 229{ 230 NSMethodSignature* signature = [super methodSignatureForSelector:selector]; 231 232 if (!signature) 233 signature = [NSApp methodSignatureForSelector:selector]; 234 235 return signature; 236} 237 238- (void)forwardInvocation:(NSInvocation *)invocation 239{ 240 SEL selector = [invocation selector]; 241 242 if ([NSApp respondsToSelector:selector]) 243 { 244 [invocation invokeWithTarget:NSApp]; 245 } else { 246 [super forwardInvocation:invocation]; 247 } 248} 249 250@end 251 252/* Main entry point to executable - should *not* be GemRB_main! */ 253int main (int __unused argc, char ** __unused argv) 254{ 255 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 256 257 /* Ensure the application object is initialized */ 258 NSApplication* app = [NSApplication sharedApplication]; 259 260 /* Set up the menubar */ 261 [NSApp setMainMenu:[[NSMenu alloc] init]]; 262 263 CocoaWrapper* wrapper = [[CocoaWrapper alloc] init]; 264 [app setDelegate:wrapper]; 265 266 /* Start the main event loop */ 267 [pool drain]; 268 [NSApp run]; 269 270 [wrapper release]; 271 [pool release]; 272 273 return 0; 274} 275