all repos — mgba @ d0c2d4e46ba6776c3572159329d50213baff2725

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