all repos — mgba @ 540e1ff1e65e7616daf94eab19a0994fbbc5bb24

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