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, µ, 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