1 //  SuperTux -  A Jump'n Run
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (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, see <http://www.gnu.org/licenses/>.
16 
17 #include "supertux/gameconfig.hpp"
18 
19 #include "config.h"
20 
21 #include "editor/overlay_widget.hpp"
22 #include "util/reader_collection.hpp"
23 #include "util/reader_document.hpp"
24 #include "util/reader_mapping.hpp"
25 #include "util/writer.hpp"
26 #include "util/log.hpp"
27 
28 #ifdef __EMSCRIPTEN__
29 #include <emscripten.h>
30 #include <emscripten/html5.h>
31 #endif
32 
Config()33 Config::Config() :
34   profile(1),
35   fullscreen_size(0, 0),
36   fullscreen_refresh_rate(0),
37   window_size(1280, 800),
38   window_resizable(true),
39   aspect_size(0, 0), // auto detect
40 #ifdef __EMSCRIPTEN__
41   fit_window(true),
42 #endif
43   magnification(0.0f),
44   use_fullscreen(false),
45   video(VideoSystem::VIDEO_AUTO),
46   try_vsync(true),
47   show_fps(false),
48   show_player_pos(false),
49   show_controller(false),
50   sound_enabled(true),
51   music_enabled(true),
52   sound_volume(100),
53   music_volume(50),
54   random_seed(0), // set by time(), by default (unless in config)
55   enable_script_debugger(false),
56   start_demo(),
57   record_demo(),
58   tux_spawn_pos(),
59   locale(),
60   keyboard_config(),
61   joystick_config(),
62 #ifdef ENABLE_TOUCHSCREEN_SUPPORT
63   mobile_controls(true),
64 #endif
65   addons(),
66   developer_mode(false),
67   christmas_mode(false),
68   transitions_enabled(true),
69   confirmation_dialog(false),
70   pause_on_focusloss(true),
71   custom_mouse_cursor(true),
72 #ifdef ENABLE_DISCORD
73   enable_discord(false),
74 #endif
75   hide_editor_levelnames(false),
76   editor_selected_snap_grid_size(3),
77   editor_render_grid(true),
78   editor_snap_to_grid(true),
79   editor_render_background(true),
80   editor_render_lighting(false),
81   editor_autotile_mode(false),
82   editor_autotile_help(true),
83   editor_autosave_frequency(5),
84   repository_url()
85 {
86 }
87 
88 void
load()89 Config::load()
90 {
91 #ifdef __EMSCRIPTEN__
92   EM_ASM({
93     supertux_loadFiles();
94   }, 0); // EM_ASM is a variadic macro and Clang requires at least 1 value for the variadic argument
95 #endif
96 
97   auto doc = ReaderDocument::from_file("config");
98   auto root = doc.get_root();
99   if (root.get_name() != "supertux-config")
100   {
101     throw std::runtime_error("File is not a supertux-config file");
102   }
103 
104   auto config_mapping = root.get_mapping();
105   config_mapping.get("profile", profile);
106   config_mapping.get("show_fps", show_fps);
107   config_mapping.get("show_player_pos", show_player_pos);
108   config_mapping.get("show_controller", show_controller);
109   config_mapping.get("developer", developer_mode);
110   config_mapping.get("confirmation_dialog", confirmation_dialog);
111   config_mapping.get("pause_on_focusloss", pause_on_focusloss);
112   config_mapping.get("custom_mouse_cursor", custom_mouse_cursor);
113 
114   boost::optional<ReaderMapping> config_integrations_mapping;
115   if (config_mapping.get("integrations", config_integrations_mapping))
116   {
117     config_integrations_mapping->get("hide_editor_levelnames", hide_editor_levelnames);
118 #ifdef ENABLE_DISCORD
119     config_integrations_mapping->get("enable_discord", enable_discord);
120 #endif
121   }
122 
123   // Compatibility; will be overwritten by the "editor" category
124   config_mapping.get("editor_autosave_frequency", editor_autosave_frequency);
125 
126   editor_autotile_help = !developer_mode;
127 
128   boost::optional<ReaderMapping> editor_mapping;
129   if (config_mapping.get("editor", editor_mapping))
130   {
131     editor_mapping->get("autosave_frequency", editor_autosave_frequency);
132     editor_mapping->get("autotile_help", editor_autotile_help);
133     editor_mapping->get("autotile_mode", editor_autotile_mode);
134     editor_mapping->get("render_background", editor_render_background);
135     editor_mapping->get("render_grid", editor_render_grid);
136     editor_mapping->get("render_lighting", editor_render_lighting);
137     editor_mapping->get("selected_snap_grid_size", editor_selected_snap_grid_size);
138     editor_mapping->get("snap_to_grid", editor_snap_to_grid);
139   } else { log_warning << "!!!!" << std::endl; }
140 
141   if (is_christmas()) {
142     config_mapping.get("christmas", christmas_mode, true);
143   }
144   config_mapping.get("transitions_enabled", transitions_enabled);
145   config_mapping.get("locale", locale);
146   config_mapping.get("random_seed", random_seed);
147   config_mapping.get("repository_url", repository_url);
148 
149   boost::optional<ReaderMapping> config_video_mapping;
150   if (config_mapping.get("video", config_video_mapping))
151   {
152     config_video_mapping->get("fullscreen", use_fullscreen);
153     std::string video_string;
154     config_video_mapping->get("video", video_string);
155     video = VideoSystem::get_video_system(video_string);
156     config_video_mapping->get("vsync", try_vsync);
157 
158     config_video_mapping->get("fullscreen_width",  fullscreen_size.width);
159     config_video_mapping->get("fullscreen_height", fullscreen_size.height);
160     if (fullscreen_size.width < 0 || fullscreen_size.height < 0)
161     {
162       // Somehow, an invalid size got entered into the config file,
163       // let's use the "auto" setting instead.
164       fullscreen_size = Size(0, 0);
165     }
166     config_video_mapping->get("fullscreen_refresh_rate", fullscreen_refresh_rate);
167 
168     config_video_mapping->get("window_width",  window_size.width);
169     config_video_mapping->get("window_height", window_size.height);
170 
171     config_video_mapping->get("window_resizable", window_resizable);
172 
173     config_video_mapping->get("aspect_width",  aspect_size.width);
174     config_video_mapping->get("aspect_height", aspect_size.height);
175 
176     config_video_mapping->get("magnification", magnification);
177 
178 #ifdef __EMSCRIPTEN__
179     // Forcibly set autofit to true
180     // TODO: Remove the autofit parameter entirely - it should always be true
181 
182     //config_video_mapping->get("fit_window", fit_window);
183     fit_window = true;
184 #endif
185   }
186 
187   boost::optional<ReaderMapping> config_audio_mapping;
188   if (config_mapping.get("audio", config_audio_mapping))
189   {
190     config_audio_mapping->get("sound_enabled", sound_enabled);
191     config_audio_mapping->get("music_enabled", music_enabled);
192     config_audio_mapping->get("sound_volume", sound_volume);
193     config_audio_mapping->get("music_volume", music_volume);
194   }
195 
196   boost::optional<ReaderMapping> config_control_mapping;
197   if (config_mapping.get("control", config_control_mapping))
198   {
199     boost::optional<ReaderMapping> keymap_mapping;
200     if (config_control_mapping->get("keymap", keymap_mapping))
201     {
202       keyboard_config.read(*keymap_mapping);
203     }
204 
205     boost::optional<ReaderMapping> joystick_mapping;
206     if (config_control_mapping->get("joystick", joystick_mapping))
207     {
208       joystick_config.read(*joystick_mapping);
209     }
210 
211 #ifdef ENABLE_TOUCHSCREEN_SUPPORT
212     config_video_mapping->get("mobile_controls", mobile_controls);
213 #endif
214   }
215 
216   boost::optional<ReaderCollection> config_addons_mapping;
217   if (config_mapping.get("addons", config_addons_mapping))
218   {
219     for (auto const& addon_node : config_addons_mapping->get_objects())
220     {
221       if (addon_node.get_name() == "addon")
222       {
223         auto addon = addon_node.get_mapping();
224 
225         std::string id;
226         bool enabled = false;
227         if (addon.get("id", id) &&
228             addon.get("enabled", enabled))
229         {
230           addons.push_back({id, enabled});
231         }
232       }
233       else
234       {
235         log_warning << "Unknown token in config file: " << addon_node.get_name() << std::endl;
236       }
237     }
238   }
239 }
240 
241 void
save()242 Config::save()
243 {
244   Writer writer("config");
245 
246   writer.start_list("supertux-config");
247 
248   writer.write("profile", profile);
249   writer.write("show_fps", show_fps);
250   writer.write("show_player_pos", show_player_pos);
251   writer.write("show_controller", show_controller);
252   writer.write("developer", developer_mode);
253   writer.write("confirmation_dialog", confirmation_dialog);
254   writer.write("pause_on_focusloss", pause_on_focusloss);
255   writer.write("custom_mouse_cursor", custom_mouse_cursor);
256 
257   writer.start_list("integrations");
258   {
259     writer.write("hide_editor_levelnames", hide_editor_levelnames);
260 #ifdef ENABLE_DISCORD
261     writer.write("enable_discord", enable_discord);
262 #endif
263   }
264   writer.end_list("integrations");
265 
266   if (is_christmas()) {
267     writer.write("christmas", christmas_mode);
268   }
269   writer.write("transitions_enabled", transitions_enabled);
270   writer.write("locale", locale);
271   writer.write("repository_url", repository_url);
272 
273   writer.start_list("video");
274   writer.write("fullscreen", use_fullscreen);
275   if (video == VideoSystem::VIDEO_NULL) {
276     // don't save NULL renderer to config as starting SuperTux without
277     // getting a window is rather confusing
278   } else {
279     writer.write("video", VideoSystem::get_video_string(video));
280   }
281   writer.write("vsync", try_vsync);
282 
283   writer.write("fullscreen_width",  fullscreen_size.width);
284   writer.write("fullscreen_height", fullscreen_size.height);
285   writer.write("fullscreen_refresh_rate", fullscreen_refresh_rate);
286 
287   writer.write("window_width",  window_size.width);
288   writer.write("window_height", window_size.height);
289 
290   writer.write("window_resizable", window_resizable);
291 
292   writer.write("aspect_width",  aspect_size.width);
293   writer.write("aspect_height", aspect_size.height);
294 
295 #ifdef __EMSCRIPTEN__
296   // Forcibly set autofit to true
297   // TODO: Remove the autofit parameter entirely - it should always be true
298   writer.write("fit_window", true /* fit_window */);
299 #endif
300 
301   writer.write("magnification", magnification);
302 
303   writer.end_list("video");
304 
305   writer.start_list("audio");
306   writer.write("sound_enabled", sound_enabled);
307   writer.write("music_enabled", music_enabled);
308   writer.write("sound_volume", sound_volume);
309   writer.write("music_volume", music_volume);
310   writer.end_list("audio");
311 
312   writer.start_list("control");
313   {
314     writer.start_list("keymap");
315     keyboard_config.write(writer);
316     writer.end_list("keymap");
317 
318     writer.start_list("joystick");
319     joystick_config.write(writer);
320     writer.end_list("joystick");
321 
322 #ifdef ENABLE_TOUCHSCREEN_SUPPORT
323     writer.write("mobile_controls", mobile_controls);
324 #endif
325   }
326   writer.end_list("control");
327 
328   writer.start_list("addons");
329   for (const auto& addon : addons)
330   {
331     writer.start_list("addon");
332     writer.write("id", addon.id);
333     writer.write("enabled", addon.enabled);
334     writer.end_list("addon");
335   }
336   writer.end_list("addons");
337 
338   writer.start_list("editor");
339   {
340     writer.write("autosave_frequency", editor_autosave_frequency);
341     writer.write("autotile_help", editor_autotile_help);
342     writer.write("autotile_mode", editor_autotile_mode);
343     writer.write("render_background", editor_render_background);
344     writer.write("render_grid", editor_render_grid);
345     writer.write("render_lighting", editor_render_lighting);
346     writer.write("selected_snap_grid_size", editor_selected_snap_grid_size);
347     writer.write("snap_to_grid", editor_snap_to_grid);
348   }
349   writer.end_list("editor");
350 
351   writer.end_list("supertux-config");
352 }
353 
354 /* EOF */
355