all repos — mgba @ b278bbb23d0f2ffcc93b598935532ed412babbdc

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