all repos — mgba @ 62e39558485d896d794e912663915349f25ae390

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 = "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}