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