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