all repos — mgba @ bf611e01f238b3366dcde7a5f176a0a9d198c520

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#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}