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