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