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