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