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