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