all repos — mgba @ a38beac307b7b42dbc5ad5806a92b91396bea555

mGBA Game Boy Advance Emulator

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