1// 2// Copyright(C) 2005-2014 Simon Howard 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 15#include <stdlib.h> 16#include <string.h> 17#include <AppKit/AppKit.h> 18 19#include "config.h" 20#include "IWADController.h" 21 22typedef enum 23{ 24 IWAD_DOOM1, 25 IWAD_DOOM2, 26 IWAD_TNT, 27 IWAD_PLUTONIA, 28 IWAD_CHEX, 29 IWAD_HERETIC, 30 IWAD_HEXEN, 31 IWAD_STRIFE, 32 IWAD_FREEDOOM1, 33 IWAD_FREEDOOM2, 34 IWAD_FREEDM, 35 NUM_IWAD_TYPES 36} IWAD; 37 38static NSString *IWADLabels[NUM_IWAD_TYPES] = 39{ 40 @"Doom", 41 @"Doom II: Hell on Earth", 42 @"Final Doom: TNT: Evilution", 43 @"Final Doom: Plutonia Experiment", 44 @"Chex Quest", 45 @"Heretic", 46 @"Hexen", 47 @"Strife", 48 @"Freedoom: Phase 1", 49 @"Freedoom: Phase 2", 50 @"FreeDM", 51}; 52 53static NSString *IWADFilenames[NUM_IWAD_TYPES + 1] = 54{ 55 @"doom.wad", 56 @"doom2.wad", 57 @"tnt.wad", 58 @"plutonia.wad", 59 @"chex.wad", 60 @"heretic.wad", 61 @"hexen.wad", 62 @"strife.wad", 63 @"freedoom1.wad", 64 @"freedoom2.wad", 65 @"freedm.wad", 66 @"undefined" 67}; 68 69@implementation IWADController 70 71- (void) getIWADList: (NSPathControl **) iwadList 72{ 73 iwadList[IWAD_DOOM1] = self->doom1; 74 iwadList[IWAD_DOOM2] = self->doom2; 75 iwadList[IWAD_TNT] = self->tnt; 76 iwadList[IWAD_PLUTONIA] = self->plutonia; 77 iwadList[IWAD_CHEX] = self->chex; 78 iwadList[IWAD_HERETIC] = self->heretic; 79 iwadList[IWAD_HEXEN] = self->hexen; 80 iwadList[IWAD_STRIFE] = self->strife; 81 iwadList[IWAD_FREEDOOM1] = self->freedoom1; 82 iwadList[IWAD_FREEDOOM2] = self->freedoom2; 83 iwadList[IWAD_FREEDM] = self->freedm; 84} 85 86- (IWAD) getSelectedIWAD 87{ 88 unsigned int i; 89 90 for (i=0; i<NUM_IWAD_TYPES; ++i) 91 { 92 if ([self->iwadSelector titleOfSelectedItem] == IWADLabels[i]) 93 { 94 return i; 95 } 96 } 97 98 return NUM_IWAD_TYPES; 99} 100 101// Get the location of the selected IWAD. 102 103- (NSString *) getIWADLocation 104{ 105 IWAD selectedIWAD; 106 NSPathControl *iwadList[NUM_IWAD_TYPES]; 107 108 selectedIWAD = [self getSelectedIWAD]; 109 110 if (selectedIWAD == NUM_IWAD_TYPES) 111 { 112 return nil; 113 } 114 else 115 { 116 [self getIWADList: iwadList]; 117 118 return [[iwadList[selectedIWAD] URL] path]; 119 } 120} 121 122static const char *NameForIWAD(IWAD iwad) 123{ 124 switch (iwad) 125 { 126 case IWAD_HERETIC: 127 return "heretic"; 128 129 case IWAD_HEXEN: 130 return "hexen"; 131 132 case IWAD_STRIFE: 133 return "strife"; 134 135 default: 136 return "doom"; 137 } 138} 139 140// Get the name used for the executable for the selected IWAD. 141 142- (const char *) getGameName 143{ 144 return NameForIWAD([self getSelectedIWAD]); 145} 146 147- (void) setIWADConfig 148{ 149 NSPathControl *iwadList[NUM_IWAD_TYPES]; 150 NSUserDefaults *defaults; 151 NSString *key; 152 NSString *value; 153 unsigned int i; 154 155 [self getIWADList: iwadList]; 156 157 // Load IWAD filename paths 158 159 defaults = [NSUserDefaults standardUserDefaults]; 160 161 for (i=0; i<NUM_IWAD_TYPES; ++i) 162 { 163 key = IWADFilenames[i]; 164 value = [defaults stringForKey:key]; 165 166 if (value != nil) 167 { 168 [iwadList[i] setURL: [NSURL fileURLWithPath: value]]; 169 } 170 } 171} 172 173// On startup, set the selected item in the IWAD dropdown 174 175- (void) setDropdownSelection 176{ 177 NSUserDefaults *defaults; 178 NSString *selected; 179 unsigned int i; 180 181 defaults = [NSUserDefaults standardUserDefaults]; 182 selected = [defaults stringForKey: @"selected_iwad"]; 183 184 if (selected == nil) 185 { 186 return; 187 } 188 189 // Find this IWAD in the filenames list, and select it. 190 191 for (i=0; i<NUM_IWAD_TYPES; ++i) 192 { 193 if ([selected isEqualToString:IWADFilenames[i]]) 194 { 195 [self->iwadSelector selectItemWithTitle:IWADLabels[i]]; 196 break; 197 } 198 } 199} 200 201// Set the dropdown list to include an entry for each IWAD that has 202// been configured. Returns true if at least one IWAD is configured. 203 204- (BOOL) setDropdownList 205{ 206 NSPathControl *iwadList[NUM_IWAD_TYPES]; 207 BOOL have_wads; 208 id location; 209 unsigned int i; 210 unsigned int enabled_wads; 211 212 // Build the new list. 213 214 [self getIWADList: iwadList]; 215 [self->iwadSelector removeAllItems]; 216 217 enabled_wads = 0; 218 219 for (i=0; i<NUM_IWAD_TYPES; ++i) 220 { 221 location = [[iwadList[i] URL] path]; 222 223 if (location != nil && [location length] > 0) 224 { 225 [self->iwadSelector addItemWithTitle: IWADLabels[i]]; 226 ++enabled_wads; 227 } 228 } 229 230 // Enable/disable the dropdown depending on whether there 231 // were any configured IWADs. 232 233 have_wads = enabled_wads > 0; 234 [self->iwadSelector setEnabled: have_wads]; 235 236 // Restore the old selection. 237 238 [self setDropdownSelection]; 239 240 return have_wads; 241} 242 243- (void) saveConfig 244{ 245 NSPathControl *iwadList[NUM_IWAD_TYPES]; 246 IWAD selectedIWAD; 247 NSUserDefaults *defaults; 248 NSString *key; 249 NSString *value; 250 unsigned int i; 251 252 [self getIWADList: iwadList]; 253 254 // Store all IWAD locations to user defaults. 255 256 defaults = [NSUserDefaults standardUserDefaults]; 257 258 for (i=0; i<NUM_IWAD_TYPES; ++i) 259 { 260 key = IWADFilenames[i]; 261 value = [[iwadList[i] URL] path]; 262 263 [defaults setObject:value forKey:key]; 264 } 265 266 // Save currently selected IWAD. 267 268 selectedIWAD = [self getSelectedIWAD]; 269 [defaults setObject:IWADFilenames[selectedIWAD] 270 forKey:@"selected_iwad"]; 271} 272 273// Callback method invoked when the configuration button in the main 274// window is clicked. 275 276- (void) openConfigWindow: (id)sender 277{ 278 if (![self->configWindow isVisible]) 279 { 280 [self->configWindow makeKeyAndOrderFront: sender]; 281 } 282} 283 284// Callback method invoked when the close button is clicked. 285 286- (void) closeConfigWindow: (id)sender 287{ 288 [self->configWindow orderOut: sender]; 289 [self saveConfig]; 290 [self setDropdownList]; 291} 292 293- (void) awakeFromNib 294{ 295 [self->configWindow center]; 296 297 // Set configuration for all IWADs from configuration file. 298 299 [self setIWADConfig]; 300 301 // Populate the dropdown IWAD list. 302 303 if ([self setDropdownList]) 304 { 305 [self setDropdownSelection]; 306 } 307} 308 309// Generate a value to set for the DOOMWADPATH environment variable 310// that contains each of the configured IWAD files. 311 312- (char *) doomWadPath 313{ 314 NSPathControl *iwadList[NUM_IWAD_TYPES]; 315 NSString *location; 316 unsigned int i; 317 BOOL first; 318 char *env; 319 size_t env_len; 320 321 [self getIWADList: iwadList]; 322 323 // Calculate length of environment string. 324 325 env_len = 0; 326 327 for (i=0; i<NUM_IWAD_TYPES; ++i) 328 { 329 location = [[iwadList[i] URL] path]; 330 331 if (location != nil && [location length] > 0) 332 { 333 env_len += [location length] + 1; 334 } 335 } 336 337 // Build string. 338 339 env = malloc(env_len); 340 strlcpy(env, "", env_len); 341 342 first = YES; 343 344 for (i=0; i<NUM_IWAD_TYPES; ++i) 345 { 346 location = [[iwadList[i] URL] path]; 347 348 if (location != nil && [location length] > 0) 349 { 350 if (!first) 351 { 352 strlcat(env, ":", env_len); 353 } 354 355 strlcat(env, [location UTF8String], env_len); 356 first = NO; 357 } 358 } 359 360 return env; 361} 362 363- (NSString *) autoloadPath 364{ 365 NSArray *array = NSSearchPathForDirectoriesInDomains( 366 NSApplicationSupportDirectory, NSUserDomainMask, YES); 367 if ([array count] == 0) 368 { 369 return nil; 370 } 371 372 IWAD selectedIWAD = [self getSelectedIWAD]; 373 if (selectedIWAD == NUM_IWAD_TYPES) 374 { 375 return nil; 376 } 377 378 NSString *base = [array objectAtIndex:0]; 379 return [NSString pathWithComponents: 380 [NSArray arrayWithObjects: base, @PACKAGE_TARNAME, @"autoload", 381 IWADFilenames[selectedIWAD], nil]]; 382} 383 384// Set the DOOMWADPATH environment variable to contain the path to each 385// of the configured IWAD files. 386 387- (void) setEnvironment 388{ 389 char *doomwadpath; 390 char *env; 391 392 // Get the value for the path. 393 394 doomwadpath = [self doomWadPath]; 395 396 asprintf(&env, "DOOMWADPATH=%s", doomwadpath); 397 398 free(doomwadpath); 399 400 // Load into environment: 401 402 putenv(env); 403 404 //free(env); 405} 406 407// Examine a path to a WAD and determine whether it is an IWAD file. 408// If so, it is added to the IWAD configuration, and true is returned. 409 410- (BOOL) addIWADPath: (NSString *) path 411{ 412 NSPathControl *iwadList[NUM_IWAD_TYPES]; 413 NSArray *pathComponents; 414 NSString *filename; 415 unsigned int i; 416 417 [self getIWADList: iwadList]; 418 419 // Find an IWAD file that matches the filename in the path that we 420 // have been given. 421 422 pathComponents = [path pathComponents]; 423 filename = [pathComponents objectAtIndex: [pathComponents count] - 1]; 424 425 for (i = 0; i < NUM_IWAD_TYPES; ++i) 426 { 427 if ([filename caseInsensitiveCompare: IWADFilenames[i]] == 0) 428 { 429 // Configure this IWAD. 430 431 [iwadList[i] setURL: [NSURL fileURLWithPath: path]]; 432 433 // Rebuild dropdown list and select the new IWAD. 434 435 [self setDropdownList]; 436 [self->iwadSelector selectItemWithTitle:IWADLabels[i]]; 437 return YES; 438 } 439 } 440 441 // No IWAD found with this name. 442 443 return NO; 444} 445 446- (BOOL) selectGameByName: (const char *) name 447{ 448 NSPathControl *iwadList[NUM_IWAD_TYPES]; 449 NSString *location; 450 const char *name2; 451 int i; 452 453 // Already selected an IWAD of the desired type? Just return 454 // success. 455 if (!strcmp(name, [self getGameName])) 456 { 457 return YES; 458 } 459 460 // Search through the configured IWADs and try to select the 461 // desired game. 462 [self getIWADList: iwadList]; 463 464 for (i = 0; i < NUM_IWAD_TYPES; ++i) 465 { 466 location = [[iwadList[i] URL] path]; 467 name2 = NameForIWAD(i); 468 469 if (!strcmp(name, name2) 470 && location != nil && [location length] > 0) 471 { 472 [self->iwadSelector selectItemWithTitle:IWADLabels[i]]; 473 return YES; 474 } 475 } 476 477 // User hasn't configured any WAD(s) for the desired game type. 478 return NO; 479} 480 481@end 482 483