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