1.45a Pico win32, code move
[picodrive.git] / platform / common / config.c
CommitLineData
835e7900 1/*
2 * Human-readable config file management for PicoDrive
3 * (c)
4 */
5
4609d0cd 6#include <stdio.h>
835e7900 7#include <string.h>
8#include <stdlib.h>
9#include "config.h"
4609d0cd 10#include "lprintf.h"
11
12static char *mystrip(char *str);
13
14#ifndef _MSC_VER
15
835e7900 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;
6fc57144 26#ifdef PSP
27extern menu_entry opt3_entries[];
28extern const int opt3_entry_count;
29#endif
835e7900 30
6fc57144 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};
835e7900 50
7b802576 51#define NL "\r\n"
835e7900 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
c46ffd31 75static void custom_write(FILE *f, const menu_entry *me, int no_def)
835e7900 76{
77 char *str, str24[24];
78
79 switch (me->id)
80 {
81 case MA_OPT_RENDERER:
602133e1 82 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
835e7900 83 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
602133e1 84 if (PicoOpt&POPT_ALT_RENDERER)
6fc57144 85 str =
86#ifndef PSP
87 "8bit "
88#endif
89 "fast";
835e7900 90 else if (currentConfig.EmuOpt&0x80)
6fc57144 91 str =
92#ifndef PSP
93 "16bit "
94#endif
95 "accurate";
835e7900 96 else
97 str = "8bit accurate";
98 fprintf(f, "Renderer = %s", str);
99 break;
100
101 case MA_OPT_SCALING:
c46ffd31 102 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
6fc57144 103#ifdef __GP2X__
835e7900 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);
6fc57144 111#endif
835e7900 112 break;
113 case MA_OPT_FRAMESKIP:
c46ffd31 114 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
835e7900 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:
602133e1 121 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
835e7900 122 defaultConfig.s_PsndRate == PsndRate) return;
602133e1 123 str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
835e7900 124 fprintf(f, "Sound Quality = %i %s", PsndRate, str);
125 break;
126 case MA_OPT_REGION:
c46ffd31 127 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
835e7900 128 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
6fc57144 129 strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
130 fprintf(f, "Region = %s", mystrip(str24));
835e7900 131 break;
132 case MA_OPT_CONFIRM_STATES:
c46ffd31 133 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
835e7900 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:
c46ffd31 143 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
6fc57144 144#ifdef __GP2X__
835e7900 145 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
6fc57144 146#elif defined(PSP)
147 fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
148#endif
835e7900 149 break;
150 case MA_OPT2_GAMMA:
c46ffd31 151 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
835e7900 152 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
153 break;
154 case MA_OPT2_SQUIDGEHACK:
c46ffd31 155 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
835e7900 156 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
157 break;
158 case MA_CDOPT_READAHEAD:
c46ffd31 159 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
835e7900 160 sprintf(str24, "%i", PicoCDBuffers * 2);
161 fprintf(f, "ReadAhead buffer = %s", str24);
162 break;
6fc57144 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;
835e7900 195
196 default:
c46ffd31 197 lprintf("unhandled custom_write: %i\n", me->id);
835e7900 198 return;
199 }
200 fprintf(f, NL);
201}
202
1ca2ea4f 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],
6fc57144 213 const int def_binds[32], const char * const names[32], int no_defaults)
1ca2ea4f 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
6fc57144 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
1ca2ea4f 232 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
233 if (me_ctrl_actions[i].mask & binds[t]) {
bdec53c9 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));
1ca2ea4f 237 }
238 }
1ca2ea4f 239
bdec53c9 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 }
1ca2ea4f 246 }
247}
248
249
c46ffd31 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:
1ca2ea4f 261 case MA_OPT2_SVP_DYNAREC:
c46ffd31 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}
835e7900 285
286int config_writesect(const char *fname, const char *section)
287{
288 FILE *fo = NULL, *fn = NULL; // old and new
c46ffd31 289 int no_defaults = 0; // avoid saving defaults
835e7900 290 menu_entry *me;
291 int t, i, tlen, ret;
292 char line[128], *tmp;
293
294 if (section != NULL)
295 {
c46ffd31 296 no_defaults = 1;
297
835e7900 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)
c46ffd31 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 }
835e7900 376 }
377 }
1ca2ea4f 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
6fc57144 386#ifndef PSP
7b802576 387 if (section == NULL)
388 fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
6fc57144 389#endif
7b802576 390
835e7900 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
835e7900 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
d6114368 419 if (strlen(lastRomFile) == 0) return -1;
835e7900 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 }
1ca2ea4f 452 fprintf(f, "LastUsedROM = %s" NL, lastRomFile);
835e7900 453 fclose(f);
454 return 0;
455}
456
1ca2ea4f 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
835e7900 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;
6fc57144 503 if (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
602133e1 504 PicoOpt |= POPT_ALT_RENDERER;
835e7900 505 }
6fc57144 506 else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
602133e1 507 PicoOpt &= ~POPT_ALT_RENDERER;
835e7900 508 currentConfig.EmuOpt |= 0x80;
509 }
510 else if (strcasecmp(val, "8bit accurate") == 0) {
602133e1 511 PicoOpt &= ~POPT_ALT_RENDERER;
835e7900 512 currentConfig.EmuOpt &= ~0x80;
513 }
514 else
515 return 0;
516 return 1;
517
518 case MA_OPT_SCALING:
6fc57144 519#ifdef __GP2X__
835e7900 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;
6fc57144 532#else
533 return 0;
534#endif
835e7900 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) {
602133e1 550 PicoOpt |= POPT_EN_STEREO;
835e7900 551 } else if (strcasecmp(tmp, "mono") == 0) {
602133e1 552 PicoOpt &= ~POPT_EN_STEREO;
835e7900 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;
c46ffd31 564 for (i = 0; p < end && i < 3; i++)
565 {
566 while (*p == ' ') p++;
835e7900 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 }
c46ffd31 574 while (*p != ' ' && *p != 0) p++;
575 if (*p == 0) break;
835e7900 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) {
1ca2ea4f 595 currentConfig.EmuOpt &= ~(5<<9);
835e7900 596 } else if (strcasecmp(val, "writes") == 0) {
1ca2ea4f 597 currentConfig.EmuOpt &= ~(5<<9);
598 currentConfig.EmuOpt |= 1<<9;
835e7900 599 } else if (strcasecmp(val, "loads") == 0) {
1ca2ea4f 600 currentConfig.EmuOpt &= ~(5<<9);
601 currentConfig.EmuOpt |= 4<<9;
835e7900 602 } else if (strcasecmp(val, "both") == 0) {
1ca2ea4f 603 currentConfig.EmuOpt &= ~(5<<9);
604 currentConfig.EmuOpt |= 5<<9;
835e7900 605 } else
606 return 0;
607 return 1;
608
609 case MA_OPT_CPU_CLOCKS:
6fc57144 610#ifdef __GP2X__
835e7900 611 if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
6fc57144 612#elif defined(PSP)
613 if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
614#endif
835e7900 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
6fc57144 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
835e7900 673 default:
c46ffd31 674 lprintf("unhandled custom_read: %i\n", me->id);
835e7900 675 return 0;
676 }
677}
678
679
bdec53c9 680static unsigned int keys_encountered = 0;
681
6fc57144 682static void keys_parse(const char *var, const char *val, int binds[32], const char * const names[32])
1ca2ea4f 683{
bdec53c9 684 int t, i;
1ca2ea4f 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
bdec53c9 696 if (binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) { // hack
1ca2ea4f 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;
1ca2ea4f 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
835e7900 733static void parse(const char *var, const char *val)
734{
735 menu_entry *me;
736 int t, i, tlen, tmp, ret = 0;
737
1ca2ea4f 738 if (strcasecmp(var, "LastUsedROM") == 0)
739 return; /* handled elsewhere */
740
7b802576 741 if (strcasecmp(var, "Sound Volume") == 0) {
742 currentConfig.volume = atoi(val);
743 return;
744 }
745
1ca2ea4f 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
835e7900 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 }
c46ffd31 781 if (!ret) lprintf("config_readsect: unhandled var: %s\n", var);
835e7900 782}
783
784
1ca2ea4f 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
835e7900 798int config_readsect(const char *fname, const char *section)
799{
4609d0cd 800 char line[128], *var, *val;
1ca2ea4f 801 FILE *f;
4609d0cd 802 int ret;
835e7900 803
1ca2ea4f 804 f = fopen(fname, "r");
d6114368 805 if (f == NULL) return -1;
835e7900 806
807 if (section != NULL)
808 {
809 ret = seek_sect(f, section);
810 if (!ret) {
c46ffd31 811 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
835e7900 812 fclose(f);
813 return -1;
814 }
815 }
816
bdec53c9 817 keys_encountered = 0;
818
835e7900 819 while (!feof(f))
820 {
4609d0cd 821 ret = config_get_var_val(f, line, sizeof(line), &var, &val);
822 if (ret == 0) break;
823 if (ret == -1) continue;
835e7900 824
825 parse(var, val);
826 }
827
828 fclose(f);
829 return 0;
830}
831
4609d0cd 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