1 /*
2    Copyright (C) 2000/2001/2002/2003 Kai Sterker <kai.sterker@gmail.com>
3    Part of the Adonthell Project <http://adonthell.nongnu.org>
4 
5    Adonthell is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    Adonthell is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with Adonthell.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 /**
20  * @file prefs.cc
21  *
22  * @author Kai Sterker
23  * @brief Adonthell's configuration
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 #include <getopt.h>
30 #include <iostream>
31 #include <dirent.h>
32 #include "prefs.h"
33 #include "python_class.h"
34 #include "game.h"
35 
config()36 config::config ()
37 {
38     // set some default values where possible
39     screen_mode = 0;                // Letterbox
40     display = 0;					// Display for fullscreen mode
41     quick_load = 0;                 // Quick-load disabled
42     audio_channels = 1;             // Stereo
43     audio_resolution = 1;           // 16 bit
44     audio_sample_rate = 2;          // 11025, 22050 or 44100 Hz
45     audio_volume = 35;              // 0 - 100%
46     language = "";                  // Let the user's environment decide
47     font = "";						// use default font
48 
49     // set OS specific directory containing the adonthellrc file
50     adonthellrc = game::get_system_dir(CONFIG);
51 }
52 
53 /**
54  * Displays the help message - for internal use only.
55  *
56  */
print_help_message(char * argv[])57 void print_help_message (char * argv[])
58 {
59     cout << "Usage: " << argv[0] << " [OPTIONS] GAME" << endl;
60     cout << endl;
61     cout << "Where [OPTIONS] can be:\n";
62     cout << "-h         print this help message" << endl;
63     cout << "-d         print the data directory and exit" << endl;
64     cout << "-v         print version and exit" << endl;
65     cout << "-l         list installed games and exit" << endl;
66     cout << "-g dir     play the game contained in dir (for development only)" << endl;
67     cout << "-c         byte-compile all Python scripts in this directory (for " << endl;
68     cout << "           development only)" << endl;
69 }
70 
71 /**
72  * Displays the available games - for internal use only.
73  *
74  */
print_available_games()75 void print_available_games ()
76 {
77     struct dirent * d;
78     DIR * mydir = opendir ((game::global_data_dir() + "/games").c_str());
79     bool nogames = true;
80 
81     if (!mydir)
82     {
83         cerr << "Cannot open directory " << game::global_data_dir() + "/games!" << endl;
84         exit (1);
85     }
86 
87     while ((d = readdir (mydir)) != NULL)
88     {
89         string s (d->d_name);
90         if (s != "." && s != "..")
91         {
92             if (nogames)
93             {
94                 nogames = false;
95                 cout << "Installed games (Suitable for the GAME parameter):\n";
96             }
97             cout << " - " << d->d_name << endl;
98         }
99     }
100 
101     if (nogames) cout << "No games available.\n";
102 
103     closedir (mydir);
104 }
105 
parse_arguments(int argc,char * argv[])106 void config::parse_arguments (int argc, char * argv[])
107 {
108     int c;
109 
110     // Check for options
111     while (1)
112     {
113         c = getopt (argc, argv, "lcdhvg:");
114         if (c == -1)
115             break;
116 
117         switch (c)
118         {
119             case 'l':
120                 print_available_games ();
121                 exit (0);
122                 break;
123 
124             case 'd':
125                 cout << game::global_data_dir() << endl;
126                 exit (0);
127                 break;
128 
129             case 'v':
130                 cout << VERSION << endl;
131                 exit (0);
132                 break;
133 
134             case 'c':
135             {
136                 python::init ();
137 #if PY_VERSION_HEX < 0x03020000
138                 python::exec_string ("import compileall; compileall.compile_dir (\".\", 0);");
139 #else
140                 // starting with Python 3.2, .pyc files end up in the __pycache__ directory unless
141                 // legacy=True parameter is used. See https://www.python.org/dev/peps/pep-3147/
142                 python::exec_string ("import compileall; compileall.compile_dir (\".\", maxlevels=0, legacy=True);");
143 #endif
144                 python::cleanup ();
145                 exit (0);
146                 break;
147             }
148 
149             case 'g':
150             {
151                 gamedir = optarg;
152                 if (gamedir[gamedir.size () - 1] == '/')
153                     gamedir.erase (gamedir.size () - 1);
154 
155                 // Check whether the requested game directory exists
156                 DIR * mydir = opendir (gamedir.c_str ());
157 
158                 if (!mydir)
159                 {
160                     cerr << "Cannot open directory " << gamedir << "!" << endl;
161                     exit (1);
162                 }
163                 closedir (mydir);
164 
165                 break;
166             }
167 
168             case '?':
169             case 'h':
170                 print_help_message (argv);
171                 exit (0);
172                 break;
173         }
174     }
175 
176     // Check whether the GAME parameter is needed
177     if (gamedir == "")
178     {
179         // Check whether the GAME parameter is given
180         if (argc - optind != 1)
181         {
182             print_help_message (argv);
183             exit (0);
184         }
185 
186         // Check whether the requested game exists
187         struct dirent * d;
188         DIR * mydir = opendir ((game::global_data_dir() + "/games").c_str());
189         bool found = false;
190 
191         if (!mydir)
192         {
193             cerr << "Cannot open directory " << game::global_data_dir() + "/games!" << endl;
194             exit (1);
195         }
196 
197         while ((d = readdir (mydir)) != NULL)
198         {
199             if (string (d->d_name) == argv[optind]) found = true;
200         }
201 
202         closedir (mydir);
203 
204         if (!found)
205         {
206             cerr << "Game '" << argv[optind] << "' can't be found.\n"
207                  << "Run '" << argv[0] << " -l' for a list of available games.\n";
208             exit (1);
209         }
210 
211         // The game exists, so let the show continue...
212         gamedir = game::global_data_dir() + "/games/";
213         gamedir += argv[optind];
214     }
215 
216     // Now check whether the directory is a valid game directory
217     string tfile = gamedir + "/gamename.txt";
218     ifstream f (tfile.c_str ());
219     if (!f.is_open ())
220     {
221         cerr << "Directory " << gamedir << " is not a valid game directory.\n";
222         exit (1);
223     }
224     char t[256];
225     f.getline (t, 256);
226     game_name = t;
227     f.close ();
228 }
229 
230 
231 
232 // That's more or less a move operator, as the source is destroyed
operator =(const config * c)233 config& config::operator =(const config *c)
234 {
235 	display = c->display;
236     screen_mode = c->screen_mode;
237     audio_channels = c->audio_channels;
238     audio_resolution = c->audio_resolution;
239     audio_sample_rate = c->audio_sample_rate;
240     audio_volume = c->audio_volume;
241     adonthellrc = c->adonthellrc;
242 
243     delete c;
244     return *this;
245 }
246 
get_adonthellrc()247 char *config::get_adonthellrc ()
248 {
249     return (char *) adonthellrc.c_str ();
250 }
251 
252 // write a default configuration file
write_adonthellrc()253 void config::write_adonthellrc ()
254 {
255     string fname;
256 
257 #ifndef WIN32
258     fname = adonthellrc + "/adonthellrc";
259 #else
260     fname = adonthellrc + "/adonthell.ini";
261 #endif
262 
263     ofstream rc (fname.c_str ());
264 
265     rc << "# Sample Adonthell configuration file;\n"
266        << "# edit to your needs!\n\n"
267        << "# Screen-mode num\n#   0  Windowed mode\n#   1  Letterbox mode\n"
268        << "#   2  Fullscreen mode\n    Screen-mode " << (int) screen_mode
269        << "\n\n" << "# Display num\n#   Index of the display to use in fullscreen mode"
270        << "\n    Display " << (int) display << "\n\n"
271        << "# Language [locale]\n#   Where locale has the form fr_FR or de_DE, etc.\n"
272        << "    Language [" << language << "]\n\n"
273        << "# Font [font.ttf]\n#   Path to a true type font to use. Leave empty for default\n"
274        << "    Font [" << font << "]\n\n"
275        << "# Quick-load num\n#   0  off\n#   1  on\n    Quick-load "
276        << (int) quick_load << "\n\n"
277        << "# Audio-channels num\n#   0  Mono\n#   1  Stereo\n"
278        << "    Audio-channels " << (int) audio_channels << "\n\n"
279        << "# Audio-resolution num\n#   0  8 bit\n#   1  16 bit\n"
280        << "    Audio-resolution " << (int) audio_resolution << "\n\n"
281        << "# Audio-sample-rate num\n#   0  11025 Hz\n#   1  22050 Hz\n#   2  44100 Hz\n"
282        << "    Audio-sample-rate " << (int) audio_sample_rate << "\n\n"
283        << "# Audio-volume num\n#   0 - 100 %\n"
284        << "    Audio-volume " << (int) audio_volume << "\n\n"
285        << "# Version number of this file. Please don't edit\n    Version [" << VERSION << "]\n";
286 
287     rc.close ();
288 }
289 
read_adonthellrc()290 bool config::read_adonthellrc ()
291 {
292     int n, i = 1;
293     u_int32 major = 0, minor = 0, micro = 0, MAJOR, MINOR, MICRO;
294     char suffix[16] = "\0", SUFFIX[16] = "\0";
295     string s, fname;
296 
297 #ifndef WIN32
298     fname = adonthellrc + "/adonthellrc";
299 #else
300     fname = adonthellrc + "/adonthell.ini";
301 #endif
302 
303     // prefsin is declared in lex.prefs.c
304     prefsin = fopen (fname.c_str (), "r");
305 
306     // open failed -> try to write new configuration
307     if (!prefsin)
308     {
309         write_adonthellrc ();
310 
311         // now try again
312         if (!(prefsin = fopen (fname.c_str (), "r")))
313         {
314             fprintf (stderr, "*** warning: prefs::read_adonthellrc: creating config file failed\n");
315             return false;
316         }
317     }
318 
319     // adonthellrc opened -> read configuration
320     while (i)
321     {
322         switch (i = parse_adonthellrc (n, s))
323         {
324             case PREFS_LANGUAGE:
325             {
326                 if (parse_adonthellrc (n, s) == PREFS_STR) language = s;
327                 break;
328             }
329             case PREFS_FONT:
330             {
331                 if (parse_adonthellrc (n, s) == PREFS_STR) font = s;
332                 break;
333             }
334             case PREFS_SCREEN_MODE:
335             {
336                 if (parse_adonthellrc (n, s) == PREFS_NUM) screen_mode = n;
337                 break;
338             }
339 
340             case PREFS_DISPLAY:
341             {
342                 if (parse_adonthellrc (n, s) == PREFS_NUM) display = n;
343                 break;
344             }
345 
346             case PREFS_QUICK_LOAD:
347             {
348                 if (parse_adonthellrc (n, s) == PREFS_NUM) quick_load = n;
349                 break;
350             }
351 
352             case PREFS_AUDIO_RESOLUTION:
353             {
354                 if (parse_adonthellrc (n, s) == PREFS_NUM) audio_resolution = n;
355                 break;
356             }
357 
358             case PREFS_AUDIO_CHANNELS:
359             {
360                 if (parse_adonthellrc (n, s) == PREFS_NUM) audio_channels = n;
361                 break;
362             }
363 
364             case PREFS_AUDIO_SAMPLE_RATE:
365             {
366                 if (parse_adonthellrc (n, s) == PREFS_NUM) audio_sample_rate = n;
367                 break;
368             }
369 
370             case PREFS_AUDIO_VOLUME:
371             {
372                 if (parse_adonthellrc (n, s) == PREFS_NUM) audio_volume = n;
373                 break;
374             }
375 
376             case PREFS_VERSION:
377             {
378                 // get config file version number
379                 if (parse_adonthellrc (n, s) == PREFS_STR)
380                     sscanf (s.c_str (), "%u.%u.%u%15s", &major, &minor, &micro, suffix);
381                 break;
382             }
383             default: break;
384         }
385     }
386 
387     fclose (prefsin);
388 
389     // get engine version numbers
390     sscanf (VERSION, "%u.%u.%u%15s", &MAJOR, &MINOR, &MICRO, SUFFIX);
391 
392     // compare version of config file and engine
393     if (major < MAJOR ||
394         (major == MAJOR && minor < MINOR) ||
395         (major == MAJOR && minor == MINOR && micro < MICRO) ||
396         strcmp (suffix, SUFFIX) != 0)
397     {
398         // update config file if engine is newer
399         write_adonthellrc ();
400     }
401 
402     return true;
403 }
404