all repos — mgba @ 51f9a76ab212514ba8035a85b60af4b963fabc11

mGBA Game Boy Advance Emulator

src/core/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 <mgba/core/config.h>
  7
  8#include <mgba/core/version.h>
  9#include <mgba-util/formatting.h>
 10#include <mgba-util/string.h>
 11#include <mgba-util/vfs.h>
 12
 13#include <sys/stat.h>
 14
 15#ifdef _WIN32
 16#include <windows.h>
 17#include <shlwapi.h>
 18#include <shlobj.h>
 19#include <strsafe.h>
 20#endif
 21
 22#ifdef PSP2
 23#include <psp2/io/stat.h>
 24#endif
 25
 26#ifdef _3DS
 27#include <mgba-util/platform/3ds/3ds-vfs.h>
 28#endif
 29
 30#define SECTION_NAME_MAX 128
 31
 32struct mCoreConfigEnumerateData {
 33	void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user);
 34	const char* prefix;
 35	void* user;
 36	enum mCoreConfigLevel level;
 37};
 38
 39static const char* _lookupValue(const struct mCoreConfig* config, const char* key) {
 40	const char* value;
 41	if (config->port) {
 42		value = ConfigurationGetValue(&config->overridesTable, config->port, key);
 43		if (value) {
 44			return value;
 45		}
 46	}
 47	value = ConfigurationGetValue(&config->overridesTable, 0, key);
 48	if (value) {
 49		return value;
 50	}
 51	if (config->port) {
 52		value = ConfigurationGetValue(&config->configTable, config->port, key);
 53		if (value) {
 54			return value;
 55		}
 56	}
 57	value = ConfigurationGetValue(&config->configTable, 0, key);
 58	if (value) {
 59		return value;
 60	}
 61	if (config->port) {
 62		value = ConfigurationGetValue(&config->defaultsTable, config->port, key);
 63		if (value) {
 64			return value;
 65		}
 66	}
 67	return ConfigurationGetValue(&config->defaultsTable, 0, key);
 68}
 69
 70static bool _lookupCharValue(const struct mCoreConfig* config, const char* key, char** out) {
 71	const char* value = _lookupValue(config, key);
 72	if (!value) {
 73		return false;
 74	}
 75	if (*out) {
 76		free(*out);
 77	}
 78	*out = strdup(value);
 79	return true;
 80}
 81
 82static bool _lookupIntValue(const struct mCoreConfig* config, const char* key, int* out) {
 83	const char* charValue = _lookupValue(config, key);
 84	if (!charValue) {
 85		return false;
 86	}
 87	char* end;
 88	long value = strtol(charValue, &end, 10);
 89	if (end == &charValue[1] && *end == 'x') {
 90		value = strtol(charValue, &end, 16);
 91	}
 92	if (*end) {
 93		return false;
 94	}
 95	*out = value;
 96	return true;
 97}
 98
 99static bool _lookupUIntValue(const struct mCoreConfig* config, const char* key, unsigned* out) {
100	const char* charValue = _lookupValue(config, key);
101	if (!charValue) {
102		return false;
103	}
104	char* end;
105	unsigned long value = strtoul(charValue, &end, 10);
106	if (*end) {
107		return false;
108	}
109	*out = value;
110	return true;
111}
112
113static bool _lookupFloatValue(const struct mCoreConfig* config, const char* key, float* out) {
114	const char* charValue = _lookupValue(config, key);
115	if (!charValue) {
116		return false;
117	}
118	char* end;
119	float value = strtof_u(charValue, &end);
120	if (*end) {
121		return false;
122	}
123	*out = value;
124	return true;
125}
126
127void mCoreConfigInit(struct mCoreConfig* config, const char* port) {
128	ConfigurationInit(&config->configTable);
129	ConfigurationInit(&config->defaultsTable);
130	ConfigurationInit(&config->overridesTable);
131	if (port) {
132		config->port = malloc(strlen("ports.") + strlen(port) + 1);
133		snprintf(config->port, strlen("ports.") + strlen(port) + 1, "ports.%s", port);
134	} else {
135		config->port = 0;
136	}
137}
138
139void mCoreConfigDeinit(struct mCoreConfig* config) {
140	ConfigurationDeinit(&config->configTable);
141	ConfigurationDeinit(&config->defaultsTable);
142	ConfigurationDeinit(&config->overridesTable);
143	free(config->port);
144}
145
146#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
147bool mCoreConfigLoad(struct mCoreConfig* config) {
148	char path[PATH_MAX];
149	mCoreConfigDirectory(path, PATH_MAX);
150	strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
151	return mCoreConfigLoadPath(config, path);
152}
153
154bool mCoreConfigSave(const struct mCoreConfig* config) {
155	char path[PATH_MAX];
156	mCoreConfigDirectory(path, PATH_MAX);
157	strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
158	return mCoreConfigSavePath(config, path);
159}
160
161bool mCoreConfigLoadPath(struct mCoreConfig* config, const char* path) {
162	return ConfigurationRead(&config->configTable, path);
163}
164
165bool mCoreConfigSavePath(const struct mCoreConfig* config, const char* path) {
166	return ConfigurationWrite(&config->configTable, path);
167}
168
169void mCoreConfigMakePortable(const struct mCoreConfig* config) {
170	struct VFile* portable = 0;
171#ifdef _WIN32
172	char out[MAX_PATH];
173	wchar_t wpath[MAX_PATH];
174	wchar_t wprojectName[MAX_PATH];
175	MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
176	HMODULE hModule = GetModuleHandleW(NULL);
177	GetModuleFileNameW(hModule, wpath, MAX_PATH);
178	PathRemoveFileSpecW(wpath);
179	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0);
180	StringCchCatA(out, MAX_PATH, "\\portable.ini");
181	portable = VFileOpen(out, O_WRONLY | O_CREAT);
182#elif defined(PSP2) || defined(_3DS) || defined(__SWITCH__) || defined(GEKKO)
183	// Already portable
184#else
185	char out[PATH_MAX];
186	getcwd(out, PATH_MAX);
187	strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out));
188	portable = VFileOpen(out, O_WRONLY | O_CREAT);
189#endif
190	if (portable) {
191		portable->close(portable);
192		mCoreConfigSave(config);
193	}
194}
195
196void mCoreConfigDirectory(char* out, size_t outLength) {
197	struct VFile* portable;
198#ifdef _WIN32
199	wchar_t wpath[MAX_PATH];
200	wchar_t wprojectName[MAX_PATH];
201	MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
202	HMODULE hModule = GetModuleHandleW(NULL);
203	GetModuleFileNameW(hModule, wpath, MAX_PATH);
204	PathRemoveFileSpecW(wpath);
205	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
206	StringCchCatA(out, outLength, "\\portable.ini");
207	portable = VFileOpen(out, O_RDONLY);
208	if (portable) {
209		portable->close(portable);
210	} else {
211		wchar_t* home;
212		SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &home);
213		StringCchPrintfW(wpath, MAX_PATH, L"%ws\\%ws", home, wprojectName);
214		CoTaskMemFree(home);
215		CreateDirectoryW(wpath, NULL);
216	}
217	WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
218#elif defined(PSP2)
219	UNUSED(portable);
220	snprintf(out, outLength, "ux0:data/%s", projectName);
221	sceIoMkdir(out, 0777);
222#elif defined(GEKKO) || defined(__SWITCH__)
223	UNUSED(portable);
224	snprintf(out, outLength, "/%s", projectName);
225	mkdir(out, 0777);
226#elif defined(_3DS)
227	UNUSED(portable);
228	snprintf(out, outLength, "/%s", projectName);
229	FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, out), 0);
230#else
231	getcwd(out, outLength);
232	strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));
233	portable = VFileOpen(out, O_RDONLY);
234	if (portable) {
235		getcwd(out, outLength);
236		portable->close(portable);
237		return;
238	}
239
240	char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
241	if (xdgConfigHome && xdgConfigHome[0] == '/') {
242		snprintf(out, outLength, "%s/%s", xdgConfigHome, binaryName);
243		mkdir(out, 0755);
244		return;
245	}
246	char* home = getenv("HOME");
247	snprintf(out, outLength, "%s/.config", home);
248	mkdir(out, 0755);
249	snprintf(out, outLength, "%s/.config/%s", home, binaryName);
250	mkdir(out, 0755);
251#endif
252}
253#endif
254
255const char* mCoreConfigGetValue(const struct mCoreConfig* config, const char* key) {
256	return _lookupValue(config, key);
257}
258
259bool mCoreConfigGetIntValue(const struct mCoreConfig* config, const char* key, int* value) {
260	return _lookupIntValue(config, key, value);
261}
262
263bool mCoreConfigGetUIntValue(const struct mCoreConfig* config, const char* key, unsigned* value) {
264	return _lookupUIntValue(config, key, value);
265}
266
267bool mCoreConfigGetFloatValue(const struct mCoreConfig* config, const char* key, float* value) {
268	return _lookupFloatValue(config, key, value);
269}
270
271void mCoreConfigSetValue(struct mCoreConfig* config, const char* key, const char* value) {
272	ConfigurationSetValue(&config->configTable, config->port, key, value);
273}
274
275void mCoreConfigSetIntValue(struct mCoreConfig* config, const char* key, int value) {
276	ConfigurationSetIntValue(&config->configTable, config->port, key, value);
277}
278
279void mCoreConfigSetUIntValue(struct mCoreConfig* config, const char* key, unsigned value) {
280	ConfigurationSetUIntValue(&config->configTable, config->port, key, value);
281}
282
283void mCoreConfigSetFloatValue(struct mCoreConfig* config, const char* key, float value) {
284	ConfigurationSetFloatValue(&config->configTable, config->port, key, value);
285}
286
287void mCoreConfigSetDefaultValue(struct mCoreConfig* config, const char* key, const char* value) {
288	ConfigurationSetValue(&config->defaultsTable, config->port, key, value);
289}
290
291void mCoreConfigSetDefaultIntValue(struct mCoreConfig* config, const char* key, int value) {
292	ConfigurationSetIntValue(&config->defaultsTable, config->port, key, value);
293}
294
295void mCoreConfigSetDefaultUIntValue(struct mCoreConfig* config, const char* key, unsigned value) {
296	ConfigurationSetUIntValue(&config->defaultsTable, config->port, key, value);
297}
298
299void mCoreConfigSetDefaultFloatValue(struct mCoreConfig* config, const char* key, float value) {
300	ConfigurationSetFloatValue(&config->defaultsTable, config->port, key, value);
301}
302
303void mCoreConfigSetOverrideValue(struct mCoreConfig* config, const char* key, const char* value) {
304	ConfigurationSetValue(&config->overridesTable, config->port, key, value);
305}
306
307void mCoreConfigSetOverrideIntValue(struct mCoreConfig* config, const char* key, int value) {
308	ConfigurationSetIntValue(&config->overridesTable, config->port, key, value);
309}
310
311void mCoreConfigSetOverrideUIntValue(struct mCoreConfig* config, const char* key, unsigned value) {
312	ConfigurationSetUIntValue(&config->overridesTable, config->port, key, value);
313}
314
315void mCoreConfigSetOverrideFloatValue(struct mCoreConfig* config, const char* key, float value) {
316	ConfigurationSetFloatValue(&config->overridesTable, config->port, key, value);
317}
318
319void mCoreConfigCopyValue(struct mCoreConfig* config, const struct mCoreConfig* src, const char* key) {
320	const char* value = mCoreConfigGetValue(src, key);
321	if (!value) {
322		return;
323	}
324	mCoreConfigSetValue(config, key, value);
325}
326
327void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts) {
328	_lookupCharValue(config, "bios", &opts->bios);
329	_lookupCharValue(config, "shader", &opts->shader);
330	_lookupIntValue(config, "logLevel", &opts->logLevel);
331	_lookupIntValue(config, "frameskip", &opts->frameskip);
332	_lookupIntValue(config, "volume", &opts->volume);
333	_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
334	_lookupIntValue(config, "rewindSave", &opts->rewindSave);
335	_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
336	unsigned audioBuffers;
337	if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
338		opts->audioBuffers = audioBuffers;
339	}
340	_lookupUIntValue(config, "sampleRate", &opts->sampleRate);
341
342	int fakeBool;
343	if (_lookupIntValue(config, "useBios", &fakeBool)) {
344		opts->useBios = fakeBool;
345	}
346	if (_lookupIntValue(config, "audioSync", &fakeBool)) {
347		opts->audioSync = fakeBool;
348	}
349	if (_lookupIntValue(config, "videoSync", &fakeBool)) {
350		opts->videoSync = fakeBool;
351	}
352	if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
353		opts->lockAspectRatio = fakeBool;
354	}
355	if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) {
356		opts->lockIntegerScaling = fakeBool;
357	}
358	if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
359		opts->resampleVideo = fakeBool;
360	}
361	if (_lookupIntValue(config, "suspendScreensaver", &fakeBool)) {
362		opts->suspendScreensaver = fakeBool;
363	}
364	if (_lookupIntValue(config, "mute", &fakeBool)) {
365		opts->mute = fakeBool;
366	}
367	if (_lookupIntValue(config, "skipBios", &fakeBool)) {
368		opts->skipBios = fakeBool;
369	}
370	if (_lookupIntValue(config, "rewindEnable", &fakeBool)) {
371		opts->rewindEnable = fakeBool;
372	}
373
374	_lookupIntValue(config, "fullscreen", &opts->fullscreen);
375	_lookupIntValue(config, "width", &opts->width);
376	_lookupIntValue(config, "height", &opts->height);
377
378	_lookupCharValue(config, "savegamePath", &opts->savegamePath);
379	_lookupCharValue(config, "savestatePath", &opts->savestatePath);
380	_lookupCharValue(config, "screenshotPath", &opts->screenshotPath);
381	_lookupCharValue(config, "patchPath", &opts->patchPath);
382	_lookupCharValue(config, "cheatsPath", &opts->cheatsPath);
383}
384
385void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptions* opts) {
386	ConfigurationSetValue(&config->defaultsTable, 0, "bios", opts->bios);
387	ConfigurationSetValue(&config->defaultsTable, 0, "shader", opts->shader);
388	ConfigurationSetIntValue(&config->defaultsTable, 0, "skipBios", opts->skipBios);
389	ConfigurationSetIntValue(&config->defaultsTable, 0, "useBios", opts->useBios);
390	ConfigurationSetIntValue(&config->defaultsTable, 0, "logLevel", opts->logLevel);
391	ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
392	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
393	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
394	ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindSave", opts->rewindSave);
395	ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
396	ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
397	ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
398	ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync);
399	ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
400	ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
401	ConfigurationSetIntValue(&config->defaultsTable, 0, "width", opts->width);
402	ConfigurationSetIntValue(&config->defaultsTable, 0, "height", opts->height);
403	ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
404	ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
405	ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
406	ConfigurationSetIntValue(&config->defaultsTable, 0, "lockIntegerScaling", opts->lockIntegerScaling);
407	ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
408	ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
409}
410
411static void _configEnum(const char* key, const char* value, void* user) {
412	struct mCoreConfigEnumerateData* data = user;
413	if (!data->prefix || startswith(key, data->prefix)) {
414		data->handler(key, value, data->level, data->user);
415	}
416}
417
418void mCoreConfigEnumerate(const struct mCoreConfig* config, const char* prefix, void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user), void* user) {
419	struct mCoreConfigEnumerateData handlerData = { handler, prefix, user, mCONFIG_LEVEL_DEFAULT };
420	ConfigurationEnumerate(&config->defaultsTable, config->port, _configEnum, &handlerData);
421	handlerData.level = mCONFIG_LEVEL_CUSTOM;
422	ConfigurationEnumerate(&config->configTable, config->port, _configEnum, &handlerData);
423	handlerData.level = mCONFIG_LEVEL_OVERRIDE;
424	ConfigurationEnumerate(&config->overridesTable, config->port, _configEnum, &handlerData);
425}
426
427// These two are basically placeholders in case the internal layout changes, e.g. for loading separate files
428struct Configuration* mCoreConfigGetInput(struct mCoreConfig* config) {
429	return &config->configTable;
430}
431
432struct Configuration* mCoreConfigGetOverrides(struct mCoreConfig* config) {
433	return &config->configTable;
434}
435
436const struct Configuration* mCoreConfigGetOverridesConst(const struct mCoreConfig* config) {
437	return &config->configTable;
438}
439
440void mCoreConfigFreeOpts(struct mCoreOptions* opts) {
441	free(opts->bios);
442	free(opts->shader);
443	free(opts->savegamePath);
444	free(opts->savestatePath);
445	free(opts->screenshotPath);
446	free(opts->patchPath);
447	free(opts->cheatsPath);
448	opts->bios = 0;
449	opts->shader = 0;
450	opts->savegamePath = 0;
451	opts->savestatePath = 0;
452	opts->screenshotPath = 0;
453	opts->patchPath = 0;
454	opts->cheatsPath = 0;
455}