all repos — mgba @ 33ca1e2e9ce98d63ecbd6ff3e815197ade0c6a1b

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 GBAConfigMakePortable(const struct GBAConfig* config) {
130	struct VFile* portable;
131#ifndef _WIN32
132	char out[PATH_MAX];
133	getcwd(out, PATH_MAX);
134	strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out));
135	portable = VFileOpen(out, O_WRONLY | O_CREAT);
136#else
137	char out[MAX_PATH];
138	wchar_t wpath[MAX_PATH];
139	wchar_t wprojectName[MAX_PATH];
140	MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
141	HMODULE hModule = GetModuleHandleW(NULL);
142	GetModuleFileNameW(hModule, wpath, MAX_PATH);
143	PathRemoveFileSpecW(wpath);
144	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0);
145	StringCchCatA(out, MAX_PATH, "\\portable.ini");
146	portable = VFileOpen(out, O_WRONLY | O_CREAT);
147#endif
148	if (portable) {
149		portable->close(portable);
150		GBAConfigSave(config);
151	}
152}
153
154void GBAConfigDirectory(char* out, size_t outLength) {
155	struct VFile* portable;
156#ifndef _WIN32
157	getcwd(out, outLength);
158	strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));
159	portable = VFileOpen(out, O_RDONLY);
160	if (portable) {
161		getcwd(out, outLength);
162		portable->close(portable);
163		return;
164	}
165
166	char* home = getenv("HOME");
167	snprintf(out, outLength, "%s/.config", home);
168	mkdir(out, 0755);
169	snprintf(out, outLength, "%s/.config/%s", home, binaryName);
170	mkdir(out, 0755);
171#else
172	wchar_t wpath[MAX_PATH];
173	wchar_t wprojectName[MAX_PATH];
174	MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
175	HMODULE hModule = GetModuleHandleW(NULL);
176	GetModuleFileNameW(hModule, wpath, MAX_PATH);
177	PathRemoveFileSpecW(wpath);
178	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
179	StringCchCatA(out, outLength, "\\portable.ini");
180	portable = VFileOpen(out, O_RDONLY);
181	if (portable) {
182		portable->close(portable);
183	} else {
184		wchar_t* home;
185		SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &home);
186		StringCchPrintfW(wpath, MAX_PATH, L"%ws\\%ws", home, wprojectName);
187		CoTaskMemFree(home);
188		CreateDirectoryW(wpath, NULL);
189	}
190	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
191#endif
192}
193
194const char* GBAConfigGetValue(const struct GBAConfig* config, const char* key) {
195	return _lookupValue(config, key);
196}
197
198void GBAConfigSetValue(struct GBAConfig* config, const char* key, const char* value) {
199	ConfigurationSetValue(&config->configTable, config->port, key, value);
200}
201
202void GBAConfigSetIntValue(struct GBAConfig* config, const char* key, int value) {
203	ConfigurationSetIntValue(&config->configTable, config->port, key, value);
204}
205
206void GBAConfigSetUIntValue(struct GBAConfig* config, const char* key, unsigned value) {
207	ConfigurationSetUIntValue(&config->configTable, config->port, key, value);
208}
209
210void GBAConfigSetFloatValue(struct GBAConfig* config, const char* key, float value) {
211	ConfigurationSetFloatValue(&config->configTable, config->port, key, value);
212}
213
214void GBAConfigSetDefaultValue(struct GBAConfig* config, const char* key, const char* value) {
215	ConfigurationSetValue(&config->defaultsTable, config->port, key, value);
216}
217
218void GBAConfigSetDefaultIntValue(struct GBAConfig* config, const char* key, int value) {
219	ConfigurationSetIntValue(&config->defaultsTable, config->port, key, value);
220}
221
222void GBAConfigSetDefaultUIntValue(struct GBAConfig* config, const char* key, unsigned value) {
223	ConfigurationSetUIntValue(&config->defaultsTable, config->port, key, value);
224}
225
226void GBAConfigSetDefaultFloatValue(struct GBAConfig* config, const char* key, float value) {
227	ConfigurationSetFloatValue(&config->defaultsTable, config->port, key, value);
228}
229
230void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
231	_lookupCharValue(config, "bios", &opts->bios);
232	_lookupIntValue(config, "logLevel", &opts->logLevel);
233	_lookupIntValue(config, "frameskip", &opts->frameskip);
234	_lookupIntValue(config, "volume", &opts->volume);
235	_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
236	_lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval);
237	_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
238	unsigned audioBuffers;
239	if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
240		opts->audioBuffers = audioBuffers;
241	}
242
243	int fakeBool;
244	if (_lookupIntValue(config, "useBios", &fakeBool)) {
245		opts->useBios = fakeBool;
246	}
247	if (_lookupIntValue(config, "audioSync", &fakeBool)) {
248		opts->audioSync = fakeBool;
249	}
250	if (_lookupIntValue(config, "videoSync", &fakeBool)) {
251		opts->videoSync = fakeBool;
252	}
253	if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
254		opts->lockAspectRatio = fakeBool;
255	}
256	if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
257		opts->resampleVideo = fakeBool;
258	}
259	if (_lookupIntValue(config, "suspendScreensaver", &fakeBool)) {
260		opts->suspendScreensaver = fakeBool;
261	}
262	if (_lookupIntValue(config, "mute", &fakeBool)) {
263		opts->mute = fakeBool;
264	}
265	if (_lookupIntValue(config, "skipBios", &fakeBool)) {
266		opts->skipBios = fakeBool;
267	}
268	if (_lookupIntValue(config, "rewindEnable", &fakeBool)) {
269		opts->rewindEnable = fakeBool;
270	}
271
272	_lookupIntValue(config, "fullscreen", &opts->fullscreen);
273	_lookupIntValue(config, "width", &opts->width);
274	_lookupIntValue(config, "height", &opts->height);
275
276	char* idleOptimization = 0;
277	if (_lookupCharValue(config, "idleOptimization", &idleOptimization)) {
278		if (strcasecmp(idleOptimization, "ignore") == 0) {
279			opts->idleOptimization = IDLE_LOOP_IGNORE;
280		} else if (strcasecmp(idleOptimization, "remove") == 0) {
281			opts->idleOptimization = IDLE_LOOP_REMOVE;
282		} else if (strcasecmp(idleOptimization, "detect") == 0) {
283			opts->idleOptimization = IDLE_LOOP_DETECT;
284		}
285		free(idleOptimization);
286	}
287}
288
289void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* opts) {
290	ConfigurationSetValue(&config->defaultsTable, 0, "bios", opts->bios);
291	ConfigurationSetIntValue(&config->defaultsTable, 0, "skipBios", opts->skipBios);
292	ConfigurationSetIntValue(&config->defaultsTable, 0, "useBios", opts->useBios);
293	ConfigurationSetIntValue(&config->defaultsTable, 0, "logLevel", opts->logLevel);
294	ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
295	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
296	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
297	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
298	ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
299	ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
300	ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync);
301	ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
302	ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
303	ConfigurationSetIntValue(&config->defaultsTable, 0, "width", opts->width);
304	ConfigurationSetIntValue(&config->defaultsTable, 0, "height", opts->height);
305	ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
306	ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
307	ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
308	ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
309	ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
310
311	switch (opts->idleOptimization) {
312	case IDLE_LOOP_IGNORE:
313		ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "ignore");
314		break;
315	case IDLE_LOOP_REMOVE:
316		ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "remove");
317		break;
318	case IDLE_LOOP_DETECT:
319		ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "detect");
320		break;
321	}
322}
323
324// These two are basically placeholders in case the internal layout changes, e.g. for loading separate files
325struct Configuration* GBAConfigGetInput(struct GBAConfig* config) {
326	return &config->configTable;
327}
328
329struct Configuration* GBAConfigGetOverrides(struct GBAConfig* config) {
330	return &config->configTable;
331}
332
333void GBAConfigFreeOpts(struct GBAOptions* opts) {
334	free(opts->bios);
335	opts->bios = 0;
336}