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