more input layer tweaks
[picodrive.git] / platform / common / config.c
CommitLineData
835e7900 1/*
2 * Human-readable config file management for PicoDrive
ca482e5d 3 * (c) notaz, 2008
835e7900 4 */
5
4609d0cd 6#include <stdio.h>
835e7900 7#include <string.h>
8#include <stdlib.h>
f8af9634 9#ifdef __EPOC32__
ca482e5d 10#include <unistd.h>
11#endif
835e7900 12#include "config.h"
4609d0cd 13#include "lprintf.h"
14
15static char *mystrip(char *str);
16
17#ifndef _MSC_VER
18
835e7900 19#include "menu.h"
20#include "emu.h"
efcba75f 21#include <pico/pico.h>
835e7900 22
23extern menu_entry opt_entries[];
24extern menu_entry opt2_entries[];
25extern menu_entry cdopt_entries[];
f0f0d2df 26extern menu_entry ctrlopt_entries[];
835e7900 27extern const int opt_entry_count;
28extern const int opt2_entry_count;
29extern const int cdopt_entry_count;
f0f0d2df 30extern const int ctrlopt_entry_count;
6fc57144 31#ifdef PSP
32extern menu_entry opt3_entries[];
33extern const int opt3_entry_count;
34#endif
835e7900 35
6fc57144 36static menu_entry *cfg_opts[] =
37{
38 opt_entries,
39 opt2_entries,
40 cdopt_entries,
f0f0d2df 41 ctrlopt_entries,
6fc57144 42#ifdef PSP
43 opt3_entries,
44#endif
45};
46
47static const int *cfg_opt_counts[] =
48{
49 &opt_entry_count,
50 &opt2_entry_count,
51 &cdopt_entry_count,
f0f0d2df 52 &ctrlopt_entry_count,
6fc57144 53#ifdef PSP
54 &opt3_entry_count,
55#endif
56};
835e7900 57
7b802576 58#define NL "\r\n"
835e7900 59
60
61static int seek_sect(FILE *f, const char *section)
62{
63 char line[128], *tmp;
64 int len;
65
66 len = strlen(section);
67 // seek to the section needed
68 while (!feof(f))
69 {
70 tmp = fgets(line, sizeof(line), f);
71 if (tmp == NULL) break;
72
73 if (line[0] != '[') continue; // not section start
74 if (strncmp(line + 1, section, len) == 0 && line[len+1] == ']')
75 return 1; // found it
76 }
77
78 return 0;
79}
80
81
c46ffd31 82static void custom_write(FILE *f, const menu_entry *me, int no_def)
835e7900 83{
84 char *str, str24[24];
85
86 switch (me->id)
87 {
88 case MA_OPT_RENDERER:
602133e1 89 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
835e7900 90 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
602133e1 91 if (PicoOpt&POPT_ALT_RENDERER)
6fc57144 92 str =
93#ifndef PSP
94 "8bit "
95#endif
96 "fast";
835e7900 97 else if (currentConfig.EmuOpt&0x80)
6fc57144 98 str =
99#ifndef PSP
100 "16bit "
101#endif
102 "accurate";
835e7900 103 else
104 str = "8bit accurate";
105 fprintf(f, "Renderer = %s", str);
106 break;
107
108 case MA_OPT_SCALING:
c46ffd31 109 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
6fc57144 110#ifdef __GP2X__
835e7900 111 switch (currentConfig.scaling) {
112 default: str = "OFF"; break;
113 case 1: str = "hw horizontal"; break;
114 case 2: str = "hw horiz. + vert."; break;
115 case 3: str = "sw horizontal"; break;
116 }
117 fprintf(f, "Scaling = %s", str);
6fc57144 118#endif
835e7900 119 break;
120 case MA_OPT_FRAMESKIP:
c46ffd31 121 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
835e7900 122 if (currentConfig.Frameskip < 0)
123 strcpy(str24, "Auto");
124 else sprintf(str24, "%i", currentConfig.Frameskip);
125 fprintf(f, "Frameskip = %s", str24);
126 break;
127 case MA_OPT_SOUND_QUALITY:
602133e1 128 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
835e7900 129 defaultConfig.s_PsndRate == PsndRate) return;
602133e1 130 str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
835e7900 131 fprintf(f, "Sound Quality = %i %s", PsndRate, str);
132 break;
133 case MA_OPT_REGION:
c46ffd31 134 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
835e7900 135 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
6fc57144 136 strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
137 fprintf(f, "Region = %s", mystrip(str24));
835e7900 138 break;
139 case MA_OPT_CONFIRM_STATES:
c46ffd31 140 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
835e7900 141 switch ((currentConfig.EmuOpt >> 9) & 5) {
142 default: str = "OFF"; break;
143 case 1: str = "writes"; break;
144 case 4: str = "loads"; break;
145 case 5: str = "both"; break;
146 }
147 fprintf(f, "Confirm savestate = %s", str);
148 break;
149 case MA_OPT_CPU_CLOCKS:
c46ffd31 150 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
6fc57144 151#ifdef __GP2X__
835e7900 152 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
6fc57144 153#elif defined(PSP)
154 fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
155#endif
835e7900 156 break;
157 case MA_OPT2_GAMMA:
c46ffd31 158 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
835e7900 159 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
160 break;
161 case MA_OPT2_SQUIDGEHACK:
c46ffd31 162 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
835e7900 163 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
164 break;
165 case MA_CDOPT_READAHEAD:
c46ffd31 166 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
835e7900 167 sprintf(str24, "%i", PicoCDBuffers * 2);
168 fprintf(f, "ReadAhead buffer = %s", str24);
169 break;
6fc57144 170 /* PSP */
171 case MA_OPT3_SCALE:
172 if (no_def && defaultConfig.scale == currentConfig.scale) return;
173 fprintf(f, "Scale factor = %.2f", currentConfig.scale);
174 break;
175 case MA_OPT3_HSCALE32:
176 if (no_def && defaultConfig.hscale32 == currentConfig.hscale32) return;
177 fprintf(f, "Hor. scale (for low res. games) = %.2f", currentConfig.hscale32);
178 break;
179 case MA_OPT3_HSCALE40:
180 if (no_def && defaultConfig.hscale40 == currentConfig.hscale40) return;
181 fprintf(f, "Hor. scale (for hi res. games) = %.2f", currentConfig.hscale40);
182 break;
183 case MA_OPT3_FILTERING:
184 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
185 fprintf(f, "Bilinear filtering = %i", currentConfig.scaling);
186 break;
187 case MA_OPT3_GAMMAA:
188 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
189 fprintf(f, "Gamma adjustment = %i", currentConfig.gamma);
190 break;
191 case MA_OPT3_BLACKLVL:
192 if (no_def && defaultConfig.gamma2 == currentConfig.gamma2) return;
193 fprintf(f, "Black level = %i", currentConfig.gamma2);
194 break;
195 case MA_OPT3_VSYNC:
196 if (no_def && (defaultConfig.EmuOpt&0x12000) == (currentConfig.gamma2&0x12000)) return;
197 strcpy(str24, "never");
198 if (currentConfig.EmuOpt & 0x2000)
199 strcpy(str24, (currentConfig.EmuOpt & 0x10000) ? "sometimes" : "always");
200 fprintf(f, "Wait for vsync = %s", str24);
201 break;
835e7900 202
203 default:
c46ffd31 204 lprintf("unhandled custom_write: %i\n", me->id);
835e7900 205 return;
206 }
207 fprintf(f, NL);
208}
209
1ca2ea4f 210
ca482e5d 211#if PLAT_HAVE_JOY
1ca2ea4f 212static const char *joyKeyNames[32] =
213{
214 "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4",
215 "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12",
216 "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20",
217 "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28"
218};
ca482e5d 219#endif
1ca2ea4f 220
221static void keys_write(FILE *fn, const char *bind_str, const int binds[32],
ca482e5d 222 const int def_binds[32], const char * const names[32], int key_count, int no_defaults)
1ca2ea4f 223{
224 int t, i;
ca482e5d 225 char act[48], name[32];
1ca2ea4f 226
ca482e5d 227 for (t = 0; t < key_count; t++)
1ca2ea4f 228 {
229 act[0] = act[31] = 0;
230 if (no_defaults && binds[t] == def_binds[t])
231 continue;
1ca2ea4f 232#ifdef __GP2X__
233 if (strcmp(names[t], "SELECT") == 0) continue;
234#endif
ca482e5d 235 if (t >= 32 || names[t] == NULL || strcmp(names[t], "???") == 0) {
236 if ((t >= '0' && t <= '9') || (t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z'))
237 sprintf(name, "%c", t);
238 else
239 sprintf(name, "\\x%02x", t);
240 }
241 else
242 strcpy(name, names[t]);
243
6fc57144 244 if (binds[t] == 0 && def_binds[t] != 0) {
ca482e5d 245 fprintf(fn, "%s %s =" NL, bind_str, name); // no binds
6fc57144 246 continue;
247 }
248
1ca2ea4f 249 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
250 if (me_ctrl_actions[i].mask & binds[t]) {
bdec53c9 251 strncpy(act, me_ctrl_actions[i].name, 31);
ca482e5d 252 fprintf(fn, "%s %s = player%i %s" NL, bind_str, name,
bdec53c9 253 ((binds[t]>>16)&1)+1, mystrip(act));
1ca2ea4f 254 }
255 }
1ca2ea4f 256
bdec53c9 257 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
258 if (emuctrl_actions[i].mask & binds[t]) {
259 strncpy(act, emuctrl_actions[i].name, 31);
ca482e5d 260 fprintf(fn, "%s %s = %s" NL, bind_str, name, mystrip(act));
bdec53c9 261 }
262 }
1ca2ea4f 263 }
264}
265
266
c46ffd31 267static int default_var(const menu_entry *me)
268{
269 switch (me->id)
270 {
271 case MA_OPT_ACC_TIMING:
272 case MA_OPT_ACC_SPRITES:
273 case MA_OPT_ARM940_SOUND:
274 case MA_OPT_6BUTTON_PAD:
275 case MA_OPT2_ENABLE_Z80:
276 case MA_OPT2_ENABLE_YM2612:
277 case MA_OPT2_ENABLE_SN76496:
1ca2ea4f 278 case MA_OPT2_SVP_DYNAREC:
c46ffd31 279 case MA_CDOPT_CDDA:
280 case MA_CDOPT_PCM:
281 case MA_CDOPT_SAVERAM:
282 case MA_CDOPT_SCALEROT_CHIP:
283 case MA_CDOPT_BETTER_SYNC:
284 return defaultConfig.s_PicoOpt;
285
286 case MA_OPT_SHOW_FPS:
287 case MA_OPT_ENABLE_SOUND:
288 case MA_OPT_SRAM_STATES:
289 case MA_OPT2_A_SN_GAMMA:
290 case MA_OPT2_VSYNC:
291 case MA_OPT2_GZIP_STATES:
292 case MA_OPT2_NO_LAST_ROM:
293 case MA_OPT2_RAMTIMINGS:
294 case MA_CDOPT_LEDS:
295 return defaultConfig.EmuOpt;
296
ca482e5d 297 case MA_CTRL_TURBO_RATE: return defaultConfig.turbo_rate;
298 case MA_OPT_SCALING: return defaultConfig.scaling;
299 case MA_OPT_ROTATION: return defaultConfig.rotation;
f0f0d2df 300
c46ffd31 301 case MA_OPT_SAVE_SLOT:
302 default:
303 return 0;
304 }
305}
835e7900 306
307int config_writesect(const char *fname, const char *section)
308{
309 FILE *fo = NULL, *fn = NULL; // old and new
c46ffd31 310 int no_defaults = 0; // avoid saving defaults
835e7900 311 menu_entry *me;
312 int t, i, tlen, ret;
313 char line[128], *tmp;
314
315 if (section != NULL)
316 {
c46ffd31 317 no_defaults = 1;
318
835e7900 319 fo = fopen(fname, "r");
320 if (fo == NULL) {
321 fn = fopen(fname, "w");
322 goto write;
323 }
324
325 ret = seek_sect(fo, section);
326 if (!ret) {
327 // sect not found, we can simply append
328 fclose(fo); fo = NULL;
329 fn = fopen(fname, "a");
330 goto write;
331 }
332
333 // use 2 files..
334 fclose(fo);
335 rename(fname, "tmp.cfg");
336 fo = fopen("tmp.cfg", "r");
337 fn = fopen(fname, "w");
338 if (fo == NULL || fn == NULL) goto write;
339
340 // copy everything until sect
341 tlen = strlen(section);
342 while (!feof(fo))
343 {
344 tmp = fgets(line, sizeof(line), fo);
345 if (tmp == NULL) break;
346
347 if (line[0] == '[' && strncmp(line + 1, section, tlen) == 0 && line[tlen+1] == ']')
348 break;
349 fputs(line, fn);
350 }
351
352 // now skip to next sect
353 while (!feof(fo))
354 {
355 tmp = fgets(line, sizeof(line), fo);
356 if (tmp == NULL) break;
357 if (line[0] == '[') {
358 fseek(fo, -strlen(line), SEEK_CUR);
359 break;
360 }
361 }
362 if (feof(fo))
363 {
364 fclose(fo); fo = NULL;
365 remove("tmp.cfg");
366 }
367 }
368 else
369 {
370 fn = fopen(fname, "w");
371 }
372
373write:
374 if (fn == NULL) {
375 if (fo) fclose(fo);
376 return -1;
377 }
378 if (section != NULL)
379 fprintf(fn, "[%s]" NL, section);
380
381 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]); t++)
382 {
383 me = cfg_opts[t];
384 tlen = *(cfg_opt_counts[t]);
385 for (i = 0; i < tlen; i++, me++)
386 {
387 if (!me->need_to_save) continue;
388 if ((me->beh != MB_ONOFF && me->beh != MB_RANGE) || me->name == NULL)
c46ffd31 389 custom_write(fn, me, no_defaults);
390 else if (me->beh == MB_ONOFF) {
391 if (!no_defaults || ((*(int *)me->var ^ default_var(me)) & me->mask))
392 fprintf(fn, "%s = %i" NL, me->name, (*(int *)me->var & me->mask) ? 1 : 0);
393 } else if (me->beh == MB_RANGE) {
f0f0d2df 394 if (!no_defaults || (*(int *)me->var ^ default_var(me)))
c46ffd31 395 fprintf(fn, "%s = %i" NL, me->name, *(int *)me->var);
396 }
835e7900 397 }
398 }
1ca2ea4f 399
400 // save key config
ca482e5d 401 keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, PLAT_MAX_KEYS, no_defaults);
402#if PLAT_HAVE_JOY
403 keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 32, 1);
404 keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 32, 1);
405 keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 32, 1);
406 keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 32, 1);
407#endif
1ca2ea4f 408
6fc57144 409#ifndef PSP
7b802576 410 if (section == NULL)
411 fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
6fc57144 412#endif
7b802576 413
835e7900 414 fprintf(fn, NL);
415
416 if (fo != NULL)
417 {
418 // copy whatever is left
419 while (!feof(fo))
420 {
421 tmp = fgets(line, sizeof(line), fo);
422 if (tmp == NULL) break;
423
424 fputs(line, fn);
425 }
426 fclose(fo);
427 remove("tmp.cfg");
428 }
429
430 fclose(fn);
431 return 0;
432}
433
434
835e7900 435int config_writelrom(const char *fname)
436{
437 char line[128], *tmp, *optr = NULL;
438 char *old_data = NULL;
439 int size;
440 FILE *f;
441
ca482e5d 442 if (strlen(loadedRomFName) == 0) return -1;
835e7900 443
444 f = fopen(fname, "r");
445 if (f != NULL)
446 {
447 fseek(f, 0, SEEK_END);
448 size = ftell(f);
449 fseek(f, 0, SEEK_SET);
450 old_data = malloc(size + size/8);
451 if (old_data != NULL)
452 {
453 optr = old_data;
454 while (!feof(f))
455 {
456 tmp = fgets(line, sizeof(line), f);
457 if (tmp == NULL) break;
458 mystrip(line);
459 if (strncasecmp(line, "LastUsedROM", 11) == 0)
460 continue;
461 sprintf(optr, "%s", line);
462 optr += strlen(optr);
463 }
464 }
465 fclose(f);
466 }
467
468 f = fopen(fname, "w");
469 if (f == NULL) return -1;
470
471 if (old_data != NULL) {
472 fwrite(old_data, 1, optr - old_data, f);
473 free(old_data);
474 }
ca482e5d 475 fprintf(f, "LastUsedROM = %s" NL, loadedRomFName);
835e7900 476 fclose(f);
477 return 0;
478}
479
1ca2ea4f 480/* --------------------------------------------------------------------------*/
481
482int config_readlrom(const char *fname)
483{
484 char line[128], *tmp;
485 int i, len, ret = -1;
486 FILE *f;
487
488 f = fopen(fname, "r");
489 if (f == NULL) return -1;
490
491 // seek to the section needed
492 while (!feof(f))
493 {
494 tmp = fgets(line, sizeof(line), f);
495 if (tmp == NULL) break;
496
497 if (strncasecmp(line, "LastUsedROM", 11) != 0) continue;
498 len = strlen(line);
499 for (i = 0; i < len; i++)
500 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
501 tmp = strchr(line, '=');
502 if (tmp == NULL) break;
503 tmp++;
504 mystrip(tmp);
505
ca482e5d 506 len = sizeof(loadedRomFName);
507 strncpy(loadedRomFName, tmp, len);
508 loadedRomFName[len-1] = 0;
1ca2ea4f 509 ret = 0;
510 break;
511 }
512 fclose(f);
513 return ret;
514}
515
835e7900 516
517static int custom_read(menu_entry *me, const char *var, const char *val)
518{
519 char *tmp;
520 int tmpi;
521
522 switch (me->id)
523 {
524 case MA_OPT_RENDERER:
525 if (strcasecmp(var, "Renderer") != 0) return 0;
6fc57144 526 if (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
602133e1 527 PicoOpt |= POPT_ALT_RENDERER;
835e7900 528 }
6fc57144 529 else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
602133e1 530 PicoOpt &= ~POPT_ALT_RENDERER;
835e7900 531 currentConfig.EmuOpt |= 0x80;
532 }
533 else if (strcasecmp(val, "8bit accurate") == 0) {
602133e1 534 PicoOpt &= ~POPT_ALT_RENDERER;
835e7900 535 currentConfig.EmuOpt &= ~0x80;
536 }
537 else
538 return 0;
539 return 1;
540
541 case MA_OPT_SCALING:
6fc57144 542#ifdef __GP2X__
835e7900 543 if (strcasecmp(var, "Scaling") != 0) return 0;
544 if (strcasecmp(val, "OFF") == 0) {
545 currentConfig.scaling = 0;
546 } else if (strcasecmp(val, "hw horizontal") == 0) {
547 currentConfig.scaling = 1;
548 } else if (strcasecmp(val, "hw horiz. + vert.") == 0) {
549 currentConfig.scaling = 2;
550 } else if (strcasecmp(val, "sw horizontal") == 0) {
551 currentConfig.scaling = 3;
552 } else
553 return 0;
554 return 1;
6fc57144 555#else
556 return 0;
557#endif
835e7900 558
559 case MA_OPT_FRAMESKIP:
560 if (strcasecmp(var, "Frameskip") != 0) return 0;
561 if (strcasecmp(val, "Auto") == 0)
562 currentConfig.Frameskip = -1;
563 else currentConfig.Frameskip = atoi(val);
564 return 1;
565
566 case MA_OPT_SOUND_QUALITY:
567 if (strcasecmp(var, "Sound Quality") != 0) return 0;
568 PsndRate = strtoul(val, &tmp, 10);
569 if (PsndRate < 8000 || PsndRate > 44100)
570 PsndRate = 22050;
571 while (*tmp == ' ') tmp++;
572 if (strcasecmp(tmp, "stereo") == 0) {
602133e1 573 PicoOpt |= POPT_EN_STEREO;
835e7900 574 } else if (strcasecmp(tmp, "mono") == 0) {
602133e1 575 PicoOpt &= ~POPT_EN_STEREO;
835e7900 576 } else
577 return 0;
578 return 1;
579
580 case MA_OPT_REGION:
581 if (strcasecmp(var, "Region") != 0) return 0;
582 if (strncasecmp(val, "Auto: ", 6) == 0)
583 {
584 const char *p = val + 5, *end = val + strlen(val);
585 int i;
586 PicoRegionOverride = PicoAutoRgnOrder = 0;
c46ffd31 587 for (i = 0; p < end && i < 3; i++)
588 {
589 while (*p == ' ') p++;
835e7900 590 if (p[0] == 'J' && p[1] == 'P') {
591 PicoAutoRgnOrder |= 1 << (i*4);
592 } else if (p[0] == 'U' && p[1] == 'S') {
593 PicoAutoRgnOrder |= 4 << (i*4);
594 } else if (p[0] == 'E' && p[1] == 'U') {
595 PicoAutoRgnOrder |= 8 << (i*4);
596 }
c46ffd31 597 while (*p != ' ' && *p != 0) p++;
598 if (*p == 0) break;
835e7900 599 }
600 }
601 else if (strcasecmp(val, "Auto") == 0) {
602 PicoRegionOverride = 0;
603 } else if (strcasecmp(val, "Japan NTSC") == 0) {
604 PicoRegionOverride = 1;
605 } else if (strcasecmp(val, "Japan PAL") == 0) {
606 PicoRegionOverride = 2;
607 } else if (strcasecmp(val, "USA") == 0) {
608 PicoRegionOverride = 4;
609 } else if (strcasecmp(val, "Europe") == 0) {
610 PicoRegionOverride = 8;
611 } else
612 return 0;
613 return 1;
614
615 case MA_OPT_CONFIRM_STATES:
616 if (strcasecmp(var, "Confirm savestate") != 0) return 0;
617 if (strcasecmp(val, "OFF") == 0) {
1ca2ea4f 618 currentConfig.EmuOpt &= ~(5<<9);
835e7900 619 } else if (strcasecmp(val, "writes") == 0) {
1ca2ea4f 620 currentConfig.EmuOpt &= ~(5<<9);
621 currentConfig.EmuOpt |= 1<<9;
835e7900 622 } else if (strcasecmp(val, "loads") == 0) {
1ca2ea4f 623 currentConfig.EmuOpt &= ~(5<<9);
624 currentConfig.EmuOpt |= 4<<9;
835e7900 625 } else if (strcasecmp(val, "both") == 0) {
1ca2ea4f 626 currentConfig.EmuOpt &= ~(5<<9);
627 currentConfig.EmuOpt |= 5<<9;
835e7900 628 } else
629 return 0;
630 return 1;
631
632 case MA_OPT_CPU_CLOCKS:
6fc57144 633#ifdef __GP2X__
835e7900 634 if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
6fc57144 635#elif defined(PSP)
636 if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
637#endif
835e7900 638 currentConfig.CPUclock = atoi(val);
639 return 1;
640
641 case MA_OPT2_GAMMA:
642 if (strcasecmp(var, "Gamma correction") != 0) return 0;
643 currentConfig.gamma = (int) (atof(val) * 100.0);
644 return 1;
645
646 case MA_OPT2_SQUIDGEHACK:
647 if (strcasecmp(var, "Squidgehack") != 0) return 0;
648 tmpi = atoi(val);
649 if (tmpi) *(int *)me->var |= me->mask;
650 else *(int *)me->var &= ~me->mask;
651 return 1;
652
653 case MA_CDOPT_READAHEAD:
654 if (strcasecmp(var, "ReadAhead buffer") != 0) return 0;
655 PicoCDBuffers = atoi(val) / 2;
656 return 1;
657
6fc57144 658 /* PSP */
659 case MA_OPT3_SCALE:
660 if (strcasecmp(var, "Scale factor") != 0) return 0;
661 currentConfig.scale = atof(val);
662 return 1;
663 case MA_OPT3_HSCALE32:
664 if (strcasecmp(var, "Hor. scale (for low res. games)") != 0) return 0;
665 currentConfig.hscale32 = atof(val);
666 return 1;
667 case MA_OPT3_HSCALE40:
668 if (strcasecmp(var, "Hor. scale (for hi res. games)") != 0) return 0;
669 currentConfig.hscale40 = atof(val);
670 return 1;
671 case MA_OPT3_FILTERING:
672 if (strcasecmp(var, "Bilinear filtering") != 0) return 0;
673 currentConfig.scaling = atoi(val);
674 return 1;
675 case MA_OPT3_GAMMAA:
676 if (strcasecmp(var, "Gamma adjustment") != 0) return 0;
677 currentConfig.gamma = atoi(val);
678 return 1;
679 case MA_OPT3_BLACKLVL:
680 if (strcasecmp(var, "Black level") != 0) return 0;
681 currentConfig.gamma2 = atoi(val);
682 return 1;
683 case MA_OPT3_VSYNC:
684 if (strcasecmp(var, "Wait for vsync") != 0) return 0;
685 if (strcasecmp(val, "never") == 0) {
686 currentConfig.EmuOpt &= ~0x12000;
687 } else if (strcasecmp(val, "sometimes") == 0) {
688 currentConfig.EmuOpt |= 0x12000;
689 } else if (strcasecmp(val, "always") == 0) {
690 currentConfig.EmuOpt &= ~0x12000;
691 currentConfig.EmuOpt |= 0x02000;
692 } else
693 return 0;
694 return 1;
695
835e7900 696 default:
c46ffd31 697 lprintf("unhandled custom_read: %i\n", me->id);
835e7900 698 return 0;
699 }
700}
701
702
bdec53c9 703static unsigned int keys_encountered = 0;
704
ca482e5d 705static void keys_parse(const char *var, const char *val, int binds[32],
706 const char * const names[32], int max_keys)
1ca2ea4f 707{
bdec53c9 708 int t, i;
1ca2ea4f 709 unsigned int player;
710
711 for (t = 0; t < 32; t++)
712 {
ca482e5d 713 if (names[t] && strcmp(names[t], var) == 0) break;
1ca2ea4f 714 }
ca482e5d 715 if (t == 32)
716 {
717 int len = strlen(var);
718 if (len == 1) t = var[0];
719 else if (len >= 4 && var[0] == '\\' && var[1] == 'x') {
720 char *p;
721 t = (int)strtoul(var + 2, &p, 16);
722 if (*p != 0) t = max_keys; // parse failed
723 }
724 else
725 t = max_keys; // invalid
726 }
727 if (t < 0 || t >= max_keys) {
1ca2ea4f 728 lprintf("unhandled bind \"%s\"\n", var);
729 return;
730 }
731
6a13ef3f 732 // unbind old, but only when key is first encountered
733 if (t < 32 && binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) {
1ca2ea4f 734 binds[t] = 0;
735 keys_encountered |= 1<<t;
736 }
737 if (val[0] == 0)
738 return;
739 if (strncasecmp(val, "player", 6) == 0)
740 {
741 player = atoi(val + 6) - 1;
742 if (player > 1) goto fail;
743 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
744 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0) {
745 binds[t] |= me_ctrl_actions[i].mask | (player<<16);
746 return;
747 }
748 }
749 }
750 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
751 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
752 binds[t] |= emuctrl_actions[i].mask;
753 return;
754 }
755 }
756
757fail:
758 lprintf("unhandled action \"%s\"\n", val);
759 return;
1ca2ea4f 760}
761
762
763#define try_joy_parse(num) { \
764 if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \
ca482e5d 765 keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames, 32); \
1ca2ea4f 766 return; \
767 } \
768}
769
835e7900 770static void parse(const char *var, const char *val)
771{
772 menu_entry *me;
773 int t, i, tlen, tmp, ret = 0;
774
1ca2ea4f 775 if (strcasecmp(var, "LastUsedROM") == 0)
776 return; /* handled elsewhere */
777
7b802576 778 if (strcasecmp(var, "Sound Volume") == 0) {
779 currentConfig.volume = atoi(val);
780 return;
781 }
782
1ca2ea4f 783 // key binds
784 if (strncasecmp(var, "bind ", 5) == 0) {
ca482e5d 785 keys_parse(var + 5, val, currentConfig.KeyBinds, keyNames, PLAT_MAX_KEYS);
1ca2ea4f 786 return;
787 }
ca482e5d 788#if PLAT_HAVE_JOY
1ca2ea4f 789 try_joy_parse(0)
790 try_joy_parse(1)
791 try_joy_parse(2)
792 try_joy_parse(3)
ca482e5d 793#endif
1ca2ea4f 794
835e7900 795 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]) && ret == 0; t++)
796 {
797 me = cfg_opts[t];
798 tlen = *(cfg_opt_counts[t]);
799 for (i = 0; i < tlen && ret == 0; i++, me++)
800 {
801 if (!me->need_to_save) continue;
802 if (me->name != NULL) {
803 if (strcasecmp(var, me->name) != 0) continue; // surely not this one
804 if (me->beh == MB_ONOFF) {
805 tmp = atoi(val);
806 if (tmp) *(int *)me->var |= me->mask;
807 else *(int *)me->var &= ~me->mask;
808 return;
809 } else if (me->beh == MB_RANGE) {
810 tmp = atoi(val);
811 if (tmp < me->min) tmp = me->min;
812 if (tmp > me->max) tmp = me->max;
813 *(int *)me->var = tmp;
814 return;
815 }
816 }
817 ret = custom_read(me, var, val);
818 }
819 }
84100c0f 820 if (!ret) lprintf("config_readsect: unhandled var: \"%s\"\n", var);
835e7900 821}
822
823
1ca2ea4f 824int config_havesect(const char *fname, const char *section)
825{
826 FILE *f;
827 int ret;
828
829 f = fopen(fname, "r");
830 if (f == NULL) return 0;
831
832 ret = seek_sect(f, section);
833 fclose(f);
834 return ret;
835}
836
835e7900 837int config_readsect(const char *fname, const char *section)
838{
4609d0cd 839 char line[128], *var, *val;
1ca2ea4f 840 FILE *f;
4609d0cd 841 int ret;
835e7900 842
1ca2ea4f 843 f = fopen(fname, "r");
d6114368 844 if (f == NULL) return -1;
835e7900 845
846 if (section != NULL)
847 {
848 ret = seek_sect(f, section);
849 if (!ret) {
c46ffd31 850 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
835e7900 851 fclose(f);
852 return -1;
853 }
854 }
855
bdec53c9 856 keys_encountered = 0;
857
835e7900 858 while (!feof(f))
859 {
4609d0cd 860 ret = config_get_var_val(f, line, sizeof(line), &var, &val);
861 if (ret == 0) break;
862 if (ret == -1) continue;
835e7900 863
864 parse(var, val);
865 }
866
867 fclose(f);
868 return 0;
869}
870
4609d0cd 871#endif // _MSC_VER
872
873static char *mystrip(char *str)
874{
875 int i, len;
876
877 len = strlen(str);
878 for (i = 0; i < len; i++)
879 if (str[i] != ' ') break;
880 if (i > 0) memmove(str, str + i, len - i + 1);
881
882 len = strlen(str);
883 for (i = len - 1; i >= 0; i--)
884 if (str[i] != ' ') break;
885 str[i+1] = 0;
886
887 return str;
888}
889
890/* returns:
891 * 0 - EOF, end
892 * 1 - parsed ok
893 * -1 - failed to parse line
894 */
895int config_get_var_val(void *file, char *line, int lsize, char **rvar, char **rval)
896{
897 char *var, *val, *tmp;
898 FILE *f = file;
899 int len, i;
900
901 tmp = fgets(line, lsize, f);
902 if (tmp == NULL) return 0;
903
904 if (line[0] == '[') return 0; // other section
905
906 // strip comments, linefeed, spaces..
907 len = strlen(line);
908 for (i = 0; i < len; i++)
909 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
910 mystrip(line);
911 len = strlen(line);
912 if (len <= 0) return -1;;
913
914 // get var and val
915 for (i = 0; i < len; i++)
916 if (line[i] == '=') break;
917 if (i >= len || strchr(&line[i+1], '=') != NULL) {
918 lprintf("config_readsect: can't parse: %s\n", line);
919 return -1;
920 }
921 line[i] = 0;
922 var = line;
923 val = &line[i+1];
924 mystrip(var);
925 mystrip(val);
926
927#ifndef _MSC_VER
928 if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
929 lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);
930 return -1;;
931 }
932#endif
933
934 *rvar = var;
935 *rval = val;
936 return 1;
937}
938