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