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