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