all repos — mgba @ d9778a98d484bd06875045ecb3ff953d1d466d78

mGBA Game Boy Advance Emulator

src/gba/supervisor/config.c (view raw)

  1/* Copyright (c) 2013-2015 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 "config.h"
  7
  8#include "util/formatting.h"
  9#include "util/string.h"
 10#include "util/vfs.h"
 11
 12#include <sys/stat.h>
 13
 14#ifdef _WIN32
 15#include <windows.h>
 16#include <shlwapi.h>
 17#include <shlobj.h>
 18#include <strsafe.h>
 19#endif
 20
 21#define SECTION_NAME_MAX 128
 22
 23static const char* _lookupValue(const struct GBAConfig* config, const char* key) {
 24	const char* value;
 25	if (config->port) {
 26		value = ConfigurationGetValue(&config->configTable, config->port, key);
 27		if (value) {
 28			return value;
 29		}
 30	}
 31	value = ConfigurationGetValue(&config->configTable, 0, key);
 32	if (value) {
 33		return value;
 34	}
 35	if (config->port) {
 36		value = ConfigurationGetValue(&config->defaultsTable, config->port, key);
 37		if (value) {
 38			return value;
 39		}
 40	}
 41	return ConfigurationGetValue(&config->defaultsTable, 0, key);
 42}
 43
 44static bool _lookupCharValue(const struct GBAConfig* config, const char* key, char** out) {
 45	const char* value = _lookupValue(config, key);
 46	if (!value) {
 47		return false;
 48	}
 49	if (*out) {
 50		free(*out);
 51	}
 52	*out = strdup(value);
 53	return true;
 54}
 55
 56static bool _lookupIntValue(const struct GBAConfig* config, const char* key, int* out) {
 57	const char* charValue = _lookupValue(config, key);
 58	if (!charValue) {
 59		return false;
 60	}
 61	char* end;
 62	long value = strtol(charValue, &end, 10);
 63	if (*end) {
 64		return false;
 65	}
 66	*out = value;
 67	return true;
 68}
 69
 70static bool _lookupUIntValue(const struct GBAConfig* config, const char* key, unsigned* out) {
 71	const char* charValue = _lookupValue(config, key);
 72	if (!charValue) {
 73		return false;
 74	}
 75	char* end;
 76	unsigned long value = strtoul(charValue, &end, 10);
 77	if (*end) {
 78		return false;
 79	}
 80	*out = value;
 81	return true;
 82}
 83
 84static bool _lookupFloatValue(const struct GBAConfig* config, const char* key, float* out) {
 85	const char* charValue = _lookupValue(config, key);
 86	if (!charValue) {
 87		return false;
 88	}
 89	char* end;
 90	float value = strtof_u(charValue, &end);
 91	if (*end) {
 92		return false;
 93	}
 94	*out = value;
 95	return true;
 96}
 97
 98void GBAConfigInit(struct GBAConfig* config, const char* port) {
 99	ConfigurationInit(&config->configTable);
100	ConfigurationInit(&config->defaultsTable);
101	if (port) {
102		config->port = malloc(strlen("ports.") + strlen(port) + 1);
103		snprintf(config->port, strlen("ports.") + strlen(port) + 1, "ports.%s", port);
104	} else {
105		config->port = 0;
106	}
107}
108
109void GBAConfigDeinit(struct GBAConfig* config) {
110	ConfigurationDeinit(&config->configTable);
111	ConfigurationDeinit(&config->defaultsTable);
112	free(config->port);
113}
114
115bool GBAConfigLoad(struct GBAConfig* config) {
116	char path[PATH_MAX];
117	GBAConfigDirectory(path, PATH_MAX);
118	strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
119	return ConfigurationRead(&config->configTable, path);
120}
121
122bool GBAConfigSave(const struct GBAConfig* config) {
123	char path[PATH_MAX];
124	GBAConfigDirectory(path, PATH_MAX);
125	strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
126	return ConfigurationWrite(&config->configTable, path);
127}
128
129void GBAConfigDirectory(char* out, size_t outLength) {
130	struct VFile* portable;
131#ifndef _WIN32
132	getcwd(out, outLength);
133	strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));
134	portable = VFileOpen(out, O_RDONLY);
135	if (portable) {
136		getcwd(out, outLength);
137		portable->close(portable);
138		return;
139	}
140
141	char* home = getenv("HOME");
142	snprintf(out, outLength, "%s/.config", home);
143	mkdir(out, 0755);
144	snprintf(out, outLength, "%s/.config/%s", home, binaryName);
145	mkdir(out, 0755);
146#else
147	wchar_t wpath[MAX_PATH];
148	wchar_t wprojectName[MAX_PATH];
149	MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
150	HMODULE hModule = GetModuleHandleW(NULL);
151	GetModuleFileNameW(hModule, wpath, MAX_PATH);
152	PathRemoveFileSpecW(wpath);
153	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
154	StringCchCatA(out, outLength, "\\portable.ini");
155	portable = VFileOpen(out, O_RDONLY);
156	if (portable) {
157		portable->close(portable);
158	} else {
159		wchar_t* home;
160		SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &home);
161		StringCchPrintfW(wpath, MAX_PATH, L"%ws\\%ws", home, wprojectName);
162		CoTaskMemFree(home);
163		CreateDirectoryW(wpath, NULL);
164	}
165	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
166#endif
167}
168
169const char* GBAConfigGetValue(const struct GBAConfig* config, const char* key) {
170	return _lookupValue(config, key);
171}
172
173void GBAConfigSetValue(struct GBAConfig* config, const char* key, const char* value) {
174	ConfigurationSetValue(&config->configTable, config->port, key, value);
175}
176
177void GBAConfigSetIntValue(struct GBAConfig* config, const char* key, int value) {
178	ConfigurationSetIntValue(&config->configTable, config->port, key, value);
179}
180
181void GBAConfigSetUIntValue(struct GBAConfig* config, const char* key, unsigned value) {
182	ConfigurationSetUIntValue(&config->configTable, config->port, key, value);
183}
184
185void GBAConfigSetFloatValue(struct GBAConfig* config, const char* key, float value) {
186	ConfigurationSetFloatValue(&config->configTable, config->port, key, value);
187}
188
189void GBAConfigSetDefaultValue(struct GBAConfig* config, const char* key, const char* value) {
190	ConfigurationSetValue(&config->defaultsTable, config->port, key, value);
191}
192
193void GBAConfigSetDefaultIntValue(struct GBAConfig* config, const char* key, int value) {
194	ConfigurationSetIntValue(&config->defaultsTable, config->port, key, value);
195}
196
197void GBAConfigSetDefaultUIntValue(struct GBAConfig* config, const char* key, unsigned value) {
198	ConfigurationSetUIntValue(&config->defaultsTable, config->port, key, value);
199}
200
201void GBAConfigSetDefaultFloatValue(struct GBAConfig* config, const char* key, float value) {
202	ConfigurationSetFloatValue(&config->defaultsTable, config->port, key, value);
203}
204
205void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
206	_lookupCharValue(config, "bios", &opts->bios);
207	_lookupIntValue(config, "logLevel", &opts->logLevel);
208	_lookupIntValue(config, "frameskip", &opts->frameskip);
209	_lookupIntValue(config, "volume", &opts->volume);
210	_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
211	_lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval);
212	_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
213	unsigned audioBuffers;
214	if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
215		opts->audioBuffers = audioBuffers;
216	}
217
218	int fakeBool;
219	if (_lookupIntValue(config, "useBios", &fakeBool)) {
220		opts->useBios = fakeBool;
221	}
222	if (_lookupIntValue(config, "audioSync", &fakeBool)) {
223		opts->audioSync = fakeBool;
224	}
225	if (_lookupIntValue(config, "videoSync", &fakeBool)) {
226		opts->videoSync = fakeBool;
227	}
228	if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
229		opts->lockAspectRatio = fakeBool;
230	}
231	if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
232		opts->resampleVideo = fakeBool;
233	}
234	if (_lookupIntValue(config, "suspendScreensaver", &fakeBool)) {
235		opts->suspendScreensaver = fakeBool;
236	}
237	if (_lookupIntValue(config, "mute", &fakeBool)) {
238		opts->mute = fakeBool;
239	}
240	if (_lookupIntValue(config, "skipBios", &fakeBool)) {
241		opts->skipBios = fakeBool;
242	}
243	if (_lookupIntValue(config, "rewindEnable", &fakeBool)) {
244		opts->rewindEnable = fakeBool;
245	}
246
247	_lookupIntValue(config, "fullscreen", &opts->fullscreen);
248	_lookupIntValue(config, "width", &opts->width);
249	_lookupIntValue(config, "height", &opts->height);
250
251	char* idleOptimization = 0;
252	if (_lookupCharValue(config, "idleOptimization", &idleOptimization)) {
253		if (strcasecmp(idleOptimization, "ignore") == 0) {
254			opts->idleOptimization = IDLE_LOOP_IGNORE;
255		} else if (strcasecmp(idleOptimization, "remove") == 0) {
256			opts->idleOptimization = IDLE_LOOP_REMOVE;
257		} else if (strcasecmp(idleOptimization, "detect") == 0) {
258			opts->idleOptimization = IDLE_LOOP_DETECT;
259		}
260		free(idleOptimization);
261	}
262}
263
264void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* opts) {
265	ConfigurationSetValue(&config->defaultsTable, 0, "bios", opts->bios);
266	ConfigurationSetIntValue(&config->defaultsTable, 0, "skipBios", opts->skipBios);
267	ConfigurationSetIntValue(&config->defaultsTable, 0, "useBios", opts->useBios);
268	ConfigurationSetIntValue(&config->defaultsTable, 0, "logLevel", opts->logLevel);
269	ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
270	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
271	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
272	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
273	ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
274	ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
275	ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync);
276	ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
277	ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
278	ConfigurationSetIntValue(&config->defaultsTable, 0, "width", opts->width);
279	ConfigurationSetIntValue(&config->defaultsTable, 0, "height", opts->height);
280	ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
281	ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
282	ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
283	ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
284	ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
285
286	switch (opts->idleOptimization) {
287	case IDLE_LOOP_IGNORE:
288		ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "ignore");
289		break;
290	case IDLE_LOOP_REMOVE:
291		ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "remove");
292		break;
293	case IDLE_LOOP_DETECT:
294		ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "detect");
295		break;
296	}
297}
298
299// These two are basically placeholders in case the internal layout changes, e.g. for loading separate files
300struct Configuration* GBAConfigGetInput(struct GBAConfig* config) {
301	return &config->configTable;
302}
303
304struct Configuration* GBAConfigGetOverrides(struct GBAConfig* config) {
305	return &config->configTable;
306}
307
308void GBAConfigFreeOpts(struct GBAOptions* opts) {
309	free(opts->bios);
310	opts->bios = 0;
311}