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