all repos — mgba @ a263d4718fbf84ce17142edc7b81c6f27f97bfb5

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