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