src/gba/supervisor/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 <shlobj.h>
17#include <strsafe.h>
18#endif
19
20#define SECTION_NAME_MAX 128
21
22static const char* _lookupValue(const struct GBAConfig* config, const char* key) {
23 const char* value;
24 if (config->port) {
25 value = ConfigurationGetValue(&config->configTable, config->port, key);
26 if (value) {
27 return value;
28 }
29 }
30 value = ConfigurationGetValue(&config->configTable, 0, key);
31 if (value) {
32 return value;
33 }
34 if (config->port) {
35 value = ConfigurationGetValue(&config->defaultsTable, config->port, key);
36 if (value) {
37 return value;
38 }
39 }
40 return ConfigurationGetValue(&config->defaultsTable, 0, key);
41}
42
43static bool _lookupCharValue(const struct GBAConfig* config, const char* key, char** out) {
44 const char* value = _lookupValue(config, key);
45 if (!value) {
46 return false;
47 }
48 if (*out) {
49 free(*out);
50 }
51 *out = strdup(value);
52 return true;
53}
54
55static bool _lookupIntValue(const struct GBAConfig* config, const char* key, int* out) {
56 const char* charValue = _lookupValue(config, key);
57 if (!charValue) {
58 return false;
59 }
60 char* end;
61 long value = strtol(charValue, &end, 10);
62 if (*end) {
63 return false;
64 }
65 *out = value;
66 return true;
67}
68
69static bool _lookupUIntValue(const struct GBAConfig* config, const char* key, unsigned* out) {
70 const char* charValue = _lookupValue(config, key);
71 if (!charValue) {
72 return false;
73 }
74 char* end;
75 unsigned long value = strtoul(charValue, &end, 10);
76 if (*end) {
77 return false;
78 }
79 *out = value;
80 return true;
81}
82
83static bool _lookupFloatValue(const struct GBAConfig* config, const char* key, float* out) {
84 const char* charValue = _lookupValue(config, key);
85 if (!charValue) {
86 return false;
87 }
88 char* end;
89 float value = strtof_u(charValue, &end);
90 if (*end) {
91 return false;
92 }
93 *out = value;
94 return true;
95}
96
97void GBAConfigInit(struct GBAConfig* config, const char* port) {
98 ConfigurationInit(&config->configTable);
99 ConfigurationInit(&config->defaultsTable);
100 if (port) {
101 config->port = malloc(strlen("ports.") + strlen(port) + 1);
102 snprintf(config->port, strlen("ports.") + strlen(port) + 1, "ports.%s", port);
103 } else {
104 config->port = 0;
105 }
106}
107
108void GBAConfigDeinit(struct GBAConfig* config) {
109 ConfigurationDeinit(&config->configTable);
110 ConfigurationDeinit(&config->defaultsTable);
111 free(config->port);
112}
113
114bool GBAConfigLoad(struct GBAConfig* config) {
115 char path[PATH_MAX];
116 GBAConfigDirectory(path, PATH_MAX);
117 strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
118 return ConfigurationRead(&config->configTable, path);
119}
120
121bool GBAConfigSave(const struct GBAConfig* config) {
122 char path[PATH_MAX];
123 GBAConfigDirectory(path, PATH_MAX);
124 strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
125 return ConfigurationWrite(&config->configTable, path);
126}
127
128void GBAConfigDirectory(char* out, size_t outLength) {
129#ifndef _WIN32
130 char* home = getenv("HOME");
131 snprintf(out, outLength, "%s/.config", home);
132 mkdir(out, 0755);
133 snprintf(out, outLength, "%s/.config/%s", home, binaryName);
134 mkdir(out, 0755);
135#else
136 wchar_t* home;
137 wchar_t wpath[MAX_PATH];
138 wchar_t wprojectName[MAX_PATH];
139 SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &home);
140 MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH);
141 StringCchPrintfW(wpath, MAX_PATH, L"%ws\\%ws", home, wprojectName);
142 CoTaskMemFree(home);
143 CreateDirectoryW(wpath, NULL);
144 WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0);
145#endif
146}
147
148const char* GBAConfigGetValue(const struct GBAConfig* config, const char* key) {
149 return _lookupValue(config, key);
150}
151
152void GBAConfigSetValue(struct GBAConfig* config, const char* key, const char* value) {
153 ConfigurationSetValue(&config->configTable, config->port, key, value);
154}
155
156void GBAConfigSetIntValue(struct GBAConfig* config, const char* key, int value) {
157 ConfigurationSetIntValue(&config->configTable, config->port, key, value);
158}
159
160void GBAConfigSetUIntValue(struct GBAConfig* config, const char* key, unsigned value) {
161 ConfigurationSetUIntValue(&config->configTable, config->port, key, value);
162}
163
164void GBAConfigSetFloatValue(struct GBAConfig* config, const char* key, float value) {
165 ConfigurationSetFloatValue(&config->configTable, config->port, key, value);
166}
167
168void GBAConfigSetDefaultValue(struct GBAConfig* config, const char* key, const char* value) {
169 ConfigurationSetValue(&config->defaultsTable, config->port, key, value);
170}
171
172void GBAConfigSetDefaultIntValue(struct GBAConfig* config, const char* key, int value) {
173 ConfigurationSetIntValue(&config->defaultsTable, config->port, key, value);
174}
175
176void GBAConfigSetDefaultUIntValue(struct GBAConfig* config, const char* key, unsigned value) {
177 ConfigurationSetUIntValue(&config->defaultsTable, config->port, key, value);
178}
179
180void GBAConfigSetDefaultFloatValue(struct GBAConfig* config, const char* key, float value) {
181 ConfigurationSetFloatValue(&config->defaultsTable, config->port, key, value);
182}
183
184void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
185 _lookupCharValue(config, "bios", &opts->bios);
186 _lookupIntValue(config, "logLevel", &opts->logLevel);
187 _lookupIntValue(config, "frameskip", &opts->frameskip);
188 _lookupIntValue(config, "volume", &opts->volume);
189 _lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
190 _lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval);
191 _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
192 unsigned audioBuffers;
193 if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
194 opts->audioBuffers = audioBuffers;
195 }
196
197 int fakeBool;
198 if (_lookupIntValue(config, "useBios", &fakeBool)) {
199 opts->useBios = fakeBool;
200 }
201 if (_lookupIntValue(config, "audioSync", &fakeBool)) {
202 opts->audioSync = fakeBool;
203 }
204 if (_lookupIntValue(config, "videoSync", &fakeBool)) {
205 opts->videoSync = fakeBool;
206 }
207 if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
208 opts->lockAspectRatio = fakeBool;
209 }
210 if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
211 opts->resampleVideo = fakeBool;
212 }
213 if (_lookupIntValue(config, "suspendScreensaver", &fakeBool)) {
214 opts->suspendScreensaver = fakeBool;
215 }
216 if (_lookupIntValue(config, "mute", &fakeBool)) {
217 opts->mute = fakeBool;
218 }
219 if (_lookupIntValue(config, "skipBios", &fakeBool)) {
220 opts->skipBios = fakeBool;
221 }
222 if (_lookupIntValue(config, "rewindEnable", &fakeBool)) {
223 opts->rewindEnable = fakeBool;
224 }
225
226 _lookupIntValue(config, "fullscreen", &opts->fullscreen);
227 _lookupIntValue(config, "width", &opts->width);
228 _lookupIntValue(config, "height", &opts->height);
229
230 char* idleOptimization = 0;
231 if (_lookupCharValue(config, "idleOptimization", &idleOptimization)) {
232 if (strcasecmp(idleOptimization, "ignore") == 0) {
233 opts->idleOptimization = IDLE_LOOP_IGNORE;
234 } else if (strcasecmp(idleOptimization, "remove") == 0) {
235 opts->idleOptimization = IDLE_LOOP_REMOVE;
236 } else if (strcasecmp(idleOptimization, "detect") == 0) {
237 opts->idleOptimization = IDLE_LOOP_DETECT;
238 }
239 free(idleOptimization);
240 }
241}
242
243void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* opts) {
244 ConfigurationSetValue(&config->defaultsTable, 0, "bios", opts->bios);
245 ConfigurationSetIntValue(&config->defaultsTable, 0, "skipBios", opts->skipBios);
246 ConfigurationSetIntValue(&config->defaultsTable, 0, "useBios", opts->useBios);
247 ConfigurationSetIntValue(&config->defaultsTable, 0, "logLevel", opts->logLevel);
248 ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
249 ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
250 ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
251 ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
252 ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
253 ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
254 ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync);
255 ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
256 ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);
257 ConfigurationSetIntValue(&config->defaultsTable, 0, "width", opts->width);
258 ConfigurationSetIntValue(&config->defaultsTable, 0, "height", opts->height);
259 ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
260 ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
261 ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
262 ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
263 ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
264
265 switch (opts->idleOptimization) {
266 case IDLE_LOOP_IGNORE:
267 ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "ignore");
268 break;
269 case IDLE_LOOP_REMOVE:
270 ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "remove");
271 break;
272 case IDLE_LOOP_DETECT:
273 ConfigurationSetValue(&config->defaultsTable, 0, "idleOptimization", "detect");
274 break;
275 }
276}
277
278// These two are basically placeholders in case the internal layout changes, e.g. for loading separate files
279struct Configuration* GBAConfigGetInput(struct GBAConfig* config) {
280 return &config->configTable;
281}
282
283struct Configuration* GBAConfigGetOverrides(struct GBAConfig* config) {
284 return &config->configTable;
285}
286
287void GBAConfigFreeOpts(struct GBAOptions* opts) {
288 free(opts->bios);
289 opts->bios = 0;
290}