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