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 = "Autosave state",
66 .data = "autosave",
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 = "Autoload state",
76 .data = "autoload",
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 = "Use BIOS if found",
86 .data = "useBios",
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 = "Select GBA BIOS path",
96 .data = "gba.bios",
97 };
98#ifdef M_CORE_GB
99 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
100 .title = "Select GB BIOS path",
101 .data = "gb.bios",
102 };
103 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
104 .title = "Select GBC BIOS path",
105 .data = "gbc.bios",
106 };
107 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
108 .title = "Select SGB BIOS path",
109 .data = "sgb.bios",
110 };
111 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
112 .title = "Interframe blending",
113 .data = "interframeBlending",
114 .submenu = 0,
115 .state = false,
116 .validStates = (const char*[]) {
117 "Off", "On"
118 },
119 .nStates = 2
120 };
121 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
122 .title = "Enable SGB features",
123 .data = "sgb.model",
124 .submenu = 0,
125 .state = true,
126 .validStates = (const char*[]) {
127 "Off", "On"
128 },
129 .stateMappings = (const struct GUIVariant[]) {
130 GUI_V_S("DMG"),
131 GUI_V_S("SGB"),
132 },
133 .nStates = 2
134 };
135 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
136 .title = "Enable SGB borders",
137 .data = "sgb.borders",
138 .submenu = 0,
139 .state = true,
140 .validStates = (const char*[]) {
141 "Off", "On"
142 },
143 .nStates = 2
144 };
145#endif
146 size_t i;
147 const char* mapNames[GUI_MAX_INPUTS + 1];
148 if (runner->keySources) {
149 for (i = 0; runner->keySources[i].id && i < GUI_MAX_INPUTS; ++i) {
150 mapNames[i] = runner->keySources[i].name;
151 }
152 if (i == 1) {
153 // Don't display a name if there's only one input source
154 i = 0;
155 }
156 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
157 .title = "Remap controls",
158 .data = "*REMAP",
159 .state = 0,
160 .validStates = i ? mapNames : 0,
161 .nStates = i
162 };
163 }
164 for (i = 0; i < nExtra; ++i) {
165 *GUIMenuItemListAppend(&menu.items) = extra[i];
166 }
167 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
168 .title = "Save",
169 .data = "*SAVE",
170 };
171 *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
172 .title = "Cancel",
173 .data = 0,
174 };
175 enum GUIMenuExitReason reason;
176 char gbaBiosPath[256] = "";
177#ifdef M_CORE_GB
178 char gbBiosPath[256] = "";
179 char gbcBiosPath[256] = "";
180 char sgbBiosPath[256] = "";
181#endif
182
183 struct GUIMenuItem* item;
184 for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
185 item = GUIMenuItemListGetPointer(&menu.items, i);
186 if (!item->validStates || !item->data) {
187 continue;
188 }
189 if (item->stateMappings) {
190 size_t j;
191 for (j = 0; j < item->nStates; ++j) {
192 const struct GUIVariant* v = &item->stateMappings[j];
193 struct GUIVariant test;
194 switch (v->type) {
195 case GUI_VARIANT_VOID:
196 if (!mCoreConfigGetValue(&runner->config, item->data)) {
197 item->state = j;
198 break;
199 }
200 break;
201 case GUI_VARIANT_UNSIGNED:
202 if (mCoreConfigGetUIntValue(&runner->config, item->data, &test.v.u) && test.v.u == v->v.u) {
203 item->state = j;
204 break;
205 }
206 break;
207 case GUI_VARIANT_INT:
208 if (mCoreConfigGetIntValue(&runner->config, item->data, &test.v.i) && test.v.i == v->v.i) {
209 item->state = j;
210 break;
211 }
212 break;
213 case GUI_VARIANT_FLOAT:
214 if (mCoreConfigGetFloatValue(&runner->config, item->data, &test.v.f) && fabsf(test.v.f - v->v.f) <= 1e-3f) {
215 item->state = j;
216 break;
217 }
218 break;
219 case GUI_VARIANT_STRING:
220 test.v.s = mCoreConfigGetValue(&runner->config, item->data);
221 if (test.v.s && strcmp(test.v.s, v->v.s) == 0) {
222 item->state = j;
223 break;
224 }
225 break;
226 }
227 }
228 } else {
229 mCoreConfigGetUIntValue(&runner->config, item->data, &item->state);
230 }
231 }
232
233 while (true) {
234 reason = GUIShowMenu(&runner->params, &menu, &item);
235 if (reason != GUI_MENU_EXIT_ACCEPT || !item->data) {
236 break;
237 }
238 if (!strcmp(item->data, "*SAVE")) {
239 if (gbaBiosPath[0]) {
240 mCoreConfigSetValue(&runner->config, "gba.bios", gbaBiosPath);
241 }
242 if (gbBiosPath[0]) {
243 mCoreConfigSetValue(&runner->config, "gb.bios", gbBiosPath);
244 }
245 if (gbcBiosPath[0]) {
246 mCoreConfigSetValue(&runner->config, "gbc.bios", gbcBiosPath);
247 }
248 if (sgbBiosPath[0]) {
249 mCoreConfigSetValue(&runner->config, "sgb.bios", sgbBiosPath);
250 }
251 for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
252 item = GUIMenuItemListGetPointer(&menu.items, i);
253 if (!item->validStates || !item->data || ((const char*) item->data)[0] == '*') {
254 continue;
255 }
256 if (item->stateMappings) {
257 const struct GUIVariant* v = &item->stateMappings[item->state];
258 switch (v->type) {
259 case GUI_VARIANT_VOID:
260 mCoreConfigSetValue(&runner->config, item->data, NULL);
261 break;
262 case GUI_VARIANT_UNSIGNED:
263 mCoreConfigSetUIntValue(&runner->config, item->data, v->v.u);
264 break;
265 case GUI_VARIANT_INT:
266 mCoreConfigSetUIntValue(&runner->config, item->data, v->v.i);
267 break;
268 case GUI_VARIANT_FLOAT:
269 mCoreConfigSetFloatValue(&runner->config, item->data, v->v.f);
270 break;
271 case GUI_VARIANT_STRING:
272 mCoreConfigSetValue(&runner->config, item->data, v->v.s);
273 break;
274 }
275 } else {
276 mCoreConfigSetUIntValue(&runner->config, item->data, item->state);
277 }
278 }
279 if (runner->keySources) {
280 size_t i;
281 for (i = 0; runner->keySources[i].id; ++i) {
282 mInputMapSave(&runner->core->inputMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
283 mInputMapSave(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
284 }
285 }
286 mCoreConfigSave(&runner->config);
287 mCoreLoadForeignConfig(runner->core, &runner->config);
288 break;
289 }
290 if (!strcmp(item->data, "*REMAP")) {
291 mGUIRemapKeys(&runner->params, &runner->core->inputMap, &runner->keySources[item->state]);
292 continue;
293 }
294 if (!strcmp(item->data, "gba.bios")) {
295 // TODO: show box if failed
296 if (!GUISelectFile(&runner->params, gbaBiosPath, sizeof(gbaBiosPath), _biosNamed, GBAIsBIOS)) {
297 gbaBiosPath[0] = '\0';
298 }
299 continue;
300 }
301#ifdef M_CORE_GB
302 if (!strcmp(item->data, "gb.bios")) {
303 // TODO: show box if failed
304 if (!GUISelectFile(&runner->params, gbBiosPath, sizeof(gbBiosPath), _biosNamed, GBIsBIOS)) {
305 gbBiosPath[0] = '\0';
306 }
307 continue;
308 }
309 if (!strcmp(item->data, "gbc.bios")) {
310 // TODO: show box if failed
311 if (!GUISelectFile(&runner->params, gbcBiosPath, sizeof(gbcBiosPath), _biosNamed, GBIsBIOS)) {
312 gbcBiosPath[0] = '\0';
313 }
314 continue;
315 }
316 if (!strcmp(item->data, "sgb.bios")) {
317 // TODO: show box if failed
318 if (!GUISelectFile(&runner->params, sgbBiosPath, sizeof(sgbBiosPath), _biosNamed, GBIsBIOS)) {
319 sgbBiosPath[0] = '\0';
320 }
321 continue;
322 }
323#endif
324 if (item->validStates) {
325 ++item->state;
326 if (item->state >= item->nStates) {
327 item->state = 0;
328 }
329 }
330 }
331}