src/feature/gui/gui-config.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "gui-config.h"
7
8#include <mgba/core/config.h>
9#include <mgba/core/core.h>
10#include "feature/gui/gui-runner.h"
11#include "feature/gui/remap.h"
12#include <mgba/internal/gba/gba.h>
13#ifdef M_CORE_GB
14#include <mgba/internal/gb/gb.h>
15#endif
16#include <mgba-util/gui/file-select.h>
17#include <mgba-util/gui/menu.h>
18#include <mgba-util/vfs.h>
19
20#ifndef GUI_MAX_INPUTS
21#define GUI_MAX_INPUTS 7
22#endif
23
24static bool _biosNamed(const char* name) {
25 char ext[PATH_MAX + 1] = {};
26 separatePath(name, NULL, NULL, ext);
27
28 if (strstr(name, "bios")) {
29 return true;
30 }
31 if (!strncmp(ext, "bin", PATH_MAX)) {
32 return true;
33 }
34 return false;
35}
36
37void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t nExtra) {
38 struct GUIMenu menu = {
39 .title = "Configure",
40 .index = 0,
41 .background = &runner->background.d
42 };
43 GUIMenuItemListInit(&menu.items, 0);
44 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
45 .title = "Frameskip",
46 .data = "frameskip",
47 .submenu = 0,
48 .state = 0,
49 .validStates = (const char*[]) {
50 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
51 },
52 .nStates = 10
53 };
54 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
55 .title = "Show framerate",
56 .data = "fpsCounter",
57 .submenu = 0,
58 .state = false,
59 .validStates = (const char*[]) {
60 "Off", "On"
61 },
62 .nStates = 2
63 };
64 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
65 .title = "Show status OSD",
66 .data = "showOSD",
67 .submenu = 0,
68 .state = true,
69 .validStates = (const char*[]) {
70 "Off", "On"
71 },
72 .nStates = 2
73 };
74 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
75 .title = "Autosave state",
76 .data = "autosave",
77 .submenu = 0,
78 .state = true,
79 .validStates = (const char*[]) {
80 "Off", "On"
81 },
82 .nStates = 2
83 };
84 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
85 .title = "Autoload state",
86 .data = "autoload",
87 .submenu = 0,
88 .state = true,
89 .validStates = (const char*[]) {
90 "Off", "On"
91 },
92 .nStates = 2
93 };
94 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
95 .title = "Mute",
96 .data = "mute",
97 .submenu = 0,
98 .state = false,
99 .validStates = (const char*[]) {
100 "Off", "On"
101 },
102 .nStates = 2
103 };
104 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
105 .title = "Use BIOS if found",
106 .data = "useBios",
107 .submenu = 0,
108 .state = true,
109 .validStates = (const char*[]) {
110 "Off", "On"
111 },
112 .nStates = 2
113 };
114#ifdef M_CORE_GBA
115 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
116 .title = "Select GBA BIOS path",
117 .data = "gba.bios",
118 };
119#endif
120#ifdef M_CORE_GB
121 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
122 .title = "Select GB BIOS path",
123 .data = "gb.bios",
124 };
125 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
126 .title = "Select GBC BIOS path",
127 .data = "gbc.bios",
128 };
129 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
130 .title = "Select SGB BIOS path",
131 .data = "sgb.bios",
132 };
133#endif
134 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
135 .title = "Interframe blending",
136 .data = "interframeBlending",
137 .submenu = 0,
138 .state = false,
139 .validStates = (const char*[]) {
140 "Off", "On"
141 },
142 .nStates = 2
143 };
144#if defined(M_CORE_GBA) && (defined(GEKKO) || defined(__SWITCH__) || defined(PSP2))
145 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
146 .title = "Enable GBP features",
147 .data = "gba.forceGbp",
148 .submenu = 0,
149 .state = false,
150 .validStates = (const char*[]) {
151 "Off", "On"
152 },
153 .nStates = 2
154 };
155#endif
156#ifdef M_CORE_GB
157 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
158 .title = "Enable SGB features",
159 .data = "sgb.model",
160 .submenu = 0,
161 .state = true,
162 .validStates = (const char*[]) {
163 "Off", "On"
164 },
165 .stateMappings = (const struct GUIVariant[]) {
166 GUI_V_S("DMG"),
167 GUI_V_S("SGB"),
168 },
169 .nStates = 2
170 };
171 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
172 .title = "Enable SGB borders",
173 .data = "sgb.borders",
174 .submenu = 0,
175 .state = true,
176 .validStates = (const char*[]) {
177 "Off", "On"
178 },
179 .nStates = 2
180 };
181 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
182 .title = "Crop SGB borders",
183 .data = "sgb.borderCrop",
184 .submenu = 0,
185 .state = false,
186 .validStates = (const char*[]) {
187 "Off", "On"
188 },
189 .nStates = 2
190 };
191#endif
192 size_t i;
193 const char* mapNames[GUI_MAX_INPUTS + 1];
194 if (runner->keySources) {
195 for (i = 0; runner->keySources[i].id && i < GUI_MAX_INPUTS; ++i) {
196 mapNames[i] = runner->keySources[i].name;
197 }
198 if (i == 1) {
199 // Don't display a name if there's only one input source
200 i = 0;
201 }
202 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
203 .title = "Remap controls",
204 .data = "*REMAP",
205 .state = 0,
206 .validStates = i ? mapNames : 0,
207 .nStates = i
208 };
209 }
210 for (i = 0; i < nExtra; ++i) {
211 *GUIMenuItemListAppend(&menu.items) = extra[i];
212 }
213 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
214 .title = "Save",
215 .data = "*SAVE",
216 };
217 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
218 .title = "Cancel",
219 .data = 0,
220 };
221 enum GUIMenuExitReason reason;
222 char gbaBiosPath[256] = "";
223#ifdef M_CORE_GB
224 char gbBiosPath[256] = "";
225 char gbcBiosPath[256] = "";
226 char sgbBiosPath[256] = "";
227#endif
228
229 struct GUIMenuItem* item;
230 for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
231 item = GUIMenuItemListGetPointer(&menu.items, i);
232 if (!item->validStates || !item->data) {
233 continue;
234 }
235 if (item->stateMappings) {
236 size_t j;
237 for (j = 0; j < item->nStates; ++j) {
238 const struct GUIVariant* v = &item->stateMappings[j];
239 struct GUIVariant test;
240 switch (v->type) {
241 case GUI_VARIANT_VOID:
242 if (!mCoreConfigGetValue(&runner->config, item->data)) {
243 item->state = j;
244 break;
245 }
246 break;
247 case GUI_VARIANT_UNSIGNED:
248 if (mCoreConfigGetUIntValue(&runner->config, item->data, &test.v.u) && test.v.u == v->v.u) {
249 item->state = j;
250 break;
251 }
252 break;
253 case GUI_VARIANT_INT:
254 if (mCoreConfigGetIntValue(&runner->config, item->data, &test.v.i) && test.v.i == v->v.i) {
255 item->state = j;
256 break;
257 }
258 break;
259 case GUI_VARIANT_FLOAT:
260 if (mCoreConfigGetFloatValue(&runner->config, item->data, &test.v.f) && fabsf(test.v.f - v->v.f) <= 1e-3f) {
261 item->state = j;
262 break;
263 }
264 break;
265 case GUI_VARIANT_STRING:
266 test.v.s = mCoreConfigGetValue(&runner->config, item->data);
267 if (test.v.s && strcmp(test.v.s, v->v.s) == 0) {
268 item->state = j;
269 break;
270 }
271 break;
272 }
273 }
274 } else {
275 mCoreConfigGetUIntValue(&runner->config, item->data, &item->state);
276 }
277 }
278
279 while (true) {
280 reason = GUIShowMenu(&runner->params, &menu, &item);
281 if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
282 break;
283 }
284 if (!strcmp(item->data, "*SAVE")) {
285 if (gbaBiosPath[0]) {
286 mCoreConfigSetValue(&runner->config, "gba.bios", gbaBiosPath);
287 }
288 if (gbBiosPath[0]) {
289 mCoreConfigSetValue(&runner->config, "gb.bios", gbBiosPath);
290 }
291 if (gbcBiosPath[0]) {
292 mCoreConfigSetValue(&runner->config, "gbc.bios", gbcBiosPath);
293 }
294 if (sgbBiosPath[0]) {
295 mCoreConfigSetValue(&runner->config, "sgb.bios", sgbBiosPath);
296 }
297 for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
298 item = GUIMenuItemListGetPointer(&menu.items, i);
299 if (!item->validStates || !item->data || ((const char*) item->data)[0] == '*') {
300 continue;
301 }
302 if (item->stateMappings) {
303 const struct GUIVariant* v = &item->stateMappings[item->state];
304 switch (v->type) {
305 case GUI_VARIANT_VOID:
306 mCoreConfigSetValue(&runner->config, item->data, NULL);
307 break;
308 case GUI_VARIANT_UNSIGNED:
309 mCoreConfigSetUIntValue(&runner->config, item->data, v->v.u);
310 break;
311 case GUI_VARIANT_INT:
312 mCoreConfigSetUIntValue(&runner->config, item->data, v->v.i);
313 break;
314 case GUI_VARIANT_FLOAT:
315 mCoreConfigSetFloatValue(&runner->config, item->data, v->v.f);
316 break;
317 case GUI_VARIANT_STRING:
318 mCoreConfigSetValue(&runner->config, item->data, v->v.s);
319 break;
320 }
321 } else {
322 mCoreConfigSetUIntValue(&runner->config, item->data, item->state);
323 }
324 }
325 if (runner->keySources) {
326 size_t i;
327 for (i = 0; runner->keySources[i].id; ++i) {
328 mInputMapSave(&runner->core->inputMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
329 mInputMapSave(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
330 }
331 }
332 mCoreConfigSave(&runner->config);
333 mCoreLoadForeignConfig(runner->core, &runner->config);
334 break;
335 }
336 if (!strcmp(item->data, "*REMAP")) {
337 mGUIRemapKeys(&runner->params, &runner->core->inputMap, &runner->keySources[item->state]);
338 continue;
339 }
340 if (!strcmp(item->data, "gba.bios")) {
341 // TODO: show box if failed
342 if (!GUISelectFile(&runner->params, gbaBiosPath, sizeof(gbaBiosPath), _biosNamed, GBAIsBIOS, NULL)) {
343 gbaBiosPath[0] = '\0';
344 }
345 continue;
346 }
347#ifdef M_CORE_GB
348 if (!strcmp(item->data, "gb.bios")) {
349 // TODO: show box if failed
350 if (!GUISelectFile(&runner->params, gbBiosPath, sizeof(gbBiosPath), _biosNamed, GBIsBIOS, NULL)) {
351 gbBiosPath[0] = '\0';
352 }
353 continue;
354 }
355 if (!strcmp(item->data, "gbc.bios")) {
356 // TODO: show box if failed
357 if (!GUISelectFile(&runner->params, gbcBiosPath, sizeof(gbcBiosPath), _biosNamed, GBIsBIOS, NULL)) {
358 gbcBiosPath[0] = '\0';
359 }
360 continue;
361 }
362 if (!strcmp(item->data, "sgb.bios")) {
363 // TODO: show box if failed
364 if (!GUISelectFile(&runner->params, sgbBiosPath, sizeof(sgbBiosPath), _biosNamed, GBIsBIOS, NULL)) {
365 sgbBiosPath[0] = '\0';
366 }
367 continue;
368 }
369#endif
370 if (item->validStates) {
371 if (item->state < item->nStates - 1) {
372 do {
373 ++item->state;
374 } while (!item->validStates[item->state] && item->state < item->nStates - 1);
375 if (!item->validStates[item->state]) {
376 item->state = 0;
377 }
378 } else {
379 item->state = 0;
380 }
381 }
382 }
383}