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 _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
335 unsigned audioBuffers;
336 if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
337 opts->audioBuffers = audioBuffers;
338 }
339 _lookupUIntValue(config, "sampleRate", &opts->sampleRate);
340
341 int fakeBool;
342 if (_lookupIntValue(config, "useBios", &fakeBool)) {
343 opts->useBios = fakeBool;
344 }
345 if (_lookupIntValue(config, "audioSync", &fakeBool)) {
346 opts->audioSync = fakeBool;
347 }
348 if (_lookupIntValue(config, "videoSync", &fakeBool)) {
349 opts->videoSync = fakeBool;
350 }
351 if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
352 opts->lockAspectRatio = fakeBool;
353 }
354 if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) {
355 opts->lockIntegerScaling = fakeBool;
356 }
357 if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
358 opts->resampleVideo = fakeBool;
359 }
360 if (_lookupIntValue(config, "suspendScreensaver", &fakeBool)) {
361 opts->suspendScreensaver = fakeBool;
362 }
363 if (_lookupIntValue(config, "mute", &fakeBool)) {
364 opts->mute = fakeBool;
365 }
366 if (_lookupIntValue(config, "skipBios", &fakeBool)) {
367 opts->skipBios = fakeBool;
368 }
369 if (_lookupIntValue(config, "rewindEnable", &fakeBool)) {
370 opts->rewindEnable = fakeBool;
371 }
372
373 _lookupIntValue(config, "fullscreen", &opts->fullscreen);
374 _lookupIntValue(config, "width", &opts->width);
375 _lookupIntValue(config, "height", &opts->height);
376
377 _lookupCharValue(config, "savegamePath", &opts->savegamePath);
378 _lookupCharValue(config, "savestatePath", &opts->savestatePath);
379 _lookupCharValue(config, "screenshotPath", &opts->screenshotPath);
380 _lookupCharValue(config, "patchPath", &opts->patchPath);
381 _lookupCharValue(config, "cheatsPath", &opts->cheatsPath);
382}
383
384void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptions* opts) {
385 ConfigurationSetValue(&config->defaultsTable, 0, "bios", opts->bios);
386 ConfigurationSetValue(&config->defaultsTable, 0, "shader", opts->shader);
387 ConfigurationSetIntValue(&config->defaultsTable, 0, "skipBios", opts->skipBios);
388 ConfigurationSetIntValue(&config->defaultsTable, 0, "useBios", opts->useBios);
389 ConfigurationSetIntValue(&config->defaultsTable, 0, "logLevel", opts->logLevel);
390 ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
391 ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
392 ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
393 ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
394 ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
395 ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
396 ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync);
397 ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
398 ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
399 ConfigurationSetIntValue(&config->defaultsTable, 0, "width", opts->width);
400 ConfigurationSetIntValue(&config->defaultsTable, 0, "height", opts->height);
401 ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
402 ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
403 ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
404 ConfigurationSetIntValue(&config->defaultsTable, 0, "lockIntegerScaling", opts->lockIntegerScaling);
405 ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
406 ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
407}
408
409static void _configEnum(const char* key, const char* value, void* user) {
410 struct mCoreConfigEnumerateData* data = user;
411 if (!data->prefix || startswith(key, data->prefix)) {
412 data->handler(key, value, data->level, data->user);
413 }
414}
415
416void mCoreConfigEnumerate(const struct mCoreConfig* config, const char* prefix, void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user), void* user) {
417 struct mCoreConfigEnumerateData handlerData = { handler, prefix, user, mCONFIG_LEVEL_DEFAULT };
418 ConfigurationEnumerate(&config->defaultsTable, config->port, _configEnum, &handlerData);
419 handlerData.level = mCONFIG_LEVEL_CUSTOM;
420 ConfigurationEnumerate(&config->configTable, config->port, _configEnum, &handlerData);
421 handlerData.level = mCONFIG_LEVEL_OVERRIDE;
422 ConfigurationEnumerate(&config->overridesTable, config->port, _configEnum, &handlerData);
423}
424
425// These two are basically placeholders in case the internal layout changes, e.g. for loading separate files
426struct Configuration* mCoreConfigGetInput(struct mCoreConfig* config) {
427 return &config->configTable;
428}
429
430struct Configuration* mCoreConfigGetOverrides(struct mCoreConfig* config) {
431 return &config->configTable;
432}
433
434const struct Configuration* mCoreConfigGetOverridesConst(const struct mCoreConfig* config) {
435 return &config->configTable;
436}
437
438void mCoreConfigFreeOpts(struct mCoreOptions* opts) {
439 free(opts->bios);
440 free(opts->shader);
441 free(opts->savegamePath);
442 free(opts->savestatePath);
443 free(opts->screenshotPath);
444 free(opts->patchPath);
445 free(opts->cheatsPath);
446 opts->bios = 0;
447 opts->shader = 0;
448 opts->savegamePath = 0;
449 opts->savestatePath = 0;
450 opts->screenshotPath = 0;
451 opts->patchPath = 0;
452 opts->cheatsPath = 0;
453}