all repos — mgba @ 27898fb1f3f6cf4c0cd19a8d6c5b485bca589b70

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