fix events loss
[libpicofe.git] / common / config.c
CommitLineData
c51bd2fe 1/*
2 * Human-readable config file management for PicoDrive
36f6fd5a 3 * (c) notaz, 2008
c51bd2fe 4 */
5
7836e87c 6#include <stdio.h>
c51bd2fe 7#include <string.h>
8#include <stdlib.h>
61f66fe0 9#ifdef __EPOC32__
36f6fd5a 10#include <unistd.h>
11#endif
c51bd2fe 12#include "config.h"
bc0420cd 13#include "input.h"
7836e87c 14#include "lprintf.h"
15
16static char *mystrip(char *str);
17
18#ifndef _MSC_VER
19
c51bd2fe 20#include "menu.h"
21#include "emu.h"
f11bad75 22#include <pico/pico.h>
c51bd2fe 23
24extern menu_entry opt_entries[];
25extern menu_entry opt2_entries[];
26extern menu_entry cdopt_entries[];
6589c840 27extern menu_entry ctrlopt_entries[];
c51bd2fe 28extern const int opt_entry_count;
29extern const int opt2_entry_count;
30extern const int cdopt_entry_count;
6589c840 31extern const int ctrlopt_entry_count;
960a8e27 32#ifdef PSP
33extern menu_entry opt3_entries[];
34extern const int opt3_entry_count;
35#endif
c51bd2fe 36
960a8e27 37static menu_entry *cfg_opts[] =
38{
39 opt_entries,
40 opt2_entries,
41 cdopt_entries,
6589c840 42 ctrlopt_entries,
960a8e27 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,
6589c840 53 &ctrlopt_entry_count,
960a8e27 54#ifdef PSP
55 &opt3_entry_count,
56#endif
57};
c51bd2fe 58
67dfdf5f 59#define NL "\r\n"
c51bd2fe 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
3e85ebdd 83static void custom_write(FILE *f, const menu_entry *me, int no_def)
c51bd2fe 84{
85 char *str, str24[24];
86
87 switch (me->id)
88 {
89 case MA_OPT_RENDERER:
dd5fd477 90 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
c51bd2fe 91 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
dd5fd477 92 if (PicoOpt&POPT_ALT_RENDERER)
960a8e27 93 str =
94#ifndef PSP
95 "8bit "
96#endif
97 "fast";
c51bd2fe 98 else if (currentConfig.EmuOpt&0x80)
960a8e27 99 str =
100#ifndef PSP
101 "16bit "
102#endif
103 "accurate";
c51bd2fe 104 else
105 str = "8bit accurate";
106 fprintf(f, "Renderer = %s", str);
107 break;
108
109 case MA_OPT_SCALING:
3e85ebdd 110 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
960a8e27 111#ifdef __GP2X__
c51bd2fe 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);
960a8e27 119#endif
c51bd2fe 120 break;
121 case MA_OPT_FRAMESKIP:
3e85ebdd 122 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
c51bd2fe 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:
dd5fd477 129 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
c51bd2fe 130 defaultConfig.s_PsndRate == PsndRate) return;
dd5fd477 131 str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
c51bd2fe 132 fprintf(f, "Sound Quality = %i %s", PsndRate, str);
133 break;
134 case MA_OPT_REGION:
3e85ebdd 135 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
c51bd2fe 136 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
960a8e27 137 strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
138 fprintf(f, "Region = %s", mystrip(str24));
c51bd2fe 139 break;
140 case MA_OPT_CONFIRM_STATES:
3e85ebdd 141 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
c51bd2fe 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:
3e85ebdd 151 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
960a8e27 152#ifdef __GP2X__
c51bd2fe 153 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
960a8e27 154#elif defined(PSP)
155 fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
156#endif
c51bd2fe 157 break;
158 case MA_OPT2_GAMMA:
3e85ebdd 159 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
c51bd2fe 160 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
161 break;
162 case MA_OPT2_SQUIDGEHACK:
3e85ebdd 163 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
c51bd2fe 164 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
165 break;
166 case MA_CDOPT_READAHEAD:
3e85ebdd 167 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
c51bd2fe 168 sprintf(str24, "%i", PicoCDBuffers * 2);
169 fprintf(f, "ReadAhead buffer = %s", str24);
170 break;
960a8e27 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;
c51bd2fe 203
204 default:
3e85ebdd 205 lprintf("unhandled custom_write: %i\n", me->id);
c51bd2fe 206 return;
207 }
208 fprintf(f, NL);
209}
210
367b6f1f 211
36f6fd5a 212#if PLAT_HAVE_JOY
367b6f1f 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};
36f6fd5a 220#endif
367b6f1f 221
bc0420cd 222static void keys_write(FILE *fn, const char *bind_str, int dev_id, const int *binds, int no_defaults)
367b6f1f 223{
bc0420cd 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);
367b6f1f 230
36f6fd5a 231 for (t = 0; t < key_count; t++)
367b6f1f 232 {
bc0420cd 233 const char *name;
367b6f1f 234 act[0] = act[31] = 0;
bc0420cd 235
367b6f1f 236 if (no_defaults && binds[t] == def_binds[t])
237 continue;
bc0420cd 238
239 name = in_get_key_name(dev_id, t);
367b6f1f 240#ifdef __GP2X__
bc0420cd 241 if (strcmp(name, "SELECT") == 0) continue;
367b6f1f 242#endif
36f6fd5a 243
960a8e27 244 if (binds[t] == 0 && def_binds[t] != 0) {
bc0420cd 245 fprintf(fn, "%s %s =" NL, bind_str, name);
960a8e27 246 continue;
247 }
248
367b6f1f 249 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
250 if (me_ctrl_actions[i].mask & binds[t]) {
8e708f92 251 strncpy(act, me_ctrl_actions[i].name, 31);
36f6fd5a 252 fprintf(fn, "%s %s = player%i %s" NL, bind_str, name,
8e708f92 253 ((binds[t]>>16)&1)+1, mystrip(act));
367b6f1f 254 }
255 }
367b6f1f 256
8e708f92 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);
36f6fd5a 260 fprintf(fn, "%s %s = %s" NL, bind_str, name, mystrip(act));
8e708f92 261 }
262 }
367b6f1f 263 }
264}
265
266
3e85ebdd 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:
367b6f1f 278 case MA_OPT2_SVP_DYNAREC:
3e85ebdd 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
36f6fd5a 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;
6589c840 300
3e85ebdd 301 case MA_OPT_SAVE_SLOT:
302 default:
303 return 0;
304 }
305}
c51bd2fe 306
307int config_writesect(const char *fname, const char *section)
308{
309 FILE *fo = NULL, *fn = NULL; // old and new
3e85ebdd 310 int no_defaults = 0; // avoid saving defaults
c51bd2fe 311 menu_entry *me;
312 int t, i, tlen, ret;
313 char line[128], *tmp;
314
315 if (section != NULL)
316 {
3e85ebdd 317 no_defaults = 1;
318
c51bd2fe 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)
3e85ebdd 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) {
6589c840 394 if (!no_defaults || (*(int *)me->var ^ default_var(me)))
3e85ebdd 395 fprintf(fn, "%s = %i" NL, me->name, *(int *)me->var);
396 }
c51bd2fe 397 }
398 }
367b6f1f 399
bc0420cd 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 */
36f6fd5a 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
bc0420cd 438#endif
367b6f1f 439
960a8e27 440#ifndef PSP
67dfdf5f 441 if (section == NULL)
442 fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
960a8e27 443#endif
67dfdf5f 444
c51bd2fe 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
c51bd2fe 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
36f6fd5a 473 if (strlen(loadedRomFName) == 0) return -1;
c51bd2fe 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 }
36f6fd5a 506 fprintf(f, "LastUsedROM = %s" NL, loadedRomFName);
c51bd2fe 507 fclose(f);
508 return 0;
509}
510
367b6f1f 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
36f6fd5a 537 len = sizeof(loadedRomFName);
538 strncpy(loadedRomFName, tmp, len);
539 loadedRomFName[len-1] = 0;
367b6f1f 540 ret = 0;
541 break;
542 }
543 fclose(f);
544 return ret;
545}
546
c51bd2fe 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;
960a8e27 557 if (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
dd5fd477 558 PicoOpt |= POPT_ALT_RENDERER;
c51bd2fe 559 }
960a8e27 560 else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
dd5fd477 561 PicoOpt &= ~POPT_ALT_RENDERER;
c51bd2fe 562 currentConfig.EmuOpt |= 0x80;
563 }
564 else if (strcasecmp(val, "8bit accurate") == 0) {
dd5fd477 565 PicoOpt &= ~POPT_ALT_RENDERER;
c51bd2fe 566 currentConfig.EmuOpt &= ~0x80;
567 }
568 else
569 return 0;
570 return 1;
571
572 case MA_OPT_SCALING:
960a8e27 573#ifdef __GP2X__
c51bd2fe 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;
960a8e27 586#else
587 return 0;
588#endif
c51bd2fe 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) {
dd5fd477 604 PicoOpt |= POPT_EN_STEREO;
c51bd2fe 605 } else if (strcasecmp(tmp, "mono") == 0) {
dd5fd477 606 PicoOpt &= ~POPT_EN_STEREO;
c51bd2fe 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;
3e85ebdd 618 for (i = 0; p < end && i < 3; i++)
619 {
620 while (*p == ' ') p++;
c51bd2fe 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 }
3e85ebdd 628 while (*p != ' ' && *p != 0) p++;
629 if (*p == 0) break;
c51bd2fe 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) {
367b6f1f 649 currentConfig.EmuOpt &= ~(5<<9);
c51bd2fe 650 } else if (strcasecmp(val, "writes") == 0) {
367b6f1f 651 currentConfig.EmuOpt &= ~(5<<9);
652 currentConfig.EmuOpt |= 1<<9;
c51bd2fe 653 } else if (strcasecmp(val, "loads") == 0) {
367b6f1f 654 currentConfig.EmuOpt &= ~(5<<9);
655 currentConfig.EmuOpt |= 4<<9;
c51bd2fe 656 } else if (strcasecmp(val, "both") == 0) {
367b6f1f 657 currentConfig.EmuOpt &= ~(5<<9);
658 currentConfig.EmuOpt |= 5<<9;
c51bd2fe 659 } else
660 return 0;
661 return 1;
662
663 case MA_OPT_CPU_CLOCKS:
960a8e27 664#ifdef __GP2X__
c51bd2fe 665 if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
960a8e27 666#elif defined(PSP)
667 if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
668#endif
c51bd2fe 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
960a8e27 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
c51bd2fe 727 default:
3e85ebdd 728 lprintf("unhandled custom_read: %i\n", me->id);
c51bd2fe 729 return 0;
730 }
731}
732
733
8e708f92 734static unsigned int keys_encountered = 0;
735
bc0420cd 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)
367b6f1f 764{
bc0420cd 765 int binds;
367b6f1f 766
bc0420cd 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/*
367b6f1f 775 for (t = 0; t < 32; t++)
776 {
36f6fd5a 777 if (names[t] && strcmp(names[t], var) == 0) break;
367b6f1f 778 }
36f6fd5a 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) {
367b6f1f 792 lprintf("unhandled bind \"%s\"\n", var);
793 return;
794 }
795
8611d124 796 // unbind old, but only when key is first encountered
797 if (t < 32 && binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) {
367b6f1f 798 binds[t] = 0;
799 keys_encountered |= 1<<t;
800 }
bc0420cd 801*/
367b6f1f 802}
803
804
805#define try_joy_parse(num) { \
806 if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \
36f6fd5a 807 keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames, 32); \
367b6f1f 808 return; \
809 } \
810}
811
bc0420cd 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
c51bd2fe 830static void parse(const char *var, const char *val)
831{
832 menu_entry *me;
833 int t, i, tlen, tmp, ret = 0;
834
367b6f1f 835 if (strcasecmp(var, "LastUsedROM") == 0)
836 return; /* handled elsewhere */
837
67dfdf5f 838 if (strcasecmp(var, "Sound Volume") == 0) {
839 currentConfig.volume = atoi(val);
840 return;
841 }
842
bc0420cd 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
367b6f1f 853 // key binds
bc0420cd 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);
367b6f1f 871 return;
872 }
bc0420cd 873
874#if 0//PLAT_HAVE_JOY
367b6f1f 875 try_joy_parse(0)
876 try_joy_parse(1)
877 try_joy_parse(2)
878 try_joy_parse(3)
36f6fd5a 879#endif
367b6f1f 880
c51bd2fe 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 }
5d239ae7 906 if (!ret) lprintf("config_readsect: unhandled var: \"%s\"\n", var);
c51bd2fe 907}
908
909
367b6f1f 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
c51bd2fe 923int config_readsect(const char *fname, const char *section)
924{
7836e87c 925 char line[128], *var, *val;
367b6f1f 926 FILE *f;
7836e87c 927 int ret;
c51bd2fe 928
367b6f1f 929 f = fopen(fname, "r");
a5365695 930 if (f == NULL) return -1;
c51bd2fe 931
932 if (section != NULL)
933 {
934 ret = seek_sect(f, section);
935 if (!ret) {
3e85ebdd 936 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
c51bd2fe 937 fclose(f);
938 return -1;
939 }
940 }
941
8e708f92 942 keys_encountered = 0;
bc0420cd 943 memset(input_dev_map, 0xff, sizeof(input_dev_map));
8e708f92 944
bc0420cd 945 in_config_start();
c51bd2fe 946 while (!feof(f))
947 {
7836e87c 948 ret = config_get_var_val(f, line, sizeof(line), &var, &val);
949 if (ret == 0) break;
950 if (ret == -1) continue;
c51bd2fe 951
952 parse(var, val);
953 }
bc0420cd 954 in_config_end();
c51bd2fe 955
956 fclose(f);
957 return 0;
958}
959
7836e87c 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