minor adjustments
[picodrive.git] / platform / common / config.c
... / ...
CommitLineData
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
14extern menu_entry opt_entries[];
15extern menu_entry opt2_entries[];
16extern menu_entry cdopt_entries[];
17extern const int opt_entry_count;
18extern const int opt2_entry_count;
19extern const int cdopt_entry_count;
20#ifdef PSP
21extern menu_entry opt3_entries[];
22extern const int opt3_entry_count;
23#endif
24
25static menu_entry *cfg_opts[] =
26{
27 opt_entries,
28 opt2_entries,
29 cdopt_entries,
30#ifdef PSP
31 opt3_entries,
32#endif
33};
34
35static const int *cfg_opt_counts[] =
36{
37 &opt_entry_count,
38 &opt2_entry_count,
39 &cdopt_entry_count,
40#ifdef PSP
41 &opt3_entry_count,
42#endif
43};
44
45#define NL "\r\n"
46
47
48static 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
66static int seek_sect(FILE *f, const char *section)
67{
68 char line[128], *tmp;
69 int len;
70
71 len = strlen(section);
72 // seek to the section needed
73 while (!feof(f))
74 {
75 tmp = fgets(line, sizeof(line), f);
76 if (tmp == NULL) break;
77
78 if (line[0] != '[') continue; // not section start
79 if (strncmp(line + 1, section, len) == 0 && line[len+1] == ']')
80 return 1; // found it
81 }
82
83 return 0;
84}
85
86
87static 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
216static const char *joyKeyNames[32] =
217{
218 "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4",
219 "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12",
220 "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20",
221 "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28"
222};
223
224static void keys_write(FILE *fn, const char *bind_str, const int binds[32],
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
262static int default_var(const menu_entry *me)
263{
264 switch (me->id)
265 {
266 case MA_OPT_ACC_TIMING:
267 case MA_OPT_ACC_SPRITES:
268 case MA_OPT_ARM940_SOUND:
269 case MA_OPT_6BUTTON_PAD:
270 case MA_OPT2_ENABLE_Z80:
271 case MA_OPT2_ENABLE_YM2612:
272 case MA_OPT2_ENABLE_SN76496:
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
298int 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
364write:
365 if (fn == NULL) {
366 if (fo) fclose(fo);
367 return -1;
368 }
369 if (section != NULL)
370 fprintf(fn, "[%s]" NL, section);
371
372 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]); t++)
373 {
374 me = cfg_opts[t];
375 tlen = *(cfg_opt_counts[t]);
376 for (i = 0; i < tlen; i++, me++)
377 {
378 if (!me->need_to_save) continue;
379 if ((me->beh != MB_ONOFF && me->beh != MB_RANGE) || me->name == NULL)
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
424int config_writelrom(const char *fname)
425{
426 char line[128], *tmp, *optr = NULL;
427 char *old_data = NULL;
428 int size;
429 FILE *f;
430
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
471int config_readlrom(const char *fname)
472{
473 char line[128], *tmp;
474 int i, len, ret = -1;
475 FILE *f;
476
477 f = fopen(fname, "r");
478 if (f == NULL) return -1;
479
480 // seek to the section needed
481 while (!feof(f))
482 {
483 tmp = fgets(line, sizeof(line), f);
484 if (tmp == NULL) break;
485
486 if (strncasecmp(line, "LastUsedROM", 11) != 0) continue;
487 len = strlen(line);
488 for (i = 0; i < len; i++)
489 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
490 tmp = strchr(line, '=');
491 if (tmp == NULL) break;
492 tmp++;
493 mystrip(tmp);
494
495 len = sizeof(lastRomFile);
496 strncpy(lastRomFile, tmp, len);
497 lastRomFile[len-1] = 0;
498 ret = 0;
499 break;
500 }
501 fclose(f);
502 return ret;
503}
504
505
506static int custom_read(menu_entry *me, const char *var, const char *val)
507{
508 char *tmp;
509 int tmpi;
510
511 switch (me->id)
512 {
513 case MA_OPT_RENDERER:
514 if (strcasecmp(var, "Renderer") != 0) return 0;
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
692static unsigned int keys_encountered = 0;
693
694static 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
732fail:
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
745static 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
797int config_havesect(const char *fname, const char *section)
798{
799 FILE *f;
800 int ret;
801
802 f = fopen(fname, "r");
803 if (f == NULL) return 0;
804
805 ret = seek_sect(f, section);
806 fclose(f);
807 return ret;
808}
809
810
811int 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