cyclone_debug fixed, minor adjustments
[picodrive.git] / platform / common / config.c
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"
11 #include "lprintf.h"
12 #include <Pico/Pico.h>
13
14 extern menu_entry opt_entries[];
15 extern menu_entry opt2_entries[];
16 extern menu_entry cdopt_entries[];
17 extern const int opt_entry_count;
18 extern const int opt2_entry_count;
19 extern const int cdopt_entry_count;
20 #ifdef PSP
21 extern menu_entry opt3_entries[];
22 extern const int opt3_entry_count;
23 #endif
24
25 static menu_entry *cfg_opts[] =
26 {
27         opt_entries,
28         opt2_entries,
29         cdopt_entries,
30 #ifdef PSP
31         opt3_entries,
32 #endif
33 };
34
35 static 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 };
44
45 #define NL "\r\n"
46
47
48 static char *mystrip(char *str)
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);
56
57         len = strlen(str);
58         for (i = len - 1; i >= 0; i--)
59                 if (str[i] != ' ') break;
60         str[i+1] = 0;
61
62         return str;
63 }
64
65
66 static 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
87 static void custom_write(FILE *f, const menu_entry *me, int no_def)
88 {
89         char *str, str24[24];
90
91         switch (me->id)
92         {
93                 case MA_OPT_RENDERER:
94                         if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
95                                 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
96                         if (PicoOpt&POPT_ALT_RENDERER)
97                                 str =
98 #ifndef PSP
99                                 "8bit "
100 #endif
101                                 "fast";
102                         else if (currentConfig.EmuOpt&0x80)
103                                 str =
104 #ifndef PSP
105                                 "16bit "
106 #endif
107                                 "accurate";
108                         else
109                                 str = "8bit accurate";
110                         fprintf(f, "Renderer = %s", str);
111                         break;
112
113                 case MA_OPT_SCALING:
114                         if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
115 #ifdef __GP2X__
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);
123 #endif
124                         break;
125                 case MA_OPT_FRAMESKIP:
126                         if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
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:
133                         if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
134                                 defaultConfig.s_PsndRate == PsndRate) return;
135                         str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
136                         fprintf(f, "Sound Quality = %i %s", PsndRate, str);
137                         break;
138                 case MA_OPT_REGION:
139                         if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
140                                 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
141                         strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
142                         fprintf(f, "Region = %s", mystrip(str24));
143                         break;
144                 case MA_OPT_CONFIRM_STATES:
145                         if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
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:
155                         if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
156 #ifdef __GP2X__
157                         fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
158 #elif defined(PSP)
159                         fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
160 #endif
161                         break;
162                 case MA_OPT2_GAMMA:
163                         if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
164                         fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
165                         break;
166                 case MA_OPT2_SQUIDGEHACK:
167                         if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
168                         fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
169                         break;
170                 case MA_CDOPT_READAHEAD:
171                         if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
172                         sprintf(str24, "%i", PicoCDBuffers * 2);
173                         fprintf(f, "ReadAhead buffer = %s", str24);
174                         break;
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;
207
208                 default:
209                         lprintf("unhandled custom_write: %i\n", me->id);
210                         return;
211         }
212         fprintf(f, NL);
213 }
214
215
216 static 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
224 static void keys_write(FILE *fn, const char *bind_str, const int binds[32],
225                 const int def_binds[32], const char * const names[32], int no_defaults)
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
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
244                 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
245                         if (me_ctrl_actions[i].mask & binds[t]) {
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));
249                         }
250                 }
251
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                 }
258         }
259 }
260
261
262 static 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:
273                 case MA_OPT2_SVP_DYNAREC:
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 }
297
298 int config_writesect(const char *fname, const char *section)
299 {
300         FILE *fo = NULL, *fn = NULL; // old and new
301         int no_defaults = 0; // avoid saving defaults
302         menu_entry *me;
303         int t, i, tlen, ret;
304         char line[128], *tmp;
305
306         if (section != NULL)
307         {
308                 no_defaults = 1;
309
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
364 write:
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)
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                         }
388                 }
389         }
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
398 #ifndef PSP
399         if (section == NULL)
400                 fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
401 #endif
402
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
424 int 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
431         if (strlen(lastRomFile) == 0) return -1;
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         }
464         fprintf(f, "LastUsedROM = %s" NL, lastRomFile);
465         fclose(f);
466         return 0;
467 }
468
469 /* --------------------------------------------------------------------------*/
470
471 int 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
505
506 static 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;
515                         if      (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
516                                 PicoOpt |=  POPT_ALT_RENDERER;
517                         }
518                         else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
519                                 PicoOpt &= ~POPT_ALT_RENDERER;
520                                 currentConfig.EmuOpt |=  0x80;
521                         }
522                         else if (strcasecmp(val, "8bit accurate") == 0) {
523                                 PicoOpt &= ~POPT_ALT_RENDERER;
524                                 currentConfig.EmuOpt &= ~0x80;
525                         }
526                         else
527                                 return 0;
528                         return 1;
529
530                 case MA_OPT_SCALING:
531 #ifdef __GP2X__
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;
544 #else
545                         return 0;
546 #endif
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) {
562                                 PicoOpt |=  POPT_EN_STEREO;
563                         } else if (strcasecmp(tmp, "mono") == 0) {
564                                 PicoOpt &= ~POPT_EN_STEREO;
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;
576                                 for (i = 0; p < end && i < 3; i++)
577                                 {
578                                         while (*p == ' ') p++;
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                                         }
586                                         while (*p != ' ' && *p != 0) p++;
587                                         if (*p == 0) break;
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) {
607                                 currentConfig.EmuOpt &= ~(5<<9);
608                         } else if (strcasecmp(val, "writes") == 0) {
609                                 currentConfig.EmuOpt &= ~(5<<9);
610                                 currentConfig.EmuOpt |=   1<<9;
611                         } else if (strcasecmp(val, "loads") == 0) {
612                                 currentConfig.EmuOpt &= ~(5<<9);
613                                 currentConfig.EmuOpt |=   4<<9;
614                         } else if (strcasecmp(val, "both") == 0) {
615                                 currentConfig.EmuOpt &= ~(5<<9);
616                                 currentConfig.EmuOpt |=   5<<9;
617                         } else
618                                 return 0;
619                         return 1;
620
621                 case MA_OPT_CPU_CLOCKS:
622 #ifdef __GP2X__
623                         if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
624 #elif defined(PSP)
625                         if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
626 #endif
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
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
685                 default:
686                         lprintf("unhandled custom_read: %i\n", me->id);
687                         return 0;
688         }
689 }
690
691
692 static unsigned int keys_encountered = 0;
693
694 static void keys_parse(const char *var, const char *val, int binds[32], const char * const names[32])
695 {
696         int t, i;
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
708         if (binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) { // hack
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
732 fail:
733         lprintf("unhandled action \"%s\"\n", val);
734         return;
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
745 static void parse(const char *var, const char *val)
746 {
747         menu_entry *me;
748         int t, i, tlen, tmp, ret = 0;
749
750         if (strcasecmp(var, "LastUsedROM") == 0)
751                 return; /* handled elsewhere */
752
753         if (strcasecmp(var, "Sound Volume") == 0) {
754                 currentConfig.volume = atoi(val);
755                 return;
756         }
757
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
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         }
793         if (!ret) lprintf("config_readsect: unhandled var: %s\n", var);
794 }
795
796
797 int 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
811 int config_readsect(const char *fname, const char *section)
812 {
813         char line[128], *var, *val, *tmp;
814         int len, i, ret;
815         FILE *f;
816
817         f = fopen(fname, "r");
818         if (f == NULL) return -1;
819
820         if (section != NULL)
821         {
822                 ret = seek_sect(f, section);
823                 if (!ret) {
824                         lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
825                         fclose(f);
826                         return -1;
827                 }
828         }
829
830         keys_encountered = 0;
831
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) {
851                         lprintf("config_readsect: can't parse: %s\n", line);
852                         continue;
853                 }
854                 line[i] = 0;
855                 var = line;
856                 val = &line[i+1];
857                 mystrip(var);
858                 mystrip(val);
859                 if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
860                         lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);
861                         continue;
862                 }
863
864                 parse(var, val);
865         }
866
867         fclose(f);
868         return 0;
869 }
870