all repos — mgba @ f4a61f91d46b8a90a61ca0f9ef04f431f0a4fd9f

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