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