all repos — mgba @ 209eed35edf2ecb54c7a093c1857b27931f77f68

mGBA Game Boy Advance Emulator

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